diff --git a/app/assets/javascripts/admin/adapters/build-plugin.js.es6 b/app/assets/javascripts/admin/adapters/build-plugin.js.es6 index b73bf7ecb1f..0297aaa8be7 100644 --- a/app/assets/javascripts/admin/adapters/build-plugin.js.es6 +++ b/app/assets/javascripts/admin/adapters/build-plugin.js.es6 @@ -1,9 +1,11 @@ -import RestAdapter from 'discourse/adapters/rest'; +import RestAdapter from "discourse/adapters/rest"; export default function buildPluginAdapter(pluginName) { return RestAdapter.extend({ pathFor(store, type, findArgs) { - return "/admin/plugins/" + pluginName + this._super(store, type, findArgs); + return ( + "/admin/plugins/" + pluginName + this._super(store, type, findArgs) + ); } }); } diff --git a/app/assets/javascripts/admin/adapters/customization-base.js.es6 b/app/assets/javascripts/admin/adapters/customization-base.js.es6 index f57240a116f..d1087e70199 100644 --- a/app/assets/javascripts/admin/adapters/customization-base.js.es6 +++ b/app/assets/javascripts/admin/adapters/customization-base.js.es6 @@ -1,4 +1,4 @@ -import RestAdapter from 'discourse/adapters/rest'; +import RestAdapter from "discourse/adapters/rest"; export default RestAdapter.extend({ basePath() { diff --git a/app/assets/javascripts/admin/adapters/embedding.js.es6 b/app/assets/javascripts/admin/adapters/embedding.js.es6 index c8985cfdcae..c1d5eceace8 100644 --- a/app/assets/javascripts/admin/adapters/embedding.js.es6 +++ b/app/assets/javascripts/admin/adapters/embedding.js.es6 @@ -1,4 +1,4 @@ -import RestAdapter from 'discourse/adapters/rest'; +import RestAdapter from "discourse/adapters/rest"; export default RestAdapter.extend({ pathFor() { diff --git a/app/assets/javascripts/admin/adapters/flagged-post.js.es6 b/app/assets/javascripts/admin/adapters/flagged-post.js.es6 index 1a1065f6388..dc44315170f 100644 --- a/app/assets/javascripts/admin/adapters/flagged-post.js.es6 +++ b/app/assets/javascripts/admin/adapters/flagged-post.js.es6 @@ -1,4 +1,4 @@ -import RestAdapter from 'discourse/adapters/rest'; +import RestAdapter from "discourse/adapters/rest"; export default RestAdapter.extend({ pathFor(store, type, findArgs) { @@ -17,20 +17,20 @@ export default RestAdapter.extend({ hasMore: pa.conversation.has_more, response: { excerpt: pa.conversation.response.excerpt, - user: helper.lookup('user', pa.conversation.response.user_id) + user: helper.lookup("user", pa.conversation.response.user_id) } }; if (pa.conversation.reply) { conversation.reply = { excerpt: pa.conversation.reply.excerpt, - user: helper.lookup('user', pa.conversation.reply.user_id) + user: helper.lookup("user", pa.conversation.reply.user_id) }; } conversations.push(conversation); } }); - flag.set('conversations', conversations); + flag.set("conversations", conversations); }); return results; diff --git a/app/assets/javascripts/admin/adapters/site-text.js.es6 b/app/assets/javascripts/admin/adapters/site-text.js.es6 index b547b06f3ca..f362b06fe1d 100644 --- a/app/assets/javascripts/admin/adapters/site-text.js.es6 +++ b/app/assets/javascripts/admin/adapters/site-text.js.es6 @@ -1,2 +1,2 @@ -import CustomizationBase from 'admin/adapters/customization-base'; +import CustomizationBase from "admin/adapters/customization-base"; export default CustomizationBase; diff --git a/app/assets/javascripts/admin/adapters/theme.js.es6 b/app/assets/javascripts/admin/adapters/theme.js.es6 index df9c8830d1a..b98ba3e4230 100644 --- a/app/assets/javascripts/admin/adapters/theme.js.es6 +++ b/app/assets/javascripts/admin/adapters/theme.js.es6 @@ -1,4 +1,4 @@ -import RestAdapter from 'discourse/adapters/rest'; +import RestAdapter from "discourse/adapters/rest"; export default RestAdapter.extend({ basePath() { @@ -7,7 +7,9 @@ export default RestAdapter.extend({ afterFindAll(results) { let map = {}; - results.forEach(theme => {map[theme.id] = theme;}); + results.forEach(theme => { + map[theme.id] = theme; + }); results.forEach(theme => { let mapped = theme.get("child_themes") || []; mapped = mapped.map(t => map[t.id]); diff --git a/app/assets/javascripts/admin/adapters/user-field.js.es6 b/app/assets/javascripts/admin/adapters/user-field.js.es6 index b547b06f3ca..f362b06fe1d 100644 --- a/app/assets/javascripts/admin/adapters/user-field.js.es6 +++ b/app/assets/javascripts/admin/adapters/user-field.js.es6 @@ -1,2 +1,2 @@ -import CustomizationBase from 'admin/adapters/customization-base'; +import CustomizationBase from "admin/adapters/customization-base"; export default CustomizationBase; diff --git a/app/assets/javascripts/admin/adapters/web-hook-event.js.es6 b/app/assets/javascripts/admin/adapters/web-hook-event.js.es6 index 122070ce3e4..4c80ac3ff0c 100644 --- a/app/assets/javascripts/admin/adapters/web-hook-event.js.es6 +++ b/app/assets/javascripts/admin/adapters/web-hook-event.js.es6 @@ -1,7 +1,7 @@ -import RESTAdapter from 'discourse/adapters/rest'; +import RESTAdapter from "discourse/adapters/rest"; export default RESTAdapter.extend({ basePath() { - return '/admin/api/'; + return "/admin/api/"; } }); diff --git a/app/assets/javascripts/admin/adapters/web-hook.js.es6 b/app/assets/javascripts/admin/adapters/web-hook.js.es6 index 122070ce3e4..4c80ac3ff0c 100644 --- a/app/assets/javascripts/admin/adapters/web-hook.js.es6 +++ b/app/assets/javascripts/admin/adapters/web-hook.js.es6 @@ -1,7 +1,7 @@ -import RESTAdapter from 'discourse/adapters/rest'; +import RESTAdapter from "discourse/adapters/rest"; export default RESTAdapter.extend({ basePath() { - return '/admin/api/'; + return "/admin/api/"; } }); diff --git a/app/assets/javascripts/admin/components/ace-editor.js.es6 b/app/assets/javascripts/admin/components/ace-editor.js.es6 index faad36798ad..192bc4b410c 100644 --- a/app/assets/javascripts/admin/components/ace-editor.js.es6 +++ b/app/assets/javascripts/admin/components/ace-editor.js.es6 @@ -1,37 +1,37 @@ -import loadScript from 'discourse/lib/load-script'; -import { observes } from 'ember-addons/ember-computed-decorators'; +import loadScript from "discourse/lib/load-script"; +import { observes } from "ember-addons/ember-computed-decorators"; const LOAD_ASYNC = !Ember.testing; export default Ember.Component.extend({ - mode: 'css', - classNames: ['ace-wrapper'], + mode: "css", + classNames: ["ace-wrapper"], _editor: null, _skipContentChangeEvent: null, disabled: false, - @observes('editorId') + @observes("editorId") editorIdChanged() { - if (this.get('autofocus')) { - this.send('focus'); + if (this.get("autofocus")) { + this.send("focus"); } }, - @observes('content') + @observes("content") contentChanged() { if (this._editor && !this._skipContentChangeEvent) { - this._editor.getSession().setValue(this.get('content')); + this._editor.getSession().setValue(this.get("content")); } }, - @observes('mode') + @observes("mode") modeChanged() { if (LOAD_ASYNC && this._editor && !this._skipContentChangeEvent) { - this._editor.getSession().setMode("ace/mode/" + this.get('mode')); + this._editor.getSession().setMode("ace/mode/" + this.get("mode")); } }, - @observes('disabled') + @observes("disabled") disabledStateChanged() { this.changeDisabledState(); }, @@ -39,7 +39,7 @@ export default Ember.Component.extend({ changeDisabledState() { const editor = this._editor; if (editor) { - const disabled = this.get('disabled'); + const disabled = this.get("disabled"); editor.setOptions({ readOnly: disabled, highlightActiveLine: !disabled, @@ -56,12 +56,11 @@ export default Ember.Component.extend({ } if (this.appEvents) { // xxx: don't run during qunit tests - this.appEvents.off('ace:resize', this, this.resize); + this.appEvents.off("ace:resize", this, this.resize); } - $(window).off('ace:resize'); - - }.on('willDestroyElement'), + $(window).off("ace:resize"); + }.on("willDestroyElement"), resize() { if (this._editor) { @@ -73,37 +72,41 @@ export default Ember.Component.extend({ this._super(); loadScript("/javascripts/ace/ace.js", { scriptTag: true }).then(() => { - window.ace.require(['ace/ace'], loadedAce => { - if (!this.element || this.isDestroying || this.isDestroyed) { return; } - const editor = loadedAce.edit(this.$('.ace')[0]); + window.ace.require(["ace/ace"], loadedAce => { + if (!this.element || this.isDestroying || this.isDestroyed) { + return; + } + const editor = loadedAce.edit(this.$(".ace")[0]); if (LOAD_ASYNC) { editor.setTheme("ace/theme/chrome"); } editor.setShowPrintMargin(false); - editor.setOptions({fontSize: "14px"}); + editor.setOptions({ fontSize: "14px" }); if (LOAD_ASYNC) { - editor.getSession().setMode("ace/mode/" + this.get('mode')); + editor.getSession().setMode("ace/mode/" + this.get("mode")); } - editor.on('change', () => { + editor.on("change", () => { this._skipContentChangeEvent = true; - this.set('content', editor.getSession().getValue()); + this.set("content", editor.getSession().getValue()); this._skipContentChangeEvent = false; }); editor.$blockScrolling = Infinity; - editor.renderer.setScrollMargin(10,10); + editor.renderer.setScrollMargin(10, 10); - this.$().data('editor', editor); + this.$().data("editor", editor); this._editor = editor; this.changeDisabledState(); - $(window).off('ace:resize').on('ace:resize', ()=>{ - this.appEvents.trigger('ace:resize'); - }); + $(window) + .off("ace:resize") + .on("ace:resize", () => { + this.appEvents.trigger("ace:resize"); + }); if (this.appEvents) { // xxx: don't run during qunit tests - this.appEvents.on('ace:resize', ()=>this.resize()); + this.appEvents.on("ace:resize", () => this.resize()); } if (this.get("autofocus")) { diff --git a/app/assets/javascripts/admin/components/admin-backups-logs.js.es6 b/app/assets/javascripts/admin/components/admin-backups-logs.js.es6 index 01f4547b17e..c2f9df52059 100644 --- a/app/assets/javascripts/admin/components/admin-backups-logs.js.es6 +++ b/app/assets/javascripts/admin/components/admin-backups-logs.js.es6 @@ -1,57 +1,64 @@ -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'; +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(bufferedRender({ - classNames: ["admin-backups-logs"], +export default Ember.Component.extend( + bufferedRender({ + classNames: ["admin-backups-logs"], - init() { - this._super(); - this._reset(); - }, + init() { + this._super(); + this._reset(); + }, - _reset() { - this.setProperties({ formattedLogs: "", index: 0 }); - }, + _reset() { + this.setProperties({ formattedLogs: "", index: 0 }); + }, - _scrollDown() { - const $div = this.$()[0]; - $div.scrollTop = $div.scrollHeight; - }, + _scrollDown() { + const $div = this.$()[0]; + $div.scrollTop = $div.scrollHeight; + }, - _updateFormattedLogs: debounce(function() { - const logs = this.get("logs"); - if (logs.length === 0) { - this._reset(); // reset the cached logs whenever the model is reset - } else { - // do the log formatting only once for HELLish performance - let formattedLogs = this.get("formattedLogs"); - for (let i = this.get("index"), length = logs.length; i < length; i++) { - const date = logs[i].get("timestamp"), - message = escapeExpression(logs[i].get("message")); - formattedLogs += "[" + date + "] " + message + "\n"; + _updateFormattedLogs: debounce(function() { + const logs = this.get("logs"); + if (logs.length === 0) { + this._reset(); // reset the cached logs whenever the model is reset + } else { + // do the log formatting only once for HELLish performance + let formattedLogs = this.get("formattedLogs"); + for (let i = this.get("index"), length = logs.length; i < length; i++) { + const date = logs[i].get("timestamp"), + message = escapeExpression(logs[i].get("message")); + formattedLogs += "[" + date + "] " + message + "\n"; + } + // update the formatted logs & cache index + this.setProperties({ + formattedLogs: formattedLogs, + index: logs.length + }); + // force rerender + this.rerenderBuffer(); } - // update the formatted logs & cache index - this.setProperties({ formattedLogs: formattedLogs, index: logs.length }); - // force rerender - this.rerenderBuffer(); - } - Ember.run.scheduleOnce('afterRender', this, this._scrollDown); - }, 150).observes("logs.[]").on('init'), + Ember.run.scheduleOnce("afterRender", this, this._scrollDown); + }, 150) + .observes("logs.[]") + .on("init"), - buildBuffer(buffer) { - const formattedLogs = this.get("formattedLogs"); - if (formattedLogs && formattedLogs.length > 0) { - buffer.push("
");
-      buffer.push(formattedLogs);
-      buffer.push("
"); - } else { - buffer.push("

" + I18n.t("admin.backups.logs.none") + "

"); + buildBuffer(buffer) { + const formattedLogs = this.get("formattedLogs"); + if (formattedLogs && formattedLogs.length > 0) { + buffer.push("
");
+        buffer.push(formattedLogs);
+        buffer.push("
"); + } else { + buffer.push("

" + I18n.t("admin.backups.logs.none") + "

"); + } + // add a loading indicator + if (this.get("status.isOperationRunning")) { + buffer.push(renderSpinner("small")); + } } - // add a loading indicator - if (this.get("status.isOperationRunning")) { - buffer.push(renderSpinner('small')); - } - } -})); + }) +); diff --git a/app/assets/javascripts/admin/components/admin-directory-toggle.js.es6 b/app/assets/javascripts/admin/components/admin-directory-toggle.js.es6 index 8d43474a9d5..add82cbbe72 100644 --- a/app/assets/javascripts/admin/components/admin-directory-toggle.js.es6 +++ b/app/assets/javascripts/admin/components/admin-directory-toggle.js.es6 @@ -1,33 +1,37 @@ -import { iconHTML } from 'discourse-common/lib/icon-library'; -import { bufferedRender } from 'discourse-common/lib/buffered-render'; +import { iconHTML } from "discourse-common/lib/icon-library"; +import { bufferedRender } from "discourse-common/lib/buffered-render"; -export default Ember.Component.extend(bufferedRender({ - tagName: 'th', - classNames: ['sortable'], - rerenderTriggers: ['order', 'ascending'], +export default Ember.Component.extend( + bufferedRender({ + tagName: "th", + classNames: ["sortable"], + rerenderTriggers: ["order", "ascending"], - buildBuffer(buffer) { - const icon = this.get('icon'); + buildBuffer(buffer) { + const icon = this.get("icon"); - if (icon) { - buffer.push(iconHTML(icon)); + if (icon) { + buffer.push(iconHTML(icon)); + } + + buffer.push(I18n.t(this.get("i18nKey"))); + + if (this.get("field") === this.get("order")) { + buffer.push( + iconHTML(this.get("ascending") ? "chevron-up" : "chevron-down") + ); + } + }, + + click() { + const currentOrder = this.get("order"); + const field = this.get("field"); + + if (currentOrder === field) { + this.set("ascending", this.get("ascending") ? null : true); + } else { + this.setProperties({ order: field, ascending: null }); + } } - - buffer.push(I18n.t(this.get('i18nKey'))); - - if (this.get('field') === this.get('order')) { - buffer.push(iconHTML(this.get('ascending') ? 'chevron-up' : 'chevron-down')); - } - }, - - click() { - const currentOrder = this.get('order'); - const field = this.get('field'); - - if (currentOrder === field) { - this.set('ascending', this.get('ascending') ? null : true); - } else { - this.setProperties({ order: field, ascending: null }); - } - } -})); + }) +); diff --git a/app/assets/javascripts/admin/components/admin-form-row.js.es6 b/app/assets/javascripts/admin/components/admin-form-row.js.es6 index e7cef2edb0c..5159168c303 100644 --- a/app/assets/javascripts/admin/components/admin-form-row.js.es6 +++ b/app/assets/javascripts/admin/components/admin-form-row.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - classNames: ['row'] + classNames: ["row"] }); diff --git a/app/assets/javascripts/admin/components/admin-graph.js.es6 b/app/assets/javascripts/admin/components/admin-graph.js.es6 index 724e9673742..7752230cfb1 100644 --- a/app/assets/javascripts/admin/components/admin-graph.js.es6 +++ b/app/assets/javascripts/admin/components/admin-graph.js.es6 @@ -1,49 +1,56 @@ -import loadScript from 'discourse/lib/load-script'; -import { number } from 'discourse/lib/formatter'; +import loadScript from "discourse/lib/load-script"; +import { number } from "discourse/lib/formatter"; export default Ember.Component.extend({ - tagName: 'canvas', - refreshChart(){ + tagName: "canvas", + refreshChart() { const ctx = this.$()[0].getContext("2d"); const model = this.get("model"); const rawData = this.get("model.data"); var data = { labels: rawData.map(r => r.x), - datasets: [{ - data: rawData.map(r => r.y), - label: model.get('title'), - backgroundColor: "rgba(200,220,240,0.3)", - borderColor: "#08C" - }] + datasets: [ + { + data: rawData.map(r => r.y), + label: model.get("title"), + backgroundColor: "rgba(200,220,240,0.3)", + borderColor: "#08C" + } + ] }; const config = { - type: 'line', + type: "line", data: data, options: { responsive: true, tooltips: { callbacks: { - title: (context) => moment(context[0].xLabel, "YYYY-MM-DD").format("LL") + title: context => + moment(context[0].xLabel, "YYYY-MM-DD").format("LL") } }, scales: { - yAxes: [{ - display: true, - ticks: { - callback: (label) => number(label), - suggestedMin: 0 + yAxes: [ + { + display: true, + ticks: { + callback: label => number(label), + suggestedMin: 0 + } } - }] + ] } - }, + } }; this._chart = new window.Chart(ctx, config); }, - didInsertElement(){ - loadScript("/javascripts/Chart.min.js").then(() => this.refreshChart.apply(this)); + didInsertElement() { + loadScript("/javascripts/Chart.min.js").then(() => + this.refreshChart.apply(this) + ); } }); diff --git a/app/assets/javascripts/admin/components/admin-nav.js.es6 b/app/assets/javascripts/admin/components/admin-nav.js.es6 index 9250c1ae73b..91ad923ffcc 100644 --- a/app/assets/javascripts/admin/components/admin-nav.js.es6 +++ b/app/assets/javascripts/admin/components/admin-nav.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - tagName: '' + tagName: "" }); diff --git a/app/assets/javascripts/admin/components/admin-report-counts.js.es6 b/app/assets/javascripts/admin/components/admin-report-counts.js.es6 index 1739a186b34..849d81460a2 100644 --- a/app/assets/javascripts/admin/components/admin-report-counts.js.es6 +++ b/app/assets/javascripts/admin/components/admin-report-counts.js.es6 @@ -1,6 +1,9 @@ export default Ember.Component.extend({ allTime: true, - tagName: 'tr', - reverseColors: Ember.computed.match('report.type', /^(time_to_first_response|topics_with_no_response)$/), - classNameBindings: ['reverseColors'] + tagName: "tr", + reverseColors: Ember.computed.match( + "report.type", + /^(time_to_first_response|topics_with_no_response)$/ + ), + classNameBindings: ["reverseColors"] }); diff --git a/app/assets/javascripts/admin/components/admin-report-per-day-counts.js.es6 b/app/assets/javascripts/admin/components/admin-report-per-day-counts.js.es6 index 25bf94db210..b7620b66cd3 100644 --- a/app/assets/javascripts/admin/components/admin-report-per-day-counts.js.es6 +++ b/app/assets/javascripts/admin/components/admin-report-per-day-counts.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - tagName: 'tr' + tagName: "tr" }); diff --git a/app/assets/javascripts/admin/components/admin-report-trust-level-counts.js.es6 b/app/assets/javascripts/admin/components/admin-report-trust-level-counts.js.es6 index 25bf94db210..b7620b66cd3 100644 --- a/app/assets/javascripts/admin/components/admin-report-trust-level-counts.js.es6 +++ b/app/assets/javascripts/admin/components/admin-report-trust-level-counts.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - tagName: 'tr' + tagName: "tr" }); diff --git a/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 b/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 index 907f803aed3..64e95cecf98 100644 --- a/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 +++ b/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 @@ -1,97 +1,111 @@ -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'; +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'], +export default Ember.Component.extend(bufferedProperty("userField"), { + editing: Ember.computed.empty("userField.id"), + classNameBindings: [":user-field"], - cantMoveUp: propertyEqual('userField', 'firstField'), - cantMoveDown: propertyEqual('userField', 'lastField'), + cantMoveUp: propertyEqual("userField", "firstField"), + cantMoveDown: propertyEqual("userField", "lastField"), userFieldsDescription: function() { - return I18n.t('admin.user_fields.description'); + return I18n.t("admin.user_fields.description"); }.property(), bufferedFieldType: function() { - return UserField.fieldTypeById(this.get('buffered.field_type')); - }.property('buffered.field_type'), + return UserField.fieldTypeById(this.get("buffered.field_type")); + }.property("buffered.field_type"), _focusOnEdit: function() { - if (this.get('editing')) { - Ember.run.scheduleOnce('afterRender', this, '_focusName'); + if (this.get("editing")) { + Ember.run.scheduleOnce("afterRender", this, "_focusName"); } - }.observes('editing').on('didInsertElement'), + } + .observes("editing") + .on("didInsertElement"), _focusName: function() { - $('.user-field-name').select(); + $(".user-field-name").select(); }, fieldName: function() { - return UserField.fieldTypeById(this.get('userField.field_type')).get('name'); - }.property('userField.field_type'), + return UserField.fieldTypeById(this.get("userField.field_type")).get( + "name" + ); + }.property("userField.field_type"), flags: function() { const ret = []; - if (this.get('userField.editable')) { - ret.push(I18n.t('admin.user_fields.editable.enabled')); + if (this.get("userField.editable")) { + ret.push(I18n.t("admin.user_fields.editable.enabled")); } - if (this.get('userField.required')) { - ret.push(I18n.t('admin.user_fields.required.enabled')); + if (this.get("userField.required")) { + ret.push(I18n.t("admin.user_fields.required.enabled")); } - if (this.get('userField.show_on_profile')) { - ret.push(I18n.t('admin.user_fields.show_on_profile.enabled')); + if (this.get("userField.show_on_profile")) { + ret.push(I18n.t("admin.user_fields.show_on_profile.enabled")); } - if (this.get('userField.show_on_user_card')) { - ret.push(I18n.t('admin.user_fields.show_on_user_card.enabled')); + if (this.get("userField.show_on_user_card")) { + ret.push(I18n.t("admin.user_fields.show_on_user_card.enabled")); } - return ret.join(', '); - }.property('userField.editable', 'userField.required', 'userField.show_on_profile', 'userField.show_on_user_card'), + return ret.join(", "); + }.property( + "userField.editable", + "userField.required", + "userField.show_on_profile", + "userField.show_on_user_card" + ), actions: { save() { const self = this; - const buffered = this.get('buffered'); - const attrs = buffered.getProperties('name', - 'description', - 'field_type', - 'editable', - 'required', - 'show_on_profile', - 'show_on_user_card', - 'options'); + const buffered = this.get("buffered"); + const attrs = buffered.getProperties( + "name", + "description", + "field_type", + "editable", + "required", + "show_on_profile", + "show_on_user_card", + "options" + ); - this.get('userField').save(attrs).then(function() { - self.set('editing', false); - self.commitBuffer(); - }).catch(popupAjaxError); + this.get("userField") + .save(attrs) + .then(function() { + self.set("editing", false); + self.commitBuffer(); + }) + .catch(popupAjaxError); }, moveUp() { - this.sendAction('moveUpAction', this.get('userField')); + this.sendAction("moveUpAction", this.get("userField")); }, moveDown() { - this.sendAction('moveDownAction', this.get('userField')); + this.sendAction("moveDownAction", this.get("userField")); }, edit() { - this.set('editing', true); + this.set("editing", true); }, destroy() { - this.sendAction('destroyAction', this.get('userField')); + this.sendAction("destroyAction", this.get("userField")); }, cancel() { - const id = this.get('userField.id'); + const id = this.get("userField.id"); if (Ember.isEmpty(id)) { - this.sendAction('destroyAction', this.get('userField')); + this.sendAction("destroyAction", this.get("userField")); } else { this.rollbackBuffer(); - this.set('editing', false); + this.set("editing", false); } } } diff --git a/app/assets/javascripts/admin/components/admin-watched-word.js.es6 b/app/assets/javascripts/admin/components/admin-watched-word.js.es6 index 3c78becb493..d1283e8b3ab 100644 --- a/app/assets/javascripts/admin/components/admin-watched-word.js.es6 +++ b/app/assets/javascripts/admin/components/admin-watched-word.js.es6 @@ -1,19 +1,28 @@ -import { iconHTML } from 'discourse-common/lib/icon-library'; -import { bufferedRender } from 'discourse-common/lib/buffered-render'; +import { iconHTML } from "discourse-common/lib/icon-library"; +import { bufferedRender } from "discourse-common/lib/buffered-render"; -export default Ember.Component.extend(bufferedRender({ - classNames: ['watched-word'], +export default Ember.Component.extend( + bufferedRender({ + classNames: ["watched-word"], - buildBuffer(buffer) { - buffer.push(iconHTML('times')); - buffer.push(' ' + this.get('word.word')); - }, + buildBuffer(buffer) { + buffer.push(iconHTML("times")); + buffer.push(" " + this.get("word.word")); + }, - click() { - this.get('word').destroy().then(() => { - this.sendAction('action', this.get('word')); - }).catch(e => { - bootbox.alert(I18n.t("generic_error_with_reason", {error: `http: ${e.status} - ${e.body}`})); - });; - } -})); + click() { + this.get("word") + .destroy() + .then(() => { + this.sendAction("action", this.get("word")); + }) + .catch(e => { + bootbox.alert( + I18n.t("generic_error_with_reason", { + error: `http: ${e.status} - ${e.body}` + }) + ); + }); + } + }) +); diff --git a/app/assets/javascripts/admin/components/admin-web-hook-event-chooser.js.es6 b/app/assets/javascripts/admin/components/admin-web-hook-event-chooser.js.es6 index 976b483995e..6e91ce55c7b 100644 --- a/app/assets/javascripts/admin/components/admin-web-hook-event-chooser.js.es6 +++ b/app/assets/javascripts/admin/components/admin-web-hook-event-chooser.js.es6 @@ -1,38 +1,40 @@ -import computed from 'ember-addons/ember-computed-decorators'; +import computed from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ - classNames: ['hook-event'], - typeName: Ember.computed.alias('type.name'), + classNames: ["hook-event"], + typeName: Ember.computed.alias("type.name"), - @computed('typeName') + @computed("typeName") name(typeName) { return I18n.t(`admin.web_hooks.${typeName}_event.name`); }, - @computed('typeName') + @computed("typeName") details(typeName) { return I18n.t(`admin.web_hooks.${typeName}_event.details`); }, - @computed('model.[]', 'typeName') + @computed("model.[]", "typeName") eventTypeExists(eventTypes, typeName) { return eventTypes.any(event => event.name === typeName); }, - @computed('eventTypeExists') + @computed("eventTypeExists") enabled: { get(eventTypeExists) { return eventTypeExists; }, set(value, eventTypeExists) { - const type = this.get('type'); - const model = this.get('model'); + const type = this.get("type"); + const model = this.get("model"); // add an association when not exists if (value !== eventTypeExists) { if (value) { model.addObject(type); } else { - model.removeObjects(model.filter(eventType => eventType.name === type.name)); + model.removeObjects( + model.filter(eventType => eventType.name === type.name) + ); } } diff --git a/app/assets/javascripts/admin/components/admin-web-hook-event.js.es6 b/app/assets/javascripts/admin/components/admin-web-hook-event.js.es6 index 264e827da61..5191412c298 100644 --- a/app/assets/javascripts/admin/components/admin-web-hook-event.js.es6 +++ b/app/assets/javascripts/admin/components/admin-web-hook-event.js.es6 @@ -1,78 +1,92 @@ -import computed from 'ember-addons/ember-computed-decorators'; -import { ajax } from 'discourse/lib/ajax'; -import { popupAjaxError } from 'discourse/lib/ajax-error'; -import { ensureJSON, plainJSON, prettyJSON } from 'discourse/lib/formatter'; +import computed from "ember-addons/ember-computed-decorators"; +import { ajax } from "discourse/lib/ajax"; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import { ensureJSON, plainJSON, prettyJSON } from "discourse/lib/formatter"; export default Ember.Component.extend({ - tagName: 'li', + tagName: "li", expandDetails: null, - @computed('model.status') + @computed("model.status") statusColorClasses(status) { - if (!status) return ''; + if (!status) return ""; if (status >= 200 && status <= 299) { - return 'text-successful'; + return "text-successful"; } else { - return 'text-danger'; + return "text-danger"; } }, - @computed('model.created_at') + @computed("model.created_at") createdAt(createdAt) { - return moment(createdAt).format('YYYY-MM-DD HH:mm:ss'); + return moment(createdAt).format("YYYY-MM-DD HH:mm:ss"); }, - @computed('model.duration') + @computed("model.duration") completion(duration) { const seconds = Math.floor(duration / 10.0) / 100.0; - return I18n.t('admin.web_hooks.events.completed_in', { count: seconds }); + return I18n.t("admin.web_hooks.events.completed_in", { count: seconds }); }, actions: { redeliver() { - return bootbox.confirm(I18n.t('admin.web_hooks.events.redeliver_confirm'), I18n.t('no_value'), I18n.t('yes_value'), result => { - if (result) { - ajax(`/admin/api/web_hooks/${this.get('model.web_hook_id')}/events/${this.get('model.id')}/redeliver`, { type: 'POST' }).then(json => { - this.set('model', json.web_hook_event); - }).catch(popupAjaxError); + return bootbox.confirm( + I18n.t("admin.web_hooks.events.redeliver_confirm"), + I18n.t("no_value"), + I18n.t("yes_value"), + result => { + if (result) { + ajax( + `/admin/api/web_hooks/${this.get( + "model.web_hook_id" + )}/events/${this.get("model.id")}/redeliver`, + { type: "POST" } + ) + .then(json => { + this.set("model", json.web_hook_event); + }) + .catch(popupAjaxError); + } } - }); + ); }, toggleRequest() { - const expandDetailsKey = 'request'; + const expandDetailsKey = "request"; - if (this.get('expandDetails') !== expandDetailsKey) { - let headers = _.extend({ - 'Request URL': this.get('model.request_url'), - 'Request method': 'POST' - }, ensureJSON(this.get('model.headers'))); + if (this.get("expandDetails") !== expandDetailsKey) { + let headers = _.extend( + { + "Request URL": this.get("model.request_url"), + "Request method": "POST" + }, + ensureJSON(this.get("model.headers")) + ); this.setProperties({ headers: plainJSON(headers), - body: prettyJSON(this.get('model.payload')), + body: prettyJSON(this.get("model.payload")), expandDetails: expandDetailsKey, - bodyLabel: I18n.t('admin.web_hooks.events.payload') + bodyLabel: I18n.t("admin.web_hooks.events.payload") }); } else { - this.set('expandDetails', null); + this.set("expandDetails", null); } }, toggleResponse() { - const expandDetailsKey = 'response'; + const expandDetailsKey = "response"; - if (this.get('expandDetails') !== expandDetailsKey) { + if (this.get("expandDetails") !== expandDetailsKey) { this.setProperties({ - headers: plainJSON(this.get('model.response_headers')), - body: this.get('model.response_body'), + headers: plainJSON(this.get("model.response_headers")), + body: this.get("model.response_body"), expandDetails: expandDetailsKey, - bodyLabel: I18n.t('admin.web_hooks.events.body') + bodyLabel: I18n.t("admin.web_hooks.events.body") }); } else { - this.set('expandDetails', null); + this.set("expandDetails", null); } } } - }); diff --git a/app/assets/javascripts/admin/components/admin-web-hook-status.js.es6 b/app/assets/javascripts/admin/components/admin-web-hook-status.js.es6 index c5f1a4a2447..9e508e904c4 100644 --- a/app/assets/javascripts/admin/components/admin-web-hook-status.js.es6 +++ b/app/assets/javascripts/admin/components/admin-web-hook-status.js.es6 @@ -1,28 +1,32 @@ -import computed from 'ember-addons/ember-computed-decorators'; -import { iconHTML } from 'discourse-common/lib/icon-library'; -import { bufferedRender } from 'discourse-common/lib/buffered-render'; +import computed from "ember-addons/ember-computed-decorators"; +import { iconHTML } from "discourse-common/lib/icon-library"; +import { bufferedRender } from "discourse-common/lib/buffered-render"; -export default Ember.Component.extend(bufferedRender({ - classes: ["text-muted", "text-danger", "text-successful"], - icons: ["circle-o", "times-circle", "circle"], +export default Ember.Component.extend( + bufferedRender({ + classes: ["text-muted", "text-danger", "text-successful"], + icons: ["circle-o", "times-circle", "circle"], - @computed('deliveryStatuses', 'model.last_delivery_status') - status(deliveryStatuses, lastDeliveryStatus) { - return deliveryStatuses.find(s => s.id === lastDeliveryStatus); - }, + @computed("deliveryStatuses", "model.last_delivery_status") + status(deliveryStatuses, lastDeliveryStatus) { + return deliveryStatuses.find(s => s.id === lastDeliveryStatus); + }, - @computed('status.id', 'icons') - icon(statusId, icons) { - return icons[statusId - 1]; - }, + @computed("status.id", "icons") + icon(statusId, icons) { + return icons[statusId - 1]; + }, - @computed('status.id', 'classes') - class(statusId, classes) { - return classes[statusId - 1]; - }, + @computed("status.id", "classes") + class(statusId, classes) { + return classes[statusId - 1]; + }, - 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')}`)); - } -})); + 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")}`) + ); + } + }) +); diff --git a/app/assets/javascripts/admin/components/admin-wrapper.js.es6 b/app/assets/javascripts/admin/components/admin-wrapper.js.es6 index 118728f66ca..034e2a0af43 100644 --- a/app/assets/javascripts/admin/components/admin-wrapper.js.es6 +++ b/app/assets/javascripts/admin/components/admin-wrapper.js.es6 @@ -1,11 +1,11 @@ export default Ember.Component.extend({ didInsertElement() { this._super(); - $('body').addClass('admin-interface'); + $("body").addClass("admin-interface"); }, willDestroyElement() { this._super(); - $('body').removeClass('admin-interface'); + $("body").removeClass("admin-interface"); } }); diff --git a/app/assets/javascripts/admin/components/cancel-link.js.es6 b/app/assets/javascripts/admin/components/cancel-link.js.es6 index 9250c1ae73b..91ad923ffcc 100644 --- a/app/assets/javascripts/admin/components/cancel-link.js.es6 +++ b/app/assets/javascripts/admin/components/cancel-link.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - tagName: '' + tagName: "" }); diff --git a/app/assets/javascripts/admin/components/color-input.js.es6 b/app/assets/javascripts/admin/components/color-input.js.es6 index 005c4f5d4ba..17ae5a9296d 100644 --- a/app/assets/javascripts/admin/components/color-input.js.es6 +++ b/app/assets/javascripts/admin/components/color-input.js.es6 @@ -1,4 +1,4 @@ -import {default as loadScript, loadCSS } from 'discourse/lib/load-script'; +import { default as loadScript, loadCSS } from "discourse/lib/load-script"; /** An input field for a color. @@ -8,35 +8,43 @@ import {default as loadScript, loadCSS } from 'discourse/lib/load-script'; @params valid is a boolean indicating if the input field is a valid color. **/ export default Ember.Component.extend({ - classNames: ['color-picker'], + classNames: ["color-picker"], hexValueChanged: function() { - var hex = this.get('hexValue'); - let $text = this.$('input.hex-input'); + var hex = this.get("hexValue"); + let $text = this.$("input.hex-input"); - if (this.get('valid')) { - $text.attr('style', 'color: ' + (this.get('brightnessValue') > 125 ? 'black' : 'white') + '; background-color: #' + hex + ';'); + if (this.get("valid")) { + $text.attr( + "style", + "color: " + + (this.get("brightnessValue") > 125 ? "black" : "white") + + "; background-color: #" + + hex + + ";" + ); - if (this.get('pickerLoaded')) { - this.$('.picker').spectrum({color: "#" + this.get('hexValue')}); + if (this.get("pickerLoaded")) { + this.$(".picker").spectrum({ color: "#" + this.get("hexValue") }); } } else { - $text.attr('style', ''); + $text.attr("style", ""); } - }.observes('hexValue', 'brightnessValue', 'valid'), + }.observes("hexValue", "brightnessValue", "valid"), didInsertElement() { - loadScript('/javascripts/spectrum.js').then(()=>{ - loadCSS('/javascripts/spectrum.css').then(()=>{ - Em.run.schedule('afterRender', ()=>{ - this.$('.picker').spectrum({color: "#" + this.get('hexValue')}) - .on("change.spectrum", (me, color)=>{ - this.set('hexValue', color.toHexString().replace("#","")); - }); - this.set('pickerLoaded', true); + loadScript("/javascripts/spectrum.js").then(() => { + loadCSS("/javascripts/spectrum.css").then(() => { + Em.run.schedule("afterRender", () => { + this.$(".picker") + .spectrum({ color: "#" + this.get("hexValue") }) + .on("change.spectrum", (me, color) => { + this.set("hexValue", color.toHexString().replace("#", "")); + }); + this.set("pickerLoaded", true); }); }); }); - Em.run.schedule('afterRender', ()=>{ + Em.run.schedule("afterRender", () => { this.hexValueChanged(); }); } diff --git a/app/assets/javascripts/admin/components/dashboard-inline-table.js.es6 b/app/assets/javascripts/admin/components/dashboard-inline-table.js.es6 index 8fae065f7bc..0435f8237e9 100644 --- a/app/assets/javascripts/admin/components/dashboard-inline-table.js.es6 +++ b/app/assets/javascripts/admin/components/dashboard-inline-table.js.es6 @@ -9,11 +9,12 @@ export default Ember.Component.extend(AsyncReport, { let payload = this.buildPayload(["total", "prev30Days"]); - return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => { - return ajax(dataSource, payload) - .then(response => { + return Ember.RSVP.Promise.all( + this.get("dataSources").map(dataSource => { + return ajax(dataSource, payload).then(response => { this.get("reports").pushObject(this.loadReport(response.report)); }); - })); + }) + ); } }); diff --git a/app/assets/javascripts/admin/components/dashboard-mini-chart.js.es6 b/app/assets/javascripts/admin/components/dashboard-mini-chart.js.es6 index 36a95dc15cd..1f0d4b7cba1 100644 --- a/app/assets/javascripts/admin/components/dashboard-mini-chart.js.es6 +++ b/app/assets/javascripts/admin/components/dashboard-mini-chart.js.es6 @@ -1,7 +1,7 @@ import { ajax } from "discourse/lib/ajax"; import AsyncReport from "admin/mixins/async-report"; import Report from "admin/models/report"; -import { number } from 'discourse/lib/formatter'; +import { number } from "discourse/lib/formatter"; import loadScript from "discourse/lib/load-script"; import { registerTooltip, unregisterTooltip } from "discourse/lib/tooltip"; @@ -9,9 +9,8 @@ function collapseWeekly(data, average) { let aggregate = []; let bucket, i; let offset = data.length % 7; - for(i = offset; i < data.length; i++) { - - if (bucket && (i % 7 === offset)) { + for (i = offset; i < data.length; i++) { + if (bucket && i % 7 === offset) { if (average) { bucket.y = parseFloat((bucket.y / 7.0).toFixed(2)); } @@ -59,12 +58,13 @@ export default Ember.Component.extend(AsyncReport, { this._chart = null; } - return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => { - return ajax(dataSource, payload) - .then(response => { + return Ember.RSVP.Promise.all( + this.get("dataSources").map(dataSource => { + return ajax(dataSource, payload).then(response => { this.get("reports").pushObject(this.loadReport(response.report)); }); - })); + }) + ); }, loadReport(report, previousReport) { @@ -77,7 +77,9 @@ export default Ember.Component.extend(AsyncReport, { if (previousReport && previousReport.color.length) { report.color = previousReport.color; } else { - const dataSourceNameIndex = this.get("dataSourceNames").split(",").indexOf(report.type); + const dataSourceNameIndex = this.get("dataSourceNames") + .split(",") + .indexOf(report.type); report.color = this.pickColorAtIndex(dataSourceNameIndex); } @@ -94,13 +96,17 @@ export default Ember.Component.extend(AsyncReport, { const reportsForPeriod = this.get("reportsForPeriod"); - const labels = Ember.makeArray(reportsForPeriod.get("firstObject.data")).map(d => d.x); + const labels = Ember.makeArray( + reportsForPeriod.get("firstObject.data") + ).map(d => d.x); const data = { labels, datasets: reportsForPeriod.map(report => { return { - data: Ember.makeArray(report.data).map(d => Math.round(parseFloat(d.y))), + data: Ember.makeArray(report.data).map(d => + Math.round(parseFloat(d.y)) + ), backgroundColor: "rgba(200,220,240,0.3)", borderColor: report.color }; @@ -129,7 +135,8 @@ export default Ember.Component.extend(AsyncReport, { options: { tooltips: { callbacks: { - title: (context) => moment(context[0].xLabel, "YYYY-MM-DD").format("LL") + title: context => + moment(context[0].xLabel, "YYYY-MM-DD").format("LL") } }, legend: { @@ -146,20 +153,24 @@ export default Ember.Component.extend(AsyncReport, { } }, scales: { - yAxes: [{ - display: true, - ticks: { callback: (label) => number(label) } - }], - xAxes: [{ - display: true, - gridLines: { display: false }, - type: "time", - time: { - parser: "YYYY-MM-DD" + yAxes: [ + { + display: true, + ticks: { callback: label => number(label) } } - }], + ], + xAxes: [ + { + display: true, + gridLines: { display: false }, + type: "time", + time: { + parser: "YYYY-MM-DD" + } + } + ] } - }, + } }; } }); diff --git a/app/assets/javascripts/admin/components/dashboard-table.js.es6 b/app/assets/javascripts/admin/components/dashboard-table.js.es6 index 96b74e6e153..fad090efacb 100644 --- a/app/assets/javascripts/admin/components/dashboard-table.js.es6 +++ b/app/assets/javascripts/admin/components/dashboard-table.js.es6 @@ -9,11 +9,12 @@ export default Ember.Component.extend(AsyncReport, { let payload = this.buildPayload(["total", "prev30Days"]); - return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => { - return ajax(dataSource, payload) - .then(response => { + return Ember.RSVP.Promise.all( + this.get("dataSources").map(dataSource => { + return ajax(dataSource, payload).then(response => { this.get("reports").pushObject(this.loadReport(response.report)); }); - })); + }) + ); } }); diff --git a/app/assets/javascripts/admin/components/embeddable-host.js.es6 b/app/assets/javascripts/admin/components/embeddable-host.js.es6 index 70c1ed272cf..7264d42c1c3 100644 --- a/app/assets/javascripts/admin/components/embeddable-host.js.es6 +++ b/app/assets/javascripts/admin/components/embeddable-host.js.es6 @@ -1,63 +1,79 @@ -import { bufferedProperty } from 'discourse/mixins/buffered-content'; -import computed from 'ember-addons/ember-computed-decorators'; -import { on, observes } from 'ember-addons/ember-computed-decorators'; -import { popupAjaxError } from 'discourse/lib/ajax-error'; +import { bufferedProperty } from "discourse/mixins/buffered-content"; +import computed from "ember-addons/ember-computed-decorators"; +import { on, observes } from "ember-addons/ember-computed-decorators"; +import { popupAjaxError } from "discourse/lib/ajax-error"; -export default Ember.Component.extend(bufferedProperty('host'), { +export default Ember.Component.extend(bufferedProperty("host"), { editToggled: false, - tagName: 'tr', + tagName: "tr", categoryId: null, - editing: Ember.computed.or('host.isNew', 'editToggled'), + editing: Ember.computed.or("host.isNew", "editToggled"), - @on('didInsertElement') - @observes('editing') + @on("didInsertElement") + @observes("editing") _focusOnInput() { - Ember.run.schedule('afterRender', () => { this.$('.host-name').focus(); }); + Ember.run.schedule("afterRender", () => { + this.$(".host-name").focus(); + }); }, - @computed('buffered.host', 'host.isSaving') + @computed("buffered.host", "host.isSaving") cantSave(host, isSaving) { return isSaving || Ember.isEmpty(host); }, actions: { edit() { - this.set('categoryId', this.get('host.category.id')); - this.set('editToggled', true); + this.set("categoryId", this.get("host.category.id")); + this.set("editToggled", true); }, save() { - if (this.get('cantSave')) { return; } + if (this.get("cantSave")) { + return; + } - const props = this.get('buffered').getProperties('host', 'path_whitelist', 'class_name'); - props.category_id = this.get('categoryId'); + const props = this.get("buffered").getProperties( + "host", + "path_whitelist", + "class_name" + ); + props.category_id = this.get("categoryId"); - const host = this.get('host'); + const host = this.get("host"); - host.save(props).then(() => { - host.set('category', Discourse.Category.findById(this.get('categoryId'))); - this.set('editToggled', false); - }).catch(popupAjaxError); + host + .save(props) + .then(() => { + host.set( + "category", + Discourse.Category.findById(this.get("categoryId")) + ); + this.set("editToggled", false); + }) + .catch(popupAjaxError); }, delete() { - bootbox.confirm(I18n.t('admin.embedding.confirm_delete'), (result) => { + bootbox.confirm(I18n.t("admin.embedding.confirm_delete"), result => { if (result) { - this.get('host').destroyRecord().then(() => { - this.sendAction('deleteHost', this.get('host')); - }); + this.get("host") + .destroyRecord() + .then(() => { + this.sendAction("deleteHost", this.get("host")); + }); } }); }, cancel() { - const host = this.get('host'); - if (host.get('isNew')) { - this.sendAction('deleteHost', host); + const host = this.get("host"); + if (host.get("isNew")) { + this.sendAction("deleteHost", host); } else { this.rollbackBuffer(); - this.set('editToggled', false); + this.set("editToggled", false); } } } diff --git a/app/assets/javascripts/admin/components/embedding-setting.js.es6 b/app/assets/javascripts/admin/components/embedding-setting.js.es6 index f1d31fcf68b..4791e84e357 100644 --- a/app/assets/javascripts/admin/components/embedding-setting.js.es6 +++ b/app/assets/javascripts/admin/components/embedding-setting.js.es6 @@ -1,22 +1,30 @@ -import computed from 'ember-addons/ember-computed-decorators'; +import computed from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ - classNames: ['embed-setting'], + classNames: ["embed-setting"], - @computed('field') - inputId(field) { return field.dasherize(); }, + @computed("field") + inputId(field) { + return field.dasherize(); + }, - @computed('field') - translationKey(field) { return `admin.embedding.${field}`; }, + @computed("field") + translationKey(field) { + return `admin.embedding.${field}`; + }, - @computed('type') - isCheckbox(type) { return type === "checkbox"; }, + @computed("type") + isCheckbox(type) { + return type === "checkbox"; + }, - @computed('value') + @computed("value") checked: { - get(value) { return !!value; }, + get(value) { + return !!value; + }, set(value) { - this.set('value', value); + this.set("value", value); return value; } } diff --git a/app/assets/javascripts/admin/components/flag-user-lists.js.es6 b/app/assets/javascripts/admin/components/flag-user-lists.js.es6 index cd843ff7ab6..ae6094c6a75 100644 --- a/app/assets/javascripts/admin/components/flag-user-lists.js.es6 +++ b/app/assets/javascripts/admin/components/flag-user-lists.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - classNames: ['flag-user-lists'] + classNames: ["flag-user-lists"] }); diff --git a/app/assets/javascripts/admin/components/flagged-post-response.js.es6 b/app/assets/javascripts/admin/components/flagged-post-response.js.es6 index e031f33e031..e8dc2a230cd 100644 --- a/app/assets/javascripts/admin/components/flagged-post-response.js.es6 +++ b/app/assets/javascripts/admin/components/flagged-post-response.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - classNames: ['flagged-post-response'] + classNames: ["flagged-post-response"] }); diff --git a/app/assets/javascripts/admin/components/flagged-post-title.js.es6 b/app/assets/javascripts/admin/components/flagged-post-title.js.es6 index 7c1c013375b..4a6fa5d604f 100644 --- a/app/assets/javascripts/admin/components/flagged-post-title.js.es6 +++ b/app/assets/javascripts/admin/components/flagged-post-title.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - tagName: 'h3' + tagName: "h3" }); diff --git a/app/assets/javascripts/admin/components/flagged-post.js.es6 b/app/assets/javascripts/admin/components/flagged-post.js.es6 index 8aff3d2a2d1..03edebab6cb 100644 --- a/app/assets/javascripts/admin/components/flagged-post.js.es6 +++ b/app/assets/javascripts/admin/components/flagged-post.js.es6 @@ -1,21 +1,21 @@ -import showModal from 'discourse/lib/show-modal'; -import computed from 'ember-addons/ember-computed-decorators'; +import showModal from "discourse/lib/show-modal"; +import computed from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ adminTools: Ember.inject.service(), expanded: false, - tagName: 'div', + tagName: "div", classNameBindings: [ - ':flagged-post', - 'flaggedPost.hidden:hidden-post', - 'flaggedPost.deleted' + ":flagged-post", + "flaggedPost.hidden:hidden-post", + "flaggedPost.deleted" ], - canAct: Ember.computed.alias('actableFilter'), + canAct: Ember.computed.alias("actableFilter"), - @computed('filter') + @computed("filter") actableFilter(filter) { - return filter === 'active'; + return filter === "active"; }, removeAfter(promise) { @@ -24,7 +24,7 @@ export default Ember.Component.extend({ _spawnModal(name, model, modalClass) { let controller = showModal(name, { model, admin: true, modalClass }); - controller.removeAfter = (p) => this.removeAfter(p); + controller.removeAfter = p => this.removeAfter(p); }, actions: { @@ -33,23 +33,25 @@ export default Ember.Component.extend({ }, disagree() { - this.removeAfter(this.get('flaggedPost').disagreeFlags()); + this.removeAfter(this.get("flaggedPost").disagreeFlags()); }, defer() { - this.removeAfter(this.get('flaggedPost').deferFlags()); + this.removeAfter(this.get("flaggedPost").deferFlags()); }, expand() { - this.get('flaggedPost').expandHidden().then(() => { - this.set('expanded', true); - }); + this.get("flaggedPost") + .expandHidden() + .then(() => { + this.set("expanded", true); + }); }, showModerationHistory() { - this.get('adminTools').showModerationHistory({ - filter: 'post', - post_id: this.get('flaggedPost.id') + this.get("adminTools").showModerationHistory({ + filter: "post", + post_id: this.get("flaggedPost.id") }); } } diff --git a/app/assets/javascripts/admin/components/highlighted-code.js.es6 b/app/assets/javascripts/admin/components/highlighted-code.js.es6 index 4fc413fd898..62cf58f21b2 100644 --- a/app/assets/javascripts/admin/components/highlighted-code.js.es6 +++ b/app/assets/javascripts/admin/components/highlighted-code.js.es6 @@ -1,12 +1,10 @@ -import { on, observes } from 'ember-addons/ember-computed-decorators'; -import highlightSyntax from 'discourse/lib/highlight-syntax'; +import { on, observes } from "ember-addons/ember-computed-decorators"; +import highlightSyntax from "discourse/lib/highlight-syntax"; export default Ember.Component.extend({ - - @on('didInsertElement') - @observes('code') + @on("didInsertElement") + @observes("code") _refresh: function() { highlightSyntax(this.$()); } - }); diff --git a/app/assets/javascripts/admin/components/inline-edit-checkbox.js.es6 b/app/assets/javascripts/admin/components/inline-edit-checkbox.js.es6 index 5c168760c7f..f14ba622018 100644 --- a/app/assets/javascripts/admin/components/inline-edit-checkbox.js.es6 +++ b/app/assets/javascripts/admin/components/inline-edit-checkbox.js.es6 @@ -1,12 +1,15 @@ -import {default as computed, observes} from "ember-addons/ember-computed-decorators"; +import { + default as computed, + observes +} from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ - init(){ + init() { this._super(); this.set("checkedInternal", this.get("checked")); }, - classNames: ['inline-edit'], + classNames: ["inline-edit"], @observes("checked") checkedChanged() { @@ -20,15 +23,15 @@ export default Ember.Component.extend({ @computed("checked", "checkedInternal") changed(checked, checkedInternal) { - return (!!checked) !== (!!checkedInternal); + return !!checked !== !!checkedInternal; }, actions: { - cancelled(){ + cancelled() { this.set("checkedInternal", this.get("checked")); }, - finished(){ + finished() { this.set("checked", this.get("checkedInternal")); this.sendAction(); } diff --git a/app/assets/javascripts/admin/components/ip-lookup.js.es6 b/app/assets/javascripts/admin/components/ip-lookup.js.es6 index a185e4c0b6e..3f06ba38d67 100644 --- a/app/assets/javascripts/admin/components/ip-lookup.js.es6 +++ b/app/assets/javascripts/admin/components/ip-lookup.js.es6 @@ -1,15 +1,17 @@ -import { ajax } from 'discourse/lib/ajax'; -import AdminUser from 'admin/models/admin-user'; +import { ajax } from "discourse/lib/ajax"; +import AdminUser from "admin/models/admin-user"; export default Ember.Component.extend({ classNames: ["ip-lookup"], - city: function () { + city: function() { return [ this.get("location.city"), this.get("location.region"), this.get("location.country") - ].filter(Boolean).join(", "); + ] + .filter(Boolean) + .join(", "); }.property("location.{city,region,country}"), otherAccountsToDelete: function() { @@ -20,14 +22,14 @@ export default Ember.Component.extend({ }.property("other_accounts", "totalOthersWithSameIP"), actions: { - lookup: function () { + lookup: function() { var self = this; this.set("show", true); if (!this.get("location")) { ajax("/admin/users/ip-info", { data: { ip: this.get("ip") } - }).then(function (location) { + }).then(function(location) { self.set("location", Em.Object.create(location)); }); } @@ -36,50 +38,57 @@ export default Ember.Component.extend({ this.set("otherAccountsLoading", true); var data = { - "ip": this.get("ip"), - "exclude": this.get("userId"), - "order": "trust_level DESC" + ip: this.get("ip"), + exclude: this.get("userId"), + order: "trust_level DESC" }; - ajax("/admin/users/total-others-with-same-ip", { data }).then(function (result) { + ajax("/admin/users/total-others-with-same-ip", { data }).then(function( + result + ) { self.set("totalOthersWithSameIP", result.total); }); - AdminUser.findAll("active", data).then(function (users) { + AdminUser.findAll("active", data).then(function(users) { self.setProperties({ other_accounts: users, - otherAccountsLoading: false, + otherAccountsLoading: false }); }); } }, - hide: function () { + hide: function() { this.set("show", false); }, deleteOtherAccounts: function() { var self = this; - bootbox.confirm(I18n.t("ip_lookup.confirm_delete_other_accounts"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) { - if (confirmed) { - self.setProperties({ - other_accounts: null, - otherAccountsLoading: true, - totalOthersWithSameIP: null - }); + bootbox.confirm( + I18n.t("ip_lookup.confirm_delete_other_accounts"), + I18n.t("no_value"), + I18n.t("yes_value"), + function(confirmed) { + if (confirmed) { + self.setProperties({ + other_accounts: null, + otherAccountsLoading: true, + totalOthersWithSameIP: null + }); - ajax("/admin/users/delete-others-with-same-ip.json", { - type: "DELETE", - data: { - "ip": self.get("ip"), - "exclude": self.get("userId"), - "order": "trust_level DESC" - } - }).then(function() { - self.send("lookup"); - }); + ajax("/admin/users/delete-others-with-same-ip.json", { + type: "DELETE", + data: { + ip: self.get("ip"), + exclude: self.get("userId"), + order: "trust_level DESC" + } + }).then(function() { + self.send("lookup"); + }); + } } - }); + ); } } }); diff --git a/app/assets/javascripts/admin/components/moderation-history-item.js.es6 b/app/assets/javascripts/admin/components/moderation-history-item.js.es6 index b8674a8aafb..b7620b66cd3 100644 --- a/app/assets/javascripts/admin/components/moderation-history-item.js.es6 +++ b/app/assets/javascripts/admin/components/moderation-history-item.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - tagName: 'tr', + tagName: "tr" }); diff --git a/app/assets/javascripts/admin/components/penalty-post-action.js.es6 b/app/assets/javascripts/admin/components/penalty-post-action.js.es6 index d89c69a32d0..b4d82aba8b3 100644 --- a/app/assets/javascripts/admin/components/penalty-post-action.js.es6 +++ b/app/assets/javascripts/admin/components/penalty-post-action.js.es6 @@ -1,6 +1,6 @@ -import computed from 'ember-addons/ember-computed-decorators'; +import computed from "ember-addons/ember-computed-decorators"; -const ACTIONS = ['delete', 'edit', 'none']; +const ACTIONS = ["delete", "edit", "none"]; export default Ember.Component.extend({ postAction: null, postEdit: null, @@ -12,19 +12,19 @@ export default Ember.Component.extend({ }); }, - editing: Ember.computed.equal('postAction', 'edit'), + editing: Ember.computed.equal("postAction", "edit"), actions: { penaltyChanged() { - let postAction = this.get('postAction'); + let postAction = this.get("postAction"); // If we switch to edit mode, jump to the edit textarea - if (postAction === 'edit') { - Ember.run.scheduleOnce('afterRender', () => { + if (postAction === "edit") { + Ember.run.scheduleOnce("afterRender", () => { let $elem = this.$(); - let body = $elem.closest('.modal-body'); + let body = $elem.closest(".modal-body"); body.scrollTop(body.height()); - $elem.find('.post-editor').focus(); + $elem.find(".post-editor").focus(); }); } } diff --git a/app/assets/javascripts/admin/components/permalink-form.js.es6 b/app/assets/javascripts/admin/components/permalink-form.js.es6 index 90dcf2a7b20..969840a308f 100644 --- a/app/assets/javascripts/admin/components/permalink-form.js.es6 +++ b/app/assets/javascripts/admin/components/permalink-form.js.es6 @@ -1,45 +1,58 @@ -import Permalink from 'admin/models/permalink'; +import Permalink from "admin/models/permalink"; export default Ember.Component.extend({ - classNames: ['permalink-form'], + classNames: ["permalink-form"], formSubmitted: false, - permalinkType: 'topic_id', + permalinkType: "topic_id", permalinkTypes: function() { return [ - {id: 'topic_id', name: I18n.t('admin.permalink.topic_id')}, - {id: 'post_id', name: I18n.t('admin.permalink.post_id')}, - {id: 'category_id', name: I18n.t('admin.permalink.category_id')}, - {id: 'external_url', name: I18n.t('admin.permalink.external_url')} + { id: "topic_id", name: I18n.t("admin.permalink.topic_id") }, + { id: "post_id", name: I18n.t("admin.permalink.post_id") }, + { id: "category_id", name: I18n.t("admin.permalink.category_id") }, + { id: "external_url", name: I18n.t("admin.permalink.external_url") } ]; }.property(), permalinkTypePlaceholder: function() { - return 'admin.permalink.' + this.get('permalinkType'); - }.property('permalinkType'), + return "admin.permalink." + this.get("permalinkType"); + }.property("permalinkType"), actions: { submit: function() { - if (!this.get('formSubmitted')) { + if (!this.get("formSubmitted")) { const self = this; - self.set('formSubmitted', true); - const permalink = Permalink.create({url: self.get('url'), permalink_type: self.get('permalinkType'), permalink_type_value: self.get('permalink_type_value')}); - permalink.save().then(function(result) { - self.set('url', ''); - self.set('permalink_type_value', ''); - self.set('formSubmitted', false); - self.sendAction('action', Permalink.create(result.permalink)); - Em.run.schedule('afterRender', function() { self.$('.permalink-url').focus(); }); - }, function(e) { - self.set('formSubmitted', false); - let error; - if (e.responseJSON && e.responseJSON.errors) { - error = I18n.t("generic_error_with_reason", {error: e.responseJSON.errors.join('. ')}); - } else { - error = I18n.t("generic_error"); - } - bootbox.alert(error, function() { self.$('.permalink-url').focus(); }); + self.set("formSubmitted", true); + const permalink = Permalink.create({ + url: self.get("url"), + permalink_type: self.get("permalinkType"), + permalink_type_value: self.get("permalink_type_value") }); + permalink.save().then( + function(result) { + self.set("url", ""); + self.set("permalink_type_value", ""); + self.set("formSubmitted", false); + self.sendAction("action", Permalink.create(result.permalink)); + Em.run.schedule("afterRender", function() { + self.$(".permalink-url").focus(); + }); + }, + function(e) { + self.set("formSubmitted", false); + let error; + if (e.responseJSON && e.responseJSON.errors) { + error = I18n.t("generic_error_with_reason", { + error: e.responseJSON.errors.join(". ") + }); + } else { + error = I18n.t("generic_error"); + } + bootbox.alert(error, function() { + self.$(".permalink-url").focus(); + }); + } + ); } } }, @@ -47,10 +60,11 @@ export default Ember.Component.extend({ didInsertElement: function() { var self = this; self._super(); - Em.run.schedule('afterRender', function() { - self.$('.external-url').keydown(function(e) { - if (e.keyCode === 13) { // enter key - self.send('submit'); + Em.run.schedule("afterRender", function() { + self.$(".external-url").keydown(function(e) { + if (e.keyCode === 13) { + // enter key + self.send("submit"); } }); }); diff --git a/app/assets/javascripts/admin/components/resumable-upload.js.es6 b/app/assets/javascripts/admin/components/resumable-upload.js.es6 index 36f420d1009..4a8545b3ab6 100644 --- a/app/assets/javascripts/admin/components/resumable-upload.js.es6 +++ b/app/assets/javascripts/admin/components/resumable-upload.js.es6 @@ -1,5 +1,5 @@ -import { iconHTML } from 'discourse-common/lib/icon-library'; -import { bufferedRender } from 'discourse-common/lib/buffered-render'; +import { iconHTML } from "discourse-common/lib/icon-library"; +import { bufferedRender } from "discourse-common/lib/buffered-render"; /*global Resumable:true */ @@ -13,111 +13,119 @@ import { bufferedRender } from 'discourse-common/lib/buffered-render'; uploadText="UPLOAD" }} **/ -export default Ember.Component.extend(bufferedRender({ - tagName: "button", - classNames: ["btn", "ru"], - classNameBindings: ["isUploading"], - attributeBindings: ["translatedTitle:title"], +export default Ember.Component.extend( + bufferedRender({ + tagName: "button", + classNames: ["btn", "ru"], + classNameBindings: ["isUploading"], + attributeBindings: ["translatedTitle:title"], - resumable: null, + resumable: null, - isUploading: false, - progress: 0, + isUploading: false, + progress: 0, - rerenderTriggers: ['isUploading', 'progress'], + rerenderTriggers: ["isUploading", "progress"], - translatedTitle: function() { - const title = this.get('title'); - return title ? I18n.t(title) : this.get('text'); - }.property('title', 'text'), + translatedTitle: function() { + const title = this.get("title"); + return title ? I18n.t(title) : this.get("text"); + }.property("title", "text"), - text: function() { - if (this.get("isUploading")) { - return this.get("progress") + " %"; - } else { - return this.get("uploadText"); - } - }.property("isUploading", "progress"), + text: function() { + if (this.get("isUploading")) { + return this.get("progress") + " %"; + } else { + return this.get("uploadText"); + } + }.property("isUploading", "progress"), - buildBuffer(buffer) { - const icon = this.get("isUploading") ? "times" : "upload"; - buffer.push(iconHTML(icon)); - buffer.push("" + this.get("text") + ""); - buffer.push(""); - }, + buildBuffer(buffer) { + const icon = this.get("isUploading") ? "times" : "upload"; + buffer.push(iconHTML(icon)); + buffer.push("" + this.get("text") + ""); + buffer.push( + "" + ); + }, + + click: function() { + if (this.get("isUploading")) { + this.resumable.cancel(); + var self = this; + Em.run.later(function() { + self._reset(); + }); + return false; + } else { + return true; + } + }, + + _reset: function() { + this.setProperties({ isUploading: false, progress: 0 }); + }, + + _initialize: function() { + this.resumable = new Resumable({ + target: Discourse.getURL(this.get("target")), + maxFiles: 1, // only 1 file at a time + headers: { + "X-CSRF-Token": $("meta[name='csrf-token']").attr("content") + } + }); - click: function() { - if (this.get("isUploading")) { - this.resumable.cancel(); var self = this; - Em.run.later(function() { self._reset(); }); - return false; - } else { - return true; - } - }, - _reset: function() { - this.setProperties({ isUploading: false, progress: 0 }); - }, - - _initialize: function() { - this.resumable = new Resumable({ - target: Discourse.getURL(this.get("target")), - maxFiles: 1, // only 1 file at a time - headers: { "X-CSRF-Token": $("meta[name='csrf-token']").attr("content") } - }); - - var self = this; - - this.resumable.on("fileAdded", function() { - // automatically upload the selected file - self.resumable.upload(); - // mark as uploading - Em.run.later(function() { - self.set("isUploading", true); + this.resumable.on("fileAdded", function() { + // automatically upload the selected file + self.resumable.upload(); + // mark as uploading + Em.run.later(function() { + self.set("isUploading", true); + }); }); - }); - this.resumable.on("fileProgress", function(file) { - // update progress - Em.run.later(function() { - self.set("progress", parseInt(file.progress() * 100, 10)); + this.resumable.on("fileProgress", function(file) { + // update progress + Em.run.later(function() { + self.set("progress", parseInt(file.progress() * 100, 10)); + }); }); - }); - this.resumable.on("fileSuccess", function(file) { - Em.run.later(function() { - // mark as not uploading anymore - self._reset(); - // fire an event to allow the parent route to reload its model - self.sendAction("success", file.fileName); + this.resumable.on("fileSuccess", function(file) { + Em.run.later(function() { + // mark as not uploading anymore + self._reset(); + // fire an event to allow the parent route to reload its model + self.sendAction("success", file.fileName); + }); }); - }); - this.resumable.on("fileError", function(file, message) { - Em.run.later(function() { - // mark as not uploading anymore - self._reset(); - // fire an event to allow the parent route to display the error message - self.sendAction("error", file.fileName, message); + this.resumable.on("fileError", function(file, message) { + Em.run.later(function() { + // mark as not uploading anymore + self._reset(); + // fire an event to allow the parent route to display the error message + self.sendAction("error", file.fileName, message); + }); }); - }); + }.on("init"), - }.on("init"), + _assignBrowse: function() { + var self = this; + Em.run.schedule("afterRender", function() { + self.resumable.assignBrowse(self.$()); + }); + }.on("didInsertElement"), - _assignBrowse: function() { - var self = this; - Em.run.schedule("afterRender", function() { - self.resumable.assignBrowse(self.$()); - }); - }.on("didInsertElement"), - - _teardown: function() { - if (this.resumable) { - this.resumable.cancel(); - this.resumable = null; - } - }.on("willDestroyElement") - -})); + _teardown: function() { + if (this.resumable) { + this.resumable.cancel(); + this.resumable = null; + } + }.on("willDestroyElement") + }) +); diff --git a/app/assets/javascripts/admin/components/save-controls.js.es6 b/app/assets/javascripts/admin/components/save-controls.js.es6 index 414bbf3661f..51adbaf3c5f 100644 --- a/app/assets/javascripts/admin/components/save-controls.js.es6 +++ b/app/assets/javascripts/admin/components/save-controls.js.es6 @@ -1,13 +1,13 @@ -import computed from 'ember-addons/ember-computed-decorators'; +import computed from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ - classNames: ['controls'], + classNames: ["controls"], - buttonDisabled: Ember.computed.or('model.isSaving', 'saveDisabled'), + buttonDisabled: Ember.computed.or("model.isSaving", "saveDisabled"), - @computed('model.isSaving') + @computed("model.isSaving") savingText(saving) { - return saving ? 'saving' : 'save'; + return saving ? "saving" : "save"; }, actions: { diff --git a/app/assets/javascripts/admin/components/screened-ip-address-form.js.es6 b/app/assets/javascripts/admin/components/screened-ip-address-form.js.es6 index ef6e7596d11..1a1dd977094 100644 --- a/app/assets/javascripts/admin/components/screened-ip-address-form.js.es6 +++ b/app/assets/javascripts/admin/components/screened-ip-address-form.js.es6 @@ -9,14 +9,14 @@ as an argument. **/ -import ScreenedIpAddress from 'admin/models/screened-ip-address'; -import computed from 'ember-addons/ember-computed-decorators'; -import { on } from 'ember-addons/ember-computed-decorators'; +import ScreenedIpAddress from "admin/models/screened-ip-address"; +import computed from "ember-addons/ember-computed-decorators"; +import { on } from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ - classNames: ['screened-ip-address-form'], + classNames: ["screened-ip-address-form"], formSubmitted: false, - actionName: 'block', + actionName: "block", @computed adminWhitelistEnabled() { @@ -27,51 +27,71 @@ export default Ember.Component.extend({ actionNames(adminWhitelistEnabled) { if (adminWhitelistEnabled) { return [ - {id: 'block', name: I18n.t('admin.logs.screened_ips.actions.block')}, - {id: 'do_nothing', name: I18n.t('admin.logs.screened_ips.actions.do_nothing')}, - {id: 'allow_admin', name: I18n.t('admin.logs.screened_ips.actions.allow_admin')} + { id: "block", name: I18n.t("admin.logs.screened_ips.actions.block") }, + { + id: "do_nothing", + name: I18n.t("admin.logs.screened_ips.actions.do_nothing") + }, + { + id: "allow_admin", + name: I18n.t("admin.logs.screened_ips.actions.allow_admin") + } ]; } else { return [ - {id: 'block', name: I18n.t('admin.logs.screened_ips.actions.block')}, - {id: 'do_nothing', name: I18n.t('admin.logs.screened_ips.actions.do_nothing')} + { id: "block", name: I18n.t("admin.logs.screened_ips.actions.block") }, + { + id: "do_nothing", + name: I18n.t("admin.logs.screened_ips.actions.do_nothing") + } ]; } }, actions: { submit() { - if (!this.get('formSubmitted')) { - this.set('formSubmitted', true); + if (!this.get("formSubmitted")) { + this.set("formSubmitted", true); const screenedIpAddress = ScreenedIpAddress.create({ - ip_address: this.get('ip_address'), - action_name: this.get('actionName') - }); - screenedIpAddress.save().then(result => { - if (result.success) { - this.setProperties({ ip_address: '', formSubmitted: false }); - this.sendAction('action', ScreenedIpAddress.create(result.screened_ip_address)); - Ember.run.schedule('afterRender', () => this.$('.ip-address-input').focus()); - } else { - bootbox.alert(result.errors); - } - }).catch(e => { - this.set('formSubmitted', false); - const msg = (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) ? - I18n.t("generic_error_with_reason", {error: e.jqXHR.responseJSON.errors.join('. ')}) : - I18n.t("generic_error"); - bootbox.alert(msg, () => this.$('.ip-address-input').focus()); + ip_address: this.get("ip_address"), + action_name: this.get("actionName") }); + screenedIpAddress + .save() + .then(result => { + if (result.success) { + this.setProperties({ ip_address: "", formSubmitted: false }); + this.sendAction( + "action", + ScreenedIpAddress.create(result.screened_ip_address) + ); + Ember.run.schedule("afterRender", () => + this.$(".ip-address-input").focus() + ); + } else { + bootbox.alert(result.errors); + } + }) + .catch(e => { + this.set("formSubmitted", false); + const msg = + e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors + ? I18n.t("generic_error_with_reason", { + error: e.jqXHR.responseJSON.errors.join(". ") + }) + : I18n.t("generic_error"); + bootbox.alert(msg, () => this.$(".ip-address-input").focus()); + }); } } }, @on("didInsertElement") _init() { - Ember.run.schedule('afterRender', () => { - this.$('.ip-address-input').keydown(e => { + Ember.run.schedule("afterRender", () => { + this.$(".ip-address-input").keydown(e => { if (e.keyCode === 13) { - this.send('submit'); + this.send("submit"); } }); }); diff --git a/app/assets/javascripts/admin/components/silence-details.js.es6 b/app/assets/javascripts/admin/components/silence-details.js.es6 index 9250c1ae73b..91ad923ffcc 100644 --- a/app/assets/javascripts/admin/components/silence-details.js.es6 +++ b/app/assets/javascripts/admin/components/silence-details.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - tagName: '' + tagName: "" }); diff --git a/app/assets/javascripts/admin/components/site-setting.js.es6 b/app/assets/javascripts/admin/components/site-setting.js.es6 index 98bdf950f65..bd68c7d11ed 100644 --- a/app/assets/javascripts/admin/components/site-setting.js.es6 +++ b/app/assets/javascripts/admin/components/site-setting.js.es6 @@ -1,10 +1,10 @@ -import BufferedContent from 'discourse/mixins/buffered-content'; -import SiteSetting from 'admin/models/site-setting'; -import SettingComponent from 'admin/mixins/setting-component'; +import BufferedContent from "discourse/mixins/buffered-content"; +import SiteSetting from "admin/models/site-setting"; +import SettingComponent from "admin/mixins/setting-component"; export default Ember.Component.extend(BufferedContent, SettingComponent, { _save() { - const setting = this.get('buffered'); - return SiteSetting.update(setting.get('setting'), setting.get('value')); + const setting = this.get("buffered"); + return SiteSetting.update(setting.get("setting"), setting.get("value")); } }); diff --git a/app/assets/javascripts/admin/components/site-settings/bool.js.es6 b/app/assets/javascripts/admin/components/site-settings/bool.js.es6 index 6be1a14e27a..f46e9658327 100644 --- a/app/assets/javascripts/admin/components/site-settings/bool.js.es6 +++ b/app/assets/javascripts/admin/components/site-settings/bool.js.es6 @@ -1,17 +1,17 @@ import computed from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ - @computed("value") enabled: { get(value) { - if (Ember.isEmpty(value)) { return false; } + if (Ember.isEmpty(value)) { + return false; + } return value.toString() === "true"; }, set(value) { this.set("value", value ? "true" : "false"); return value; } - }, - + } }); diff --git a/app/assets/javascripts/admin/components/site-settings/category-list.js.es6 b/app/assets/javascripts/admin/components/site-settings/category-list.js.es6 index 487239b78fa..36c712fa8df 100644 --- a/app/assets/javascripts/admin/components/site-settings/category-list.js.es6 +++ b/app/assets/javascripts/admin/components/site-settings/category-list.js.es6 @@ -1,7 +1,6 @@ import computed from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ - @computed("value") selectedCategories: { get(value) { @@ -12,5 +11,4 @@ export default Ember.Component.extend({ return value; } } - }); diff --git a/app/assets/javascripts/admin/components/site-text-summary.js.es6 b/app/assets/javascripts/admin/components/site-text-summary.js.es6 index 642164f8710..0ebd139614e 100644 --- a/app/assets/javascripts/admin/components/site-text-summary.js.es6 +++ b/app/assets/javascripts/admin/components/site-text-summary.js.es6 @@ -1,25 +1,27 @@ -import { on } from 'ember-addons/ember-computed-decorators'; +import { on } from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ - classNames: ['site-text'], - classNameBindings: ['siteText.overridden'], + classNames: ["site-text"], + classNameBindings: ["siteText.overridden"], - @on('didInsertElement') + @on("didInsertElement") highlightTerm() { - const term = this.get('term'); + const term = this.get("term"); if (term) { - this.$('.site-text-id, .site-text-value').highlight(term, {className: 'text-highlight'}); + this.$(".site-text-id, .site-text-value").highlight(term, { + className: "text-highlight" + }); } - this.$('.site-text-value').ellipsis(); + this.$(".site-text-value").ellipsis(); }, click() { - this.send('edit'); + this.send("edit"); }, actions: { edit() { - this.sendAction('editAction', this.get('siteText')); + this.sendAction("editAction", this.get("siteText")); } } }); diff --git a/app/assets/javascripts/admin/components/staff-actions.js.es6 b/app/assets/javascripts/admin/components/staff-actions.js.es6 index 9e742526afa..07764a80184 100644 --- a/app/assets/javascripts/admin/components/staff-actions.js.es6 +++ b/app/assets/javascripts/admin/components/staff-actions.js.es6 @@ -1,20 +1,20 @@ -import DiscourseURL from 'discourse/lib/url'; +import DiscourseURL from "discourse/lib/url"; export default Ember.Component.extend({ - classNames: ['table', 'staff-actions'], + classNames: ["table", "staff-actions"], willDestroyElement() { - this.$().off('click.discourse-staff-logs'); + this.$().off("click.discourse-staff-logs"); }, didInsertElement() { this._super(); - this.$().on('click.discourse-staff-logs', '[data-link-post-id]', e => { - let postId = $(e.target).attr('data-link-post-id'); + this.$().on("click.discourse-staff-logs", "[data-link-post-id]", e => { + let postId = $(e.target).attr("data-link-post-id"); - this.store.find('post', postId).then(p => { - DiscourseURL.routeTo(p.get('url')); + this.store.find("post", postId).then(p => { + DiscourseURL.routeTo(p.get("url")); }); return false; }); diff --git a/app/assets/javascripts/admin/components/suspension-details.js.es6 b/app/assets/javascripts/admin/components/suspension-details.js.es6 index 9250c1ae73b..91ad923ffcc 100644 --- a/app/assets/javascripts/admin/components/suspension-details.js.es6 +++ b/app/assets/javascripts/admin/components/suspension-details.js.es6 @@ -1,3 +1,3 @@ export default Ember.Component.extend({ - tagName: '' + tagName: "" }); diff --git a/app/assets/javascripts/admin/components/theme-setting.js.es6 b/app/assets/javascripts/admin/components/theme-setting.js.es6 index eb576e9b644..c95dd220db4 100644 --- a/app/assets/javascripts/admin/components/theme-setting.js.es6 +++ b/app/assets/javascripts/admin/components/theme-setting.js.es6 @@ -1,9 +1,12 @@ -import BufferedContent from 'discourse/mixins/buffered-content'; -import SettingComponent from 'admin/mixins/setting-component'; +import BufferedContent from "discourse/mixins/buffered-content"; +import SettingComponent from "admin/mixins/setting-component"; export default Ember.Component.extend(BufferedContent, SettingComponent, { - layoutName: 'admin/templates/components/site-setting', + layoutName: "admin/templates/components/site-setting", _save() { - return this.get('model').saveSettings(this.get('setting.setting'), this.get('buffered.value')); + return this.get("model").saveSettings( + this.get("setting.setting"), + this.get("buffered.value") + ); } }); diff --git a/app/assets/javascripts/admin/components/value-list.js.es6 b/app/assets/javascripts/admin/components/value-list.js.es6 index 3aa66ff1875..60e4a3cda73 100644 --- a/app/assets/javascripts/admin/components/value-list.js.es6 +++ b/app/assets/javascripts/admin/components/value-list.js.es6 @@ -1,5 +1,5 @@ export default Ember.Component.extend({ - classNameBindings: [':value-list'], + classNameBindings: [":value-list"], _enableSorting: function() { const self = this; @@ -10,16 +10,16 @@ export default Ember.Component.extend({ let over = null; let nodePlacement; - this.$().on('dragstart.discourse', '.values .value', function(e) { + this.$().on("dragstart.discourse", ".values .value", function(e) { dragging = e.currentTarget; - e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setData("text/html", e.currentTarget); }); - this.$().on('dragend.discourse', '.values .value', function() { + this.$().on("dragend.discourse", ".values .value", function() { Ember.run(function() { dragging.parentNode.removeChild(placeholder); - dragging.style.display = 'block'; + dragging.style.display = "block"; // Update data const from = Number(dragging.dataset.index); @@ -27,7 +27,7 @@ export default Ember.Component.extend({ if (from < to) to--; if (nodePlacement === "after") to++; - const collection = self.get('collection'); + const collection = self.get("collection"); const fromObj = collection.objectAt(from); collection.replace(from, 1); collection.replace(to, 0, [fromObj]); @@ -36,10 +36,12 @@ export default Ember.Component.extend({ return false; }); - this.$().on('dragover.discourse', '.values', function(e) { + this.$().on("dragover.discourse", ".values", function(e) { e.preventDefault(); - dragging.style.display = 'none'; - if (e.target.className === "placeholder") { return; } + dragging.style.display = "none"; + if (e.target.className === "placeholder") { + return; + } over = e.target; const relY = e.originalEvent.clientY - over.offsetTop; @@ -49,54 +51,61 @@ export default Ember.Component.extend({ if (relY > height) { nodePlacement = "after"; parent.insertBefore(placeholder, e.target.nextElementSibling); - } else if(relY < height) { + } else if (relY < height) { nodePlacement = "before"; parent.insertBefore(placeholder, e.target); } }); - }.on('didInsertElement'), + }.on("didInsertElement"), _removeSorting: function() { - this.$().off('dragover.discourse').off('dragend.discourse').off('dragstart.discourse'); - }.on('willDestroyElement'), + this.$() + .off("dragover.discourse") + .off("dragend.discourse") + .off("dragstart.discourse"); + }.on("willDestroyElement"), _setupCollection: function() { - const values = this.get('values'); - if (this.get('inputType') === "array") { - this.set('collection', values || []); + const values = this.get("values"); + if (this.get("inputType") === "array") { + this.set("collection", values || []); } else { - this.set('collection', (values && values.length) ? values.split("\n") : []); + this.set("collection", values && values.length ? values.split("\n") : []); } - }.on('init').observes('values'), + } + .on("init") + .observes("values"), _saveValues: function() { - if (this.get('inputType') === "array") { - this.set('values', this.get('collection')); + if (this.get("inputType") === "array") { + this.set("values", this.get("collection")); } else { - this.set('values', this.get('collection').join("\n")); + this.set("values", this.get("collection").join("\n")); } }, - inputInvalid: Ember.computed.empty('newValue'), + inputInvalid: Ember.computed.empty("newValue"), keyDown(e) { if (e.keyCode === 13) { - this.send('addValue'); + this.send("addValue"); } }, actions: { addValue() { - if (this.get('inputInvalid')) { return; } + if (this.get("inputInvalid")) { + return; + } - this.get('collection').addObject(this.get('newValue')); - this.set('newValue', ''); + this.get("collection").addObject(this.get("newValue")); + this.set("newValue", ""); this._saveValues(); }, removeValue(value) { - const collection = this.get('collection'); + const collection = this.get("collection"); collection.removeObject(value); this._saveValues(); } diff --git a/app/assets/javascripts/admin/components/watched-word-form.js.es6 b/app/assets/javascripts/admin/components/watched-word-form.js.es6 index e7ead641f2e..c9a61965e83 100644 --- a/app/assets/javascripts/admin/components/watched-word-form.js.es6 +++ b/app/assets/javascripts/admin/components/watched-word-form.js.es6 @@ -1,65 +1,94 @@ -import WatchedWord from 'admin/models/watched-word'; -import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators'; +import WatchedWord from "admin/models/watched-word"; +import { + default as computed, + on, + observes +} from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ - classNames: ['watched-word-form'], + classNames: ["watched-word-form"], formSubmitted: false, actionKey: null, showMessage: false, - @computed('regularExpressions') + @computed("regularExpressions") placeholderKey(regularExpressions) { - return "admin.watched_words.form.placeholder" + - (regularExpressions ? "_regexp" : ""); + return ( + "admin.watched_words.form.placeholder" + + (regularExpressions ? "_regexp" : "") + ); }, - @observes('word') + @observes("word") removeMessage() { - if (this.get('showMessage') && !Ember.isEmpty(this.get('word'))) { - this.set('showMessage', false); + if (this.get("showMessage") && !Ember.isEmpty(this.get("word"))) { + this.set("showMessage", false); } }, - @computed('word') + @computed("word") isUniqueWord(word) { const words = this.get("filteredContent") || []; - const filtered = words.filter(content => content.action === this.get("actionKey")); - return filtered.every(content => content.word.toLowerCase() !== word.toLowerCase()); + const filtered = words.filter( + content => content.action === this.get("actionKey") + ); + return filtered.every( + content => content.word.toLowerCase() !== word.toLowerCase() + ); }, actions: { submit() { if (!this.get("isUniqueWord")) { - this.setProperties({ showMessage: true, message: I18n.t('admin.watched_words.form.exists') }); + this.setProperties({ + showMessage: true, + message: I18n.t("admin.watched_words.form.exists") + }); return; } - if (!this.get('formSubmitted')) { - this.set('formSubmitted', true); + if (!this.get("formSubmitted")) { + this.set("formSubmitted", true); - const watchedWord = WatchedWord.create({ word: this.get('word'), action: this.get('actionKey') }); - - watchedWord.save().then(result => { - this.setProperties({ word: '', formSubmitted: false, showMessage: true, message: I18n.t('admin.watched_words.form.success') }); - this.sendAction('action', WatchedWord.create(result)); - Ember.run.schedule('afterRender', () => this.$('.watched-word-input').focus()); - }).catch(e => { - this.set('formSubmitted', false); - const msg = (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) ? - I18n.t("generic_error_with_reason", {error: e.jqXHR.responseJSON.errors.join('. ')}) : - I18n.t("generic_error"); - bootbox.alert(msg, () => this.$('.watched-word-input').focus()); + const watchedWord = WatchedWord.create({ + word: this.get("word"), + action: this.get("actionKey") }); + + watchedWord + .save() + .then(result => { + this.setProperties({ + word: "", + formSubmitted: false, + showMessage: true, + message: I18n.t("admin.watched_words.form.success") + }); + this.sendAction("action", WatchedWord.create(result)); + Ember.run.schedule("afterRender", () => + this.$(".watched-word-input").focus() + ); + }) + .catch(e => { + this.set("formSubmitted", false); + const msg = + e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors + ? I18n.t("generic_error_with_reason", { + error: e.jqXHR.responseJSON.errors.join(". ") + }) + : I18n.t("generic_error"); + bootbox.alert(msg, () => this.$(".watched-word-input").focus()); + }); } } }, @on("didInsertElement") _init() { - Ember.run.schedule('afterRender', () => { - this.$('.watched-word-input').keydown(e => { + Ember.run.schedule("afterRender", () => { + this.$(".watched-word-input").keydown(e => { if (e.keyCode === 13) { - this.send('submit'); + this.send("submit"); } }); }); diff --git a/app/assets/javascripts/admin/components/watched-word-uploader.js.es6 b/app/assets/javascripts/admin/components/watched-word-uploader.js.es6 index 8d9e6ba0a97..e5ff9a1fd77 100644 --- a/app/assets/javascripts/admin/components/watched-word-uploader.js.es6 +++ b/app/assets/javascripts/admin/components/watched-word-uploader.js.es6 @@ -2,16 +2,16 @@ import computed from "ember-addons/ember-computed-decorators"; import UploadMixin from "discourse/mixins/upload"; export default Em.Component.extend(UploadMixin, { - type: 'csv', - classNames: 'watched-words-uploader', - uploadUrl: '/admin/logs/watched_words/upload', + type: "csv", + classNames: "watched-words-uploader", + uploadUrl: "/admin/logs/watched_words/upload", addDisabled: Em.computed.alias("uploading"), validateUploadedFilesOptions() { return { csvOnly: true }; }, - @computed('actionKey') + @computed("actionKey") data(actionKey) { return { action_key: actionKey }; }, diff --git a/app/assets/javascripts/admin/controllers/admin-api-keys.js.es6 b/app/assets/javascripts/admin/controllers/admin-api-keys.js.es6 index 529538263c8..69a1c321f78 100644 --- a/app/assets/javascripts/admin/controllers/admin-api-keys.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-api-keys.js.es6 @@ -1,32 +1,40 @@ -import ApiKey from 'admin/models/api-key'; +import ApiKey from "admin/models/api-key"; export default Ember.Controller.extend({ - actions: { generateMasterKey() { - ApiKey.generateMasterKey().then(key => this.get('model').pushObject(key)); + ApiKey.generateMasterKey().then(key => this.get("model").pushObject(key)); }, regenerateKey(key) { - bootbox.confirm(I18n.t("admin.api.confirm_regen"), I18n.t("no_value"), I18n.t("yes_value"), result => { - if (result) { - key.regenerate(); + bootbox.confirm( + I18n.t("admin.api.confirm_regen"), + I18n.t("no_value"), + I18n.t("yes_value"), + result => { + if (result) { + key.regenerate(); + } } - }); + ); }, revokeKey(key) { - bootbox.confirm(I18n.t("admin.api.confirm_revoke"), I18n.t("no_value"), I18n.t("yes_value"), result => { - if (result) { - key.revoke().then(() => this.get('model').removeObject(key)); + bootbox.confirm( + I18n.t("admin.api.confirm_revoke"), + I18n.t("no_value"), + I18n.t("yes_value"), + result => { + if (result) { + key.revoke().then(() => this.get("model").removeObject(key)); + } } - }); + ); } }, // Has a master key already been generated? hasMasterKey: function() { - return !!this.get('model').findBy('user', null); - }.property('model.[]') - + return !!this.get("model").findBy("user", null); + }.property("model.[]") }); diff --git a/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 index 67b6c58d061..49e8d9d4412 100644 --- a/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 @@ -1,13 +1,15 @@ -import { ajax } from 'discourse/lib/ajax'; +import { ajax } from "discourse/lib/ajax"; export default Ember.Controller.extend({ adminBackups: Ember.inject.controller(), - status: Ember.computed.alias('adminBackups.model'), + status: Ember.computed.alias("adminBackups.model"), - uploadLabel: function() { return I18n.t("admin.backups.upload.label"); }.property(), + uploadLabel: function() { + return I18n.t("admin.backups.upload.label"); + }.property(), restoreTitle: function() { - if (!this.get('status.allowRestore')) { + if (!this.get("status.allowRestore")) { return "admin.backups.operations.restore.is_disabled"; } else if (this.get("status.isOperationRunning")) { return "admin.backups.operations.is_running"; @@ -17,7 +19,6 @@ export default Ember.Controller.extend({ }.property("status.{allowRestore,isOperationRunning}"), actions: { - toggleReadOnlyMode() { var self = this; if (!this.site.get("isReadOnly")) { @@ -38,9 +39,8 @@ export default Ember.Controller.extend({ }, download(backup) { - let link = backup.get('filename'); - ajax("/admin/backups/" + link, { type: "PUT" }) - .then(() => { + let link = backup.get("filename"); + ajax("/admin/backups/" + link, { type: "PUT" }).then(() => { bootbox.alert(I18n.t("admin.backups.operations.download.alert")); }); } diff --git a/app/assets/javascripts/admin/controllers/admin-backups.js.es6 b/app/assets/javascripts/admin/controllers/admin-backups.js.es6 index a429883378e..5cfa57271eb 100644 --- a/app/assets/javascripts/admin/controllers/admin-backups.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-backups.js.es6 @@ -1,5 +1,9 @@ export default Ember.Controller.extend({ noOperationIsRunning: Ember.computed.not("model.isOperationRunning"), - rollbackEnabled: Ember.computed.and("model.canRollback", "model.restoreEnabled", "noOperationIsRunning"), + rollbackEnabled: Ember.computed.and( + "model.canRollback", + "model.restoreEnabled", + "noOperationIsRunning" + ), rollbackDisabled: Ember.computed.not("rollbackEnabled") }); diff --git a/app/assets/javascripts/admin/controllers/admin-badges-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-badges-show.js.es6 index 7ddc6f3ac4e..c0d58e3b114 100644 --- a/app/assets/javascripts/admin/controllers/admin-badges-show.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-badges-show.js.es6 @@ -1,108 +1,139 @@ -import { popupAjaxError } from 'discourse/lib/ajax-error'; -import BufferedContent from 'discourse/mixins/buffered-content'; -import { propertyNotEqual } from 'discourse/lib/computed'; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import BufferedContent from "discourse/mixins/buffered-content"; +import { propertyNotEqual } from "discourse/lib/computed"; export default Ember.Controller.extend(BufferedContent, { adminBadges: Ember.inject.controller(), saving: false, - savingStatus: '', + savingStatus: "", - badgeTypes: Ember.computed.alias('adminBadges.badgeTypes'), - badgeGroupings: Ember.computed.alias('adminBadges.badgeGroupings'), - badgeTriggers: Ember.computed.alias('adminBadges.badgeTriggers'), - protectedSystemFields: Ember.computed.alias('adminBadges.protectedSystemFields'), + badgeTypes: Ember.computed.alias("adminBadges.badgeTypes"), + badgeGroupings: Ember.computed.alias("adminBadges.badgeGroupings"), + badgeTriggers: Ember.computed.alias("adminBadges.badgeTriggers"), + protectedSystemFields: Ember.computed.alias( + "adminBadges.protectedSystemFields" + ), - readOnly: Ember.computed.alias('buffered.system'), - showDisplayName: propertyNotEqual('name', 'displayName'), + readOnly: Ember.computed.alias("buffered.system"), + showDisplayName: propertyNotEqual("name", "displayName"), hasQuery: function() { - const bQuery = this.get('buffered.query'); + const bQuery = this.get("buffered.query"); if (bQuery) { return bQuery.trim().length > 0; } - const mQuery = this.get('model.query'); + const mQuery = this.get("model.query"); return mQuery && mQuery.trim().length > 0; - }.property('model.query', 'buffered.query'), + }.property("model.query", "buffered.query"), _resetSaving: function() { - this.set('saving', false); - this.set('savingStatus', ''); - }.observes('model.id'), + this.set("saving", false); + this.set("savingStatus", ""); + }.observes("model.id"), actions: { save() { - if (!this.get('saving')) { - let fields = ['allow_title', 'multiple_grant', - 'listable', 'auto_revoke', - 'enabled', 'show_posts', - 'target_posts', 'name', 'description', - 'long_description', - 'icon', 'image', 'query', 'badge_grouping_id', - 'trigger', 'badge_type_id']; + if (!this.get("saving")) { + let fields = [ + "allow_title", + "multiple_grant", + "listable", + "auto_revoke", + "enabled", + "show_posts", + "target_posts", + "name", + "description", + "long_description", + "icon", + "image", + "query", + "badge_grouping_id", + "trigger", + "badge_type_id" + ]; - if (this.get('buffered.system')){ - var protectedFields = this.get('protectedSystemFields'); - fields = _.filter(fields, function(f){ - return !_.include(protectedFields,f); + if (this.get("buffered.system")) { + var protectedFields = this.get("protectedSystemFields"); + fields = _.filter(fields, function(f) { + return !_.include(protectedFields, f); }); } - this.set('saving', true); - this.set('savingStatus', I18n.t('saving')); + this.set("saving", true); + this.set("savingStatus", I18n.t("saving")); - const boolFields = ['allow_title', 'multiple_grant', - 'listable', 'auto_revoke', - 'enabled', 'show_posts', - 'target_posts' ]; + const boolFields = [ + "allow_title", + "multiple_grant", + "listable", + "auto_revoke", + "enabled", + "show_posts", + "target_posts" + ]; const data = {}; - const buffered = this.get('buffered'); - fields.forEach(function(field){ + const buffered = this.get("buffered"); + fields.forEach(function(field) { var d = buffered.get(field); - if (_.include(boolFields, field)) { d = !!d; } + if (_.include(boolFields, field)) { + d = !!d; + } data[field] = d; }); - const newBadge = !this.get('id'); - const model = this.get('model'); - this.get('model').save(data).then(() => { - if (newBadge) { - const adminBadges = this.get('adminBadges.model'); - if (!adminBadges.includes(model)) { - adminBadges.pushObject(model); + const newBadge = !this.get("id"); + const model = this.get("model"); + this.get("model") + .save(data) + .then(() => { + if (newBadge) { + const adminBadges = this.get("adminBadges.model"); + if (!adminBadges.includes(model)) { + adminBadges.pushObject(model); + } + this.transitionToRoute("adminBadges.show", model.get("id")); + } else { + this.commitBuffer(); + this.set("savingStatus", I18n.t("saved")); } - this.transitionToRoute('adminBadges.show', model.get('id')); - } else { - this.commitBuffer(); - this.set('savingStatus', I18n.t('saved')); - } - - }).catch(popupAjaxError).finally(() => { - this.set('saving', false); - this.set('savingStatus', ''); - }); + }) + .catch(popupAjaxError) + .finally(() => { + this.set("saving", false); + this.set("savingStatus", ""); + }); } }, destroy() { - const adminBadges = this.get('adminBadges.model'); - const model = this.get('model'); + const adminBadges = this.get("adminBadges.model"); + const model = this.get("model"); - if (!model.get('id')) { - this.transitionToRoute('adminBadges.index'); + if (!model.get("id")) { + this.transitionToRoute("adminBadges.index"); return; } - return bootbox.confirm(I18n.t("admin.badges.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => { - if (result) { - model.destroy().then(() => { - adminBadges.removeObject(model); - this.transitionToRoute('adminBadges.index'); - }).catch(() => { - bootbox.alert(I18n.t('generic_error')); - }); + return bootbox.confirm( + I18n.t("admin.badges.delete_confirm"), + I18n.t("no_value"), + I18n.t("yes_value"), + result => { + if (result) { + model + .destroy() + .then(() => { + adminBadges.removeObject(model); + this.transitionToRoute("adminBadges.index"); + }) + .catch(() => { + bootbox.alert(I18n.t("generic_error")); + }); + } } - }); + ); } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-colors-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-colors-show.js.es6 index e333d2a58de..ca62835a04c 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-colors-show.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-colors-show.js.es6 @@ -1,7 +1,7 @@ -import computed from 'ember-addons/ember-computed-decorators'; +import computed from "ember-addons/ember-computed-decorators"; export default Ember.Controller.extend({ - @computed("model.colors","onlyOverridden") + @computed("model.colors", "onlyOverridden") colors(allColors, onlyOverridden) { if (onlyOverridden) { return allColors.filter(color => color.get("overridden")); @@ -11,7 +11,6 @@ export default Ember.Controller.extend({ }, actions: { - revert: function(color) { color.revert(); }, @@ -28,14 +27,20 @@ export default Ember.Controller.extend({ let range = document.createRange(); range.selectNode(area[0]); window.getSelection().addRange(range); - let successful = document.execCommand('copy'); + let successful = document.execCommand("copy"); if (successful) { - this.set("model.savingStatus", I18n.t("admin.customize.copied_to_clipboard")); + this.set( + "model.savingStatus", + I18n.t("admin.customize.copied_to_clipboard") + ); } else { - this.set("model.savingStatus", I18n.t("admin.customize.copy_to_clipboard_error")); + this.set( + "model.savingStatus", + I18n.t("admin.customize.copy_to_clipboard_error") + ); } - setTimeout(()=>{ + setTimeout(() => { this.set("model.savingStatus", null); }, 2000); @@ -46,29 +51,38 @@ export default Ember.Controller.extend({ }, copy() { - var newColorScheme = Em.copy(this.get('model'), true); - newColorScheme.set('name', I18n.t('admin.customize.colors.copy_name_prefix') + ' ' + this.get('model.name')); - newColorScheme.save().then(()=>{ - this.get('allColors').pushObject(newColorScheme); - this.replaceRoute('adminCustomize.colors.show', newColorScheme); + var newColorScheme = Em.copy(this.get("model"), true); + newColorScheme.set( + "name", + I18n.t("admin.customize.colors.copy_name_prefix") + + " " + + this.get("model.name") + ); + newColorScheme.save().then(() => { + this.get("allColors").pushObject(newColorScheme); + this.replaceRoute("adminCustomize.colors.show", newColorScheme); }); }, save: function() { - this.get('model').save(); + this.get("model").save(); }, destroy: function() { - - const model = this.get('model'); - return bootbox.confirm(I18n.t("admin.customize.colors.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => { - if (result) { - model.destroy().then(()=>{ - this.get('allColors').removeObject(model); - this.replaceRoute('adminCustomize.colors'); - }); + const model = this.get("model"); + return bootbox.confirm( + I18n.t("admin.customize.colors.delete_confirm"), + I18n.t("no_value"), + I18n.t("yes_value"), + result => { + if (result) { + model.destroy().then(() => { + this.get("allColors").removeObject(model); + this.replaceRoute("adminCustomize.colors"); + }); + } } - }); + ); } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 index 87166e386f5..945a46acf36 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 @@ -1,41 +1,43 @@ -import showModal from 'discourse/lib/show-modal'; +import showModal from "discourse/lib/show-modal"; export default Ember.Controller.extend({ baseColorScheme: function() { - return this.get('model').findBy('is_base', true); - }.property('model.@each.id'), + return this.get("model").findBy("is_base", true); + }.property("model.@each.id"), baseColorSchemes: function() { - return this.get('model').filterBy('is_base', true); - }.property('model.@each.id'), + return this.get("model").filterBy("is_base", true); + }.property("model.@each.id"), baseColors: function() { var baseColorsHash = Em.Object.create({}); - _.each(this.get('baseColorScheme.colors'), function(color){ - baseColorsHash.set(color.get('name'), color); + _.each(this.get("baseColorScheme.colors"), function(color) { + baseColorsHash.set(color.get("name"), color); }); return baseColorsHash; - }.property('baseColorScheme'), + }.property("baseColorScheme"), actions: { - newColorSchemeWithBase(baseKey) { - const base = this.get('baseColorSchemes').findBy('base_scheme_id', baseKey); + const base = this.get("baseColorSchemes").findBy( + "base_scheme_id", + baseKey + ); const newColorScheme = Em.copy(base, true); - newColorScheme.set('name', I18n.t('admin.customize.colors.new_name')); - newColorScheme.set('base_scheme_id', base.get('base_scheme_id')); - newColorScheme.save().then(()=>{ - this.get('model').pushObject(newColorScheme); - newColorScheme.set('savingStatus', null); - this.replaceRoute('adminCustomize.colors.show', newColorScheme); + newColorScheme.set("name", I18n.t("admin.customize.colors.new_name")); + newColorScheme.set("base_scheme_id", base.get("base_scheme_id")); + newColorScheme.save().then(() => { + this.get("model").pushObject(newColorScheme); + newColorScheme.set("savingStatus", null); + this.replaceRoute("adminCustomize.colors.show", newColorScheme); }); }, newColorScheme() { - showModal('admin-color-scheme-select-base', { model: this.get('baseColorSchemes'), admin: true}); - }, - - + showModal("admin-color-scheme-select-base", { + model: this.get("baseColorSchemes"), + admin: true + }); + } } - }); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-email-templates-edit.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-email-templates-edit.js.es6 index 4c5a2a28549..5a20a94cbfb 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-email-templates-edit.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-email-templates-edit.js.es6 @@ -1,38 +1,47 @@ -import { popupAjaxError } from 'discourse/lib/ajax-error'; -import { bufferedProperty } from 'discourse/mixins/buffered-content'; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import { bufferedProperty } from "discourse/mixins/buffered-content"; -export default Ember.Controller.extend(bufferedProperty('emailTemplate'), { +export default Ember.Controller.extend(bufferedProperty("emailTemplate"), { saved: false, hasMultipleSubjects: function() { - const buffered = this.get('buffered'); - if (buffered.getProperties('subject')['subject']) { + const buffered = this.get("buffered"); + if (buffered.getProperties("subject")["subject"]) { return false; } else { - return buffered.getProperties('id')['id']; + return buffered.getProperties("id")["id"]; } }.property("buffered"), actions: { saveChanges() { - this.set('saved', false); - const buffered = this.get('buffered'); - this.get('emailTemplate').save(buffered.getProperties('subject', 'body')).then(() => { - this.set('saved', true); - }).catch(popupAjaxError); + this.set("saved", false); + const buffered = this.get("buffered"); + this.get("emailTemplate") + .save(buffered.getProperties("subject", "body")) + .then(() => { + this.set("saved", true); + }) + .catch(popupAjaxError); }, revertChanges() { - this.set('saved', false); - bootbox.confirm(I18n.t('admin.customize.email_templates.revert_confirm'), result => { - if (result) { - this.get('emailTemplate').revert().then(props => { - const buffered = this.get('buffered'); - buffered.setProperties(props); - this.commitBuffer(); - }).catch(popupAjaxError); + this.set("saved", false); + bootbox.confirm( + I18n.t("admin.customize.email_templates.revert_confirm"), + result => { + if (result) { + this.get("emailTemplate") + .revert() + .then(props => { + const buffered = this.get("buffered"); + buffered.setProperties(props); + this.commitBuffer(); + }) + .catch(popupAjaxError); + } } - }); + ); } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-email-templates.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-email-templates.js.es6 index fe9cd69804c..6fe215c9c3e 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-email-templates.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-email-templates.js.es6 @@ -1,6 +1,6 @@ export default Ember.Controller.extend({ - titleSorting: ['title'], + titleSorting: ["title"], emailTemplates: null, - sortedTemplates: Ember.computed.sort('emailTemplates', 'titleSorting') + sortedTemplates: Ember.computed.sort("emailTemplates", "titleSorting") }); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 index 28ae718afab..33b46886dbe 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 @@ -1,66 +1,91 @@ -import { url } from 'discourse/lib/computed'; -import { default as computed, observes } from 'ember-addons/ember-computed-decorators'; +import { url } from "discourse/lib/computed"; +import { + default as computed, + observes +} from "ember-addons/ember-computed-decorators"; export default Ember.Controller.extend({ maximized: false, section: null, - editRouteName: 'adminCustomizeThemes.edit', + editRouteName: "adminCustomizeThemes.edit", targets: [ - { id: 0, name: 'common' }, - { id: 1, name: 'desktop' }, - { id: 2, name: 'mobile' }, - { id: 3, name: 'settings' } + { id: 0, name: "common" }, + { id: 1, name: "desktop" }, + { id: 2, name: "mobile" }, + { id: 3, name: "settings" } ], - fieldsForTarget: function (target) { - const common = ["scss", "head_tag", "header", "after_header", "body_tag", "footer"]; - switch(target) { - case "common": return [...common, "embedded_scss"]; - case "desktop": return common; - case "mobile": return common; - case "settings": return ["yaml"]; + fieldsForTarget: function(target) { + const common = [ + "scss", + "head_tag", + "header", + "after_header", + "body_tag", + "footer" + ]; + switch (target) { + case "common": + return [...common, "embedded_scss"]; + case "desktop": + return common; + case "mobile": + return common; + case "settings": + return ["yaml"]; } }, - @computed('onlyOverridden') + @computed("onlyOverridden") showCommon() { - return this.shouldShow('common'); + return this.shouldShow("common"); }, - @computed('onlyOverridden') + @computed("onlyOverridden") showDesktop() { - return this.shouldShow('desktop'); + return this.shouldShow("desktop"); }, - @computed('onlyOverridden') + @computed("onlyOverridden") showMobile() { - return this.shouldShow('mobile'); + return this.shouldShow("mobile"); }, - @computed('onlyOverridden', 'model.remote_theme') + @computed("onlyOverridden", "model.remote_theme") showSettings() { return false; }, - @observes('onlyOverridden') + @observes("onlyOverridden") onlyOverriddenChanged() { - if (this.get('onlyOverridden')) { - if (!this.get('model').hasEdited(this.get('currentTargetName'), this.get('fieldName'))) { - let target = (this.get('showCommon') && 'common') || - (this.get('showDesktop') && 'desktop') || - (this.get('showMobile') && 'mobile'); + if (this.get("onlyOverridden")) { + if ( + !this.get("model").hasEdited( + this.get("currentTargetName"), + this.get("fieldName") + ) + ) { + let target = + (this.get("showCommon") && "common") || + (this.get("showDesktop") && "desktop") || + (this.get("showMobile") && "mobile"); - let fields = this.get('model.theme_fields'); - let field = fields && fields.find(f => (f.target === target)); - this.replaceRoute(this.get('editRouteName'), this.get('model.id'), target, field && field.name); + let fields = this.get("model.theme_fields"); + let field = fields && fields.find(f => f.target === target); + this.replaceRoute( + this.get("editRouteName"), + this.get("model.id"), + target, + field && field.name + ); } } }, - shouldShow(target){ - if(!this.get("onlyOverridden")) { + shouldShow(target) { + if (!this.get("onlyOverridden")) { return true; } return this.get("model").hasEdited(target); @@ -69,13 +94,13 @@ export default Ember.Controller.extend({ currentTarget: 0, setTargetName: function(name) { - const target = this.get('targets').find(t => t.name === name); + const target = this.get("targets").find(t => t.name === name); this.set("currentTarget", target && target.id); }, @computed("currentTarget") currentTargetName(id) { - const target = this.get('targets').find(t => t.id === parseInt(id, 10)); + const target = this.get("targets").find(t => t.id === parseInt(id, 10)); return target && target.name; }, @@ -87,7 +112,7 @@ export default Ember.Controller.extend({ @computed("currentTargetName", "fieldName", "saving") error(target, fieldName) { - return this.get('model').getError(target, fieldName); + return this.get("model").getError(target, fieldName); }, @computed("fieldName", "currentTargetName") @@ -116,9 +141,9 @@ export default Ember.Controller.extend({ fields = fields.filter(name => model.hasEdited(targetName, name)); } - return fields.map(name=>{ + return fields.map(name => { let hash = { - key: (`admin.customize.theme.${name}.text`), + key: `admin.customize.theme.${name}.text`, name: name }; @@ -132,30 +157,36 @@ export default Ember.Controller.extend({ }); }, - previewUrl: url('model.id', '/admin/themes/%@/preview'), + previewUrl: url("model.id", "/admin/themes/%@/preview"), maximizeIcon: function() { - return this.get('maximized') ? 'compress' : 'expand'; - }.property('maximized'), + return this.get("maximized") ? "compress" : "expand"; + }.property("maximized"), saveButtonText: function() { - return this.get('model.isSaving') ? I18n.t('saving') : I18n.t('admin.customize.save'); - }.property('model.isSaving'), + return this.get("model.isSaving") + ? I18n.t("saving") + : I18n.t("admin.customize.save"); + }.property("model.isSaving"), saveDisabled: function() { - return !this.get('model.changed') || this.get('model.isSaving'); - }.property('model.changed', 'model.isSaving'), + return !this.get("model.changed") || this.get("model.isSaving"); + }.property("model.changed", "model.isSaving"), actions: { save() { - this.set('saving', true); - this.get('model').saveChanges("theme_fields").finally(()=>{this.set('saving', false);}); + this.set("saving", true); + this.get("model") + .saveChanges("theme_fields") + .finally(() => { + this.set("saving", false); + }); }, toggleMaximize: function() { - this.toggleProperty('maximized'); - Em.run.next(()=>{ - this.appEvents.trigger('ace:resize'); + this.toggleProperty("maximized"); + Em.run.next(() => { + this.appEvents.trigger("ace:resize"); }); } } diff --git a/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 index 16eff829470..20a16ab6269 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 @@ -1,63 +1,72 @@ -import { default as computed } from 'ember-addons/ember-computed-decorators'; -import { url } from 'discourse/lib/computed'; -import { popupAjaxError } from 'discourse/lib/ajax-error'; -import showModal from 'discourse/lib/show-modal'; -import ThemeSettings from 'admin/models/theme-settings'; +import { default as computed } from "ember-addons/ember-computed-decorators"; +import { url } from "discourse/lib/computed"; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import showModal from "discourse/lib/show-modal"; +import ThemeSettings from "admin/models/theme-settings"; const THEME_UPLOAD_VAR = 2; export default Ember.Controller.extend({ - - editRouteName: 'adminCustomizeThemes.edit', + editRouteName: "adminCustomizeThemes.edit", @computed("model", "allThemes") parentThemes(model, allThemes) { let parents = allThemes.filter(theme => - _.contains(theme.get("childThemes"), model)); + _.contains(theme.get("childThemes"), model) + ); return parents.length === 0 ? null : parents; }, @computed("model.theme_fields.@each") hasEditedFields(fields) { - return fields.any(f=>!Em.isBlank(f.value)); + return fields.any(f => !Em.isBlank(f.value)); }, - @computed('model.theme_fields.@each') + @computed("model.theme_fields.@each") editedDescriptions(fields) { let descriptions = []; let description = target => { - let current = fields.filter(field => field.target === target && !Em.isBlank(field.value)); + let current = fields.filter( + field => field.target === target && !Em.isBlank(field.value) + ); if (current.length > 0) { - let text = I18n.t('admin.customize.theme.'+target); - let localized = current.map(f=>I18n.t('admin.customize.theme.'+f.name + '.text')); + let text = I18n.t("admin.customize.theme." + target); + let localized = current.map(f => + I18n.t("admin.customize.theme." + f.name + ".text") + ); return text + ": " + localized.join(" , "); } }; - ['common', 'desktop', 'mobile'].forEach(target => { + ["common", "desktop", "mobile"].forEach(target => { descriptions.push(description(target)); }); - return descriptions.reject(d=>Em.isBlank(d)); + return descriptions.reject(d => Em.isBlank(d)); }, - previewUrl: url('model.id', '/admin/themes/%@/preview'), + previewUrl: url("model.id", "/admin/themes/%@/preview"), @computed("colorSchemeId", "model.color_scheme_id") colorSchemeChanged(colorSchemeId, existingId) { colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId); - return colorSchemeId !== existingId; + return colorSchemeId !== existingId; }, - @computed("availableChildThemes", "model.childThemes.@each", "model", "allowChildThemes") + @computed( + "availableChildThemes", + "model.childThemes.@each", + "model", + "allowChildThemes" + ) selectableChildThemes(available, childThemes, model, allowChildThemes) { if (!allowChildThemes && (!childThemes || childThemes.length === 0)) { return null; } let themes = []; - available.forEach(t=> { - if (!childThemes || (childThemes.indexOf(t) === -1)) { + available.forEach(t => { + if (!childThemes || childThemes.indexOf(t) === -1) { themes.push(t); - }; + } }); return themes.length === 0 ? null : themes; }, @@ -90,44 +99,48 @@ export default Ember.Controller.extend({ return settings.length > 0; }, - downloadUrl: url('model.id', '/admin/themes/%@'), + downloadUrl: url("model.id", "/admin/themes/%@"), actions: { - updateToLatest() { this.set("updatingRemote", true); - this.get("model").updateToLatest() + this.get("model") + .updateToLatest() .catch(popupAjaxError) - .finally(()=>{ + .finally(() => { this.set("updatingRemote", false); }); }, checkForThemeUpdates() { this.set("updatingRemote", true); - this.get("model").checkForUpdates() + this.get("model") + .checkForUpdates() .catch(popupAjaxError) - .finally(()=>{ + .finally(() => { this.set("updatingRemote", false); }); }, addUploadModal() { - showModal('admin-add-upload', {admin: true, name: ''}); + showModal("admin-add-upload", { admin: true, name: "" }); }, addUpload(info) { let model = this.get("model"); - model.setField('common', info.name, '', info.upload_id, THEME_UPLOAD_VAR); - model.saveChanges('theme_fields').catch(e => popupAjaxError(e)); + model.setField("common", info.name, "", info.upload_id, THEME_UPLOAD_VAR); + model.saveChanges("theme_fields").catch(e => popupAjaxError(e)); }, cancelChangeScheme() { this.set("colorSchemeId", this.get("model.color_scheme_id")); }, - changeScheme(){ + changeScheme() { let schemeId = this.get("colorSchemeId"); - this.set("model.color_scheme_id", schemeId === null ? null : parseInt(schemeId)); + this.set( + "model.color_scheme_id", + schemeId === null ? null : parseInt(schemeId) + ); this.get("model").saveChanges("color_scheme_id"); }, startEditingName() { @@ -144,14 +157,23 @@ export default Ember.Controller.extend({ }, editTheme() { - let edit = ()=>this.transitionToRoute(this.get('editRouteName'), this.get('model.id'), 'common', 'scss'); + let edit = () => + this.transitionToRoute( + this.get("editRouteName"), + this.get("model.id"), + "common", + "scss" + ); if (this.get("model.remote_theme")) { - bootbox.confirm(I18n.t("admin.customize.theme.edit_confirm"), result => { - if (result) { - edit(); - } - }); + bootbox.confirm( + I18n.t("admin.customize.theme.edit_confirm"), + result => { + if (result) { + edit(); + } + } + ); } else { edit(); } @@ -159,10 +181,10 @@ export default Ember.Controller.extend({ applyDefault() { const model = this.get("model"); - model.saveChanges("default").then(()=>{ + model.saveChanges("default").then(() => { if (model.get("default")) { - this.get("allThemes").forEach(theme=>{ - if (theme !== model && theme.get('default')) { + this.get("allThemes").forEach(theme => { + if (theme !== model && theme.get("default")) { theme.set("default", false); } }); @@ -182,13 +204,15 @@ export default Ember.Controller.extend({ removeUpload(upload) { return bootbox.confirm( - I18n.t("admin.customize.theme.delete_upload_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), result => { - if (result) { - this.get("model").removeField(upload); - } - }); + I18n.t("admin.customize.theme.delete_upload_confirm"), + I18n.t("no_value"), + I18n.t("yes_value"), + result => { + if (result) { + this.get("model").removeField(upload); + } + } + ); }, removeChildTheme(theme) { @@ -196,17 +220,20 @@ export default Ember.Controller.extend({ }, destroy() { - return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => { - if (result) { - const model = this.get('model'); - model.destroyRecord().then(() => { - this.get('allThemes').removeObject(model); - this.transitionToRoute('adminCustomizeThemes'); - }); + return bootbox.confirm( + I18n.t("admin.customize.delete_confirm"), + I18n.t("no_value"), + I18n.t("yes_value"), + result => { + if (result) { + const model = this.get("model"); + model.destroyRecord().then(() => { + this.get("allThemes").removeObject(model); + this.transitionToRoute("adminCustomizeThemes"); + }); + } } - }); - }, - + ); + } } - }); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-themes.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-themes.js.es6 index b9a897a26a1..ecd80018955 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-themes.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-themes.js.es6 @@ -1,10 +1,14 @@ -import { default as computed } from 'ember-addons/ember-computed-decorators'; +import { default as computed } from "ember-addons/ember-computed-decorators"; export default Ember.Controller.extend({ - @computed('model', 'model.@each') + @computed("model", "model.@each") sortedThemes(themes) { return _.sortBy(themes.content, t => { - return [!t.get("default"), !t.get("user_selectable"), t.get("name").toLowerCase()]; + return [ + !t.get("default"), + !t.get("user_selectable"), + t.get("name").toLowerCase() + ]; }); } }); diff --git a/app/assets/javascripts/admin/controllers/admin-dashboard-next.js.es6 b/app/assets/javascripts/admin/controllers/admin-dashboard-next.js.es6 index f65e2f04fdc..4ba338f5ad3 100644 --- a/app/assets/javascripts/admin/controllers/admin-dashboard-next.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-dashboard-next.js.es6 @@ -26,31 +26,46 @@ export default Ember.Controller.extend({ fetchDashboard() { if (this.get("isLoading")) return; - if (!this.get("dashboardFetchedAt") || moment().subtract(30, "minutes").toDate() > this.get("dashboardFetchedAt")) { + if ( + !this.get("dashboardFetchedAt") || + moment() + .subtract(30, "minutes") + .toDate() > this.get("dashboardFetchedAt") + ) { this.set("isLoading", true); const versionChecks = this.siteSettings.version_checks; - AdminDashboardNext.find().then(adminDashboardNextModel => { + AdminDashboardNext.find() + .then(adminDashboardNextModel => { + if (versionChecks) { + this.set( + "versionCheck", + VersionCheck.create(adminDashboardNextModel.version_check) + ); + } - if (versionChecks) { - this.set("versionCheck", VersionCheck.create(adminDashboardNextModel.version_check)); - } - - this.setProperties({ - dashboardFetchedAt: new Date(), - model: adminDashboardNextModel, - reports: adminDashboardNextModel.reports.map(x => Report.create(x)) + this.setProperties({ + dashboardFetchedAt: new Date(), + model: adminDashboardNextModel, + reports: adminDashboardNextModel.reports.map(x => Report.create(x)) + }); + }) + .catch(e => { + this.get("exceptionController").set("thrown", e.jqXHR); + this.replaceRoute("exception"); + }) + .finally(() => { + this.set("isLoading", false); }); - }).catch(e => { - this.get("exceptionController").set("thrown", e.jqXHR); - this.replaceRoute("exception"); - }).finally(() => { - this.set("isLoading", false); - }); } - if (!this.get("problemsFetchedAt") || moment().subtract(PROBLEMS_CHECK_MINUTES, "minutes").toDate() > this.get("problemsFetchedAt")) { + if ( + !this.get("problemsFetchedAt") || + moment() + .subtract(PROBLEMS_CHECK_MINUTES, "minutes") + .toDate() > this.get("problemsFetchedAt") + ) { this.loadProblems(); } }, @@ -58,21 +73,28 @@ export default Ember.Controller.extend({ loadProblems() { this.set("loadingProblems", true); this.set("problemsFetchedAt", new Date()); - AdminDashboardNext.fetchProblems().then(d => { - this.set("problems", d.problems); - }).finally(() => { - this.set("loadingProblems", false); - }); + AdminDashboardNext.fetchProblems() + .then(d => { + this.set("problems", d.problems); + }) + .finally(() => { + this.set("loadingProblems", false); + }); }, @computed("problemsFetchedAt") problemsTimestamp(problemsFetchedAt) { - return moment(problemsFetchedAt).locale("en").format("LLL"); + return moment(problemsFetchedAt) + .locale("en") + .format("LLL"); }, @computed("period") startDate(period) { - let fullDay = moment().locale("en").utc().subtract(1, "day"); + let fullDay = moment() + .locale("en") + .utc() + .subtract(1, "day"); switch (period) { case "yearly": @@ -94,12 +116,20 @@ export default Ember.Controller.extend({ @computed() lastWeek() { - return moment().locale("en").utc().endOf("day").subtract(1, "week"); + return moment() + .locale("en") + .utc() + .endOf("day") + .subtract(1, "week"); }, @computed() endDate() { - return moment().locale("en").utc().subtract(1, "day").endOf("day"); + return moment() + .locale("en") + .utc() + .subtract(1, "day") + .endOf("day"); }, @computed("model.attributes.updated_at") @@ -118,7 +148,7 @@ export default Ember.Controller.extend({ }, refreshProblems() { this.loadProblems(); - }, + } }, _reportsForPeriodURL(period) { diff --git a/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 b/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 index 973cd3d57c9..9b545fc2fbe 100644 --- a/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 @@ -1,50 +1,74 @@ -import AdminDashboard from 'admin/models/admin-dashboard'; -import Report from 'admin/models/report'; -import AdminUser from 'admin/models/admin-user'; -import computed from 'ember-addons/ember-computed-decorators'; +import AdminDashboard from "admin/models/admin-dashboard"; +import Report from "admin/models/report"; +import AdminUser from "admin/models/admin-user"; +import computed from "ember-addons/ember-computed-decorators"; +const ATTRIBUTES = [ + "disk_space", + "admins", + "moderators", + "silenced", + "suspended", + "top_traffic_sources", + "top_referred_topics", + "updated_at" +]; -const ATTRIBUTES = [ 'disk_space','admins', 'moderators', 'silenced', 'suspended', 'top_traffic_sources', - 'top_referred_topics', 'updated_at']; - -const REPORTS = [ 'global_reports', 'page_view_reports', 'private_message_reports', 'http_reports', - 'user_reports', 'mobile_reports']; +const REPORTS = [ + "global_reports", + "page_view_reports", + "private_message_reports", + "http_reports", + "user_reports", + "mobile_reports" +]; // This controller supports the default interface when you enter the admin section. export default Ember.Controller.extend({ loading: null, versionCheck: null, dashboardFetchedAt: null, - exceptionController: Ember.inject.controller('exception'), + exceptionController: Ember.inject.controller("exception"), fetchDashboard() { - if (!this.get('dashboardFetchedAt') || moment().subtract(30, 'minutes').toDate() > this.get('dashboardFetchedAt')) { - this.set('loading', true); - AdminDashboard.find().then(d => { - this.set('dashboardFetchedAt', new Date()); + if ( + !this.get("dashboardFetchedAt") || + moment() + .subtract(30, "minutes") + .toDate() > this.get("dashboardFetchedAt") + ) { + this.set("loading", true); + AdminDashboard.find() + .then(d => { + this.set("dashboardFetchedAt", new Date()); - REPORTS.forEach(name => this.set(name, d[name].map(r => Report.create(r)))); + REPORTS.forEach(name => + this.set(name, d[name].map(r => Report.create(r))) + ); - const topReferrers = d.top_referrers; - if (topReferrers && topReferrers.data) { - d.top_referrers.data = topReferrers.data.map(user => AdminUser.create(user)); - this.set('top_referrers', topReferrers); - } + const topReferrers = d.top_referrers; + if (topReferrers && topReferrers.data) { + d.top_referrers.data = topReferrers.data.map(user => + AdminUser.create(user) + ); + this.set("top_referrers", topReferrers); + } - ATTRIBUTES.forEach(a => this.set(a, d[a])); - }).catch(e => { - this.get('exceptionController').set('thrown', e.jqXHR); - this.replaceRoute('exception'); - }).finally(() => { - this.set('loading', false); - }); + ATTRIBUTES.forEach(a => this.set(a, d[a])); + }) + .catch(e => { + this.get("exceptionController").set("thrown", e.jqXHR); + this.replaceRoute("exception"); + }) + .finally(() => { + this.set("loading", false); + }); } }, - - @computed('updated_at') + @computed("updated_at") updatedTimestamp(updatedAt) { - return moment(updatedAt).format('LLL'); + return moment(updatedAt).format("LLL"); }, actions: { @@ -52,5 +76,4 @@ export default Ember.Controller.extend({ this.set("showTrafficReport", true); } } - }); diff --git a/app/assets/javascripts/admin/controllers/admin-email-bounced.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-bounced.js.es6 index ae75d187155..230fa07afb7 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-bounced.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-bounced.js.es6 @@ -1,6 +1,6 @@ -import AdminEmailLogsController from 'admin/controllers/admin-email-logs'; -import debounce from 'discourse/lib/debounce'; -import EmailLog from 'admin/models/email-log'; +import AdminEmailLogsController from "admin/controllers/admin-email-logs"; +import debounce from "discourse/lib/debounce"; +import EmailLog from "admin/models/email-log"; export default AdminEmailLogsController.extend({ filterEmailLogs: debounce(function() { diff --git a/app/assets/javascripts/admin/controllers/admin-email-incomings.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-incomings.js.es6 index a6acd9af78f..9dc59589fbd 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-incomings.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-incomings.js.es6 @@ -1,21 +1,25 @@ -import IncomingEmail from 'admin/models/incoming-email'; +import IncomingEmail from "admin/models/incoming-email"; export default Ember.Controller.extend({ loading: false, actions: { - loadMore() { - if (this.get("loading") || this.get("model.allLoaded")) { return; } - this.set('loading', true); + if (this.get("loading") || this.get("model.allLoaded")) { + return; + } + this.set("loading", true); IncomingEmail.findAll(this.get("filter"), this.get("model.length")) - .then(incoming => { - if (incoming.length < 50) { this.get("model").set("allLoaded", true); } - this.get("model").addObjects(incoming); - }).finally(() => { - this.set('loading', false); - }); + .then(incoming => { + if (incoming.length < 50) { + this.get("model").set("allLoaded", true); + } + this.get("model").addObjects(incoming); + }) + .finally(() => { + this.set("loading", false); + }); } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 index fce0cb891ef..27b7bb498cf 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 @@ -1,12 +1,11 @@ -import { ajax } from 'discourse/lib/ajax'; +import { ajax } from "discourse/lib/ajax"; export default Ember.Controller.extend({ - /** Is the "send test email" button disabled? @property sendTestEmailDisabled **/ - sendTestEmailDisabled: Em.computed.empty('testEmailAddress'), + sendTestEmailDisabled: Em.computed.empty("testEmailAddress"), /** Clears the 'sentTestEmail' property on successful send. @@ -14,8 +13,8 @@ export default Ember.Controller.extend({ @method testEmailAddressChanged **/ testEmailAddressChanged: function() { - this.set('sentTestEmail', false); - }.observes('testEmailAddress'), + this.set("sentTestEmail", false); + }.observes("testEmailAddress"), actions: { /** @@ -31,21 +30,28 @@ export default Ember.Controller.extend({ var self = this; ajax("/admin/email/test", { - type: 'POST', - data: { email_address: this.get('testEmailAddress') } - }).then(function () { - self.set('sentTestEmail', true); - }, function(e) { - if (e.responseJSON && e.responseJSON.errors) { - bootbox.alert(I18n.t('admin.email.error', { server_error: e.responseJSON.errors[0] })); - } else { - bootbox.alert(I18n.t('admin.email.test_error')); - } - }).finally(function() { - self.set('sendingEmail', false); - }); - + type: "POST", + data: { email_address: this.get("testEmailAddress") } + }) + .then( + function() { + self.set("sentTestEmail", true); + }, + function(e) { + if (e.responseJSON && e.responseJSON.errors) { + bootbox.alert( + I18n.t("admin.email.error", { + server_error: e.responseJSON.errors[0] + }) + ); + } else { + bootbox.alert(I18n.t("admin.email.test_error")); + } + } + ) + .finally(function() { + self.set("sendingEmail", false); + }); } } - }); diff --git a/app/assets/javascripts/admin/controllers/admin-email-logs.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-logs.js.es6 index 44a38dfa324..a86ae421115 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-logs.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-logs.js.es6 @@ -1,20 +1,25 @@ -import EmailLog from 'admin/models/email-log'; +import EmailLog from "admin/models/email-log"; export default Ember.Controller.extend({ loading: false, actions: { loadMore() { - if (this.get("loading") || this.get("model.allLoaded")) { return; } + if (this.get("loading") || this.get("model.allLoaded")) { + return; + } - this.set('loading', true); + this.set("loading", true); return EmailLog.findAll(this.get("filter"), this.get("model.length")) - .then(logs => { - if (logs.length < 50) { this.get("model").set("allLoaded", true); } - this.get("model").addObjects(logs); - }).finally(() => { - this.set('loading', false); - }); + .then(logs => { + if (logs.length < 50) { + this.get("model").set("allLoaded", true); + } + this.get("model").addObjects(logs); + }) + .finally(() => { + this.set("loading", false); + }); } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-email-preview-digest.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-preview-digest.js.es6 index eb03301c92d..9517a3f9a74 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-preview-digest.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-preview-digest.js.es6 @@ -1,52 +1,60 @@ -import EmailPreview from 'admin/models/email-preview'; -import { popupAjaxError } from 'discourse/lib/ajax-error'; +import EmailPreview from "admin/models/email-preview"; +import { popupAjaxError } from "discourse/lib/ajax-error"; export default Ember.Controller.extend({ username: null, lastSeen: null, - emailEmpty: Ember.computed.empty('email'), - sendEmailDisabled: Ember.computed.or('emailEmpty', 'sendingEmail'), - showSendEmailForm: Ember.computed.notEmpty('model.html_content'), - htmlEmpty: Ember.computed.empty('model.html_content'), + emailEmpty: Ember.computed.empty("email"), + sendEmailDisabled: Ember.computed.or("emailEmpty", "sendingEmail"), + showSendEmailForm: Ember.computed.notEmpty("model.html_content"), + htmlEmpty: Ember.computed.empty("model.html_content"), actions: { refresh() { - const model = this.get('model'); + const model = this.get("model"); - this.set('loading', true); - this.set('sentEmail', false); + this.set("loading", true); + this.set("sentEmail", false); - let username = this.get('username'); + let username = this.get("username"); if (!username) { - username = this.currentUser.get('username'); - this.set('username', username); + username = this.currentUser.get("username"); + this.set("username", username); } - EmailPreview.findDigest(username, this.get('lastSeen')).then(email => { - model.setProperties(email.getProperties('html_content', 'text_content')); - this.set('loading', false); + EmailPreview.findDigest(username, this.get("lastSeen")).then(email => { + model.setProperties( + email.getProperties("html_content", "text_content") + ); + this.set("loading", false); }); }, toggleShowHtml() { - this.toggleProperty('showHtml'); + this.toggleProperty("showHtml"); }, sendEmail() { - this.set('sendingEmail', true); - this.set('sentEmail', false); + this.set("sendingEmail", true); + this.set("sentEmail", false); - EmailPreview.sendDigest(this.get('username'), this.get('lastSeen'), this.get('email')).then(result => { - if (result.errors) { - bootbox.alert(result.errors); - } else { - this.set('sentEmail', true); - } - }).catch(popupAjaxError).finally(() => { - this.set('sendingEmail', false); - }); + EmailPreview.sendDigest( + this.get("username"), + this.get("lastSeen"), + this.get("email") + ) + .then(result => { + if (result.errors) { + bootbox.alert(result.errors); + } else { + this.set("sentEmail", true); + } + }) + .catch(popupAjaxError) + .finally(() => { + this.set("sendingEmail", false); + }); } } - }); diff --git a/app/assets/javascripts/admin/controllers/admin-email-received.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-received.js.es6 index 69ebd5e4c49..a5f240fdbfd 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-received.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-received.js.es6 @@ -1,9 +1,11 @@ -import AdminEmailIncomingsController from 'admin/controllers/admin-email-incomings'; -import debounce from 'discourse/lib/debounce'; -import IncomingEmail from 'admin/models/incoming-email'; +import AdminEmailIncomingsController from "admin/controllers/admin-email-incomings"; +import debounce from "discourse/lib/debounce"; +import IncomingEmail from "admin/models/incoming-email"; export default AdminEmailIncomingsController.extend({ filterIncomingEmails: debounce(function() { - IncomingEmail.findAll(this.get("filter")).then(incomings => this.set("model", incomings)); + IncomingEmail.findAll(this.get("filter")).then(incomings => + this.set("model", incomings) + ); }, 250).observes("filter.{from,to,subject}") }); diff --git a/app/assets/javascripts/admin/controllers/admin-email-rejected.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-rejected.js.es6 index 317a669cd0e..b9341dd7e35 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-rejected.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-rejected.js.es6 @@ -1,9 +1,11 @@ -import AdminEmailIncomingsController from 'admin/controllers/admin-email-incomings'; -import debounce from 'discourse/lib/debounce'; -import IncomingEmail from 'admin/models/incoming-email'; +import AdminEmailIncomingsController from "admin/controllers/admin-email-incomings"; +import debounce from "discourse/lib/debounce"; +import IncomingEmail from "admin/models/incoming-email"; export default AdminEmailIncomingsController.extend({ filterIncomingEmails: debounce(function() { - IncomingEmail.findAll(this.get("filter")).then(incomings => this.set("model", incomings)); + IncomingEmail.findAll(this.get("filter")).then(incomings => + this.set("model", incomings) + ); }, 250).observes("filter.{from,to,subject,error}") }); diff --git a/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 index d73d640adca..691b017c6f0 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 @@ -1,6 +1,6 @@ -import AdminEmailLogsController from 'admin/controllers/admin-email-logs'; -import debounce from 'discourse/lib/debounce'; -import EmailLog from 'admin/models/email-log'; +import AdminEmailLogsController from "admin/controllers/admin-email-logs"; +import debounce from "discourse/lib/debounce"; +import EmailLog from "admin/models/email-log"; export default AdminEmailLogsController.extend({ filterEmailLogs: debounce(function() { diff --git a/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 index ae75d187155..230fa07afb7 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 @@ -1,6 +1,6 @@ -import AdminEmailLogsController from 'admin/controllers/admin-email-logs'; -import debounce from 'discourse/lib/debounce'; -import EmailLog from 'admin/models/email-log'; +import AdminEmailLogsController from "admin/controllers/admin-email-logs"; +import debounce from "discourse/lib/debounce"; +import EmailLog from "admin/models/email-log"; export default AdminEmailLogsController.extend({ filterEmailLogs: debounce(function() { diff --git a/app/assets/javascripts/admin/controllers/admin-embedding.js.es6 b/app/assets/javascripts/admin/controllers/admin-embedding.js.es6 index 266aa6975e6..33240955b55 100644 --- a/app/assets/javascripts/admin/controllers/admin-embedding.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-embedding.js.es6 @@ -1,22 +1,20 @@ -import computed from 'ember-addons/ember-computed-decorators'; -import { popupAjaxError } from 'discourse/lib/ajax-error'; +import computed from "ember-addons/ember-computed-decorators"; +import { popupAjaxError } from "discourse/lib/ajax-error"; export default Ember.Controller.extend({ saved: false, embedding: null, // show settings if we have at least one created host - @computed('embedding.embeddable_hosts.@each.isCreated') + @computed("embedding.embeddable_hosts.@each.isCreated") showSecondary() { - const hosts = this.get('embedding.embeddable_hosts'); - return hosts.length && hosts.findBy('isCreated'); + const hosts = this.get("embedding.embeddable_hosts"); + return hosts.length && hosts.findBy("isCreated"); }, - @computed('embedding.base_url') + @computed("embedding.base_url") embeddingCode(baseUrl) { - - const html = -`
+ const html = `
gmail.com'); + fillIn(".search-query", "@gmail.com"); andThen(() => { - assert.ok(exists('.search-advanced-options span:contains("gmail.com")'), 'it escapes search term'); + assert.ok( + exists( + '.search-advanced-options span:contains("gmail.com")' + ), + "it escapes search term" + ); }); }); QUnit.test("update username through advanced search ui", assert => { visit("/search"); - fillIn('.search-query', 'none'); - fillIn('.search-advanced-options .user-selector', 'admin'); - click('.search-advanced-options .user-selector'); - keyEvent('.search-advanced-options .user-selector', 'keydown', 8); + fillIn(".search-query", "none"); + fillIn(".search-advanced-options .user-selector", "admin"); + click(".search-advanced-options .user-selector"); + keyEvent(".search-advanced-options .user-selector", "keydown", 8); andThen(() => { waitFor(assert, () => { - assert.ok(visible('.search-advanced-options .autocomplete'), '"autocomplete" popup is visible'); - assert.ok(exists('.search-advanced-options .autocomplete ul li a span.username:contains("admin")'), '"autocomplete" popup has an entry for "admin"'); + assert.ok( + visible(".search-advanced-options .autocomplete"), + '"autocomplete" popup is visible' + ); + assert.ok( + exists( + '.search-advanced-options .autocomplete ul li a span.username:contains("admin")' + ), + '"autocomplete" popup has an entry for "admin"' + ); - click('.search-advanced-options .autocomplete ul li a:first'); + click(".search-advanced-options .autocomplete ul li a:first"); andThen(() => { - assert.ok(exists('.search-advanced-options span:contains("admin")'), 'has "admin" pre-populated'); - assert.equal(find('.search-query').val(), "none @admin", 'has updated search term to "none user:admin"'); + assert.ok( + exists('.search-advanced-options span:contains("admin")'), + 'has "admin" pre-populated' + ); + assert.equal( + find(".search-query").val(), + "none @admin", + 'has updated search term to "none user:admin"' + ); }); }); }); }); QUnit.test("update category through advanced search ui", assert => { - const categoryChooser = selectKit('.search-advanced-options .category-chooser'); + const categoryChooser = selectKit( + ".search-advanced-options .category-chooser" + ); visit("/search"); - fillIn('.search-query', 'none'); - categoryChooser.expand().fillInFilter('faq').selectRowByValue(4); + fillIn(".search-query", "none"); + categoryChooser + .expand() + .fillInFilter("faq") + .selectRowByValue(4); andThen(() => { - assert.ok(exists('.search-advanced-options .badge-category:contains("faq")'), 'has "faq" populated'); - assert.equal(find('.search-query').val(), "none #faq", 'has updated search term to "none #faq"'); + assert.ok( + exists('.search-advanced-options .badge-category:contains("faq")'), + 'has "faq" populated' + ); + assert.equal( + find(".search-query").val(), + "none #faq", + 'has updated search term to "none #faq"' + ); }); }); @@ -176,109 +259,181 @@ QUnit.test("update category through advanced search ui", assert => { // }); // }); // -QUnit.test("update in:title filter through advanced search ui", async assert => { - await visit("/search"); - await fillIn('.search-query', 'none'); - await click('.search-advanced-options .in-title'); +QUnit.test( + "update in:title filter through advanced search ui", + async assert => { + await visit("/search"); + await fillIn(".search-query", "none"); + await click(".search-advanced-options .in-title"); - assert.ok(exists('.search-advanced-options .in-title:checked'), 'has "in title" populated'); - assert.equal(find('.search-query').val(), "none in:title", 'has updated search term to "none in:title"'); -}); + assert.ok( + exists(".search-advanced-options .in-title:checked"), + 'has "in title" populated' + ); + assert.equal( + find(".search-query").val(), + "none in:title", + 'has updated search term to "none in:title"' + ); + } +); -QUnit.test("update in:likes filter through advanced search ui", async assert => { - await visit("/search"); - await fillIn('.search-query', 'none'); - await click('.search-advanced-options .in-likes'); +QUnit.test( + "update in:likes filter through advanced search ui", + async assert => { + await visit("/search"); + await fillIn(".search-query", "none"); + await click(".search-advanced-options .in-likes"); - assert.ok(exists('.search-advanced-options .in-likes:checked'), 'has "I liked" populated'); - assert.equal(find('.search-query').val(), "none in:likes", 'has updated search term to "none in:likes"'); -}); + assert.ok( + exists(".search-advanced-options .in-likes:checked"), + 'has "I liked" populated' + ); + assert.equal( + find(".search-query").val(), + "none in:likes", + 'has updated search term to "none in:likes"' + ); + } +); QUnit.test("update in:private filter through advanced search ui", assert => { visit("/search"); - fillIn('.search-query', 'none'); - click('.search-advanced-options .in-private'); + fillIn(".search-query", "none"); + click(".search-advanced-options .in-private"); andThen(() => { - assert.ok(exists('.search-advanced-options .in-private:checked'), 'has "are in my messages" populated'); - assert.equal(find('.search-query').val(), "none in:private", 'has updated search term to "none in:private"'); + assert.ok( + exists(".search-advanced-options .in-private:checked"), + 'has "are in my messages" populated' + ); + assert.equal( + find(".search-query").val(), + "none in:private", + 'has updated search term to "none in:private"' + ); }); }); QUnit.test("update in:seen filter through advanced search ui", assert => { visit("/search"); - fillIn('.search-query', 'none'); - click('.search-advanced-options .in-seen'); + fillIn(".search-query", "none"); + click(".search-advanced-options .in-seen"); andThen(() => { - assert.ok(exists('.search-advanced-options .in-seen:checked'), 'it should check the right checkbox'); + assert.ok( + exists(".search-advanced-options .in-seen:checked"), + "it should check the right checkbox" + ); - assert.equal(find('.search-query').val(), "none in:seen", - 'it should update the search term' + assert.equal( + find(".search-query").val(), + "none in:seen", + "it should update the search term" ); }); }); QUnit.test("update in filter through advanced search ui", assert => { - const inSelector = selectKit('.search-advanced-options .select-kit#in'); + const inSelector = selectKit(".search-advanced-options .select-kit#in"); visit("/search"); - fillIn('.search-query', 'none'); - inSelector.expand().selectRowByValue('bookmarks'); + fillIn(".search-query", "none"); + inSelector.expand().selectRowByValue("bookmarks"); andThen(() => { - assert.ok(inSelector.rowByName("I bookmarked").exists(), 'has "I bookmarked" populated'); - assert.equal(find('.search-query').val(), "none in:bookmarks", 'has updated search term to "none in:bookmarks"'); + assert.ok( + inSelector.rowByName("I bookmarked").exists(), + 'has "I bookmarked" populated' + ); + assert.equal( + find(".search-query").val(), + "none in:bookmarks", + 'has updated search term to "none in:bookmarks"' + ); }); }); QUnit.test("update status through advanced search ui", assert => { - const statusSelector = selectKit('.search-advanced-options .select-kit#status'); + const statusSelector = selectKit( + ".search-advanced-options .select-kit#status" + ); visit("/search"); - fillIn('.search-query', 'none'); - statusSelector.expand().selectRowByValue('closed'); + fillIn(".search-query", "none"); + statusSelector.expand().selectRowByValue("closed"); andThen(() => { - assert.ok(statusSelector.rowByName("are closed").exists(), 'has "are closed" populated'); - assert.equal(find('.search-query').val(), "none status:closed", 'has updated search term to "none status:closed"'); + assert.ok( + statusSelector.rowByName("are closed").exists(), + 'has "are closed" populated' + ); + assert.equal( + find(".search-query").val(), + "none status:closed", + 'has updated search term to "none status:closed"' + ); }); }); QUnit.test("update post time through advanced search ui", assert => { - const postTimeSelector = selectKit('.search-advanced-options .select-kit#postTime'); + const postTimeSelector = selectKit( + ".search-advanced-options .select-kit#postTime" + ); visit("/search"); - fillIn('.search-query', 'none'); - fillIn('#search-post-date .date-picker', '2016-10-05'); - postTimeSelector.expand().selectRowByValue('after'); + fillIn(".search-query", "none"); + fillIn("#search-post-date .date-picker", "2016-10-05"); + postTimeSelector.expand().selectRowByValue("after"); andThen(() => { - assert.ok(postTimeSelector.rowByName("after").exists(), 'has "after" populated'); - assert.equal(find('.search-query').val(), "none after:2016-10-05", 'has updated search term to "none after:2016-10-05"'); + assert.ok( + postTimeSelector.rowByName("after").exists(), + 'has "after" populated' + ); + assert.equal( + find(".search-query").val(), + "none after:2016-10-05", + 'has updated search term to "none after:2016-10-05"' + ); }); }); QUnit.test("update min post count through advanced search ui", assert => { visit("/search"); - fillIn('.search-query', 'none'); - fillIn('#search-min-post-count', '5'); + fillIn(".search-query", "none"); + fillIn("#search-min-post-count", "5"); andThen(() => { - assert.equal(find('.search-advanced-options #search-min-post-count').val(), "5", 'has "5" populated'); - assert.equal(find('.search-query').val(), "none min_post_count:5", 'has updated search term to "none min_post_count:5"'); + assert.equal( + find(".search-advanced-options #search-min-post-count").val(), + "5", + 'has "5" populated' + ); + assert.equal( + find(".search-query").val(), + "none min_post_count:5", + 'has updated search term to "none min_post_count:5"' + ); }); }); QUnit.test("validate advanced search when initially empty", assert => { visit("/search?expanded=true"); - click('.search-advanced-options .in-likes'); + click(".search-advanced-options .in-likes"); andThen(() => { - assert.ok(exists('.search-advanced-options .in-likes:checked'), 'has "I liked" populated'); - assert.equal(find('.search-query').val(), "in:likes", 'has updated search term to "in:likes"'); + assert.ok( + exists(".search-advanced-options .in-likes:checked"), + 'has "I liked" populated' + ); + assert.equal( + find(".search-query").val(), + "in:likes", + 'has updated search term to "in:likes"' + ); }); }); diff --git a/test/javascripts/acceptance/search-test.js.es6 b/test/javascripts/acceptance/search-test.js.es6 index d388d67787d..b8d463977d5 100644 --- a/test/javascripts/acceptance/search-test.js.es6 +++ b/test/javascripts/acceptance/search-test.js.es6 @@ -1,124 +1,140 @@ import { acceptance, logIn } from "helpers/qunit-helpers"; acceptance("Search"); -QUnit.test("search", (assert) => { +QUnit.test("search", assert => { visit("/"); - click('#search-button'); + click("#search-button"); andThen(() => { - assert.ok(exists('#search-term'), 'it shows the search bar'); - assert.ok(!exists('.search-menu .results ul li'), 'no results by default'); + assert.ok(exists("#search-term"), "it shows the search bar"); + assert.ok(!exists(".search-menu .results ul li"), "no results by default"); }); - fillIn('#search-term', 'dev'); - keyEvent('#search-term', 'keyup', 16); + fillIn("#search-term", "dev"); + keyEvent("#search-term", "keyup", 16); andThen(() => { - assert.ok(exists('.search-menu .results ul li'), 'it shows results'); + assert.ok(exists(".search-menu .results ul li"), "it shows results"); }); - click('.show-help'); + click(".show-help"); andThen(() => { - assert.equal(find('.full-page-search').val(), 'dev', 'it shows the search term'); - assert.ok(exists('.search-advanced-options'), 'advanced search is expanded'); + assert.equal( + find(".full-page-search").val(), + "dev", + "it shows the search term" + ); + assert.ok( + exists(".search-advanced-options"), + "advanced search is expanded" + ); }); }); -QUnit.test("search for a tag", (assert) => { +QUnit.test("search for a tag", assert => { visit("/"); - click('#search-button'); + click("#search-button"); - fillIn('#search-term', 'evil'); - keyEvent('#search-term', 'keyup', 16); + fillIn("#search-term", "evil"); + keyEvent("#search-term", "keyup", 16); andThen(() => { - assert.ok(exists('.search-menu .results ul li'), 'it shows results'); + assert.ok(exists(".search-menu .results ul li"), "it shows results"); }); }); QUnit.test("search scope checkbox", assert => { visit("/c/bug"); - click('#search-button'); + click("#search-button"); andThen(() => { - assert.ok(exists('.search-context input:checked'), 'scope to category checkbox is checked'); + assert.ok( + exists(".search-context input:checked"), + "scope to category checkbox is checked" + ); }); - click('#search-button'); + click("#search-button"); visit("/t/internationalization-localization/280"); - click('#search-button'); + click("#search-button"); andThen(() => { - assert.not(exists('.search-context input:checked'), 'scope to topic checkbox is not checked'); + assert.not( + exists(".search-context input:checked"), + "scope to topic checkbox is not checked" + ); }); - click('#search-button'); + click("#search-button"); visit("/u/eviltrout"); - click('#search-button'); + click("#search-button"); andThen(() => { - assert.ok(exists('.search-context input:checked'), 'scope to user checkbox is checked'); + assert.ok( + exists(".search-context input:checked"), + "scope to user checkbox is checked" + ); }); }); QUnit.test("Search with context", assert => { visit("/t/internationalization-localization/280/1"); - click('#search-button'); - fillIn('#search-term', 'dev'); + click("#search-button"); + fillIn("#search-term", "dev"); click(".search-context input[type='checkbox']"); - keyEvent('#search-term', 'keyup', 16); + keyEvent("#search-term", "keyup", 16); andThen(() => { - assert.ok(exists('.search-menu .results ul li'), 'it shows results'); + assert.ok(exists(".search-menu .results ul li"), "it shows results"); assert.ok( - exists('.cooked span.highlight-strong'), - 'it should highlight the search term' + exists(".cooked span.highlight-strong"), + "it should highlight the search term" ); }); visit("/"); - click('#search-button'); + click("#search-button"); andThen(() => { assert.ok(!exists(".search-context input[type='checkbox']")); }); visit("/t/internationalization-localization/280/1"); - click('#search-button'); + click("#search-button"); andThen(() => { - assert.ok(!$('.search-context input[type=checkbox]').is(":checked")); + assert.ok(!$(".search-context input[type=checkbox]").is(":checked")); }); }); QUnit.test("Right filters are shown to anonymous users", assert => { - const inSelector = selectKit('.select-kit#in'); + const inSelector = selectKit(".select-kit#in"); visit("/search?expanded=true"); inSelector.expand(); andThen(() => { - assert.ok(inSelector.rowByValue('first').exists()); - assert.ok(inSelector.rowByValue('pinned').exists()); - assert.ok(inSelector.rowByValue('unpinned').exists()); - assert.ok(inSelector.rowByValue('wiki').exists()); - assert.ok(inSelector.rowByValue('images').exists()); + assert.ok(inSelector.rowByValue("first").exists()); + assert.ok(inSelector.rowByValue("pinned").exists()); + assert.ok(inSelector.rowByValue("unpinned").exists()); + assert.ok(inSelector.rowByValue("wiki").exists()); + assert.ok(inSelector.rowByValue("images").exists()); - assert.notOk(inSelector.rowByValue('unseen').exists()); - assert.notOk(inSelector.rowByValue('posted').exists()); - assert.notOk(inSelector.rowByValue('watching').exists()); - assert.notOk(inSelector.rowByValue('tracking').exists()); - assert.notOk(inSelector.rowByValue('bookmarks').exists()); + assert.notOk(inSelector.rowByValue("unseen").exists()); + assert.notOk(inSelector.rowByValue("posted").exists()); + assert.notOk(inSelector.rowByValue("watching").exists()); + assert.notOk(inSelector.rowByValue("tracking").exists()); + assert.notOk(inSelector.rowByValue("bookmarks").exists()); - assert.notOk(exists('.search-advanced-options .in-likes')); - assert.notOk(exists('.search-advanced-options .in-private')); - assert.notOk(exists('.search-advanced-options .in-seen')); + assert.notOk(exists(".search-advanced-options .in-likes")); + assert.notOk(exists(".search-advanced-options .in-private")); + assert.notOk(exists(".search-advanced-options .in-seen")); }); }); QUnit.test("Right filters are shown to logged-in users", assert => { - const inSelector = selectKit('.select-kit#in'); + const inSelector = selectKit(".select-kit#in"); logIn(); Discourse.reset(); @@ -127,20 +143,20 @@ QUnit.test("Right filters are shown to logged-in users", assert => { inSelector.expand(); andThen(() => { - assert.ok(inSelector.rowByValue('first').exists()); - assert.ok(inSelector.rowByValue('pinned').exists()); - assert.ok(inSelector.rowByValue('unpinned').exists()); - assert.ok(inSelector.rowByValue('wiki').exists()); - assert.ok(inSelector.rowByValue('images').exists()); + assert.ok(inSelector.rowByValue("first").exists()); + assert.ok(inSelector.rowByValue("pinned").exists()); + assert.ok(inSelector.rowByValue("unpinned").exists()); + assert.ok(inSelector.rowByValue("wiki").exists()); + assert.ok(inSelector.rowByValue("images").exists()); - assert.ok(inSelector.rowByValue('unseen').exists()); - assert.ok(inSelector.rowByValue('posted').exists()); - assert.ok(inSelector.rowByValue('watching').exists()); - assert.ok(inSelector.rowByValue('tracking').exists()); - assert.ok(inSelector.rowByValue('bookmarks').exists()); + assert.ok(inSelector.rowByValue("unseen").exists()); + assert.ok(inSelector.rowByValue("posted").exists()); + assert.ok(inSelector.rowByValue("watching").exists()); + assert.ok(inSelector.rowByValue("tracking").exists()); + assert.ok(inSelector.rowByValue("bookmarks").exists()); - assert.ok(exists('.search-advanced-options .in-likes')); - assert.ok(exists('.search-advanced-options .in-private')); - assert.ok(exists('.search-advanced-options .in-seen')); + assert.ok(exists(".search-advanced-options .in-likes")); + assert.ok(exists(".search-advanced-options .in-private")); + assert.ok(exists(".search-advanced-options .in-seen")); }); }); diff --git a/test/javascripts/acceptance/shared-drafts-test.js.es6 b/test/javascripts/acceptance/shared-drafts-test.js.es6 index 6aea6ec38be..28f2ce0ca37 100644 --- a/test/javascripts/acceptance/shared-drafts-test.js.es6 +++ b/test/javascripts/acceptance/shared-drafts-test.js.es6 @@ -2,18 +2,18 @@ import { acceptance } from "helpers/qunit-helpers"; acceptance("Shared Drafts", { loggedIn: true }); -QUnit.test('Viewing', assert => { +QUnit.test("Viewing", assert => { visit("/t/some-topic/9"); andThen(() => { - assert.ok(find('.shared-draft-controls').length === 1); - let categoryChooser = selectKit('.shared-draft-controls .category-chooser'); - assert.equal(categoryChooser.header().value(), '3'); + assert.ok(find(".shared-draft-controls").length === 1); + let categoryChooser = selectKit(".shared-draft-controls .category-chooser"); + assert.equal(categoryChooser.header().value(), "3"); }); - click('.publish-shared-draft'); - click('.bootbox .btn-primary'); + click(".publish-shared-draft"); + click(".bootbox .btn-primary"); andThen(() => { - assert.ok(find('.shared-draft-controls').length === 0); + assert.ok(find(".shared-draft-controls").length === 0); }); }); diff --git a/test/javascripts/acceptance/sign-in-test.js.es6 b/test/javascripts/acceptance/sign-in-test.js.es6 index 1320be56ee2..0a075db20f0 100644 --- a/test/javascripts/acceptance/sign-in-test.js.es6 +++ b/test/javascripts/acceptance/sign-in-test.js.es6 @@ -5,23 +5,29 @@ QUnit.test("sign in", assert => { visit("/"); click("header .login-button"); andThen(() => { - assert.ok(exists('.login-modal'), "it shows the login modal"); + assert.ok(exists(".login-modal"), "it shows the login modal"); }); // Test invalid password first - fillIn('#login-account-name', 'eviltrout'); - fillIn('#login-account-password', 'incorrect'); - click('.modal-footer .btn-primary'); + fillIn("#login-account-name", "eviltrout"); + fillIn("#login-account-password", "incorrect"); + click(".modal-footer .btn-primary"); andThen(() => { - assert.ok(exists('#modal-alert:visible'), 'it displays the login error'); - assert.not(exists('.modal-footer .btn-primary:disabled'), "enables the login button"); + assert.ok(exists("#modal-alert:visible"), "it displays the login error"); + assert.not( + exists(".modal-footer .btn-primary:disabled"), + "enables the login button" + ); }); // Use the correct password - fillIn('#login-account-password', 'correct'); - click('.modal-footer .btn-primary'); + fillIn("#login-account-password", "correct"); + click(".modal-footer .btn-primary"); andThen(() => { - assert.ok(exists('.modal-footer .btn-primary:disabled'), "disables the login button"); + assert.ok( + exists(".modal-footer .btn-primary:disabled"), + "disables the login button" + ); }); }); @@ -30,21 +36,27 @@ QUnit.test("sign in - not activated", assert => { andThen(() => { click("header .login-button"); andThen(() => { - assert.ok(exists('.login-modal'), "it shows the login modal"); + assert.ok(exists(".login-modal"), "it shows the login modal"); }); - fillIn('#login-account-name', 'eviltrout'); - fillIn('#login-account-password', 'not-activated'); - click('.modal-footer .btn-primary'); + fillIn("#login-account-name", "eviltrout"); + fillIn("#login-account-password", "not-activated"); + click(".modal-footer .btn-primary"); andThen(() => { - assert.equal(find('.modal-body b').text(), 'eviltrout@example.com'); - assert.ok(!exists('.modal-body small'), 'it escapes the email address'); + assert.equal( + find(".modal-body b").text(), + "eviltrout@example.com" + ); + assert.ok(!exists(".modal-body small"), "it escapes the email address"); }); - click('.modal-footer button.resend'); + click(".modal-footer button.resend"); andThen(() => { - assert.equal(find('.modal-body b').text(), 'current@example.com'); - assert.ok(!exists('.modal-body small'), 'it escapes the email address'); + assert.equal( + find(".modal-body b").text(), + "current@example.com" + ); + assert.ok(!exists(".modal-body small"), "it escapes the email address"); }); }); }); @@ -54,24 +66,28 @@ QUnit.test("sign in - not activated - edit email", assert => { andThen(() => { click("header .login-button"); andThen(() => { - assert.ok(exists('.login-modal'), "it shows the login modal"); + assert.ok(exists(".login-modal"), "it shows the login modal"); }); - fillIn('#login-account-name', 'eviltrout'); - fillIn('#login-account-password', 'not-activated-edit'); - click('.modal-footer .btn-primary'); - click('.modal-footer button.edit-email'); + fillIn("#login-account-name", "eviltrout"); + fillIn("#login-account-password", "not-activated-edit"); + click(".modal-footer .btn-primary"); + click(".modal-footer button.edit-email"); andThen(() => { - assert.equal(find('.activate-new-email').val(), 'current@example.com'); - assert.equal(find('.modal-footer .btn-primary:disabled').length, 1, "must change email"); + assert.equal(find(".activate-new-email").val(), "current@example.com"); + assert.equal( + find(".modal-footer .btn-primary:disabled").length, + 1, + "must change email" + ); }); - fillIn('.activate-new-email', 'different@example.com'); + fillIn(".activate-new-email", "different@example.com"); andThen(() => { - assert.equal(find('.modal-footer .btn-primary:disabled').length, 0); + assert.equal(find(".modal-footer .btn-primary:disabled").length, 0); }); click(".modal-footer .btn-primary"); andThen(() => { - assert.equal(find('.modal-body b').text(), 'different@example.com'); + assert.equal(find(".modal-body b").text(), "different@example.com"); }); }); }); @@ -81,25 +97,37 @@ QUnit.test("second factor", assert => { click("header .login-button"); andThen(() => { - assert.ok(exists('.login-modal'), "it shows the login modal"); + assert.ok(exists(".login-modal"), "it shows the login modal"); }); - fillIn('#login-account-name', 'eviltrout'); - fillIn('#login-account-password', 'need-second-factor'); - click('.modal-footer .btn-primary'); + fillIn("#login-account-name", "eviltrout"); + fillIn("#login-account-password", "need-second-factor"); + click(".modal-footer .btn-primary"); andThen(() => { - assert.not(exists('#modal-alert:visible'), 'it hides the login error'); - assert.not(exists('#credentials:visible'), 'it hides the username and password prompt'); - assert.ok(exists('#second-factor:visible'), 'it displays the second factor prompt'); - assert.not(exists('.modal-footer .btn-primary:disabled'), "enables the login button"); + assert.not(exists("#modal-alert:visible"), "it hides the login error"); + assert.not( + exists("#credentials:visible"), + "it hides the username and password prompt" + ); + assert.ok( + exists("#second-factor:visible"), + "it displays the second factor prompt" + ); + assert.not( + exists(".modal-footer .btn-primary:disabled"), + "enables the login button" + ); }); - fillIn('#login-second-factor', '123456'); - click('.modal-footer .btn-primary'); + fillIn("#login-second-factor", "123456"); + click(".modal-footer .btn-primary"); andThen(() => { - assert.ok(exists('.modal-footer .btn-primary:disabled'), "disables the login button"); + assert.ok( + exists(".modal-footer .btn-primary:disabled"), + "disables the login button" + ); }); }); @@ -108,29 +136,47 @@ QUnit.test("create account", assert => { click("header .sign-up-button"); andThen(() => { - assert.ok(exists('.create-account'), "it shows the create account modal"); - assert.ok(exists('.modal-footer .btn-primary:disabled'), 'create account is disabled at first'); + assert.ok(exists(".create-account"), "it shows the create account modal"); + assert.ok( + exists(".modal-footer .btn-primary:disabled"), + "create account is disabled at first" + ); }); - fillIn('#new-account-name', 'Dr. Good Tuna'); - fillIn('#new-account-password', 'cool password bro'); + fillIn("#new-account-name", "Dr. Good Tuna"); + fillIn("#new-account-password", "cool password bro"); // Check username - fillIn('#new-account-email', 'good.tuna@test.com'); - fillIn('#new-account-username', 'taken'); + fillIn("#new-account-email", "good.tuna@test.com"); + fillIn("#new-account-username", "taken"); andThen(() => { - assert.ok(exists('#username-validation.bad'), 'the username validation is bad'); - assert.ok(exists('.modal-footer .btn-primary:disabled'), 'create account is still disabled'); + assert.ok( + exists("#username-validation.bad"), + "the username validation is bad" + ); + assert.ok( + exists(".modal-footer .btn-primary:disabled"), + "create account is still disabled" + ); }); - fillIn('#new-account-username', 'goodtuna'); + fillIn("#new-account-username", "goodtuna"); andThen(() => { - assert.ok(exists('#username-validation.good'), 'the username validation is good'); - assert.not(exists('.modal-footer .btn-primary:disabled'), 'create account is enabled'); + assert.ok( + exists("#username-validation.good"), + "the username validation is good" + ); + assert.not( + exists(".modal-footer .btn-primary:disabled"), + "create account is enabled" + ); }); - click('.modal-footer .btn-primary'); + click(".modal-footer .btn-primary"); andThen(() => { - assert.ok(exists('.modal-footer .btn-primary:disabled'), "create account is disabled"); + assert.ok( + exists(".modal-footer .btn-primary:disabled"), + "create account is disabled" + ); }); }); diff --git a/test/javascripts/acceptance/static-test.js.es6 b/test/javascripts/acceptance/static-test.js.es6 index 97592dbd4a9..47fa81dd5ec 100644 --- a/test/javascripts/acceptance/static-test.js.es6 +++ b/test/javascripts/acceptance/static-test.js.es6 @@ -4,30 +4,34 @@ acceptance("Static"); QUnit.test("Static Pages", assert => { visit("/faq"); andThen(() => { - assert.ok($('body.static-faq').length, "has the body class"); + assert.ok($("body.static-faq").length, "has the body class"); assert.ok(exists(".body-page"), "The content is present"); }); visit("/guidelines"); andThen(() => { - assert.ok($('body.static-guidelines').length, "has the body class"); + assert.ok($("body.static-guidelines").length, "has the body class"); assert.ok(exists(".body-page"), "The content is present"); }); visit("/tos"); andThen(() => { - assert.ok($('body.static-tos').length, "has the body class"); + assert.ok($("body.static-tos").length, "has the body class"); assert.ok(exists(".body-page"), "The content is present"); }); visit("/privacy"); andThen(() => { - assert.ok($('body.static-privacy').length, "has the body class"); + assert.ok($("body.static-privacy").length, "has the body class"); assert.ok(exists(".body-page"), "The content is present"); }); visit("/login"); andThen(() => { - assert.equal(currentPath(), "discovery.latest", "it redirects them to latest unless `login_required`"); + assert.equal( + currentPath(), + "discovery.latest", + "it redirects them to latest unless `login_required`" + ); }); -}); \ No newline at end of file +}); diff --git a/test/javascripts/acceptance/tag-hashtag-test.js.es6 b/test/javascripts/acceptance/tag-hashtag-test.js.es6 index 559b9ff9269..e71e7251754 100644 --- a/test/javascripts/acceptance/tag-hashtag-test.js.es6 +++ b/test/javascripts/acceptance/tag-hashtag-test.js.es6 @@ -4,40 +4,49 @@ acceptance("Tag Hashtag", { loggedIn: true, settings: { tagging_enabled: true }, beforeEach() { - const response = (object) => { - return [ - 200, - {"Content-Type": "application/json"}, - object - ]; + const response = object => { + return [200, { "Content-Type": "application/json" }, object]; }; - server.get('/tags/filter/search', () => { //eslint-disable-line - return response({ results: [{ text: 'monkey', count: 1 }] }); + server.get("/tags/filter/search", () => { + //eslint-disable-line + return response({ results: [{ text: "monkey", count: 1 }] }); }); - server.get('/category_hashtags/check', () => { //eslint-disable-line + server.get("/category_hashtags/check", () => { + //eslint-disable-line return response({ valid: [] }); }); - server.get('/tags/check', () => { //eslint-disable-line - return response({ valid: [{ value: 'monkey', url: '/tags/monkey' }] }); + server.get("/tags/check", () => { + //eslint-disable-line + return response({ valid: [{ value: "monkey", url: "/tags/monkey" }] }); }); } }); QUnit.test("tag is cooked properly", assert => { visit("/t/internationalization-localization/280"); - click('#topic-footer-buttons .btn.create'); + click("#topic-footer-buttons .btn.create"); - fillIn('.d-editor-input', "this is a tag hashtag #monkey::tag"); + fillIn(".d-editor-input", "this is a tag hashtag #monkey::tag"); andThen(() => { // TODO: Test that the autocomplete shows - assert.equal(find('.d-editor-preview:visible').html().trim(), "

this is a tag hashtag #monkey

"); + assert.equal( + find(".d-editor-preview:visible") + .html() + .trim(), + '

this is a tag hashtag #monkey

' + ); }); - click('#reply-control .btn.create'); + click("#reply-control .btn.create"); andThen(() => { - assert.equal(find('.topic-post:last .cooked').html().trim(), "

this is a tag hashtag #monkey

"); + assert.equal( + find(".topic-post:last .cooked") + .html() + .trim(), + '

this is a tag hashtag #monkey

' + ); }); -}); \ No newline at end of file +}); diff --git a/test/javascripts/acceptance/tags-test.js.es6 b/test/javascripts/acceptance/tags-test.js.es6 index 8971f83ee33..ebaf1218eec 100644 --- a/test/javascripts/acceptance/tags-test.js.es6 +++ b/test/javascripts/acceptance/tags-test.js.es6 @@ -5,8 +5,8 @@ QUnit.test("list the tags", assert => { visit("/tags"); andThen(() => { - assert.ok($('body.tags-page').length, "has the body class"); - assert.ok(exists('.tag-eviltrout'), "shows the evil trout tag"); + assert.ok($("body.tags-page").length, "has the body class"); + assert.ok(exists(".tag-eviltrout"), "shows the evil trout tag"); }); }); @@ -17,48 +17,70 @@ acceptance("Tags listed by group", { } }); - QUnit.test("list the tags in groups", assert => { - server.get('/tags', () => { // eslint-disable-line no-undef + server.get("/tags", () => { + // eslint-disable-line no-undef return [ 200, { "Content-Type": "application/json" }, { - "tags":[{id: 'planned', text: 'planned', count: 7, pm_count: 0}], - "extras": { "tag_groups": [ - {id: 2, name: "Ford Cars", tags: [ - {id: 'escort', text: 'escort', count: 1, pm_count: 0}, - {id: 'focus', text: 'focus', count: 3, pm_count: 0} - ]}, - {id: 1, name: "Honda Cars", tags: [ - {id: 'civic', text: 'civic', count: 4, pm_count: 0}, - {id: 'accord', text: 'accord', count: 2, pm_count: 0} - ]}, - {id: 1, name: "Makes", tags: [ - {id: 'ford', text: 'ford', count: 5, pm_count: 0}, - {id: 'honda', text: 'honda', count: 6, pm_count: 0} - ]} - ]} + tags: [{ id: "planned", text: "planned", count: 7, pm_count: 0 }], + extras: { + tag_groups: [ + { + id: 2, + name: "Ford Cars", + tags: [ + { id: "escort", text: "escort", count: 1, pm_count: 0 }, + { id: "focus", text: "focus", count: 3, pm_count: 0 } + ] + }, + { + id: 1, + name: "Honda Cars", + tags: [ + { id: "civic", text: "civic", count: 4, pm_count: 0 }, + { id: "accord", text: "accord", count: 2, pm_count: 0 } + ] + }, + { + id: 1, + name: "Makes", + tags: [ + { id: "ford", text: "ford", count: 5, pm_count: 0 }, + { id: "honda", text: "honda", count: 6, pm_count: 0 } + ] + } + ] + } } ]; }); - visit('/tags'); + visit("/tags"); andThen(() => { - assert.equal($('.tag-list').length, 4, "shows separate lists for the 3 groups and the ungrouped tags"); - assert.ok( - _.isEqual( - _.map($('.tag-list h3'), i => { return $(i).text(); } ), - ['Ford Cars', 'Honda Cars', 'Makes', 'Other Tags'] - ), - 'shown in given order and with tags that are not in a group' + assert.equal( + $(".tag-list").length, + 4, + "shows separate lists for the 3 groups and the ungrouped tags" ); assert.ok( _.isEqual( - _.map($('.tag-list:first .discourse-tag'), i => { return $(i).text(); }), - ['focus', 'escort'] + _.map($(".tag-list h3"), i => { + return $(i).text(); + }), + ["Ford Cars", "Honda Cars", "Makes", "Other Tags"] ), - 'shows the tags in default sort (by count)' + "shown in given order and with tags that are not in a group" + ); + assert.ok( + _.isEqual( + _.map($(".tag-list:first .discourse-tag"), i => { + return $(i).text(); + }), + ["focus", "escort"] + ), + "shows the tags in default sort (by count)" ); }); }); diff --git a/test/javascripts/acceptance/topic-anonymous-test.js.es6 b/test/javascripts/acceptance/topic-anonymous-test.js.es6 index c0eff198659..1ec1ed32f57 100644 --- a/test/javascripts/acceptance/topic-anonymous-test.js.es6 +++ b/test/javascripts/acceptance/topic-anonymous-test.js.es6 @@ -6,7 +6,10 @@ QUnit.test("Enter a Topic", assert => { andThen(() => { assert.ok(exists("#topic"), "The topic was rendered"); assert.ok(exists("#topic .cooked"), "The topic has cooked posts"); - assert.ok(find('.shared-draft-notice').length === 0, "no shared draft unless there's a dest category id"); + assert.ok( + find(".shared-draft-notice").length === 0, + "no shared draft unless there's a dest category id" + ); }); }); @@ -21,7 +24,10 @@ QUnit.test("Enter a 404 topic", assert => { visit("/t/not-found/404"); andThen(() => { assert.ok(!exists("#topic"), "The topic was not rendered"); - assert.ok(find(".not-found").text() === "not found", "it renders the error message"); + assert.ok( + find(".not-found").text() === "not found", + "it renders the error message" + ); }); }); diff --git a/test/javascripts/acceptance/topic-discovery-test.js.es6 b/test/javascripts/acceptance/topic-discovery-test.js.es6 index 458c3471a03..e66382be969 100644 --- a/test/javascripts/acceptance/topic-discovery-test.js.es6 +++ b/test/javascripts/acceptance/topic-discovery-test.js.es6 @@ -4,43 +4,64 @@ acceptance("Topic Discovery"); QUnit.test("Visit Discovery Pages", assert => { visit("/"); andThen(() => { - assert.ok($('body.navigation-topics').length, "has the default navigation"); + assert.ok($("body.navigation-topics").length, "has the default navigation"); assert.ok(exists(".topic-list"), "The list of topics was rendered"); - assert.ok(exists('.topic-list .topic-list-item'), "has topics"); + assert.ok(exists(".topic-list .topic-list-item"), "has topics"); }); visit("/c/bug"); andThen(() => { assert.ok(exists(".topic-list"), "The list of topics was rendered"); - assert.ok(exists('.topic-list .topic-list-item'), "has topics"); - assert.ok(!exists('.category-list'), "doesn't render subcategories"); - assert.ok($('body.category-bug').length, "has a custom css class for the category id on the body"); + assert.ok(exists(".topic-list .topic-list-item"), "has topics"); + assert.ok(!exists(".category-list"), "doesn't render subcategories"); + assert.ok( + $("body.category-bug").length, + "has a custom css class for the category id on the body" + ); }); visit("/categories"); andThen(() => { - assert.ok($('body.navigation-categories').length, "has the body class"); - assert.ok($('body.category-bug').length === 0, "removes the custom category class"); - assert.ok(exists('.category'), "has a list of categories"); - assert.ok($('body.categories-list').length, "has a custom class to indicate categories"); + assert.ok($("body.navigation-categories").length, "has the body class"); + assert.ok( + $("body.category-bug").length === 0, + "removes the custom category class" + ); + assert.ok(exists(".category"), "has a list of categories"); + assert.ok( + $("body.categories-list").length, + "has a custom class to indicate categories" + ); }); visit("/top"); andThen(() => { - assert.ok($('body.categories-list').length === 0, "removes the `categories-list` class"); - assert.ok(exists('.topic-list .topic-list-item'), "has topics"); + assert.ok( + $("body.categories-list").length === 0, + "removes the `categories-list` class" + ); + assert.ok(exists(".topic-list .topic-list-item"), "has topics"); }); visit("/c/feature"); andThen(() => { assert.ok(exists(".topic-list"), "The list of topics was rendered"); - assert.ok(exists(".category-boxes"), "The list of subcategories were rendered with box style"); + assert.ok( + exists(".category-boxes"), + "The list of subcategories were rendered with box style" + ); }); visit("/c/dev"); andThen(() => { assert.ok(exists(".topic-list"), "The list of topics was rendered"); - assert.ok(exists(".category-boxes-with-topics"), "The list of subcategories were rendered with box-with-featured-topics style"); - assert.ok(exists(".category-boxes-with-topics .featured-topics"), "The featured topics are there too"); + assert.ok( + exists(".category-boxes-with-topics"), + "The list of subcategories were rendered with box-with-featured-topics style" + ); + assert.ok( + exists(".category-boxes-with-topics .featured-topics"), + "The featured topics are there too" + ); }); -}); \ No newline at end of file +}); diff --git a/test/javascripts/acceptance/topic-edit-timer-test.js.es6 b/test/javascripts/acceptance/topic-edit-timer-test.js.es6 index b3ed4df7440..f437f298840 100644 --- a/test/javascripts/acceptance/topic-edit-timer-test.js.es6 +++ b/test/javascripts/acceptance/topic-edit-timer-test.js.es6 @@ -1,163 +1,207 @@ -import { acceptance, replaceCurrentUser } from 'helpers/qunit-helpers'; -acceptance('Topic - Edit timer', { loggedIn: true }); +import { acceptance, replaceCurrentUser } from "helpers/qunit-helpers"; +acceptance("Topic - Edit timer", { loggedIn: true }); -QUnit.test('default', assert => { - const timerType = selectKit('.select-kit.timer-type'); - const futureDateInputSelector = selectKit('.future-date-input-selector'); +QUnit.test("default", assert => { + const timerType = selectKit(".select-kit.timer-type"); + const futureDateInputSelector = selectKit(".future-date-input-selector"); - visit('/t/internationalization-localization'); - click('.toggle-admin-menu'); - click('.topic-admin-status-update button'); + visit("/t/internationalization-localization"); + click(".toggle-admin-menu"); + click(".topic-admin-status-update button"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Select a timeframe'); + assert.equal( + futureDateInputSelector.header().title(), + "Select a timeframe" + ); assert.equal(futureDateInputSelector.header().value(), null); }); - click('#private-topic-timer'); + click("#private-topic-timer"); andThen(() => { - assert.equal(timerType.header().title(), 'Remind Me'); - assert.equal(timerType.header().value(), 'reminder'); + assert.equal(timerType.header().title(), "Remind Me"); + assert.equal(timerType.header().value(), "reminder"); - assert.equal(futureDateInputSelector.header().title(), 'Select a timeframe'); + assert.equal( + futureDateInputSelector.header().title(), + "Select a timeframe" + ); assert.equal(futureDateInputSelector.header().value(), null); }); }); -QUnit.test('autoclose - specific time', assert => { - const futureDateInputSelector = selectKit('.future-date-input-selector'); +QUnit.test("autoclose - specific time", assert => { + const futureDateInputSelector = selectKit(".future-date-input-selector"); - visit('/t/internationalization-localization'); - click('.toggle-admin-menu'); - click('.topic-admin-status-update button'); + visit("/t/internationalization-localization"); + click(".toggle-admin-menu"); + click(".topic-admin-status-update button"); - futureDateInputSelector.expand().selectRowByValue('next_week'); + futureDateInputSelector.expand().selectRowByValue("next_week"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Next week'); - assert.equal(futureDateInputSelector.header().value(), 'next_week'); + assert.equal(futureDateInputSelector.header().title(), "Next week"); + assert.equal(futureDateInputSelector.header().value(), "next_week"); const regex = /will automatically close in/g; - const html = find('.future-date-input .topic-status-info').html().trim(); + const html = find(".future-date-input .topic-status-info") + .html() + .trim(); assert.ok(regex.test(html)); }); }); -QUnit.test('autoclose', assert => { - const futureDateInputSelector = selectKit('.future-date-input-selector'); +QUnit.test("autoclose", assert => { + const futureDateInputSelector = selectKit(".future-date-input-selector"); - visit('/t/internationalization-localization'); - click('.toggle-admin-menu'); - click('.topic-admin-status-update button'); + visit("/t/internationalization-localization"); + click(".toggle-admin-menu"); + click(".topic-admin-status-update button"); - futureDateInputSelector.expand().selectRowByValue('next_week'); + futureDateInputSelector.expand().selectRowByValue("next_week"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Next week'); - assert.equal(futureDateInputSelector.header().value(), 'next_week'); + assert.equal(futureDateInputSelector.header().title(), "Next week"); + assert.equal(futureDateInputSelector.header().value(), "next_week"); const regex = /will automatically close in/g; - const html = find('.future-date-input .topic-status-info').html().trim(); + const html = find(".future-date-input .topic-status-info") + .html() + .trim(); assert.ok(regex.test(html)); }); - futureDateInputSelector.expand().selectRowByValue('pick_date_and_time'); + futureDateInputSelector.expand().selectRowByValue("pick_date_and_time"); - fillIn('.future-date-input .date-picker', '2099-11-24'); + fillIn(".future-date-input .date-picker", "2099-11-24"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Pick date and time'); - assert.equal(futureDateInputSelector.header().value(), 'pick_date_and_time'); + assert.equal( + futureDateInputSelector.header().title(), + "Pick date and time" + ); + assert.equal( + futureDateInputSelector.header().value(), + "pick_date_and_time" + ); const regex = /will automatically close in/g; - const html = find('.future-date-input .topic-status-info').html().trim(); + const html = find(".future-date-input .topic-status-info") + .html() + .trim(); assert.ok(regex.test(html)); }); - futureDateInputSelector.expand().selectRowByValue('set_based_on_last_post'); + futureDateInputSelector.expand().selectRowByValue("set_based_on_last_post"); - fillIn('.future-date-input input[type=number]', '2'); + fillIn(".future-date-input input[type=number]", "2"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Close based on last post'); - assert.equal(futureDateInputSelector.header().value(), 'set_based_on_last_post'); + assert.equal( + futureDateInputSelector.header().title(), + "Close based on last post" + ); + assert.equal( + futureDateInputSelector.header().value(), + "set_based_on_last_post" + ); const regex = /This topic will close.*after the last reply/g; - const html = find('.future-date-input .topic-status-info').html().trim(); + const html = find(".future-date-input .topic-status-info") + .html() + .trim(); assert.ok(regex.test(html)); }); }); -QUnit.test('close temporarily', assert => { - const timerType = selectKit('.select-kit.timer-type'); - const futureDateInputSelector = selectKit('.future-date-input-selector'); +QUnit.test("close temporarily", assert => { + const timerType = selectKit(".select-kit.timer-type"); + const futureDateInputSelector = selectKit(".future-date-input-selector"); - visit('/t/internationalization-localization'); - click('.toggle-admin-menu'); - click('.topic-admin-status-update button'); + visit("/t/internationalization-localization"); + click(".toggle-admin-menu"); + click(".topic-admin-status-update button"); - timerType.expand().selectRowByValue('open'); + timerType.expand().selectRowByValue("open"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Select a timeframe'); + assert.equal( + futureDateInputSelector.header().title(), + "Select a timeframe" + ); assert.equal(futureDateInputSelector.header().value(), null); }); - futureDateInputSelector.expand().selectRowByValue('next_week'); + futureDateInputSelector.expand().selectRowByValue("next_week"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Next week'); - assert.equal(futureDateInputSelector.header().value(), 'next_week'); + assert.equal(futureDateInputSelector.header().title(), "Next week"); + assert.equal(futureDateInputSelector.header().value(), "next_week"); const regex = /will automatically open in/g; - const html = find('.future-date-input .topic-status-info').html().trim(); + const html = find(".future-date-input .topic-status-info") + .html() + .trim(); assert.ok(regex.test(html)); }); - futureDateInputSelector.expand().selectRowByValue('pick_date_and_time'); + futureDateInputSelector.expand().selectRowByValue("pick_date_and_time"); - fillIn('.future-date-input .date-picker', '2099-11-24'); + fillIn(".future-date-input .date-picker", "2099-11-24"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Pick date and time'); - assert.equal(futureDateInputSelector.header().value(), 'pick_date_and_time'); + assert.equal( + futureDateInputSelector.header().title(), + "Pick date and time" + ); + assert.equal( + futureDateInputSelector.header().value(), + "pick_date_and_time" + ); const regex = /will automatically open in/g; - const html = find('.future-date-input .topic-status-info').html().trim(); + const html = find(".future-date-input .topic-status-info") + .html() + .trim(); assert.ok(regex.test(html)); }); }); -QUnit.test('schedule', assert => { - const timerType = selectKit('.select-kit.timer-type'); - const categoryChooser = selectKit('.modal-body .category-chooser'); - const futureDateInputSelector = selectKit('.future-date-input-selector'); +QUnit.test("schedule", assert => { + const timerType = selectKit(".select-kit.timer-type"); + const categoryChooser = selectKit(".modal-body .category-chooser"); + const futureDateInputSelector = selectKit(".future-date-input-selector"); - visit('/t/internationalization-localization'); - click('.toggle-admin-menu'); - click('.topic-admin-status-update button'); + visit("/t/internationalization-localization"); + click(".toggle-admin-menu"); + click(".topic-admin-status-update button"); - timerType.expand().selectRowByValue('publish_to_category'); + timerType.expand().selectRowByValue("publish_to_category"); andThen(() => { - assert.equal(categoryChooser.header().title(), 'uncategorized'); + assert.equal(categoryChooser.header().title(), "uncategorized"); assert.equal(categoryChooser.header().value(), null); - assert.equal(futureDateInputSelector.header().title(), 'Select a timeframe'); + assert.equal( + futureDateInputSelector.header().title(), + "Select a timeframe" + ); assert.equal(futureDateInputSelector.header().value(), null); }); - categoryChooser.expand().selectRowByValue('7'); + categoryChooser.expand().selectRowByValue("7"); - futureDateInputSelector.expand().selectRowByValue('next_week'); + futureDateInputSelector.expand().selectRowByValue("next_week"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Next week'); - assert.equal(futureDateInputSelector.header().value(), 'next_week'); + assert.equal(futureDateInputSelector.header().title(), "Next week"); + assert.equal(futureDateInputSelector.header().value(), "next_week"); const regex = /will be published to #dev/g; - const text = find('.future-date-input .topic-status-info').text().trim(); + const text = find(".future-date-input .topic-status-info") + .text() + .trim(); assert.ok(regex.test(text)); }); }); @@ -165,11 +209,11 @@ QUnit.test('schedule', assert => { QUnit.test("TL4 can't auto-delete", assert => { replaceCurrentUser({ staff: false, trust_level: 4 }); - visit('/t/internationalization-localization'); - click('.toggle-admin-menu'); - click('.topic-admin-status-update button'); + visit("/t/internationalization-localization"); + click(".toggle-admin-menu"); + click(".topic-admin-status-update button"); - const timerType = selectKit('.select-kit.timer-type'); + const timerType = selectKit(".select-kit.timer-type"); timerType.expand(); @@ -178,29 +222,34 @@ QUnit.test("TL4 can't auto-delete", assert => { }); }); -QUnit.test('auto delete', assert => { - const timerType = selectKit('.select-kit.timer-type'); - const futureDateInputSelector = selectKit('.future-date-input-selector'); +QUnit.test("auto delete", assert => { + const timerType = selectKit(".select-kit.timer-type"); + const futureDateInputSelector = selectKit(".future-date-input-selector"); - visit('/t/internationalization-localization'); - click('.toggle-admin-menu'); - click('.topic-admin-status-update button'); + visit("/t/internationalization-localization"); + click(".toggle-admin-menu"); + click(".topic-admin-status-update button"); - timerType.expand().selectRowByValue('delete'); + timerType.expand().selectRowByValue("delete"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Select a timeframe'); + assert.equal( + futureDateInputSelector.header().title(), + "Select a timeframe" + ); assert.equal(futureDateInputSelector.header().value(), null); }); - futureDateInputSelector.expand().selectRowByValue('two_weeks'); + futureDateInputSelector.expand().selectRowByValue("two_weeks"); andThen(() => { - assert.equal(futureDateInputSelector.header().title(), 'Two Weeks'); - assert.equal(futureDateInputSelector.header().value(), 'two_weeks'); + assert.equal(futureDateInputSelector.header().title(), "Two Weeks"); + assert.equal(futureDateInputSelector.header().value(), "two_weeks"); const regex = /will be automatically deleted/g; - const html = find('.future-date-input .topic-status-info').html().trim(); + const html = find(".future-date-input .topic-status-info") + .html() + .trim(); assert.ok(regex.test(html)); }); }); diff --git a/test/javascripts/acceptance/topic-notifications-button-test.js.es6 b/test/javascripts/acceptance/topic-notifications-button-test.js.es6 index 4fe88775954..5e9e032ac36 100644 --- a/test/javascripts/acceptance/topic-notifications-button-test.js.es6 +++ b/test/javascripts/acceptance/topic-notifications-button-test.js.es6 @@ -3,21 +3,20 @@ acceptance("Topic Notifications button", { loggedIn: true, beforeEach() { const response = object => { - return [ - 200, - { "Content-Type": "application/json" }, - object - ]; + return [200, { "Content-Type": "application/json" }, object]; }; - server.post('/t/280/notifications', () => { // eslint-disable-line no-undef + server.post("/t/280/notifications", () => { + // eslint-disable-line no-undef return response({}); }); } }); QUnit.test("Updating topic notification level", assert => { - const notificationOptions = selectKit("#topic-footer-buttons .topic-notifications-options"); + const notificationOptions = selectKit( + "#topic-footer-buttons .topic-notifications-options" + ); visit("/t/internationalization-localization/280"); diff --git a/test/javascripts/acceptance/topic-test.js.es6 b/test/javascripts/acceptance/topic-test.js.es6 index b84afe844f7..926530f67e6 100644 --- a/test/javascripts/acceptance/topic-test.js.es6 +++ b/test/javascripts/acceptance/topic-test.js.es6 @@ -4,17 +4,17 @@ acceptance("Topic", { loggedIn: true }); QUnit.test("Share Popup", assert => { visit("/t/internationalization-localization/280"); andThen(() => { - assert.ok(!exists('#share-link.visible'), 'it is not visible'); + assert.ok(!exists("#share-link.visible"), "it is not visible"); }); click("button[data-share-url]"); andThen(() => { - assert.ok(exists('#share-link.visible'), 'it shows the popup'); + assert.ok(exists("#share-link.visible"), "it shows the popup"); }); - click('#share-link .close-share'); + click("#share-link .close-share"); andThen(() => { - assert.ok(!exists('#share-link.visible'), 'it closes the popup'); + assert.ok(!exists("#share-link.visible"), "it closes the popup"); }); // TODO tgxworld This fails on Travis but we need to push the security fix out @@ -33,57 +33,67 @@ QUnit.test("Share Popup", assert => { QUnit.test("Showing and hiding the edit controls", assert => { visit("/t/internationalization-localization/280"); - click('#topic-title .d-icon-pencil'); + click("#topic-title .d-icon-pencil"); andThen(() => { - assert.ok(exists('#edit-title'), 'it shows the editing controls'); - assert.ok(!exists('.title-wrapper .remove-featured-link'), 'link to remove featured link is not shown'); + assert.ok(exists("#edit-title"), "it shows the editing controls"); + assert.ok( + !exists(".title-wrapper .remove-featured-link"), + "link to remove featured link is not shown" + ); }); - fillIn('#edit-title', 'this is the new title'); - click('#topic-title .cancel-edit'); + fillIn("#edit-title", "this is the new title"); + click("#topic-title .cancel-edit"); andThen(() => { - assert.ok(!exists('#edit-title'), 'it hides the editing controls'); + assert.ok(!exists("#edit-title"), "it hides the editing controls"); }); }); QUnit.test("Updating the topic title and category", assert => { - const categoryChooser = selectKit('.title-wrapper .category-chooser'); + const categoryChooser = selectKit(".title-wrapper .category-chooser"); visit("/t/internationalization-localization/280"); - click('#topic-title .d-icon-pencil'); - fillIn('#edit-title', 'this is the new title'); + click("#topic-title .d-icon-pencil"); + fillIn("#edit-title", "this is the new title"); categoryChooser.expand().selectRowByValue(4); - click('#topic-title .submit-edit'); + click("#topic-title .submit-edit"); andThen(() => { - assert.equal(find('#topic-title .badge-category').text(), 'faq', 'it displays the new category'); - assert.equal(find('.fancy-title').text().trim(), 'this is the new title', 'it displays the new title'); + assert.equal( + find("#topic-title .badge-category").text(), + "faq", + "it displays the new category" + ); + assert.equal( + find(".fancy-title") + .text() + .trim(), + "this is the new title", + "it displays the new title" + ); }); }); QUnit.test("Marking a topic as wiki", assert => { - server.put('/posts/398/wiki', () => { // eslint-disable-line no-undef - return [ - 200, - { "Content-Type": "application/json" }, - {} - ]; + server.put("/posts/398/wiki", () => { + // eslint-disable-line no-undef + return [200, { "Content-Type": "application/json" }, {}]; }); visit("/t/internationalization-localization/280"); andThen(() => { - assert.ok(find('a.wiki').length === 0, 'it does not show the wiki icon'); + assert.ok(find("a.wiki").length === 0, "it does not show the wiki icon"); }); - click('.topic-post:eq(0) button.show-more-actions'); - click('.topic-post:eq(0) button.show-post-admin-menu'); - click('.btn.wiki'); + click(".topic-post:eq(0) button.show-more-actions"); + click(".topic-post:eq(0) button.show-post-admin-menu"); + click(".btn.wiki"); andThen(() => { - assert.ok(find('a.wiki').length === 1, 'it shows the wiki icon'); + assert.ok(find("a.wiki").length === 1, "it shows the wiki icon"); }); }); @@ -93,15 +103,22 @@ QUnit.test("Reply as new topic", assert => { click(".reply-as-new-topic a"); andThen(() => { - assert.ok(exists('.d-editor-input'), 'the composer input is visible'); + assert.ok(exists(".d-editor-input"), "the composer input is visible"); assert.equal( - find('.d-editor-input').val().trim(), - `Continuing the discussion from [Internationalization / localization](${window.location.origin}/t/internationalization-localization/280):`, + find(".d-editor-input") + .val() + .trim(), + `Continuing the discussion from [Internationalization / localization](${ + window.location.origin + }/t/internationalization-localization/280):`, "it fills composer with the ring string" ); assert.equal( - selectKit('.category-chooser').header().value(), "2", + selectKit(".category-chooser") + .header() + .value(), + "2", "it fills category selector with the right category" ); }); @@ -113,28 +130,35 @@ QUnit.test("Reply as new message", assert => { click(".reply-as-new-topic a"); andThen(() => { - assert.ok(exists('.d-editor-input'), 'the composer input is visible'); + assert.ok(exists(".d-editor-input"), "the composer input is visible"); assert.equal( - find('.d-editor-input').val().trim(), - `Continuing the discussion from [PM for testing](${window.location.origin}/t/pm-for-testing/12):`, + find(".d-editor-input") + .val() + .trim(), + `Continuing the discussion from [PM for testing](${ + window.location.origin + }/t/pm-for-testing/12):`, "it fills composer with the ring string" ); - const targets = find('.item span', '.composer-fields'); + const targets = find(".item span", ".composer-fields"); assert.equal( - $(targets[0]).text(), "someguy", + $(targets[0]).text(), + "someguy", "it fills up the composer with the right user to start the PM to" ); assert.equal( - $(targets[1]).text(), "test", + $(targets[1]).text(), + "test", "it fills up the composer with the right user to start the PM to" ); assert.equal( - $(targets[2]).text(), "Group", + $(targets[2]).text(), + "Group", "it fills up the composer with the right group to start the PM to" ); }); @@ -145,8 +169,11 @@ QUnit.test("Visit topic routes", assert => { andThen(() => { assert.equal( - find('.fancy-title').text().trim(), 'PM for testing', - 'it routes to the right topic' + find(".fancy-title") + .text() + .trim(), + "PM for testing", + "it routes to the right topic" ); }); @@ -154,22 +181,31 @@ QUnit.test("Visit topic routes", assert => { andThen(() => { assert.equal( - find('.fancy-title').text().trim(), 'Internationalization / localization', - 'it routes to the right topic' + find(".fancy-title") + .text() + .trim(), + "Internationalization / localization", + "it routes to the right topic" ); }); }); QUnit.test("Updating the topic title with emojis", assert => { visit("/t/internationalization-localization/280"); - click('#topic-title .d-icon-pencil'); + click("#topic-title .d-icon-pencil"); - fillIn('#edit-title', 'emojis title :bike: :blonde_woman:t6:'); + fillIn("#edit-title", "emojis title :bike: :blonde_woman:t6:"); - click('#topic-title .submit-edit'); + click("#topic-title .submit-edit"); andThen(() => { - assert.equal(find('.fancy-title').html().trim(), 'emojis title \"bike\" \"blonde_woman:t6\"', 'it displays the new title with emojis'); + assert.equal( + find(".fancy-title") + .html() + .trim(), + 'emojis title bike blonde_woman:t6', + "it displays the new title with emojis" + ); }); }); @@ -184,12 +220,18 @@ acceptance("Topic featured links", { QUnit.test("remove featured link", assert => { visit("/t/299/1"); andThen(() => { - assert.ok(exists('.title-wrapper .topic-featured-link'), 'link is shown with topic title'); + assert.ok( + exists(".title-wrapper .topic-featured-link"), + "link is shown with topic title" + ); }); - click('.title-wrapper .edit-topic'); + click(".title-wrapper .edit-topic"); andThen(() => { - assert.ok(exists('.title-wrapper .remove-featured-link'), 'link to remove featured link'); + assert.ok( + exists(".title-wrapper .remove-featured-link"), + "link to remove featured link" + ); }); // this test only works in a browser: diff --git a/test/javascripts/acceptance/user-anonymous-test.js.es6 b/test/javascripts/acceptance/user-anonymous-test.js.es6 index 91f3c62c758..30d8554811f 100644 --- a/test/javascripts/acceptance/user-anonymous-test.js.es6 +++ b/test/javascripts/acceptance/user-anonymous-test.js.es6 @@ -3,30 +3,30 @@ acceptance("User Anonymous"); function hasStream(assert) { andThen(() => { - assert.ok(exists('.user-main .about'), 'it has the about section'); - assert.ok(count('.user-stream .item') > 0, 'it has stream items'); + assert.ok(exists(".user-main .about"), "it has the about section"); + assert.ok(count(".user-stream .item") > 0, "it has stream items"); }); } function hasTopicList(assert) { andThen(() => { - assert.equal(count('.user-stream .item'), 0, "has no stream displayed"); - assert.ok(count('.topic-list tr') > 0, 'it has a topic list'); + assert.equal(count(".user-stream .item"), 0, "has no stream displayed"); + assert.ok(count(".topic-list tr") > 0, "it has a topic list"); }); } QUnit.test("Root URL", assert => { visit("/u/eviltrout"); andThen(() => { - assert.ok($('body.user-summary-page').length, "has the body class"); - assert.equal(currentPath(), 'user.summary', "it defaults to summary"); + assert.ok($("body.user-summary-page").length, "has the body class"); + assert.equal(currentPath(), "user.summary", "it defaults to summary"); }); }); QUnit.test("Filters", assert => { visit("/u/eviltrout/activity"); andThen(() => { - assert.ok($('body.user-activity-page').length, "has the body class"); + assert.ok($("body.user-activity-page").length, "has the body class"); }); hasStream(assert); @@ -40,7 +40,7 @@ QUnit.test("Filters", assert => { QUnit.test("Badges", assert => { visit("/u/eviltrout/badges"); andThen(() => { - assert.ok($('body.user-badges-page').length, "has the body class"); + assert.ok($("body.user-badges-page").length, "has the body class"); assert.ok(exists(".user-badges-list .badge-card"), "shows a badge"); }); }); @@ -49,6 +49,10 @@ QUnit.test("Restricted Routes", assert => { visit("/u/eviltrout/preferences"); andThen(() => { - assert.equal(currentURL(), '/u/eviltrout/activity', "it redirects from preferences"); + assert.equal( + currentURL(), + "/u/eviltrout/activity", + "it redirects from preferences" + ); }); }); diff --git a/test/javascripts/acceptance/user-card-test.js.es6 b/test/javascripts/acceptance/user-card-test.js.es6 index dcab0c2930f..a502f488cf3 100644 --- a/test/javascripts/acceptance/user-card-test.js.es6 +++ b/test/javascripts/acceptance/user-card-test.js.es6 @@ -3,13 +3,12 @@ import { acceptance } from "helpers/qunit-helpers"; acceptance("User Card"); QUnit.test("user card", assert => { - visit('/'); + visit("/"); - assert.ok(invisible('#user-card'), 'user card is invisible by default'); - click('a[data-user-card=eviltrout]:first'); + assert.ok(invisible("#user-card"), "user card is invisible by default"); + click("a[data-user-card=eviltrout]:first"); andThen(() => { - assert.ok(visible('#user-card'), 'card should appear'); + assert.ok(visible("#user-card"), "card should appear"); }); - }); diff --git a/test/javascripts/acceptance/user-test.js.es6 b/test/javascripts/acceptance/user-test.js.es6 index 261a9b66327..05fe5885aa6 100644 --- a/test/javascripts/acceptance/user-test.js.es6 +++ b/test/javascripts/acceptance/user-test.js.es6 @@ -1,46 +1,50 @@ import { acceptance } from "helpers/qunit-helpers"; -acceptance("User", {loggedIn: true}); +acceptance("User", { loggedIn: true }); QUnit.test("Invites", assert => { visit("/u/eviltrout/invited/pending"); andThen(() => { - assert.ok($('body.user-invites-page').length, "has the body class"); + assert.ok($("body.user-invites-page").length, "has the body class"); }); }); QUnit.test("Messages", assert => { visit("/u/eviltrout/messages"); andThen(() => { - assert.ok($('body.user-messages-page').length, "has the body class"); + assert.ok($("body.user-messages-page").length, "has the body class"); }); }); QUnit.test("Notifications", assert => { visit("/u/eviltrout/notifications"); andThen(() => { - assert.ok($('body.user-notifications-page').length, "has the body class"); + assert.ok($("body.user-notifications-page").length, "has the body class"); }); }); QUnit.test("Root URL - Viewing Self", assert => { visit("/u/eviltrout"); andThen(() => { - assert.ok($('body.user-activity-page').length, "has the body class"); - assert.equal(currentPath(), 'user.userActivity.index', "it defaults to activity"); - assert.ok(exists('.container.viewing-self'), "has the viewing-self class"); + assert.ok($("body.user-activity-page").length, "has the body class"); + assert.equal( + currentPath(), + "user.userActivity.index", + "it defaults to activity" + ); + assert.ok(exists(".container.viewing-self"), "has the viewing-self class"); }); }); QUnit.test("Viewing Summary", assert => { visit("/u/eviltrout/summary"); andThen(() => { - assert.ok(exists('.replies-section li a'), 'replies'); - assert.ok(exists('.topics-section li a'), 'topics'); - assert.ok(exists('.links-section li a'), 'links'); - assert.ok(exists('.replied-section .user-info'), 'liked by'); - assert.ok(exists('.liked-by-section .user-info'), 'liked by'); - assert.ok(exists('.liked-section .user-info'), 'liked'); - assert.ok(exists('.badges-section .badge-card'), 'badges'); + assert.ok(exists(".replies-section li a"), "replies"); + assert.ok(exists(".topics-section li a"), "topics"); + assert.ok(exists(".links-section li a"), "links"); + assert.ok(exists(".replied-section .user-info"), "liked by"); + assert.ok(exists(".liked-by-section .user-info"), "liked by"); + assert.ok(exists(".liked-section .user-info"), "liked"); + assert.ok(exists(".badges-section .badge-card"), "badges"); }); }); diff --git a/test/javascripts/acceptance/users-test.js.es6 b/test/javascripts/acceptance/users-test.js.es6 index 648b41890d1..57e1302b637 100644 --- a/test/javascripts/acceptance/users-test.js.es6 +++ b/test/javascripts/acceptance/users-test.js.es6 @@ -3,23 +3,23 @@ acceptance("User Directory"); QUnit.test("Visit Page", async assert => { await visit("/u"); - assert.ok($('body.users-page').length, "has the body class"); - assert.ok(exists('.directory table tr'), "has a list of users"); + assert.ok($("body.users-page").length, "has the body class"); + assert.ok(exists(".directory table tr"), "has a list of users"); }); QUnit.test("Visit All Time", async assert => { await visit("/u?period=all"); - assert.ok(exists('.time-read'), "has time read column"); + assert.ok(exists(".time-read"), "has time read column"); }); QUnit.test("Visit Without Usernames", async assert => { await visit("/u?exclude_usernames=system"); - assert.ok($('body.users-page').length, "has the body class"); - assert.ok(exists('.directory table tr'), "has a list of users"); + assert.ok($("body.users-page").length, "has the body class"); + assert.ok(exists(".directory table tr"), "has a list of users"); }); QUnit.test("Visit With Group Filter", async assert => { await visit("/u?group=trust_level_0"); - assert.ok($('body.users-page').length, "has the body class"); - assert.ok(exists('.directory table tr'), "has a list of users"); -}); \ No newline at end of file + assert.ok($("body.users-page").length, "has the body class"); + assert.ok(exists(".directory table tr"), "has a list of users"); +}); diff --git a/test/javascripts/admin/controllers/admin-customize-themes-test.js.es6 b/test/javascripts/admin/controllers/admin-customize-themes-test.js.es6 index 41468970937..2611c4b1c74 100644 --- a/test/javascripts/admin/controllers/admin-customize-themes-test.js.es6 +++ b/test/javascripts/admin/controllers/admin-customize-themes-test.js.es6 @@ -1,32 +1,32 @@ -import { mapRoutes } from 'discourse/mapping-router'; -import Theme from 'admin/models/theme'; +import { mapRoutes } from "discourse/mapping-router"; +import Theme from "admin/models/theme"; -moduleFor('controller:admin-customize-themes', { +moduleFor("controller:admin-customize-themes", { beforeEach() { - this.registry.register('router:main', mapRoutes()); + this.registry.register("router:main", mapRoutes()); }, - needs: ['controller:adminUser'] + needs: ["controller:adminUser"] }); QUnit.test("can list sorted themes", function(assert) { - - const defaultTheme = Theme.create({id: 2, 'default': true, name: 'default'}); - const userTheme = Theme.create({id: 3, 'user_selectable': true, name: 'name'}); - const strayTheme1 = Theme.create({id: 4, name: 'stray1'}); - const strayTheme2 = Theme.create({id: 5, name: 'stray2'}); + const defaultTheme = Theme.create({ id: 2, default: true, name: "default" }); + const userTheme = Theme.create({ + id: 3, + user_selectable: true, + name: "name" + }); + const strayTheme1 = Theme.create({ id: 4, name: "stray1" }); + const strayTheme2 = Theme.create({ id: 5, name: "stray2" }); const controller = this.subject({ - model: - { - content: [strayTheme2, strayTheme1, userTheme, defaultTheme] - } + model: { + content: [strayTheme2, strayTheme1, userTheme, defaultTheme] + } }); - - assert.deepEqual(controller.get('sortedThemes').map(t=>t.get('name')), [ - defaultTheme, - userTheme, - strayTheme1, - strayTheme2 - ].map(t=>t.get('name')), "sorts themes correctly"); + assert.deepEqual( + controller.get("sortedThemes").map(t => t.get("name")), + [defaultTheme, userTheme, strayTheme1, strayTheme2].map(t => t.get("name")), + "sorts themes correctly" + ); }); diff --git a/test/javascripts/admin/controllers/admin-user-badges-test.js.es6 b/test/javascripts/admin/controllers/admin-user-badges-test.js.es6 index 763644eaddb..79a7ca7fcdf 100644 --- a/test/javascripts/admin/controllers/admin-user-badges-test.js.es6 +++ b/test/javascripts/admin/controllers/admin-user-badges-test.js.es6 @@ -1,19 +1,44 @@ -import Badge from 'discourse/models/badge'; -import { mapRoutes } from 'discourse/mapping-router'; +import Badge from "discourse/models/badge"; +import { mapRoutes } from "discourse/mapping-router"; -moduleFor('controller:admin-user-badges', { +moduleFor("controller:admin-user-badges", { beforeEach() { - this.registry.register('router:main', mapRoutes()); + this.registry.register("router:main", mapRoutes()); }, - needs: ['controller:adminUser'] + needs: ["controller:adminUser"] }); QUnit.test("grantableBadges", function(assert) { - const badgeFirst = Badge.create({ id: 3, name: "A Badge", enabled: true, manually_grantable: true }); - const badgeMiddle = Badge.create({ id: 1, name: "My Badge", enabled: true, manually_grantable: true }); - const badgeLast = Badge.create({ id: 2, name: "Zoo Badge", enabled: true, manually_grantable: true }); - const badgeDisabled = Badge.create({ id: 4, name: "Disabled Badge", enabled: false, manually_grantable: true }); - const badgeAutomatic = Badge.create({ id: 5, name: "Automatic Badge", enabled: true, manually_grantable: false }); + const badgeFirst = Badge.create({ + id: 3, + name: "A Badge", + enabled: true, + manually_grantable: true + }); + const badgeMiddle = Badge.create({ + id: 1, + name: "My Badge", + enabled: true, + manually_grantable: true + }); + const badgeLast = Badge.create({ + id: 2, + name: "Zoo Badge", + enabled: true, + manually_grantable: true + }); + const badgeDisabled = Badge.create({ + id: 4, + name: "Disabled Badge", + enabled: false, + manually_grantable: true + }); + const badgeAutomatic = Badge.create({ + id: 5, + name: "Automatic Badge", + enabled: true, + manually_grantable: false + }); const controller = this.subject({ model: [], @@ -21,11 +46,10 @@ QUnit.test("grantableBadges", function(assert) { }); const sortedNames = [badgeFirst.name, badgeMiddle.name, badgeLast.name]; - const badgeNames = controller.get('grantableBadges').map(function(badge) { + const badgeNames = controller.get("grantableBadges").map(function(badge) { return badge.name; }); - assert.not(badgeNames.includes(badgeDisabled), "excludes disabled badges"); assert.deepEqual(badgeNames, sortedNames, "sorts badges by name"); }); diff --git a/test/javascripts/admin/models/admin-user-test.js.es6 b/test/javascripts/admin/models/admin-user-test.js.es6 index e15a2b904f5..729cd55d05c 100644 --- a/test/javascripts/admin/models/admin-user-test.js.es6 +++ b/test/javascripts/admin/models/admin-user-test.js.es6 @@ -1,26 +1,30 @@ -import AdminUser from 'admin/models/admin-user'; -import ApiKey from 'admin/models/api-key'; +import AdminUser from "admin/models/admin-user"; +import ApiKey from "admin/models/api-key"; QUnit.module("model:admin-user"); -QUnit.test('generate key', function(assert) { +QUnit.test("generate key", function(assert) { assert.expect(2); - var adminUser = AdminUser.create({id: 333}); - assert.ok(!adminUser.get('api_key'), 'it has no api key by default'); + var adminUser = AdminUser.create({ id: 333 }); + assert.ok(!adminUser.get("api_key"), "it has no api key by default"); return adminUser.generateApiKey().then(function() { - assert.present(adminUser.get('api_key'), 'it has an api_key now'); + assert.present(adminUser.get("api_key"), "it has an api_key now"); }); }); -QUnit.test('revoke key', function(assert) { +QUnit.test("revoke key", function(assert) { assert.expect(2); - var apiKey = ApiKey.create({id: 1234, key: 'asdfasdf'}), - adminUser = AdminUser.create({id: 333, api_key: apiKey}); + var apiKey = ApiKey.create({ id: 1234, key: "asdfasdf" }), + adminUser = AdminUser.create({ id: 333, api_key: apiKey }); - assert.equal(adminUser.get('api_key'), apiKey, 'it has the api key in the beginning'); + assert.equal( + adminUser.get("api_key"), + apiKey, + "it has the api key in the beginning" + ); return adminUser.revokeApiKey().then(function() { - assert.blank(adminUser.get('api_key'), 'it cleared the api_key'); + assert.blank(adminUser.get("api_key"), "it cleared the api_key"); }); }); diff --git a/test/javascripts/admin/models/theme-test.js.es6 b/test/javascripts/admin/models/theme-test.js.es6 index 4a16c584c85..c2e49ce3c30 100644 --- a/test/javascripts/admin/models/theme-test.js.es6 +++ b/test/javascripts/admin/models/theme-test.js.es6 @@ -1,17 +1,21 @@ -import Theme from 'admin/models/theme'; +import Theme from "admin/models/theme"; QUnit.module("model:theme"); -QUnit.test('can add an upload correctly', function(assert) { +QUnit.test("can add an upload correctly", function(assert) { let theme = Theme.create(); - assert.equal(theme.get("uploads.length"), 0, "uploads should be an empty array"); + assert.equal( + theme.get("uploads.length"), + 0, + "uploads should be an empty array" + ); - theme.setField('common', 'bob', '', 999, 2); + theme.setField("common", "bob", "", 999, 2); let fields = theme.get("theme_fields"); - assert.equal(fields.length, 1, 'expecting 1 theme field'); - assert.equal(fields[0].upload_id, 999, 'expecting upload id to be set'); - assert.equal(fields[0].type_id, 2, 'expecting type id to be set'); + assert.equal(fields.length, 1, "expecting 1 theme field"); + assert.equal(fields[0].upload_id, 999, "expecting upload id to be set"); + assert.equal(fields[0].type_id, 2, "expecting type id to be set"); assert.equal(theme.get("uploads.length"), 1, "expecting an upload"); -}); \ No newline at end of file +}); diff --git a/test/javascripts/components/ace-editor-test.js.es6 b/test/javascripts/components/ace-editor-test.js.es6 index e831da6eb53..7551a93da6b 100644 --- a/test/javascripts/components/ace-editor-test.js.es6 +++ b/test/javascripts/components/ace-editor-test.js.es6 @@ -1,38 +1,50 @@ -import componentTest from 'helpers/component-test'; +import componentTest from "helpers/component-test"; -moduleForComponent('ace-editor', {integration: true}); +moduleForComponent("ace-editor", { integration: true }); -componentTest('css editor', { +componentTest("css editor", { template: '{{ace-editor mode="css"}}', test(assert) { assert.expect(1); - assert.ok(this.$('.ace_editor').length, 'it renders the ace editor'); + assert.ok(this.$(".ace_editor").length, "it renders the ace editor"); } }); -componentTest('html editor', { +componentTest("html editor", { template: '{{ace-editor mode="html" content="wat"}}', test(assert) { assert.expect(1); - assert.ok(this.$('.ace_editor').length, 'it renders the ace editor'); + assert.ok(this.$(".ace_editor").length, "it renders the ace editor"); } }); -componentTest('sql editor', { +componentTest("sql editor", { template: '{{ace-editor mode="sql" content="SELECT * FROM users"}}', test(assert) { assert.expect(1); - assert.ok(this.$('.ace_editor').length, 'it renders the ace editor'); + assert.ok(this.$(".ace_editor").length, "it renders the ace editor"); } }); -componentTest('disabled editor', { - template: '{{ace-editor mode="sql" content="SELECT * FROM users" disabled=true}}', +componentTest("disabled editor", { + template: + '{{ace-editor mode="sql" content="SELECT * FROM users" disabled=true}}', test(assert) { - const $ace = this.$('.ace_editor'); + const $ace = this.$(".ace_editor"); assert.expect(3); - assert.ok($ace.length, 'it renders the ace editor'); - assert.equal($ace.parent().data().editor.getReadOnly(), true, 'it sets ACE to read-only mode'); - assert.equal($ace.parent().attr('data-disabled'), "true", 'ACE wrapper has `data-disabled` attribute set to true'); + assert.ok($ace.length, "it renders the ace editor"); + assert.equal( + $ace + .parent() + .data() + .editor.getReadOnly(), + true, + "it sets ACE to read-only mode" + ); + assert.equal( + $ace.parent().attr("data-disabled"), + "true", + "ACE wrapper has `data-disabled` attribute set to true" + ); } }); diff --git a/test/javascripts/components/categories-admin-dropdown-test.js.es6 b/test/javascripts/components/categories-admin-dropdown-test.js.es6 index 124a4fa8565..8825cf33733 100644 --- a/test/javascripts/components/categories-admin-dropdown-test.js.es6 +++ b/test/javascripts/components/categories-admin-dropdown-test.js.es6 @@ -1,8 +1,8 @@ -import componentTest from 'helpers/component-test'; -moduleForComponent('categories-admin-dropdown', {integration: true}); +import componentTest from "helpers/component-test"; +moduleForComponent("categories-admin-dropdown", { integration: true }); -componentTest('default', { - template: '{{categories-admin-dropdown}}', +componentTest("default", { + template: "{{categories-admin-dropdown}}", test(assert) { const subject = selectKit(); diff --git a/test/javascripts/components/category-chooser-test.js.es6 b/test/javascripts/components/category-chooser-test.js.es6 index 62df66fadea..d8265f26cfa 100644 --- a/test/javascripts/components/category-chooser-test.js.es6 +++ b/test/javascripts/components/category-chooser-test.js.es6 @@ -1,51 +1,87 @@ -import componentTest from 'helpers/component-test'; +import componentTest from "helpers/component-test"; -moduleForComponent('category-chooser', { +moduleForComponent("category-chooser", { integration: true, beforeEach: function() { - this.set('subject', selectKit()); + this.set("subject", selectKit()); } }); -componentTest('with value', { - template: '{{category-chooser value=2}}', +componentTest("with value", { + template: "{{category-chooser value=2}}", test(assert) { andThen(() => { - assert.equal(this.get('subject').header().value(), 2); - assert.equal(this.get('subject').header().title(), 'feature'); + assert.equal( + this.get("subject") + .header() + .value(), + 2 + ); + assert.equal( + this.get("subject") + .header() + .title(), + "feature" + ); }); } }); -componentTest('with excludeCategoryId', { - template: '{{category-chooser excludeCategoryId=2}}', +componentTest("with excludeCategoryId", { + template: "{{category-chooser excludeCategoryId=2}}", test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); - andThen(() => assert.notOk(this.get('subject').rowByValue(2).exists())); + andThen(() => + assert.notOk( + this.get("subject") + .rowByValue(2) + .exists() + ) + ); } }); -componentTest('with scopedCategoryId', { - template: '{{category-chooser scopedCategoryId=2}}', +componentTest("with scopedCategoryId", { + template: "{{category-chooser scopedCategoryId=2}}", test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').rowByIndex(0).title(), 'Discussion about features or potential features of Discourse: how they work, why they work, etc.'); - assert.equal(this.get('subject').rowByIndex(0).value(), 2); - assert.equal(this.get('subject').rowByIndex(1).title(), 'My idea here is to have mini specs for features we would like built but have no bandwidth to build'); - assert.equal(this.get('subject').rowByIndex(1).value(), 26); - assert.equal(this.get('subject').rows().length, 2); + assert.equal( + this.get("subject") + .rowByIndex(0) + .title(), + "Discussion about features or potential features of Discourse: how they work, why they work, etc." + ); + assert.equal( + this.get("subject") + .rowByIndex(0) + .value(), + 2 + ); + assert.equal( + this.get("subject") + .rowByIndex(1) + .title(), + "My idea here is to have mini specs for features we would like built but have no bandwidth to build" + ); + assert.equal( + this.get("subject") + .rowByIndex(1) + .value(), + 26 + ); + assert.equal(this.get("subject").rows().length, 2); }); } }); -componentTest('with allowUncategorized=null', { - template: '{{category-chooser allowUncategorized=null}}', +componentTest("with allowUncategorized=null", { + template: "{{category-chooser allowUncategorized=null}}", beforeEach() { this.siteSettings.allow_uncategorized_topics = false; @@ -53,14 +89,24 @@ componentTest('with allowUncategorized=null', { test(assert) { andThen(() => { - assert.equal(this.get('subject').header().value(), null); - assert.equal(this.get('subject').header().title(), "Select a category"); + assert.equal( + this.get("subject") + .header() + .value(), + null + ); + assert.equal( + this.get("subject") + .header() + .title(), + "Select a category" + ); }); } }); -componentTest('with allowUncategorized=null rootNone=true', { - template: '{{category-chooser allowUncategorized=null rootNone=true}}', +componentTest("with allowUncategorized=null rootNone=true", { + template: "{{category-chooser allowUncategorized=null rootNone=true}}", beforeEach() { this.siteSettings.allow_uncategorized_topics = false; @@ -68,30 +114,51 @@ componentTest('with allowUncategorized=null rootNone=true', { test(assert) { andThen(() => { - assert.equal(this.get('subject').header().value(), null); - assert.equal(this.get('subject').header().title(), 'Select a category'); + assert.equal( + this.get("subject") + .header() + .value(), + null + ); + assert.equal( + this.get("subject") + .header() + .title(), + "Select a category" + ); }); } }); -componentTest('with disallowed uncategorized, rootNone and rootNoneLabel', { - template: '{{category-chooser allowUncategorized=null rootNone=true rootNoneLabel="test.root"}}', +componentTest("with disallowed uncategorized, rootNone and rootNoneLabel", { + template: + '{{category-chooser allowUncategorized=null rootNone=true rootNoneLabel="test.root"}}', beforeEach() { - I18n.translations[I18n.locale].js.test = { root: 'root none label' }; + I18n.translations[I18n.locale].js.test = { root: "root none label" }; this.siteSettings.allow_uncategorized_topics = false; }, test(assert) { andThen(() => { - assert.equal(this.get('subject').header().value(), null); - assert.equal(this.get('subject').header().title(), 'Select a category'); + assert.equal( + this.get("subject") + .header() + .value(), + null + ); + assert.equal( + this.get("subject") + .header() + .title(), + "Select a category" + ); }); } }); -componentTest('with allowed uncategorized', { - template: '{{category-chooser allowUncategorized=true}}', +componentTest("with allowed uncategorized", { + template: "{{category-chooser allowUncategorized=true}}", beforeEach() { this.siteSettings.allow_uncategorized_topics = true; @@ -99,14 +166,24 @@ componentTest('with allowed uncategorized', { test(assert) { andThen(() => { - assert.equal(this.get('subject').header().value(), null); - assert.equal(this.get('subject').header().title(), 'uncategorized'); + assert.equal( + this.get("subject") + .header() + .value(), + null + ); + assert.equal( + this.get("subject") + .header() + .title(), + "uncategorized" + ); }); } }); -componentTest('with allowed uncategorized and rootNone', { - template: '{{category-chooser allowUncategorized=true rootNone=true}}', +componentTest("with allowed uncategorized and rootNone", { + template: "{{category-chooser allowUncategorized=true rootNone=true}}", beforeEach() { this.siteSettings.allow_uncategorized_topics = true; @@ -114,24 +191,45 @@ componentTest('with allowed uncategorized and rootNone', { test(assert) { andThen(() => { - assert.equal(this.get('subject').header().value(), null); - assert.equal(this.get('subject').header().title(), '(no category)'); + assert.equal( + this.get("subject") + .header() + .value(), + null + ); + assert.equal( + this.get("subject") + .header() + .title(), + "(no category)" + ); }); } }); -componentTest('with allowed uncategorized rootNone and rootNoneLabel', { - template: '{{category-chooser allowUncategorized=true rootNone=true rootNoneLabel="test.root"}}', +componentTest("with allowed uncategorized rootNone and rootNoneLabel", { + template: + '{{category-chooser allowUncategorized=true rootNone=true rootNoneLabel="test.root"}}', beforeEach() { - I18n.translations[I18n.locale].js.test = { root: 'root none label' }; + I18n.translations[I18n.locale].js.test = { root: "root none label" }; this.siteSettings.allow_uncategorized_topics = true; }, test(assert) { andThen(() => { - assert.equal(this.get('subject').header().value(), null); - assert.equal(this.get('subject').header().title(), 'root none label'); + assert.equal( + this.get("subject") + .header() + .value(), + null + ); + assert.equal( + this.get("subject") + .header() + .title(), + "root none label" + ); }); } }); diff --git a/test/javascripts/components/category-selector-test.js.es6 b/test/javascripts/components/category-selector-test.js.es6 index befc4a9e166..d4f158d1b01 100644 --- a/test/javascripts/components/category-selector-test.js.es6 +++ b/test/javascripts/components/category-selector-test.js.es6 @@ -1,88 +1,106 @@ -import componentTest from 'helpers/component-test'; +import componentTest from "helpers/component-test"; import Category from "discourse/models/category"; -moduleForComponent('category-selector', { +moduleForComponent("category-selector", { integration: true, beforeEach: function() { - this.set('subject', selectKit()); + this.set("subject", selectKit()); } }); -componentTest('default', { - template: '{{category-selector categories=categories}}', +componentTest("default", { + template: "{{category-selector categories=categories}}", beforeEach() { - this.set('categories', [ Category.findById(2) ]); + this.set("categories", [Category.findById(2)]); }, test(assert) { andThen(() => { - assert.equal(this.get('subject').header().value(), 2); + assert.equal( + this.get("subject") + .header() + .value(), + 2 + ); assert.notOk( - this.get('subject').rowByValue(2).exists(), + this.get("subject") + .rowByValue(2) + .exists(), "selected categories are not in the list" ); }); } }); -componentTest('with blacklist', { - template: '{{category-selector categories=categories blacklist=blacklist}}', +componentTest("with blacklist", { + template: "{{category-selector categories=categories blacklist=blacklist}}", beforeEach() { - this.set('categories', [ Category.findById(2) ]); - this.set('blacklist', [ Category.findById(8) ]); + this.set("categories", [Category.findById(2)]); + this.set("blacklist", [Category.findById(8)]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { assert.ok( - this.get('subject').rowByValue(6).exists(), + this.get("subject") + .rowByValue(6) + .exists(), "not blacklisted categories are in the list" ); assert.notOk( - this.get('subject').rowByValue(8).exists(), + this.get("subject") + .rowByValue(8) + .exists(), "blacklisted categories are not in the list" ); }); } }); -componentTest('interactions', { - template: '{{category-selector categories=categories}}', +componentTest("interactions", { + template: "{{category-selector categories=categories}}", beforeEach() { - this.set('categories', [ - Category.findById(2), - Category.findById(6) - ]); + this.set("categories", [Category.findById(2), Category.findById(6)]); }, test(assert) { - this.get('subject').expand().selectRowByValue(8); + this.get("subject") + .expand() + .selectRowByValue(8); andThen(() => { assert.equal( - this.get('subject').header().value(), - '2,6,8', - 'it adds the selected category' + this.get("subject") + .header() + .value(), + "2,6,8", + "it adds the selected category" ); - assert.equal(this.get('categories').length, 3); + assert.equal(this.get("categories").length, 3); }); - this.get('subject').expand(); - this.get('subject').keyboard().backspace(); - this.get('subject').keyboard().backspace(); + this.get("subject").expand(); + this.get("subject") + .keyboard() + .backspace(); + this.get("subject") + .keyboard() + .backspace(); andThen(() => { assert.equal( - this.get('subject').header().value(), - '2,6', - 'it removes the last selected category' + this.get("subject") + .header() + .value(), + "2,6", + "it removes the last selected category" ); - assert.equal(this.get('categories').length, 2); + assert.equal(this.get("categories").length, 2); }); } }); diff --git a/test/javascripts/components/combo-box-test.js.es6 b/test/javascripts/components/combo-box-test.js.es6 index 45af48de9aa..b2b2dc9ffe2 100644 --- a/test/javascripts/components/combo-box-test.js.es6 +++ b/test/javascripts/components/combo-box-test.js.es6 @@ -1,211 +1,328 @@ -import componentTest from 'helpers/component-test'; -moduleForComponent('combo-box', { +import componentTest from "helpers/component-test"; +moduleForComponent("combo-box", { integration: true, beforeEach: function() { - this.set('subject', selectKit()); + this.set("subject", selectKit()); } }); -componentTest('default', { - template: '{{combo-box content=items}}', +componentTest("default", { + template: "{{combo-box content=items}}", beforeEach() { - this.set('items', [{id: 1, name: 'hello'}, {id: 2, name: 'world'}]); + this.set("items", [{ id: 1, name: "hello" }, { id: 2, name: "world" }]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').header().name(), "hello"); - assert.equal(this.get('subject').rowByValue(1).name(), "hello"); - assert.equal(this.get('subject').rowByValue(2).name(), "world"); + assert.equal( + this.get("subject") + .header() + .name(), + "hello" + ); + assert.equal( + this.get("subject") + .rowByValue(1) + .name(), + "hello" + ); + assert.equal( + this.get("subject") + .rowByValue(2) + .name(), + "world" + ); }); } }); -componentTest('with valueAttribute', { +componentTest("with valueAttribute", { template: '{{combo-box content=items valueAttribute="value"}}', beforeEach() { - this.set('items', [{value: 0, name: 'hello'}, {value: 1, name: 'world'}]); + this.set("items", [ + { value: 0, name: "hello" }, + { value: 1, name: "world" } + ]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').rowByValue(0).name(), "hello"); - assert.equal(this.get('subject').rowByValue(1).name(), "world"); + assert.equal( + this.get("subject") + .rowByValue(0) + .name(), + "hello" + ); + assert.equal( + this.get("subject") + .rowByValue(1) + .name(), + "world" + ); }); } }); -componentTest('with nameProperty', { +componentTest("with nameProperty", { template: '{{combo-box content=items nameProperty="text"}}', beforeEach() { - this.set('items', [{id: 0, text: 'hello'}, {id: 1, text: 'world'}]); + this.set("items", [{ id: 0, text: "hello" }, { id: 1, text: "world" }]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').rowByValue(0).name(), "hello"); - assert.equal(this.get('subject').rowByValue(1).name(), "world"); + assert.equal( + this.get("subject") + .rowByValue(0) + .name(), + "hello" + ); + assert.equal( + this.get("subject") + .rowByValue(1) + .name(), + "world" + ); }); } }); -componentTest('with an array as content', { - template: '{{combo-box content=items value=value}}', +componentTest("with an array as content", { + template: "{{combo-box content=items value=value}}", beforeEach() { - this.set('items', ['evil', 'trout', 'hat']); + this.set("items", ["evil", "trout", "hat"]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').rowByValue('evil').name(), "evil"); - assert.equal(this.get('subject').rowByValue('trout').name(), "trout"); + assert.equal( + this.get("subject") + .rowByValue("evil") + .name(), + "evil" + ); + assert.equal( + this.get("subject") + .rowByValue("trout") + .name(), + "trout" + ); }); } }); -componentTest('with value and none as a string', { +componentTest("with value and none as a string", { template: '{{combo-box content=items none="test.none" value=value}}', beforeEach() { - I18n.translations[I18n.locale].js.test = {none: 'none'}; - this.set('items', ['evil', 'trout', 'hat']); - this.set('value', 'trout'); + I18n.translations[I18n.locale].js.test = { none: "none" }; + this.set("items", ["evil", "trout", "hat"]); + this.set("value", "trout"); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').noneRow().name(), 'none'); - assert.equal(this.get('subject').rowByValue("evil").name(), "evil"); - assert.equal(this.get('subject').rowByValue("trout").name(), "trout"); - assert.equal(this.get('subject').header().name(), 'trout'); - assert.equal(this.get('value'), 'trout'); + assert.equal( + this.get("subject") + .noneRow() + .name(), + "none" + ); + assert.equal( + this.get("subject") + .rowByValue("evil") + .name(), + "evil" + ); + assert.equal( + this.get("subject") + .rowByValue("trout") + .name(), + "trout" + ); + assert.equal( + this.get("subject") + .header() + .name(), + "trout" + ); + assert.equal(this.get("value"), "trout"); }); - this.get('subject').selectNoneRow(); + this.get("subject").selectNoneRow(); andThen(() => { - assert.equal(this.get('value'), null); + assert.equal(this.get("value"), null); }); } }); -componentTest('with value and none as an object', { - template: '{{combo-box content=items none=none value=value}}', +componentTest("with value and none as an object", { + template: "{{combo-box content=items none=none value=value}}", beforeEach() { - this.set('none', { id: 'something', name: 'none' }); - this.set('items', ['evil', 'trout', 'hat']); - this.set('value', 'evil'); + this.set("none", { id: "something", name: "none" }); + this.set("items", ["evil", "trout", "hat"]); + this.set("value", "evil"); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').noneRow().name(), 'none'); - assert.equal(this.get('subject').rowByValue("evil").name(), "evil"); - assert.equal(this.get('subject').rowByValue("trout").name(), "trout"); - assert.equal(this.get('subject').header().name(), 'evil'); - assert.equal(this.get('value'), 'evil'); + assert.equal( + this.get("subject") + .noneRow() + .name(), + "none" + ); + assert.equal( + this.get("subject") + .rowByValue("evil") + .name(), + "evil" + ); + assert.equal( + this.get("subject") + .rowByValue("trout") + .name(), + "trout" + ); + assert.equal( + this.get("subject") + .header() + .name(), + "evil" + ); + assert.equal(this.get("value"), "evil"); }); - this.get('subject').selectNoneRow(); + this.get("subject").selectNoneRow(); andThen(() => { - assert.equal(this.get('value'), null); + assert.equal(this.get("value"), null); }); } }); -componentTest('with no value and none as an object', { - template: '{{combo-box content=items none=none value=value}}', +componentTest("with no value and none as an object", { + template: "{{combo-box content=items none=none value=value}}", beforeEach() { - I18n.translations[I18n.locale].js.test = {none: 'none'}; - this.set('none', { id: 'something', name: 'none' }); - this.set('items', ['evil', 'trout', 'hat']); - this.set('value', null); + I18n.translations[I18n.locale].js.test = { none: "none" }; + this.set("none", { id: "something", name: "none" }); + this.set("items", ["evil", "trout", "hat"]); + this.set("value", null); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').header().name(), 'none'); + assert.equal( + this.get("subject") + .header() + .name(), + "none" + ); }); } }); -componentTest('with no value and none string', { - template: '{{combo-box content=items none=none value=value}}', +componentTest("with no value and none string", { + template: "{{combo-box content=items none=none value=value}}", beforeEach() { - I18n.translations[I18n.locale].js.test = {none: 'none'}; - this.set('none', 'test.none'); - this.set('items', ['evil', 'trout', 'hat']); - this.set('value', null); + I18n.translations[I18n.locale].js.test = { none: "none" }; + this.set("none", "test.none"); + this.set("items", ["evil", "trout", "hat"]); + this.set("value", null); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').header().name(), 'none'); + assert.equal( + this.get("subject") + .header() + .name(), + "none" + ); }); } }); -componentTest('with no value and no none', { - template: '{{combo-box content=items value=value}}', +componentTest("with no value and no none", { + template: "{{combo-box content=items value=value}}", beforeEach() { - this.set('items', ['evil', 'trout', 'hat']); - this.set('value', null); + this.set("items", ["evil", "trout", "hat"]); + this.set("value", null); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').header().name(), 'evil', 'it sets the first row as value'); + assert.equal( + this.get("subject") + .header() + .name(), + "evil", + "it sets the first row as value" + ); }); } }); -componentTest('with empty string as value', { - template: '{{combo-box content=items value=value}}', +componentTest("with empty string as value", { + template: "{{combo-box content=items value=value}}", beforeEach() { - this.set('items', ['evil', 'trout', 'hat']); - this.set('value', ''); + this.set("items", ["evil", "trout", "hat"]); + this.set("value", ""); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').header().name(), 'evil', 'it sets the first row as value'); + assert.equal( + this.get("subject") + .header() + .name(), + "evil", + "it sets the first row as value" + ); }); } }); -componentTest('with noneLabel', { - template: '{{combo-box content=items allowAutoSelectFirst=false noneLabel=noneLabel}}', +componentTest("with noneLabel", { + template: + "{{combo-box content=items allowAutoSelectFirst=false noneLabel=noneLabel}}", beforeEach() { - I18n.translations[I18n.locale].js.test = {none: 'none'}; - this.set('items', ['evil', 'trout', 'hat']); - this.set('noneLabel', 'test.none'); + I18n.translations[I18n.locale].js.test = { none: "none" }; + this.set("items", ["evil", "trout", "hat"]); + this.set("noneLabel", "test.none"); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').header().name(), 'none', 'it displays noneLabel as the header name'); + assert.equal( + this.get("subject") + .header() + .name(), + "none", + "it displays noneLabel as the header name" + ); }); } }); diff --git a/test/javascripts/components/d-button-test.js.es6 b/test/javascripts/components/d-button-test.js.es6 index d283da4f21c..f07d6926c26 100644 --- a/test/javascripts/components/d-button-test.js.es6 +++ b/test/javascripts/components/d-button-test.js.es6 @@ -1,31 +1,37 @@ -import componentTest from 'helpers/component-test'; -moduleForComponent('d-button', {integration: true}); +import componentTest from "helpers/component-test"; +moduleForComponent("d-button", { integration: true }); -componentTest('icon only button', { +componentTest("icon only button", { template: '{{d-button icon="plus" tabindex="3"}}', test(assert) { - assert.ok(this.$('button.btn.btn-icon.no-text').length, 'it has all the classes'); - assert.ok(this.$('button .d-icon.d-icon-plus').length, 'it has the icon'); - assert.equal(this.$('button').attr('tabindex'), "3", 'it has the tabindex'); + assert.ok( + this.$("button.btn.btn-icon.no-text").length, + "it has all the classes" + ); + assert.ok(this.$("button .d-icon.d-icon-plus").length, "it has the icon"); + assert.equal(this.$("button").attr("tabindex"), "3", "it has the tabindex"); } }); -componentTest('icon and text button', { +componentTest("icon and text button", { template: '{{d-button icon="plus" label="topic.create"}}', test(assert) { - assert.ok(this.$('button.btn.btn-icon-text').length, 'it has all the classes'); - assert.ok(this.$('button .d-icon.d-icon-plus').length, 'it has the icon'); - assert.ok(this.$('button span.d-button-label').length, 'it has the label'); + assert.ok( + this.$("button.btn.btn-icon-text").length, + "it has all the classes" + ); + assert.ok(this.$("button .d-icon.d-icon-plus").length, "it has the icon"); + assert.ok(this.$("button span.d-button-label").length, "it has the label"); } }); -componentTest('text only button', { +componentTest("text only button", { template: '{{d-button label="topic.create"}}', test(assert) { - assert.ok(this.$('button.btn.btn-text').length, 'it has all the classes'); - assert.ok(this.$('button span.d-button-label').length, 'it has the label'); + assert.ok(this.$("button.btn.btn-text").length, "it has all the classes"); + assert.ok(this.$("button span.d-button-label").length, "it has the label"); } }); diff --git a/test/javascripts/components/d-editor-test.js.es6 b/test/javascripts/components/d-editor-test.js.es6 index e0409e4957d..4bf18bca967 100644 --- a/test/javascripts/components/d-editor-test.js.es6 +++ b/test/javascripts/components/d-editor-test.js.es6 @@ -1,45 +1,67 @@ -import componentTest from 'helpers/component-test'; -import { withPluginApi } from 'discourse/lib/plugin-api'; +import componentTest from "helpers/component-test"; +import { withPluginApi } from "discourse/lib/plugin-api"; -moduleForComponent('d-editor', {integration: true}); +moduleForComponent("d-editor", { integration: true }); -componentTest('preview updates with markdown', { - template: '{{d-editor value=value}}', +componentTest("preview updates with markdown", { + template: "{{d-editor value=value}}", test(assert) { - assert.ok(this.$('.d-editor-button-bar').length); - fillIn('.d-editor-input', 'hello **world**'); + assert.ok(this.$(".d-editor-button-bar").length); + fillIn(".d-editor-input", "hello **world**"); andThen(() => { - assert.equal(this.get('value'), 'hello **world**'); - assert.equal(this.$('.d-editor-preview').html().trim(), '

hello world

'); + assert.equal(this.get("value"), "hello **world**"); + assert.equal( + this.$(".d-editor-preview") + .html() + .trim(), + "

hello world

" + ); }); } }); -componentTest('preview sanitizes HTML', { - template: '{{d-editor value=value}}', +componentTest("preview sanitizes HTML", { + template: "{{d-editor value=value}}", test(assert) { - fillIn('.d-editor-input', `">`); + fillIn(".d-editor-input", `">`); andThen(() => { - assert.equal(this.$('.d-editor-preview').html().trim(), '

\">

'); + assert.equal( + this.$(".d-editor-preview") + .html() + .trim(), + '

">

' + ); }); } }); -componentTest('updating the value refreshes the preview', { - template: '{{d-editor value=value}}', +componentTest("updating the value refreshes the preview", { + template: "{{d-editor value=value}}", beforeEach() { - this.set('value', 'evil trout'); + this.set("value", "evil trout"); }, test(assert) { - assert.equal(this.$('.d-editor-preview').html().trim(), '

evil trout

'); + assert.equal( + this.$(".d-editor-preview") + .html() + .trim(), + "

evil trout

" + ); - andThen(() => this.set('value', 'zogstrip')); - andThen(() => assert.equal(this.$('.d-editor-preview').html().trim(), '

zogstrip

')); + andThen(() => this.set("value", "zogstrip")); + andThen(() => + assert.equal( + this.$(".d-editor-preview") + .html() + .trim(), + "

zogstrip

" + ) + ); } }); @@ -51,12 +73,12 @@ function jumpEnd(textarea) { function testCase(title, testFunc) { componentTest(title, { - template: '{{d-editor value=value}}', + template: "{{d-editor value=value}}", beforeEach() { - this.set('value', 'hello world.'); + this.set("value", "hello world."); }, test(assert) { - const textarea = jumpEnd(this.$('textarea.d-editor-input')[0]); + const textarea = jumpEnd(this.$("textarea.d-editor-input")[0]); testFunc.call(this, assert, textarea); } }); @@ -64,12 +86,12 @@ function testCase(title, testFunc) { function composerTestCase(title, testFunc) { componentTest(title, { - template: '{{d-editor value=value composerEvents=true}}', + template: "{{d-editor value=value composerEvents=true}}", beforeEach() { - this.set('value', 'hello world.'); + this.set("value", "hello world."); }, test(assert) { - const textarea = jumpEnd(this.$('textarea.d-editor-input')[0]); + const textarea = jumpEnd(this.$("textarea.d-editor-input")[0]); testFunc.call(this, assert, textarea); } }); @@ -81,7 +103,7 @@ testCase(`selecting the space before a word`, function(assert, textarea) { click(`button.bold`); andThen(() => { - assert.equal(this.get('value'), `hello **w**orld.`); + assert.equal(this.get("value"), `hello **w**orld.`); assert.equal(textarea.selectionStart, 8); assert.equal(textarea.selectionEnd, 9); }); @@ -93,7 +115,7 @@ testCase(`selecting the space after a word`, function(assert, textarea) { click(`button.bold`); andThen(() => { - assert.equal(this.get('value'), `**hello** world.`); + assert.equal(this.get("value"), `**hello** world.`); assert.equal(textarea.selectionStart, 2); assert.equal(textarea.selectionEnd, 7); }); @@ -103,7 +125,7 @@ testCase(`bold button with no selection`, function(assert, textarea) { click(`button.bold`); andThen(() => { const example = I18n.t(`composer.bold_text`); - assert.equal(this.get('value'), `hello world.**${example}**`); + assert.equal(this.get("value"), `hello world.**${example}**`); assert.equal(textarea.selectionStart, 14); assert.equal(textarea.selectionEnd, 14 + example.length); }); @@ -115,21 +137,21 @@ testCase(`bold button with a selection`, function(assert, textarea) { click(`button.bold`); andThen(() => { - assert.equal(this.get('value'), `hello **world**.`); + assert.equal(this.get("value"), `hello **world**.`); assert.equal(textarea.selectionStart, 8); assert.equal(textarea.selectionEnd, 13); }); click(`button.bold`); andThen(() => { - assert.equal(this.get('value'), 'hello world.'); + assert.equal(this.get("value"), "hello world."); assert.equal(textarea.selectionStart, 6); assert.equal(textarea.selectionEnd, 11); }); }); -testCase(`bold with a multiline selection`, function (assert, textarea) { - this.set('value', "hello\n\nworld\n\ntest."); +testCase(`bold with a multiline selection`, function(assert, textarea) { + this.set("value", "hello\n\nworld\n\ntest."); andThen(() => { textarea.selectionStart = 0; @@ -138,14 +160,14 @@ testCase(`bold with a multiline selection`, function (assert, textarea) { click(`button.bold`); andThen(() => { - assert.equal(this.get('value'), `**hello**\n\n**world**\n\ntest.`); + assert.equal(this.get("value"), `**hello**\n\n**world**\n\ntest.`); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 20); }); click(`button.bold`); andThen(() => { - assert.equal(this.get('value'), `hello\n\nworld\n\ntest.`); + assert.equal(this.get("value"), `hello\n\nworld\n\ntest.`); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 12); }); @@ -155,7 +177,7 @@ testCase(`italic button with no selection`, function(assert, textarea) { click(`button.italic`); andThen(() => { const example = I18n.t(`composer.italic_text`); - assert.equal(this.get('value'), `hello world._${example}_`); + assert.equal(this.get("value"), `hello world._${example}_`); assert.equal(textarea.selectionStart, 13); assert.equal(textarea.selectionEnd, 13 + example.length); @@ -168,21 +190,21 @@ testCase(`italic button with a selection`, function(assert, textarea) { click(`button.italic`); andThen(() => { - assert.equal(this.get('value'), `hello _world_.`); + assert.equal(this.get("value"), `hello _world_.`); assert.equal(textarea.selectionStart, 7); assert.equal(textarea.selectionEnd, 12); }); click(`button.italic`); andThen(() => { - assert.equal(this.get('value'), 'hello world.'); + assert.equal(this.get("value"), "hello world."); assert.equal(textarea.selectionStart, 6); assert.equal(textarea.selectionEnd, 11); }); }); -testCase(`italic with a multiline selection`, function (assert, textarea) { - this.set('value', "hello\n\nworld\n\ntest."); +testCase(`italic with a multiline selection`, function(assert, textarea) { + this.set("value", "hello\n\nworld\n\ntest."); andThen(() => { textarea.selectionStart = 0; @@ -191,108 +213,117 @@ testCase(`italic with a multiline selection`, function (assert, textarea) { click(`button.italic`); andThen(() => { - assert.equal(this.get('value'), `_hello_\n\n_world_\n\ntest.`); + assert.equal(this.get("value"), `_hello_\n\n_world_\n\ntest.`); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 16); }); click(`button.italic`); andThen(() => { - assert.equal(this.get('value'), `hello\n\nworld\n\ntest.`); + assert.equal(this.get("value"), `hello\n\nworld\n\ntest.`); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 12); }); }); -testCase('link modal (cancel)', function(assert) { - assert.equal(this.$('.insert-link.hidden').length, 1); +testCase("link modal (cancel)", function(assert) { + assert.equal(this.$(".insert-link.hidden").length, 1); - click('button.link'); + click("button.link"); andThen(() => { - assert.equal(this.$('.insert-link.hidden').length, 0); + assert.equal(this.$(".insert-link.hidden").length, 0); }); - click('.insert-link button.btn-danger'); + click(".insert-link button.btn-danger"); andThen(() => { - assert.equal(this.$('.insert-link.hidden').length, 1); - assert.equal(this.get('value'), 'hello world.'); + assert.equal(this.$(".insert-link.hidden").length, 1); + assert.equal(this.get("value"), "hello world."); }); }); -testCase('link modal (simple link)', function(assert, textarea) { - click('button.link'); +testCase("link modal (simple link)", function(assert, textarea) { + click("button.link"); - const url = 'http://eviltrout.com'; + const url = "http://eviltrout.com"; - fillIn('.insert-link input.link-url', url); - click('.insert-link button.btn-primary'); + fillIn(".insert-link input.link-url", url); + click(".insert-link button.btn-primary"); andThen(() => { - assert.equal(this.$('.insert-link.hidden').length, 1); - assert.equal(this.get('value'), `hello world.[${url}](${url})`); + assert.equal(this.$(".insert-link.hidden").length, 1); + assert.equal(this.get("value"), `hello world.[${url}](${url})`); assert.equal(textarea.selectionStart, 13); assert.equal(textarea.selectionEnd, 13 + url.length); }); }); -testCase('link modal auto http addition', function(assert) { - click('button.link'); - fillIn('.insert-link input.link-url', 'sam.com'); - click('.insert-link button.btn-primary'); +testCase("link modal auto http addition", function(assert) { + click("button.link"); + fillIn(".insert-link input.link-url", "sam.com"); + click(".insert-link button.btn-primary"); andThen(() => { - assert.equal(this.get('value'), `hello world.[sam.com](http://sam.com)`); + assert.equal(this.get("value"), `hello world.[sam.com](http://sam.com)`); }); }); -testCase('link modal (simple link) with selected text', function(assert, textarea) { +testCase("link modal (simple link) with selected text", function( + assert, + textarea +) { textarea.selectionStart = 0; textarea.selectionEnd = 12; - click('button.link'); + click("button.link"); andThen(() => { - assert.equal(this.$('input.link-text')[0].value, 'hello world.'); + assert.equal(this.$("input.link-text")[0].value, "hello world."); }); - fillIn('.insert-link input.link-url', 'http://eviltrout.com'); - click('.insert-link button.btn-primary'); + fillIn(".insert-link input.link-url", "http://eviltrout.com"); + click(".insert-link button.btn-primary"); andThen(() => { - assert.equal(this.$('.insert-link.hidden').length, 1); - assert.equal(this.get('value'), '[hello world.](http://eviltrout.com)'); + assert.equal(this.$(".insert-link.hidden").length, 1); + assert.equal(this.get("value"), "[hello world.](http://eviltrout.com)"); }); }); -testCase('link modal (link with description)', function(assert) { - click('button.link'); - fillIn('.insert-link input.link-url', 'http://eviltrout.com'); - fillIn('.insert-link input.link-text', 'evil trout'); - click('.insert-link button.btn-primary'); +testCase("link modal (link with description)", function(assert) { + click("button.link"); + fillIn(".insert-link input.link-url", "http://eviltrout.com"); + fillIn(".insert-link input.link-text", "evil trout"); + click(".insert-link button.btn-primary"); andThen(() => { - assert.equal(this.$('.insert-link.hidden').length, 1); - assert.equal(this.get('value'), 'hello world.[evil trout](http://eviltrout.com)'); + assert.equal(this.$(".insert-link.hidden").length, 1); + assert.equal( + this.get("value"), + "hello world.[evil trout](http://eviltrout.com)" + ); }); }); -componentTest('advanced code', { - template: '{{d-editor value=value}}', +componentTest("advanced code", { + template: "{{d-editor value=value}}", beforeEach() { - this.siteSettings.code_formatting_style = '4-spaces-indent'; - this.set('value', -` + this.siteSettings.code_formatting_style = "4-spaces-indent"; + this.set( + "value", + ` function xyz(x, y, z) { if (y === z) { return true; } } -` ); +` + ); }, test(assert) { - const textarea = this.$('textarea.d-editor-input')[0]; + const textarea = this.$("textarea.d-editor-input")[0]; textarea.selectionStart = 0; textarea.selectionEnd = textarea.value.length; - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), -` + assert.equal( + this.get("value"), + ` function xyz(x, y, z) { if (y === z) { return true; @@ -302,54 +333,52 @@ function xyz(x, y, z) { ); }); } - }); -componentTest('code button', { - template: '{{d-editor value=value}}', +componentTest("code button", { + template: "{{d-editor value=value}}", beforeEach() { - this.siteSettings.code_formatting_style = '4-spaces-indent'; + this.siteSettings.code_formatting_style = "4-spaces-indent"; }, test(assert) { - const textarea = jumpEnd(this.$('textarea.d-editor-input')[0]); + const textarea = jumpEnd(this.$("textarea.d-editor-input")[0]); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), -` ${I18n.t('composer.code_text')}` - ); + assert.equal(this.get("value"), ` ${I18n.t("composer.code_text")}`); - this.set('value', "first line\n\nsecond line\n\nthird line"); + this.set("value", "first line\n\nsecond line\n\nthird line"); textarea.selectionStart = 11; textarea.selectionEnd = 11; }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), -`first line - ${I18n.t('composer.code_text')} + assert.equal( + this.get("value"), + `first line + ${I18n.t("composer.code_text")} second line third line` ); - this.set('value', "first line\n\nsecond line\n\nthird line"); + this.set("value", "first line\n\nsecond line\n\nthird line"); }); - - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), -`first line + assert.equal( + this.get("value"), + `first line second line -third line\`${I18n.t('composer.code_title')}\`` +third line\`${I18n.t("composer.code_title")}\`` ); - this.set('value', "first line\n\nsecond line\n\nthird line"); + this.set("value", "first line\n\nsecond line\n\nthird line"); }); andThen(() => { @@ -357,16 +386,17 @@ third line\`${I18n.t('composer.code_title')}\`` textarea.selectionEnd = 5; }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), -`first\`${I18n.t('composer.code_title')}\` line + assert.equal( + this.get("value"), + `first\`${I18n.t("composer.code_title")}\` line second line third line` ); - this.set('value', "first line\n\nsecond line\n\nthird line"); + this.set("value", "first line\n\nsecond line\n\nthird line"); }); andThen(() => { @@ -374,16 +404,22 @@ third line` textarea.selectionEnd = 10; }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), "first `line`\n\nsecond line\n\nthird line"); + assert.equal( + this.get("value"), + "first `line`\n\nsecond line\n\nthird line" + ); assert.equal(textarea.selectionStart, 7); assert.equal(textarea.selectionEnd, 11); }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), "first line\n\nsecond line\n\nthird line"); + assert.equal( + this.get("value"), + "first line\n\nsecond line\n\nthird line" + ); assert.equal(textarea.selectionStart, 6); assert.equal(textarea.selectionEnd, 10); @@ -391,35 +427,42 @@ third line` textarea.selectionEnd = 23; }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), " first line\n\n second line\n\nthird line"); + assert.equal( + this.get("value"), + " first line\n\n second line\n\nthird line" + ); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 31); }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), "first line\n\nsecond line\n\nthird line"); + assert.equal( + this.get("value"), + "first line\n\nsecond line\n\nthird line" + ); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 23); }); } }); -componentTest('code fences', { - template: '{{d-editor value=value}}', +componentTest("code fences", { + template: "{{d-editor value=value}}", beforeEach() { - this.set('value', ''); + this.set("value", ""); }, test(assert) { - const textarea = jumpEnd(this.$('textarea.d-editor-input')[0]); + const textarea = jumpEnd(this.$("textarea.d-editor-input")[0]); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), -`\`\`\` + assert.equal( + this.get("value"), + `\`\`\` ${I18n.t("composer.paste_code_text")} \`\`\`` ); @@ -427,16 +470,17 @@ ${I18n.t("composer.paste_code_text")} assert.equal(textarea.selectionStart, 4); assert.equal(textarea.selectionEnd, 27); - this.set('value', 'first line\nsecond line\nthird line'); + this.set("value", "first line\nsecond line\nthird line"); textarea.selectionStart = 0; textarea.selectionEnd = textarea.value.length; }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), -`\`\`\` + assert.equal( + this.get("value"), + `\`\`\` first line second line third line @@ -447,33 +491,38 @@ third line assert.equal(textarea.selectionStart, textarea.value.length); assert.equal(textarea.selectionEnd, textarea.value.length); - this.set('value', 'first line\nsecond line\nthird line'); + this.set("value", "first line\nsecond line\nthird line"); textarea.selectionStart = 0; textarea.selectionEnd = 0; }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), -`\`${I18n.t('composer.code_title')}\`first line + assert.equal( + this.get("value"), + `\`${I18n.t("composer.code_title")}\`first line second line third line` ); assert.equal(textarea.selectionStart, 1); - assert.equal(textarea.selectionEnd, I18n.t('composer.code_title').length + 1); + assert.equal( + textarea.selectionEnd, + I18n.t("composer.code_title").length + 1 + ); - this.set('value', 'first line\nsecond line\nthird line'); + this.set("value", "first line\nsecond line\nthird line"); textarea.selectionStart = 0; textarea.selectionEnd = 10; }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), -`\`first line\` + assert.equal( + this.get("value"), + `\`first line\` second line third line` ); @@ -481,16 +530,17 @@ third line` assert.equal(textarea.selectionStart, 1); assert.equal(textarea.selectionEnd, 11); - this.set('value', 'first line\nsecond line\nthird line'); + this.set("value", "first line\nsecond line\nthird line"); textarea.selectionStart = 0; textarea.selectionEnd = 23; }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), -`\`\`\` + assert.equal( + this.get("value"), + `\`\`\` first line second line \`\`\` @@ -500,15 +550,18 @@ third line` assert.equal(textarea.selectionStart, 30); assert.equal(textarea.selectionEnd, 30); - this.set('value', 'first line\nsecond line\nthird line'); + this.set("value", "first line\nsecond line\nthird line"); textarea.selectionStart = 6; textarea.selectionEnd = 17; }); - click('button.code'); + click("button.code"); andThen(() => { - assert.equal(this.get('value'), `first \n\`\`\`\nline\nsecond\n\`\`\`\n line\nthird line`); + assert.equal( + this.get("value"), + `first \n\`\`\`\nline\nsecond\n\`\`\`\n line\nthird line` + ); assert.equal(textarea.selectionStart, 27); assert.equal(textarea.selectionEnd, 27); @@ -516,71 +569,69 @@ third line` } }); - componentTest("quote button - empty lines", { - template: '{{d-editor value=value composerEvents=true}}', + template: "{{d-editor value=value composerEvents=true}}", beforeEach() { - this.set('value', "one\n\ntwo\n\nthree"); + this.set("value", "one\n\ntwo\n\nthree"); }, test(assert) { - const textarea = jumpEnd(this.$('textarea.d-editor-input')[0]); + const textarea = jumpEnd(this.$("textarea.d-editor-input")[0]); andThen(() => { textarea.selectionStart = 0; }); - click('button.quote'); + click("button.quote"); andThen(() => { - assert.equal(this.get('value'), "> one\n> \n> two\n> \n> three"); + assert.equal(this.get("value"), "> one\n> \n> two\n> \n> three"); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 25); }); - click('button.quote'); + click("button.quote"); andThen(() => { - assert.equal(this.get('value'), "one\n\ntwo\n\nthree"); + assert.equal(this.get("value"), "one\n\ntwo\n\nthree"); }); } }); componentTest("quote button - selecting empty lines", { - template: '{{d-editor value=value composerEvents=true}}', + template: "{{d-editor value=value composerEvents=true}}", beforeEach() { - this.set('value', "one\n\n\n\ntwo"); + this.set("value", "one\n\n\n\ntwo"); }, test(assert) { - const textarea = jumpEnd(this.$('textarea.d-editor-input')[0]); + const textarea = jumpEnd(this.$("textarea.d-editor-input")[0]); andThen(() => { textarea.selectionStart = 6; textarea.selectionEnd = 10; }); - click('button.quote'); + click("button.quote"); andThen(() => { - assert.equal(this.get('value'), "one\n\n\n> \n> two"); + assert.equal(this.get("value"), "one\n\n\n> \n> two"); }); } }); -testCase('quote button', function(assert, textarea) { - +testCase("quote button", function(assert, textarea) { andThen(() => { textarea.selectionStart = 6; textarea.selectionEnd = 9; }); - click('button.quote'); + click("button.quote"); andThen(() => { - assert.equal(this.get('value'), 'hello\n\n> wor\n\nld.'); + assert.equal(this.get("value"), "hello\n\n> wor\n\nld."); assert.equal(textarea.selectionStart, 7); assert.equal(textarea.selectionEnd, 12); }); - click('button.quote'); + click("button.quote"); andThen(() => { - assert.equal(this.get('value'), 'hello\n\nwor\n\nld.'); + assert.equal(this.get("value"), "hello\n\nwor\n\nld."); assert.equal(textarea.selectionStart, 7); assert.equal(textarea.selectionEnd, 10); }); @@ -590,26 +641,25 @@ testCase('quote button', function(assert, textarea) { textarea.selectionEnd = 15; }); - click('button.quote'); + click("button.quote"); andThen(() => { - assert.equal(this.get('value'), 'hello\n\nwor\n\nld.\n\n> Blockquote'); + assert.equal(this.get("value"), "hello\n\nwor\n\nld.\n\n> Blockquote"); }); - }); testCase(`bullet button with no selection`, function(assert, textarea) { - const example = I18n.t('composer.list_item'); + const example = I18n.t("composer.list_item"); click(`button.bullet`); andThen(() => { - assert.equal(this.get('value'), `hello world.\n\n* ${example}`); + assert.equal(this.get("value"), `hello world.\n\n* ${example}`); assert.equal(textarea.selectionStart, 14); assert.equal(textarea.selectionEnd, 16 + example.length); }); click(`button.bullet`); andThen(() => { - assert.equal(this.get('value'), `hello world.\n\n${example}`); + assert.equal(this.get("value"), `hello world.\n\n${example}`); }); }); @@ -619,21 +669,24 @@ testCase(`bullet button with a selection`, function(assert, textarea) { click(`button.bullet`); andThen(() => { - assert.equal(this.get('value'), `hello\n\n* world\n\n.`); + assert.equal(this.get("value"), `hello\n\n* world\n\n.`); assert.equal(textarea.selectionStart, 7); assert.equal(textarea.selectionEnd, 14); }); click(`button.bullet`); andThen(() => { - assert.equal(this.get('value'), `hello\n\nworld\n\n.`); + assert.equal(this.get("value"), `hello\n\nworld\n\n.`); assert.equal(textarea.selectionStart, 7); assert.equal(textarea.selectionEnd, 12); }); }); -testCase(`bullet button with a multiple line selection`, function(assert, textarea) { - this.set('value', "* Hello\n\nWorld\n\nEvil"); +testCase(`bullet button with a multiple line selection`, function( + assert, + textarea +) { + this.set("value", "* Hello\n\nWorld\n\nEvil"); andThen(() => { textarea.selectionStart = 0; @@ -642,32 +695,32 @@ testCase(`bullet button with a multiple line selection`, function(assert, textar click(`button.bullet`); andThen(() => { - assert.equal(this.get('value'), "Hello\n\nWorld\n\nEvil"); + assert.equal(this.get("value"), "Hello\n\nWorld\n\nEvil"); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 18); }); click(`button.bullet`); andThen(() => { - assert.equal(this.get('value'), "* Hello\n\n* World\n\n* Evil"); + assert.equal(this.get("value"), "* Hello\n\n* World\n\n* Evil"); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 24); }); }); testCase(`list button with no selection`, function(assert, textarea) { - const example = I18n.t('composer.list_item'); + const example = I18n.t("composer.list_item"); click(`button.list`); andThen(() => { - assert.equal(this.get('value'), `hello world.\n\n1. ${example}`); + assert.equal(this.get("value"), `hello world.\n\n1. ${example}`); assert.equal(textarea.selectionStart, 14); assert.equal(textarea.selectionEnd, 17 + example.length); }); click(`button.list`); andThen(() => { - assert.equal(this.get('value'), `hello world.\n\n${example}`); + assert.equal(this.get("value"), `hello world.\n\n${example}`); assert.equal(textarea.selectionStart, 14); assert.equal(textarea.selectionEnd, 14 + example.length); }); @@ -679,21 +732,21 @@ testCase(`list button with a selection`, function(assert, textarea) { click(`button.list`); andThen(() => { - assert.equal(this.get('value'), `hello\n\n1. world\n\n.`); + assert.equal(this.get("value"), `hello\n\n1. world\n\n.`); assert.equal(textarea.selectionStart, 7); assert.equal(textarea.selectionEnd, 15); }); click(`button.list`); andThen(() => { - assert.equal(this.get('value'), `hello\n\nworld\n\n.`); + assert.equal(this.get("value"), `hello\n\nworld\n\n.`); assert.equal(textarea.selectionStart, 7); assert.equal(textarea.selectionEnd, 12); }); }); testCase(`list button with line sequence`, function(assert, textarea) { - this.set('value', "Hello\n\nWorld\n\nEvil"); + this.set("value", "Hello\n\nWorld\n\nEvil"); andThen(() => { textarea.selectionStart = 0; @@ -702,46 +755,45 @@ testCase(`list button with line sequence`, function(assert, textarea) { click(`button.list`); andThen(() => { - assert.equal(this.get('value'), "1. Hello\n\n2. World\n\n3. Evil"); + assert.equal(this.get("value"), "1. Hello\n\n2. World\n\n3. Evil"); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 27); }); click(`button.list`); andThen(() => { - assert.equal(this.get('value'), "Hello\n\nWorld\n\nEvil"); + assert.equal(this.get("value"), "Hello\n\nWorld\n\nEvil"); assert.equal(textarea.selectionStart, 0); assert.equal(textarea.selectionEnd, 18); }); }); -componentTest('clicking the toggle-direction button toggles the direction', { - template: '{{d-editor value=value}}', +componentTest("clicking the toggle-direction button toggles the direction", { + template: "{{d-editor value=value}}", beforeEach() { this.siteSettings.support_mixed_text_direction = true; this.siteSettings.default_locale = "en"; }, test(assert) { - const textarea = this.$('textarea.d-editor-input'); - click('button.toggle-direction'); + const textarea = this.$("textarea.d-editor-input"); + click("button.toggle-direction"); andThen(() => { - assert.equal(textarea.attr('dir'), 'rtl'); + assert.equal(textarea.attr("dir"), "rtl"); }); - click('button.toggle-direction'); + click("button.toggle-direction"); andThen(() => { - assert.equal(textarea.attr('dir'), 'ltr'); + assert.equal(textarea.attr("dir"), "ltr"); }); } }); testCase(`doesn't jump to bottom with long text`, function(assert, textarea) { - - let longText = 'hello world.'; - for (let i=0; i<8; i++) { + let longText = "hello world."; + for (let i = 0; i < 8; i++) { longText = longText + longText; } - this.set('value', longText); + this.set("value", longText); andThen(() => { $(textarea).scrollTop(0); @@ -749,110 +801,127 @@ testCase(`doesn't jump to bottom with long text`, function(assert, textarea) { textarea.selectionEnd = 3; }); - click('button.bold'); + click("button.bold"); andThen(() => { - assert.equal($(textarea).scrollTop(), 0, 'it stays scrolled up'); + assert.equal($(textarea).scrollTop(), 0, "it stays scrolled up"); }); }); -componentTest('emoji', { - template: '{{d-editor value=value}}', +componentTest("emoji", { + template: "{{d-editor value=value}}", beforeEach() { // Test adding a custom button - withPluginApi('0.1', api => { + withPluginApi("0.1", api => { api.onToolbarCreate(toolbar => { toolbar.addButton({ - id: 'emoji', - group: 'extras', - icon: 'smile-o', - action: 'emoji' + id: "emoji", + group: "extras", + icon: "smile-o", + action: "emoji" }); }); }); - this.set('value', 'hello world.'); + this.set("value", "hello world."); }, test(assert) { - jumpEnd(this.$('textarea.d-editor-input')[0]); - click('button.emoji'); + jumpEnd(this.$("textarea.d-editor-input")[0]); + click("button.emoji"); - click('.emoji-picker .section[data-section="people"] button.emoji[title="grinning"]'); + click( + '.emoji-picker .section[data-section="people"] button.emoji[title="grinning"]' + ); andThen(() => { - assert.equal(this.get('value'), 'hello world.:grinning:'); + assert.equal(this.get("value"), "hello world.:grinning:"); }); } }); testCase("replace-text event by default", function(assert) { - this.set('value', "red green blue"); + this.set("value", "red green blue"); andThen(() => { - this.container.lookup('app-events:main').trigger('composer:replace-text', 'green', 'yellow'); + this.container + .lookup("app-events:main") + .trigger("composer:replace-text", "green", "yellow"); }); andThen(() => { - assert.equal(this.get('value'), 'red green blue'); + assert.equal(this.get("value"), "red green blue"); }); }); composerTestCase("replace-text event for composer", function(assert) { - this.set('value', "red green blue"); + this.set("value", "red green blue"); andThen(() => { - this.container.lookup('app-events:main').trigger('composer:replace-text', 'green', 'yellow'); + this.container + .lookup("app-events:main") + .trigger("composer:replace-text", "green", "yellow"); }); andThen(() => { - assert.equal(this.get('value'), 'red yellow blue'); + assert.equal(this.get("value"), "red yellow blue"); }); }); - (() => { // Tests to check cursor/selection after replace-text event. - const BEFORE = 'red green blue'; - const NEEDLE = 'green'; - const REPLACE = 'yellow'; + const BEFORE = "red green blue"; + const NEEDLE = "green"; + const REPLACE = "yellow"; const AFTER = BEFORE.replace(NEEDLE, REPLACE); const CASES = [ { - description: 'cursor at start remains there', + description: "cursor at start remains there", before: [0, 0], after: [0, 0] - },{ - description: 'cursor before needle becomes cursor before replacement', + }, + { + description: "cursor before needle becomes cursor before replacement", before: [BEFORE.indexOf(NEEDLE), 0], after: [AFTER.indexOf(REPLACE), 0] - },{ - description: 'cursor at needle start + 1 moves behind replacement', + }, + { + description: "cursor at needle start + 1 moves behind replacement", before: [BEFORE.indexOf(NEEDLE) + 1, 0], after: [AFTER.indexOf(REPLACE) + REPLACE.length, 0] - },{ - description: 'cursor at needle end - 1 stays behind replacement', + }, + { + description: "cursor at needle end - 1 stays behind replacement", before: [BEFORE.indexOf(NEEDLE) + NEEDLE.length - 1, 0], after: [AFTER.indexOf(REPLACE) + REPLACE.length, 0] - },{ - description: 'cursor behind needle becomes cursor behind replacement', + }, + { + description: "cursor behind needle becomes cursor behind replacement", before: [BEFORE.indexOf(NEEDLE) + NEEDLE.length, 0], after: [AFTER.indexOf(REPLACE) + REPLACE.length, 0] - },{ - description: 'cursor at end remains there', + }, + { + description: "cursor at end remains there", before: [BEFORE.length, 0], after: [AFTER.length, 0] - },{ - description: 'selection spanning needle start becomes selection until replacement start', + }, + { + description: + "selection spanning needle start becomes selection until replacement start", before: [BEFORE.indexOf(NEEDLE) - 1, 2], after: [AFTER.indexOf(REPLACE) - 1, 1] - },{ - description: 'selection spanning needle end becomes selection from replacement end', + }, + { + description: + "selection spanning needle end becomes selection from replacement end", before: [BEFORE.indexOf(NEEDLE) + NEEDLE.length - 1, 2], after: [AFTER.indexOf(REPLACE) + REPLACE.length, 1] - },{ - description: 'selection spanning needle becomes selection spanning replacement', + }, + { + description: + "selection spanning needle becomes selection spanning replacement", before: [BEFORE.indexOf(NEEDLE) - 1, NEEDLE.length + 2], after: [AFTER.indexOf(REPLACE) - 1, REPLACE.length + 2] - },{ - description: 'complete selection remains complete', + }, + { + description: "complete selection remains complete", before: [0, BEFORE.length], after: [0, AFTER.length] } @@ -873,25 +942,33 @@ composerTestCase("replace-text event for composer", function(assert) { return [ '"', text.substr(0, start), - '<', + "<", text.substr(start, len), - '>', - text.substr(start+len), - '"', - ].join(''); + ">", + text.substr(start + len), + '"' + ].join(""); } for (let i = 0; i < CASES.length; i++) { const CASE = CASES[i]; - composerTestCase(`replace-text event: ${CASE.description}`, function(assert, textarea) { - this.set('value', BEFORE); + composerTestCase(`replace-text event: ${CASE.description}`, function( + assert, + textarea + ) { + this.set("value", BEFORE); setSelection(textarea, CASE.before); andThen(() => { - this.container.lookup('app-events:main').trigger('composer:replace-text', 'green', 'yellow'); + this.container + .lookup("app-events:main") + .trigger("composer:replace-text", "green", "yellow"); }); andThen(() => { let expect = formatTextWithSelection(AFTER, CASE.after); - let actual = formatTextWithSelection(this.get('value'), getSelection(textarea)); + let actual = formatTextWithSelection( + this.get("value"), + getSelection(textarea) + ); assert.equal(actual, expect); }); }); diff --git a/test/javascripts/components/d-icon-test.js.es6 b/test/javascripts/components/d-icon-test.js.es6 index d4f3d489aa5..bcaca7f7533 100644 --- a/test/javascripts/components/d-icon-test.js.es6 +++ b/test/javascripts/components/d-icon-test.js.es6 @@ -1,21 +1,28 @@ -import componentTest from 'helpers/component-test'; +import componentTest from "helpers/component-test"; -moduleForComponent('d-icon', {integration: true}); +moduleForComponent("d-icon", { integration: true }); -componentTest('default', { +componentTest("default", { template: '{{d-icon "bars"}}', test(assert) { - const html = this.$().html().trim(); + const html = this.$() + .html() + .trim(); assert.equal(html, ''); } }); -componentTest('with replacement', { +componentTest("with replacement", { template: '{{d-icon "d-watching"}}', test(assert) { - const html = this.$().html().trim(); - assert.equal(html, ''); + const html = this.$() + .html() + .trim(); + assert.equal( + html, + '' + ); } }); diff --git a/test/javascripts/components/group-membership-button-test.js.es6 b/test/javascripts/components/group-membership-button-test.js.es6 index a321d7ed4f8..1c468551506 100644 --- a/test/javascripts/components/group-membership-button-test.js.es6 +++ b/test/javascripts/components/group-membership-button-test.js.es6 @@ -1,67 +1,73 @@ -moduleFor('component:group-membership-button'); +moduleFor("component:group-membership-button"); -QUnit.test('canJoinGroup', function(assert) { +QUnit.test("canJoinGroup", function(assert) { this.subject().setProperties({ model: { public_admission: false, is_group_user: true } }); assert.equal( - this.subject().get("canJoinGroup"), false, + this.subject().get("canJoinGroup"), + false, "can't join group if public_admission is false" ); this.subject().set("model.public_admission", true); assert.equal( - this.subject().get("canJoinGroup"), false, + this.subject().get("canJoinGroup"), + false, "can't join group if user is already in the group" ); this.subject().set("model.is_group_user", false); assert.equal( - this.subject().get("canJoinGroup"), true, + this.subject().get("canJoinGroup"), + true, "allowed to join group" ); }); -QUnit.test('canLeaveGroup', function(assert) { +QUnit.test("canLeaveGroup", function(assert) { this.subject().setProperties({ model: { public_exit: false, is_group_user: false } }); assert.equal( - this.subject().get("canLeaveGroup"), false, + this.subject().get("canLeaveGroup"), + false, "can't leave group if public_exit is false" ); this.subject().set("model.public_exit", true); assert.equal( - this.subject().get("canLeaveGroup"), false, + this.subject().get("canLeaveGroup"), + false, "can't leave group if user is not in the group" ); this.subject().set("model.is_group_user", true); assert.equal( - this.subject().get("canLeaveGroup"), true, + this.subject().get("canLeaveGroup"), + true, "allowed to leave group" ); }); -QUnit.test('userIsGroupUser', function(assert) { +QUnit.test("userIsGroupUser", function(assert) { this.subject().setProperties({ model: { is_group_user: true } }); - assert.equal(this.subject().get('userIsGroupUser'), true); + assert.equal(this.subject().get("userIsGroupUser"), true); - this.subject().set('model.is_group_user', false); + this.subject().set("model.is_group_user", false); - assert.equal(this.subject().get('userIsGroupUser'), false); + assert.equal(this.subject().get("userIsGroupUser"), false); - this.subject().set('model.is_group_user', null); + this.subject().set("model.is_group_user", null); - assert.equal(this.subject().get('userIsGroupUser'), false); + assert.equal(this.subject().get("userIsGroupUser"), false); }); diff --git a/test/javascripts/components/keyboard-shortcuts-test.js.es6 b/test/javascripts/components/keyboard-shortcuts-test.js.es6 index 642f35d5432..d127c89fa6b 100644 --- a/test/javascripts/components/keyboard-shortcuts-test.js.es6 +++ b/test/javascripts/components/keyboard-shortcuts-test.js.es6 @@ -1,7 +1,7 @@ -import DiscourseURL from 'discourse/lib/url'; +import DiscourseURL from "discourse/lib/url"; var testMouseTrap; -import KeyboardShortcuts from 'discourse/lib/keyboard-shortcuts'; +import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts"; QUnit.module("lib:keyboard-shortcuts", { beforeEach() { @@ -15,8 +15,7 @@ QUnit.module("lib:keyboard-shortcuts", { if (_.isArray(bindings)) { _.each(bindings, registerBinding, this); - } - else { + } else { registerBinding(bindings); } }, @@ -28,38 +27,39 @@ QUnit.module("lib:keyboard-shortcuts", { sandbox.stub(DiscourseURL, "routeTo"); - $("#qunit-fixture").html([ - "
", - "" + - "
", - "
", - " ", - "
", - "", - " ", - "
", - " ", - "
", - "", - "
", - "", - "
", - "
", - "
", - "
", - "
" - ].join("\n")); + $("#qunit-fixture").html( + [ + "
", + "" + "
", + "
", + " ", + "
", + "", + " ", + "
", + " ", + "
", + "", + "
", + "", + "
", + "
", + "
", + "
", + "
" + ].join("\n") + ); }, afterEach() { @@ -93,9 +93,13 @@ _.each(clickBindings, function(selector, binding) { assert.ok(true, selector + " was clicked"); }); - _.each(bindings, function(b) { - testMouseTrap.trigger(b); - }, this); + _.each( + bindings, + function(b) { + testMouseTrap.trigger(b); + }, + this + ); }); }); @@ -115,14 +119,14 @@ _.each(functionBindings, function(func, binding) { }); QUnit.test("selectDown calls _moveSelection with 1", assert => { - var spy = sandbox.spy(KeyboardShortcuts, '_moveSelection'); + var spy = sandbox.spy(KeyboardShortcuts, "_moveSelection"); KeyboardShortcuts.selectDown(); assert.ok(spy.calledWith(1), "_moveSelection is called with 1"); }); QUnit.test("selectUp calls _moveSelection with -1", assert => { - var spy = sandbox.spy(KeyboardShortcuts, '_moveSelection'); + var spy = sandbox.spy(KeyboardShortcuts, "_moveSelection"); KeyboardShortcuts.selectUp(); assert.ok(spy.calledWith(-1), "_moveSelection is called with -1"); @@ -130,7 +134,7 @@ QUnit.test("selectUp calls _moveSelection with -1", assert => { QUnit.test("goBack calls history.back", assert => { var called = false; - sandbox.stub(history, 'back', function() { + sandbox.stub(history, "back", function() { called = true; }); @@ -139,14 +143,14 @@ QUnit.test("goBack calls history.back", assert => { }); QUnit.test("nextSection calls _changeSection with 1", assert => { - var spy = sandbox.spy(KeyboardShortcuts, '_changeSection'); + var spy = sandbox.spy(KeyboardShortcuts, "_changeSection"); KeyboardShortcuts.nextSection(); assert.ok(spy.calledWith(1), "_changeSection is called with 1"); }); QUnit.test("prevSection calls _changeSection with -1", assert => { - var spy = sandbox.spy(KeyboardShortcuts, '_changeSection'); + var spy = sandbox.spy(KeyboardShortcuts, "_changeSection"); KeyboardShortcuts.prevSection(); assert.ok(spy.calledWith(-1), "_changeSection is called with -1"); diff --git a/test/javascripts/components/list-setting-test.js.es6 b/test/javascripts/components/list-setting-test.js.es6 index d45e02429cb..a69d17a8f90 100644 --- a/test/javascripts/components/list-setting-test.js.es6 +++ b/test/javascripts/components/list-setting-test.js.es6 @@ -1,85 +1,108 @@ -import componentTest from 'helpers/component-test'; +import componentTest from "helpers/component-test"; -moduleForComponent('list-setting', {integration: true}); +moduleForComponent("list-setting", { integration: true }); -componentTest('default', { - template: '{{list-setting settingValue=settingValue choices=choices}}', +componentTest("default", { + template: "{{list-setting settingValue=settingValue choices=choices}}", beforeEach() { - this.set('settingValue', 'bold|italic'); - this.set('choices', ['bold', 'italic', 'underline']); + this.set("settingValue", "bold|italic"); + this.set("choices", ["bold", "italic", "underline"]); }, test(assert) { andThen(() => { - assert.equal(selectKit().header().title(), 'bold,italic'); - assert.equal(selectKit().header().value(), 'bold,italic'); + assert.equal( + selectKit() + .header() + .title(), + "bold,italic" + ); + assert.equal( + selectKit() + .header() + .value(), + "bold,italic" + ); }); } }); -componentTest('with empty string as value', { - template: '{{list-setting settingValue=settingValue}}', +componentTest("with empty string as value", { + template: "{{list-setting settingValue=settingValue}}", beforeEach() { - this.set('settingValue', ''); + this.set("settingValue", ""); }, test(assert) { andThen(() => { - assert.equal(selectKit().header().value(), ""); + assert.equal( + selectKit() + .header() + .value(), + "" + ); }); } }); -componentTest('with only setting value', { - template: '{{list-setting settingValue=settingValue}}', +componentTest("with only setting value", { + template: "{{list-setting settingValue=settingValue}}", beforeEach() { - this.set('settingValue', 'bold|italic'); + this.set("settingValue", "bold|italic"); }, test(assert) { andThen(() => { - assert.equal(selectKit().header().value(), 'bold,italic'); + assert.equal( + selectKit() + .header() + .value(), + "bold,italic" + ); }); } }); -componentTest('interactions', { - template: '{{list-setting settingValue=settingValue choices=choices}}', +componentTest("interactions", { + template: "{{list-setting settingValue=settingValue choices=choices}}", beforeEach() { - this.set('settingValue', 'bold|italic'); - this.set('choices', ['bold', 'italic', 'underline']); + this.set("settingValue", "bold|italic"); + this.set("choices", ["bold", "italic", "underline"]); }, test(assert) { const listSetting = selectKit(); - listSetting.expand().selectRowByValue('underline'); + listSetting.expand().selectRowByValue("underline"); andThen(() => { - assert.equal(listSetting.header().value(), 'bold,italic,underline'); + assert.equal(listSetting.header().value(), "bold,italic,underline"); }); - listSetting.expand().fillInFilter('strike'); + listSetting.expand().fillInFilter("strike"); andThen(() => { - assert.equal(listSetting.highlightedRow().value(), 'strike'); + assert.equal(listSetting.highlightedRow().value(), "strike"); }); listSetting.keyboard().enter(); andThen(() => { - assert.equal(listSetting.header().value(), 'bold,italic,underline,strike'); + assert.equal( + listSetting.header().value(), + "bold,italic,underline,strike" + ); }); listSetting.keyboard().backspace(); listSetting.keyboard().backspace(); andThen(() => { - assert.equal(listSetting.header().value(), 'bold,italic,underline'); + assert.equal(listSetting.header().value(), "bold,italic,underline"); }); } }); diff --git a/test/javascripts/components/multi-select-test.js.es6 b/test/javascripts/components/multi-select-test.js.es6 index c829b3ea534..cf37448d71b 100644 --- a/test/javascripts/components/multi-select-test.js.es6 +++ b/test/javascripts/components/multi-select-test.js.es6 @@ -1,203 +1,301 @@ -import componentTest from 'helpers/component-test'; -moduleForComponent('multi-select', { +import componentTest from "helpers/component-test"; +moduleForComponent("multi-select", { integration: true, beforeEach: function() { - this.set('subject', selectKit()); + this.set("subject", selectKit()); } }); -componentTest('with objects and values', { - template: '{{multi-select content=items values=values}}', +componentTest("with objects and values", { + template: "{{multi-select content=items values=values}}", beforeEach() { - this.set('items', [{id: 1, name: 'hello'}, {id: 2, name: 'world'}]); - this.set('values', [1, 2]); + this.set("items", [{ id: 1, name: "hello" }, { id: 2, name: "world" }]); + this.set("values", [1, 2]); }, test(assert) { andThen(() => { - assert.equal(this.get('subject').header().value(), '1,2'); + assert.equal( + this.get("subject") + .header() + .value(), + "1,2" + ); }); } }); -componentTest('with title', { +componentTest("with title", { template: '{{multi-select title=(i18n "test.title")}}', beforeEach() { - I18n.translations[I18n.locale].js.test = {title: 'My title'}; + I18n.translations[I18n.locale].js.test = { title: "My title" }; }, test(assert) { - andThen(() => assert.equal(selectKit().header().title(), 'My title') ); + andThen(() => + assert.equal( + selectKit() + .header() + .title(), + "My title" + ) + ); } }); - -componentTest('interactions', { - template: '{{multi-select none=none content=items values=values}}', +componentTest("interactions", { + template: "{{multi-select none=none content=items values=values}}", beforeEach() { - I18n.translations[I18n.locale].js.test = {none: 'none'}; - this.set('items', [{id: 1, name: 'regis'}, {id: 2, name: 'sam'}, {id: 3, name: 'robin'}]); - this.set('values', [1, 2]); + I18n.translations[I18n.locale].js.test = { none: "none" }; + this.set("items", [ + { id: 1, name: "regis" }, + { id: 2, name: "sam" }, + { id: 3, name: "robin" } + ]); + this.set("values", [1, 2]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { assert.equal( - this.get('subject').highlightedRow().name(), - 'robin', - 'it highlights the first content row' + this.get("subject") + .highlightedRow() + .name(), + "robin", + "it highlights the first content row" ); }); - this.set('none', 'test.none'); + this.set("none", "test.none"); andThen(() => { - assert.ok(this.get('subject').noneRow().exists()); + assert.ok( + this.get("subject") + .noneRow() + .exists() + ); assert.equal( - this.get('subject').highlightedRow().name(), - 'robin', - 'it highlights the first content row' + this.get("subject") + .highlightedRow() + .name(), + "robin", + "it highlights the first content row" ); }); - this.get('subject').selectRowByValue(3); - this.get('subject').expand(); + this.get("subject").selectRowByValue(3); + this.get("subject").expand(); andThen(() => { assert.equal( - this.get('subject').highlightedRow().name(), - 'none', - 'it highlights none row if no content' + this.get("subject") + .highlightedRow() + .name(), + "none", + "it highlights none row if no content" ); }); - this.get('subject').fillInFilter('joffrey'); + this.get("subject").fillInFilter("joffrey"); andThen(() => { assert.equal( - this.get('subject').highlightedRow().name(), - 'joffrey', - 'it highlights create row when filling filter' + this.get("subject") + .highlightedRow() + .name(), + "joffrey", + "it highlights create row when filling filter" ); }); - this.get('subject').keyboard().enter(); + this.get("subject") + .keyboard() + .enter(); andThen(() => { assert.equal( - this.get('subject').highlightedRow().name(), - 'none', - 'it highlights none row after creating content and no content left' + this.get("subject") + .highlightedRow() + .name(), + "none", + "it highlights none row after creating content and no content left" ); }); - this.get('subject').keyboard().backspace(); + this.get("subject") + .keyboard() + .backspace(); andThen(() => { - const $lastSelectedName = this.get('subject').header().el().find('.selected-name').last(); - assert.equal($lastSelectedName.attr('data-name'), 'joffrey'); - assert.ok($lastSelectedName.hasClass('is-highlighted'), 'it highlights the last selected name when using backspace'); + const $lastSelectedName = this.get("subject") + .header() + .el() + .find(".selected-name") + .last(); + assert.equal($lastSelectedName.attr("data-name"), "joffrey"); + assert.ok( + $lastSelectedName.hasClass("is-highlighted"), + "it highlights the last selected name when using backspace" + ); }); - this.get('subject').keyboard().backspace(); + this.get("subject") + .keyboard() + .backspace(); andThen(() => { - const $lastSelectedName = this.get('subject').header().el().find('.selected-name').last(); - assert.equal($lastSelectedName.attr('data-name'), 'robin', 'it removes the previous highlighted selected content'); - assert.notOk(this.get('subject').rowByValue('joffrey').exists(), 'generated content shouldn’t appear in content when removed'); + const $lastSelectedName = this.get("subject") + .header() + .el() + .find(".selected-name") + .last(); + assert.equal( + $lastSelectedName.attr("data-name"), + "robin", + "it removes the previous highlighted selected content" + ); + assert.notOk( + this.get("subject") + .rowByValue("joffrey") + .exists(), + "generated content shouldn’t appear in content when removed" + ); }); - this.get('subject').keyboard().selectAll(); + this.get("subject") + .keyboard() + .selectAll(); andThen(() => { - const $highlightedSelectedNames = this.get('subject').header().el().find('.selected-name.is-highlighted'); - assert.equal($highlightedSelectedNames.length, 3, 'it highlights each selected name'); + const $highlightedSelectedNames = this.get("subject") + .header() + .el() + .find(".selected-name.is-highlighted"); + assert.equal( + $highlightedSelectedNames.length, + 3, + "it highlights each selected name" + ); }); - this.get('subject').keyboard().backspace(); + this.get("subject") + .keyboard() + .backspace(); andThen(() => { - const $selectedNames = this.get('subject').header().el().find('.selected-name'); - assert.equal($selectedNames.length, 0, 'it removed all selected content'); + const $selectedNames = this.get("subject") + .header() + .el() + .find(".selected-name"); + assert.equal($selectedNames.length, 0, "it removed all selected content"); }); andThen(() => { - assert.ok(this.get('subject').isFocused()); - assert.ok(this.get('subject').isExpanded()); + assert.ok(this.get("subject").isFocused()); + assert.ok(this.get("subject").isExpanded()); }); - this.get('subject').keyboard().escape(); + this.get("subject") + .keyboard() + .escape(); andThen(() => { - assert.ok(this.get('subject').isFocused()); - assert.notOk(this.get('subject').isExpanded()); + assert.ok(this.get("subject").isFocused()); + assert.notOk(this.get("subject").isExpanded()); }); - this.get('subject').keyboard().escape(); + this.get("subject") + .keyboard() + .escape(); andThen(() => { - assert.notOk(this.get('subject').isFocused()); - assert.notOk(this.get('subject').isExpanded()); + assert.notOk(this.get("subject").isFocused()); + assert.notOk(this.get("subject").isExpanded()); }); } }); -componentTest('with limitMatches', { - template: '{{multi-select content=content limitMatches=2}}', +componentTest("with limitMatches", { + template: "{{multi-select content=content limitMatches=2}}", beforeEach() { - this.set('content', ['sam', 'jeff', 'neil']); + this.set("content", ["sam", "jeff", "neil"]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); - andThen(() => assert.equal(this.get('subject').el().find(".select-kit-row").length, 2)); + andThen(() => + assert.equal( + this.get("subject") + .el() + .find(".select-kit-row").length, + 2 + ) + ); } }); -componentTest('with minimum', { - template: '{{multi-select content=content minimum=1}}', +componentTest("with minimum", { + template: "{{multi-select content=content minimum=1}}", beforeEach() { - this.set('content', ['sam', 'jeff', 'neil']); + this.set("content", ["sam", "jeff", "neil"]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); - andThen(() => assert.equal(this.get('subject').validationMessage(), 'Select at least 1 item.')); + andThen(() => + assert.equal( + this.get("subject").validationMessage(), + "Select at least 1 item." + ) + ); - this.get('subject').selectRowByValue('sam'); + this.get("subject").selectRowByValue("sam"); andThen(() => { - assert.equal(this.get('subject').header().label(), 'sam'); + assert.equal( + this.get("subject") + .header() + .label(), + "sam" + ); }); } }); -componentTest('with minimumLabel', { - template: '{{multi-select content=content minimum=1 minimumLabel="test.minimum"}}', +componentTest("with minimumLabel", { + template: + '{{multi-select content=content minimum=1 minimumLabel="test.minimum"}}', beforeEach() { - I18n.translations[I18n.locale].js.test = { minimum: 'min %{count}' }; - this.set('content', ['sam', 'jeff', 'neil']); + I18n.translations[I18n.locale].js.test = { minimum: "min %{count}" }; + this.set("content", ["sam", "jeff", "neil"]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); - andThen(() => assert.equal(this.get('subject').validationMessage(), 'min 1')); + andThen(() => + assert.equal(this.get("subject").validationMessage(), "min 1") + ); - this.get('subject').selectRowByValue('jeff'); + this.get("subject").selectRowByValue("jeff"); andThen(() => { - assert.equal(this.get('subject').header().label(), 'jeff'); + assert.equal( + this.get("subject") + .header() + .label(), + "jeff" + ); }); } }); diff --git a/test/javascripts/components/pinned-options-test.js.es6 b/test/javascripts/components/pinned-options-test.js.es6 index 87aafc07d25..c195ad5a8e9 100644 --- a/test/javascripts/components/pinned-options-test.js.es6 +++ b/test/javascripts/components/pinned-options-test.js.es6 @@ -1,5 +1,5 @@ -import componentTest from 'helpers/component-test'; -import Topic from 'discourse/models/topic'; +import componentTest from "helpers/component-test"; +import Topic from "discourse/models/topic"; const buildTopic = function() { return Topic.create({ @@ -10,15 +10,15 @@ const buildTopic = function() { }); }; -moduleForComponent('pinned-options', { +moduleForComponent("pinned-options", { integration: true, beforeEach: function() { - this.set('subject', selectKit()); + this.set("subject", selectKit()); } }); -componentTest('updating the content refreshes the list', { - template: '{{pinned-options value=pinned topic=topic}}', +componentTest("updating the content refreshes the list", { + template: "{{pinned-options value=pinned topic=topic}}", beforeEach() { this.siteSettings.automatically_unpin_topics = false; @@ -28,13 +28,23 @@ componentTest('updating the content refreshes the list', { test(assert) { andThen(() => { - assert.equal(this.get('subject').header().name(), "pinned"); + assert.equal( + this.get("subject") + .header() + .name(), + "pinned" + ); }); andThen(() => this.set("pinned", false)); andThen(() => { - assert.equal(this.get('subject').header().name(), "unpinned"); + assert.equal( + this.get("subject") + .header() + .name(), + "unpinned" + ); }); } }); diff --git a/test/javascripts/components/share-button-test.js.es6 b/test/javascripts/components/share-button-test.js.es6 index 41d0fa84814..95861c305a7 100644 --- a/test/javascripts/components/share-button-test.js.es6 +++ b/test/javascripts/components/share-button-test.js.es6 @@ -1,16 +1,15 @@ -import componentTest from 'helpers/component-test'; -moduleForComponent('share-button', {integration: true}); +import componentTest from "helpers/component-test"; +moduleForComponent("share-button", { integration: true }); -componentTest('share button', { +componentTest("share button", { template: '{{share-button url="https://eviltrout.com"}}', test(assert) { - assert.ok(this.$(`button.share`).length, 'it has all the classes'); + assert.ok(this.$(`button.share`).length, "it has all the classes"); assert.ok( this.$(`button[data-share-url="https://eviltrout.com"]`).length, - 'it has the data attribute for sharing' + "it has the data attribute for sharing" ); } }); - diff --git a/test/javascripts/components/single-select-test.js.es6 b/test/javascripts/components/single-select-test.js.es6 index 671896bc188..b436a64897c 100644 --- a/test/javascripts/components/single-select-test.js.es6 +++ b/test/javascripts/components/single-select-test.js.es6 @@ -1,26 +1,31 @@ -import componentTest from 'helpers/component-test'; -import { withPluginApi } from 'discourse/lib/plugin-api'; -import { clearCallbacks } from 'select-kit/mixins/plugin-api'; +import componentTest from "helpers/component-test"; +import { withPluginApi } from "discourse/lib/plugin-api"; +import { clearCallbacks } from "select-kit/mixins/plugin-api"; -moduleForComponent('single-select', { +moduleForComponent("single-select", { integration: true, beforeEach: function() { - this.set('subject', selectKit()); + this.set("subject", selectKit()); } }); -componentTest('updating the content refreshes the list', { - template: '{{single-select value=1 content=content}}', +componentTest("updating the content refreshes the list", { + template: "{{single-select value=1 content=content}}", beforeEach() { this.set("content", [{ id: 1, name: "BEFORE" }]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').rowByValue(1).name(), "BEFORE"); + assert.equal( + this.get("subject") + .rowByValue(1) + .name(), + "BEFORE" + ); }); andThen(() => { @@ -28,13 +33,18 @@ componentTest('updating the content refreshes the list', { }); andThen(() => { - assert.equal(this.get('subject').rowByValue(1).name(), "AFTER"); + assert.equal( + this.get("subject") + .rowByValue(1) + .name(), + "AFTER" + ); }); } }); -componentTest('accepts a value by reference', { - template: '{{single-select value=value content=content}}', +componentTest("accepts a value by reference", { + template: "{{single-select value=value content=content}}", beforeEach() { this.set("value", 1); @@ -42,16 +52,19 @@ componentTest('accepts a value by reference', { }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { assert.equal( - this.get('subject').selectedRow().name(), "robin", + this.get("subject") + .selectedRow() + .name(), + "robin", "it highlights the row corresponding to the value" ); }); - this.get('subject').selectRowByValue(1); + this.get("subject").selectRowByValue(1); andThen(() => { assert.equal(this.get("value"), 1, "it mutates the value"); @@ -59,72 +72,92 @@ componentTest('accepts a value by reference', { } }); -componentTest('no default icon', { - template: '{{single-select}}', +componentTest("no default icon", { + template: "{{single-select}}", test(assert) { assert.equal( - this.get('subject').header().icon().length, + this.get("subject") + .header() + .icon().length, 0, "it doesn’t have an icon if not specified" ); } }); -componentTest('default search icon', { - template: '{{single-select filterable=true}}', +componentTest("default search icon", { + template: "{{single-select filterable=true}}", test(assert) { - this.get('subject').expand(); - - andThen(() => { - assert.ok(exists(this.get('subject').filter().icon()), "it has an icon"); - }); - } -}); - -componentTest('with no search icon', { - template: '{{single-select filterable=true filterIcon=null}}', - - test(assert) { - this.get('subject').expand(); - - andThen(() => { - assert.notOk(exists(this.get('subject').filter().icon()), "it has no icon"); - }); - } -}); - -componentTest('custom search icon', { - template: '{{single-select filterable=true filterIcon="shower"}}', - - test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { assert.ok( - this.get('subject').filter().icon().hasClass("d-icon-shower"), + exists( + this.get("subject") + .filter() + .icon() + ), + "it has an icon" + ); + }); + } +}); + +componentTest("with no search icon", { + template: "{{single-select filterable=true filterIcon=null}}", + + test(assert) { + this.get("subject").expand(); + + andThen(() => { + assert.notOk( + exists( + this.get("subject") + .filter() + .icon() + ), + "it has no icon" + ); + }); + } +}); + +componentTest("custom search icon", { + template: '{{single-select filterable=true filterIcon="shower"}}', + + test(assert) { + this.get("subject").expand(); + + andThen(() => { + assert.ok( + this.get("subject") + .filter() + .icon() + .hasClass("d-icon-shower"), "it has a the correct icon" ); }); } }); -componentTest('is expandable', { - template: '{{single-select}}', +componentTest("is expandable", { + template: "{{single-select}}", test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); - andThen(() => assert.ok(this.get('subject').isExpanded()) ); + andThen(() => assert.ok(this.get("subject").isExpanded())); - this.get('subject').collapse(); + this.get("subject").collapse(); - andThen(() => assert.notOk(this.get('subject').isExpanded()) ); + andThen(() => assert.notOk(this.get("subject").isExpanded())); } }); -componentTest('accepts custom value/name keys', { - template: '{{single-select value=value nameProperty="item" content=content valueAttribute="identifier"}}', +componentTest("accepts custom value/name keys", { + template: + '{{single-select value=value nameProperty="item" content=content valueAttribute="identifier"}}', beforeEach() { this.set("value", 1); @@ -132,16 +165,21 @@ componentTest('accepts custom value/name keys', { }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').selectedRow().name(), "robin"); + assert.equal( + this.get("subject") + .selectedRow() + .name(), + "robin" + ); }); } }); -componentTest('doesn’t render collection content before first expand', { - template: '{{single-select value=1 content=content}}', +componentTest("doesn’t render collection content before first expand", { + template: "{{single-select value=1 content=content}}", beforeEach() { this.set("content", [{ value: 1, name: "robin" }]); @@ -150,7 +188,7 @@ componentTest('doesn’t render collection content before first expand', { test(assert) { assert.notOk(exists(find(".select-kit-collection"))); - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { assert.ok(exists(find(".select-kit-collection"))); @@ -158,34 +196,41 @@ componentTest('doesn’t render collection content before first expand', { } }); -componentTest('dynamic headerText', { - template: '{{single-select value=1 content=content}}', +componentTest("dynamic headerText", { + template: "{{single-select value=1 content=content}}", beforeEach() { this.set("content", [{ id: 1, name: "robin" }, { id: 2, name: "regis" }]); }, test(assert) { - this.get('subject').expand(); - - andThen(() => { - assert.equal(this.get('subject').header().name(), 'robin'); - }); - - this.get('subject').selectRowByValue(2); + this.get("subject").expand(); andThen(() => { assert.equal( - this.get('subject').header().name(), - 'regis', - 'it changes header text' + this.get("subject") + .header() + .name(), + "robin" + ); + }); + + this.get("subject").selectRowByValue(2); + + andThen(() => { + assert.equal( + this.get("subject") + .header() + .name(), + "regis", + "it changes header text" ); }); } }); -componentTest('supports custom row template', { - template: '{{single-select content=content templateForRow=templateForRow}}', +componentTest("supports custom row template", { + template: "{{single-select content=content templateForRow=templateForRow}}", beforeEach() { this.set("content", [{ id: 1, name: "robin" }]); @@ -195,224 +240,331 @@ componentTest('supports custom row template', { }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { assert.equal( - this.get('subject').rowByValue(1).el().html().trim(), "robin" + this.get("subject") + .rowByValue(1) + .el() + .html() + .trim(), + "robin" ); }); } }); -componentTest('supports converting select value to integer', { - template: '{{single-select value=value content=content castInteger=true}}', +componentTest("supports converting select value to integer", { + template: "{{single-select value=value content=content castInteger=true}}", beforeEach() { - this.set('value', 2); - this.set('content', [{ id: '1', name: 'robin'}, {id: '2', name: 'régis' }]); + this.set("value", 2); + this.set("content", [ + { id: "1", name: "robin" }, + { id: "2", name: "régis" } + ]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); - andThen(() => assert.equal(this.get('subject').selectedRow().name(), 'régis') ); + andThen(() => + assert.equal( + this.get("subject") + .selectedRow() + .name(), + "régis" + ) + ); andThen(() => { - this.set('value', 1); + this.set("value", 1); }); andThen(() => { assert.equal( - this.get('subject').selectedRow().name(), - 'robin', - 'it works with dynamic content' + this.get("subject") + .selectedRow() + .name(), + "robin", + "it works with dynamic content" ); }); } }); -componentTest('supports converting string as boolean to boolean', { - template: '{{single-select value=value content=content castBoolean=true}}', +componentTest("supports converting string as boolean to boolean", { + template: "{{single-select value=value content=content castBoolean=true}}", beforeEach() { - this.set('value', true); - this.set('content', [{ id: 'true', name: 'ASC'}, {id: 'false', name: 'DESC' }]); + this.set("value", true); + this.set("content", [ + { id: "true", name: "ASC" }, + { id: "false", name: "DESC" } + ]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); - andThen(() => assert.equal(this.get('subject').selectedRow().name(), 'ASC') ); + andThen(() => + assert.equal( + this.get("subject") + .selectedRow() + .name(), + "ASC" + ) + ); andThen(() => { - this.set('value', false); + this.set("value", false); }); andThen(() => { assert.equal( - this.get('subject').selectedRow().name(), - 'DESC', - 'it works with dynamic content' + this.get("subject") + .selectedRow() + .name(), + "DESC", + "it works with dynamic content" ); }); } }); -componentTest('supports keyboard events', { - template: '{{single-select content=content filterable=true}}', +componentTest("supports keyboard events", { + template: "{{single-select content=content filterable=true}}", beforeEach() { this.set("content", [{ id: 1, name: "robin" }, { id: 2, name: "regis" }]); }, test(assert) { - this.get('subject').expand().keyboard().down(); + this.get("subject") + .expand() + .keyboard() + .down(); andThen(() => { - assert.equal(this.get('subject').highlightedRow().title(), "regis", "the next row is highlighted"); + assert.equal( + this.get("subject") + .highlightedRow() + .title(), + "regis", + "the next row is highlighted" + ); }); - this.get('subject').keyboard().down(); + this.get("subject") + .keyboard() + .down(); andThen(() => { - assert.equal(this.get('subject').highlightedRow().title(), "robin", "it returns to the first row"); + assert.equal( + this.get("subject") + .highlightedRow() + .title(), + "robin", + "it returns to the first row" + ); }); - this.get('subject').keyboard().up(); + this.get("subject") + .keyboard() + .up(); andThen(() => { - assert.equal(this.get('subject').highlightedRow().title(), "regis", "it highlights the last row"); + assert.equal( + this.get("subject") + .highlightedRow() + .title(), + "regis", + "it highlights the last row" + ); }); - this.get('subject').keyboard().enter(); + this.get("subject") + .keyboard() + .enter(); andThen(() => { - assert.equal(this.get('subject').selectedRow().title(), "regis", "it selects the row when pressing enter"); - assert.notOk(this.get('subject').isExpanded(), "it collapses the select box when selecting a row"); + assert.equal( + this.get("subject") + .selectedRow() + .title(), + "regis", + "it selects the row when pressing enter" + ); + assert.notOk( + this.get("subject").isExpanded(), + "it collapses the select box when selecting a row" + ); }); - this.get('subject').expand().keyboard().escape(); + this.get("subject") + .expand() + .keyboard() + .escape(); andThen(() => { - assert.notOk(this.get('subject').isExpanded(), "it collapses the select box"); + assert.notOk( + this.get("subject").isExpanded(), + "it collapses the select box" + ); }); - this.get('subject').expand().fillInFilter('regis').keyboard().tab(); + this.get("subject") + .expand() + .fillInFilter("regis") + .keyboard() + .tab(); andThen(() => { - assert.notOk(this.get('subject').isExpanded(), "it collapses the select box when selecting a row"); + assert.notOk( + this.get("subject").isExpanded(), + "it collapses the select box when selecting a row" + ); }); } }); - -componentTest('with allowInitialValueMutation', { - template: '{{single-select value=value content=content allowInitialValueMutation=true}}', +componentTest("with allowInitialValueMutation", { + template: + "{{single-select value=value content=content allowInitialValueMutation=true}}", beforeEach() { this.set("value", ""); - this.set("content", [{ id: "1", name: "robin"}, {id: "2", name: "régis" }]); + this.set("content", [ + { id: "1", name: "robin" }, + { id: "2", name: "régis" } + ]); }, test(assert) { andThen(() => { - assert.equal(this.get("value"), "1", "it mutates the value on initial rendering"); + assert.equal( + this.get("value"), + "1", + "it mutates the value on initial rendering" + ); }); } }); -componentTest('support appending content through plugin api', { - template: '{{single-select content=content}}', +componentTest("support appending content through plugin api", { + template: "{{single-select content=content}}", beforeEach() { - withPluginApi('0.8.13', api => { - api.modifySelectKit('select-kit') - .appendContent([{ id: '2', name: 'regis'}]); - }); - - this.set('content', [{ id: '1', name: 'robin'}]); - }, - test(assert) { - this.get('subject').expand(); - - andThen(() => { - assert.equal(this.get('subject').rows().length, 2); - assert.equal(this.get('subject').rowByIndex(1).name(), 'regis'); - }); - - andThen(() => clearCallbacks()); - } -}); - -componentTest('support modifying content through plugin api', { - template: '{{single-select content=content}}', - - beforeEach() { - withPluginApi('0.8.13', api => { - api.modifySelectKit("select-kit") - .modifyContent((context, existingContent) => { - existingContent.splice(1, 0, { id: "2", name: "sam" }); - return existingContent; - }); - }); - - this.set("content", [{ id: "1", name: "robin"}, { id: "3", name: "regis"}]); - }, - - test(assert) { - this.get('subject').expand(); - - andThen(() => { - assert.equal(this.get('subject').rows().length, 3); - assert.equal(this.get('subject').rowByIndex(1).name(), "sam"); - }); - - andThen(() => clearCallbacks()); - } -}); - -componentTest('support prepending content through plugin api', { - template: '{{single-select content=content}}', - - beforeEach() { - withPluginApi('0.8.13', api => { - api.modifySelectKit("select-kit") - .prependContent([{ id: "2", name: "regis"}]); - }); - - this.set("content", [{ id: "1", name: "robin"}]); - }, - - test(assert) { - this.get('subject').expand(); - - andThen(() => { - assert.equal(this.get('subject').rows().length, 2); - assert.equal(this.get('subject').rowByIndex(0).name(), "regis"); - }); - - andThen(() => clearCallbacks()); - } -}); - -componentTest('support modifying on select behavior through plugin api', { - template: '{{single-select content=content}}', - - beforeEach() { - withPluginApi('0.8.13', api => { + withPluginApi("0.8.13", api => { api .modifySelectKit("select-kit") - .onSelect((context, value) => { - find(".on-select-test").html(value); + .appendContent([{ id: "2", name: "regis" }]); + }); + + this.set("content", [{ id: "1", name: "robin" }]); + }, + test(assert) { + this.get("subject").expand(); + + andThen(() => { + assert.equal(this.get("subject").rows().length, 2); + assert.equal( + this.get("subject") + .rowByIndex(1) + .name(), + "regis" + ); + }); + + andThen(() => clearCallbacks()); + } +}); + +componentTest("support modifying content through plugin api", { + template: "{{single-select content=content}}", + + beforeEach() { + withPluginApi("0.8.13", api => { + api + .modifySelectKit("select-kit") + .modifyContent((context, existingContent) => { + existingContent.splice(1, 0, { id: "2", name: "sam" }); + return existingContent; }); }); - this.set("content", [{ id: "1", name: "robin"}]); + this.set("content", [ + { id: "1", name: "robin" }, + { id: "3", name: "regis" } + ]); }, test(assert) { - this.get('subject').expand().selectRowByValue(1); + this.get("subject").expand(); + + andThen(() => { + assert.equal(this.get("subject").rows().length, 3); + assert.equal( + this.get("subject") + .rowByIndex(1) + .name(), + "sam" + ); + }); + + andThen(() => clearCallbacks()); + } +}); + +componentTest("support prepending content through plugin api", { + template: "{{single-select content=content}}", + + beforeEach() { + withPluginApi("0.8.13", api => { + api + .modifySelectKit("select-kit") + .prependContent([{ id: "2", name: "regis" }]); + }); + + this.set("content", [{ id: "1", name: "robin" }]); + }, + + test(assert) { + this.get("subject").expand(); + + andThen(() => { + assert.equal(this.get("subject").rows().length, 2); + assert.equal( + this.get("subject") + .rowByIndex(0) + .name(), + "regis" + ); + }); + + andThen(() => clearCallbacks()); + } +}); + +componentTest("support modifying on select behavior through plugin api", { + template: + '{{single-select content=content}}', + + beforeEach() { + withPluginApi("0.8.13", api => { + api.modifySelectKit("select-kit").onSelect((context, value) => { + find(".on-select-test").html(value); + }); + }); + + this.set("content", [{ id: "1", name: "robin" }]); + }, + + test(assert) { + this.get("subject") + .expand() + .selectRowByValue(1); andThen(() => { assert.equal(find(".on-select-test").html(), "1"); @@ -422,19 +574,24 @@ componentTest('support modifying on select behavior through plugin api', { } }); -componentTest('with nameChanges', { - template: '{{single-select content=content nameChanges=true}}', +componentTest("with nameChanges", { + template: "{{single-select content=content nameChanges=true}}", beforeEach() { - this.set("robin", { id: "1", name: "robin"}); + this.set("robin", { id: "1", name: "robin" }); this.set("content", [this.get("robin")]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').header().name(), "robin"); + assert.equal( + this.get("subject") + .header() + .name(), + "robin" + ); }); andThen(() => { @@ -442,130 +599,183 @@ componentTest('with nameChanges', { }); andThen(() => { - assert.equal(this.get('subject').header().name(), "robin2"); + assert.equal( + this.get("subject") + .header() + .name(), + "robin2" + ); }); } }); - -componentTest('with null value', { - template: '{{single-select content=content}}', +componentTest("with null value", { + template: "{{single-select content=content}}", beforeEach() { this.set("content", [{ name: "robin" }]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').header().name(), "robin"); - assert.equal(this.get('subject').header().value(), undefined); + assert.equal( + this.get("subject") + .header() + .name(), + "robin" + ); + assert.equal( + this.get("subject") + .header() + .value(), + undefined + ); }); } }); -componentTest('with collection header', { - template: '{{single-select collectionHeader=collectionHeader}}', +componentTest("with collection header", { + template: "{{single-select collectionHeader=collectionHeader}}", beforeEach() { this.set("collectionHeader", "

Hello

"); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => assert.ok(exists(".collection-header h2"))); } }); -componentTest('with title', { +componentTest("with title", { template: '{{single-select title=(i18n "test.title")}}', beforeEach() { - I18n.translations[I18n.locale].js.test = {title: 'My title'}; + I18n.translations[I18n.locale].js.test = { title: "My title" }; }, test(assert) { - andThen(() => assert.equal(this.get('subject').header().title(), 'My title') ); + andThen(() => + assert.equal( + this.get("subject") + .header() + .title(), + "My title" + ) + ); } }); -componentTest('support modifying header computed content through plugin api', { - template: '{{single-select content=content}}', +componentTest("support modifying header computed content through plugin api", { + template: "{{single-select content=content}}", beforeEach() { - withPluginApi('0.8.15', api => { - api.modifySelectKit("select-kit") + withPluginApi("0.8.15", api => { + api + .modifySelectKit("select-kit") .modifyHeaderComputedContent((context, computedContent) => { computedContent.title = "Not so evil"; return computedContent; }); }); - this.set("content", [{ id: "1", name: "robin"}]); + this.set("content", [{ id: "1", name: "robin" }]); }, test(assert) { andThen(() => { - assert.equal(this.get('subject').header().title(), "Not so evil"); + assert.equal( + this.get("subject") + .header() + .title(), + "Not so evil" + ); }); andThen(() => clearCallbacks()); } }); -componentTest('with limitMatches', { - template: '{{single-select content=content limitMatches=2}}', +componentTest("with limitMatches", { + template: "{{single-select content=content limitMatches=2}}", beforeEach() { - this.set('content', ['sam', 'jeff', 'neil']); + this.set("content", ["sam", "jeff", "neil"]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); - andThen(() => assert.equal(this.get('subject').el().find(".select-kit-row").length, 2)); + andThen(() => + assert.equal( + this.get("subject") + .el() + .find(".select-kit-row").length, + 2 + ) + ); } }); -componentTest('with minimum', { - template: '{{single-select content=content minimum=1 allowAutoSelectFirst=false}}', +componentTest("with minimum", { + template: + "{{single-select content=content minimum=1 allowAutoSelectFirst=false}}", beforeEach() { - this.set('content', ['sam', 'jeff', 'neil']); + this.set("content", ["sam", "jeff", "neil"]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); - andThen(() => assert.equal(this.get('subject').validationMessage(), 'Select at least 1 item.')); + andThen(() => + assert.equal( + this.get("subject").validationMessage(), + "Select at least 1 item." + ) + ); - this.get('subject').selectRowByValue('sam'); + this.get("subject").selectRowByValue("sam"); andThen(() => { - assert.equal(this.get('subject').header().label(), 'sam'); + assert.equal( + this.get("subject") + .header() + .label(), + "sam" + ); }); } }); -componentTest('with minimumLabel', { - template: '{{single-select content=content minimum=1 minimumLabel="test.minimum" allowAutoSelectFirst=false}}', +componentTest("with minimumLabel", { + template: + '{{single-select content=content minimum=1 minimumLabel="test.minimum" allowAutoSelectFirst=false}}', beforeEach() { - I18n.translations[I18n.locale].js.test = { minimum: 'min %{count}' }; - this.set('content', ['sam', 'jeff', 'neil']); + I18n.translations[I18n.locale].js.test = { minimum: "min %{count}" }; + this.set("content", ["sam", "jeff", "neil"]); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); - andThen(() => assert.equal(this.get('subject').validationMessage(), 'min 1')); + andThen(() => + assert.equal(this.get("subject").validationMessage(), "min 1") + ); - this.get('subject').selectRowByValue('jeff'); + this.get("subject").selectRowByValue("jeff"); andThen(() => { - assert.equal(this.get('subject').header().label(), 'jeff'); + assert.equal( + this.get("subject") + .header() + .label(), + "jeff" + ); }); } }); diff --git a/test/javascripts/components/text-field-test.js.es6 b/test/javascripts/components/text-field-test.js.es6 index 7a2de69a5a2..6535c923974 100644 --- a/test/javascripts/components/text-field-test.js.es6 +++ b/test/javascripts/components/text-field-test.js.es6 @@ -1,4 +1,4 @@ -import componentTest from 'helpers/component-test'; +import componentTest from "helpers/component-test"; moduleForComponent("text-field", { integration: true }); @@ -6,7 +6,7 @@ componentTest("renders correctly with no properties set", { template: `{{text-field}}`, test(assert) { - assert.ok(this.$('input[type=text]').length); + assert.ok(this.$("input[type=text]").length); } }); @@ -18,8 +18,8 @@ componentTest("support a placeholder", { }, test(assert) { - assert.ok(this.$('input[type=text]').length); - assert.equal(this.$('input').prop('placeholder'), 'placeholder.i18n.key'); + assert.ok(this.$("input[type=text]").length); + assert.equal(this.$("input").prop("placeholder"), "placeholder.i18n.key"); } }); @@ -30,7 +30,7 @@ componentTest("sets the dir attribute to ltr for Hebrew text", { }, test(assert) { - assert.equal(this.$('input').attr('dir'), 'rtl'); + assert.equal(this.$("input").attr("dir"), "rtl"); } }); @@ -41,8 +41,6 @@ componentTest("sets the dir attribute to ltr for English text", { }, test(assert) { - assert.equal(this.$('input').attr('dir'), 'ltr'); + assert.equal(this.$("input").attr("dir"), "ltr"); } }); - - diff --git a/test/javascripts/components/topic-footer-mobile-dropdown-test.js.es6 b/test/javascripts/components/topic-footer-mobile-dropdown-test.js.es6 index 3bdd8ec4fc6..26915e8066b 100644 --- a/test/javascripts/components/topic-footer-mobile-dropdown-test.js.es6 +++ b/test/javascripts/components/topic-footer-mobile-dropdown-test.js.es6 @@ -1,44 +1,66 @@ -import componentTest from 'helpers/component-test'; -import Topic from 'discourse/models/topic'; +import componentTest from "helpers/component-test"; +import Topic from "discourse/models/topic"; const buildTopic = function() { return Topic.create({ id: 1234, - title: 'Qunit Test Topic' + title: "Qunit Test Topic" }); }; -moduleForComponent('topic-footer-mobile-dropdown', { +moduleForComponent("topic-footer-mobile-dropdown", { integration: true, beforeEach: function() { - this.set('subject', selectKit()); + this.set("subject", selectKit()); } }); -componentTest('default', { - template: '{{topic-footer-mobile-dropdown topic=topic}}', +componentTest("default", { + template: "{{topic-footer-mobile-dropdown topic=topic}}", beforeEach() { - this.set('topic', buildTopic()); + this.set("topic", buildTopic()); }, test(assert) { - this.get('subject').expand(); + this.get("subject").expand(); andThen(() => { - assert.equal(this.get('subject').header().title(), 'Topic Controls'); - assert.equal(this.get('subject').header().value(), null); - assert.equal(this.get('subject').rowByIndex(0).name(), 'Bookmark'); - assert.equal(this.get('subject').rowByIndex(1).name(), 'Share'); + assert.equal( + this.get("subject") + .header() + .title(), + "Topic Controls" + ); + assert.equal( + this.get("subject") + .header() + .value(), + null + ); + assert.equal( + this.get("subject") + .rowByIndex(0) + .name(), + "Bookmark" + ); + assert.equal( + this.get("subject") + .rowByIndex(1) + .name(), + "Share" + ); assert.notOk( - this.get('subject').selectedRow().exists(), - 'it doesn’t preselect first row' + this.get("subject") + .selectedRow() + .exists(), + "it doesn’t preselect first row" ); }); - this.get('subject').selectRowByValue('share'); + this.get("subject").selectRowByValue("share"); andThen(() => { - assert.equal(this.get('value'), null, 'it resets the value'); + assert.equal(this.get("value"), null, "it resets the value"); }); } }); diff --git a/test/javascripts/components/topic-notifications-button-test.js.es6 b/test/javascripts/components/topic-notifications-button-test.js.es6 index 8dac8739395..de27d63f72c 100644 --- a/test/javascripts/components/topic-notifications-button-test.js.es6 +++ b/test/javascripts/components/topic-notifications-button-test.js.es6 @@ -1,5 +1,5 @@ -import componentTest from 'helpers/component-test'; -import Topic from 'discourse/models/topic'; +import componentTest from "helpers/component-test"; +import Topic from "discourse/models/topic"; const buildTopic = function(level) { return Topic.create({ @@ -11,10 +11,11 @@ const buildTopic = function(level) { }); }; -moduleForComponent('topic-notifications-button', { integration: true }); +moduleForComponent("topic-notifications-button", { integration: true }); -componentTest('the header has a localized title', { - template: '{{topic-notifications-button notificationLevel=topic.details.notification_level topic=topic}}', +componentTest("the header has a localized title", { + template: + "{{topic-notifications-button notificationLevel=topic.details.notification_level topic=topic}}", beforeEach() { this.set("topic", buildTopic(1)); @@ -22,13 +23,25 @@ componentTest('the header has a localized title', { test(assert) { andThen(() => { - assert.equal(selectKit().header().name(), "Normal", "it has the correct title"); + assert.equal( + selectKit() + .header() + .name(), + "Normal", + "it has the correct title" + ); }); this.set("topic", buildTopic(2)); andThen(() => { - assert.equal(selectKit().header().name(), "Tracking", "it correctly changes the title"); + assert.equal( + selectKit() + .header() + .name(), + "Tracking", + "it correctly changes the title" + ); }); } }); diff --git a/test/javascripts/components/value-list-test.js.es6 b/test/javascripts/components/value-list-test.js.es6 index feefcbea079..aaa936c901e 100644 --- a/test/javascripts/components/value-list-test.js.es6 +++ b/test/javascripts/components/value-list-test.js.es6 @@ -1,67 +1,76 @@ -import componentTest from 'helpers/component-test'; -moduleForComponent('value-list', {integration: true}); +import componentTest from "helpers/component-test"; +moduleForComponent("value-list", { integration: true }); -componentTest('functionality', { +componentTest("functionality", { template: '{{value-list values=values inputType="array"}}', test(assert) { - assert.ok(this.$('.values .value').length === 0, 'it has no values'); - assert.ok(this.$('input').length, 'it renders the input'); - assert.ok(this.$('.btn-primary[disabled]').length, 'it is disabled with no value'); + assert.ok(this.$(".values .value").length === 0, "it has no values"); + assert.ok(this.$("input").length, "it renders the input"); + assert.ok( + this.$(".btn-primary[disabled]").length, + "it is disabled with no value" + ); - fillIn('input', 'eviltrout'); + fillIn("input", "eviltrout"); andThen(() => { - assert.ok(!this.$('.btn-primary[disabled]').length, "it isn't disabled anymore"); + assert.ok( + !this.$(".btn-primary[disabled]").length, + "it isn't disabled anymore" + ); }); - click('.btn-primary'); + click(".btn-primary"); andThen(() => { - assert.equal(this.$('.values .value').length, 1, 'it adds the value'); - assert.equal(this.$('input').val(), '', 'it clears the input'); - assert.ok(this.$('.btn-primary[disabled]').length, "it is disabled again"); - assert.equal(this.get('values'), 'eviltrout', 'it appends the value'); + assert.equal(this.$(".values .value").length, 1, "it adds the value"); + assert.equal(this.$("input").val(), "", "it clears the input"); + assert.ok( + this.$(".btn-primary[disabled]").length, + "it is disabled again" + ); + assert.equal(this.get("values"), "eviltrout", "it appends the value"); }); - click('.value .btn-small'); + click(".value .btn-small"); andThen(() => { - assert.ok(this.$('.values .value').length === 0, 'it removes the value'); + assert.ok(this.$(".values .value").length === 0, "it removes the value"); }); } }); -componentTest('with string delimited values', { - template: '{{value-list values=valueString}}', +componentTest("with string delimited values", { + template: "{{value-list values=valueString}}", beforeEach() { - this.set('valueString', "hello\nworld"); + this.set("valueString", "hello\nworld"); }, test(assert) { - assert.equal(this.$('.values .value').length, 2); + assert.equal(this.$(".values .value").length, 2); - fillIn('input', 'eviltrout'); - click('.btn-primary'); + fillIn("input", "eviltrout"); + click(".btn-primary"); andThen(() => { - assert.equal(this.$('.values .value').length, 3); - assert.equal(this.get('valueString'), "hello\nworld\neviltrout"); + assert.equal(this.$(".values .value").length, 3); + assert.equal(this.get("valueString"), "hello\nworld\neviltrout"); }); } }); -componentTest('with array values', { +componentTest("with array values", { template: '{{value-list values=valueArray inputType="array"}}', beforeEach() { - this.set('valueArray', ['abc', 'def']); + this.set("valueArray", ["abc", "def"]); }, test(assert) { - assert.equal(this.$('.values .value').length, 2); + assert.equal(this.$(".values .value").length, 2); - fillIn('input', 'eviltrout'); - click('.btn-primary'); + fillIn("input", "eviltrout"); + click(".btn-primary"); andThen(() => { - assert.equal(this.$('.values .value').length, 3); - assert.deepEqual(this.get('valueArray'), ['abc', 'def', 'eviltrout']); + assert.equal(this.$(".values .value").length, 3); + assert.deepEqual(this.get("valueArray"), ["abc", "def", "eviltrout"]); }); } }); diff --git a/test/javascripts/controllers/avatar-selector-test.js.es6 b/test/javascripts/controllers/avatar-selector-test.js.es6 index ce26c62163f..b5856e886b2 100644 --- a/test/javascripts/controllers/avatar-selector-test.js.es6 +++ b/test/javascripts/controllers/avatar-selector-test.js.es6 @@ -1,10 +1,10 @@ -import { mapRoutes } from 'discourse/mapping-router'; +import { mapRoutes } from "discourse/mapping-router"; moduleFor("controller:avatar-selector", "controller:avatar-selector", { beforeEach() { - this.registry.register('router:main', mapRoutes()); + this.registry.register("router:main", mapRoutes()); }, - needs: ['controller:modal'] + needs: ["controller:modal"] }); QUnit.test("avatarTemplate", function(assert) { @@ -12,16 +12,28 @@ QUnit.test("avatarTemplate", function(assert) { avatarSelectorController.setProperties({ selected: "system", - system_avatar_upload_id:1, - gravatar_avatar_upload_id:2, + system_avatar_upload_id: 1, + gravatar_avatar_upload_id: 2, custom_avatar_upload_id: 3 }); - assert.equal(avatarSelectorController.get("selectedUploadId"), 1, "we are using system by default"); + assert.equal( + avatarSelectorController.get("selectedUploadId"), + 1, + "we are using system by default" + ); - avatarSelectorController.set('selected', 'gravatar'); - assert.equal(avatarSelectorController.get("selectedUploadId"), 2, "we are using gravatar when set"); + avatarSelectorController.set("selected", "gravatar"); + assert.equal( + avatarSelectorController.get("selectedUploadId"), + 2, + "we are using gravatar when set" + ); avatarSelectorController.set("selected", "custom"); - assert.equal(avatarSelectorController.get("selectedUploadId"), 3, "we are using custom when set"); + assert.equal( + avatarSelectorController.get("selectedUploadId"), + 3, + "we are using custom when set" + ); }); diff --git a/test/javascripts/controllers/create-account-test.js.es6 b/test/javascripts/controllers/create-account-test.js.es6 index f5604d8169b..ec2d4250842 100644 --- a/test/javascripts/controllers/create-account-test.js.es6 +++ b/test/javascripts/controllers/create-account-test.js.es6 @@ -1,55 +1,86 @@ -import { mapRoutes } from 'discourse/mapping-router'; +import { mapRoutes } from "discourse/mapping-router"; moduleFor("controller:create-account", "controller:create-account", { beforeEach() { - this.registry.register('router:main', mapRoutes()); + this.registry.register("router:main", mapRoutes()); }, - needs: ['controller:modal', 'controller:login'] + needs: ["controller:modal", "controller:login"] }); -QUnit.test('basicUsernameValidation', function(assert) { +QUnit.test("basicUsernameValidation", function(assert) { var subject = this.subject; var testInvalidUsername = function(username, expectedReason) { var controller = subject({ siteSettings: Discourse.SiteSettings }); - controller.set('accountUsername', username); - assert.equal(controller.get('basicUsernameValidation.failed'), true, 'username should be invalid: ' + username); - assert.equal(controller.get('basicUsernameValidation.reason'), expectedReason, 'username validation reason: ' + username + ', ' + expectedReason); + controller.set("accountUsername", username); + assert.equal( + controller.get("basicUsernameValidation.failed"), + true, + "username should be invalid: " + username + ); + assert.equal( + controller.get("basicUsernameValidation.reason"), + expectedReason, + "username validation reason: " + username + ", " + expectedReason + ); }; - testInvalidUsername('', undefined); - testInvalidUsername('x', I18n.t('user.username.too_short')); - testInvalidUsername('123456789012345678901', I18n.t('user.username.too_long')); + testInvalidUsername("", undefined); + testInvalidUsername("x", I18n.t("user.username.too_short")); + testInvalidUsername( + "123456789012345678901", + I18n.t("user.username.too_long") + ); var controller = subject({ siteSettings: Discourse.SiteSettings }); - controller.set('accountUsername', 'porkchops'); - controller.set('prefilledUsername', 'porkchops'); - assert.equal(controller.get('basicUsernameValidation.ok'), true, 'Prefilled username is valid'); - assert.equal(controller.get('basicUsernameValidation.reason'), I18n.t('user.username.prefilled'), 'Prefilled username is valid'); + controller.set("accountUsername", "porkchops"); + controller.set("prefilledUsername", "porkchops"); + assert.equal( + controller.get("basicUsernameValidation.ok"), + true, + "Prefilled username is valid" + ); + assert.equal( + controller.get("basicUsernameValidation.reason"), + I18n.t("user.username.prefilled"), + "Prefilled username is valid" + ); }); -QUnit.test('passwordValidation', function(assert) { +QUnit.test("passwordValidation", function(assert) { var subject = this.subject; var controller = subject({ siteSettings: Discourse.SiteSettings }); - controller.set('passwordRequired', true); - controller.set('accountEmail', 'pork@chops.com'); - controller.set('accountUsername', 'porkchops'); - controller.set('prefilledUsername', 'porkchops'); + controller.set("passwordRequired", true); + controller.set("accountEmail", "pork@chops.com"); + controller.set("accountUsername", "porkchops"); + controller.set("prefilledUsername", "porkchops"); - controller.set('accountPassword', 'b4fcdae11f9167'); - assert.equal(controller.get('passwordValidation.ok'), true, 'Password is ok'); - assert.equal(controller.get('passwordValidation.reason'), I18n.t('user.password.ok'), 'Password is valid'); + controller.set("accountPassword", "b4fcdae11f9167"); + assert.equal(controller.get("passwordValidation.ok"), true, "Password is ok"); + assert.equal( + controller.get("passwordValidation.reason"), + I18n.t("user.password.ok"), + "Password is valid" + ); var testInvalidPassword = function(password, expectedReason) { var c = subject({ siteSettings: Discourse.SiteSettings }); - c.set('accountPassword', password); - assert.equal(c.get('passwordValidation.failed'), true, 'password should be invalid: ' + password); - assert.equal(c.get('passwordValidation.reason'), expectedReason, 'password validation reason: ' + password + ', ' + expectedReason); + c.set("accountPassword", password); + assert.equal( + c.get("passwordValidation.failed"), + true, + "password should be invalid: " + password + ); + assert.equal( + c.get("passwordValidation.reason"), + expectedReason, + "password validation reason: " + password + ", " + expectedReason + ); }; - testInvalidPassword('', undefined); - testInvalidPassword('x', I18n.t('user.password.too_short')); - testInvalidPassword('porkchops', I18n.t('user.password.same_as_username')); - testInvalidPassword('pork@chops.com', I18n.t('user.password.same_as_email')); + testInvalidPassword("", undefined); + testInvalidPassword("x", I18n.t("user.password.too_short")); + testInvalidPassword("porkchops", I18n.t("user.password.same_as_username")); + testInvalidPassword("pork@chops.com", I18n.t("user.password.same_as_email")); }); diff --git a/test/javascripts/controllers/group-test.js.es6 b/test/javascripts/controllers/group-test.js.es6 index a5defd2d14a..eaf09be5bec 100644 --- a/test/javascripts/controllers/group-test.js.es6 +++ b/test/javascripts/controllers/group-test.js.es6 @@ -1,5 +1,5 @@ moduleFor("controller:group", { - needs: ['controller:application'] + needs: ["controller:application"] }); QUnit.test("canEditGroup", function(assert) { @@ -9,13 +9,25 @@ QUnit.test("canEditGroup", function(assert) { model: { is_group_owner: true, automatic: true } }); - assert.equal(GroupController.get("canEditGroup"), false, "automatic groups cannot be edited"); + assert.equal( + GroupController.get("canEditGroup"), + false, + "automatic groups cannot be edited" + ); GroupController.set("model.automatic", false); - assert.equal(GroupController.get("canEditGroup"), true, "owners can edit groups"); + assert.equal( + GroupController.get("canEditGroup"), + true, + "owners can edit groups" + ); GroupController.set("model.is_group_owner", false); - assert.equal(GroupController.get("canEditGroup"), false, "normal users cannot edit groups"); + assert.equal( + GroupController.get("canEditGroup"), + false, + "normal users cannot edit groups" + ); }); diff --git a/test/javascripts/controllers/history-test.js.es6 b/test/javascripts/controllers/history-test.js.es6 index 11fe31a27e2..c2eca9962a5 100644 --- a/test/javascripts/controllers/history-test.js.es6 +++ b/test/javascripts/controllers/history-test.js.es6 @@ -8,21 +8,24 @@ QUnit.test("displayEdit", function(assert) { }); assert.equal( - HistoryController.get("displayEdit"), false, + HistoryController.get("displayEdit"), + false, "it should not display edit button when user cannot edit the post" ); HistoryController.set("model.can_edit", true); assert.equal( - HistoryController.get("displayEdit"), true, + HistoryController.get("displayEdit"), + true, "it should display edit button when user can edit the post" ); HistoryController.set("model.current_revision", 2); assert.equal( - HistoryController.get("displayEdit"), false, + HistoryController.get("displayEdit"), + false, "it should only display the edit button on the latest revision" ); }); diff --git a/test/javascripts/controllers/preferences-second-factor-test.js.es6 b/test/javascripts/controllers/preferences-second-factor-test.js.es6 index 0bbf07100a2..753552674f3 100644 --- a/test/javascripts/controllers/preferences-second-factor-test.js.es6 +++ b/test/javascripts/controllers/preferences-second-factor-test.js.es6 @@ -1,27 +1,26 @@ moduleFor("controller:preferences/second-factor"); -QUnit.test("displayOAuthWarning when OAuth login methods are disabled", function(assert) { - const controller = this.subject({ - siteSettings: { - enable_google_oauth2_logins: false - } - }); +QUnit.test( + "displayOAuthWarning when OAuth login methods are disabled", + function(assert) { + const controller = this.subject({ + siteSettings: { + enable_google_oauth2_logins: false + } + }); - assert.equal( - controller.get('displayOAuthWarning'), - false - ); -}); + assert.equal(controller.get("displayOAuthWarning"), false); + } +); -QUnit.test("displayOAuthWarning when OAuth login methods are enabled", function(assert) { +QUnit.test("displayOAuthWarning when OAuth login methods are enabled", function( + assert +) { const controller = this.subject({ siteSettings: { enable_google_oauth2_logins: true } }); - assert.equal( - controller.get('displayOAuthWarning'), - true - ); + assert.equal(controller.get("displayOAuthWarning"), true); }); diff --git a/test/javascripts/controllers/topic-test.js.es6 b/test/javascripts/controllers/topic-test.js.es6 index eeb72861e99..d8a907acad4 100644 --- a/test/javascripts/controllers/topic-test.js.es6 +++ b/test/javascripts/controllers/topic-test.js.es6 @@ -4,7 +4,9 @@ import Topic from "discourse/models/topic"; moduleFor("controller:topic", "controller:topic", { needs: ["controller:composer", "controller:application"], beforeEach() { - this.registry.register("app-events:main", AppEvents.create(), { instantiate: false }); + this.registry.register("app-events:main", AppEvents.create(), { + instantiate: false + }); this.registry.injection("controller", "appEvents", "app-events:main"); } }); @@ -18,41 +20,70 @@ QUnit.test("editTopic", function(assert) { controller.set("model.details.can_edit", false); controller.send("editTopic"); - assert.not(controller.get("editingTopic"), "calling editTopic doesn't enable editing unless the user can edit"); + assert.not( + controller.get("editingTopic"), + "calling editTopic doesn't enable editing unless the user can edit" + ); controller.set("model.details.can_edit", true); controller.send("editTopic"); - assert.ok(controller.get("editingTopic"), "calling editTopic enables editing if the user can edit"); + assert.ok( + controller.get("editingTopic"), + "calling editTopic enables editing if the user can edit" + ); assert.equal(controller.get("buffered.title"), model.get("title")); - assert.equal(controller.get("buffered.category_id"), model.get("category_id")); + assert.equal( + controller.get("buffered.category_id"), + model.get("category_id") + ); controller.send("cancelEditingTopic"); - assert.not(controller.get("editingTopic"), "cancelling edit mode reverts the property value"); + assert.not( + controller.get("editingTopic"), + "cancelling edit mode reverts the property value" + ); }); QUnit.test("toggleMultiSelect", function(assert) { const model = Topic.create(); const controller = this.subject({ model }); - assert.not(controller.get("multiSelect"), "multi selection mode is disabled by default"); + assert.not( + controller.get("multiSelect"), + "multi selection mode is disabled by default" + ); controller.get("selectedPostIds").pushObject(1); assert.equal(controller.get("selectedPostIds.length"), 1); controller.send("toggleMultiSelect"); - assert.ok(controller.get("multiSelect"), "calling 'toggleMultiSelect' once enables multi selection mode"); - assert.equal(controller.get("selectedPostIds.length"), 0, "toggling 'multiSelect' clears 'selectedPostIds'"); + assert.ok( + controller.get("multiSelect"), + "calling 'toggleMultiSelect' once enables multi selection mode" + ); + assert.equal( + controller.get("selectedPostIds.length"), + 0, + "toggling 'multiSelect' clears 'selectedPostIds'" + ); controller.get("selectedPostIds").pushObject(2); assert.equal(controller.get("selectedPostIds.length"), 1); controller.send("toggleMultiSelect"); - assert.not(controller.get("multiSelect"), "calling 'toggleMultiSelect' twice disables multi selection mode"); - assert.equal(controller.get("selectedPostIds.length"), 0, "toggling 'multiSelect' clears 'selectedPostIds'"); + assert.not( + controller.get("multiSelect"), + "calling 'toggleMultiSelect' twice disables multi selection mode" + ); + assert.equal( + controller.get("selectedPostIds.length"), + 0, + "toggling 'multiSelect' clears 'selectedPostIds'" + ); }); QUnit.test("selectedPosts", function(assert) { @@ -62,8 +93,15 @@ QUnit.test("selectedPosts", function(assert) { controller.set("selectedPostIds", [1, 2, 42]); - assert.equal(controller.get("selectedPosts.length"), 2, "selectedPosts only contains already loaded posts"); - assert.not(controller.get("selectedPosts").some(p => p === undefined), "selectedPosts only contains valid post objects"); + assert.equal( + controller.get("selectedPosts.length"), + 2, + "selectedPosts only contains already loaded posts" + ); + assert.not( + controller.get("selectedPosts").some(p => p === undefined), + "selectedPosts only contains valid post objects" + ); }); QUnit.test("selectedAllPosts", function(assert) { @@ -81,7 +119,10 @@ QUnit.test("selectedAllPosts", function(assert) { controller.get("selectedPostIds").pushObject(42); - assert.ok(controller.get("selectedAllPosts"), "all posts (including filtered posts) are selected"); + assert.ok( + controller.get("selectedAllPosts"), + "all posts (including filtered posts) are selected" + ); }); QUnit.test("selectedPostsUsername", function(assert) { @@ -89,7 +130,7 @@ QUnit.test("selectedPostsUsername", function(assert) { posts: [ { id: 1, username: "gary" }, { id: 2, username: "gary" }, - { id: 3, username: "lili" }, + { id: 3, username: "lili" } ], stream: [1, 2, 3] }; @@ -98,23 +139,43 @@ QUnit.test("selectedPostsUsername", function(assert) { const controller = this.subject({ model }); const selectedPostIds = controller.get("selectedPostIds"); - assert.equal(controller.get("selectedPostsUsername"), undefined, "no username when no selected posts"); + assert.equal( + controller.get("selectedPostsUsername"), + undefined, + "no username when no selected posts" + ); selectedPostIds.pushObject(1); - assert.equal(controller.get("selectedPostsUsername"), "gary", "username of the selected posts"); + assert.equal( + controller.get("selectedPostsUsername"), + "gary", + "username of the selected posts" + ); selectedPostIds.pushObject(2); - assert.equal(controller.get("selectedPostsUsername"), "gary", "username of all the selected posts when same user"); + assert.equal( + controller.get("selectedPostsUsername"), + "gary", + "username of all the selected posts when same user" + ); selectedPostIds.pushObject(3); - assert.equal(controller.get("selectedPostsUsername"), undefined, "no username when more than 1 user"); + assert.equal( + controller.get("selectedPostsUsername"), + undefined, + "no username when more than 1 user" + ); selectedPostIds.replace(2, 1, [42]); - assert.equal(controller.get("selectedPostsUsername"), undefined, "no username when not already loaded posts are selected"); + assert.equal( + controller.get("selectedPostsUsername"), + undefined, + "no username when not already loaded posts are selected" + ); }); QUnit.test("showSelectedPostsAtBottom", function(assert) { @@ -126,11 +187,17 @@ QUnit.test("showSelectedPostsAtBottom", function(assert) { site.set("mobileView", true); - assert.not(controller.get("showSelectedPostsAtBottom"), "requires at least 3 posts on mobile"); + assert.not( + controller.get("showSelectedPostsAtBottom"), + "requires at least 3 posts on mobile" + ); model.set("posts_count", 4); - assert.ok(controller.get("showSelectedPostsAtBottom"), "true when mobile and more than 3 posts"); + assert.ok( + controller.get("showSelectedPostsAtBottom"), + "true when mobile and more than 3 posts" + ); }); QUnit.test("canDeleteSelected", function(assert) { @@ -147,19 +214,31 @@ QUnit.test("canDeleteSelected", function(assert) { const controller = this.subject({ model }); const selectedPostIds = controller.get("selectedPostIds"); - assert.not(controller.get("canDeleteSelected"), "false when no posts are selected"); + assert.not( + controller.get("canDeleteSelected"), + "false when no posts are selected" + ); selectedPostIds.pushObject(1); - assert.not(controller.get("canDeleteSelected"), "false when can't delete one of the selected posts"); + assert.not( + controller.get("canDeleteSelected"), + "false when can't delete one of the selected posts" + ); selectedPostIds.replace(0, 1, [2, 3]); - assert.ok(controller.get("canDeleteSelected"), "true when all selected posts can be deleted"); + assert.ok( + controller.get("canDeleteSelected"), + "true when all selected posts can be deleted" + ); selectedPostIds.pushObject(1); - assert.ok(controller.get("canDeleteSelected"), "true when all posts are selected"); + assert.ok( + controller.get("canDeleteSelected"), + "true when all posts are selected" + ); }); QUnit.test("Can split/merge topic", function(assert) { @@ -172,17 +251,32 @@ QUnit.test("Can split/merge topic", function(assert) { stream: [1, 2, 3] }; - const model = Topic.create({ postStream, details: { can_move_posts: false } }); + const model = Topic.create({ + postStream, + details: { can_move_posts: false } + }); const controller = this.subject({ model }); const selectedPostIds = controller.get("selectedPostIds"); - assert.not(controller.get("canSplitTopic"), "can't split topic when no posts are selected"); - assert.not(controller.get("canMergeTopic"), "can't merge topic when no posts are selected"); + assert.not( + controller.get("canSplitTopic"), + "can't split topic when no posts are selected" + ); + assert.not( + controller.get("canMergeTopic"), + "can't merge topic when no posts are selected" + ); selectedPostIds.pushObject(1); - assert.not(controller.get("canSplitTopic"), "can't split topic when can't move posts"); - assert.not(controller.get("canMergeTopic"), "can't merge topic when can't move posts"); + assert.not( + controller.get("canSplitTopic"), + "can't split topic when can't move posts" + ); + assert.not( + controller.get("canMergeTopic"), + "can't merge topic when can't move posts" + ); model.set("details.can_move_posts", true); @@ -192,33 +286,47 @@ QUnit.test("Can split/merge topic", function(assert) { selectedPostIds.removeObject(1); selectedPostIds.pushObject(2); - assert.not(controller.get("canSplitTopic"), "can't split topic when 1st post is not a regular post"); - assert.ok(controller.get("canMergeTopic"), "can merge topic when 1st post is not a regular post"); + assert.not( + controller.get("canSplitTopic"), + "can't split topic when 1st post is not a regular post" + ); + assert.ok( + controller.get("canMergeTopic"), + "can merge topic when 1st post is not a regular post" + ); selectedPostIds.pushObject(3); - assert.not(controller.get("canSplitTopic"), "can't split topic when all posts are selected"); - assert.ok(controller.get("canMergeTopic"), "can merge topic when all posts are selected"); + assert.not( + controller.get("canSplitTopic"), + "can't split topic when all posts are selected" + ); + assert.ok( + controller.get("canMergeTopic"), + "can merge topic when all posts are selected" + ); }); QUnit.test("canChangeOwner", function(assert) { const currentUser = Discourse.User.create({ admin: false }); - this.registry.register("current-user:main", currentUser, { instantiate: false }); + this.registry.register("current-user:main", currentUser, { + instantiate: false + }); this.registry.injection("controller", "currentUser", "current-user:main"); const postStream = { - posts: [ - { id: 1, username: "gary" }, - { id: 2, username: "lili" }, - ], + posts: [{ id: 1, username: "gary" }, { id: 2, username: "lili" }], stream: [1, 2] }; - const model = Topic.create({ postStream, currentUser: { admin: false }}); + const model = Topic.create({ postStream, currentUser: { admin: false } }); const controller = this.subject({ model }); const selectedPostIds = controller.get("selectedPostIds"); - assert.not(controller.get("canChangeOwner"), "false when no posts are selected"); + assert.not( + controller.get("canChangeOwner"), + "false when no posts are selected" + ); selectedPostIds.pushObject(1); @@ -226,11 +334,17 @@ QUnit.test("canChangeOwner", function(assert) { currentUser.set("admin", true); - assert.ok(controller.get("canChangeOwner"), "true when admin and one post is selected"); + assert.ok( + controller.get("canChangeOwner"), + "true when admin and one post is selected" + ); selectedPostIds.pushObject(2); - assert.not(controller.get("canChangeOwner"), "false when admin but more than 1 user"); + assert.not( + controller.get("canChangeOwner"), + "false when admin but more than 1 user" + ); }); QUnit.test("canMergePosts", function(assert) { @@ -239,7 +353,7 @@ QUnit.test("canMergePosts", function(assert) { { id: 1, username: "gary", can_delete: true }, { id: 2, username: "lili", can_delete: true }, { id: 3, username: "gary", can_delete: false }, - { id: 4, username: "gary", can_delete: true }, + { id: 4, username: "gary", can_delete: true } ], stream: [1, 2, 3] }; @@ -248,23 +362,38 @@ QUnit.test("canMergePosts", function(assert) { const controller = this.subject({ model }); const selectedPostIds = controller.get("selectedPostIds"); - assert.not(controller.get("canMergePosts"), "false when no posts are selected"); + assert.not( + controller.get("canMergePosts"), + "false when no posts are selected" + ); selectedPostIds.pushObject(1); - assert.not(controller.get("canMergePosts"), "false when only one post is selected"); + assert.not( + controller.get("canMergePosts"), + "false when only one post is selected" + ); selectedPostIds.pushObject(2); - assert.not(controller.get("canMergePosts"), "false when selected posts are from different users"); + assert.not( + controller.get("canMergePosts"), + "false when selected posts are from different users" + ); selectedPostIds.replace(1, 1, [3]); - assert.not(controller.get("canMergePosts"), "false when selected posts can't be deleted"); + assert.not( + controller.get("canMergePosts"), + "false when selected posts can't be deleted" + ); selectedPostIds.replace(1, 1, [4]); - assert.ok(controller.get("canMergePosts"), "true when all selected posts are deletable and by the same user"); + assert.ok( + controller.get("canMergePosts"), + "true when all selected posts are deletable and by the same user" + ); }); QUnit.test("Select/deselect all", function(assert) { @@ -272,15 +401,27 @@ QUnit.test("Select/deselect all", function(assert) { const model = Topic.create({ postStream }); const controller = this.subject({ model }); - assert.equal(controller.get("selectedPostsCount"), 0, "no posts selected by default"); + assert.equal( + controller.get("selectedPostsCount"), + 0, + "no posts selected by default" + ); controller.send("selectAll"); - assert.equal(controller.get("selectedPostsCount"), postStream.stream.length, "calling 'selectAll' selects all posts"); + assert.equal( + controller.get("selectedPostsCount"), + postStream.stream.length, + "calling 'selectAll' selects all posts" + ); controller.send("deselectAll"); - assert.equal(controller.get("selectedPostsCount"), 0, "calling 'deselectAll' deselects all posts"); + assert.equal( + controller.get("selectedPostsCount"), + 0, + "calling 'deselectAll' deselects all posts" + ); }); QUnit.test("togglePostSelection", function(assert) { @@ -291,11 +432,19 @@ QUnit.test("togglePostSelection", function(assert) { controller.send("togglePostSelection", { id: 1 }); - assert.equal(selectedPostIds[0], 1, "adds the selected post id if not already selected"); + assert.equal( + selectedPostIds[0], + 1, + "adds the selected post id if not already selected" + ); controller.send("togglePostSelection", { id: 1 }); - assert.equal(selectedPostIds[0], undefined, "removes the selected post id if already selected"); + assert.equal( + selectedPostIds[0], + undefined, + "removes the selected post id if already selected" + ); }); // QUnit.test("selectReplies", function(assert) { diff --git a/test/javascripts/ember/resolver-test.js.es6 b/test/javascripts/ember/resolver-test.js.es6 index 85424065ecf..e374abc56fd 100644 --- a/test/javascripts/ember/resolver-test.js.es6 +++ b/test/javascripts/ember/resolver-test.js.es6 @@ -1,4 +1,4 @@ -import { setResolverOption, buildResolver } from 'discourse-common/resolver'; +import { setResolverOption, buildResolver } from "discourse-common/resolver"; let originalTemplates; let resolver; @@ -15,7 +15,7 @@ function setTemplates(lookupTemplateStrings) { }); } -const DiscourseResolver = buildResolver('discourse'); +const DiscourseResolver = buildResolver("discourse"); QUnit.module("lib:resolver", { beforeEach() { @@ -31,12 +31,7 @@ QUnit.module("lib:resolver", { }); QUnit.test("finds templates in top level dir", assert => { - setTemplates([ - "foobar", - "fooBar", - "foo_bar", - "foo.bar" - ]); + setTemplates(["foobar", "fooBar", "foo_bar", "foo.bar"]); lookupTemplate(assert, "template:foobar", "foobar", "by lowcased name"); lookupTemplate(assert, "template:fooBar", "fooBar", "by camel cased name"); @@ -45,96 +40,224 @@ QUnit.test("finds templates in top level dir", assert => { }); QUnit.test("finds templates in first-level subdir", assert => { - setTemplates([ - "foo/bar_baz" - ]); + setTemplates(["foo/bar_baz"]); - lookupTemplate(assert, "template:foo/bar_baz", "foo/bar_baz", "with subdir defined by slash"); - lookupTemplate(assert, "template:foo.bar_baz", "foo/bar_baz", "with subdir defined by dot"); - lookupTemplate(assert, "template:fooBarBaz", "foo/bar_baz", "with subdir defined by first camel case and the rest of camel cases converted to underscores"); - lookupTemplate(assert, "template:foo_bar_baz", "foo/bar_baz", "with subdir defined by first underscore"); + lookupTemplate( + assert, + "template:foo/bar_baz", + "foo/bar_baz", + "with subdir defined by slash" + ); + lookupTemplate( + assert, + "template:foo.bar_baz", + "foo/bar_baz", + "with subdir defined by dot" + ); + lookupTemplate( + assert, + "template:fooBarBaz", + "foo/bar_baz", + "with subdir defined by first camel case and the rest of camel cases converted to underscores" + ); + lookupTemplate( + assert, + "template:foo_bar_baz", + "foo/bar_baz", + "with subdir defined by first underscore" + ); }); -QUnit.test("resolves precedence between overlapping top level dir and first level subdir templates", assert => { - setTemplates([ - "fooBar", - "foo_bar", - "foo.bar", - "foo/bar" - ]); +QUnit.test( + "resolves precedence between overlapping top level dir and first level subdir templates", + assert => { + setTemplates(["fooBar", "foo_bar", "foo.bar", "foo/bar"]); - lookupTemplate(assert, "template:foo.bar", "foo/bar", "preferring first level subdir for dotted name"); - lookupTemplate(assert, "template:fooBar", "fooBar", "preferring top level dir for camel cased name"); - lookupTemplate(assert, "template:foo_bar", "foo_bar", "preferring top level dir for underscored name"); -}); + lookupTemplate( + assert, + "template:foo.bar", + "foo/bar", + "preferring first level subdir for dotted name" + ); + lookupTemplate( + assert, + "template:fooBar", + "fooBar", + "preferring top level dir for camel cased name" + ); + lookupTemplate( + assert, + "template:foo_bar", + "foo_bar", + "preferring top level dir for underscored name" + ); + } +); QUnit.test("finds templates in subdir deeper than one level", assert => { - setTemplates([ - "foo/bar/baz/qux" - ]); + setTemplates(["foo/bar/baz/qux"]); - lookupTemplate(assert, "template:foo/bar/baz/qux", "foo/bar/baz/qux", "for subdirs defined by slashes"); - lookupTemplate(assert, "template:foo.bar.baz.qux", "foo/bar/baz/qux", "for subdirs defined by dots"); - lookupTemplate(assert, "template:foo/bar/bazQux", "foo/bar/baz/qux", "for subdirs defined by slashes plus one camel case"); - lookupTemplate(assert, "template:foo/bar/baz_qux", "foo/bar/baz/qux", "for subdirs defined by slashes plus one underscore"); + lookupTemplate( + assert, + "template:foo/bar/baz/qux", + "foo/bar/baz/qux", + "for subdirs defined by slashes" + ); + lookupTemplate( + assert, + "template:foo.bar.baz.qux", + "foo/bar/baz/qux", + "for subdirs defined by dots" + ); + lookupTemplate( + assert, + "template:foo/bar/bazQux", + "foo/bar/baz/qux", + "for subdirs defined by slashes plus one camel case" + ); + lookupTemplate( + assert, + "template:foo/bar/baz_qux", + "foo/bar/baz/qux", + "for subdirs defined by slashes plus one underscore" + ); - lookupTemplate(assert, "template:fooBarBazQux", undefined, "but not for subdirs defined by more than one camel case"); - lookupTemplate(assert, "template:foo_bar_baz_qux", undefined, "but not for subdirs defined by more than one underscore"); - lookupTemplate(assert, "template:foo.bar.bazQux", undefined, "but not for subdirs defined by dots plus one camel case"); - lookupTemplate(assert, "template:foo.bar.baz_qux", undefined, "but not for subdirs defined by dots plus one underscore"); + lookupTemplate( + assert, + "template:fooBarBazQux", + undefined, + "but not for subdirs defined by more than one camel case" + ); + lookupTemplate( + assert, + "template:foo_bar_baz_qux", + undefined, + "but not for subdirs defined by more than one underscore" + ); + lookupTemplate( + assert, + "template:foo.bar.bazQux", + undefined, + "but not for subdirs defined by dots plus one camel case" + ); + lookupTemplate( + assert, + "template:foo.bar.baz_qux", + undefined, + "but not for subdirs defined by dots plus one underscore" + ); }); QUnit.test("resolves mobile templates to 'mobile/' namespace", assert => { - setTemplates([ + setTemplates(["mobile/foo", "bar", "mobile/bar", "baz"]); + + setResolverOption("mobileView", true); + + lookupTemplate( + assert, + "template:foo", "mobile/foo", - "bar", + "finding mobile version even if normal one is not present" + ); + lookupTemplate( + assert, + "template:bar", "mobile/bar", - "baz" - ]); - - setResolverOption('mobileView', true); - - lookupTemplate(assert, "template:foo", "mobile/foo", "finding mobile version even if normal one is not present"); - lookupTemplate(assert, "template:bar", "mobile/bar", "preferring mobile version when both mobile and normal versions are present"); - lookupTemplate(assert, "template:baz", "baz", "falling back to a normal version when mobile version is not present"); + "preferring mobile version when both mobile and normal versions are present" + ); + lookupTemplate( + assert, + "template:baz", + "baz", + "falling back to a normal version when mobile version is not present" + ); }); QUnit.test("resolves plugin templates to 'javascripts/' namespace", assert => { - setTemplates([ + setTemplates(["javascripts/foo", "bar", "javascripts/bar", "baz"]); + + lookupTemplate( + assert, + "template:foo", "javascripts/foo", - "bar", + "finding plugin version even if normal one is not present" + ); + lookupTemplate( + assert, + "template:bar", "javascripts/bar", - "baz" - ]); - - lookupTemplate(assert, "template:foo", "javascripts/foo", "finding plugin version even if normal one is not present"); - lookupTemplate(assert, "template:bar", "javascripts/bar", "preferring plugin version when both versions are present"); - lookupTemplate(assert, "template:baz", "baz", "falling back to a normal version when plugin version is not present"); + "preferring plugin version when both versions are present" + ); + lookupTemplate( + assert, + "template:baz", + "baz", + "falling back to a normal version when plugin version is not present" + ); }); -QUnit.test("resolves templates with 'admin' prefix to 'admin/templates/' namespace", assert => { - setTemplates([ - "admin/templates/foo", - "adminBar", - "admin_bar", - "admin.bar", - "admin/templates/bar" - ]); +QUnit.test( + "resolves templates with 'admin' prefix to 'admin/templates/' namespace", + assert => { + setTemplates([ + "admin/templates/foo", + "adminBar", + "admin_bar", + "admin.bar", + "admin/templates/bar" + ]); - lookupTemplate(assert, "template:adminFoo", "admin/templates/foo", "when prefix is separated by camel case"); - lookupTemplate(assert, "template:admin_foo", "admin/templates/foo", "when prefix is separated by underscore"); - lookupTemplate(assert, "template:admin.foo", "admin/templates/foo", "when prefix is separated by dot"); + lookupTemplate( + assert, + "template:adminFoo", + "admin/templates/foo", + "when prefix is separated by camel case" + ); + lookupTemplate( + assert, + "template:admin_foo", + "admin/templates/foo", + "when prefix is separated by underscore" + ); + lookupTemplate( + assert, + "template:admin.foo", + "admin/templates/foo", + "when prefix is separated by dot" + ); - lookupTemplate(assert, "template:adminfoo", undefined, "but not when prefix is not separated in any way"); - lookupTemplate(assert, "template:adminBar", "adminBar", "but not when template with the exact camel cased name exists"); - lookupTemplate(assert, "template:admin_bar", "admin_bar", "but not when template with the exact underscored name exists"); - lookupTemplate(assert, "template:admin.bar", "admin.bar", "but not when template with the exact dotted name exists"); -}); + lookupTemplate( + assert, + "template:adminfoo", + undefined, + "but not when prefix is not separated in any way" + ); + lookupTemplate( + assert, + "template:adminBar", + "adminBar", + "but not when template with the exact camel cased name exists" + ); + lookupTemplate( + assert, + "template:admin_bar", + "admin_bar", + "but not when template with the exact underscored name exists" + ); + lookupTemplate( + assert, + "template:admin.bar", + "admin.bar", + "but not when template with the exact dotted name exists" + ); + } +); -QUnit.test("returns 'not_found' template when template name cannot be resolved", assert => { - setTemplates([ - "not_found" - ]); +QUnit.test( + "returns 'not_found' template when template name cannot be resolved", + assert => { + setTemplates(["not_found"]); - lookupTemplate(assert, "template:foo/bar/baz", "not_found", ""); -}); + lookupTemplate(assert, "template:foo/bar/baz", "not_found", ""); + } +); diff --git a/test/javascripts/fixtures/about.js.es6 b/test/javascripts/fixtures/about.js.es6 index 4261dd2a489..08f89c2f1f2 100644 --- a/test/javascripts/fixtures/about.js.es6 +++ b/test/javascripts/fixtures/about.js.es6 @@ -1,3 +1,109 @@ export default { - "about.json": {"about":{"stats":{"topic_count":5969,"post_count":65860,"user_count":10858,"topics_7_days":112,"posts_7_days":1302,"users_7_days":111,"like_count":37747,"likes_7_days":1143},"description":"Discussion about the next-generation open source Discourse forum software","title":"Discourse Meta","locale":"en","version":"0.9.9.16","moderators":[{"id":3,"username":"supermathie","uploaded_avatar_id":5247,"avatar_template":"/images/avatar.png"},{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/images/avatar.png"},{"id":19,"username":"eviltrout","uploaded_avatar_id":5275,"avatar_template":"/images/avatar.png"},{"id":2,"username":"neil","uploaded_avatar_id":5245,"avatar_template":"/images/avatar.png"},{"id":1,"username":"sam","uploaded_avatar_id":5243,"avatar_template":"/images/avatar.png"},{"id":1995,"username":"zogstrip","uploaded_avatar_id":8630,"avatar_template":"/images/avatar.png"}],"admins":[{"id":3,"username":"supermathie","uploaded_avatar_id":5247,"avatar_template":"/images/avatar.png"},{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/images/avatar.png"},{"id":19,"username":"eviltrout","uploaded_avatar_id":5275,"avatar_template":"/images/avatar.png"},{"id":38,"username":"frandallfarmer","uploaded_avatar_id":5307,"avatar_template":"/images/avatar.png"},{"id":6626,"username":"riking","uploaded_avatar_id":9779,"avatar_template":"/images/avatar.png"},{"id":2,"username":"neil","uploaded_avatar_id":5245,"avatar_template":"/images/avatar.png"},{"id":1,"username":"sam","uploaded_avatar_id":5243,"avatar_template":"/images/avatar.png"},{"id":1995,"username":"zogstrip","uploaded_avatar_id":8630,"avatar_template":"/images/avatar.png"}]}} + "about.json": { + about: { + stats: { + topic_count: 5969, + post_count: 65860, + user_count: 10858, + topics_7_days: 112, + posts_7_days: 1302, + users_7_days: 111, + like_count: 37747, + likes_7_days: 1143 + }, + description: + "Discussion about the next-generation open source Discourse forum software", + title: "Discourse Meta", + locale: "en", + version: "0.9.9.16", + moderators: [ + { + id: 3, + username: "supermathie", + uploaded_avatar_id: 5247, + avatar_template: "/images/avatar.png" + }, + { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: "/images/avatar.png" + }, + { + id: 19, + username: "eviltrout", + uploaded_avatar_id: 5275, + avatar_template: "/images/avatar.png" + }, + { + id: 2, + username: "neil", + uploaded_avatar_id: 5245, + avatar_template: "/images/avatar.png" + }, + { + id: 1, + username: "sam", + uploaded_avatar_id: 5243, + avatar_template: "/images/avatar.png" + }, + { + id: 1995, + username: "zogstrip", + uploaded_avatar_id: 8630, + avatar_template: "/images/avatar.png" + } + ], + admins: [ + { + id: 3, + username: "supermathie", + uploaded_avatar_id: 5247, + avatar_template: "/images/avatar.png" + }, + { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: "/images/avatar.png" + }, + { + id: 19, + username: "eviltrout", + uploaded_avatar_id: 5275, + avatar_template: "/images/avatar.png" + }, + { + id: 38, + username: "frandallfarmer", + uploaded_avatar_id: 5307, + avatar_template: "/images/avatar.png" + }, + { + id: 6626, + username: "riking", + uploaded_avatar_id: 9779, + avatar_template: "/images/avatar.png" + }, + { + id: 2, + username: "neil", + uploaded_avatar_id: 5245, + avatar_template: "/images/avatar.png" + }, + { + id: 1, + username: "sam", + uploaded_avatar_id: 5243, + avatar_template: "/images/avatar.png" + }, + { + id: 1995, + username: "zogstrip", + uploaded_avatar_id: 8630, + avatar_template: "/images/avatar.png" + } + ] + } + } }; diff --git a/test/javascripts/fixtures/badges_fixture.js.es6 b/test/javascripts/fixtures/badges_fixture.js.es6 index 9dcab162e72..43ad075bc79 100644 --- a/test/javascripts/fixtures/badges_fixture.js.es6 +++ b/test/javascripts/fixtures/badges_fixture.js.es6 @@ -1,1679 +1,1682 @@ export default { - "\/badges.json": { - "badge_types": [ + "/badges.json": { + badge_types: [ { - "id": 2, - "name": "Silver" + id: 2, + name: "Silver" }, { - "id": 3, - "name": "Bronze" + id: 3, + name: "Bronze" }, { - "id": 1, - "name": "Gold" + id: 1, + name: "Gold" } ], - "badge_groupings": [ + badge_groupings: [ { - "id": 8, - "name": "Development", - "description": null, - "position": 5 + id: 8, + name: "Development", + description: null, + position: 5 }, { - "id": 4, - "name": "Trust Level", - "description": null, - "position": 3 + id: 4, + name: "Trust Level", + description: null, + position: 3 }, { - "id": 7, - "name": "Testing", - "description": null, - "position": 4 + id: 7, + name: "Testing", + description: null, + position: 4 }, { - "id": 1, - "name": "Getting Started", - "description": null, - "position": 0 + id: 1, + name: "Getting Started", + description: null, + position: 0 }, { - "id": 3, - "name": "Posting", - "description": null, - "position": 2 + id: 3, + name: "Posting", + description: null, + position: 2 }, { - "id": 2, - "name": "Community", - "description": null, - "position": 1 + id: 2, + name: "Community", + description: null, + position: 1 } ], - "badges": [ + badges: [ { - "id": 108, - "name": "Great contributor", - "description": "contributed 25 accepted pull request", - "grant_count": 11, - "allow_title": true, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 8, - "system": false, - "badge_type_id": 2 + id: 108, + name: "Great contributor", + description: "contributed 25 accepted pull request", + grant_count: 11, + allow_title: true, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 8, + system: false, + badge_type_id: 2 }, { - "id": 118, - "name": "Plugin Author", - "description": "Developed a plugin<\/a> for Discourse ", - "grant_count": 10, - "allow_title": true, - "multiple_grant": false, - "icon": "fa-cog", - "listable": true, - "enabled": true, - "badge_grouping_id": 8, - "system": false, - "badge_type_id": 2 + id: 118, + name: "Plugin Author", + description: + 'Developed a plugin for Discourse ', + grant_count: 10, + allow_title: true, + multiple_grant: false, + icon: "fa-cog", + listable: true, + enabled: true, + badge_grouping_id: 8, + system: false, + badge_type_id: 2 }, { - "id": 3, - "name": "Leader", - "description": null, - "grant_count": 29, - "allow_title": true, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 4, - "system": true, - "badge_type_id": 2 + id: 3, + name: "Leader", + description: null, + grant_count: 29, + allow_title: true, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 4, + system: true, + badge_type_id: 2 }, { - "id": 107, - "name": "Contributor", - "description": "contributed an accepted pull request", - "grant_count": 200, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 8, - "system": false, - "badge_type_id": 3 + id: 107, + name: "Contributor", + description: "contributed an accepted pull request", + grant_count: 200, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 8, + system: false, + badge_type_id: 3 }, { - "id": 116, - "name": "Tester", - "description": "Reported 10 bugs that were liked by the Discourse team<\/a>", - "grant_count": 9, - "allow_title": true, - "multiple_grant": false, - "icon": "fa-bug", - "listable": true, - "enabled": true, - "badge_grouping_id": 7, - "system": false, - "badge_type_id": 2 + id: 116, + name: "Tester", + description: + 'Reported 10 bugs that were liked by the Discourse team', + grant_count: 9, + allow_title: true, + multiple_grant: false, + icon: "fa-bug", + listable: true, + enabled: true, + badge_grouping_id: 7, + system: false, + badge_type_id: 2 }, { - "id": 109, - "name": "Amazing contributor", - "description": "contributed 250 accepted pull request", - "grant_count": 0, - "allow_title": true, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 8, - "system": false, - "badge_type_id": 1 + id: 109, + name: "Amazing contributor", + description: "contributed 250 accepted pull request", + grant_count: 0, + allow_title: true, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 8, + system: false, + badge_type_id: 1 }, { - "id": 2, - "name": "Regular User", - "description": null, - "grant_count": 467, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 4, - "system": true, - "badge_type_id": 3 + id: 2, + name: "Regular User", + description: null, + grant_count: 467, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 4, + system: true, + badge_type_id: 3 }, { - "id": 114, - "name": "Bug Reporter", - "description": "Reported a bug that was liked by the Discourse team<\/a>", - "grant_count": 183, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-bug", - "listable": true, - "enabled": true, - "badge_grouping_id": 7, - "system": false, - "badge_type_id": 3 + id: 114, + name: "Bug Reporter", + description: + 'Reported a bug that was liked by the Discourse team', + grant_count: 183, + allow_title: false, + multiple_grant: false, + icon: "fa-bug", + listable: true, + enabled: true, + badge_grouping_id: 7, + system: false, + badge_type_id: 3 }, { - "id": 4, - "name": "Elder", - "description": null, - "grant_count": 4, - "allow_title": true, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 4, - "system": true, - "badge_type_id": 1 + id: 4, + name: "Elder", + description: null, + grant_count: 4, + allow_title: true, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 4, + system: true, + badge_type_id: 1 }, { - "id": 17, - "name": "Reader", - "description": null, - "grant_count": 278, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 1, - "system": true, - "badge_type_id": 3 + id: 17, + name: "Reader", + description: null, + grant_count: 278, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 1, + system: true, + badge_type_id: 3 }, { - "id": 1, - "name": "Basic User", - "description": null, - "grant_count": 5834, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 4, - "system": true, - "badge_type_id": 3 + id: 1, + name: "Basic User", + description: null, + grant_count: 5834, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 4, + system: true, + badge_type_id: 3 }, { - "id": 16, - "name": "Read Guidelines", - "description": null, - "grant_count": 60, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 1, - "system": true, - "badge_type_id": 3 + id: 16, + name: "Read Guidelines", + description: null, + grant_count: 60, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 1, + system: true, + badge_type_id: 3 }, { - "id": 7, - "name": "Good Post", - "description": null, - "grant_count": 22, - "allow_title": false, - "multiple_grant": true, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 3, - "system": true, - "badge_type_id": 2 + id: 7, + name: "Good Post", + description: null, + grant_count: 22, + allow_title: false, + multiple_grant: true, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 3, + system: true, + badge_type_id: 2 }, { - "id": 8, - "name": "Great Post", - "description": null, - "grant_count": 2, - "allow_title": false, - "multiple_grant": true, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 3, - "system": true, - "badge_type_id": 1 + id: 8, + name: "Great Post", + description: null, + grant_count: 2, + allow_title: false, + multiple_grant: true, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 3, + system: true, + badge_type_id: 1 }, { - "id": 11, - "name": "First Like", - "description": null, - "grant_count": 2387, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 1, - "system": true, - "badge_type_id": 3 + id: 11, + name: "First Like", + description: null, + grant_count: 2387, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 1, + system: true, + badge_type_id: 3 }, { - "id": 12, - "name": "First Share", - "description": null, - "grant_count": 285, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 1, - "system": true, - "badge_type_id": 3 + id: 12, + name: "First Share", + description: null, + grant_count: 285, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 1, + system: true, + badge_type_id: 3 }, { - "id": 13, - "name": "First Flag", - "description": null, - "grant_count": 42, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 2, - "system": true, - "badge_type_id": 3 + id: 13, + name: "First Flag", + description: null, + grant_count: 42, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 2, + system: true, + badge_type_id: 3 }, { - "id": 5, - "name": "Welcome", - "description": null, - "grant_count": 1718, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 2, - "system": true, - "badge_type_id": 3 + id: 5, + name: "Welcome", + description: null, + grant_count: 1718, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 2, + system: true, + badge_type_id: 3 }, { - "id": 15, - "name": "First Quote", - "description": null, - "grant_count": 270, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 1, - "system": true, - "badge_type_id": 3 + id: 15, + name: "First Quote", + description: null, + grant_count: 270, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 1, + system: true, + badge_type_id: 3 }, { - "id": 9, - "name": "Autobiographer", - "description": null, - "long_description": "hello", - "grant_count": 545, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 1, - "system": true, - "badge_type_id": 3 + id: 9, + name: "Autobiographer", + description: null, + long_description: "hello", + grant_count: 545, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 1, + system: true, + badge_type_id: 3 }, { - "id": 14, - "name": "First Link", - "description": null, - "grant_count": 397, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 1, - "system": true, - "badge_type_id": 3 + id: 14, + name: "First Link", + description: null, + grant_count: 397, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 1, + system: true, + badge_type_id: 3 }, { - "id": 6, - "name": "Nice Post", - "description": null, - "grant_count": 259, - "allow_title": false, - "multiple_grant": true, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 3, - "system": true, - "badge_type_id": 3 + id: 6, + name: "Nice Post", + description: null, + grant_count: 259, + allow_title: false, + multiple_grant: true, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 3, + system: true, + badge_type_id: 3 }, { - "id": 10, - "name": "Editor", - "description": null, - "grant_count": 933, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 2, - "system": true, - "badge_type_id": 3 + id: 10, + name: "Editor", + description: null, + grant_count: 933, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 2, + system: true, + badge_type_id: 3 } ] }, "/badges/9": { - "badge_types": [ + badge_types: [ { - "id": 3, - "name": "Bronze" + id: 3, + name: "Bronze" } ], - "badge": { - "id": 9, - "name": "Autobiographer", - "description": null, - "long_description": "", - "grant_count": 545, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 1, - "system": true, - "badge_type_id": 3 + badge: { + id: 9, + name: "Autobiographer", + description: null, + long_description: "", + grant_count: 545, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 1, + system: true, + badge_type_id: 3 } }, "/user_badges.json": { - "badges": [ + badges: [ { - "id": 9, - "name": "Autobiographer", - "description": null, - "grant_count": 545, - "allow_title": false, - "multiple_grant": false, - "icon": "fa-certificate", - "listable": true, - "enabled": true, - "badge_grouping_id": 1, - "system": true, - "badge_type_id": 3 + id: 9, + name: "Autobiographer", + description: null, + grant_count: 545, + allow_title: false, + multiple_grant: false, + icon: "fa-certificate", + listable: true, + enabled: true, + badge_grouping_id: 1, + system: true, + badge_type_id: 3 } ], - "badge_types": [ + badge_types: [ { - "id": 3, - "name": "Bronze" + id: 3, + name: "Bronze" } ], - "users": [ + users: [ { - "id": 11209, - "username": "icaroperseo", - "uploaded_avatar_id": 33076, - "avatar_template":"/images/avatar.png" + id: 11209, + username: "icaroperseo", + uploaded_avatar_id: 33076, + avatar_template: "/images/avatar.png" }, { - "id": -1, - "username": "system", - "uploaded_avatar_id": 5241, - "avatar_template":"/images/avatar.png" + id: -1, + username: "system", + uploaded_avatar_id: 5241, + avatar_template: "/images/avatar.png" }, { - "id": 11234, - "username": "allard", - "uploaded_avatar_id": 33117, - "avatar_template":"/images/avatar.png" + id: 11234, + username: "allard", + uploaded_avatar_id: 33117, + avatar_template: "/images/avatar.png" }, { - "id": 8944, - "username": "hunterboerner", - "uploaded_avatar_id": 33072, - "avatar_template":"/images/avatar.png" + id: 8944, + username: "hunterboerner", + uploaded_avatar_id: 33072, + avatar_template: "/images/avatar.png" }, { - "id": 11232, - "username": "daydreamer", - "uploaded_avatar_id": 33101, - "avatar_template":"/images/avatar.png" + id: 11232, + username: "daydreamer", + uploaded_avatar_id: 33101, + avatar_template: "/images/avatar.png" }, { - "id": 11160, - "username": "boomzilla", - "uploaded_avatar_id": 33029, - "avatar_template":"/images/avatar.png" + id: 11160, + username: "boomzilla", + uploaded_avatar_id: 33029, + avatar_template: "/images/avatar.png" }, { - "id": 5303, - "username": "ybart", - "uploaded_avatar_id": 14132, - "avatar_template":"/images/avatar.png" + id: 5303, + username: "ybart", + uploaded_avatar_id: 14132, + avatar_template: "/images/avatar.png" }, { - "id": 11142, - "username": "Fluffy", - "uploaded_avatar_id": 32957, - "avatar_template":"/images/avatar.png" + id: 11142, + username: "Fluffy", + uploaded_avatar_id: 32957, + avatar_template: "/images/avatar.png" }, { - "id": 8843, - "username": "timoroso", - "uploaded_avatar_id": 19114, - "avatar_template":"/images/avatar.png" + id: 8843, + username: "timoroso", + uploaded_avatar_id: 19114, + avatar_template: "/images/avatar.png" }, { - "id": 10990, - "username": "Nagesh", - "uploaded_avatar_id": 32736, - "avatar_template":"/images/avatar.png" + id: 10990, + username: "Nagesh", + uploaded_avatar_id: 32736, + avatar_template: "/images/avatar.png" }, { - "id": 11027, - "username": "dullroar", - "uploaded_avatar_id": 32801, - "avatar_template":"/images/avatar.png" + id: 11027, + username: "dullroar", + uploaded_avatar_id: 32801, + avatar_template: "/images/avatar.png" }, { - "id": 10481, - "username": "Air_Cooled_Nut", - "uploaded_avatar_id": 31833, - "avatar_template":"/images/avatar.png" + id: 10481, + username: "Air_Cooled_Nut", + uploaded_avatar_id: 31833, + avatar_template: "/images/avatar.png" }, { - "id": 10977, - "username": "stevebridger", - "uploaded_avatar_id": 32705, - "avatar_template":"/images/avatar.png" + id: 10977, + username: "stevebridger", + uploaded_avatar_id: 32705, + avatar_template: "/images/avatar.png" }, { - "id": 10921, - "username": "lnikkila", - "uploaded_avatar_id": 32627, - "avatar_template":"/images/avatar.png" + id: 10921, + username: "lnikkila", + uploaded_avatar_id: 32627, + avatar_template: "/images/avatar.png" }, { - "id": 8493, - "username": "PJH", - "uploaded_avatar_id": 33082, - "avatar_template":"/images/avatar.png" + id: 8493, + username: "PJH", + uploaded_avatar_id: 33082, + avatar_template: "/images/avatar.png" }, { - "id": 10635, - "username": "Ganzuelo", - "uploaded_avatar_id": 32217, - "avatar_template":"/images/avatar.png" + id: 10635, + username: "Ganzuelo", + uploaded_avatar_id: 32217, + avatar_template: "/images/avatar.png" }, { - "id": 8300, - "username": "cpradio", - "uploaded_avatar_id": 4970, - "avatar_template":"/images/avatar.png" + id: 8300, + username: "cpradio", + uploaded_avatar_id: 4970, + avatar_template: "/images/avatar.png" }, { - "id": 8571, - "username": "tobiaseigen", - "uploaded_avatar_id": 9785, - "avatar_template":"/images/avatar.png" + id: 8571, + username: "tobiaseigen", + uploaded_avatar_id: 9785, + avatar_template: "/images/avatar.png" }, { - "id": 4263, - "username": "mcwumbly", - "uploaded_avatar_id": 9796, - "avatar_template":"/images/avatar.png" + id: 4263, + username: "mcwumbly", + uploaded_avatar_id: 9796, + avatar_template: "/images/avatar.png" }, { - "id": 471, - "username": "BhaelOchon", - "uploaded_avatar_id": 6069, - "avatar_template":"/images/avatar.png" + id: 471, + username: "BhaelOchon", + uploaded_avatar_id: 6069, + avatar_template: "/images/avatar.png" }, { - "id": 5249, - "username": "cawas", - "uploaded_avatar_id": 14043, - "avatar_template":"/images/avatar.png" + id: 5249, + username: "cawas", + uploaded_avatar_id: 14043, + avatar_template: "/images/avatar.png" }, { - "id": 5461, - "username": "thepractice", - "uploaded_avatar_id": 2397, - "avatar_template":"/images/avatar.png" + id: 5461, + username: "thepractice", + uploaded_avatar_id: 2397, + avatar_template: "/images/avatar.png" }, { - "id": 10467, - "username": "chris18890", - "uploaded_avatar_id": 31806, - "avatar_template":"/images/avatar.png" + id: 10467, + username: "chris18890", + uploaded_avatar_id: 31806, + avatar_template: "/images/avatar.png" }, { - "id": 375, - "username": "weirdcanada", - "uploaded_avatar_id": 5902, - "avatar_template":"/images/avatar.png" + id: 375, + username: "weirdcanada", + uploaded_avatar_id: 5902, + avatar_template: "/images/avatar.png" }, { - "id": 8617, - "username": "Mittineague", - "uploaded_avatar_id": 4462, - "avatar_template":"/images/avatar.png" + id: 8617, + username: "Mittineague", + uploaded_avatar_id: 4462, + avatar_template: "/images/avatar.png" }, { - "id": 5962, - "username": "TheMarkus", - "uploaded_avatar_id": 15186, - "avatar_template":"/images/avatar.png" + id: 5962, + username: "TheMarkus", + uploaded_avatar_id: 15186, + avatar_template: "/images/avatar.png" }, { - "id": 2806, - "username": "fayimora", - "uploaded_avatar_id": 10007, - "avatar_template":"/images/avatar.png" + id: 2806, + username: "fayimora", + uploaded_avatar_id: 10007, + avatar_template: "/images/avatar.png" }, { - "id": 8364, - "username": "codetricity", - "uploaded_avatar_id": 3773, - "avatar_template":"/images/avatar.png" + id: 8364, + username: "codetricity", + uploaded_avatar_id: 3773, + avatar_template: "/images/avatar.png" }, { - "id": 3752, - "username": "liberatiluca", - "uploaded_avatar_id": 11568, - "avatar_template":"/images/avatar.png" + id: 3752, + username: "liberatiluca", + uploaded_avatar_id: 11568, + avatar_template: "/images/avatar.png" }, { - "id": 3483, - "username": "Packetknife", - "uploaded_avatar_id": 11144, - "avatar_template":"/images/avatar.png" + id: 3483, + username: "Packetknife", + uploaded_avatar_id: 11144, + avatar_template: "/images/avatar.png" }, { - "id": 32, - "username": "codinghorror", - "uploaded_avatar_id": 5297, - "avatar_template":"/images/avatar.png" + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: "/images/avatar.png" }, { - "id": 19, - "username": "eviltrout", - "uploaded_avatar_id": 5275, - "avatar_template":"/images/avatar.png" + id: 19, + username: "eviltrout", + uploaded_avatar_id: 5275, + avatar_template: "/images/avatar.png" }, { - "id": 7229, - "username": "DavidGNavas", - "uploaded_avatar_id": 17081, - "avatar_template":"/images/avatar.png" + id: 7229, + username: "DavidGNavas", + uploaded_avatar_id: 17081, + avatar_template: "/images/avatar.png" }, { - "id": 1219, - "username": "Gweebz", - "uploaded_avatar_id": 7304, - "avatar_template":"/images/avatar.png" + id: 1219, + username: "Gweebz", + uploaded_avatar_id: 7304, + avatar_template: "/images/avatar.png" }, { - "id": 7743, - "username": "ZeroFlux", - "uploaded_avatar_id": 2256, - "avatar_template":"/images/avatar.png" + id: 7743, + username: "ZeroFlux", + uploaded_avatar_id: 2256, + avatar_template: "/images/avatar.png" }, { - "id": 8510, - "username": "tannerfilip", - "uploaded_avatar_id": 18674, - "avatar_template":"/images/avatar.png" + id: 8510, + username: "tannerfilip", + uploaded_avatar_id: 18674, + avatar_template: "/images/avatar.png" }, { - "id": 1496, - "username": "cfstras", - "uploaded_avatar_id": 7776, - "avatar_template":"/images/avatar.png" + id: 1496, + username: "cfstras", + uploaded_avatar_id: 7776, + avatar_template: "/images/avatar.png" }, { - "id": 3986, - "username": "creativetech", - "uploaded_avatar_id": 11955, - "avatar_template":"/images/avatar.png" + id: 3986, + username: "creativetech", + uploaded_avatar_id: 11955, + avatar_template: "/images/avatar.png" }, { - "id": 3800, - "username": "stealthii", - "uploaded_avatar_id": 11645, - "avatar_template":"/images/avatar.png" + id: 3800, + username: "stealthii", + uploaded_avatar_id: 11645, + avatar_template: "/images/avatar.png" }, { - "id": 6613, - "username": "haiku", - "uploaded_avatar_id": 9781, - "avatar_template":"/images/avatar.png" + id: 6613, + username: "haiku", + uploaded_avatar_id: 9781, + avatar_template: "/images/avatar.png" }, { - "id": 5351, - "username": "erlend_sh", - "uploaded_avatar_id": 9794, - "avatar_template":"/images/avatar.png" + id: 5351, + username: "erlend_sh", + uploaded_avatar_id: 9794, + avatar_template: "/images/avatar.png" }, { - "id": 5983, - "username": "JohnSReid", - "uploaded_avatar_id": 32238, - "avatar_template":"/images/avatar.png" + id: 5983, + username: "JohnSReid", + uploaded_avatar_id: 32238, + avatar_template: "/images/avatar.png" }, { - "id": 701, - "username": "johncoder", - "uploaded_avatar_id": 6447, - "avatar_template":"/images/avatar.png" + id: 701, + username: "johncoder", + uploaded_avatar_id: 6447, + avatar_template: "/images/avatar.png" }, { - "id": 5707, - "username": "trident", - "uploaded_avatar_id": 31178, - "avatar_template":"/images/avatar.png" + id: 5707, + username: "trident", + uploaded_avatar_id: 31178, + avatar_template: "/images/avatar.png" }, { - "id": 255, - "username": "uwe_keim", - "uploaded_avatar_id": 5697, - "avatar_template":"/images/avatar.png" + id: 255, + username: "uwe_keim", + uploaded_avatar_id: 5697, + avatar_template: "/images/avatar.png" }, { - "id": 9931, - "username": "Frank", - "uploaded_avatar_id": 32861, - "avatar_template":"/images/avatar.png" + id: 9931, + username: "Frank", + uploaded_avatar_id: 32861, + avatar_template: "/images/avatar.png" }, { - "id": 5543, - "username": "trevor", - "uploaded_avatar_id": 14507, - "avatar_template":"/images/avatar.png" + id: 5543, + username: "trevor", + uploaded_avatar_id: 14507, + avatar_template: "/images/avatar.png" }, { - "id": 3987, - "username": "Sander78", - "uploaded_avatar_id": 9787, - "avatar_template":"/images/avatar.png" + id: 3987, + username: "Sander78", + uploaded_avatar_id: 9787, + avatar_template: "/images/avatar.png" }, { - "id": 7850, - "username": "tudorv", - "uploaded_avatar_id": 2568, - "avatar_template":"/images/avatar.png" + id: 7850, + username: "tudorv", + uploaded_avatar_id: 2568, + avatar_template: "/images/avatar.png" }, { - "id": 6653, - "username": "amitfrid", - "uploaded_avatar_id": 16262, - "avatar_template":"/images/avatar.png" + id: 6653, + username: "amitfrid", + uploaded_avatar_id: 16262, + avatar_template: "/images/avatar.png" }, { - "id": 4419, - "username": "sasivarnakumar", - "uploaded_avatar_id": 12661, - "avatar_template":"/images/avatar.png" + id: 4419, + username: "sasivarnakumar", + uploaded_avatar_id: 12661, + avatar_template: "/images/avatar.png" }, { - "id": 5710, - "username": "elvanja", - "uploaded_avatar_id": 14781, - "avatar_template":"/images/avatar.png" + id: 5710, + username: "elvanja", + uploaded_avatar_id: 14781, + avatar_template: "/images/avatar.png" }, { - "id": 5401, - "username": "nilaykumar", - "uploaded_avatar_id": 14275, - "avatar_template":"/images/avatar.png" + id: 5401, + username: "nilaykumar", + uploaded_avatar_id: 14275, + avatar_template: "/images/avatar.png" }, { - "id": 6809, - "username": "buster", - "uploaded_avatar_id": 31175, - "avatar_template":"/images/avatar.png" + id: 6809, + username: "buster", + uploaded_avatar_id: 31175, + avatar_template: "/images/avatar.png" }, { - "id": 169, - "username": "blowmage", - "uploaded_avatar_id": 5545, - "avatar_template":"/images/avatar.png" + id: 169, + username: "blowmage", + uploaded_avatar_id: 5545, + avatar_template: "/images/avatar.png" }, { - "id": 766, - "username": "dworthley", - "uploaded_avatar_id": 6561, - "avatar_template":"/images/avatar.png" + id: 766, + username: "dworthley", + uploaded_avatar_id: 6561, + avatar_template: "/images/avatar.png" }, { - "id": 1612, - "username": "trottier", - "uploaded_avatar_id": 7977, - "avatar_template":"/images/avatar.png" + id: 1612, + username: "trottier", + uploaded_avatar_id: 7977, + avatar_template: "/images/avatar.png" }, { - "id": 6019, - "username": "mandie", - "uploaded_avatar_id": 15273, - "avatar_template":"/images/avatar.png" + id: 6019, + username: "mandie", + uploaded_avatar_id: 15273, + avatar_template: "/images/avatar.png" }, { - "id": 3724, - "username": "Manikin75", - "uploaded_avatar_id": 11520, - "avatar_template":"/images/avatar.png" + id: 3724, + username: "Manikin75", + uploaded_avatar_id: 11520, + avatar_template: "/images/avatar.png" }, { - "id": 1556, - "username": "OfferKaye", - "uploaded_avatar_id": 7878, - "avatar_template":"/images/avatar.png" + id: 1556, + username: "OfferKaye", + uploaded_avatar_id: 7878, + avatar_template: "/images/avatar.png" }, { - "id": 4063, - "username": "blanco", - "uploaded_avatar_id": 12082, - "avatar_template":"/images/avatar.png" + id: 4063, + username: "blanco", + uploaded_avatar_id: 12082, + avatar_template: "/images/avatar.png" }, { - "id": 1621, - "username": "bnb", - "uploaded_avatar_id": 7992, - "avatar_template":"/images/avatar.png" + id: 1621, + username: "bnb", + uploaded_avatar_id: 7992, + avatar_template: "/images/avatar.png" }, { - "id": 3095, - "username": "ayush", - "uploaded_avatar_id": 10504, - "avatar_template":"/images/avatar.png" + id: 3095, + username: "ayush", + uploaded_avatar_id: 10504, + avatar_template: "/images/avatar.png" }, { - "id": 754, - "username": "danneu", - "uploaded_avatar_id": 6540, - "avatar_template":"/images/avatar.png" + id: 754, + username: "danneu", + uploaded_avatar_id: 6540, + avatar_template: "/images/avatar.png" }, { - "id": 6548, - "username": "michaeld", - "uploaded_avatar_id": 1594, - "avatar_template":"/images/avatar.png" + id: 6548, + username: "michaeld", + uploaded_avatar_id: 1594, + avatar_template: "/images/avatar.png" }, { - "id": 4457, - "username": "Lee_Ars", - "uploaded_avatar_id": 1597, - "avatar_template":"/images/avatar.png" + id: 4457, + username: "Lee_Ars", + uploaded_avatar_id: 1597, + avatar_template: "/images/avatar.png" }, { - "id": 5160, - "username": "eriko", - "uploaded_avatar_id": 1915, - "avatar_template":"/images/avatar.png" + id: 5160, + username: "eriko", + uploaded_avatar_id: 1915, + avatar_template: "/images/avatar.png" }, { - "id": 10150, - "username": "ampburner", - "uploaded_avatar_id": 5103, - "avatar_template":"/images/avatar.png" + id: 10150, + username: "ampburner", + uploaded_avatar_id: 5103, + avatar_template: "/images/avatar.png" }, { - "id": 1, - "username": "sam", - "uploaded_avatar_id": 5243, - "avatar_template":"/images/avatar.png" + id: 1, + username: "sam", + uploaded_avatar_id: 5243, + avatar_template: "/images/avatar.png" }, { - "id": 1995, - "username": "zogstrip", - "uploaded_avatar_id": 8630, - "avatar_template":"/images/avatar.png" + id: 1995, + username: "zogstrip", + uploaded_avatar_id: 8630, + avatar_template: "/images/avatar.png" }, { - "id": 9536, - "username": "nahtnam", - "uploaded_avatar_id": 20077, - "avatar_template":"/images/avatar.png" + id: 9536, + username: "nahtnam", + uploaded_avatar_id: 20077, + avatar_template: "/images/avatar.png" }, { - "id": 5559, - "username": "downey", - "uploaded_avatar_id": 14532, - "avatar_template":"/images/avatar.png" + id: 5559, + username: "downey", + uploaded_avatar_id: 14532, + avatar_template: "/images/avatar.png" }, { - "id": 6626, - "username": "riking", - "uploaded_avatar_id": 9779, - "avatar_template":"/images/avatar.png" + id: 6626, + username: "riking", + uploaded_avatar_id: 9779, + avatar_template: "/images/avatar.png" }, { - "id": 562, - "username": "nightpool", - "uploaded_avatar_id": 6220, - "avatar_template":"/images/avatar.png" + id: 562, + username: "nightpool", + uploaded_avatar_id: 6220, + avatar_template: "/images/avatar.png" }, { - "id": 2770, - "username": "awesomerobot", - "uploaded_avatar_id": 32393, - "avatar_template":"/images/avatar.png" + id: 2770, + username: "awesomerobot", + uploaded_avatar_id: 32393, + avatar_template: "/images/avatar.png" }, { - "id": 4385, - "username": "jeans", - "uploaded_avatar_id": 12606, - "avatar_template":"/images/avatar.png" + id: 4385, + username: "jeans", + uploaded_avatar_id: 12606, + avatar_template: "/images/avatar.png" }, { - "id": 8222, - "username": "techAPJ", - "uploaded_avatar_id": 3281, - "avatar_template":"/images/avatar.png" + id: 8222, + username: "techAPJ", + uploaded_avatar_id: 3281, + avatar_template: "/images/avatar.png" }, { - "id": 1274, - "username": "binaryphile", - "uploaded_avatar_id": 7399, - "avatar_template":"/images/avatar.png" + id: 1274, + username: "binaryphile", + uploaded_avatar_id: 7399, + avatar_template: "/images/avatar.png" }, { - "id": 15, - "username": "Hanzo", - "uploaded_avatar_id": 5267, - "avatar_template":"/images/avatar.png" + id: 15, + username: "Hanzo", + uploaded_avatar_id: 5267, + avatar_template: "/images/avatar.png" }, { - "id": 5199, - "username": "sefier", - "uploaded_avatar_id": 31207, - "avatar_template":"/images/avatar.png" + id: 5199, + username: "sefier", + uploaded_avatar_id: 31207, + avatar_template: "/images/avatar.png" }, { - "id": 2316, - "username": "pakl", - "uploaded_avatar_id": 9157, - "avatar_template":"/images/avatar.png" + id: 2316, + username: "pakl", + uploaded_avatar_id: 9157, + avatar_template: "/images/avatar.png" }, { - "id": 393, - "username": "freney", - "uploaded_avatar_id": 5932, - "avatar_template":"/images/avatar.png" + id: 393, + username: "freney", + uploaded_avatar_id: 5932, + avatar_template: "/images/avatar.png" }, { - "id": 8492, - "username": "Onaldan", - "uploaded_avatar_id": 18651, - "avatar_template":"/images/avatar.png" + id: 8492, + username: "Onaldan", + uploaded_avatar_id: 18651, + avatar_template: "/images/avatar.png" }, { - "id": 5002, - "username": "jakeberger", - "uploaded_avatar_id": 13630, - "avatar_template":"/images/avatar.png" + id: 5002, + username: "jakeberger", + uploaded_avatar_id: 13630, + avatar_template: "/images/avatar.png" }, { - "id": 2544, - "username": "davideyre", - "uploaded_avatar_id": 9543, - "avatar_template":"/images/avatar.png" + id: 2544, + username: "davideyre", + uploaded_avatar_id: 9543, + avatar_template: "/images/avatar.png" }, { - "id": 8342, - "username": "sethuv", - "uploaded_avatar_id": 3036, - "avatar_template":"/images/avatar.png" + id: 8342, + username: "sethuv", + uploaded_avatar_id: 3036, + avatar_template: "/images/avatar.png" }, { - "id": 1128, - "username": "Tigraine", - "uploaded_avatar_id": 7152, - "avatar_template":"/images/avatar.png" + id: 1128, + username: "Tigraine", + uploaded_avatar_id: 7152, + avatar_template: "/images/avatar.png" }, { - "id": 2477, - "username": "billybonks", - "uploaded_avatar_id": 9430, - "avatar_template":"/images/avatar.png" + id: 2477, + username: "billybonks", + uploaded_avatar_id: 9430, + avatar_template: "/images/avatar.png" }, { - "id": 4549, - "username": "davidcelis", - "uploaded_avatar_id": 12882, - "avatar_template":"/images/avatar.png" + id: 4549, + username: "davidcelis", + uploaded_avatar_id: 12882, + avatar_template: "/images/avatar.png" }, { - "id": 7264, - "username": "etrowbridge", - "uploaded_avatar_id": 31199, - "avatar_template":"/images/avatar.png" + id: 7264, + username: "etrowbridge", + uploaded_avatar_id: 31199, + avatar_template: "/images/avatar.png" }, { - "id": 413, - "username": "adam_baldwin", - "uploaded_avatar_id": 5962, - "avatar_template":"/images/avatar.png" + id: 413, + username: "adam_baldwin", + uploaded_avatar_id: 5962, + avatar_template: "/images/avatar.png" }, { - "id": 8658, - "username": "Datachick", - "uploaded_avatar_id": 18865, - "avatar_template":"/images/avatar.png" + id: 8658, + username: "Datachick", + uploaded_avatar_id: 18865, + avatar_template: "/images/avatar.png" }, { - "id": 5294, - "username": "madbomber", - "uploaded_avatar_id": 14118, - "avatar_template":"/images/avatar.png" + id: 5294, + username: "madbomber", + uploaded_avatar_id: 14118, + avatar_template: "/images/avatar.png" }, { - "id": 4750, - "username": "dainbinder", - "uploaded_avatar_id": 13220, - "avatar_template":"/images/avatar.png" + id: 4750, + username: "dainbinder", + uploaded_avatar_id: 13220, + avatar_template: "/images/avatar.png" }, { - "id": 2735, - "username": "royce_williams", - "uploaded_avatar_id": 9887, - "avatar_template":"/images/avatar.png" + id: 2735, + username: "royce_williams", + uploaded_avatar_id: 9887, + avatar_template: "/images/avatar.png" }, { - "id": 9089, - "username": "Keezer", - "uploaded_avatar_id": 31186, - "avatar_template":"/images/avatar.png" + id: 9089, + username: "Keezer", + uploaded_avatar_id: 31186, + avatar_template: "/images/avatar.png" } ], - "user_badges": [ + user_badges: [ { - "id": 39085, - "granted_at": "2014-07-30T20:06:09.461-04:00", - "badge_id": 9, - "user_id": 11209, - "granted_by_id": -1 + id: 39085, + granted_at: "2014-07-30T20:06:09.461-04:00", + badge_id: 9, + user_id: 11209, + granted_by_id: -1 }, { - "id": 39035, - "granted_at": "2014-07-29T15:26:57.028-04:00", - "badge_id": 9, - "user_id": 11234, - "granted_by_id": -1 + id: 39035, + granted_at: "2014-07-29T15:26:57.028-04:00", + badge_id: 9, + user_id: 11234, + granted_by_id: -1 }, { - "id": 39034, - "granted_at": "2014-07-29T14:28:43.359-04:00", - "badge_id": 9, - "user_id": 8944, - "granted_by_id": -1 + id: 39034, + granted_at: "2014-07-29T14:28:43.359-04:00", + badge_id: 9, + user_id: 8944, + granted_by_id: -1 }, { - "id": 39027, - "granted_at": "2014-07-29T13:14:40.612-04:00", - "badge_id": 9, - "user_id": 11232, - "granted_by_id": -1 + id: 39027, + granted_at: "2014-07-29T13:14:40.612-04:00", + badge_id: 9, + user_id: 11232, + granted_by_id: -1 }, { - "id": 38874, - "granted_at": "2014-07-25T14:39:57.034-04:00", - "badge_id": 9, - "user_id": 11160, - "granted_by_id": -1 + id: 38874, + granted_at: "2014-07-25T14:39:57.034-04:00", + badge_id: 9, + user_id: 11160, + granted_by_id: -1 }, { - "id": 38866, - "granted_at": "2014-07-25T05:10:33.585-04:00", - "badge_id": 9, - "user_id": 5303, - "granted_by_id": -1 + id: 38866, + granted_at: "2014-07-25T05:10:33.585-04:00", + badge_id: 9, + user_id: 5303, + granted_by_id: -1 }, { - "id": 38838, - "granted_at": "2014-07-24T11:57:21.389-04:00", - "badge_id": 9, - "user_id": 11142, - "granted_by_id": -1 + id: 38838, + granted_at: "2014-07-24T11:57:21.389-04:00", + badge_id: 9, + user_id: 11142, + granted_by_id: -1 }, { - "id": 37659, - "granted_at": "2014-07-22T03:11:21.336-04:00", - "badge_id": 9, - "user_id": 8843, - "granted_by_id": -1 + id: 37659, + granted_at: "2014-07-22T03:11:21.336-04:00", + badge_id: 9, + user_id: 8843, + granted_by_id: -1 }, { - "id": 37611, - "granted_at": "2014-07-21T13:02:19.724-04:00", - "badge_id": 9, - "user_id": 10990, - "granted_by_id": -1 + id: 37611, + granted_at: "2014-07-21T13:02:19.724-04:00", + badge_id: 9, + user_id: 10990, + granted_by_id: -1 }, { - "id": 37537, - "granted_at": "2014-07-19T13:56:59.699-04:00", - "badge_id": 9, - "user_id": 11027, - "granted_by_id": -1 + id: 37537, + granted_at: "2014-07-19T13:56:59.699-04:00", + badge_id: 9, + user_id: 11027, + granted_by_id: -1 }, { - "id": 37497, - "granted_at": "2014-07-18T10:03:45.294-04:00", - "badge_id": 9, - "user_id": 10481, - "granted_by_id": -1 + id: 37497, + granted_at: "2014-07-18T10:03:45.294-04:00", + badge_id: 9, + user_id: 10481, + granted_by_id: -1 }, { - "id": 37217, - "granted_at": "2014-07-15T15:29:45.464-04:00", - "badge_id": 9, - "user_id": 10977, - "granted_by_id": -1 + id: 37217, + granted_at: "2014-07-15T15:29:45.464-04:00", + badge_id: 9, + user_id: 10977, + granted_by_id: -1 }, { - "id": 36865, - "granted_at": "2014-07-12T08:45:09.386-04:00", - "badge_id": 9, - "user_id": 10921, - "granted_by_id": -1 + id: 36865, + granted_at: "2014-07-12T08:45:09.386-04:00", + badge_id: 9, + user_id: 10921, + granted_by_id: -1 }, { - "id": 36126, - "granted_at": "2014-07-09T08:02:43.143-04:00", - "badge_id": 9, - "user_id": 8493, - "granted_by_id": -1 + id: 36126, + granted_at: "2014-07-09T08:02:43.143-04:00", + badge_id: 9, + user_id: 8493, + granted_by_id: -1 }, { - "id": 32551, - "granted_at": "2014-07-06T00:07:44.455-04:00", - "badge_id": 9, - "user_id": 10635, - "granted_by_id": -1 + id: 32551, + granted_at: "2014-07-06T00:07:44.455-04:00", + badge_id: 9, + user_id: 10635, + granted_by_id: -1 }, { - "id": 32550, - "granted_at": "2014-07-05T17:45:16.661-04:00", - "badge_id": 9, - "user_id": 8300, - "granted_by_id": -1 + id: 32550, + granted_at: "2014-07-05T17:45:16.661-04:00", + badge_id: 9, + user_id: 8300, + granted_by_id: -1 }, { - "id": 15327, - "granted_at": "2014-07-04T16:03:28.852-04:00", - "badge_id": 9, - "user_id": 8571, - "granted_by_id": -1 + id: 15327, + granted_at: "2014-07-04T16:03:28.852-04:00", + badge_id: 9, + user_id: 8571, + granted_by_id: -1 }, { - "id": 7787, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 4263, - "granted_by_id": -1 + id: 7787, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 4263, + granted_by_id: -1 }, { - "id": 7786, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 471, - "granted_by_id": -1 + id: 7786, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 471, + granted_by_id: -1 }, { - "id": 7785, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5249, - "granted_by_id": -1 + id: 7785, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5249, + granted_by_id: -1 }, { - "id": 7784, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5461, - "granted_by_id": -1 + id: 7784, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5461, + granted_by_id: -1 }, { - "id": 7783, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 10467, - "granted_by_id": -1 + id: 7783, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 10467, + granted_by_id: -1 }, { - "id": 7782, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 375, - "granted_by_id": -1 + id: 7782, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 375, + granted_by_id: -1 }, { - "id": 7781, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 8617, - "granted_by_id": -1 + id: 7781, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 8617, + granted_by_id: -1 }, { - "id": 7780, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5962, - "granted_by_id": -1 + id: 7780, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5962, + granted_by_id: -1 }, { - "id": 7779, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 2806, - "granted_by_id": -1 + id: 7779, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 2806, + granted_by_id: -1 }, { - "id": 7778, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 8364, - "granted_by_id": -1 + id: 7778, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 8364, + granted_by_id: -1 }, { - "id": 7777, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 3752, - "granted_by_id": -1 + id: 7777, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 3752, + granted_by_id: -1 }, { - "id": 7776, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 3483, - "granted_by_id": -1 + id: 7776, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 3483, + granted_by_id: -1 }, { - "id": 7775, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 32, - "granted_by_id": -1 + id: 7775, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 32, + granted_by_id: -1 }, { - "id": 7774, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 19, - "granted_by_id": -1 + id: 7774, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 19, + granted_by_id: -1 }, { - "id": 7773, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 7229, - "granted_by_id": -1 + id: 7773, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 7229, + granted_by_id: -1 }, { - "id": 7772, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 1219, - "granted_by_id": -1 + id: 7772, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 1219, + granted_by_id: -1 }, { - "id": 7771, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 7743, - "granted_by_id": -1 + id: 7771, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 7743, + granted_by_id: -1 }, { - "id": 7770, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 8510, - "granted_by_id": -1 + id: 7770, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 8510, + granted_by_id: -1 }, { - "id": 7769, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 1496, - "granted_by_id": -1 + id: 7769, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 1496, + granted_by_id: -1 }, { - "id": 7768, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 3986, - "granted_by_id": -1 + id: 7768, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 3986, + granted_by_id: -1 }, { - "id": 7767, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 3800, - "granted_by_id": -1 + id: 7767, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 3800, + granted_by_id: -1 }, { - "id": 7766, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 6613, - "granted_by_id": -1 + id: 7766, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 6613, + granted_by_id: -1 }, { - "id": 7765, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5351, - "granted_by_id": -1 + id: 7765, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5351, + granted_by_id: -1 }, { - "id": 7764, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5983, - "granted_by_id": -1 + id: 7764, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5983, + granted_by_id: -1 }, { - "id": 7763, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 701, - "granted_by_id": -1 + id: 7763, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 701, + granted_by_id: -1 }, { - "id": 7762, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5707, - "granted_by_id": -1 + id: 7762, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5707, + granted_by_id: -1 }, { - "id": 7761, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 255, - "granted_by_id": -1 + id: 7761, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 255, + granted_by_id: -1 }, { - "id": 7760, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 9931, - "granted_by_id": -1 + id: 7760, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 9931, + granted_by_id: -1 }, { - "id": 7759, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": -1, - "granted_by_id": -1 + id: 7759, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: -1, + granted_by_id: -1 }, { - "id": 7758, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5543, - "granted_by_id": -1 + id: 7758, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5543, + granted_by_id: -1 }, { - "id": 7757, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 3987, - "granted_by_id": -1 + id: 7757, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 3987, + granted_by_id: -1 }, { - "id": 7756, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 7850, - "granted_by_id": -1 + id: 7756, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 7850, + granted_by_id: -1 }, { - "id": 7755, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 6653, - "granted_by_id": -1 + id: 7755, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 6653, + granted_by_id: -1 }, { - "id": 7754, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 4419, - "granted_by_id": -1 + id: 7754, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 4419, + granted_by_id: -1 }, { - "id": 7753, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5710, - "granted_by_id": -1 + id: 7753, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5710, + granted_by_id: -1 }, { - "id": 7752, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5401, - "granted_by_id": -1 + id: 7752, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5401, + granted_by_id: -1 }, { - "id": 7751, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 6809, - "granted_by_id": -1 + id: 7751, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 6809, + granted_by_id: -1 }, { - "id": 7750, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 169, - "granted_by_id": -1 + id: 7750, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 169, + granted_by_id: -1 }, { - "id": 7749, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 766, - "granted_by_id": -1 + id: 7749, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 766, + granted_by_id: -1 }, { - "id": 7748, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 1612, - "granted_by_id": -1 + id: 7748, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 1612, + granted_by_id: -1 }, { - "id": 7747, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 6019, - "granted_by_id": -1 + id: 7747, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 6019, + granted_by_id: -1 }, { - "id": 7746, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 3724, - "granted_by_id": -1 + id: 7746, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 3724, + granted_by_id: -1 }, { - "id": 7745, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 1556, - "granted_by_id": -1 + id: 7745, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 1556, + granted_by_id: -1 }, { - "id": 7744, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 4063, - "granted_by_id": -1 + id: 7744, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 4063, + granted_by_id: -1 }, { - "id": 7743, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 1621, - "granted_by_id": -1 + id: 7743, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 1621, + granted_by_id: -1 }, { - "id": 7742, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 3095, - "granted_by_id": -1 + id: 7742, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 3095, + granted_by_id: -1 }, { - "id": 7741, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 754, - "granted_by_id": -1 + id: 7741, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 754, + granted_by_id: -1 }, { - "id": 7740, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 6548, - "granted_by_id": -1 + id: 7740, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 6548, + granted_by_id: -1 }, { - "id": 7739, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 4457, - "granted_by_id": -1 + id: 7739, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 4457, + granted_by_id: -1 }, { - "id": 7738, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5160, - "granted_by_id": -1 + id: 7738, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5160, + granted_by_id: -1 }, { - "id": 7737, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 10150, - "granted_by_id": -1 + id: 7737, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 10150, + granted_by_id: -1 }, { - "id": 7736, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 1, - "granted_by_id": -1 + id: 7736, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 1, + granted_by_id: -1 }, { - "id": 7735, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 1995, - "granted_by_id": -1 + id: 7735, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 1995, + granted_by_id: -1 }, { - "id": 7734, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 9536, - "granted_by_id": -1 + id: 7734, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 9536, + granted_by_id: -1 }, { - "id": 7733, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5559, - "granted_by_id": -1 + id: 7733, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5559, + granted_by_id: -1 }, { - "id": 7732, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 6626, - "granted_by_id": -1 + id: 7732, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 6626, + granted_by_id: -1 }, { - "id": 7731, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 562, - "granted_by_id": -1 + id: 7731, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 562, + granted_by_id: -1 }, { - "id": 7730, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 2770, - "granted_by_id": -1 + id: 7730, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 2770, + granted_by_id: -1 }, { - "id": 7729, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 4385, - "granted_by_id": -1 + id: 7729, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 4385, + granted_by_id: -1 }, { - "id": 7728, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 8222, - "granted_by_id": -1 + id: 7728, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 8222, + granted_by_id: -1 }, { - "id": 7727, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 1274, - "granted_by_id": -1 + id: 7727, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 1274, + granted_by_id: -1 }, { - "id": 7726, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 15, - "granted_by_id": -1 + id: 7726, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 15, + granted_by_id: -1 }, { - "id": 7725, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5199, - "granted_by_id": -1 + id: 7725, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5199, + granted_by_id: -1 }, { - "id": 7724, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 2316, - "granted_by_id": -1 + id: 7724, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 2316, + granted_by_id: -1 }, { - "id": 7723, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 393, - "granted_by_id": -1 + id: 7723, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 393, + granted_by_id: -1 }, { - "id": 7722, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 8492, - "granted_by_id": -1 + id: 7722, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 8492, + granted_by_id: -1 }, { - "id": 7721, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5002, - "granted_by_id": -1 + id: 7721, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5002, + granted_by_id: -1 }, { - "id": 7720, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 2544, - "granted_by_id": -1 + id: 7720, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 2544, + granted_by_id: -1 }, { - "id": 7719, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 8342, - "granted_by_id": -1 + id: 7719, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 8342, + granted_by_id: -1 }, { - "id": 7718, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 1128, - "granted_by_id": -1 + id: 7718, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 1128, + granted_by_id: -1 }, { - "id": 7717, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 2477, - "granted_by_id": -1 + id: 7717, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 2477, + granted_by_id: -1 }, { - "id": 7716, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 4549, - "granted_by_id": -1 + id: 7716, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 4549, + granted_by_id: -1 }, { - "id": 7715, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 7264, - "granted_by_id": -1 + id: 7715, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 7264, + granted_by_id: -1 }, { - "id": 7714, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 413, - "granted_by_id": -1 + id: 7714, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 413, + granted_by_id: -1 }, { - "id": 7713, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 8658, - "granted_by_id": -1 + id: 7713, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 8658, + granted_by_id: -1 }, { - "id": 7712, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 5294, - "granted_by_id": -1 + id: 7712, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 5294, + granted_by_id: -1 }, { - "id": 7711, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 4750, - "granted_by_id": -1 + id: 7711, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 4750, + granted_by_id: -1 }, { - "id": 7710, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 2735, - "granted_by_id": -1 + id: 7710, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 2735, + granted_by_id: -1 }, { - "id": 7709, - "granted_at": "2014-07-03T06:36:46.939-04:00", - "badge_id": 9, - "user_id": 9089, - "granted_by_id": -1 + id: 7709, + granted_at: "2014-07-03T06:36:46.939-04:00", + badge_id: 9, + user_id: 9089, + granted_by_id: -1 } ] } diff --git a/test/javascripts/fixtures/category-fixtures.js.es6 b/test/javascripts/fixtures/category-fixtures.js.es6 index 1769e488da0..ea1842a71a2 100644 --- a/test/javascripts/fixtures/category-fixtures.js.es6 +++ b/test/javascripts/fixtures/category-fixtures.js.es6 @@ -1,3 +1,43 @@ export default { - "/c/1/show.json": {"category":{"id":1,"name":"bug","color":"e9dd00","text_color":"000000","slug":"bug","topic_count":2030,"post_count":13418,"description":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","description_text":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","topic_url":"/t/category-definition-for-bug/2","read_restricted":false,"permission":null,"notification_level":null,"logo_url":null,"background_url":null,"available_groups":["admins","discourse","everyone","moderators","staff","translators","trust_level_0","trust_level_1","trust_level_2","trust_level_3","trust_level_4"],"auto_close_hours":null,"auto_close_based_on_last_post":false,"group_permissions":[{"permission_type":1,"group_name":"everyone"}],"position":25,"cannot_delete_reason":"Can't delete this category because it has 2030 topics. Oldest topic is When a new post appears in a topic, the bookmark isn't updated.","allow_badges":true}} + "/c/1/show.json": { + category: { + id: 1, + name: "bug", + color: "e9dd00", + text_color: "000000", + slug: "bug", + topic_count: 2030, + post_count: 13418, + description: + "A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.", + description_text: + "A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.", + topic_url: "/t/category-definition-for-bug/2", + read_restricted: false, + permission: null, + notification_level: null, + logo_url: null, + background_url: null, + available_groups: [ + "admins", + "discourse", + "everyone", + "moderators", + "staff", + "translators", + "trust_level_0", + "trust_level_1", + "trust_level_2", + "trust_level_3", + "trust_level_4" + ], + auto_close_hours: null, + auto_close_based_on_last_post: false, + group_permissions: [{ permission_type: 1, group_name: "everyone" }], + position: 25, + cannot_delete_reason: + "Can't delete this category because it has 2030 topics. Oldest topic is When a new post appears in a topic, the bookmark isn't updated.", + allow_badges: true + } + } }; diff --git a/test/javascripts/fixtures/daily-engaged-users.js.es6 b/test/javascripts/fixtures/daily-engaged-users.js.es6 index 4787c458f37..b734c47b74b 100644 --- a/test/javascripts/fixtures/daily-engaged-users.js.es6 +++ b/test/javascripts/fixtures/daily-engaged-users.js.es6 @@ -1,20 +1,20 @@ export default { "/admin/reports/daily_engaged_users": { - "report": { - "type": "daily_engaged_users", - "title": "Daily Engaged Users", - "xaxis": "Day", - "yaxis": "Engaged Users", - "description": "Number of users that have liked or posted in the last day", - "data": null, - "total": null, - "start_date": "2018-04-03", - "end_date": "2018-05-03", - "category_id": null, - "group_id": null, - "prev30Days": null, - "labels": null, - "report_key": "" + report: { + type: "daily_engaged_users", + title: "Daily Engaged Users", + xaxis: "Day", + yaxis: "Engaged Users", + description: "Number of users that have liked or posted in the last day", + data: null, + total: null, + start_date: "2018-04-03", + end_date: "2018-05-03", + category_id: null, + group_id: null, + prev30Days: null, + labels: null, + report_key: "" } } }; diff --git a/test/javascripts/fixtures/dashboard-next.js.es6 b/test/javascripts/fixtures/dashboard-next.js.es6 index 91ff5ea2bf8..f1dfb0b7d03 100644 --- a/test/javascripts/fixtures/dashboard-next.js.es6 +++ b/test/javascripts/fixtures/dashboard-next.js.es6 @@ -1,13 +1,13 @@ export default { "/admin/dashboard-next.json": { - "reports": [], - "last_backup_taken_at": "2018-04-13T12:51:19.926Z", - "updated_at": "2018-04-25T08:06:11.292Z", - "disk_space": { - "uploads_used": "74.5 KB", - "uploads_free": "117 GB", - "backups_used": "4.24 GB", - "backups_free": "117 GB" + reports: [], + last_backup_taken_at: "2018-04-13T12:51:19.926Z", + updated_at: "2018-04-25T08:06:11.292Z", + disk_space: { + uploads_used: "74.5 KB", + uploads_free: "117 GB", + backups_used: "4.24 GB", + backups_free: "117 GB" } } }; diff --git a/test/javascripts/fixtures/dau-by-mau.js.es6 b/test/javascripts/fixtures/dau-by-mau.js.es6 index 78a125d9522..221486bcf6d 100644 --- a/test/javascripts/fixtures/dau-by-mau.js.es6 +++ b/test/javascripts/fixtures/dau-by-mau.js.es6 @@ -1,20 +1,20 @@ export default { "/admin/reports/dau_by_mau": { - "report": { - "type": "dau_by_mau", - "title": "DAU/MAU", - "xaxis": "Day", - "yaxis": "DAU/MAY", - "description": "Percentage of daily active users on monthly active users", - "data": null, - "total": null, - "start_date": "2018-01-26T00:00:00.000Z", - "end_date": "2018-04-27T23:59:59.999Z", - "category_id": null, - "group_id": null, - "prev30Days": 46, - "labels": null, - "report_key": "" + report: { + type: "dau_by_mau", + title: "DAU/MAU", + xaxis: "Day", + yaxis: "DAU/MAY", + description: "Percentage of daily active users on monthly active users", + data: null, + total: null, + start_date: "2018-01-26T00:00:00.000Z", + end_date: "2018-04-27T23:59:59.999Z", + category_id: null, + group_id: null, + prev30Days: 46, + labels: null, + report_key: "" } } }; diff --git a/test/javascripts/fixtures/directory-fixtures.js.es6 b/test/javascripts/fixtures/directory-fixtures.js.es6 index 82ae8d6090d..ab1b647f4e8 100644 --- a/test/javascripts/fixtures/directory-fixtures.js.es6 +++ b/test/javascripts/fixtures/directory-fixtures.js.es6 @@ -1,3 +1,609 @@ export default { - "directory_items": {"directory_items":[{"id":32,"username":"codinghorror","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"55d","likes_received":9370,"likes_given":7725,"topics_entered":11453,"topic_count":184,"post_count":12263},{"id":1,"username":"sam","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"52d","likes_received":7834,"likes_given":2693,"topics_entered":11024,"topic_count":276,"post_count":7802},{"id":19,"username":"eviltrout","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"25d","likes_received":2383,"likes_given":319,"topics_entered":8041,"topic_count":34,"post_count":1602},{"id":6626,"username":"riking","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"17d","likes_received":2101,"likes_given":2756,"topics_entered":9055,"topic_count":163,"post_count":2548},{"id":1995,"username":"zogstrip","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"32d","likes_received":1838,"likes_given":4588,"topics_entered":10823,"topic_count":16,"post_count":2050},{"id":8300,"username":"cpradio","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"11d","likes_received":1538,"likes_given":1001,"topics_entered":6121,"topic_count":111,"post_count":1430},{"id":2,"username":"neil","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"24d","likes_received":1238,"likes_given":684,"topics_entered":3250,"topic_count":27,"post_count":969},{"id":4263,"username":"mcwumbly","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"15d","likes_received":1223,"likes_given":1296,"topics_entered":5924,"topic_count":81,"post_count":1031},{"id":5351,"username":"erlend_sh","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"9d","likes_received":1115,"likes_given":747,"topics_entered":3260,"topic_count":154,"post_count":721},{"id":5559,"username":"downey","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"5d","likes_received":983,"likes_given":1713,"topics_entered":2995,"topic_count":131,"post_count":850},{"id":2770,"username":"awesomerobot","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"9d","likes_received":952,"likes_given":195,"topics_entered":2411,"topic_count":13,"post_count":402},{"id":9775,"username":"elberet","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"7d","likes_received":930,"likes_given":159,"topics_entered":4077,"topic_count":28,"post_count":755},{"id":8222,"username":"techAPJ","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"12d","likes_received":791,"likes_given":1005,"topics_entered":3691,"topic_count":43,"post_count":463},{"id":6060,"username":"lightyear","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"3d","likes_received":708,"likes_given":330,"topics_entered":1717,"topic_count":34,"post_count":312},{"id":8,"username":"geek","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"20d","likes_received":634,"likes_given":152,"topics_entered":920,"topic_count":48,"post_count":298},{"id":464,"username":"DeanMarkTaylor","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"10d","likes_received":578,"likes_given":299,"topics_entered":2976,"topic_count":116,"post_count":485},{"id":11160,"username":"boomzilla","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"1d","likes_received":561,"likes_given":398,"topics_entered":822,"topic_count":23,"post_count":185},{"id":4457,"username":"Lee_Ars","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"3d","likes_received":530,"likes_given":163,"topics_entered":2250,"topic_count":46,"post_count":327},{"id":8571,"username":"tobiaseigen","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"6d","likes_received":524,"likes_given":1275,"topics_entered":2545,"topic_count":140,"post_count":435},{"id":3,"username":"supermathie","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"20d","likes_received":510,"likes_given":312,"topics_entered":1733,"topic_count":62,"post_count":438},{"id":8493,"username":"PJH","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"3d","likes_received":458,"likes_given":96,"topics_entered":1219,"topic_count":74,"post_count":318},{"id":8617,"username":"Mittineague","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"16d","likes_received":415,"likes_given":291,"topics_entered":6662,"topic_count":22,"post_count":757},{"id":10778,"username":"Lid","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"8d","likes_received":398,"likes_given":296,"topics_entered":1771,"topic_count":82,"post_count":307},{"id":6548,"username":"michaeld","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"3d","likes_received":387,"likes_given":165,"topics_entered":1407,"topic_count":48,"post_count":330},{"id":471,"username":"BhaelOchon","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"22d","likes_received":386,"likes_given":765,"topics_entered":8051,"topic_count":55,"post_count":486},{"id":4881,"username":"gerhard","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"6d","likes_received":360,"likes_given":393,"topics_entered":3030,"topic_count":57,"post_count":250},{"id":5707,"username":"trident","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"6d","likes_received":344,"likes_given":181,"topics_entered":4905,"topic_count":2,"post_count":549},{"id":3987,"username":"Sander78","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"3d","likes_received":326,"likes_given":276,"topics_entered":3613,"topic_count":94,"post_count":392},{"id":3415,"username":"radq","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"3d","likes_received":299,"likes_given":110,"topics_entered":2503,"topic_count":16,"post_count":157},{"id":2989,"username":"meglio","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"3d","likes_received":280,"likes_given":436,"topics_entered":1086,"topic_count":198,"post_count":458},{"id":4,"username":"stienman","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"15d","likes_received":276,"likes_given":100,"topics_entered":291,"topic_count":13,"post_count":132},{"id":10855,"username":"abarker","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"1d","likes_received":270,"likes_given":131,"topics_entered":703,"topic_count":14,"post_count":77},{"id":9653,"username":"TechnoBear","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"4d","likes_received":263,"likes_given":507,"topics_entered":2931,"topic_count":51,"post_count":220},{"id":7948,"username":"probus","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"6d","likes_received":261,"likes_given":71,"topics_entered":2399,"topic_count":51,"post_count":206},{"id":9741,"username":"chapel","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"2d","likes_received":239,"likes_given":169,"topics_entered":1228,"topic_count":11,"post_count":167},{"id":8810,"username":"fantasticfears","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"4d","likes_received":213,"likes_given":184,"topics_entered":2161,"topic_count":29,"post_count":227},{"id":38,"username":"frandallfarmer","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"19d","likes_received":212,"likes_given":104,"topics_entered":3169,"topic_count":6,"post_count":114},{"id":8085,"username":"watchmanmonitor","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"2d","likes_received":202,"likes_given":654,"topics_entered":1453,"topic_count":73,"post_count":278},{"id":8909,"username":"AdamCapriola","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"3d","likes_received":200,"likes_given":179,"topics_entered":1689,"topic_count":49,"post_count":169},{"id":14,"username":"clay","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"14d","likes_received":183,"likes_given":103,"topics_entered":780,"topic_count":24,"post_count":97},{"id":6613,"username":"haiku","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"3d","likes_received":183,"likes_given":308,"topics_entered":1919,"topic_count":33,"post_count":188},{"id":13132,"username":"purldator","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"4d","likes_received":178,"likes_given":685,"topics_entered":1891,"topic_count":20,"post_count":299},{"id":810,"username":"ChrisHanel","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"16d","likes_received":169,"likes_given":42,"topics_entered":639,"topic_count":9,"post_count":94},{"id":2625,"username":"kpfleming","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"10d","likes_received":165,"likes_given":288,"topics_entered":2539,"topic_count":15,"post_count":233},{"id":7717,"username":"lake54","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"2d","likes_received":148,"likes_given":440,"topics_entered":1604,"topic_count":33,"post_count":194},{"id":8018,"username":"shivermetimbers","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"15h","likes_received":139,"likes_given":40,"topics_entered":185,"topic_count":30,"post_count":181},{"id":2316,"username":"pakl","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"14d","likes_received":135,"likes_given":198,"topics_entered":1034,"topic_count":46,"post_count":130},{"id":3681,"username":"Ajarn","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"3d","likes_received":126,"likes_given":664,"topics_entered":1893,"topic_count":46,"post_count":291},{"id":7229,"username":"DavidGNavas","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"4d","likes_received":124,"likes_given":210,"topics_entered":2032,"topic_count":17,"post_count":60},{"id":3062,"username":"Sailsman63","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","time_read":"8d","likes_received":124,"likes_given":139,"topics_entered":4257,"topic_count":10,"post_count":146}],"total_rows_directory_items":12546,"load_more_directory_items":"/directory_items?id=all&order=likes_received&page=1"} + directory_items: { + directory_items: [ + { + id: 32, + username: "codinghorror", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "55d", + likes_received: 9370, + likes_given: 7725, + topics_entered: 11453, + topic_count: 184, + post_count: 12263 + }, + { + id: 1, + username: "sam", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "52d", + likes_received: 7834, + likes_given: 2693, + topics_entered: 11024, + topic_count: 276, + post_count: 7802 + }, + { + id: 19, + username: "eviltrout", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "25d", + likes_received: 2383, + likes_given: 319, + topics_entered: 8041, + topic_count: 34, + post_count: 1602 + }, + { + id: 6626, + username: "riking", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "17d", + likes_received: 2101, + likes_given: 2756, + topics_entered: 9055, + topic_count: 163, + post_count: 2548 + }, + { + id: 1995, + username: "zogstrip", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "32d", + likes_received: 1838, + likes_given: 4588, + topics_entered: 10823, + topic_count: 16, + post_count: 2050 + }, + { + id: 8300, + username: "cpradio", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "11d", + likes_received: 1538, + likes_given: 1001, + topics_entered: 6121, + topic_count: 111, + post_count: 1430 + }, + { + id: 2, + username: "neil", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "24d", + likes_received: 1238, + likes_given: 684, + topics_entered: 3250, + topic_count: 27, + post_count: 969 + }, + { + id: 4263, + username: "mcwumbly", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "15d", + likes_received: 1223, + likes_given: 1296, + topics_entered: 5924, + topic_count: 81, + post_count: 1031 + }, + { + id: 5351, + username: "erlend_sh", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "9d", + likes_received: 1115, + likes_given: 747, + topics_entered: 3260, + topic_count: 154, + post_count: 721 + }, + { + id: 5559, + username: "downey", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "5d", + likes_received: 983, + likes_given: 1713, + topics_entered: 2995, + topic_count: 131, + post_count: 850 + }, + { + id: 2770, + username: "awesomerobot", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "9d", + likes_received: 952, + likes_given: 195, + topics_entered: 2411, + topic_count: 13, + post_count: 402 + }, + { + id: 9775, + username: "elberet", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "7d", + likes_received: 930, + likes_given: 159, + topics_entered: 4077, + topic_count: 28, + post_count: 755 + }, + { + id: 8222, + username: "techAPJ", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "12d", + likes_received: 791, + likes_given: 1005, + topics_entered: 3691, + topic_count: 43, + post_count: 463 + }, + { + id: 6060, + username: "lightyear", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "3d", + likes_received: 708, + likes_given: 330, + topics_entered: 1717, + topic_count: 34, + post_count: 312 + }, + { + id: 8, + username: "geek", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "20d", + likes_received: 634, + likes_given: 152, + topics_entered: 920, + topic_count: 48, + post_count: 298 + }, + { + id: 464, + username: "DeanMarkTaylor", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "10d", + likes_received: 578, + likes_given: 299, + topics_entered: 2976, + topic_count: 116, + post_count: 485 + }, + { + id: 11160, + username: "boomzilla", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "1d", + likes_received: 561, + likes_given: 398, + topics_entered: 822, + topic_count: 23, + post_count: 185 + }, + { + id: 4457, + username: "Lee_Ars", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "3d", + likes_received: 530, + likes_given: 163, + topics_entered: 2250, + topic_count: 46, + post_count: 327 + }, + { + id: 8571, + username: "tobiaseigen", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "6d", + likes_received: 524, + likes_given: 1275, + topics_entered: 2545, + topic_count: 140, + post_count: 435 + }, + { + id: 3, + username: "supermathie", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "20d", + likes_received: 510, + likes_given: 312, + topics_entered: 1733, + topic_count: 62, + post_count: 438 + }, + { + id: 8493, + username: "PJH", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "3d", + likes_received: 458, + likes_given: 96, + topics_entered: 1219, + topic_count: 74, + post_count: 318 + }, + { + id: 8617, + username: "Mittineague", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "16d", + likes_received: 415, + likes_given: 291, + topics_entered: 6662, + topic_count: 22, + post_count: 757 + }, + { + id: 10778, + username: "Lid", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "8d", + likes_received: 398, + likes_given: 296, + topics_entered: 1771, + topic_count: 82, + post_count: 307 + }, + { + id: 6548, + username: "michaeld", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "3d", + likes_received: 387, + likes_given: 165, + topics_entered: 1407, + topic_count: 48, + post_count: 330 + }, + { + id: 471, + username: "BhaelOchon", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "22d", + likes_received: 386, + likes_given: 765, + topics_entered: 8051, + topic_count: 55, + post_count: 486 + }, + { + id: 4881, + username: "gerhard", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "6d", + likes_received: 360, + likes_given: 393, + topics_entered: 3030, + topic_count: 57, + post_count: 250 + }, + { + id: 5707, + username: "trident", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "6d", + likes_received: 344, + likes_given: 181, + topics_entered: 4905, + topic_count: 2, + post_count: 549 + }, + { + id: 3987, + username: "Sander78", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "3d", + likes_received: 326, + likes_given: 276, + topics_entered: 3613, + topic_count: 94, + post_count: 392 + }, + { + id: 3415, + username: "radq", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "3d", + likes_received: 299, + likes_given: 110, + topics_entered: 2503, + topic_count: 16, + post_count: 157 + }, + { + id: 2989, + username: "meglio", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "3d", + likes_received: 280, + likes_given: 436, + topics_entered: 1086, + topic_count: 198, + post_count: 458 + }, + { + id: 4, + username: "stienman", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "15d", + likes_received: 276, + likes_given: 100, + topics_entered: 291, + topic_count: 13, + post_count: 132 + }, + { + id: 10855, + username: "abarker", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "1d", + likes_received: 270, + likes_given: 131, + topics_entered: 703, + topic_count: 14, + post_count: 77 + }, + { + id: 9653, + username: "TechnoBear", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "4d", + likes_received: 263, + likes_given: 507, + topics_entered: 2931, + topic_count: 51, + post_count: 220 + }, + { + id: 7948, + username: "probus", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "6d", + likes_received: 261, + likes_given: 71, + topics_entered: 2399, + topic_count: 51, + post_count: 206 + }, + { + id: 9741, + username: "chapel", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "2d", + likes_received: 239, + likes_given: 169, + topics_entered: 1228, + topic_count: 11, + post_count: 167 + }, + { + id: 8810, + username: "fantasticfears", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "4d", + likes_received: 213, + likes_given: 184, + topics_entered: 2161, + topic_count: 29, + post_count: 227 + }, + { + id: 38, + username: "frandallfarmer", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "19d", + likes_received: 212, + likes_given: 104, + topics_entered: 3169, + topic_count: 6, + post_count: 114 + }, + { + id: 8085, + username: "watchmanmonitor", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "2d", + likes_received: 202, + likes_given: 654, + topics_entered: 1453, + topic_count: 73, + post_count: 278 + }, + { + id: 8909, + username: "AdamCapriola", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "3d", + likes_received: 200, + likes_given: 179, + topics_entered: 1689, + topic_count: 49, + post_count: 169 + }, + { + id: 14, + username: "clay", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "14d", + likes_received: 183, + likes_given: 103, + topics_entered: 780, + topic_count: 24, + post_count: 97 + }, + { + id: 6613, + username: "haiku", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "3d", + likes_received: 183, + likes_given: 308, + topics_entered: 1919, + topic_count: 33, + post_count: 188 + }, + { + id: 13132, + username: "purldator", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "4d", + likes_received: 178, + likes_given: 685, + topics_entered: 1891, + topic_count: 20, + post_count: 299 + }, + { + id: 810, + username: "ChrisHanel", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "16d", + likes_received: 169, + likes_given: 42, + topics_entered: 639, + topic_count: 9, + post_count: 94 + }, + { + id: 2625, + username: "kpfleming", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "10d", + likes_received: 165, + likes_given: 288, + topics_entered: 2539, + topic_count: 15, + post_count: 233 + }, + { + id: 7717, + username: "lake54", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "2d", + likes_received: 148, + likes_given: 440, + topics_entered: 1604, + topic_count: 33, + post_count: 194 + }, + { + id: 8018, + username: "shivermetimbers", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "15h", + likes_received: 139, + likes_given: 40, + topics_entered: 185, + topic_count: 30, + post_count: 181 + }, + { + id: 2316, + username: "pakl", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "14d", + likes_received: 135, + likes_given: 198, + topics_entered: 1034, + topic_count: 46, + post_count: 130 + }, + { + id: 3681, + username: "Ajarn", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "3d", + likes_received: 126, + likes_given: 664, + topics_entered: 1893, + topic_count: 46, + post_count: 291 + }, + { + id: 7229, + username: "DavidGNavas", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "4d", + likes_received: 124, + likes_given: 210, + topics_entered: 2032, + topic_count: 17, + post_count: 60 + }, + { + id: 3062, + username: "Sailsman63", + uploaded_avatar_id: null, + avatar_template: "/images/avatar.png", + time_read: "8d", + likes_received: 124, + likes_given: 139, + topics_entered: 4257, + topic_count: 10, + post_count: 146 + } + ], + total_rows_directory_items: 12546, + load_more_directory_items: + "/directory_items?id=all&order=likes_received&page=1" + } }; diff --git a/test/javascripts/fixtures/discovery_fixtures.js.es6 b/test/javascripts/fixtures/discovery_fixtures.js.es6 index 8f0acc137d9..3f1dc63adb4 100644 --- a/test/javascripts/fixtures/discovery_fixtures.js.es6 +++ b/test/javascripts/fixtures/discovery_fixtures.js.es6 @@ -1,9 +1,6014 @@ /*jshint maxlen:10000000 */ export default { -"/latest.json": {"users":[{"id":7204,"username":"reyman64","avatar_template":"/images/avatar.png"},{"id":1,"username":"sam","avatar_template":"/images/avatar.png"},{"id":5481,"username":"f0rkz","avatar_template":"/images/avatar.png"},{"id":6473,"username":"jkf","avatar_template":"/images/avatar.png"},{"id":6973,"username":"stellarhopper","avatar_template":"/images/avatar.png"},{"id":19,"username":"eviltrout","avatar_template":"/images/avatar.png"},{"id":14,"username":"clay","avatar_template":"/images/avatar.png"},{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"},{"id":1917,"username":"sil","avatar_template":"/images/avatar.png"},{"id":7197,"username":"peeja","avatar_template":"/images/avatar.png"},{"id":1995,"username":"zogstrip","avatar_template":"/images/avatar.png"},{"id":8021,"username":"Abhishek_Gupta","avatar_template":"/images/avatar.png"},{"id":2291,"username":"PabloC","avatar_template":"/images/avatar.png"},{"id":791,"username":"srid","avatar_template":"/images/avatar.png"},{"id":1580,"username":"ABillionSuns","avatar_template":"/images/avatar.png"},{"id":7270,"username":"mhurwi","avatar_template":"/images/avatar.png"},{"id":6695,"username":"illspirit","avatar_template":"/images/avatar.png"},{"id":6929,"username":"BCHK","avatar_template":"/images/avatar.png"},{"id":4385,"username":"jeans","avatar_template":"/images/avatar.png"},{"id":7073,"username":"5an1ty","avatar_template":"/images/avatar.png"},{"id":6626,"username":"riking","avatar_template":"/images/avatar.png"},{"id":4457,"username":"Lee_Ars","avatar_template":"/images/avatar.png"},{"id":4263,"username":"mcwumbly","avatar_template":"/images/avatar.png"},{"id":8134,"username":"iontishina","avatar_template":"/images/avatar.png"},{"id":2072,"username":"nXqd","avatar_template":"/images/avatar.png"},{"id":4983,"username":"hey_julien","avatar_template":"/images/avatar.png"},{"id":3657,"username":"steelmaiden","avatar_template":"/images/avatar.png"},{"id":2624,"username":"BowlingX","avatar_template":"/images/avatar.png"},{"id":8085,"username":"watchmanmonitor","avatar_template":"/images/avatar.png"},{"id":4612,"username":"Iszi","avatar_template":"/images/avatar.png"},{"id":8018,"username":"shivermetimbers","avatar_template":"/images/avatar.png"},{"id":6060,"username":"lightyear","avatar_template":"/images/avatar.png"},{"id":2,"username":"neil","avatar_template":"/images/avatar.png"},{"id":8037,"username":"printec","avatar_template":"/images/avatar.png"},{"id":3415,"username":"radq","avatar_template":"/images/avatar.png"},{"id":6283,"username":"hrishikesh","avatar_template":"/images/avatar.png"},{"id":471,"username":"BhaelOchon","avatar_template":"/images/avatar.png"},{"id":6548,"username":"michaeld","avatar_template":"/images/avatar.png"},{"id":7286,"username":"mrotsnahoj","avatar_template":"/images/avatar.png"},{"id":3169,"username":"dgw","avatar_template":"/images/avatar.png"},{"id":926,"username":"martinnormark","avatar_template":"/images/avatar.png"},{"id":2003,"username":"taylor","avatar_template":"/images/avatar.png"},{"id":369,"username":"CvX","avatar_template":"/images/avatar.png"},{"id":562,"username":"nightpool","avatar_template":"/images/avatar.png"},{"id":6653,"username":"amitfrid","avatar_template":"/images/avatar.png"},{"id":6677,"username":"Tropnevad","avatar_template":"/images/avatar.png"},{"id":5048,"username":"SneakySly","avatar_template":"/images/avatar.png"},{"id":7333,"username":"Jong","avatar_template":"/images/avatar.png"},{"id":3124,"username":"sipp11","avatar_template":"/images/avatar.png"},{"id":7604,"username":"citkane","avatar_template":"/images/avatar.png"},{"id":3929,"username":"ScotterC","avatar_template":"/images/avatar.png"},{"id":6680,"username":"cdman","avatar_template":"/images/avatar.png"},{"id":500,"username":"aeid","avatar_template":"/images/avatar.png"},{"id":8,"username":"geek","avatar_template":"/images/avatar.png"},{"id":606,"username":"Cafeine","avatar_template":"/images/avatar.png"}],"topic_list":{"can_create_topic":false,"more_topics_url":"/latest.json?page=1","draft":null,"draft_key":"new_topic","draft_sequence":null,"topics":[{"id":11557,"title":"Error after upgrade to 0.9.7.9+","fancy_title":"Error after upgrade to 0.9.7.9+","slug":"error-after-upgrade-to-0-9-7-9","posts_count":83,"reply_count":58,"highest_post_number":85,"image_url":null,"created_at":"2013-12-22T17:12:05.000-05:00","last_posted_at":"2014-01-16T00:52:30.000-05:00","bumped":true,"bumped_at":"2014-01-16T00:52:30.000-05:00","unseen":false,"pinned":true,"excerpt":"Hi, \n\nI'm using webfaction postgresql specific private instance to run discourse (custom port already configured for discourse 0.9.7.6). \n\nThis is not my first update, but this time i have an error. Impossible to upgrade…","visible":true,"closed":false,"archived":false,"views":1230,"like_count":40,"has_summary":true,"archetype":"regular","last_poster_username":"stellarhopper","category_id":17,"posters":[{"extras":null,"description":"Original Poster","user_id":7204},{"extras":null,"description":"Most Posts","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":5481},{"extras":null,"description":"Frequent Poster","user_id":6473},{"extras":"latest","description":"Most Recent Poster","user_id":6973}]},{"id":1,"title":"Welcome to meta.discourse.org","fancy_title":"Welcome to meta.discourse.org","slug":"welcome-to-meta-discourse-org","posts_count":5,"reply_count":5,"highest_post_number":23,"image_url":null,"created_at":"2013-01-31T23:52:28.000-05:00","last_posted_at":"2013-02-07T16:50:41.000-05:00","bumped":true,"bumped_at":"2013-02-07T11:57:34.000-05:00","unseen":false,"pinned":true,"excerpt":"Welcome to meta, the official site for discussing the next-gen open source Discourse forum software. You'll find topics on features, bugs, hosting, development, and general support here. \n\nDiscourse is early beta softwar…","visible":true,"closed":true,"archived":false,"views":13792,"like_count":108,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":17,"posters":[{"extras":null,"description":"Original Poster","user_id":1},{"extras":null,"description":"Most Posts","user_id":19},{"extras":null,"description":"Frequent Poster","user_id":14},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11997,"title":"Create topic in the future","fancy_title":"Create topic in the future","slug":"create-topic-in-the-future","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-16T12:14:36.000-05:00","last_posted_at":"2014-01-16T12:14:36.000-05:00","bumped":false,"bumped_at":"2014-01-16T12:14:36.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":7,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sil","category_id":2,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":1917}]},{"id":11996,"title":"It's really hard to navigate the Create Topic / Reply pane with the keyboard","fancy_title":"It’s really hard to navigate the Create Topic / Reply pane with the keyboard","slug":"its-really-hard-to-navigate-the-create-topic-reply-pane-with-the-keyboard","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2014-01-16T10:51:36.000-05:00","last_posted_at":"2014-01-16T11:11:10.000-05:00","bumped":true,"bumped_at":"2014-01-16T11:11:10.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":12,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":9,"posters":[{"extras":null,"description":"Original Poster","user_id":7197},{"extras":"latest","description":"Most Recent Poster","user_id":1995}]},{"id":11994,"title":"Cross domain rules, followed?","fancy_title":"Cross domain rules, followed?","slug":"cross-domain-rules-followed","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":"/plugins/emoji/images/smile.png","created_at":"2014-01-16T09:59:15.000-05:00","last_posted_at":"2014-01-16T09:59:15.000-05:00","bumped":true,"bumped_at":"2014-01-16T11:04:32.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":15,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"Abhishek_Gupta","category_id":1,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":8021}]},{"id":11995,"title":"Discourse as a CAS Server","fancy_title":"Discourse as a CAS Server","slug":"discourse-as-a-cas-server","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-16T10:15:30.000-05:00","last_posted_at":"2014-01-16T10:15:31.000-05:00","bumped":true,"bumped_at":"2014-01-16T10:15:31.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":12,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"PabloC","category_id":6,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":2291}]},{"id":11993,"title":"How to check the user level via ajax?","fancy_title":"How to check the user level via ajax?","slug":"how-to-check-the-user-level-via-ajax","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-16T08:13:09.000-05:00","last_posted_at":"2014-01-16T08:13:09.000-05:00","bumped":true,"bumped_at":"2014-01-16T09:20:59.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":13,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"Abhishek_Gupta","category_id":7,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":8021}]},{"id":9540,"title":"Docker images for Discourse","fancy_title":"Docker images for Discourse","slug":"docker-images-for-discourse","posts_count":35,"reply_count":28,"highest_post_number":36,"image_url":null,"created_at":"2013-09-02T00:07:02.000-04:00","last_posted_at":"2014-01-16T07:47:18.000-05:00","bumped":true,"bumped_at":"2014-01-16T07:47:18.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":1322,"like_count":23,"has_summary":false,"archetype":"regular","last_poster_username":"illspirit","category_id":8,"posters":[{"extras":null,"description":"Original Poster","user_id":791},{"extras":null,"description":"Most Posts","user_id":1580},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":7270},{"extras":"latest","description":"Most Recent Poster","user_id":6695}]},{"id":11957,"title":"Daily Active Users, Monthly Active Users - Statistics Need","fancy_title":"Daily Active Users, Monthly Active Users - Statistics Need","slug":"daily-active-users-monthly-active-users-statistics-need","posts_count":8,"reply_count":4,"highest_post_number":8,"image_url":null,"created_at":"2014-01-14T13:40:56.000-05:00","last_posted_at":"2014-01-16T06:46:05.000-05:00","bumped":true,"bumped_at":"2014-01-16T06:46:05.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":97,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"jeans","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":6929},{"extras":null,"description":"Most Posts","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":4385}]},{"id":11973,"title":"Pressing Wrench Icon in the Categories section","fancy_title":"Pressing Wrench Icon in the Categories section","slug":"pressing-wrench-icon-in-the-categories-section","posts_count":6,"reply_count":3,"highest_post_number":6,"image_url":"/uploads/default/2907/d8d4e0accd5ee244.png","created_at":"2014-01-15T05:58:12.000-05:00","last_posted_at":"2014-01-16T05:15:52.000-05:00","bumped":true,"bumped_at":"2014-01-16T05:15:52.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":46,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"5an1ty","category_id":9,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":7073},{"extras":null,"description":"Most Posts","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":6626}]},{"id":11835,"title":"The Road to Discourse 1.0","fancy_title":"The Road to Discourse 1.0","slug":"the-road-to-discourse-1-0","posts_count":6,"reply_count":2,"highest_post_number":6,"image_url":null,"created_at":"2014-01-08T19:08:44.000-05:00","last_posted_at":"2014-01-16T04:49:16.000-05:00","bumped":true,"bumped_at":"2014-01-16T04:49:16.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":421,"like_count":33,"has_summary":false,"archetype":"regular","last_poster_username":"iontishina","category_id":13,"posters":[{"extras":null,"description":"Original Poster","user_id":32},{"extras":null,"description":"Most Posts","user_id":4457},{"extras":null,"description":"Frequent Poster","user_id":4263},{"extras":"latest","description":"Most Recent Poster","user_id":8134}]},{"id":11992,"title":"Specific customization for each category","fancy_title":"Specific customization for each category","slug":"specific-customization-for-each-category","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-16T04:04:58.000-05:00","last_posted_at":"2014-01-16T04:04:58.000-05:00","bumped":false,"bumped_at":"2014-01-16T04:04:58.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":18,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"nXqd","category_id":2,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":2072}]},{"id":9214,"title":"Please make category url shorter","fancy_title":"Please make category url shorter","slug":"please-make-category-url-shorter","posts_count":9,"reply_count":3,"highest_post_number":9,"image_url":null,"created_at":"2013-08-20T05:28:17.000-04:00","last_posted_at":"2014-01-16T04:02:46.000-05:00","bumped":true,"bumped_at":"2014-01-16T04:02:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":319,"like_count":13,"has_summary":false,"archetype":"regular","last_poster_username":"nXqd","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":4983},{"extras":null,"description":"Most Posts","user_id":3657},{"extras":null,"description":"Frequent Poster","user_id":2624},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":2072}]},{"id":11989,"title":"Where to change the email subject prefix","fancy_title":"Where to change the email subject prefix","slug":"where-to-change-the-email-subject-prefix","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":"/uploads/default/2919/adbfe0ff90353440.png","created_at":"2014-01-16T01:03:48.000-05:00","last_posted_at":"2014-01-16T03:20:09.000-05:00","bumped":true,"bumped_at":"2014-01-16T03:20:09.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":19,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":6,"posters":[{"extras":null,"description":"Original Poster","user_id":8085},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":10866,"title":"Header logo overflows the top header area","fancy_title":"Header logo overflows the top header area","slug":"header-logo-overflows-the-top-header-area","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2013-11-09T03:40:04.000-05:00","last_posted_at":"2014-01-16T02:27:52.000-05:00","bumped":true,"bumped_at":"2014-01-16T02:40:47.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":157,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"stellarhopper","category_id":6,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":6973},{"extras":null,"description":"Most Posts","user_id":32}]},{"id":11988,"title":"Could not locate Gemfile error","fancy_title":"Could not locate Gemfile error","slug":"could-not-locate-gemfile-error","posts_count":7,"reply_count":3,"highest_post_number":7,"image_url":null,"created_at":"2014-01-16T00:41:57.000-05:00","last_posted_at":"2014-01-16T01:20:46.000-05:00","bumped":true,"bumped_at":"2014-01-16T01:20:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":18,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":6,"posters":[{"extras":null,"description":"Original Poster","user_id":6973},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":6266,"title":"What sort of replies trigger a notice?","fancy_title":"What sort of replies trigger a notice?","slug":"what-sort-of-replies-trigger-a-notice","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2013-04-30T17:46:39.000-04:00","last_posted_at":"2014-01-16T00:52:21.000-05:00","bumped":true,"bumped_at":"2014-01-16T00:57:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":115,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":17,"posters":[{"extras":null,"description":"Original Poster","user_id":4612},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11610,"title":"Private replies that only admins can see","fancy_title":"Private replies that only admins can see","slug":"private-replies-that-only-admins-can-see","posts_count":21,"reply_count":20,"highest_post_number":23,"image_url":null,"created_at":"2013-12-26T20:31:10.000-05:00","last_posted_at":"2014-01-16T00:18:19.000-05:00","bumped":true,"bumped_at":"2014-01-16T00:18:19.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":206,"like_count":9,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":8018},{"extras":null,"description":"Most Posts","user_id":4263},{"extras":null,"description":"Frequent Poster","user_id":6060},{"extras":null,"description":"Frequent Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11888,"title":"Uncategorized topics not allowed, still seeing tag places","fancy_title":"Uncategorized topics not allowed, still seeing tag places","slug":"uncategorized-topics-not-allowed-still-seeing-tag-places","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":null,"created_at":"2014-01-10T19:23:37.000-05:00","last_posted_at":"2014-01-15T22:41:25.000-05:00","bumped":true,"bumped_at":"2014-01-15T22:41:25.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":50,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"illspirit","category_id":1,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":6695},{"extras":null,"description":"Most Posts","user_id":32},{"extras":null,"description":"Frequent Poster","user_id":2}]},{"id":11985,"title":"Installation nearly installs on Centos 6.5 with Apache/Phusion","fancy_title":"Installation nearly installs on Centos 6.5 with Apache/Phusion","slug":"installation-nearly-installs-on-centos-6-5-with-apache-phusion","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-15T19:48:30.000-05:00","last_posted_at":"2014-01-15T19:48:30.000-05:00","bumped":false,"bumped_at":"2014-01-15T19:48:30.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":26,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"printec","category_id":6,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":8037}]},{"id":11981,"title":"Excluding categories from the top view?","fancy_title":"Excluding categories from the top view?","slug":"excluding-categories-from-the-top-view","posts_count":6,"reply_count":1,"highest_post_number":6,"image_url":"/uploads/default/_optimized/f01/22f/7ea01f77b9_690x355.png","created_at":"2014-01-15T15:01:37.000-05:00","last_posted_at":"2014-01-15T18:57:52.000-05:00","bumped":true,"bumped_at":"2014-01-15T18:57:47.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":43,"like_count":6,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":3415},{"extras":null,"description":"Most Posts","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":1995}]},{"id":9408,"title":"Different home page for regular vs. new user","fancy_title":"Different home page for regular vs. new user","slug":"different-home-page-for-regular-vs-new-user","posts_count":25,"reply_count":17,"highest_post_number":25,"image_url":null,"created_at":"2013-08-28T09:54:41.000-04:00","last_posted_at":"2014-01-15T18:33:16.000-05:00","bumped":true,"bumped_at":"2014-01-15T18:33:16.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":334,"like_count":21,"has_summary":false,"archetype":"regular","last_poster_username":"mcwumbly","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":6283},{"extras":null,"description":"Most Posts","user_id":32},{"extras":null,"description":"Frequent Poster","user_id":1995},{"extras":null,"description":"Frequent Poster","user_id":471},{"extras":"latest","description":"Most Recent Poster","user_id":4263}]},{"id":11896,"title":"Problem creating new account","fancy_title":"Problem creating new account","slug":"problem-creating-new-account","posts_count":11,"reply_count":2,"highest_post_number":11,"image_url":null,"created_at":"2014-01-11T09:07:20.000-05:00","last_posted_at":"2014-01-15T20:50:05.000-05:00","bumped":true,"bumped_at":"2014-01-15T15:23:32.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":87,"like_count":6,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":6,"posters":[{"extras":null,"description":"Original Poster","user_id":6548},{"extras":null,"description":"Most Posts","user_id":32},{"extras":null,"description":"Frequent Poster","user_id":2},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":10511,"title":"External urls should open in new tab","fancy_title":"External urls should open in new tab","slug":"external-urls-should-open-in-new-tab","posts_count":7,"reply_count":3,"highest_post_number":7,"image_url":null,"created_at":"2013-10-20T14:54:27.000-04:00","last_posted_at":"2014-01-15T14:02:11.000-05:00","bumped":true,"bumped_at":"2014-01-15T14:01:55.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":242,"like_count":10,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":7286},{"extras":null,"description":"Most Posts","user_id":3169},{"extras":null,"description":"Frequent Poster","user_id":4263},{"extras":null,"description":"Frequent Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":1589,"title":"Keyboard shortcuts?","fancy_title":"Keyboard shortcuts?","slug":"keyboard-shortcuts","posts_count":19,"reply_count":10,"highest_post_number":20,"image_url":null,"created_at":"2013-02-06T14:05:01.000-05:00","last_posted_at":"2014-01-15T13:52:45.000-05:00","bumped":true,"bumped_at":"2014-01-15T13:52:45.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":754,"like_count":31,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":926},{"extras":null,"description":"Most Posts","user_id":2003},{"extras":null,"description":"Frequent Poster","user_id":369},{"extras":null,"description":"Frequent Poster","user_id":562},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11763,"title":"Google AdSense plugin is now available","fancy_title":"Google AdSense plugin is now available","slug":"google-adsense-plugin-is-now-available","posts_count":7,"reply_count":2,"highest_post_number":7,"image_url":"/uploads/default/_optimized/66d/cf0/d69e6709fe_496x500.PNG","created_at":"2014-01-05T14:28:58.000-05:00","last_posted_at":"2014-01-15T13:32:35.000-05:00","bumped":true,"bumped_at":"2014-01-15T13:32:35.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":213,"like_count":14,"has_summary":false,"archetype":"regular","last_poster_username":"michaeld","category_id":5,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":6548},{"extras":null,"description":"Most Posts","user_id":6653},{"extras":null,"description":"Frequent Poster","user_id":6677},{"extras":null,"description":"Frequent Poster","user_id":5048},{"extras":null,"description":"Frequent Poster","user_id":7333}]},{"id":9151,"title":"Apple touch icon doesn't show if there is no sub domain","fancy_title":"Apple touch icon doesn’t show if there is no sub domain","slug":"apple-touch-icon-doesnt-show-if-there-is-no-sub-domain","posts_count":7,"reply_count":4,"highest_post_number":7,"image_url":null,"created_at":"2013-08-16T18:16:53.000-04:00","last_posted_at":"2014-01-15T17:10:18.000-05:00","bumped":true,"bumped_at":"2014-01-15T13:19:22.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":188,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":3124},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":11977,"title":"Show subcategory topics in categories list summary","fancy_title":"Show subcategory topics in categories list summary","slug":"show-subcategory-topics-in-categories-list-summary","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":"/uploads/default/_optimized/084/4e4/8af88c0839_571x500.png","created_at":"2014-01-15T12:09:49.000-05:00","last_posted_at":"2014-01-15T12:50:04.000-05:00","bumped":true,"bumped_at":"2014-01-15T12:50:04.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":32,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":7604},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":10201,"title":"How To override an existing handlebars template from plugin","fancy_title":"How To override an existing handlebars template from plugin","slug":"how-to-override-an-existing-handlebars-template-from-plugin","posts_count":6,"reply_count":1,"highest_post_number":6,"image_url":null,"created_at":"2013-10-04T10:44:33.000-04:00","last_posted_at":"2014-01-15T12:35:01.000-05:00","bumped":true,"bumped_at":"2014-01-15T12:34:58.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":325,"like_count":6,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":3929},{"extras":null,"description":"Most Posts","user_id":3415},{"extras":null,"description":"Frequent Poster","user_id":6680},{"extras":null,"description":"Frequent Poster","user_id":2},{"extras":"latest","description":"Most Recent Poster","user_id":1995}]},{"id":531,"title":"Discourse and Wordpress Integration","fancy_title":"Discourse and Wordpress Integration","slug":"discourse-and-wordpress-integration","posts_count":76,"reply_count":64,"highest_post_number":78,"image_url":null,"created_at":"2013-02-05T18:56:37.000-05:00","last_posted_at":"2014-01-15T11:56:54.000-05:00","bumped":true,"bumped_at":"2014-01-15T11:56:54.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":3809,"like_count":84,"has_summary":true,"archetype":"regular","last_poster_username":"codinghorror","category_id":5,"posters":[{"extras":null,"description":"Original Poster","user_id":500},{"extras":null,"description":"Most Posts","user_id":8},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":606},{"extras":"latest","description":"Most Recent Poster","user_id":32}]}]}}, -"/categories.json": {"category_list":{"can_create_category":false,"can_create_topic":false,"draft":null,"draft_key":"new_topic","draft_sequence":null,"categories":[{"id":1,"name":"bug","color":"e9dd00","text_color":"000000","slug":"bug","topic_count":660,"description":"Bug reports on Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","topic_url":"/t/category-definition-for-bug/2","read_restricted":false,"permission":null,"post_count":4318,"topics_day":0,"topics_week":18,"topics_month":54,"topics_year":658,"posts_day":0,"posts_week":330,"posts_month":574,"posts_year":4319,"description_excerpt":"Bug reports on Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","featured_user_ids":[8021,32,6695,2,1995],"topics":[{"id":11994,"title":"Cross domain rules, followed?","fancy_title":"Cross domain rules, followed?","slug":"cross-domain-rules-followed","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":"/plugins/emoji/images/smile.png","created_at":"2014-01-16T09:59:15.000-05:00","last_posted_at":"2014-01-16T09:59:15.000-05:00","bumped":true,"bumped_at":"2014-01-16T11:04:32.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":8021,"username":"Abhishek_Gupta","avatar_template":"/images/avatar.png"}},{"id":11888,"title":"Uncategorized topics not allowed, still seeing tag places","fancy_title":"Uncategorized topics not allowed, still seeing tag places","slug":"uncategorized-topics-not-allowed-still-seeing-tag-places","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":null,"created_at":"2014-01-10T19:23:37.000-05:00","last_posted_at":"2014-01-15T22:41:25.000-05:00","bumped":true,"bumped_at":"2014-01-15T22:41:25.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":6695,"username":"illspirit","avatar_template":"/images/avatar.png"}},{"id":9151,"title":"Apple touch icon doesn't show if there is no sub domain","fancy_title":"Apple touch icon doesn’t show if there is no sub domain","slug":"apple-touch-icon-doesnt-show-if-there-is-no-sub-domain","posts_count":7,"reply_count":4,"highest_post_number":7,"image_url":null,"created_at":"2013-08-16T18:16:53.000-04:00","last_posted_at":"2014-01-15T17:10:18.000-05:00","bumped":true,"bumped_at":"2014-01-15T13:19:22.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}}]},{"id":2,"name":"feature","color":"0E76BD","text_color":"FFFFFF","slug":"feature","topic_count":727,"description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.","topic_url":"/t/category-definition-for-feature/11","read_restricted":false,"permission":null,"post_count":6186,"topics_day":0,"topics_week":17,"topics_month":46,"topics_year":725,"posts_day":0,"posts_week":180,"posts_month":468,"posts_year":6187,"description_excerpt":"Discussion about features or potential features of Discourse: how they work, why they work, etc.","featured_user_ids":[1917,4385,2072,32,4263],"topics":[{"id":11997,"title":"Create topic in the future","fancy_title":"Create topic in the future","slug":"create-topic-in-the-future","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-16T12:14:36.000-05:00","last_posted_at":"2014-01-16T12:14:36.000-05:00","bumped":false,"bumped_at":"2014-01-16T12:14:36.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":1917,"username":"sil","avatar_template":"/images/avatar.png"}},{"id":11957,"title":"Daily Active Users, Monthly Active Users - Statistics Need","fancy_title":"Daily Active Users, Monthly Active Users - Statistics Need","slug":"daily-active-users-monthly-active-users-statistics-need","posts_count":8,"reply_count":4,"highest_post_number":8,"image_url":null,"created_at":"2014-01-14T13:40:56.000-05:00","last_posted_at":"2014-01-16T06:46:05.000-05:00","bumped":true,"bumped_at":"2014-01-16T06:46:05.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":4385,"username":"jeans","avatar_template":"/images/avatar.png"}},{"id":11992,"title":"Specific customization for each category","fancy_title":"Specific customization for each category","slug":"specific-customization-for-each-category","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-16T04:04:58.000-05:00","last_posted_at":"2014-01-16T04:04:58.000-05:00","bumped":false,"bumped_at":"2014-01-16T04:04:58.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":2072,"username":"nXqd","avatar_template":"/images/avatar.png"}}]},{"id":6,"name":"support","color":"b99","text_color":"FFFFFF","slug":"support","topic_count":782,"description":"Support on configuring, using, and installing Discourse. Not for software development related topics, but for admins and end users configuring and using Discourse.","topic_url":"/t/category-definition-for-support/389","read_restricted":false,"permission":null,"post_count":5396,"topics_day":0,"topics_week":16,"topics_month":67,"topics_year":779,"posts_day":0,"posts_week":122,"posts_month":481,"posts_year":5400,"description_excerpt":"Support on configuring, using, and installing Discourse. Not for software development related topics, but for admins and end users configuring and using Discourse.","featured_user_ids":[2291,32,6973,1,8085],"topics":[{"id":11995,"title":"Discourse as a CAS Server","fancy_title":"Discourse as a CAS Server","slug":"discourse-as-a-cas-server","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-16T10:15:30.000-05:00","last_posted_at":"2014-01-16T10:15:31.000-05:00","bumped":true,"bumped_at":"2014-01-16T10:15:31.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":2291,"username":"PabloC","avatar_template":"/images/avatar.png"}},{"id":11989,"title":"Where to change the email subject prefix","fancy_title":"Where to change the email subject prefix","slug":"where-to-change-the-email-subject-prefix","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":"/uploads/default/2919/adbfe0ff90353440.png","created_at":"2014-01-16T01:03:48.000-05:00","last_posted_at":"2014-01-16T03:20:09.000-05:00","bumped":true,"bumped_at":"2014-01-16T03:20:09.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}},{"id":10866,"title":"Header logo overflows the top header area","fancy_title":"Header logo overflows the top header area","slug":"header-logo-overflows-the-top-header-area","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2013-11-09T03:40:04.000-05:00","last_posted_at":"2014-01-16T02:27:52.000-05:00","bumped":true,"bumped_at":"2014-01-16T02:40:47.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":6973,"username":"stellarhopper","avatar_template":"/images/avatar.png"}}]},{"id":7,"name":"dev","color":"000","text_color":"FFFFFF","slug":"dev","topic_count":284,"description":"This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.","topic_url":"/t/category-definition-for-dev/1026","read_restricted":false,"permission":null,"post_count":2352,"topics_day":0,"topics_week":3,"topics_month":19,"topics_year":284,"posts_day":0,"posts_week":37,"posts_month":150,"posts_year":2353,"description_excerpt":"This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.","featured_user_ids":[8021,1995,5428,8208,7995],"topics":[{"id":3823,"title":"So, you want to help out with Discourse","fancy_title":"So, you want to help out with Discourse","slug":"so-you-want-to-help-out-with-discourse","posts_count":22,"reply_count":28,"highest_post_number":56,"image_url":null,"created_at":"2013-02-23T00:46:11.000-05:00","last_posted_at":"2014-01-12T21:33:12.000-05:00","bumped":true,"bumped_at":"2014-01-12T21:33:12.000-05:00","unseen":false,"pinned":true,"excerpt":"People are wondering, how it is they can help out with Discourse. \n\nWe have seen some chattering both here and on Github. \n\nI wanted to create a topic @eviltrout , @codinghorror and myself can keep up to date with clear…","visible":true,"closed":false,"archived":false,"last_poster":{"id":7995,"username":"Hunter","avatar_template":"/images/avatar.png"}},{"id":11993,"title":"How to check the user level via ajax?","fancy_title":"How to check the user level via ajax?","slug":"how-to-check-the-user-level-via-ajax","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-16T08:13:09.000-05:00","last_posted_at":"2014-01-16T08:13:09.000-05:00","bumped":true,"bumped_at":"2014-01-16T09:20:59.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":8021,"username":"Abhishek_Gupta","avatar_template":"/images/avatar.png"}},{"id":10201,"title":"How To override an existing handlebars template from plugin","fancy_title":"How To override an existing handlebars template from plugin","slug":"how-to-override-an-existing-handlebars-template-from-plugin","posts_count":6,"reply_count":1,"highest_post_number":6,"image_url":null,"created_at":"2013-10-04T10:44:33.000-04:00","last_posted_at":"2014-01-15T12:35:01.000-05:00","bumped":true,"bumped_at":"2014-01-15T12:34:58.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"last_poster":{"id":1995,"username":"zogstrip","avatar_template":"/images/avatar.png"}}]},{"id":9,"name":"ux","color":"5F497A","text_color":"FFFFFF","slug":"ux","topic_count":184,"description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.","topic_url":"/t/category-definition-for-ux/2628","read_restricted":false,"permission":null,"post_count":1511,"topics_day":0,"topics_week":3,"topics_month":10,"topics_year":183,"posts_day":0,"posts_week":34,"posts_month":117,"posts_year":1511,"description_excerpt":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.","featured_user_ids":[1995,7197,7073,1,6626],"topics":[{"id":11996,"title":"It's really hard to navigate the Create Topic / Reply pane with the keyboard","fancy_title":"It’s really hard to navigate the Create Topic / Reply pane with the keyboard","slug":"its-really-hard-to-navigate-the-create-topic-reply-pane-with-the-keyboard","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2014-01-16T10:51:36.000-05:00","last_posted_at":"2014-01-16T11:11:10.000-05:00","bumped":true,"bumped_at":"2014-01-16T11:11:10.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":1995,"username":"zogstrip","avatar_template":"/images/avatar.png"}},{"id":11973,"title":"Pressing Wrench Icon in the Categories section","fancy_title":"Pressing Wrench Icon in the Categories section","slug":"pressing-wrench-icon-in-the-categories-section","posts_count":6,"reply_count":3,"highest_post_number":6,"image_url":"/uploads/default/2907/d8d4e0accd5ee244.png","created_at":"2014-01-15T05:58:12.000-05:00","last_posted_at":"2014-01-16T05:15:52.000-05:00","bumped":true,"bumped_at":"2014-01-16T05:15:52.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":7073,"username":"5an1ty","avatar_template":"/images/avatar.png"}},{"id":5542,"title":"Title character requirements not very visible","fancy_title":"Title character requirements not very visible","slug":"title-character-requirements-not-very-visible","posts_count":24,"reply_count":11,"highest_post_number":24,"image_url":null,"created_at":"2013-04-02T20:09:59.000-04:00","last_posted_at":"2014-01-15T05:26:07.000-05:00","bumped":true,"bumped_at":"2014-01-15T05:26:04.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"last_poster":{"id":1995,"username":"zogstrip","avatar_template":"/images/avatar.png"}}]},{"id":5,"name":"extensibility","color":"FE8432","text_color":"FFFFFF","slug":"extensibility","topic_count":102,"description":"Topics about extending the functionality of Discourse with plugins, themes, add-ons, or other mechanisms for extensibility. ","topic_url":"/t/category-definition-for-extensibility/28","read_restricted":false,"permission":null,"post_count":964,"topics_day":0,"topics_week":2,"topics_month":18,"topics_year":102,"posts_day":0,"posts_week":17,"posts_month":76,"posts_year":964,"description_excerpt":"Topics about extending the functionality of Discourse with plugins, themes, add-ons, or other mechanisms for extensibility.","featured_user_ids":[6548,32,8202,6677,7333],"topics":[{"id":11763,"title":"Google AdSense plugin is now available","fancy_title":"Google AdSense plugin is now available","slug":"google-adsense-plugin-is-now-available","posts_count":7,"reply_count":2,"highest_post_number":7,"image_url":"/uploads/default/_optimized/66d/cf0/d69e6709fe_496x500.PNG","created_at":"2014-01-05T14:28:58.000-05:00","last_posted_at":"2014-01-15T13:32:35.000-05:00","bumped":true,"bumped_at":"2014-01-15T13:32:35.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":6548,"username":"michaeld","avatar_template":"/images/avatar.png"}},{"id":531,"title":"Discourse and Wordpress Integration","fancy_title":"Discourse and Wordpress Integration","slug":"discourse-and-wordpress-integration","posts_count":76,"reply_count":64,"highest_post_number":78,"image_url":null,"created_at":"2013-02-05T18:56:37.000-05:00","last_posted_at":"2014-01-15T11:56:54.000-05:00","bumped":true,"bumped_at":"2014-01-15T11:56:54.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}},{"id":11965,"title":"In your opinion, what is the best wiki engine to be associated with discourse?","fancy_title":"In your opinion, what is the best wiki engine to be associated with discourse?","slug":"in-your-opinion-what-is-the-best-wiki-engine-to-be-associated-with-discourse","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-14T19:27:06.000-05:00","last_posted_at":"2014-01-14T19:27:06.000-05:00","bumped":false,"bumped_at":"2014-01-14T19:27:06.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":8202,"username":"Matthieu","avatar_template":"/images/avatar.png"}}]},{"id":8,"name":"hosting","color":"74CCED","text_color":"FFFFFF","slug":"hosting","topic_count":69,"description":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","topic_url":"/t/category-definition-for-hosting/2626","read_restricted":false,"permission":null,"post_count":664,"topics_day":0,"topics_week":2,"topics_month":2,"topics_year":69,"posts_day":0,"posts_week":15,"posts_month":35,"posts_year":664,"description_excerpt":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","featured_user_ids":[6695,1,6018,1580,7030],"topics":[{"id":9540,"title":"Docker images for Discourse","fancy_title":"Docker images for Discourse","slug":"docker-images-for-discourse","posts_count":35,"reply_count":28,"highest_post_number":36,"image_url":null,"created_at":"2013-09-02T00:07:02.000-04:00","last_posted_at":"2014-01-16T07:47:18.000-05:00","bumped":true,"bumped_at":"2014-01-16T07:47:18.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":6695,"username":"illspirit","avatar_template":"/images/avatar.png"}},{"id":11971,"title":"Installing Discourse on Ubuntu 12.04 with Parallels Plesk and Apache","fancy_title":"Installing Discourse on Ubuntu 12.04 with Parallels Plesk and Apache","slug":"installing-discourse-on-ubuntu-12-04-with-parallels-plesk-and-apache","posts_count":3,"reply_count":1,"highest_post_number":3,"image_url":null,"created_at":"2014-01-15T04:23:38.000-05:00","last_posted_at":"2014-01-15T04:47:20.000-05:00","bumped":true,"bumped_at":"2014-01-15T04:47:20.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":7030,"username":"naabster","avatar_template":"/images/avatar.png"}},{"id":10844,"title":"Discourse in a Docker container","fancy_title":"Discourse in a Docker container","slug":"discourse-in-a-docker-container","posts_count":12,"reply_count":8,"highest_post_number":12,"image_url":null,"created_at":"2013-11-07T19:12:22.000-05:00","last_posted_at":"2014-01-11T14:43:53.000-05:00","bumped":true,"bumped_at":"2014-01-11T14:43:53.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":1,"username":"sam","avatar_template":"/images/avatar.png"}}]},{"id":17,"name":"uncategorized","color":"AB9364","text_color":"FFFFFF","slug":"uncategorized","topic_count":229,"description":"","topic_url":null,"read_restricted":false,"permission":null,"post_count":2138,"topics_day":0,"topics_week":0,"topics_month":9,"topics_year":229,"posts_day":1,"posts_week":11,"posts_month":183,"posts_year":2138,"description_excerpt":"","is_uncategorized":true,"featured_user_ids":[6973,32,1,1995,7073],"topics":[{"id":1,"title":"Welcome to meta.discourse.org","fancy_title":"Welcome to meta.discourse.org","slug":"welcome-to-meta-discourse-org","posts_count":5,"reply_count":5,"highest_post_number":23,"image_url":null,"created_at":"2013-01-31T23:52:28.000-05:00","last_posted_at":"2013-02-07T16:50:41.000-05:00","bumped":true,"bumped_at":"2013-02-07T11:57:34.000-05:00","unseen":false,"pinned":true,"excerpt":"Welcome to meta, the official site for discussing the next-gen open source Discourse forum software. You'll find topics on features, bugs, hosting, development, and general support here. \n\nDiscourse is early beta softwar…","visible":true,"closed":true,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}},{"id":11557,"title":"Error after upgrade to 0.9.7.9+","fancy_title":"Error after upgrade to 0.9.7.9+","slug":"error-after-upgrade-to-0-9-7-9","posts_count":83,"reply_count":58,"highest_post_number":85,"image_url":null,"created_at":"2013-12-22T17:12:05.000-05:00","last_posted_at":"2014-01-16T00:52:30.000-05:00","bumped":true,"bumped_at":"2014-01-16T00:52:30.000-05:00","unseen":false,"pinned":true,"excerpt":"Hi, \n\nI'm using webfaction postgresql specific private instance to run discourse (custom port already configured for discourse 0.9.7.6). \n\nThis is not my first update, but this time i have an error. Impossible to upgrade…","visible":true,"closed":false,"archived":false,"last_poster":{"id":6973,"username":"stellarhopper","avatar_template":"/images/avatar.png"}},{"id":6266,"title":"What sort of replies trigger a notice?","fancy_title":"What sort of replies trigger a notice?","slug":"what-sort-of-replies-trigger-a-notice","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2013-04-30T17:46:39.000-04:00","last_posted_at":"2014-01-16T00:52:21.000-05:00","bumped":true,"bumped_at":"2014-01-16T00:57:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}}]},{"id":11,"name":"login","color":"edb400","text_color":"FFFFFF","slug":"login","topic_count":27,"description":"Topics about logging in to Discourse, using any standard third party provider (Twitter, Facebook, Google), traditional username and password, or with a custom plugin.","topic_url":"/t/category-definition-for-login/2828","read_restricted":false,"permission":null,"post_count":200,"topics_day":0,"topics_week":1,"topics_month":1,"topics_year":27,"posts_day":0,"posts_week":10,"posts_month":27,"posts_year":200,"description_excerpt":"Topics about logging in to Discourse, using any standard third party provider (Twitter, Facebook, Google), traditional username and password, or with a custom plugin.","featured_user_ids":[8163,19,7796,32,8024],"topics":[{"id":11959,"title":"Get current user information via JSON","fancy_title":"Get current user information via JSON","slug":"get-current-user-information-via-json","posts_count":3,"reply_count":1,"highest_post_number":3,"image_url":null,"created_at":"2014-01-14T15:05:34.000-05:00","last_posted_at":"2014-01-14T16:43:28.000-05:00","bumped":true,"bumped_at":"2014-01-14T16:43:28.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":8163,"username":"znation","avatar_template":"/images/avatar.png"}},{"id":6242,"title":"Allow authentication via multiple services on one account","fancy_title":"Allow authentication via multiple services on one account","slug":"allow-authentication-via-multiple-services-on-one-account","posts_count":34,"reply_count":27,"highest_post_number":34,"image_url":null,"created_at":"2013-04-29T18:51:52.000-04:00","last_posted_at":"2014-01-14T00:25:42.000-05:00","bumped":true,"bumped_at":"2014-01-14T00:25:42.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":7796,"username":"almereyda","avatar_template":"/images/avatar.png"}},{"id":4738,"title":"Login support for browser password managers","fancy_title":"Login support for browser password managers","slug":"login-support-for-browser-password-managers","posts_count":6,"reply_count":2,"highest_post_number":6,"image_url":null,"created_at":"2013-03-13T17:55:29.000-04:00","last_posted_at":"2014-01-13T14:21:34.000-05:00","bumped":true,"bumped_at":"2014-01-13T14:21:34.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}}]},{"id":3,"name":"meta","color":"aaa","text_color":"FFFFFF","slug":"meta","topic_count":79,"description":"Discussion about meta.discourse.org itself, the organization of this forum about Discourse, how it works, and how we can improve this site.","topic_url":"/t/category-definition-for-meta/24","read_restricted":false,"permission":null,"post_count":695,"topics_day":0,"topics_week":1,"topics_month":3,"topics_year":79,"posts_day":0,"posts_week":4,"posts_month":18,"posts_year":696,"description_excerpt":"Discussion about meta.discourse.org itself, the organization of this forum about Discourse, how it works, and how we can improve this site.","featured_user_ids":[19,8085,32,5174,4534],"topics":[{"id":5249,"title":"What is \"Meta\"?","fancy_title":"What is “Meta”?","slug":"what-is-meta","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2013-03-25T18:00:52.000-04:00","last_posted_at":"2013-03-25T18:00:56.000-04:00","bumped":false,"bumped_at":"2013-03-25T18:00:52.000-04:00","unseen":false,"pinned":true,"excerpt":"Meta means discussion of the discussion itself instead of the actual topic of the discussion. \n\nWhy do we need a meta category?\n\nMeta is where communities come together to decide who they are and what they are about. \n…","visible":true,"closed":false,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}},{"id":11943,"title":"How far to take user documentation?","fancy_title":"How far to take user documentation?","slug":"how-far-to-take-user-documentation","posts_count":4,"reply_count":2,"highest_post_number":4,"image_url":"/plugins/emoji/images/smile.png","created_at":"2014-01-13T19:21:26.000-05:00","last_posted_at":"2014-01-14T14:19:46.000-05:00","bumped":true,"bumped_at":"2014-01-14T14:19:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":19,"username":"eviltrout","avatar_template":"/images/avatar.png"}},{"id":11822,"title":"Search engine traffic share and level to Discourse","fancy_title":"Search engine traffic share and level to Discourse","slug":"search-engine-traffic-share-and-level-to-discourse","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2014-01-08T01:54:56.000-05:00","last_posted_at":"2014-01-08T02:21:25.000-05:00","bumped":true,"bumped_at":"2014-01-08T02:21:25.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}}]},{"id":12,"name":"discourse hub","color":"b2c79f","text_color":"FFFFFF","slug":"discourse-hub","topic_count":4,"description":"Topics about current or future Discourse Hub functionality at discourse.org including nickname registration, global user pages, and the site directory.","topic_url":"/t/category-definition-for-discourse-hub/3038","read_restricted":false,"permission":null,"post_count":121,"topics_day":0,"topics_week":0,"topics_month":0,"topics_year":4,"posts_day":0,"posts_week":3,"posts_month":3,"posts_year":121,"description_excerpt":"Topics about current or future Discourse Hub functionality at discourse.org including nickname registration, global user pages, and the site directory.","featured_user_ids":[2,32,2316,6695,4457],"topics":[{"id":6547,"title":"Where to get discourse_org_access_key?","fancy_title":"Where to get discourse_org_access_key?","slug":"where-to-get-discourse-org-access-key","posts_count":13,"reply_count":4,"highest_post_number":13,"image_url":null,"created_at":"2013-05-10T22:06:08.000-04:00","last_posted_at":"2014-01-13T11:38:15.000-05:00","bumped":true,"bumped_at":"2014-01-13T11:38:15.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":2,"username":"neil","avatar_template":"/images/avatar.png"}},{"id":2544,"title":"Discourse central hub questions","fancy_title":"Discourse central hub questions","slug":"discourse-central-hub-questions","posts_count":51,"reply_count":44,"highest_post_number":52,"image_url":null,"created_at":"2013-02-09T04:28:21.000-05:00","last_posted_at":"2013-09-19T13:36:49.000-04:00","bumped":true,"bumped_at":"2013-09-19T14:04:08.000-04:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":2128,"username":"ultimape","avatar_template":"/images/avatar.png"}},{"id":424,"title":"What are the 'consequences' of changing your name?","fancy_title":"What are the ‘consequences’ of changing your name?","slug":"what-are-the-consequences-of-changing-your-name","posts_count":35,"reply_count":36,"highest_post_number":43,"image_url":null,"created_at":"2013-02-05T17:37:52.000-05:00","last_posted_at":"2013-09-19T13:55:11.000-04:00","bumped":true,"bumped_at":"2013-09-19T13:55:11.000-04:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":2128,"username":"ultimape","avatar_template":"/images/avatar.png"}}]},{"id":13,"name":"blog","color":"ED207B","text_color":"FFFFFF","slug":"blog","topic_count":14,"description":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","topic_url":"/t/category-definition-for-blog/5250","read_restricted":false,"permission":null,"post_count":206,"topics_day":0,"topics_week":0,"topics_month":1,"topics_year":14,"posts_day":0,"posts_week":2,"posts_month":11,"posts_year":206,"description_excerpt":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","featured_user_ids":[8134,32,4457,4263,1995],"topics":[{"id":11835,"title":"The Road to Discourse 1.0","fancy_title":"The Road to Discourse 1.0","slug":"the-road-to-discourse-1-0","posts_count":6,"reply_count":2,"highest_post_number":6,"image_url":null,"created_at":"2014-01-08T19:08:44.000-05:00","last_posted_at":"2014-01-16T04:49:16.000-05:00","bumped":true,"bumped_at":"2014-01-16T04:49:16.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":8134,"username":"iontishina","avatar_template":"/images/avatar.png"}},{"id":5751,"title":"Discourse as Your First Rails App","fancy_title":"Discourse as Your First Rails App","slug":"discourse-as-your-first-rails-app","posts_count":62,"reply_count":43,"highest_post_number":71,"image_url":null,"created_at":"2013-04-09T19:08:33.000-04:00","last_posted_at":"2013-12-19T18:27:37.000-05:00","bumped":true,"bumped_at":"2013-12-19T18:27:37.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":1995,"username":"zogstrip","avatar_template":"/images/avatar.png"}},{"id":5898,"title":"The Discourse Servers","fancy_title":"The Discourse Servers","slug":"the-discourse-servers","posts_count":42,"reply_count":32,"highest_post_number":42,"image_url":null,"created_at":"2013-04-15T15:19:09.000-04:00","last_posted_at":"2013-11-29T15:14:35.000-05:00","bumped":true,"bumped_at":"2013-11-29T15:14:35.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":6626,"username":"riking","avatar_template":"/images/avatar.png"}}]},{"id":4,"name":"faq","color":"33b","text_color":"FFFFFF","slug":"faq","topic_count":49,"description":"Topics that come up very often when discussing Discourse will eventually be classified into this Frequently Asked Questions category. Should only be added to popular topics.","topic_url":"/t/category-definition-for-faq/25","read_restricted":false,"permission":null,"post_count":450,"topics_day":0,"topics_week":0,"topics_month":0,"topics_year":49,"posts_day":0,"posts_week":1,"posts_month":10,"posts_year":450,"description_excerpt":"Topics that come up very often when discussing Discourse will eventually be classified into this Frequently Asked Questions category. Should only be added to popular topics.","featured_user_ids":[32,8047,7483,2,6626],"topics":[{"id":5372,"title":"UX confusion (or me confusion) is it possible to edit old posts or only your most recent post in a topic?","fancy_title":"UX confusion (or me confusion) is it possible to edit old posts or only your most recent post in a topic?","slug":"ux-confusion-or-me-confusion-is-it-possible-to-edit-old-posts-or-only-your-most-recent-post-in-a-topic","posts_count":3,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2013-03-28T22:25:57.000-04:00","last_posted_at":"2014-01-13T13:44:39.000-05:00","bumped":true,"bumped_at":"2014-01-13T13:44:39.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}},{"id":9631,"title":"All the options to deploy Discourse with their relative pros and cons","fancy_title":"All the options to deploy Discourse with their relative pros and cons","slug":"all-the-options-to-deploy-discourse-with-their-relative-pros-and-cons","posts_count":14,"reply_count":7,"highest_post_number":15,"image_url":null,"created_at":"2013-09-06T03:55:09.000-04:00","last_posted_at":"2013-09-26T18:49:04.000-04:00","bumped":true,"bumped_at":"2013-12-30T12:32:59.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":3929,"username":"ScotterC","avatar_template":"/images/avatar.png"}},{"id":4325,"title":"How to delete a user?","fancy_title":"How to delete a user?","slug":"how-to-delete-a-user","posts_count":31,"reply_count":23,"highest_post_number":33,"image_url":null,"created_at":"2013-03-01T23:18:55.000-05:00","last_posted_at":"2013-12-20T21:26:06.000-05:00","bumped":true,"bumped_at":"2013-12-20T21:26:06.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}}]},{"id":14,"name":"marketplace","color":"8C6238","text_color":"FFFFFF","slug":"marketplace","topic_count":24,"description":"About commercial Discourse related stuff: jobs or paid gigs, plugins, themes, hosting, etc.","topic_url":"/t/category-definition-for-marketplace/5425","read_restricted":false,"permission":null,"post_count":106,"topics_day":0,"topics_week":1,"topics_month":3,"topics_year":24,"posts_day":0,"posts_week":1,"posts_month":7,"posts_year":106,"description_excerpt":"About commercial Discourse related stuff: jobs or paid gigs, plugins, themes, hosting, etc.","featured_user_ids":[6548,32,5548,2291,4755],"topics":[{"id":11866,"title":"DiscourseHosting is now accepting BTC payments","fancy_title":"DiscourseHosting is now accepting BTC payments","slug":"discoursehosting-is-now-accepting-btc-payments","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-10T10:17:28.000-05:00","last_posted_at":"2014-01-10T10:17:28.000-05:00","bumped":false,"bumped_at":"2014-01-10T10:17:28.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":6548,"username":"michaeld","avatar_template":"/images/avatar.png"}},{"id":11571,"title":"Looking for a developer for Discourse Customization","fancy_title":"Looking for a developer for Discourse Customization","slug":"looking-for-a-developer-for-discourse-customization","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2013-12-23T20:54:04.000-05:00","last_posted_at":"2013-12-24T13:12:17.000-05:00","bumped":true,"bumped_at":"2013-12-30T16:36:17.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":2291,"username":"PabloC","avatar_template":"/images/avatar.png"}},{"id":11594,"title":"Need someone to fix a topic in my discourse install that won't load for moderators. Will pay","fancy_title":"Need someone to fix a topic in my discourse install that won’t load for moderators. Will pay","slug":"need-someone-to-fix-a-topic-in-my-discourse-install-that-wont-load-for-moderators-will-pay","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2013-12-25T10:25:57.000-05:00","last_posted_at":"2013-12-26T17:01:41.000-05:00","bumped":true,"bumped_at":"2013-12-25T17:01:15.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"last_poster":{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"}}]},{"id":10,"name":"howto","color":"76923C","text_color":"FFFFFF","slug":"howto","topic_count":58,"description":"Tutorial topics that describe how to set up, configure, or install Discourse using a specific platform or environment. Topics in this category may only be created by trust level 2 and up. ","topic_url":"/t/category-definition-for-howto/2629","read_restricted":false,"permission":null,"post_count":677,"topics_day":0,"topics_week":0,"topics_month":1,"topics_year":58,"posts_day":0,"posts_week":0,"posts_month":13,"posts_year":675,"description_excerpt":"Tutorial topics that describe how to set up, configure, or install Discourse using a specific platform or environment. Topics in this category may only be created by trust level 2 and up.","featured_user_ids":[7984,4457,1995,6018,5351],"topics":[{"id":7582,"title":"Twitter login with Passenger + Varnish - quick lessons learned","fancy_title":"Twitter login with Passenger + Varnish - quick lessons learned","slug":"twitter-login-with-passenger-varnish-quick-lessons-learned","posts_count":9,"reply_count":3,"highest_post_number":9,"image_url":"/plugins/emoji/images/smile.png","created_at":"2013-06-17T19:46:31.000-04:00","last_posted_at":"2013-12-31T21:03:59.000-05:00","bumped":true,"bumped_at":"2013-12-31T21:03:59.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":7984,"username":"sophearak","avatar_template":"/images/avatar.png"}},{"id":7229,"title":"How to set up image uploads to S3?","fancy_title":"How to set up image uploads to S3?","slug":"how-to-set-up-image-uploads-to-s3","posts_count":14,"reply_count":11,"highest_post_number":14,"image_url":"/uploads/meta_discourse/1019/782cbc7e309ce43f.png","created_at":"2013-06-06T15:37:43.000-04:00","last_posted_at":"2013-12-31T11:54:18.000-05:00","bumped":true,"bumped_at":"2013-12-31T11:54:18.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":1995,"username":"zogstrip","avatar_template":"/images/avatar.png"}},{"id":11628,"title":"My experience with a successful migration (hints for a guide)","fancy_title":"My experience with a successful migration (hints for a guide)","slug":"my-experience-with-a-successful-migration-hints-for-a-guide","posts_count":3,"reply_count":1,"highest_post_number":3,"image_url":null,"created_at":"2013-12-28T09:23:45.000-05:00","last_posted_at":"2013-12-28T10:38:48.000-05:00","bumped":true,"bumped_at":"2013-12-28T10:38:48.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"last_poster":{"id":6018,"username":"robypez","avatar_template":"/images/avatar.png"}}]}]}}, -"/c/bug/l/latest.json": {"users":[{"id":1,"username":"sam","avatar_template":"/images/avatar.png"},{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"},{"id":8021,"username":"Abhishek_Gupta","avatar_template":"/images/avatar.png"},{"id":6695,"username":"illspirit","avatar_template":"/images/avatar.png"},{"id":2,"username":"neil","avatar_template":"/images/avatar.png"},{"id":3124,"username":"sipp11","avatar_template":"/images/avatar.png"},{"id":7513,"username":"digit","avatar_template":"/images/avatar.png"},{"id":19,"username":"eviltrout","avatar_template":"/images/avatar.png"},{"id":3,"username":"supermathie","avatar_template":"/images/avatar.png"},{"id":7073,"username":"5an1ty","avatar_template":"/images/avatar.png"},{"id":4996,"username":"wmertens","avatar_template":"/images/avatar.png"},{"id":6377,"username":"zh99998","avatar_template":"/images/avatar.png"},{"id":1496,"username":"cfstras","avatar_template":"/images/avatar.png"},{"id":7995,"username":"Hunter","avatar_template":"/images/avatar.png"},{"id":6626,"username":"riking","avatar_template":"/images/avatar.png"},{"id":1995,"username":"zogstrip","avatar_template":"/images/avatar.png"},{"id":5048,"username":"SneakySly","avatar_template":"/images/avatar.png"},{"id":7731,"username":"YOU","avatar_template":"/images/avatar.png"},{"id":7985,"username":"onlinedev","avatar_template":"/images/avatar.png"},{"id":3415,"username":"radq","avatar_template":"/images/avatar.png"},{"id":5351,"username":"erlend_sh","avatar_template":"/images/avatar.png"},{"id":471,"username":"BhaelOchon","avatar_template":"/images/avatar.png"},{"id":7,"username":"pekka","avatar_template":"/images/avatar.png"},{"id":4780,"username":"HugoAlmeida","avatar_template":"/images/avatar.png"},{"id":5053,"username":"Blue","avatar_template":"/images/avatar.png"},{"id":212,"username":"alxndr","avatar_template":"/images/avatar.png"},{"id":6118,"username":"lukelarris","avatar_template":"/images/avatar.png"},{"id":7076,"username":"philnelson","avatar_template":"/images/avatar.png"},{"id":4851,"username":"jab","avatar_template":"/images/avatar.png"},{"id":4457,"username":"Lee_Ars","avatar_template":"/images/avatar.png"},{"id":6280,"username":"mx2000","avatar_template":"/images/avatar.png"},{"id":3681,"username":"Ajarn","avatar_template":"/images/avatar.png"},{"id":1621,"username":"bnb","avatar_template":"/images/avatar.png"},{"id":6266,"username":"bragi","avatar_template":"/images/avatar.png"},{"id":5335,"username":"masda70","avatar_template":"/images/avatar.png"},{"id":6314,"username":"rafaelfranca","avatar_template":"/images/avatar.png"}],"topic_list":{"can_create_topic":false,"more_topics_url":"/latest.json?category=1&page=1","draft":null,"draft_key":"new_topic","draft_sequence":null,"topics":[{"id":2,"title":"Category definition for bug","fancy_title":"Category definition for bug","slug":"category-definition-for-bug","posts_count":2,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2013-01-31T23:56:34.000-05:00","last_posted_at":"2013-03-07T22:42:27.000-05:00","bumped":true,"bumped_at":"2013-02-26T18:52:56.000-05:00","unseen":false,"pinned":true,"excerpt":"Bug reports on Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","visible":true,"closed":false,"archived":false,"views":469,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":1},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11994,"title":"Cross domain rules, followed?","fancy_title":"Cross domain rules, followed?","slug":"cross-domain-rules-followed","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":"/plugins/emoji/images/smile.png","created_at":"2014-01-16T09:59:15.000-05:00","last_posted_at":"2014-01-16T09:59:15.000-05:00","bumped":true,"bumped_at":"2014-01-16T11:04:32.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":15,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"Abhishek_Gupta","category_id":1,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":8021}]},{"id":11888,"title":"Uncategorized topics not allowed, still seeing tag places","fancy_title":"Uncategorized topics not allowed, still seeing tag places","slug":"uncategorized-topics-not-allowed-still-seeing-tag-places","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":null,"created_at":"2014-01-10T19:23:37.000-05:00","last_posted_at":"2014-01-15T22:41:25.000-05:00","bumped":true,"bumped_at":"2014-01-15T22:41:25.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":50,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"illspirit","category_id":1,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":6695},{"extras":null,"description":"Most Posts","user_id":32},{"extras":null,"description":"Frequent Poster","user_id":2}]},{"id":9151,"title":"Apple touch icon doesn't show if there is no sub domain","fancy_title":"Apple touch icon doesn’t show if there is no sub domain","slug":"apple-touch-icon-doesnt-show-if-there-is-no-sub-domain","posts_count":7,"reply_count":4,"highest_post_number":7,"image_url":null,"created_at":"2013-08-16T18:16:53.000-04:00","last_posted_at":"2014-01-15T17:10:18.000-05:00","bumped":true,"bumped_at":"2014-01-15T13:19:22.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":188,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":3124},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":10911,"title":"/users/activate-account pulling blank logo instead of defaulting to h2","fancy_title":"/users/activate-account pulling blank logo instead of defaulting to h2","slug":"users-activate-account-pulling-blank-logo-instead-of-defaulting-to-h2","posts_count":3,"reply_count":1,"highest_post_number":3,"image_url":null,"created_at":"2013-11-12T14:49:04.000-05:00","last_posted_at":"2014-01-15T10:21:37.000-05:00","bumped":true,"bumped_at":"2014-01-15T10:21:37.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":121,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"eviltrout","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":7513},{"extras":"latest","description":"Most Recent Poster","user_id":19}]},{"id":11937,"title":"Smiley parser is busted","fancy_title":"Smiley parser is busted","slug":"smiley-parser-is-busted","posts_count":4,"reply_count":4,"highest_post_number":7,"image_url":"/plugins/emoji/images/smile.png","created_at":"2014-01-13T15:42:00.000-05:00","last_posted_at":"2014-01-15T05:51:16.000-05:00","bumped":true,"bumped_at":"2014-01-15T05:51:16.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":66,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":3},{"extras":null,"description":"Most Posts","user_id":7073},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":6625,"title":"Error 500 on PUT of site config","fancy_title":"Error 500 on PUT of site config","slug":"error-500-on-put-of-site-config","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2013-05-14T18:13:56.000-04:00","last_posted_at":"2014-01-16T04:55:50.000-05:00","bumped":true,"bumped_at":"2014-01-15T04:43:23.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":132,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":4996},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":11225,"title":"Forum acts weirdly after client side updates","fancy_title":"Forum acts weirdly after client side updates","slug":"forum-acts-weirdly-after-client-side-updates","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":null,"created_at":"2013-12-02T18:32:10.000-05:00","last_posted_at":"2014-01-15T04:04:55.000-05:00","bumped":true,"bumped_at":"2014-01-15T02:55:18.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":117,"like_count":7,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":1},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":11903,"title":"Error after update to 0.9.8.1","fancy_title":"Error after update to 0.9.8.1","slug":"error-after-update-to-0-9-8-1","posts_count":14,"reply_count":6,"highest_post_number":17,"image_url":null,"created_at":"2014-01-12T06:55:45.000-05:00","last_posted_at":"2014-01-15T01:48:58.000-05:00","bumped":true,"bumped_at":"2014-01-15T01:48:58.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":121,"like_count":6,"has_summary":false,"archetype":"regular","last_poster_username":"zh99998","category_id":1,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":6377},{"extras":null,"description":"Most Posts","user_id":1496},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":19}]},{"id":11969,"title":"Qunit error and possibly related ember.js problem","fancy_title":"Qunit error and possibly related ember.js problem","slug":"qunit-error-and-possibly-related-ember-js-problem","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-14T22:51:32.000-05:00","last_posted_at":"2014-01-14T22:51:32.000-05:00","bumped":false,"bumped_at":"2014-01-14T22:51:32.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":32,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"Hunter","category_id":1,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":7995}]},{"id":11945,"title":"Stuff disappears on the groups page","fancy_title":"Stuff disappears on the groups page","slug":"stuff-disappears-on-the-groups-page","posts_count":7,"reply_count":2,"highest_post_number":7,"image_url":null,"created_at":"2014-01-13T23:03:53.000-05:00","last_posted_at":"2014-01-15T01:26:07.000-05:00","bumped":true,"bumped_at":"2014-01-14T21:09:01.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":54,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":6695},{"extras":null,"description":"Most Posts","user_id":6626},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":1995}]},{"id":11520,"title":"Discourse WordPress Plugin: Emoji's do not properly display","fancy_title":"Discourse WordPress Plugin: Emoji’s do not properly display","slug":"discourse-wordpress-plugin-emojis-do-not-properly-display","posts_count":9,"reply_count":4,"highest_post_number":9,"image_url":"/uploads/default/_optimized/638/4db/eff43a45b8_690x420.png","created_at":"2013-12-19T23:32:03.000-05:00","last_posted_at":"2014-01-15T04:32:19.000-05:00","bumped":true,"bumped_at":"2014-01-14T17:53:34.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":168,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":5048},{"extras":null,"description":"Frequent Poster","user_id":7731},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":11597,"title":"All categories drop down does not close after clicking on first menu \"all categories\"","fancy_title":"All categories drop down does not close after clicking on first menu “all categories”","slug":"all-categories-drop-down-does-not-close-after-clicking-on-first-menu-all-categories","posts_count":5,"reply_count":2,"highest_post_number":5,"image_url":"/uploads/default/2495/f9efe463ae67632d.png","created_at":"2013-12-25T15:09:27.000-05:00","last_posted_at":"2014-01-14T17:46:41.000-05:00","bumped":true,"bumped_at":"2014-01-14T17:46:41.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":73,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"radq","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":7985},{"extras":null,"description":"Most Posts","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":3415}]},{"id":11962,"title":"Editor When Clicking on Wrench Issue","fancy_title":"Editor When Clicking on Wrench Issue","slug":"editor-when-clicking-on-wrench-issue","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":"/uploads/default/_optimized/ca4/f70/ac7278b8f6_690x176.png","created_at":"2014-01-14T17:23:20.000-05:00","last_posted_at":"2014-01-14T17:24:02.000-05:00","bumped":true,"bumped_at":"2014-01-14T17:24:02.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":30,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":7073},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11831,"title":"Broken links, possibly related to HTTPS","fancy_title":"Broken links, possibly related to HTTPS","slug":"broken-links-possibly-related-to-https","posts_count":17,"reply_count":13,"highest_post_number":18,"image_url":null,"created_at":"2014-01-08T17:40:45.000-05:00","last_posted_at":"2014-01-14T16:03:07.000-05:00","bumped":true,"bumped_at":"2014-01-14T16:03:07.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":102,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"eviltrout","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":5351},{"extras":null,"description":"Most Posts","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":471},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":19}]},{"id":11916,"title":"Unable to save user preferences","fancy_title":"Unable to save user preferences","slug":"unable-to-save-user-preferences","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2014-01-13T02:29:26.000-05:00","last_posted_at":"2014-01-14T14:39:32.000-05:00","bumped":true,"bumped_at":"2014-01-14T14:39:29.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":34,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster","user_id":1995}]},{"id":10425,"title":"Editing category permissions: select value doesn't change","fancy_title":"Editing category permissions: select value doesn’t change","slug":"editing-category-permissions-select-value-doesnt-change","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":"/uploads/meta_discourse/1956/d55fba29dbd7e1fe.png","created_at":"2013-10-17T18:20:20.000-04:00","last_posted_at":"2013-10-17T18:20:21.000-04:00","bumped":true,"bumped_at":"2014-01-14T13:35:37.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":92,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"pekka","category_id":1,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":7}]},{"id":6557,"title":"Middle clicking a link twice does not work as expected","fancy_title":"Middle clicking a link twice does not work as expected","slug":"middle-clicking-a-link-twice-does-not-work-as-expected","posts_count":10,"reply_count":7,"highest_post_number":10,"image_url":null,"created_at":"2013-05-11T13:56:02.000-04:00","last_posted_at":"2014-01-14T13:13:04.000-05:00","bumped":true,"bumped_at":"2014-01-14T13:13:04.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":401,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"neil","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":4780},{"extras":null,"description":"Most Posts","user_id":5053},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":2}]},{"id":11944,"title":"Regression: Cannot sort topic list","fancy_title":"Regression: Cannot sort topic list","slug":"regression-cannot-sort-topic-list","posts_count":5,"reply_count":0,"highest_post_number":5,"image_url":null,"created_at":"2014-01-13T20:14:06.000-05:00","last_posted_at":"2014-01-14T19:31:28.000-05:00","bumped":true,"bumped_at":"2014-01-14T07:31:19.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":true,"views":37,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":1995}]},{"id":10462,"title":"Rebake error when posts contain deleted YouTube video","fancy_title":"Rebake error when posts contain deleted YouTube video","slug":"rebake-error-when-posts-contain-deleted-youtube-video","posts_count":7,"reply_count":1,"highest_post_number":7,"image_url":null,"created_at":"2013-10-19T00:01:21.000-04:00","last_posted_at":"2014-01-14T02:24:19.000-05:00","bumped":true,"bumped_at":"2014-01-14T02:24:12.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":178,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":6695},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11932,"title":"Use of blockquote tag causes text outside a paragraph","fancy_title":"Use of blockquote tag causes text outside a paragraph","slug":"use-of-blockquote-tag-causes-text-outside-a-paragraph","posts_count":4,"reply_count":2,"highest_post_number":4,"image_url":null,"created_at":"2014-01-13T13:38:15.000-05:00","last_posted_at":"2014-01-13T19:30:37.000-05:00","bumped":true,"bumped_at":"2014-01-14T02:22:58.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":54,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":10357,"title":"Displaced Wrench Icon Chrome","fancy_title":"Displaced Wrench Icon Chrome","slug":"displaced-wrench-icon-chrome","posts_count":12,"reply_count":4,"highest_post_number":12,"image_url":"/uploads/default/_optimized/9f3/f35/c5379beffe_690x300.jpg","created_at":"2013-10-14T05:48:21.000-04:00","last_posted_at":"2014-01-14T03:21:32.000-05:00","bumped":true,"bumped_at":"2014-01-13T19:03:33.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":206,"like_count":10,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":7073},{"extras":null,"description":"Frequent Poster","user_id":212},{"extras":null,"description":"Frequent Poster","user_id":6118},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":10114,"title":"Invitation expiry workflow is wonky","fancy_title":"Invitation expiry workflow is wonky","slug":"invitation-expiry-workflow-is-wonky","posts_count":14,"reply_count":7,"highest_post_number":14,"image_url":null,"created_at":"2013-09-30T00:59:36.000-04:00","last_posted_at":"2014-01-13T18:51:26.000-05:00","bumped":true,"bumped_at":"2014-01-13T18:51:26.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":176,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":1},{"extras":null,"description":"Most Posts","user_id":7076},{"extras":null,"description":"Frequent Poster","user_id":2},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":6330,"title":"Reply not disabled if topic closed while viewing","fancy_title":"Reply not disabled if topic closed while viewing","slug":"reply-not-disabled-if-topic-closed-while-viewing","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":"https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon","created_at":"2013-05-02T06:02:06.000-04:00","last_posted_at":"2014-01-13T11:54:22.000-05:00","bumped":true,"bumped_at":"2014-01-13T11:54:22.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":164,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":4851},{"extras":null,"description":"Most Posts","user_id":2},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":8367,"title":"Very fast scrolling fails to mark all posts read in a thread","fancy_title":"Very fast scrolling fails to mark all posts read in a thread","slug":"very-fast-scrolling-fails-to-mark-all-posts-read-in-a-thread","posts_count":11,"reply_count":7,"highest_post_number":13,"image_url":null,"created_at":"2013-07-14T12:37:02.000-04:00","last_posted_at":"2014-01-13T11:16:56.000-05:00","bumped":true,"bumped_at":"2014-01-13T11:16:33.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":288,"like_count":5,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":4457},{"extras":null,"description":"Most Posts","user_id":6280},{"extras":null,"description":"Frequent Poster","user_id":3681},{"extras":null,"description":"Frequent Poster","user_id":1621},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":8815,"title":"Cache headers confuse proxies","fancy_title":"Cache headers confuse proxies","slug":"cache-headers-confuse-proxies","posts_count":9,"reply_count":3,"highest_post_number":9,"image_url":null,"created_at":"2013-08-02T05:45:26.000-04:00","last_posted_at":"2014-01-13T11:12:09.000-05:00","bumped":true,"bumped_at":"2014-01-13T10:41:44.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":true,"views":314,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":6266},{"extras":null,"description":"Most Posts","user_id":19},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":4457},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":32}]},{"id":11371,"title":"Search not working for Staff users","fancy_title":"Search not working for Staff users","slug":"search-not-working-for-staff-users","posts_count":15,"reply_count":10,"highest_post_number":15,"image_url":null,"created_at":"2013-12-11T13:22:56.000-05:00","last_posted_at":"2014-01-13T01:41:50.000-05:00","bumped":true,"bumped_at":"2014-01-13T01:41:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":true,"views":217,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":5335},{"extras":null,"description":"Most Posts","user_id":19},{"extras":null,"description":"Frequent Poster","user_id":6314},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":9908,"title":"Draft bar overrides pagination widget","fancy_title":"Draft bar overrides pagination widget","slug":"draft-bar-overrides-pagination-widget","posts_count":4,"reply_count":0,"highest_post_number":4,"image_url":null,"created_at":"2013-09-19T17:19:52.000-04:00","last_posted_at":"2014-01-13T01:26:01.000-05:00","bumped":true,"bumped_at":"2014-01-13T01:25:12.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":true,"views":108,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":5351},{"extras":null,"description":"Most Posts","user_id":471},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":6134,"title":"Unread topic is stuck as unread after insertion of staff message","fancy_title":"Unread topic is stuck as unread after insertion of staff message","slug":"unread-topic-is-stuck-as-unread-after-insertion-of-staff-message","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":"https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon","created_at":"2013-04-24T13:37:32.000-04:00","last_posted_at":"2014-01-13T01:22:49.000-05:00","bumped":true,"bumped_at":"2014-01-13T01:22:42.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":169,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":1,"posters":[{"extras":null,"description":"Original Poster","user_id":3681},{"extras":null,"description":"Most Posts","user_id":5351},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11914,"title":"Google analytics is not registering page views","fancy_title":"Google analytics is not registering page views","slug":"google-analytics-is-not-registering-page-views","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-13T00:32:45.000-05:00","last_posted_at":"2014-01-13T00:32:46.000-05:00","bumped":true,"bumped_at":"2014-01-13T00:32:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":37,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":1,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":1}]}]}}, -"/c/feature/l/latest.json": {"users":[{"id":1,"username":"sam","avatar_template":"/images/avatar.png"},{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"},{"id":8021,"username":"Abhishek_Gupta","avatar_template":"/images/avatar.png"},{"id":6695,"username":"illspirit","avatar_template":"/images/avatar.png"},{"id":2,"username":"neil","avatar_template":"/images/avatar.png"},{"id":3124,"username":"sipp11","avatar_template":"/images/avatar.png"},{"id":7513,"username":"digit","avatar_template":"/images/avatar.png"},{"id":19,"username":"eviltrout","avatar_template":"/images/avatar.png"},{"id":3,"username":"supermathie","avatar_template":"/images/avatar.png"},{"id":7073,"username":"5an1ty","avatar_template":"/images/avatar.png"},{"id":4996,"username":"wmertens","avatar_template":"/images/avatar.png"},{"id":6377,"username":"zh99998","avatar_template":"/images/avatar.png"},{"id":1496,"username":"cfstras","avatar_template":"/images/avatar.png"},{"id":7995,"username":"Hunter","avatar_template":"/images/avatar.png"},{"id":6626,"username":"riking","avatar_template":"/images/avatar.png"},{"id":1995,"username":"zogstrip","avatar_template":"/images/avatar.png"},{"id":5048,"username":"SneakySly","avatar_template":"/images/avatar.png"},{"id":7731,"username":"YOU","avatar_template":"/images/avatar.png"},{"id":7985,"username":"onlinedev","avatar_template":"/images/avatar.png"},{"id":3415,"username":"radq","avatar_template":"/images/avatar.png"},{"id":5351,"username":"erlend_sh","avatar_template":"/images/avatar.png"},{"id":471,"username":"BhaelOchon","avatar_template":"/images/avatar.png"},{"id":7,"username":"pekka","avatar_template":"/images/avatar.png"},{"id":4780,"username":"HugoAlmeida","avatar_template":"/images/avatar.png"},{"id":5053,"username":"Blue","avatar_template":"/images/avatar.png"},{"id":212,"username":"alxndr","avatar_template":"/images/avatar.png"},{"id":6118,"username":"lukelarris","avatar_template":"/images/avatar.png"},{"id":7076,"username":"philnelson","avatar_template":"/images/avatar.png"},{"id":4851,"username":"jab","avatar_template":"/images/avatar.png"},{"id":4457,"username":"Lee_Ars","avatar_template":"/images/avatar.png"},{"id":6280,"username":"mx2000","avatar_template":"/images/avatar.png"},{"id":3681,"username":"Ajarn","avatar_template":"/images/avatar.png"},{"id":1621,"username":"bnb","avatar_template":"/images/avatar.png"},{"id":6266,"username":"bragi","avatar_template":"/images/avatar.png"},{"id":5335,"username":"masda70","avatar_template":"/images/avatar.png"},{"id":6314,"username":"rafaelfranca","avatar_template":"/images/avatar.png"}],"topic_list":{"can_create_topic":false,"more_topics_url":"/latest.json?category=2&page=1","draft":null,"draft_key":"new_topic","draft_sequence":null,"topics":[{"id":2,"title":"Category definition for feature","fancy_title":"Category definition for feature","slug":"category-definition-for-feature","posts_count":2,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2013-01-31T23:56:34.000-05:00","last_posted_at":"2013-03-07T22:42:27.000-05:00","bumped":true,"bumped_at":"2013-02-26T18:52:56.000-05:00","unseen":false,"pinned":true,"excerpt":"Features on Discourse.","visible":true,"closed":false,"archived":false,"views":469,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":1},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11994,"title":"Cross domain rules, followed?","fancy_title":"Cross domain rules, followed?","slug":"cross-domain-rules-followed","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":"/plugins/emoji/images/smile.png","created_at":"2014-01-16T09:59:15.000-05:00","last_posted_at":"2014-01-16T09:59:15.000-05:00","bumped":true,"bumped_at":"2014-01-16T11:04:32.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":15,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"Abhishek_Gupta","category_id":2,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":8021}]},{"id":11888,"title":"Uncategorized topics not allowed, still seeing tag places","fancy_title":"Uncategorized topics not allowed, still seeing tag places","slug":"uncategorized-topics-not-allowed-still-seeing-tag-places","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":null,"created_at":"2014-01-10T19:23:37.000-05:00","last_posted_at":"2014-01-15T22:41:25.000-05:00","bumped":true,"bumped_at":"2014-01-15T22:41:25.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":50,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"illspirit","category_id":2,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":6695},{"extras":null,"description":"Most Posts","user_id":32},{"extras":null,"description":"Frequent Poster","user_id":2}]},{"id":9151,"title":"Apple touch icon doesn't show if there is no sub domain","fancy_title":"Apple touch icon doesn’t show if there is no sub domain","slug":"apple-touch-icon-doesnt-show-if-there-is-no-sub-domain","posts_count":7,"reply_count":4,"highest_post_number":7,"image_url":null,"created_at":"2013-08-16T18:16:53.000-04:00","last_posted_at":"2014-01-15T17:10:18.000-05:00","bumped":true,"bumped_at":"2014-01-15T13:19:22.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":188,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":3124},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":10911,"title":"/users/activate-account pulling blank logo instead of defaulting to h2","fancy_title":"/users/activate-account pulling blank logo instead of defaulting to h2","slug":"users-activate-account-pulling-blank-logo-instead-of-defaulting-to-h2","posts_count":3,"reply_count":1,"highest_post_number":3,"image_url":null,"created_at":"2013-11-12T14:49:04.000-05:00","last_posted_at":"2014-01-15T10:21:37.000-05:00","bumped":true,"bumped_at":"2014-01-15T10:21:37.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":121,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"eviltrout","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":7513},{"extras":"latest","description":"Most Recent Poster","user_id":19}]},{"id":11937,"title":"Smiley parser is busted","fancy_title":"Smiley parser is busted","slug":"smiley-parser-is-busted","posts_count":4,"reply_count":4,"highest_post_number":7,"image_url":"/plugins/emoji/images/smile.png","created_at":"2014-01-13T15:42:00.000-05:00","last_posted_at":"2014-01-15T05:51:16.000-05:00","bumped":true,"bumped_at":"2014-01-15T05:51:16.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":66,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":3},{"extras":null,"description":"Most Posts","user_id":7073},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":6625,"title":"Error 500 on PUT of site config","fancy_title":"Error 500 on PUT of site config","slug":"error-500-on-put-of-site-config","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2013-05-14T18:13:56.000-04:00","last_posted_at":"2014-01-16T04:55:50.000-05:00","bumped":true,"bumped_at":"2014-01-15T04:43:23.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":132,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":4996},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":11225,"title":"Forum acts weirdly after client side updates","fancy_title":"Forum acts weirdly after client side updates","slug":"forum-acts-weirdly-after-client-side-updates","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":null,"created_at":"2013-12-02T18:32:10.000-05:00","last_posted_at":"2014-01-15T04:04:55.000-05:00","bumped":true,"bumped_at":"2014-01-15T02:55:18.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":117,"like_count":7,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":1},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":11903,"title":"Error after update to 0.9.8.1","fancy_title":"Error after update to 0.9.8.1","slug":"error-after-update-to-0-9-8-1","posts_count":14,"reply_count":6,"highest_post_number":17,"image_url":null,"created_at":"2014-01-12T06:55:45.000-05:00","last_posted_at":"2014-01-15T01:48:58.000-05:00","bumped":true,"bumped_at":"2014-01-15T01:48:58.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":121,"like_count":6,"has_summary":false,"archetype":"regular","last_poster_username":"zh99998","category_id":2,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":6377},{"extras":null,"description":"Most Posts","user_id":1496},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":19}]},{"id":11969,"title":"Qunit error and possibly related ember.js problem","fancy_title":"Qunit error and possibly related ember.js problem","slug":"qunit-error-and-possibly-related-ember-js-problem","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-14T22:51:32.000-05:00","last_posted_at":"2014-01-14T22:51:32.000-05:00","bumped":false,"bumped_at":"2014-01-14T22:51:32.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":32,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"Hunter","category_id":2,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":7995}]},{"id":11945,"title":"Stuff disappears on the groups page","fancy_title":"Stuff disappears on the groups page","slug":"stuff-disappears-on-the-groups-page","posts_count":7,"reply_count":2,"highest_post_number":7,"image_url":null,"created_at":"2014-01-13T23:03:53.000-05:00","last_posted_at":"2014-01-15T01:26:07.000-05:00","bumped":true,"bumped_at":"2014-01-14T21:09:01.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":54,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":6695},{"extras":null,"description":"Most Posts","user_id":6626},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":1995}]},{"id":11520,"title":"Discourse WordPress Plugin: Emoji's do not properly display","fancy_title":"Discourse WordPress Plugin: Emoji’s do not properly display","slug":"discourse-wordpress-plugin-emojis-do-not-properly-display","posts_count":9,"reply_count":4,"highest_post_number":9,"image_url":"/uploads/default/_optimized/638/4db/eff43a45b8_690x420.png","created_at":"2013-12-19T23:32:03.000-05:00","last_posted_at":"2014-01-15T04:32:19.000-05:00","bumped":true,"bumped_at":"2014-01-14T17:53:34.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":168,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":5048},{"extras":null,"description":"Frequent Poster","user_id":7731},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":11597,"title":"All categories drop down does not close after clicking on first menu \"all categories\"","fancy_title":"All categories drop down does not close after clicking on first menu “all categories”","slug":"all-categories-drop-down-does-not-close-after-clicking-on-first-menu-all-categories","posts_count":5,"reply_count":2,"highest_post_number":5,"image_url":"/uploads/default/2495/f9efe463ae67632d.png","created_at":"2013-12-25T15:09:27.000-05:00","last_posted_at":"2014-01-14T17:46:41.000-05:00","bumped":true,"bumped_at":"2014-01-14T17:46:41.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":73,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"radq","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":7985},{"extras":null,"description":"Most Posts","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":3415}]},{"id":11962,"title":"Editor When Clicking on Wrench Issue","fancy_title":"Editor When Clicking on Wrench Issue","slug":"editor-when-clicking-on-wrench-issue","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":"/uploads/default/_optimized/ca4/f70/ac7278b8f6_690x176.png","created_at":"2014-01-14T17:23:20.000-05:00","last_posted_at":"2014-01-14T17:24:02.000-05:00","bumped":true,"bumped_at":"2014-01-14T17:24:02.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":30,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":7073},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11831,"title":"Broken links, possibly related to HTTPS","fancy_title":"Broken links, possibly related to HTTPS","slug":"broken-links-possibly-related-to-https","posts_count":17,"reply_count":13,"highest_post_number":18,"image_url":null,"created_at":"2014-01-08T17:40:45.000-05:00","last_posted_at":"2014-01-14T16:03:07.000-05:00","bumped":true,"bumped_at":"2014-01-14T16:03:07.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":102,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"eviltrout","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":5351},{"extras":null,"description":"Most Posts","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":471},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":19}]},{"id":11916,"title":"Unable to save user preferences","fancy_title":"Unable to save user preferences","slug":"unable-to-save-user-preferences","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2014-01-13T02:29:26.000-05:00","last_posted_at":"2014-01-14T14:39:32.000-05:00","bumped":true,"bumped_at":"2014-01-14T14:39:29.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":34,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster","user_id":1995}]},{"id":10425,"title":"Editing category permissions: select value doesn't change","fancy_title":"Editing category permissions: select value doesn’t change","slug":"editing-category-permissions-select-value-doesnt-change","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":"/uploads/meta_discourse/1956/d55fba29dbd7e1fe.png","created_at":"2013-10-17T18:20:20.000-04:00","last_posted_at":"2013-10-17T18:20:21.000-04:00","bumped":true,"bumped_at":"2014-01-14T13:35:37.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":92,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"pekka","category_id":2,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":7}]},{"id":6557,"title":"Middle clicking a link twice does not work as expected","fancy_title":"Middle clicking a link twice does not work as expected","slug":"middle-clicking-a-link-twice-does-not-work-as-expected","posts_count":10,"reply_count":7,"highest_post_number":10,"image_url":null,"created_at":"2013-05-11T13:56:02.000-04:00","last_posted_at":"2014-01-14T13:13:04.000-05:00","bumped":true,"bumped_at":"2014-01-14T13:13:04.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":401,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"neil","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":4780},{"extras":null,"description":"Most Posts","user_id":5053},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":2}]},{"id":11944,"title":"Regression: Cannot sort topic list","fancy_title":"Regression: Cannot sort topic list","slug":"regression-cannot-sort-topic-list","posts_count":5,"reply_count":0,"highest_post_number":5,"image_url":null,"created_at":"2014-01-13T20:14:06.000-05:00","last_posted_at":"2014-01-14T19:31:28.000-05:00","bumped":true,"bumped_at":"2014-01-14T07:31:19.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":true,"views":37,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":1995}]},{"id":10462,"title":"Rebake error when posts contain deleted YouTube video","fancy_title":"Rebake error when posts contain deleted YouTube video","slug":"rebake-error-when-posts-contain-deleted-youtube-video","posts_count":7,"reply_count":1,"highest_post_number":7,"image_url":null,"created_at":"2013-10-19T00:01:21.000-04:00","last_posted_at":"2014-01-14T02:24:19.000-05:00","bumped":true,"bumped_at":"2014-01-14T02:24:12.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":178,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":6695},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11932,"title":"Use of blockquote tag causes text outside a paragraph","fancy_title":"Use of blockquote tag causes text outside a paragraph","slug":"use-of-blockquote-tag-causes-text-outside-a-paragraph","posts_count":4,"reply_count":2,"highest_post_number":4,"image_url":null,"created_at":"2014-01-13T13:38:15.000-05:00","last_posted_at":"2014-01-13T19:30:37.000-05:00","bumped":true,"bumped_at":"2014-01-14T02:22:58.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":54,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":10357,"title":"Displaced Wrench Icon Chrome","fancy_title":"Displaced Wrench Icon Chrome","slug":"displaced-wrench-icon-chrome","posts_count":12,"reply_count":4,"highest_post_number":12,"image_url":"/uploads/default/_optimized/9f3/f35/c5379beffe_690x300.jpg","created_at":"2013-10-14T05:48:21.000-04:00","last_posted_at":"2014-01-14T03:21:32.000-05:00","bumped":true,"bumped_at":"2014-01-13T19:03:33.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":206,"like_count":10,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":7073},{"extras":null,"description":"Frequent Poster","user_id":212},{"extras":null,"description":"Frequent Poster","user_id":6118},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":10114,"title":"Invitation expiry workflow is wonky","fancy_title":"Invitation expiry workflow is wonky","slug":"invitation-expiry-workflow-is-wonky","posts_count":14,"reply_count":7,"highest_post_number":14,"image_url":null,"created_at":"2013-09-30T00:59:36.000-04:00","last_posted_at":"2014-01-13T18:51:26.000-05:00","bumped":true,"bumped_at":"2014-01-13T18:51:26.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":176,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":1},{"extras":null,"description":"Most Posts","user_id":7076},{"extras":null,"description":"Frequent Poster","user_id":2},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":6330,"title":"Reply not disabled if topic closed while viewing","fancy_title":"Reply not disabled if topic closed while viewing","slug":"reply-not-disabled-if-topic-closed-while-viewing","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":"https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon","created_at":"2013-05-02T06:02:06.000-04:00","last_posted_at":"2014-01-13T11:54:22.000-05:00","bumped":true,"bumped_at":"2014-01-13T11:54:22.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":164,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":4851},{"extras":null,"description":"Most Posts","user_id":2},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":8367,"title":"Very fast scrolling fails to mark all posts read in a thread","fancy_title":"Very fast scrolling fails to mark all posts read in a thread","slug":"very-fast-scrolling-fails-to-mark-all-posts-read-in-a-thread","posts_count":11,"reply_count":7,"highest_post_number":13,"image_url":null,"created_at":"2013-07-14T12:37:02.000-04:00","last_posted_at":"2014-01-13T11:16:56.000-05:00","bumped":true,"bumped_at":"2014-01-13T11:16:33.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":288,"like_count":5,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":4457},{"extras":null,"description":"Most Posts","user_id":6280},{"extras":null,"description":"Frequent Poster","user_id":3681},{"extras":null,"description":"Frequent Poster","user_id":1621},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":8815,"title":"Cache headers confuse proxies","fancy_title":"Cache headers confuse proxies","slug":"cache-headers-confuse-proxies","posts_count":9,"reply_count":3,"highest_post_number":9,"image_url":null,"created_at":"2013-08-02T05:45:26.000-04:00","last_posted_at":"2014-01-13T11:12:09.000-05:00","bumped":true,"bumped_at":"2014-01-13T10:41:44.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":true,"views":314,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":6266},{"extras":null,"description":"Most Posts","user_id":19},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":4457},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":32}]},{"id":11371,"title":"Search not working for Staff users","fancy_title":"Search not working for Staff users","slug":"search-not-working-for-staff-users","posts_count":15,"reply_count":10,"highest_post_number":15,"image_url":null,"created_at":"2013-12-11T13:22:56.000-05:00","last_posted_at":"2014-01-13T01:41:50.000-05:00","bumped":true,"bumped_at":"2014-01-13T01:41:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":true,"views":217,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":5335},{"extras":null,"description":"Most Posts","user_id":19},{"extras":null,"description":"Frequent Poster","user_id":6314},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":9908,"title":"Draft bar overrides pagination widget","fancy_title":"Draft bar overrides pagination widget","slug":"draft-bar-overrides-pagination-widget","posts_count":4,"reply_count":0,"highest_post_number":4,"image_url":null,"created_at":"2013-09-19T17:19:52.000-04:00","last_posted_at":"2014-01-13T01:26:01.000-05:00","bumped":true,"bumped_at":"2014-01-13T01:25:12.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":true,"views":108,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":5351},{"extras":null,"description":"Most Posts","user_id":471},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":6134,"title":"Unread topic is stuck as unread after insertion of staff message","fancy_title":"Unread topic is stuck as unread after insertion of staff message","slug":"unread-topic-is-stuck-as-unread-after-insertion-of-staff-message","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":"https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon","created_at":"2013-04-24T13:37:32.000-04:00","last_posted_at":"2014-01-13T01:22:49.000-05:00","bumped":true,"bumped_at":"2014-01-13T01:22:42.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":169,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":2,"posters":[{"extras":null,"description":"Original Poster","user_id":3681},{"extras":null,"description":"Most Posts","user_id":5351},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11914,"title":"Google analytics is not registering page views","fancy_title":"Google analytics is not registering page views","slug":"google-analytics-is-not-registering-page-views","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-13T00:32:45.000-05:00","last_posted_at":"2014-01-13T00:32:46.000-05:00","bumped":true,"bumped_at":"2014-01-13T00:32:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":37,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":2,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":1}]}]}}, -"/c/dev/l/latest.json": {"users":[{"id":1,"username":"sam","avatar_template":"/images/avatar.png"},{"id":32,"username":"codinghorror","avatar_template":"/images/avatar.png"},{"id":8021,"username":"Abhishek_Gupta","avatar_template":"/images/avatar.png"},{"id":6695,"username":"illspirit","avatar_template":"/images/avatar.png"},{"id":2,"username":"neil","avatar_template":"/images/avatar.png"},{"id":3124,"username":"sipp11","avatar_template":"/images/avatar.png"},{"id":7513,"username":"digit","avatar_template":"/images/avatar.png"},{"id":19,"username":"eviltrout","avatar_template":"/images/avatar.png"},{"id":3,"username":"supermathie","avatar_template":"/images/avatar.png"},{"id":7073,"username":"5an1ty","avatar_template":"/images/avatar.png"},{"id":4996,"username":"wmertens","avatar_template":"/images/avatar.png"},{"id":6377,"username":"zh99998","avatar_template":"/images/avatar.png"},{"id":1496,"username":"cfstras","avatar_template":"/images/avatar.png"},{"id":7995,"username":"Hunter","avatar_template":"/images/avatar.png"},{"id":6626,"username":"riking","avatar_template":"/images/avatar.png"},{"id":1995,"username":"zogstrip","avatar_template":"/images/avatar.png"},{"id":5048,"username":"SneakySly","avatar_template":"/images/avatar.png"},{"id":7731,"username":"YOU","avatar_template":"/images/avatar.png"},{"id":7985,"username":"onlinedev","avatar_template":"/images/avatar.png"},{"id":3415,"username":"radq","avatar_template":"/images/avatar.png"},{"id":5351,"username":"erlend_sh","avatar_template":"/images/avatar.png"},{"id":471,"username":"BhaelOchon","avatar_template":"/images/avatar.png"},{"id":7,"username":"pekka","avatar_template":"/images/avatar.png"},{"id":4780,"username":"HugoAlmeida","avatar_template":"/images/avatar.png"},{"id":5053,"username":"Blue","avatar_template":"/images/avatar.png"},{"id":212,"username":"alxndr","avatar_template":"/images/avatar.png"},{"id":6118,"username":"lukelarris","avatar_template":"/images/avatar.png"},{"id":7076,"username":"philnelson","avatar_template":"/images/avatar.png"},{"id":4851,"username":"jab","avatar_template":"/images/avatar.png"},{"id":4457,"username":"Lee_Ars","avatar_template":"/images/avatar.png"},{"id":6280,"username":"mx2000","avatar_template":"/images/avatar.png"},{"id":3681,"username":"Ajarn","avatar_template":"/images/avatar.png"},{"id":1621,"username":"bnb","avatar_template":"/images/avatar.png"},{"id":6266,"username":"bragi","avatar_template":"/images/avatar.png"},{"id":5335,"username":"masda70","avatar_template":"/images/avatar.png"},{"id":6314,"username":"rafaelfranca","avatar_template":"/images/avatar.png"}],"topic_list":{"can_create_topic":false,"more_topics_url":"/latest.json?category=2&page=1","draft":null,"draft_key":"new_topic","draft_sequence":null,"topics":[{"id":2,"title":"Category definition for dev","fancy_title":"Category definition for dev","slug":"category-definition-for-dev","posts_count":2,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2013-01-31T23:56:34.000-05:00","last_posted_at":"2013-03-07T22:42:27.000-05:00","bumped":true,"bumped_at":"2013-02-26T18:52:56.000-05:00","unseen":false,"pinned":true,"excerpt":"Development of Discourse.","visible":true,"closed":false,"archived":false,"views":469,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":1},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11994,"title":"Cross domain rules, followed?","fancy_title":"Cross domain rules, followed?","slug":"cross-domain-rules-followed","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":"/plugins/emoji/images/smile.png","created_at":"2014-01-16T09:59:15.000-05:00","last_posted_at":"2014-01-16T09:59:15.000-05:00","bumped":true,"bumped_at":"2014-01-16T11:04:32.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":15,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"Abhishek_Gupta","category_id":7,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":8021}]},{"id":11888,"title":"Uncategorized topics not allowed, still seeing tag places","fancy_title":"Uncategorized topics not allowed, still seeing tag places","slug":"uncategorized-topics-not-allowed-still-seeing-tag-places","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":null,"created_at":"2014-01-10T19:23:37.000-05:00","last_posted_at":"2014-01-15T22:41:25.000-05:00","bumped":true,"bumped_at":"2014-01-15T22:41:25.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":50,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"illspirit","category_id":7,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":6695},{"extras":null,"description":"Most Posts","user_id":32},{"extras":null,"description":"Frequent Poster","user_id":2}]},{"id":9151,"title":"Apple touch icon doesn't show if there is no sub domain","fancy_title":"Apple touch icon doesn’t show if there is no sub domain","slug":"apple-touch-icon-doesnt-show-if-there-is-no-sub-domain","posts_count":7,"reply_count":4,"highest_post_number":7,"image_url":null,"created_at":"2013-08-16T18:16:53.000-04:00","last_posted_at":"2014-01-15T17:10:18.000-05:00","bumped":true,"bumped_at":"2014-01-15T13:19:22.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":188,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":3124},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":10911,"title":"/users/activate-account pulling blank logo instead of defaulting to h2","fancy_title":"/users/activate-account pulling blank logo instead of defaulting to h2","slug":"users-activate-account-pulling-blank-logo-instead-of-defaulting-to-h2","posts_count":3,"reply_count":1,"highest_post_number":3,"image_url":null,"created_at":"2013-11-12T14:49:04.000-05:00","last_posted_at":"2014-01-15T10:21:37.000-05:00","bumped":true,"bumped_at":"2014-01-15T10:21:37.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":121,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"eviltrout","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":7513},{"extras":"latest","description":"Most Recent Poster","user_id":19}]},{"id":11937,"title":"Smiley parser is busted","fancy_title":"Smiley parser is busted","slug":"smiley-parser-is-busted","posts_count":4,"reply_count":4,"highest_post_number":7,"image_url":"/plugins/emoji/images/smile.png","created_at":"2014-01-13T15:42:00.000-05:00","last_posted_at":"2014-01-15T05:51:16.000-05:00","bumped":true,"bumped_at":"2014-01-15T05:51:16.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":66,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":3},{"extras":null,"description":"Most Posts","user_id":7073},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":6625,"title":"Error 500 on PUT of site config","fancy_title":"Error 500 on PUT of site config","slug":"error-500-on-put-of-site-config","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2013-05-14T18:13:56.000-04:00","last_posted_at":"2014-01-16T04:55:50.000-05:00","bumped":true,"bumped_at":"2014-01-15T04:43:23.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":132,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":4996},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":11225,"title":"Forum acts weirdly after client side updates","fancy_title":"Forum acts weirdly after client side updates","slug":"forum-acts-weirdly-after-client-side-updates","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":null,"created_at":"2013-12-02T18:32:10.000-05:00","last_posted_at":"2014-01-15T04:04:55.000-05:00","bumped":true,"bumped_at":"2014-01-15T02:55:18.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":117,"like_count":7,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":1},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":11903,"title":"Error after update to 0.9.8.1","fancy_title":"Error after update to 0.9.8.1","slug":"error-after-update-to-0-9-8-1","posts_count":14,"reply_count":6,"highest_post_number":17,"image_url":null,"created_at":"2014-01-12T06:55:45.000-05:00","last_posted_at":"2014-01-15T01:48:58.000-05:00","bumped":true,"bumped_at":"2014-01-15T01:48:58.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":121,"like_count":6,"has_summary":false,"archetype":"regular","last_poster_username":"zh99998","category_id":7,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":6377},{"extras":null,"description":"Most Posts","user_id":1496},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":19}]},{"id":11969,"title":"Qunit error and possibly related ember.js problem","fancy_title":"Qunit error and possibly related ember.js problem","slug":"qunit-error-and-possibly-related-ember-js-problem","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-14T22:51:32.000-05:00","last_posted_at":"2014-01-14T22:51:32.000-05:00","bumped":false,"bumped_at":"2014-01-14T22:51:32.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":32,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"Hunter","category_id":7,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":7995}]},{"id":11945,"title":"Stuff disappears on the groups page","fancy_title":"Stuff disappears on the groups page","slug":"stuff-disappears-on-the-groups-page","posts_count":7,"reply_count":2,"highest_post_number":7,"image_url":null,"created_at":"2014-01-13T23:03:53.000-05:00","last_posted_at":"2014-01-15T01:26:07.000-05:00","bumped":true,"bumped_at":"2014-01-14T21:09:01.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":54,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":6695},{"extras":null,"description":"Most Posts","user_id":6626},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":1995}]},{"id":11520,"title":"Discourse WordPress Plugin: Emoji's do not properly display","fancy_title":"Discourse WordPress Plugin: Emoji’s do not properly display","slug":"discourse-wordpress-plugin-emojis-do-not-properly-display","posts_count":9,"reply_count":4,"highest_post_number":9,"image_url":"/uploads/default/_optimized/638/4db/eff43a45b8_690x420.png","created_at":"2013-12-19T23:32:03.000-05:00","last_posted_at":"2014-01-15T04:32:19.000-05:00","bumped":true,"bumped_at":"2014-01-14T17:53:34.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":168,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":5048},{"extras":null,"description":"Frequent Poster","user_id":7731},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":11597,"title":"All categories drop down does not close after clicking on first menu \"all categories\"","fancy_title":"All categories drop down does not close after clicking on first menu “all categories”","slug":"all-categories-drop-down-does-not-close-after-clicking-on-first-menu-all-categories","posts_count":5,"reply_count":2,"highest_post_number":5,"image_url":"/uploads/default/2495/f9efe463ae67632d.png","created_at":"2013-12-25T15:09:27.000-05:00","last_posted_at":"2014-01-14T17:46:41.000-05:00","bumped":true,"bumped_at":"2014-01-14T17:46:41.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":73,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"radq","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":7985},{"extras":null,"description":"Most Posts","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":3415}]},{"id":11962,"title":"Editor When Clicking on Wrench Issue","fancy_title":"Editor When Clicking on Wrench Issue","slug":"editor-when-clicking-on-wrench-issue","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":"/uploads/default/_optimized/ca4/f70/ac7278b8f6_690x176.png","created_at":"2014-01-14T17:23:20.000-05:00","last_posted_at":"2014-01-14T17:24:02.000-05:00","bumped":true,"bumped_at":"2014-01-14T17:24:02.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":30,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":7073},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11831,"title":"Broken links, possibly related to HTTPS","fancy_title":"Broken links, possibly related to HTTPS","slug":"broken-links-possibly-related-to-https","posts_count":17,"reply_count":13,"highest_post_number":18,"image_url":null,"created_at":"2014-01-08T17:40:45.000-05:00","last_posted_at":"2014-01-14T16:03:07.000-05:00","bumped":true,"bumped_at":"2014-01-14T16:03:07.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":102,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"eviltrout","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":5351},{"extras":null,"description":"Most Posts","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":471},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":19}]},{"id":11916,"title":"Unable to save user preferences","fancy_title":"Unable to save user preferences","slug":"unable-to-save-user-preferences","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2014-01-13T02:29:26.000-05:00","last_posted_at":"2014-01-14T14:39:32.000-05:00","bumped":true,"bumped_at":"2014-01-14T14:39:29.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":34,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster","user_id":1995}]},{"id":10425,"title":"Editing category permissions: select value doesn't change","fancy_title":"Editing category permissions: select value doesn’t change","slug":"editing-category-permissions-select-value-doesnt-change","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":"/uploads/meta_discourse/1956/d55fba29dbd7e1fe.png","created_at":"2013-10-17T18:20:20.000-04:00","last_posted_at":"2013-10-17T18:20:21.000-04:00","bumped":true,"bumped_at":"2014-01-14T13:35:37.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":92,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"pekka","category_id":7,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":7}]},{"id":6557,"title":"Middle clicking a link twice does not work as expected","fancy_title":"Middle clicking a link twice does not work as expected","slug":"middle-clicking-a-link-twice-does-not-work-as-expected","posts_count":10,"reply_count":7,"highest_post_number":10,"image_url":null,"created_at":"2013-05-11T13:56:02.000-04:00","last_posted_at":"2014-01-14T13:13:04.000-05:00","bumped":true,"bumped_at":"2014-01-14T13:13:04.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":401,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"neil","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":4780},{"extras":null,"description":"Most Posts","user_id":5053},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":2}]},{"id":11944,"title":"Regression: Cannot sort topic list","fancy_title":"Regression: Cannot sort topic list","slug":"regression-cannot-sort-topic-list","posts_count":5,"reply_count":0,"highest_post_number":5,"image_url":null,"created_at":"2014-01-13T20:14:06.000-05:00","last_posted_at":"2014-01-14T19:31:28.000-05:00","bumped":true,"bumped_at":"2014-01-14T07:31:19.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":true,"views":37,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"zogstrip","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":1995}]},{"id":10462,"title":"Rebake error when posts contain deleted YouTube video","fancy_title":"Rebake error when posts contain deleted YouTube video","slug":"rebake-error-when-posts-contain-deleted-youtube-video","posts_count":7,"reply_count":1,"highest_post_number":7,"image_url":null,"created_at":"2013-10-19T00:01:21.000-04:00","last_posted_at":"2014-01-14T02:24:19.000-05:00","bumped":true,"bumped_at":"2014-01-14T02:24:12.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":178,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":6695},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11932,"title":"Use of blockquote tag causes text outside a paragraph","fancy_title":"Use of blockquote tag causes text outside a paragraph","slug":"use-of-blockquote-tag-causes-text-outside-a-paragraph","posts_count":4,"reply_count":2,"highest_post_number":4,"image_url":null,"created_at":"2014-01-13T13:38:15.000-05:00","last_posted_at":"2014-01-13T19:30:37.000-05:00","bumped":true,"bumped_at":"2014-01-14T02:22:58.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":54,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":6626},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":10357,"title":"Displaced Wrench Icon Chrome","fancy_title":"Displaced Wrench Icon Chrome","slug":"displaced-wrench-icon-chrome","posts_count":12,"reply_count":4,"highest_post_number":12,"image_url":"/uploads/default/_optimized/9f3/f35/c5379beffe_690x300.jpg","created_at":"2013-10-14T05:48:21.000-04:00","last_posted_at":"2014-01-14T03:21:32.000-05:00","bumped":true,"bumped_at":"2014-01-13T19:03:33.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":206,"like_count":10,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":7073},{"extras":null,"description":"Frequent Poster","user_id":212},{"extras":null,"description":"Frequent Poster","user_id":6118},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":"latest","description":"Most Recent Poster, Most Posts","user_id":32}]},{"id":10114,"title":"Invitation expiry workflow is wonky","fancy_title":"Invitation expiry workflow is wonky","slug":"invitation-expiry-workflow-is-wonky","posts_count":14,"reply_count":7,"highest_post_number":14,"image_url":null,"created_at":"2013-09-30T00:59:36.000-04:00","last_posted_at":"2014-01-13T18:51:26.000-05:00","bumped":true,"bumped_at":"2014-01-13T18:51:26.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":176,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":1},{"extras":null,"description":"Most Posts","user_id":7076},{"extras":null,"description":"Frequent Poster","user_id":2},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":6330,"title":"Reply not disabled if topic closed while viewing","fancy_title":"Reply not disabled if topic closed while viewing","slug":"reply-not-disabled-if-topic-closed-while-viewing","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":"https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon","created_at":"2013-05-02T06:02:06.000-04:00","last_posted_at":"2014-01-13T11:54:22.000-05:00","bumped":true,"bumped_at":"2014-01-13T11:54:22.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":164,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":4851},{"extras":null,"description":"Most Posts","user_id":2},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":8367,"title":"Very fast scrolling fails to mark all posts read in a thread","fancy_title":"Very fast scrolling fails to mark all posts read in a thread","slug":"very-fast-scrolling-fails-to-mark-all-posts-read-in-a-thread","posts_count":11,"reply_count":7,"highest_post_number":13,"image_url":null,"created_at":"2013-07-14T12:37:02.000-04:00","last_posted_at":"2014-01-13T11:16:56.000-05:00","bumped":true,"bumped_at":"2014-01-13T11:16:33.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":288,"like_count":5,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":4457},{"extras":null,"description":"Most Posts","user_id":6280},{"extras":null,"description":"Frequent Poster","user_id":3681},{"extras":null,"description":"Frequent Poster","user_id":1621},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":8815,"title":"Cache headers confuse proxies","fancy_title":"Cache headers confuse proxies","slug":"cache-headers-confuse-proxies","posts_count":9,"reply_count":3,"highest_post_number":9,"image_url":null,"created_at":"2013-08-02T05:45:26.000-04:00","last_posted_at":"2014-01-13T11:12:09.000-05:00","bumped":true,"bumped_at":"2014-01-13T10:41:44.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":true,"views":314,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":6266},{"extras":null,"description":"Most Posts","user_id":19},{"extras":null,"description":"Frequent Poster","user_id":1},{"extras":null,"description":"Frequent Poster","user_id":4457},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":32}]},{"id":11371,"title":"Search not working for Staff users","fancy_title":"Search not working for Staff users","slug":"search-not-working-for-staff-users","posts_count":15,"reply_count":10,"highest_post_number":15,"image_url":null,"created_at":"2013-12-11T13:22:56.000-05:00","last_posted_at":"2014-01-13T01:41:50.000-05:00","bumped":true,"bumped_at":"2014-01-13T01:41:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":true,"views":217,"like_count":4,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":5335},{"extras":null,"description":"Most Posts","user_id":19},{"extras":null,"description":"Frequent Poster","user_id":6314},{"extras":null,"description":"Frequent Poster","user_id":32},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":9908,"title":"Draft bar overrides pagination widget","fancy_title":"Draft bar overrides pagination widget","slug":"draft-bar-overrides-pagination-widget","posts_count":4,"reply_count":0,"highest_post_number":4,"image_url":null,"created_at":"2013-09-19T17:19:52.000-04:00","last_posted_at":"2014-01-13T01:26:01.000-05:00","bumped":true,"bumped_at":"2014-01-13T01:25:12.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":true,"views":108,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":5351},{"extras":null,"description":"Most Posts","user_id":471},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":6134,"title":"Unread topic is stuck as unread after insertion of staff message","fancy_title":"Unread topic is stuck as unread after insertion of staff message","slug":"unread-topic-is-stuck-as-unread-after-insertion-of-staff-message","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":"https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon","created_at":"2013-04-24T13:37:32.000-04:00","last_posted_at":"2014-01-13T01:22:49.000-05:00","bumped":true,"bumped_at":"2014-01-13T01:22:42.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":true,"archived":false,"views":169,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"codinghorror","category_id":7,"posters":[{"extras":null,"description":"Original Poster","user_id":3681},{"extras":null,"description":"Most Posts","user_id":5351},{"extras":"latest","description":"Most Recent Poster","user_id":32}]},{"id":11914,"title":"Google analytics is not registering page views","fancy_title":"Google analytics is not registering page views","slug":"google-analytics-is-not-registering-page-views","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2014-01-13T00:32:45.000-05:00","last_posted_at":"2014-01-13T00:32:46.000-05:00","bumped":true,"bumped_at":"2014-01-13T00:32:46.000-05:00","unseen":false,"pinned":false,"visible":true,"closed":false,"archived":false,"views":37,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sam","category_id":7,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":1}]}]}}, -"/categories_and_latest.json": {"category_list":{"can_create_category":false,"can_create_topic":false,"draft":null,"draft_key":"new_topic","draft_sequence":null,"categories":[{"id":1,"name":"Uncategorized","color":"AB9364","text_color":"FFFFFF","slug":"uncategorized","topic_count":1,"post_count":0,"position":0,"description":"Topics that don't need a category, or don't fit into any other existing category.","description_text":"","topic_url":null,"logo_url":null,"background_url":null,"read_restricted":false,"permission":null,"notification_level":null,"topic_template":null,"has_children":false,"topics_day":0,"topics_week":0,"topics_month":0,"topics_year":0,"topics_all_time":1,"description_excerpt":"Topics that don't need a category, or don't fit into any other existing category.","is_uncategorized":true},{"id":3,"name":"Site Feedback","color":"808281","text_color":"FFFFFF","slug":"site-feedback","topic_count":0,"post_count":0,"position":1,"description":"Discussion about this site, its organization, how it works, and how we can improve it.","description_text":"Discussion about this site, its organization, how it works, and how we can improve it.","topic_url":"/t/about-the-site-feedback-category/2","logo_url":null,"background_url":null,"read_restricted":false,"permission":null,"notification_level":null,"topic_template":null,"has_children":false,"topics_day":0,"topics_week":0,"topics_month":0,"topics_year":0,"topics_all_time":0,"description_excerpt":"Discussion about this site, its organization, how it works, and how we can improve it."}]},"topic_list":{"can_create_topic":false,"draft":null,"draft_key":"new_topic","draft_sequence":null,"per_page":30,"topics":[{"id":8,"title":"Welcome to Discourse","fancy_title":"Welcome to Discourse","slug":"welcome-to-discourse","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2016-08-29T20:38:19.359Z","last_posted_at":"2016-08-29T20:38:19.402Z","bumped":true,"bumped_at":"2016-08-29T20:38:19.402Z","unseen":false,"pinned":true,"unpinned":null,"excerpt":"The first paragraph of this pinned topic will be visible as a welcome message to all new visitors on your homepage. It's important! \n\nEdit this into a brief description of your community: \n\n\nWho is it for?\nWhat can they …","visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":0,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"system","category_id":1,"pinned_globally":true,"posters":[{"extras":"latest single","description":"Original Poster, Most Recent Poster","user_id":-1}]}]}} + "/latest.json": { + users: [ + { id: 7204, username: "reyman64", avatar_template: "/images/avatar.png" }, + { id: 1, username: "sam", avatar_template: "/images/avatar.png" }, + { id: 5481, username: "f0rkz", avatar_template: "/images/avatar.png" }, + { id: 6473, username: "jkf", avatar_template: "/images/avatar.png" }, + { + id: 6973, + username: "stellarhopper", + avatar_template: "/images/avatar.png" + }, + { id: 19, username: "eviltrout", avatar_template: "/images/avatar.png" }, + { id: 14, username: "clay", avatar_template: "/images/avatar.png" }, + { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + }, + { id: 1917, username: "sil", avatar_template: "/images/avatar.png" }, + { id: 7197, username: "peeja", avatar_template: "/images/avatar.png" }, + { id: 1995, username: "zogstrip", avatar_template: "/images/avatar.png" }, + { + id: 8021, + username: "Abhishek_Gupta", + avatar_template: "/images/avatar.png" + }, + { id: 2291, username: "PabloC", avatar_template: "/images/avatar.png" }, + { id: 791, username: "srid", avatar_template: "/images/avatar.png" }, + { + id: 1580, + username: "ABillionSuns", + avatar_template: "/images/avatar.png" + }, + { id: 7270, username: "mhurwi", avatar_template: "/images/avatar.png" }, + { + id: 6695, + username: "illspirit", + avatar_template: "/images/avatar.png" + }, + { id: 6929, username: "BCHK", avatar_template: "/images/avatar.png" }, + { id: 4385, username: "jeans", avatar_template: "/images/avatar.png" }, + { id: 7073, username: "5an1ty", avatar_template: "/images/avatar.png" }, + { id: 6626, username: "riking", avatar_template: "/images/avatar.png" }, + { id: 4457, username: "Lee_Ars", avatar_template: "/images/avatar.png" }, + { id: 4263, username: "mcwumbly", avatar_template: "/images/avatar.png" }, + { + id: 8134, + username: "iontishina", + avatar_template: "/images/avatar.png" + }, + { id: 2072, username: "nXqd", avatar_template: "/images/avatar.png" }, + { + id: 4983, + username: "hey_julien", + avatar_template: "/images/avatar.png" + }, + { + id: 3657, + username: "steelmaiden", + avatar_template: "/images/avatar.png" + }, + { id: 2624, username: "BowlingX", avatar_template: "/images/avatar.png" }, + { + id: 8085, + username: "watchmanmonitor", + avatar_template: "/images/avatar.png" + }, + { id: 4612, username: "Iszi", avatar_template: "/images/avatar.png" }, + { + id: 8018, + username: "shivermetimbers", + avatar_template: "/images/avatar.png" + }, + { + id: 6060, + username: "lightyear", + avatar_template: "/images/avatar.png" + }, + { id: 2, username: "neil", avatar_template: "/images/avatar.png" }, + { id: 8037, username: "printec", avatar_template: "/images/avatar.png" }, + { id: 3415, username: "radq", avatar_template: "/images/avatar.png" }, + { + id: 6283, + username: "hrishikesh", + avatar_template: "/images/avatar.png" + }, + { + id: 471, + username: "BhaelOchon", + avatar_template: "/images/avatar.png" + }, + { id: 6548, username: "michaeld", avatar_template: "/images/avatar.png" }, + { + id: 7286, + username: "mrotsnahoj", + avatar_template: "/images/avatar.png" + }, + { id: 3169, username: "dgw", avatar_template: "/images/avatar.png" }, + { + id: 926, + username: "martinnormark", + avatar_template: "/images/avatar.png" + }, + { id: 2003, username: "taylor", avatar_template: "/images/avatar.png" }, + { id: 369, username: "CvX", avatar_template: "/images/avatar.png" }, + { id: 562, username: "nightpool", avatar_template: "/images/avatar.png" }, + { id: 6653, username: "amitfrid", avatar_template: "/images/avatar.png" }, + { + id: 6677, + username: "Tropnevad", + avatar_template: "/images/avatar.png" + }, + { + id: 5048, + username: "SneakySly", + avatar_template: "/images/avatar.png" + }, + { id: 7333, username: "Jong", avatar_template: "/images/avatar.png" }, + { id: 3124, username: "sipp11", avatar_template: "/images/avatar.png" }, + { id: 7604, username: "citkane", avatar_template: "/images/avatar.png" }, + { id: 3929, username: "ScotterC", avatar_template: "/images/avatar.png" }, + { id: 6680, username: "cdman", avatar_template: "/images/avatar.png" }, + { id: 500, username: "aeid", avatar_template: "/images/avatar.png" }, + { id: 8, username: "geek", avatar_template: "/images/avatar.png" }, + { id: 606, username: "Cafeine", avatar_template: "/images/avatar.png" } + ], + topic_list: { + can_create_topic: false, + more_topics_url: "/latest.json?page=1", + draft: null, + draft_key: "new_topic", + draft_sequence: null, + topics: [ + { + id: 11557, + title: "Error after upgrade to 0.9.7.9+", + fancy_title: "Error after upgrade to 0.9.7.9+", + slug: "error-after-upgrade-to-0-9-7-9", + posts_count: 83, + reply_count: 58, + highest_post_number: 85, + image_url: null, + created_at: "2013-12-22T17:12:05.000-05:00", + last_posted_at: "2014-01-16T00:52:30.000-05:00", + bumped: true, + bumped_at: "2014-01-16T00:52:30.000-05:00", + unseen: false, + pinned: true, + excerpt: + "Hi, \n\nI'm using webfaction postgresql specific private instance to run discourse (custom port already configured for discourse 0.9.7.6). \n\nThis is not my first update, but this time i have an error. Impossible to upgrade…", + visible: true, + closed: false, + archived: false, + views: 1230, + like_count: 40, + has_summary: true, + archetype: "regular", + last_poster_username: "stellarhopper", + category_id: 17, + posters: [ + { extras: null, description: "Original Poster", user_id: 7204 }, + { extras: null, description: "Most Posts", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 5481 }, + { extras: null, description: "Frequent Poster", user_id: 6473 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 6973 + } + ] + }, + { + id: 1, + title: "Welcome to meta.discourse.org", + fancy_title: "Welcome to meta.discourse.org", + slug: "welcome-to-meta-discourse-org", + posts_count: 5, + reply_count: 5, + highest_post_number: 23, + image_url: null, + created_at: "2013-01-31T23:52:28.000-05:00", + last_posted_at: "2013-02-07T16:50:41.000-05:00", + bumped: true, + bumped_at: "2013-02-07T11:57:34.000-05:00", + unseen: false, + pinned: true, + excerpt: + "Welcome to meta, the official site for discussing the next-gen open source Discourse forum software. You'll find topics on features, bugs, hosting, development, and general support here. \n\nDiscourse is early beta softwar…", + visible: true, + closed: true, + archived: false, + views: 13792, + like_count: 108, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 17, + posters: [ + { extras: null, description: "Original Poster", user_id: 1 }, + { extras: null, description: "Most Posts", user_id: 19 }, + { extras: null, description: "Frequent Poster", user_id: 14 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11997, + title: "Create topic in the future", + fancy_title: "Create topic in the future", + slug: "create-topic-in-the-future", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-16T12:14:36.000-05:00", + last_posted_at: "2014-01-16T12:14:36.000-05:00", + bumped: false, + bumped_at: "2014-01-16T12:14:36.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 7, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "sil", + category_id: 2, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 1917 + } + ] + }, + { + id: 11996, + title: + "It's really hard to navigate the Create Topic / Reply pane with the keyboard", + fancy_title: + "It’s really hard to navigate the Create Topic / Reply pane with the keyboard", + slug: + "its-really-hard-to-navigate-the-create-topic-reply-pane-with-the-keyboard", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: null, + created_at: "2014-01-16T10:51:36.000-05:00", + last_posted_at: "2014-01-16T11:11:10.000-05:00", + bumped: true, + bumped_at: "2014-01-16T11:11:10.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 12, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 9, + posters: [ + { extras: null, description: "Original Poster", user_id: 7197 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 1995 + } + ] + }, + { + id: 11994, + title: "Cross domain rules, followed?", + fancy_title: "Cross domain rules, followed?", + slug: "cross-domain-rules-followed", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2014-01-16T09:59:15.000-05:00", + last_posted_at: "2014-01-16T09:59:15.000-05:00", + bumped: true, + bumped_at: "2014-01-16T11:04:32.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 15, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "Abhishek_Gupta", + category_id: 1, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 8021 + } + ] + }, + { + id: 11995, + title: "Discourse as a CAS Server", + fancy_title: "Discourse as a CAS Server", + slug: "discourse-as-a-cas-server", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-16T10:15:30.000-05:00", + last_posted_at: "2014-01-16T10:15:31.000-05:00", + bumped: true, + bumped_at: "2014-01-16T10:15:31.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 12, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "PabloC", + category_id: 6, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 2291 + } + ] + }, + { + id: 11993, + title: "How to check the user level via ajax?", + fancy_title: "How to check the user level via ajax?", + slug: "how-to-check-the-user-level-via-ajax", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-16T08:13:09.000-05:00", + last_posted_at: "2014-01-16T08:13:09.000-05:00", + bumped: true, + bumped_at: "2014-01-16T09:20:59.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 13, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "Abhishek_Gupta", + category_id: 7, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 8021 + } + ] + }, + { + id: 9540, + title: "Docker images for Discourse", + fancy_title: "Docker images for Discourse", + slug: "docker-images-for-discourse", + posts_count: 35, + reply_count: 28, + highest_post_number: 36, + image_url: null, + created_at: "2013-09-02T00:07:02.000-04:00", + last_posted_at: "2014-01-16T07:47:18.000-05:00", + bumped: true, + bumped_at: "2014-01-16T07:47:18.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 1322, + like_count: 23, + has_summary: false, + archetype: "regular", + last_poster_username: "illspirit", + category_id: 8, + posters: [ + { extras: null, description: "Original Poster", user_id: 791 }, + { extras: null, description: "Most Posts", user_id: 1580 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 7270 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 6695 + } + ] + }, + { + id: 11957, + title: "Daily Active Users, Monthly Active Users - Statistics Need", + fancy_title: + "Daily Active Users, Monthly Active Users - Statistics Need", + slug: "daily-active-users-monthly-active-users-statistics-need", + posts_count: 8, + reply_count: 4, + highest_post_number: 8, + image_url: null, + created_at: "2014-01-14T13:40:56.000-05:00", + last_posted_at: "2014-01-16T06:46:05.000-05:00", + bumped: true, + bumped_at: "2014-01-16T06:46:05.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 97, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "jeans", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 6929 }, + { extras: null, description: "Most Posts", user_id: 32 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 4385 + } + ] + }, + { + id: 11973, + title: "Pressing Wrench Icon in the Categories section", + fancy_title: "Pressing Wrench Icon in the Categories section", + slug: "pressing-wrench-icon-in-the-categories-section", + posts_count: 6, + reply_count: 3, + highest_post_number: 6, + image_url: "/uploads/default/2907/d8d4e0accd5ee244.png", + created_at: "2014-01-15T05:58:12.000-05:00", + last_posted_at: "2014-01-16T05:15:52.000-05:00", + bumped: true, + bumped_at: "2014-01-16T05:15:52.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 46, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "5an1ty", + category_id: 9, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 7073 + }, + { extras: null, description: "Most Posts", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 6626 } + ] + }, + { + id: 11835, + title: "The Road to Discourse 1.0", + fancy_title: "The Road to Discourse 1.0", + slug: "the-road-to-discourse-1-0", + posts_count: 6, + reply_count: 2, + highest_post_number: 6, + image_url: null, + created_at: "2014-01-08T19:08:44.000-05:00", + last_posted_at: "2014-01-16T04:49:16.000-05:00", + bumped: true, + bumped_at: "2014-01-16T04:49:16.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 421, + like_count: 33, + has_summary: false, + archetype: "regular", + last_poster_username: "iontishina", + category_id: 13, + posters: [ + { extras: null, description: "Original Poster", user_id: 32 }, + { extras: null, description: "Most Posts", user_id: 4457 }, + { extras: null, description: "Frequent Poster", user_id: 4263 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 8134 + } + ] + }, + { + id: 11992, + title: "Specific customization for each category", + fancy_title: "Specific customization for each category", + slug: "specific-customization-for-each-category", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-16T04:04:58.000-05:00", + last_posted_at: "2014-01-16T04:04:58.000-05:00", + bumped: false, + bumped_at: "2014-01-16T04:04:58.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 18, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "nXqd", + category_id: 2, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 2072 + } + ] + }, + { + id: 9214, + title: "Please make category url shorter", + fancy_title: "Please make category url shorter", + slug: "please-make-category-url-shorter", + posts_count: 9, + reply_count: 3, + highest_post_number: 9, + image_url: null, + created_at: "2013-08-20T05:28:17.000-04:00", + last_posted_at: "2014-01-16T04:02:46.000-05:00", + bumped: true, + bumped_at: "2014-01-16T04:02:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 319, + like_count: 13, + has_summary: false, + archetype: "regular", + last_poster_username: "nXqd", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 4983 }, + { extras: null, description: "Most Posts", user_id: 3657 }, + { extras: null, description: "Frequent Poster", user_id: 2624 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 2072 + } + ] + }, + { + id: 11989, + title: "Where to change the email subject prefix", + fancy_title: "Where to change the email subject prefix", + slug: "where-to-change-the-email-subject-prefix", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: "/uploads/default/2919/adbfe0ff90353440.png", + created_at: "2014-01-16T01:03:48.000-05:00", + last_posted_at: "2014-01-16T03:20:09.000-05:00", + bumped: true, + bumped_at: "2014-01-16T03:20:09.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 19, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 6, + posters: [ + { extras: null, description: "Original Poster", user_id: 8085 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 10866, + title: "Header logo overflows the top header area", + fancy_title: "Header logo overflows the top header area", + slug: "header-logo-overflows-the-top-header-area", + posts_count: 4, + reply_count: 1, + highest_post_number: 4, + image_url: null, + created_at: "2013-11-09T03:40:04.000-05:00", + last_posted_at: "2014-01-16T02:27:52.000-05:00", + bumped: true, + bumped_at: "2014-01-16T02:40:47.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 157, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "stellarhopper", + category_id: 6, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 6973 + }, + { extras: null, description: "Most Posts", user_id: 32 } + ] + }, + { + id: 11988, + title: "Could not locate Gemfile error", + fancy_title: "Could not locate Gemfile error", + slug: "could-not-locate-gemfile-error", + posts_count: 7, + reply_count: 3, + highest_post_number: 7, + image_url: null, + created_at: "2014-01-16T00:41:57.000-05:00", + last_posted_at: "2014-01-16T01:20:46.000-05:00", + bumped: true, + bumped_at: "2014-01-16T01:20:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 18, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 6, + posters: [ + { extras: null, description: "Original Poster", user_id: 6973 }, + { extras: "latest", description: "Most Recent Poster", user_id: 1 } + ] + }, + { + id: 6266, + title: "What sort of replies trigger a notice?", + fancy_title: "What sort of replies trigger a notice?", + slug: "what-sort-of-replies-trigger-a-notice", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: null, + created_at: "2013-04-30T17:46:39.000-04:00", + last_posted_at: "2014-01-16T00:52:21.000-05:00", + bumped: true, + bumped_at: "2014-01-16T00:57:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 115, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 17, + posters: [ + { extras: null, description: "Original Poster", user_id: 4612 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11610, + title: "Private replies that only admins can see", + fancy_title: "Private replies that only admins can see", + slug: "private-replies-that-only-admins-can-see", + posts_count: 21, + reply_count: 20, + highest_post_number: 23, + image_url: null, + created_at: "2013-12-26T20:31:10.000-05:00", + last_posted_at: "2014-01-16T00:18:19.000-05:00", + bumped: true, + bumped_at: "2014-01-16T00:18:19.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 206, + like_count: 9, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 8018 }, + { extras: null, description: "Most Posts", user_id: 4263 }, + { extras: null, description: "Frequent Poster", user_id: 6060 }, + { extras: null, description: "Frequent Poster", user_id: 6626 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11888, + title: "Uncategorized topics not allowed, still seeing tag places", + fancy_title: + "Uncategorized topics not allowed, still seeing tag places", + slug: "uncategorized-topics-not-allowed-still-seeing-tag-places", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: null, + created_at: "2014-01-10T19:23:37.000-05:00", + last_posted_at: "2014-01-15T22:41:25.000-05:00", + bumped: true, + bumped_at: "2014-01-15T22:41:25.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 50, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "illspirit", + category_id: 1, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 6695 + }, + { extras: null, description: "Most Posts", user_id: 32 }, + { extras: null, description: "Frequent Poster", user_id: 2 } + ] + }, + { + id: 11985, + title: + "Installation nearly installs on Centos 6.5 with Apache/Phusion", + fancy_title: + "Installation nearly installs on Centos 6.5 with Apache/Phusion", + slug: + "installation-nearly-installs-on-centos-6-5-with-apache-phusion", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-15T19:48:30.000-05:00", + last_posted_at: "2014-01-15T19:48:30.000-05:00", + bumped: false, + bumped_at: "2014-01-15T19:48:30.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 26, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "printec", + category_id: 6, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 8037 + } + ] + }, + { + id: 11981, + title: "Excluding categories from the top view?", + fancy_title: "Excluding categories from the top view?", + slug: "excluding-categories-from-the-top-view", + posts_count: 6, + reply_count: 1, + highest_post_number: 6, + image_url: + "/uploads/default/_optimized/f01/22f/7ea01f77b9_690x355.png", + created_at: "2014-01-15T15:01:37.000-05:00", + last_posted_at: "2014-01-15T18:57:52.000-05:00", + bumped: true, + bumped_at: "2014-01-15T18:57:47.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 43, + like_count: 6, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 3415 }, + { extras: null, description: "Most Posts", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 1995 + } + ] + }, + { + id: 9408, + title: "Different home page for regular vs. new user", + fancy_title: "Different home page for regular vs. new user", + slug: "different-home-page-for-regular-vs-new-user", + posts_count: 25, + reply_count: 17, + highest_post_number: 25, + image_url: null, + created_at: "2013-08-28T09:54:41.000-04:00", + last_posted_at: "2014-01-15T18:33:16.000-05:00", + bumped: true, + bumped_at: "2014-01-15T18:33:16.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 334, + like_count: 21, + has_summary: false, + archetype: "regular", + last_poster_username: "mcwumbly", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 6283 }, + { extras: null, description: "Most Posts", user_id: 32 }, + { extras: null, description: "Frequent Poster", user_id: 1995 }, + { extras: null, description: "Frequent Poster", user_id: 471 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 4263 + } + ] + }, + { + id: 11896, + title: "Problem creating new account", + fancy_title: "Problem creating new account", + slug: "problem-creating-new-account", + posts_count: 11, + reply_count: 2, + highest_post_number: 11, + image_url: null, + created_at: "2014-01-11T09:07:20.000-05:00", + last_posted_at: "2014-01-15T20:50:05.000-05:00", + bumped: true, + bumped_at: "2014-01-15T15:23:32.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 87, + like_count: 6, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 6, + posters: [ + { extras: null, description: "Original Poster", user_id: 6548 }, + { extras: null, description: "Most Posts", user_id: 32 }, + { extras: null, description: "Frequent Poster", user_id: 2 }, + { extras: "latest", description: "Most Recent Poster", user_id: 1 } + ] + }, + { + id: 10511, + title: "External urls should open in new tab", + fancy_title: "External urls should open in new tab", + slug: "external-urls-should-open-in-new-tab", + posts_count: 7, + reply_count: 3, + highest_post_number: 7, + image_url: null, + created_at: "2013-10-20T14:54:27.000-04:00", + last_posted_at: "2014-01-15T14:02:11.000-05:00", + bumped: true, + bumped_at: "2014-01-15T14:01:55.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 242, + like_count: 10, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 7286 }, + { extras: null, description: "Most Posts", user_id: 3169 }, + { extras: null, description: "Frequent Poster", user_id: 4263 }, + { extras: null, description: "Frequent Poster", user_id: 6626 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 1589, + title: "Keyboard shortcuts?", + fancy_title: "Keyboard shortcuts?", + slug: "keyboard-shortcuts", + posts_count: 19, + reply_count: 10, + highest_post_number: 20, + image_url: null, + created_at: "2013-02-06T14:05:01.000-05:00", + last_posted_at: "2014-01-15T13:52:45.000-05:00", + bumped: true, + bumped_at: "2014-01-15T13:52:45.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 754, + like_count: 31, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 926 }, + { extras: null, description: "Most Posts", user_id: 2003 }, + { extras: null, description: "Frequent Poster", user_id: 369 }, + { extras: null, description: "Frequent Poster", user_id: 562 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11763, + title: "Google AdSense plugin is now available", + fancy_title: "Google AdSense plugin is now available", + slug: "google-adsense-plugin-is-now-available", + posts_count: 7, + reply_count: 2, + highest_post_number: 7, + image_url: + "/uploads/default/_optimized/66d/cf0/d69e6709fe_496x500.PNG", + created_at: "2014-01-05T14:28:58.000-05:00", + last_posted_at: "2014-01-15T13:32:35.000-05:00", + bumped: true, + bumped_at: "2014-01-15T13:32:35.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 213, + like_count: 14, + has_summary: false, + archetype: "regular", + last_poster_username: "michaeld", + category_id: 5, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 6548 + }, + { extras: null, description: "Most Posts", user_id: 6653 }, + { extras: null, description: "Frequent Poster", user_id: 6677 }, + { extras: null, description: "Frequent Poster", user_id: 5048 }, + { extras: null, description: "Frequent Poster", user_id: 7333 } + ] + }, + { + id: 9151, + title: "Apple touch icon doesn't show if there is no sub domain", + fancy_title: + "Apple touch icon doesn’t show if there is no sub domain", + slug: "apple-touch-icon-doesnt-show-if-there-is-no-sub-domain", + posts_count: 7, + reply_count: 4, + highest_post_number: 7, + image_url: null, + created_at: "2013-08-16T18:16:53.000-04:00", + last_posted_at: "2014-01-15T17:10:18.000-05:00", + bumped: true, + bumped_at: "2014-01-15T13:19:22.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 188, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 3124 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 11977, + title: "Show subcategory topics in categories list summary", + fancy_title: "Show subcategory topics in categories list summary", + slug: "show-subcategory-topics-in-categories-list-summary", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: + "/uploads/default/_optimized/084/4e4/8af88c0839_571x500.png", + created_at: "2014-01-15T12:09:49.000-05:00", + last_posted_at: "2014-01-15T12:50:04.000-05:00", + bumped: true, + bumped_at: "2014-01-15T12:50:04.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 32, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 7604 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 10201, + title: "How To override an existing handlebars template from plugin", + fancy_title: + "How To override an existing handlebars template from plugin", + slug: "how-to-override-an-existing-handlebars-template-from-plugin", + posts_count: 6, + reply_count: 1, + highest_post_number: 6, + image_url: null, + created_at: "2013-10-04T10:44:33.000-04:00", + last_posted_at: "2014-01-15T12:35:01.000-05:00", + bumped: true, + bumped_at: "2014-01-15T12:34:58.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 325, + like_count: 6, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 3929 }, + { extras: null, description: "Most Posts", user_id: 3415 }, + { extras: null, description: "Frequent Poster", user_id: 6680 }, + { extras: null, description: "Frequent Poster", user_id: 2 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 1995 + } + ] + }, + { + id: 531, + title: "Discourse and Wordpress Integration", + fancy_title: "Discourse and Wordpress Integration", + slug: "discourse-and-wordpress-integration", + posts_count: 76, + reply_count: 64, + highest_post_number: 78, + image_url: null, + created_at: "2013-02-05T18:56:37.000-05:00", + last_posted_at: "2014-01-15T11:56:54.000-05:00", + bumped: true, + bumped_at: "2014-01-15T11:56:54.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 3809, + like_count: 84, + has_summary: true, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 5, + posters: [ + { extras: null, description: "Original Poster", user_id: 500 }, + { extras: null, description: "Most Posts", user_id: 8 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 606 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + } + ] + } + }, + "/categories.json": { + category_list: { + can_create_category: false, + can_create_topic: false, + draft: null, + draft_key: "new_topic", + draft_sequence: null, + categories: [ + { + id: 1, + name: "bug", + color: "e9dd00", + text_color: "000000", + slug: "bug", + topic_count: 660, + description: + "Bug reports on Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.", + topic_url: "/t/category-definition-for-bug/2", + read_restricted: false, + permission: null, + post_count: 4318, + topics_day: 0, + topics_week: 18, + topics_month: 54, + topics_year: 658, + posts_day: 0, + posts_week: 330, + posts_month: 574, + posts_year: 4319, + description_excerpt: + "Bug reports on Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.", + featured_user_ids: [8021, 32, 6695, 2, 1995], + topics: [ + { + id: 11994, + title: "Cross domain rules, followed?", + fancy_title: "Cross domain rules, followed?", + slug: "cross-domain-rules-followed", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2014-01-16T09:59:15.000-05:00", + last_posted_at: "2014-01-16T09:59:15.000-05:00", + bumped: true, + bumped_at: "2014-01-16T11:04:32.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 8021, + username: "Abhishek_Gupta", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11888, + title: + "Uncategorized topics not allowed, still seeing tag places", + fancy_title: + "Uncategorized topics not allowed, still seeing tag places", + slug: "uncategorized-topics-not-allowed-still-seeing-tag-places", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: null, + created_at: "2014-01-10T19:23:37.000-05:00", + last_posted_at: "2014-01-15T22:41:25.000-05:00", + bumped: true, + bumped_at: "2014-01-15T22:41:25.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 6695, + username: "illspirit", + avatar_template: "/images/avatar.png" + } + }, + { + id: 9151, + title: "Apple touch icon doesn't show if there is no sub domain", + fancy_title: + "Apple touch icon doesn’t show if there is no sub domain", + slug: "apple-touch-icon-doesnt-show-if-there-is-no-sub-domain", + posts_count: 7, + reply_count: 4, + highest_post_number: 7, + image_url: null, + created_at: "2013-08-16T18:16:53.000-04:00", + last_posted_at: "2014-01-15T17:10:18.000-05:00", + bumped: true, + bumped_at: "2014-01-15T13:19:22.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 2, + name: "feature", + color: "0E76BD", + text_color: "FFFFFF", + slug: "feature", + topic_count: 727, + description: + "Discussion about features or potential features of Discourse: how they work, why they work, etc.", + topic_url: "/t/category-definition-for-feature/11", + read_restricted: false, + permission: null, + post_count: 6186, + topics_day: 0, + topics_week: 17, + topics_month: 46, + topics_year: 725, + posts_day: 0, + posts_week: 180, + posts_month: 468, + posts_year: 6187, + description_excerpt: + "Discussion about features or potential features of Discourse: how they work, why they work, etc.", + featured_user_ids: [1917, 4385, 2072, 32, 4263], + topics: [ + { + id: 11997, + title: "Create topic in the future", + fancy_title: "Create topic in the future", + slug: "create-topic-in-the-future", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-16T12:14:36.000-05:00", + last_posted_at: "2014-01-16T12:14:36.000-05:00", + bumped: false, + bumped_at: "2014-01-16T12:14:36.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 1917, + username: "sil", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11957, + title: + "Daily Active Users, Monthly Active Users - Statistics Need", + fancy_title: + "Daily Active Users, Monthly Active Users - Statistics Need", + slug: "daily-active-users-monthly-active-users-statistics-need", + posts_count: 8, + reply_count: 4, + highest_post_number: 8, + image_url: null, + created_at: "2014-01-14T13:40:56.000-05:00", + last_posted_at: "2014-01-16T06:46:05.000-05:00", + bumped: true, + bumped_at: "2014-01-16T06:46:05.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 4385, + username: "jeans", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11992, + title: "Specific customization for each category", + fancy_title: "Specific customization for each category", + slug: "specific-customization-for-each-category", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-16T04:04:58.000-05:00", + last_posted_at: "2014-01-16T04:04:58.000-05:00", + bumped: false, + bumped_at: "2014-01-16T04:04:58.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 2072, + username: "nXqd", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 6, + name: "support", + color: "b99", + text_color: "FFFFFF", + slug: "support", + topic_count: 782, + description: + "Support on configuring, using, and installing Discourse. Not for software development related topics, but for admins and end users configuring and using Discourse.", + topic_url: "/t/category-definition-for-support/389", + read_restricted: false, + permission: null, + post_count: 5396, + topics_day: 0, + topics_week: 16, + topics_month: 67, + topics_year: 779, + posts_day: 0, + posts_week: 122, + posts_month: 481, + posts_year: 5400, + description_excerpt: + "Support on configuring, using, and installing Discourse. Not for software development related topics, but for admins and end users configuring and using Discourse.", + featured_user_ids: [2291, 32, 6973, 1, 8085], + topics: [ + { + id: 11995, + title: "Discourse as a CAS Server", + fancy_title: "Discourse as a CAS Server", + slug: "discourse-as-a-cas-server", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-16T10:15:30.000-05:00", + last_posted_at: "2014-01-16T10:15:31.000-05:00", + bumped: true, + bumped_at: "2014-01-16T10:15:31.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 2291, + username: "PabloC", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11989, + title: "Where to change the email subject prefix", + fancy_title: "Where to change the email subject prefix", + slug: "where-to-change-the-email-subject-prefix", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: "/uploads/default/2919/adbfe0ff90353440.png", + created_at: "2014-01-16T01:03:48.000-05:00", + last_posted_at: "2014-01-16T03:20:09.000-05:00", + bumped: true, + bumped_at: "2014-01-16T03:20:09.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + }, + { + id: 10866, + title: "Header logo overflows the top header area", + fancy_title: "Header logo overflows the top header area", + slug: "header-logo-overflows-the-top-header-area", + posts_count: 4, + reply_count: 1, + highest_post_number: 4, + image_url: null, + created_at: "2013-11-09T03:40:04.000-05:00", + last_posted_at: "2014-01-16T02:27:52.000-05:00", + bumped: true, + bumped_at: "2014-01-16T02:40:47.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 6973, + username: "stellarhopper", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 7, + name: "dev", + color: "000", + text_color: "FFFFFF", + slug: "dev", + topic_count: 284, + description: + "This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.", + topic_url: "/t/category-definition-for-dev/1026", + read_restricted: false, + permission: null, + post_count: 2352, + topics_day: 0, + topics_week: 3, + topics_month: 19, + topics_year: 284, + posts_day: 0, + posts_week: 37, + posts_month: 150, + posts_year: 2353, + description_excerpt: + "This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.", + featured_user_ids: [8021, 1995, 5428, 8208, 7995], + topics: [ + { + id: 3823, + title: "So, you want to help out with Discourse", + fancy_title: "So, you want to help out with Discourse", + slug: "so-you-want-to-help-out-with-discourse", + posts_count: 22, + reply_count: 28, + highest_post_number: 56, + image_url: null, + created_at: "2013-02-23T00:46:11.000-05:00", + last_posted_at: "2014-01-12T21:33:12.000-05:00", + bumped: true, + bumped_at: "2014-01-12T21:33:12.000-05:00", + unseen: false, + pinned: true, + excerpt: + "People are wondering, how it is they can help out with Discourse. \n\nWe have seen some chattering both here and on Github. \n\nI wanted to create a topic @eviltrout , @codinghorror and myself can keep up to date with clear…", + visible: true, + closed: false, + archived: false, + last_poster: { + id: 7995, + username: "Hunter", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11993, + title: "How to check the user level via ajax?", + fancy_title: "How to check the user level via ajax?", + slug: "how-to-check-the-user-level-via-ajax", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-16T08:13:09.000-05:00", + last_posted_at: "2014-01-16T08:13:09.000-05:00", + bumped: true, + bumped_at: "2014-01-16T09:20:59.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 8021, + username: "Abhishek_Gupta", + avatar_template: "/images/avatar.png" + } + }, + { + id: 10201, + title: + "How To override an existing handlebars template from plugin", + fancy_title: + "How To override an existing handlebars template from plugin", + slug: + "how-to-override-an-existing-handlebars-template-from-plugin", + posts_count: 6, + reply_count: 1, + highest_post_number: 6, + image_url: null, + created_at: "2013-10-04T10:44:33.000-04:00", + last_posted_at: "2014-01-15T12:35:01.000-05:00", + bumped: true, + bumped_at: "2014-01-15T12:34:58.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + last_poster: { + id: 1995, + username: "zogstrip", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 9, + name: "ux", + color: "5F497A", + text_color: "FFFFFF", + slug: "ux", + topic_count: 184, + description: + "Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", + topic_url: "/t/category-definition-for-ux/2628", + read_restricted: false, + permission: null, + post_count: 1511, + topics_day: 0, + topics_week: 3, + topics_month: 10, + topics_year: 183, + posts_day: 0, + posts_week: 34, + posts_month: 117, + posts_year: 1511, + description_excerpt: + "Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", + featured_user_ids: [1995, 7197, 7073, 1, 6626], + topics: [ + { + id: 11996, + title: + "It's really hard to navigate the Create Topic / Reply pane with the keyboard", + fancy_title: + "It’s really hard to navigate the Create Topic / Reply pane with the keyboard", + slug: + "its-really-hard-to-navigate-the-create-topic-reply-pane-with-the-keyboard", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: null, + created_at: "2014-01-16T10:51:36.000-05:00", + last_posted_at: "2014-01-16T11:11:10.000-05:00", + bumped: true, + bumped_at: "2014-01-16T11:11:10.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 1995, + username: "zogstrip", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11973, + title: "Pressing Wrench Icon in the Categories section", + fancy_title: "Pressing Wrench Icon in the Categories section", + slug: "pressing-wrench-icon-in-the-categories-section", + posts_count: 6, + reply_count: 3, + highest_post_number: 6, + image_url: "/uploads/default/2907/d8d4e0accd5ee244.png", + created_at: "2014-01-15T05:58:12.000-05:00", + last_posted_at: "2014-01-16T05:15:52.000-05:00", + bumped: true, + bumped_at: "2014-01-16T05:15:52.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 7073, + username: "5an1ty", + avatar_template: "/images/avatar.png" + } + }, + { + id: 5542, + title: "Title character requirements not very visible", + fancy_title: "Title character requirements not very visible", + slug: "title-character-requirements-not-very-visible", + posts_count: 24, + reply_count: 11, + highest_post_number: 24, + image_url: null, + created_at: "2013-04-02T20:09:59.000-04:00", + last_posted_at: "2014-01-15T05:26:07.000-05:00", + bumped: true, + bumped_at: "2014-01-15T05:26:04.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + last_poster: { + id: 1995, + username: "zogstrip", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 5, + name: "extensibility", + color: "FE8432", + text_color: "FFFFFF", + slug: "extensibility", + topic_count: 102, + description: + "Topics about extending the functionality of Discourse with plugins, themes, add-ons, or other mechanisms for extensibility. ", + topic_url: "/t/category-definition-for-extensibility/28", + read_restricted: false, + permission: null, + post_count: 964, + topics_day: 0, + topics_week: 2, + topics_month: 18, + topics_year: 102, + posts_day: 0, + posts_week: 17, + posts_month: 76, + posts_year: 964, + description_excerpt: + "Topics about extending the functionality of Discourse with plugins, themes, add-ons, or other mechanisms for extensibility.", + featured_user_ids: [6548, 32, 8202, 6677, 7333], + topics: [ + { + id: 11763, + title: "Google AdSense plugin is now available", + fancy_title: "Google AdSense plugin is now available", + slug: "google-adsense-plugin-is-now-available", + posts_count: 7, + reply_count: 2, + highest_post_number: 7, + image_url: + "/uploads/default/_optimized/66d/cf0/d69e6709fe_496x500.PNG", + created_at: "2014-01-05T14:28:58.000-05:00", + last_posted_at: "2014-01-15T13:32:35.000-05:00", + bumped: true, + bumped_at: "2014-01-15T13:32:35.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 6548, + username: "michaeld", + avatar_template: "/images/avatar.png" + } + }, + { + id: 531, + title: "Discourse and Wordpress Integration", + fancy_title: "Discourse and Wordpress Integration", + slug: "discourse-and-wordpress-integration", + posts_count: 76, + reply_count: 64, + highest_post_number: 78, + image_url: null, + created_at: "2013-02-05T18:56:37.000-05:00", + last_posted_at: "2014-01-15T11:56:54.000-05:00", + bumped: true, + bumped_at: "2014-01-15T11:56:54.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11965, + title: + "In your opinion, what is the best wiki engine to be associated with discourse?", + fancy_title: + "In your opinion, what is the best wiki engine to be associated with discourse?", + slug: + "in-your-opinion-what-is-the-best-wiki-engine-to-be-associated-with-discourse", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-14T19:27:06.000-05:00", + last_posted_at: "2014-01-14T19:27:06.000-05:00", + bumped: false, + bumped_at: "2014-01-14T19:27:06.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 8202, + username: "Matthieu", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 8, + name: "hosting", + color: "74CCED", + text_color: "FFFFFF", + slug: "hosting", + topic_count: 69, + description: + "Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.", + topic_url: "/t/category-definition-for-hosting/2626", + read_restricted: false, + permission: null, + post_count: 664, + topics_day: 0, + topics_week: 2, + topics_month: 2, + topics_year: 69, + posts_day: 0, + posts_week: 15, + posts_month: 35, + posts_year: 664, + description_excerpt: + "Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.", + featured_user_ids: [6695, 1, 6018, 1580, 7030], + topics: [ + { + id: 9540, + title: "Docker images for Discourse", + fancy_title: "Docker images for Discourse", + slug: "docker-images-for-discourse", + posts_count: 35, + reply_count: 28, + highest_post_number: 36, + image_url: null, + created_at: "2013-09-02T00:07:02.000-04:00", + last_posted_at: "2014-01-16T07:47:18.000-05:00", + bumped: true, + bumped_at: "2014-01-16T07:47:18.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 6695, + username: "illspirit", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11971, + title: + "Installing Discourse on Ubuntu 12.04 with Parallels Plesk and Apache", + fancy_title: + "Installing Discourse on Ubuntu 12.04 with Parallels Plesk and Apache", + slug: + "installing-discourse-on-ubuntu-12-04-with-parallels-plesk-and-apache", + posts_count: 3, + reply_count: 1, + highest_post_number: 3, + image_url: null, + created_at: "2014-01-15T04:23:38.000-05:00", + last_posted_at: "2014-01-15T04:47:20.000-05:00", + bumped: true, + bumped_at: "2014-01-15T04:47:20.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 7030, + username: "naabster", + avatar_template: "/images/avatar.png" + } + }, + { + id: 10844, + title: "Discourse in a Docker container", + fancy_title: "Discourse in a Docker container", + slug: "discourse-in-a-docker-container", + posts_count: 12, + reply_count: 8, + highest_post_number: 12, + image_url: null, + created_at: "2013-11-07T19:12:22.000-05:00", + last_posted_at: "2014-01-11T14:43:53.000-05:00", + bumped: true, + bumped_at: "2014-01-11T14:43:53.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 1, + username: "sam", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 17, + name: "uncategorized", + color: "AB9364", + text_color: "FFFFFF", + slug: "uncategorized", + topic_count: 229, + description: "", + topic_url: null, + read_restricted: false, + permission: null, + post_count: 2138, + topics_day: 0, + topics_week: 0, + topics_month: 9, + topics_year: 229, + posts_day: 1, + posts_week: 11, + posts_month: 183, + posts_year: 2138, + description_excerpt: "", + is_uncategorized: true, + featured_user_ids: [6973, 32, 1, 1995, 7073], + topics: [ + { + id: 1, + title: "Welcome to meta.discourse.org", + fancy_title: "Welcome to meta.discourse.org", + slug: "welcome-to-meta-discourse-org", + posts_count: 5, + reply_count: 5, + highest_post_number: 23, + image_url: null, + created_at: "2013-01-31T23:52:28.000-05:00", + last_posted_at: "2013-02-07T16:50:41.000-05:00", + bumped: true, + bumped_at: "2013-02-07T11:57:34.000-05:00", + unseen: false, + pinned: true, + excerpt: + "Welcome to meta, the official site for discussing the next-gen open source Discourse forum software. You'll find topics on features, bugs, hosting, development, and general support here. \n\nDiscourse is early beta softwar…", + visible: true, + closed: true, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11557, + title: "Error after upgrade to 0.9.7.9+", + fancy_title: "Error after upgrade to 0.9.7.9+", + slug: "error-after-upgrade-to-0-9-7-9", + posts_count: 83, + reply_count: 58, + highest_post_number: 85, + image_url: null, + created_at: "2013-12-22T17:12:05.000-05:00", + last_posted_at: "2014-01-16T00:52:30.000-05:00", + bumped: true, + bumped_at: "2014-01-16T00:52:30.000-05:00", + unseen: false, + pinned: true, + excerpt: + "Hi, \n\nI'm using webfaction postgresql specific private instance to run discourse (custom port already configured for discourse 0.9.7.6). \n\nThis is not my first update, but this time i have an error. Impossible to upgrade…", + visible: true, + closed: false, + archived: false, + last_poster: { + id: 6973, + username: "stellarhopper", + avatar_template: "/images/avatar.png" + } + }, + { + id: 6266, + title: "What sort of replies trigger a notice?", + fancy_title: "What sort of replies trigger a notice?", + slug: "what-sort-of-replies-trigger-a-notice", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: null, + created_at: "2013-04-30T17:46:39.000-04:00", + last_posted_at: "2014-01-16T00:52:21.000-05:00", + bumped: true, + bumped_at: "2014-01-16T00:57:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 11, + name: "login", + color: "edb400", + text_color: "FFFFFF", + slug: "login", + topic_count: 27, + description: + "Topics about logging in to Discourse, using any standard third party provider (Twitter, Facebook, Google), traditional username and password, or with a custom plugin.", + topic_url: "/t/category-definition-for-login/2828", + read_restricted: false, + permission: null, + post_count: 200, + topics_day: 0, + topics_week: 1, + topics_month: 1, + topics_year: 27, + posts_day: 0, + posts_week: 10, + posts_month: 27, + posts_year: 200, + description_excerpt: + "Topics about logging in to Discourse, using any standard third party provider (Twitter, Facebook, Google), traditional username and password, or with a custom plugin.", + featured_user_ids: [8163, 19, 7796, 32, 8024], + topics: [ + { + id: 11959, + title: "Get current user information via JSON", + fancy_title: "Get current user information via JSON", + slug: "get-current-user-information-via-json", + posts_count: 3, + reply_count: 1, + highest_post_number: 3, + image_url: null, + created_at: "2014-01-14T15:05:34.000-05:00", + last_posted_at: "2014-01-14T16:43:28.000-05:00", + bumped: true, + bumped_at: "2014-01-14T16:43:28.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 8163, + username: "znation", + avatar_template: "/images/avatar.png" + } + }, + { + id: 6242, + title: + "Allow authentication via multiple services on one account", + fancy_title: + "Allow authentication via multiple services on one account", + slug: "allow-authentication-via-multiple-services-on-one-account", + posts_count: 34, + reply_count: 27, + highest_post_number: 34, + image_url: null, + created_at: "2013-04-29T18:51:52.000-04:00", + last_posted_at: "2014-01-14T00:25:42.000-05:00", + bumped: true, + bumped_at: "2014-01-14T00:25:42.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 7796, + username: "almereyda", + avatar_template: "/images/avatar.png" + } + }, + { + id: 4738, + title: "Login support for browser password managers", + fancy_title: "Login support for browser password managers", + slug: "login-support-for-browser-password-managers", + posts_count: 6, + reply_count: 2, + highest_post_number: 6, + image_url: null, + created_at: "2013-03-13T17:55:29.000-04:00", + last_posted_at: "2014-01-13T14:21:34.000-05:00", + bumped: true, + bumped_at: "2014-01-13T14:21:34.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 3, + name: "meta", + color: "aaa", + text_color: "FFFFFF", + slug: "meta", + topic_count: 79, + description: + "Discussion about meta.discourse.org itself, the organization of this forum about Discourse, how it works, and how we can improve this site.", + topic_url: "/t/category-definition-for-meta/24", + read_restricted: false, + permission: null, + post_count: 695, + topics_day: 0, + topics_week: 1, + topics_month: 3, + topics_year: 79, + posts_day: 0, + posts_week: 4, + posts_month: 18, + posts_year: 696, + description_excerpt: + "Discussion about meta.discourse.org itself, the organization of this forum about Discourse, how it works, and how we can improve this site.", + featured_user_ids: [19, 8085, 32, 5174, 4534], + topics: [ + { + id: 5249, + title: 'What is "Meta"?', + fancy_title: "What is “Meta”?", + slug: "what-is-meta", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: null, + created_at: "2013-03-25T18:00:52.000-04:00", + last_posted_at: "2013-03-25T18:00:56.000-04:00", + bumped: false, + bumped_at: "2013-03-25T18:00:52.000-04:00", + unseen: false, + pinned: true, + excerpt: + "Meta means discussion of the discussion itself instead of the actual topic of the discussion. \n\nWhy do we need a meta category?\n\nMeta is where communities come together to decide who they are and what they are about. \n…", + visible: true, + closed: false, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11943, + title: "How far to take user documentation?", + fancy_title: "How far to take user documentation?", + slug: "how-far-to-take-user-documentation", + posts_count: 4, + reply_count: 2, + highest_post_number: 4, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2014-01-13T19:21:26.000-05:00", + last_posted_at: "2014-01-14T14:19:46.000-05:00", + bumped: true, + bumped_at: "2014-01-14T14:19:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 19, + username: "eviltrout", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11822, + title: "Search engine traffic share and level to Discourse", + fancy_title: "Search engine traffic share and level to Discourse", + slug: "search-engine-traffic-share-and-level-to-discourse", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: null, + created_at: "2014-01-08T01:54:56.000-05:00", + last_posted_at: "2014-01-08T02:21:25.000-05:00", + bumped: true, + bumped_at: "2014-01-08T02:21:25.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 12, + name: "discourse hub", + color: "b2c79f", + text_color: "FFFFFF", + slug: "discourse-hub", + topic_count: 4, + description: + "Topics about current or future Discourse Hub functionality at discourse.org including nickname registration, global user pages, and the site directory.", + topic_url: "/t/category-definition-for-discourse-hub/3038", + read_restricted: false, + permission: null, + post_count: 121, + topics_day: 0, + topics_week: 0, + topics_month: 0, + topics_year: 4, + posts_day: 0, + posts_week: 3, + posts_month: 3, + posts_year: 121, + description_excerpt: + "Topics about current or future Discourse Hub functionality at discourse.org including nickname registration, global user pages, and the site directory.", + featured_user_ids: [2, 32, 2316, 6695, 4457], + topics: [ + { + id: 6547, + title: "Where to get discourse_org_access_key?", + fancy_title: "Where to get discourse_org_access_key?", + slug: "where-to-get-discourse-org-access-key", + posts_count: 13, + reply_count: 4, + highest_post_number: 13, + image_url: null, + created_at: "2013-05-10T22:06:08.000-04:00", + last_posted_at: "2014-01-13T11:38:15.000-05:00", + bumped: true, + bumped_at: "2014-01-13T11:38:15.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 2, + username: "neil", + avatar_template: "/images/avatar.png" + } + }, + { + id: 2544, + title: "Discourse central hub questions", + fancy_title: "Discourse central hub questions", + slug: "discourse-central-hub-questions", + posts_count: 51, + reply_count: 44, + highest_post_number: 52, + image_url: null, + created_at: "2013-02-09T04:28:21.000-05:00", + last_posted_at: "2013-09-19T13:36:49.000-04:00", + bumped: true, + bumped_at: "2013-09-19T14:04:08.000-04:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 2128, + username: "ultimape", + avatar_template: "/images/avatar.png" + } + }, + { + id: 424, + title: "What are the 'consequences' of changing your name?", + fancy_title: + "What are the ‘consequences’ of changing your name?", + slug: "what-are-the-consequences-of-changing-your-name", + posts_count: 35, + reply_count: 36, + highest_post_number: 43, + image_url: null, + created_at: "2013-02-05T17:37:52.000-05:00", + last_posted_at: "2013-09-19T13:55:11.000-04:00", + bumped: true, + bumped_at: "2013-09-19T13:55:11.000-04:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 2128, + username: "ultimape", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 13, + name: "blog", + color: "ED207B", + text_color: "FFFFFF", + slug: "blog", + topic_count: 14, + description: + "Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.", + topic_url: "/t/category-definition-for-blog/5250", + read_restricted: false, + permission: null, + post_count: 206, + topics_day: 0, + topics_week: 0, + topics_month: 1, + topics_year: 14, + posts_day: 0, + posts_week: 2, + posts_month: 11, + posts_year: 206, + description_excerpt: + "Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.", + featured_user_ids: [8134, 32, 4457, 4263, 1995], + topics: [ + { + id: 11835, + title: "The Road to Discourse 1.0", + fancy_title: "The Road to Discourse 1.0", + slug: "the-road-to-discourse-1-0", + posts_count: 6, + reply_count: 2, + highest_post_number: 6, + image_url: null, + created_at: "2014-01-08T19:08:44.000-05:00", + last_posted_at: "2014-01-16T04:49:16.000-05:00", + bumped: true, + bumped_at: "2014-01-16T04:49:16.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 8134, + username: "iontishina", + avatar_template: "/images/avatar.png" + } + }, + { + id: 5751, + title: "Discourse as Your First Rails App", + fancy_title: "Discourse as Your First Rails App", + slug: "discourse-as-your-first-rails-app", + posts_count: 62, + reply_count: 43, + highest_post_number: 71, + image_url: null, + created_at: "2013-04-09T19:08:33.000-04:00", + last_posted_at: "2013-12-19T18:27:37.000-05:00", + bumped: true, + bumped_at: "2013-12-19T18:27:37.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 1995, + username: "zogstrip", + avatar_template: "/images/avatar.png" + } + }, + { + id: 5898, + title: "The Discourse Servers", + fancy_title: "The Discourse Servers", + slug: "the-discourse-servers", + posts_count: 42, + reply_count: 32, + highest_post_number: 42, + image_url: null, + created_at: "2013-04-15T15:19:09.000-04:00", + last_posted_at: "2013-11-29T15:14:35.000-05:00", + bumped: true, + bumped_at: "2013-11-29T15:14:35.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 6626, + username: "riking", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 4, + name: "faq", + color: "33b", + text_color: "FFFFFF", + slug: "faq", + topic_count: 49, + description: + "Topics that come up very often when discussing Discourse will eventually be classified into this Frequently Asked Questions category. Should only be added to popular topics.", + topic_url: "/t/category-definition-for-faq/25", + read_restricted: false, + permission: null, + post_count: 450, + topics_day: 0, + topics_week: 0, + topics_month: 0, + topics_year: 49, + posts_day: 0, + posts_week: 1, + posts_month: 10, + posts_year: 450, + description_excerpt: + "Topics that come up very often when discussing Discourse will eventually be classified into this Frequently Asked Questions category. Should only be added to popular topics.", + featured_user_ids: [32, 8047, 7483, 2, 6626], + topics: [ + { + id: 5372, + title: + "UX confusion (or me confusion) is it possible to edit old posts or only your most recent post in a topic?", + fancy_title: + "UX confusion (or me confusion) is it possible to edit old posts or only your most recent post in a topic?", + slug: + "ux-confusion-or-me-confusion-is-it-possible-to-edit-old-posts-or-only-your-most-recent-post-in-a-topic", + posts_count: 3, + reply_count: 0, + highest_post_number: 3, + image_url: null, + created_at: "2013-03-28T22:25:57.000-04:00", + last_posted_at: "2014-01-13T13:44:39.000-05:00", + bumped: true, + bumped_at: "2014-01-13T13:44:39.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + }, + { + id: 9631, + title: + "All the options to deploy Discourse with their relative pros and cons", + fancy_title: + "All the options to deploy Discourse with their relative pros and cons", + slug: + "all-the-options-to-deploy-discourse-with-their-relative-pros-and-cons", + posts_count: 14, + reply_count: 7, + highest_post_number: 15, + image_url: null, + created_at: "2013-09-06T03:55:09.000-04:00", + last_posted_at: "2013-09-26T18:49:04.000-04:00", + bumped: true, + bumped_at: "2013-12-30T12:32:59.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 3929, + username: "ScotterC", + avatar_template: "/images/avatar.png" + } + }, + { + id: 4325, + title: "How to delete a user?", + fancy_title: "How to delete a user?", + slug: "how-to-delete-a-user", + posts_count: 31, + reply_count: 23, + highest_post_number: 33, + image_url: null, + created_at: "2013-03-01T23:18:55.000-05:00", + last_posted_at: "2013-12-20T21:26:06.000-05:00", + bumped: true, + bumped_at: "2013-12-20T21:26:06.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 14, + name: "marketplace", + color: "8C6238", + text_color: "FFFFFF", + slug: "marketplace", + topic_count: 24, + description: + "About commercial Discourse related stuff: jobs or paid gigs, plugins, themes, hosting, etc.", + topic_url: "/t/category-definition-for-marketplace/5425", + read_restricted: false, + permission: null, + post_count: 106, + topics_day: 0, + topics_week: 1, + topics_month: 3, + topics_year: 24, + posts_day: 0, + posts_week: 1, + posts_month: 7, + posts_year: 106, + description_excerpt: + "About commercial Discourse related stuff: jobs or paid gigs, plugins, themes, hosting, etc.", + featured_user_ids: [6548, 32, 5548, 2291, 4755], + topics: [ + { + id: 11866, + title: "DiscourseHosting is now accepting BTC payments", + fancy_title: "DiscourseHosting is now accepting BTC payments", + slug: "discoursehosting-is-now-accepting-btc-payments", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-10T10:17:28.000-05:00", + last_posted_at: "2014-01-10T10:17:28.000-05:00", + bumped: false, + bumped_at: "2014-01-10T10:17:28.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 6548, + username: "michaeld", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11571, + title: "Looking for a developer for Discourse Customization", + fancy_title: + "Looking for a developer for Discourse Customization", + slug: "looking-for-a-developer-for-discourse-customization", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: null, + created_at: "2013-12-23T20:54:04.000-05:00", + last_posted_at: "2013-12-24T13:12:17.000-05:00", + bumped: true, + bumped_at: "2013-12-30T16:36:17.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 2291, + username: "PabloC", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11594, + title: + "Need someone to fix a topic in my discourse install that won't load for moderators. Will pay", + fancy_title: + "Need someone to fix a topic in my discourse install that won’t load for moderators. Will pay", + slug: + "need-someone-to-fix-a-topic-in-my-discourse-install-that-wont-load-for-moderators-will-pay", + posts_count: 4, + reply_count: 1, + highest_post_number: 4, + image_url: null, + created_at: "2013-12-25T10:25:57.000-05:00", + last_posted_at: "2013-12-26T17:01:41.000-05:00", + bumped: true, + bumped_at: "2013-12-25T17:01:15.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + last_poster: { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + } + } + ] + }, + { + id: 10, + name: "howto", + color: "76923C", + text_color: "FFFFFF", + slug: "howto", + topic_count: 58, + description: + "Tutorial topics that describe how to set up, configure, or install Discourse using a specific platform or environment. Topics in this category may only be created by trust level 2 and up. ", + topic_url: "/t/category-definition-for-howto/2629", + read_restricted: false, + permission: null, + post_count: 677, + topics_day: 0, + topics_week: 0, + topics_month: 1, + topics_year: 58, + posts_day: 0, + posts_week: 0, + posts_month: 13, + posts_year: 675, + description_excerpt: + "Tutorial topics that describe how to set up, configure, or install Discourse using a specific platform or environment. Topics in this category may only be created by trust level 2 and up.", + featured_user_ids: [7984, 4457, 1995, 6018, 5351], + topics: [ + { + id: 7582, + title: + "Twitter login with Passenger + Varnish - quick lessons learned", + fancy_title: + "Twitter login with Passenger + Varnish - quick lessons learned", + slug: + "twitter-login-with-passenger-varnish-quick-lessons-learned", + posts_count: 9, + reply_count: 3, + highest_post_number: 9, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2013-06-17T19:46:31.000-04:00", + last_posted_at: "2013-12-31T21:03:59.000-05:00", + bumped: true, + bumped_at: "2013-12-31T21:03:59.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 7984, + username: "sophearak", + avatar_template: "/images/avatar.png" + } + }, + { + id: 7229, + title: "How to set up image uploads to S3?", + fancy_title: "How to set up image uploads to S3?", + slug: "how-to-set-up-image-uploads-to-s3", + posts_count: 14, + reply_count: 11, + highest_post_number: 14, + image_url: "/uploads/meta_discourse/1019/782cbc7e309ce43f.png", + created_at: "2013-06-06T15:37:43.000-04:00", + last_posted_at: "2013-12-31T11:54:18.000-05:00", + bumped: true, + bumped_at: "2013-12-31T11:54:18.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 1995, + username: "zogstrip", + avatar_template: "/images/avatar.png" + } + }, + { + id: 11628, + title: + "My experience with a successful migration (hints for a guide)", + fancy_title: + "My experience with a successful migration (hints for a guide)", + slug: + "my-experience-with-a-successful-migration-hints-for-a-guide", + posts_count: 3, + reply_count: 1, + highest_post_number: 3, + image_url: null, + created_at: "2013-12-28T09:23:45.000-05:00", + last_posted_at: "2013-12-28T10:38:48.000-05:00", + bumped: true, + bumped_at: "2013-12-28T10:38:48.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + last_poster: { + id: 6018, + username: "robypez", + avatar_template: "/images/avatar.png" + } + } + ] + } + ] + } + }, + "/c/bug/l/latest.json": { + users: [ + { id: 1, username: "sam", avatar_template: "/images/avatar.png" }, + { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + }, + { + id: 8021, + username: "Abhishek_Gupta", + avatar_template: "/images/avatar.png" + }, + { + id: 6695, + username: "illspirit", + avatar_template: "/images/avatar.png" + }, + { id: 2, username: "neil", avatar_template: "/images/avatar.png" }, + { id: 3124, username: "sipp11", avatar_template: "/images/avatar.png" }, + { id: 7513, username: "digit", avatar_template: "/images/avatar.png" }, + { id: 19, username: "eviltrout", avatar_template: "/images/avatar.png" }, + { id: 3, username: "supermathie", avatar_template: "/images/avatar.png" }, + { id: 7073, username: "5an1ty", avatar_template: "/images/avatar.png" }, + { id: 4996, username: "wmertens", avatar_template: "/images/avatar.png" }, + { id: 6377, username: "zh99998", avatar_template: "/images/avatar.png" }, + { id: 1496, username: "cfstras", avatar_template: "/images/avatar.png" }, + { id: 7995, username: "Hunter", avatar_template: "/images/avatar.png" }, + { id: 6626, username: "riking", avatar_template: "/images/avatar.png" }, + { id: 1995, username: "zogstrip", avatar_template: "/images/avatar.png" }, + { + id: 5048, + username: "SneakySly", + avatar_template: "/images/avatar.png" + }, + { id: 7731, username: "YOU", avatar_template: "/images/avatar.png" }, + { + id: 7985, + username: "onlinedev", + avatar_template: "/images/avatar.png" + }, + { id: 3415, username: "radq", avatar_template: "/images/avatar.png" }, + { + id: 5351, + username: "erlend_sh", + avatar_template: "/images/avatar.png" + }, + { + id: 471, + username: "BhaelOchon", + avatar_template: "/images/avatar.png" + }, + { id: 7, username: "pekka", avatar_template: "/images/avatar.png" }, + { + id: 4780, + username: "HugoAlmeida", + avatar_template: "/images/avatar.png" + }, + { id: 5053, username: "Blue", avatar_template: "/images/avatar.png" }, + { id: 212, username: "alxndr", avatar_template: "/images/avatar.png" }, + { + id: 6118, + username: "lukelarris", + avatar_template: "/images/avatar.png" + }, + { + id: 7076, + username: "philnelson", + avatar_template: "/images/avatar.png" + }, + { id: 4851, username: "jab", avatar_template: "/images/avatar.png" }, + { id: 4457, username: "Lee_Ars", avatar_template: "/images/avatar.png" }, + { id: 6280, username: "mx2000", avatar_template: "/images/avatar.png" }, + { id: 3681, username: "Ajarn", avatar_template: "/images/avatar.png" }, + { id: 1621, username: "bnb", avatar_template: "/images/avatar.png" }, + { id: 6266, username: "bragi", avatar_template: "/images/avatar.png" }, + { id: 5335, username: "masda70", avatar_template: "/images/avatar.png" }, + { + id: 6314, + username: "rafaelfranca", + avatar_template: "/images/avatar.png" + } + ], + topic_list: { + can_create_topic: false, + more_topics_url: "/latest.json?category=1&page=1", + draft: null, + draft_key: "new_topic", + draft_sequence: null, + topics: [ + { + id: 2, + title: "Category definition for bug", + fancy_title: "Category definition for bug", + slug: "category-definition-for-bug", + posts_count: 2, + reply_count: 0, + highest_post_number: 3, + image_url: null, + created_at: "2013-01-31T23:56:34.000-05:00", + last_posted_at: "2013-03-07T22:42:27.000-05:00", + bumped: true, + bumped_at: "2013-02-26T18:52:56.000-05:00", + unseen: false, + pinned: true, + excerpt: + "Bug reports on Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.", + visible: true, + closed: false, + archived: false, + views: 469, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 1 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11994, + title: "Cross domain rules, followed?", + fancy_title: "Cross domain rules, followed?", + slug: "cross-domain-rules-followed", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2014-01-16T09:59:15.000-05:00", + last_posted_at: "2014-01-16T09:59:15.000-05:00", + bumped: true, + bumped_at: "2014-01-16T11:04:32.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 15, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "Abhishek_Gupta", + category_id: 1, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 8021 + } + ] + }, + { + id: 11888, + title: "Uncategorized topics not allowed, still seeing tag places", + fancy_title: + "Uncategorized topics not allowed, still seeing tag places", + slug: "uncategorized-topics-not-allowed-still-seeing-tag-places", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: null, + created_at: "2014-01-10T19:23:37.000-05:00", + last_posted_at: "2014-01-15T22:41:25.000-05:00", + bumped: true, + bumped_at: "2014-01-15T22:41:25.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 50, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "illspirit", + category_id: 1, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 6695 + }, + { extras: null, description: "Most Posts", user_id: 32 }, + { extras: null, description: "Frequent Poster", user_id: 2 } + ] + }, + { + id: 9151, + title: "Apple touch icon doesn't show if there is no sub domain", + fancy_title: + "Apple touch icon doesn’t show if there is no sub domain", + slug: "apple-touch-icon-doesnt-show-if-there-is-no-sub-domain", + posts_count: 7, + reply_count: 4, + highest_post_number: 7, + image_url: null, + created_at: "2013-08-16T18:16:53.000-04:00", + last_posted_at: "2014-01-15T17:10:18.000-05:00", + bumped: true, + bumped_at: "2014-01-15T13:19:22.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 188, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 3124 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 10911, + title: + "/users/activate-account pulling blank logo instead of defaulting to h2", + fancy_title: + "/users/activate-account pulling blank logo instead of defaulting to h2", + slug: + "users-activate-account-pulling-blank-logo-instead-of-defaulting-to-h2", + posts_count: 3, + reply_count: 1, + highest_post_number: 3, + image_url: null, + created_at: "2013-11-12T14:49:04.000-05:00", + last_posted_at: "2014-01-15T10:21:37.000-05:00", + bumped: true, + bumped_at: "2014-01-15T10:21:37.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 121, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "eviltrout", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 7513 }, + { extras: "latest", description: "Most Recent Poster", user_id: 19 } + ] + }, + { + id: 11937, + title: "Smiley parser is busted", + fancy_title: "Smiley parser is busted", + slug: "smiley-parser-is-busted", + posts_count: 4, + reply_count: 4, + highest_post_number: 7, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2014-01-13T15:42:00.000-05:00", + last_posted_at: "2014-01-15T05:51:16.000-05:00", + bumped: true, + bumped_at: "2014-01-15T05:51:16.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 66, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 3 }, + { extras: null, description: "Most Posts", user_id: 7073 }, + { extras: "latest", description: "Most Recent Poster", user_id: 1 } + ] + }, + { + id: 6625, + title: "Error 500 on PUT of site config", + fancy_title: "Error 500 on PUT of site config", + slug: "error-500-on-put-of-site-config", + posts_count: 4, + reply_count: 1, + highest_post_number: 4, + image_url: null, + created_at: "2013-05-14T18:13:56.000-04:00", + last_posted_at: "2014-01-16T04:55:50.000-05:00", + bumped: true, + bumped_at: "2014-01-15T04:43:23.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 132, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 4996 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 11225, + title: "Forum acts weirdly after client side updates", + fancy_title: "Forum acts weirdly after client side updates", + slug: "forum-acts-weirdly-after-client-side-updates", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: null, + created_at: "2013-12-02T18:32:10.000-05:00", + last_posted_at: "2014-01-15T04:04:55.000-05:00", + bumped: true, + bumped_at: "2014-01-15T02:55:18.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 117, + like_count: 7, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 1 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 11903, + title: "Error after update to 0.9.8.1", + fancy_title: "Error after update to 0.9.8.1", + slug: "error-after-update-to-0-9-8-1", + posts_count: 14, + reply_count: 6, + highest_post_number: 17, + image_url: null, + created_at: "2014-01-12T06:55:45.000-05:00", + last_posted_at: "2014-01-15T01:48:58.000-05:00", + bumped: true, + bumped_at: "2014-01-15T01:48:58.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 121, + like_count: 6, + has_summary: false, + archetype: "regular", + last_poster_username: "zh99998", + category_id: 1, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 6377 + }, + { extras: null, description: "Most Posts", user_id: 1496 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 19 } + ] + }, + { + id: 11969, + title: "Qunit error and possibly related ember.js problem", + fancy_title: "Qunit error and possibly related ember.js problem", + slug: "qunit-error-and-possibly-related-ember-js-problem", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-14T22:51:32.000-05:00", + last_posted_at: "2014-01-14T22:51:32.000-05:00", + bumped: false, + bumped_at: "2014-01-14T22:51:32.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 32, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "Hunter", + category_id: 1, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 7995 + } + ] + }, + { + id: 11945, + title: "Stuff disappears on the groups page", + fancy_title: "Stuff disappears on the groups page", + slug: "stuff-disappears-on-the-groups-page", + posts_count: 7, + reply_count: 2, + highest_post_number: 7, + image_url: null, + created_at: "2014-01-13T23:03:53.000-05:00", + last_posted_at: "2014-01-15T01:26:07.000-05:00", + bumped: true, + bumped_at: "2014-01-14T21:09:01.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 54, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 6695 }, + { extras: null, description: "Most Posts", user_id: 6626 }, + { + extras: "latest", + description: "Most Recent Poster, Frequent Poster", + user_id: 1995 + } + ] + }, + { + id: 11520, + title: "Discourse WordPress Plugin: Emoji's do not properly display", + fancy_title: + "Discourse WordPress Plugin: Emoji’s do not properly display", + slug: "discourse-wordpress-plugin-emojis-do-not-properly-display", + posts_count: 9, + reply_count: 4, + highest_post_number: 9, + image_url: + "/uploads/default/_optimized/638/4db/eff43a45b8_690x420.png", + created_at: "2013-12-19T23:32:03.000-05:00", + last_posted_at: "2014-01-15T04:32:19.000-05:00", + bumped: true, + bumped_at: "2014-01-14T17:53:34.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 168, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 5048 }, + { extras: null, description: "Frequent Poster", user_id: 7731 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 11597, + title: + 'All categories drop down does not close after clicking on first menu "all categories"', + fancy_title: + "All categories drop down does not close after clicking on first menu “all categories”", + slug: + "all-categories-drop-down-does-not-close-after-clicking-on-first-menu-all-categories", + posts_count: 5, + reply_count: 2, + highest_post_number: 5, + image_url: "/uploads/default/2495/f9efe463ae67632d.png", + created_at: "2013-12-25T15:09:27.000-05:00", + last_posted_at: "2014-01-14T17:46:41.000-05:00", + bumped: true, + bumped_at: "2014-01-14T17:46:41.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 73, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "radq", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 7985 }, + { extras: null, description: "Most Posts", user_id: 32 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 3415 + } + ] + }, + { + id: 11962, + title: "Editor When Clicking on Wrench Issue", + fancy_title: "Editor When Clicking on Wrench Issue", + slug: "editor-when-clicking-on-wrench-issue", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: + "/uploads/default/_optimized/ca4/f70/ac7278b8f6_690x176.png", + created_at: "2014-01-14T17:23:20.000-05:00", + last_posted_at: "2014-01-14T17:24:02.000-05:00", + bumped: true, + bumped_at: "2014-01-14T17:24:02.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 30, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 7073 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11831, + title: "Broken links, possibly related to HTTPS", + fancy_title: "Broken links, possibly related to HTTPS", + slug: "broken-links-possibly-related-to-https", + posts_count: 17, + reply_count: 13, + highest_post_number: 18, + image_url: null, + created_at: "2014-01-08T17:40:45.000-05:00", + last_posted_at: "2014-01-14T16:03:07.000-05:00", + bumped: true, + bumped_at: "2014-01-14T16:03:07.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 102, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "eviltrout", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 5351 }, + { extras: null, description: "Most Posts", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 471 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { extras: "latest", description: "Most Recent Poster", user_id: 19 } + ] + }, + { + id: 11916, + title: "Unable to save user preferences", + fancy_title: "Unable to save user preferences", + slug: "unable-to-save-user-preferences", + posts_count: 4, + reply_count: 1, + highest_post_number: 4, + image_url: null, + created_at: "2014-01-13T02:29:26.000-05:00", + last_posted_at: "2014-01-14T14:39:32.000-05:00", + bumped: true, + bumped_at: "2014-01-14T14:39:29.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 34, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 6626 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 1995 + } + ] + }, + { + id: 10425, + title: "Editing category permissions: select value doesn't change", + fancy_title: + "Editing category permissions: select value doesn’t change", + slug: "editing-category-permissions-select-value-doesnt-change", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: "/uploads/meta_discourse/1956/d55fba29dbd7e1fe.png", + created_at: "2013-10-17T18:20:20.000-04:00", + last_posted_at: "2013-10-17T18:20:21.000-04:00", + bumped: true, + bumped_at: "2014-01-14T13:35:37.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 92, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "pekka", + category_id: 1, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 7 + } + ] + }, + { + id: 6557, + title: "Middle clicking a link twice does not work as expected", + fancy_title: "Middle clicking a link twice does not work as expected", + slug: "middle-clicking-a-link-twice-does-not-work-as-expected", + posts_count: 10, + reply_count: 7, + highest_post_number: 10, + image_url: null, + created_at: "2013-05-11T13:56:02.000-04:00", + last_posted_at: "2014-01-14T13:13:04.000-05:00", + bumped: true, + bumped_at: "2014-01-14T13:13:04.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 401, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "neil", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 4780 }, + { extras: null, description: "Most Posts", user_id: 5053 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { extras: "latest", description: "Most Recent Poster", user_id: 2 } + ] + }, + { + id: 11944, + title: "Regression: Cannot sort topic list", + fancy_title: "Regression: Cannot sort topic list", + slug: "regression-cannot-sort-topic-list", + posts_count: 5, + reply_count: 0, + highest_post_number: 5, + image_url: null, + created_at: "2014-01-13T20:14:06.000-05:00", + last_posted_at: "2014-01-14T19:31:28.000-05:00", + bumped: true, + bumped_at: "2014-01-14T07:31:19.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: true, + views: 37, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 6626 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 1995 + } + ] + }, + { + id: 10462, + title: "Rebake error when posts contain deleted YouTube video", + fancy_title: "Rebake error when posts contain deleted YouTube video", + slug: "rebake-error-when-posts-contain-deleted-youtube-video", + posts_count: 7, + reply_count: 1, + highest_post_number: 7, + image_url: null, + created_at: "2013-10-19T00:01:21.000-04:00", + last_posted_at: "2014-01-14T02:24:19.000-05:00", + bumped: true, + bumped_at: "2014-01-14T02:24:12.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 178, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 6695 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11932, + title: "Use of blockquote tag causes text outside a paragraph", + fancy_title: "Use of blockquote tag causes text outside a paragraph", + slug: "use-of-blockquote-tag-causes-text-outside-a-paragraph", + posts_count: 4, + reply_count: 2, + highest_post_number: 4, + image_url: null, + created_at: "2014-01-13T13:38:15.000-05:00", + last_posted_at: "2014-01-13T19:30:37.000-05:00", + bumped: true, + bumped_at: "2014-01-14T02:22:58.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 54, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 6626 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 10357, + title: "Displaced Wrench Icon Chrome", + fancy_title: "Displaced Wrench Icon Chrome", + slug: "displaced-wrench-icon-chrome", + posts_count: 12, + reply_count: 4, + highest_post_number: 12, + image_url: + "/uploads/default/_optimized/9f3/f35/c5379beffe_690x300.jpg", + created_at: "2013-10-14T05:48:21.000-04:00", + last_posted_at: "2014-01-14T03:21:32.000-05:00", + bumped: true, + bumped_at: "2014-01-13T19:03:33.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 206, + like_count: 10, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 7073 }, + { extras: null, description: "Frequent Poster", user_id: 212 }, + { extras: null, description: "Frequent Poster", user_id: 6118 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 10114, + title: "Invitation expiry workflow is wonky", + fancy_title: "Invitation expiry workflow is wonky", + slug: "invitation-expiry-workflow-is-wonky", + posts_count: 14, + reply_count: 7, + highest_post_number: 14, + image_url: null, + created_at: "2013-09-30T00:59:36.000-04:00", + last_posted_at: "2014-01-13T18:51:26.000-05:00", + bumped: true, + bumped_at: "2014-01-13T18:51:26.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 176, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 1 }, + { extras: null, description: "Most Posts", user_id: 7076 }, + { extras: null, description: "Frequent Poster", user_id: 2 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 6330, + title: "Reply not disabled if topic closed while viewing", + fancy_title: "Reply not disabled if topic closed while viewing", + slug: "reply-not-disabled-if-topic-closed-while-viewing", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: + "https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon", + created_at: "2013-05-02T06:02:06.000-04:00", + last_posted_at: "2014-01-13T11:54:22.000-05:00", + bumped: true, + bumped_at: "2014-01-13T11:54:22.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 164, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 4851 }, + { extras: null, description: "Most Posts", user_id: 2 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 8367, + title: "Very fast scrolling fails to mark all posts read in a thread", + fancy_title: + "Very fast scrolling fails to mark all posts read in a thread", + slug: "very-fast-scrolling-fails-to-mark-all-posts-read-in-a-thread", + posts_count: 11, + reply_count: 7, + highest_post_number: 13, + image_url: null, + created_at: "2013-07-14T12:37:02.000-04:00", + last_posted_at: "2014-01-13T11:16:56.000-05:00", + bumped: true, + bumped_at: "2014-01-13T11:16:33.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 288, + like_count: 5, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 4457 }, + { extras: null, description: "Most Posts", user_id: 6280 }, + { extras: null, description: "Frequent Poster", user_id: 3681 }, + { extras: null, description: "Frequent Poster", user_id: 1621 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 8815, + title: "Cache headers confuse proxies", + fancy_title: "Cache headers confuse proxies", + slug: "cache-headers-confuse-proxies", + posts_count: 9, + reply_count: 3, + highest_post_number: 9, + image_url: null, + created_at: "2013-08-02T05:45:26.000-04:00", + last_posted_at: "2014-01-13T11:12:09.000-05:00", + bumped: true, + bumped_at: "2014-01-13T10:41:44.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: true, + views: 314, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 6266 }, + { extras: null, description: "Most Posts", user_id: 19 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 4457 }, + { + extras: "latest", + description: "Most Recent Poster, Frequent Poster", + user_id: 32 + } + ] + }, + { + id: 11371, + title: "Search not working for Staff users", + fancy_title: "Search not working for Staff users", + slug: "search-not-working-for-staff-users", + posts_count: 15, + reply_count: 10, + highest_post_number: 15, + image_url: null, + created_at: "2013-12-11T13:22:56.000-05:00", + last_posted_at: "2014-01-13T01:41:50.000-05:00", + bumped: true, + bumped_at: "2014-01-13T01:41:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: true, + views: 217, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 5335 }, + { extras: null, description: "Most Posts", user_id: 19 }, + { extras: null, description: "Frequent Poster", user_id: 6314 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { extras: "latest", description: "Most Recent Poster", user_id: 1 } + ] + }, + { + id: 9908, + title: "Draft bar overrides pagination widget", + fancy_title: "Draft bar overrides pagination widget", + slug: "draft-bar-overrides-pagination-widget", + posts_count: 4, + reply_count: 0, + highest_post_number: 4, + image_url: null, + created_at: "2013-09-19T17:19:52.000-04:00", + last_posted_at: "2014-01-13T01:26:01.000-05:00", + bumped: true, + bumped_at: "2014-01-13T01:25:12.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: true, + views: 108, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 5351 }, + { extras: null, description: "Most Posts", user_id: 471 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 6134, + title: + "Unread topic is stuck as unread after insertion of staff message", + fancy_title: + "Unread topic is stuck as unread after insertion of staff message", + slug: + "unread-topic-is-stuck-as-unread-after-insertion-of-staff-message", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: + "https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon", + created_at: "2013-04-24T13:37:32.000-04:00", + last_posted_at: "2014-01-13T01:22:49.000-05:00", + bumped: true, + bumped_at: "2014-01-13T01:22:42.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 169, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 1, + posters: [ + { extras: null, description: "Original Poster", user_id: 3681 }, + { extras: null, description: "Most Posts", user_id: 5351 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11914, + title: "Google analytics is not registering page views", + fancy_title: "Google analytics is not registering page views", + slug: "google-analytics-is-not-registering-page-views", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-13T00:32:45.000-05:00", + last_posted_at: "2014-01-13T00:32:46.000-05:00", + bumped: true, + bumped_at: "2014-01-13T00:32:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 37, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 1, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 1 + } + ] + } + ] + } + }, + "/c/feature/l/latest.json": { + users: [ + { id: 1, username: "sam", avatar_template: "/images/avatar.png" }, + { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + }, + { + id: 8021, + username: "Abhishek_Gupta", + avatar_template: "/images/avatar.png" + }, + { + id: 6695, + username: "illspirit", + avatar_template: "/images/avatar.png" + }, + { id: 2, username: "neil", avatar_template: "/images/avatar.png" }, + { id: 3124, username: "sipp11", avatar_template: "/images/avatar.png" }, + { id: 7513, username: "digit", avatar_template: "/images/avatar.png" }, + { id: 19, username: "eviltrout", avatar_template: "/images/avatar.png" }, + { id: 3, username: "supermathie", avatar_template: "/images/avatar.png" }, + { id: 7073, username: "5an1ty", avatar_template: "/images/avatar.png" }, + { id: 4996, username: "wmertens", avatar_template: "/images/avatar.png" }, + { id: 6377, username: "zh99998", avatar_template: "/images/avatar.png" }, + { id: 1496, username: "cfstras", avatar_template: "/images/avatar.png" }, + { id: 7995, username: "Hunter", avatar_template: "/images/avatar.png" }, + { id: 6626, username: "riking", avatar_template: "/images/avatar.png" }, + { id: 1995, username: "zogstrip", avatar_template: "/images/avatar.png" }, + { + id: 5048, + username: "SneakySly", + avatar_template: "/images/avatar.png" + }, + { id: 7731, username: "YOU", avatar_template: "/images/avatar.png" }, + { + id: 7985, + username: "onlinedev", + avatar_template: "/images/avatar.png" + }, + { id: 3415, username: "radq", avatar_template: "/images/avatar.png" }, + { + id: 5351, + username: "erlend_sh", + avatar_template: "/images/avatar.png" + }, + { + id: 471, + username: "BhaelOchon", + avatar_template: "/images/avatar.png" + }, + { id: 7, username: "pekka", avatar_template: "/images/avatar.png" }, + { + id: 4780, + username: "HugoAlmeida", + avatar_template: "/images/avatar.png" + }, + { id: 5053, username: "Blue", avatar_template: "/images/avatar.png" }, + { id: 212, username: "alxndr", avatar_template: "/images/avatar.png" }, + { + id: 6118, + username: "lukelarris", + avatar_template: "/images/avatar.png" + }, + { + id: 7076, + username: "philnelson", + avatar_template: "/images/avatar.png" + }, + { id: 4851, username: "jab", avatar_template: "/images/avatar.png" }, + { id: 4457, username: "Lee_Ars", avatar_template: "/images/avatar.png" }, + { id: 6280, username: "mx2000", avatar_template: "/images/avatar.png" }, + { id: 3681, username: "Ajarn", avatar_template: "/images/avatar.png" }, + { id: 1621, username: "bnb", avatar_template: "/images/avatar.png" }, + { id: 6266, username: "bragi", avatar_template: "/images/avatar.png" }, + { id: 5335, username: "masda70", avatar_template: "/images/avatar.png" }, + { + id: 6314, + username: "rafaelfranca", + avatar_template: "/images/avatar.png" + } + ], + topic_list: { + can_create_topic: false, + more_topics_url: "/latest.json?category=2&page=1", + draft: null, + draft_key: "new_topic", + draft_sequence: null, + topics: [ + { + id: 2, + title: "Category definition for feature", + fancy_title: "Category definition for feature", + slug: "category-definition-for-feature", + posts_count: 2, + reply_count: 0, + highest_post_number: 3, + image_url: null, + created_at: "2013-01-31T23:56:34.000-05:00", + last_posted_at: "2013-03-07T22:42:27.000-05:00", + bumped: true, + bumped_at: "2013-02-26T18:52:56.000-05:00", + unseen: false, + pinned: true, + excerpt: "Features on Discourse.", + visible: true, + closed: false, + archived: false, + views: 469, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 1 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11994, + title: "Cross domain rules, followed?", + fancy_title: "Cross domain rules, followed?", + slug: "cross-domain-rules-followed", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2014-01-16T09:59:15.000-05:00", + last_posted_at: "2014-01-16T09:59:15.000-05:00", + bumped: true, + bumped_at: "2014-01-16T11:04:32.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 15, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "Abhishek_Gupta", + category_id: 2, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 8021 + } + ] + }, + { + id: 11888, + title: "Uncategorized topics not allowed, still seeing tag places", + fancy_title: + "Uncategorized topics not allowed, still seeing tag places", + slug: "uncategorized-topics-not-allowed-still-seeing-tag-places", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: null, + created_at: "2014-01-10T19:23:37.000-05:00", + last_posted_at: "2014-01-15T22:41:25.000-05:00", + bumped: true, + bumped_at: "2014-01-15T22:41:25.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 50, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "illspirit", + category_id: 2, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 6695 + }, + { extras: null, description: "Most Posts", user_id: 32 }, + { extras: null, description: "Frequent Poster", user_id: 2 } + ] + }, + { + id: 9151, + title: "Apple touch icon doesn't show if there is no sub domain", + fancy_title: + "Apple touch icon doesn’t show if there is no sub domain", + slug: "apple-touch-icon-doesnt-show-if-there-is-no-sub-domain", + posts_count: 7, + reply_count: 4, + highest_post_number: 7, + image_url: null, + created_at: "2013-08-16T18:16:53.000-04:00", + last_posted_at: "2014-01-15T17:10:18.000-05:00", + bumped: true, + bumped_at: "2014-01-15T13:19:22.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 188, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 3124 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 10911, + title: + "/users/activate-account pulling blank logo instead of defaulting to h2", + fancy_title: + "/users/activate-account pulling blank logo instead of defaulting to h2", + slug: + "users-activate-account-pulling-blank-logo-instead-of-defaulting-to-h2", + posts_count: 3, + reply_count: 1, + highest_post_number: 3, + image_url: null, + created_at: "2013-11-12T14:49:04.000-05:00", + last_posted_at: "2014-01-15T10:21:37.000-05:00", + bumped: true, + bumped_at: "2014-01-15T10:21:37.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 121, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "eviltrout", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 7513 }, + { extras: "latest", description: "Most Recent Poster", user_id: 19 } + ] + }, + { + id: 11937, + title: "Smiley parser is busted", + fancy_title: "Smiley parser is busted", + slug: "smiley-parser-is-busted", + posts_count: 4, + reply_count: 4, + highest_post_number: 7, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2014-01-13T15:42:00.000-05:00", + last_posted_at: "2014-01-15T05:51:16.000-05:00", + bumped: true, + bumped_at: "2014-01-15T05:51:16.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 66, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 3 }, + { extras: null, description: "Most Posts", user_id: 7073 }, + { extras: "latest", description: "Most Recent Poster", user_id: 1 } + ] + }, + { + id: 6625, + title: "Error 500 on PUT of site config", + fancy_title: "Error 500 on PUT of site config", + slug: "error-500-on-put-of-site-config", + posts_count: 4, + reply_count: 1, + highest_post_number: 4, + image_url: null, + created_at: "2013-05-14T18:13:56.000-04:00", + last_posted_at: "2014-01-16T04:55:50.000-05:00", + bumped: true, + bumped_at: "2014-01-15T04:43:23.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 132, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 4996 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 11225, + title: "Forum acts weirdly after client side updates", + fancy_title: "Forum acts weirdly after client side updates", + slug: "forum-acts-weirdly-after-client-side-updates", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: null, + created_at: "2013-12-02T18:32:10.000-05:00", + last_posted_at: "2014-01-15T04:04:55.000-05:00", + bumped: true, + bumped_at: "2014-01-15T02:55:18.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 117, + like_count: 7, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 1 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 11903, + title: "Error after update to 0.9.8.1", + fancy_title: "Error after update to 0.9.8.1", + slug: "error-after-update-to-0-9-8-1", + posts_count: 14, + reply_count: 6, + highest_post_number: 17, + image_url: null, + created_at: "2014-01-12T06:55:45.000-05:00", + last_posted_at: "2014-01-15T01:48:58.000-05:00", + bumped: true, + bumped_at: "2014-01-15T01:48:58.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 121, + like_count: 6, + has_summary: false, + archetype: "regular", + last_poster_username: "zh99998", + category_id: 2, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 6377 + }, + { extras: null, description: "Most Posts", user_id: 1496 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 19 } + ] + }, + { + id: 11969, + title: "Qunit error and possibly related ember.js problem", + fancy_title: "Qunit error and possibly related ember.js problem", + slug: "qunit-error-and-possibly-related-ember-js-problem", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-14T22:51:32.000-05:00", + last_posted_at: "2014-01-14T22:51:32.000-05:00", + bumped: false, + bumped_at: "2014-01-14T22:51:32.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 32, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "Hunter", + category_id: 2, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 7995 + } + ] + }, + { + id: 11945, + title: "Stuff disappears on the groups page", + fancy_title: "Stuff disappears on the groups page", + slug: "stuff-disappears-on-the-groups-page", + posts_count: 7, + reply_count: 2, + highest_post_number: 7, + image_url: null, + created_at: "2014-01-13T23:03:53.000-05:00", + last_posted_at: "2014-01-15T01:26:07.000-05:00", + bumped: true, + bumped_at: "2014-01-14T21:09:01.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 54, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 6695 }, + { extras: null, description: "Most Posts", user_id: 6626 }, + { + extras: "latest", + description: "Most Recent Poster, Frequent Poster", + user_id: 1995 + } + ] + }, + { + id: 11520, + title: "Discourse WordPress Plugin: Emoji's do not properly display", + fancy_title: + "Discourse WordPress Plugin: Emoji’s do not properly display", + slug: "discourse-wordpress-plugin-emojis-do-not-properly-display", + posts_count: 9, + reply_count: 4, + highest_post_number: 9, + image_url: + "/uploads/default/_optimized/638/4db/eff43a45b8_690x420.png", + created_at: "2013-12-19T23:32:03.000-05:00", + last_posted_at: "2014-01-15T04:32:19.000-05:00", + bumped: true, + bumped_at: "2014-01-14T17:53:34.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 168, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 5048 }, + { extras: null, description: "Frequent Poster", user_id: 7731 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 11597, + title: + 'All categories drop down does not close after clicking on first menu "all categories"', + fancy_title: + "All categories drop down does not close after clicking on first menu “all categories”", + slug: + "all-categories-drop-down-does-not-close-after-clicking-on-first-menu-all-categories", + posts_count: 5, + reply_count: 2, + highest_post_number: 5, + image_url: "/uploads/default/2495/f9efe463ae67632d.png", + created_at: "2013-12-25T15:09:27.000-05:00", + last_posted_at: "2014-01-14T17:46:41.000-05:00", + bumped: true, + bumped_at: "2014-01-14T17:46:41.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 73, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "radq", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 7985 }, + { extras: null, description: "Most Posts", user_id: 32 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 3415 + } + ] + }, + { + id: 11962, + title: "Editor When Clicking on Wrench Issue", + fancy_title: "Editor When Clicking on Wrench Issue", + slug: "editor-when-clicking-on-wrench-issue", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: + "/uploads/default/_optimized/ca4/f70/ac7278b8f6_690x176.png", + created_at: "2014-01-14T17:23:20.000-05:00", + last_posted_at: "2014-01-14T17:24:02.000-05:00", + bumped: true, + bumped_at: "2014-01-14T17:24:02.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 30, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 7073 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11831, + title: "Broken links, possibly related to HTTPS", + fancy_title: "Broken links, possibly related to HTTPS", + slug: "broken-links-possibly-related-to-https", + posts_count: 17, + reply_count: 13, + highest_post_number: 18, + image_url: null, + created_at: "2014-01-08T17:40:45.000-05:00", + last_posted_at: "2014-01-14T16:03:07.000-05:00", + bumped: true, + bumped_at: "2014-01-14T16:03:07.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 102, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "eviltrout", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 5351 }, + { extras: null, description: "Most Posts", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 471 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { extras: "latest", description: "Most Recent Poster", user_id: 19 } + ] + }, + { + id: 11916, + title: "Unable to save user preferences", + fancy_title: "Unable to save user preferences", + slug: "unable-to-save-user-preferences", + posts_count: 4, + reply_count: 1, + highest_post_number: 4, + image_url: null, + created_at: "2014-01-13T02:29:26.000-05:00", + last_posted_at: "2014-01-14T14:39:32.000-05:00", + bumped: true, + bumped_at: "2014-01-14T14:39:29.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 34, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 6626 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 1995 + } + ] + }, + { + id: 10425, + title: "Editing category permissions: select value doesn't change", + fancy_title: + "Editing category permissions: select value doesn’t change", + slug: "editing-category-permissions-select-value-doesnt-change", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: "/uploads/meta_discourse/1956/d55fba29dbd7e1fe.png", + created_at: "2013-10-17T18:20:20.000-04:00", + last_posted_at: "2013-10-17T18:20:21.000-04:00", + bumped: true, + bumped_at: "2014-01-14T13:35:37.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 92, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "pekka", + category_id: 2, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 7 + } + ] + }, + { + id: 6557, + title: "Middle clicking a link twice does not work as expected", + fancy_title: "Middle clicking a link twice does not work as expected", + slug: "middle-clicking-a-link-twice-does-not-work-as-expected", + posts_count: 10, + reply_count: 7, + highest_post_number: 10, + image_url: null, + created_at: "2013-05-11T13:56:02.000-04:00", + last_posted_at: "2014-01-14T13:13:04.000-05:00", + bumped: true, + bumped_at: "2014-01-14T13:13:04.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 401, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "neil", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 4780 }, + { extras: null, description: "Most Posts", user_id: 5053 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { extras: "latest", description: "Most Recent Poster", user_id: 2 } + ] + }, + { + id: 11944, + title: "Regression: Cannot sort topic list", + fancy_title: "Regression: Cannot sort topic list", + slug: "regression-cannot-sort-topic-list", + posts_count: 5, + reply_count: 0, + highest_post_number: 5, + image_url: null, + created_at: "2014-01-13T20:14:06.000-05:00", + last_posted_at: "2014-01-14T19:31:28.000-05:00", + bumped: true, + bumped_at: "2014-01-14T07:31:19.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: true, + views: 37, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 6626 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 1995 + } + ] + }, + { + id: 10462, + title: "Rebake error when posts contain deleted YouTube video", + fancy_title: "Rebake error when posts contain deleted YouTube video", + slug: "rebake-error-when-posts-contain-deleted-youtube-video", + posts_count: 7, + reply_count: 1, + highest_post_number: 7, + image_url: null, + created_at: "2013-10-19T00:01:21.000-04:00", + last_posted_at: "2014-01-14T02:24:19.000-05:00", + bumped: true, + bumped_at: "2014-01-14T02:24:12.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 178, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 6695 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11932, + title: "Use of blockquote tag causes text outside a paragraph", + fancy_title: "Use of blockquote tag causes text outside a paragraph", + slug: "use-of-blockquote-tag-causes-text-outside-a-paragraph", + posts_count: 4, + reply_count: 2, + highest_post_number: 4, + image_url: null, + created_at: "2014-01-13T13:38:15.000-05:00", + last_posted_at: "2014-01-13T19:30:37.000-05:00", + bumped: true, + bumped_at: "2014-01-14T02:22:58.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 54, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 6626 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 10357, + title: "Displaced Wrench Icon Chrome", + fancy_title: "Displaced Wrench Icon Chrome", + slug: "displaced-wrench-icon-chrome", + posts_count: 12, + reply_count: 4, + highest_post_number: 12, + image_url: + "/uploads/default/_optimized/9f3/f35/c5379beffe_690x300.jpg", + created_at: "2013-10-14T05:48:21.000-04:00", + last_posted_at: "2014-01-14T03:21:32.000-05:00", + bumped: true, + bumped_at: "2014-01-13T19:03:33.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 206, + like_count: 10, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 7073 }, + { extras: null, description: "Frequent Poster", user_id: 212 }, + { extras: null, description: "Frequent Poster", user_id: 6118 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 10114, + title: "Invitation expiry workflow is wonky", + fancy_title: "Invitation expiry workflow is wonky", + slug: "invitation-expiry-workflow-is-wonky", + posts_count: 14, + reply_count: 7, + highest_post_number: 14, + image_url: null, + created_at: "2013-09-30T00:59:36.000-04:00", + last_posted_at: "2014-01-13T18:51:26.000-05:00", + bumped: true, + bumped_at: "2014-01-13T18:51:26.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 176, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 1 }, + { extras: null, description: "Most Posts", user_id: 7076 }, + { extras: null, description: "Frequent Poster", user_id: 2 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 6330, + title: "Reply not disabled if topic closed while viewing", + fancy_title: "Reply not disabled if topic closed while viewing", + slug: "reply-not-disabled-if-topic-closed-while-viewing", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: + "https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon", + created_at: "2013-05-02T06:02:06.000-04:00", + last_posted_at: "2014-01-13T11:54:22.000-05:00", + bumped: true, + bumped_at: "2014-01-13T11:54:22.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 164, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 4851 }, + { extras: null, description: "Most Posts", user_id: 2 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 8367, + title: "Very fast scrolling fails to mark all posts read in a thread", + fancy_title: + "Very fast scrolling fails to mark all posts read in a thread", + slug: "very-fast-scrolling-fails-to-mark-all-posts-read-in-a-thread", + posts_count: 11, + reply_count: 7, + highest_post_number: 13, + image_url: null, + created_at: "2013-07-14T12:37:02.000-04:00", + last_posted_at: "2014-01-13T11:16:56.000-05:00", + bumped: true, + bumped_at: "2014-01-13T11:16:33.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 288, + like_count: 5, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 4457 }, + { extras: null, description: "Most Posts", user_id: 6280 }, + { extras: null, description: "Frequent Poster", user_id: 3681 }, + { extras: null, description: "Frequent Poster", user_id: 1621 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 8815, + title: "Cache headers confuse proxies", + fancy_title: "Cache headers confuse proxies", + slug: "cache-headers-confuse-proxies", + posts_count: 9, + reply_count: 3, + highest_post_number: 9, + image_url: null, + created_at: "2013-08-02T05:45:26.000-04:00", + last_posted_at: "2014-01-13T11:12:09.000-05:00", + bumped: true, + bumped_at: "2014-01-13T10:41:44.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: true, + views: 314, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 6266 }, + { extras: null, description: "Most Posts", user_id: 19 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 4457 }, + { + extras: "latest", + description: "Most Recent Poster, Frequent Poster", + user_id: 32 + } + ] + }, + { + id: 11371, + title: "Search not working for Staff users", + fancy_title: "Search not working for Staff users", + slug: "search-not-working-for-staff-users", + posts_count: 15, + reply_count: 10, + highest_post_number: 15, + image_url: null, + created_at: "2013-12-11T13:22:56.000-05:00", + last_posted_at: "2014-01-13T01:41:50.000-05:00", + bumped: true, + bumped_at: "2014-01-13T01:41:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: true, + views: 217, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 5335 }, + { extras: null, description: "Most Posts", user_id: 19 }, + { extras: null, description: "Frequent Poster", user_id: 6314 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { extras: "latest", description: "Most Recent Poster", user_id: 1 } + ] + }, + { + id: 9908, + title: "Draft bar overrides pagination widget", + fancy_title: "Draft bar overrides pagination widget", + slug: "draft-bar-overrides-pagination-widget", + posts_count: 4, + reply_count: 0, + highest_post_number: 4, + image_url: null, + created_at: "2013-09-19T17:19:52.000-04:00", + last_posted_at: "2014-01-13T01:26:01.000-05:00", + bumped: true, + bumped_at: "2014-01-13T01:25:12.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: true, + views: 108, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 5351 }, + { extras: null, description: "Most Posts", user_id: 471 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 6134, + title: + "Unread topic is stuck as unread after insertion of staff message", + fancy_title: + "Unread topic is stuck as unread after insertion of staff message", + slug: + "unread-topic-is-stuck-as-unread-after-insertion-of-staff-message", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: + "https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon", + created_at: "2013-04-24T13:37:32.000-04:00", + last_posted_at: "2014-01-13T01:22:49.000-05:00", + bumped: true, + bumped_at: "2014-01-13T01:22:42.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 169, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 2, + posters: [ + { extras: null, description: "Original Poster", user_id: 3681 }, + { extras: null, description: "Most Posts", user_id: 5351 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11914, + title: "Google analytics is not registering page views", + fancy_title: "Google analytics is not registering page views", + slug: "google-analytics-is-not-registering-page-views", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-13T00:32:45.000-05:00", + last_posted_at: "2014-01-13T00:32:46.000-05:00", + bumped: true, + bumped_at: "2014-01-13T00:32:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 37, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 2, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 1 + } + ] + } + ] + } + }, + "/c/dev/l/latest.json": { + users: [ + { id: 1, username: "sam", avatar_template: "/images/avatar.png" }, + { + id: 32, + username: "codinghorror", + avatar_template: "/images/avatar.png" + }, + { + id: 8021, + username: "Abhishek_Gupta", + avatar_template: "/images/avatar.png" + }, + { + id: 6695, + username: "illspirit", + avatar_template: "/images/avatar.png" + }, + { id: 2, username: "neil", avatar_template: "/images/avatar.png" }, + { id: 3124, username: "sipp11", avatar_template: "/images/avatar.png" }, + { id: 7513, username: "digit", avatar_template: "/images/avatar.png" }, + { id: 19, username: "eviltrout", avatar_template: "/images/avatar.png" }, + { id: 3, username: "supermathie", avatar_template: "/images/avatar.png" }, + { id: 7073, username: "5an1ty", avatar_template: "/images/avatar.png" }, + { id: 4996, username: "wmertens", avatar_template: "/images/avatar.png" }, + { id: 6377, username: "zh99998", avatar_template: "/images/avatar.png" }, + { id: 1496, username: "cfstras", avatar_template: "/images/avatar.png" }, + { id: 7995, username: "Hunter", avatar_template: "/images/avatar.png" }, + { id: 6626, username: "riking", avatar_template: "/images/avatar.png" }, + { id: 1995, username: "zogstrip", avatar_template: "/images/avatar.png" }, + { + id: 5048, + username: "SneakySly", + avatar_template: "/images/avatar.png" + }, + { id: 7731, username: "YOU", avatar_template: "/images/avatar.png" }, + { + id: 7985, + username: "onlinedev", + avatar_template: "/images/avatar.png" + }, + { id: 3415, username: "radq", avatar_template: "/images/avatar.png" }, + { + id: 5351, + username: "erlend_sh", + avatar_template: "/images/avatar.png" + }, + { + id: 471, + username: "BhaelOchon", + avatar_template: "/images/avatar.png" + }, + { id: 7, username: "pekka", avatar_template: "/images/avatar.png" }, + { + id: 4780, + username: "HugoAlmeida", + avatar_template: "/images/avatar.png" + }, + { id: 5053, username: "Blue", avatar_template: "/images/avatar.png" }, + { id: 212, username: "alxndr", avatar_template: "/images/avatar.png" }, + { + id: 6118, + username: "lukelarris", + avatar_template: "/images/avatar.png" + }, + { + id: 7076, + username: "philnelson", + avatar_template: "/images/avatar.png" + }, + { id: 4851, username: "jab", avatar_template: "/images/avatar.png" }, + { id: 4457, username: "Lee_Ars", avatar_template: "/images/avatar.png" }, + { id: 6280, username: "mx2000", avatar_template: "/images/avatar.png" }, + { id: 3681, username: "Ajarn", avatar_template: "/images/avatar.png" }, + { id: 1621, username: "bnb", avatar_template: "/images/avatar.png" }, + { id: 6266, username: "bragi", avatar_template: "/images/avatar.png" }, + { id: 5335, username: "masda70", avatar_template: "/images/avatar.png" }, + { + id: 6314, + username: "rafaelfranca", + avatar_template: "/images/avatar.png" + } + ], + topic_list: { + can_create_topic: false, + more_topics_url: "/latest.json?category=2&page=1", + draft: null, + draft_key: "new_topic", + draft_sequence: null, + topics: [ + { + id: 2, + title: "Category definition for dev", + fancy_title: "Category definition for dev", + slug: "category-definition-for-dev", + posts_count: 2, + reply_count: 0, + highest_post_number: 3, + image_url: null, + created_at: "2013-01-31T23:56:34.000-05:00", + last_posted_at: "2013-03-07T22:42:27.000-05:00", + bumped: true, + bumped_at: "2013-02-26T18:52:56.000-05:00", + unseen: false, + pinned: true, + excerpt: "Development of Discourse.", + visible: true, + closed: false, + archived: false, + views: 469, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 1 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11994, + title: "Cross domain rules, followed?", + fancy_title: "Cross domain rules, followed?", + slug: "cross-domain-rules-followed", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2014-01-16T09:59:15.000-05:00", + last_posted_at: "2014-01-16T09:59:15.000-05:00", + bumped: true, + bumped_at: "2014-01-16T11:04:32.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 15, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "Abhishek_Gupta", + category_id: 7, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 8021 + } + ] + }, + { + id: 11888, + title: "Uncategorized topics not allowed, still seeing tag places", + fancy_title: + "Uncategorized topics not allowed, still seeing tag places", + slug: "uncategorized-topics-not-allowed-still-seeing-tag-places", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: null, + created_at: "2014-01-10T19:23:37.000-05:00", + last_posted_at: "2014-01-15T22:41:25.000-05:00", + bumped: true, + bumped_at: "2014-01-15T22:41:25.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 50, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "illspirit", + category_id: 7, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 6695 + }, + { extras: null, description: "Most Posts", user_id: 32 }, + { extras: null, description: "Frequent Poster", user_id: 2 } + ] + }, + { + id: 9151, + title: "Apple touch icon doesn't show if there is no sub domain", + fancy_title: + "Apple touch icon doesn’t show if there is no sub domain", + slug: "apple-touch-icon-doesnt-show-if-there-is-no-sub-domain", + posts_count: 7, + reply_count: 4, + highest_post_number: 7, + image_url: null, + created_at: "2013-08-16T18:16:53.000-04:00", + last_posted_at: "2014-01-15T17:10:18.000-05:00", + bumped: true, + bumped_at: "2014-01-15T13:19:22.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 188, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 3124 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 10911, + title: + "/users/activate-account pulling blank logo instead of defaulting to h2", + fancy_title: + "/users/activate-account pulling blank logo instead of defaulting to h2", + slug: + "users-activate-account-pulling-blank-logo-instead-of-defaulting-to-h2", + posts_count: 3, + reply_count: 1, + highest_post_number: 3, + image_url: null, + created_at: "2013-11-12T14:49:04.000-05:00", + last_posted_at: "2014-01-15T10:21:37.000-05:00", + bumped: true, + bumped_at: "2014-01-15T10:21:37.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 121, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "eviltrout", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 7513 }, + { extras: "latest", description: "Most Recent Poster", user_id: 19 } + ] + }, + { + id: 11937, + title: "Smiley parser is busted", + fancy_title: "Smiley parser is busted", + slug: "smiley-parser-is-busted", + posts_count: 4, + reply_count: 4, + highest_post_number: 7, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2014-01-13T15:42:00.000-05:00", + last_posted_at: "2014-01-15T05:51:16.000-05:00", + bumped: true, + bumped_at: "2014-01-15T05:51:16.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 66, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 3 }, + { extras: null, description: "Most Posts", user_id: 7073 }, + { extras: "latest", description: "Most Recent Poster", user_id: 1 } + ] + }, + { + id: 6625, + title: "Error 500 on PUT of site config", + fancy_title: "Error 500 on PUT of site config", + slug: "error-500-on-put-of-site-config", + posts_count: 4, + reply_count: 1, + highest_post_number: 4, + image_url: null, + created_at: "2013-05-14T18:13:56.000-04:00", + last_posted_at: "2014-01-16T04:55:50.000-05:00", + bumped: true, + bumped_at: "2014-01-15T04:43:23.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 132, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 4996 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 11225, + title: "Forum acts weirdly after client side updates", + fancy_title: "Forum acts weirdly after client side updates", + slug: "forum-acts-weirdly-after-client-side-updates", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: null, + created_at: "2013-12-02T18:32:10.000-05:00", + last_posted_at: "2014-01-15T04:04:55.000-05:00", + bumped: true, + bumped_at: "2014-01-15T02:55:18.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 117, + like_count: 7, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 1 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 11903, + title: "Error after update to 0.9.8.1", + fancy_title: "Error after update to 0.9.8.1", + slug: "error-after-update-to-0-9-8-1", + posts_count: 14, + reply_count: 6, + highest_post_number: 17, + image_url: null, + created_at: "2014-01-12T06:55:45.000-05:00", + last_posted_at: "2014-01-15T01:48:58.000-05:00", + bumped: true, + bumped_at: "2014-01-15T01:48:58.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 121, + like_count: 6, + has_summary: false, + archetype: "regular", + last_poster_username: "zh99998", + category_id: 7, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 6377 + }, + { extras: null, description: "Most Posts", user_id: 1496 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 19 } + ] + }, + { + id: 11969, + title: "Qunit error and possibly related ember.js problem", + fancy_title: "Qunit error and possibly related ember.js problem", + slug: "qunit-error-and-possibly-related-ember-js-problem", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-14T22:51:32.000-05:00", + last_posted_at: "2014-01-14T22:51:32.000-05:00", + bumped: false, + bumped_at: "2014-01-14T22:51:32.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 32, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "Hunter", + category_id: 7, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 7995 + } + ] + }, + { + id: 11945, + title: "Stuff disappears on the groups page", + fancy_title: "Stuff disappears on the groups page", + slug: "stuff-disappears-on-the-groups-page", + posts_count: 7, + reply_count: 2, + highest_post_number: 7, + image_url: null, + created_at: "2014-01-13T23:03:53.000-05:00", + last_posted_at: "2014-01-15T01:26:07.000-05:00", + bumped: true, + bumped_at: "2014-01-14T21:09:01.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 54, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 6695 }, + { extras: null, description: "Most Posts", user_id: 6626 }, + { + extras: "latest", + description: "Most Recent Poster, Frequent Poster", + user_id: 1995 + } + ] + }, + { + id: 11520, + title: "Discourse WordPress Plugin: Emoji's do not properly display", + fancy_title: + "Discourse WordPress Plugin: Emoji’s do not properly display", + slug: "discourse-wordpress-plugin-emojis-do-not-properly-display", + posts_count: 9, + reply_count: 4, + highest_post_number: 9, + image_url: + "/uploads/default/_optimized/638/4db/eff43a45b8_690x420.png", + created_at: "2013-12-19T23:32:03.000-05:00", + last_posted_at: "2014-01-15T04:32:19.000-05:00", + bumped: true, + bumped_at: "2014-01-14T17:53:34.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 168, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 5048 }, + { extras: null, description: "Frequent Poster", user_id: 7731 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 11597, + title: + 'All categories drop down does not close after clicking on first menu "all categories"', + fancy_title: + "All categories drop down does not close after clicking on first menu “all categories”", + slug: + "all-categories-drop-down-does-not-close-after-clicking-on-first-menu-all-categories", + posts_count: 5, + reply_count: 2, + highest_post_number: 5, + image_url: "/uploads/default/2495/f9efe463ae67632d.png", + created_at: "2013-12-25T15:09:27.000-05:00", + last_posted_at: "2014-01-14T17:46:41.000-05:00", + bumped: true, + bumped_at: "2014-01-14T17:46:41.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 73, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "radq", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 7985 }, + { extras: null, description: "Most Posts", user_id: 32 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 3415 + } + ] + }, + { + id: 11962, + title: "Editor When Clicking on Wrench Issue", + fancy_title: "Editor When Clicking on Wrench Issue", + slug: "editor-when-clicking-on-wrench-issue", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: + "/uploads/default/_optimized/ca4/f70/ac7278b8f6_690x176.png", + created_at: "2014-01-14T17:23:20.000-05:00", + last_posted_at: "2014-01-14T17:24:02.000-05:00", + bumped: true, + bumped_at: "2014-01-14T17:24:02.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 30, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 7073 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11831, + title: "Broken links, possibly related to HTTPS", + fancy_title: "Broken links, possibly related to HTTPS", + slug: "broken-links-possibly-related-to-https", + posts_count: 17, + reply_count: 13, + highest_post_number: 18, + image_url: null, + created_at: "2014-01-08T17:40:45.000-05:00", + last_posted_at: "2014-01-14T16:03:07.000-05:00", + bumped: true, + bumped_at: "2014-01-14T16:03:07.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 102, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "eviltrout", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 5351 }, + { extras: null, description: "Most Posts", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 471 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { extras: "latest", description: "Most Recent Poster", user_id: 19 } + ] + }, + { + id: 11916, + title: "Unable to save user preferences", + fancy_title: "Unable to save user preferences", + slug: "unable-to-save-user-preferences", + posts_count: 4, + reply_count: 1, + highest_post_number: 4, + image_url: null, + created_at: "2014-01-13T02:29:26.000-05:00", + last_posted_at: "2014-01-14T14:39:32.000-05:00", + bumped: true, + bumped_at: "2014-01-14T14:39:29.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 34, + like_count: 3, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 6626 }, + { + extras: "latest", + description: "Most Recent Poster", + user_id: 1995 + } + ] + }, + { + id: 10425, + title: "Editing category permissions: select value doesn't change", + fancy_title: + "Editing category permissions: select value doesn’t change", + slug: "editing-category-permissions-select-value-doesnt-change", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: "/uploads/meta_discourse/1956/d55fba29dbd7e1fe.png", + created_at: "2013-10-17T18:20:20.000-04:00", + last_posted_at: "2013-10-17T18:20:21.000-04:00", + bumped: true, + bumped_at: "2014-01-14T13:35:37.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 92, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "pekka", + category_id: 7, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 7 + } + ] + }, + { + id: 6557, + title: "Middle clicking a link twice does not work as expected", + fancy_title: "Middle clicking a link twice does not work as expected", + slug: "middle-clicking-a-link-twice-does-not-work-as-expected", + posts_count: 10, + reply_count: 7, + highest_post_number: 10, + image_url: null, + created_at: "2013-05-11T13:56:02.000-04:00", + last_posted_at: "2014-01-14T13:13:04.000-05:00", + bumped: true, + bumped_at: "2014-01-14T13:13:04.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 401, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "neil", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 4780 }, + { extras: null, description: "Most Posts", user_id: 5053 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { extras: "latest", description: "Most Recent Poster", user_id: 2 } + ] + }, + { + id: 11944, + title: "Regression: Cannot sort topic list", + fancy_title: "Regression: Cannot sort topic list", + slug: "regression-cannot-sort-topic-list", + posts_count: 5, + reply_count: 0, + highest_post_number: 5, + image_url: null, + created_at: "2014-01-13T20:14:06.000-05:00", + last_posted_at: "2014-01-14T19:31:28.000-05:00", + bumped: true, + bumped_at: "2014-01-14T07:31:19.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: true, + views: 37, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "zogstrip", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 6626 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 1995 + } + ] + }, + { + id: 10462, + title: "Rebake error when posts contain deleted YouTube video", + fancy_title: "Rebake error when posts contain deleted YouTube video", + slug: "rebake-error-when-posts-contain-deleted-youtube-video", + posts_count: 7, + reply_count: 1, + highest_post_number: 7, + image_url: null, + created_at: "2013-10-19T00:01:21.000-04:00", + last_posted_at: "2014-01-14T02:24:19.000-05:00", + bumped: true, + bumped_at: "2014-01-14T02:24:12.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 178, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 6695 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11932, + title: "Use of blockquote tag causes text outside a paragraph", + fancy_title: "Use of blockquote tag causes text outside a paragraph", + slug: "use-of-blockquote-tag-causes-text-outside-a-paragraph", + posts_count: 4, + reply_count: 2, + highest_post_number: 4, + image_url: null, + created_at: "2014-01-13T13:38:15.000-05:00", + last_posted_at: "2014-01-13T19:30:37.000-05:00", + bumped: true, + bumped_at: "2014-01-14T02:22:58.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 54, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 6626 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 10357, + title: "Displaced Wrench Icon Chrome", + fancy_title: "Displaced Wrench Icon Chrome", + slug: "displaced-wrench-icon-chrome", + posts_count: 12, + reply_count: 4, + highest_post_number: 12, + image_url: + "/uploads/default/_optimized/9f3/f35/c5379beffe_690x300.jpg", + created_at: "2013-10-14T05:48:21.000-04:00", + last_posted_at: "2014-01-14T03:21:32.000-05:00", + bumped: true, + bumped_at: "2014-01-13T19:03:33.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 206, + like_count: 10, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 7073 }, + { extras: null, description: "Frequent Poster", user_id: 212 }, + { extras: null, description: "Frequent Poster", user_id: 6118 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { + extras: "latest", + description: "Most Recent Poster, Most Posts", + user_id: 32 + } + ] + }, + { + id: 10114, + title: "Invitation expiry workflow is wonky", + fancy_title: "Invitation expiry workflow is wonky", + slug: "invitation-expiry-workflow-is-wonky", + posts_count: 14, + reply_count: 7, + highest_post_number: 14, + image_url: null, + created_at: "2013-09-30T00:59:36.000-04:00", + last_posted_at: "2014-01-13T18:51:26.000-05:00", + bumped: true, + bumped_at: "2014-01-13T18:51:26.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 176, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 1 }, + { extras: null, description: "Most Posts", user_id: 7076 }, + { extras: null, description: "Frequent Poster", user_id: 2 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 6330, + title: "Reply not disabled if topic closed while viewing", + fancy_title: "Reply not disabled if topic closed while viewing", + slug: "reply-not-disabled-if-topic-closed-while-viewing", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: + "https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon", + created_at: "2013-05-02T06:02:06.000-04:00", + last_posted_at: "2014-01-13T11:54:22.000-05:00", + bumped: true, + bumped_at: "2014-01-13T11:54:22.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 164, + like_count: 1, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 4851 }, + { extras: null, description: "Most Posts", user_id: 2 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 8367, + title: "Very fast scrolling fails to mark all posts read in a thread", + fancy_title: + "Very fast scrolling fails to mark all posts read in a thread", + slug: "very-fast-scrolling-fails-to-mark-all-posts-read-in-a-thread", + posts_count: 11, + reply_count: 7, + highest_post_number: 13, + image_url: null, + created_at: "2013-07-14T12:37:02.000-04:00", + last_posted_at: "2014-01-13T11:16:56.000-05:00", + bumped: true, + bumped_at: "2014-01-13T11:16:33.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 288, + like_count: 5, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 4457 }, + { extras: null, description: "Most Posts", user_id: 6280 }, + { extras: null, description: "Frequent Poster", user_id: 3681 }, + { extras: null, description: "Frequent Poster", user_id: 1621 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 8815, + title: "Cache headers confuse proxies", + fancy_title: "Cache headers confuse proxies", + slug: "cache-headers-confuse-proxies", + posts_count: 9, + reply_count: 3, + highest_post_number: 9, + image_url: null, + created_at: "2013-08-02T05:45:26.000-04:00", + last_posted_at: "2014-01-13T11:12:09.000-05:00", + bumped: true, + bumped_at: "2014-01-13T10:41:44.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: true, + views: 314, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 6266 }, + { extras: null, description: "Most Posts", user_id: 19 }, + { extras: null, description: "Frequent Poster", user_id: 1 }, + { extras: null, description: "Frequent Poster", user_id: 4457 }, + { + extras: "latest", + description: "Most Recent Poster, Frequent Poster", + user_id: 32 + } + ] + }, + { + id: 11371, + title: "Search not working for Staff users", + fancy_title: "Search not working for Staff users", + slug: "search-not-working-for-staff-users", + posts_count: 15, + reply_count: 10, + highest_post_number: 15, + image_url: null, + created_at: "2013-12-11T13:22:56.000-05:00", + last_posted_at: "2014-01-13T01:41:50.000-05:00", + bumped: true, + bumped_at: "2014-01-13T01:41:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: true, + views: 217, + like_count: 4, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 5335 }, + { extras: null, description: "Most Posts", user_id: 19 }, + { extras: null, description: "Frequent Poster", user_id: 6314 }, + { extras: null, description: "Frequent Poster", user_id: 32 }, + { extras: "latest", description: "Most Recent Poster", user_id: 1 } + ] + }, + { + id: 9908, + title: "Draft bar overrides pagination widget", + fancy_title: "Draft bar overrides pagination widget", + slug: "draft-bar-overrides-pagination-widget", + posts_count: 4, + reply_count: 0, + highest_post_number: 4, + image_url: null, + created_at: "2013-09-19T17:19:52.000-04:00", + last_posted_at: "2014-01-13T01:26:01.000-05:00", + bumped: true, + bumped_at: "2014-01-13T01:25:12.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: true, + views: 108, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 5351 }, + { extras: null, description: "Most Posts", user_id: 471 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 6134, + title: + "Unread topic is stuck as unread after insertion of staff message", + fancy_title: + "Unread topic is stuck as unread after insertion of staff message", + slug: + "unread-topic-is-stuck-as-unread-after-insertion-of-staff-message", + posts_count: 5, + reply_count: 1, + highest_post_number: 5, + image_url: + "https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8.png?s=40&r=pg&d=identicon", + created_at: "2013-04-24T13:37:32.000-04:00", + last_posted_at: "2014-01-13T01:22:49.000-05:00", + bumped: true, + bumped_at: "2014-01-13T01:22:42.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: true, + archived: false, + views: 169, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 7, + posters: [ + { extras: null, description: "Original Poster", user_id: 3681 }, + { extras: null, description: "Most Posts", user_id: 5351 }, + { extras: "latest", description: "Most Recent Poster", user_id: 32 } + ] + }, + { + id: 11914, + title: "Google analytics is not registering page views", + fancy_title: "Google analytics is not registering page views", + slug: "google-analytics-is-not-registering-page-views", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2014-01-13T00:32:45.000-05:00", + last_posted_at: "2014-01-13T00:32:46.000-05:00", + bumped: true, + bumped_at: "2014-01-13T00:32:46.000-05:00", + unseen: false, + pinned: false, + visible: true, + closed: false, + archived: false, + views: 37, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 7, + posters: [ + { + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 1 + } + ] + } + ] + } + }, + "/categories_and_latest.json": { + category_list: { + can_create_category: false, + can_create_topic: false, + draft: null, + draft_key: "new_topic", + draft_sequence: null, + categories: [ + { + id: 1, + name: "Uncategorized", + color: "AB9364", + text_color: "FFFFFF", + slug: "uncategorized", + topic_count: 1, + post_count: 0, + position: 0, + description: + "Topics that don't need a category, or don't fit into any other existing category.", + description_text: "", + topic_url: null, + logo_url: null, + background_url: null, + read_restricted: false, + permission: null, + notification_level: null, + topic_template: null, + has_children: false, + topics_day: 0, + topics_week: 0, + topics_month: 0, + topics_year: 0, + topics_all_time: 1, + description_excerpt: + "Topics that don't need a category, or don't fit into any other existing category.", + is_uncategorized: true + }, + { + id: 3, + name: "Site Feedback", + color: "808281", + text_color: "FFFFFF", + slug: "site-feedback", + topic_count: 0, + post_count: 0, + position: 1, + description: + "Discussion about this site, its organization, how it works, and how we can improve it.", + description_text: + "Discussion about this site, its organization, how it works, and how we can improve it.", + topic_url: "/t/about-the-site-feedback-category/2", + logo_url: null, + background_url: null, + read_restricted: false, + permission: null, + notification_level: null, + topic_template: null, + has_children: false, + topics_day: 0, + topics_week: 0, + topics_month: 0, + topics_year: 0, + topics_all_time: 0, + description_excerpt: + "Discussion about this site, its organization, how it works, and how we can improve it." + } + ] + }, + topic_list: { + can_create_topic: false, + draft: null, + draft_key: "new_topic", + draft_sequence: null, + per_page: 30, + topics: [ + { + id: 8, + title: "Welcome to Discourse", + fancy_title: "Welcome to Discourse", + slug: "welcome-to-discourse", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2016-08-29T20:38:19.359Z", + last_posted_at: "2016-08-29T20:38:19.402Z", + bumped: true, + bumped_at: "2016-08-29T20:38:19.402Z", + unseen: false, + pinned: true, + unpinned: null, + excerpt: + "The first paragraph of this pinned topic will be visible as a welcome message to all new visitors on your homepage. It's important! \n\nEdit this into a brief description of your community: \n\n\nWho is it for?\nWhat can they …", + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 0, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "system", + category_id: 1, + pinned_globally: true, + posters: [ + { + extras: "latest single", + description: "Original Poster, Most Recent Poster", + user_id: -1 + } + ] + } + ] + } + } }; diff --git a/test/javascripts/fixtures/group-fixtures.js.es6 b/test/javascripts/fixtures/group-fixtures.js.es6 index 4493e65df7b..50adde6fef0 100644 --- a/test/javascripts/fixtures/group-fixtures.js.es6 +++ b/test/javascripts/fixtures/group-fixtures.js.es6 @@ -1,1230 +1,1299 @@ export default { - "/groups/moderators.json":{ - "group": { - "id": 50, - "automatic": true, - "name": "moderators", - "display_name": "moderators", - "mentionable_level": 0, - "messageable_level": 99, - "visibility_level": 0, - "automatic_membership_email_domains": null, - "automatic_membership_retroactive": false, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": true, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 2, - "membership_request_template": null, - "is_group_user": true, - "is_group_owner": true, - "mentionable": false, - "messageable": true - }, - }, - "/groups/discourse.json":{ - "group":{ - "id":47, - "automatic":false, - "name":"discourse", - "full_name":"Awesome Team", - "user_count":8, - "alias_level":99, - "visible":true, - "public_admission":true, - "public_exit":false, - "flair_url": 'fa-adjust', - "is_group_owner":true, - "mentionable":true, - "messageable":true - }, - "extras": { - "visible_group_names": ["discourse"] + "/groups/moderators.json": { + group: { + id: 50, + automatic: true, + name: "moderators", + display_name: "moderators", + mentionable_level: 0, + messageable_level: 99, + visibility_level: 0, + automatic_membership_email_domains: null, + automatic_membership_retroactive: false, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: true, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 2, + membership_request_template: null, + is_group_user: true, + is_group_owner: true, + mentionable: false, + messageable: true } }, - "/topics/groups/discourse.json":{ - "users":[ + "/groups/discourse.json": { + group: { + id: 47, + automatic: false, + name: "discourse", + full_name: "Awesome Team", + user_count: 8, + alias_level: 99, + visible: true, + public_admission: true, + public_exit: false, + flair_url: "fa-adjust", + is_group_owner: true, + mentionable: true, + messageable: true + }, + extras: { + visible_group_names: ["discourse"] + } + }, + "/topics/groups/discourse.json": { + users: [ { - "id":2, - "username":"bruce1", - "avatar_template":"/user_avatar/meta.discourse.org/bruce1/{size}/5245.png" + id: 2, + username: "bruce1", + avatar_template: + "/user_avatar/meta.discourse.org/bruce1/{size}/5245.png" }, { - "id":1, - "username":"bruce0", - "avatar_template":"/user_avatar/meta.discourse.org/bruce0/{size}/5245.png" + id: 1, + username: "bruce0", + avatar_template: + "/user_avatar/meta.discourse.org/bruce0/{size}/5245.png" } ], - "primary_groups":[], - "topic_list":{ - "can_create_topic":true, - "draft":null, - "draft_key":"new_topic", - "draft_sequence":1, - "per_page":30, - "topics":[ + primary_groups: [], + topic_list: { + can_create_topic: true, + draft: null, + draft_key: "new_topic", + draft_sequence: 1, + per_page: 30, + topics: [ { - "id":12074, - "title":"This is a test topic 1", - "fancy_title":"This is a test topic 1", - "slug":"this-is-a-test-topic-1", - "posts_count":0, - "reply_count":0, - "highest_post_number":0, - "image_url":null, - "created_at":"2018-03-15T03:12:48.955Z", - "last_posted_at":null, - "bumped":true, - "bumped_at":"2018-03-15T03:12:48.955Z", - "unseen":true, - "pinned":false, - "unpinned":null, - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":0, - "like_count":0, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"bruce1", - "category_id":1, - "pinned_globally":false, - "featured_link":null, - "posters":[ + id: 12074, + title: "This is a test topic 1", + fancy_title: "This is a test topic 1", + slug: "this-is-a-test-topic-1", + posts_count: 0, + reply_count: 0, + highest_post_number: 0, + image_url: null, + created_at: "2018-03-15T03:12:48.955Z", + last_posted_at: null, + bumped: true, + bumped_at: "2018-03-15T03:12:48.955Z", + unseen: true, + pinned: false, + unpinned: null, + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 0, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "bruce1", + category_id: 1, + pinned_globally: false, + featured_link: null, + posters: [ { - "extras":"latest single", - "description":"Original Poster, Most Recent Poster", - "user_id":2, - "primary_group_id":null + extras: "latest single", + description: "Original Poster, Most Recent Poster", + user_id: 2, + primary_group_id: null } ] }, { - "id":12073, - "title":"This is a test topic 0", - "fancy_title":"This is a test topic 0", - "slug":"this-is-a-test-topic-0", - "posts_count":0, - "reply_count":0, - "highest_post_number":0, - "image_url":null, - "created_at":"2018-03-15T03:12:48.899Z", - "last_posted_at":null, - "bumped":true, - "bumped_at":"2018-03-15T03:12:48.900Z", - "unseen":true, - "pinned":false, - "unpinned":null, - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":0, - "like_count":0, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"bruce0", - "category_id":1, - "pinned_globally":false, - "featured_link":null, - "posters":[ + id: 12073, + title: "This is a test topic 0", + fancy_title: "This is a test topic 0", + slug: "this-is-a-test-topic-0", + posts_count: 0, + reply_count: 0, + highest_post_number: 0, + image_url: null, + created_at: "2018-03-15T03:12:48.899Z", + last_posted_at: null, + bumped: true, + bumped_at: "2018-03-15T03:12:48.900Z", + unseen: true, + pinned: false, + unpinned: null, + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 0, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "bruce0", + category_id: 1, + pinned_globally: false, + featured_link: null, + posters: [ { - "extras":"latest single", - "description":"Original Poster, Most Recent Poster", - "user_id":1, - "primary_group_id":null + extras: "latest single", + description: "Original Poster, Most Recent Poster", + user_id: 1, + primary_group_id: null } ] } ] } }, - "/groups/discourse/counts.json":{ - "counts":{ - "posts":17829, - "members":7 + "/groups/discourse/counts.json": { + counts: { + posts: 17829, + members: 7 } }, - "/groups/discourse/members.json":{ - "owners":[ - - ], - "members":[ + "/groups/discourse/members.json": { + owners: [], + members: [ { - "id":2770, - "username":"awesomerobot", - "uploaded_avatar_id":33872, - "avatar_template":"/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png", - "name":"", - "last_seen_at":"2015-01-23T15:53:17.844Z" + id: 2770, + username: "awesomerobot", + uploaded_avatar_id: 33872, + avatar_template: + "/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png", + name: "", + last_seen_at: "2015-01-23T15:53:17.844Z" }, { - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png", - "name":"Jeff Atwood", - "last_seen_at":"2015-01-23T06:05:25.457Z" + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png", + name: "Jeff Atwood", + last_seen_at: "2015-01-23T06:05:25.457Z" }, { - "id":19, - "username":"eviltrout", - "uploaded_avatar_id":5275, - "avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275.png", - "name":"Robin Ward", - "last_seen_at":"2015-01-23T16:03:45.098Z" + id: 19, + username: "eviltrout", + uploaded_avatar_id: 5275, + avatar_template: + "/user_avatar/meta.discourse.org/eviltrout/{size}/5275.png", + name: "Robin Ward", + last_seen_at: "2015-01-23T16:03:45.098Z" }, { - "id":2, - "username":"neil", - "uploaded_avatar_id":5245, - "avatar_template":"/user_avatar/meta.discourse.org/neil/{size}/5245.png", - "name":"Neil Lalonde", - "last_seen_at":"2015-01-23T15:22:10.244Z" + id: 2, + username: "neil", + uploaded_avatar_id: 5245, + avatar_template: "/user_avatar/meta.discourse.org/neil/{size}/5245.png", + name: "Neil Lalonde", + last_seen_at: "2015-01-23T15:22:10.244Z" }, { - "id":1, - "username":"sam", - "uploaded_avatar_id":5243, - "avatar_template":"/user_avatar/meta.discourse.org/sam/{size}/5243.png", - "name":"Sam Saffron", - "last_seen_at":"2015-01-23T11:07:06.233Z" + id: 1, + username: "sam", + uploaded_avatar_id: 5243, + avatar_template: "/user_avatar/meta.discourse.org/sam/{size}/5243.png", + name: "Sam Saffron", + last_seen_at: "2015-01-23T11:07:06.233Z" }, { - "id":3, - "username":"supermathie", - "uploaded_avatar_id":34097, - "avatar_template":"/user_avatar/meta.discourse.org/supermathie/{size}/34097.png", - "name":"Michael Brown", - "last_seen_at":"2015-01-22T05:16:42.254Z" + id: 3, + username: "supermathie", + uploaded_avatar_id: 34097, + avatar_template: + "/user_avatar/meta.discourse.org/supermathie/{size}/34097.png", + name: "Michael Brown", + last_seen_at: "2015-01-22T05:16:42.254Z" }, { - "id":1995, - "username":"zogstrip", - "uploaded_avatar_id":8630, - "avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png", - "name":"Régis Hanol", - "last_seen_at":"2015-01-23T15:45:34.196Z" + id: 1995, + username: "zogstrip", + uploaded_avatar_id: 8630, + avatar_template: + "/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png", + name: "Régis Hanol", + last_seen_at: "2015-01-23T15:45:34.196Z" } ], - "meta":{ - "total":7, - "limit":50, - "offset":0 + meta: { + total: 7, + limit: 50, + offset: 0 } }, - "/groups/discourse/posts.json":[ + "/groups/discourse/posts.json": [ { - "id":94607, - "cooked":"

I don't know how to pronounce that in English, but this makes me think of the French word \"disquette\" (floppy disk)

", - "created_at":"2015-01-23T15:13:01.935Z", - "title":"Consistent new indicator", - "url":"/t/consistent-new-indicator/24355/1", - "user_title":"designerator", - "user_long_name":"", - "category":{ - "id":9, - "name":"ux", - "color":"5F497A", - "topic_id":2628, - "topic_count":540, - "created_at":"2013-02-10T03:52:21.322Z", - "updated_at":"2015-01-22T18:05:32.152Z", - "user_id":32, - "topics_year":370, - "topics_month":33, - "topics_week":3, - "slug":"ux", - "description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":5823, - "latest_post_id":94610, - "latest_topic_id":24355, - "position":25, - "parent_category_id":null, - "posts_year":4264, - "posts_month":609, - "posts_week":103, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":0, - "posts_day":28, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"ux", - "auto_close_based_on_last_post":false + id: 94607, + cooked: + '

I don\'t know how to pronounce that in English, but this makes me think of the French word "disquette" (floppy disk)

', + created_at: "2015-01-23T15:13:01.935Z", + title: "Consistent new indicator", + url: "/t/consistent-new-indicator/24355/1", + user_title: "designerator", + user_long_name: "", + category: { + id: 9, + name: "ux", + color: "5F497A", + topic_id: 2628, + topic_count: 540, + created_at: "2013-02-10T03:52:21.322Z", + updated_at: "2015-01-22T18:05:32.152Z", + user_id: 32, + topics_year: 370, + topics_month: 33, + topics_week: 3, + slug: "ux", + description: + "Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 5823, + latest_post_id: 94610, + latest_topic_id: 24355, + position: 25, + parent_category_id: null, + posts_year: 4264, + posts_month: 609, + posts_week: 103, + email_in: null, + email_in_allow_strangers: false, + topics_day: 0, + posts_day: 28, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "ux", + auto_close_based_on_last_post: false }, - "user":{ - "id":2770, - "username":"awesomerobot", - "uploaded_avatar_id":33872, - "avatar_template":"/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png" + user: { + id: 2770, + username: "awesomerobot", + uploaded_avatar_id: 33872, + avatar_template: + "/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png" } }, { - "id":94603, - "cooked":"

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

", - "created_at":"2015-01-23T14:59:21.941Z", - "title":"The end of Clown Vomit, or, simplified category styles", - "url":"/t/the-end-of-clown-vomit-or-simplified-category-styles/24249/63", - "user_title":"designerator", - "user_long_name":"", - "category":{ - "id":9, - "name":"ux", - "color":"5F497A", - "topic_id":2628, - "topic_count":540, - "created_at":"2013-02-10T03:52:21.322Z", - "updated_at":"2015-01-22T18:05:32.152Z", - "user_id":32, - "topics_year":370, - "topics_month":33, - "topics_week":3, - "slug":"ux", - "description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":5823, - "latest_post_id":94610, - "latest_topic_id":24355, - "position":25, - "parent_category_id":null, - "posts_year":4264, - "posts_month":609, - "posts_week":103, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":0, - "posts_day":28, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"ux", - "auto_close_based_on_last_post":false + id: 94603, + cooked: + "

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

", + created_at: "2015-01-23T14:59:21.941Z", + title: "The end of Clown Vomit, or, simplified category styles", + url: "/t/the-end-of-clown-vomit-or-simplified-category-styles/24249/63", + user_title: "designerator", + user_long_name: "", + category: { + id: 9, + name: "ux", + color: "5F497A", + topic_id: 2628, + topic_count: 540, + created_at: "2013-02-10T03:52:21.322Z", + updated_at: "2015-01-22T18:05:32.152Z", + user_id: 32, + topics_year: 370, + topics_month: 33, + topics_week: 3, + slug: "ux", + description: + "Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 5823, + latest_post_id: 94610, + latest_topic_id: 24355, + position: 25, + parent_category_id: null, + posts_year: 4264, + posts_month: 609, + posts_week: 103, + email_in: null, + email_in_allow_strangers: false, + topics_day: 0, + posts_day: 28, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "ux", + auto_close_based_on_last_post: false }, - "user":{ - "id":2770, - "username":"awesomerobot", - "uploaded_avatar_id":33872, - "avatar_template":"/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png" + user: { + id: 2770, + username: "awesomerobot", + uploaded_avatar_id: 33872, + avatar_template: + "/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png" } }, { - "id":94601, - "cooked":"

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

", - "created_at":"2015-01-23T14:51:55.497Z", - "title":"The end of Clown Vomit, or, simplified category styles", - "url":"/t/the-end-of-clown-vomit-or-simplified-category-styles/24249/62", - "user_title":"designerator", - "user_long_name":"", - "category":{ - "id":9, - "name":"ux", - "color":"5F497A", - "topic_id":2628, - "topic_count":540, - "created_at":"2013-02-10T03:52:21.322Z", - "updated_at":"2015-01-22T18:05:32.152Z", - "user_id":32, - "topics_year":370, - "topics_month":33, - "topics_week":3, - "slug":"ux", - "description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":5823, - "latest_post_id":94610, - "latest_topic_id":24355, - "position":25, - "parent_category_id":null, - "posts_year":4264, - "posts_month":609, - "posts_week":103, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":0, - "posts_day":28, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"ux", - "auto_close_based_on_last_post":false + id: 94601, + cooked: + "

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

", + created_at: "2015-01-23T14:51:55.497Z", + title: "The end of Clown Vomit, or, simplified category styles", + url: "/t/the-end-of-clown-vomit-or-simplified-category-styles/24249/62", + user_title: "designerator", + user_long_name: "", + category: { + id: 9, + name: "ux", + color: "5F497A", + topic_id: 2628, + topic_count: 540, + created_at: "2013-02-10T03:52:21.322Z", + updated_at: "2015-01-22T18:05:32.152Z", + user_id: 32, + topics_year: 370, + topics_month: 33, + topics_week: 3, + slug: "ux", + description: + "Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 5823, + latest_post_id: 94610, + latest_topic_id: 24355, + position: 25, + parent_category_id: null, + posts_year: 4264, + posts_month: 609, + posts_week: 103, + email_in: null, + email_in_allow_strangers: false, + topics_day: 0, + posts_day: 28, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "ux", + auto_close_based_on_last_post: false }, - "user":{ - "id":2770, - "username":"awesomerobot", - "uploaded_avatar_id":33872, - "avatar_template":"/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png" + user: { + id: 2770, + username: "awesomerobot", + uploaded_avatar_id: 33872, + avatar_template: + "/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png" } }, { - "id":94577, - "cooked":"

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

", - "created_at":"2015-01-23T10:50:55.846Z", - "title":"Quote reply insertion at cursor position", - "url":"/t/quote-reply-insertion-at-cursor-position/24344/4", - "user_title":"team", - "user_long_name":"Régis Hanol", - "category":{ - "id":2, - "name":"feature", - "color":"0E76BD", - "topic_id":11, - "topic_count":1592, - "created_at":"2013-02-02T21:42:52.552Z", - "updated_at":"2015-01-22T18:05:32.647Z", - "user_id":1, - "topics_year":919, - "topics_month":60, - "topics_week":20, - "slug":"feature", - "description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":14360, - "latest_post_id":94600, - "latest_topic_id":24344, - "position":25, - "parent_category_id":null, - "posts_year":8617, - "posts_month":690, - "posts_week":190, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":2, - "posts_day":8, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"feature", - "auto_close_based_on_last_post":false + id: 94577, + cooked: + "

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

", + created_at: "2015-01-23T10:50:55.846Z", + title: "Quote reply insertion at cursor position", + url: "/t/quote-reply-insertion-at-cursor-position/24344/4", + user_title: "team", + user_long_name: "Régis Hanol", + category: { + id: 2, + name: "feature", + color: "0E76BD", + topic_id: 11, + topic_count: 1592, + created_at: "2013-02-02T21:42:52.552Z", + updated_at: "2015-01-22T18:05:32.647Z", + user_id: 1, + topics_year: 919, + topics_month: 60, + topics_week: 20, + slug: "feature", + description: + "Discussion about features or potential features of Discourse: how they work, why they work, etc.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 14360, + latest_post_id: 94600, + latest_topic_id: 24344, + position: 25, + parent_category_id: null, + posts_year: 8617, + posts_month: 690, + posts_week: 190, + email_in: null, + email_in_allow_strangers: false, + topics_day: 2, + posts_day: 8, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "feature", + auto_close_based_on_last_post: false }, - "user":{ - "id":1995, - "username":"zogstrip", - "uploaded_avatar_id":8630, - "avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png" + user: { + id: 1995, + username: "zogstrip", + uploaded_avatar_id: 8630, + avatar_template: + "/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png" } }, { - "id":94574, - "cooked":"

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

", - "created_at":"2015-01-23T10:31:29.222Z", - "title":"Quote reply insertion at cursor position", - "url":"/t/quote-reply-insertion-at-cursor-position/24344/2", - "user_title":"team", - "user_long_name":"Régis Hanol", - "category":{ - "id":2, - "name":"feature", - "color":"0E76BD", - "topic_id":11, - "topic_count":1592, - "created_at":"2013-02-02T21:42:52.552Z", - "updated_at":"2015-01-22T18:05:32.647Z", - "user_id":1, - "topics_year":919, - "topics_month":60, - "topics_week":20, - "slug":"feature", - "description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":14360, - "latest_post_id":94600, - "latest_topic_id":24344, - "position":25, - "parent_category_id":null, - "posts_year":8617, - "posts_month":690, - "posts_week":190, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":2, - "posts_day":8, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"feature", - "auto_close_based_on_last_post":false + id: 94574, + cooked: + "

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

", + created_at: "2015-01-23T10:31:29.222Z", + title: "Quote reply insertion at cursor position", + url: "/t/quote-reply-insertion-at-cursor-position/24344/2", + user_title: "team", + user_long_name: "Régis Hanol", + category: { + id: 2, + name: "feature", + color: "0E76BD", + topic_id: 11, + topic_count: 1592, + created_at: "2013-02-02T21:42:52.552Z", + updated_at: "2015-01-22T18:05:32.647Z", + user_id: 1, + topics_year: 919, + topics_month: 60, + topics_week: 20, + slug: "feature", + description: + "Discussion about features or potential features of Discourse: how they work, why they work, etc.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 14360, + latest_post_id: 94600, + latest_topic_id: 24344, + position: 25, + parent_category_id: null, + posts_year: 8617, + posts_month: 690, + posts_week: 190, + email_in: null, + email_in_allow_strangers: false, + topics_day: 2, + posts_day: 8, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "feature", + auto_close_based_on_last_post: false }, - "user":{ - "id":1995, - "username":"zogstrip", - "uploaded_avatar_id":8630, - "avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png" + user: { + id: 1995, + username: "zogstrip", + uploaded_avatar_id: 8630, + avatar_template: + "/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png" } }, { - "id":94572, - "cooked":"

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

", - "created_at":"2015-01-23T09:46:00.901Z", - "title":"Translations frequently broken", - "url":"/t/translations-frequently-broken/22546/27", - "user_title":"team", - "user_long_name":"Régis Hanol", - "category":{ - "id":27, - "name":"translations", - "color":"808281", - "topic_id":14549, - "topic_count":146, - "created_at":"2014-04-07T20:30:17.623Z", - "updated_at":"2015-01-22T18:05:33.111Z", - "user_id":2, - "topics_year":134, - "topics_month":5, - "topics_week":3, - "slug":"translations", - "description":"This category is for discussion about localizing Discourse.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":1167, - "latest_post_id":94575, - "latest_topic_id":24301, - "position":25, - "parent_category_id":7, - "posts_year":965, - "posts_month":60, - "posts_week":29, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":1, - "posts_day":5, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"translations", - "auto_close_based_on_last_post":false + id: 94572, + cooked: + "

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

", + created_at: "2015-01-23T09:46:00.901Z", + title: "Translations frequently broken", + url: "/t/translations-frequently-broken/22546/27", + user_title: "team", + user_long_name: "Régis Hanol", + category: { + id: 27, + name: "translations", + color: "808281", + topic_id: 14549, + topic_count: 146, + created_at: "2014-04-07T20:30:17.623Z", + updated_at: "2015-01-22T18:05:33.111Z", + user_id: 2, + topics_year: 134, + topics_month: 5, + topics_week: 3, + slug: "translations", + description: + "This category is for discussion about localizing Discourse.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 1167, + latest_post_id: 94575, + latest_topic_id: 24301, + position: 25, + parent_category_id: 7, + posts_year: 965, + posts_month: 60, + posts_week: 29, + email_in: null, + email_in_allow_strangers: false, + topics_day: 1, + posts_day: 5, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "translations", + auto_close_based_on_last_post: false }, - "user":{ - "id":1995, - "username":"zogstrip", - "uploaded_avatar_id":8630, - "avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png" + user: { + id: 1995, + username: "zogstrip", + uploaded_avatar_id: 8630, + avatar_template: + "/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png" } }, { - "id":94555, - "cooked":"

I don't know how to pronounce that in English, but this makes me think of the French word \"disquette\" (floppy disk)

", - "created_at":"2015-01-23T08:17:31.700Z", - "title":"Introducing Discette - a minimal ember-cli front end to Discourse", - "url":"/t/introducing-discette-a-minimal-ember-cli-front-end-to-discourse/24321/3", - "user_title":"team", - "user_long_name":"Régis Hanol", - "category":{ - "id":7, - "name":"dev", - "color":"000", - "topic_id":1026, - "topic_count":574, - "created_at":"2013-02-06T08:43:41.550Z", - "updated_at":"2015-01-22T18:05:32.855Z", - "user_id":32, - "topics_year":298, - "topics_month":29, - "topics_week":2, - "slug":"dev", - "description":"This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":4196, - "latest_post_id":94590, - "latest_topic_id":24349, - "position":25, - "parent_category_id":null, - "posts_year":2095, - "posts_month":172, - "posts_week":16, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":0, - "posts_day":3, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"dev", - "auto_close_based_on_last_post":false + id: 94555, + cooked: + '

I don\'t know how to pronounce that in English, but this makes me think of the French word "disquette" (floppy disk)

', + created_at: "2015-01-23T08:17:31.700Z", + title: + "Introducing Discette - a minimal ember-cli front end to Discourse", + url: + "/t/introducing-discette-a-minimal-ember-cli-front-end-to-discourse/24321/3", + user_title: "team", + user_long_name: "Régis Hanol", + category: { + id: 7, + name: "dev", + color: "000", + topic_id: 1026, + topic_count: 574, + created_at: "2013-02-06T08:43:41.550Z", + updated_at: "2015-01-22T18:05:32.855Z", + user_id: 32, + topics_year: 298, + topics_month: 29, + topics_week: 2, + slug: "dev", + description: + "This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 4196, + latest_post_id: 94590, + latest_topic_id: 24349, + position: 25, + parent_category_id: null, + posts_year: 2095, + posts_month: 172, + posts_week: 16, + email_in: null, + email_in_allow_strangers: false, + topics_day: 0, + posts_day: 3, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "dev", + auto_close_based_on_last_post: false }, - "user":{ - "id":1995, - "username":"zogstrip", - "uploaded_avatar_id":8630, - "avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png" + user: { + id: 1995, + username: "zogstrip", + uploaded_avatar_id: 8630, + avatar_template: + "/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png" } }, { - "id":94544, - "cooked":"

@techapj fixed this for 1.2.

", - "created_at":"2015-01-23T05:49:35.881Z", - "title":"After sign-in, I'm not redirected to the conversation", - "url":"/t/after-sign-in-im-not-redirected-to-the-conversation/17753/8", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":9, - "name":"ux", - "color":"5F497A", - "topic_id":2628, - "topic_count":540, - "created_at":"2013-02-10T03:52:21.322Z", - "updated_at":"2015-01-22T18:05:32.152Z", - "user_id":32, - "topics_year":370, - "topics_month":33, - "topics_week":3, - "slug":"ux", - "description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":5823, - "latest_post_id":94610, - "latest_topic_id":24355, - "position":25, - "parent_category_id":null, - "posts_year":4264, - "posts_month":609, - "posts_week":103, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":0, - "posts_day":28, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"ux", - "auto_close_based_on_last_post":false + id: 94544, + cooked: + '

@techapj fixed this for 1.2.

', + created_at: "2015-01-23T05:49:35.881Z", + title: "After sign-in, I'm not redirected to the conversation", + url: "/t/after-sign-in-im-not-redirected-to-the-conversation/17753/8", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 9, + name: "ux", + color: "5F497A", + topic_id: 2628, + topic_count: 540, + created_at: "2013-02-10T03:52:21.322Z", + updated_at: "2015-01-22T18:05:32.152Z", + user_id: 32, + topics_year: 370, + topics_month: 33, + topics_week: 3, + slug: "ux", + description: + "Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 5823, + latest_post_id: 94610, + latest_topic_id: 24355, + position: 25, + parent_category_id: null, + posts_year: 4264, + posts_month: 609, + posts_week: 103, + email_in: null, + email_in_allow_strangers: false, + topics_day: 0, + posts_day: 28, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "ux", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94543, - "cooked":"

Oh yes IOS 8.2 -- well, let's see what happens because there is really no fix on our end. Basic HTML / CSS stuff is broken.

", - "created_at":"2015-01-23T05:45:40.306Z", - "title":"Dealing with iOS 8 Mobile Safari bugs?", - "url":"/t/dealing-with-ios-8-mobile-safari-bugs/24101/7", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":2, - "name":"feature", - "color":"0E76BD", - "topic_id":11, - "topic_count":1592, - "created_at":"2013-02-02T21:42:52.552Z", - "updated_at":"2015-01-22T18:05:32.647Z", - "user_id":1, - "topics_year":919, - "topics_month":60, - "topics_week":20, - "slug":"feature", - "description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":14360, - "latest_post_id":94600, - "latest_topic_id":24344, - "position":25, - "parent_category_id":null, - "posts_year":8617, - "posts_month":690, - "posts_week":190, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":2, - "posts_day":8, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"feature", - "auto_close_based_on_last_post":false + id: 94543, + cooked: + "

Oh yes IOS 8.2 -- well, let's see what happens because there is really no fix on our end. Basic HTML / CSS stuff is broken.

", + created_at: "2015-01-23T05:45:40.306Z", + title: "Dealing with iOS 8 Mobile Safari bugs?", + url: "/t/dealing-with-ios-8-mobile-safari-bugs/24101/7", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 2, + name: "feature", + color: "0E76BD", + topic_id: 11, + topic_count: 1592, + created_at: "2013-02-02T21:42:52.552Z", + updated_at: "2015-01-22T18:05:32.647Z", + user_id: 1, + topics_year: 919, + topics_month: 60, + topics_week: 20, + slug: "feature", + description: + "Discussion about features or potential features of Discourse: how they work, why they work, etc.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 14360, + latest_post_id: 94600, + latest_topic_id: 24344, + position: 25, + parent_category_id: null, + posts_year: 8617, + posts_month: 690, + posts_week: 190, + email_in: null, + email_in_allow_strangers: false, + topics_day: 2, + posts_day: 8, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "feature", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94542, - "cooked":"

Hmm that looks like a bug, @techapj can you have a look?

", - "created_at":"2015-01-23T05:43:55.602Z", - "title":"RSS is not valid", - "url":"/t/rss-is-not-valid/24338/2", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":6, - "name":"support", - "color":"CEA9A9", - "topic_id":389, - "topic_count":1781, - "created_at":"2013-02-05T22:16:38.672Z", - "updated_at":"2015-01-22T18:05:33.572Z", - "user_id":1, - "topics_year":1541, - "topics_month":167, - "topics_week":49, - "slug":"support", - "description":"Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":12272, - "latest_post_id":94602, - "latest_topic_id":24346, - "position":25, - "parent_category_id":null, - "posts_year":10571, - "posts_month":1254, - "posts_week":413, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":5, - "posts_day":70, - "logo_url":"", - "background_url":"", - "allow_badges":true, - "name_lower":"support", - "auto_close_based_on_last_post":false + id: 94542, + cooked: + '

Hmm that looks like a bug, @techapj can you have a look?

', + created_at: "2015-01-23T05:43:55.602Z", + title: "RSS is not valid", + url: "/t/rss-is-not-valid/24338/2", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 6, + name: "support", + color: "CEA9A9", + topic_id: 389, + topic_count: 1781, + created_at: "2013-02-05T22:16:38.672Z", + updated_at: "2015-01-22T18:05:33.572Z", + user_id: 1, + topics_year: 1541, + topics_month: 167, + topics_week: 49, + slug: "support", + description: + "Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 12272, + latest_post_id: 94602, + latest_topic_id: 24346, + position: 25, + parent_category_id: null, + posts_year: 10571, + posts_month: 1254, + posts_week: 413, + email_in: null, + email_in_allow_strangers: false, + topics_day: 5, + posts_day: 70, + logo_url: "", + background_url: "", + allow_badges: true, + name_lower: "support", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94522, - "cooked":"

Oh I see. @zogstrip can you have a look?

", - "created_at":"2015-01-23T03:00:20.485Z", - "title":"Pasted image upload size error", - "url":"/t/pasted-image-upload-size-error/24320/4", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":1, - "name":"bug", - "color":"e9dd00", - "topic_id":2, - "topic_count":1729, - "created_at":"2013-02-01T04:56:34.914Z", - "updated_at":"2015-01-22T18:05:33.426Z", - "user_id":1, - "topics_year":1114, - "topics_month":69, - "topics_week":22, - "slug":"bug", - "description":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.", - "text_color":"000000", - "read_restricted":false, - "auto_close_hours":null, - "post_count":11179, - "latest_post_id":94611, - "latest_topic_id":24350, - "position":25, - "parent_category_id":null, - "posts_year":7138, - "posts_month":397, - "posts_week":121, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":1, - "posts_day":6, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"bug", - "auto_close_based_on_last_post":false + id: 94522, + cooked: + '

Oh I see. @zogstrip can you have a look?

', + created_at: "2015-01-23T03:00:20.485Z", + title: "Pasted image upload size error", + url: "/t/pasted-image-upload-size-error/24320/4", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 1, + name: "bug", + color: "e9dd00", + topic_id: 2, + topic_count: 1729, + created_at: "2013-02-01T04:56:34.914Z", + updated_at: "2015-01-22T18:05:33.426Z", + user_id: 1, + topics_year: 1114, + topics_month: 69, + topics_week: 22, + slug: "bug", + description: + "A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.", + text_color: "000000", + read_restricted: false, + auto_close_hours: null, + post_count: 11179, + latest_post_id: 94611, + latest_topic_id: 24350, + position: 25, + parent_category_id: null, + posts_year: 7138, + posts_month: 397, + posts_week: 121, + email_in: null, + email_in_allow_strangers: false, + topics_day: 1, + posts_day: 6, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "bug", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94521, - "cooked":"

@techapj fixed this for 1.2.

", - "created_at":"2015-01-23T02:58:27.451Z", - "title":"The end of Clown Vomit, or, simplified category styles", - "url":"/t/the-end-of-clown-vomit-or-simplified-category-styles/24249/57", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":9, - "name":"ux", - "color":"5F497A", - "topic_id":2628, - "topic_count":540, - "created_at":"2013-02-10T03:52:21.322Z", - "updated_at":"2015-01-22T18:05:32.152Z", - "user_id":32, - "topics_year":370, - "topics_month":33, - "topics_week":3, - "slug":"ux", - "description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":5823, - "latest_post_id":94610, - "latest_topic_id":24355, - "position":25, - "parent_category_id":null, - "posts_year":4264, - "posts_month":609, - "posts_week":103, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":0, - "posts_day":28, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"ux", - "auto_close_based_on_last_post":false + id: 94521, + cooked: + '

@techapj fixed this for 1.2.

', + created_at: "2015-01-23T02:58:27.451Z", + title: "The end of Clown Vomit, or, simplified category styles", + url: "/t/the-end-of-clown-vomit-or-simplified-category-styles/24249/57", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 9, + name: "ux", + color: "5F497A", + topic_id: 2628, + topic_count: 540, + created_at: "2013-02-10T03:52:21.322Z", + updated_at: "2015-01-22T18:05:32.152Z", + user_id: 32, + topics_year: 370, + topics_month: 33, + topics_week: 3, + slug: "ux", + description: + "Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 5823, + latest_post_id: 94610, + latest_topic_id: 24355, + position: 25, + parent_category_id: null, + posts_year: 4264, + posts_month: 609, + posts_week: 103, + email_in: null, + email_in_allow_strangers: false, + topics_day: 0, + posts_day: 28, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "ux", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94519, - "cooked":"

What would you suggest writing here that would be more clear?

", - "created_at":"2015-01-23T02:45:36.859Z", - "title":"What is \"Born mobile, born to touch\" supposed to tell me?", - "url":"/t/what-is-born-mobile-born-to-touch-supposed-to-tell-me/24329/3", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":3, - "name":"meta", - "color":"aaa", - "topic_id":24, - "topic_count":139, - "created_at":"2013-02-03T00:00:15.230Z", - "updated_at":"2015-01-22T18:05:32.797Z", - "user_id":1, - "topics_year":68, - "topics_month":5, - "topics_week":1, - "slug":"meta", - "description":"Discussion about meta.discourse.org itself, the organization of this forum about Discourse, how it works, and how we can improve this site.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":1116, - "latest_post_id":94559, - "latest_topic_id":24208, - "position":25, - "parent_category_id":null, - "posts_year":553, - "posts_month":33, - "posts_week":8, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":0, - "posts_day":0, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"meta", - "auto_close_based_on_last_post":false + id: 94519, + cooked: + "

What would you suggest writing here that would be more clear?

", + created_at: "2015-01-23T02:45:36.859Z", + title: 'What is "Born mobile, born to touch" supposed to tell me?', + url: "/t/what-is-born-mobile-born-to-touch-supposed-to-tell-me/24329/3", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 3, + name: "meta", + color: "aaa", + topic_id: 24, + topic_count: 139, + created_at: "2013-02-03T00:00:15.230Z", + updated_at: "2015-01-22T18:05:32.797Z", + user_id: 1, + topics_year: 68, + topics_month: 5, + topics_week: 1, + slug: "meta", + description: + "Discussion about meta.discourse.org itself, the organization of this forum about Discourse, how it works, and how we can improve this site.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 1116, + latest_post_id: 94559, + latest_topic_id: 24208, + position: 25, + parent_category_id: null, + posts_year: 553, + posts_month: 33, + posts_week: 8, + email_in: null, + email_in_allow_strangers: false, + topics_day: 0, + posts_day: 0, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "meta", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94518, - "cooked":"

You should generally create topics to host things like this, then make them wiki, close them, etc.

", - "created_at":"2015-01-23T02:42:20.053Z", - "title":"How to Create Static Pages in Discourse?", - "url":"/t/how-to-create-static-pages-in-discourse/24313/2", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":6, - "name":"support", - "color":"CEA9A9", - "topic_id":389, - "topic_count":1781, - "created_at":"2013-02-05T22:16:38.672Z", - "updated_at":"2015-01-22T18:05:33.572Z", - "user_id":1, - "topics_year":1541, - "topics_month":167, - "topics_week":49, - "slug":"support", - "description":"Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":12272, - "latest_post_id":94602, - "latest_topic_id":24346, - "position":25, - "parent_category_id":null, - "posts_year":10571, - "posts_month":1254, - "posts_week":413, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":5, - "posts_day":70, - "logo_url":"", - "background_url":"", - "allow_badges":true, - "name_lower":"support", - "auto_close_based_on_last_post":false + id: 94518, + cooked: + "

You should generally create topics to host things like this, then make them wiki, close them, etc.

", + created_at: "2015-01-23T02:42:20.053Z", + title: "How to Create Static Pages in Discourse?", + url: "/t/how-to-create-static-pages-in-discourse/24313/2", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 6, + name: "support", + color: "CEA9A9", + topic_id: 389, + topic_count: 1781, + created_at: "2013-02-05T22:16:38.672Z", + updated_at: "2015-01-22T18:05:33.572Z", + user_id: 1, + topics_year: 1541, + topics_month: 167, + topics_week: 49, + slug: "support", + description: + "Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 12272, + latest_post_id: 94602, + latest_topic_id: 24346, + position: 25, + parent_category_id: null, + posts_year: 10571, + posts_month: 1254, + posts_week: 413, + email_in: null, + email_in_allow_strangers: false, + topics_day: 5, + posts_day: 70, + logo_url: "", + background_url: "", + allow_badges: true, + name_lower: "support", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94517, - "cooked":"

Doubtful this is a bug, probably dependent on the PNG encoding.

\n\n

Try using PNGOUT, or converting to 8 bit PNGOUT, to see some of the differences. And PNGOUT is lossless!

", - "created_at":"2015-01-23T02:41:30.287Z", - "title":"Pasted image upload size error", - "url":"/t/pasted-image-upload-size-error/24320/2", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":1, - "name":"bug", - "color":"e9dd00", - "topic_id":2, - "topic_count":1729, - "created_at":"2013-02-01T04:56:34.914Z", - "updated_at":"2015-01-22T18:05:33.426Z", - "user_id":1, - "topics_year":1114, - "topics_month":69, - "topics_week":22, - "slug":"bug", - "description":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.", - "text_color":"000000", - "read_restricted":false, - "auto_close_hours":null, - "post_count":11179, - "latest_post_id":94611, - "latest_topic_id":24350, - "position":25, - "parent_category_id":null, - "posts_year":7138, - "posts_month":397, - "posts_week":121, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":1, - "posts_day":6, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"bug", - "auto_close_based_on_last_post":false + id: 94517, + cooked: + "

Doubtful this is a bug, probably dependent on the PNG encoding.

\n\n

Try using PNGOUT, or converting to 8 bit PNGOUT, to see some of the differences. And PNGOUT is lossless!

", + created_at: "2015-01-23T02:41:30.287Z", + title: "Pasted image upload size error", + url: "/t/pasted-image-upload-size-error/24320/2", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 1, + name: "bug", + color: "e9dd00", + topic_id: 2, + topic_count: 1729, + created_at: "2013-02-01T04:56:34.914Z", + updated_at: "2015-01-22T18:05:33.426Z", + user_id: 1, + topics_year: 1114, + topics_month: 69, + topics_week: 22, + slug: "bug", + description: + "A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.", + text_color: "000000", + read_restricted: false, + auto_close_hours: null, + post_count: 11179, + latest_post_id: 94611, + latest_topic_id: 24350, + position: 25, + parent_category_id: null, + posts_year: 7138, + posts_month: 397, + posts_week: 121, + email_in: null, + email_in_allow_strangers: false, + topics_day: 1, + posts_day: 6, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "bug", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94516, - "cooked":"

I would worry about getting your expenses down to $5 per month, that seems more likely over time as hosting for Docker compliant sites gets cheaper.

", - "created_at":"2015-01-23T02:40:11.726Z", - "title":"Monetizing Discourse Talk", - "url":"/t/monetizing-discourse-talk/24316/4", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":6, - "name":"support", - "color":"CEA9A9", - "topic_id":389, - "topic_count":1781, - "created_at":"2013-02-05T22:16:38.672Z", - "updated_at":"2015-01-22T18:05:33.572Z", - "user_id":1, - "topics_year":1541, - "topics_month":167, - "topics_week":49, - "slug":"support", - "description":"Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":12272, - "latest_post_id":94602, - "latest_topic_id":24346, - "position":25, - "parent_category_id":null, - "posts_year":10571, - "posts_month":1254, - "posts_week":413, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":5, - "posts_day":70, - "logo_url":"", - "background_url":"", - "allow_badges":true, - "name_lower":"support", - "auto_close_based_on_last_post":false + id: 94516, + cooked: + "

I would worry about getting your expenses down to $5 per month, that seems more likely over time as hosting for Docker compliant sites gets cheaper.

", + created_at: "2015-01-23T02:40:11.726Z", + title: "Monetizing Discourse Talk", + url: "/t/monetizing-discourse-talk/24316/4", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 6, + name: "support", + color: "CEA9A9", + topic_id: 389, + topic_count: 1781, + created_at: "2013-02-05T22:16:38.672Z", + updated_at: "2015-01-22T18:05:33.572Z", + user_id: 1, + topics_year: 1541, + topics_month: 167, + topics_week: 49, + slug: "support", + description: + "Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 12272, + latest_post_id: 94602, + latest_topic_id: 24346, + position: 25, + parent_category_id: null, + posts_year: 10571, + posts_month: 1254, + posts_week: 413, + email_in: null, + email_in_allow_strangers: false, + topics_day: 5, + posts_day: 70, + logo_url: "", + background_url: "", + allow_badges: true, + name_lower: "support", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94515, - "cooked":"

I would worry about getting your expenses down to $5 per month, that seems more likely over time as hosting for Docker compliant sites gets cheaper.

", - "created_at":"2015-01-23T02:38:29.185Z", - "title":"Introducing Discette - a minimal ember-cli front end to Discourse", - "url":"/t/introducing-discette-a-minimal-ember-cli-front-end-to-discourse/24321/2", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":7, - "name":"dev", - "color":"000", - "topic_id":1026, - "topic_count":574, - "created_at":"2013-02-06T08:43:41.550Z", - "updated_at":"2015-01-22T18:05:32.855Z", - "user_id":32, - "topics_year":298, - "topics_month":29, - "topics_week":2, - "slug":"dev", - "description":"This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":4196, - "latest_post_id":94590, - "latest_topic_id":24349, - "position":25, - "parent_category_id":null, - "posts_year":2095, - "posts_month":172, - "posts_week":16, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":0, - "posts_day":3, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"dev", - "auto_close_based_on_last_post":false + id: 94515, + cooked: + "

I would worry about getting your expenses down to $5 per month, that seems more likely over time as hosting for Docker compliant sites gets cheaper.

", + created_at: "2015-01-23T02:38:29.185Z", + title: + "Introducing Discette - a minimal ember-cli front end to Discourse", + url: + "/t/introducing-discette-a-minimal-ember-cli-front-end-to-discourse/24321/2", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 7, + name: "dev", + color: "000", + topic_id: 1026, + topic_count: 574, + created_at: "2013-02-06T08:43:41.550Z", + updated_at: "2015-01-22T18:05:32.855Z", + user_id: 32, + topics_year: 298, + topics_month: 29, + topics_week: 2, + slug: "dev", + description: + "This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 4196, + latest_post_id: 94590, + latest_topic_id: 24349, + position: 25, + parent_category_id: null, + posts_year: 2095, + posts_month: 172, + posts_week: 16, + email_in: null, + email_in_allow_strangers: false, + topics_day: 0, + posts_day: 3, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "dev", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94514, - "cooked":"

I would worry about getting your expenses down to $5 per month, that seems more likely over time as hosting for Docker compliant sites gets cheaper.

", - "created_at":"2015-01-23T02:37:39.518Z", - "title":"How to do \"Object Oriented Discussion\" through Oneboxes?", - "url":"/t/how-to-do-object-oriented-discussion-through-oneboxes/24328/2", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":5, - "name":"extensibility", - "color":"FE8432", - "topic_id":28, - "topic_count":295, - "created_at":"2013-02-03T08:42:06.329Z", - "updated_at":"2015-01-22T18:05:32.698Z", - "user_id":1, - "topics_year":187, - "topics_month":17, - "topics_week":7, - "slug":"extensibility", - "description":"Topics about extending the functionality of Discourse with plugins, themes, add-ons, or other mechanisms for extensibility. ", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":2574, - "latest_post_id":94582, - "latest_topic_id":24328, - "position":25, - "parent_category_id":null, - "posts_year":1485, - "posts_month":196, - "posts_week":52, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":2, - "posts_day":8, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"extensibility", - "auto_close_based_on_last_post":false + id: 94514, + cooked: + "

I would worry about getting your expenses down to $5 per month, that seems more likely over time as hosting for Docker compliant sites gets cheaper.

", + created_at: "2015-01-23T02:37:39.518Z", + title: 'How to do "Object Oriented Discussion" through Oneboxes?', + url: "/t/how-to-do-object-oriented-discussion-through-oneboxes/24328/2", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 5, + name: "extensibility", + color: "FE8432", + topic_id: 28, + topic_count: 295, + created_at: "2013-02-03T08:42:06.329Z", + updated_at: "2015-01-22T18:05:32.698Z", + user_id: 1, + topics_year: 187, + topics_month: 17, + topics_week: 7, + slug: "extensibility", + description: + "Topics about extending the functionality of Discourse with plugins, themes, add-ons, or other mechanisms for extensibility. ", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 2574, + latest_post_id: 94582, + latest_topic_id: 24328, + position: 25, + parent_category_id: null, + posts_year: 1485, + posts_month: 196, + posts_week: 52, + email_in: null, + email_in_allow_strangers: false, + topics_day: 2, + posts_day: 8, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "extensibility", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94512, - "cooked":"

Hmm, have not seen problems updating on 1gb instance provided swap is there.

\n\n

Anything else running on the machine?

\n\n

Maybe reboot, then upgrade Docker from command line, then upgrade Discourse from command line.

", - "created_at":"2015-01-23T02:32:31.383Z", - "title":"Update Failed and Now Showing Currently Upgrading", - "url":"/t/update-failed-and-now-showing-currently-upgrading/24332/2", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":6, - "name":"support", - "color":"CEA9A9", - "topic_id":389, - "topic_count":1781, - "created_at":"2013-02-05T22:16:38.672Z", - "updated_at":"2015-01-22T18:05:33.572Z", - "user_id":1, - "topics_year":1541, - "topics_month":167, - "topics_week":49, - "slug":"support", - "description":"Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":12272, - "latest_post_id":94602, - "latest_topic_id":24346, - "position":25, - "parent_category_id":null, - "posts_year":10571, - "posts_month":1254, - "posts_week":413, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":5, - "posts_day":70, - "logo_url":"", - "background_url":"", - "allow_badges":true, - "name_lower":"support", - "auto_close_based_on_last_post":false + id: 94512, + cooked: + "

Hmm, have not seen problems updating on 1gb instance provided swap is there.

\n\n

Anything else running on the machine?

\n\n

Maybe reboot, then upgrade Docker from command line, then upgrade Discourse from command line.

", + created_at: "2015-01-23T02:32:31.383Z", + title: "Update Failed and Now Showing Currently Upgrading", + url: "/t/update-failed-and-now-showing-currently-upgrading/24332/2", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 6, + name: "support", + color: "CEA9A9", + topic_id: 389, + topic_count: 1781, + created_at: "2013-02-05T22:16:38.672Z", + updated_at: "2015-01-22T18:05:33.572Z", + user_id: 1, + topics_year: 1541, + topics_month: 167, + topics_week: 49, + slug: "support", + description: + "Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 12272, + latest_post_id: 94602, + latest_topic_id: 24346, + position: 25, + parent_category_id: null, + posts_year: 10571, + posts_month: 1254, + posts_week: 413, + email_in: null, + email_in_allow_strangers: false, + topics_day: 5, + posts_day: 70, + logo_url: "", + background_url: "", + allow_badges: true, + name_lower: "support", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } }, { - "id":94511, - "cooked":"

Hmm, not sure about that, good odds they will be fixed in iOS 8.1 which is due soon.

", - "created_at":"2015-01-23T02:27:16.786Z", - "title":"Dealing with iOS 8 Mobile Safari bugs?", - "url":"/t/dealing-with-ios-8-mobile-safari-bugs/24101/5", - "user_title":"co-founder", - "user_long_name":"Jeff Atwood", - "category":{ - "id":2, - "name":"feature", - "color":"0E76BD", - "topic_id":11, - "topic_count":1592, - "created_at":"2013-02-02T21:42:52.552Z", - "updated_at":"2015-01-22T18:05:32.647Z", - "user_id":1, - "topics_year":919, - "topics_month":60, - "topics_week":20, - "slug":"feature", - "description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.", - "text_color":"FFFFFF", - "read_restricted":false, - "auto_close_hours":null, - "post_count":14360, - "latest_post_id":94600, - "latest_topic_id":24344, - "position":25, - "parent_category_id":null, - "posts_year":8617, - "posts_month":690, - "posts_week":190, - "email_in":null, - "email_in_allow_strangers":false, - "topics_day":2, - "posts_day":8, - "logo_url":null, - "background_url":null, - "allow_badges":true, - "name_lower":"feature", - "auto_close_based_on_last_post":false + id: 94511, + cooked: + "

Hmm, not sure about that, good odds they will be fixed in iOS 8.1 which is due soon.

", + created_at: "2015-01-23T02:27:16.786Z", + title: "Dealing with iOS 8 Mobile Safari bugs?", + url: "/t/dealing-with-ios-8-mobile-safari-bugs/24101/5", + user_title: "co-founder", + user_long_name: "Jeff Atwood", + category: { + id: 2, + name: "feature", + color: "0E76BD", + topic_id: 11, + topic_count: 1592, + created_at: "2013-02-02T21:42:52.552Z", + updated_at: "2015-01-22T18:05:32.647Z", + user_id: 1, + topics_year: 919, + topics_month: 60, + topics_week: 20, + slug: "feature", + description: + "Discussion about features or potential features of Discourse: how they work, why they work, etc.", + text_color: "FFFFFF", + read_restricted: false, + auto_close_hours: null, + post_count: 14360, + latest_post_id: 94600, + latest_topic_id: 24344, + position: 25, + parent_category_id: null, + posts_year: 8617, + posts_month: 690, + posts_week: 190, + email_in: null, + email_in_allow_strangers: false, + topics_day: 2, + posts_day: 8, + logo_url: null, + background_url: null, + allow_badges: true, + name_lower: "feature", + auto_close_based_on_last_post: false }, - "user":{ - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" + user: { + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png" } } ] diff --git a/test/javascripts/fixtures/groups-fixtures.js.es6 b/test/javascripts/fixtures/groups-fixtures.js.es6 index 79f47e64cb1..136f8bcb392 100644 --- a/test/javascripts/fixtures/groups-fixtures.js.es6 +++ b/test/javascripts/fixtures/groups-fixtures.js.es6 @@ -1,3 +1,54 @@ export default { - "/groups.json": {"groups":[{"id":41,"automatic":false,"name":"discourse","user_count":0,"alias_level":0,"visible":true,"automatic_membership_email_domains":"","automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":"","bio_cooked":null,"public_admission":true,"allow_membership_requests":false,"full_name":"Awesome Team"},{"id":42,"automatic":false,"name":"Macdonald","user_count":0,"alias_level":99,"visible":true,"automatic_membership_email_domains":"","automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public_admission":false,"allow_membership_requests":true,"membership_request_template":"Please add me","full_name":null}],"extras":{"group_user_ids":[]},"total_rows_groups":2,"load_more_groups":"/groups?page=1"} -} + "/groups.json": { + groups: [ + { + id: 41, + automatic: false, + name: "discourse", + user_count: 0, + alias_level: 0, + visible: true, + automatic_membership_email_domains: "", + automatic_membership_retroactive: false, + primary_group: false, + title: null, + grant_trust_level: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: "", + bio_cooked: null, + public_admission: true, + allow_membership_requests: false, + full_name: "Awesome Team" + }, + { + id: 42, + automatic: false, + name: "Macdonald", + user_count: 0, + alias_level: 99, + visible: true, + automatic_membership_email_domains: "", + automatic_membership_retroactive: false, + primary_group: false, + title: null, + grant_trust_level: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + public_admission: false, + allow_membership_requests: true, + membership_request_template: "Please add me", + full_name: null + } + ], + extras: { group_user_ids: [] }, + total_rows_groups: 2, + load_more_groups: "/groups?page=1" + } +}; diff --git a/test/javascripts/fixtures/new-contributors.js.es6 b/test/javascripts/fixtures/new-contributors.js.es6 index fc3d6f86c9e..5b89bc059b4 100644 --- a/test/javascripts/fixtures/new-contributors.js.es6 +++ b/test/javascripts/fixtures/new-contributors.js.es6 @@ -1,49 +1,60 @@ export default { "/admin/reports/new_contributors": { - "report": { - "type": "new_contributors", - "title": "New Contributors", - "xaxis": "", - "yaxis": "", - "data": [{ - "x": "2018-04-11", - "y": 10 - }, { - "x": "2018-04-12", - "y": 10 - }, { - "x": "2018-04-13", - "y": 60 - }, { - "x": "2018-04-14", - "y": 60 - }, { - "x": "2018-04-15", - "y": 10 - }, { - "x": "2018-04-16", - "y": 10 - }, { - "x": "2018-04-17", - "y": 10 - }, { - "x": "2018-04-19", - "y": 10 - }, { - "x": "2018-04-18", - "y": 10 - }, { - "x": "2018-04-20", - "y": 1 - }], - "total": 121, - "start_date": "2018-03-26T00:00:00.000Z", - "end_date": "2018-04-25T23:59:59.999Z", - "category_id": null, - "group_id": null, - "prev30Days": null, - "labels": null, - "report_key": "" + report: { + type: "new_contributors", + title: "New Contributors", + xaxis: "", + yaxis: "", + data: [ + { + x: "2018-04-11", + y: 10 + }, + { + x: "2018-04-12", + y: 10 + }, + { + x: "2018-04-13", + y: 60 + }, + { + x: "2018-04-14", + y: 60 + }, + { + x: "2018-04-15", + y: 10 + }, + { + x: "2018-04-16", + y: 10 + }, + { + x: "2018-04-17", + y: 10 + }, + { + x: "2018-04-19", + y: 10 + }, + { + x: "2018-04-18", + y: 10 + }, + { + x: "2018-04-20", + y: 1 + } + ], + total: 121, + start_date: "2018-03-26T00:00:00.000Z", + end_date: "2018-04-25T23:59:59.999Z", + category_id: null, + group_id: null, + prev30Days: null, + labels: null, + report_key: "" } } }; diff --git a/test/javascripts/fixtures/notification_fixtures.js.es6 b/test/javascripts/fixtures/notification_fixtures.js.es6 index a1b092c503c..d9669d9d9b2 100644 --- a/test/javascripts/fixtures/notification_fixtures.js.es6 +++ b/test/javascripts/fixtures/notification_fixtures.js.es6 @@ -1,2 +1,16 @@ /*jshint maxlen:10000000 */ -export default {"/notifications": {notifications: [ { id: 123, notification_type: 2, read: false, post_number: 2, topic_id: 1234, slug: "a-slug", data: { topic_title: "some title", display_username: "velesin" } } ] }}; +export default { + "/notifications": { + notifications: [ + { + id: 123, + notification_type: 2, + read: false, + post_number: 2, + topic_id: 1234, + slug: "a-slug", + data: { topic_title: "some title", display_username: "velesin" } + } + ] + } +}; diff --git a/test/javascripts/fixtures/post.js.es6 b/test/javascripts/fixtures/post.js.es6 index 345c20a1bec..28288e47394 100644 --- a/test/javascripts/fixtures/post.js.es6 +++ b/test/javascripts/fixtures/post.js.es6 @@ -1,6 +1,157 @@ export default { - "/posts/398": {"id":398,"name":"Uwe Keim","username":"uwe_keim","avatar_template":"/user_avatar/meta.discourse.org/uwe_keim/{size}/5697.png","uploaded_avatar_id":5697,"created_at":"2013-02-05T21:29:00.280Z","cooked":"

Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?

","post_number":1,"post_type":1,"updated_at":"2013-02-05T21:29:00.280Z","like_count":0,"reply_count":1,"reply_to_post_number":null,"quote_count":0,"avg_time":25,"incoming_link_count":314,"reads":475,"score":1702.25,"yours":false,"topic_id":280,"topic_slug":"internationalization-localization","display_username":"Uwe Keim","primary_group_name":null,"version":1,"can_edit":true,"can_delete":false,"can_recover":true,"user_title":null,"raw":"Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?","actions_summary":[{"id":2,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":3,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":4,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":5,"count":0,"hidden":true,"can_act":true,"can_defer_flags":false},{"id":6,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":7,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false},{"id":8,"count":0,"hidden":false,"can_act":true,"can_defer_flags":false}],"moderator":false,"admin":false,"staff":false,"user_id":255,"hidden":false,"hidden_reason_id":null,"trust_level":2,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false}, - "/posts/18": {"id":18,"username":"eviltrout","avatar_template":"//www.gravatar.com/avatar/c6e17f2ae2a215e87ff9e878a4e63cd9.png?s={size}&r=pg&d=identicon","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:11.840Z","cooked":"

This is the first post.

","post_number":1,"post_type":1,"updated_at":"2015-08-13T14:49:11.840Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":false,"can_recover":true,"user_title":null,"raw":"This is the first post.","actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false}, - "/posts/19": {"id":19,"username":"eviltrout","avatar_template":"//www.gravatar.com/avatar/c6e17f2ae2a215e87ff9e878a4e63cd9.png?s={size}&r=pg&d=identicon","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:18.231Z","cooked":"

This is the second post.

","post_number":2,"post_type":1,"updated_at":"2015-08-13T14:49:18.231Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":true,"can_recover":true,"read":true,"user_title":null,"raw":"This is the second post.","actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false} + "/posts/398": { + id: 398, + name: "Uwe Keim", + username: "uwe_keim", + avatar_template: "/user_avatar/meta.discourse.org/uwe_keim/{size}/5697.png", + uploaded_avatar_id: 5697, + created_at: "2013-02-05T21:29:00.280Z", + cooked: + "

Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?

", + post_number: 1, + post_type: 1, + updated_at: "2013-02-05T21:29:00.280Z", + like_count: 0, + reply_count: 1, + reply_to_post_number: null, + quote_count: 0, + avg_time: 25, + incoming_link_count: 314, + reads: 475, + score: 1702.25, + yours: false, + topic_id: 280, + topic_slug: "internationalization-localization", + display_username: "Uwe Keim", + primary_group_name: null, + version: 1, + can_edit: true, + can_delete: false, + can_recover: true, + user_title: null, + raw: + "Any plans to support localization of UI elements, so that I (for example) could set up a completely German speaking forum?", + actions_summary: [ + { id: 2, count: 0, hidden: false, can_act: true, can_defer_flags: false }, + { id: 3, count: 0, hidden: false, can_act: true, can_defer_flags: false }, + { id: 4, count: 0, hidden: false, can_act: true, can_defer_flags: false }, + { id: 5, count: 0, hidden: true, can_act: true, can_defer_flags: false }, + { id: 6, count: 0, hidden: false, can_act: true, can_defer_flags: false }, + { id: 7, count: 0, hidden: false, can_act: true, can_defer_flags: false }, + { id: 8, count: 0, hidden: false, can_act: true, can_defer_flags: false } + ], + moderator: false, + admin: false, + staff: false, + user_id: 255, + hidden: false, + hidden_reason_id: null, + trust_level: 2, + deleted_at: null, + user_deleted: false, + edit_reason: null, + can_view_edit_history: true, + wiki: false + }, + "/posts/18": { + id: 18, + username: "eviltrout", + avatar_template: + "//www.gravatar.com/avatar/c6e17f2ae2a215e87ff9e878a4e63cd9.png?s={size}&r=pg&d=identicon", + name: "Evil Trout", + uploaded_avatar_id: 9, + created_at: "2015-08-13T14:49:11.840Z", + cooked: "

This is the first post.

", + post_number: 1, + post_type: 1, + updated_at: "2015-08-13T14:49:11.840Z", + reply_count: 0, + reply_to_post_number: null, + quote_count: 0, + avg_time: null, + incoming_link_count: 0, + reads: 1, + score: 0, + yours: true, + topic_id: 9, + topic_slug: "this-is-a-test-topic", + display_username: "", + primary_group_name: null, + version: 1, + can_edit: true, + can_delete: false, + can_recover: true, + user_title: null, + raw: "This is the first post.", + actions_summary: [ + { id: 3, can_act: true }, + { id: 4, can_act: true }, + { id: 5, hidden: true, can_act: true }, + { id: 7, can_act: true }, + { id: 8, can_act: true } + ], + moderator: false, + admin: true, + staff: true, + user_id: 1, + hidden: false, + hidden_reason_id: null, + trust_level: 4, + deleted_at: null, + user_deleted: false, + edit_reason: null, + can_view_edit_history: true, + wiki: false + }, + "/posts/19": { + id: 19, + username: "eviltrout", + avatar_template: + "//www.gravatar.com/avatar/c6e17f2ae2a215e87ff9e878a4e63cd9.png?s={size}&r=pg&d=identicon", + name: "Evil Trout", + uploaded_avatar_id: 9, + created_at: "2015-08-13T14:49:18.231Z", + cooked: "

This is the second post.

", + post_number: 2, + post_type: 1, + updated_at: "2015-08-13T14:49:18.231Z", + reply_count: 0, + reply_to_post_number: null, + quote_count: 0, + avg_time: null, + incoming_link_count: 0, + reads: 1, + score: 0, + yours: true, + topic_id: 9, + topic_slug: "this-is-a-test-topic", + display_username: "", + primary_group_name: null, + version: 1, + can_edit: true, + can_delete: true, + can_recover: true, + read: true, + user_title: null, + raw: "This is the second post.", + actions_summary: [ + { id: 3, can_act: true }, + { id: 4, can_act: true }, + { id: 5, hidden: true, can_act: true }, + { id: 7, can_act: true }, + { id: 8, can_act: true } + ], + moderator: false, + admin: true, + staff: true, + user_id: 1, + hidden: false, + hidden_reason_id: null, + trust_level: 4, + deleted_at: null, + user_deleted: false, + edit_reason: null, + can_view_edit_history: true, + wiki: false + } }; - diff --git a/test/javascripts/fixtures/posts.js.es6 b/test/javascripts/fixtures/posts.js.es6 index dea89045210..caa70dc3370 100644 --- a/test/javascripts/fixtures/posts.js.es6 +++ b/test/javascripts/fixtures/posts.js.es6 @@ -1,19 +1,19 @@ export default { "/admin/reports/posts": { - "report": { - "type": "topics", - "title": "Topics", - "xaxis": "Day", - "yaxis": "Number of new posts", - "data": null, - "total": null, - "start_date": "2018-03-26T00:00:00.000Z", - "end_date": "2018-04-25T23:59:59.999Z", - "category_id": null, - "group_id": null, - "prev30Days": 0, - "labels": null, - "report_key": "" + report: { + type: "topics", + title: "Topics", + xaxis: "Day", + yaxis: "Number of new posts", + data: null, + total: null, + start_date: "2018-03-26T00:00:00.000Z", + end_date: "2018-04-25T23:59:59.999Z", + category_id: null, + group_id: null, + prev30Days: 0, + labels: null, + report_key: "" } } }; diff --git a/test/javascripts/fixtures/search-fixtures.js.es6 b/test/javascripts/fixtures/search-fixtures.js.es6 index a0b8e22bdaa..d49a7085107 100644 --- a/test/javascripts/fixtures/search-fixtures.js.es6 +++ b/test/javascripts/fixtures/search-fixtures.js.es6 @@ -1,770 +1,818 @@ export default { "/search.json": { - "users":[ + users: [ { - "id":19, - "username":"eviltrout", - "uploaded_avatar_id":5275, - "avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_1.png" + id: 19, + username: "eviltrout", + uploaded_avatar_id: 5275, + avatar_template: + "/user_avatar/meta.discourse.org/eviltrout/{size}/5275_1.png" }, { - "id":8617, - "username":"Mittineague", - "uploaded_avatar_id":40997, - "avatar_template":"/user_avatar/meta.discourse.org/mittineague/{size}/40997_1.png" + id: 8617, + username: "Mittineague", + uploaded_avatar_id: 40997, + avatar_template: + "/user_avatar/meta.discourse.org/mittineague/{size}/40997_1.png" }, { - "id":12662, - "username":"singmajesty", - "uploaded_avatar_id":36342, - "avatar_template":"/user_avatar/meta.discourse.org/singmajesty/{size}/36342_1.png" + id: 12662, + username: "singmajesty", + uploaded_avatar_id: 36342, + avatar_template: + "/user_avatar/meta.discourse.org/singmajesty/{size}/36342_1.png" }, { - "id":6626, - "username":"riking", - "uploaded_avatar_id":40212, - "avatar_template":"/user_avatar/meta.discourse.org/riking/{size}/40212_1.png" + id: 6626, + username: "riking", + uploaded_avatar_id: 40212, + avatar_template: + "/user_avatar/meta.discourse.org/riking/{size}/40212_1.png" }, { - "id":8300, - "username":"cpradio", - "uploaded_avatar_id":4970, - "avatar_template":"/user_avatar/meta.discourse.org/cpradio/{size}/4970_1.png" + id: 8300, + username: "cpradio", + uploaded_avatar_id: 4970, + avatar_template: + "/user_avatar/meta.discourse.org/cpradio/{size}/4970_1.png" }, { - "id":2602, - "username":"georgekaplan59", - "uploaded_avatar_id":31197, - "avatar_template":"/user_avatar/meta.discourse.org/georgekaplan59/{size}/31197_1.png" + id: 2602, + username: "georgekaplan59", + uploaded_avatar_id: 31197, + avatar_template: + "/user_avatar/meta.discourse.org/georgekaplan59/{size}/31197_1.png" }, { - "id":754, - "username":"danneu", - "uploaded_avatar_id":6540, - "avatar_template":"/user_avatar/meta.discourse.org/danneu/{size}/6540_1.png" + id: 754, + username: "danneu", + uploaded_avatar_id: 6540, + avatar_template: + "/user_avatar/meta.discourse.org/danneu/{size}/6540_1.png" }, { - "id":1995, - "username":"zogstrip", - "uploaded_avatar_id":8630, - "avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630_1.png" + id: 1995, + username: "zogstrip", + uploaded_avatar_id: 8630, + avatar_template: + "/user_avatar/meta.discourse.org/zogstrip/{size}/8630_1.png" }, { - "id":1, - "username":"sam", - "uploaded_avatar_id":5243, - "avatar_template":"/user_avatar/meta.discourse.org/sam/{size}/5243_1.png" + id: 1, + username: "sam", + uploaded_avatar_id: 5243, + avatar_template: "/user_avatar/meta.discourse.org/sam/{size}/5243_1.png" }, { - "id":8810, - "username":"fantasticfears", - "uploaded_avatar_id":36351, - "avatar_template":"/user_avatar/meta.discourse.org/fantasticfears/{size}/36351_1.png" + id: 8810, + username: "fantasticfears", + uploaded_avatar_id: 36351, + avatar_template: + "/user_avatar/meta.discourse.org/fantasticfears/{size}/36351_1.png" }, { - "id":14446, - "username":"ladydanger", - "uploaded_avatar_id":null, - "avatar_template":"/letter_avatar/ladydanger/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png" + id: 14446, + username: "ladydanger", + uploaded_avatar_id: null, + avatar_template: + "/letter_avatar/ladydanger/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png" }, { - "id":14474, - "username":"dnatoli_redbubble", - "uploaded_avatar_id":null, - "avatar_template":"/letter_avatar/dnatoli_redbubble/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png" + id: 14474, + username: "dnatoli_redbubble", + uploaded_avatar_id: null, + avatar_template: + "/letter_avatar/dnatoli_redbubble/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png" }, { - "id":14514, - "username":"adelsmee", - "uploaded_avatar_id":40445, - "avatar_template":"/user_avatar/meta.discourse.org/adelsmee/{size}/40445_1.png" + id: 14514, + username: "adelsmee", + uploaded_avatar_id: 40445, + avatar_template: + "/user_avatar/meta.discourse.org/adelsmee/{size}/40445_1.png" }, { - "id":32, - "username":"codinghorror", - "uploaded_avatar_id":5297, - "avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297_1.png" + id: 32, + username: "codinghorror", + uploaded_avatar_id: 5297, + avatar_template: + "/user_avatar/meta.discourse.org/codinghorror/{size}/5297_1.png" }, { - "id":14448, - "username":"snjqi188", - "uploaded_avatar_id":null, - "avatar_template":"/letter_avatar/snjqi188/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png" + id: 14448, + username: "snjqi188", + uploaded_avatar_id: null, + avatar_template: + "/letter_avatar/snjqi188/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png" }, { - "id":14657, - "username":"Alex_Flom", - "uploaded_avatar_id":41037, - "avatar_template":"/user_avatar/meta.discourse.org/alex_flom/{size}/41037_1.png" + id: 14657, + username: "Alex_Flom", + uploaded_avatar_id: 41037, + avatar_template: + "/user_avatar/meta.discourse.org/alex_flom/{size}/41037_1.png" }, { - "id":14353, - "username":"Simon_Cossar", - "uploaded_avatar_id":40130, - "avatar_template":"/user_avatar/meta.discourse.org/simon_cossar/{size}/40130_1.png" + id: 14353, + username: "Simon_Cossar", + uploaded_avatar_id: 40130, + avatar_template: + "/user_avatar/meta.discourse.org/simon_cossar/{size}/40130_1.png" }, { - "id":14184, - "username":"takaminacchan", - "uploaded_avatar_id":39685, - "avatar_template":"/user_avatar/meta.discourse.org/takaminacchan/{size}/39685_1.png" + id: 14184, + username: "takaminacchan", + uploaded_avatar_id: 39685, + avatar_template: + "/user_avatar/meta.discourse.org/takaminacchan/{size}/39685_1.png" }, { - "id":9931, - "username":"Frank", - "uploaded_avatar_id":null, - "avatar_template":"/letter_avatar/frank/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png" + id: 9931, + username: "Frank", + uploaded_avatar_id: null, + avatar_template: + "/letter_avatar/frank/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png" }, { - "id":8364, - "username":"codetricity", - "uploaded_avatar_id":3773, - "avatar_template":"/user_avatar/meta.discourse.org/codetricity/{size}/3773_1.png" + id: 8364, + username: "codetricity", + uploaded_avatar_id: 3773, + avatar_template: + "/user_avatar/meta.discourse.org/codetricity/{size}/3773_1.png" }, { - "id":4949, - "username":"brodock", - "uploaded_avatar_id":13541, - "avatar_template":"/user_avatar/meta.discourse.org/brodock/{size}/13541_1.png" + id: 4949, + username: "brodock", + uploaded_avatar_id: 13541, + avatar_template: + "/user_avatar/meta.discourse.org/brodock/{size}/13541_1.png" }, { - "id":14, - "username":"clay", - "uploaded_avatar_id":5265, - "avatar_template":"/user_avatar/meta.discourse.org/clay/{size}/5265_1.png" + id: 14, + username: "clay", + uploaded_avatar_id: 5265, + avatar_template: + "/user_avatar/meta.discourse.org/clay/{size}/5265_1.png" }, { - "id":8385, - "username":"zchrykng", - "uploaded_avatar_id":18517, - "avatar_template":"/user_avatar/meta.discourse.org/zchrykng/{size}/18517_1.png" + id: 8385, + username: "zchrykng", + uploaded_avatar_id: 18517, + avatar_template: + "/user_avatar/meta.discourse.org/zchrykng/{size}/18517_1.png" }, { - "id":3520, - "username":"arlyxiao", - "uploaded_avatar_id":11206, - "avatar_template":"/user_avatar/meta.discourse.org/arlyxiao/{size}/11206_1.png" + id: 3520, + username: "arlyxiao", + uploaded_avatar_id: 11206, + avatar_template: + "/user_avatar/meta.discourse.org/arlyxiao/{size}/11206_1.png" }, { - "id":3493, - "username":"richp10", - "uploaded_avatar_id":11160, - "avatar_template":"/user_avatar/meta.discourse.org/richp10/{size}/11160_1.png" + id: 3493, + username: "richp10", + uploaded_avatar_id: 11160, + avatar_template: + "/user_avatar/meta.discourse.org/richp10/{size}/11160_1.png" }, { - "id":2395, - "username":"lookingsideways", - "uploaded_avatar_id":9290, - "avatar_template":"/user_avatar/meta.discourse.org/lookingsideways/{size}/9290_1.png" + id: 2395, + username: "lookingsideways", + uploaded_avatar_id: 9290, + avatar_template: + "/user_avatar/meta.discourse.org/lookingsideways/{size}/9290_1.png" }, { - "id":2477, - "username":"billybonks", - "uploaded_avatar_id":9430, - "avatar_template":"/user_avatar/meta.discourse.org/billybonks/{size}/9430_1.png" + id: 2477, + username: "billybonks", + uploaded_avatar_id: 9430, + avatar_template: + "/user_avatar/meta.discourse.org/billybonks/{size}/9430_1.png" }, { - "id":7301, - "username":"jasonwhat", - "uploaded_avatar_id":null, - "avatar_template":"/letter_avatar/jasonwhat/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png" + id: 7301, + username: "jasonwhat", + uploaded_avatar_id: null, + avatar_template: + "/letter_avatar/jasonwhat/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png" }, { - "id":1819, - "username":"stephan", - "uploaded_avatar_id":8327, - "avatar_template":"/user_avatar/meta.discourse.org/stephan/{size}/8327_1.png" + id: 1819, + username: "stephan", + uploaded_avatar_id: 8327, + avatar_template: + "/user_avatar/meta.discourse.org/stephan/{size}/8327_1.png" }, { - "id":2, - "username":"neil", - "uploaded_avatar_id":5245, - "avatar_template":"/user_avatar/meta.discourse.org/neil/{size}/5245_1.png" + id: 2, + username: "neil", + uploaded_avatar_id: 5245, + avatar_template: + "/user_avatar/meta.discourse.org/neil/{size}/5245_1.png" }, { - "id":2471, - "username":"robconery", - "uploaded_avatar_id":9418, - "avatar_template":"/user_avatar/meta.discourse.org/robconery/{size}/9418_1.png" + id: 2471, + username: "robconery", + uploaded_avatar_id: 9418, + avatar_template: + "/user_avatar/meta.discourse.org/robconery/{size}/9418_1.png" } ], - "topic_list":{ - "can_create_topic":false, - "draft":null, - "draft_key":"new_topic", - "draft_sequence":null, - "per_page":30, - "topics":[ + topic_list: { + can_create_topic: false, + draft: null, + draft_key: "new_topic", + draft_sequence: null, + per_page: 30, + topics: [ { - "id":9318, - "title":"Discourse has a new Markdown Parser!", - "fancy_title":"Discourse has a new Markdown Parser!", - "slug":"discourse-has-a-new-markdown-parser", - "posts_count":1, - "reply_count":0, - "highest_post_number":1, - "image_url":null, - "created_at":"2013-08-24T18:08:06.063Z", - "last_posted_at":"2013-08-24T18:08:06.259Z", - "bumped":true, - "bumped_at":"2015-03-09T04:54:43.977Z", - "unseen":false, - "linked_post_number":1, - "pinned":false, - "unpinned":null, - "excerpt":"...0 lines of Javascript code! An inline example Let's say you want to replace all occurances of \"evil trout\" with a link that says \"EVIL TROUT IS AWESOME\": Discourse.Dialect.on(\"register\", function(event) {...", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":2645, - "like_count":21, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"eviltrout", - "category_id":7, - "pinned_globally":false, - "posters":[ + id: 9318, + title: "Discourse has a new Markdown Parser!", + fancy_title: "Discourse has a new Markdown Parser!", + slug: "discourse-has-a-new-markdown-parser", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2013-08-24T18:08:06.063Z", + last_posted_at: "2013-08-24T18:08:06.259Z", + bumped: true, + bumped_at: "2015-03-09T04:54:43.977Z", + unseen: false, + linked_post_number: 1, + pinned: false, + unpinned: null, + excerpt: + '...0 lines of Javascript code! An inline example Let\'s say you want to replace all occurances of "evil trout" with a link that says "EVIL TROUT IS AWESOME": Discourse.Dialect.on("register", function(event) {...', + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 2645, + like_count: 21, + has_summary: false, + archetype: "regular", + last_poster_username: "eviltrout", + category_id: 7, + pinned_globally: false, + posters: [ { - "extras":"latest single", - "description":"Original Poster, Most Recent Poster", - "user_id":19 + extras: "latest single", + description: "Original Poster, Most Recent Poster", + user_id: 19 } ] }, { - "id":21792, - "title":"Adding custom emoji/emoticons via a plugin", - "fancy_title":"Adding custom emoji/emoticons via a plugin", - "slug":"adding-custom-emoji-emoticons-via-a-plugin", - "posts_count":34, - "reply_count":24, - "highest_post_number":35, - "image_url":null, - "created_at":"2014-11-03T21:48:48.283Z", - "last_posted_at":"2014-12-23T12:45:11.245Z", - "bumped":true, - "bumped_at":"2014-12-23T12:45:11.245Z", - "unseen":false, - "linked_post_number":1, - "pinned":false, - "unpinned":null, - "excerpt":"...plugin that executes the following method to register a new emoji: Discourse.Dialect.registerEmoji('trout', 'http://cdn.eviltrout.com/images/trout-square.jpg'); Here's a sample plugin that adds a :trout: e...", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":1260, - "like_count":25, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"cpradio", - "category_id":22, - "pinned_globally":false, - "posters":[ + id: 21792, + title: "Adding custom emoji/emoticons via a plugin", + fancy_title: "Adding custom emoji/emoticons via a plugin", + slug: "adding-custom-emoji-emoticons-via-a-plugin", + posts_count: 34, + reply_count: 24, + highest_post_number: 35, + image_url: null, + created_at: "2014-11-03T21:48:48.283Z", + last_posted_at: "2014-12-23T12:45:11.245Z", + bumped: true, + bumped_at: "2014-12-23T12:45:11.245Z", + unseen: false, + linked_post_number: 1, + pinned: false, + unpinned: null, + excerpt: + "...plugin that executes the following method to register a new emoji: Discourse.Dialect.registerEmoji('trout', 'http://cdn.eviltrout.com/images/trout-square.jpg'); Here's a sample plugin that adds a :trout: e...", + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 1260, + like_count: 25, + has_summary: false, + archetype: "regular", + last_poster_username: "cpradio", + category_id: 22, + pinned_globally: false, + posters: [ { - "extras":null, - "description":"Original Poster", - "user_id":19 + extras: null, + description: "Original Poster", + user_id: 19 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":8617 + extras: null, + description: "Frequent Poster", + user_id: 8617 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":12662 + extras: null, + description: "Frequent Poster", + user_id: 12662 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":6626 + extras: null, + description: "Frequent Poster", + user_id: 6626 }, { - "extras":"latest", - "description":"Most Recent Poster", - "user_id":8300 + extras: "latest", + description: "Most Recent Poster", + user_id: 8300 } ] }, { - "id":3071, - "title":"Would it be possible to make Slug localizable?", - "fancy_title":"Would it be possible to make Slug localizable?", - "slug":"would-it-be-possible-to-make-slug-localizable", - "posts_count":12, - "reply_count":7, - "highest_post_number":12, - "image_url":null, - "created_at":"2013-02-14T11:48:21.474Z", - "last_posted_at":"2014-09-18T14:38:59.064Z", - "bumped":true, - "bumped_at":"2014-09-18T14:38:59.064Z", - "unseen":false, - "linked_post_number":10, - "pinned":false, - "unpinned":null, - "excerpt":"...in `block (2 levels) in < top (required) > ' 3) Slug replaces symbols Failure/Error: Slug.for('evil#trout').should == 'evil-trout' expected: \"evil-trout\" got: \"evil-number-trout\" (using ==) # ./spec/compon...", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":766, - "like_count":5, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"fantasticfears", - "category_id":17, - "pinned_globally":false, - "posters":[ + id: 3071, + title: "Would it be possible to make Slug localizable?", + fancy_title: "Would it be possible to make Slug localizable?", + slug: "would-it-be-possible-to-make-slug-localizable", + posts_count: 12, + reply_count: 7, + highest_post_number: 12, + image_url: null, + created_at: "2013-02-14T11:48:21.474Z", + last_posted_at: "2014-09-18T14:38:59.064Z", + bumped: true, + bumped_at: "2014-09-18T14:38:59.064Z", + unseen: false, + linked_post_number: 10, + pinned: false, + unpinned: null, + excerpt: + "...in `block (2 levels) in < top (required) > ' 3) Slug replaces symbols Failure/Error: Slug.for('evil#trout').should == 'evil-trout' expected: \"evil-trout\" got: \"evil-number-trout\" (using ==) # ./spec/compon...", + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 766, + like_count: 5, + has_summary: false, + archetype: "regular", + last_poster_username: "fantasticfears", + category_id: 17, + pinned_globally: false, + posters: [ { - "extras":null, - "description":"Original Poster", - "user_id":2602 + extras: null, + description: "Original Poster", + user_id: 2602 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":754 + extras: null, + description: "Frequent Poster", + user_id: 754 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":1995 + extras: null, + description: "Frequent Poster", + user_id: 1995 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":1 + extras: null, + description: "Frequent Poster", + user_id: 1 }, { - "extras":"latest", - "description":"Most Recent Poster", - "user_id":8810 + extras: "latest", + description: "Most Recent Poster", + user_id: 8810 } ] }, { - "id":26875, - "title":"Rails Girls SoC Banter", - "fancy_title":"Rails Girls SoC Banter", - "slug":"rails-girls-soc-banter", - "posts_count":48, - "reply_count":30, - "highest_post_number":48, - "image_url":null, - "created_at":"2015-03-27T11:26:09.903Z", - "last_posted_at":"2015-07-13T23:11:31.481Z", - "bumped":true, - "bumped_at":"2015-07-13T23:11:31.481Z", - "unseen":false, - "linked_post_number":42, - "pinned":false, - "unpinned":null, - "excerpt":"...e inserted by plugins. ## Usage If you handlebars template has: ```handlebars {{plugin-outlet \"evil-trout\"}} ``` Then any handlebars files you create in the `connectors/evil-trout` directory will automatic...", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":1224, - "like_count":81, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"snjqi188", - "category_id":7, - "pinned_globally":false, - "posters":[ + id: 26875, + title: "Rails Girls SoC Banter", + fancy_title: "Rails Girls SoC Banter", + slug: "rails-girls-soc-banter", + posts_count: 48, + reply_count: 30, + highest_post_number: 48, + image_url: null, + created_at: "2015-03-27T11:26:09.903Z", + last_posted_at: "2015-07-13T23:11:31.481Z", + bumped: true, + bumped_at: "2015-07-13T23:11:31.481Z", + unseen: false, + linked_post_number: 42, + pinned: false, + unpinned: null, + excerpt: + '...e inserted by plugins. ## Usage If you handlebars template has: ```handlebars {{plugin-outlet "evil-trout"}} ``` Then any handlebars files you create in the `connectors/evil-trout` directory will automatic...', + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 1224, + like_count: 81, + has_summary: false, + archetype: "regular", + last_poster_username: "snjqi188", + category_id: 7, + pinned_globally: false, + posters: [ { - "extras":null, - "description":"Original Poster", - "user_id":14446 + extras: null, + description: "Original Poster", + user_id: 14446 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":14474 + extras: null, + description: "Frequent Poster", + user_id: 14474 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":14514 + extras: null, + description: "Frequent Poster", + user_id: 14514 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":32 + extras: null, + description: "Frequent Poster", + user_id: 32 }, { - "extras":"latest", - "description":"Most Recent Poster", - "user_id":14448 + extras: "latest", + description: "Most Recent Poster", + user_id: 14448 } ] }, { - "id":31001, - "title":"Beginner's Guide to Creating Discourse Plugins Part 2: Plugin Outlets", - "fancy_title":"Beginner’s Guide to Creating Discourse Plugins Part 2: Plugin Outlets", - "slug":"beginners-guide-to-creating-discourse-plugins-part-2-plugin-outlets", - "posts_count":1, - "reply_count":0, - "highest_post_number":1, - "image_url":null, - "created_at":"2015-07-12T17:48:27.322Z", - "last_posted_at":"2015-07-12T17:48:27.403Z", - "bumped":true, - "bumped_at":"2015-07-13T04:18:14.901Z", - "unseen":false, - "linked_post_number":1, - "pinned":false, - "unpinned":null, - "excerpt":"...nectors/ < outlet name > in it. For example, if your handlebars template has: {{plugin-outlet \"evil-trout\"}} Then any handlebars files you create in the connectors/evil-trout directory will automatically b...", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":220, - "like_count":16, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"eviltrout", - "category_id":10, - "pinned_globally":false, - "posters":[ + id: 31001, + title: + "Beginner's Guide to Creating Discourse Plugins Part 2: Plugin Outlets", + fancy_title: + "Beginner’s Guide to Creating Discourse Plugins Part 2: Plugin Outlets", + slug: + "beginners-guide-to-creating-discourse-plugins-part-2-plugin-outlets", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2015-07-12T17:48:27.322Z", + last_posted_at: "2015-07-12T17:48:27.403Z", + bumped: true, + bumped_at: "2015-07-13T04:18:14.901Z", + unseen: false, + linked_post_number: 1, + pinned: false, + unpinned: null, + excerpt: + '...nectors/ < outlet name > in it. For example, if your handlebars template has: {{plugin-outlet "evil-trout"}} Then any handlebars files you create in the connectors/evil-trout directory will automatically b...', + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 220, + like_count: 16, + has_summary: false, + archetype: "regular", + last_poster_username: "eviltrout", + category_id: 10, + pinned_globally: false, + posters: [ { - "extras":"latest single", - "description":"Original Poster, Most Recent Poster", - "user_id":19 + extras: "latest single", + description: "Original Poster, Most Recent Poster", + user_id: 19 } ] }, { - "id":29176, - "title":"How can I add some custom html to the bottom of the categories page?", - "fancy_title":"How can I add some custom html to the bottom of the categories page?", - "slug":"how-can-i-add-some-custom-html-to-the-bottom-of-the-categories-page", - "posts_count":12, - "reply_count":10, - "highest_post_number":13, - "image_url":null, - "created_at":"2015-05-23T19:08:35.447Z", - "last_posted_at":"2015-05-25T08:16:25.989Z", - "bumped":true, - "bumped_at":"2015-05-25T08:16:25.989Z", - "unseen":false, - "linked_post_number":12, - "pinned":false, - "unpinned":null, - "excerpt":"...e inserted by plugins. ## Usage If you handlebars template has: ```handlebars {{plugin-outlet \"evil-trout\"}} ``` Then any handlebars files you create in the `connectors/evil-trout` directory will automatic...", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":190, - "like_count":8, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"sam", - "category_id":6, - "pinned_globally":false, - "posters":[ + id: 29176, + title: + "How can I add some custom html to the bottom of the categories page?", + fancy_title: + "How can I add some custom html to the bottom of the categories page?", + slug: + "how-can-i-add-some-custom-html-to-the-bottom-of-the-categories-page", + posts_count: 12, + reply_count: 10, + highest_post_number: 13, + image_url: null, + created_at: "2015-05-23T19:08:35.447Z", + last_posted_at: "2015-05-25T08:16:25.989Z", + bumped: true, + bumped_at: "2015-05-25T08:16:25.989Z", + unseen: false, + linked_post_number: 12, + pinned: false, + unpinned: null, + excerpt: + '...e inserted by plugins. ## Usage If you handlebars template has: ```handlebars {{plugin-outlet "evil-trout"}} ``` Then any handlebars files you create in the `connectors/evil-trout` directory will automatic...', + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 190, + like_count: 8, + has_summary: false, + archetype: "regular", + last_poster_username: "sam", + category_id: 6, + pinned_globally: false, + posters: [ { - "extras":null, - "description":"Original Poster", - "user_id":14657 + extras: null, + description: "Original Poster", + user_id: 14657 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":6626 + extras: null, + description: "Frequent Poster", + user_id: 6626 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":8617 + extras: null, + description: "Frequent Poster", + user_id: 8617 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":14353 + extras: null, + description: "Frequent Poster", + user_id: 14353 }, { - "extras":"latest", - "description":"Most Recent Poster", - "user_id":1 + extras: "latest", + description: "Most Recent Poster", + user_id: 1 } ] }, { - "id":26192, - "title":"403 when embedding a DigitalOcean droplet", - "fancy_title":"403 when embedding a DigitalOcean droplet", - "slug":"403-when-embedding-a-digital-ocean-droplet", - "posts_count":7, - "reply_count":3, - "highest_post_number":7, - "image_url":null, - "created_at":"2015-03-10T21:22:19.206Z", - "last_posted_at":"2015-03-11T22:31:04.520Z", - "bumped":true, - "bumped_at":"2015-03-11T22:31:04.520Z", - "unseen":false, - "linked_post_number":4, - "pinned":false, - "unpinned":null, - "excerpt":"Yes I am Robin as well as Evil Trout smile :smile: If you followed those instructions and are getting access errors, you might want to d...", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":232, - "like_count":2, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"codinghorror", - "category_id":6, - "pinned_globally":false, - "posters":[ + id: 26192, + title: "403 when embedding a DigitalOcean droplet", + fancy_title: "403 when embedding a DigitalOcean droplet", + slug: "403-when-embedding-a-digital-ocean-droplet", + posts_count: 7, + reply_count: 3, + highest_post_number: 7, + image_url: null, + created_at: "2015-03-10T21:22:19.206Z", + last_posted_at: "2015-03-11T22:31:04.520Z", + bumped: true, + bumped_at: "2015-03-11T22:31:04.520Z", + unseen: false, + linked_post_number: 4, + pinned: false, + unpinned: null, + excerpt: + "Yes I am Robin as well as Evil Trout smile :smile: If you followed those instructions and are getting access errors, you might want to d...", + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 232, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "codinghorror", + category_id: 6, + pinned_globally: false, + posters: [ { - "extras":null, - "description":"Original Poster", - "user_id":14184 + extras: null, + description: "Original Poster", + user_id: 14184 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":19 + extras: null, + description: "Frequent Poster", + user_id: 19 }, { - "extras":"latest", - "description":"Most Recent Poster", - "user_id":32 + extras: "latest", + description: "Most Recent Poster", + user_id: 32 } ] }, { - "id":20883, - "title":"S3 competitor integration", - "fancy_title":"S3 competitor integration", - "slug":"s3-competitor-integration", - "posts_count":3, - "reply_count":1, - "highest_post_number":3, - "image_url":"https://discourse-cdn.global.ssl.fastly.net/meta/images/emoji/twitter/smile.png?v=1", - "created_at":"2014-10-07T13:37:19.628Z", - "last_posted_at":"2014-10-07T18:46:22.493Z", - "bumped":true, - "bumped_at":"2014-10-07T18:46:22.493Z", - "unseen":false, - "linked_post_number":3, - "pinned":false, - "unpinned":null, - "excerpt":"I have seem some of your testing 'stuff' (evil trout's actually). And it looks like a HUUUUUUUUGGGE time sink (ice pick to the eyeballs). but...I believ...", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":141, - "like_count":2, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"Frank", - "category_id":2, - "pinned_globally":false, - "posters":[ + id: 20883, + title: "S3 competitor integration", + fancy_title: "S3 competitor integration", + slug: "s3-competitor-integration", + posts_count: 3, + reply_count: 1, + highest_post_number: 3, + image_url: + "https://discourse-cdn.global.ssl.fastly.net/meta/images/emoji/twitter/smile.png?v=1", + created_at: "2014-10-07T13:37:19.628Z", + last_posted_at: "2014-10-07T18:46:22.493Z", + bumped: true, + bumped_at: "2014-10-07T18:46:22.493Z", + unseen: false, + linked_post_number: 3, + pinned: false, + unpinned: null, + excerpt: + "I have seem some of your testing 'stuff' (evil trout's actually). And it looks like a HUUUUUUUUGGGE time sink (ice pick to the eyeballs). but...I believ...", + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 141, + like_count: 2, + has_summary: false, + archetype: "regular", + last_poster_username: "Frank", + category_id: 2, + pinned_globally: false, + posters: [ { - "extras":"latest", - "description":"Original Poster, Most Recent Poster", - "user_id":9931 + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 9931 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":1995 + extras: null, + description: "Frequent Poster", + user_id: 1995 } ] }, { - "id":13534, - "title":"Blogging Platforms, Ghost, and Discourse", - "fancy_title":"Blogging Platforms, Ghost, and Discourse", - "slug":"blogging-platforms-ghost-and-discourse", - "posts_count":18, - "reply_count":13, - "highest_post_number":18, - "image_url":null, - "created_at":"2014-03-08T15:46:35.174Z", - "last_posted_at":"2014-03-26T18:25:45.895Z", - "bumped":true, - "bumped_at":"2014-03-26T18:25:45.895Z", - "unseen":false, - "linked_post_number":1, - "pinned":false, - "unpinned":null, - "excerpt":"...urse, do you mean that the blog comments for Ghost will be driven by Discourse, similar to the Evil Trout blog ? What about using Discourse as the blog platform itself, not as the comment engine at the end...", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":2182, - "like_count":17, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"codetricity", - "category_id":17, - "pinned_globally":false, - "posters":[ + id: 13534, + title: "Blogging Platforms, Ghost, and Discourse", + fancy_title: "Blogging Platforms, Ghost, and Discourse", + slug: "blogging-platforms-ghost-and-discourse", + posts_count: 18, + reply_count: 13, + highest_post_number: 18, + image_url: null, + created_at: "2014-03-08T15:46:35.174Z", + last_posted_at: "2014-03-26T18:25:45.895Z", + bumped: true, + bumped_at: "2014-03-26T18:25:45.895Z", + unseen: false, + linked_post_number: 1, + pinned: false, + unpinned: null, + excerpt: + "...urse, do you mean that the blog comments for Ghost will be driven by Discourse, similar to the Evil Trout blog ? What about using Discourse as the blog platform itself, not as the comment engine at the end...", + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 2182, + like_count: 17, + has_summary: false, + archetype: "regular", + last_poster_username: "codetricity", + category_id: 17, + pinned_globally: false, + posters: [ { - "extras":"latest", - "description":"Original Poster, Most Recent Poster", - "user_id":8364 + extras: "latest", + description: "Original Poster, Most Recent Poster", + user_id: 8364 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":4949 + extras: null, + description: "Frequent Poster", + user_id: 4949 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":14 + extras: null, + description: "Frequent Poster", + user_id: 14 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":32 + extras: null, + description: "Frequent Poster", + user_id: 32 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":8385 + extras: null, + description: "Frequent Poster", + user_id: 8385 } ] }, { - "id":4859, - "title":"All of the site functions based on ajax?", - "fancy_title":"All of the site functions based on ajax?", - "slug":"all-of-the-site-functions-based-on-ajax", - "posts_count":28, - "reply_count":20, - "highest_post_number":28, - "image_url":null, - "created_at":"2013-03-18T08:59:46.135Z", - "last_posted_at":"2013-10-18T20:22:30.677Z", - "bumped":true, - "bumped_at":"2013-10-18T20:22:30.677Z", - "unseen":false, - "linked_post_number":21, - "pinned":false, - "unpinned":null, - "excerpt":"please see evil trouts blog post http://eviltrout.com/2013/02/27/adding-to-discourse-part-1.html", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":1629, - "like_count":17, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"jasonwhat", - "category_id":17, - "pinned_globally":false, - "posters":[ + id: 4859, + title: "All of the site functions based on ajax?", + fancy_title: "All of the site functions based on ajax?", + slug: "all-of-the-site-functions-based-on-ajax", + posts_count: 28, + reply_count: 20, + highest_post_number: 28, + image_url: null, + created_at: "2013-03-18T08:59:46.135Z", + last_posted_at: "2013-10-18T20:22:30.677Z", + bumped: true, + bumped_at: "2013-10-18T20:22:30.677Z", + unseen: false, + linked_post_number: 21, + pinned: false, + unpinned: null, + excerpt: + "please see evil trouts blog post http://eviltrout.com/2013/02/27/adding-to-discourse-part-1.html", + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 1629, + like_count: 17, + has_summary: false, + archetype: "regular", + last_poster_username: "jasonwhat", + category_id: 17, + pinned_globally: false, + posters: [ { - "extras":null, - "description":"Original Poster", - "user_id":3520 + extras: null, + description: "Original Poster", + user_id: 3520 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":3493 + extras: null, + description: "Frequent Poster", + user_id: 3493 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":2395 + extras: null, + description: "Frequent Poster", + user_id: 2395 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":2477 + extras: null, + description: "Frequent Poster", + user_id: 2477 }, { - "extras":"latest", - "description":"Most Recent Poster", - "user_id":7301 + extras: "latest", + description: "Most Recent Poster", + user_id: 7301 } ] }, { - "id":7220, - "title":"Javascript dependencies", - "fancy_title":"Javascript dependencies", - "slug":"javascript-dependencies", - "posts_count":8, - "reply_count":5, - "highest_post_number":8, - "image_url":null, - "created_at":"2013-06-06T11:11:18.522Z", - "last_posted_at":"2013-06-07T18:43:51.449Z", - "bumped":true, - "bumped_at":"2013-06-07T18:43:51.449Z", - "unseen":false, - "linked_post_number":3, - "pinned":false, - "unpinned":null, - "excerpt":"...ould be in vendor directory of one of the gems Ahh I need to look at Gemfile Ahh I need to use Evil Trouts bundle open handlebars trick. I am completely against this new best practice, its inconsistent wit...", - "visible":true, - "closed":false, - "archived":false, - "bookmarked":null, - "liked":null, - "views":1010, - "like_count":0, - "has_summary":false, - "archetype":"regular", - "last_poster_username":"eviltrout", - "category_id":7, - "pinned_globally":false, - "posters":[ + id: 7220, + title: "Javascript dependencies", + fancy_title: "Javascript dependencies", + slug: "javascript-dependencies", + posts_count: 8, + reply_count: 5, + highest_post_number: 8, + image_url: null, + created_at: "2013-06-06T11:11:18.522Z", + last_posted_at: "2013-06-07T18:43:51.449Z", + bumped: true, + bumped_at: "2013-06-07T18:43:51.449Z", + unseen: false, + linked_post_number: 3, + pinned: false, + unpinned: null, + excerpt: + "...ould be in vendor directory of one of the gems Ahh I need to look at Gemfile Ahh I need to use Evil Trouts bundle open handlebars trick. I am completely against this new best practice, its inconsistent wit...", + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + views: 1010, + like_count: 0, + has_summary: false, + archetype: "regular", + last_poster_username: "eviltrout", + category_id: 7, + pinned_globally: false, + posters: [ { - "extras":null, - "description":"Original Poster", - "user_id":1819 + extras: null, + description: "Original Poster", + user_id: 1819 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":2 + extras: null, + description: "Frequent Poster", + user_id: 2 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":1 + extras: null, + description: "Frequent Poster", + user_id: 1 }, { - "extras":null, - "description":"Frequent Poster", - "user_id":2471 + extras: null, + description: "Frequent Poster", + user_id: 2471 }, { - "extras":"latest", - "description":"Most Recent Poster", - "user_id":19 + extras: "latest", + description: "Most Recent Poster", + user_id: 19 } ] } @@ -772,668 +820,684 @@ export default { } }, "search/query": { -   "posts": [ -     { -       "id": 3833, -       "name": "Bill Dudney", -       "username": "bdudney", -       "avatar_template": "/user_avatar/meta.discourse.org/bdudney/{size}/8343_1.png", -       "uploaded_avatar_id": 8343, -       "created_at": "2013-02-07T17:46:57.469Z", -       "cooked": "

I've gotten vagrant up and running with a development environment but it's taking forever to load.<\/p>\n\n

For example http://192.168.10.200:3000/<\/a> takes tens of seconds to load.<\/p>\n\n

I'm running the whole stack on a new rMBP with OS X 10.8.2.<\/p>\n\n

Any ideas of what I've done wrong? Or is this just a function of being on the bleeding edge?<\/p>\n\n

Thanks,<\/p>\n\n

-bd<\/p>", -       "post_number": 1, -       "post_type": 1, -       "updated_at": "2013-02-07T17:46:57.469Z", -       "like_count": 0, -       "reply_count": 1, -       "reply_to_post_number": null, -       "quote_count": 0, -       "avg_time": 24, -       "incoming_link_count": 4422, -       "reads": 327, -       "score": 21978.4, -       "yours": false, -       "topic_id": 2179, -       "topic_slug": "development-mode-super-slow", -       "display_username": "Bill Dudney", -       "primary_group_name": null, -       "version": 2, -       "can_edit": false, -       "can_delete": false, -       "can_recover": false, -       "user_title": null, -       "actions_summary": [ -         { -           "id": 2, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 3, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 4, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 5, -           "count": 0, -           "hidden": true, -           "can_act": false -         }, -         { -           "id": 6, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 7, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 8, -           "count": 0, -           "hidden": false, -           "can_act": false -         } -       ], -       "moderator": false, -       "admin": false, -       "staff": false, -       "user_id": 1828, -       "hidden": false, -       "hidden_reason_id": null, -       "trust_level": 1, -       "deleted_at": null, -       "user_deleted": false, -       "edit_reason": null, -       "can_view_edit_history": true, -       "wiki": false, -       "blurb": "I've gotten vagrant up and running with a development environment but it's taking forever to load. For example http://192.168.10.200:3000/ takes..." -     }, -     { -       "id": 48887, -       "name": "Arpit Jalan", -       "username": "techAPJ", -       "avatar_template": "/user_avatar/meta.discourse.org/techapj/{size}/3281_1.png", -       "uploaded_avatar_id": 3281, -       "created_at": "2014-04-12T22:22:07.930Z", -       "cooked": "

So you want to set up Discourse on Ubuntu to hack on and develop with?<\/p>\n\n

We'll assume that you don't have Ruby/Rails/Postgre/Redis installed on your Ubuntu system. Let's begin!<\/p>\n\n

Although this guide assumes that you are using Ubuntu, but the set-up instructions will work fine for any Debian based ditribution.<\/em><\/p>\n\n

(If you want to install Discourse for production use, see our install guide<\/a>)<\/em><\/p>\n\n

Install Discourse Dependencies<\/h2>\n\n

Run this script<\/a> in terminal, to setup Rails development environment:<\/p>\n\n

bash <(wget -qO- https://raw.githubusercontent.com/techAPJ/install-rails/master/linux)<\/code><\/pre>\n\n

\nlinux_script.png<\/span>770x211 9.62 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

This will install following new packages on your system:<\/p>\n\n

    \n
  • Git<\/a><\/li>\n
  • rbenv<\/a><\/li>\n
  • ruby-build<\/a><\/li>\n
  • \nRuby<\/a> (stable)<\/li>\n
  • Rails<\/a><\/li>\n
  • PostgreSQL<\/a><\/li>\n
  • SQLite<\/a><\/li>\n
  • Redis<\/a><\/li>\n
  • Bundler<\/a><\/li>\n
  • ImageMagick<\/a><\/li>\n<\/ul>\n\n

    Install Phantomjs:<\/p>\n\n

    For 32 bit macine:<\/p>\n\n

    cd /usr/local/share\nsudo wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.8-linux-i686.tar.bz2\nsudo tar xvf phantomjs-1.9.8-linux-i686.tar.bz2\nsudo rm phantomjs-1.9.8-linux-i686.tar.bz2\nsudo ln -s /usr/local/share/phantomjs-1.9.8-linux-i686/bin/phantomjs /usr/local/bin/phantomjs\ncd<\/code><\/pre>\n\n

    For 64 bit machine:<\/p>\n\n

    cd /usr/local/share\nsudo wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.8-linux-x86_64.tar.bz2\nsudo tar xvf phantomjs-1.9.8-linux-x86_64.tar.bz2\nsudo rm phantomjs-1.9.8-linux-x86_64.tar.bz2\nsudo ln -s /usr/local/share/phantomjs-1.9.8-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs\ncd<\/code><\/pre>\n\n

    \nphantomjs.png<\/span>969x171 10.1 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    In case you have any of this package pre-installed and don't want to run entire script, see the script<\/a> and pick the packages you don't have currently installed. The script is fine-tuned for Discourse, and includes all the packages required for Discourse installation.<\/em><\/p>\n\n

    Now that we have installed Discourse dependencies, let's move on to install Discourse itself.<\/p>\n\n

    Clone Discourse<\/h2>\n\n

    Clone the Discourse repository in ~/discourse<\/code> folder:<\/p>\n\n

    git clone https://github.com/discourse/discourse.git ~/discourse<\/code><\/pre>\n\n

    \ngit_clone.png<\/span>967x137 7.73 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Setup Database<\/h2>\n\n

    Open psql prompt as postgre user<\/p>\n\n

    sudo -u postgres psql postgres<\/code><\/pre>\n\n

    \npg.png<\/span>725x187 5.79 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Create role with the same name as your ubuntu system username<\/strong> with discourse<\/em> as password:<\/p>\n\n

    CREATE ROLE discourse WITH LOGIN ENCRYPTED PASSWORD 'discourse' CREATEDB SUPERUSER;<\/code><\/pre>\n\n

    In the above command, I named the role as discourse<\/strong>, this means that my ubuntu system username is discourse<\/strong>. (It is necessary for role name to be same as system username, otherwise migrations will not run<\/em>)<\/p>\n\n

    Check that you have successfully created discourse<\/strong> role:<\/p>\n\n

    \\du<\/code><\/pre>\n\n

    \npg_user.png<\/span>725x185 7.5 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Create discourse_development<\/strong> and discourse_test<\/strong> database:<\/p>\n\n

    CREATE DATABASE discourse_development WITH OWNER discourse ENCODING 'UTF8' TEMPLATE template0;\nCREATE DATABASE discourse_test WITH OWNER discourse ENCODING 'UTF8' TEMPLATE template0;<\/code><\/pre>\n\n

    \npg_db.png<\/span>724x143 6.82 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Exit psql prompt by pressing ctrl<\/kbd>d<\/kbd><\/p>\n\n

    Now access psql prompt in discourse_development<\/strong> database as discourse<\/strong> user:<\/p>\n\n

    psql -d discourse_development -U discourse -h localhost<\/code><\/pre>\n\n

    When prompted for password, provide the password which you set at the time of creating role, if you followed the guide as is, the password is discourse<\/strong><\/p>\n\n

    Run following commands, separately:<\/p>\n\n

    CREATE EXTENSION pg_trgm;\nCREATE EXTENSION hstore;<\/code><\/pre>\n\n

    \npg_dev.png<\/span>726x316 13.4 KB<\/span><\/span>\n<\/div><\/a><\/div><\/p>\n\n

    Exit psql prompt by pressing ctrl<\/kbd>d<\/kbd><\/p>\n\n

    Now access psql prompt in discourse_test<\/strong> database as discourse<\/strong> user:<\/p>\n\n

    psql -d discourse_test -U discourse -h localhost<\/code><\/pre>\n\n

    When prompted for password, provide the password which you set at the time of creating role, if you followed the guide as is, the password is discourse<\/strong><\/p>\n\n

    Run following commands, separately:<\/p>\n\n

    CREATE EXTENSION pg_trgm;\nCREATE EXTENSION hstore;<\/code><\/pre>\n\n

    \npg_test.png<\/span>726x318 12.9 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Exit psql prompt by pressing ctrl<\/kbd>d<\/kbd><\/p>\n\n

    You have set-up the database successfully!<\/p>\n\n

    Bootstrap Discourse<\/h2>\n\n

    Switch to your Discourse folder:<\/p>\n\n

    cd ~/discourse<\/code><\/pre>\n\n

    Install the needed gems<\/p>\n\n

    bundle install<\/code><\/pre>\n\n

    \nbundle.png<\/span>724x248 9.75 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Now that you have successfully configured database connection, run this command:<\/p>\n\n

    bundle exec rake db:migrate db:test:prepare db:seed_fu<\/code><\/pre>\n\n

    Now, try running the specs: <\/p>\n\n

    bundle exec rake autospec<\/code><\/pre>\n\n

    \nspecs.png<\/span>717x263 8.63 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Start rails server:<\/p>\n\n

    bundle exec rails server<\/code><\/pre>\n\n

    \nserver.png<\/span>724x229 10.8 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    You should now be able to connect to discourse app on http://localhost:3000<\/a> - try it out!<\/p>\n\n

    \ndiscourse_start.png<\/span>1919x525 20.3 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Configure Mail and Create New Account<\/h2>\n\n

    We will use MailCatcher<\/a> to serve emails in development environment. Install and run MailCatcher:<\/p>\n\n

    gem install mailcatcher\nmailcatcher --http-ip 0.0.0.0<\/code><\/pre>\n\n

    Create new account:<\/p>\n\n

    \ncreate_account.png<\/span>720x401 13.5 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Check confirmation email by going to MailCatcher web interface at http://localhost:1080/<\/a><\/p>\n\n

    \nmc_sign_up_email.png<\/span>1919x480 21.5 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    If you did not receive the email, try running this in console<\/em>: bundle exec sidekiq -q default<\/code><\/p>\n\n

    Click the confirmation link and your account will be activated!<\/p>\n\n

    \ndisc_normal_acc.png<\/span>1919x430 21.8 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Access Admin<\/h2>\n\n

    Now, to make your account as admin, run the following commands in rails console:<\/p>\n\n

    RAILS_ENV=development bundle exec rails c\nu = User.last\nu.admin = true\nu.save<\/code><\/pre>\n\n

    \nadmin_console.png<\/span>722x462 31.7 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Once you execute the above commands successfully, check out your Discourse account again:<\/p>\n\n

    \nadmin_success.png<\/span>1919x1032 30.3 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

    Congratulations! You are now the admin of your own Discourse installation!<\/p>\n\n

    Happy hacking!<\/p>\n\n

    If anything needs to be improved in this guide, feel free to ask on meta.discourse.org<\/a>, or even better, submit a pull request<\/a>.<\/p>", -       "post_number": 1, -       "post_type": 1, -       "updated_at": "2015-06-22T17:24:20.607Z", -       "like_count": 15, -       "reply_count": 2, -       "reply_to_post_number": null, -       "quote_count": 0, -       "avg_time": 36, -       "incoming_link_count": 4680, -       "reads": 491, -       "score": 23815.8, -       "yours": false, -       "topic_id": 14727, -       "topic_slug": "beginners-guide-to-install-discourse-on-ubuntu-for-development", -       "display_username": "Arpit Jalan", -       "primary_group_name": null, -       "version": 26, -       "can_edit": false, -       "can_delete": false, -       "can_recover": false, -       "user_title": "team", -       "actions_summary": [ -         { -           "id": 2, -           "count": 15, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 3, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 4, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 5, -           "count": 0, -           "hidden": true, -           "can_act": false -         }, -         { -           "id": 6, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 7, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 8, -           "count": 0, -           "hidden": false, -           "can_act": false -         } -       ], -       "moderator": true, -       "admin": true, -       "staff": true, -       "user_id": 8222, -       "hidden": false, -       "hidden_reason_id": null, -       "trust_level": 4, -       "deleted_at": null, -       "user_deleted": false, -       "edit_reason": null, -       "can_view_edit_history": true, -       "wiki": true, -       "blurb": "So you want to set up Discourse on Ubuntu to hack on and develop with? We'll assume that you don't have Ruby/Rails/Postgre/Redis installed on your Ubuntu system..." -     }, -     { -       "id": 53437, -       "name": "Arpit Jalan", -       "username": "techAPJ", -       "avatar_template": "/user_avatar/meta.discourse.org/techapj/{size}/3281_1.png", -       "uploaded_avatar_id": 3281, -       "created_at": "2014-05-19T16:59:51.082Z", -       "cooked": "

    So you want to set up Discourse on Mac OS X to hack on and develop with?<\/p>\n\n

    We'll assume that you don't have Ruby/Rails/Postgre/Redis installed on your Mac. Let's begin!<\/p>\n\n

    (If you want to install Discourse for production use, see our install guide<\/a>)<\/em><\/p>\n\n

    Install Discourse Dependencies<\/h2>\n\n

    Run this script<\/a> in terminal, to setup Rails development environment:<\/p>\n\n

    bash <(curl -s https://raw.githubusercontent.com/techAPJ/install-rails/master/mac)<\/code><\/pre>\n\n

    This script will install following new packages on your system:<\/p>\n\n

      \n
    • Git<\/a><\/li>\n
    • rbenv<\/a><\/li>\n
    • ruby-build<\/a><\/li>\n
    • \nRuby<\/a> (latest stable)<\/li>\n
    • Rails<\/a><\/li>\n
    • PostgreSQL<\/a><\/li>\n
    • Redis<\/a><\/li>\n
    • Bundler<\/a><\/li>\n
    • ImageMagick<\/a><\/li>\n
    • PhantomJS<\/a><\/li>\n<\/ul>\n\n

      In case you have any of this package pre-installed and don't want to run entire script, see the script<\/a> and pick the packages you don't have currently installed. The script is fine-tuned for Discourse, and includes all the packages required for Discourse installation.<\/em><\/p>\n\n

      Now that we have installed Discourse dependencies, let's move on to install Discourse itself.<\/p>\n\n

      Clone Discourse<\/h2>\n\n

      Clone the Discourse repository in ~/discourse<\/code> folder:<\/p>\n\n

      git clone https://github.com/discourse/discourse.git ~/discourse<\/code><\/pre>\n\n

      <\/p>\n\n

      ~<\/code> indicates home folder, so Discourse source code will be available in your home folder.<\/p>\n\n

      Setup Database<\/h2>\n\n

      Open psql prompt:<\/p>\n\n

      psql postgres<\/code><\/pre>\n\n

      <\/p>\n\n

      Create discourse_development<\/strong> and discourse_test<\/strong> database with your account short name<\/a><\/em> specified as role:<\/p>\n\n

      CREATE DATABASE discourse_development WITH OWNER techapj ENCODING 'UTF8' TEMPLATE template0;\nCREATE DATABASE discourse_test WITH OWNER techapj ENCODING 'UTF8' TEMPLATE template0;<\/code><\/pre>\n\n

      Note that in above commands I specified the role as techapj<\/em>, this means that my short name<\/a> is techapj<\/em>, replace this with your own short name<\/a>.<\/strong><\/p>\n\n

      <\/p>\n\n

      Exit psql prompt by pressing control<\/kbd>d<\/kbd><\/p>\n\n

      Now access psql prompt in discourse_development<\/strong> database as your short name<\/em> user:<\/p>\n\n

      psql -d discourse_development -U techapj -h localhost<\/code><\/pre>\n\n

      Run following commands, separately:<\/p>\n\n

      CREATE EXTENSION pg_trgm;\nCREATE EXTENSION hstore;<\/code><\/pre>\n\n

      <\/p>\n\n

      Exit psql prompt by pressing control<\/kbd>d<\/kbd><\/p>\n\n

      Now access psql prompt in discourse_test<\/strong> database as your short name<\/em> user:<\/p>\n\n

      psql -d discourse_test -U techapj -h localhost<\/code><\/pre>\n\n

      Run following commands, separately:<\/p>\n\n

      CREATE EXTENSION pg_trgm;\nCREATE EXTENSION hstore;<\/code><\/pre>\n\n

      <\/p>\n\n

      Exit psql prompt by pressing control<\/kbd>d<\/kbd><\/p>\n\n

      You have set-up the database successfully!<\/p>\n\n

      Bootstrap Discourse<\/h2>\n\n

      Switch to your Discourse folder:<\/p>\n\n

      cd ~/discourse<\/code><\/pre>\n\n

      Install the needed gems<\/p>\n\n

      bundle install<\/code><\/pre>\n\n

      <\/p>\n\n

      Now that you have successfully installed gems, run this command:<\/p>\n\n

      bundle exec rake db:migrate db:test:prepare db:seed_fu<\/code><\/pre>\n\n

      Try running the specs: <\/p>\n\n

      bundle exec rake autospec<\/code><\/pre>\n\n

      <\/p>\n\n

      All the tests should pass.<\/p>\n\n

      Start rails server:<\/p>\n\n

      bundle exec rails server<\/code><\/pre>\n\n

      <\/p>\n\n

      You should now be able to connect with your Discourse app on http://localhost:3000<\/a> - try it out!<\/p>\n\n

      \nScreen Shot 2014-05-19 at 13.04.01.png<\/span>1255x461 98.7 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

      Create New Admin<\/h2>\n\n

      To create a new admin, run the following commands in rails console:<\/p>\n\n

      RAILS_ENV=development bundle exec rake admin:create<\/code><\/pre>\n\n

      Just enter your input as suggested, you can create an admin account. <\/p>\n\n

      \nfccdb29463e82f23.png<\/span>1919x430<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

      \nScreen Shot 2014-05-19 at 13.20.02.png<\/span>1256x756 124 KB<\/span><\/span>\n<\/div><\/a><\/div> <\/p>\n\n

      Happy hacking!<\/p>", -       "post_number": 1, -       "post_type": 1, -       "updated_at": "2015-04-26T06:51:23.549Z", -       "like_count": 13, -       "reply_count": 1, -       "reply_to_post_number": null, -       "quote_count": 0, -       "avg_time": 36, -       "incoming_link_count": 1483, -       "reads": 274, -       "score": 7985.4, -       "yours": false, -       "topic_id": 15772, -       "topic_slug": "beginners-guide-to-install-discourse-on-mac-os-x-for-development", -       "display_username": "Arpit Jalan", -       "primary_group_name": null, -       "version": 12, -       "can_edit": false, -       "can_delete": false, -       "can_recover": false, -       "user_title": "team", -       "actions_summary": [ -         { -           "id": 2, -           "count": 13, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 3, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 4, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 5, -           "count": 0, -           "hidden": true, -           "can_act": false -         }, -         { -           "id": 6, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 7, -           "count": 0, -           "hidden": false, -           "can_act": false -         }, -         { -           "id": 8, -           "count": 0, -           "hidden": false, -           "can_act": false -         } -       ], -       "moderator": true, -       "admin": true, -       "staff": true, -       "user_id": 8222, -       "hidden": false, -       "hidden_reason_id": null, -       "trust_level": 4, -       "deleted_at": null, -       "user_deleted": false, -       "edit_reason": "", -       "can_view_edit_history": true, -       "wiki": true, -       "blurb": "So you want to set up Discourse on Mac OS X to hack on and develop with? We'll assume that you don't have Ruby/Rails/Postgre/Redis installed on your Mac. Let's be..." -     }, -     { -       "id": 38398, -       "name": "Eric Carlson", -       "username": "ecuk", -       "avatar_template": "/letter_avatar/ecuk/{size}/5_fcf819f9b3791cb8c87edf29c8984f83.png", -       "uploaded_avatar_id": null, -       "created_at": "2014-01-24T15:08:06.111Z", -       "cooked": "

      Continuing the discussion from Log of setting up Docker in Virtualbox<\/a>:<\/p>\n\n

      `); +
      ` + ); } }); @@ -56,14 +54,14 @@ var generateClickEventOn = function(selector) { }; QUnit.test("does not track clicks on lightboxes", assert => { - var clickEvent = generateClickEventOn('.lightbox'); + var clickEvent = generateClickEventOn(".lightbox"); sandbox.stub(clickEvent, "preventDefault"); assert.ok(track(clickEvent)); assert.ok(!clickEvent.preventDefault.calledOnce); }); QUnit.test("it calls preventDefault when clicking on an a", assert => { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); sandbox.stub(clickEvent, "preventDefault"); track(clickEvent); assert.ok(clickEvent.preventDefault.calledOnce); @@ -71,37 +69,37 @@ QUnit.test("it calls preventDefault when clicking on an a", assert => { }); QUnit.test("does not track clicks when forcibly disabled", assert => { - assert.ok(track(generateClickEventOn('.no-track-link'))); + assert.ok(track(generateClickEventOn(".no-track-link"))); }); QUnit.test("does not track clicks on back buttons", assert => { - assert.ok(track(generateClickEventOn('.back'))); + assert.ok(track(generateClickEventOn(".back"))); }); QUnit.test("does not track clicks on category badges", assert => { - assert.ok(track(generateClickEventOn('.hashtag'))); + assert.ok(track(generateClickEventOn(".hashtag"))); }); QUnit.test("removes the href and put it as a data attribute", assert => { - track(generateClickEventOn('a')); + track(generateClickEventOn("a")); - var $link = fixture('a').first(); - assert.ok($link.hasClass('no-href')); - assert.equal($link.data('href'), 'http://www.google.com'); - assert.blank($link.attr('href')); - assert.ok($link.data('auto-route')); + var $link = fixture("a").first(); + assert.ok($link.hasClass("no-href")); + assert.equal($link.data("href"), "http://www.google.com"); + assert.blank($link.attr("href")); + assert.ok($link.data("auto-route")); assert.ok(DiscourseURL.redirectTo.calledOnce); }); asyncTestDiscourse("restores the href after a while", function(assert) { assert.expect(1); - track(generateClickEventOn('a')); + track(generateClickEventOn("a")); const done = assert.async(); setTimeout(function() { done(); - assert.equal(fixture('a').attr('href'), "http://www.google.com"); + assert.equal(fixture("a").attr("href"), "http://www.google.com"); }, 75); }); @@ -112,18 +110,28 @@ var trackRightClick = function(target) { }; QUnit.test("right clicks change the href", assert => { - assert.ok(trackRightClick('a')); - assert.equal(fixture('a').first().prop('href'), "http://www.google.com/"); + assert.ok(trackRightClick("a")); + assert.equal( + fixture("a") + .first() + .prop("href"), + "http://www.google.com/" + ); }); QUnit.test("right clicks are tracked", assert => { Discourse.SiteSettings.track_external_right_clicks = true; - trackRightClick('a'); - assert.equal(fixture('a').first().attr('href'), "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"); + trackRightClick("a"); + assert.equal( + fixture("a") + .first() + .attr("href"), + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337" + ); }); QUnit.test("preventDefault is not called for right clicks", assert => { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); clickEvent.which = 3; sandbox.stub(clickEvent, "preventDefault"); assert.ok(track(clickEvent)); @@ -132,7 +140,7 @@ QUnit.test("preventDefault is not called for right clicks", assert => { var testOpenInANewTab = function(description, clickEventModifier) { test(description, function(assert) { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); clickEventModifier(clickEvent); sandbox.stub(clickEvent, "preventDefault"); assert.ok(track(clickEvent)); @@ -140,19 +148,27 @@ var testOpenInANewTab = function(description, clickEventModifier) { }); }; -testOpenInANewTab("it opens in a new tab when pressing shift", function(clickEvent) { +testOpenInANewTab("it opens in a new tab when pressing shift", function( + clickEvent +) { clickEvent.shiftKey = true; }); -testOpenInANewTab("it opens in a new tab when pressing meta", function(clickEvent) { +testOpenInANewTab("it opens in a new tab when pressing meta", function( + clickEvent +) { clickEvent.metaKey = true; }); -testOpenInANewTab("it opens in a new tab when pressing ctrl", function(clickEvent) { +testOpenInANewTab("it opens in a new tab when pressing ctrl", function( + clickEvent +) { clickEvent.ctrlKey = true; }); -testOpenInANewTab("it opens in a new tab on middle click", function(clickEvent) { +testOpenInANewTab("it opens in a new tab on middle click", function( + clickEvent +) { clickEvent.button = 2; }); @@ -160,7 +176,7 @@ QUnit.test("tracks via AJAX if we're on the same site", assert => { sandbox.stub(DiscourseURL, "routeTo"); sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com"); - assert.ok(!track(generateClickEventOn('#same-site'))); + assert.ok(!track(generateClickEventOn("#same-site"))); assert.ok(DiscourseURL.routeTo.calledOnce); }); @@ -168,19 +184,31 @@ QUnit.test("does not track via AJAX for attachments", assert => { sandbox.stub(DiscourseURL, "routeTo"); sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com"); - assert.ok(!track(generateClickEventOn('.attachment'))); + assert.ok(!track(generateClickEventOn(".attachment"))); assert.ok(DiscourseURL.redirectTo.calledOnce); }); QUnit.test("tracks custom urls when opening in another window", assert => { - var clickEvent = generateClickEventOn('a'); - sandbox.stub(Discourse.User, "currentProp").withArgs('external_links_in_new_tab').returns(true); + var clickEvent = generateClickEventOn("a"); + sandbox + .stub(Discourse.User, "currentProp") + .withArgs("external_links_in_new_tab") + .returns(true); assert.ok(!track(clickEvent)); - assert.ok(windowOpen.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337', '_blank')); + assert.ok( + windowOpen.calledWith( + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337", + "_blank" + ) + ); }); QUnit.test("tracks custom urls when opening in another window", assert => { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); assert.ok(!track(clickEvent)); - assert.ok(redirectTo.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337')); + assert.ok( + redirectTo.calledWith( + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337" + ) + ); }); diff --git a/test/javascripts/lib/click-track-profile-page-test.js.es6 b/test/javascripts/lib/click-track-profile-page-test.js.es6 index c809059c060..fdf3162f13b 100644 --- a/test/javascripts/lib/click-track-profile-page-test.js.es6 +++ b/test/javascripts/lib/click-track-profile-page-test.js.es6 @@ -1,15 +1,12 @@ import DiscourseURL from "discourse/lib/url"; import ClickTrack from "discourse/lib/click-track"; -var windowOpen, - win, - redirectTo; +var windowOpen, win, redirectTo; QUnit.module("lib:click-track-profile-page", { beforeEach() { - // Prevent any of these tests from navigating away - win = {focus: function() { } }; + win = { focus: function() {} }; redirectTo = sandbox.stub(DiscourseURL, "redirectTo"); windowOpen = sandbox.stub(window, "open").returns(win); sandbox.stub(win, "focus"); @@ -38,7 +35,8 @@ QUnit.module("lib:click-track-profile-page", { forum log.txt #hashtag -

      `); +

      ` + ); } }); @@ -50,14 +48,14 @@ var generateClickEventOn = function(selector) { }; QUnit.test("does not track clicks on lightboxes", assert => { - var clickEvent = generateClickEventOn('.lightbox'); + var clickEvent = generateClickEventOn(".lightbox"); sandbox.stub(clickEvent, "preventDefault"); assert.ok(track(clickEvent)); assert.ok(!clickEvent.preventDefault.calledOnce); }); QUnit.test("it calls preventDefault when clicking on an a", assert => { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); sandbox.stub(clickEvent, "preventDefault"); track(clickEvent); assert.ok(clickEvent.preventDefault.calledOnce); @@ -65,37 +63,37 @@ QUnit.test("it calls preventDefault when clicking on an a", assert => { }); QUnit.test("does not track clicks when forcibly disabled", assert => { - assert.ok(track(generateClickEventOn('.no-track-link'))); + assert.ok(track(generateClickEventOn(".no-track-link"))); }); QUnit.test("does not track clicks on back buttons", assert => { - assert.ok(track(generateClickEventOn('.back'))); + assert.ok(track(generateClickEventOn(".back"))); }); QUnit.test("does not track clicks on category badges", assert => { - assert.ok(track(generateClickEventOn('.hashtag'))); + assert.ok(track(generateClickEventOn(".hashtag"))); }); QUnit.test("removes the href and put it as a data attribute", assert => { - track(generateClickEventOn('a')); + track(generateClickEventOn("a")); - var $link = fixture('a').first(); - assert.ok($link.hasClass('no-href')); - assert.equal($link.data('href'), 'http://www.google.com'); - assert.blank($link.attr('href')); - assert.ok($link.data('auto-route')); + var $link = fixture("a").first(); + assert.ok($link.hasClass("no-href")); + assert.equal($link.data("href"), "http://www.google.com"); + assert.blank($link.attr("href")); + assert.ok($link.data("auto-route")); assert.ok(DiscourseURL.redirectTo.calledOnce); }); asyncTestDiscourse("restores the href after a while", function(assert) { assert.expect(1); - track(generateClickEventOn('a')); + track(generateClickEventOn("a")); const done = assert.async(); setTimeout(function() { done(); - assert.equal(fixture('a').attr('href'), "http://www.google.com"); + assert.equal(fixture("a").attr("href"), "http://www.google.com"); }, 75); }); @@ -106,24 +104,39 @@ var trackRightClick = function(target) { }; QUnit.test("right clicks change the href", assert => { - assert.ok(trackRightClick('a')); - assert.equal(fixture('a').first().prop('href'), "http://www.google.com/"); + assert.ok(trackRightClick("a")); + assert.equal( + fixture("a") + .first() + .prop("href"), + "http://www.google.com/" + ); }); QUnit.test("right clicks are tracked", assert => { Discourse.SiteSettings.track_external_right_clicks = true; - trackRightClick('a'); - assert.equal(fixture('.first a').first().attr('href'), "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"); + trackRightClick("a"); + assert.equal( + fixture(".first a") + .first() + .attr("href"), + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337" + ); }); QUnit.test("right clicks are tracked for second excerpt", assert => { Discourse.SiteSettings.track_external_right_clicks = true; - trackRightClick('.second a'); - assert.equal(fixture('.second a').first().attr('href'), "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331"); + trackRightClick(".second a"); + assert.equal( + fixture(".second a") + .first() + .attr("href"), + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331" + ); }); QUnit.test("preventDefault is not called for right clicks", assert => { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); clickEvent.which = 3; sandbox.stub(clickEvent, "preventDefault"); assert.ok(track(clickEvent)); @@ -132,7 +145,7 @@ QUnit.test("preventDefault is not called for right clicks", assert => { var testOpenInANewTab = function(description, clickEventModifier) { test(description, function(assert) { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); clickEventModifier(clickEvent); sandbox.stub(clickEvent, "preventDefault"); assert.ok(track(clickEvent)); @@ -140,19 +153,27 @@ var testOpenInANewTab = function(description, clickEventModifier) { }); }; -testOpenInANewTab("it opens in a new tab when pressing shift", function(clickEvent) { +testOpenInANewTab("it opens in a new tab when pressing shift", function( + clickEvent +) { clickEvent.shiftKey = true; }); -testOpenInANewTab("it opens in a new tab when pressing meta", function(clickEvent) { +testOpenInANewTab("it opens in a new tab when pressing meta", function( + clickEvent +) { clickEvent.metaKey = true; }); -testOpenInANewTab("it opens in a new tab when pressing ctrl", function(clickEvent) { +testOpenInANewTab("it opens in a new tab when pressing ctrl", function( + clickEvent +) { clickEvent.ctrlKey = true; }); -testOpenInANewTab("it opens in a new tab on middle click", function(clickEvent) { +testOpenInANewTab("it opens in a new tab on middle click", function( + clickEvent +) { clickEvent.button = 2; }); @@ -160,7 +181,7 @@ QUnit.test("tracks via AJAX if we're on the same site", assert => { sandbox.stub(DiscourseURL, "routeTo"); sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com"); - assert.ok(!track(generateClickEventOn('#same-site'))); + assert.ok(!track(generateClickEventOn("#same-site"))); assert.ok(DiscourseURL.routeTo.calledOnce); }); @@ -168,32 +189,62 @@ QUnit.test("does not track via AJAX for attachments", assert => { sandbox.stub(DiscourseURL, "routeTo"); sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com"); - assert.ok(!track(generateClickEventOn('.attachment'))); + assert.ok(!track(generateClickEventOn(".attachment"))); assert.ok(DiscourseURL.redirectTo.calledOnce); }); QUnit.test("tracks custom urls when opening in another window", assert => { - var clickEvent = generateClickEventOn('a'); - sandbox.stub(Discourse.User, "currentProp").withArgs('external_links_in_new_tab').returns(true); + var clickEvent = generateClickEventOn("a"); + sandbox + .stub(Discourse.User, "currentProp") + .withArgs("external_links_in_new_tab") + .returns(true); assert.ok(!track(clickEvent)); - assert.ok(windowOpen.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337', '_blank')); + assert.ok( + windowOpen.calledWith( + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337", + "_blank" + ) + ); }); -QUnit.test("tracks custom urls on second excerpt when opening in another window", assert => { - var clickEvent = generateClickEventOn('.second a'); - sandbox.stub(Discourse.User, "currentProp").withArgs('external_links_in_new_tab').returns(true); - assert.ok(!track(clickEvent)); - assert.ok(windowOpen.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331', '_blank')); -}); +QUnit.test( + "tracks custom urls on second excerpt when opening in another window", + assert => { + var clickEvent = generateClickEventOn(".second a"); + sandbox + .stub(Discourse.User, "currentProp") + .withArgs("external_links_in_new_tab") + .returns(true); + assert.ok(!track(clickEvent)); + assert.ok( + windowOpen.calledWith( + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331", + "_blank" + ) + ); + } +); QUnit.test("tracks custom urls when opening in another window", assert => { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); assert.ok(!track(clickEvent)); - assert.ok(redirectTo.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337')); + assert.ok( + redirectTo.calledWith( + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337" + ) + ); }); -QUnit.test("tracks custom urls on second excerpt when opening in another window", assert => { - var clickEvent = generateClickEventOn('.second a'); - assert.ok(!track(clickEvent)); - assert.ok(redirectTo.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331')); -}); +QUnit.test( + "tracks custom urls on second excerpt when opening in another window", + assert => { + var clickEvent = generateClickEventOn(".second a"); + assert.ok(!track(clickEvent)); + assert.ok( + redirectTo.calledWith( + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331" + ) + ); + } +); diff --git a/test/javascripts/lib/click-track-test.js.es6 b/test/javascripts/lib/click-track-test.js.es6 index dc6013ee085..3933768c152 100644 --- a/test/javascripts/lib/click-track-test.js.es6 +++ b/test/javascripts/lib/click-track-test.js.es6 @@ -1,14 +1,12 @@ import DiscourseURL from "discourse/lib/url"; import ClickTrack from "discourse/lib/click-track"; -var windowOpen, - win, - redirectTo; +var windowOpen, win, redirectTo; QUnit.module("lib:click-track", { beforeEach() { // Prevent any of these tests from navigating away - win = {focus: function() { } }; + win = { focus: function() {} }; redirectTo = sandbox.stub(DiscourseURL, "redirectTo"); windowOpen = sandbox.stub(window, "open").returns(win); sandbox.stub(win, "focus"); @@ -35,7 +33,8 @@ QUnit.module("lib:click-track", { foobar -
      `); +
      ` + ); } }); @@ -47,14 +46,14 @@ var generateClickEventOn = function(selector) { }; QUnit.test("does not track clicks on lightboxes", function(assert) { - var clickEvent = generateClickEventOn('.lightbox'); + var clickEvent = generateClickEventOn(".lightbox"); sandbox.stub(clickEvent, "preventDefault"); assert.ok(track(clickEvent)); assert.ok(!clickEvent.preventDefault.calledOnce); }); QUnit.test("it calls preventDefault when clicking on an a", function(assert) { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); sandbox.stub(clickEvent, "preventDefault"); track(clickEvent); assert.ok(clickEvent.preventDefault.calledOnce); @@ -62,90 +61,106 @@ QUnit.test("it calls preventDefault when clicking on an a", function(assert) { }); QUnit.test("does not track clicks when forcibly disabled", function(assert) { - assert.ok(track(generateClickEventOn('.no-track-link'))); + assert.ok(track(generateClickEventOn(".no-track-link"))); }); QUnit.test("does not track clicks on back buttons", function(assert) { - assert.ok(track(generateClickEventOn('.back'))); + assert.ok(track(generateClickEventOn(".back"))); }); QUnit.test("does not track clicks in quotes", function(assert) { - track(generateClickEventOn('.inside-quote')); + track(generateClickEventOn(".inside-quote")); assert.ok(DiscourseURL.redirectTo.calledWith("http://discuss.domain.com")); }); QUnit.test("does not track clicks on category badges", assert => { - assert.ok(track(generateClickEventOn('.hashtag'))); + assert.ok(track(generateClickEventOn(".hashtag"))); }); QUnit.test("does not track clicks on mailto", function(assert) { - assert.ok(track(generateClickEventOn('.mailto'))); + assert.ok(track(generateClickEventOn(".mailto"))); }); QUnit.test("removes the href and put it as a data attribute", function(assert) { - track(generateClickEventOn('a')); + track(generateClickEventOn("a")); - var $link = fixture('a').first(); - assert.ok($link.hasClass('no-href')); - assert.equal($link.data('href'), 'http://www.google.com'); - assert.blank($link.attr('href')); - assert.ok($link.data('auto-route')); + var $link = fixture("a").first(); + assert.ok($link.hasClass("no-href")); + assert.equal($link.data("href"), "http://www.google.com"); + assert.blank($link.attr("href")); + assert.ok($link.data("auto-route")); assert.ok(DiscourseURL.redirectTo.calledOnce); }); asyncTestDiscourse("restores the href after a while", function(assert) { assert.expect(1); - track(generateClickEventOn('a')); + track(generateClickEventOn("a")); const done = assert.async(); setTimeout(function() { done(); - assert.equal(fixture('a').attr('href'), "http://www.google.com"); + assert.equal(fixture("a").attr("href"), "http://www.google.com"); }, 75); }); var badgeClickCount = function(assert, id, expected) { - track(generateClickEventOn('#' + id)); - var $badge = $('span.badge', fixture('#' + id).first()); + track(generateClickEventOn("#" + id)); + var $badge = $("span.badge", fixture("#" + id).first()); assert.equal(parseInt($badge.html(), 10), expected); }; QUnit.test("does not update badge clicks on my own link", function(assert) { - sandbox.stub(Discourse.User, 'currentProp').withArgs('id').returns(314); - badgeClickCount(assert, 'with-badge', 1); + sandbox + .stub(Discourse.User, "currentProp") + .withArgs("id") + .returns(314); + badgeClickCount(assert, "with-badge", 1); }); QUnit.test("does not update badge clicks in my own post", function(assert) { - sandbox.stub(Discourse.User, 'currentProp').withArgs('id').returns(3141); - badgeClickCount(assert, 'with-badge-but-not-mine', 1); + sandbox + .stub(Discourse.User, "currentProp") + .withArgs("id") + .returns(3141); + badgeClickCount(assert, "with-badge-but-not-mine", 1); }); QUnit.test("updates badge counts correctly", function(assert) { - badgeClickCount(assert, 'inside-onebox', 1); - badgeClickCount(assert, 'inside-onebox-forced', 2); - badgeClickCount(assert, 'with-badge', 2); + badgeClickCount(assert, "inside-onebox", 1); + badgeClickCount(assert, "inside-onebox-forced", 2); + badgeClickCount(assert, "with-badge", 2); }); var trackRightClick = function() { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); clickEvent.which = 3; return track(clickEvent); }; QUnit.test("right clicks change the href", function(assert) { assert.ok(trackRightClick()); - assert.equal(fixture('a').first().prop('href'), "http://www.google.com/"); + assert.equal( + fixture("a") + .first() + .prop("href"), + "http://www.google.com/" + ); }); QUnit.test("right clicks are tracked", function(assert) { Discourse.SiteSettings.track_external_right_clicks = true; trackRightClick(); - assert.equal(fixture('a').first().attr('href'), "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"); + assert.equal( + fixture("a") + .first() + .attr("href"), + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337" + ); }); QUnit.test("preventDefault is not called for right clicks", function(assert) { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); clickEvent.which = 3; sandbox.stub(clickEvent, "preventDefault"); assert.ok(track(clickEvent)); @@ -154,7 +169,7 @@ QUnit.test("preventDefault is not called for right clicks", function(assert) { var testOpenInANewTab = function(description, clickEventModifier) { test(description, function(assert) { - var clickEvent = generateClickEventOn('a'); + var clickEvent = generateClickEventOn("a"); clickEventModifier(clickEvent); sandbox.stub(clickEvent, "preventDefault"); assert.ok(track(clickEvent)); @@ -162,19 +177,27 @@ var testOpenInANewTab = function(description, clickEventModifier) { }); }; -testOpenInANewTab("it opens in a new tab when pressing shift", function(clickEvent) { +testOpenInANewTab("it opens in a new tab when pressing shift", function( + clickEvent +) { clickEvent.shiftKey = true; }); -testOpenInANewTab("it opens in a new tab when pressing meta", function(clickEvent) { +testOpenInANewTab("it opens in a new tab when pressing meta", function( + clickEvent +) { clickEvent.metaKey = true; }); -testOpenInANewTab("it opens in a new tab when pressing ctrl", function(clickEvent) { +testOpenInANewTab("it opens in a new tab when pressing ctrl", function( + clickEvent +) { clickEvent.ctrlKey = true; }); -testOpenInANewTab("it opens in a new tab on middle click", function(clickEvent) { +testOpenInANewTab("it opens in a new tab on middle click", function( + clickEvent +) { clickEvent.button = 2; }); @@ -182,7 +205,7 @@ QUnit.test("tracks via AJAX if we're on the same site", function(assert) { sandbox.stub(DiscourseURL, "routeTo"); sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com"); - assert.ok(!track(generateClickEventOn('#same-site'))); + assert.ok(!track(generateClickEventOn("#same-site"))); assert.ok(DiscourseURL.routeTo.calledOnce); }); @@ -190,19 +213,35 @@ QUnit.test("does not track via AJAX for attachments", function(assert) { sandbox.stub(DiscourseURL, "routeTo"); sandbox.stub(DiscourseURL, "origin").returns("http://discuss.domain.com"); - assert.ok(!track(generateClickEventOn('.attachment'))); + assert.ok(!track(generateClickEventOn(".attachment"))); assert.ok(DiscourseURL.redirectTo.calledOnce); }); -QUnit.test("tracks custom urls when opening in another window", function(assert) { - var clickEvent = generateClickEventOn('a'); - sandbox.stub(Discourse.User, "currentProp").withArgs('external_links_in_new_tab').returns(true); +QUnit.test("tracks custom urls when opening in another window", function( + assert +) { + var clickEvent = generateClickEventOn("a"); + sandbox + .stub(Discourse.User, "currentProp") + .withArgs("external_links_in_new_tab") + .returns(true); assert.ok(!track(clickEvent)); - assert.ok(windowOpen.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337', '_blank')); + assert.ok( + windowOpen.calledWith( + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337", + "_blank" + ) + ); }); -QUnit.test("tracks custom urls when opening in another window", function(assert) { - var clickEvent = generateClickEventOn('a'); +QUnit.test("tracks custom urls when opening in another window", function( + assert +) { + var clickEvent = generateClickEventOn("a"); assert.ok(!track(clickEvent)); - assert.ok(redirectTo.calledWith('/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337')); + assert.ok( + redirectTo.calledWith( + "/clicks/track?url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337" + ) + ); }); diff --git a/test/javascripts/lib/computed-test.js.es6 b/test/javascripts/lib/computed-test.js.es6 index 6752784a2cb..26158686ade 100644 --- a/test/javascripts/lib/computed-test.js.es6 +++ b/test/javascripts/lib/computed-test.js.es6 @@ -1,4 +1,11 @@ -import { setting, propertyEqual, propertyNotEqual, fmt, i18n, url } from 'discourse/lib/computed'; +import { + setting, + propertyEqual, + propertyNotEqual, + fmt, + i18n, + url +} from "discourse/lib/computed"; QUnit.module("lib:computed", { beforeEach() { @@ -14,91 +21,135 @@ QUnit.module("lib:computed", { QUnit.test("setting", assert => { var t = Em.Object.extend({ - vehicle: setting('vehicle'), - missingProp: setting('madeUpThing') + vehicle: setting("vehicle"), + missingProp: setting("madeUpThing") }).create(); Discourse.SiteSettings.vehicle = "airplane"; - assert.equal(t.get('vehicle'), "airplane", "it has the value of the site setting"); - assert.ok(!t.get('missingProp'), "it is falsy when the site setting is not defined"); + assert.equal( + t.get("vehicle"), + "airplane", + "it has the value of the site setting" + ); + assert.ok( + !t.get("missingProp"), + "it is falsy when the site setting is not defined" + ); }); QUnit.test("propertyEqual", assert => { var t = Em.Object.extend({ - same: propertyEqual('cookies', 'biscuits') + same: propertyEqual("cookies", "biscuits") }).create({ cookies: 10, biscuits: 10 }); - assert.ok(t.get('same'), "it is true when the properties are the same"); - t.set('biscuits', 9); - assert.ok(!t.get('same'), "it isn't true when one property is different"); + assert.ok(t.get("same"), "it is true when the properties are the same"); + t.set("biscuits", 9); + assert.ok(!t.get("same"), "it isn't true when one property is different"); }); QUnit.test("propertyNotEqual", assert => { var t = Em.Object.extend({ - diff: propertyNotEqual('cookies', 'biscuits') + diff: propertyNotEqual("cookies", "biscuits") }).create({ cookies: 10, biscuits: 10 }); - assert.ok(!t.get('diff'), "it isn't true when the properties are the same"); - t.set('biscuits', 9); - assert.ok(t.get('diff'), "it is true when one property is different"); + assert.ok(!t.get("diff"), "it isn't true when the properties are the same"); + t.set("biscuits", 9); + assert.ok(t.get("diff"), "it is true when one property is different"); }); - QUnit.test("fmt", assert => { var t = Em.Object.extend({ - exclaimyUsername: fmt('username', "!!! %@ !!!"), - multiple: fmt('username', 'mood', "%@ is %@") + exclaimyUsername: fmt("username", "!!! %@ !!!"), + multiple: fmt("username", "mood", "%@ is %@") }).create({ - username: 'eviltrout', + username: "eviltrout", mood: "happy" }); - assert.equal(t.get('exclaimyUsername'), '!!! eviltrout !!!', "it inserts the string"); - assert.equal(t.get('multiple'), "eviltrout is happy", "it inserts multiple strings"); + assert.equal( + t.get("exclaimyUsername"), + "!!! eviltrout !!!", + "it inserts the string" + ); + assert.equal( + t.get("multiple"), + "eviltrout is happy", + "it inserts multiple strings" + ); - t.set('username', 'codinghorror'); - assert.equal(t.get('multiple'), "codinghorror is happy", "it supports changing properties"); - t.set('mood', 'ecstatic'); - assert.equal(t.get('multiple'), "codinghorror is ecstatic", "it supports changing another property"); + t.set("username", "codinghorror"); + assert.equal( + t.get("multiple"), + "codinghorror is happy", + "it supports changing properties" + ); + t.set("mood", "ecstatic"); + assert.equal( + t.get("multiple"), + "codinghorror is ecstatic", + "it supports changing another property" + ); }); - QUnit.test("i18n", assert => { var t = Em.Object.extend({ - exclaimyUsername: i18n('username', "!!! %@ !!!"), - multiple: i18n('username', 'mood', "%@ is %@") + exclaimyUsername: i18n("username", "!!! %@ !!!"), + multiple: i18n("username", "mood", "%@ is %@") }).create({ - username: 'eviltrout', + username: "eviltrout", mood: "happy" }); - assert.equal(t.get('exclaimyUsername'), '%@ translated: !!! eviltrout !!!', "it inserts the string and then translates"); - assert.equal(t.get('multiple'), "%@ translated: eviltrout is happy", "it inserts multiple strings and then translates"); + assert.equal( + t.get("exclaimyUsername"), + "%@ translated: !!! eviltrout !!!", + "it inserts the string and then translates" + ); + assert.equal( + t.get("multiple"), + "%@ translated: eviltrout is happy", + "it inserts multiple strings and then translates" + ); - t.set('username', 'codinghorror'); - assert.equal(t.get('multiple'), "%@ translated: codinghorror is happy", "it supports changing properties"); - t.set('mood', 'ecstatic'); - assert.equal(t.get('multiple'), "%@ translated: codinghorror is ecstatic", "it supports changing another property"); + t.set("username", "codinghorror"); + assert.equal( + t.get("multiple"), + "%@ translated: codinghorror is happy", + "it supports changing properties" + ); + t.set("mood", "ecstatic"); + assert.equal( + t.get("multiple"), + "%@ translated: codinghorror is ecstatic", + "it supports changing another property" + ); }); - QUnit.test("url", assert => { var t, testClass; testClass = Em.Object.extend({ - userUrl: url('username', "/u/%@") + userUrl: url("username", "/u/%@") }); - t = testClass.create({ username: 'eviltrout' }); - assert.equal(t.get('userUrl'), "/u/eviltrout", "it supports urls without a prefix"); + t = testClass.create({ username: "eviltrout" }); + assert.equal( + t.get("userUrl"), + "/u/eviltrout", + "it supports urls without a prefix" + ); Discourse.BaseUri = "/prefixed"; - t = testClass.create({ username: 'eviltrout' }); - assert.equal(t.get('userUrl'), "/prefixed/u/eviltrout", "it supports urls with a prefix"); + t = testClass.create({ username: "eviltrout" }); + assert.equal( + t.get("userUrl"), + "/prefixed/u/eviltrout", + "it supports urls with a prefix" + ); }); diff --git a/test/javascripts/lib/discourse-test.js.es6 b/test/javascripts/lib/discourse-test.js.es6 index 4bafe429abb..9627eafdbb9 100644 --- a/test/javascripts/lib/discourse-test.js.es6 +++ b/test/javascripts/lib/discourse-test.js.es6 @@ -3,5 +3,9 @@ QUnit.module("lib:discourse"); QUnit.test("getURL on subfolder install", assert => { Discourse.BaseUri = "/forum"; assert.equal(Discourse.getURL("/"), "/forum/", "root url has subfolder"); - assert.equal(Discourse.getURL("/u/neil"), "/forum/u/neil", "relative url has subfolder"); -}); \ No newline at end of file + assert.equal( + Discourse.getURL("/u/neil"), + "/forum/u/neil", + "relative url has subfolder" + ); +}); diff --git a/test/javascripts/lib/emoji-test.js.es6 b/test/javascripts/lib/emoji-test.js.es6 index 8512b9f05ce..47665a8c8ae 100644 --- a/test/javascripts/lib/emoji-test.js.es6 +++ b/test/javascripts/lib/emoji-test.js.es6 @@ -1,27 +1,68 @@ -import { emojiSearch, IMAGE_VERSION as v } from 'pretty-text/emoji'; -import { emojiUnescape } from 'discourse/lib/text'; +import { emojiSearch, IMAGE_VERSION as v } from "pretty-text/emoji"; +import { emojiUnescape } from "discourse/lib/text"; -QUnit.module('lib:emoji'); +QUnit.module("lib:emoji"); QUnit.test("emojiUnescape", assert => { const testUnescape = (input, expected, description) => { assert.equal(emojiUnescape(input), expected, description); }; - testUnescape("Not emoji :O) :frog) :smile)", "Not emoji :O) :frog) :smile)", "title without emoji"); - testUnescape("Not emoji :frog :smile", "Not emoji :frog :smile", "end colon is not optional"); - testUnescape("emoticons :)", `emoticons slight_smile`, "emoticons are still supported"); - testUnescape("With emoji :O: :frog: :smile:", + testUnescape( + "Not emoji :O) :frog) :smile)", + "Not emoji :O) :frog) :smile)", + "title without emoji" + ); + testUnescape( + "Not emoji :frog :smile", + "Not emoji :frog :smile", + "end colon is not optional" + ); + testUnescape( + "emoticons :)", + `emoticons slight_smile`, + "emoticons are still supported" + ); + testUnescape( + "With emoji :O: :frog: :smile:", `With emoji O frog smile`, - "title with emoji"); - testUnescape("a:smile:a", "a:smile:a", "word characters not allowed next to emoji"); - testUnescape("(:frog:) :)", `(frog) slight_smile`, "non-word characters allowed next to emoji"); - testUnescape(":smile: hi", `smile hi`, "start of line"); - testUnescape("hi :smile:", `hi smile`, "end of line"); - testUnescape("hi :blonde_woman:t4:", `hi blonde_woman:t4`, "support for skin tones"); - testUnescape("hi :blonde_woman:t4: :blonde_man:t6:", `hi blonde_woman:t4 blonde_man:t6`, "support for multiple skin tones"); - testUnescape("hi :blonde_man:t6", "hi :blonde_man:t6", "end colon not optional for skin tones"); - + "title with emoji" + ); + testUnescape( + "a:smile:a", + "a:smile:a", + "word characters not allowed next to emoji" + ); + testUnescape( + "(:frog:) :)", + `(frog) slight_smile`, + "non-word characters allowed next to emoji" + ); + testUnescape( + ":smile: hi", + `smile hi`, + "start of line" + ); + testUnescape( + "hi :smile:", + `hi smile`, + "end of line" + ); + testUnescape( + "hi :blonde_woman:t4:", + `hi blonde_woman:t4`, + "support for skin tones" + ); + testUnescape( + "hi :blonde_woman:t4: :blonde_man:t6:", + `hi blonde_woman:t4 blonde_man:t6`, + "support for multiple skin tones" + ); + testUnescape( + "hi :blonde_man:t6", + "hi :blonde_man:t6", + "end colon not optional for skin tones" + ); }); QUnit.test("Emoji search", assert => { @@ -29,6 +70,5 @@ QUnit.test("Emoji search", assert => { assert.equal(emojiSearch("+1").length, 1); // able to find middle of line search - assert.equal(emojiSearch("check", {maxResults: 3}).length, 3); - + assert.equal(emojiSearch("check", { maxResults: 3 }).length, 3); }); diff --git a/test/javascripts/lib/formatter-test.js.es6 b/test/javascripts/lib/formatter-test.js.es6 index 984f689a63c..693fdeb8238 100644 --- a/test/javascripts/lib/formatter-test.js.es6 +++ b/test/javascripts/lib/formatter-test.js.es6 @@ -1,10 +1,18 @@ var clock; -import { relativeAge, autoUpdatingRelativeAge, updateRelativeAge, breakUp, number, longDate, durationTiny } from 'discourse/lib/formatter'; +import { + relativeAge, + autoUpdatingRelativeAge, + updateRelativeAge, + breakUp, + number, + longDate, + durationTiny +} from "discourse/lib/formatter"; QUnit.module("lib:formatter", { beforeEach() { - clock = sinon.useFakeTimers(new Date(2012,11,31,12,0).getTime()); + clock = sinon.useFakeTimers(new Date(2012, 11, 31, 12, 0).getTime()); }, afterEach() { @@ -14,12 +22,12 @@ QUnit.module("lib:formatter", { var format = "tiny"; var leaveAgo = false; -var mins_ago = function(mins){ - return new Date((new Date()) - mins * 60 * 1000); +var mins_ago = function(mins) { + return new Date(new Date() - mins * 60 * 1000); }; var formatMins = function(mins) { - return relativeAge(mins_ago(mins), {format: format, leaveAgo: leaveAgo}); + return relativeAge(mins_ago(mins), { format: format, leaveAgo: leaveAgo }); }; var formatHours = function(hours) { @@ -30,19 +38,22 @@ var formatDays = function(days) { return formatHours(days * 24); }; -var shortDate = function(days){ - return moment().subtract(days, 'days').format('MMM D'); +var shortDate = function(days) { + return moment() + .subtract(days, "days") + .format("MMM D"); }; QUnit.test("formating medium length dates", assert => { - format = "medium"; - var strip = function(html){ + var strip = function(html) { return $(html).text(); }; - var shortDateYear = function(days){ - return moment().subtract(days, 'days').format("MMM D, 'YY"); + var shortDateYear = function(days) { + return moment() + .subtract(days, "days") + .format("MMM D, 'YY"); }; leaveAgo = true; @@ -76,15 +87,17 @@ QUnit.test("formating medium length dates", assert => { assert.equal($(formatDays(0)).attr("class"), "date"); clock.restore(); - clock = sinon.useFakeTimers(new Date(2012,0,9,12,0).getTime()); // Jan 9, 2012 + clock = sinon.useFakeTimers(new Date(2012, 0, 9, 12, 0).getTime()); // Jan 9, 2012 assert.equal(strip(formatDays(8)), shortDate(8)); assert.equal(strip(formatDays(10)), shortDateYear(10)); }); QUnit.test("formating tiny dates", assert => { - var shortDateYear = function(days){ - return moment().subtract(days, 'days').format("MMM 'YY"); + var shortDateYear = function(days) { + return moment() + .subtract(days, "days") + .format("MMM 'YY"); }; format = "tiny"; @@ -103,7 +116,7 @@ QUnit.test("formating tiny dates", assert => { assert.equal(formatDays(365), shortDate(365)); assert.equal(formatDays(366), shortDateYear(366)); // leap year assert.equal(formatDays(500), shortDateYear(500)); - assert.equal(formatDays(365*2 + 1), shortDateYear(365*2 + 1)); // one leap year + assert.equal(formatDays(365 * 2 + 1), shortDateYear(365 * 2 + 1)); // one leap year var originalValue = Discourse.SiteSettings.relative_date_duration; Discourse.SiteSettings.relative_date_duration = 7; @@ -124,14 +137,14 @@ QUnit.test("formating tiny dates", assert => { assert.equal(formatDays(366), shortDateYear(366)); Discourse.SiteSettings.relative_date_duration = null; - assert.equal(formatDays(1), '1d'); - assert.equal(formatDays(14), '14d'); + assert.equal(formatDays(1), "1d"); + assert.equal(formatDays(14), "14d"); assert.equal(formatDays(15), shortDate(15)); Discourse.SiteSettings.relative_date_duration = 14; clock.restore(); - clock = sinon.useFakeTimers(new Date(2012,0,12,12,0).getTime()); // Jan 12, 2012 + clock = sinon.useFakeTimers(new Date(2012, 0, 12, 12, 0).getTime()); // Jan 12, 2012 assert.equal(formatDays(11), "11d"); assert.equal(formatDays(14), "14d"); @@ -139,7 +152,7 @@ QUnit.test("formating tiny dates", assert => { assert.equal(formatDays(366), shortDateYear(366)); clock.restore(); - clock = sinon.useFakeTimers(new Date(2012,0,20,12,0).getTime()); // Jan 20, 2012 + clock = sinon.useFakeTimers(new Date(2012, 0, 20, 12, 0).getTime()); // Jan 20, 2012 assert.equal(formatDays(14), "14d"); assert.equal(formatDays(15), shortDate(15)); @@ -149,51 +162,59 @@ QUnit.test("formating tiny dates", assert => { }); QUnit.test("autoUpdatingRelativeAge", assert => { - var d = moment().subtract(1, 'day').toDate(); + var d = moment() + .subtract(1, "day") + .toDate(); var $elem = $(autoUpdatingRelativeAge(d)); - assert.equal($elem.data('format'), "tiny"); - assert.equal($elem.data('time'), d.getTime()); - assert.equal($elem.attr('title'), undefined); + assert.equal($elem.data("format"), "tiny"); + assert.equal($elem.data("time"), d.getTime()); + assert.equal($elem.attr("title"), undefined); - $elem = $(autoUpdatingRelativeAge(d, {title: true})); - assert.equal($elem.attr('title'), longDate(d)); + $elem = $(autoUpdatingRelativeAge(d, { title: true })); + assert.equal($elem.attr("title"), longDate(d)); - $elem = $(autoUpdatingRelativeAge(d,{format: 'medium', title: true, leaveAgo: true})); - assert.equal($elem.data('format'), "medium-with-ago"); - assert.equal($elem.data('time'), d.getTime()); - assert.equal($elem.attr('title'), longDate(d)); - assert.equal($elem.html(), '1 day ago'); + $elem = $( + autoUpdatingRelativeAge(d, { + format: "medium", + title: true, + leaveAgo: true + }) + ); + assert.equal($elem.data("format"), "medium-with-ago"); + assert.equal($elem.data("time"), d.getTime()); + assert.equal($elem.attr("title"), longDate(d)); + assert.equal($elem.html(), "1 day ago"); - $elem = $(autoUpdatingRelativeAge(d,{format: 'medium'})); - assert.equal($elem.data('format'), "medium"); - assert.equal($elem.data('time'), d.getTime()); - assert.equal($elem.attr('title'), undefined); - assert.equal($elem.html(), '1 day'); + $elem = $(autoUpdatingRelativeAge(d, { format: "medium" })); + assert.equal($elem.data("format"), "medium"); + assert.equal($elem.data("time"), d.getTime()); + assert.equal($elem.attr("title"), undefined); + assert.equal($elem.html(), "1 day"); }); -QUnit.test("updateRelativeAge", assert =>{ - +QUnit.test("updateRelativeAge", assert => { var d = new Date(); var $elem = $(autoUpdatingRelativeAge(d)); - $elem.data('time', d.getTime() - 2 * 60 * 1000); + $elem.data("time", d.getTime() - 2 * 60 * 1000); updateRelativeAge($elem); assert.equal($elem.html(), "2m"); d = new Date(); - $elem = $(autoUpdatingRelativeAge(d, {format: 'medium', leaveAgo: true})); - $elem.data('time', d.getTime() - 2 * 60 * 1000); + $elem = $(autoUpdatingRelativeAge(d, { format: "medium", leaveAgo: true })); + $elem.data("time", d.getTime() - 2 * 60 * 1000); updateRelativeAge($elem); assert.equal($elem.html(), "2 mins ago"); }); -QUnit.test("breakUp", assert =>{ - - var b = function(s,hint){ return breakUp(s,hint); }; +QUnit.test("breakUp", assert => { + var b = function(s, hint) { + return breakUp(s, hint); + }; assert.equal(b("hello"), "hello"); assert.equal(b("helloworld"), "helloworld"); @@ -201,8 +222,10 @@ QUnit.test("breakUp", assert =>{ assert.equal(b("he_man"), "he_​man"); assert.equal(b("he11111"), "he​11111"); assert.equal(b("HRCBob"), "HRC​Bob"); - assert.equal(b("bobmarleytoo","Bob Marley Too"), "bob​marley​too"); - + assert.equal( + b("bobmarleytoo", "Bob Marley Too"), + "bob​marley​too" + ); }); QUnit.test("number", assert => { @@ -214,32 +237,56 @@ QUnit.test("number", assert => { assert.equal(number("2499999.5"), "2.5M", "it abbreviates millions"); assert.equal(number(1000000), "1.0M", "it abbreviates a million"); assert.equal(number(999999), "999k", "it abbreviates hundreds of thousands"); - assert.equal(number(18.2), "18", "it returns a float number rounded to an integer as a string"); - assert.equal(number(18.6), "19", "it returns a float number rounded to an integer as a string"); - assert.equal(number("12.3"), "12", "it returns a string float rounded to an integer as a string"); - assert.equal(number("12.6"), "13", "it returns a string float rounded to an integer as a string"); + assert.equal( + number(18.2), + "18", + "it returns a float number rounded to an integer as a string" + ); + assert.equal( + number(18.6), + "19", + "it returns a float number rounded to an integer as a string" + ); + assert.equal( + number("12.3"), + "12", + "it returns a string float rounded to an integer as a string" + ); + assert.equal( + number("12.6"), + "13", + "it returns a string float rounded to an integer as a string" + ); }); QUnit.test("durationTiny", assert => { - assert.equal(durationTiny(), '—', "undefined is a dash"); - assert.equal(durationTiny(null), '—', "null is a dash"); - assert.equal(durationTiny(0), '< 1m', "0 seconds shows as < 1m"); - assert.equal(durationTiny(59), '< 1m', "59 seconds shows as < 1m"); - assert.equal(durationTiny(60), '1m', "60 seconds shows as 1m"); - assert.equal(durationTiny(90), '2m', "90 seconds shows as 2m"); - assert.equal(durationTiny(120), '2m', "120 seconds shows as 2m"); - assert.equal(durationTiny(60 * 45), '1h', "45 minutes shows as 1h"); - assert.equal(durationTiny(60 * 60), '1h', "60 minutes shows as 1h"); - assert.equal(durationTiny(60 * 90), '2h', "90 minutes shows as 2h"); - assert.equal(durationTiny(3600 * 23), '23h', "23 hours shows as 23h"); - assert.equal(durationTiny(3600 * 24 - 29), '1d', "23 hours 31 mins shows as 1d"); - assert.equal(durationTiny(3600 * 24 * 89), '89d', "89 days shows as 89d"); - assert.equal(durationTiny(60 * (525600 - 1)), '12mon', "364 days shows as 12mon"); - assert.equal(durationTiny(60 * 525600), '1y', "365 days shows as 1y"); - assert.equal(durationTiny(86400 * 456), '1y', "456 days shows as 1y"); - assert.equal(durationTiny(86400 * 457), '> 1y', "457 days shows as > 1y"); - assert.equal(durationTiny(86400 * 638), '> 1y', "638 days shows as > 1y"); - assert.equal(durationTiny(86400 * 639), '2y', "639 days shows as 2y"); - assert.equal(durationTiny(86400 * 821), '2y', "821 days shows as 2y"); - assert.equal(durationTiny(86400 * 822), '> 2y', "822 days shows as > 2y"); + assert.equal(durationTiny(), "—", "undefined is a dash"); + assert.equal(durationTiny(null), "—", "null is a dash"); + assert.equal(durationTiny(0), "< 1m", "0 seconds shows as < 1m"); + assert.equal(durationTiny(59), "< 1m", "59 seconds shows as < 1m"); + assert.equal(durationTiny(60), "1m", "60 seconds shows as 1m"); + assert.equal(durationTiny(90), "2m", "90 seconds shows as 2m"); + assert.equal(durationTiny(120), "2m", "120 seconds shows as 2m"); + assert.equal(durationTiny(60 * 45), "1h", "45 minutes shows as 1h"); + assert.equal(durationTiny(60 * 60), "1h", "60 minutes shows as 1h"); + assert.equal(durationTiny(60 * 90), "2h", "90 minutes shows as 2h"); + assert.equal(durationTiny(3600 * 23), "23h", "23 hours shows as 23h"); + assert.equal( + durationTiny(3600 * 24 - 29), + "1d", + "23 hours 31 mins shows as 1d" + ); + assert.equal(durationTiny(3600 * 24 * 89), "89d", "89 days shows as 89d"); + assert.equal( + durationTiny(60 * (525600 - 1)), + "12mon", + "364 days shows as 12mon" + ); + assert.equal(durationTiny(60 * 525600), "1y", "365 days shows as 1y"); + assert.equal(durationTiny(86400 * 456), "1y", "456 days shows as 1y"); + assert.equal(durationTiny(86400 * 457), "> 1y", "457 days shows as > 1y"); + assert.equal(durationTiny(86400 * 638), "> 1y", "638 days shows as > 1y"); + assert.equal(durationTiny(86400 * 639), "2y", "639 days shows as 2y"); + assert.equal(durationTiny(86400 * 821), "2y", "821 days shows as 2y"); + assert.equal(durationTiny(86400 * 822), "> 2y", "822 days shows as > 2y"); }); diff --git a/test/javascripts/lib/i18n-test.js.es6 b/test/javascripts/lib/i18n-test.js.es6 index e653a8d0ca3..0f142589015 100644 --- a/test/javascripts/lib/i18n-test.js.es6 +++ b/test/javascripts/lib/i18n-test.js.es6 @@ -7,50 +7,50 @@ QUnit.module("lib:i18n", { I18n.locale = "fr"; I18n.translations = { - "fr_FOO": { - "js": { - "topic": { - "reply": { - "title": "Foo" + fr_FOO: { + js: { + topic: { + reply: { + title: "Foo" } - }, - } - }, - "fr": { - "js": { - "hello": "Bonjour", - "topic": { - "reply": { - "title": "Répondre" - }, - "share": { - "title": "Partager" - } - }, - "character_count": { - "zero": "{{count}} ZERO", - "one": "{{count}} ONE", - "two": "{{count}} TWO", - "few": "{{count}} FEW", - "many": "{{count}} MANY", - "other": "{{count}} OTHER" } } }, - "en": { - "js": { - "hello": { - "world": "Hello World!", - "universe": "" - }, - "topic": { - "reply": { - "help": "begin composing a reply to this topic" + fr: { + js: { + hello: "Bonjour", + topic: { + reply: { + title: "Répondre" + }, + share: { + title: "Partager" } }, - "word_count": { - "one": "1 word", - "other": "{{count}} words" + character_count: { + zero: "{{count}} ZERO", + one: "{{count}} ONE", + two: "{{count}} TWO", + few: "{{count}} FEW", + many: "{{count}} MANY", + other: "{{count}} OTHER" + } + } + }, + en: { + js: { + hello: { + world: "Hello World!", + universe: "" + }, + topic: { + reply: { + help: "begin composing a reply to this topic" + } + }, + word_count: { + one: "1 word", + other: "{{count}} words" } } } @@ -61,7 +61,7 @@ QUnit.module("lib:i18n", { if (n === 0) return "zero"; if (n === 1) return "one"; if (n === 2) return "two"; - if (n >= 3 && n <= 9) return "few"; + if (n >= 3 && n <= 9) return "few"; if (n >= 10 && n <= 99) return "many"; return "other"; }; @@ -80,16 +80,32 @@ QUnit.test("defaults", assert => { }); QUnit.test("translations", assert => { - assert.equal(I18n.t("topic.reply.title"), "Répondre", "uses locale translations when they exist"); - assert.equal(I18n.t("topic.reply.help"), "begin composing a reply to this topic", "fallbacks to English translations"); - assert.equal(I18n.t("hello.world"), "Hello World!", "doesn't break if a key is overriden in a locale"); + assert.equal( + I18n.t("topic.reply.title"), + "Répondre", + "uses locale translations when they exist" + ); + assert.equal( + I18n.t("topic.reply.help"), + "begin composing a reply to this topic", + "fallbacks to English translations" + ); + assert.equal( + I18n.t("hello.world"), + "Hello World!", + "doesn't break if a key is overriden in a locale" + ); assert.equal(I18n.t("hello.universe"), "", "allows empty strings"); }); QUnit.test("extra translations", assert => { - I18n.extras = [{ "admin": { "title": "Discourse Admin" }}]; + I18n.extras = [{ admin: { title: "Discourse Admin" } }]; - assert.equal(I18n.t("admin.title"), "Discourse Admin", "it check extra translations when they exists"); + assert.equal( + I18n.t("admin.title"), + "Discourse Admin", + "it check extra translations when they exists" + ); }); QUnit.test("pluralizations", assert => { @@ -112,7 +128,19 @@ QUnit.test("fallback", assert => { I18n.locale = "fr_FOO"; I18n.fallbackLocale = "fr"; - assert.equal(I18n.t("topic.reply.title"), "Foo", "uses locale translations when they exist"); - assert.equal(I18n.t("topic.share.title"), "Partager", "falls back to fallbackLocale translations when they exist"); - assert.equal(I18n.t("topic.reply.help"), "begin composing a reply to this topic", "falls back to English translations"); + assert.equal( + I18n.t("topic.reply.title"), + "Foo", + "uses locale translations when they exist" + ); + assert.equal( + I18n.t("topic.share.title"), + "Partager", + "falls back to fallbackLocale translations when they exist" + ); + assert.equal( + I18n.t("topic.reply.help"), + "begin composing a reply to this topic", + "falls back to English translations" + ); }); diff --git a/test/javascripts/lib/key-value-store-test.js.es6 b/test/javascripts/lib/key-value-store-test.js.es6 index 1bbafcc5cbc..5e21cb87709 100644 --- a/test/javascripts/lib/key-value-store-test.js.es6 +++ b/test/javascripts/lib/key-value-store-test.js.es6 @@ -2,17 +2,17 @@ import KeyValueStore from "discourse/lib/key-value-store"; QUnit.module("lib:key-value-store"); -QUnit.test("it's able to get the result back from the store", (assert) => { +QUnit.test("it's able to get the result back from the store", assert => { const store = new KeyValueStore("_test"); store.set({ key: "bob", value: "uncle" }); assert.equal(store.get("bob"), "uncle"); }); -QUnit.test("is able to nuke the store", (assert) => { +QUnit.test("is able to nuke the store", assert => { const store = new KeyValueStore("_test"); store.set({ key: "bob1", value: "uncle" }); store.abandonLocal(); localStorage.a = 1; assert.equal(store.get("bob1"), void 0); assert.equal(localStorage.a, "1"); -}); \ No newline at end of file +}); diff --git a/test/javascripts/lib/preload-store-test.js.es6 b/test/javascripts/lib/preload-store-test.js.es6 index 26f8d04059c..b17d8f20e8c 100644 --- a/test/javascripts/lib/preload-store-test.js.es6 +++ b/test/javascripts/lib/preload-store-test.js.es6 @@ -1,78 +1,100 @@ -import PreloadStore from 'preload-store'; +import PreloadStore from "preload-store"; QUnit.module("preload-store", { beforeEach() { - PreloadStore.store('bane', 'evil'); + PreloadStore.store("bane", "evil"); } }); QUnit.test("get", assert => { - assert.blank(PreloadStore.get('joker'), "returns blank for a missing key"); - assert.equal(PreloadStore.get('bane'), 'evil', "returns the value for that key"); + assert.blank(PreloadStore.get("joker"), "returns blank for a missing key"); + assert.equal( + PreloadStore.get("bane"), + "evil", + "returns the value for that key" + ); }); QUnit.test("remove", assert => { - PreloadStore.remove('bane'); - assert.blank(PreloadStore.get('bane'), "removes the value if the key exists"); + PreloadStore.remove("bane"); + assert.blank(PreloadStore.get("bane"), "removes the value if the key exists"); }); -asyncTestDiscourse("getAndRemove returns a promise that resolves to null", function(assert) { +asyncTestDiscourse( + "getAndRemove returns a promise that resolves to null", + function(assert) { + assert.expect(1); + + const done = assert.async(); + PreloadStore.getAndRemove("joker").then(function(result) { + assert.blank(result); + done(); + }); + } +); + +asyncTestDiscourse( + "getAndRemove returns a promise that resolves to the result of the finder", + function(assert) { + assert.expect(1); + + const done = assert.async(); + const finder = function() { + return "batdance"; + }; + PreloadStore.getAndRemove("joker", finder).then(function(result) { + assert.equal(result, "batdance"); + done(); + }); + } +); + +asyncTestDiscourse( + "getAndRemove returns a promise that resolves to the result of the finder's promise", + function(assert) { + assert.expect(1); + + const finder = function() { + return new Ember.RSVP.Promise(function(resolve) { + resolve("hahahah"); + }); + }; + + const done = assert.async(); + PreloadStore.getAndRemove("joker", finder).then(function(result) { + assert.equal(result, "hahahah"); + done(); + }); + } +); + +asyncTestDiscourse( + "returns a promise that rejects with the result of the finder's rejected promise", + function(assert) { + assert.expect(1); + + const finder = function() { + return new Ember.RSVP.Promise(function(resolve, reject) { + reject("error"); + }); + }; + + const done = assert.async(); + PreloadStore.getAndRemove("joker", finder).then(null, function(result) { + assert.equal(result, "error"); + done(); + }); + } +); + +asyncTestDiscourse("returns a promise that resolves to 'evil'", function( + assert +) { assert.expect(1); const done = assert.async(); - PreloadStore.getAndRemove('joker').then(function(result) { - assert.blank(result); - done(); - }); -}); - -asyncTestDiscourse("getAndRemove returns a promise that resolves to the result of the finder", function(assert) { - assert.expect(1); - - const done = assert.async(); - const finder = function() { return 'batdance'; }; - PreloadStore.getAndRemove('joker', finder).then(function(result) { - assert.equal(result, 'batdance'); - done(); - }); - -}); - -asyncTestDiscourse("getAndRemove returns a promise that resolves to the result of the finder's promise", function(assert) { - assert.expect(1); - - const finder = function() { - return new Ember.RSVP.Promise(function(resolve) { resolve('hahahah'); }); - }; - - const done = assert.async(); - PreloadStore.getAndRemove('joker', finder).then(function(result) { - assert.equal(result, 'hahahah'); - done(); - }); -}); - -asyncTestDiscourse("returns a promise that rejects with the result of the finder's rejected promise", function(assert) { - assert.expect(1); - - const finder = function() { - return new Ember.RSVP.Promise(function(resolve, reject) { reject('error'); }); - }; - - const done = assert.async(); - PreloadStore.getAndRemove('joker', finder).then(null, function(result) { - assert.equal(result, 'error'); - done(); - }); - -}); - -asyncTestDiscourse("returns a promise that resolves to 'evil'", function(assert) { - assert.expect(1); - - const done = assert.async(); - PreloadStore.getAndRemove('bane').then(function(result) { - assert.equal(result, 'evil'); + PreloadStore.getAndRemove("bane").then(function(result) { + assert.equal(result, "evil"); done(); }); }); diff --git a/test/javascripts/lib/pretty-text-test.js.es6 b/test/javascripts/lib/pretty-text-test.js.es6 index 892a9aca49c..c7313b99da8 100644 --- a/test/javascripts/lib/pretty-text-test.js.es6 +++ b/test/javascripts/lib/pretty-text-test.js.es6 @@ -1,7 +1,7 @@ -import Quote from 'discourse/lib/quote'; -import Post from 'discourse/models/post'; -import { default as PrettyText, buildOptions } from 'pretty-text/pretty-text'; -import { IMAGE_VERSION as v} from 'pretty-text/emoji'; +import Quote from "discourse/lib/quote"; +import Post from "discourse/models/post"; +import { default as PrettyText, buildOptions } from "pretty-text/pretty-text"; +import { IMAGE_VERSION as v } from "pretty-text/emoji"; QUnit.module("lib:pretty-text"); @@ -10,13 +10,13 @@ const rawOpts = { enable_emoji: true, enable_emoji_shortcuts: true, enable_mentions: true, - emoji_set: 'emoji_one', - highlighted_languages: 'json|ruby|javascript', - default_code_lang: 'auto', + emoji_set: "emoji_one", + highlighted_languages: "json|ruby|javascript", + default_code_lang: "auto", enable_markdown_linkify: true, - markdown_linkify_tlds: 'com' + markdown_linkify_tlds: "com" }, - censoredWords: 'shucks|whiz|whizzer|a**le|badword*', + censoredWords: "shucks|whiz|whizzer|a**le|badword*", getURL: url => url }; @@ -47,18 +47,31 @@ QUnit.assert.cookedPara = function(input, expected, message) { QUnit.assert.cooked(input, `

      ${expected}

      `, message); }; - QUnit.skip("Pending Engine fixes and spec fixes", assert => { - assert.cooked("Derpy: http://derp.com?_test_=1", - '

      Derpy: http://derp.com?_test_=1

      ', - "works with underscores in urls"); + assert.cooked( + "Derpy: http://derp.com?_test_=1", + '

      Derpy: http://derp.com?_test_=1

      ', + "works with underscores in urls" + ); - assert.cooked("**a*_b**", "

      a*_b

      ", "allows for characters within bold"); + assert.cooked( + "**a*_b**", + "

      a*_b

      ", + "allows for characters within bold" + ); }); QUnit.test("buildOptions", assert => { - assert.ok(buildOptions({ siteSettings: { enable_emoji: true } }).discourse.features.emoji, 'emoji enabled'); - assert.ok(!buildOptions({ siteSettings: { enable_emoji: false } }).discourse.features.emoji, 'emoji disabled'); + assert.ok( + buildOptions({ siteSettings: { enable_emoji: true } }).discourse.features + .emoji, + "emoji enabled" + ); + assert.ok( + !buildOptions({ siteSettings: { enable_emoji: false } }).discourse.features + .emoji, + "emoji disabled" + ); }); QUnit.test("basic cooking", assert => { @@ -67,151 +80,275 @@ QUnit.test("basic cooking", assert => { assert.cooked("__bold__", "

      bold

      ", "it bolds text."); assert.cooked("*trout*", "

      trout

      ", "it italicizes text."); assert.cooked("_trout_", "

      trout

      ", "it italicizes text."); - assert.cooked("***hello***", "

      hello

      ", "it can do bold and italics at once."); - assert.cooked("word_with_underscores", "

      word_with_underscores

      ", "it doesn't do intraword italics"); - assert.cooked("common/_special_font_face.html.erb", "

      common/_special_font_face.html.erb

      ", "it doesn't intraword with a slash"); - assert.cooked("hello \\*evil\\*", "

      hello *evil*

      ", "it supports escaping of asterisks"); - assert.cooked("hello \\_evil\\_", "

      hello _evil_

      ", "it supports escaping of italics"); - assert.cooked("brussels sprouts are *awful*.", "

      brussels sprouts are awful.

      ", "it doesn't swallow periods."); + assert.cooked( + "***hello***", + "

      hello

      ", + "it can do bold and italics at once." + ); + assert.cooked( + "word_with_underscores", + "

      word_with_underscores

      ", + "it doesn't do intraword italics" + ); + assert.cooked( + "common/_special_font_face.html.erb", + "

      common/_special_font_face.html.erb

      ", + "it doesn't intraword with a slash" + ); + assert.cooked( + "hello \\*evil\\*", + "

      hello *evil*

      ", + "it supports escaping of asterisks" + ); + assert.cooked( + "hello \\_evil\\_", + "

      hello _evil_

      ", + "it supports escaping of italics" + ); + assert.cooked( + "brussels sprouts are *awful*.", + "

      brussels sprouts are awful.

      ", + "it doesn't swallow periods." + ); }); QUnit.test("Nested bold and italics", assert => { - assert.cooked("*this is italic **with some bold** inside*", "

      this is italic with some bold inside

      ", "it handles nested bold in italics"); + assert.cooked( + "*this is italic **with some bold** inside*", + "

      this is italic with some bold inside

      ", + "it handles nested bold in italics" + ); }); QUnit.test("Traditional Line Breaks", assert => { const input = "1\n2\n3"; - assert.cooked(input, "

      1
      \n2
      \n3

      ", "automatically handles trivial newlines"); - assert.cookedOptions(input, { siteSettings: {traditional_markdown_linebreaks: true} }, "

      1\n2\n3

      "); + assert.cooked( + input, + "

      1
      \n2
      \n3

      ", + "automatically handles trivial newlines" + ); + assert.cookedOptions( + input, + { siteSettings: { traditional_markdown_linebreaks: true } }, + "

      1\n2\n3

      " + ); }); QUnit.test("Unbalanced underscores", assert => { - assert.cooked("[evil_trout][1] hello_\n\n[1]: http://eviltrout.com", "

      evil_trout hello_

      "); + assert.cooked( + "[evil_trout][1] hello_\n\n[1]: http://eviltrout.com", + '

      evil_trout hello_

      ' + ); }); QUnit.test("Line Breaks", assert => { - assert.cooked("[] first choice\n[] second choice", - "

      [] first choice
      \n[] second choice

      ", - "it handles new lines correctly with [] options"); + assert.cooked( + "[] first choice\n[] second choice", + "

      [] first choice
      \n[] second choice

      ", + "it handles new lines correctly with [] options" + ); // note this is a change from previous engine but is correct // we have an html block and behavior is defined per common mark // spec // ole engine would wrap trout in a

      - assert.cooked("

      evil
      \ntrout", - "
      evil
      \ntrout", - "it doesn't insert
      after blockquotes"); + assert.cooked( + "
      evil
      \ntrout", + "
      evil
      \ntrout", + "it doesn't insert
      after blockquotes" + ); - assert.cooked("leading
      evil
      \ntrout", - "

      leading

      evil

      \ntrout

      ", - "it doesn't insert
      after blockquotes with leading text"); + assert.cooked( + "leading
      evil
      \ntrout", + "

      leading

      evil

      \ntrout

      ", + "it doesn't insert
      after blockquotes with leading text" + ); }); QUnit.test("Paragraphs for HTML", assert => { - assert.cooked("
      hello world
      ", "
      hello world
      ", "it doesn't surround
      with paragraphs"); - assert.cooked("

      hello world

      ", "

      hello world

      ", "it doesn't surround

      with paragraphs"); - assert.cooked("hello world", "

      hello world

      ", "it surrounds inline html tags with paragraphs"); - assert.cooked("hello world", "

      hello world

      ", "it surrounds inline html tags with paragraphs"); + assert.cooked( + "
      hello world
      ", + "
      hello world
      ", + "it doesn't surround
      with paragraphs" + ); + assert.cooked( + "

      hello world

      ", + "

      hello world

      ", + "it doesn't surround

      with paragraphs" + ); + assert.cooked( + "hello world", + "

      hello world

      ", + "it surrounds inline html tags with paragraphs" + ); + assert.cooked( + "hello world", + "

      hello world

      ", + "it surrounds inline html tags with paragraphs" + ); }); QUnit.test("Links", assert => { + assert.cooked( + "EvilTrout: http://eviltrout.com", + '

      EvilTrout: http://eviltrout.com

      ', + "autolinks a URL" + ); - assert.cooked("EvilTrout: http://eviltrout.com", - '

      EvilTrout: http://eviltrout.com

      ', - "autolinks a URL"); + assert.cooked( + "Youtube: http://www.youtube.com/watch?v=1MrpeBRkM5A", + '

      Youtube: http://www.youtube.com/watch?v=1MrpeBRkM5A

      ', + "allows links to contain query params" + ); - assert.cooked("Youtube: http://www.youtube.com/watch?v=1MrpeBRkM5A", - '

      Youtube: http://www.youtube.com/watch?v=1MrpeBRkM5A

      ', - "allows links to contain query params"); + assert.cooked( + "Derpy: http://derp.com?__test=1", + '

      Derpy: http://derp.com?__test=1

      ', + "works with double underscores in urls" + ); - assert.cooked("Derpy: http://derp.com?__test=1", - '

      Derpy: http://derp.com?__test=1

      ', - "works with double underscores in urls"); + assert.cooked( + "Atwood: www.codinghorror.com", + '

      Atwood: www.codinghorror.com

      ', + "autolinks something that begins with www" + ); - assert.cooked("Atwood: www.codinghorror.com", - '

      Atwood: www.codinghorror.com

      ', - "autolinks something that begins with www"); + assert.cooked( + "Atwood: http://www.codinghorror.com", + '

      Atwood: http://www.codinghorror.com

      ', + "autolinks a URL with http://www" + ); - assert.cooked("Atwood: http://www.codinghorror.com", - '

      Atwood: http://www.codinghorror.com

      ', - "autolinks a URL with http://www"); + assert.cooked( + "EvilTrout: http://eviltrout.com hello", + '

      EvilTrout: http://eviltrout.com hello

      ', + "autolinks with trailing text" + ); - assert.cooked("EvilTrout: http://eviltrout.com hello", - '

      EvilTrout: http://eviltrout.com hello

      ', - "autolinks with trailing text"); + assert.cooked( + "here is [an example](http://twitter.com)", + '

      here is an example

      ', + "supports markdown style links" + ); - assert.cooked("here is [an example](http://twitter.com)", - '

      here is an example

      ', - "supports markdown style links"); + assert.cooked( + "Batman: http://en.wikipedia.org/wiki/The_Dark_Knight_(film)", + '

      Batman: http://en.wikipedia.org/wiki/The_Dark_Knight_(film)

      ', + "autolinks a URL with parentheses (like Wikipedia)" + ); - assert.cooked("Batman: http://en.wikipedia.org/wiki/The_Dark_Knight_(film)", - '

      Batman: http://en.wikipedia.org/wiki/The_Dark_Knight_(film)

      ', - "autolinks a URL with parentheses (like Wikipedia)"); + assert.cooked( + "Here's a tweet:\nhttps://twitter.com/evil_trout/status/345954894420787200", + '

      Here\'s a tweet:
      \nhttps://twitter.com/evil_trout/status/345954894420787200

      ', + "It doesn't strip the new line." + ); - assert.cooked("Here's a tweet:\nhttps://twitter.com/evil_trout/status/345954894420787200", - "

      Here's a tweet:
      \nhttps://twitter.com/evil_trout/status/345954894420787200

      ", - "It doesn't strip the new line."); + assert.cooked( + "1. View @eviltrout's profile here: http://meta.discourse.org/u/eviltrout/activity
      next line.", + '
        \n
      1. View @eviltrout\'s profile here: http://meta.discourse.org/u/eviltrout/activity
        next line.
      2. \n
      ', + "allows autolinking within a list without inserting a paragraph." + ); - assert.cooked("1. View @eviltrout's profile here: http://meta.discourse.org/u/eviltrout/activity
      next line.", - "
        \n
      1. View @eviltrout's profile here: http://meta.discourse.org/u/eviltrout/activity
        next line.
      2. \n
      ", - "allows autolinking within a list without inserting a paragraph."); + assert.cooked( + "[3]: http://eviltrout.com", + "", + "It doesn't autolink markdown link references" + ); - assert.cooked("[3]: http://eviltrout.com", "", "It doesn't autolink markdown link references"); + assert.cooked( + "[]: http://eviltrout.com", + '

      []: http://eviltrout.com

      ', + "It doesn't accept empty link references" + ); - assert.cooked("[]: http://eviltrout.com", "

      []: http://eviltrout.com

      ", "It doesn't accept empty link references"); + assert.cooked( + "[b]label[/b]: description", + '

      label: description

      ', + "It doesn't accept BBCode as link references" + ); - assert.cooked("[b]label[/b]: description", "

      label: description

      ", "It doesn't accept BBCode as link references"); + assert.cooked( + "http://discourse.org and http://discourse.org/another_url and http://www.imdb.com/name/nm2225369", + '

      http://discourse.org and ' + + 'http://discourse.org/another_url and ' + + 'http://www.imdb.com/name/nm2225369

      ', + "allows multiple links on one line" + ); - assert.cooked("http://discourse.org and http://discourse.org/another_url and http://www.imdb.com/name/nm2225369", - "

      http://discourse.org and " + - "http://discourse.org/another_url and " + - "http://www.imdb.com/name/nm2225369

      ", - 'allows multiple links on one line'); + assert.cooked( + "* [Evil Trout][1]\n\n[1]: http://eviltrout.com", + '', + "allows markdown link references in a list" + ); - assert.cooked("* [Evil Trout][1]\n\n[1]: http://eviltrout.com", - "", - "allows markdown link references in a list"); + assert.cooked( + "User [MOD]: Hello!", + "

      User [MOD]: Hello!

      ", + "It does not consider references that are obviously not URLs" + ); - assert.cooked("User [MOD]: Hello!", - "

      User [MOD]: Hello!

      ", - "It does not consider references that are obviously not URLs"); + assert.cooked( + "http://eviltrout.com", + '

      http://eviltrout.com

      ', + "Links within HTML tags" + ); - assert.cooked("http://eviltrout.com", "

      http://eviltrout.com

      ", "Links within HTML tags"); + assert.cooked( + "[http://google.com ... wat](http://discourse.org)", + '

      http://google.com ... wat

      ', + "it supports links within links" + ); - assert.cooked("[http://google.com ... wat](http://discourse.org)", - "

      http://google.com ... wat

      ", - "it supports links within links"); + assert.cooked( + "[http://google.com](http://discourse.org)", + '

      http://google.com

      ', + "it supports markdown links where the name and link match" + ); - assert.cooked("[http://google.com](http://discourse.org)", - "

      http://google.com

      ", - "it supports markdown links where the name and link match"); + assert.cooked( + '[Link](http://www.example.com) (with an outer "description")', + '

      Link (with an outer "description")

      ', + "it doesn't consume closing parens as part of the url" + ); + assert.cooked( + "A link inside parentheses (http://www.example.com)", + '

      A link inside parentheses (http://www.example.com)

      ', + "it auto-links a url within parentheses" + ); - assert.cooked("[Link](http://www.example.com) (with an outer \"description\")", - "

      Link (with an outer "description")

      ", - "it doesn't consume closing parens as part of the url"); - - assert.cooked("A link inside parentheses (http://www.example.com)", - "

      A link inside parentheses (http://www.example.com)

      ", - "it auto-links a url within parentheses"); - - assert.cooked("[ul][1]\n\n[1]: http://eviltrout.com", - "

      ul

      ", - "it can use `ul` as a link name"); + assert.cooked( + "[ul][1]\n\n[1]: http://eviltrout.com", + '

      ul

      ', + "it can use `ul` as a link name" + ); }); QUnit.test("simple quotes", assert => { - assert.cooked("> nice!", "
      \n

      nice!

      \n
      ", "it supports simple quotes"); - assert.cooked(" > nice!", "
      \n

      nice!

      \n
      ", "it allows quotes with preceding spaces"); - assert.cooked("> level 1\n> > level 2", - "
      \n

      level 1

      \n
      \n

      level 2

      \n
      \n
      ", - "it allows nesting of blockquotes"); - assert.cooked("> level 1\n> > level 2", - "
      \n

      level 1

      \n
      \n

      level 2

      \n
      \n
      ", - "it allows nesting of blockquotes with spaces"); + assert.cooked( + "> nice!", + "
      \n

      nice!

      \n
      ", + "it supports simple quotes" + ); + assert.cooked( + " > nice!", + "
      \n

      nice!

      \n
      ", + "it allows quotes with preceding spaces" + ); + assert.cooked( + "> level 1\n> > level 2", + "
      \n

      level 1

      \n
      \n

      level 2

      \n
      \n
      ", + "it allows nesting of blockquotes" + ); + assert.cooked( + "> level 1\n> > level 2", + "
      \n

      level 1

      \n
      \n

      level 2

      \n
      \n
      ", + "it allows nesting of blockquotes with spaces" + ); - assert.cooked("- hello\n\n > world\n > eviltrout", -`
        + assert.cooked( + "- hello\n\n > world\n > eviltrout", + `
        • hello

          @@ -220,21 +357,26 @@ eviltrout

        `, - "it allows quotes within a list."); + "it allows quotes within a list." + ); - assert.cooked("-

        eviltrout

        ", - "
          \n
        • \n

          eviltrout

        • \n
        ", - "it allows paragraphs within a list."); - - - assert.cooked(" > indent 1\n > indent 2", "
        \n

        indent 1
        \nindent 2

        \n
        ", "allow multiple spaces to indent"); + assert.cooked( + "-

        eviltrout

        ", + "
          \n
        • \n

          eviltrout

        • \n
        ", + "it allows paragraphs within a list." + ); + assert.cooked( + " > indent 1\n > indent 2", + "
        \n

        indent 1
        \nindent 2

        \n
        ", + "allow multiple spaces to indent" + ); }); QUnit.test("Quotes", assert => { - - assert.cookedOptions("[quote=\"eviltrout, post: 1\"]\na quote\n\nsecond line\n\nthird line\n[/quote]", - { topicId: 2 }, + assert.cookedOptions( + '[quote="eviltrout, post: 1"]\na quote\n\nsecond line\n\nthird line\n[/quote]', + { topicId: 2 }, ``, - "works with multiple lines"); + "works with multiple lines" + ); - assert.cookedOptions("[quote=\"bob, post:1\"]\nmy quote\n[/quote]", - { topicId: 2, lookupAvatar: function() { } }, + assert.cookedOptions( + '[quote="bob, post:1"]\nmy quote\n[/quote]', + { topicId: 2, lookupAvatar: function() {} }, ``, - "includes no avatar if none is found"); + "includes no avatar if none is found" + ); - assert.cooked(`[quote]\na\n\n[quote]\nb\n[/quote]\n[/quote]`, + assert.cooked( + `[quote]\na\n\n[quote]\nb\n[/quote]\n[/quote]`, ` `, - "handles nested quotes properly"); + "handles nested quotes properly" + ); - assert.cookedOptions(`[quote="bob, post:1, topic:1"]\ntest quote\n[/quote]`, { lookupPrimaryUserGroupByPostNumber: () => "aUserGroup" }, - `
      "), "
      "); - assert.equal(pt.sanitize("

      hello

      "), "

      hello

      "); + assert.equal(pt.sanitize('bug'), "bug"); + assert.equal( + pt.sanitize("
      "), + "
      " + ); + assert.equal( + pt.sanitize("

      hello

      "), + "

      hello

      " + ); assert.equal(pt.sanitize("<3 <3"), "<3 <3"); assert.equal(pt.sanitize("<_<"), "<_<"); - cooked("hello", "

      hello

      ", "it sanitizes while cooking"); + cooked( + "hello", + "

      hello

      ", + "it sanitizes while cooking" + ); - cooked("disney reddit", - "

      disney reddit

      ", - "we can embed proper links"); + cooked( + "disney reddit", + '

      disney reddit

      ', + "we can embed proper links" + ); cooked("
      hello
      ", "hello", "it does not allow centering"); - cooked("
      a\n
      \n", "
      a\n
      ", "it does not double sanitize"); + cooked( + "
      a\n
      \n", + "
      a\n
      ", + "it does not double sanitize" + ); - cooked("", "", "it does not allow most iframes"); + cooked( + '', + "", + "it does not allow most iframes" + ); - cooked("", - "", - "it allows iframe to google maps"); + cooked( + '', + '', + "it allows iframe to google maps" + ); - cooked("", - "", - "it allows iframe to OpenStreetMap"); + cooked( + '', + '', + "it allows iframe to OpenStreetMap" + ); assert.equal(pt.sanitize(""), "hullo"); assert.equal(pt.sanitize(""), "press me!"); @@ -40,24 +70,62 @@ QUnit.test("sanitize", assert => { assert.equal(pt.sanitize("hello"), "hello"); assert.equal(pt.sanitize("highlight"), "highlight"); - cooked("[the answer](javascript:alert(42))", "

      [the answer](javascript:alert(42))

      ", "it prevents XSS"); + cooked( + "[the answer](javascript:alert(42))", + "

      [the answer](javascript:alert(42))

      ", + "it prevents XSS" + ); - cooked("\n", "

      ", "it doesn't circumvent XSS with comments"); + cooked( + '\n', + "

      ", + "it doesn't circumvent XSS with comments" + ); - cooked("a", "

      a

      ", "it sanitizes spans"); - cooked("a", "

      a

      ", "it sanitizes spans"); - cooked("a", "

      a

      ", "it sanitizes spans"); + cooked( + 'a', + "

      a

      ", + "it sanitizes spans" + ); + cooked( + 'a', + "

      a

      ", + "it sanitizes spans" + ); + cooked( + 'a', + '

      a

      ', + "it sanitizes spans" + ); cooked("Ctrl+C", "

      Ctrl+C

      "); - cooked("it has been 1 day 0 days since our last test failure", "

      it has been 1 day 0 days since our last test failure

      "); - cooked(`it has been 1 day 0 days since our last test failure`, `

      it has been 1 day 0 days since our last test failure

      `); + cooked( + "it has been 1 day 0 days since our last test failure", + "

      it has been 1 day 0 days since our last test failure

      " + ); + cooked( + `it has been 1 day 0 days since our last test failure`, + `

      it has been 1 day 0 days since our last test failure

      ` + ); cooked(`
      hello
      `, `
      hello
      `); - cooked(`1 + 1 is 3 2`, `

      1 + 1 is 3 2

      `); - cooked(`JS`, `

      JS

      `); - cooked(`
      Forum
      Software
      `, `
      Forum
      Software
      `); - cooked(`high low HUGE`, `

      high low HUGE

      `); + cooked( + `1 + 1 is 3 2`, + `

      1 + 1 is 3 2

      ` + ); + cooked( + `JS`, + `

      JS

      ` + ); + cooked( + `
      Forum
      Software
      `, + `
      Forum
      Software
      ` + ); + cooked( + `high low HUGE`, + `

      high low HUGE

      ` + ); cooked(`
      RTL text
      `, `
      RTL text
      `); }); @@ -65,22 +133,58 @@ QUnit.test("sanitize", assert => { QUnit.test("ids on headings", assert => { const pt = new PrettyText(buildOptions({ siteSettings: {} })); assert.equal(pt.sanitize("

      Test Heading

      "), "

      Test Heading

      "); - assert.equal(pt.sanitize(`

      Test Heading

      `), `

      Test Heading

      `); - assert.equal(pt.sanitize(`

      Test Heading

      `), `

      Test Heading

      `); - assert.equal(pt.sanitize(`

      Test Heading

      `), `

      Test Heading

      `); - assert.equal(pt.sanitize(`

      Test Heading

      `), `

      Test Heading

      `); - assert.equal(pt.sanitize(`
      Test Heading
      `), `
      Test Heading
      `); - assert.equal(pt.sanitize(`
      Test Heading
      `), `
      Test Heading
      `); + assert.equal( + pt.sanitize(`

      Test Heading

      `), + `

      Test Heading

      ` + ); + assert.equal( + pt.sanitize(`

      Test Heading

      `), + `

      Test Heading

      ` + ); + assert.equal( + pt.sanitize(`

      Test Heading

      `), + `

      Test Heading

      ` + ); + assert.equal( + pt.sanitize(`

      Test Heading

      `), + `

      Test Heading

      ` + ); + assert.equal( + pt.sanitize(`
      Test Heading
      `), + `
      Test Heading
      ` + ); + assert.equal( + pt.sanitize(`
      Test Heading
      `), + `
      Test Heading
      ` + ); }); QUnit.test("poorly formed ids on headings", assert => { let pt = new PrettyText(buildOptions({ siteSettings: {} })); - assert.equal(pt.sanitize(`

      Test Heading

      `), `

      Test Heading

      `); - assert.equal(pt.sanitize(`

      Test Heading

      `), `

      Test Heading

      `); - assert.equal(pt.sanitize(`

      Test Heading

      `), `

      Test Heading

      `); - assert.equal(pt.sanitize(`

      Test Heading

      `), `

      Test Heading

      `); - assert.equal(pt.sanitize(`

      Test Heading

      `), `

      Test Heading

      `); - assert.equal(pt.sanitize(`

      Test Heading

      `), `

      Test Heading

      `); + assert.equal( + pt.sanitize(`

      Test Heading

      `), + `

      Test Heading

      ` + ); + assert.equal( + pt.sanitize(`

      Test Heading

      `), + `

      Test Heading

      ` + ); + assert.equal( + pt.sanitize(`

      Test Heading

      `), + `

      Test Heading

      ` + ); + assert.equal( + pt.sanitize(`

      Test Heading

      `), + `

      Test Heading

      ` + ); + assert.equal( + pt.sanitize(`

      Test Heading

      `), + `

      Test Heading

      ` + ); + assert.equal( + pt.sanitize(`

      Test Heading

      `), + `

      Test Heading

      ` + ); }); QUnit.test("urlAllowed", assert => { @@ -91,8 +195,9 @@ QUnit.test("urlAllowed", assert => { allowed("https://eviltrout.com/evil/trout", "allows https urls"); allowed("//eviltrout.com/evil/trout", "allows protocol relative urls"); - assert.equal(hrefAllowed("http://google.com/test'onmouseover=alert('XSS!');//.swf"), - "http://google.com/test%27onmouseover=alert(%27XSS!%27);//.swf", - "escape single quotes"); + assert.equal( + hrefAllowed("http://google.com/test'onmouseover=alert('XSS!');//.swf"), + "http://google.com/test%27onmouseover=alert(%27XSS!%27);//.swf", + "escape single quotes" + ); }); - diff --git a/test/javascripts/lib/text-direction-test.js.es6 b/test/javascripts/lib/text-direction-test.js.es6 index c8ddee262b8..b266af0165a 100644 --- a/test/javascripts/lib/text-direction-test.js.es6 +++ b/test/javascripts/lib/text-direction-test.js.es6 @@ -1,23 +1,22 @@ -import { isRTL, isLTR } from 'discourse/lib/text-direction'; +import { isRTL, isLTR } from "discourse/lib/text-direction"; -QUnit.module('lib:text-direction'); +QUnit.module("lib:text-direction"); QUnit.test("isRTL", assert => { // Hebrew - assert.equal(isRTL('זה מבחן'), true); + assert.equal(isRTL("זה מבחן"), true); // Arabic - assert.equal(isRTL('هذا اختبار'), true); + assert.equal(isRTL("هذا اختبار"), true); // Persian - assert.equal(isRTL('این یک امتحان است'), true); + assert.equal(isRTL("این یک امتحان است"), true); - assert.equal(isRTL('This is a test'), false); - assert.equal(isRTL(''), false); + assert.equal(isRTL("This is a test"), false); + assert.equal(isRTL(""), false); }); QUnit.test("isLTR", assert => { - assert.equal(isLTR('This is a test'), true); - assert.equal(isLTR('זה מבחן'), false); + assert.equal(isLTR("This is a test"), true); + assert.equal(isLTR("זה מבחן"), false); }); - diff --git a/test/javascripts/lib/to-markdown-test.js.es6 b/test/javascripts/lib/to-markdown-test.js.es6 index 6336da48ad1..ec59e164a6f 100644 --- a/test/javascripts/lib/to-markdown-test.js.es6 +++ b/test/javascripts/lib/to-markdown-test.js.es6 @@ -1,4 +1,4 @@ -import toMarkdown from 'discourse/lib/to-markdown'; +import toMarkdown from "discourse/lib/to-markdown"; QUnit.module("lib:to-markdown"); @@ -119,31 +119,34 @@ QUnit.test("converts table tags", assert => { assert.equal(toMarkdown(html), markdown); }); -QUnit.test("replace pipes with spaces if table format not supported", assert => { - let html = ` +QUnit.test( + "replace pipes with spaces if table format not supported", + assert => { + let html = `
      Headi

      ng 1
      Head 2
      Loremipsum
      sit amet
      `; - let markdown = `Headi\n\nng 1 Head 2\nLorem ipsum\n[![](http://dolor.com/image.png)](http://example.com) *sit amet*`; - assert.equal(toMarkdown(html), markdown); + let markdown = `Headi\n\nng 1 Head 2\nLorem ipsum\n[![](http://dolor.com/image.png)](http://example.com) *sit amet*`; + assert.equal(toMarkdown(html), markdown); - html = ` + html = `
      Heading 1
      Lorem
      sit amet
      `; - markdown = `Heading 1\nLorem\n*sit amet*`; - assert.equal(toMarkdown(html), markdown); + markdown = `Heading 1\nLorem\n*sit amet*`; + assert.equal(toMarkdown(html), markdown); - html = `
      Loremsit amet
      `; - markdown = `Lorem **sit amet**`; - assert.equal(toMarkdown(html), markdown); -}); + html = `
      Loremsit amet
      `; + markdown = `Lorem **sit amet**`; + assert.equal(toMarkdown(html), markdown); + } +); QUnit.test("converts img tag", assert => { const url = "https://example.com/image.png"; @@ -154,10 +157,16 @@ QUnit.test("converts img tag", assert => { assert.equal(toMarkdown(html), `![description|50x100](${url})`); html = `description`; - assert.equal(toMarkdown(html), `[![description](${url})](http://example.com)`); + assert.equal( + toMarkdown(html), + `[![description](${url})](http://example.com)` + ); html = `description `; - assert.equal(toMarkdown(html), `[description ![](${url})](http://example.com)`); + assert.equal( + toMarkdown(html), + `[description ![](${url})](http://example.com)` + ); html = `description`; assert.equal(toMarkdown(html), ""); @@ -167,7 +176,8 @@ QUnit.test("converts img tag", assert => { }); QUnit.test("supporting html tags by keeping them", assert => { - let html = "Lorem ipsum dolor sit amet, consectetur"; + let html = + "Lorem ipsum dolor sit amet, consectetur"; let output = html; assert.equal(toMarkdown(html), output); @@ -216,11 +226,13 @@ QUnit.test("converts blockquote tag", assert => { let output = "> Lorem ipsum"; assert.equal(toMarkdown(html), output); - html = "
      Lorem ipsum

      dolor sit amet

      "; + html = + "
      Lorem ipsum

      dolor sit amet

      "; output = "> Lorem ipsum\n\n> dolor sit amet"; assert.equal(toMarkdown(html), output); - html = "
      \nLorem ipsum\n

      dolor

      sit
      amet

      "; + html = + "
      \nLorem ipsum\n

      dolor

      sit
      amet

      "; output = "> Lorem ipsum\n> > dolor\n> > > sit\n> > amet"; assert.equal(toMarkdown(html), output); }); diff --git a/test/javascripts/lib/url-test.js.es6 b/test/javascripts/lib/url-test.js.es6 index 15f760d92d7..993fe8c23f4 100644 --- a/test/javascripts/lib/url-test.js.es6 +++ b/test/javascripts/lib/url-test.js.es6 @@ -1,4 +1,4 @@ -import { default as DiscourseURL, userPath } from 'discourse/lib/url'; +import { default as DiscourseURL, userPath } from "discourse/lib/url"; QUnit.module("lib:url"); @@ -7,34 +7,61 @@ QUnit.test("isInternal with a HTTP url", assert => { assert.not(DiscourseURL.isInternal(null), "a blank URL is not internal"); assert.ok(DiscourseURL.isInternal("/test"), "relative URLs are internal"); - assert.ok(DiscourseURL.isInternal("//eviltrout.com"), "a url on the same host is internal (protocol-less)"); - assert.ok(DiscourseURL.isInternal("http://eviltrout.com/tophat"), "a url on the same host is internal"); - assert.ok(DiscourseURL.isInternal("https://eviltrout.com/moustache"), "a url on a HTTPS of the same host is internal"); - assert.not(DiscourseURL.isInternal("//twitter.com.com"), "a different host is not internal (protocol-less)"); - assert.not(DiscourseURL.isInternal("http://twitter.com"), "a different host is not internal"); + assert.ok( + DiscourseURL.isInternal("//eviltrout.com"), + "a url on the same host is internal (protocol-less)" + ); + assert.ok( + DiscourseURL.isInternal("http://eviltrout.com/tophat"), + "a url on the same host is internal" + ); + assert.ok( + DiscourseURL.isInternal("https://eviltrout.com/moustache"), + "a url on a HTTPS of the same host is internal" + ); + assert.not( + DiscourseURL.isInternal("//twitter.com.com"), + "a different host is not internal (protocol-less)" + ); + assert.not( + DiscourseURL.isInternal("http://twitter.com"), + "a different host is not internal" + ); }); QUnit.test("isInternal with a HTTPS url", assert => { sandbox.stub(DiscourseURL, "origin").returns("https://eviltrout.com"); - assert.ok(DiscourseURL.isInternal("http://eviltrout.com/monocle"), "HTTPS urls match HTTP urls"); + assert.ok( + DiscourseURL.isInternal("http://eviltrout.com/monocle"), + "HTTPS urls match HTTP urls" + ); }); QUnit.test("isInternal on subfolder install", assert => { sandbox.stub(DiscourseURL, "origin").returns("http://eviltrout.com/forum"); - assert.not(DiscourseURL.isInternal("http://eviltrout.com"), "the host root is not internal"); - assert.not(DiscourseURL.isInternal("http://eviltrout.com/tophat"), "a url on the same host but on a different folder is not internal"); - assert.ok(DiscourseURL.isInternal("http://eviltrout.com/forum/moustache"), "a url on the same host and on the same folder is internal"); + assert.not( + DiscourseURL.isInternal("http://eviltrout.com"), + "the host root is not internal" + ); + assert.not( + DiscourseURL.isInternal("http://eviltrout.com/tophat"), + "a url on the same host but on a different folder is not internal" + ); + assert.ok( + DiscourseURL.isInternal("http://eviltrout.com/forum/moustache"), + "a url on the same host and on the same folder is internal" + ); }); QUnit.test("userPath", assert => { - assert.equal(userPath(), '/u'); - assert.equal(userPath('eviltrout'), '/u/eviltrout'); - assert.equal(userPath('hp.json'), '/u/hp.json'); + assert.equal(userPath(), "/u"); + assert.equal(userPath("eviltrout"), "/u/eviltrout"); + assert.equal(userPath("hp.json"), "/u/hp.json"); }); QUnit.test("userPath with BaseUri", assert => { Discourse.BaseUri = "/forum"; - assert.equal(userPath(), '/forum/u'); - assert.equal(userPath('eviltrout'), '/forum/u/eviltrout'); - assert.equal(userPath('hp.json'), '/forum/u/hp.json'); + assert.equal(userPath(), "/forum/u"); + assert.equal(userPath("eviltrout"), "/forum/u/eviltrout"); + assert.equal(userPath("hp.json"), "/forum/u/hp.json"); }); diff --git a/test/javascripts/lib/user-search-test.js.es6 b/test/javascripts/lib/user-search-test.js.es6 index a34f1143950..1e48064857e 100644 --- a/test/javascripts/lib/user-search-test.js.es6 +++ b/test/javascripts/lib/user-search-test.js.es6 @@ -1,67 +1,68 @@ -import userSearch from 'discourse/lib/user-search'; +import userSearch from "discourse/lib/user-search"; QUnit.module("lib:user-search", { beforeEach() { - const response = (object) => { - return [ - 200, - {"Content-Type": "application/json"}, - object - ]; + const response = object => { + return [200, { "Content-Type": "application/json" }, object]; }; - server.get('/u/search/users', () => { //eslint-disable-line - return response( - { - users: [ - { - "username": "TeaMoe", - "name": "TeaMoe", - "avatar_template": "https://avatars.discourse.org/v3/letter/t/41988e/{size}.png" - }, - { - "username": "TeamOneJ", - "name": "J Cobb", - "avatar_template": + server.get("/u/search/users", () => { + //eslint-disable-line + return response({ + users: [ + { + username: "TeaMoe", + name: "TeaMoe", + avatar_template: + "https://avatars.discourse.org/v3/letter/t/41988e/{size}.png" + }, + { + username: "TeamOneJ", + name: "J Cobb", + avatar_template: "https://avatars.discourse.org/v3/letter/t/3d9bf3/{size}.png" - }, - { - "username": "kudos", - "name": "Team Blogeto.com", - "avatar_template": "/user_avatar/meta.discourse.org/kudos/{size}/62185_1.png" - }, - { - "username": "RosieLinda", - "name": "Linda Teaman", - "avatar_template": "https://avatars.discourse.org/v3/letter/r/bc8723/{size}.png" - }, - { - "username": "legalatom", - "name": "Team LegalAtom", - "avatar_template": "https://avatars.discourse.org/v3/letter/l/a9a28c/{size}.png" - }, - { - "username": "dzsat_team", - "name": "Dz Sat Dz Sat", - "avatar_template": "https://avatars.discourse.org/v3/letter/d/eb9ed0/{size}.png" - } - ], - groups: [ - { - "name": "bob", - "usernames": [] - }, - { - "name": "team", - "usernames": [] - } - ] - }); + }, + { + username: "kudos", + name: "Team Blogeto.com", + avatar_template: + "/user_avatar/meta.discourse.org/kudos/{size}/62185_1.png" + }, + { + username: "RosieLinda", + name: "Linda Teaman", + avatar_template: + "https://avatars.discourse.org/v3/letter/r/bc8723/{size}.png" + }, + { + username: "legalatom", + name: "Team LegalAtom", + avatar_template: + "https://avatars.discourse.org/v3/letter/l/a9a28c/{size}.png" + }, + { + username: "dzsat_team", + name: "Dz Sat Dz Sat", + avatar_template: + "https://avatars.discourse.org/v3/letter/d/eb9ed0/{size}.png" + } + ], + groups: [ + { + name: "bob", + usernames: [] + }, + { + name: "team", + usernames: [] + } + ] + }); }); } }); QUnit.test("it places groups unconditionally for exact match", async assert => { - let results = await userSearch({term: 'Team'}); - assert.equal(results[results.length-1]["name"], "team"); + let results = await userSearch({ term: "Team" }); + assert.equal(results[results.length - 1]["name"], "team"); }); diff --git a/test/javascripts/lib/utilities-test.js.es6 b/test/javascripts/lib/utilities-test.js.es6 index 9b5f4e4265a..82fbfe6bba6 100644 --- a/test/javascripts/lib/utilities-test.js.es6 +++ b/test/javascripts/lib/utilities-test.js.es6 @@ -16,21 +16,43 @@ import { caretRowCol, setCaretPosition, fillMissingDates -} from 'discourse/lib/utilities'; -import * as Utilities from 'discourse/lib/utilities'; +} from "discourse/lib/utilities"; +import * as Utilities from "discourse/lib/utilities"; QUnit.module("lib:utilities"); QUnit.test("emailValid", assert => { - assert.ok(emailValid('Bob@example.com'), "allows upper case in the first part of emails"); - assert.ok(emailValid('bob@EXAMPLE.com'), "allows upper case in the email domain"); + assert.ok( + emailValid("Bob@example.com"), + "allows upper case in the first part of emails" + ); + assert.ok( + emailValid("bob@EXAMPLE.com"), + "allows upper case in the email domain" + ); }); QUnit.test("extractDomainFromUrl", assert => { - assert.equal(extractDomainFromUrl('http://meta.discourse.org:443/random'), 'meta.discourse.org', "extract domain name from url"); - assert.equal(extractDomainFromUrl('meta.discourse.org:443/random'), 'meta.discourse.org', "extract domain regardless of scheme presence"); - assert.equal(extractDomainFromUrl('http://192.168.0.1:443/random'), '192.168.0.1', "works for IP address"); - assert.equal(extractDomainFromUrl('http://localhost:443/random'), 'localhost', "works for localhost"); + assert.equal( + extractDomainFromUrl("http://meta.discourse.org:443/random"), + "meta.discourse.org", + "extract domain name from url" + ); + assert.equal( + extractDomainFromUrl("meta.discourse.org:443/random"), + "meta.discourse.org", + "extract domain regardless of scheme presence" + ); + assert.equal( + extractDomainFromUrl("http://192.168.0.1:443/random"), + "192.168.0.1", + "works for IP address" + ); + assert.equal( + extractDomainFromUrl("http://localhost:443/random"), + "localhost", + "works for localhost" + ); }); var validUpload = validateUploadedFiles; @@ -45,7 +67,7 @@ QUnit.test("uploading one file", assert => { sandbox.stub(bootbox, "alert"); assert.not(validUpload([1, 2])); - assert.ok(bootbox.alert.calledWith(I18n.t('post.errors.too_many_uploads'))); + assert.ok(bootbox.alert.calledWith(I18n.t("post.errors.too_many_uploads"))); }); QUnit.test("new user cannot upload images", assert => { @@ -53,8 +75,13 @@ QUnit.test("new user cannot upload images", assert => { Discourse.User.resetCurrent(Discourse.User.create()); sandbox.stub(bootbox, "alert"); - assert.not(validUpload([{name: "image.png"}]), 'the upload is not valid'); - assert.ok(bootbox.alert.calledWith(I18n.t('post.errors.image_upload_not_allowed_for_new_user')), 'the alert is called'); + assert.not(validUpload([{ name: "image.png" }]), "the upload is not valid"); + assert.ok( + bootbox.alert.calledWith( + I18n.t("post.errors.image_upload_not_allowed_for_new_user") + ), + "the alert is called" + ); }); QUnit.test("new user cannot upload attachments", assert => { @@ -62,14 +89,24 @@ QUnit.test("new user cannot upload attachments", assert => { sandbox.stub(bootbox, "alert"); Discourse.User.resetCurrent(Discourse.User.create()); - assert.not(validUpload([{name: "roman.txt"}])); - assert.ok(bootbox.alert.calledWith(I18n.t('post.errors.attachment_upload_not_allowed_for_new_user'))); + assert.not(validUpload([{ name: "roman.txt" }])); + assert.ok( + bootbox.alert.calledWith( + I18n.t("post.errors.attachment_upload_not_allowed_for_new_user") + ) + ); }); QUnit.test("ensures an authorized upload", assert => { sandbox.stub(bootbox, "alert"); assert.not(validUpload([{ name: "unauthorized.html" }])); - assert.ok(bootbox.alert.calledWith(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: authorizedExtensions() }))); + assert.ok( + bootbox.alert.calledWith( + I18n.t("post.errors.upload_not_authorized", { + authorized_extensions: authorizedExtensions() + }) + ) + ); }); QUnit.test("staff can upload anything in PM", assert => { @@ -80,19 +117,28 @@ QUnit.test("staff can upload anything in PM", assert => { sandbox.stub(bootbox, "alert"); assert.not(validUpload(files)); - assert.ok(validUpload(files, { isPrivateMessage: true, allowStaffToUploadAnyFileInPm: true })); + assert.ok( + validUpload(files, { + isPrivateMessage: true, + allowStaffToUploadAnyFileInPm: true + }) + ); }); var imageSize = 10 * 1024; var dummyBlob = function() { - var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; + var BlobBuilder = + window.BlobBuilder || + window.WebKitBlobBuilder || + window.MozBlobBuilder || + window.MSBlobBuilder; if (BlobBuilder) { var bb = new BlobBuilder(); bb.append([new Int8Array(imageSize)]); return bb.getBlob("image/png"); } else { - return new Blob([new Int8Array(imageSize)], { "type" : "image\/png" }); + return new Blob([new Int8Array(imageSize)], { type: "image/png" }); } }; @@ -122,23 +168,43 @@ var testUploadMarkdown = function(filename) { }; QUnit.test("getUploadMarkdown", assert => { - assert.equal(testUploadMarkdown("lolcat.gif"),'![lolcat|100x200](/uploads/123/abcdef.ext)'); - assert.equal(testUploadMarkdown("[foo|bar].png"),'![%5Bfoo%7Cbar%5D|100x200](/uploads/123/abcdef.ext)'); - assert.ok(testUploadMarkdown("important.txt") === 'important.txt (42 Bytes)\n'); + assert.equal( + testUploadMarkdown("lolcat.gif"), + "![lolcat|100x200](/uploads/123/abcdef.ext)" + ); + assert.equal( + testUploadMarkdown("[foo|bar].png"), + "![%5Bfoo%7Cbar%5D|100x200](/uploads/123/abcdef.ext)" + ); + assert.ok( + testUploadMarkdown("important.txt") === + 'important.txt (42 Bytes)\n' + ); }); QUnit.test("replaces GUID in image alt text on iOS", assert => { - assert.equal(testUploadMarkdown("8F2B469B-6B2C-4213-BC68-57B4876365A0.jpeg"),'![8F2B469B-6B2C-4213-BC68-57B4876365A0|100x200](/uploads/123/abcdef.ext)'); + assert.equal( + testUploadMarkdown("8F2B469B-6B2C-4213-BC68-57B4876365A0.jpeg"), + "![8F2B469B-6B2C-4213-BC68-57B4876365A0|100x200](/uploads/123/abcdef.ext)" + ); - sandbox.stub(Utilities, 'isAppleDevice').returns(true); - assert.equal(testUploadMarkdown("8F2B469B-6B2C-4213-BC68-57B4876365A0.jpeg"),'![image|100x200](/uploads/123/abcdef.ext)'); + sandbox.stub(Utilities, "isAppleDevice").returns(true); + assert.equal( + testUploadMarkdown("8F2B469B-6B2C-4213-BC68-57B4876365A0.jpeg"), + "![image|100x200](/uploads/123/abcdef.ext)" + ); }); QUnit.test("isAnImage", assert => { - _.each(["png", "jpg", "jpeg", "bmp", "gif", "tif", "tiff", "ico"], function(extension) { + _.each(["png", "jpg", "jpeg", "bmp", "gif", "tif", "tiff", "ico"], function( + extension + ) { var image = "image." + extension; assert.ok(isAnImage(image), image + " is recognized as an image"); - assert.ok(isAnImage("http://foo.bar/path/to/" + image), image + " is recognized as an image"); + assert.ok( + isAnImage("http://foo.bar/path/to/" + image), + image + " is recognized as an image" + ); }); assert.not(isAnImage("file.txt")); assert.not(isAnImage("http://foo.bar/path/to/file.txt")); @@ -147,13 +213,21 @@ QUnit.test("isAnImage", assert => { QUnit.test("avatarUrl", assert => { var rawSize = getRawSize; - assert.blank(avatarUrl('', 'tiny'), "no template returns blank"); - assert.equal(avatarUrl('/fake/template/{size}.png', 'tiny'), "/fake/template/" + rawSize(20) + ".png", "simple avatar url"); - assert.equal(avatarUrl('/fake/template/{size}.png', 'large'), "/fake/template/" + rawSize(45) + ".png", "different size"); + assert.blank(avatarUrl("", "tiny"), "no template returns blank"); + assert.equal( + avatarUrl("/fake/template/{size}.png", "tiny"), + "/fake/template/" + rawSize(20) + ".png", + "simple avatar url" + ); + assert.equal( + avatarUrl("/fake/template/{size}.png", "large"), + "/fake/template/" + rawSize(45) + ".png", + "different size" + ); }); var setDevicePixelRatio = function(value) { - if (Object.defineProperty && !window.hasOwnProperty('devicePixelRatio')) { + if (Object.defineProperty && !window.hasOwnProperty("devicePixelRatio")) { Object.defineProperty(window, "devicePixelRatio", { value: 2 }); } else { window.devicePixelRatio = value; @@ -165,20 +239,36 @@ QUnit.test("avatarImg", assert => { setDevicePixelRatio(2); var avatarTemplate = "/path/to/avatar/{size}.png"; - assert.equal(avatarImg({avatarTemplate: avatarTemplate, size: 'tiny'}), - "", - "it returns the avatar html"); + assert.equal( + avatarImg({ avatarTemplate: avatarTemplate, size: "tiny" }), + "", + "it returns the avatar html" + ); - assert.equal(avatarImg({avatarTemplate: avatarTemplate, size: 'tiny', title: 'evilest trout'}), - "", - "it adds a title if supplied"); + assert.equal( + avatarImg({ + avatarTemplate: avatarTemplate, + size: "tiny", + title: "evilest trout" + }), + "", + "it adds a title if supplied" + ); - assert.equal(avatarImg({avatarTemplate: avatarTemplate, size: 'tiny', extraClasses: 'evil fish'}), - "", - "it adds extra classes if supplied"); + assert.equal( + avatarImg({ + avatarTemplate: avatarTemplate, + size: "tiny", + extraClasses: "evil fish" + }), + "", + "it adds extra classes if supplied" + ); - assert.blank(avatarImg({avatarTemplate: "", size: 'tiny'}), - "it doesn't render avatars for invalid avatar template"); + assert.blank( + avatarImg({ avatarTemplate: "", size: "tiny" }), + "it doesn't render avatars for invalid avatar template" + ); setDevicePixelRatio(oldRatio); }); @@ -191,22 +281,33 @@ QUnit.test("allowsImages", assert => { assert.ok(allowsImages(), "works with old extensions syntax"); Discourse.SiteSettings.authorized_extensions = "txt|pdf|*"; - assert.ok(allowsImages(), "images are allowed when all extensions are allowed"); + assert.ok( + allowsImages(), + "images are allowed when all extensions are allowed" + ); Discourse.SiteSettings.authorized_extensions = "json|jpg|pdf|txt"; - assert.ok(allowsImages(), "images are allowed when at least one extension is an image extension"); + assert.ok( + allowsImages(), + "images are allowed when at least one extension is an image extension" + ); }); - QUnit.test("allowsAttachments", assert => { Discourse.SiteSettings.authorized_extensions = "jpg|jpeg|gif"; assert.not(allowsAttachments(), "no attachments allowed by default"); Discourse.SiteSettings.authorized_extensions = "jpg|jpeg|gif|*"; - assert.ok(allowsAttachments(), "attachments are allowed when all extensions are allowed"); + assert.ok( + allowsAttachments(), + "attachments are allowed when all extensions are allowed" + ); Discourse.SiteSettings.authorized_extensions = "jpg|jpeg|gif|pdf"; - assert.ok(allowsAttachments(), "attachments are allowed when at least one extension is not an image extension"); + assert.ok( + allowsAttachments(), + "attachments are allowed when at least one extension is not an image extension" + ); Discourse.SiteSettings.authorized_extensions = ".jpg|.jpeg|.gif|.pdf"; assert.ok(allowsAttachments(), "works with old extensions syntax"); @@ -214,12 +315,20 @@ QUnit.test("allowsAttachments", assert => { QUnit.test("defaultHomepage", assert => { Discourse.SiteSettings.top_menu = "latest|top|hot"; - assert.equal(defaultHomepage(), "latest", "default homepage is the first item in the top_menu site setting"); + assert.equal( + defaultHomepage(), + "latest", + "default homepage is the first item in the top_menu site setting" + ); var meta = document.createElement("meta"); meta.name = "discourse_current_homepage"; meta.content = "hot"; document.body.appendChild(meta); - assert.equal(defaultHomepage(), "hot", "default homepage is pulled from "); + assert.equal( + defaultHomepage(), + "hot", + "default homepage is pulled from " + ); document.body.removeChild(meta); }); @@ -229,12 +338,16 @@ QUnit.test("setDefaultHomepage", assert => { meta.content = "hot"; document.body.appendChild(meta); setDefaultHomepage("top"); - assert.equal(meta.content, "top", "default homepage set by setDefaultHomepage"); + assert.equal( + meta.content, + "top", + "default homepage set by setDefaultHomepage" + ); document.body.removeChild(meta); }); QUnit.test("caretRowCol", assert => { - var textarea = document.createElement('textarea'); + var textarea = document.createElement("textarea"); const content = document.createTextNode("01234\n56789\n012345"); textarea.appendChild(content); document.body.appendChild(textarea); @@ -243,8 +356,16 @@ QUnit.test("caretRowCol", assert => { setCaretPosition(textarea, setCaretPos); const result = caretRowCol(textarea); - assert.equal(result.rowNum, expectedRowNum, "returns the right row of the caret"); - assert.equal(result.colNum, expectedColNum, "returns the right col of the caret"); + assert.equal( + result.rowNum, + expectedRowNum, + "returns the right row of the caret" + ); + assert.equal( + result.colNum, + expectedColNum, + "returns the right col of the caret" + ); }; assertResult(0, 1, 0); @@ -259,8 +380,12 @@ QUnit.test("caretRowCol", assert => { QUnit.test("fillMissingDates", assert => { const startDate = "2017-11-12"; // YYYY-MM-DD const endDate = "2017-12-12"; // YYYY-MM-DD - const data = '[{"x":"2017-11-12","y":3},{"x":"2017-11-27","y":2},{"x":"2017-12-06","y":9},{"x":"2017-12-11","y":2}]'; + const data = + '[{"x":"2017-11-12","y":3},{"x":"2017-11-27","y":2},{"x":"2017-12-06","y":9},{"x":"2017-12-11","y":2}]'; - assert.equal(fillMissingDates(JSON.parse(data), startDate, endDate).length, 31, - "it returns a JSON array with 31 dates"); + assert.equal( + fillMissingDates(JSON.parse(data), startDate, endDate).length, + 31, + "it returns a JSON array with 31 dates" + ); }); diff --git a/test/javascripts/lib/white-lister-test.js.es6 b/test/javascripts/lib/white-lister-test.js.es6 index e8bb3d8f300..0529e52515a 100644 --- a/test/javascripts/lib/white-lister-test.js.es6 +++ b/test/javascripts/lib/white-lister-test.js.es6 @@ -1,48 +1,58 @@ -import WhiteLister from 'pretty-text/white-lister'; +import WhiteLister from "pretty-text/white-lister"; QUnit.module("lib:whiteLister"); QUnit.test("whiteLister", assert => { const whiteLister = new WhiteLister(); - assert.ok(Object.keys(whiteLister.getWhiteList().tagList).length > 1, "should have some defaults"); + assert.ok( + Object.keys(whiteLister.getWhiteList().tagList).length > 1, + "should have some defaults" + ); whiteLister.disable("default"); - assert.ok(Object.keys(whiteLister.getWhiteList().tagList).length === 0, "should have no defaults if disabled"); + assert.ok( + Object.keys(whiteLister.getWhiteList().tagList).length === 0, + "should have no defaults if disabled" + ); whiteLister.whiteListFeature("test", [ - 'custom.foo', - 'custom.baz', - 'custom[data-*]', - 'custom[rel=nofollow]' + "custom.foo", + "custom.baz", + "custom[data-*]", + "custom[rel=nofollow]" ]); - whiteLister.whiteListFeature("test", [ - 'custom[rel=test]' - ]); + whiteLister.whiteListFeature("test", ["custom[rel=test]"]); whiteLister.enable("test"); - assert.deepEqual(whiteLister.getWhiteList(), { - tagList: { - custom: [] - }, - attrList: { - custom: { - "class": ["foo", "baz"], - "data-*": ["*"], - "rel": ["nofollow", "test"] + assert.deepEqual( + whiteLister.getWhiteList(), + { + tagList: { + custom: [] + }, + attrList: { + custom: { + class: ["foo", "baz"], + "data-*": ["*"], + rel: ["nofollow", "test"] + } } - } - }, 'Expecting a correct white list'); - + }, + "Expecting a correct white list" + ); whiteLister.disable("test"); - assert.deepEqual(whiteLister.getWhiteList(), { - tagList: {}, - attrList: {} - }, 'Expecting an empty white list'); - + assert.deepEqual( + whiteLister.getWhiteList(), + { + tagList: {}, + attrList: {} + }, + "Expecting an empty white list" + ); }); diff --git a/test/javascripts/mixins/grant-badge-controller-test.js.es6 b/test/javascripts/mixins/grant-badge-controller-test.js.es6 index d1e34cd133a..c08276c80dc 100644 --- a/test/javascripts/mixins/grant-badge-controller-test.js.es6 +++ b/test/javascripts/mixins/grant-badge-controller-test.js.es6 @@ -1,38 +1,83 @@ -import GrantBadgeControllerMixin from 'discourse/mixins/grant-badge-controller'; -import Badge from 'discourse/models/badge'; +import GrantBadgeControllerMixin from "discourse/mixins/grant-badge-controller"; +import Badge from "discourse/models/badge"; -QUnit.module('mixin:grant-badge-controller', { +QUnit.module("mixin:grant-badge-controller", { before: function() { - this.GrantBadgeController = Ember.Controller.extend(GrantBadgeControllerMixin); + this.GrantBadgeController = Ember.Controller.extend( + GrantBadgeControllerMixin + ); - this.badgeFirst = Badge.create({ id: 3, name: 'A Badge', enabled: true, manually_grantable: true }); - this.badgeMiddle = Badge.create({ id: 1, name: 'My Badge', enabled: true, manually_grantable: true }); - this.badgeLast = Badge.create({ id: 2, name: 'Zoo Badge', enabled: true, manually_grantable: true }); - this.badgeDisabled = Badge.create({ id: 4, name: 'Disabled Badge', enabled: false, manually_grantable: true }); - this.badgeAutomatic = Badge.create({ id: 5, name: 'Automatic Badge', enabled: true, manually_grantable: false }); + this.badgeFirst = Badge.create({ + id: 3, + name: "A Badge", + enabled: true, + manually_grantable: true + }); + this.badgeMiddle = Badge.create({ + id: 1, + name: "My Badge", + enabled: true, + manually_grantable: true + }); + this.badgeLast = Badge.create({ + id: 2, + name: "Zoo Badge", + enabled: true, + manually_grantable: true + }); + this.badgeDisabled = Badge.create({ + id: 4, + name: "Disabled Badge", + enabled: false, + manually_grantable: true + }); + this.badgeAutomatic = Badge.create({ + id: 5, + name: "Automatic Badge", + enabled: true, + manually_grantable: false + }); }, beforeEach: function() { this.subject = this.GrantBadgeController.create({ userBadges: [], - allBadges: [this.badgeLast, this.badgeFirst, this.badgeMiddle, this.badgeDisabled, this.badgeAutomatic], + allBadges: [ + this.badgeLast, + this.badgeFirst, + this.badgeMiddle, + this.badgeDisabled, + this.badgeAutomatic + ] }); } }); -QUnit.test('grantableBadges', function(assert) { - const sortedNames = [this.badgeFirst.name, this.badgeMiddle.name, this.badgeLast.name]; - const badgeNames = this.subject.get('grantableBadges').map(badge => badge.name); +QUnit.test("grantableBadges", function(assert) { + const sortedNames = [ + this.badgeFirst.name, + this.badgeMiddle.name, + this.badgeLast.name + ]; + const badgeNames = this.subject + .get("grantableBadges") + .map(badge => badge.name); - assert.not(badgeNames.includes(this.badgeDisabled), 'excludes disabled badges'); - assert.not(badgeNames.includes(this.badgeAutomatic), 'excludes automatic badges'); - assert.deepEqual(badgeNames, sortedNames, 'sorts badges by name'); + assert.not( + badgeNames.includes(this.badgeDisabled), + "excludes disabled badges" + ); + assert.not( + badgeNames.includes(this.badgeAutomatic), + "excludes automatic badges" + ); + assert.deepEqual(badgeNames, sortedNames, "sorts badges by name"); }); -QUnit.test('selectedBadgeGrantable', function(assert) { - this.subject.set('selectedBadgeId', this.badgeDisabled.id); - assert.not(this.subject.get('selectedBadgeGrantable')); +QUnit.test("selectedBadgeGrantable", function(assert) { + this.subject.set("selectedBadgeId", this.badgeDisabled.id); + assert.not(this.subject.get("selectedBadgeGrantable")); - this.subject.set('selectedBadgeId', this.badgeFirst.id); - assert.ok(this.subject.get('selectedBadgeGrantable')); + this.subject.set("selectedBadgeId", this.badgeFirst.id); + assert.ok(this.subject.get("selectedBadgeGrantable")); }); diff --git a/test/javascripts/mixins/singleton-test.js.es6 b/test/javascripts/mixins/singleton-test.js.es6 index 2e5635a4d18..0ed6c198ee6 100644 --- a/test/javascripts/mixins/singleton-test.js.es6 +++ b/test/javascripts/mixins/singleton-test.js.es6 @@ -1,4 +1,4 @@ -import Singleton from 'discourse/mixins/singleton'; +import Singleton from "discourse/mixins/singleton"; QUnit.module("mixin:singleton"); @@ -7,9 +7,17 @@ QUnit.test("current", assert => { DummyModel.reopenClass(Singleton); var current = DummyModel.current(); - assert.present(current, 'current returns the current instance'); - assert.equal(current, DummyModel.current(), 'calling it again returns the same instance'); - assert.notEqual(current, DummyModel.create({}), 'we can create other instances that are not the same as current'); + assert.present(current, "current returns the current instance"); + assert.equal( + current, + DummyModel.current(), + "calling it again returns the same instance" + ); + assert.notEqual( + current, + DummyModel.create({}), + "we can create other instances that are not the same as current" + ); }); QUnit.test("currentProp reading", assert => { @@ -17,39 +25,60 @@ QUnit.test("currentProp reading", assert => { DummyModel.reopenClass(Singleton); var current = DummyModel.current(); - assert.blank(DummyModel.currentProp('evil'), 'by default attributes are blank'); - current.set('evil', 'trout'); - assert.equal(DummyModel.currentProp('evil'), 'trout', 'after changing the instance, the value is set'); + assert.blank( + DummyModel.currentProp("evil"), + "by default attributes are blank" + ); + current.set("evil", "trout"); + assert.equal( + DummyModel.currentProp("evil"), + "trout", + "after changing the instance, the value is set" + ); }); QUnit.test("currentProp writing", assert => { var DummyModel = Ember.Object.extend({}); DummyModel.reopenClass(Singleton); - assert.blank(DummyModel.currentProp('adventure'), 'by default attributes are blank'); - var result = DummyModel.currentProp('adventure', 'time'); - assert.equal(result, 'time', 'it returns the new value'); - assert.equal(DummyModel.currentProp('adventure'), 'time', 'after calling currentProp the value is set'); + assert.blank( + DummyModel.currentProp("adventure"), + "by default attributes are blank" + ); + var result = DummyModel.currentProp("adventure", "time"); + assert.equal(result, "time", "it returns the new value"); + assert.equal( + DummyModel.currentProp("adventure"), + "time", + "after calling currentProp the value is set" + ); - DummyModel.currentProp('count', 0); - assert.equal(DummyModel.currentProp('count'), 0, 'we can set the value to 0'); + DummyModel.currentProp("count", 0); + assert.equal(DummyModel.currentProp("count"), 0, "we can set the value to 0"); - DummyModel.currentProp('adventure', null); - assert.equal(DummyModel.currentProp('adventure'), null, 'we can set the value to null'); + DummyModel.currentProp("adventure", null); + assert.equal( + DummyModel.currentProp("adventure"), + null, + "we can set the value to null" + ); }); QUnit.test("createCurrent", assert => { var Shoe = Ember.Object.extend({}); Shoe.reopenClass(Singleton, { createCurrent: function() { - return Shoe.create({toes: 5}); + return Shoe.create({ toes: 5 }); } }); - assert.equal(Shoe.currentProp('toes'), 5, 'it created the class using `createCurrent`'); + assert.equal( + Shoe.currentProp("toes"), + 5, + "it created the class using `createCurrent`" + ); }); - QUnit.test("createCurrent that returns null", assert => { var Missing = Ember.Object.extend({}); Missing.reopenClass(Singleton, { @@ -59,5 +88,8 @@ QUnit.test("createCurrent that returns null", assert => { }); assert.blank(Missing.current(), "it doesn't return an instance"); - assert.blank(Missing.currentProp('madeup'), "it won't raise an error asking for a property. Will just return null."); -}); \ No newline at end of file + assert.blank( + Missing.currentProp("madeup"), + "it won't raise an error asking for a property. Will just return null." + ); +}); diff --git a/test/javascripts/models/badge-test.js.es6 b/test/javascripts/models/badge-test.js.es6 index d9a6e0b2d34..e29710e1f83 100644 --- a/test/javascripts/models/badge-test.js.es6 +++ b/test/javascripts/models/badge-test.js.es6 @@ -1,51 +1,75 @@ -import Badge from 'discourse/models/badge'; +import Badge from "discourse/models/badge"; QUnit.module("model:badge"); -QUnit.test('newBadge', assert => { - const badge1 = Badge.create({name: "New Badge"}), - badge2 = Badge.create({id: 1, name: "Old Badge"}); - assert.ok(badge1.get('newBadge'), "badges without ids are new"); - assert.ok(!badge2.get('newBadge'), "badges with ids are not new"); +QUnit.test("newBadge", assert => { + const badge1 = Badge.create({ name: "New Badge" }), + badge2 = Badge.create({ id: 1, name: "Old Badge" }); + assert.ok(badge1.get("newBadge"), "badges without ids are new"); + assert.ok(!badge2.get("newBadge"), "badges with ids are not new"); }); - -QUnit.test('createFromJson array', assert => { - const badgesJson = {"badge_types":[{"id":6,"name":"Silver 1"}],"badges":[{"id":1126,"name":"Badge 1","description":null,"badge_type_id":6}]}; +QUnit.test("createFromJson array", assert => { + const badgesJson = { + badge_types: [{ id: 6, name: "Silver 1" }], + badges: [{ id: 1126, name: "Badge 1", description: null, badge_type_id: 6 }] + }; const badges = Badge.createFromJson(badgesJson); assert.ok(Array.isArray(badges), "returns an array"); - assert.equal(badges[0].get('name'), "Badge 1", "badge details are set"); - assert.equal(badges[0].get('badge_type.name'), "Silver 1", "badge_type reference is set"); + assert.equal(badges[0].get("name"), "Badge 1", "badge details are set"); + assert.equal( + badges[0].get("badge_type.name"), + "Silver 1", + "badge_type reference is set" + ); }); -QUnit.test('createFromJson single', assert => { - const badgeJson = {"badge_types":[{"id":6,"name":"Silver 1"}],"badge":{"id":1126,"name":"Badge 1","description":null,"badge_type_id":6}}; +QUnit.test("createFromJson single", assert => { + const badgeJson = { + badge_types: [{ id: 6, name: "Silver 1" }], + badge: { id: 1126, name: "Badge 1", description: null, badge_type_id: 6 } + }; const badge = Badge.createFromJson(badgeJson); assert.ok(!Array.isArray(badge), "does not returns an array"); }); -QUnit.test('updateFromJson', assert => { - const badgeJson = {"badge_types":[{"id":6,"name":"Silver 1"}],"badge":{"id":1126,"name":"Badge 1","description":null,"badge_type_id":6}}; - const badge = Badge.create({name: "Badge 1"}); +QUnit.test("updateFromJson", assert => { + const badgeJson = { + badge_types: [{ id: 6, name: "Silver 1" }], + badge: { id: 1126, name: "Badge 1", description: null, badge_type_id: 6 } + }; + const badge = Badge.create({ name: "Badge 1" }); badge.updateFromJson(badgeJson); - assert.equal(badge.get('id'), 1126, "id is set"); - assert.equal(badge.get('badge_type.name'), "Silver 1", "badge_type reference is set"); + assert.equal(badge.get("id"), 1126, "id is set"); + assert.equal( + badge.get("badge_type.name"), + "Silver 1", + "badge_type reference is set" + ); }); -QUnit.test('save', assert => { +QUnit.test("save", assert => { assert.expect(0); - const badge = Badge.create({name: "New Badge", description: "This is a new badge.", badge_type_id: 1}); + const badge = Badge.create({ + name: "New Badge", + description: "This is a new badge.", + badge_type_id: 1 + }); return badge.save(["name", "description", "badge_type_id"]); }); -QUnit.test('destroy', assert => { +QUnit.test("destroy", assert => { assert.expect(0); - const badge = Badge.create({name: "New Badge", description: "This is a new badge.", badge_type_id: 1}); + const badge = Badge.create({ + name: "New Badge", + description: "This is a new badge.", + badge_type_id: 1 + }); badge.destroy(); - badge.set('id', 3); + badge.set("id", 3); return badge.destroy(); -}); \ No newline at end of file +}); diff --git a/test/javascripts/models/category-test.js.es6 b/test/javascripts/models/category-test.js.es6 index fc5065566ae..9e2437cf576 100644 --- a/test/javascripts/models/category-test.js.es6 +++ b/test/javascripts/models/category-test.js.es6 @@ -1,146 +1,322 @@ -import createStore from 'helpers/create-store'; -import Category from 'discourse/models/category'; +import createStore from "helpers/create-store"; +import Category from "discourse/models/category"; QUnit.module("model:category"); -QUnit.test('slugFor', assert =>{ +QUnit.test("slugFor", assert => { const store = createStore(); const slugFor = function(cat, val, text) { assert.equal(Discourse.Category.slugFor(cat), val, text); }; - slugFor(store.createRecord('category', {slug: 'hello'}), "hello", "It calculates the proper slug for hello"); - slugFor(store.createRecord('category', {id: 123, slug: ''}), "123-category", "It returns id-category for empty strings"); - slugFor(store.createRecord('category', {id: 456}), "456-category", "It returns id-category for undefined slugs"); - slugFor(store.createRecord('category', {slug: '熱帶風暴畫眉'}), "熱帶風暴畫眉", "It can be non english characters"); + slugFor( + store.createRecord("category", { slug: "hello" }), + "hello", + "It calculates the proper slug for hello" + ); + slugFor( + store.createRecord("category", { id: 123, slug: "" }), + "123-category", + "It returns id-category for empty strings" + ); + slugFor( + store.createRecord("category", { id: 456 }), + "456-category", + "It returns id-category for undefined slugs" + ); + slugFor( + store.createRecord("category", { slug: "熱帶風暴畫眉" }), + "熱帶風暴畫眉", + "It can be non english characters" + ); - const parentCategory = store.createRecord('category', {id: 345, slug: 'darth'}); - slugFor(store.createRecord('category', {slug: 'luke', parentCategory: parentCategory}), - "darth/luke", - "it uses the parent slug before the child"); + const parentCategory = store.createRecord("category", { + id: 345, + slug: "darth" + }); + slugFor( + store.createRecord("category", { + slug: "luke", + parentCategory: parentCategory + }), + "darth/luke", + "it uses the parent slug before the child" + ); - slugFor(store.createRecord('category', {id: 555, parentCategory: parentCategory}), - "darth/555-category", - "it uses the parent slug before the child and then uses id"); + slugFor( + store.createRecord("category", { id: 555, parentCategory: parentCategory }), + "darth/555-category", + "it uses the parent slug before the child and then uses id" + ); - parentCategory.set('slug', null); - slugFor(store.createRecord('category', {id: 555, parentCategory: parentCategory}), - "345-category/555-category", - "it uses the parent before the child and uses ids for both"); + parentCategory.set("slug", null); + slugFor( + store.createRecord("category", { id: 555, parentCategory: parentCategory }), + "345-category/555-category", + "it uses the parent before the child and uses ids for both" + ); }); - -QUnit.test('findBySlug', assert => { +QUnit.test("findBySlug", assert => { assert.expect(6); const store = createStore(); - const darth = store.createRecord('category', {id: 1, slug: 'darth'}), - luke = store.createRecord('category', {id: 2, slug: 'luke', parentCategory: darth}), - hurricane = store.createRecord('category', {id: 3, slug: '熱帶風暴畫眉'}), - newsFeed = store.createRecord('category', {id: 4, slug: '뉴스피드', parentCategory: hurricane}), - time = store.createRecord('category', {id: 5, slug: '时间', parentCategory: darth}), - bah = store.createRecord('category', {id: 6, slug: 'bah', parentCategory: hurricane}), + const darth = store.createRecord("category", { id: 1, slug: "darth" }), + luke = store.createRecord("category", { + id: 2, + slug: "luke", + parentCategory: darth + }), + hurricane = store.createRecord("category", { id: 3, slug: "熱帶風暴畫眉" }), + newsFeed = store.createRecord("category", { + id: 4, + slug: "뉴스피드", + parentCategory: hurricane + }), + time = store.createRecord("category", { + id: 5, + slug: "时间", + parentCategory: darth + }), + bah = store.createRecord("category", { + id: 6, + slug: "bah", + parentCategory: hurricane + }), categoryList = [darth, luke, hurricane, newsFeed, time, bah]; - sandbox.stub(Discourse.Category, 'list').returns(categoryList); + sandbox.stub(Discourse.Category, "list").returns(categoryList); - assert.deepEqual(Discourse.Category.findBySlug('darth'), darth, 'we can find a category'); - assert.deepEqual(Discourse.Category.findBySlug('luke', 'darth'), luke, 'we can find the other category with parent category'); - assert.deepEqual(Discourse.Category.findBySlug('熱帶風暴畫眉'), hurricane, 'we can find a category with CJK slug'); - assert.deepEqual(Discourse.Category.findBySlug('뉴스피드', '熱帶風暴畫眉'), newsFeed, 'we can find a category with CJK slug whose parent slug is also CJK'); - assert.deepEqual(Discourse.Category.findBySlug('时间', 'darth'), time, 'we can find a category with CJK slug whose parent slug is english'); - assert.deepEqual(Discourse.Category.findBySlug('bah', '熱帶風暴畫眉'), bah, 'we can find a category with english slug whose parent slug is CJK'); + assert.deepEqual( + Discourse.Category.findBySlug("darth"), + darth, + "we can find a category" + ); + assert.deepEqual( + Discourse.Category.findBySlug("luke", "darth"), + luke, + "we can find the other category with parent category" + ); + assert.deepEqual( + Discourse.Category.findBySlug("熱帶風暴畫眉"), + hurricane, + "we can find a category with CJK slug" + ); + assert.deepEqual( + Discourse.Category.findBySlug("뉴스피드", "熱帶風暴畫眉"), + newsFeed, + "we can find a category with CJK slug whose parent slug is also CJK" + ); + assert.deepEqual( + Discourse.Category.findBySlug("时间", "darth"), + time, + "we can find a category with CJK slug whose parent slug is english" + ); + assert.deepEqual( + Discourse.Category.findBySlug("bah", "熱帶風暴畫眉"), + bah, + "we can find a category with english slug whose parent slug is CJK" + ); sandbox.restore(); }); -QUnit.test('findSingleBySlug', assert => { +QUnit.test("findSingleBySlug", assert => { assert.expect(6); const store = createStore(); - const darth = store.createRecord('category', {id: 1, slug: 'darth'}), - luke = store.createRecord('category', {id: 2, slug: 'luke', parentCategory: darth}), - hurricane = store.createRecord('category', {id: 3, slug: '熱帶風暴畫眉'}), - newsFeed = store.createRecord('category', {id: 4, slug: '뉴스피드', parentCategory: hurricane}), - time = store.createRecord('category', {id: 5, slug: '时间', parentCategory: darth}), - bah = store.createRecord('category', {id: 6, slug: 'bah', parentCategory: hurricane}), + const darth = store.createRecord("category", { id: 1, slug: "darth" }), + luke = store.createRecord("category", { + id: 2, + slug: "luke", + parentCategory: darth + }), + hurricane = store.createRecord("category", { id: 3, slug: "熱帶風暴畫眉" }), + newsFeed = store.createRecord("category", { + id: 4, + slug: "뉴스피드", + parentCategory: hurricane + }), + time = store.createRecord("category", { + id: 5, + slug: "时间", + parentCategory: darth + }), + bah = store.createRecord("category", { + id: 6, + slug: "bah", + parentCategory: hurricane + }), categoryList = [darth, luke, hurricane, newsFeed, time, bah]; - sandbox.stub(Discourse.Category, 'list').returns(categoryList); + sandbox.stub(Discourse.Category, "list").returns(categoryList); - assert.deepEqual(Discourse.Category.findSingleBySlug('darth'), darth, 'we can find a category'); - assert.deepEqual(Discourse.Category.findSingleBySlug('darth/luke'), luke, 'we can find the other category with parent category'); - assert.deepEqual(Discourse.Category.findSingleBySlug('熱帶風暴畫眉'), hurricane, 'we can find a category with CJK slug'); - assert.deepEqual(Discourse.Category.findSingleBySlug('熱帶風暴畫眉/뉴스피드'), newsFeed, 'we can find a category with CJK slug whose parent slug is also CJK'); - assert.deepEqual(Discourse.Category.findSingleBySlug('darth/时间'), time, 'we can find a category with CJK slug whose parent slug is english'); - assert.deepEqual(Discourse.Category.findSingleBySlug('熱帶風暴畫眉/bah'), bah, 'we can find a category with english slug whose parent slug is CJK'); + assert.deepEqual( + Discourse.Category.findSingleBySlug("darth"), + darth, + "we can find a category" + ); + assert.deepEqual( + Discourse.Category.findSingleBySlug("darth/luke"), + luke, + "we can find the other category with parent category" + ); + assert.deepEqual( + Discourse.Category.findSingleBySlug("熱帶風暴畫眉"), + hurricane, + "we can find a category with CJK slug" + ); + assert.deepEqual( + Discourse.Category.findSingleBySlug("熱帶風暴畫眉/뉴스피드"), + newsFeed, + "we can find a category with CJK slug whose parent slug is also CJK" + ); + assert.deepEqual( + Discourse.Category.findSingleBySlug("darth/时间"), + time, + "we can find a category with CJK slug whose parent slug is english" + ); + assert.deepEqual( + Discourse.Category.findSingleBySlug("熱帶風暴畫眉/bah"), + bah, + "we can find a category with english slug whose parent slug is CJK" + ); }); -QUnit.test('findByIds', assert => { +QUnit.test("findByIds", assert => { const store = createStore(); - const categories = { - 1: store.createRecord('category', {id: 1}), - 2: store.createRecord('category', {id: 2}) + const categories = { + 1: store.createRecord("category", { id: 1 }), + 2: store.createRecord("category", { id: 2 }) }; - sandbox.stub(Discourse.Category, 'idMap').returns(categories); - assert.deepEqual(Discourse.Category.findByIds([1,2,3]), _.values(categories)); + sandbox.stub(Discourse.Category, "idMap").returns(categories); + assert.deepEqual( + Discourse.Category.findByIds([1, 2, 3]), + _.values(categories) + ); }); -QUnit.test('search with category name', assert => { +QUnit.test("search with category name", assert => { const store = createStore(), - category1 = store.createRecord('category', { id: 1, name: 'middle term', slug: 'different-slug' }), - category2 = store.createRecord('category', { id: 2, name: 'middle term', slug: 'another-different-slug' }); + category1 = store.createRecord("category", { + id: 1, + name: "middle term", + slug: "different-slug" + }), + category2 = store.createRecord("category", { + id: 2, + name: "middle term", + slug: "another-different-slug" + }); sandbox.stub(Category, "listByActivity").returns([category1, category2]); - assert.deepEqual(Category.search('term', { limit: 0 }), [], "returns an empty array when limit is 0"); - assert.deepEqual(Category.search(''), [category1, category2], "orders by activity if no term is matched"); - assert.deepEqual(Category.search('term'), [category1, category2], "orders by activity"); + assert.deepEqual( + Category.search("term", { limit: 0 }), + [], + "returns an empty array when limit is 0" + ); + assert.deepEqual( + Category.search(""), + [category1, category2], + "orders by activity if no term is matched" + ); + assert.deepEqual( + Category.search("term"), + [category1, category2], + "orders by activity" + ); - category2.set('name', 'TeRm start'); - assert.deepEqual(Category.search('tErM'), [category2, category1], "ignores case of category name and search term"); + category2.set("name", "TeRm start"); + assert.deepEqual( + Category.search("tErM"), + [category2, category1], + "ignores case of category name and search term" + ); - category2.set('name', 'term start'); - assert.deepEqual(Category.search('term'), [category2, category1], "orders matching begin with and then contains"); + category2.set("name", "term start"); + assert.deepEqual( + Category.search("term"), + [category2, category1], + "orders matching begin with and then contains" + ); sandbox.restore(); - const child_category1 = store.createRecord('category', { id: 3, name: 'term start', parent_category_id: category1.get('id') }), - read_restricted_category = store.createRecord('category', { id: 4, name: 'some term', read_restricted: true }); + const child_category1 = store.createRecord("category", { + id: 3, + name: "term start", + parent_category_id: category1.get("id") + }), + read_restricted_category = store.createRecord("category", { + id: 4, + name: "some term", + read_restricted: true + }); - sandbox.stub(Category, "listByActivity").returns([read_restricted_category, category1, child_category1, category2]); + sandbox + .stub(Category, "listByActivity") + .returns([read_restricted_category, category1, child_category1, category2]); - assert.deepEqual(Category.search(''), - [category1, category2, read_restricted_category], - "prioritize non read_restricted and does not include child categories when term is blank"); + assert.deepEqual( + Category.search(""), + [category1, category2, read_restricted_category], + "prioritize non read_restricted and does not include child categories when term is blank" + ); - assert.deepEqual(Category.search('', { limit: 3 }), - [category1, category2, read_restricted_category], - "prioritize non read_restricted and does not include child categories categories when term is blank with limit"); + assert.deepEqual( + Category.search("", { limit: 3 }), + [category1, category2, read_restricted_category], + "prioritize non read_restricted and does not include child categories categories when term is blank with limit" + ); - assert.deepEqual(Category.search('term'), - [child_category1, category2, category1, read_restricted_category], - "prioritize non read_restricted"); + assert.deepEqual( + Category.search("term"), + [child_category1, category2, category1, read_restricted_category], + "prioritize non read_restricted" + ); - assert.deepEqual(Category.search('term', { limit: 3 }), - [child_category1, category2, read_restricted_category], - "prioritize non read_restricted with limit"); + assert.deepEqual( + Category.search("term", { limit: 3 }), + [child_category1, category2, read_restricted_category], + "prioritize non read_restricted with limit" + ); sandbox.restore(); }); -QUnit.test('search with category slug', assert => { +QUnit.test("search with category slug", assert => { const store = createStore(), - category1 = store.createRecord('category', { id: 1, name: 'middle term', slug: 'different-slug' }), - category2 = store.createRecord('category', { id: 2, name: 'middle term', slug: 'another-different-slug' }); + category1 = store.createRecord("category", { + id: 1, + name: "middle term", + slug: "different-slug" + }), + category2 = store.createRecord("category", { + id: 2, + name: "middle term", + slug: "another-different-slug" + }); sandbox.stub(Category, "listByActivity").returns([category1, category2]); - assert.deepEqual(Category.search('different-slug'), [category1, category2], "returns the right categories"); - assert.deepEqual(Category.search('another-different'), [category2], "returns the right categories"); + assert.deepEqual( + Category.search("different-slug"), + [category1, category2], + "returns the right categories" + ); + assert.deepEqual( + Category.search("another-different"), + [category2], + "returns the right categories" + ); - category2.set('slug', 'ANOTher-DIFfereNT'); - assert.deepEqual(Category.search('anOtHer-dIfFeREnt'), [category2], "ignores case of category slug and search term"); + category2.set("slug", "ANOTher-DIFfereNT"); + assert.deepEqual( + Category.search("anOtHer-dIfFeREnt"), + [category2], + "ignores case of category slug and search term" + ); }); diff --git a/test/javascripts/models/composer-test.js.es6 b/test/javascripts/models/composer-test.js.es6 index 4add3a84525..f1706421f76 100644 --- a/test/javascripts/models/composer-test.js.es6 +++ b/test/javascripts/models/composer-test.js.es6 @@ -1,13 +1,13 @@ -import { currentUser } from 'helpers/qunit-helpers'; -import Composer from 'discourse/models/composer'; -import createStore from 'helpers/create-store'; +import { currentUser } from "helpers/qunit-helpers"; +import Composer from "discourse/models/composer"; +import createStore from "helpers/create-store"; QUnit.module("model:composer"); function createComposer(opts) { opts = opts || {}; opts.user = opts.user || currentUser(); - return createStore().createRecord('composer', opts); + return createStore().createRecord("composer", opts); } function openComposer(opts) { @@ -16,85 +16,150 @@ function openComposer(opts) { return composer; } -QUnit.test('replyLength', assert => { +QUnit.test("replyLength", assert => { const replyLength = function(val, expectedLength) { const composer = createComposer({ reply: val }); - assert.equal(composer.get('replyLength'), expectedLength); + assert.equal(composer.get("replyLength"), expectedLength); }; replyLength("basic reply", 11, "basic reply length"); replyLength(" \nbasic reply\t", 11, "trims whitespaces"); replyLength("ba sic\n\nreply", 12, "count only significant whitespaces"); - replyLength("1[quote=]not counted[/quote]2[quote=]at all[/quote]3", 3, "removes quotes"); - replyLength("1[quote=]not[quote=]counted[/quote]yay[/quote]2", 2, "handles nested quotes correctly"); + replyLength( + "1[quote=]not counted[/quote]2[quote=]at all[/quote]3", + 3, + "removes quotes" + ); + replyLength( + "1[quote=]not[quote=]counted[/quote]yay[/quote]2", + 2, + "handles nested quotes correctly" + ); }); -QUnit.test('missingReplyCharacters', assert => { +QUnit.test("missingReplyCharacters", assert => { Discourse.SiteSettings.min_first_post_length = 40; - const missingReplyCharacters = function(val, isPM, isFirstPost, expected, message) { - const composer = createComposer({ reply: val, creatingPrivateMessage: isPM, creatingTopic: isFirstPost }); - assert.equal(composer.get('missingReplyCharacters'), expected, message); + const missingReplyCharacters = function( + val, + isPM, + isFirstPost, + expected, + message + ) { + const composer = createComposer({ + reply: val, + creatingPrivateMessage: isPM, + creatingTopic: isFirstPost + }); + assert.equal(composer.get("missingReplyCharacters"), expected, message); }; - missingReplyCharacters('hi', false, false, Discourse.SiteSettings.min_post_length - 2, 'too short public post'); - missingReplyCharacters('hi', false, true, Discourse.SiteSettings.min_first_post_length - 2, 'too short first post'); - missingReplyCharacters('hi', true, false, Discourse.SiteSettings.min_personal_message_post_length - 2, 'too short private message'); + missingReplyCharacters( + "hi", + false, + false, + Discourse.SiteSettings.min_post_length - 2, + "too short public post" + ); + missingReplyCharacters( + "hi", + false, + true, + Discourse.SiteSettings.min_first_post_length - 2, + "too short first post" + ); + missingReplyCharacters( + "hi", + true, + false, + Discourse.SiteSettings.min_personal_message_post_length - 2, + "too short private message" + ); const link = "http://imgur.com/gallery/grxX8"; - const composer = createComposer({ canEditTopicFeaturedLink: true, title: link, featuredLink: link, reply: link }); + const composer = createComposer({ + canEditTopicFeaturedLink: true, + title: link, + featuredLink: link, + reply: link + }); - assert.equal(composer.get('missingReplyCharacters'), 0, "don't require any post content"); + assert.equal( + composer.get("missingReplyCharacters"), + 0, + "don't require any post content" + ); }); -QUnit.test('missingTitleCharacters', assert => { +QUnit.test("missingTitleCharacters", assert => { const missingTitleCharacters = function(val, isPM, expected, message) { - const composer = createComposer({ title: val, creatingPrivateMessage: isPM }); - assert.equal(composer.get('missingTitleCharacters'), expected, message); + const composer = createComposer({ + title: val, + creatingPrivateMessage: isPM + }); + assert.equal(composer.get("missingTitleCharacters"), expected, message); }; - missingTitleCharacters('hi', false, Discourse.SiteSettings.min_topic_title_length - 2, 'too short post title'); - missingTitleCharacters('z', true, Discourse.SiteSettings.min_personal_message_title_length - 1, 'too short pm title'); + missingTitleCharacters( + "hi", + false, + Discourse.SiteSettings.min_topic_title_length - 2, + "too short post title" + ); + missingTitleCharacters( + "z", + true, + Discourse.SiteSettings.min_personal_message_title_length - 1, + "too short pm title" + ); }); -QUnit.test('replyDirty', assert => { +QUnit.test("replyDirty", assert => { const composer = createComposer(); - assert.ok(!composer.get('replyDirty'), "by default it's false"); + assert.ok(!composer.get("replyDirty"), "by default it's false"); composer.setProperties({ originalText: "hello", reply: "hello" }); - assert.ok(!composer.get('replyDirty'), "it's false when the originalText is the same as the reply"); - composer.set('reply', 'hello world'); - assert.ok(composer.get('replyDirty'), "it's true when the reply changes"); + assert.ok( + !composer.get("replyDirty"), + "it's false when the originalText is the same as the reply" + ); + composer.set("reply", "hello world"); + assert.ok(composer.get("replyDirty"), "it's true when the reply changes"); }); QUnit.test("appendText", assert => { const composer = createComposer(); - assert.blank(composer.get('reply'), "the reply is blank by default"); + assert.blank(composer.get("reply"), "the reply is blank by default"); composer.appendText("hello"); - assert.equal(composer.get('reply'), "hello", "it appends text to nothing"); + assert.equal(composer.get("reply"), "hello", "it appends text to nothing"); composer.appendText(" world"); - assert.equal(composer.get('reply'), "hello world", "it appends text to existing text"); + assert.equal( + composer.get("reply"), + "hello world", + "it appends text to existing text" + ); composer.clearState(); composer.appendText("a\n\n\n\nb"); - composer.appendText("c",3,{block: true}); + composer.appendText("c", 3, { block: true }); assert.equal(composer.get("reply"), "a\n\nc\n\nb"); composer.clearState(); composer.appendText("ab"); - composer.appendText("c",1,{block: true}); + composer.appendText("c", 1, { block: true }); assert.equal(composer.get("reply"), "a\n\nc\n\nb"); composer.clearState(); composer.appendText("\nab"); - composer.appendText("c",0,{block: true}); + composer.appendText("c", 0, { block: true }); assert.equal(composer.get("reply"), "c\n\nab"); }); @@ -102,16 +167,24 @@ QUnit.test("appendText", assert => { QUnit.test("prependText", assert => { const composer = createComposer(); - assert.blank(composer.get('reply'), "the reply is blank by default"); + assert.blank(composer.get("reply"), "the reply is blank by default"); composer.prependText("hello"); - assert.equal(composer.get('reply'), "hello", "it prepends text to nothing"); + assert.equal(composer.get("reply"), "hello", "it prepends text to nothing"); composer.prependText("world "); - assert.equal(composer.get('reply'), "world hello", "it prepends text to existing text"); + assert.equal( + composer.get("reply"), + "world hello", + "it prepends text to existing text" + ); - composer.prependText("before new line", {new_line: true}); - assert.equal(composer.get('reply'), "before new line\n\nworld hello", "it prepends text with new line to existing text"); + composer.prependText("before new line", { new_line: true }); + assert.equal( + composer.get("reply"), + "before new line\n\nworld hello", + "it prepends text with new line to existing text" + ); }); QUnit.test("Title length for regular topics", assert => { @@ -119,44 +192,44 @@ QUnit.test("Title length for regular topics", assert => { Discourse.SiteSettings.max_topic_title_length = 10; const composer = createComposer(); - composer.set('title', 'asdf'); - assert.ok(!composer.get('titleLengthValid'), "short titles are not valid"); + composer.set("title", "asdf"); + assert.ok(!composer.get("titleLengthValid"), "short titles are not valid"); - composer.set('title', 'this is a long title'); - assert.ok(!composer.get('titleLengthValid'), "long titles are not valid"); + composer.set("title", "this is a long title"); + assert.ok(!composer.get("titleLengthValid"), "long titles are not valid"); - composer.set('title', 'just right'); - assert.ok(composer.get('titleLengthValid'), "in the range is okay"); + composer.set("title", "just right"); + assert.ok(composer.get("titleLengthValid"), "in the range is okay"); }); QUnit.test("Title length for private messages", assert => { Discourse.SiteSettings.min_personal_message_title_length = 5; Discourse.SiteSettings.max_topic_title_length = 10; - const composer = createComposer({action: Composer.PRIVATE_MESSAGE}); + const composer = createComposer({ action: Composer.PRIVATE_MESSAGE }); - composer.set('title', 'asdf'); - assert.ok(!composer.get('titleLengthValid'), "short titles are not valid"); + composer.set("title", "asdf"); + assert.ok(!composer.get("titleLengthValid"), "short titles are not valid"); - composer.set('title', 'this is a long title'); - assert.ok(!composer.get('titleLengthValid'), "long titles are not valid"); + composer.set("title", "this is a long title"); + assert.ok(!composer.get("titleLengthValid"), "long titles are not valid"); - composer.set('title', 'just right'); - assert.ok(composer.get('titleLengthValid'), "in the range is okay"); + composer.set("title", "just right"); + assert.ok(composer.get("titleLengthValid"), "in the range is okay"); }); QUnit.test("Title length for private messages", assert => { Discourse.SiteSettings.min_personal_message_title_length = 5; Discourse.SiteSettings.max_topic_title_length = 10; - const composer = createComposer({action: Composer.PRIVATE_MESSAGE}); + const composer = createComposer({ action: Composer.PRIVATE_MESSAGE }); - composer.set('title', 'asdf'); - assert.ok(!composer.get('titleLengthValid'), "short titles are not valid"); + composer.set("title", "asdf"); + assert.ok(!composer.get("titleLengthValid"), "short titles are not valid"); - composer.set('title', 'this is a long title'); - assert.ok(!composer.get('titleLengthValid'), "long titles are not valid"); + composer.set("title", "this is a long title"); + assert.ok(!composer.get("titleLengthValid"), "long titles are not valid"); - composer.set('title', 'just right'); - assert.ok(composer.get('titleLengthValid'), "in the range is okay"); + composer.set("title", "just right"); + assert.ok(composer.get("titleLengthValid"), "in the range is okay"); }); QUnit.test("Post length for private messages with non human users", assert => { @@ -164,59 +237,88 @@ QUnit.test("Post length for private messages with non human users", assert => { topic: Ember.Object.create({ pm_with_non_human_user: true }) }); - assert.equal(composer.get('minimumPostLength'), 1); + assert.equal(composer.get("minimumPostLength"), 1); }); -QUnit.test('editingFirstPost', assert => { +QUnit.test("editingFirstPost", assert => { const composer = createComposer(); - assert.ok(!composer.get('editingFirstPost'), "it's false by default"); + assert.ok(!composer.get("editingFirstPost"), "it's false by default"); - const post = Discourse.Post.create({id: 123, post_number: 2}); - composer.setProperties({post: post, action: Composer.EDIT }); - assert.ok(!composer.get('editingFirstPost'), "it's false when not editing the first post"); - - post.set('post_number', 1); - assert.ok(composer.get('editingFirstPost'), "it's true when editing the first post"); + const post = Discourse.Post.create({ id: 123, post_number: 2 }); + composer.setProperties({ post: post, action: Composer.EDIT }); + assert.ok( + !composer.get("editingFirstPost"), + "it's false when not editing the first post" + ); + post.set("post_number", 1); + assert.ok( + composer.get("editingFirstPost"), + "it's true when editing the first post" + ); }); -QUnit.test('clearState', assert => { +QUnit.test("clearState", assert => { const composer = createComposer({ - originalText: 'asdf', - reply: 'asdf2', - post: Discourse.Post.create({id: 1}), - title: 'wat' + originalText: "asdf", + reply: "asdf2", + post: Discourse.Post.create({ id: 1 }), + title: "wat" }); composer.clearState(); - assert.blank(composer.get('originalText')); - assert.blank(composer.get('reply')); - assert.blank(composer.get('post')); - assert.blank(composer.get('title')); - + assert.blank(composer.get("originalText")); + assert.blank(composer.get("reply")); + assert.blank(composer.get("post")); + assert.blank(composer.get("title")); }); -QUnit.test('initial category when uncategorized is allowed', assert => { +QUnit.test("initial category when uncategorized is allowed", assert => { Discourse.SiteSettings.allow_uncategorized_topics = true; - const composer = openComposer({action: 'createTopic', draftKey: 'asfd', draftSequence: 1}); - assert.ok(!composer.get('categoryId'), "Uncategorized by default"); + const composer = openComposer({ + action: "createTopic", + draftKey: "asfd", + draftSequence: 1 + }); + assert.ok(!composer.get("categoryId"), "Uncategorized by default"); }); -QUnit.test('initial category when uncategorized is not allowed', assert => { +QUnit.test("initial category when uncategorized is not allowed", assert => { Discourse.SiteSettings.allow_uncategorized_topics = false; - const composer = openComposer({action: 'createTopic', draftKey: 'asfd', draftSequence: 1}); - assert.ok(!composer.get('categoryId'), "Uncategorized by default. Must choose a category."); + const composer = openComposer({ + action: "createTopic", + draftKey: "asfd", + draftSequence: 1 + }); + assert.ok( + !composer.get("categoryId"), + "Uncategorized by default. Must choose a category." + ); }); -QUnit.test('open with a quote', assert => { - const quote = '[quote="neil, post:5, topic:413"]\nSimmer down you two.\n[/quote]'; +QUnit.test("open with a quote", assert => { + const quote = + '[quote="neil, post:5, topic:413"]\nSimmer down you two.\n[/quote]'; const newComposer = function() { - return openComposer({action: Composer.REPLY, draftKey: 'asfd', draftSequence: 1, quote: quote}); + return openComposer({ + action: Composer.REPLY, + draftKey: "asfd", + draftSequence: 1, + quote: quote + }); }; - assert.equal(newComposer().get('originalText'), quote, "originalText is the quote" ); - assert.equal(newComposer().get('replyDirty'), false, "replyDirty is initally false with a quote" ); + assert.equal( + newComposer().get("originalText"), + quote, + "originalText is the quote" + ); + assert.equal( + newComposer().get("replyDirty"), + false, + "replyDirty is initally false with a quote" + ); }); QUnit.test("Title length for static page topics as admin", assert => { @@ -224,42 +326,69 @@ QUnit.test("Title length for static page topics as admin", assert => { Discourse.SiteSettings.max_topic_title_length = 10; const composer = createComposer(); - const post = Discourse.Post.create({id: 123, post_number: 2, static_doc: true}); - composer.setProperties({post: post, action: Composer.EDIT }); + const post = Discourse.Post.create({ + id: 123, + post_number: 2, + static_doc: true + }); + composer.setProperties({ post: post, action: Composer.EDIT }); - composer.set('title', 'asdf'); - assert.ok(composer.get('titleLengthValid'), "admins can use short titles"); + composer.set("title", "asdf"); + assert.ok(composer.get("titleLengthValid"), "admins can use short titles"); - composer.set('title', 'this is a long title'); - assert.ok(composer.get('titleLengthValid'), "admins can use long titles"); + composer.set("title", "this is a long title"); + assert.ok(composer.get("titleLengthValid"), "admins can use long titles"); - composer.set('title', 'just right'); - assert.ok(composer.get('titleLengthValid'), "in the range is okay"); + composer.set("title", "just right"); + assert.ok(composer.get("titleLengthValid"), "in the range is okay"); - composer.set('title', ''); - assert.ok(!composer.get('titleLengthValid'), "admins must set title to at least 1 character"); + composer.set("title", ""); + assert.ok( + !composer.get("titleLengthValid"), + "admins must set title to at least 1 character" + ); }); QUnit.test("title placeholder depends on what you're doing", assert => { - let composer = createComposer({action: Composer.CREATE_TOPIC}); - assert.equal(composer.get('titlePlaceholder'), 'composer.title_placeholder', "placeholder for normal topic"); + let composer = createComposer({ action: Composer.CREATE_TOPIC }); + assert.equal( + composer.get("titlePlaceholder"), + "composer.title_placeholder", + "placeholder for normal topic" + ); - composer = createComposer({action: Composer.PRIVATE_MESSAGE}); - assert.equal(composer.get('titlePlaceholder'), 'composer.title_placeholder', "placeholder for private message"); + composer = createComposer({ action: Composer.PRIVATE_MESSAGE }); + assert.equal( + composer.get("titlePlaceholder"), + "composer.title_placeholder", + "placeholder for private message" + ); Discourse.SiteSettings.topic_featured_link_enabled = true; - composer = createComposer({action: Composer.CREATE_TOPIC}); - assert.equal(composer.get('titlePlaceholder'), 'composer.title_or_link_placeholder', "placeholder invites you to paste a link"); + composer = createComposer({ action: Composer.CREATE_TOPIC }); + assert.equal( + composer.get("titlePlaceholder"), + "composer.title_or_link_placeholder", + "placeholder invites you to paste a link" + ); - composer = createComposer({action: Composer.PRIVATE_MESSAGE}); - assert.equal(composer.get('titlePlaceholder'), 'composer.title_placeholder', "placeholder for private message with topic links enabled"); + composer = createComposer({ action: Composer.PRIVATE_MESSAGE }); + assert.equal( + composer.get("titlePlaceholder"), + "composer.title_placeholder", + "placeholder for private message with topic links enabled" + ); }); QUnit.test("allows featured link before choosing a category", assert => { Discourse.SiteSettings.topic_featured_link_enabled = true; Discourse.SiteSettings.allow_uncategorized_topics = false; - let composer = createComposer({action: Composer.CREATE_TOPIC}); - assert.equal(composer.get('titlePlaceholder'), 'composer.title_or_link_placeholder', "placeholder invites you to paste a link"); - assert.ok(composer.get('canEditTopicFeaturedLink'), "can paste link"); -}); \ No newline at end of file + let composer = createComposer({ action: Composer.CREATE_TOPIC }); + assert.equal( + composer.get("titlePlaceholder"), + "composer.title_or_link_placeholder", + "placeholder invites you to paste a link" + ); + assert.ok(composer.get("canEditTopicFeaturedLink"), "can paste link"); +}); diff --git a/test/javascripts/models/email-log-test.js.es6 b/test/javascripts/models/email-log-test.js.es6 index be18f9190f4..e322da5a908 100644 --- a/test/javascripts/models/email-log-test.js.es6 +++ b/test/javascripts/models/email-log-test.js.es6 @@ -1,7 +1,7 @@ -import EmailLog from 'admin/models/email-log'; +import EmailLog from "admin/models/email-log"; QUnit.module("Discourse.EmailLog"); QUnit.test("create", assert => { assert.ok(EmailLog.create(), "it can be created without arguments"); -}); \ No newline at end of file +}); diff --git a/test/javascripts/models/group-test.js.es6 b/test/javascripts/models/group-test.js.es6 index 6b1b726dd58..33cb5a709c1 100644 --- a/test/javascripts/models/group-test.js.es6 +++ b/test/javascripts/models/group-test.js.es6 @@ -1,13 +1,21 @@ -import Group from 'discourse/models/group'; +import Group from "discourse/models/group"; QUnit.module("model:group"); -QUnit.test('displayName', assert => { - const group = Group.create({ name: "test", display_name: 'donkey' }); +QUnit.test("displayName", assert => { + const group = Group.create({ name: "test", display_name: "donkey" }); - assert.equal(group.get('displayName'), "donkey", 'it should return the display name'); + assert.equal( + group.get("displayName"), + "donkey", + "it should return the display name" + ); - group.set('display_name', null); + group.set("display_name", null); - assert.equal(group.get('displayName'), "test", "it should return the group's name"); + assert.equal( + group.get("displayName"), + "test", + "it should return the group's name" + ); }); diff --git a/test/javascripts/models/invite-test.js.es6 b/test/javascripts/models/invite-test.js.es6 index 0cdc361fca6..323375d42b3 100644 --- a/test/javascripts/models/invite-test.js.es6 +++ b/test/javascripts/models/invite-test.js.es6 @@ -1,7 +1,7 @@ -import Invite from 'discourse/models/invite'; +import Invite from "discourse/models/invite"; QUnit.module("model:invite"); QUnit.test("create", assert => { assert.ok(Invite.create(), "it can be created without arguments"); -}); \ No newline at end of file +}); diff --git a/test/javascripts/models/model-test.js.es6 b/test/javascripts/models/model-test.js.es6 index dba22bd390a..f5e81acae78 100644 --- a/test/javascripts/models/model-test.js.es6 +++ b/test/javascripts/models/model-test.js.es6 @@ -1,21 +1,35 @@ -import Model from 'discourse/models/model'; +import Model from "discourse/models/model"; QUnit.module("model:discourse"); -QUnit.test("extractByKey: converts a list of hashes into a hash of instances of specified class, indexed by their ids", assert => { - var firstObject = {id: "id_1", foo: "foo_1"}; - var secondObject = {id: "id_2", foo: "foo_2"}; +QUnit.test( + "extractByKey: converts a list of hashes into a hash of instances of specified class, indexed by their ids", + assert => { + var firstObject = { id: "id_1", foo: "foo_1" }; + var secondObject = { id: "id_2", foo: "foo_2" }; - var actual = Model.extractByKey([firstObject, secondObject], Ember.Object); - var expected = { - id_1: Ember.Object.create(firstObject), - id_2: Ember.Object.create(secondObject) - }; + var actual = Model.extractByKey([firstObject, secondObject], Ember.Object); + var expected = { + id_1: Ember.Object.create(firstObject), + id_2: Ember.Object.create(secondObject) + }; - assert.ok(_.isEqual(actual, expected)); -}); + assert.ok(_.isEqual(actual, expected)); + } +); -QUnit.test("extractByKey: returns an empty hash if there isn't anything to convert", assert => { - assert.deepEqual(Model.extractByKey(), {}, "when called without parameters"); - assert.deepEqual(Model.extractByKey([]), {}, "when called with an empty array"); -}); +QUnit.test( + "extractByKey: returns an empty hash if there isn't anything to convert", + assert => { + assert.deepEqual( + Model.extractByKey(), + {}, + "when called without parameters" + ); + assert.deepEqual( + Model.extractByKey([]), + {}, + "when called with an empty array" + ); + } +); diff --git a/test/javascripts/models/nav-item-test.js.es6 b/test/javascripts/models/nav-item-test.js.es6 index 38174eb639b..115417e4d48 100644 --- a/test/javascripts/models/nav-item-test.js.es6 +++ b/test/javascripts/models/nav-item-test.js.es6 @@ -1,25 +1,32 @@ -import createStore from 'helpers/create-store'; +import createStore from "helpers/create-store"; QUnit.module("Discourse.NavItem", { beforeEach() { Ember.run(function() { - const asianCategory = Discourse.Category.create({name: '确实是这样', id: 343434}); - Discourse.Site.currentProp('categories').addObject(asianCategory); + const asianCategory = Discourse.Category.create({ + name: "确实是这样", + id: 343434 + }); + Discourse.Site.currentProp("categories").addObject(asianCategory); }); } }); -QUnit.test('href', assert =>{ +QUnit.test("href", assert => { assert.expect(4); function href(text, expected, label) { - assert.equal(Discourse.NavItem.fromText(text, {}).get('href'), expected, label); + assert.equal( + Discourse.NavItem.fromText(text, {}).get("href"), + expected, + label + ); } - href('latest', '/latest', 'latest'); - href('categories', '/categories', 'categories'); - href('category/bug', '/c/bug', 'English category name'); - href('category/确实是这样', '/c/343434-category', 'Chinese category name'); + href("latest", "/latest", "latest"); + href("categories", "/categories", "categories"); + href("category/bug", "/c/bug", "English category name"); + href("category/确实是这样", "/c/343434-category", "Chinese category name"); }); QUnit.test("count", assert => { @@ -31,5 +38,9 @@ QUnit.test("count", assert => { tracker.states["t1"] = { topic_id: 1, last_read_post_number: null }; tracker.incrementMessageCount(); - assert.equal(navItem.get("count"), 1, "it updates when a new message arrives"); + assert.equal( + navItem.get("count"), + 1, + "it updates when a new message arrives" + ); }); diff --git a/test/javascripts/models/post-stream-test.js.es6 b/test/javascripts/models/post-stream-test.js.es6 index 7418102a173..c33e9d6d51b 100644 --- a/test/javascripts/models/post-stream-test.js.es6 +++ b/test/javascripts/models/post-stream-test.js.es6 @@ -1,85 +1,145 @@ QUnit.module("model:post-stream"); -import createStore from 'helpers/create-store'; +import createStore from "helpers/create-store"; const buildStream = function(id, stream) { const store = createStore(); - const topic = store.createRecord('topic', {id, chunk_size: 5}); - const ps = topic.get('postStream'); + const topic = store.createRecord("topic", { id, chunk_size: 5 }); + const ps = topic.get("postStream"); if (stream) { - ps.set('stream', stream); + ps.set("stream", stream); } return ps; }; -const participant = {username: 'eviltrout'}; +const participant = { username: "eviltrout" }; -QUnit.test('create', assert => { +QUnit.test("create", assert => { const store = createStore(); - assert.ok(store.createRecord('postStream'), 'it can be created with no parameters'); + assert.ok( + store.createRecord("postStream"), + "it can be created with no parameters" + ); }); -QUnit.test('defaults', assert => { +QUnit.test("defaults", assert => { const postStream = buildStream(1234); - assert.blank(postStream.get('posts'), "there are no posts in a stream by default"); - assert.ok(!postStream.get('loaded'), "it has never loaded"); - assert.present(postStream.get('topic')); + assert.blank( + postStream.get("posts"), + "there are no posts in a stream by default" + ); + assert.ok(!postStream.get("loaded"), "it has never loaded"); + assert.present(postStream.get("topic")); }); -QUnit.test('appending posts', assert => { +QUnit.test("appending posts", assert => { const postStream = buildStream(4567, [1, 3, 4]); const store = postStream.store; - assert.equal(postStream.get('firstPostId'), 1); - assert.equal(postStream.get('lastPostId'), 4, "the last post id is 4"); + assert.equal(postStream.get("firstPostId"), 1); + assert.equal(postStream.get("lastPostId"), 4, "the last post id is 4"); - assert.ok(!postStream.get('hasPosts'), "there are no posts by default"); - assert.ok(!postStream.get('firstPostPresent'), "the first post is not loaded"); - assert.ok(!postStream.get('loadedAllPosts'), "the last post is not loaded"); - assert.equal(postStream.get('posts.length'), 0, "it has no posts initially"); + assert.ok(!postStream.get("hasPosts"), "there are no posts by default"); + assert.ok( + !postStream.get("firstPostPresent"), + "the first post is not loaded" + ); + assert.ok(!postStream.get("loadedAllPosts"), "the last post is not loaded"); + assert.equal(postStream.get("posts.length"), 0, "it has no posts initially"); - postStream.appendPost(store.createRecord('post', {id: 2, post_number: 2})); - assert.ok(!postStream.get('firstPostPresent'), "the first post is still not loaded"); - assert.equal(postStream.get('posts.length'), 1, "it has one post in the stream"); + postStream.appendPost(store.createRecord("post", { id: 2, post_number: 2 })); + assert.ok( + !postStream.get("firstPostPresent"), + "the first post is still not loaded" + ); + assert.equal( + postStream.get("posts.length"), + 1, + "it has one post in the stream" + ); - postStream.appendPost(store.createRecord('post', {id: 4, post_number: 4})); - assert.ok(!postStream.get('firstPostPresent'), "the first post is still loaded"); - assert.ok(postStream.get('loadedAllPosts'), "the last post is now loaded"); - assert.equal(postStream.get('posts.length'), 2, "it has two posts in the stream"); + postStream.appendPost(store.createRecord("post", { id: 4, post_number: 4 })); + assert.ok( + !postStream.get("firstPostPresent"), + "the first post is still loaded" + ); + assert.ok(postStream.get("loadedAllPosts"), "the last post is now loaded"); + assert.equal( + postStream.get("posts.length"), + 2, + "it has two posts in the stream" + ); - postStream.appendPost(store.createRecord('post', {id: 4, post_number: 4})); - assert.equal(postStream.get('posts.length'), 2, "it will not add the same post with id twice"); + postStream.appendPost(store.createRecord("post", { id: 4, post_number: 4 })); + assert.equal( + postStream.get("posts.length"), + 2, + "it will not add the same post with id twice" + ); - const stagedPost = store.createRecord('post', {raw: 'incomplete post'}); + const stagedPost = store.createRecord("post", { raw: "incomplete post" }); postStream.appendPost(stagedPost); - assert.equal(postStream.get('posts.length'), 3, "it can handle posts without ids"); + assert.equal( + postStream.get("posts.length"), + 3, + "it can handle posts without ids" + ); postStream.appendPost(stagedPost); - assert.equal(postStream.get('posts.length'), 3, "it won't add the same post without an id twice"); + assert.equal( + postStream.get("posts.length"), + 3, + "it won't add the same post without an id twice" + ); // change the stream - postStream.set('stream', [1, 2, 4]); - assert.ok(!postStream.get('firstPostPresent'), "the first post no longer loaded since the stream changed."); - assert.ok(postStream.get('loadedAllPosts'), "the last post is still the last post in the new stream"); + postStream.set("stream", [1, 2, 4]); + assert.ok( + !postStream.get("firstPostPresent"), + "the first post no longer loaded since the stream changed." + ); + assert.ok( + postStream.get("loadedAllPosts"), + "the last post is still the last post in the new stream" + ); }); -QUnit.test('closestPostNumberFor', assert => { +QUnit.test("closestPostNumberFor", assert => { const postStream = buildStream(1231); const store = postStream.store; - assert.blank(postStream.closestPostNumberFor(1), "there is no closest post when nothing is loaded"); + assert.blank( + postStream.closestPostNumberFor(1), + "there is no closest post when nothing is loaded" + ); - postStream.appendPost(store.createRecord('post', {id: 1, post_number: 2})); - postStream.appendPost(store.createRecord('post', {id: 2, post_number: 3})); + postStream.appendPost(store.createRecord("post", { id: 1, post_number: 2 })); + postStream.appendPost(store.createRecord("post", { id: 2, post_number: 3 })); - assert.equal(postStream.closestPostNumberFor(2), 2, "If a post is in the stream it returns its post number"); - assert.equal(postStream.closestPostNumberFor(3), 3, "If a post is in the stream it returns its post number"); - assert.equal(postStream.closestPostNumberFor(10), 3, "it clips to the upper bound of the stream"); - assert.equal(postStream.closestPostNumberFor(0), 2, "it clips to the lower bound of the stream"); + assert.equal( + postStream.closestPostNumberFor(2), + 2, + "If a post is in the stream it returns its post number" + ); + assert.equal( + postStream.closestPostNumberFor(3), + 3, + "If a post is in the stream it returns its post number" + ); + assert.equal( + postStream.closestPostNumberFor(10), + 3, + "it clips to the upper bound of the stream" + ); + assert.equal( + postStream.closestPostNumberFor(0), + 2, + "it clips to the lower bound of the stream" + ); }); -QUnit.test('closestDaysAgoFor', assert => { +QUnit.test("closestDaysAgoFor", assert => { const postStream = buildStream(1231); - postStream.set('timelineLookup', [[1, 10], [3, 8], [5, 1]]); + postStream.set("timelineLookup", [[1, 10], [3, 8], [5, 1]]); assert.equal(postStream.closestDaysAgoFor(1), 10); assert.equal(postStream.closestDaysAgoFor(2), 10); @@ -93,35 +153,35 @@ QUnit.test('closestDaysAgoFor', assert => { assert.equal(postStream.closestDaysAgoFor(10), 1); }); -QUnit.test('closestDaysAgoFor - empty', assert => { +QUnit.test("closestDaysAgoFor - empty", assert => { const postStream = buildStream(1231); - postStream.set('timelineLookup', []); + postStream.set("timelineLookup", []); assert.equal(postStream.closestDaysAgoFor(1), null); }); -QUnit.test('updateFromJson', assert => { +QUnit.test("updateFromJson", assert => { const postStream = buildStream(1231); postStream.updateFromJson({ - posts: [{id: 1}], + posts: [{ id: 1 }], stream: [1], extra_property: 12 }); - assert.equal(postStream.get('posts.length'), 1, 'it loaded the posts'); - assert.containsInstance(postStream.get('posts'), Discourse.Post); + assert.equal(postStream.get("posts.length"), 1, "it loaded the posts"); + assert.containsInstance(postStream.get("posts"), Discourse.Post); - assert.equal(postStream.get('extra_property'), 12); + assert.equal(postStream.get("extra_property"), 12); }); QUnit.test("removePosts", assert => { - const postStream = buildStream(10000001, [1,2,3]); + const postStream = buildStream(10000001, [1, 2, 3]); const store = postStream.store; - const p1 = store.createRecord('post', {id: 1, post_number: 2}), - p2 = store.createRecord('post', {id: 2, post_number: 3}), - p3 = store.createRecord('post', {id: 3, post_number: 4}); + const p1 = store.createRecord("post", { id: 1, post_number: 2 }), + p2 = store.createRecord("post", { id: 2, post_number: 3 }), + p3 = store.createRecord("post", { id: 3, post_number: 4 }); postStream.appendPost(p1); postStream.appendPost(p2); @@ -129,12 +189,11 @@ QUnit.test("removePosts", assert => { // Removing nothing does nothing postStream.removePosts(); - assert.equal(postStream.get('posts.length'), 3); + assert.equal(postStream.get("posts.length"), 3); postStream.removePosts([p1, p3]); - assert.equal(postStream.get('posts.length'), 1); - assert.deepEqual(postStream.get('stream'), [2]); - + assert.equal(postStream.get("posts.length"), 1); + assert.deepEqual(postStream.get("stream"), [2]); }); QUnit.test("cancelFilter", assert => { @@ -142,156 +201,283 @@ QUnit.test("cancelFilter", assert => { sandbox.stub(postStream, "refresh").returns(new Ember.RSVP.resolve()); - postStream.set('summary', true); + postStream.set("summary", true); postStream.cancelFilter(); - assert.ok(!postStream.get('summary'), "summary is cancelled"); + assert.ok(!postStream.get("summary"), "summary is cancelled"); postStream.toggleParticipant(participant); postStream.cancelFilter(); - assert.blank(postStream.get('userFilters'), "cancelling the filters clears the userFilters"); + assert.blank( + postStream.get("userFilters"), + "cancelling the filters clears the userFilters" + ); }); QUnit.test("findPostIdForPostNumber", assert => { const postStream = buildStream(1234, [10, 20, 30, 40, 50, 60, 70]); - postStream.set('gaps', { before: { 60: [55, 58] } }); - - assert.equal(postStream.findPostIdForPostNumber(500), null, 'it returns null when the post cannot be found'); - assert.equal(postStream.findPostIdForPostNumber(1), 10, 'it finds the postId at the beginning'); - assert.equal(postStream.findPostIdForPostNumber(5), 50, 'it finds the postId in the middle'); - assert.equal(postStream.findPostIdForPostNumber(8), 60, 'it respects gaps'); + postStream.set("gaps", { before: { 60: [55, 58] } }); + assert.equal( + postStream.findPostIdForPostNumber(500), + null, + "it returns null when the post cannot be found" + ); + assert.equal( + postStream.findPostIdForPostNumber(1), + 10, + "it finds the postId at the beginning" + ); + assert.equal( + postStream.findPostIdForPostNumber(5), + 50, + "it finds the postId in the middle" + ); + assert.equal(postStream.findPostIdForPostNumber(8), 60, "it respects gaps"); }); QUnit.test("toggleParticipant", assert => { const postStream = buildStream(1236); sandbox.stub(postStream, "refresh").returns(new Ember.RSVP.resolve()); - assert.equal(postStream.get('userFilters.length'), 0, "by default no participants are toggled"); + assert.equal( + postStream.get("userFilters.length"), + 0, + "by default no participants are toggled" + ); postStream.toggleParticipant(participant.username); - assert.ok(postStream.get('userFilters').includes('eviltrout'), 'eviltrout is in the filters'); + assert.ok( + postStream.get("userFilters").includes("eviltrout"), + "eviltrout is in the filters" + ); postStream.toggleParticipant(participant.username); - assert.blank(postStream.get('userFilters'), "toggling the participant again removes them"); + assert.blank( + postStream.get("userFilters"), + "toggling the participant again removes them" + ); }); QUnit.test("streamFilters", assert => { const postStream = buildStream(1237); sandbox.stub(postStream, "refresh").returns(new Ember.RSVP.resolve()); - assert.deepEqual(postStream.get('streamFilters'), {}, "there are no postFilters by default"); - assert.ok(postStream.get('hasNoFilters'), "there are no filters by default"); + assert.deepEqual( + postStream.get("streamFilters"), + {}, + "there are no postFilters by default" + ); + assert.ok(postStream.get("hasNoFilters"), "there are no filters by default"); - postStream.set('summary', true); - assert.deepEqual(postStream.get('streamFilters'), {filter: "summary"}, "postFilters contains the summary flag"); - assert.ok(!postStream.get('hasNoFilters'), "now there are filters present"); + postStream.set("summary", true); + assert.deepEqual( + postStream.get("streamFilters"), + { filter: "summary" }, + "postFilters contains the summary flag" + ); + assert.ok(!postStream.get("hasNoFilters"), "now there are filters present"); postStream.toggleParticipant(participant.username); - assert.deepEqual(postStream.get('streamFilters'), { - username_filters: 'eviltrout', - }, "streamFilters contains the username we filtered"); + assert.deepEqual( + postStream.get("streamFilters"), + { + username_filters: "eviltrout" + }, + "streamFilters contains the username we filtered" + ); }); QUnit.test("loading", assert => { let postStream = buildStream(1234); - assert.ok(!postStream.get('loading'), "we're not loading by default"); + assert.ok(!postStream.get("loading"), "we're not loading by default"); - postStream.set('loadingAbove', true); - assert.ok(postStream.get('loading'), "we're loading if loading above"); + postStream.set("loadingAbove", true); + assert.ok(postStream.get("loading"), "we're loading if loading above"); postStream = buildStream(1234); - postStream.set('loadingBelow', true); - assert.ok(postStream.get('loading'), "we're loading if loading below"); + postStream.set("loadingBelow", true); + assert.ok(postStream.get("loading"), "we're loading if loading below"); postStream = buildStream(1234); - postStream.set('loadingFilter', true); - assert.ok(postStream.get('loading'), "we're loading if loading a filter"); + postStream.set("loadingFilter", true); + assert.ok(postStream.get("loading"), "we're loading if loading a filter"); }); QUnit.test("nextWindow", assert => { - const postStream = buildStream(1234, [1,2,3,5,8,9,10,11,13,14,15,16]); + const postStream = buildStream(1234, [ + 1, + 2, + 3, + 5, + 8, + 9, + 10, + 11, + 13, + 14, + 15, + 16 + ]); - assert.blank(postStream.get('nextWindow'), 'With no posts loaded, the window is blank'); + assert.blank( + postStream.get("nextWindow"), + "With no posts loaded, the window is blank" + ); - postStream.updateFromJson({ posts: [{id: 1}, {id: 2}] }); - assert.deepEqual(postStream.get('nextWindow'), [3,5,8,9,10], - "If we've loaded the first 2 posts, the window should be the 5 after that"); + postStream.updateFromJson({ posts: [{ id: 1 }, { id: 2 }] }); + assert.deepEqual( + postStream.get("nextWindow"), + [3, 5, 8, 9, 10], + "If we've loaded the first 2 posts, the window should be the 5 after that" + ); - postStream.updateFromJson({ posts: [{id: 13}] }); - assert.deepEqual(postStream.get('nextWindow'), [14, 15, 16], "Boundary check: stop at the end."); + postStream.updateFromJson({ posts: [{ id: 13 }] }); + assert.deepEqual( + postStream.get("nextWindow"), + [14, 15, 16], + "Boundary check: stop at the end." + ); - postStream.updateFromJson({ posts: [{id: 16}] }); - assert.blank(postStream.get('nextWindow'), "Once we've seen everything there's nothing to load."); + postStream.updateFromJson({ posts: [{ id: 16 }] }); + assert.blank( + postStream.get("nextWindow"), + "Once we've seen everything there's nothing to load." + ); }); QUnit.test("previousWindow", assert => { - const postStream = buildStream(1234, [1,2,3,5,8,9,10,11,13,14,15,16]); + const postStream = buildStream(1234, [ + 1, + 2, + 3, + 5, + 8, + 9, + 10, + 11, + 13, + 14, + 15, + 16 + ]); - assert.blank(postStream.get('previousWindow'), 'With no posts loaded, the window is blank'); + assert.blank( + postStream.get("previousWindow"), + "With no posts loaded, the window is blank" + ); - postStream.updateFromJson({ posts: [{id: 11}, {id: 13}] }); - assert.deepEqual(postStream.get('previousWindow'), [3, 5, 8, 9, 10], - "If we've loaded in the middle, it's the previous 5 posts"); + postStream.updateFromJson({ posts: [{ id: 11 }, { id: 13 }] }); + assert.deepEqual( + postStream.get("previousWindow"), + [3, 5, 8, 9, 10], + "If we've loaded in the middle, it's the previous 5 posts" + ); - postStream.updateFromJson({ posts: [{id: 3}] }); - assert.deepEqual(postStream.get('previousWindow'), [1, 2], "Boundary check: stop at the beginning."); + postStream.updateFromJson({ posts: [{ id: 3 }] }); + assert.deepEqual( + postStream.get("previousWindow"), + [1, 2], + "Boundary check: stop at the beginning." + ); - postStream.updateFromJson({ posts: [{id: 1}] }); - assert.blank(postStream.get('previousWindow'), "Once we've seen everything there's nothing to load."); + postStream.updateFromJson({ posts: [{ id: 1 }] }); + assert.blank( + postStream.get("previousWindow"), + "Once we've seen everything there's nothing to load." + ); }); QUnit.test("storePost", assert => { const postStream = buildStream(1234), - store = postStream.store, - post = store.createRecord('post', {id: 1, post_number: 100, raw: 'initial value'}); + store = postStream.store, + post = store.createRecord("post", { + id: 1, + post_number: 100, + raw: "initial value" + }); - assert.blank(postStream.get('topic.highest_post_number'), "it has no highest post number yet"); + assert.blank( + postStream.get("topic.highest_post_number"), + "it has no highest post number yet" + ); let stored = postStream.storePost(post); assert.equal(post, stored, "it returns the post it stored"); - assert.equal(post.get('topic'), postStream.get('topic'), "it creates the topic reference properly"); - assert.equal(postStream.get('topic.highest_post_number'), 100, "it set the highest post number"); + assert.equal( + post.get("topic"), + postStream.get("topic"), + "it creates the topic reference properly" + ); + assert.equal( + postStream.get("topic.highest_post_number"), + 100, + "it set the highest post number" + ); - const dupePost = store.createRecord('post', {id: 1, post_number: 100, raw: 'updated value'}); + const dupePost = store.createRecord("post", { + id: 1, + post_number: 100, + raw: "updated value" + }); const storedDupe = postStream.storePost(dupePost); - assert.equal(storedDupe, post, "it returns the previously stored post instead to avoid dupes"); - assert.equal(storedDupe.get('raw'), 'updated value', 'it updates the previously stored post'); + assert.equal( + storedDupe, + post, + "it returns the previously stored post instead to avoid dupes" + ); + assert.equal( + storedDupe.get("raw"), + "updated value", + "it updates the previously stored post" + ); - const postWithoutId = store.createRecord('post', {raw: 'hello world'}); + const postWithoutId = store.createRecord("post", { raw: "hello world" }); stored = postStream.storePost(postWithoutId); assert.equal(stored, postWithoutId, "it returns the same post back"); - }); QUnit.test("identity map", assert => { const postStream = buildStream(1234); const store = postStream.store; - const p1 = postStream.appendPost(store.createRecord('post', {id: 1, post_number: 1})); - const p3 = postStream.appendPost(store.createRecord('post', {id: 3, post_number: 4})); + const p1 = postStream.appendPost( + store.createRecord("post", { id: 1, post_number: 1 }) + ); + const p3 = postStream.appendPost( + store.createRecord("post", { id: 3, post_number: 4 }) + ); - assert.equal(postStream.findLoadedPost(1), p1, "it can return cached posts by id"); + assert.equal( + postStream.findLoadedPost(1), + p1, + "it can return cached posts by id" + ); assert.blank(postStream.findLoadedPost(4), "it can't find uncached posts"); // Find posts by ids uses the identity map return postStream.findPostsByIds([1, 2, 3]).then(result => { assert.equal(result.length, 3); assert.equal(result.objectAt(0), p1); - assert.equal(result.objectAt(1).get('post_number'), 2); + assert.equal(result.objectAt(1).get("post_number"), 2); assert.equal(result.objectAt(2), p3); }); }); QUnit.test("loadIntoIdentityMap with no data", assert => { - return buildStream(1234).loadIntoIdentityMap([]).then(result => { - assert.equal(result.length, 0, 'requesting no posts produces no posts'); - }); + return buildStream(1234) + .loadIntoIdentityMap([]) + .then(result => { + assert.equal(result.length, 0, "requesting no posts produces no posts"); + }); }); QUnit.test("loadIntoIdentityMap with post ids", assert => { const postStream = buildStream(1234); return postStream.loadIntoIdentityMap([10]).then(function() { - assert.present(postStream.findLoadedPost(10), "it adds the returned post to the store"); + assert.present( + postStream.findLoadedPost(10), + "it adds the returned post to the store" + ); }); }); @@ -299,14 +485,29 @@ QUnit.test("staging and undoing a new post", assert => { const postStream = buildStream(10101, [1]); const store = postStream.store; - const original = store.createRecord('post', {id: 1, post_number: 1, topic_id: 10101}); + const original = store.createRecord("post", { + id: 1, + post_number: 1, + topic_id: 10101 + }); postStream.appendPost(original); - assert.ok(postStream.get('lastAppended'), original, "the original post is lastAppended"); + assert.ok( + postStream.get("lastAppended"), + original, + "the original post is lastAppended" + ); - const user = Discourse.User.create({username: 'eviltrout', name: 'eviltrout', id: 321}); - const stagedPost = store.createRecord('post', { raw: 'hello world this is my new post', topic_id: 10101 }); + const user = Discourse.User.create({ + username: "eviltrout", + name: "eviltrout", + id: 321 + }); + const stagedPost = store.createRecord("post", { + raw: "hello world this is my new post", + topic_id: 10101 + }); - const topic = postStream.get('topic'); + const topic = postStream.get("topic"); topic.setProperties({ posts_count: 1, highest_post_number: 1 @@ -315,67 +516,149 @@ QUnit.test("staging and undoing a new post", assert => { // Stage the new post in the stream const result = postStream.stagePost(stagedPost, user); assert.equal(result, "staged", "it returns staged"); - assert.equal(topic.get('highest_post_number'), 2, "it updates the highest_post_number"); - assert.ok(postStream.get('loading'), "it is loading while the post is being staged"); - assert.ok(postStream.get('lastAppended'), original, "it doesn't consider staged posts as the lastAppended"); + assert.equal( + topic.get("highest_post_number"), + 2, + "it updates the highest_post_number" + ); + assert.ok( + postStream.get("loading"), + "it is loading while the post is being staged" + ); + assert.ok( + postStream.get("lastAppended"), + original, + "it doesn't consider staged posts as the lastAppended" + ); - assert.equal(topic.get('posts_count'), 2, "it increases the post count"); - assert.present(topic.get('last_posted_at'), "it updates last_posted_at"); - assert.equal(topic.get('details.last_poster'), user, "it changes the last poster"); + assert.equal(topic.get("posts_count"), 2, "it increases the post count"); + assert.present(topic.get("last_posted_at"), "it updates last_posted_at"); + assert.equal( + topic.get("details.last_poster"), + user, + "it changes the last poster" + ); - assert.equal(stagedPost.get('topic'), topic, "it assigns the topic reference"); - assert.equal(stagedPost.get('post_number'), 2, "it is assigned the probable post_number"); - assert.present(stagedPost.get('created_at'), "it is assigned a created date"); - assert.ok(postStream.get('posts').includes(stagedPost), "the post is added to the stream"); - assert.equal(stagedPost.get('id'), -1, "the post has a magical -1 id"); + assert.equal( + stagedPost.get("topic"), + topic, + "it assigns the topic reference" + ); + assert.equal( + stagedPost.get("post_number"), + 2, + "it is assigned the probable post_number" + ); + assert.present(stagedPost.get("created_at"), "it is assigned a created date"); + assert.ok( + postStream.get("posts").includes(stagedPost), + "the post is added to the stream" + ); + assert.equal(stagedPost.get("id"), -1, "the post has a magical -1 id"); // Undoing a created post (there was an error) postStream.undoPost(stagedPost); - assert.ok(!postStream.get('loading'), "it is no longer loading"); - assert.equal(topic.get('highest_post_number'), 1, "it reverts the highest_post_number"); - assert.equal(topic.get('posts_count'), 1, "it reverts the post count"); - assert.equal(postStream.get('filteredPostsCount'), 1, "it retains the filteredPostsCount"); - assert.ok(!postStream.get('posts').includes(stagedPost), "the post is removed from the stream"); - assert.ok(postStream.get('lastAppended'), original, "it doesn't consider undid post lastAppended"); + assert.ok(!postStream.get("loading"), "it is no longer loading"); + assert.equal( + topic.get("highest_post_number"), + 1, + "it reverts the highest_post_number" + ); + assert.equal(topic.get("posts_count"), 1, "it reverts the post count"); + assert.equal( + postStream.get("filteredPostsCount"), + 1, + "it retains the filteredPostsCount" + ); + assert.ok( + !postStream.get("posts").includes(stagedPost), + "the post is removed from the stream" + ); + assert.ok( + postStream.get("lastAppended"), + original, + "it doesn't consider undid post lastAppended" + ); }); QUnit.test("staging and committing a post", assert => { const postStream = buildStream(10101, [1]); const store = postStream.store; - const original = store.createRecord('post', {id: 1, post_number: 1, topic_id: 10101}); + const original = store.createRecord("post", { + id: 1, + post_number: 1, + topic_id: 10101 + }); postStream.appendPost(original); - assert.ok(postStream.get('lastAppended'), original, "the original post is lastAppended"); + assert.ok( + postStream.get("lastAppended"), + original, + "the original post is lastAppended" + ); - const user = Discourse.User.create({username: 'eviltrout', name: 'eviltrout', id: 321}); - const stagedPost = store.createRecord('post', { raw: 'hello world this is my new post', topic_id: 10101 }); + const user = Discourse.User.create({ + username: "eviltrout", + name: "eviltrout", + id: 321 + }); + const stagedPost = store.createRecord("post", { + raw: "hello world this is my new post", + topic_id: 10101 + }); - const topic = postStream.get('topic'); - topic.set('posts_count', 1); + const topic = postStream.get("topic"); + topic.set("posts_count", 1); // Stage the new post in the stream let result = postStream.stagePost(stagedPost, user); assert.equal(result, "staged", "it returns staged"); - assert.ok(postStream.get('loading'), "it is loading while the post is being staged"); + assert.ok( + postStream.get("loading"), + "it is loading while the post is being staged" + ); stagedPost.setProperties({ id: 1234, raw: "different raw value" }); result = postStream.stagePost(stagedPost, user); - assert.equal(result, "alreadyStaging", "you can't stage a post while it is currently staging"); - assert.ok(postStream.get('lastAppended'), original, "staging a post doesn't change the lastAppended"); + assert.equal( + result, + "alreadyStaging", + "you can't stage a post while it is currently staging" + ); + assert.ok( + postStream.get("lastAppended"), + original, + "staging a post doesn't change the lastAppended" + ); postStream.commitPost(stagedPost); - assert.ok(postStream.get('posts').includes(stagedPost), "the post is still in the stream"); - assert.ok(!postStream.get('loading'), "it is no longer loading"); + assert.ok( + postStream.get("posts").includes(stagedPost), + "the post is still in the stream" + ); + assert.ok(!postStream.get("loading"), "it is no longer loading"); - assert.equal(postStream.get('filteredPostsCount'), 2, "it increases the filteredPostsCount"); + assert.equal( + postStream.get("filteredPostsCount"), + 2, + "it increases the filteredPostsCount" + ); - const found = postStream.findLoadedPost(stagedPost.get('id')); + const found = postStream.findLoadedPost(stagedPost.get("id")); assert.present(found, "the post is in the identity map"); assert.ok(postStream.indexOf(stagedPost) > -1, "the post is in the stream"); - assert.equal(found.get('raw'), 'different raw value', 'it also updated the value in the stream'); - assert.ok(postStream.get('lastAppended'), found, "comitting a post changes lastAppended"); + assert.equal( + found.get("raw"), + "different raw value", + "it also updated the value in the stream" + ); + assert.ok( + postStream.get("lastAppended"), + found, + "comitting a post changes lastAppended" + ); }); QUnit.test("loadedAllPosts when the id changes", assert => { @@ -383,55 +666,74 @@ QUnit.test("loadedAllPosts when the id changes", assert => { // message bus. If the id of a post changes we should reconsider the loadedAllPosts property. const postStream = buildStream(10101, [1, 2]); const store = postStream.store; - const postWithoutId = store.createRecord('post', { raw: 'hello world this is my new post' }); + const postWithoutId = store.createRecord("post", { + raw: "hello world this is my new post" + }); - postStream.appendPost(store.createRecord('post', {id: 1, post_number: 1})); + postStream.appendPost(store.createRecord("post", { id: 1, post_number: 1 })); postStream.appendPost(postWithoutId); - assert.ok(!postStream.get('loadedAllPosts'), 'the last post is not loaded'); + assert.ok(!postStream.get("loadedAllPosts"), "the last post is not loaded"); - postWithoutId.set('id', 2); - assert.ok(postStream.get('loadedAllPosts'), 'the last post is loaded now that the post has an id'); + postWithoutId.set("id", 2); + assert.ok( + postStream.get("loadedAllPosts"), + "the last post is loaded now that the post has an id" + ); }); QUnit.test("comitting and triggerNewPostInStream race condition", assert => { const postStream = buildStream(4964); const store = postStream.store; - postStream.appendPost(store.createRecord('post', {id: 1, post_number: 1})); - const user = Discourse.User.create({username: 'eviltrout', name: 'eviltrout', id: 321}); - const stagedPost = store.createRecord('post', { raw: 'hello world this is my new post' }); + postStream.appendPost(store.createRecord("post", { id: 1, post_number: 1 })); + const user = Discourse.User.create({ + username: "eviltrout", + name: "eviltrout", + id: 321 + }); + const stagedPost = store.createRecord("post", { + raw: "hello world this is my new post" + }); postStream.stagePost(stagedPost, user); - assert.equal(postStream.get('filteredPostsCount'), 0, "it has no filteredPostsCount yet"); - stagedPost.set('id', 123); + assert.equal( + postStream.get("filteredPostsCount"), + 0, + "it has no filteredPostsCount yet" + ); + stagedPost.set("id", 123); - sandbox.stub(postStream, 'appendMore'); + sandbox.stub(postStream, "appendMore"); postStream.triggerNewPostInStream(123); - assert.equal(postStream.get('filteredPostsCount'), 1, "it added the post"); + assert.equal(postStream.get("filteredPostsCount"), 1, "it added the post"); postStream.commitPost(stagedPost); - assert.equal(postStream.get('filteredPostsCount'), 1, "it does not add the same post twice"); + assert.equal( + postStream.get("filteredPostsCount"), + 1, + "it does not add the same post twice" + ); }); QUnit.test("postsWithPlaceholders", assert => { const postStream = buildStream(4964, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - const postsWithPlaceholders = postStream.get('postsWithPlaceholders'); + const postsWithPlaceholders = postStream.get("postsWithPlaceholders"); const store = postStream.store; const testProxy = Ember.ArrayProxy.create({ content: postsWithPlaceholders }); - const p1 = store.createRecord('post', {id: 1, post_number: 1}); - const p2 = store.createRecord('post', {id: 2, post_number: 2}); - const p3 = store.createRecord('post', {id: 3, post_number: 3}); - const p4 = store.createRecord('post', {id: 4, post_number: 4}); + const p1 = store.createRecord("post", { id: 1, post_number: 1 }); + const p2 = store.createRecord("post", { id: 2, post_number: 2 }); + const p3 = store.createRecord("post", { id: 3, post_number: 3 }); + const p4 = store.createRecord("post", { id: 4, post_number: 4 }); postStream.appendPost(p1); postStream.appendPost(p2); postStream.appendPost(p3); // Test enumerable and array access - assert.equal(postsWithPlaceholders.get('length'), 3); - assert.equal(testProxy.get('length'), 3); + assert.equal(postsWithPlaceholders.get("length"), 3); + assert.equal(testProxy.get("length"), 3); assert.equal(postsWithPlaceholders.nextObject(0), p1); assert.equal(postsWithPlaceholders.objectAt(0), p1); assert.equal(postsWithPlaceholders.nextObject(1, p1), p2); @@ -440,8 +742,12 @@ QUnit.test("postsWithPlaceholders", assert => { assert.equal(postsWithPlaceholders.objectAt(2), p3); const promise = postStream.appendMore(); - assert.equal(postsWithPlaceholders.get('length'), 8, 'we immediately have a larger placeholder window'); - assert.equal(testProxy.get('length'), 8); + assert.equal( + postsWithPlaceholders.get("length"), + 8, + "we immediately have a larger placeholder window" + ); + assert.equal(testProxy.get("length"), 8); assert.ok(!!postsWithPlaceholders.nextObject(3, p3)); assert.ok(!!postsWithPlaceholders.objectAt(4)); assert.ok(postsWithPlaceholders.objectAt(3) !== p4); @@ -449,9 +755,12 @@ QUnit.test("postsWithPlaceholders", assert => { return promise.then(() => { assert.equal(postsWithPlaceholders.objectAt(3), p4); - assert.equal(postsWithPlaceholders.get('length'), 8, 'have a larger placeholder window when loaded'); - assert.equal(testProxy.get('length'), 8); + assert.equal( + postsWithPlaceholders.get("length"), + 8, + "have a larger placeholder window when loaded" + ); + assert.equal(testProxy.get("length"), 8); assert.equal(testProxy.objectAt(3), p4); }); - }); diff --git a/test/javascripts/models/post-test.js.es6 b/test/javascripts/models/post-test.js.es6 index 22cb0bde24d..2ded01501f3 100644 --- a/test/javascripts/models/post-test.js.es6 +++ b/test/javascripts/models/post-test.js.es6 @@ -1,72 +1,96 @@ QUnit.module("Discourse.Post"); var buildPost = function(args) { - return Discourse.Post.create(_.merge({ - id: 1, - can_delete: true, - version: 1 - }, args || {})); + return Discourse.Post.create( + _.merge( + { + id: 1, + can_delete: true, + version: 1 + }, + args || {} + ) + ); }; -QUnit.test('defaults', assert => { - var post = Discourse.Post.create({id: 1}); - assert.blank(post.get('deleted_at'), "it has no deleted_at by default"); - assert.blank(post.get('deleted_by'), "there is no deleted_by by default"); +QUnit.test("defaults", assert => { + var post = Discourse.Post.create({ id: 1 }); + assert.blank(post.get("deleted_at"), "it has no deleted_at by default"); + assert.blank(post.get("deleted_by"), "there is no deleted_by by default"); }); -QUnit.test('new_user', assert => { - var post = Discourse.Post.create({trust_level: 0}); - assert.ok(post.get('new_user'), "post is from a new user"); +QUnit.test("new_user", assert => { + var post = Discourse.Post.create({ trust_level: 0 }); + assert.ok(post.get("new_user"), "post is from a new user"); - post.set('trust_level', 1); - assert.ok(!post.get('new_user'), "post is no longer from a new user"); + post.set("trust_level", 1); + assert.ok(!post.get("new_user"), "post is no longer from a new user"); }); -QUnit.test('firstPost', assert => { - var post = Discourse.Post.create({post_number: 1}); - assert.ok(post.get('firstPost'), "it's the first post"); +QUnit.test("firstPost", assert => { + var post = Discourse.Post.create({ post_number: 1 }); + assert.ok(post.get("firstPost"), "it's the first post"); - post.set('post_number', 10); - assert.ok(!post.get('firstPost'), "post is no longer the first post"); + post.set("post_number", 10); + assert.ok(!post.get("firstPost"), "post is no longer the first post"); }); -QUnit.test('updateFromPost', assert => { +QUnit.test("updateFromPost", assert => { var post = Discourse.Post.create({ post_number: 1, - raw: 'hello world' + raw: "hello world" }); - post.updateFromPost(Discourse.Post.create({ - raw: 'different raw', - wat: function() { return 123; } - })); + post.updateFromPost( + Discourse.Post.create({ + raw: "different raw", + wat: function() { + return 123; + } + }) + ); - assert.equal(post.get('raw'), "different raw", "raw field updated"); + assert.equal(post.get("raw"), "different raw", "raw field updated"); }); -QUnit.test('destroy by staff', assert => { - var user = Discourse.User.create({username: 'staff', staff: true}), - post = buildPost({user: user}); +QUnit.test("destroy by staff", assert => { + var user = Discourse.User.create({ username: "staff", staff: true }), + post = buildPost({ user: user }); post.destroy(user); - assert.present(post.get('deleted_at'), "it has a `deleted_at` field."); - assert.equal(post.get('deleted_by'), user, "it has the user in the `deleted_by` field"); + assert.present(post.get("deleted_at"), "it has a `deleted_at` field."); + assert.equal( + post.get("deleted_by"), + user, + "it has the user in the `deleted_by` field" + ); post.recover(); - assert.blank(post.get('deleted_at'), "it clears `deleted_at` when recovering"); - assert.blank(post.get('deleted_by'), "it clears `deleted_by` when recovering"); - + assert.blank( + post.get("deleted_at"), + "it clears `deleted_at` when recovering" + ); + assert.blank( + post.get("deleted_by"), + "it clears `deleted_by` when recovering" + ); }); -QUnit.test('destroy by non-staff', assert => { +QUnit.test("destroy by non-staff", assert => { var originalCooked = "this is the original cooked value", - user = Discourse.User.create({username: 'evil trout'}), - post = buildPost({user: user, cooked: originalCooked}); + user = Discourse.User.create({ username: "evil trout" }), + post = buildPost({ user: user, cooked: originalCooked }); return post.destroy(user).then(() => { - assert.ok(!post.get('can_delete'), "the post can't be deleted again in this session"); - assert.ok(post.get('cooked') !== originalCooked, "the cooked content changed"); - assert.equal(post.get('version'), 2, "the version number increased"); + assert.ok( + !post.get("can_delete"), + "the post can't be deleted again in this session" + ); + assert.ok( + post.get("cooked") !== originalCooked, + "the cooked content changed" + ); + assert.equal(post.get("version"), 2, "the version number increased"); }); }); diff --git a/test/javascripts/models/report-test.js.es6 b/test/javascripts/models/report-test.js.es6 index 6af13105731..96b05d94b76 100644 --- a/test/javascripts/models/report-test.js.es6 +++ b/test/javascripts/models/report-test.js.es6 @@ -6,7 +6,12 @@ function reportWithData(data) { return Report.create({ type: "topics", data: _.map(data, (val, index) => { - return { x: moment().subtract(index, "days").format("YYYY-MM-DD"), y: val }; + return { + x: moment() + .subtract(index, "days") + .format("YYYY-MM-DD"), + y: val + }; }) }); } @@ -16,11 +21,23 @@ QUnit.test("counts", assert => { assert.equal(report.get("todayCount"), 5); assert.equal(report.get("yesterdayCount"), 4); - assert.equal(report.valueFor(2, 4), 6, "adds the values for the given range of days, inclusive"); - assert.equal(report.get("lastSevenDaysCount"), 307, "sums 7 days excluding today"); + assert.equal( + report.valueFor(2, 4), + 6, + "adds the values for the given range of days, inclusive" + ); + assert.equal( + report.get("lastSevenDaysCount"), + 307, + "sums 7 days excluding today" + ); report.set("method", "average"); - assert.equal(report.valueFor(2, 4), 2, "averages the values for the given range of days"); + assert.equal( + report.valueFor(2, 4), + 2, + "averages the values for the given range of days" + ); }); QUnit.test("percentChangeString", assert => { @@ -29,32 +46,55 @@ QUnit.test("percentChangeString", assert => { assert.equal(report.percentChangeString(5, 8), "+60%", "value increased"); assert.equal(report.percentChangeString(8, 2), "-75%", "value decreased"); assert.equal(report.percentChangeString(8, 8), "0%", "value unchanged"); - assert.blank(report.percentChangeString(0, 8), "returns blank when previous value was 0"); + assert.blank( + report.percentChangeString(0, 8), + "returns blank when previous value was 0" + ); assert.equal(report.percentChangeString(8, 0), "-100%", "yesterday was 0"); - assert.blank(report.percentChangeString(0, 0), "returns blank when both were 0"); + assert.blank( + report.percentChangeString(0, 0), + "returns blank when both were 0" + ); }); QUnit.test("yesterdayCountTitle with valid values", assert => { - const title = reportWithData([6,8,5,2,1]).get("yesterdayCountTitle"); + const title = reportWithData([6, 8, 5, 2, 1]).get("yesterdayCountTitle"); assert.ok(title.indexOf("+60%") !== -1); assert.ok(title.match(/Was 5/)); }); QUnit.test("yesterdayCountTitle when two days ago was 0", assert => { - const title = reportWithData([6,8,0,2,1]).get("yesterdayCountTitle"); + const title = reportWithData([6, 8, 0, 2, 1]).get("yesterdayCountTitle"); assert.equal(title.indexOf("%"), -1); assert.ok(title.match(/Was 0/)); }); - QUnit.test("sevenDaysCountTitle", assert => { - const title = reportWithData([100,1,1,1,1,1,1,1,2,2,2,2,2,2,2,100,100]).get("sevenDaysCountTitle"); + const title = reportWithData([ + 100, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 100, + 100 + ]).get("sevenDaysCountTitle"); assert.ok(title.match(/-50%/)); assert.ok(title.match(/Was 14/)); }); QUnit.test("thirtyDaysCountTitle", assert => { - const report = reportWithData([5,5,5,5]); + const report = reportWithData([5, 5, 5, 5]); report.set("prev30Days", 10); const title = report.get("thirtyDaysCountTitle"); @@ -66,23 +106,23 @@ QUnit.test("sevenDaysTrend", assert => { let report; let trend; - report = reportWithData([0, 1,1,1,1,1,1,1, 1,1,1,1,1,1,1]); + report = reportWithData([0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); trend = report.get("sevenDaysTrend"); assert.ok(trend === "no-change"); - report = reportWithData([0, 1,1,1,1,1,1,1, 0,0,0,0,0,0,0]); + report = reportWithData([0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]); trend = report.get("sevenDaysTrend"); assert.ok(trend === "high-trending-up"); - report = reportWithData([0, 1,1,1,1,1,1,1, 1,1,1,1,1,1,0]); + report = reportWithData([0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]); trend = report.get("sevenDaysTrend"); assert.ok(trend === "trending-up"); - report = reportWithData([0, 0,0,0,0,0,0,0, 1,1,1,1,1,1,1]); + report = reportWithData([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]); trend = report.get("sevenDaysTrend"); assert.ok(trend === "high-trending-down"); - report = reportWithData([0, 1,1,1,1,1,1,0, 1,1,1,1,1,1,1]); + report = reportWithData([0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1]); trend = report.get("sevenDaysTrend"); assert.ok(trend === "trending-down"); }); @@ -116,27 +156,187 @@ QUnit.test("thirtyDaysTrend", assert => { let report; let trend; - report = reportWithData([0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]); + report = reportWithData([ + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ]); report.set("prev30Days", 30); trend = report.get("thirtyDaysTrend"); assert.ok(trend === "no-change"); - report = reportWithData([0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]); + report = reportWithData([ + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ]); report.set("prev30Days", 0); trend = report.get("thirtyDaysTrend"); assert.ok(trend === "high-trending-up"); - report = reportWithData([0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]); + report = reportWithData([ + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ]); report.set("prev30Days", 25); trend = report.get("thirtyDaysTrend"); assert.ok(trend === "trending-up"); - report = reportWithData([0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]); + report = reportWithData([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ]); report.set("prev30Days", 60); trend = report.get("thirtyDaysTrend"); assert.ok(trend === "high-trending-down"); - report = reportWithData([0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0]); + report = reportWithData([ + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0 + ]); report.set("prev30Days", 35); trend = report.get("thirtyDaysTrend"); assert.ok(trend === "trending-down"); @@ -171,11 +371,11 @@ QUnit.test("small variation (-2/+2% change) is no-change", assert => { let report; let trend; - report = reportWithData([0, 1,1,1,1,1,1,0.9, 1,1,1,1,1,1,1]); + report = reportWithData([0, 1, 1, 1, 1, 1, 1, 0.9, 1, 1, 1, 1, 1, 1, 1]); trend = report.get("sevenDaysTrend"); assert.ok(trend === "no-change"); - report = reportWithData([0, 1,1,1,1,1,1,1.1, 1,1,1,1,1,1,1]); + report = reportWithData([0, 1, 1, 1, 1, 1, 1, 1.1, 1, 1, 1, 1, 1, 1, 1]); trend = report.get("sevenDaysTrend"); assert.ok(trend === "no-change"); }); diff --git a/test/javascripts/models/rest-model-test.js.es6 b/test/javascripts/models/rest-model-test.js.es6 index 031cb3b0c25..a19578a22ba 100644 --- a/test/javascripts/models/rest-model-test.js.es6 +++ b/test/javascripts/models/rest-model-test.js.es6 @@ -1,9 +1,9 @@ -QUnit.module('rest-model'); +QUnit.module("rest-model"); -import createStore from 'helpers/create-store'; -import RestModel from 'discourse/models/rest'; +import createStore from "helpers/create-store"; +import RestModel from "discourse/models/rest"; -QUnit.test('munging', assert => { +QUnit.test("munging", assert => { const store = createStore(); const Grape = RestModel.extend(); Grape.reopenClass({ @@ -14,82 +14,81 @@ QUnit.test('munging', assert => { }); var g = Grape.create({ store, percent: 0.4 }); - assert.equal(g.get('inverse'), 0.6, 'it runs `munge` on `create`'); + assert.equal(g.get("inverse"), 0.6, "it runs `munge` on `create`"); }); -QUnit.test('update', assert => { +QUnit.test("update", assert => { const store = createStore(); - return store.find('widget', 123).then(function(widget) { - assert.equal(widget.get('name'), 'Trout Lure'); + return store.find("widget", 123).then(function(widget) { + assert.equal(widget.get("name"), "Trout Lure"); - assert.ok(!widget.get('isSaving')); - const promise = widget.update({ name: 'new name' }); - assert.ok(widget.get('isSaving')); + assert.ok(!widget.get("isSaving")); + const promise = widget.update({ name: "new name" }); + assert.ok(widget.get("isSaving")); promise.then(function() { - assert.ok(!widget.get('isSaving')); - assert.equal(widget.get('name'), 'new name'); + assert.ok(!widget.get("isSaving")); + assert.equal(widget.get("name"), "new name"); }); }); }); -QUnit.test('updating simultaneously', assert => { +QUnit.test("updating simultaneously", assert => { assert.expect(2); const store = createStore(); - return store.find('widget', 123).then(function(widget) { - - const firstPromise = widget.update({ name: 'new name' }); - const secondPromise = widget.update({ name: 'new name' }); + return store.find("widget", 123).then(function(widget) { + const firstPromise = widget.update({ name: "new name" }); + const secondPromise = widget.update({ name: "new name" }); firstPromise.then(function() { - assert.ok(true, 'the first promise succeeeds'); + assert.ok(true, "the first promise succeeeds"); }); secondPromise.catch(function() { - assert.ok(true, 'the second promise fails'); + assert.ok(true, "the second promise fails"); }); }); }); -QUnit.test('save new', assert => { +QUnit.test("save new", assert => { const store = createStore(); - const widget = store.createRecord('widget'); + const widget = store.createRecord("widget"); - assert.ok(widget.get('isNew'), 'it is a new record'); - assert.ok(!widget.get('isCreated'), 'it is not created'); - assert.ok(!widget.get('isSaving')); + assert.ok(widget.get("isNew"), "it is a new record"); + assert.ok(!widget.get("isCreated"), "it is not created"); + assert.ok(!widget.get("isSaving")); - const promise = widget.save({ name: 'Evil Widget' }); - assert.ok(widget.get('isSaving')); + const promise = widget.save({ name: "Evil Widget" }); + assert.ok(widget.get("isSaving")); return promise.then(function() { - assert.ok(!widget.get('isSaving')); - assert.ok(widget.get('id'), 'it has an id'); - assert.ok(widget.get('name'), 'Evil Widget'); - assert.ok(widget.get('isCreated'), 'it is created'); - assert.ok(!widget.get('isNew'), 'it is no longer new'); + assert.ok(!widget.get("isSaving")); + assert.ok(widget.get("id"), "it has an id"); + assert.ok(widget.get("name"), "Evil Widget"); + assert.ok(widget.get("isCreated"), "it is created"); + assert.ok(!widget.get("isNew"), "it is no longer new"); }); }); -QUnit.test('creating simultaneously', assert => { +QUnit.test("creating simultaneously", assert => { assert.expect(2); const store = createStore(); - const widget = store.createRecord('widget'); + const widget = store.createRecord("widget"); - const firstPromise = widget.save({ name: 'Evil Widget' }); - const secondPromise = widget.save({ name: 'Evil Widget' }); + const firstPromise = widget.save({ name: "Evil Widget" }); + const secondPromise = widget.save({ name: "Evil Widget" }); firstPromise.then(function() { - assert.ok(true, 'the first promise succeeeds'); + assert.ok(true, "the first promise succeeeds"); }); secondPromise.catch(function() { - assert.ok(true, 'the second promise fails'); + assert.ok(true, "the second promise fails"); }); }); -QUnit.test('destroyRecord', assert => { +QUnit.test("destroyRecord", assert => { const store = createStore(); - return store.find('widget', 123).then(function(widget) { + return store.find("widget", 123).then(function(widget) { widget.destroyRecord().then(function(result) { assert.ok(result); }); diff --git a/test/javascripts/models/result-set-test.js.es6 b/test/javascripts/models/result-set-test.js.es6 index 2912b8ddb80..1f819b317e5 100644 --- a/test/javascripts/models/result-set-test.js.es6 +++ b/test/javascripts/models/result-set-test.js.es6 @@ -1,49 +1,53 @@ -QUnit.module('result-set'); +QUnit.module("result-set"); -import ResultSet from 'discourse/models/result-set'; -import createStore from 'helpers/create-store'; +import ResultSet from "discourse/models/result-set"; +import createStore from "helpers/create-store"; -QUnit.test('defaults', assert => { +QUnit.test("defaults", assert => { const rs = ResultSet.create({ content: [] }); - assert.equal(rs.get('length'), 0); - assert.equal(rs.get('totalRows'), 0); - assert.ok(!rs.get('loadMoreUrl')); - assert.ok(!rs.get('loading')); - assert.ok(!rs.get('loadingMore')); - assert.ok(!rs.get('refreshing')); + assert.equal(rs.get("length"), 0); + assert.equal(rs.get("totalRows"), 0); + assert.ok(!rs.get("loadMoreUrl")); + assert.ok(!rs.get("loading")); + assert.ok(!rs.get("loadingMore")); + assert.ok(!rs.get("refreshing")); }); -QUnit.test('pagination support', assert => { +QUnit.test("pagination support", assert => { const store = createStore(); - return store.findAll('widget').then(function(rs) { - assert.equal(rs.get('length'), 2); - assert.equal(rs.get('totalRows'), 4); - assert.ok(rs.get('loadMoreUrl'), 'has a url to load more'); - assert.ok(!rs.get('loadingMore'), 'it is not loading more'); - assert.ok(rs.get('canLoadMore')); + return store.findAll("widget").then(function(rs) { + assert.equal(rs.get("length"), 2); + assert.equal(rs.get("totalRows"), 4); + assert.ok(rs.get("loadMoreUrl"), "has a url to load more"); + assert.ok(!rs.get("loadingMore"), "it is not loading more"); + assert.ok(rs.get("canLoadMore")); const promise = rs.loadMore(); - assert.ok(rs.get('loadingMore'), 'it is loading more'); + assert.ok(rs.get("loadingMore"), "it is loading more"); promise.then(function() { - assert.ok(!rs.get('loadingMore'), 'it finished loading more'); - assert.equal(rs.get('length'), 4); - assert.ok(!rs.get('loadMoreUrl')); - assert.ok(!rs.get('canLoadMore')); + assert.ok(!rs.get("loadingMore"), "it finished loading more"); + assert.equal(rs.get("length"), 4); + assert.ok(!rs.get("loadMoreUrl")); + assert.ok(!rs.get("canLoadMore")); }); }); }); -QUnit.test('refresh support', assert => { +QUnit.test("refresh support", assert => { const store = createStore(); - return store.findAll('widget').then(function(rs) { - assert.equal(rs.get('refreshUrl'), '/widgets?refresh=true', 'it has the refresh url'); + return store.findAll("widget").then(function(rs) { + assert.equal( + rs.get("refreshUrl"), + "/widgets?refresh=true", + "it has the refresh url" + ); const promise = rs.refresh(); - assert.ok(rs.get('refreshing'), 'it is refreshing'); + assert.ok(rs.get("refreshing"), "it is refreshing"); promise.then(function() { - assert.ok(!rs.get('refreshing'), 'it is finished refreshing'); + assert.ok(!rs.get("refreshing"), "it is finished refreshing"); }); }); -}); \ No newline at end of file +}); diff --git a/test/javascripts/models/session-test.js.es6 b/test/javascripts/models/session-test.js.es6 index ff330fd77fe..2208c91a011 100644 --- a/test/javascripts/models/session-test.js.es6 +++ b/test/javascripts/models/session-test.js.es6 @@ -2,7 +2,11 @@ import Session from "discourse/models/session"; QUnit.module("model:session"); -QUnit.test('highestSeenByTopic', assert => { +QUnit.test("highestSeenByTopic", assert => { const session = Session.current(); - assert.deepEqual(session.get('highestSeenByTopic'), {}, "by default it returns an empty object"); -}); \ No newline at end of file + assert.deepEqual( + session.get("highestSeenByTopic"), + {}, + "by default it returns an empty object" + ); +}); diff --git a/test/javascripts/models/site-test.js.es6 b/test/javascripts/models/site-test.js.es6 index bce69122dc6..d240abe45ba 100644 --- a/test/javascripts/models/site-test.js.es6 +++ b/test/javascripts/models/site-test.js.es6 @@ -1,48 +1,69 @@ -import createStore from 'helpers/create-store'; +import createStore from "helpers/create-store"; QUnit.module("model:site"); -QUnit.test('create', assert => { - assert.ok(Discourse.Site.create(), 'it can create with no parameters'); +QUnit.test("create", assert => { + assert.ok(Discourse.Site.create(), "it can create with no parameters"); }); -QUnit.test('instance', assert => { +QUnit.test("instance", assert => { const site = Discourse.Site.current(); assert.present(site, "We have a current site singleton"); - assert.present(site.get('categories'), "The instance has a list of categories"); - assert.present(site.get('flagTypes'), "The instance has a list of flag types"); - assert.present(site.get('trustLevels'), "The instance has a list of trust levels"); - + assert.present( + site.get("categories"), + "The instance has a list of categories" + ); + assert.present( + site.get("flagTypes"), + "The instance has a list of flag types" + ); + assert.present( + site.get("trustLevels"), + "The instance has a list of trust levels" + ); }); -QUnit.test('create categories', assert => { +QUnit.test("create categories", assert => { const store = createStore(); - const site = store.createRecord('site', { - categories: [{ id: 1234, name: 'Test'}, - { id: 3456, name: 'Test Subcategory', parent_category_id: 1234}, - { id: 3458, name: 'Invalid Subcategory', parent_category_id: 6666}] + const site = store.createRecord("site", { + categories: [ + { id: 1234, name: "Test" }, + { id: 3456, name: "Test Subcategory", parent_category_id: 1234 }, + { id: 3458, name: "Invalid Subcategory", parent_category_id: 6666 } + ] }); - const categories = site.get('categories'); - site.get('sortedCategories'); + const categories = site.get("categories"); + site.get("sortedCategories"); assert.present(categories, "The categories are present"); assert.equal(categories.length, 3, "it loaded all three categories"); - const parent = categories.findBy('id', 1234); + const parent = categories.findBy("id", 1234); assert.present(parent, "it loaded the parent category"); - assert.blank(parent.get('parentCategory'), 'it has no parent category'); + assert.blank(parent.get("parentCategory"), "it has no parent category"); - const subcategory = categories.findBy('id', 3456); + const subcategory = categories.findBy("id", 3456); assert.present(subcategory, "it loaded the subcategory"); - assert.equal(subcategory.get('parentCategory'), parent, "it has associated the child with the parent"); + assert.equal( + subcategory.get("parentCategory"), + parent, + "it has associated the child with the parent" + ); // remove invalid category and child categories.removeObject(categories[2]); categories.removeObject(categories[1]); - assert.equal(categories.length, site.get('categoriesByCount').length, "categories by count should change on removal"); - assert.equal(categories.length, site.get('sortedCategories').length, "sorted categories should change on removal"); - -}); \ No newline at end of file + assert.equal( + categories.length, + site.get("categoriesByCount").length, + "categories by count should change on removal" + ); + assert.equal( + categories.length, + site.get("sortedCategories").length, + "sorted categories should change on removal" + ); +}); diff --git a/test/javascripts/models/staff-action-log-test.js.es6 b/test/javascripts/models/staff-action-log-test.js.es6 index 2e9756ff9e7..22ef0192dba 100644 --- a/test/javascripts/models/staff-action-log-test.js.es6 +++ b/test/javascripts/models/staff-action-log-test.js.es6 @@ -1,7 +1,7 @@ -import StaffActionLog from 'admin/models/staff-action-log'; +import StaffActionLog from "admin/models/staff-action-log"; QUnit.module("StaffActionLog"); QUnit.test("create", assert => { assert.ok(StaffActionLog.create(), "it can be created without arguments"); -}); \ No newline at end of file +}); diff --git a/test/javascripts/models/store-test.js.es6 b/test/javascripts/models/store-test.js.es6 index e62d77dc4e0..a9ce8e2c59c 100644 --- a/test/javascripts/models/store-test.js.es6 +++ b/test/javascripts/models/store-test.js.es6 @@ -1,169 +1,190 @@ -QUnit.module('service:store'); +QUnit.module("service:store"); -import createStore from 'helpers/create-store'; +import createStore from "helpers/create-store"; -QUnit.test('createRecord', assert => { +QUnit.test("createRecord", assert => { const store = createStore(); - const widget = store.createRecord('widget', {id: 111, name: 'hello'}); + const widget = store.createRecord("widget", { id: 111, name: "hello" }); - assert.ok(!widget.get('isNew'), 'it is not a new record'); - assert.equal(widget.get('name'), 'hello'); - assert.equal(widget.get('id'), 111); + assert.ok(!widget.get("isNew"), "it is not a new record"); + assert.equal(widget.get("name"), "hello"); + assert.equal(widget.get("id"), 111); }); -QUnit.test('createRecord without an `id`', assert => { +QUnit.test("createRecord without an `id`", assert => { const store = createStore(); - const widget = store.createRecord('widget', {name: 'hello'}); + const widget = store.createRecord("widget", { name: "hello" }); - assert.ok(widget.get('isNew'), 'it is a new record'); - assert.ok(!widget.get('id'), 'there is no id'); + assert.ok(widget.get("isNew"), "it is a new record"); + assert.ok(!widget.get("id"), "there is no id"); }); QUnit.test("createRecord doesn't modify the input `id` field", assert => { const store = createStore(); - const widget = store.createRecord('widget', {id: 1, name: 'hello'}); + const widget = store.createRecord("widget", { id: 1, name: "hello" }); - const obj = { id: 1, name: 'something' }; + const obj = { id: 1, name: "something" }; - const other = store.createRecord('widget', obj); - assert.equal(widget, other, 'returns the same record'); - assert.equal(widget.name, 'something', 'it updates the properties'); - assert.equal(obj.id, 1, 'it does not remove the id from the input'); + const other = store.createRecord("widget", obj); + assert.equal(widget, other, "returns the same record"); + assert.equal(widget.name, "something", "it updates the properties"); + assert.equal(obj.id, 1, "it does not remove the id from the input"); }); -QUnit.test('createRecord without attributes', assert => { +QUnit.test("createRecord without attributes", assert => { const store = createStore(); - const widget = store.createRecord('widget'); + const widget = store.createRecord("widget"); - assert.ok(!widget.get('id'), 'there is no id'); - assert.ok(widget.get('isNew'), 'it is a new record'); + assert.ok(!widget.get("id"), "there is no id"); + assert.ok(widget.get("isNew"), "it is a new record"); }); -QUnit.test('createRecord with a record as attributes returns that record from the map', assert => { - const store = createStore(); - const widget = store.createRecord('widget', {id: 33}); - const secondWidget = store.createRecord('widget', {id: 33}); +QUnit.test( + "createRecord with a record as attributes returns that record from the map", + assert => { + const store = createStore(); + const widget = store.createRecord("widget", { id: 33 }); + const secondWidget = store.createRecord("widget", { id: 33 }); - assert.equal(widget, secondWidget, 'they should be the same'); -}); + assert.equal(widget, secondWidget, "they should be the same"); + } +); -QUnit.test('find', assert => { +QUnit.test("find", assert => { const store = createStore(); - return store.find('widget', 123).then(function(w) { - assert.equal(w.get('name'), 'Trout Lure'); - assert.equal(w.get('id'), 123); - assert.ok(!w.get('isNew'), 'found records are not new'); - assert.equal(w.get('extras.hello'), 'world', "extra attributes are set"); + return store.find("widget", 123).then(function(w) { + assert.equal(w.get("name"), "Trout Lure"); + assert.equal(w.get("id"), 123); + assert.ok(!w.get("isNew"), "found records are not new"); + assert.equal(w.get("extras.hello"), "world", "extra attributes are set"); // A second find by id returns the same object - store.find('widget', 123).then(function(w2) { + store.find("widget", 123).then(function(w2) { assert.equal(w, w2); - assert.equal(w.get('extras.hello'), 'world', "extra attributes are set"); + assert.equal(w.get("extras.hello"), "world", "extra attributes are set"); }); }); }); -QUnit.test('find with object id', assert => { +QUnit.test("find with object id", assert => { const store = createStore(); - return store.find('widget', {id: 123}).then(function(w) { - assert.equal(w.get('firstObject.name'), 'Trout Lure'); + return store.find("widget", { id: 123 }).then(function(w) { + assert.equal(w.get("firstObject.name"), "Trout Lure"); }); }); -QUnit.test('find with query param', assert => { +QUnit.test("find with query param", assert => { const store = createStore(); - return store.find('widget', {name: 'Trout Lure'}).then(function(w) { - assert.equal(w.get('firstObject.id'), 123); + return store.find("widget", { name: "Trout Lure" }).then(function(w) { + assert.equal(w.get("firstObject.id"), 123); }); }); -QUnit.test('findStale with no stale results', (assert) => { +QUnit.test("findStale with no stale results", assert => { const store = createStore(); - const stale = store.findStale('widget', {name: 'Trout Lure'}); + const stale = store.findStale("widget", { name: "Trout Lure" }); - assert.ok(!stale.hasResults, 'there are no stale results'); - assert.ok(!stale.results, 'results are present'); + assert.ok(!stale.hasResults, "there are no stale results"); + assert.ok(!stale.results, "results are present"); return stale.refresh().then(function(w) { - assert.equal(w.get('firstObject.id'), 123, 'a `refresh()` method provides results for stale'); + assert.equal( + w.get("firstObject.id"), + 123, + "a `refresh()` method provides results for stale" + ); }); }); -QUnit.test('update', assert => { +QUnit.test("update", assert => { const store = createStore(); - return store.update('widget', 123, {name: 'hello'}).then(function(result) { + return store.update("widget", 123, { name: "hello" }).then(function(result) { assert.ok(result); }); }); -QUnit.test('update with a multi world name', function(assert) { +QUnit.test("update with a multi world name", function(assert) { const store = createStore(); - return store.update('cool-thing', 123, {name: 'hello'}).then(function(result) { - assert.ok(result); - assert.equal(result.payload.name, 'hello'); + return store + .update("cool-thing", 123, { name: "hello" }) + .then(function(result) { + assert.ok(result); + assert.equal(result.payload.name, "hello"); + }); +}); + +QUnit.test("findAll", assert => { + const store = createStore(); + return store.findAll("widget").then(function(result) { + assert.equal(result.get("length"), 2); + const w = result.findBy("id", 124); + assert.ok(!w.get("isNew"), "found records are not new"); + assert.equal(w.get("name"), "Evil Repellant"); }); }); -QUnit.test('findAll', assert => { +QUnit.test("destroyRecord", function(assert) { const store = createStore(); - return store.findAll('widget').then(function(result) { - assert.equal(result.get('length'), 2); - const w = result.findBy('id', 124); - assert.ok(!w.get('isNew'), 'found records are not new'); - assert.equal(w.get('name'), 'Evil Repellant'); - }); -}); - -QUnit.test('destroyRecord', function(assert) { - const store = createStore(); - return store.find('widget', 123).then(function(w) { - store.destroyRecord('widget', w).then(function(result) { + return store.find("widget", 123).then(function(w) { + store.destroyRecord("widget", w).then(function(result) { assert.ok(result); }); }); }); -QUnit.test('destroyRecord when new', function(assert) { +QUnit.test("destroyRecord when new", function(assert) { const store = createStore(); - const w = store.createRecord('widget', {name: 'hello'}); - store.destroyRecord('widget', w).then(function(result) { + const w = store.createRecord("widget", { name: "hello" }); + store.destroyRecord("widget", w).then(function(result) { assert.ok(result); }); }); -QUnit.test('find embedded', function(assert) { +QUnit.test("find embedded", function(assert) { const store = createStore(); - return store.find('fruit', 2).then(function(f) { - assert.ok(f.get('farmer'), 'it has the embedded object'); + return store.find("fruit", 2).then(function(f) { + assert.ok(f.get("farmer"), "it has the embedded object"); - const fruitCols = f.get('colors'); + const fruitCols = f.get("colors"); assert.equal(fruitCols.length, 2); - assert.equal(fruitCols[0].get('id'), 1); - assert.equal(fruitCols[1].get('id'), 2); + assert.equal(fruitCols[0].get("id"), 1); + assert.equal(fruitCols[1].get("id"), 2); - assert.ok(f.get('category'), 'categories are found automatically'); + assert.ok(f.get("category"), "categories are found automatically"); }); }); -QUnit.test('meta types', function(assert) { +QUnit.test("meta types", function(assert) { const store = createStore(); - return store.find('barn', 1).then(function(f) { - assert.equal(f.get('owner.name'), 'Old MacDonald', 'it has the embedded farmer'); + return store.find("barn", 1).then(function(f) { + assert.equal( + f.get("owner.name"), + "Old MacDonald", + "it has the embedded farmer" + ); }); }); -QUnit.test('findAll embedded', function(assert) { +QUnit.test("findAll embedded", function(assert) { const store = createStore(); - return store.findAll('fruit').then(function(fruits) { - assert.equal(fruits.objectAt(0).get('farmer.name'), 'Old MacDonald'); - assert.equal(fruits.objectAt(0).get('farmer'), fruits.objectAt(1).get('farmer'), 'points at the same object'); - assert.equal(fruits.get('extras.hello'), 'world', 'it can supply extra information'); + return store.findAll("fruit").then(function(fruits) { + assert.equal(fruits.objectAt(0).get("farmer.name"), "Old MacDonald"); + assert.equal( + fruits.objectAt(0).get("farmer"), + fruits.objectAt(1).get("farmer"), + "points at the same object" + ); + assert.equal( + fruits.get("extras.hello"), + "world", + "it can supply extra information" + ); - const fruitCols = fruits.objectAt(0).get('colors'); + const fruitCols = fruits.objectAt(0).get("colors"); assert.equal(fruitCols.length, 2); - assert.equal(fruitCols[0].get('id'), 1); - assert.equal(fruitCols[1].get('id'), 2); + assert.equal(fruitCols[0].get("id"), 1); + assert.equal(fruitCols[1].get("id"), 2); - assert.equal(fruits.objectAt(2).get('farmer.name'), 'Luke Skywalker'); + assert.equal(fruits.objectAt(2).get("farmer.name"), "Luke Skywalker"); }); }); diff --git a/test/javascripts/models/topic-details-test.js.es6 b/test/javascripts/models/topic-details-test.js.es6 index bc004748a13..35534faf696 100644 --- a/test/javascripts/models/topic-details-test.js.es6 +++ b/test/javascripts/models/topic-details-test.js.es6 @@ -1,26 +1,29 @@ QUnit.module("model:topic-details"); -import Topic from 'discourse/models/topic'; +import Topic from "discourse/models/topic"; var buildDetails = function(id) { - var topic = Topic.create({id: id}); - return topic.get('details'); + var topic = Topic.create({ id: id }); + return topic.get("details"); }; -QUnit.test('defaults', assert => { +QUnit.test("defaults", assert => { var details = buildDetails(1234); assert.present(details, "the details are present by default"); - assert.ok(!details.get('loaded'), "details are not loaded by default"); + assert.ok(!details.get("loaded"), "details are not loaded by default"); }); -QUnit.test('updateFromJson', assert => { +QUnit.test("updateFromJson", assert => { var details = buildDetails(1234); details.updateFromJson({ - allowed_users: [{username: 'eviltrout'}] + allowed_users: [{ username: "eviltrout" }] }); - assert.equal(details.get('allowed_users.length'), 1, 'it loaded the allowed users'); - assert.containsInstance(details.get('allowed_users'), Discourse.User); - + assert.equal( + details.get("allowed_users.length"), + 1, + "it loaded the allowed users" + ); + assert.containsInstance(details.get("allowed_users"), Discourse.User); }); diff --git a/test/javascripts/models/topic-test.js.es6 b/test/javascripts/models/topic-test.js.es6 index 7c33bceff03..de606ef6b38 100644 --- a/test/javascripts/models/topic-test.js.es6 +++ b/test/javascripts/models/topic-test.js.es6 @@ -1,58 +1,75 @@ -import { IMAGE_VERSION as v } from 'pretty-text/emoji'; +import { IMAGE_VERSION as v } from "pretty-text/emoji"; QUnit.module("model:topic"); -import Topic from 'discourse/models/topic'; +import Topic from "discourse/models/topic"; QUnit.test("defaults", assert => { const topic = Topic.create({ id: 1234 }); - assert.blank(topic.get('deleted_at'), 'deleted_at defaults to blank'); - assert.blank(topic.get('deleted_by'), 'deleted_by defaults to blank'); + assert.blank(topic.get("deleted_at"), "deleted_at defaults to blank"); + assert.blank(topic.get("deleted_by"), "deleted_by defaults to blank"); }); QUnit.test("visited", assert => { - const topic = Topic.create({ highest_post_number: 2, last_read_post_number: 1 }); + const topic = Topic.create({ + highest_post_number: 2, + last_read_post_number: 1 + }); - assert.not(topic.get("visited"), "not visited unless we've read all the posts"); + assert.not( + topic.get("visited"), + "not visited unless we've read all the posts" + ); topic.set("last_read_post_number", 2); assert.ok(topic.get("visited"), "is visited once we've read all the posts"); topic.set("last_read_post_number", 3); - assert.ok(topic.get("visited"), "is visited if we've read all the posts and some are deleted at the end"); + assert.ok( + topic.get("visited"), + "is visited if we've read all the posts and some are deleted at the end" + ); }); -QUnit.test('has details', assert => { +QUnit.test("has details", assert => { const topic = Topic.create({ id: 1234 }); - const topicDetails = topic.get('details'); + const topicDetails = topic.get("details"); assert.present(topicDetails, "a topic has topicDetails after we create it"); - assert.equal(topicDetails.get('topic'), topic, "the topicDetails has a reference back to the topic"); + assert.equal( + topicDetails.get("topic"), + topic, + "the topicDetails has a reference back to the topic" + ); }); -QUnit.test('has a postStream', assert => { +QUnit.test("has a postStream", assert => { const topic = Topic.create({ id: 1234 }); - const postStream = topic.get('postStream'); + const postStream = topic.get("postStream"); assert.present(postStream, "a topic has a postStream after we create it"); - assert.equal(postStream.get('topic'), topic, "the postStream has a reference back to the topic"); + assert.equal( + postStream.get("topic"), + topic, + "the postStream has a reference back to the topic" + ); }); -QUnit.test('has suggestedTopics', assert => { +QUnit.test("has suggestedTopics", assert => { const topic = Topic.create({ suggested_topics: [{ id: 1 }, { id: 2 }] }); - const suggestedTopics = topic.get('suggestedTopics'); + const suggestedTopics = topic.get("suggestedTopics"); - assert.equal(suggestedTopics.length, 2, 'it loaded the suggested_topics'); + assert.equal(suggestedTopics.length, 2, "it loaded the suggested_topics"); assert.containsInstance(suggestedTopics, Topic); }); -QUnit.test('category relationship', assert => { +QUnit.test("category relationship", assert => { // It finds the category by id const category = Discourse.Category.list()[0]; - const topic = Topic.create({ id: 1111, category_id: category.get('id') }); + const topic = Topic.create({ id: 1111, category_id: category.get("id") }); - assert.equal(topic.get('category'), category); + assert.equal(topic.get("category"), category); }); QUnit.test("updateFromJson", assert => { @@ -60,57 +77,78 @@ QUnit.test("updateFromJson", assert => { const category = Discourse.Category.list()[0]; topic.updateFromJson({ - post_stream: [1,2,3], - details: { hello: 'world' }, - cool: 'property', - category_id: category.get('id') + post_stream: [1, 2, 3], + details: { hello: "world" }, + cool: "property", + category_id: category.get("id") }); - assert.blank(topic.get('post_stream'), "it does not update post_stream"); - assert.equal(topic.get('details.hello'), 'world', 'it updates the details'); - assert.equal(topic.get('cool'), "property", "it updates other properties"); - assert.equal(topic.get('category'), category); + assert.blank(topic.get("post_stream"), "it does not update post_stream"); + assert.equal(topic.get("details.hello"), "world", "it updates the details"); + assert.equal(topic.get("cool"), "property", "it updates other properties"); + assert.equal(topic.get("category"), category); }); QUnit.test("destroy", assert => { - const user = Discourse.User.create({ username: 'eviltrout' }); + const user = Discourse.User.create({ username: "eviltrout" }); const topic = Topic.create({ id: 1234 }); topic.destroy(user); - assert.present(topic.get('deleted_at'), 'deleted at is set'); - assert.equal(topic.get('deleted_by'), user, 'deleted by is set'); + assert.present(topic.get("deleted_at"), "deleted at is set"); + assert.equal(topic.get("deleted_by"), user, "deleted by is set"); }); QUnit.test("recover", assert => { - const user = Discourse.User.create({ username: 'eviltrout' }); - const topic = Topic.create({ id: 1234, deleted_at: new Date(), deleted_by: user }); + const user = Discourse.User.create({ username: "eviltrout" }); + const topic = Topic.create({ + id: 1234, + deleted_at: new Date(), + deleted_by: user + }); topic.recover(); - assert.blank(topic.get('deleted_at'), "it clears deleted_at"); - assert.blank(topic.get('deleted_by'), "it clears deleted_by"); + assert.blank(topic.get("deleted_at"), "it clears deleted_at"); + assert.blank(topic.get("deleted_by"), "it clears deleted_by"); }); -QUnit.test('fancyTitle', assert => { - const topic = Topic.create({ fancy_title: ":smile: with all :) the emojis :pear::peach:" }); +QUnit.test("fancyTitle", assert => { + const topic = Topic.create({ + fancy_title: ":smile: with all :) the emojis :pear::peach:" + }); - assert.equal(topic.get('fancyTitle'), - `smile with all slight_smile the emojis pearpeach`, - "supports emojis"); + assert.equal( + topic.get("fancyTitle"), + `smile with all slight_smile the emojis pearpeach`, + "supports emojis" + ); }); -QUnit.test('fancyTitle direction', assert => { +QUnit.test("fancyTitle direction", assert => { const rtlTopic = Topic.create({ fancy_title: "هذا اختبار" }); - const ltrTopic = Topic.create({ fancy_title: "This is a test"}); + const ltrTopic = Topic.create({ fancy_title: "This is a test" }); Discourse.SiteSettings.support_mixed_text_direction = true; - assert.equal(rtlTopic.get('fancyTitle'), `هذا اختبار`, "sets the dir-span to rtl"); - assert.equal(ltrTopic.get('fancyTitle'), `This is a test`, "sets the dir-span to ltr"); + assert.equal( + rtlTopic.get("fancyTitle"), + `هذا اختبار`, + "sets the dir-span to rtl" + ); + assert.equal( + ltrTopic.get("fancyTitle"), + `This is a test`, + "sets the dir-span to ltr" + ); }); -QUnit.test('excerpt', assert => { - const topic = Topic.create({ excerpt: "This is a test topic :smile:", pinned: true }); +QUnit.test("excerpt", assert => { + const topic = Topic.create({ + excerpt: "This is a test topic :smile:", + pinned: true + }); - assert.equal(topic.get('escapedExcerpt'), - `This is a test topic smile`, - "supports emojis"); + assert.equal( + topic.get("escapedExcerpt"), + `This is a test topic smile`, + "supports emojis" + ); }); diff --git a/test/javascripts/models/topic-tracking-state-test.js.es6 b/test/javascripts/models/topic-tracking-state-test.js.es6 index 3c184cb7591..5799b4f1167 100644 --- a/test/javascripts/models/topic-tracking-state-test.js.es6 +++ b/test/javascripts/models/topic-tracking-state-test.js.es6 @@ -1,50 +1,92 @@ -import TopicTrackingState from 'discourse/models/topic-tracking-state'; -import createStore from 'helpers/create-store'; +import TopicTrackingState from "discourse/models/topic-tracking-state"; +import createStore from "helpers/create-store"; QUnit.module("model:topic-tracking-state"); -QUnit.test("sync", function (assert) { +QUnit.test("sync", function(assert) { const state = TopicTrackingState.create(); - state.states["t111"] = {last_read_post_number: null}; + state.states["t111"] = { last_read_post_number: null }; state.updateSeen(111, 7); - const list = {topics: [{ - highest_post_number: null, - id: 111, - unread: 10, - new_posts: 10 - }]}; + const list = { + topics: [ + { + highest_post_number: null, + id: 111, + unread: 10, + new_posts: 10 + } + ] + }; state.sync(list, "new"); - assert.equal(list.topics.length, 0, "expect new topic to be removed as it was seen"); + assert.equal( + list.topics.length, + 0, + "expect new topic to be removed as it was seen" + ); }); -QUnit.test("subscribe to category", function(assert){ - +QUnit.test("subscribe to category", function(assert) { const store = createStore(); - const darth = store.createRecord('category', {id: 1, slug: 'darth'}), - luke = store.createRecord('category', {id: 2, slug: 'luke', parentCategory: darth}), + const darth = store.createRecord("category", { id: 1, slug: "darth" }), + luke = store.createRecord("category", { + id: 2, + slug: "luke", + parentCategory: darth + }), categoryList = [darth, luke]; - sandbox.stub(Discourse.Category, 'list').returns(categoryList); - + sandbox.stub(Discourse.Category, "list").returns(categoryList); const state = TopicTrackingState.create(); - state.trackIncoming('c/darth/l/latest'); + state.trackIncoming("c/darth/l/latest"); - state.notify({message_type: 'new_topic', topic_id: 1, payload: {category_id: 2, topic_id: 1}}); - state.notify({message_type: 'new_topic', topic_id: 2, payload: {category_id: 3, topic_id: 2}}); - state.notify({message_type: 'new_topic', topic_id: 3, payload: {category_id: 1, topic_id: 3}}); + state.notify({ + message_type: "new_topic", + topic_id: 1, + payload: { category_id: 2, topic_id: 1 } + }); + state.notify({ + message_type: "new_topic", + topic_id: 2, + payload: { category_id: 3, topic_id: 2 } + }); + state.notify({ + message_type: "new_topic", + topic_id: 3, + payload: { category_id: 1, topic_id: 3 } + }); - assert.equal(state.get("incomingCount"), 2, "expect to properly track incoming for category"); + assert.equal( + state.get("incomingCount"), + 2, + "expect to properly track incoming for category" + ); state.resetTracking(); - state.trackIncoming('c/darth/luke/l/latest'); + state.trackIncoming("c/darth/luke/l/latest"); - state.notify({message_type: 'new_topic', topic_id: 1, payload: {category_id: 2, topic_id: 1}}); - state.notify({message_type: 'new_topic', topic_id: 2, payload: {category_id: 3, topic_id: 2}}); - state.notify({message_type: 'new_topic', topic_id: 3, payload: {category_id: 1, topic_id: 3}}); + state.notify({ + message_type: "new_topic", + topic_id: 1, + payload: { category_id: 2, topic_id: 1 } + }); + state.notify({ + message_type: "new_topic", + topic_id: 2, + payload: { category_id: 3, topic_id: 2 } + }); + state.notify({ + message_type: "new_topic", + topic_id: 3, + payload: { category_id: 1, topic_id: 3 } + }); - assert.equal(state.get("incomingCount"), 1, "expect to properly track incoming for subcategory"); -}); \ No newline at end of file + assert.equal( + state.get("incomingCount"), + 1, + "expect to properly track incoming for subcategory" + ); +}); diff --git a/test/javascripts/models/user-action-test.js.es6 b/test/javascripts/models/user-action-test.js.es6 index 93a988acac7..c39d7d828f0 100644 --- a/test/javascripts/models/user-action-test.js.es6 +++ b/test/javascripts/models/user-action-test.js.es6 @@ -7,12 +7,14 @@ QUnit.test("collapsing likes", assert => { topic_id: 1, user_id: 1, post_number: 1 - }), Discourse.UserAction.create({ + }), + Discourse.UserAction.create({ action_type: Discourse.UserAction.TYPES.edits, topic_id: 2, user_id: 1, post_number: 1 - }), Discourse.UserAction.create({ + }), + Discourse.UserAction.create({ action_type: Discourse.UserAction.TYPES.likes_given, topic_id: 1, user_id: 2, @@ -22,6 +24,6 @@ QUnit.test("collapsing likes", assert => { assert.equal(actions.length, 2); - assert.equal(actions[0].get('children.length'), 1); - assert.equal(actions[0].get('children')[0].items.length, 2); -}); \ No newline at end of file + assert.equal(actions[0].get("children.length"), 1); + assert.equal(actions[0].get("children")[0].items.length, 2); +}); diff --git a/test/javascripts/models/user-badge-test.js.es6 b/test/javascripts/models/user-badge-test.js.es6 index 0794c2f92ca..2a0fdd9d6f7 100644 --- a/test/javascripts/models/user-badge-test.js.es6 +++ b/test/javascripts/models/user-badge-test.js.es6 @@ -1,42 +1,60 @@ -import UserBadge from 'discourse/models/user-badge'; -import badgeFixtures from 'fixtures/user-badges'; +import UserBadge from "discourse/models/user-badge"; +import badgeFixtures from "fixtures/user-badges"; QUnit.module("model:user-badge"); -QUnit.test('createFromJson single', assert => { - const userBadge = UserBadge.createFromJson(badgeFixtures['/user_badges']); +QUnit.test("createFromJson single", assert => { + const userBadge = UserBadge.createFromJson(badgeFixtures["/user_badges"]); assert.ok(!Array.isArray(userBadge), "does not return an array"); - assert.equal(userBadge.get('badge.name'), "Badge 2", "badge reference is set"); - assert.equal(userBadge.get('badge.badge_type.name'), "Silver 2", "badge.badge_type reference is set"); - assert.equal(userBadge.get('granted_by.username'), "anne3", "granted_by reference is set"); + assert.equal( + userBadge.get("badge.name"), + "Badge 2", + "badge reference is set" + ); + assert.equal( + userBadge.get("badge.badge_type.name"), + "Silver 2", + "badge.badge_type reference is set" + ); + assert.equal( + userBadge.get("granted_by.username"), + "anne3", + "granted_by reference is set" + ); }); -QUnit.test('createFromJson array', assert => { - const userBadges = UserBadge.createFromJson(badgeFixtures['/user-badges/:username']); +QUnit.test("createFromJson array", assert => { + const userBadges = UserBadge.createFromJson( + badgeFixtures["/user-badges/:username"] + ); assert.ok(Array.isArray(userBadges), "returns an array"); - assert.equal(userBadges[0].get('granted_by'), null, "granted_by reference is not set when null"); + assert.equal( + userBadges[0].get("granted_by"), + null, + "granted_by reference is not set when null" + ); }); -QUnit.test('findByUsername', assert => { +QUnit.test("findByUsername", assert => { return UserBadge.findByUsername("anne3").then(function(badges) { assert.ok(Array.isArray(badges), "returns an array"); }); }); -QUnit.test('findByBadgeId', assert => { +QUnit.test("findByBadgeId", assert => { return UserBadge.findByBadgeId(880).then(function(badges) { assert.ok(Array.isArray(badges), "returns an array"); }); }); -QUnit.test('grant', assert => { +QUnit.test("grant", assert => { return UserBadge.grant(1, "username").then(function(userBadge) { assert.ok(!Array.isArray(userBadge), "does not return an array"); }); }); -QUnit.test('revoke', assert => { +QUnit.test("revoke", assert => { assert.expect(0); - const userBadge = UserBadge.create({id: 1}); + const userBadge = UserBadge.create({ id: 1 }); return userBadge.revoke(); -}); \ No newline at end of file +}); diff --git a/test/javascripts/models/user-stream-test.js.es6 b/test/javascripts/models/user-stream-test.js.es6 index 272dae3d334..d8be562c7ac 100644 --- a/test/javascripts/models/user-stream-test.js.es6 +++ b/test/javascripts/models/user-stream-test.js.es6 @@ -1,30 +1,31 @@ QUnit.module("Discourse.UserStream"); -QUnit.test('basics', assert =>{ - var user = Discourse.User.create({id: 1, username: 'eviltrout'}); - var stream = user.get('stream'); +QUnit.test("basics", assert => { + var user = Discourse.User.create({ id: 1, username: "eviltrout" }); + var stream = user.get("stream"); assert.present(stream, "a user has a stream by default"); - assert.equal(stream.get('user'), user, "the stream points back to the user"); + assert.equal(stream.get("user"), user, "the stream points back to the user"); - assert.equal(stream.get('itemsLoaded'), 0, "no items are loaded by default"); - assert.blank(stream.get('content'), "no content by default"); - assert.blank(stream.get('filter'), "no filter by default"); + assert.equal(stream.get("itemsLoaded"), 0, "no items are loaded by default"); + assert.blank(stream.get("content"), "no content by default"); + assert.blank(stream.get("filter"), "no filter by default"); - assert.ok(!stream.get('loaded'), "the stream is not loaded by default"); + assert.ok(!stream.get("loaded"), "the stream is not loaded by default"); }); - -QUnit.test('filterParam', assert => { - var user = Discourse.User.create({id: 1, username: 'eviltrout'}); - var stream = user.get('stream'); +QUnit.test("filterParam", assert => { + var user = Discourse.User.create({ id: 1, username: "eviltrout" }); + var stream = user.get("stream"); // defaults to posts/topics - assert.equal(stream.get('filterParam'), "4,5"); + assert.equal(stream.get("filterParam"), "4,5"); - stream.set('filter', Discourse.UserAction.TYPES.likes_given); - assert.equal(stream.get('filterParam'), Discourse.UserAction.TYPES.likes_given); + stream.set("filter", Discourse.UserAction.TYPES.likes_given); + assert.equal( + stream.get("filterParam"), + Discourse.UserAction.TYPES.likes_given + ); - stream.set('filter', Discourse.UserAction.TYPES.replies); - assert.equal(stream.get('filterParam'), '6,9'); - -}); \ No newline at end of file + stream.set("filter", Discourse.UserAction.TYPES.replies); + assert.equal(stream.get("filterParam"), "6,9"); +}); diff --git a/test/javascripts/models/user-test.js.es6 b/test/javascripts/models/user-test.js.es6 index fb379374952..20ef656cf2b 100644 --- a/test/javascripts/models/user-test.js.es6 +++ b/test/javascripts/models/user-test.js.es6 @@ -1,46 +1,68 @@ -import User from 'discourse/models/user'; -import Group from 'discourse/models/group'; +import User from "discourse/models/user"; +import Group from "discourse/models/group"; QUnit.module("model:user"); -QUnit.test('staff', assert =>{ - var user = User.create({id: 1, username: 'eviltrout'}); +QUnit.test("staff", assert => { + var user = User.create({ id: 1, username: "eviltrout" }); - assert.ok(!user.get('staff'), "user is not staff"); + assert.ok(!user.get("staff"), "user is not staff"); - user.toggleProperty('moderator'); - assert.ok(user.get('staff'), "moderators are staff"); + user.toggleProperty("moderator"); + assert.ok(user.get("staff"), "moderators are staff"); - user.setProperties({moderator: false, admin: true}); - assert.ok(user.get('staff'), "admins are staff"); + user.setProperties({ moderator: false, admin: true }); + assert.ok(user.get("staff"), "admins are staff"); }); -QUnit.test('searchContext', assert => { - var user = User.create({id: 1, username: 'EvilTrout'}); +QUnit.test("searchContext", assert => { + var user = User.create({ id: 1, username: "EvilTrout" }); - assert.deepEqual(user.get('searchContext'), {type: 'user', id: 'eviltrout', user: user}, "has a search context"); + assert.deepEqual( + user.get("searchContext"), + { type: "user", id: "eviltrout", user: user }, + "has a search context" + ); }); QUnit.test("isAllowedToUploadAFile", assert => { var user = User.create({ trust_level: 0, admin: true }); - assert.ok(user.isAllowedToUploadAFile("image"), "admin can always upload a file"); + assert.ok( + user.isAllowedToUploadAFile("image"), + "admin can always upload a file" + ); user.setProperties({ admin: false, moderator: true }); - assert.ok(user.isAllowedToUploadAFile("image"), "moderator can always upload a file"); + assert.ok( + user.isAllowedToUploadAFile("image"), + "moderator can always upload a file" + ); }); -QUnit.test('canMangeGroup', assert => { +QUnit.test("canMangeGroup", assert => { let user = User.create({ admin: true }); let group = Group.create({ automatic: true }); - assert.equal(user.canManageGroup(group), false, "automatic groups cannot be managed."); + assert.equal( + user.canManageGroup(group), + false, + "automatic groups cannot be managed." + ); group.set("automatic", false); - assert.equal(user.canManageGroup(group), true, "an admin should be able to manage the group"); + assert.equal( + user.canManageGroup(group), + true, + "an admin should be able to manage the group" + ); - user.set('admin', false); + user.set("admin", false); group.setProperties({ is_group_owner: true }); - assert.equal(user.canManageGroup(group), true, "a group owner should be able to manage the group"); -}); \ No newline at end of file + assert.equal( + user.canManageGroup(group), + true, + "a group owner should be able to manage the group" + ); +}); diff --git a/test/javascripts/widgets/actions-summary-test.js.es6 b/test/javascripts/widgets/actions-summary-test.js.es6 index f4eb8b78821..2a6fbbd3448 100644 --- a/test/javascripts/widgets/actions-summary-test.js.es6 +++ b/test/javascripts/widgets/actions-summary-test.js.es6 @@ -1,80 +1,97 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; -moduleForWidget('actions-summary'); +moduleForWidget("actions-summary"); -widgetTest('listing actions', { +widgetTest("listing actions", { template: '{{mount-widget widget="actions-summary" args=args}}', beforeEach() { - this.set('args', { + this.set("args", { actionsSummary: [ - {id: 1, action: 'off_topic', description: 'very off topic'}, - {id: 2, action: 'spam', description: 'suspicious message'} + { id: 1, action: "off_topic", description: "very off topic" }, + { id: 2, action: "spam", description: "suspicious message" } ] }); }, test(assert) { - assert.equal(this.$('.post-actions .post-action').length, 2); + assert.equal(this.$(".post-actions .post-action").length, 2); - click('.post-action:eq(0) .action-link a'); + click(".post-action:eq(0) .action-link a"); andThen(() => { - assert.equal(this.$('.post-action:eq(0) img.avatar').length, 1, 'clicking it shows the user'); + assert.equal( + this.$(".post-action:eq(0) img.avatar").length, + 1, + "clicking it shows the user" + ); }); } }); -widgetTest('undo', { - template: '{{mount-widget widget="actions-summary" args=args undoPostAction=undoPostAction}}', +widgetTest("undo", { + template: + '{{mount-widget widget="actions-summary" args=args undoPostAction=undoPostAction}}', beforeEach() { - this.set('args', { + this.set("args", { actionsSummary: [ - {action: 'off_topic', description: 'very off topic', canUndo: true}, + { action: "off_topic", description: "very off topic", canUndo: true } ] }); - this.set('undoPostAction', () => this.undid = true); + this.set("undoPostAction", () => (this.undid = true)); }, test(assert) { - assert.equal(this.$('.post-actions .post-action').length, 1); + assert.equal(this.$(".post-actions .post-action").length, 1); - click('.action-link.undo'); + click(".action-link.undo"); andThen(() => { - assert.ok(this.undid, 'it triggered the action'); + assert.ok(this.undid, "it triggered the action"); }); } }); -widgetTest('deferFlags', { - template: '{{mount-widget widget="actions-summary" args=args deferPostActionFlags="deferPostActionFlags"}}', +widgetTest("deferFlags", { + template: + '{{mount-widget widget="actions-summary" args=args deferPostActionFlags="deferPostActionFlags"}}', beforeEach() { - this.set('args', { + this.set("args", { actionsSummary: [ - {action: 'off_topic', description: 'very off topic', canDeferFlags: true, count: 1}, + { + action: "off_topic", + description: "very off topic", + canDeferFlags: true, + count: 1 + } ] }); - this.on('deferPostActionFlags', () => this.deferred = true); + this.on("deferPostActionFlags", () => (this.deferred = true)); }, test(assert) { - assert.equal(this.$('.post-actions .post-action').length, 1); + assert.equal(this.$(".post-actions .post-action").length, 1); - click('.action-link.defer-flags'); + click(".action-link.defer-flags"); andThen(() => { - assert.ok(this.deferred, 'it triggered the action'); + assert.ok(this.deferred, "it triggered the action"); }); } }); -widgetTest('post deleted', { +widgetTest("post deleted", { template: '{{mount-widget widget="actions-summary" args=args}}', beforeEach() { - this.set('args', { + this.set("args", { deleted_at: "2016-01-01", - deletedByUsername: 'eviltrout', - deletedByAvatarTemplate: '/images/avatar.png' + deletedByUsername: "eviltrout", + deletedByAvatarTemplate: "/images/avatar.png" }); }, test(assert) { - assert.ok(this.$('.post-action .d-icon-trash-o').length === 1, 'it has the deleted icon'); - assert.ok(this.$('.avatar[title=eviltrout]').length === 1, 'it has the deleted by avatar'); + assert.ok( + this.$(".post-action .d-icon-trash-o").length === 1, + "it has the deleted icon" + ); + assert.ok( + this.$(".avatar[title=eviltrout]").length === 1, + "it has the deleted by avatar" + ); } }); diff --git a/test/javascripts/widgets/button-test.js.es6 b/test/javascripts/widgets/button-test.js.es6 index 96bd4b245c7..6286e3947b6 100644 --- a/test/javascripts/widgets/button-test.js.es6 +++ b/test/javascripts/widgets/button-test.js.es6 @@ -1,43 +1,52 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; -moduleForWidget('button'); +moduleForWidget("button"); -widgetTest('icon only button', { +widgetTest("icon only button", { template: '{{mount-widget widget="button" args=args}}', beforeEach() { - this.set('args', { icon: 'smile-o' }); + this.set("args", { icon: "smile-o" }); }, test(assert) { - assert.ok(this.$('button.btn.btn-icon.no-text').length, 'it has all the classes'); - assert.ok(this.$('button .d-icon.d-icon-smile-o').length, 'it has the icon'); + assert.ok( + this.$("button.btn.btn-icon.no-text").length, + "it has all the classes" + ); + assert.ok( + this.$("button .d-icon.d-icon-smile-o").length, + "it has the icon" + ); } }); -widgetTest('icon and text button', { +widgetTest("icon and text button", { template: '{{mount-widget widget="button" args=args}}', beforeEach() { - this.set('args', { icon: 'plus', label: 'topic.create' }); + this.set("args", { icon: "plus", label: "topic.create" }); }, test(assert) { - assert.ok(this.$('button.btn.btn-icon-text').length, 'it has all the classes'); - assert.ok(this.$('button .d-icon.d-icon-plus').length, 'it has the icon'); - assert.ok(this.$('button span.d-button-label').length, 'it has the label'); + assert.ok( + this.$("button.btn.btn-icon-text").length, + "it has all the classes" + ); + assert.ok(this.$("button .d-icon.d-icon-plus").length, "it has the icon"); + assert.ok(this.$("button span.d-button-label").length, "it has the label"); } }); -widgetTest('text only button', { +widgetTest("text only button", { template: '{{mount-widget widget="button" args=args}}', beforeEach() { - this.set('args', { label: 'topic.create' }); + this.set("args", { label: "topic.create" }); }, test(assert) { - assert.ok(this.$('button.btn.btn-text').length, 'it has all the classes'); - assert.ok(this.$('button span.d-button-label').length, 'it has the label'); + assert.ok(this.$("button.btn.btn-text").length, "it has all the classes"); + assert.ok(this.$("button span.d-button-label").length, "it has the label"); } }); diff --git a/test/javascripts/widgets/hamburger-menu-test.js.es6 b/test/javascripts/widgets/hamburger-menu-test.js.es6 index c8f67ccf1ec..63759812bcb 100644 --- a/test/javascripts/widgets/hamburger-menu-test.js.es6 +++ b/test/javascripts/widgets/hamburger-menu-test.js.es6 @@ -1,63 +1,66 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; -moduleForWidget('hamburger-menu'); +moduleForWidget("hamburger-menu"); -widgetTest('prioritize faq', { +widgetTest("prioritize faq", { template: '{{mount-widget widget="hamburger-menu"}}', beforeEach() { - this.siteSettings.faq_url = 'http://example.com/faq'; - this.currentUser.set('read_faq', false); + this.siteSettings.faq_url = "http://example.com/faq"; + this.currentUser.set("read_faq", false); }, test(assert) { - assert.ok(this.$('.faq-priority').length); - assert.ok(!this.$('.faq-link').length); + assert.ok(this.$(".faq-priority").length); + assert.ok(!this.$(".faq-link").length); } }); -widgetTest('prioritize faq - user has read', { +widgetTest("prioritize faq - user has read", { template: '{{mount-widget widget="hamburger-menu"}}', beforeEach() { - this.siteSettings.faq_url = 'http://example.com/faq'; - this.currentUser.set('read_faq', true); + this.siteSettings.faq_url = "http://example.com/faq"; + this.currentUser.set("read_faq", true); }, test(assert) { - assert.ok(!this.$('.faq-priority').length); - assert.ok(this.$('.faq-link').length); + assert.ok(!this.$(".faq-priority").length); + assert.ok(this.$(".faq-link").length); } }); -widgetTest('staff menu - not staff', { +widgetTest("staff menu - not staff", { template: '{{mount-widget widget="hamburger-menu"}}', beforeEach() { - this.currentUser.set('staff', false); + this.currentUser.set("staff", false); }, test(assert) { - assert.ok(!this.$('.admin-link').length); + assert.ok(!this.$(".admin-link").length); } }); -widgetTest('staff menu', { +widgetTest("staff menu", { template: '{{mount-widget widget="hamburger-menu"}}', beforeEach() { - this.currentUser.setProperties({ staff: true, site_flagged_posts_count: 3 }); + this.currentUser.setProperties({ + staff: true, + site_flagged_posts_count: 3 + }); }, test(assert) { - assert.ok(this.$('.admin-link').length); - assert.ok(this.$('.flagged-posts-link').length); - assert.equal(this.$('.flagged-posts').text(), '3'); - assert.ok(!this.$('.settings-link').length); + assert.ok(this.$(".admin-link").length); + assert.ok(this.$(".flagged-posts-link").length); + assert.equal(this.$(".flagged-posts").text(), "3"); + assert.ok(!this.$(".settings-link").length); } }); -widgetTest('staff menu - admin', { +widgetTest("staff menu - admin", { template: '{{mount-widget widget="hamburger-menu"}}', beforeEach() { @@ -65,12 +68,11 @@ widgetTest('staff menu - admin', { }, test(assert) { - assert.ok(this.$('.settings-link').length); + assert.ok(this.$(".settings-link").length); } }); - -widgetTest('queued posts', { +widgetTest("queued posts", { template: '{{mount-widget widget="hamburger-menu"}}', beforeEach() { @@ -82,12 +84,12 @@ widgetTest('queued posts', { }, test(assert) { - assert.ok(this.$('.queued-posts-link').length); - assert.equal(this.$('.queued-posts').text(), '5'); + assert.ok(this.$(".queued-posts-link").length); + assert.equal(this.$(".queued-posts").text(), "5"); } }); -widgetTest('queued posts - disabled', { +widgetTest("queued posts - disabled", { template: '{{mount-widget widget="hamburger-menu"}}', beforeEach() { @@ -95,36 +97,35 @@ widgetTest('queued posts - disabled', { }, test(assert) { - assert.ok(!this.$('.queued-posts-link').length); + assert.ok(!this.$(".queued-posts-link").length); } }); - -widgetTest('logged in links', { +widgetTest("logged in links", { template: '{{mount-widget widget="hamburger-menu"}}', test(assert) { - assert.ok(this.$('.new-topics-link').length); - assert.ok(this.$('.unread-topics-link').length); + assert.ok(this.$(".new-topics-link").length); + assert.ok(this.$(".unread-topics-link").length); } }); -widgetTest('general links', { +widgetTest("general links", { template: '{{mount-widget widget="hamburger-menu"}}', anonymous: true, test(assert) { assert.ok(this.$("li[class='']").length === 0); - assert.ok(this.$('.latest-topics-link').length); - assert.ok(!this.$('.new-topics-link').length); - assert.ok(!this.$('.unread-topics-link').length); - assert.ok(this.$('.top-topics-link').length); - assert.ok(this.$('.badge-link').length); - assert.ok(this.$('.category-link').length > 0); + assert.ok(this.$(".latest-topics-link").length); + assert.ok(!this.$(".new-topics-link").length); + assert.ok(!this.$(".unread-topics-link").length); + assert.ok(this.$(".top-topics-link").length); + assert.ok(this.$(".badge-link").length); + assert.ok(this.$(".category-link").length > 0); } }); -widgetTest('badges link - disabled', { +widgetTest("badges link - disabled", { template: '{{mount-widget widget="hamburger-menu"}}', beforeEach() { @@ -132,27 +133,27 @@ widgetTest('badges link - disabled', { }, test(assert) { - assert.ok(!this.$('.badge-link').length); + assert.ok(!this.$(".badge-link").length); } }); -widgetTest('badges link', { +widgetTest("badges link", { template: '{{mount-widget widget="hamburger-menu"}}', test(assert) { - assert.ok(this.$('.badge-link').length); + assert.ok(this.$(".badge-link").length); } }); -widgetTest('user directory link', { +widgetTest("user directory link", { template: '{{mount-widget widget="hamburger-menu"}}', test(assert) { - assert.ok(this.$('.user-directory-link').length); + assert.ok(this.$(".user-directory-link").length); } }); -widgetTest('user directory link - disabled', { +widgetTest("user directory link - disabled", { template: '{{mount-widget widget="hamburger-menu"}}', beforeEach() { @@ -160,15 +161,15 @@ widgetTest('user directory link - disabled', { }, test(assert) { - assert.ok(!this.$('.user-directory-link').length); + assert.ok(!this.$(".user-directory-link").length); } }); -widgetTest('general links', { +widgetTest("general links", { template: '{{mount-widget widget="hamburger-menu"}}', test(assert) { - assert.ok(this.$('.about-link').length); - assert.ok(this.$('.keyboard-shortcuts-link').length); + assert.ok(this.$(".about-link").length); + assert.ok(this.$(".keyboard-shortcuts-link").length); } }); diff --git a/test/javascripts/widgets/header-test.js.es6 b/test/javascripts/widgets/header-test.js.es6 index f91ab7755ea..7fdebfbb366 100644 --- a/test/javascripts/widgets/header-test.js.es6 +++ b/test/javascripts/widgets/header-test.js.es6 @@ -1,75 +1,78 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; -moduleForWidget('header'); +moduleForWidget("header"); -widgetTest('rendering basics', { +widgetTest("rendering basics", { template: '{{mount-widget widget="header"}}', test(assert) { - assert.ok(this.$('header.d-header').length); - assert.ok(this.$('#site-logo').length); + assert.ok(this.$("header.d-header").length); + assert.ok(this.$("#site-logo").length); } }); -widgetTest('sign up / login buttons', { - template: '{{mount-widget widget="header" showCreateAccount="showCreateAccount" showLogin="showLogin" args=args}}', +widgetTest("sign up / login buttons", { + template: + '{{mount-widget widget="header" showCreateAccount="showCreateAccount" showLogin="showLogin" args=args}}', anonymous: true, beforeEach() { - this.set('args', { canSignUp: true }); - this.on('showCreateAccount', () => this.signupShown = true); - this.on('showLogin', () => this.loginShown = true); + this.set("args", { canSignUp: true }); + this.on("showCreateAccount", () => (this.signupShown = true)); + this.on("showLogin", () => (this.loginShown = true)); }, test(assert) { - assert.ok(this.$('button.sign-up-button').length); - assert.ok(this.$('button.login-button').length); + assert.ok(this.$("button.sign-up-button").length); + assert.ok(this.$("button.login-button").length); - click('button.sign-up-button'); + click("button.sign-up-button"); andThen(() => { assert.ok(this.signupShown); }); - click('button.login-button'); + click("button.login-button"); andThen(() => { assert.ok(this.loginShown); }); } }); -widgetTest('anon when login required', { - template: '{{mount-widget widget="header" showCreateAccount="showCreateAccount" showLogin="showLogin" args=args}}', +widgetTest("anon when login required", { + template: + '{{mount-widget widget="header" showCreateAccount="showCreateAccount" showLogin="showLogin" args=args}}', anonymous: true, beforeEach() { - this.set('args', { canSignUp: true }); - this.on('showCreateAccount', () => this.signupShown = true); - this.on('showLogin', () => this.loginShown = true); + this.set("args", { canSignUp: true }); + this.on("showCreateAccount", () => (this.signupShown = true)); + this.on("showLogin", () => (this.loginShown = true)); this.siteSettings.login_required = true; }, test(assert) { - assert.ok(exists('button.login-button')); - assert.ok(exists('button.sign-up-button')); - assert.ok(!exists('#search-button')); - assert.ok(!exists('#toggle-hamburger-menu')); + assert.ok(exists("button.login-button")); + assert.ok(exists("button.sign-up-button")); + assert.ok(!exists("#search-button")); + assert.ok(!exists("#toggle-hamburger-menu")); } }); -widgetTest('logged in when login required', { - template: '{{mount-widget widget="header" showCreateAccount="showCreateAccount" showLogin="showLogin" args=args}}', +widgetTest("logged in when login required", { + template: + '{{mount-widget widget="header" showCreateAccount="showCreateAccount" showLogin="showLogin" args=args}}', beforeEach() { - this.set('args', { canSignUp: true }); - this.on('showCreateAccount', () => this.signupShown = true); - this.on('showLogin', () => this.loginShown = true); + this.set("args", { canSignUp: true }); + this.on("showCreateAccount", () => (this.signupShown = true)); + this.on("showLogin", () => (this.loginShown = true)); this.siteSettings.login_required = true; }, test(assert) { - assert.ok(!exists('button.login-button')); - assert.ok(!exists('button.sign-up-button')); - assert.ok(exists('#search-button')); - assert.ok(exists('#toggle-hamburger-menu')); - assert.ok(exists('#current-user')); + assert.ok(!exists("button.login-button")); + assert.ok(!exists("button.sign-up-button")); + assert.ok(exists("#search-button")); + assert.ok(exists("#toggle-hamburger-menu")); + assert.ok(exists("#current-user")); } }); diff --git a/test/javascripts/widgets/home-logo-test.js.es6 b/test/javascripts/widgets/home-logo-test.js.es6 index 70615b7805d..e64b9ac41e8 100644 --- a/test/javascripts/widgets/home-logo-test.js.es6 +++ b/test/javascripts/widgets/home-logo-test.js.es6 @@ -1,90 +1,90 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; -moduleForWidget('home-logo'); +moduleForWidget("home-logo"); -const bigLogo = '/images/d-logo-sketch.png?test'; -const smallLogo = '/images/d-logo-sketch-small.png?test'; -const mobileLogo = '/images/d-logo-sketch.png?mobile'; +const bigLogo = "/images/d-logo-sketch.png?test"; +const smallLogo = "/images/d-logo-sketch-small.png?test"; +const mobileLogo = "/images/d-logo-sketch.png?mobile"; const title = "Cool Forum"; -widgetTest('basics', { +widgetTest("basics", { template: '{{mount-widget widget="home-logo" args=args}}', beforeEach() { this.siteSettings.logo_url = bigLogo; this.siteSettings.logo_small_url = smallLogo; this.siteSettings.title = title; - this.set('args', { minimized: false }); + this.set("args", { minimized: false }); }, test(assert) { - assert.ok(this.$('.title').length === 1); + assert.ok(this.$(".title").length === 1); - assert.ok(this.$('img#site-logo.logo-big').length === 1); - assert.equal(this.$('#site-logo').attr('src'), bigLogo); - assert.equal(this.$('#site-logo').attr('alt'), title); + assert.ok(this.$("img#site-logo.logo-big").length === 1); + assert.equal(this.$("#site-logo").attr("src"), bigLogo); + assert.equal(this.$("#site-logo").attr("alt"), title); } }); -widgetTest('basics - minimized', { +widgetTest("basics - minimized", { template: '{{mount-widget widget="home-logo" args=args}}', beforeEach() { this.siteSettings.logo_url = bigLogo; - this.siteSettings.logo_small_url= smallLogo; + this.siteSettings.logo_small_url = smallLogo; this.siteSettings.title = title; - this.set('args', { minimized: true }); + this.set("args", { minimized: true }); }, test(assert) { - assert.ok(this.$('img.logo-small').length === 1); - assert.equal(this.$('img.logo-small').attr('src'), smallLogo); - assert.equal(this.$('img.logo-small').attr('alt'), title); + assert.ok(this.$("img.logo-small").length === 1); + assert.equal(this.$("img.logo-small").attr("src"), smallLogo); + assert.equal(this.$("img.logo-small").attr("alt"), title); } }); -widgetTest('no logo', { +widgetTest("no logo", { template: '{{mount-widget widget="home-logo" args=args}}', beforeEach() { - this.siteSettings.logo_url = ''; - this.siteSettings.logo_small_url = ''; + this.siteSettings.logo_url = ""; + this.siteSettings.logo_small_url = ""; this.siteSettings.title = title; - this.set('args', { minimized: false }); + this.set("args", { minimized: false }); }, test(assert) { - assert.ok(this.$('h1#site-text-logo.text-logo').length === 1); - assert.equal(this.$('#site-text-logo').text(), title); + assert.ok(this.$("h1#site-text-logo.text-logo").length === 1); + assert.equal(this.$("#site-text-logo").text(), title); } }); -widgetTest('no logo - minimized', { +widgetTest("no logo - minimized", { template: '{{mount-widget widget="home-logo" args=args}}', beforeEach() { - this.siteSettings.logo_url = ''; - this.siteSettings.logo_small_url = ''; + this.siteSettings.logo_url = ""; + this.siteSettings.logo_small_url = ""; this.siteSettings.title = title; - this.set('args', { minimized: true }); + this.set("args", { minimized: true }); }, test(assert) { - assert.ok(this.$('.d-icon-home').length === 1); + assert.ok(this.$(".d-icon-home").length === 1); } }); -widgetTest('mobile logo', { +widgetTest("mobile logo", { template: '{{mount-widget widget="home-logo" args=args}}', beforeEach() { this.siteSettings.mobile_logo_url = mobileLogo; - this.siteSettings.logo_small_url= smallLogo; + this.siteSettings.logo_small_url = smallLogo; this.site.mobileView = true; }, test(assert) { - assert.ok(this.$('img#site-logo.logo-big').length === 1); - assert.equal(this.$('#site-logo').attr('src'), mobileLogo); + assert.ok(this.$("img#site-logo.logo-big").length === 1); + assert.equal(this.$("#site-logo").attr("src"), mobileLogo); } }); -widgetTest('mobile without logo', { +widgetTest("mobile without logo", { template: '{{mount-widget widget="home-logo" args=args}}', beforeEach() { this.siteSettings.logo_url = bigLogo; @@ -92,7 +92,7 @@ widgetTest('mobile without logo', { }, test(assert) { - assert.ok(this.$('img#site-logo.logo-big').length === 1); - assert.equal(this.$('#site-logo').attr('src'), bigLogo); + assert.ok(this.$("img#site-logo.logo-big").length === 1); + assert.equal(this.$("#site-logo").attr("src"), bigLogo); } }); diff --git a/test/javascripts/widgets/post-links-test.js.es6 b/test/javascripts/widgets/post-links-test.js.es6 index c3e8f0c267f..ed40a6fadbb 100644 --- a/test/javascripts/widgets/post-links-test.js.es6 +++ b/test/javascripts/widgets/post-links-test.js.es6 @@ -1,27 +1,39 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; -moduleForWidget('post-links'); +moduleForWidget("post-links"); widgetTest("duplicate links", { template: '{{mount-widget widget="post-links" args=args}}', beforeEach() { - this.set('args', { + this.set("args", { id: 2, links: [ - { title: "Evil Trout Link", url: "http://eviltrout.com", reflection: true }, - { title: "Evil Trout Link", url: "http://dupe.eviltrout.com", reflection: true } + { + title: "Evil Trout Link", + url: "http://eviltrout.com", + reflection: true + }, + { + title: "Evil Trout Link", + url: "http://dupe.eviltrout.com", + reflection: true + } ] }); }, test(assert) { - assert.equal(this.$('.post-links a.track-link').length, 1, 'it hides the dupe link'); + assert.equal( + this.$(".post-links a.track-link").length, + 1, + "it hides the dupe link" + ); } }); widgetTest("collapsed links", { template: '{{mount-widget widget="post-links" args=args}}', beforeEach() { - this.set('args', { + this.set("args", { id: 1, links: [ { title: "Link 1", url: "http://eviltrout.com?1", reflection: true }, @@ -30,15 +42,15 @@ widgetTest("collapsed links", { { title: "Link 4", url: "http://eviltrout.com?4", reflection: true }, { title: "Link 5", url: "http://eviltrout.com?5", reflection: true }, { title: "Link 6", url: "http://eviltrout.com?6", reflection: true }, - { title: "Link 7", url: "http://eviltrout.com?7", reflection: true }, + { title: "Link 7", url: "http://eviltrout.com?7", reflection: true } ] }); }, test(assert) { - assert.ok(this.$('.expand-links').length === 1, 'collapsed by default'); - click('a.expand-links'); + assert.ok(this.$(".expand-links").length === 1, "collapsed by default"); + click("a.expand-links"); andThen(() => { - assert.equal(this.$('.post-links a.track-link').length, 7); + assert.equal(this.$(".post-links a.track-link").length, 7); }); } }); diff --git a/test/javascripts/widgets/post-stream-test.js.es6 b/test/javascripts/widgets/post-stream-test.js.es6 index 071d59db957..040084efc46 100644 --- a/test/javascripts/widgets/post-stream-test.js.es6 +++ b/test/javascripts/widgets/post-stream-test.js.es6 @@ -1,70 +1,144 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; -import Topic from 'discourse/models/topic'; -import Post from 'discourse/models/post'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; +import Topic from "discourse/models/topic"; +import Post from "discourse/models/post"; -moduleForWidget('post-stream'); +moduleForWidget("post-stream"); function postStreamTest(name, attrs) { widgetTest(name, { template: `{{mount-widget widget="post-stream" args=(hash posts=posts)}}`, beforeEach() { - const site = this.container.lookup('site:main'); + const site = this.container.lookup("site:main"); let posts = attrs.posts.call(this); - posts.forEach(p => p.set('site', site)); - this.set('posts', posts); + posts.forEach(p => p.set("site", site)); + this.set("posts", posts); }, test: attrs.test }); } -postStreamTest('basics', { +postStreamTest("basics", { posts() { - const site = this.container.lookup('site:main'); + const site = this.container.lookup("site:main"); const topic = Topic.create({ details: { created_by: { id: 123 } } }); return [ - Post.create({ topic, id: 1, post_number: 1, user_id: 123, primary_group_name: 'trout', avatar_template: '/images/avatar.png' }), - Post.create({ topic, id: 2, post_number: 2, post_type: site.get('post_types.moderator_action') }), + Post.create({ + topic, + id: 1, + post_number: 1, + user_id: 123, + primary_group_name: "trout", + avatar_template: "/images/avatar.png" + }), + Post.create({ + topic, + id: 2, + post_number: 2, + post_type: site.get("post_types.moderator_action") + }), Post.create({ topic, id: 3, post_number: 3, hidden: true }), - Post.create({ topic, id: 4, post_number: 4, post_type: site.get('post_types.whisper') }), - Post.create({ topic, id: 5, post_number: 5, wiki: true, via_email: true }), - Post.create({ topic, id: 6, post_number: 6, via_email: true, is_auto_generated: true }), + Post.create({ + topic, + id: 4, + post_number: 4, + post_type: site.get("post_types.whisper") + }), + Post.create({ + topic, + id: 5, + post_number: 5, + wiki: true, + via_email: true + }), + Post.create({ + topic, + id: 6, + post_number: 6, + via_email: true, + is_auto_generated: true + }) ]; }, test(assert) { - assert.equal(this.$('.post-stream').length, 1); - assert.equal(this.$('.topic-post').length, 6, 'renders all posts'); + assert.equal(this.$(".post-stream").length, 1); + assert.equal(this.$(".topic-post").length, 6, "renders all posts"); // look for special class bindings - assert.equal(this.$('.topic-post:eq(0).topic-owner').length, 1, 'it applies the topic owner class'); - assert.equal(this.$('.topic-post:eq(0).group-trout').length, 1, 'it applies the primary group class'); - assert.equal(this.$('.topic-post:eq(0).regular').length, 1, 'it applies the regular class'); - assert.equal(this.$('.topic-post:eq(1).moderator').length, 1, 'it applies the moderator class'); - assert.equal(this.$('.topic-post:eq(2).post-hidden').length, 1, 'it applies the hidden class'); - assert.equal(this.$('.topic-post:eq(3).whisper').length, 1, 'it applies the whisper class'); - assert.equal(this.$('.topic-post:eq(4).wiki').length, 1, 'it applies the wiki class'); + assert.equal( + this.$(".topic-post:eq(0).topic-owner").length, + 1, + "it applies the topic owner class" + ); + assert.equal( + this.$(".topic-post:eq(0).group-trout").length, + 1, + "it applies the primary group class" + ); + assert.equal( + this.$(".topic-post:eq(0).regular").length, + 1, + "it applies the regular class" + ); + assert.equal( + this.$(".topic-post:eq(1).moderator").length, + 1, + "it applies the moderator class" + ); + assert.equal( + this.$(".topic-post:eq(2).post-hidden").length, + 1, + "it applies the hidden class" + ); + assert.equal( + this.$(".topic-post:eq(3).whisper").length, + 1, + "it applies the whisper class" + ); + assert.equal( + this.$(".topic-post:eq(4).wiki").length, + 1, + "it applies the wiki class" + ); // it renders an article for the body with appropriate attributes - assert.equal(this.$('article#post_2').length, 1); - assert.equal(this.$('article[data-user-id=123]').length, 1); - assert.equal(this.$('article[data-post-id=3]').length, 1); - assert.equal(this.$('article#post_5.via-email').length, 1); - assert.equal(this.$('article#post_6.is-auto-generated').length, 1); + assert.equal(this.$("article#post_2").length, 1); + assert.equal(this.$("article[data-user-id=123]").length, 1); + assert.equal(this.$("article[data-post-id=3]").length, 1); + assert.equal(this.$("article#post_5.via-email").length, 1); + assert.equal(this.$("article#post_6.is-auto-generated").length, 1); - assert.equal(this.$('article:eq(0) .main-avatar').length, 1, 'renders the main avatar'); + assert.equal( + this.$("article:eq(0) .main-avatar").length, + 1, + "renders the main avatar" + ); } }); -postStreamTest('deleted posts', { +postStreamTest("deleted posts", { posts() { const topic = Topic.create({ details: { created_by: { id: 123 } } }); return [ - Post.create({ topic, id: 1, post_number: 1, deleted_at: new Date().toString() }), + Post.create({ + topic, + id: 1, + post_number: 1, + deleted_at: new Date().toString() + }) ]; }, test(assert) { - assert.equal(this.$('.topic-post.deleted').length, 1, 'it applies the deleted class'); - assert.equal(this.$('.deleted-user-avatar').length, 1, 'it has the trash avatar'); + assert.equal( + this.$(".topic-post.deleted").length, + 1, + "it applies the deleted class" + ); + assert.equal( + this.$(".deleted-user-avatar").length, + 1, + "it has the trash avatar" + ); } }); diff --git a/test/javascripts/widgets/post-test.js.es6 b/test/javascripts/widgets/post-test.js.es6 index 0296c9dcf3d..4cc8fb34ced 100644 --- a/test/javascripts/widgets/post-test.js.es6 +++ b/test/javascripts/widgets/post-test.js.es6 @@ -1,146 +1,163 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; -moduleForWidget('post'); +moduleForWidget("post"); -widgetTest('basic elements', { +widgetTest("basic elements", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { shareUrl: '/example', post_number: 1 }); + this.set("args", { shareUrl: "/example", post_number: 1 }); }, test(assert) { - assert.ok(this.$('.names').length, 'includes poster name'); + assert.ok(this.$(".names").length, "includes poster name"); - assert.ok(this.$('a.post-date').length, 'includes post date'); - assert.ok(this.$('a.post-date[data-share-url]').length); - assert.ok(this.$('a.post-date[data-post-number]').length); + assert.ok(this.$("a.post-date").length, "includes post date"); + assert.ok(this.$("a.post-date[data-share-url]").length); + assert.ok(this.$("a.post-date[data-post-number]").length); } }); -widgetTest('wiki', { - template: '{{mount-widget widget="post" args=args showHistory="showHistory"}}', +widgetTest("wiki", { + template: + '{{mount-widget widget="post" args=args showHistory="showHistory"}}', beforeEach() { - this.set('args', { wiki: true, version: 2, canViewEditHistory: true }); - this.on('showHistory', () => this.historyShown = true); + this.set("args", { wiki: true, version: 2, canViewEditHistory: true }); + this.on("showHistory", () => (this.historyShown = true)); }, test(assert) { - click('.post-info .wiki'); + click(".post-info .wiki"); andThen(() => { - assert.ok(this.historyShown, 'clicking the wiki icon displays the post history'); + assert.ok( + this.historyShown, + "clicking the wiki icon displays the post history" + ); }); } }); -widgetTest('wiki without revision', { +widgetTest("wiki without revision", { template: '{{mount-widget widget="post" args=args editPost="editPost"}}', beforeEach() { - this.set('args', { wiki: true, version: 1, canViewEditHistory: true }); - this.on('editPost', () => this.editPostCalled = true); + this.set("args", { wiki: true, version: 1, canViewEditHistory: true }); + this.on("editPost", () => (this.editPostCalled = true)); }, test(assert) { - click('.post-info .wiki'); + click(".post-info .wiki"); andThen(() => { - assert.ok(this.editPostCalled, 'clicking wiki icon edits the post'); + assert.ok(this.editPostCalled, "clicking wiki icon edits the post"); }); } }); -widgetTest('via-email', { - template: '{{mount-widget widget="post" args=args showRawEmail="showRawEmail"}}', +widgetTest("via-email", { + template: + '{{mount-widget widget="post" args=args showRawEmail="showRawEmail"}}', beforeEach() { - this.set('args', { via_email: true, canViewRawEmail: true }); - this.on('showRawEmail', () => this.rawEmailShown = true); + this.set("args", { via_email: true, canViewRawEmail: true }); + this.on("showRawEmail", () => (this.rawEmailShown = true)); }, test(assert) { - click('.post-info.via-email'); + click(".post-info.via-email"); andThen(() => { - assert.ok(this.rawEmailShown, 'clicking the enveloppe shows the raw email'); + assert.ok( + this.rawEmailShown, + "clicking the enveloppe shows the raw email" + ); }); } }); -widgetTest('via-email without permission', { - template: '{{mount-widget widget="post" args=args showRawEmail="showRawEmail"}}', +widgetTest("via-email without permission", { + template: + '{{mount-widget widget="post" args=args showRawEmail="showRawEmail"}}', beforeEach() { - this.set('args', { via_email: true, canViewRawEmail: false }); - this.on('showRawEmail', () => this.rawEmailShown = true); + this.set("args", { via_email: true, canViewRawEmail: false }); + this.on("showRawEmail", () => (this.rawEmailShown = true)); }, test(assert) { - click('.post-info.via-email'); + click(".post-info.via-email"); andThen(() => { - assert.ok(!this.rawEmailShown, `clicking the enveloppe doesn't show the raw email`); + assert.ok( + !this.rawEmailShown, + `clicking the enveloppe doesn't show the raw email` + ); }); } }); -widgetTest('history', { - template: '{{mount-widget widget="post" args=args showHistory="showHistory"}}', +widgetTest("history", { + template: + '{{mount-widget widget="post" args=args showHistory="showHistory"}}', beforeEach() { - this.set('args', { version: 3, canViewEditHistory: true }); - this.on('showHistory', () => this.historyShown = true); + this.set("args", { version: 3, canViewEditHistory: true }); + this.on("showHistory", () => (this.historyShown = true)); }, test(assert) { - click('.post-info.edits'); + click(".post-info.edits"); andThen(() => { - assert.ok(this.historyShown, 'clicking the pencil shows the history'); + assert.ok(this.historyShown, "clicking the pencil shows the history"); }); } }); -widgetTest('history without view permission', { - template: '{{mount-widget widget="post" args=args showHistory="showHistory"}}', +widgetTest("history without view permission", { + template: + '{{mount-widget widget="post" args=args showHistory="showHistory"}}', beforeEach() { - this.set('args', { version: 3, canViewEditHistory: false }); - this.on('showHistory', () => this.historyShown = true); + this.set("args", { version: 3, canViewEditHistory: false }); + this.on("showHistory", () => (this.historyShown = true)); }, test(assert) { - click('.post-info.edits'); + click(".post-info.edits"); andThen(() => { - assert.ok(!this.historyShown, `clicking the pencil doesn't show the history`); + assert.ok( + !this.historyShown, + `clicking the pencil doesn't show the history` + ); }); } }); -widgetTest('whisper', { +widgetTest("whisper", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { isWhisper: true }); + this.set("args", { isWhisper: true }); }, test(assert) { - assert.ok(this.$('.topic-post.whisper').length === 1); - assert.ok(this.$('.post-info.whisper').length === 1); + assert.ok(this.$(".topic-post.whisper").length === 1); + assert.ok(this.$(".post-info.whisper").length === 1); } }); -widgetTest('like count button', { +widgetTest("like count button", { template: '{{mount-widget widget="post" model=post args=args}}', beforeEach(store) { - const topic = store.createRecord('topic', {id: 123}); - const post = store.createRecord('post', { + const topic = store.createRecord("topic", { id: 123 }); + const post = store.createRecord("post", { id: 1, post_number: 1, topic, like_count: 3, - actions_summary: [ {id: 2, count: 1, hidden: false, can_act: true} ] + actions_summary: [{ id: 2, count: 1, hidden: false, can_act: true }] }); - this.set('post', post); - this.set('args', { likeCount: 1 }); + this.set("post", post); + this.set("args", { likeCount: 1 }); }, test(assert) { - assert.ok(this.$('button.like-count').length === 1); - assert.ok(this.$('.who-liked').length === 0); + assert.ok(this.$("button.like-count").length === 1); + assert.ok(this.$(".who-liked").length === 0); // toggle it on - click('button.like-count'); + click("button.like-count"); andThen(() => { - assert.ok(this.$('.who-liked').length === 1); - assert.ok(this.$('.who-liked a.trigger-user-card').length === 1); + assert.ok(this.$(".who-liked").length === 1); + assert.ok(this.$(".who-liked a.trigger-user-card").length === 1); }); // toggle it off - click('button.like-count'); + click("button.like-count"); andThen(() => { - assert.ok(this.$('.who-liked').length === 0); - assert.ok(this.$('.who-liked a.trigger-user-card').length === 0); + assert.ok(this.$(".who-liked").length === 0); + assert.ok(this.$(".who-liked a.trigger-user-card").length === 0); }); } }); @@ -148,83 +165,87 @@ widgetTest('like count button', { widgetTest(`like count with no likes`, { template: '{{mount-widget widget="post" model=post args=args}}', beforeEach() { - this.set('args', { likeCount: 0 }); + this.set("args", { likeCount: 0 }); }, test(assert) { - assert.ok(this.$('button.like-count').length === 0); + assert.ok(this.$("button.like-count").length === 0); } }); -widgetTest('share button', { +widgetTest("share button", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { shareUrl: 'http://share-me.example.com' }); + this.set("args", { shareUrl: "http://share-me.example.com" }); }, test(assert) { - assert.ok(!!this.$('.actions button[data-share-url]').length, 'it renders a share button'); + assert.ok( + !!this.$(".actions button[data-share-url]").length, + "it renders a share button" + ); } }); -widgetTest('liking', { - template: '{{mount-widget widget="post-menu" args=args toggleLike="toggleLike"}}', +widgetTest("liking", { + template: + '{{mount-widget widget="post-menu" args=args toggleLike="toggleLike"}}', beforeEach() { const args = { showLike: true, canToggleLike: true }; - this.set('args', args); - this.on('toggleLike', () => { + this.set("args", args); + this.on("toggleLike", () => { args.liked = !args.liked; args.likeCount = args.liked ? 1 : 0; }); }, test(assert) { - assert.ok(!!this.$('.actions button.like').length); - assert.ok(this.$('.actions button.like-count').length === 0); + assert.ok(!!this.$(".actions button.like").length); + assert.ok(this.$(".actions button.like-count").length === 0); - click('.actions button.like'); + click(".actions button.like"); andThen(() => { - assert.ok(!this.$('.actions button.like').length); - assert.ok(!!this.$('.actions button.has-like').length); - assert.ok(this.$('.actions button.like-count').length === 1); + assert.ok(!this.$(".actions button.like").length); + assert.ok(!!this.$(".actions button.has-like").length); + assert.ok(this.$(".actions button.like-count").length === 1); }); - click('.actions button.has-like'); + click(".actions button.has-like"); andThen(() => { - assert.ok(!!this.$('.actions button.like').length); - assert.ok(!this.$('.actions button.has-like').length); - assert.ok(this.$('.actions button.like-count').length === 0); + assert.ok(!!this.$(".actions button.like").length); + assert.ok(!this.$(".actions button.has-like").length); + assert.ok(this.$(".actions button.like-count").length === 0); }); } }); -widgetTest('anon liking', { - template: '{{mount-widget widget="post-menu" args=args showLogin="showLogin"}}', +widgetTest("anon liking", { + template: + '{{mount-widget widget="post-menu" args=args showLogin="showLogin"}}', anonymous: true, beforeEach() { const args = { showLike: true }; - this.set('args', args); - this.on("showLogin", () => this.loginShown = true); + this.set("args", args); + this.on("showLogin", () => (this.loginShown = true)); }, test(assert) { - assert.ok(!!this.$('.actions button.like').length); - assert.ok(this.$('.actions button.like-count').length === 0); + assert.ok(!!this.$(".actions button.like").length); + assert.ok(this.$(".actions button.like-count").length === 0); - click('.actions button.like'); + click(".actions button.like"); andThen(() => { assert.ok(this.loginShown); }); } }); - -widgetTest('edit button', { +widgetTest("edit button", { template: '{{mount-widget widget="post" args=args editPost="editPost"}}', beforeEach() { - this.set('args', { canEdit: true }); - this.on('editPost', () => this.editPostCalled = true); + this.set("args", { canEdit: true }); + this.on("editPost", () => (this.editPostCalled = true)); }, test(assert) { - click('button.edit'); + click("button.edit"); andThen(() => { - assert.ok(this.editPostCalled, 'it triggered the edit action'); + assert.ok(this.editPostCalled, "it triggered the edit action"); }); } }); @@ -232,37 +253,37 @@ widgetTest('edit button', { widgetTest(`edit button - can't edit`, { template: '{{mount-widget widget="post" args=args editPost="editPost"}}', beforeEach() { - this.set('args', { canEdit: false }); + this.set("args", { canEdit: false }); }, test(assert) { - assert.equal(this.$('button.edit').length, 0, `button is not displayed`); + assert.equal(this.$("button.edit").length, 0, `button is not displayed`); } }); -widgetTest('recover button', { +widgetTest("recover button", { template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}', beforeEach() { - this.set('args', { canDelete: true }); - this.on('deletePost', () => this.deletePostCalled = true); + this.set("args", { canDelete: true }); + this.on("deletePost", () => (this.deletePostCalled = true)); }, test(assert) { - click('button.delete'); + click("button.delete"); andThen(() => { - assert.ok(this.deletePostCalled, 'it triggered the delete action'); + assert.ok(this.deletePostCalled, "it triggered the delete action"); }); } }); -widgetTest('delete topic button', { +widgetTest("delete topic button", { template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}', beforeEach() { - this.set('args', { canDeleteTopic: true }); - this.on('deletePost', () => this.deletePostCalled = true); + this.set("args", { canDeleteTopic: true }); + this.on("deletePost", () => (this.deletePostCalled = true)); }, test(assert) { - click('button.delete'); + click("button.delete"); andThen(() => { - assert.ok(this.deletePostCalled, 'it triggered the delete action'); + assert.ok(this.deletePostCalled, "it triggered the delete action"); }); } }); @@ -270,21 +291,22 @@ widgetTest('delete topic button', { widgetTest(`delete topic button - can't delete`, { template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}', beforeEach() { - this.set('args', { canDeleteTopic: false }); + this.set("args", { canDeleteTopic: false }); }, test(assert) { - assert.equal(this.$('button.delete').length, 0, `button is not displayed`); + assert.equal(this.$("button.delete").length, 0, `button is not displayed`); } }); -widgetTest('recover topic button', { - template: '{{mount-widget widget="post" args=args recoverPost="recoverPost"}}', +widgetTest("recover topic button", { + template: + '{{mount-widget widget="post" args=args recoverPost="recoverPost"}}', beforeEach() { - this.set('args', { canRecoverTopic: true }); - this.on('recoverPost', () => this.recovered = true); + this.set("args", { canRecoverTopic: true }); + this.on("recoverPost", () => (this.recovered = true)); }, test(assert) { - click('button.recover'); + click("button.recover"); andThen(() => assert.ok(this.recovered)); } }); @@ -292,23 +314,23 @@ widgetTest('recover topic button', { widgetTest(`recover topic button - can't recover`, { template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}', beforeEach() { - this.set('args', { canRecoverTopic: false }); + this.set("args", { canRecoverTopic: false }); }, test(assert) { - assert.equal(this.$('button.recover').length, 0, `button is not displayed`); + assert.equal(this.$("button.recover").length, 0, `button is not displayed`); } }); -widgetTest('delete post button', { +widgetTest("delete post button", { template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}', beforeEach() { - this.set('args', { canDelete: true }); - this.on('deletePost', () => this.deletePostCalled = true); + this.set("args", { canDelete: true }); + this.on("deletePost", () => (this.deletePostCalled = true)); }, test(assert) { - click('button.delete'); + click("button.delete"); andThen(() => { - assert.ok(this.deletePostCalled, 'it triggered the delete action'); + assert.ok(this.deletePostCalled, "it triggered the delete action"); }); } }); @@ -316,21 +338,22 @@ widgetTest('delete post button', { widgetTest(`delete post button - can't delete`, { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { canDelete: false }); + this.set("args", { canDelete: false }); }, test(assert) { - assert.equal(this.$('button.delete').length, 0, `button is not displayed`); + assert.equal(this.$("button.delete").length, 0, `button is not displayed`); } }); -widgetTest('recover post button', { - template: '{{mount-widget widget="post" args=args recoverPost="recoverPost"}}', +widgetTest("recover post button", { + template: + '{{mount-widget widget="post" args=args recoverPost="recoverPost"}}', beforeEach() { - this.set('args', { canRecover: true }); - this.on('recoverPost', () => this.recovered = true); + this.set("args", { canRecover: true }); + this.on("recoverPost", () => (this.recovered = true)); }, test(assert) { - click('button.recover'); + click("button.recover"); andThen(() => assert.ok(this.recovered)); } }); @@ -338,25 +361,25 @@ widgetTest('recover post button', { widgetTest(`recover post button - can't recover`, { template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}', beforeEach() { - this.set('args', { canRecover: false }); + this.set("args", { canRecover: false }); }, test(assert) { - assert.equal(this.$('button.recover').length, 0, `button is not displayed`); + assert.equal(this.$("button.recover").length, 0, `button is not displayed`); } }); widgetTest(`flagging`, { template: '{{mount-widget widget="post" args=args showFlags="showFlags"}}', beforeEach() { - this.set('args', { canFlag: true }); - this.on('showFlags', () => this.flagsShown = true); + this.set("args", { canFlag: true }); + this.on("showFlags", () => (this.flagsShown = true)); }, test(assert) { - assert.ok(this.$('button.create-flag').length === 1); + assert.ok(this.$("button.create-flag").length === 1); - click('button.create-flag'); + click("button.create-flag"); andThen(() => { - assert.ok(this.flagsShown, 'it triggered the action'); + assert.ok(this.flagsShown, "it triggered the action"); }); } }); @@ -364,93 +387,98 @@ widgetTest(`flagging`, { widgetTest(`flagging: can't flag`, { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { canFlag: false }); + this.set("args", { canFlag: false }); }, test(assert) { - assert.ok(this.$('button.create-flag').length === 0); + assert.ok(this.$("button.create-flag").length === 0); } }); widgetTest(`read indicator`, { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { read: true }); + this.set("args", { read: true }); }, test(assert) { - assert.ok(this.$('.read-state.read').length); + assert.ok(this.$(".read-state.read").length); } }); widgetTest(`unread indicator`, { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { read: false }); + this.set("args", { read: false }); }, test(assert) { - assert.ok(this.$('.read-state').length); + assert.ok(this.$(".read-state").length); } }); widgetTest("reply directly above (supressed)", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { - replyToUsername: 'eviltrout', - replyToAvatarTemplate: '/images/avatar.png', + this.set("args", { + replyToUsername: "eviltrout", + replyToAvatarTemplate: "/images/avatar.png", replyDirectlyAbove: true }); }, test(assert) { - assert.equal(this.$('a.reply-to-tab').length, 0, 'hides the tab'); - assert.equal(this.$('.avoid-tab').length, 0, "doesn't have the avoid tab class"); + assert.equal(this.$("a.reply-to-tab").length, 0, "hides the tab"); + assert.equal( + this.$(".avoid-tab").length, + 0, + "doesn't have the avoid tab class" + ); } }); widgetTest("reply a few posts above (supressed)", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { - replyToUsername: 'eviltrout', - replyToAvatarTemplate: '/images/avatar.png', + this.set("args", { + replyToUsername: "eviltrout", + replyToAvatarTemplate: "/images/avatar.png", replyDirectlyAbove: false }); }, test(assert) { - assert.ok(this.$('a.reply-to-tab').length, 'shows the tab'); - assert.equal(this.$('.avoid-tab').length, 1, "has the avoid tab class"); + assert.ok(this.$("a.reply-to-tab").length, "shows the tab"); + assert.equal(this.$(".avoid-tab").length, 1, "has the avoid tab class"); } }); widgetTest("reply directly above", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { - replyToUsername: 'eviltrout', - replyToAvatarTemplate: '/images/avatar.png', + this.set("args", { + replyToUsername: "eviltrout", + replyToAvatarTemplate: "/images/avatar.png", replyDirectlyAbove: true }); this.siteSettings.suppress_reply_directly_above = false; }, test(assert) { - assert.equal(this.$('.avoid-tab').length, 1, "has the avoid tab class"); - click('a.reply-to-tab'); + assert.equal(this.$(".avoid-tab").length, 1, "has the avoid tab class"); + click("a.reply-to-tab"); andThen(() => { - assert.equal(this.$('section.embedded-posts.top .cooked').length, 1); - assert.equal(this.$('section.embedded-posts .d-icon-arrow-up').length, 1); + assert.equal(this.$("section.embedded-posts.top .cooked").length, 1); + assert.equal(this.$("section.embedded-posts .d-icon-arrow-up").length, 1); }); } }); widgetTest("cooked content hidden", { - template: '{{mount-widget widget="post" args=args expandHidden="expandHidden"}}', + template: + '{{mount-widget widget="post" args=args expandHidden="expandHidden"}}', beforeEach() { - this.set('args', { cooked_hidden: true }); - this.on('expandHidden', () => this.unhidden = true); + this.set("args", { cooked_hidden: true }); + this.on("expandHidden", () => (this.unhidden = true)); }, test(assert) { - click('.topic-body .expand-hidden'); + click(".topic-body .expand-hidden"); andThen(() => { - assert.ok(this.unhidden, 'triggers the action'); + assert.ok(this.unhidden, "triggers the action"); }); } }); @@ -458,13 +486,13 @@ widgetTest("cooked content hidden", { widgetTest("expand first post", { template: '{{mount-widget widget="post" model=post args=args}}', beforeEach(store) { - this.set('args', { expandablePost: true }); - this.set('post', store.createRecord('post', { id: 1234 })); + this.set("args", { expandablePost: true }); + this.set("post", store.createRecord("post", { id: 1234 })); }, test(assert) { - click('.topic-body .expand-post'); + click(".topic-body .expand-post"); andThen(() => { - assert.equal(this.$('.expand-post').length, 0, 'button is gone'); + assert.equal(this.$(".expand-post").length, 0, "button is gone"); }); } }); @@ -472,29 +500,30 @@ widgetTest("expand first post", { widgetTest("can't bookmark", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { canBookmark: false }); + this.set("args", { canBookmark: false }); }, test(assert) { - assert.equal(this.$('button.bookmark').length, 0); - assert.equal(this.$('button.bookmarked').length, 0); + assert.equal(this.$("button.bookmark").length, 0); + assert.equal(this.$("button.bookmarked").length, 0); } }); widgetTest("bookmark", { - template: '{{mount-widget widget="post" args=args toggleBookmark="toggleBookmark"}}', + template: + '{{mount-widget widget="post" args=args toggleBookmark="toggleBookmark"}}', beforeEach() { const args = { canBookmark: true }; - this.set('args', args); - this.on('toggleBookmark', () => args.bookmarked = true); + this.set("args", args); + this.on("toggleBookmark", () => (args.bookmarked = true)); }, test(assert) { - assert.equal(this.$('.post-menu-area .bookmark').length, 1); - assert.equal(this.$('button.bookmarked').length, 0); + assert.equal(this.$(".post-menu-area .bookmark").length, 1); + assert.equal(this.$("button.bookmarked").length, 0); - click('button.bookmark'); + click("button.bookmark"); andThen(() => { - assert.equal(this.$('button.bookmarked').length, 1); + assert.equal(this.$("button.bookmarked").length, 1); }); } }); @@ -502,58 +531,64 @@ widgetTest("bookmark", { widgetTest("can't show admin menu when you can't manage", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { canManage: false }); + this.set("args", { canManage: false }); }, test(assert) { - assert.equal(this.$('.post-menu-area .show-post-admin-menu').length, 0); + assert.equal(this.$(".post-menu-area .show-post-admin-menu").length, 0); } }); widgetTest("show admin menu", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { canManage: true }); + this.set("args", { canManage: true }); }, test(assert) { - assert.equal(this.$('.post-admin-menu').length, 0); - click('.post-menu-area .show-post-admin-menu'); + assert.equal(this.$(".post-admin-menu").length, 0); + click(".post-menu-area .show-post-admin-menu"); andThen(() => { - assert.equal(this.$('.post-admin-menu').length, 1, 'it shows the popup'); + assert.equal(this.$(".post-admin-menu").length, 1, "it shows the popup"); }); - click('.post-menu-area'); + click(".post-menu-area"); andThen(() => { - assert.equal(this.$('.post-admin-menu').length, 0, 'clicking outside clears the popup'); + assert.equal( + this.$(".post-admin-menu").length, + 0, + "clicking outside clears the popup" + ); }); } }); widgetTest("toggle moderator post", { - template: '{{mount-widget widget="post" args=args togglePostType="togglePostType"}}', + template: + '{{mount-widget widget="post" args=args togglePostType="togglePostType"}}', beforeEach() { - this.set('args', { canManage: true }); - this.on('togglePostType', () => this.toggled = true); + this.set("args", { canManage: true }); + this.on("togglePostType", () => (this.toggled = true)); }, test(assert) { - click('.post-menu-area .show-post-admin-menu'); - click('.post-admin-menu .toggle-post-type'); + click(".post-menu-area .show-post-admin-menu"); + click(".post-admin-menu .toggle-post-type"); andThen(() => { assert.ok(this.toggled); - assert.equal(this.$('.post-admin-menu').length, 0, 'also hides the menu'); + assert.equal(this.$(".post-admin-menu").length, 0, "also hides the menu"); }); } }); widgetTest("toggle moderator post", { - template: '{{mount-widget widget="post" args=args togglePostType="togglePostType"}}', + template: + '{{mount-widget widget="post" args=args togglePostType="togglePostType"}}', beforeEach() { - this.set('args', { canManage: true }); - this.on('togglePostType', () => this.toggled = true); + this.set("args", { canManage: true }); + this.on("togglePostType", () => (this.toggled = true)); }, test(assert) { - click('.post-menu-area .show-post-admin-menu'); - click('.post-admin-menu .toggle-post-type'); + click(".post-menu-area .show-post-admin-menu"); + click(".post-admin-menu .toggle-post-type"); andThen(() => { assert.ok(this.toggled); - assert.equal(this.$('.post-admin-menu').length, 0, 'also hides the menu'); + assert.equal(this.$(".post-admin-menu").length, 0, "also hides the menu"); }); } }); @@ -561,15 +596,15 @@ widgetTest("toggle moderator post", { widgetTest("rebake post", { template: '{{mount-widget widget="post" args=args rebakePost="rebakePost"}}', beforeEach() { - this.set('args', { canManage: true }); - this.on('rebakePost', () => this.baked = true); + this.set("args", { canManage: true }); + this.on("rebakePost", () => (this.baked = true)); }, test(assert) { - click('.post-menu-area .show-post-admin-menu'); - click('.post-admin-menu .rebuild-html'); + click(".post-menu-area .show-post-admin-menu"); + click(".post-admin-menu .rebuild-html"); andThen(() => { assert.ok(this.baked); - assert.equal(this.$('.post-admin-menu').length, 0, 'also hides the menu'); + assert.equal(this.$(".post-admin-menu").length, 0, "also hides the menu"); }); } }); @@ -577,44 +612,46 @@ widgetTest("rebake post", { widgetTest("unhide post", { template: '{{mount-widget widget="post" args=args unhidePost="unhidePost"}}', beforeEach() { - this.set('args', { canManage: true, hidden: true }); - this.on('unhidePost', () => this.unhidden = true); + this.set("args", { canManage: true, hidden: true }); + this.on("unhidePost", () => (this.unhidden = true)); }, test(assert) { - click('.post-menu-area .show-post-admin-menu'); - click('.post-admin-menu .unhide-post'); + click(".post-menu-area .show-post-admin-menu"); + click(".post-admin-menu .unhide-post"); andThen(() => { assert.ok(this.unhidden); - assert.equal(this.$('.post-admin-menu').length, 0, 'also hides the menu'); + assert.equal(this.$(".post-admin-menu").length, 0, "also hides the menu"); }); } }); widgetTest("change owner", { - template: '{{mount-widget widget="post" args=args changePostOwner="changePostOwner"}}', + template: + '{{mount-widget widget="post" args=args changePostOwner="changePostOwner"}}', beforeEach() { this.currentUser.admin = true; - this.set('args', { canManage: true }); - this.on('changePostOwner', () => this.owned = true); + this.set("args", { canManage: true }); + this.on("changePostOwner", () => (this.owned = true)); }, test(assert) { - click('.post-menu-area .show-post-admin-menu'); - click('.post-admin-menu .change-owner'); + click(".post-menu-area .show-post-admin-menu"); + click(".post-admin-menu .change-owner"); andThen(() => { assert.ok(this.owned); - assert.equal(this.$('.post-admin-menu').length, 0, 'also hides the menu'); + assert.equal(this.$(".post-admin-menu").length, 0, "also hides the menu"); }); } }); widgetTest("reply", { - template: '{{mount-widget widget="post" args=args replyToPost="replyToPost"}}', + template: + '{{mount-widget widget="post" args=args replyToPost="replyToPost"}}', beforeEach() { - this.set('args', { canCreatePost: true }); - this.on('replyToPost', () => this.replied = true); + this.set("args", { canCreatePost: true }); + this.on("replyToPost", () => (this.replied = true)); }, test(assert) { - click('.post-controls .create'); + click(".post-controls .create"); andThen(() => { assert.ok(this.replied); }); @@ -624,20 +661,20 @@ widgetTest("reply", { widgetTest("reply - without permissions", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { canCreatePost: false }); + this.set("args", { canCreatePost: false }); }, test(assert) { - assert.equal(this.$('.post-controls .create').length, 0); + assert.equal(this.$(".post-controls .create").length, 0); } }); widgetTest("replies - no replies", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', {replyCount: 0}); + this.set("args", { replyCount: 0 }); }, test(assert) { - assert.equal(this.$('button.show-replies').length, 0); + assert.equal(this.$("button.show-replies").length, 0); } }); @@ -645,10 +682,10 @@ widgetTest("replies - multiple replies", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { this.siteSettings.suppress_reply_directly_below = true; - this.set('args', {replyCount: 2, replyDirectlyBelow: true}); + this.set("args", { replyCount: 2, replyDirectlyBelow: true }); }, test(assert) { - assert.equal(this.$('button.show-replies').length, 1); + assert.equal(this.$("button.show-replies").length, 1); } }); @@ -656,10 +693,10 @@ widgetTest("replies - one below, suppressed", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { this.siteSettings.suppress_reply_directly_below = true; - this.set('args', {replyCount: 1, replyDirectlyBelow: true}); + this.set("args", { replyCount: 1, replyDirectlyBelow: true }); }, test(assert) { - assert.equal(this.$('button.show-replies').length, 0); + assert.equal(this.$("button.show-replies").length, 0); } }); @@ -667,13 +704,16 @@ widgetTest("replies - one below, not suppressed", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { this.siteSettings.suppress_reply_directly_below = false; - this.set('args', {id: 6654, replyCount: 1, replyDirectlyBelow: true}); + this.set("args", { id: 6654, replyCount: 1, replyDirectlyBelow: true }); }, test(assert) { - click('button.show-replies'); + click("button.show-replies"); andThen(() => { - assert.equal(this.$('section.embedded-posts.bottom .cooked').length, 1); - assert.equal(this.$('section.embedded-posts .d-icon-arrow-down').length, 1); + assert.equal(this.$("section.embedded-posts.bottom .cooked").length, 1); + assert.equal( + this.$("section.embedded-posts .d-icon-arrow-down").length, + 1 + ); }); } }); @@ -681,31 +721,36 @@ widgetTest("replies - one below, not suppressed", { widgetTest("topic map not shown", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { showTopicMap: false }); + this.set("args", { showTopicMap: false }); }, test(assert) { - assert.equal(this.$('.topic-map').length, 0); + assert.equal(this.$(".topic-map").length, 0); } }); widgetTest("topic map - few posts", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { + this.set("args", { showTopicMap: true, topicPostsCount: 2, - participants: [ - {username: 'eviltrout'}, - {username: 'codinghorror'}, - ] + participants: [{ username: "eviltrout" }, { username: "codinghorror" }] }); }, test(assert) { - assert.equal(this.$('li.avatars a.poster').length, 0, 'shows no participants when collapsed'); + assert.equal( + this.$("li.avatars a.poster").length, + 0, + "shows no participants when collapsed" + ); - click('nav.buttons button'); + click("nav.buttons button"); andThen(() => { - assert.equal(this.$('.topic-map-expanded a.poster').length, 2, 'shows all when expanded'); + assert.equal( + this.$(".topic-map-expanded a.poster").length, + 2, + "shows all when expanded" + ); }); } }); @@ -713,26 +758,34 @@ widgetTest("topic map - few posts", { widgetTest("topic map - participants", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { + this.set("args", { showTopicMap: true, topicPostsCount: 10, participants: [ - {username: 'eviltrout'}, - {username: 'codinghorror'}, - {username: 'sam'}, - {username: 'ZogStrIP'}, + { username: "eviltrout" }, + { username: "codinghorror" }, + { username: "sam" }, + { username: "ZogStrIP" } ], - userFilters: ['sam', 'codinghorror'] + userFilters: ["sam", "codinghorror"] }); }, test(assert) { - assert.equal(this.$('li.avatars a.poster').length, 3, 'limits to three participants'); + assert.equal( + this.$("li.avatars a.poster").length, + 3, + "limits to three participants" + ); - click('nav.buttons button'); + click("nav.buttons button"); andThen(() => { - assert.equal(this.$('li.avatars a.poster').length, 0); - assert.equal(this.$('.topic-map-expanded a.poster').length, 4, 'shows all when expanded'); - assert.equal(this.$('a.poster.toggled').length, 2, 'two are toggled'); + assert.equal(this.$("li.avatars a.poster").length, 0); + assert.equal( + this.$(".topic-map-expanded a.poster").length, + 4, + "shows all when expanded" + ); + assert.equal(this.$("a.poster.toggled").length, 2, "two are toggled"); }); } }); @@ -740,34 +793,42 @@ widgetTest("topic map - participants", { widgetTest("topic map - links", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { + this.set("args", { showTopicMap: true, topicLinks: [ - {url: 'http://link1.example.com', clicks: 0}, - {url: 'http://link2.example.com', clicks: 0}, - {url: 'http://link3.example.com', clicks: 0}, - {url: 'http://link4.example.com', clicks: 0}, - {url: 'http://link5.example.com', clicks: 0}, - {url: 'http://link6.example.com', clicks: 0}, + { url: "http://link1.example.com", clicks: 0 }, + { url: "http://link2.example.com", clicks: 0 }, + { url: "http://link3.example.com", clicks: 0 }, + { url: "http://link4.example.com", clicks: 0 }, + { url: "http://link5.example.com", clicks: 0 }, + { url: "http://link6.example.com", clicks: 0 } ] }); }, test(assert) { - assert.equal(this.$('.topic-map').length, 1); - assert.equal(this.$('.map.map-collapsed').length, 1); - assert.equal(this.$('.topic-map-expanded').length, 0); + assert.equal(this.$(".topic-map").length, 1); + assert.equal(this.$(".map.map-collapsed").length, 1); + assert.equal(this.$(".topic-map-expanded").length, 0); - click('nav.buttons button'); + click("nav.buttons button"); andThen(() => { - assert.equal(this.$('.map.map-collapsed').length, 0); - assert.equal(this.$('.topic-map .d-icon-chevron-up').length, 1); - assert.equal(this.$('.topic-map-expanded').length, 1); - assert.equal(this.$('.topic-map-expanded .topic-link').length, 5, 'it limits the links displayed'); + assert.equal(this.$(".map.map-collapsed").length, 0); + assert.equal(this.$(".topic-map .d-icon-chevron-up").length, 1); + assert.equal(this.$(".topic-map-expanded").length, 1); + assert.equal( + this.$(".topic-map-expanded .topic-link").length, + 5, + "it limits the links displayed" + ); }); - click('.link-summary button'); + click(".link-summary button"); andThen(() => { - assert.equal(this.$('.topic-map-expanded .topic-link').length, 6, 'all links now shown'); + assert.equal( + this.$(".topic-map-expanded .topic-link").length, + 6, + "all links now shown" + ); }); } }); @@ -775,23 +836,24 @@ widgetTest("topic map - links", { widgetTest("topic map - no summary", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { showTopicMap: true }); + this.set("args", { showTopicMap: true }); }, test(assert) { - assert.equal(this.$('.toggle-summary').length, 0); + assert.equal(this.$(".toggle-summary").length, 0); } }); widgetTest("topic map - has summary", { - template: '{{mount-widget widget="post" args=args toggleSummary="toggleSummary"}}', + template: + '{{mount-widget widget="post" args=args toggleSummary="toggleSummary"}}', beforeEach() { - this.set('args', { showTopicMap: true, hasTopicSummary: true }); - this.on('toggleSummary', () => this.summaryToggled = true); + this.set("args", { showTopicMap: true, hasTopicSummary: true }); + this.on("toggleSummary", () => (this.summaryToggled = true)); }, test(assert) { - assert.equal(this.$('.toggle-summary').length, 1); + assert.equal(this.$(".toggle-summary").length, 1); - click('.toggle-summary button'); + click(".toggle-summary button"); andThen(() => assert.ok(this.summaryToggled)); } }); @@ -799,15 +861,15 @@ widgetTest("topic map - has summary", { widgetTest("pm map", { template: '{{mount-widget widget="post" args=args}}', beforeEach() { - this.set('args', { + this.set("args", { showTopicMap: true, showPMMap: true, allowedGroups: [], - allowedUsers: [ Ember.Object.create({ username: 'eviltrout' }) ] + allowedUsers: [Ember.Object.create({ username: "eviltrout" })] }); }, test(assert) { - assert.equal(this.$('.private-message-map').length, 1); - assert.equal(this.$('.private-message-map .user').length, 1); + assert.equal(this.$(".private-message-map").length, 1); + assert.equal(this.$(".private-message-map .user").length, 1); } }); diff --git a/test/javascripts/widgets/poster-name-test.js.es6 b/test/javascripts/widgets/poster-name-test.js.es6 index c8ab8391d7a..c618f6f32ba 100644 --- a/test/javascripts/widgets/poster-name-test.js.es6 +++ b/test/javascripts/widgets/poster-name-test.js.es6 @@ -1,57 +1,58 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; -moduleForWidget('poster-name'); +moduleForWidget("poster-name"); -widgetTest('basic rendering', { +widgetTest("basic rendering", { template: '{{mount-widget widget="poster-name" args=args}}', beforeEach() { - this.set('args', { - username: 'eviltrout', - usernameUrl: '/u/eviltrout', - name: 'Robin Ward', - user_title: 'Trout Master' }); + this.set("args", { + username: "eviltrout", + usernameUrl: "/u/eviltrout", + name: "Robin Ward", + user_title: "Trout Master" + }); }, test(assert) { - assert.ok(this.$('.names').length); - assert.ok(this.$('span.username').length); - assert.ok(this.$('a[data-user-card=eviltrout]').length); - assert.equal(this.$('.username a').text(), 'eviltrout'); - assert.equal(this.$('.full-name a').text(), 'Robin Ward'); - assert.equal(this.$('.user-title').text(), 'Trout Master'); + assert.ok(this.$(".names").length); + assert.ok(this.$("span.username").length); + assert.ok(this.$("a[data-user-card=eviltrout]").length); + assert.equal(this.$(".username a").text(), "eviltrout"); + assert.equal(this.$(".full-name a").text(), "Robin Ward"); + assert.equal(this.$(".user-title").text(), "Trout Master"); } }); -widgetTest('extra classes and glyphs', { +widgetTest("extra classes and glyphs", { template: '{{mount-widget widget="poster-name" args=args}}', beforeEach() { - this.set('args', { - username: 'eviltrout', - usernameUrl: '/u/eviltrout', + this.set("args", { + username: "eviltrout", + usernameUrl: "/u/eviltrout", staff: true, admin: true, moderator: true, new_user: true, - primary_group_name: 'fish' - }); + primary_group_name: "fish" + }); }, test(assert) { - assert.ok(this.$('span.staff').length); - assert.ok(this.$('span.admin').length); - assert.ok(this.$('span.moderator').length); - assert.ok(this.$('.d-icon-shield').length); - assert.ok(this.$('span.new-user').length); - assert.ok(this.$('span.fish').length); + assert.ok(this.$("span.staff").length); + assert.ok(this.$("span.admin").length); + assert.ok(this.$("span.moderator").length); + assert.ok(this.$(".d-icon-shield").length); + assert.ok(this.$("span.new-user").length); + assert.ok(this.$("span.fish").length); } }); -widgetTest('disable display name on posts', { +widgetTest("disable display name on posts", { template: '{{mount-widget widget="poster-name" args=args}}', beforeEach() { this.siteSettings.display_name_on_posts = false; - this.set('args', { username: 'eviltrout', name: 'Robin Ward' }); + this.set("args", { username: "eviltrout", name: "Robin Ward" }); }, test(assert) { - assert.equal(this.$('.full-name').length, 0); + assert.equal(this.$(".full-name").length, 0); } }); @@ -60,9 +61,9 @@ widgetTest("doesn't render a name if it's similar to the username", { beforeEach() { this.siteSettings.prioritize_username_in_ux = true; this.siteSettings.display_name_on_posts = true; - this.set('args', { username: 'eviltrout', name: 'evil-trout' }); + this.set("args", { username: "eviltrout", name: "evil-trout" }); }, test(assert) { - assert.equal(this.$('.second').length, 0); + assert.equal(this.$(".second").length, 0); } }); diff --git a/test/javascripts/widgets/topic-participant-test.js.es6 b/test/javascripts/widgets/topic-participant-test.js.es6 index 67ba90a0635..88f309da1e1 100644 --- a/test/javascripts/widgets/topic-participant-test.js.es6 +++ b/test/javascripts/widgets/topic-participant-test.js.es6 @@ -1,43 +1,49 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; -moduleForWidget('topic-participant'); +moduleForWidget("topic-participant"); -widgetTest('one post', { +widgetTest("one post", { template: '{{mount-widget widget="topic-participant" args=args}}', beforeEach() { - this.set('args', { - username: 'test', - avatar_template: '/images/avatar.png', + this.set("args", { + username: "test", + avatar_template: "/images/avatar.png", post_count: 1 }); }, test(assert) { - assert.ok(exists('a.poster.trigger-user-card')); - assert.ok(!exists('span.post-count'), "don't show count for only 1 post"); - assert.ok(!exists('.avatar-flair'), "no avatar flair"); + assert.ok(exists("a.poster.trigger-user-card")); + assert.ok(!exists("span.post-count"), "don't show count for only 1 post"); + assert.ok(!exists(".avatar-flair"), "no avatar flair"); } }); -widgetTest('many posts, a primary group with flair', { +widgetTest("many posts, a primary group with flair", { template: '{{mount-widget widget="topic-participant" args=args}}', beforeEach() { - this.set('args', { - username: 'test', - avatar_template: '/images/avatar.png', + this.set("args", { + username: "test", + avatar_template: "/images/avatar.png", post_count: 5, - primary_group_name: 'devs', + primary_group_name: "devs", primary_group_flair_url: "/images/d-logo-sketch-small.png", primary_group_flair_bg_color: "222" }); }, test(assert) { - assert.ok(exists('a.poster.trigger-user-card')); - assert.ok(exists('span.post-count'), "show count for many posts"); - assert.ok(exists('.group-devs a.poster'), "add class for the group outside the link"); - assert.ok(exists('.avatar-flair.avatar-flair-devs'), "show flair with group class"); + assert.ok(exists("a.poster.trigger-user-card")); + assert.ok(exists("span.post-count"), "show count for many posts"); + assert.ok( + exists(".group-devs a.poster"), + "add class for the group outside the link" + ); + assert.ok( + exists(".avatar-flair.avatar-flair-devs"), + "show flair with group class" + ); } }); diff --git a/test/javascripts/widgets/user-menu-test.js.es6 b/test/javascripts/widgets/user-menu-test.js.es6 index 19b4640a658..951e0451859 100644 --- a/test/javascripts/widgets/user-menu-test.js.es6 +++ b/test/javascripts/widgets/user-menu-test.js.es6 @@ -1,80 +1,81 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; -moduleForWidget('user-menu'); +moduleForWidget("user-menu"); -widgetTest('basics', { +widgetTest("basics", { template: '{{mount-widget widget="user-menu"}}', test(assert) { - assert.ok(this.$('.user-menu').length); - assert.ok(this.$('.user-activity-link').length); - assert.ok(this.$('.user-bookmarks-link').length); - assert.ok(this.$('.user-preferences-link').length); - assert.ok(this.$('.notifications').length); - assert.ok(this.$('.dismiss-link').length); + assert.ok(this.$(".user-menu").length); + assert.ok(this.$(".user-activity-link").length); + assert.ok(this.$(".user-bookmarks-link").length); + assert.ok(this.$(".user-preferences-link").length); + assert.ok(this.$(".notifications").length); + assert.ok(this.$(".dismiss-link").length); } }); -widgetTest('log out', { +widgetTest("log out", { template: '{{mount-widget widget="user-menu" logout="logout"}}', beforeEach() { - this.on('logout', () => this.loggedOut = true); + this.on("logout", () => (this.loggedOut = true)); }, test(assert) { - assert.ok(this.$('.logout').length); + assert.ok(this.$(".logout").length); - click('.logout'); + click(".logout"); andThen(() => { assert.ok(this.loggedOut); }); } }); -widgetTest('private messages - disabled', { +widgetTest("private messages - disabled", { template: '{{mount-widget widget="user-menu"}}', beforeEach() { this.siteSettings.enable_personal_messages = false; }, test(assert) { - assert.ok(!this.$('.user-pms-link').length); + assert.ok(!this.$(".user-pms-link").length); } }); -widgetTest('private messages - enabled', { +widgetTest("private messages - enabled", { template: '{{mount-widget widget="user-menu"}}', beforeEach() { this.siteSettings.enable_personal_messages = true; }, test(assert) { - assert.ok(this.$('.user-pms-link').length); + assert.ok(this.$(".user-pms-link").length); } }); -widgetTest('anonymous', { - template: '{{mount-widget widget="user-menu" toggleAnonymous="toggleAnonymous"}}', +widgetTest("anonymous", { + template: + '{{mount-widget widget="user-menu" toggleAnonymous="toggleAnonymous"}}', beforeEach() { this.currentUser.setProperties({ is_anonymous: false, trust_level: 3 }); this.siteSettings.allow_anonymous_posting = true; this.siteSettings.anonymous_posting_min_trust_level = 3; - this.on('toggleAnonymous', () => this.anonymous = true); + this.on("toggleAnonymous", () => (this.anonymous = true)); }, test(assert) { - assert.ok(this.$('.enable-anonymous').length); - click('.enable-anonymous'); + assert.ok(this.$(".enable-anonymous").length); + click(".enable-anonymous"); andThen(() => { assert.ok(this.anonymous); }); } }); -widgetTest('anonymous - disabled', { +widgetTest("anonymous - disabled", { template: '{{mount-widget widget="user-menu"}}', beforeEach() { @@ -82,23 +83,24 @@ widgetTest('anonymous - disabled', { }, test(assert) { - assert.ok(!this.$('.enable-anonymous').length); + assert.ok(!this.$(".enable-anonymous").length); } }); -widgetTest('anonymous - switch back', { - template: '{{mount-widget widget="user-menu" toggleAnonymous="toggleAnonymous"}}', +widgetTest("anonymous - switch back", { + template: + '{{mount-widget widget="user-menu" toggleAnonymous="toggleAnonymous"}}', beforeEach() { this.currentUser.setProperties({ is_anonymous: true }); this.siteSettings.allow_anonymous_posting = true; - this.on('toggleAnonymous', () => this.anonymous = true); + this.on("toggleAnonymous", () => (this.anonymous = true)); }, test(assert) { - assert.ok(this.$('.disable-anonymous').length); - click('.disable-anonymous'); + assert.ok(this.$(".disable-anonymous").length); + click(".disable-anonymous"); andThen(() => { assert.ok(this.anonymous); }); diff --git a/test/javascripts/widgets/widget-test.js.es6 b/test/javascripts/widgets/widget-test.js.es6 index 78502f6ccdd..b8afd17cc5d 100644 --- a/test/javascripts/widgets/widget-test.js.es6 +++ b/test/javascripts/widgets/widget-test.js.es6 @@ -1,24 +1,24 @@ -import { moduleForWidget, widgetTest } from 'helpers/widget-test'; -import { createWidget } from 'discourse/widgets/widget'; -import { withPluginApi } from 'discourse/lib/plugin-api'; -import hbs from 'discourse/widgets/hbs-compiler'; +import { moduleForWidget, widgetTest } from "helpers/widget-test"; +import { createWidget } from "discourse/widgets/widget"; +import { withPluginApi } from "discourse/lib/plugin-api"; +import hbs from "discourse/widgets/hbs-compiler"; -moduleForWidget('base'); +moduleForWidget("base"); -widgetTest('widget attributes are passed in via args', { +widgetTest("widget attributes are passed in via args", { template: `{{mount-widget widget="hello-test" args=args}}`, beforeEach() { - createWidget('hello-test', { - tagName: 'div.test', + createWidget("hello-test", { + tagName: "div.test", template: hbs`Hello {{attrs.name}}` }); - this.set('args', { name: 'Robin' }); + this.set("args", { name: "Robin" }); }, test(assert) { - assert.equal(this.$('.test').text(), "Hello Robin"); + assert.equal(this.$(".test").text(), "Hello Robin"); } }); @@ -26,15 +26,15 @@ widgetTest("hbs template - no tagName", { template: `{{mount-widget widget="hbs-test" args=args}}`, beforeEach() { - createWidget('hbs-test', { + createWidget("hbs-test", { template: hbs`
      Hello {{attrs.name}}
      ` }); - this.set('args', { name: 'Robin' }); + this.set("args", { name: "Robin" }); }, test(assert) { - assert.equal(this.$('div.test').text(), "Hello Robin"); + assert.equal(this.$("div.test").text(), "Hello Robin"); } }); @@ -42,84 +42,87 @@ widgetTest("hbs template - with tagName", { template: `{{mount-widget widget="hbs-test" args=args}}`, beforeEach() { - createWidget('hbs-test', { - tagName: 'div.test', + createWidget("hbs-test", { + tagName: "div.test", template: hbs`Hello {{attrs.name}}` }); - this.set('args', { name: 'Robin' }); + this.set("args", { name: "Robin" }); }, test(assert) { - assert.equal(this.$('div.test').text(), "Hello Robin"); + assert.equal(this.$("div.test").text(), "Hello Robin"); } }); -widgetTest('buildClasses', { +widgetTest("buildClasses", { template: `{{mount-widget widget="classname-test" args=args}}`, beforeEach() { - createWidget('classname-test', { - tagName: 'div.test', + createWidget("classname-test", { + tagName: "div.test", buildClasses(attrs) { - return ['static', attrs.dynamic]; + return ["static", attrs.dynamic]; } }); - this.set('args', { dynamic: 'cool-class' }); + this.set("args", { dynamic: "cool-class" }); }, test(assert) { - assert.ok(this.$('.test.static.cool-class').length, 'it has all the classes'); + assert.ok( + this.$(".test.static.cool-class").length, + "it has all the classes" + ); } }); -widgetTest('buildAttributes', { +widgetTest("buildAttributes", { template: `{{mount-widget widget="attributes-test" args=args}}`, beforeEach() { - createWidget('attributes-test', { - tagName: 'div.test', + createWidget("attributes-test", { + tagName: "div.test", buildAttributes(attrs) { - return { "data-evil": 'trout', "aria-label": attrs.label }; + return { "data-evil": "trout", "aria-label": attrs.label }; } }); - this.set('args', { label: 'accessibility' }); + this.set("args", { label: "accessibility" }); }, test(assert) { - assert.ok(this.$('.test[data-evil=trout]').length); - assert.ok(this.$('.test[aria-label=accessibility]').length); + assert.ok(this.$(".test[data-evil=trout]").length); + assert.ok(this.$(".test[aria-label=accessibility]").length); } }); -widgetTest('buildId', { +widgetTest("buildId", { template: `{{mount-widget widget="id-test" args=args}}`, beforeEach() { - createWidget('id-test', { + createWidget("id-test", { buildId(attrs) { return `test-${attrs.id}`; } }); - this.set('args', { id: 1234 }); + this.set("args", { id: 1234 }); }, test(assert) { - assert.ok(this.$('#test-1234').length); + assert.ok(this.$("#test-1234").length); } }); -widgetTest('widget state', { +widgetTest("widget state", { template: `{{mount-widget widget="state-test"}}`, beforeEach() { - createWidget('state-test', { - tagName: 'button.test', + createWidget("state-test", { + tagName: "button.test", buildKey: () => `button-test`, template: hbs`{{state.clicks}} clicks`, @@ -134,23 +137,23 @@ widgetTest('widget state', { }, test(assert) { - assert.ok(this.$('button.test').length, 'it renders the button'); - assert.equal(this.$('button.test').text(), "0 clicks"); + assert.ok(this.$("button.test").length, "it renders the button"); + assert.equal(this.$("button.test").text(), "0 clicks"); - click(this.$('button')); + click(this.$("button")); andThen(() => { - assert.equal(this.$('button.test').text(), "1 clicks"); + assert.equal(this.$("button.test").text(), "1 clicks"); }); } }); -widgetTest('widget update with promise', { +widgetTest("widget update with promise", { template: `{{mount-widget widget="promise-test"}}`, beforeEach() { - createWidget('promise-test', { - tagName: 'button.test', - buildKey: () => 'promise-test', + createWidget("promise-test", { + tagName: "button.test", + buildKey: () => "promise-test", template: hbs` {{#if state.name}} {{state.name}} @@ -171,30 +174,40 @@ widgetTest('widget update with promise', { }, test(assert) { - assert.equal(this.$('button.test').text().trim(), "No name"); + assert.equal( + this.$("button.test") + .text() + .trim(), + "No name" + ); - click(this.$('button')); + click(this.$("button")); andThen(() => { - assert.equal(this.$('button.test').text().trim(), "Robin"); + assert.equal( + this.$("button.test") + .text() + .trim(), + "Robin" + ); }); } }); -widgetTest('widget attaching', { +widgetTest("widget attaching", { template: `{{mount-widget widget="attach-test"}}`, beforeEach() { - createWidget('test-embedded', { tagName: 'div.embedded' }); + createWidget("test-embedded", { tagName: "div.embedded" }); - createWidget('attach-test', { - tagName: 'div.container', + createWidget("attach-test", { + tagName: "div.container", template: hbs`{{attach widget="test-embedded" attrs=attrs}}` }); }, test(assert) { - assert.ok(this.$('.container').length, "renders container"); - assert.ok(this.$('.container .embedded').length, "renders attached"); + assert.ok(this.$(".container").length, "renders container"); + assert.ok(this.$(".container .embedded").length, "renders attached"); } }); @@ -202,13 +215,13 @@ widgetTest("handlebars d-icon", { template: `{{mount-widget widget="hbs-icon-test" args=args}}`, beforeEach() { - createWidget('hbs-icon-test', { + createWidget("hbs-icon-test", { template: hbs`{{d-icon "arrow-down"}}` }); }, test(assert) { - assert.equal(this.$('i.fa.fa-arrow-down').length, 1); + assert.equal(this.$("i.fa.fa-arrow-down").length, 1); } }); @@ -216,34 +229,36 @@ widgetTest("handlebars i18n", { template: `{{mount-widget widget="hbs-i18n-test" args=args}}`, beforeEach() { - createWidget('hbs-i18n-test', { + createWidget("hbs-i18n-test", { template: hbs` {{i18n "hbs_test0"}} {{i18n attrs.key}} test ` }); - I18n.extras = [ { - "hbs_test0": "evil", - "hbs_test1": "trout" - } ]; - this.set('args', { key: 'hbs_test1' }); + I18n.extras = [ + { + hbs_test0: "evil", + hbs_test1: "trout" + } + ]; + this.set("args", { key: "hbs_test1" }); }, test(assert) { // comin up - assert.equal(this.$('span.string').text(), 'evil'); - assert.equal(this.$('span.var').text(), 'trout'); - assert.equal(this.$('a').prop('title'), 'evil'); + assert.equal(this.$("span.string").text(), "evil"); + assert.equal(this.$("span.var").text(), "trout"); + assert.equal(this.$("a").prop("title"), "evil"); } }); -widgetTest('handlebars #each', { +widgetTest("handlebars #each", { template: `{{mount-widget widget="hbs-each-test" args=args}}`, beforeEach() { - createWidget('hbs-each-test', { - tagName: 'ul', + createWidget("hbs-each-test", { + tagName: "ul", template: hbs` {{#each attrs.items as |item|}}
    • {{item}}
    • @@ -251,77 +266,76 @@ widgetTest('handlebars #each', { ` }); - this.set('args', { - items: ['one', 'two', 'three'] + this.set("args", { + items: ["one", "two", "three"] }); }, test(assert) { - assert.equal(this.$('ul li').length, 3); - assert.equal(this.$('ul li:eq(0)').text(), "one"); + assert.equal(this.$("ul li").length, 3); + assert.equal(this.$("ul li:eq(0)").text(), "one"); } - }); -widgetTest('widget decorating', { +widgetTest("widget decorating", { template: `{{mount-widget widget="decorate-test"}}`, beforeEach() { - createWidget('decorate-test', { - tagName: 'div.decorate', + createWidget("decorate-test", { + tagName: "div.decorate", template: hbs`main content` }); - withPluginApi('0.1', api => { - api.decorateWidget('decorate-test:before', dec => { - return dec.h('b', 'before'); + withPluginApi("0.1", api => { + api.decorateWidget("decorate-test:before", dec => { + return dec.h("b", "before"); }); - api.decorateWidget('decorate-test:after', dec => { - return dec.h('i', 'after'); + api.decorateWidget("decorate-test:after", dec => { + return dec.h("i", "after"); }); }); }, test(assert) { - assert.ok(this.$('.decorate').length); - assert.equal(this.$('.decorate b').text(), 'before'); - assert.equal(this.$('.decorate i').text(), 'after'); + assert.ok(this.$(".decorate").length); + assert.equal(this.$(".decorate b").text(), "before"); + assert.equal(this.$(".decorate i").text(), "after"); } }); -widgetTest('widget settings', { +widgetTest("widget settings", { template: `{{mount-widget widget="settings-test"}}`, beforeEach() { - createWidget('settings-test', { - tagName: 'div.settings', + createWidget("settings-test", { + tagName: "div.settings", template: hbs`age is {{settings.age}}`, settings: { age: 36 } }); }, test(assert) { - assert.equal(this.$('.settings').text(), 'age is 36'); + assert.equal(this.$(".settings").text(), "age is 36"); } }); -widgetTest('override settings', { +widgetTest("override settings", { template: `{{mount-widget widget="ov-settings-test"}}`, beforeEach() { - createWidget('ov-settings-test', { - tagName: 'div.settings', + createWidget("ov-settings-test", { + tagName: "div.settings", template: hbs`age is {{settings.age}}`, - settings: { age: 36 }, + settings: { age: 36 } }); - withPluginApi('0.1', api => { - api.changeWidgetSetting('ov-settings-test', 'age', 37); + withPluginApi("0.1", api => { + api.changeWidgetSetting("ov-settings-test", "age", 37); }); }, test(assert) { - assert.equal(this.$('.settings').text(), 'age is 37'); + assert.equal(this.$(".settings").text(), "age is 37"); } });