nodebb-plugin-poll 1.1.7 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "nodebb-plugin-poll",
3
- "version": "1.1.7",
3
+ "version": "2.0.2",
4
4
  "description": "NodeBB Poll Plugin",
5
5
  "main": "library.js",
6
6
  "scripts": {
7
+ "lint": "eslint .",
7
8
  "test": "echo \"Error: no test specified\" && exit 1"
8
9
  },
9
10
  "repository": {
@@ -21,11 +22,15 @@
21
22
  "url": "https://github.com/Schamper/nodebb-plugin-poll/issues"
22
23
  },
23
24
  "dependencies": {
24
- "async": "~1.5.2",
25
- "xregexp": "~3.1.0",
26
- "cron": "~1.1.0"
25
+ "async": "^3.2.0",
26
+ "cron": "^1.8.2"
27
27
  },
28
28
  "nbbpm": {
29
- "compatibility": "^1.15.0"
29
+ "compatibility": "^1.17.0"
30
+ },
31
+ "devDependencies": {
32
+ "eslint": "^7.31.0",
33
+ "eslint-config-airbnb-base": "^14.2.1",
34
+ "eslint-plugin-import": "^2.23.4"
30
35
  }
31
36
  }
package/plugin.json CHANGED
@@ -26,7 +26,8 @@
26
26
  { "hook": "action:topic.restore", "method": "hooks.action.topicRestore" }
27
27
  ],
28
28
  "staticDirs": {
29
- "public": "public"
29
+ "public": "public",
30
+ "vendor": "vendor"
30
31
  },
31
32
  "less": [
32
33
  "public/less/style.less"
@@ -36,14 +37,19 @@
36
37
  "public/js/poll/serializer.js",
37
38
  "public/js/poll/sockets.js",
38
39
  "public/js/poll/creator.js",
39
- "public/js/poll/view.js",
40
- "public/js/vendor/moment.min.js",
41
- "public/js/vendor/flatpickr.min.js",
42
- "public/js/vendor/jquery.serialize-object.min.js"
40
+ "public/js/poll/view.js"
43
41
  ],
44
42
  "acpScripts": [
45
43
  "public/js/admin.js"
46
44
  ],
45
+ "modules": {
46
+ "dayjs.js": "vendor/dayjs.min.js",
47
+ "flatpickr.js": "vendor/flatpickr.min.js",
48
+ "flatpickr.i10n.js": "vendor/flatpickr.l10n.js"
49
+ },
50
+ "css": [
51
+ "vendor/flatpickr.min.css"
52
+ ],
47
53
  "templates": "templates",
48
54
  "languages": "languages",
