missing prettified files
This commit is contained in:
parent
2268e29c26
commit
60ff0e9b8c
app/assets/javascripts/select-kit/components
plugins
discourse-details
assets/javascripts
test/javascripts
discourse-local-dates/assets/javascripts
discourse-narrative-bot/assets/javascripts/initializers
discourse-presence/assets/javascripts/discourse/components
lazyYT/assets/javascripts/initializers
poll
assets/javascripts
controllers
initializers
lib
widgets
test/javascripts
test/javascripts/components
|
@ -1,5 +1,8 @@
|
|||
import NotificationOptionsComponent from "select-kit/components/notifications-button";
|
||||
import { default as computed, on } from "ember-addons/ember-computed-decorators";
|
||||
import {
|
||||
default as computed,
|
||||
on
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
import { topicLevels } from "discourse/lib/notification-levels";
|
||||
|
||||
export default NotificationOptionsComponent.extend({
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { withPluginApi } from 'discourse/lib/plugin-api';
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
|
||||
function initializeDetails(api) {
|
||||
api.decorateCooked($elem => $("details", $elem).details());
|
||||
|
||||
api.addToolbarPopupMenuOptionsCallback(() => {
|
||||
return {
|
||||
action: 'insertDetails',
|
||||
icon: 'caret-right',
|
||||
label: 'details.title'
|
||||
action: "insertDetails",
|
||||
icon: "caret-right",
|
||||
label: "details.title"
|
||||
};
|
||||
});
|
||||
|
||||
api.modifyClass('controller:composer', {
|
||||
api.modifyClass("controller:composer", {
|
||||
actions: {
|
||||
insertDetails() {
|
||||
this.get("toolbarEvent").applySurround(
|
||||
|
@ -29,6 +29,6 @@ export default {
|
|||
name: "apply-details",
|
||||
|
||||
initialize() {
|
||||
withPluginApi('0.8.7', initializeDetails);
|
||||
withPluginApi("0.8.7", initializeDetails);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
const rule = {
|
||||
tag: 'details',
|
||||
tag: "details",
|
||||
before: function(state, tagInfo) {
|
||||
const attrs = tagInfo.attrs;
|
||||
state.push('bbcode_open', 'details', 1);
|
||||
state.push('bbcode_open', 'summary', 1);
|
||||
state.push("bbcode_open", "details", 1);
|
||||
state.push("bbcode_open", "summary", 1);
|
||||
|
||||
let token = state.push('text', '', 0);
|
||||
token.content = attrs['_default'] || '';
|
||||
let token = state.push("text", "", 0);
|
||||
token.content = attrs["_default"] || "";
|
||||
|
||||
state.push('bbcode_close', 'summary', -1);
|
||||
state.push("bbcode_close", "summary", -1);
|
||||
},
|
||||
|
||||
after: function(state) {
|
||||
state.push('bbcode_close', 'details', -1);
|
||||
state.push("bbcode_close", "details", -1);
|
||||
}
|
||||
};
|
||||
|
||||
export function setup(helper) {
|
||||
helper.whiteList([
|
||||
'summary',
|
||||
'summary[title]',
|
||||
'details',
|
||||
'details[open]',
|
||||
'details.elided'
|
||||
"summary",
|
||||
"summary[title]",
|
||||
"details",
|
||||
"details[open]",
|
||||
"details.elided"
|
||||
]);
|
||||
|
||||
helper.registerPlugin(md => {
|
||||
md.block.bbcode.ruler.push('details', rule);
|
||||
md.block.bbcode.ruler.push("details", rule);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { acceptance } from "helpers/qunit-helpers";
|
||||
import { clearPopupMenuOptionsCallback } from "discourse/controllers/composer";
|
||||
|
||||
acceptance('Details Button', {
|
||||
acceptance("Details Button", {
|
||||
loggedIn: true,
|
||||
beforeEach: function() {
|
||||
clearPopupMenuOptionsCallback();
|
||||
|
@ -12,23 +12,25 @@ function findTextarea() {
|
|||
return find(".d-editor-input")[0];
|
||||
}
|
||||
|
||||
test('details button', (assert) => {
|
||||
const popupMenu = selectKit('.toolbar-popup-menu-options');
|
||||
test("details button", assert => {
|
||||
const popupMenu = selectKit(".toolbar-popup-menu-options");
|
||||
|
||||
visit("/");
|
||||
click('#create-topic');
|
||||
click("#create-topic");
|
||||
|
||||
popupMenu.expand().selectRowByValue('insertDetails');
|
||||
popupMenu.expand().selectRowByValue("insertDetails");
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(
|
||||
find(".d-editor-input").val(),
|
||||
`\n[details="${I18n.t("composer.details_title")}"]\n${I18n.t("composer.details_text")}\n[/details]\n`,
|
||||
'it should contain the right output'
|
||||
`\n[details="${I18n.t("composer.details_title")}"]\n${I18n.t(
|
||||
"composer.details_text"
|
||||
)}\n[/details]\n`,
|
||||
"it should contain the right output"
|
||||
);
|
||||
});
|
||||
|
||||
fillIn('.d-editor-input', "This is my title");
|
||||
fillIn(".d-editor-input", "This is my title");
|
||||
|
||||
andThen(() => {
|
||||
const textarea = findTextarea();
|
||||
|
@ -36,21 +38,31 @@ test('details button', (assert) => {
|
|||
textarea.selectionEnd = textarea.value.length;
|
||||
});
|
||||
|
||||
popupMenu.expand().selectRowByValue('insertDetails');
|
||||
popupMenu.expand().selectRowByValue("insertDetails");
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(
|
||||
find(".d-editor-input").val(),
|
||||
`\n[details="${I18n.t("composer.details_title")}"]\nThis is my title\n[/details]\n`,
|
||||
'it should contain the right selected output'
|
||||
`\n[details="${I18n.t(
|
||||
"composer.details_title"
|
||||
)}"]\nThis is my title\n[/details]\n`,
|
||||
"it should contain the right selected output"
|
||||
);
|
||||
|
||||
const textarea = findTextarea();
|
||||
assert.equal(textarea.selectionStart, 21, 'it should start highlighting at the right position');
|
||||
assert.equal(textarea.selectionEnd, 37, 'it should end highlighting at the right position');
|
||||
assert.equal(
|
||||
textarea.selectionStart,
|
||||
21,
|
||||
"it should start highlighting at the right position"
|
||||
);
|
||||
assert.equal(
|
||||
textarea.selectionEnd,
|
||||
37,
|
||||
"it should end highlighting at the right position"
|
||||
);
|
||||
});
|
||||
|
||||
fillIn('.d-editor-input', "Before some text in between After");
|
||||
fillIn(".d-editor-input", "Before some text in between After");
|
||||
|
||||
andThen(() => {
|
||||
const textarea = findTextarea();
|
||||
|
@ -58,21 +70,31 @@ test('details button', (assert) => {
|
|||
textarea.selectionEnd = 28;
|
||||
});
|
||||
|
||||
popupMenu.expand().selectRowByValue('insertDetails');
|
||||
popupMenu.expand().selectRowByValue("insertDetails");
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(
|
||||
find(".d-editor-input").val(),
|
||||
`Before \n[details="${I18n.t("composer.details_title")}"]\nsome text in between\n[/details]\n After`,
|
||||
'it should contain the right output'
|
||||
`Before \n[details="${I18n.t(
|
||||
"composer.details_title"
|
||||
)}"]\nsome text in between\n[/details]\n After`,
|
||||
"it should contain the right output"
|
||||
);
|
||||
|
||||
const textarea = findTextarea();
|
||||
assert.equal(textarea.selectionStart, 28, 'it should start highlighting at the right position');
|
||||
assert.equal(textarea.selectionEnd, 48, 'it should end highlighting at the right position');
|
||||
assert.equal(
|
||||
textarea.selectionStart,
|
||||
28,
|
||||
"it should start highlighting at the right position"
|
||||
);
|
||||
assert.equal(
|
||||
textarea.selectionEnd,
|
||||
48,
|
||||
"it should end highlighting at the right position"
|
||||
);
|
||||
});
|
||||
|
||||
fillIn('.d-editor-input', "Before \nsome text in between\n After");
|
||||
fillIn(".d-editor-input", "Before \nsome text in between\n After");
|
||||
|
||||
andThen(() => {
|
||||
const textarea = findTextarea();
|
||||
|
@ -80,28 +102,38 @@ test('details button', (assert) => {
|
|||
textarea.selectionEnd = 29;
|
||||
});
|
||||
|
||||
popupMenu.expand().selectRowByValue('insertDetails');
|
||||
popupMenu.expand().selectRowByValue("insertDetails");
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(
|
||||
find(".d-editor-input").val(),
|
||||
`Before \n\n[details="${I18n.t("composer.details_title")}"]\nsome text in between\n[/details]\n\n After`,
|
||||
'it should contain the right output'
|
||||
`Before \n\n[details="${I18n.t(
|
||||
"composer.details_title"
|
||||
)}"]\nsome text in between\n[/details]\n\n After`,
|
||||
"it should contain the right output"
|
||||
);
|
||||
|
||||
const textarea = findTextarea();
|
||||
assert.equal(textarea.selectionStart, 29, 'it should start highlighting at the right position');
|
||||
assert.equal(textarea.selectionEnd, 49, 'it should end highlighting at the right position');
|
||||
assert.equal(
|
||||
textarea.selectionStart,
|
||||
29,
|
||||
"it should start highlighting at the right position"
|
||||
);
|
||||
assert.equal(
|
||||
textarea.selectionEnd,
|
||||
49,
|
||||
"it should end highlighting at the right position"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('details button surrounds all selected text in a single details block', (assert) => {
|
||||
const multilineInput = 'first line\n\nsecond line\n\nthird line';
|
||||
const popupMenu = selectKit('.toolbar-popup-menu-options');
|
||||
test("details button surrounds all selected text in a single details block", assert => {
|
||||
const multilineInput = "first line\n\nsecond line\n\nthird line";
|
||||
const popupMenu = selectKit(".toolbar-popup-menu-options");
|
||||
|
||||
visit("/");
|
||||
click('#create-topic');
|
||||
fillIn('.d-editor-input', multilineInput);
|
||||
click("#create-topic");
|
||||
fillIn(".d-editor-input", multilineInput);
|
||||
|
||||
andThen(() => {
|
||||
const textarea = findTextarea();
|
||||
|
@ -109,13 +141,15 @@ test('details button surrounds all selected text in a single details block', (as
|
|||
textarea.selectionEnd = textarea.value.length;
|
||||
});
|
||||
|
||||
popupMenu.expand().selectRowByValue('insertDetails');
|
||||
popupMenu.expand().selectRowByValue("insertDetails");
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(
|
||||
find(".d-editor-input").val(),
|
||||
`\n[details="${I18n.t('composer.details_title')}"]\n${multilineInput}\n[/details]\n`,
|
||||
'it should contain the right output'
|
||||
`\n[details="${I18n.t(
|
||||
"composer.details_title"
|
||||
)}"]\n${multilineInput}\n[/details]\n`,
|
||||
"it should contain the right output"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,31 +1,38 @@
|
|||
import { default as PrettyText, buildOptions } from 'pretty-text/pretty-text';
|
||||
import { default as PrettyText, buildOptions } from "pretty-text/pretty-text";
|
||||
|
||||
QUnit.module("lib:details-cooked-test");
|
||||
|
||||
const defaultOpts = buildOptions({
|
||||
siteSettings: {
|
||||
enable_emoji: 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"
|
||||
},
|
||||
censoredWords: 'shucks|whiz|whizzer',
|
||||
censoredWords: "shucks|whiz|whizzer",
|
||||
getURL: url => url
|
||||
});
|
||||
|
||||
|
||||
test("details", assert => {
|
||||
const cooked = (input, expected, text) => {
|
||||
assert.equal(new PrettyText(defaultOpts).cook(input), expected.replace(/\/>/g, ">"), text);
|
||||
assert.equal(
|
||||
new PrettyText(defaultOpts).cook(input),
|
||||
expected.replace(/\/>/g, ">"),
|
||||
text
|
||||
);
|
||||
};
|
||||
cooked(`<details><summary>Info</summary>coucou</details>`,
|
||||
`<details><summary>Info</summary>coucou</details>`,
|
||||
"manual HTML for details");
|
||||
cooked(
|
||||
`<details><summary>Info</summary>coucou</details>`,
|
||||
`<details><summary>Info</summary>coucou</details>`,
|
||||
"manual HTML for details"
|
||||
);
|
||||
|
||||
cooked("[details=testing]\ntest\n[/details]",
|
||||
`<details>
|
||||
cooked(
|
||||
"[details=testing]\ntest\n[/details]",
|
||||
`<details>
|
||||
<summary>
|
||||
testing</summary>
|
||||
<p>test</p>
|
||||
</details>`);
|
||||
</details>`
|
||||
);
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ function initializeDiscourseLocalDates(api) {
|
|||
};
|
||||
});
|
||||
|
||||
api.modifyClass('controller:composer', {
|
||||
api.modifyClass("controller:composer", {
|
||||
actions: {
|
||||
insertDiscourseLocalDate() {
|
||||
showModal("discourse-local-dates-create-modal").setProperties({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { parseBBCodeTag } from 'pretty-text/engines/discourse-markdown/bbcode-block';
|
||||
import { parseBBCodeTag } from "pretty-text/engines/discourse-markdown/bbcode-block";
|
||||
|
||||
function addLocalDate(buffer, matches, state) {
|
||||
let token;
|
||||
|
@ -10,7 +10,11 @@ function addLocalDate(buffer, matches, state) {
|
|||
timezones: ""
|
||||
};
|
||||
|
||||
let parsed = parseBBCodeTag("[date date" + matches[1] + "]", 0, matches[1].length + 11);
|
||||
let parsed = parseBBCodeTag(
|
||||
"[date date" + matches[1] + "]",
|
||||
0,
|
||||
matches[1].length + 11
|
||||
);
|
||||
|
||||
config.date = parsed.attrs.date;
|
||||
config.time = parsed.attrs.time;
|
||||
|
@ -18,53 +22,58 @@ function addLocalDate(buffer, matches, state) {
|
|||
config.format = parsed.attrs.format || config.format;
|
||||
config.timezones = parsed.attrs.timezones || config.timezones;
|
||||
|
||||
token = new state.Token('span_open', 'span', 1);
|
||||
token = new state.Token("span_open", "span", 1);
|
||||
token.attrs = [
|
||||
['class', 'discourse-local-date'],
|
||||
['data-date', config.date],
|
||||
['data-time', config.time],
|
||||
['data-format', config.format],
|
||||
['data-timezones', config.timezones],
|
||||
["class", "discourse-local-date"],
|
||||
["data-date", config.date],
|
||||
["data-time", config.time],
|
||||
["data-format", config.format],
|
||||
["data-timezones", config.timezones]
|
||||
];
|
||||
if (config.recurring) {
|
||||
token.attrs.push(['data-recurring', config.recurring]);
|
||||
token.attrs.push(["data-recurring", config.recurring]);
|
||||
}
|
||||
buffer.push(token);
|
||||
|
||||
const previews = config.timezones.split("|").filter(t => t).map(timezone => {
|
||||
const dateTime = moment
|
||||
.utc(`${config.date} ${config.time}`, "YYYY-MM-DD HH:mm")
|
||||
.tz(timezone)
|
||||
.format(config.format);
|
||||
const previews = config.timezones
|
||||
.split("|")
|
||||
.filter(t => t)
|
||||
.map(timezone => {
|
||||
const dateTime = moment
|
||||
.utc(`${config.date} ${config.time}`, "YYYY-MM-DD HH:mm")
|
||||
.tz(timezone)
|
||||
.format(config.format);
|
||||
|
||||
const formattedTimezone = timezone.replace("/", ": ").replace("_", " ");
|
||||
const formattedTimezone = timezone.replace("/", ": ").replace("_", " ");
|
||||
|
||||
if (dateTime.match(/TZ/)) {
|
||||
return dateTime.replace("TZ", formattedTimezone);
|
||||
} else {
|
||||
return `${dateTime} (${formattedTimezone})`;
|
||||
}
|
||||
});
|
||||
if (dateTime.match(/TZ/)) {
|
||||
return dateTime.replace("TZ", formattedTimezone);
|
||||
} else {
|
||||
return `${dateTime} (${formattedTimezone})`;
|
||||
}
|
||||
});
|
||||
|
||||
token.attrs.push(['data-email-preview', previews[0]]);
|
||||
token.attrs.push(["data-email-preview", previews[0]]);
|
||||
|
||||
token = new state.Token('text', '', 0);
|
||||
token = new state.Token("text", "", 0);
|
||||
token.content = previews.join(", ");
|
||||
buffer.push(token);
|
||||
|
||||
token = new state.Token('span_close', 'span', -1);
|
||||
token = new state.Token("span_close", "span", -1);
|
||||
buffer.push(token);
|
||||
}
|
||||
|
||||
export function setup(helper) {
|
||||
helper.whiteList([
|
||||
'span.discourse-local-date',
|
||||
'span[data-*]',
|
||||
'span[title]'
|
||||
"span.discourse-local-date",
|
||||
"span[data-*]",
|
||||
"span[title]"
|
||||
]);
|
||||
|
||||
helper.registerOptions((opts, siteSettings) => {
|
||||
opts.features['discourse-local-dates'] = !!siteSettings.discourse_local_dates_enabled;
|
||||
opts.features[
|
||||
"discourse-local-dates"
|
||||
] = !!siteSettings.discourse_local_dates_enabled;
|
||||
});
|
||||
|
||||
helper.registerPlugin(md => {
|
||||
|
@ -73,6 +82,6 @@ export function setup(helper) {
|
|||
onMatch: addLocalDate
|
||||
};
|
||||
|
||||
md.core.textPostProcess.ruler.push('discourse-local-dates', rule);
|
||||
md.core.textPostProcess.ruler.push("discourse-local-dates", rule);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
import { withPluginApi } from 'discourse/lib/plugin-api';
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
|
||||
function initialize(api) {
|
||||
const messageBus = api.container.lookup('message-bus:main');
|
||||
const messageBus = api.container.lookup("message-bus:main");
|
||||
const currentUser = api.getCurrentUser();
|
||||
const appEvents = api.container.lookup('app-events:main');
|
||||
const appEvents = api.container.lookup("app-events:main");
|
||||
|
||||
api.modifyClass('component:site-header', {
|
||||
api.modifyClass("component:site-header", {
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
this.dispatch('header:search-context-trigger', 'header');
|
||||
this.dispatch("header:search-context-trigger", "header");
|
||||
}
|
||||
});
|
||||
|
||||
api.attachWidgetAction('header', 'headerSearchContextTrigger', function() {
|
||||
api.attachWidgetAction("header", "headerSearchContextTrigger", function() {
|
||||
if (this.site.mobileView) {
|
||||
this.state.skipSearchContext = false;
|
||||
} else {
|
||||
this.state.contextEnabled = true;
|
||||
this.state.searchContextType = 'topic';
|
||||
this.state.searchContextType = "topic";
|
||||
}
|
||||
});
|
||||
|
||||
if (messageBus && currentUser) {
|
||||
messageBus.subscribe(`/new_user_narrative/tutorial_search`, () => {
|
||||
appEvents.trigger('header:search-context-trigger');
|
||||
appEvents.trigger("header:search-context-trigger");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ export default {
|
|||
name: "new-user-narratve",
|
||||
|
||||
initialize(container) {
|
||||
const siteSettings = container.lookup('site-settings:main');
|
||||
if (siteSettings.discourse_narrative_bot_enabled) withPluginApi('0.8.7', initialize);
|
||||
const siteSettings = container.lookup("site-settings:main");
|
||||
if (siteSettings.discourse_narrative_bot_enabled)
|
||||
withPluginApi("0.8.7", initialize);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import { ajax } from 'discourse/lib/ajax';
|
||||
import { default as computed, observes, on } from 'ember-addons/ember-computed-decorators';
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import {
|
||||
default as computed,
|
||||
observes,
|
||||
on
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export const keepAliveDuration = 10000;
|
||||
export const bufferTime = 3000;
|
||||
|
@ -20,90 +24,103 @@ export default Ember.Component.extend({
|
|||
presenceUsers: null,
|
||||
channel: null,
|
||||
|
||||
@on('didInsertElement')
|
||||
@on("didInsertElement")
|
||||
composerOpened() {
|
||||
this._lastPublish = new Date();
|
||||
Ember.run.once(this, 'updateState');
|
||||
Ember.run.once(this, "updateState");
|
||||
},
|
||||
|
||||
@observes('action', 'post.id', 'topic.id')
|
||||
@observes("action", "post.id", "topic.id")
|
||||
composerStateChanged() {
|
||||
Ember.run.once(this, 'updateState');
|
||||
Ember.run.once(this, "updateState");
|
||||
},
|
||||
|
||||
@observes('reply', 'title')
|
||||
@observes("reply", "title")
|
||||
typing() {
|
||||
if (new Date() - this._lastPublish > keepAliveDuration) {
|
||||
this.publish({ current: this.get('currentState') });
|
||||
this.publish({ current: this.get("currentState") });
|
||||
}
|
||||
},
|
||||
|
||||
@on('willDestroyElement')
|
||||
@on("willDestroyElement")
|
||||
composerClosing() {
|
||||
this.publish({ previous: this.get('currentState') });
|
||||
this.publish({ previous: this.get("currentState") });
|
||||
Ember.run.cancel(this._pingTimer);
|
||||
Ember.run.cancel(this._clearTimer);
|
||||
},
|
||||
|
||||
updateState() {
|
||||
let state = null;
|
||||
const action = this.get('action');
|
||||
const action = this.get("action");
|
||||
|
||||
if (action === 'reply' || action === 'edit') {
|
||||
if (action === "reply" || action === "edit") {
|
||||
state = { action };
|
||||
if (action === 'reply') state.topic_id = this.get('topic.id');
|
||||
if (action === 'edit') state.post_id = this.get('post.id');
|
||||
if (action === "reply") state.topic_id = this.get("topic.id");
|
||||
if (action === "edit") state.post_id = this.get("post.id");
|
||||
}
|
||||
|
||||
this.set('previousState', this.get('currentState'));
|
||||
this.set('currentState', state);
|
||||
this.set("previousState", this.get("currentState"));
|
||||
this.set("currentState", state);
|
||||
},
|
||||
|
||||
@observes('currentState')
|
||||
@observes("currentState")
|
||||
currentStateChanged() {
|
||||
if (this.get('channel')) {
|
||||
this.messageBus.unsubscribe(this.get('channel'));
|
||||
this.set('channel', null);
|
||||
if (this.get("channel")) {
|
||||
this.messageBus.unsubscribe(this.get("channel"));
|
||||
this.set("channel", null);
|
||||
}
|
||||
|
||||
this.clear();
|
||||
|
||||
if (!['reply', 'edit'].includes(this.get('action'))) {
|
||||
if (!["reply", "edit"].includes(this.get("action"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.publish({
|
||||
response_needed: true,
|
||||
previous: this.get('previousState'),
|
||||
current: this.get('currentState')
|
||||
previous: this.get("previousState"),
|
||||
current: this.get("currentState")
|
||||
}).then(r => {
|
||||
if (this.get('isDestroyed')) { return; }
|
||||
this.set('presenceUsers', r.users);
|
||||
this.set('channel', r.messagebus_channel);
|
||||
if (this.get("isDestroyed")) {
|
||||
return;
|
||||
}
|
||||
this.set("presenceUsers", r.users);
|
||||
this.set("channel", r.messagebus_channel);
|
||||
|
||||
if (!r.messagebus_channel) { return; }
|
||||
if (!r.messagebus_channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.messageBus.subscribe(r.messagebus_channel, message => {
|
||||
if (!this.get('isDestroyed')) this.set('presenceUsers', message.users);
|
||||
this._clearTimer = Ember.run.debounce(this, 'clear', keepAliveDuration + bufferTime);
|
||||
}, r.messagebus_id);
|
||||
this.messageBus.subscribe(
|
||||
r.messagebus_channel,
|
||||
message => {
|
||||
if (!this.get("isDestroyed"))
|
||||
this.set("presenceUsers", message.users);
|
||||
this._clearTimer = Ember.run.debounce(
|
||||
this,
|
||||
"clear",
|
||||
keepAliveDuration + bufferTime
|
||||
);
|
||||
},
|
||||
r.messagebus_id
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
clear() {
|
||||
if (!this.get('isDestroyed')) this.set('presenceUsers', []);
|
||||
if (!this.get("isDestroyed")) this.set("presenceUsers", []);
|
||||
},
|
||||
|
||||
publish(data) {
|
||||
this._lastPublish = new Date();
|
||||
return ajax('/presence/publish', { type: 'POST', data });
|
||||
return ajax("/presence/publish", { type: "POST", data });
|
||||
},
|
||||
|
||||
@computed('presenceUsers', 'currentUser.id')
|
||||
@computed("presenceUsers", "currentUser.id")
|
||||
users(users, currentUserId) {
|
||||
return (users || []).filter(user => user.id !== currentUserId);
|
||||
},
|
||||
|
||||
isReply: Ember.computed.equal('action', 'reply'),
|
||||
shouldDisplay: Ember.computed.gt('users.length', 0)
|
||||
isReply: Ember.computed.equal("action", "reply"),
|
||||
shouldDisplay: Ember.computed.gt("users.length", 0)
|
||||
});
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import { default as computed, on } from 'ember-addons/ember-computed-decorators';
|
||||
import { keepAliveDuration, bufferTime } from 'discourse/plugins/discourse-presence/discourse/components/composer-presence-display';
|
||||
import {
|
||||
default as computed,
|
||||
on
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
import {
|
||||
keepAliveDuration,
|
||||
bufferTime
|
||||
} from "discourse/plugins/discourse-presence/discourse/components/composer-presence-display";
|
||||
|
||||
const MB_GET_LAST_MESSAGE = -2;
|
||||
|
||||
|
@ -8,35 +14,42 @@ export default Ember.Component.extend({
|
|||
presenceUsers: null,
|
||||
|
||||
clear() {
|
||||
if (!this.get('isDestroyed')) this.set('presenceUsers', []);
|
||||
if (!this.get("isDestroyed")) this.set("presenceUsers", []);
|
||||
},
|
||||
|
||||
@on('didInsertElement')
|
||||
@on("didInsertElement")
|
||||
_inserted() {
|
||||
this.clear();
|
||||
|
||||
this.messageBus.subscribe(this.get('channel'), message => {
|
||||
if (!this.get('isDestroyed')) this.set('presenceUsers', message.users);
|
||||
this._clearTimer = Ember.run.debounce(this, 'clear', keepAliveDuration + bufferTime);
|
||||
}, MB_GET_LAST_MESSAGE);
|
||||
this.messageBus.subscribe(
|
||||
this.get("channel"),
|
||||
message => {
|
||||
if (!this.get("isDestroyed")) this.set("presenceUsers", message.users);
|
||||
this._clearTimer = Ember.run.debounce(
|
||||
this,
|
||||
"clear",
|
||||
keepAliveDuration + bufferTime
|
||||
);
|
||||
},
|
||||
MB_GET_LAST_MESSAGE
|
||||
);
|
||||
},
|
||||
|
||||
@on('willDestroyElement')
|
||||
@on("willDestroyElement")
|
||||
_destroyed() {
|
||||
Ember.run.cancel(this._clearTimer);
|
||||
this.messageBus.unsubscribe(this.get('channel'));
|
||||
this.messageBus.unsubscribe(this.get("channel"));
|
||||
},
|
||||
|
||||
@computed('topicId')
|
||||
@computed("topicId")
|
||||
channel(topicId) {
|
||||
return `/presence/topic/${topicId}`;
|
||||
},
|
||||
|
||||
@computed('presenceUsers', 'currentUser.id')
|
||||
@computed("presenceUsers", "currentUser.id")
|
||||
users(users, currentUserId) {
|
||||
return (users || []).filter(user => user.id !== currentUserId);
|
||||
},
|
||||
|
||||
shouldDisplay: Ember.computed.gt('users.length', 0)
|
||||
|
||||
shouldDisplay: Ember.computed.gt("users.length", 0)
|
||||
});
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
import { withPluginApi } from 'discourse/lib/plugin-api';
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
|
||||
export default {
|
||||
name: "apply-lazyYT",
|
||||
initialize() {
|
||||
withPluginApi('0.1', api => {
|
||||
withPluginApi("0.1", api => {
|
||||
api.decorateCooked($elem => {
|
||||
const iframes = $(".lazyYT", $elem);
|
||||
if (iframes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const iframes = $('.lazyYT', $elem);
|
||||
if (iframes.length === 0) { return; }
|
||||
|
||||
$('.lazyYT', $elem).lazyYT({
|
||||
$(".lazyYT", $elem).lazyYT({
|
||||
onPlay(e, $el) {
|
||||
// don't cloak posts that have playing videos in them
|
||||
const postId = parseInt($el.closest('article').data('post-id'));
|
||||
const postId = parseInt($el.closest("article").data("post-id"));
|
||||
if (postId) {
|
||||
api.preventCloak(postId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||
import InputValidation from 'discourse/models/input-validation';
|
||||
import {
|
||||
default as computed,
|
||||
observes
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
import InputValidation from "discourse/models/input-validation";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
regularPollType: 'regular',
|
||||
numberPollType: 'number',
|
||||
multiplePollType: 'multiple',
|
||||
regularPollType: "regular",
|
||||
numberPollType: "number",
|
||||
multiplePollType: "multiple",
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
@ -14,9 +17,18 @@ export default Ember.Controller.extend({
|
|||
@computed("regularPollType", "numberPollType", "multiplePollType")
|
||||
pollTypes(regularPollType, numberPollType, multiplePollType) {
|
||||
return [
|
||||
{ name: I18n.t("poll.ui_builder.poll_type.regular"), value: regularPollType },
|
||||
{ name: I18n.t("poll.ui_builder.poll_type.number"), value: numberPollType },
|
||||
{ name: I18n.t("poll.ui_builder.poll_type.multiple"), value: multiplePollType },
|
||||
{
|
||||
name: I18n.t("poll.ui_builder.poll_type.regular"),
|
||||
value: regularPollType
|
||||
},
|
||||
{
|
||||
name: I18n.t("poll.ui_builder.poll_type.number"),
|
||||
value: numberPollType
|
||||
},
|
||||
{
|
||||
name: I18n.t("poll.ui_builder.poll_type.multiple"),
|
||||
value: multiplePollType
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
|
@ -27,7 +39,7 @@ export default Ember.Controller.extend({
|
|||
|
||||
@computed("pollType", "pollOptionsCount", "multiplePollType")
|
||||
isMultiple(pollType, count, multiplePollType) {
|
||||
return (pollType === multiplePollType) && count > 0;
|
||||
return pollType === multiplePollType && count > 0;
|
||||
},
|
||||
|
||||
@computed("pollType", "numberPollType")
|
||||
|
@ -73,11 +85,21 @@ export default Ember.Controller.extend({
|
|||
if (isMultiple) {
|
||||
return this._comboboxOptions(1, count + 1);
|
||||
} else if (isNumber) {
|
||||
return this._comboboxOptions(1, this.siteSettings.poll_maximum_options + 1);
|
||||
return this._comboboxOptions(
|
||||
1,
|
||||
this.siteSettings.poll_maximum_options + 1
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@computed("isRegular", "isMultiple", "isNumber", "pollOptionsCount", "pollMin", "pollStep")
|
||||
@computed(
|
||||
"isRegular",
|
||||
"isMultiple",
|
||||
"isNumber",
|
||||
"pollOptionsCount",
|
||||
"pollMin",
|
||||
"pollStep"
|
||||
)
|
||||
pollMaxOptions(isRegular, isMultiple, isNumber, count, pollMin, pollStep) {
|
||||
if (isRegular) return;
|
||||
const pollMinInt = parseInt(pollMin) || 1;
|
||||
|
@ -89,7 +111,10 @@ export default Ember.Controller.extend({
|
|||
if (pollStepInt < 1) {
|
||||
pollStepInt = 1;
|
||||
}
|
||||
return this._comboboxOptions(pollMinInt + 1, pollMinInt + (this.siteSettings.poll_maximum_options * pollStepInt));
|
||||
return this._comboboxOptions(
|
||||
pollMinInt + 1,
|
||||
pollMinInt + this.siteSettings.poll_maximum_options * pollStepInt
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -99,19 +124,47 @@ export default Ember.Controller.extend({
|
|||
return this._comboboxOptions(1, (parseInt(pollMax) || 1) + 1);
|
||||
},
|
||||
|
||||
@computed("isNumber", "showMinMax", "pollType", "publicPoll", "pollOptions", "pollMin", "pollMax", "pollStep", "autoClose", "date", "time")
|
||||
pollOutput(isNumber, showMinMax, pollType, publicPoll, pollOptions, pollMin, pollMax, pollStep, autoClose, date, time) {
|
||||
let pollHeader = '[poll';
|
||||
let output = '';
|
||||
@computed(
|
||||
"isNumber",
|
||||
"showMinMax",
|
||||
"pollType",
|
||||
"publicPoll",
|
||||
"pollOptions",
|
||||
"pollMin",
|
||||
"pollMax",
|
||||
"pollStep",
|
||||
"autoClose",
|
||||
"date",
|
||||
"time"
|
||||
)
|
||||
pollOutput(
|
||||
isNumber,
|
||||
showMinMax,
|
||||
pollType,
|
||||
publicPoll,
|
||||
pollOptions,
|
||||
pollMin,
|
||||
pollMax,
|
||||
pollStep,
|
||||
autoClose,
|
||||
date,
|
||||
time
|
||||
) {
|
||||
let pollHeader = "[poll";
|
||||
let output = "";
|
||||
|
||||
const match = this.get("toolbarEvent").getText().match(/\[poll(\s+name=[^\s\]]+)*.*\]/igm);
|
||||
const match = this.get("toolbarEvent")
|
||||
.getText()
|
||||
.match(/\[poll(\s+name=[^\s\]]+)*.*\]/gim);
|
||||
|
||||
if (match) {
|
||||
pollHeader += ` name=poll${match.length + 1}`;
|
||||
};
|
||||
}
|
||||
|
||||
let step = pollStep;
|
||||
if (step < 1) { step = 1; }
|
||||
if (step < 1) {
|
||||
step = 1;
|
||||
}
|
||||
|
||||
if (pollType) pollHeader += ` type=${pollType}`;
|
||||
if (pollMin && showMinMax) pollHeader += ` min=${pollMin}`;
|
||||
|
@ -119,11 +172,14 @@ export default Ember.Controller.extend({
|
|||
if (isNumber) pollHeader += ` step=${step}`;
|
||||
if (publicPoll) pollHeader += ` public=true`;
|
||||
if (autoClose) {
|
||||
let closeDate = moment(date + " " + time, "YYYY-MM-DD HH:mm").toISOString();
|
||||
let closeDate = moment(
|
||||
date + " " + time,
|
||||
"YYYY-MM-DD HH:mm"
|
||||
).toISOString();
|
||||
if (closeDate) pollHeader += ` close=${closeDate}`;
|
||||
}
|
||||
|
||||
pollHeader += ']';
|
||||
pollHeader += "]";
|
||||
output += `${pollHeader}\n`;
|
||||
|
||||
if (pollOptions.length > 0 && !isNumber) {
|
||||
|
@ -132,13 +188,24 @@ export default Ember.Controller.extend({
|
|||
});
|
||||
}
|
||||
|
||||
output += '[/poll]';
|
||||
output += "[/poll]";
|
||||
return output;
|
||||
},
|
||||
|
||||
@computed("pollOptionsCount", "isRegular", "isMultiple", "isNumber", "pollMin", "pollMax")
|
||||
@computed(
|
||||
"pollOptionsCount",
|
||||
"isRegular",
|
||||
"isMultiple",
|
||||
"isNumber",
|
||||
"pollMin",
|
||||
"pollMax"
|
||||
)
|
||||
disableInsert(count, isRegular, isMultiple, isNumber, pollMin, pollMax) {
|
||||
return (isRegular && count < 2) || (isMultiple && count < pollMin && pollMin >= pollMax) || (isNumber ? false : (count < 2));
|
||||
return (
|
||||
(isRegular && count < 2) ||
|
||||
(isMultiple && count < pollMin && pollMin >= pollMax) ||
|
||||
(isNumber ? false : count < 2)
|
||||
);
|
||||
},
|
||||
|
||||
@computed("pollMin", "pollMax")
|
||||
|
@ -146,7 +213,10 @@ export default Ember.Controller.extend({
|
|||
let options = { ok: true };
|
||||
|
||||
if (pollMin >= pollMax) {
|
||||
options = { failed: true, reason: I18n.t("poll.ui_builder.help.invalid_values") };
|
||||
options = {
|
||||
failed: true,
|
||||
reason: I18n.t("poll.ui_builder.help.invalid_values")
|
||||
};
|
||||
}
|
||||
|
||||
return InputValidation.create(options);
|
||||
|
@ -157,7 +227,10 @@ export default Ember.Controller.extend({
|
|||
let options = { ok: true };
|
||||
|
||||
if (pollStep < 1) {
|
||||
options = { failed: true, reason: I18n.t("poll.ui_builder.help.min_step_value") };
|
||||
options = {
|
||||
failed: true,
|
||||
reason: I18n.t("poll.ui_builder.help.min_step_value")
|
||||
};
|
||||
}
|
||||
|
||||
return InputValidation.create(options);
|
||||
|
@ -168,7 +241,10 @@ export default Ember.Controller.extend({
|
|||
let options = { ok: true };
|
||||
|
||||
if (disableInsert) {
|
||||
options = { failed: true, reason: I18n.t("poll.ui_builder.help.options_count") };
|
||||
options = {
|
||||
failed: true,
|
||||
reason: I18n.t("poll.ui_builder.help.options_count")
|
||||
};
|
||||
}
|
||||
|
||||
return InputValidation.create(options);
|
||||
|
@ -184,13 +260,17 @@ export default Ember.Controller.extend({
|
|||
this.setProperties({
|
||||
pollType: null,
|
||||
publicPoll: false,
|
||||
pollOptions: '',
|
||||
pollOptions: "",
|
||||
pollMin: 1,
|
||||
pollMax: null,
|
||||
pollStep: 1,
|
||||
autoClose: false,
|
||||
date: moment().add(1, "day").format("YYYY-MM-DD"),
|
||||
time: moment().add(1, "hour").format("HH:mm"),
|
||||
date: moment()
|
||||
.add(1, "day")
|
||||
.format("YYYY-MM-DD"),
|
||||
time: moment()
|
||||
.add(1, "hour")
|
||||
.format("HH:mm")
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -1,43 +1,48 @@
|
|||
import { withPluginApi } from 'discourse/lib/plugin-api';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
function initializePollUIBuilder(api) {
|
||||
api.modifyClass('controller:composer', {
|
||||
@computed('siteSettings.poll_enabled', 'siteSettings.poll_minimum_trust_level_to_create', 'model.topic.pm_with_non_human_user')
|
||||
api.modifyClass("controller:composer", {
|
||||
@computed(
|
||||
"siteSettings.poll_enabled",
|
||||
"siteSettings.poll_minimum_trust_level_to_create",
|
||||
"model.topic.pm_with_non_human_user"
|
||||
)
|
||||
canBuildPoll(pollEnabled, minimumTrustLevel, pmWithNonHumanUser) {
|
||||
return pollEnabled &&
|
||||
(
|
||||
pmWithNonHumanUser ||
|
||||
this.currentUser &&
|
||||
(
|
||||
this.currentUser.staff ||
|
||||
this.currentUser.trust_level >= minimumTrustLevel
|
||||
)
|
||||
);
|
||||
return (
|
||||
pollEnabled &&
|
||||
(pmWithNonHumanUser ||
|
||||
(this.currentUser &&
|
||||
(this.currentUser.staff ||
|
||||
this.currentUser.trust_level >= minimumTrustLevel)))
|
||||
);
|
||||
},
|
||||
|
||||
actions: {
|
||||
showPollBuilder() {
|
||||
showModal('poll-ui-builder').set('toolbarEvent', this.get('toolbarEvent'));
|
||||
showModal("poll-ui-builder").set(
|
||||
"toolbarEvent",
|
||||
this.get("toolbarEvent")
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
api.addToolbarPopupMenuOptionsCallback(function() {
|
||||
return {
|
||||
action: 'showPollBuilder',
|
||||
icon: 'bar-chart-o',
|
||||
label: 'poll.ui_builder.title',
|
||||
condition: 'canBuildPoll'
|
||||
action: "showPollBuilder",
|
||||
icon: "bar-chart-o",
|
||||
label: "poll.ui_builder.title",
|
||||
condition: "canBuildPoll"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'add-poll-ui-builder',
|
||||
name: "add-poll-ui-builder",
|
||||
|
||||
initialize() {
|
||||
withPluginApi('0.8.7', initializePollUIBuilder);
|
||||
withPluginApi("0.8.7", initializePollUIBuilder);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import { withPluginApi } from 'discourse/lib/plugin-api';
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import { observes } from "ember-addons/ember-computed-decorators";
|
||||
import { getRegister } from 'discourse-common/lib/get-owner';
|
||||
import WidgetGlue from 'discourse/widgets/glue';
|
||||
import { getRegister } from "discourse-common/lib/get-owner";
|
||||
import WidgetGlue from "discourse/widgets/glue";
|
||||
|
||||
function initializePolls(api) {
|
||||
const register = getRegister(api);
|
||||
|
||||
api.modifyClass('controller:topic', {
|
||||
subscribe(){
|
||||
api.modifyClass("controller:topic", {
|
||||
subscribe() {
|
||||
this._super();
|
||||
this.messageBus.subscribe("/polls/" + this.get("model.id"), msg => {
|
||||
const post = this.get('model.postStream').findLoadedPost(msg.post_id);
|
||||
const post = this.get("model.postStream").findLoadedPost(msg.post_id);
|
||||
if (post) {
|
||||
post.set('polls', msg.polls);
|
||||
post.set("polls", msg.polls);
|
||||
}
|
||||
});
|
||||
},
|
||||
unsubscribe(){
|
||||
this.messageBus.unsubscribe('/polls/*');
|
||||
unsubscribe() {
|
||||
this.messageBus.unsubscribe("/polls/*");
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ function initializePolls(api) {
|
|||
_glued.forEach(g => g.queueRerender());
|
||||
}
|
||||
|
||||
api.modifyClass('model:post', {
|
||||
api.modifyClass("model:post", {
|
||||
_polls: null,
|
||||
pollsObject: null,
|
||||
|
||||
|
@ -39,7 +39,7 @@ function initializePolls(api) {
|
|||
const polls = this.get("polls");
|
||||
if (polls) {
|
||||
this._polls = this._polls || {};
|
||||
_.map(polls, (v,k) => {
|
||||
_.map(polls, (v, k) => {
|
||||
const existing = this._polls[k];
|
||||
if (existing) {
|
||||
this._polls[k].setProperties(v);
|
||||
|
@ -53,19 +53,22 @@ function initializePolls(api) {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
function attachPolls($elem, helper) {
|
||||
const $polls = $('.poll', $elem);
|
||||
if (!$polls.length) { return; }
|
||||
const $polls = $(".poll", $elem);
|
||||
if (!$polls.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const post = helper.getModel();
|
||||
api.preventCloak(post.id);
|
||||
const votes = post.get('polls_votes') || {};
|
||||
const votes = post.get("polls_votes") || {};
|
||||
|
||||
post.pollsChanged();
|
||||
|
||||
const polls = post.get("pollsObject");
|
||||
if (!polls) { return; }
|
||||
if (!polls) {
|
||||
return;
|
||||
}
|
||||
|
||||
_interval = _interval || setInterval(rerender, 30000);
|
||||
|
||||
|
@ -74,14 +77,14 @@ function initializePolls(api) {
|
|||
const pollName = $poll.data("poll-name");
|
||||
const poll = polls[pollName];
|
||||
if (poll) {
|
||||
const isMultiple = poll.get('type') === 'multiple';
|
||||
const isMultiple = poll.get("type") === "multiple";
|
||||
|
||||
const glue = new WidgetGlue('discourse-poll', register, {
|
||||
const glue = new WidgetGlue("discourse-poll", register, {
|
||||
id: `${pollName}-${post.id}`,
|
||||
post,
|
||||
poll,
|
||||
vote: votes[pollName] || [],
|
||||
isMultiple,
|
||||
isMultiple
|
||||
});
|
||||
glue.appendTo(pollElem);
|
||||
_glued.push(glue);
|
||||
|
@ -108,6 +111,6 @@ export default {
|
|||
name: "extend-for-poll",
|
||||
|
||||
initialize() {
|
||||
withPluginApi('0.8.7', initializePolls);
|
||||
withPluginApi("0.8.7", initializePolls);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,7 +2,17 @@
|
|||
|
||||
const DATA_PREFIX = "data-poll-";
|
||||
const DEFAULT_POLL_NAME = "poll";
|
||||
const WHITELISTED_ATTRIBUTES = ["type", "name", "min", "max", "step", "order", "status", "public", "close"];
|
||||
const WHITELISTED_ATTRIBUTES = [
|
||||
"type",
|
||||
"name",
|
||||
"min",
|
||||
"max",
|
||||
"step",
|
||||
"order",
|
||||
"status",
|
||||
"public",
|
||||
"close"
|
||||
];
|
||||
|
||||
function replaceToken(tokens, target, list) {
|
||||
let pos = tokens.indexOf(target);
|
||||
|
@ -12,44 +22,48 @@ function replaceToken(tokens, target, list) {
|
|||
list[0].map = target.map;
|
||||
|
||||
// resequence levels
|
||||
for(;pos<tokens.length;pos++) {
|
||||
for (; pos < tokens.length; pos++) {
|
||||
let nesting = tokens[pos].nesting;
|
||||
if (nesting < 0) { level--; }
|
||||
if (nesting < 0) {
|
||||
level--;
|
||||
}
|
||||
tokens[pos].level = level;
|
||||
if (nesting > 0) { level++; }
|
||||
if (nesting > 0) {
|
||||
level++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// analyzes the block to that we have poll options
|
||||
function getListItems(tokens, startToken) {
|
||||
let i = tokens.length-1;
|
||||
let i = tokens.length - 1;
|
||||
let listItems = [];
|
||||
let buffer = [];
|
||||
|
||||
for(;tokens[i]!==startToken;i--) {
|
||||
for (; tokens[i] !== startToken; i--) {
|
||||
if (i === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let token = tokens[i];
|
||||
if (token.level === 0) {
|
||||
if (token.tag !== 'ol' && token.tag !== 'ul') {
|
||||
if (token.tag !== "ol" && token.tag !== "ul") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (token.level === 1 && token.nesting === 1) {
|
||||
if (token.tag === 'li') {
|
||||
listItems.push([token, buffer.reverse().join(' ')]);
|
||||
if (token.tag === "li") {
|
||||
listItems.push([token, buffer.reverse().join(" ")]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (token.level === 1 && token.nesting === 1 && token.tag === 'li') {
|
||||
if (token.level === 1 && token.nesting === 1 && token.tag === "li") {
|
||||
buffer = [];
|
||||
} else {
|
||||
if (token.type === 'text' || token.type === 'inline') {
|
||||
if (token.type === "text" || token.type === "inline") {
|
||||
buffer.push(token.content);
|
||||
}
|
||||
}
|
||||
|
@ -59,22 +73,21 @@ function getListItems(tokens, startToken) {
|
|||
}
|
||||
|
||||
function invalidPoll(state, tag) {
|
||||
let token = state.push('text', '', 0);
|
||||
token.content = '[/' + tag + ']';
|
||||
let token = state.push("text", "", 0);
|
||||
token.content = "[/" + tag + "]";
|
||||
}
|
||||
|
||||
const rule = {
|
||||
tag: 'poll',
|
||||
tag: "poll",
|
||||
|
||||
before: function(state, tagInfo, raw){
|
||||
let token = state.push('text', '', 0);
|
||||
before: function(state, tagInfo, raw) {
|
||||
let token = state.push("text", "", 0);
|
||||
token.content = raw;
|
||||
token.bbcode_attrs = tagInfo.attrs;
|
||||
token.bbcode_type = 'poll_open';
|
||||
token.bbcode_type = "poll_open";
|
||||
},
|
||||
|
||||
after: function(state, openToken, raw) {
|
||||
|
||||
let items = getListItems(state.tokens, openToken);
|
||||
if (!items) {
|
||||
return invalidPoll(state, raw);
|
||||
|
@ -85,7 +98,7 @@ const rule = {
|
|||
// default poll attributes
|
||||
const attributes = [["class", "poll"]];
|
||||
|
||||
if (!attrs['status']) {
|
||||
if (!attrs["status"]) {
|
||||
attributes.push([DATA_PREFIX + "status", "open"]);
|
||||
}
|
||||
|
||||
|
@ -111,48 +124,54 @@ const rule = {
|
|||
|
||||
let header = [];
|
||||
|
||||
let token = new state.Token('poll_open', 'div', 1);
|
||||
let token = new state.Token("poll_open", "div", 1);
|
||||
token.block = true;
|
||||
token.attrs = attributes;
|
||||
header.push(token);
|
||||
|
||||
token = new state.Token('poll_open', 'div', 1);
|
||||
token = new state.Token("poll_open", "div", 1);
|
||||
token.block = true;
|
||||
header.push(token);
|
||||
|
||||
token = new state.Token('poll_open', 'div', 1);
|
||||
token.attrs = [['class', 'poll-container']];
|
||||
token = new state.Token("poll_open", "div", 1);
|
||||
token.attrs = [["class", "poll-container"]];
|
||||
|
||||
header.push(token);
|
||||
|
||||
// generate the options when the type is "number"
|
||||
if (attrs["type"] === "number") {
|
||||
// default values
|
||||
if (isNaN(min)) { min = 1; }
|
||||
if (isNaN(max)) { max = state.md.options.discourse.pollMaximumOptions; }
|
||||
if (isNaN(step)) { step = 1; }
|
||||
if (isNaN(min)) {
|
||||
min = 1;
|
||||
}
|
||||
if (isNaN(max)) {
|
||||
max = state.md.options.discourse.pollMaximumOptions;
|
||||
}
|
||||
if (isNaN(step)) {
|
||||
step = 1;
|
||||
}
|
||||
|
||||
if (items.length > 0) {
|
||||
return invalidPoll(state, raw);
|
||||
}
|
||||
|
||||
// dynamically generate options
|
||||
token = new state.Token('bullet_list_open', 'ul', 1);
|
||||
token = new state.Token("bullet_list_open", "ul", 1);
|
||||
header.push(token);
|
||||
|
||||
for (let o = min; o <= max; o += step) {
|
||||
token = new state.Token('list_item_open', 'li', 1);
|
||||
items.push([token, String(o)]);
|
||||
token = new state.Token("list_item_open", "li", 1);
|
||||
items.push([token, String(o)]);
|
||||
header.push(token);
|
||||
|
||||
token = new state.Token('text', '', 0);
|
||||
token = new state.Token("text", "", 0);
|
||||
token.content = String(o);
|
||||
header.push(token);
|
||||
|
||||
token = new state.Token('list_item_close', 'li', -1);
|
||||
token = new state.Token("list_item_close", "li", -1);
|
||||
header.push(token);
|
||||
}
|
||||
token = new state.Token('bullet_item_close', '', -1);
|
||||
token = new state.Token("bullet_item_close", "", -1);
|
||||
header.push(token);
|
||||
}
|
||||
|
||||
|
@ -163,41 +182,41 @@ const rule = {
|
|||
|
||||
token.attrs = token.attrs || [];
|
||||
let md5Hash = md5(JSON.stringify([text]));
|
||||
token.attrs.push([DATA_PREFIX + 'option-id', md5Hash]);
|
||||
token.attrs.push([DATA_PREFIX + "option-id", md5Hash]);
|
||||
}
|
||||
|
||||
replaceToken(state.tokens, openToken, header);
|
||||
|
||||
// we got to correct the level on the state
|
||||
// we just resequenced
|
||||
state.level = state.tokens[state.tokens.length-1].level;
|
||||
state.level = state.tokens[state.tokens.length - 1].level;
|
||||
|
||||
state.push('poll_close', 'div', -1);
|
||||
state.push("poll_close", "div", -1);
|
||||
|
||||
token = state.push('poll_open', 'div', 1);
|
||||
token.attrs = [['class', 'poll-info']];
|
||||
token = state.push("poll_open", "div", 1);
|
||||
token.attrs = [["class", "poll-info"]];
|
||||
|
||||
state.push('paragraph_open', 'p', 1);
|
||||
state.push("paragraph_open", "p", 1);
|
||||
|
||||
token = state.push('span_open', 'span', 1);
|
||||
token = state.push("span_open", "span", 1);
|
||||
token.block = false;
|
||||
token.attrs = [['class', 'info-number']];
|
||||
token = state.push('text', '', 0);
|
||||
token.content = '0';
|
||||
state.push('span_close', 'span', -1);
|
||||
token.attrs = [["class", "info-number"]];
|
||||
token = state.push("text", "", 0);
|
||||
token.content = "0";
|
||||
state.push("span_close", "span", -1);
|
||||
|
||||
token = state.push('span_open', 'span', 1);
|
||||
token = state.push("span_open", "span", 1);
|
||||
token.block = false;
|
||||
token.attrs = [['class', 'info-label']];
|
||||
token = state.push('text', '', 0);
|
||||
token.attrs = [["class", "info-label"]];
|
||||
token = state.push("text", "", 0);
|
||||
token.content = I18n.t("poll.voters", { count: 0 });
|
||||
state.push('span_close', 'span', -1);
|
||||
state.push("span_close", "span", -1);
|
||||
|
||||
state.push('paragraph_close', 'p', -1);
|
||||
state.push("paragraph_close", "p", -1);
|
||||
|
||||
state.push('poll_close', 'div', -1);
|
||||
state.push('poll_close', 'div', -1);
|
||||
state.push('poll_close', 'div', -1);
|
||||
state.push("poll_close", "div", -1);
|
||||
state.push("poll_close", "div", -1);
|
||||
state.push("poll_close", "div", -1);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -208,27 +227,26 @@ function newApiInit(helper) {
|
|||
});
|
||||
|
||||
helper.registerPlugin(md => {
|
||||
md.block.bbcode.ruler.push('poll', rule);
|
||||
md.block.bbcode.ruler.push("poll", rule);
|
||||
});
|
||||
}
|
||||
|
||||
export function setup(helper) {
|
||||
helper.whiteList([
|
||||
'div.poll',
|
||||
'div.poll-info',
|
||||
'div.poll-container',
|
||||
'div.poll-buttons',
|
||||
'div[data-*]',
|
||||
'span.info-number',
|
||||
'span.info-text',
|
||||
'span.info-label',
|
||||
'a.button.cast-votes',
|
||||
'a.button.toggle-results',
|
||||
'li[data-*]'
|
||||
"div.poll",
|
||||
"div.poll-info",
|
||||
"div.poll-container",
|
||||
"div.poll-buttons",
|
||||
"div[data-*]",
|
||||
"span.info-number",
|
||||
"span.info-text",
|
||||
"span.info-label",
|
||||
"a.button.cast-votes",
|
||||
"a.button.toggle-results",
|
||||
"li[data-*]"
|
||||
]);
|
||||
|
||||
newApiInit(helper);
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -243,7 +261,10 @@ export function setup(helper) {
|
|||
* http://www.opensource.org/licenses/bsd-license
|
||||
*/
|
||||
function md5cycle(x, k) {
|
||||
var a = x[0], b = x[1], c = x[2], d = x[3];
|
||||
var a = x[0],
|
||||
b = x[1],
|
||||
c = x[2],
|
||||
d = x[3];
|
||||
|
||||
a = ff(a, b, c, d, k[0], 7, -680876936);
|
||||
d = ff(d, a, b, c, k[1], 12, -389564586);
|
||||
|
@ -325,11 +346,11 @@ function cmn(q, a, b, x, s, t) {
|
|||
}
|
||||
|
||||
function ff(a, b, c, d, x, s, t) {
|
||||
return cmn((b & c) | ((~b) & d), a, b, x, s, t);
|
||||
return cmn((b & c) | (~b & d), a, b, x, s, t);
|
||||
}
|
||||
|
||||
function gg(a, b, c, d, x, s, t) {
|
||||
return cmn((b & d) | (c & (~d)), a, b, x, s, t);
|
||||
return cmn((b & d) | (c & ~d), a, b, x, s, t);
|
||||
}
|
||||
|
||||
function hh(a, b, c, d, x, s, t) {
|
||||
|
@ -337,7 +358,7 @@ function hh(a, b, c, d, x, s, t) {
|
|||
}
|
||||
|
||||
function ii(a, b, c, d, x, s, t) {
|
||||
return cmn(c ^ (b | (~d)), a, b, x, s, t);
|
||||
return cmn(c ^ (b | ~d), a, b, x, s, t);
|
||||
}
|
||||
|
||||
function md51(s) {
|
||||
|
@ -345,15 +366,17 @@ function md51(s) {
|
|||
if (/[\x80-\xFF]/.test(s)) {
|
||||
s = unescape(encodeURI(s));
|
||||
}
|
||||
var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i;
|
||||
var n = s.length,
|
||||
state = [1732584193, -271733879, -1732584194, 271733878],
|
||||
i;
|
||||
for (i = 64; i <= s.length; i += 64) {
|
||||
md5cycle(state, md5blk(s.substring(i - 64, i)));
|
||||
}
|
||||
s = s.substring(i - 64);
|
||||
var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
for (i = 0; i < s.length; i++)
|
||||
tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
|
||||
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
|
||||
tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3);
|
||||
tail[i >> 2] |= 0x80 << (i % 4 << 3);
|
||||
if (i > 55) {
|
||||
md5cycle(state, tail);
|
||||
for (i = 0; i < 16; i++) tail[i] = 0;
|
||||
|
@ -363,35 +386,37 @@ function md51(s) {
|
|||
return state;
|
||||
}
|
||||
|
||||
function md5blk(s) { /* I figured global was faster. */
|
||||
var md5blks = [], i; /* Andy King said do it this way. */
|
||||
function md5blk(s) {
|
||||
/* I figured global was faster. */
|
||||
var md5blks = [],
|
||||
i; /* Andy King said do it this way. */
|
||||
for (i = 0; i < 64; i += 4) {
|
||||
md5blks[i >> 2] = s.charCodeAt(i) +
|
||||
(s.charCodeAt(i + 1) << 8) +
|
||||
(s.charCodeAt(i + 2) << 16) +
|
||||
(s.charCodeAt(i + 3) << 24);
|
||||
md5blks[i >> 2] =
|
||||
s.charCodeAt(i) +
|
||||
(s.charCodeAt(i + 1) << 8) +
|
||||
(s.charCodeAt(i + 2) << 16) +
|
||||
(s.charCodeAt(i + 3) << 24);
|
||||
}
|
||||
return md5blks;
|
||||
}
|
||||
|
||||
var hex_chr = '0123456789abcdef'.split('');
|
||||
var hex_chr = "0123456789abcdef".split("");
|
||||
|
||||
function rhex(n) {
|
||||
var s = '', j = 0;
|
||||
var s = "",
|
||||
j = 0;
|
||||
for (; j < 4; j++)
|
||||
s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] +
|
||||
hex_chr[(n >> (j * 8)) & 0x0F];
|
||||
s += hex_chr[(n >> (j * 8 + 4)) & 0x0f] + hex_chr[(n >> (j * 8)) & 0x0f];
|
||||
return s;
|
||||
}
|
||||
|
||||
function hex(x) {
|
||||
for (var i = 0; i < x.length; i++)
|
||||
x[i] = rhex(x[i]);
|
||||
return x.join('');
|
||||
for (var i = 0; i < x.length; i++) x[i] = rhex(x[i]);
|
||||
return x.join("");
|
||||
}
|
||||
|
||||
function add32(a, b) {
|
||||
return (a + b) & 0xFFFFFFFF;
|
||||
return (a + b) & 0xffffffff;
|
||||
}
|
||||
|
||||
function md5(s) {
|
||||
|
|
|
@ -12,7 +12,7 @@ export default function(percentages) {
|
|||
// and increase the corresponding item in the percentages array by 1
|
||||
let greatest = 0;
|
||||
let index = 0;
|
||||
for (let j=0; j < decimals.length; j++) {
|
||||
for (let j = 0; j < decimals.length; j++) {
|
||||
if (decimals[j] > greatest) {
|
||||
index = j;
|
||||
greatest = decimals[j];
|
||||
|
@ -25,5 +25,4 @@ export default function(percentages) {
|
|||
}
|
||||
|
||||
return percentages.map(p => Math.floor(p));
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { createWidget } from 'discourse/widgets/widget';
|
||||
import { h } from 'virtual-dom';
|
||||
import { iconNode } from 'discourse-common/lib/icon-library';
|
||||
import RawHtml from 'discourse/widgets/raw-html';
|
||||
import { ajax } from 'discourse/lib/ajax';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { createWidget } from "discourse/widgets/widget";
|
||||
import { h } from "virtual-dom";
|
||||
import { iconNode } from "discourse-common/lib/icon-library";
|
||||
import RawHtml from "discourse/widgets/raw-html";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import evenRound from "discourse/plugins/poll/lib/even-round";
|
||||
import { avatarFor } from 'discourse/widgets/post';
|
||||
import { avatarFor } from "discourse/widgets/post";
|
||||
import round from "discourse/lib/round";
|
||||
import { relativeAge } from 'discourse/lib/formatter';
|
||||
import { relativeAge } from "discourse/lib/formatter";
|
||||
|
||||
function optionHtml(option) {
|
||||
return new RawHtml({ html: `<span>${option.html}</span>` });
|
||||
|
@ -17,20 +17,20 @@ function fetchVoters(payload) {
|
|||
return ajax("/polls/voters.json", {
|
||||
type: "get",
|
||||
data: payload
|
||||
}).catch((error) => {
|
||||
}).catch(error => {
|
||||
if (error) {
|
||||
popupAjaxError(error);
|
||||
} else {
|
||||
bootbox.alert(I18n.t('poll.error_while_fetching_voters'));
|
||||
bootbox.alert(I18n.t("poll.error_while_fetching_voters"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createWidget('discourse-poll-option', {
|
||||
tagName: 'li',
|
||||
createWidget("discourse-poll-option", {
|
||||
tagName: "li",
|
||||
|
||||
buildAttributes(attrs) {
|
||||
return { 'data-poll-option-id': attrs.option.id };
|
||||
return { "data-poll-option-id": attrs.option.id };
|
||||
},
|
||||
|
||||
html(attrs) {
|
||||
|
@ -39,11 +39,11 @@ createWidget('discourse-poll-option', {
|
|||
const chosen = vote.indexOf(option.id) !== -1;
|
||||
|
||||
if (attrs.isMultiple) {
|
||||
result.push(iconNode(chosen ? 'check-square-o' : 'square-o'));
|
||||
result.push(iconNode(chosen ? "check-square-o" : "square-o"));
|
||||
} else {
|
||||
result.push(iconNode(chosen ? 'dot-circle-o' : 'circle-o'));
|
||||
result.push(iconNode(chosen ? "dot-circle-o" : "circle-o"));
|
||||
}
|
||||
result.push(' ');
|
||||
result.push(" ");
|
||||
result.push(optionHtml(option));
|
||||
|
||||
return result;
|
||||
|
@ -51,13 +51,13 @@ createWidget('discourse-poll-option', {
|
|||
|
||||
click(e) {
|
||||
if ($(e.target).closest("a").length === 0) {
|
||||
this.sendWidgetAction('toggleOption', this.attrs.option);
|
||||
this.sendWidgetAction("toggleOption", this.attrs.option);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
createWidget('discourse-poll-load-more', {
|
||||
tagName: 'div.poll-voters-toggle-expand',
|
||||
createWidget("discourse-poll-load-more", {
|
||||
tagName: "div.poll-voters-toggle-expand",
|
||||
buildKey: attrs => `${attrs.id}-load-more`,
|
||||
|
||||
defaultState() {
|
||||
|
@ -65,36 +65,43 @@ createWidget('discourse-poll-load-more', {
|
|||
},
|
||||
|
||||
html(attrs, state) {
|
||||
return state.loading ? h('div.spinner.small') : h('a', iconNode('chevron-down'));
|
||||
return state.loading
|
||||
? h("div.spinner.small")
|
||||
: h("a", iconNode("chevron-down"));
|
||||
},
|
||||
|
||||
click() {
|
||||
const { state } = this;
|
||||
if (state.loading) { return; }
|
||||
if (state.loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.loading = true;
|
||||
return this.sendWidgetAction('loadMore').finally(() => state.loading = false);
|
||||
return this.sendWidgetAction("loadMore").finally(
|
||||
() => (state.loading = false)
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
createWidget('discourse-poll-voters', {
|
||||
tagName: 'ul.poll-voters-list',
|
||||
createWidget("discourse-poll-voters", {
|
||||
tagName: "ul.poll-voters-list",
|
||||
buildKey: attrs => attrs.id(),
|
||||
|
||||
defaultState() {
|
||||
return {
|
||||
loaded: 'new',
|
||||
loaded: "new",
|
||||
pollVoters: [],
|
||||
offset: 1,
|
||||
offset: 1
|
||||
};
|
||||
},
|
||||
|
||||
fetchVoters() {
|
||||
const { attrs, state } = this;
|
||||
if (state.loaded === 'loading') { return; }
|
||||
if (state.loaded === "loading") {
|
||||
return;
|
||||
}
|
||||
|
||||
state.loaded = 'loading';
|
||||
state.loaded = "loading";
|
||||
|
||||
return fetchVoters({
|
||||
post_id: attrs.postId,
|
||||
|
@ -102,11 +109,12 @@ createWidget('discourse-poll-voters', {
|
|||
option_id: attrs.optionId,
|
||||
offset: state.offset
|
||||
}).then(result => {
|
||||
state.loaded = 'loaded';
|
||||
state.loaded = "loaded";
|
||||
state.offset += 1;
|
||||
|
||||
const pollResult = result[attrs.pollName];
|
||||
const newVoters = attrs.pollType === 'number' ? pollResult : pollResult[attrs.optionId];
|
||||
const newVoters =
|
||||
attrs.pollType === "number" ? pollResult : pollResult[attrs.optionId];
|
||||
state.pollVoters = state.pollVoters.concat(newVoters);
|
||||
|
||||
this.scheduleRerender();
|
||||
|
@ -118,46 +126,50 @@ createWidget('discourse-poll-voters', {
|
|||
},
|
||||
|
||||
html(attrs, state) {
|
||||
if (attrs.pollVoters && state.loaded === 'new') {
|
||||
if (attrs.pollVoters && state.loaded === "new") {
|
||||
state.pollVoters = attrs.pollVoters;
|
||||
}
|
||||
|
||||
const contents = state.pollVoters.map(user => {
|
||||
return h('li', [avatarFor('tiny', {
|
||||
username: user.username,
|
||||
template: user.avatar_template
|
||||
}), ' ']);
|
||||
return h("li", [
|
||||
avatarFor("tiny", {
|
||||
username: user.username,
|
||||
template: user.avatar_template
|
||||
}),
|
||||
" "
|
||||
]);
|
||||
});
|
||||
|
||||
if (state.pollVoters.length < attrs.totalVotes) {
|
||||
contents.push(this.attach('discourse-poll-load-more', { id: attrs.id() }));
|
||||
contents.push(
|
||||
this.attach("discourse-poll-load-more", { id: attrs.id() })
|
||||
);
|
||||
}
|
||||
|
||||
return h('div.poll-voters', contents);
|
||||
return h("div.poll-voters", contents);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
createWidget('discourse-poll-standard-results', {
|
||||
tagName: 'ul.results',
|
||||
createWidget("discourse-poll-standard-results", {
|
||||
tagName: "ul.results",
|
||||
buildKey: attrs => `${attrs.id}-standard-results`,
|
||||
|
||||
defaultState() {
|
||||
return {
|
||||
loaded: 'new'
|
||||
loaded: "new"
|
||||
};
|
||||
},
|
||||
|
||||
fetchVoters() {
|
||||
const { attrs, state } = this;
|
||||
|
||||
if (state.loaded === 'new') {
|
||||
if (state.loaded === "new") {
|
||||
fetchVoters({
|
||||
post_id: attrs.post.id,
|
||||
poll_name: attrs.poll.get('name')
|
||||
poll_name: attrs.poll.get("name")
|
||||
}).then(result => {
|
||||
state.voters = result[attrs.poll.get('name')];
|
||||
state.loaded = 'loaded';
|
||||
state.voters = result[attrs.poll.get("name")];
|
||||
state.loaded = "loaded";
|
||||
this.scheduleRerender();
|
||||
});
|
||||
}
|
||||
|
@ -165,11 +177,11 @@ createWidget('discourse-poll-standard-results', {
|
|||
|
||||
html(attrs, state) {
|
||||
const { poll } = attrs;
|
||||
const options = poll.get('options');
|
||||
const options = poll.get("options");
|
||||
|
||||
if (options) {
|
||||
const voters = poll.get('voters');
|
||||
const isPublic = poll.get('public');
|
||||
const voters = poll.get("voters");
|
||||
const isPublic = poll.get("public");
|
||||
|
||||
const ordered = _.clone(options).sort((a, b) => {
|
||||
if (a.votes < b.votes) {
|
||||
|
@ -185,11 +197,14 @@ createWidget('discourse-poll-standard-results', {
|
|||
}
|
||||
});
|
||||
|
||||
const percentages = voters === 0 ?
|
||||
Array(ordered.length).fill(0) :
|
||||
ordered.map(o => 100 * o.votes / voters);
|
||||
const percentages =
|
||||
voters === 0
|
||||
? Array(ordered.length).fill(0)
|
||||
: ordered.map(o => (100 * o.votes) / voters);
|
||||
|
||||
const rounded = attrs.isMultiple ? percentages.map(Math.floor) : evenRound(percentages);
|
||||
const rounded = attrs.isMultiple
|
||||
? percentages.map(Math.floor)
|
||||
: evenRound(percentages);
|
||||
|
||||
if (isPublic) this.fetchVoters();
|
||||
|
||||
|
@ -198,50 +213,58 @@ createWidget('discourse-poll-standard-results', {
|
|||
const per = rounded[idx].toString();
|
||||
const chosen = (attrs.vote || []).includes(option.id);
|
||||
|
||||
contents.push(h('div.option',
|
||||
h('p', [ h('span.percentage', `${per}%`), optionHtml(option) ])
|
||||
));
|
||||
contents.push(
|
||||
h(
|
||||
"div.option",
|
||||
h("p", [h("span.percentage", `${per}%`), optionHtml(option)])
|
||||
)
|
||||
);
|
||||
|
||||
contents.push(h('div.bar-back',
|
||||
h('div.bar', { attributes: { style: `width:${per}%` }})
|
||||
));
|
||||
contents.push(
|
||||
h(
|
||||
"div.bar-back",
|
||||
h("div.bar", { attributes: { style: `width:${per}%` } })
|
||||
)
|
||||
);
|
||||
|
||||
if (isPublic) {
|
||||
contents.push(this.attach('discourse-poll-voters', {
|
||||
id: () => `poll-voters-${option.id}`,
|
||||
postId: attrs.post.id,
|
||||
optionId: option.id,
|
||||
pollName: poll.get('name'),
|
||||
totalVotes: option.votes,
|
||||
pollVoters: (state.voters && state.voters[option.id]) || []
|
||||
}));
|
||||
contents.push(
|
||||
this.attach("discourse-poll-voters", {
|
||||
id: () => `poll-voters-${option.id}`,
|
||||
postId: attrs.post.id,
|
||||
optionId: option.id,
|
||||
pollName: poll.get("name"),
|
||||
totalVotes: option.votes,
|
||||
pollVoters: (state.voters && state.voters[option.id]) || []
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return h('li', { className: `${chosen ? 'chosen' : ''}` }, contents);
|
||||
return h("li", { className: `${chosen ? "chosen" : ""}` }, contents);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
createWidget('discourse-poll-number-results', {
|
||||
createWidget("discourse-poll-number-results", {
|
||||
buildKey: attrs => `${attrs.id}-number-results`,
|
||||
|
||||
defaultState() {
|
||||
return {
|
||||
loaded: 'new'
|
||||
loaded: "new"
|
||||
};
|
||||
},
|
||||
|
||||
fetchVoters() {
|
||||
const { attrs, state } = this;
|
||||
|
||||
if (state.loaded === 'new') {
|
||||
if (state.loaded === "new") {
|
||||
fetchVoters({
|
||||
post_id: attrs.post.id,
|
||||
poll_name: attrs.poll.get('name')
|
||||
poll_name: attrs.poll.get("name")
|
||||
}).then(result => {
|
||||
state.voters = result[attrs.poll.get('name')];
|
||||
state.loaded = 'loaded';
|
||||
state.voters = result[attrs.poll.get("name")];
|
||||
state.loaded = "loaded";
|
||||
this.scheduleRerender();
|
||||
});
|
||||
}
|
||||
|
@ -249,59 +272,69 @@ createWidget('discourse-poll-number-results', {
|
|||
|
||||
html(attrs, state) {
|
||||
const { poll } = attrs;
|
||||
const isPublic = poll.get('public');
|
||||
const isPublic = poll.get("public");
|
||||
|
||||
const totalScore = poll.get('options').reduce((total, o) => {
|
||||
const totalScore = poll.get("options").reduce((total, o) => {
|
||||
return total + parseInt(o.html, 10) * parseInt(o.votes, 10);
|
||||
}, 0);
|
||||
|
||||
const voters = poll.voters;
|
||||
const average = voters === 0 ? 0 : round(totalScore / voters, -2);
|
||||
const averageRating = I18n.t("poll.average_rating", { average });
|
||||
const results = [h('div.poll-results-number-rating', new RawHtml({ html: `<span>${averageRating}</span>` }))];
|
||||
const results = [
|
||||
h(
|
||||
"div.poll-results-number-rating",
|
||||
new RawHtml({ html: `<span>${averageRating}</span>` })
|
||||
)
|
||||
];
|
||||
|
||||
if (isPublic) {
|
||||
this.fetchVoters();
|
||||
|
||||
results.push(this.attach('discourse-poll-voters', {
|
||||
id: () => `poll-voters-${poll.get('name')}`,
|
||||
totalVotes: poll.get('voters'),
|
||||
pollVoters: state.voters || [],
|
||||
postId: attrs.post.id,
|
||||
pollName: poll.get('name'),
|
||||
pollType: poll.get('type')
|
||||
}));
|
||||
results.push(
|
||||
this.attach("discourse-poll-voters", {
|
||||
id: () => `poll-voters-${poll.get("name")}`,
|
||||
totalVotes: poll.get("voters"),
|
||||
pollVoters: state.voters || [],
|
||||
postId: attrs.post.id,
|
||||
pollName: poll.get("name"),
|
||||
pollType: poll.get("type")
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
});
|
||||
|
||||
createWidget('discourse-poll-container', {
|
||||
tagName: 'div.poll-container',
|
||||
createWidget("discourse-poll-container", {
|
||||
tagName: "div.poll-container",
|
||||
html(attrs) {
|
||||
const { poll } = attrs;
|
||||
|
||||
if (attrs.showResults || attrs.isClosed) {
|
||||
const type = poll.get('type') === 'number' ? 'number' : 'standard';
|
||||
const type = poll.get("type") === "number" ? "number" : "standard";
|
||||
return this.attach(`discourse-poll-${type}-results`, attrs);
|
||||
}
|
||||
|
||||
const options = poll.get('options');
|
||||
const options = poll.get("options");
|
||||
if (options) {
|
||||
return h('ul', options.map(option => {
|
||||
return this.attach('discourse-poll-option', {
|
||||
option,
|
||||
isMultiple: attrs.isMultiple,
|
||||
vote: attrs.vote
|
||||
});
|
||||
}));
|
||||
return h(
|
||||
"ul",
|
||||
options.map(option => {
|
||||
return this.attach("discourse-poll-option", {
|
||||
option,
|
||||
isMultiple: attrs.isMultiple,
|
||||
vote: attrs.vote
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
createWidget('discourse-poll-info', {
|
||||
tagName: 'div.poll-info',
|
||||
createWidget("discourse-poll-info", {
|
||||
tagName: "div.poll-info",
|
||||
|
||||
multipleHelpText(min, max, options) {
|
||||
if (max > 0) {
|
||||
|
@ -311,9 +344,14 @@ createWidget('discourse-poll-info', {
|
|||
}
|
||||
} else if (min > 1) {
|
||||
if (max < options) {
|
||||
return I18n.t("poll.multiple.help.between_min_and_max_options", { min, max });
|
||||
return I18n.t("poll.multiple.help.between_min_and_max_options", {
|
||||
min,
|
||||
max
|
||||
});
|
||||
} else {
|
||||
return I18n.t("poll.multiple.help.at_least_min_options", { count: min });
|
||||
return I18n.t("poll.multiple.help.at_least_min_options", {
|
||||
count: min
|
||||
});
|
||||
}
|
||||
} else if (max <= options) {
|
||||
return I18n.t("poll.multiple.help.up_to_max_options", { count: max });
|
||||
|
@ -323,33 +361,46 @@ createWidget('discourse-poll-info', {
|
|||
|
||||
html(attrs) {
|
||||
const { poll } = attrs;
|
||||
const count = poll.get('voters');
|
||||
const result = [h('p', [
|
||||
h('span.info-number', count.toString()),
|
||||
h('span.info-label', I18n.t('poll.voters', { count }))
|
||||
])];
|
||||
const count = poll.get("voters");
|
||||
const result = [
|
||||
h("p", [
|
||||
h("span.info-number", count.toString()),
|
||||
h("span.info-label", I18n.t("poll.voters", { count }))
|
||||
])
|
||||
];
|
||||
|
||||
if (attrs.isMultiple) {
|
||||
if (attrs.showResults || attrs.isClosed) {
|
||||
const totalVotes = poll.get('options').reduce((total, o) => {
|
||||
const totalVotes = poll.get("options").reduce((total, o) => {
|
||||
return total + parseInt(o.votes, 10);
|
||||
}, 0);
|
||||
|
||||
result.push(h('p', [
|
||||
h('span.info-number', totalVotes.toString()),
|
||||
h('span.info-label', I18n.t("poll.total_votes", { count: totalVotes }))
|
||||
]));
|
||||
result.push(
|
||||
h("p", [
|
||||
h("span.info-number", totalVotes.toString()),
|
||||
h(
|
||||
"span.info-label",
|
||||
I18n.t("poll.total_votes", { count: totalVotes })
|
||||
)
|
||||
])
|
||||
);
|
||||
} else {
|
||||
const help = this.multipleHelpText(attrs.min, attrs.max, poll.get('options.length'));
|
||||
const help = this.multipleHelpText(
|
||||
attrs.min,
|
||||
attrs.max,
|
||||
poll.get("options.length")
|
||||
);
|
||||
if (help) {
|
||||
result.push(new RawHtml({ html: `<span class="info-text">${help}</span>` }));
|
||||
result.push(
|
||||
new RawHtml({ html: `<span class="info-text">${help}</span>` })
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!attrs.isClosed) {
|
||||
if (!attrs.showResults && poll.get('public')) {
|
||||
result.push(h('span.info-text', I18n.t('poll.public.title')));
|
||||
if (!attrs.showResults && poll.get("public")) {
|
||||
result.push(h("span.info-text", I18n.t("poll.public.title")));
|
||||
}
|
||||
|
||||
if (poll.close) {
|
||||
|
@ -358,9 +409,14 @@ createWidget('discourse-poll-info', {
|
|||
const title = closeDate.format("LLL");
|
||||
const timeLeft = moment().to(closeDate.local(), true);
|
||||
|
||||
result.push(new RawHtml({
|
||||
html: `<span class="info-text" title="${title}">${I18n.t("poll.automatic_close.closes_in", { timeLeft })}</span>`
|
||||
}));
|
||||
result.push(
|
||||
new RawHtml({
|
||||
html: `<span class="info-text" title="${title}">${I18n.t(
|
||||
"poll.automatic_close.closes_in",
|
||||
{ timeLeft }
|
||||
)}</span>`
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -369,46 +425,52 @@ createWidget('discourse-poll-info', {
|
|||
}
|
||||
});
|
||||
|
||||
createWidget('discourse-poll-buttons', {
|
||||
tagName: 'div.poll-buttons',
|
||||
createWidget("discourse-poll-buttons", {
|
||||
tagName: "div.poll-buttons",
|
||||
|
||||
html(attrs) {
|
||||
const results = [];
|
||||
const { poll, post } = attrs;
|
||||
const topicArchived = post.get('topic.archived');
|
||||
const topicArchived = post.get("topic.archived");
|
||||
const closed = attrs.isClosed;
|
||||
const hideResultsDisabled = closed || topicArchived;
|
||||
|
||||
if (attrs.isMultiple && !hideResultsDisabled) {
|
||||
const castVotesDisabled = !attrs.canCastVotes;
|
||||
results.push(this.attach('button', {
|
||||
className: `btn cast-votes ${castVotesDisabled ? '' : 'btn-primary'}`,
|
||||
label: 'poll.cast-votes.label',
|
||||
title: 'poll.cast-votes.title',
|
||||
disabled: castVotesDisabled,
|
||||
action: 'castVotes'
|
||||
}));
|
||||
results.push(' ');
|
||||
results.push(
|
||||
this.attach("button", {
|
||||
className: `btn cast-votes ${castVotesDisabled ? "" : "btn-primary"}`,
|
||||
label: "poll.cast-votes.label",
|
||||
title: "poll.cast-votes.title",
|
||||
disabled: castVotesDisabled,
|
||||
action: "castVotes"
|
||||
})
|
||||
);
|
||||
results.push(" ");
|
||||
}
|
||||
|
||||
if (attrs.showResults || hideResultsDisabled) {
|
||||
results.push(this.attach('button', {
|
||||
className: 'btn toggle-results',
|
||||
label: 'poll.hide-results.label',
|
||||
title: 'poll.hide-results.title',
|
||||
icon: 'eye-slash',
|
||||
disabled: hideResultsDisabled,
|
||||
action: 'toggleResults'
|
||||
}));
|
||||
results.push(
|
||||
this.attach("button", {
|
||||
className: "btn toggle-results",
|
||||
label: "poll.hide-results.label",
|
||||
title: "poll.hide-results.title",
|
||||
icon: "eye-slash",
|
||||
disabled: hideResultsDisabled,
|
||||
action: "toggleResults"
|
||||
})
|
||||
);
|
||||
} else {
|
||||
results.push(this.attach('button', {
|
||||
className: 'btn toggle-results',
|
||||
label: 'poll.show-results.label',
|
||||
title: 'poll.show-results.title',
|
||||
icon: 'eye',
|
||||
disabled: poll.get('voters') === 0,
|
||||
action: 'toggleResults'
|
||||
}));
|
||||
results.push(
|
||||
this.attach("button", {
|
||||
className: "btn toggle-results",
|
||||
label: "poll.show-results.label",
|
||||
title: "poll.show-results.title",
|
||||
icon: "eye",
|
||||
disabled: poll.get("voters") === 0,
|
||||
action: "toggleResults"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (attrs.isAutomaticallyClosed) {
|
||||
|
@ -416,34 +478,44 @@ createWidget('discourse-poll-buttons', {
|
|||
const title = closeDate.format("LLL");
|
||||
const age = relativeAge(closeDate.toDate(), { addAgo: true });
|
||||
|
||||
results.push(new RawHtml({
|
||||
html: `<span class="info-text" title="${title}">${I18n.t("poll.automatic_close.age", { age })}</span>`
|
||||
}));
|
||||
results.push(
|
||||
new RawHtml({
|
||||
html: `<span class="info-text" title="${title}">${I18n.t(
|
||||
"poll.automatic_close.age",
|
||||
{ age }
|
||||
)}</span>`
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (this.currentUser &&
|
||||
(this.currentUser.get("id") === post.get('user_id') ||
|
||||
this.currentUser.get("staff")) &&
|
||||
!topicArchived) {
|
||||
|
||||
if (
|
||||
this.currentUser &&
|
||||
(this.currentUser.get("id") === post.get("user_id") ||
|
||||
this.currentUser.get("staff")) &&
|
||||
!topicArchived
|
||||
) {
|
||||
if (closed) {
|
||||
if (!attrs.isAutomaticallyClosed) {
|
||||
results.push(this.attach('button', {
|
||||
className: 'btn toggle-status',
|
||||
label: 'poll.open.label',
|
||||
title: 'poll.open.title',
|
||||
icon: 'unlock-alt',
|
||||
action: 'toggleStatus'
|
||||
}));
|
||||
results.push(
|
||||
this.attach("button", {
|
||||
className: "btn toggle-status",
|
||||
label: "poll.open.label",
|
||||
title: "poll.open.title",
|
||||
icon: "unlock-alt",
|
||||
action: "toggleStatus"
|
||||
})
|
||||
);
|
||||
}
|
||||
} else {
|
||||
results.push(this.attach('button', {
|
||||
className: 'btn toggle-status btn-danger',
|
||||
label: 'poll.close.label',
|
||||
title: 'poll.close.title',
|
||||
icon: 'lock',
|
||||
action: 'toggleStatus'
|
||||
}));
|
||||
results.push(
|
||||
this.attach("button", {
|
||||
className: "btn toggle-status btn-danger",
|
||||
label: "poll.close.label",
|
||||
title: "poll.close.title",
|
||||
icon: "lock",
|
||||
action: "toggleStatus"
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -451,23 +523,23 @@ createWidget('discourse-poll-buttons', {
|
|||
}
|
||||
});
|
||||
|
||||
export default createWidget('discourse-poll', {
|
||||
tagName: 'div.poll',
|
||||
export default createWidget("discourse-poll", {
|
||||
tagName: "div.poll",
|
||||
buildKey: attrs => attrs.id,
|
||||
|
||||
buildAttributes(attrs) {
|
||||
const { poll } = attrs;
|
||||
return {
|
||||
"data-poll-type": poll.get('type'),
|
||||
"data-poll-name": poll.get('name'),
|
||||
"data-poll-status": poll.get('status'),
|
||||
"data-poll-public": poll.get('public'),
|
||||
"data-poll-close": poll.get('close'),
|
||||
"data-poll-type": poll.get("type"),
|
||||
"data-poll-name": poll.get("name"),
|
||||
"data-poll-status": poll.get("status"),
|
||||
"data-poll-public": poll.get("public"),
|
||||
"data-poll-close": poll.get("close")
|
||||
};
|
||||
},
|
||||
|
||||
defaultState(attrs) {
|
||||
const showResults = this.isClosed() || attrs.post.get('topic.archived');
|
||||
const showResults = this.isClosed() || attrs.post.get("topic.archived");
|
||||
return { loading: false, showResults };
|
||||
},
|
||||
|
||||
|
@ -482,23 +554,27 @@ export default createWidget('discourse-poll', {
|
|||
max: this.max()
|
||||
});
|
||||
|
||||
return h('div', [
|
||||
this.attach('discourse-poll-container', newAttrs),
|
||||
this.attach('discourse-poll-info', newAttrs),
|
||||
this.attach('discourse-poll-buttons', newAttrs)
|
||||
return h("div", [
|
||||
this.attach("discourse-poll-container", newAttrs),
|
||||
this.attach("discourse-poll-info", newAttrs),
|
||||
this.attach("discourse-poll-buttons", newAttrs)
|
||||
]);
|
||||
},
|
||||
|
||||
min() {
|
||||
let min = parseInt(this.attrs.poll.min, 10);
|
||||
if (isNaN(min) || min < 1) { min = 1; }
|
||||
if (isNaN(min) || min < 1) {
|
||||
min = 1;
|
||||
}
|
||||
return min;
|
||||
},
|
||||
|
||||
max() {
|
||||
let max = parseInt(this.attrs.poll.max, 10);
|
||||
const numOptions = this.attrs.poll.options.length;
|
||||
if (isNaN(max) || max > numOptions) { max = numOptions; }
|
||||
if (isNaN(max) || max > numOptions) {
|
||||
max = numOptions;
|
||||
}
|
||||
return max;
|
||||
},
|
||||
|
||||
|
@ -522,7 +598,9 @@ export default createWidget('discourse-poll', {
|
|||
const selectedOptionCount = attrs.vote.length;
|
||||
|
||||
if (attrs.isMultiple) {
|
||||
return selectedOptionCount >= this.min() && selectedOptionCount <= this.max();
|
||||
return (
|
||||
selectedOptionCount >= this.min() && selectedOptionCount <= this.max()
|
||||
);
|
||||
}
|
||||
|
||||
return selectedOptionCount > 0;
|
||||
|
@ -532,7 +610,9 @@ export default createWidget('discourse-poll', {
|
|||
const { state, attrs } = this;
|
||||
const { post, poll } = attrs;
|
||||
|
||||
if (this.isAutomaticallyClosed()) { return; }
|
||||
if (this.isAutomaticallyClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bootbox.confirm(
|
||||
I18n.t(this.isClosed() ? "poll.open.confirm" : "poll.close.confirm"),
|
||||
|
@ -546,22 +626,25 @@ export default createWidget('discourse-poll', {
|
|||
ajax("/polls/toggle_status", {
|
||||
type: "PUT",
|
||||
data: {
|
||||
post_id: post.get('id'),
|
||||
poll_name: poll.get('name'),
|
||||
status,
|
||||
post_id: post.get("id"),
|
||||
poll_name: poll.get("name"),
|
||||
status
|
||||
}
|
||||
}).then(() => {
|
||||
poll.set('status', status);
|
||||
this.scheduleRerender();
|
||||
}).catch((error) => {
|
||||
if (error) {
|
||||
popupAjaxError(error);
|
||||
} else {
|
||||
bootbox.alert(I18n.t("poll.error_while_toggling_status"));
|
||||
}
|
||||
}).finally(() => {
|
||||
state.loading = false;
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
poll.set("status", status);
|
||||
this.scheduleRerender();
|
||||
})
|
||||
.catch(error => {
|
||||
if (error) {
|
||||
popupAjaxError(error);
|
||||
} else {
|
||||
bootbox.alert(I18n.t("poll.error_while_toggling_status"));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
state.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -572,14 +655,18 @@ export default createWidget('discourse-poll', {
|
|||
},
|
||||
|
||||
showLogin() {
|
||||
this.register.lookup('route:application').send('showLogin');
|
||||
this.register.lookup("route:application").send("showLogin");
|
||||
},
|
||||
|
||||
toggleOption(option) {
|
||||
const { attrs } = this;
|
||||
|
||||
if (this.isClosed()) { return; }
|
||||
if (!this.currentUser) { this.showLogin(); }
|
||||
if (this.isClosed()) {
|
||||
return;
|
||||
}
|
||||
if (!this.currentUser) {
|
||||
this.showLogin();
|
||||
}
|
||||
|
||||
const { vote } = attrs;
|
||||
|
||||
|
@ -600,8 +687,12 @@ export default createWidget('discourse-poll', {
|
|||
},
|
||||
|
||||
castVotes() {
|
||||
if (!this.canCastVotes()) { return; }
|
||||
if (!this.currentUser) { return this.showLogin(); }
|
||||
if (!this.canCastVotes()) {
|
||||
return;
|
||||
}
|
||||
if (!this.currentUser) {
|
||||
return this.showLogin();
|
||||
}
|
||||
|
||||
const { attrs, state } = this;
|
||||
|
||||
|
@ -614,16 +705,19 @@ export default createWidget('discourse-poll', {
|
|||
poll_name: attrs.poll.name,
|
||||
options: attrs.vote
|
||||
}
|
||||
}).then(() => {
|
||||
state.showResults = true;
|
||||
}).catch((error) => {
|
||||
if (error) {
|
||||
popupAjaxError(error);
|
||||
} else {
|
||||
bootbox.alert(I18n.t("poll.error_while_casting_votes"));
|
||||
}
|
||||
}).finally(() => {
|
||||
state.loading = false;
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
state.showResults = true;
|
||||
})
|
||||
.catch(error => {
|
||||
if (error) {
|
||||
popupAjaxError(error);
|
||||
} else {
|
||||
bootbox.alert(I18n.t("poll.error_while_casting_votes"));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
state.loading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -13,32 +13,41 @@ acceptance("Poll Builder - polls are disabled", {
|
|||
}
|
||||
});
|
||||
|
||||
test("regular user - sufficient trust level", (assert) => {
|
||||
test("regular user - sufficient trust level", assert => {
|
||||
replaceCurrentUser({ staff: false, trust_level: 3 });
|
||||
|
||||
displayPollBuilderButton();
|
||||
|
||||
andThen(() => {
|
||||
assert.ok(!exists(".select-kit-row[title='Build Poll']"), "it hides the builder button");
|
||||
assert.ok(
|
||||
!exists(".select-kit-row[title='Build Poll']"),
|
||||
"it hides the builder button"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("regular user - insufficient trust level", (assert) => {
|
||||
test("regular user - insufficient trust level", assert => {
|
||||
replaceCurrentUser({ staff: false, trust_level: 1 });
|
||||
|
||||
displayPollBuilderButton();
|
||||
|
||||
andThen(() => {
|
||||
assert.ok(!exists(".select-kit-row[title='Build Poll']"), "it hides the builder button");
|
||||
assert.ok(
|
||||
!exists(".select-kit-row[title='Build Poll']"),
|
||||
"it hides the builder button"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("staff", (assert) => {
|
||||
test("staff", assert => {
|
||||
replaceCurrentUser({ staff: true });
|
||||
|
||||
displayPollBuilderButton();
|
||||
|
||||
andThen(() => {
|
||||
assert.ok(!exists(".select-kit-row[title='Build Poll']"), "it hides the builder button");
|
||||
assert.ok(
|
||||
!exists(".select-kit-row[title='Build Poll']"),
|
||||
"it hides the builder button"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,32 +13,41 @@ acceptance("Poll Builder - polls are enabled", {
|
|||
}
|
||||
});
|
||||
|
||||
test("regular user - sufficient trust level", (assert) => {
|
||||
test("regular user - sufficient trust level", assert => {
|
||||
replaceCurrentUser({ staff: false, trust_level: 1 });
|
||||
|
||||
displayPollBuilderButton();
|
||||
|
||||
andThen(() => {
|
||||
assert.ok(exists(".select-kit-row[title='Build Poll']"), "it shows the builder button");
|
||||
assert.ok(
|
||||
exists(".select-kit-row[title='Build Poll']"),
|
||||
"it shows the builder button"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("regular user - insufficient trust level", (assert) => {
|
||||
test("regular user - insufficient trust level", assert => {
|
||||
replaceCurrentUser({ staff: false, trust_level: 0 });
|
||||
|
||||
displayPollBuilderButton();
|
||||
|
||||
andThen(() => {
|
||||
assert.ok(!exists(".select-kit-row[title='Build Poll']"), "it hides the builder button");
|
||||
assert.ok(
|
||||
!exists(".select-kit-row[title='Build Poll']"),
|
||||
"it hides the builder button"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("staff - with insufficient trust level", (assert) => {
|
||||
test("staff - with insufficient trust level", assert => {
|
||||
replaceCurrentUser({ staff: true, trust_level: 0 });
|
||||
|
||||
displayPollBuilderButton();
|
||||
|
||||
andThen(() => {
|
||||
assert.ok(exists(".select-kit-row[title='Build Poll']"), "it shows the builder button");
|
||||
assert.ok(
|
||||
exists(".select-kit-row[title='Build Poll']"),
|
||||
"it shows the builder button"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,13 +1,13 @@
|
|||
import { mapRoutes } from 'discourse/mapping-router';
|
||||
import { mapRoutes } from "discourse/mapping-router";
|
||||
|
||||
moduleFor("controller:poll-ui-builder", "controller:poll-ui-builder", {
|
||||
setup() {
|
||||
this.registry.register('router:main', mapRoutes());
|
||||
this.subject().set('toolbarEvent', {
|
||||
this.registry.register("router:main", mapRoutes());
|
||||
this.subject().set("toolbarEvent", {
|
||||
getText: () => ""
|
||||
});
|
||||
},
|
||||
needs: ['controller:modal']
|
||||
needs: ["controller:modal"]
|
||||
});
|
||||
|
||||
test("isMultiple", function(assert) {
|
||||
|
@ -91,46 +91,73 @@ test("pollMinOptions", function(assert) {
|
|||
pollOptionsCount: 1
|
||||
});
|
||||
|
||||
assert.deepEqual(controller.get("pollMinOptions"), [{ name: 1, value: 1 }], "it should return the right options");
|
||||
assert.deepEqual(
|
||||
controller.get("pollMinOptions"),
|
||||
[{ name: 1, value: 1 }],
|
||||
"it should return the right options"
|
||||
);
|
||||
|
||||
controller.set("pollOptionsCount", 2);
|
||||
|
||||
assert.deepEqual(controller.get("pollMinOptions"), [
|
||||
{ name: 1, value: 1 }, { name: 2, value: 2 }
|
||||
], "it should return the right options");
|
||||
assert.deepEqual(
|
||||
controller.get("pollMinOptions"),
|
||||
[{ name: 1, value: 1 }, { name: 2, value: 2 }],
|
||||
"it should return the right options"
|
||||
);
|
||||
|
||||
controller.set("isNumber", true);
|
||||
controller.siteSettings.poll_maximum_options = 2;
|
||||
|
||||
assert.deepEqual(controller.get("pollMinOptions"), [
|
||||
{ name: 1, value: 1 }, { name: 2, value: 2 }
|
||||
], "it should return the right options");
|
||||
assert.deepEqual(
|
||||
controller.get("pollMinOptions"),
|
||||
[{ name: 1, value: 1 }, { name: 2, value: 2 }],
|
||||
"it should return the right options"
|
||||
);
|
||||
});
|
||||
|
||||
test("pollMaxOptions", function(assert) {
|
||||
const controller = this.subject();
|
||||
controller.siteSettings = Discourse.SiteSettings;
|
||||
|
||||
controller.setProperties({ isMultiple: true, pollOptionsCount: 1, pollMin: 1 });
|
||||
controller.setProperties({
|
||||
isMultiple: true,
|
||||
pollOptionsCount: 1,
|
||||
pollMin: 1
|
||||
});
|
||||
|
||||
assert.deepEqual(controller.get("pollMaxOptions"), [], "it should return the right options");
|
||||
assert.deepEqual(
|
||||
controller.get("pollMaxOptions"),
|
||||
[],
|
||||
"it should return the right options"
|
||||
);
|
||||
|
||||
controller.set("pollOptionsCount", 2);
|
||||
|
||||
assert.deepEqual(controller.get("pollMaxOptions"), [
|
||||
{ name: 2, value: 2 }
|
||||
], "it should return the right options");
|
||||
assert.deepEqual(
|
||||
controller.get("pollMaxOptions"),
|
||||
[{ name: 2, value: 2 }],
|
||||
"it should return the right options"
|
||||
);
|
||||
|
||||
controller.siteSettings.poll_maximum_options = 3;
|
||||
controller.setProperties({ isMultiple: false, isNumber: true, pollStep: 2, pollMin: 1 });
|
||||
controller.setProperties({
|
||||
isMultiple: false,
|
||||
isNumber: true,
|
||||
pollStep: 2,
|
||||
pollMin: 1
|
||||
});
|
||||
|
||||
assert.deepEqual(controller.get("pollMaxOptions"), [
|
||||
{ name: 2, value: 2 },
|
||||
{ name: 3, value: 3 },
|
||||
{ name: 4, value: 4 },
|
||||
{ name: 5, value: 5 },
|
||||
{ name: 6, value: 6 }
|
||||
], "it should return the right options");
|
||||
assert.deepEqual(
|
||||
controller.get("pollMaxOptions"),
|
||||
[
|
||||
{ name: 2, value: 2 },
|
||||
{ name: 3, value: 3 },
|
||||
{ name: 4, value: 4 },
|
||||
{ name: 5, value: 5 },
|
||||
{ name: 6, value: 6 }
|
||||
],
|
||||
"it should return the right options"
|
||||
);
|
||||
});
|
||||
|
||||
test("pollStepOptions", function(assert) {
|
||||
|
@ -140,15 +167,19 @@ test("pollStepOptions", function(assert) {
|
|||
|
||||
controller.set("isNumber", false);
|
||||
|
||||
assert.equal(controller.get("pollStepOptions"), null, "is should return null");
|
||||
assert.equal(
|
||||
controller.get("pollStepOptions"),
|
||||
null,
|
||||
"is should return null"
|
||||
);
|
||||
|
||||
controller.setProperties({ isNumber: true });
|
||||
|
||||
assert.deepEqual(controller.get("pollStepOptions"), [
|
||||
{ name: 1, value: 1 },
|
||||
{ name: 2, value: 2 },
|
||||
{ name: 3, value: 3 }
|
||||
], "it should return the right options");
|
||||
assert.deepEqual(
|
||||
controller.get("pollStepOptions"),
|
||||
[{ name: 1, value: 1 }, { name: 2, value: 2 }, { name: 3, value: 3 }],
|
||||
"it should return the right options"
|
||||
);
|
||||
});
|
||||
|
||||
test("disableInsert", function(assert) {
|
||||
|
@ -187,19 +218,35 @@ test("number pollOutput", function(assert) {
|
|||
pollMin: 1
|
||||
});
|
||||
|
||||
assert.equal(controller.get("pollOutput"), "[poll type=number min=1 max=20 step=1]\n[/poll]", "it should return the right output");
|
||||
assert.equal(
|
||||
controller.get("pollOutput"),
|
||||
"[poll type=number min=1 max=20 step=1]\n[/poll]",
|
||||
"it should return the right output"
|
||||
);
|
||||
|
||||
controller.set("pollStep", 2);
|
||||
|
||||
assert.equal(controller.get("pollOutput"), "[poll type=number min=1 max=20 step=2]\n[/poll]", "it should return the right output");
|
||||
assert.equal(
|
||||
controller.get("pollOutput"),
|
||||
"[poll type=number min=1 max=20 step=2]\n[/poll]",
|
||||
"it should return the right output"
|
||||
);
|
||||
|
||||
controller.set("publicPoll", true);
|
||||
|
||||
assert.equal(controller.get("pollOutput"), "[poll type=number min=1 max=20 step=2 public=true]\n[/poll]", "it should return the right output");
|
||||
assert.equal(
|
||||
controller.get("pollOutput"),
|
||||
"[poll type=number min=1 max=20 step=2 public=true]\n[/poll]",
|
||||
"it should return the right output"
|
||||
);
|
||||
|
||||
controller.set("pollStep", 0);
|
||||
|
||||
assert.equal(controller.get("pollOutput"), "[poll type=number min=1 max=20 step=1 public=true]\n[/poll]", "it should return the right output");
|
||||
assert.equal(
|
||||
controller.get("pollOutput"),
|
||||
"[poll type=number min=1 max=20 step=1 public=true]\n[/poll]",
|
||||
"it should return the right output"
|
||||
);
|
||||
});
|
||||
|
||||
test("regular pollOutput", function(assert) {
|
||||
|
@ -213,14 +260,21 @@ test("regular pollOutput", function(assert) {
|
|||
pollType: controller.get("regularPollType")
|
||||
});
|
||||
|
||||
assert.equal(controller.get("pollOutput"), "[poll type=regular]\n* 1\n* 2\n[/poll]", "it should return the right output");
|
||||
assert.equal(
|
||||
controller.get("pollOutput"),
|
||||
"[poll type=regular]\n* 1\n* 2\n[/poll]",
|
||||
"it should return the right output"
|
||||
);
|
||||
|
||||
controller.set("publicPoll", "true");
|
||||
|
||||
assert.equal(controller.get("pollOutput"), "[poll type=regular public=true]\n* 1\n* 2\n[/poll]", "it should return the right output");
|
||||
assert.equal(
|
||||
controller.get("pollOutput"),
|
||||
"[poll type=regular public=true]\n* 1\n* 2\n[/poll]",
|
||||
"it should return the right output"
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
test("multiple pollOutput", function(assert) {
|
||||
const controller = this.subject();
|
||||
controller.siteSettings = Discourse.SiteSettings;
|
||||
|
@ -233,9 +287,17 @@ test("multiple pollOutput", function(assert) {
|
|||
pollOptions: "\n\n1\n\n2"
|
||||
});
|
||||
|
||||
assert.equal(controller.get("pollOutput"), "[poll type=multiple min=1 max=2]\n* 1\n* 2\n[/poll]", "it should return the right output");
|
||||
assert.equal(
|
||||
controller.get("pollOutput"),
|
||||
"[poll type=multiple min=1 max=2]\n* 1\n* 2\n[/poll]",
|
||||
"it should return the right output"
|
||||
);
|
||||
|
||||
controller.set("publicPoll", "true");
|
||||
|
||||
assert.equal(controller.get("pollOutput"), "[poll type=multiple min=1 max=2 public=true]\n* 1\n* 2\n[/poll]", "it should return the right output");
|
||||
assert.equal(
|
||||
controller.get("pollOutput"),
|
||||
"[poll type=multiple min=1 max=2 public=true]\n* 1\n* 2\n[/poll]",
|
||||
"it should return the right output"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,64 +1,64 @@
|
|||
import { moduleForWidget, widgetTest } from 'helpers/widget-test';
|
||||
moduleForWidget('discourse-poll-option');
|
||||
import { moduleForWidget, widgetTest } from "helpers/widget-test";
|
||||
moduleForWidget("discourse-poll-option");
|
||||
|
||||
const template = `{{mount-widget
|
||||
widget="discourse-poll-option"
|
||||
args=(hash option=option isMultiple=isMultiple vote=vote)}}`;
|
||||
|
||||
widgetTest('single, not selected', {
|
||||
widgetTest("single, not selected", {
|
||||
template,
|
||||
|
||||
beforeEach() {
|
||||
this.set('option', { id: 'opt-id' });
|
||||
this.set('vote', []);
|
||||
this.set("option", { id: "opt-id" });
|
||||
this.set("vote", []);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(find('li .d-icon-circle-o:eq(0)').length === 1);
|
||||
assert.ok(find("li .d-icon-circle-o:eq(0)").length === 1);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('single, selected', {
|
||||
widgetTest("single, selected", {
|
||||
template,
|
||||
|
||||
beforeEach() {
|
||||
this.set('option', { id: 'opt-id' });
|
||||
this.set('vote', ['opt-id']);
|
||||
this.set("option", { id: "opt-id" });
|
||||
this.set("vote", ["opt-id"]);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(find('li .d-icon-dot-circle-o:eq(0)').length === 1);
|
||||
assert.ok(find("li .d-icon-dot-circle-o:eq(0)").length === 1);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('multi, not selected', {
|
||||
widgetTest("multi, not selected", {
|
||||
template,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({
|
||||
option: { id: 'opt-id' },
|
||||
option: { id: "opt-id" },
|
||||
isMultiple: true,
|
||||
vote: []
|
||||
});
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(find('li .d-icon-square-o:eq(0)').length === 1);
|
||||
assert.ok(find("li .d-icon-square-o:eq(0)").length === 1);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('multi, selected', {
|
||||
widgetTest("multi, selected", {
|
||||
template,
|
||||
|
||||
beforeEach() {
|
||||
this.setProperties({
|
||||
option: { id: 'opt-id' },
|
||||
option: { id: "opt-id" },
|
||||
isMultiple: true,
|
||||
vote: ['opt-id']
|
||||
vote: ["opt-id"]
|
||||
});
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(find('li .d-icon-check-square-o:eq(0)').length === 1);
|
||||
assert.ok(find("li .d-icon-check-square-o:eq(0)").length === 1);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,67 +1,76 @@
|
|||
import { moduleForWidget, widgetTest } from 'helpers/widget-test';
|
||||
moduleForWidget('discourse-poll-standard-results');
|
||||
import { moduleForWidget, widgetTest } from "helpers/widget-test";
|
||||
moduleForWidget("discourse-poll-standard-results");
|
||||
|
||||
const template = `{{mount-widget
|
||||
widget="discourse-poll-standard-results"
|
||||
args=(hash poll=poll isMultiple=isMultiple)}}`;
|
||||
|
||||
widgetTest('options in descending order', {
|
||||
widgetTest("options in descending order", {
|
||||
template,
|
||||
|
||||
beforeEach() {
|
||||
this.set('poll', Ember.Object.create({
|
||||
options: [{ votes: 5 }, { votes: 4 }],
|
||||
voters: 9
|
||||
}));
|
||||
this.set(
|
||||
"poll",
|
||||
Ember.Object.create({
|
||||
options: [{ votes: 5 }, { votes: 4 }],
|
||||
voters: 9
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.$('.option .percentage:eq(0)').text(), '56%');
|
||||
assert.equal(this.$('.option .percentage:eq(1)').text(), '44%');
|
||||
assert.equal(this.$(".option .percentage:eq(0)").text(), "56%");
|
||||
assert.equal(this.$(".option .percentage:eq(1)").text(), "44%");
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('options in ascending order', {
|
||||
widgetTest("options in ascending order", {
|
||||
template,
|
||||
|
||||
beforeEach() {
|
||||
this.set('poll', Ember.Object.create({
|
||||
options: [{ votes: 4 }, { votes: 5 }],
|
||||
voters: 9
|
||||
}));
|
||||
this.set(
|
||||
"poll",
|
||||
Ember.Object.create({
|
||||
options: [{ votes: 4 }, { votes: 5 }],
|
||||
voters: 9
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.$('.option .percentage:eq(0)').text(), '56%');
|
||||
assert.equal(this.$('.option .percentage:eq(1)').text(), '44%');
|
||||
assert.equal(this.$(".option .percentage:eq(0)").text(), "56%");
|
||||
assert.equal(this.$(".option .percentage:eq(1)").text(), "44%");
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('multiple options in descending order', {
|
||||
widgetTest("multiple options in descending order", {
|
||||
template,
|
||||
|
||||
beforeEach() {
|
||||
this.set('isMultiple', true);
|
||||
this.set('poll', Ember.Object.create({
|
||||
type: 'multiple',
|
||||
options: [
|
||||
{ votes: 5, html: 'a' },
|
||||
{ votes: 2, html: 'b' },
|
||||
{ votes: 4, html: 'c' },
|
||||
{ votes: 1, html: 'b' },
|
||||
{ votes: 1, html: 'a' }
|
||||
],
|
||||
voters: 12
|
||||
}));
|
||||
this.set("isMultiple", true);
|
||||
this.set(
|
||||
"poll",
|
||||
Ember.Object.create({
|
||||
type: "multiple",
|
||||
options: [
|
||||
{ votes: 5, html: "a" },
|
||||
{ votes: 2, html: "b" },
|
||||
{ votes: 4, html: "c" },
|
||||
{ votes: 1, html: "b" },
|
||||
{ votes: 1, html: "a" }
|
||||
],
|
||||
voters: 12
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.$('.option .percentage:eq(0)').text(), '41%');
|
||||
assert.equal(this.$('.option .percentage:eq(1)').text(), '33%');
|
||||
assert.equal(this.$('.option .percentage:eq(2)').text(), '16%');
|
||||
assert.equal(this.$('.option .percentage:eq(3)').text(), '8%');
|
||||
assert.equal(this.$('.option span:nth-child(2):eq(3)').text(), 'a');
|
||||
assert.equal(this.$('.option .percentage:eq(4)').text(), '8%');
|
||||
assert.equal(this.$('.option span:nth-child(2):eq(4)').text(), 'b');
|
||||
assert.equal(this.$(".option .percentage:eq(0)").text(), "41%");
|
||||
assert.equal(this.$(".option .percentage:eq(1)").text(), "33%");
|
||||
assert.equal(this.$(".option .percentage:eq(2)").text(), "16%");
|
||||
assert.equal(this.$(".option .percentage:eq(3)").text(), "8%");
|
||||
assert.equal(this.$(".option span:nth-child(2):eq(3)").text(), "a");
|
||||
assert.equal(this.$(".option .percentage:eq(4)").text(), "8%");
|
||||
assert.equal(this.$(".option span:nth-child(2):eq(4)").text(), "b");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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(archetype) {
|
||||
return Topic.create({
|
||||
|
@ -13,9 +13,11 @@ const buildTopic = function(archetype) {
|
|||
};
|
||||
|
||||
function extractDescs(rows) {
|
||||
return Array.from(rows.find(".desc").map(function() {
|
||||
return this.textContent.trim();
|
||||
}));
|
||||
return Array.from(
|
||||
rows.find(".desc").map(function() {
|
||||
return this.textContent.trim();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function getTranslations(type = "") {
|
||||
|
@ -24,10 +26,11 @@ function getTranslations(type = "") {
|
|||
});
|
||||
}
|
||||
|
||||
moduleForComponent('topic-notifications-options', { integration: true });
|
||||
moduleForComponent("topic-notifications-options", { integration: true });
|
||||
|
||||
componentTest('regular topic notification level descriptions', {
|
||||
template: '{{topic-notifications-options value=topic.details.notification_level topic=topic}}',
|
||||
componentTest("regular topic notification level descriptions", {
|
||||
template:
|
||||
"{{topic-notifications-options value=topic.details.notification_level topic=topic}}",
|
||||
|
||||
test(assert) {
|
||||
selectKit().expand();
|
||||
|
@ -37,16 +40,25 @@ componentTest('regular topic notification level descriptions', {
|
|||
const uiTexts = extractDescs(selectKit().rows());
|
||||
const descriptions = getTranslations();
|
||||
|
||||
assert.equal(uiTexts.length, descriptions.length, "it has the correct copy");
|
||||
assert.equal(
|
||||
uiTexts.length,
|
||||
descriptions.length,
|
||||
"it has the correct copy"
|
||||
);
|
||||
uiTexts.forEach((text, index) => {
|
||||
assert.equal(text.trim(), descriptions[index].trim(), "it has the correct copy");
|
||||
assert.equal(
|
||||
text.trim(),
|
||||
descriptions[index].trim(),
|
||||
"it has the correct copy"
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
componentTest('PM topic notification level descriptions', {
|
||||
template: '{{topic-notifications-options value=topic.details.notification_level topic=topic}}',
|
||||
componentTest("PM topic notification level descriptions", {
|
||||
template:
|
||||
"{{topic-notifications-options value=topic.details.notification_level topic=topic}}",
|
||||
|
||||
test(assert) {
|
||||
selectKit().expand();
|
||||
|
@ -56,9 +68,17 @@ componentTest('PM topic notification level descriptions', {
|
|||
const uiTexts = extractDescs(selectKit().rows());
|
||||
const descriptions = getTranslations("_pm");
|
||||
|
||||
assert.equal(uiTexts.length, descriptions.length, "it has the correct copy");
|
||||
assert.equal(
|
||||
uiTexts.length,
|
||||
descriptions.length,
|
||||
"it has the correct copy"
|
||||
);
|
||||
uiTexts.forEach((text, index) => {
|
||||
assert.equal(text.trim(), descriptions[index].trim(), "it has the correct copy");
|
||||
assert.equal(
|
||||
text.trim(),
|
||||
descriptions[index].trim(),
|
||||
"it has the correct copy"
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue