missing prettified files

This commit is contained in:
Joffrey JAFFEUX
2018-06-15 18:42:20 +02:00
committed by GitHub
parent 2268e29c26
commit 60ff0e9b8c
24 changed files with 3009 additions and 689 deletions

View File

@@ -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")
});
},

View File

@@ -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);
}
};

View File

@@ -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);
}
};

View File

@@ -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) {

View File

@@ -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));
};
}

View File

@@ -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;
});
}
});