49
55
  "upgrades": [
@@ -0,0 +1,82 @@
1
+ {
2
+ "globals": {
3
+ "app": true,
4
+ "io": true,
5
+ "socket": true,
6
+ "ajaxify": true,
7
+ "config": true,
8
+ "utils": true,
9
+ "overrides": true,
10
+ "componentHandler": true,
11
+ "bootbox": true,
12
+ "Visibility": true,
13
+ "Tinycon": true,
14
+ "Promise": true
15
+ },
16
+ "env": {
17
+ "jquery": true,
18
+ "amd": true,
19
+ "browser": true,
20
+ "es6": true
21
+ },
22
+ "rules": {
23
+ "comma-dangle": ["error", {
24
+ "arrays": "always-multiline",
25
+ "objects": "always-multiline",
26
+ "imports": "always-multiline",
27
+ "exports": "always-multiline",
28
+ "functions": "never"
29
+ }],
30
+ "block-scoped-var": "off",
31
+ "no-dupe-class-members": "off",
32
+ "prefer-object-spread": "off",
33
+ "prefer-reflect": "off",
34
+
35
+ // ES6
36
+ "prefer-rest-params": "off",
37
+ "prefer-spread": "off",
38
+ "prefer-arrow-callback": "off",
39
+ "prefer-template": "off",
40
+ "no-var": "off",
41
+ "object-shorthand": "off",
42
+ "vars-on-top": "off",
43
+ "prefer-destructuring": "off",
44
+ // identical to airbnb rule
45
+ // except for allowing for..in, because for..of is unavailable on some clients
46
+ "no-restricted-syntax": [
47
+ "error",
48
+ {
49
+ "selector": "ForOfStatement",
50
+ "message": "iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations."
51
+ },
52
+ {
53
+ "selector": "LabeledStatement",
54
+ "message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
55
+ },
56
+ {
57
+ "selector": "WithStatement",
58
+ "message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
59
+ }
60
+ ]
61
+ },
62
+ "parserOptions": {
63
+ "ecmaVersion": 2018,
64
+ "ecmaFeatures": {
65
+ "classes": false,
66
+ "defaultParams": false,
67
+ "blockBindings": false,
68
+ "forOf": false,
69
+ "generators": false,
70
+ "globalReturn": false,
71
+ "jsx": false,
72
+ "modules": false,
73
+ "objectLiteralComputedProperties": false,
74
+ "objectLiteralDuplicateProperties": false,
75
+ "objectLiteralShorthandMethods": false,
76
+ "objectLiteralShorthandProperties": false,
77
+ "impliedStrict": false,
78
+ "restParams": false,
79
+ "superInFunctions": false
80
+ }
81
+ }
82
+ }
@@ -1,36 +1,38 @@
1
1
  'use strict';
2
- /* globals $, app, socket, define, bootbox */
3
2
 
4
- define('admin/plugins/poll', ['settings'], function(Settings) {
3
+ define('admin/plugins/poll', ['settings'], function (Settings) {
5
4
  var wrapper;
6
5
 
7
6
  var ACP = {};
8
7
 
9
- ACP.init = function() {
8
+ ACP.init = function () {
10
9
  wrapper = $('.poll-settings');
11
10
 
12
11
  Settings.sync('poll', wrapper);
13
12
 
14
- $('#save').on('click', function() {
13
+ $('#save').on('click', function () {
15
14
  save();
16
15
  });
17
16
 
18
- $('#reset').click(function() {
17
+ $('#reset').click(function () {
19
18
  reset();
20
19
  });
21
20
  };
22
21
 
23
22
  function save() {
24
- Settings.persist('poll', wrapper, function() {
23
+ Settings.persist('poll', wrapper, function () {
25
24
  socket.emit('admin.plugins.poll.sync');
26
25
  });
27
26
  }
28
27
 
29
28
  function reset() {
30
- bootbox.confirm('Are you sure you wish to reset the settings?', function(sure) {
29
+ bootbox.confirm('Are you sure you wish to reset the settings?', function (sure) {
31
30
  if (sure) {
32
31
  socket.emit('admin.plugins.poll.getDefaults', null, function (err, data) {
33
- Settings.set('poll', data, wrapper, function(){
32
+ if (err) {
33
+ console.error(err);
34
+ }
35
+ Settings.set('poll', data, wrapper, function () {
34
36
  socket.emit('admin.plugins.poll.sync');
35
37
  });
36
38
  });
@@ -39,4 +41,4 @@ define('admin/plugins/poll', ['settings'], function(Settings) {
39
41
  }
40
42
 
41
43
  return ACP;
42
- });
44
+ });
@@ -1,8 +1,6 @@
1
- "use strict";
2
- /* globals $, app, templates, bootbox, define */
3
-
4
- (function(Poll) {
1
+ 'use strict';
5
2
 
3
+ (function (Poll) {
6
4
  var Creator = {};
7
5
 
8
6
  function init() {
@@ -10,7 +8,7 @@
10
8
 
11
9
  $(window).on('action:redactor.load', initRedactor);
12
10
 
13
- $(window).on('action:composer.loaded', function(ev, data) {
11
+ $(window).on('action:composer.loaded', function (ev, data) {
14
12
  if ($.Redactor) {
15
13
  if (data.composerData.isMain && $.Redactor.opts.plugins.indexOf('poll') === -1) {
16
14
  $.Redactor.opts.plugins.push('poll');
@@ -22,9 +20,9 @@
22
20
  }
23
21
 
24
22
  function initComposer() {
25
- require(['composer', 'composer/formatting', 'composer/controls'], function(composer, formatting, controls) {
23
+ require(['composer', 'composer/formatting', 'composer/controls'], function (composer, formatting, controls) {
26
24
  if (formatting && controls) {
27
- formatting.addButtonDispatch('poll', function(textarea) {
25
+ formatting.addButtonDispatch('poll', function (textarea) {
28
26
  composerBtnHandle(composer, textarea);
29
27
  });
30
28
  }
@@ -39,45 +37,49 @@
39
37
 
40
38
  // require translator as such because it was undefined without it
41
39
  require(['translator'], function (translator) {
42
- translator.translate('[[poll:creator_title]]', function(translated) {
40
+ translator.translate('[[poll:creator_title]]', function (translated) {
43
41
  var button = self.button.add('poll', translated);
44
42
  self.button.setIcon(button, '<i class="fa fa-bar-chart-o"></i>');
45
43
  self.button.addCallback(button, self.poll.onClick);
46
44
  });
47
- })
45
+ });
48
46
  },
49
47
  onClick: function () {
50
48
  var self = this;
51
49
  var code = this.code.get();
52
- require(['composer'], function(composer) {
50
+ require(['composer'], function (composer) {
53
51
  composerBtnHandle(composer, {
54
52
  value: code,
55
53
  redactor: function (code) {
56
54
  self.code.set(code);
57
- }
55
+ },
58
56
  });
59
57
  });
60
- }
58
+ },
61
59
  };
62
60
  };
63
61
  }
64
62
 
65
63
  function composerBtnHandle(composer, textarea) {
66
- require(['composer/controls'], function(controls) {
64
+ require(['composer/controls'], function (controls) {
67
65
  var post = composer.posts[composer.active];
68
66
  if (!post || !post.isMain || (isNaN(parseInt(post.cid, 10)) && isNaN(parseInt(post.pid, 10)))) {
69
67
  return app.alertError('[[poll:error.not_main]]');
70
68
  }
71
69
  if (parseInt(post.cid, 10) === 0) {
72
- return app.alertError("[[error:category-not-selected]]");
70
+ return app.alertError('[[error:category-not-selected]]');
73
71
  }
74
72
 
75
- Poll.sockets.canCreate({cid: post.cid, pid: post.pid}, function(err, canCreate) {
73
+ Poll.sockets.canCreate({ cid: post.cid, pid: post.pid }, function (err, canCreate) {
76
74
  if (err || !canCreate) {
77
75
  return app.alertError(err.message);
78
76
  }
79
77
 
80
- Poll.sockets.getConfig(null, function(err, config) {
78
+ Poll.sockets.getConfig(null, function (err, config) {
79
+ if (err) {
80
+ console.error(err);
81
+ }
82
+
81
83
  var poll = {};
82
84
 
83
85
  // If there's already a poll in the post, serialize it for editing
@@ -91,7 +93,7 @@
91
93
  }
92
94
  }
93
95
 
94
- Creator.show(poll, config, function(data) {
96
+ Creator.show(poll, config, function (data) {
95
97
  // Anything invalid will be discarded by the serializer
96
98
  var markup = Poll.serializer.deserialize(data, config);
97
99
 
@@ -114,85 +116,97 @@
114
116
  });
115
117
  }
116
118
 
117
- Creator.show = function(poll, config, callback) {
119
+ Creator.show = function (poll, config, callback) {
118
120
  if (poll.hasOwnProperty('info')) {
119
121
  return app.alertError('Editing not implemented.');
120
122
  }
121
123
 
122
- app.parseAndTranslate('poll/creator', { poll: poll, config: config, isRedactor: !!$.Redactor }, function(html) {
123
- // Initialise modal
124
- var modal = bootbox.dialog({
125
- title: '[[poll:creator_title]]',
126
- message: html,
127
- className: 'poll-creator',
128
- buttons: {
129
- cancel: {
130
- label: '[[modules:bootbox.cancel]]',
131
- className: 'btn-default',
132
- callback: function() {
133
- return true
134
- }
124
+ require(['flatpickr', 'flatpickr.i10n', 'bootbox', 'dayjs', 'translator'], function (flatpickr, flatpickrI10N, bootbox, dayjs, Translator) {
125
+ app.parseAndTranslate('poll/creator', { poll: poll, config: config, isRedactor: !!$.Redactor }, function (html) {
126
+ // Initialise modal
127
+ var modal = bootbox.dialog({
128
+ title: '[[poll:creator_title]]',
129
+ message: html,
130
+ className: 'poll-creator',
131
+ buttons: {
132
+ cancel: {
133
+ label: '[[modules:bootbox.cancel]]',
134
+ className: 'btn-default',
135
+ callback: function () {
136
+ return true;
137
+ },
138
+ },
139
+ save: {
140
+ label: '[[modules:bootbox.confirm]]',
141
+ className: 'btn-primary',
142
+ callback: function (e) {
143
+ clearErrors();
144
+ var form = $(e.currentTarget).parents('.bootbox').find('#pollCreator');
145
+ var obj = serializeObjectFromForm(form);
146
+
147
+ // Let's be nice and at least show an error if there are no options
148
+ obj.options.filter(function (obj) {
149
+ return obj.length;
150
+ });
151
+
152
+ if (obj.options.length === 0) {
153
+ return error('[[poll:error.no_options]]');
154
+ }
155
+
156
+ if (obj.settings.end && !dayjs(new Date(obj.settings.end)).isValid()) {
157
+ return error('[[poll:error.valid_date]]');
158
+ } else if (obj.settings.end) {
159
+ obj.settings.end = dayjs(new Date(obj.settings.end)).valueOf();
160
+ }
161
+
162
+ callback(obj);
163
+ return true;
164
+ },
165
+ },
135
166
  },
136
- save: {
137
- label: '[[modules:bootbox.confirm]]',
138
- className: 'btn-primary',
139
- callback: function(e) {
140
- clearErrors();
141
- var form = $(e.currentTarget).parents('.bootbox').find('#pollCreator');
142
- var obj = form.serializeObject();
143
-
144
- // Let's be nice and at least show an error if there are no options
145
- obj.options.filter(function(obj) {
146
- return obj.length;
147
- });
148
-
149
- if (obj.options.length == 0) {
150
- return error('[[poll:error.no_options]]');
151
- }
167
+ });
152
168
 
153
- if (obj.settings.end && !moment(new Date(obj.settings.end)).isValid()) {
154
- return error('[[poll:error.valid_date]]');
155
- } else if (obj.settings.end) {
156
- obj.settings.end = moment(new Date(obj.settings.end)).valueOf();
157
- }
169
+ // Add option adder
170
+ modal.find('#pollAddOption')
171
+ .off('click')
172
+ .on('click', function (e) {
173
+ var el = $(e.currentTarget);
174
+ var prevOption = el.prev();
158
175
 
159
- callback(obj);
160
- return true;
176
+ if (config.limits.maxOptions <= el.prevAll('input').length) {
177
+ clearErrors();
178
+ require(['translator'], function (translator) {
179
+ translator.translate('[[poll:error.max_options]]', function (text) {
180
+ error(text.replace('%d', config.limits.maxOptions));
181
+ });
182
+ });
183
+ return false;
161
184
  }
162
- }
163
- }
164
- });
165
185
 
166
- // Add option adder
167
- modal.find('#pollAddOption')
168
- .off('click')
169
- .on('click', function(e) {
170
- var el = $(e.currentTarget);
171
- var prevOption = el.prev();
172
-
173
- if (config.limits.maxOptions <= el.prevAll('input').length) {
174
- clearErrors();
175
- translator.translate('[[poll:error.max_options]]', function(text) {
176
- error(text.replace('%d', config.limits.maxOptions));
177
- });
178
- return false;
179
- }
180
-
181
- if (prevOption.val().length != 0) {
182
- prevOption.clone().val('').insertBefore(el).focus();
183
- }
184
- });
186
+ if (prevOption.val().length !== 0) {
187
+ prevOption.clone().val('').insertBefore(el).focus();
188
+ }
189
+ });
185
190
 
186
- flatpickr("#pollInputEnd", {
191
+ var currentLocale = Translator.getLanguage();
192
+ var flatpickrInstance = flatpickr('.flatpickr', {
187
193
  enableTime: true,
188
- altFormat: "F j, Y h:i K",
194
+ altFormat: 'F j, Y h:i K',
189
195
  time_24hr: false,
190
- wrap: true
196
+ wrap: true,
197
+ locale: getFlatpickrLocale(currentLocale, flatpickrI10N.default),
198
+ onOpen: function () {
199
+ modal.removeAttr('tabindex');
200
+ },
201
+ onClose: function () {
202
+ modal.attr('tabindex', -1);
203
+ },
191
204
  });
192
205
 
193
206
  if (poll.settings && poll.settings.end) {
194
- flatpickr.setDate(poll.settings.end)
207
+ flatpickrInstance.setDate(poll.settings.end);
195
208
  }
209
+ });
196
210
  });
197
211
  };
198
212
 
@@ -209,31 +223,29 @@
209
223
  $('#pollErrorBox').addClass('hidden').html('');
210
224
  }
211
225
 
212
- function dumbifyObject(obj) {
213
- var result = {};
226
+ function serializeObjectFromForm(form) {
227
+ var obj = form.serializeObject();
228
+ var result = {
229
+ options: obj.options,
230
+ settings: {
231
+ title: obj['settings.title'],
232
+ maxvotes: obj['settings.maxvotes'],
233
+ disallowVoteUpdate: obj['settings.disallowVoteUpdate'] === 'on' ? 'true' : 'false',
234
+ end: obj['settings.end'],
235
+ },
236
+ };
214
237
 
215
- for (var key in obj) {
216
- if (obj.hasOwnProperty(key)) {
217
- var val = obj[key];
238
+ return result;
239
+ }
218
240
 
219
- if (jQuery.isPlainObject(val)) {
220
- var obj1 = dumbifyObject(val);
221
- for (var k1 in obj1) {
222
- if (obj1.hasOwnProperty(k1)) {
223
- result[key + '.' + k1] = obj1[k1];
224
- }
225
- }
226
- } else {
227
- result[key] = val;
228
- }
229
- }
241
+ function getFlatpickrLocale(nodebbLocale, flatpickrLocales = {}) {
242
+ if (Object.keys(flatpickrLocales).includes(nodebbLocale.toLowerCase())) {
243
+ return flatpickrLocales[nodebbLocale];
230
244
  }
231
-
232
- return result;
245
+ return flatpickrLocales.default;
233
246
  }
234
247
 
235
248
  Poll.creator = Creator;
236
249
 
237
250
  init();
238
-
239
- })(window.Poll);
251
+ }(window.Poll));
@@ -1,16 +1,15 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
3
  var Poll = {};
4
4
 
5
- (function() {
6
-
7
- $(window).on('action:topic.loading', function() {
5
+ (function () {
6
+ $(window).on('action:topic.loading', function () {
8
7
  if (ajaxify.data.posts.length > 0 && ajaxify.data.posts[0].hasOwnProperty('pollId')) {
9
8
  getPoll(ajaxify.data.posts[0].pollId);
10
9
  }
11
10
  });
12
11
 
13
- $(window).on('action:posts.edited', function(ev, data) {
12
+ $(window).on('action:posts.edited', function (ev, data) {
14
13
  if (data.post.hasOwnProperty('pollId')) {
15
14
  getPoll(data.post.pollId);
16
15
  }
@@ -20,7 +19,7 @@ var Poll = {};
20
19
  pollId = parseInt(pollId, 10);
21
20
 
22
21
  if (!isNaN(pollId)) {
23
- Poll.sockets.getPoll({pollId: pollId}, function(err, pollData) {
22
+ Poll.sockets.getPoll({ pollId: pollId }, function (err, pollData) {
24
23
  if (err) {
25
24
  return app.alertError(err.message);
26
25
  }
@@ -28,5 +27,4 @@ var Poll = {};
28
27
  });
29
28
  }
30
29
  }
31
-
32
- })();
30
+ }());