thredded 0.12.4 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -33
  3. data/app/assets/javascripts/thredded/components/currently_online.es6 +28 -21
  4. data/app/assets/javascripts/thredded/components/flash_messages.es6 +5 -7
  5. data/app/assets/javascripts/thredded/components/mention_autocompletion.es6 +39 -0
  6. data/app/assets/javascripts/thredded/components/post_form.es6 +28 -33
  7. data/app/assets/javascripts/thredded/components/preview_area.es6 +27 -23
  8. data/app/assets/javascripts/thredded/components/quote_post.es6 +5 -1
  9. data/app/assets/javascripts/thredded/components/time_stamps.es6 +24 -9
  10. data/app/assets/javascripts/thredded/components/topic_form.es6 +72 -54
  11. data/app/assets/javascripts/thredded/components/topics.es6 +28 -19
  12. data/app/assets/javascripts/thredded/components/turboforms.es6 +23 -13
  13. data/app/assets/javascripts/thredded/components/user_preferences_form.es6 +33 -31
  14. data/app/assets/javascripts/thredded/components/user_textcomplete.es6 +47 -0
  15. data/app/assets/javascripts/thredded/components/users_select.es6 +102 -52
  16. data/app/assets/javascripts/thredded/core/debounce.es6 +1 -1
  17. data/app/assets/javascripts/thredded/core/escape_html.es6 +7 -0
  18. data/app/assets/javascripts/thredded/core/hide_soft_keyboard.es6 +1 -1
  19. data/app/assets/javascripts/thredded/core/on_page_load.es6 +1 -1
  20. data/app/assets/javascripts/thredded/core/serialize_form.es6 +9 -0
  21. data/app/assets/javascripts/thredded/dependencies.js +2 -5
  22. data/app/assets/javascripts/thredded/dependencies/textcomplete.js +1 -0
  23. data/app/assets/javascripts/thredded/dependencies/timeago.js +1 -0
  24. data/app/assets/javascripts/thredded/dependencies/ujs.js +1 -1
  25. data/app/assets/stylesheets/thredded/_dependencies.scss +0 -1
  26. data/app/assets/stylesheets/thredded/_thredded.scss +0 -1
  27. data/app/assets/stylesheets/thredded/components/_mention-autocomplete.scss +15 -2
  28. data/app/controllers/concerns/thredded/new_private_topic_params.rb +2 -2
  29. data/app/controllers/thredded/autocomplete_users_controller.rb +0 -1
  30. data/app/forms/thredded/private_topic_form.rb +46 -2
  31. data/app/helpers/thredded/application_helper.rb +12 -14
  32. data/app/views/thredded/private_topics/_form.html.erb +7 -6
  33. data/app/views/thredded/topics/_topic.html.erb +2 -2
  34. data/config/locales/pl.yml +1 -1
  35. data/lib/thredded.rb +1 -3
  36. data/lib/thredded/version.rb +1 -1
  37. data/vendor/assets/javascripts/textcomplete.min.js +1 -0
  38. metadata +12 -37
  39. data/app/assets/javascripts/thredded/core/mention_autocompletion.es6 +0 -54
  40. data/app/assets/javascripts/thredded/dependencies/jquery.js +0 -1
  41. data/app/assets/stylesheets/thredded/components/_select2.scss +0 -112
  42. data/vendor/assets/javascripts/jquery.textcomplete.js +0 -1488
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 86b9c121f8dce31cb5c0bf7faa2f9e5b9e0b23bf
4
- data.tar.gz: e1d798dfa2ca61bca551b052cbc23b4e91ba66c4
3
+ metadata.gz: 633ce6690d9ffc55e74596d44c9c6547778700e6
4
+ data.tar.gz: ab7417716f223e806bf189263da331ebe9b1635f
5
5
  SHA512:
6
- metadata.gz: 63e6821ccfc2a24cc7e2c3fd5ec5a1f080523b2eaaa4c64c8e4898101742f51c69e5d9946b419824f22dfe4a3e7593732b15036ff3257c38c1df688d900e4ee9
7
- data.tar.gz: 10dc01b049b0e019b807e76a30b597ca2bec14746eb5ac8e3c005baa2f25d975eecec5cae7d667e942a2bda0d6b7f35f27cb8cdf4f3a1e1f7cd655cf557c7fc8
6
+ metadata.gz: a513cd6fd50b5b1e3dc4f3af5a3d81719b942df9ef66f27f32c9e874ea1784ec39bd41752c94694bdf80b59fa24b1d203dcb31322d7294f43d1aefd79781197a
7
+ data.tar.gz: 6ff341b70414e6fdbcb79faf28c97290dae8cedb4be683171ef33fad0453d7b3ac012447d535f460d12cb52f9f3b01a6dfcc09a02f2b875237d56baab00479e1
data/README.md CHANGED
@@ -47,7 +47,6 @@ Table of Contents
47
47
  * [Reference your paths so that Thredded can find them](#reference-your-paths-so-that-thredded-can-find-them)
48
48
  * [Add Thredded styles](#add-thredded-styles)
49
49
  * [Add Thredded JavaScripts](#add-thredded-javascripts)
50
- * [jQuery version](#jquery-version)
51
50
  * [User profile page](#user-profile-page)
52
51
  * [Customizing views](#customizing-views)
53
52
  * [View hooks](#view-hooks)
@@ -99,7 +98,7 @@ Then, see the rest of this Readme for more information about using and customizi
99
98
  Add the gem to your Gemfile:
100
99
 
101
100
  ```ruby
102
- gem 'thredded', '~> 0.12.4'
101
+ gem 'thredded', '~> 0.13.0'
103
102
  ```
104
103
 
105
104
  Add the Thredded [initializer] to your parent app by running the install generator.
@@ -230,27 +229,38 @@ Include thredded JavaScripts in your `application.js`:
230
229
 
231
230
  Thredded is fully compatible with deferred and async script loading.
232
231
 
233
- ##### jQuery version
232
+ ##### Rails UJS version
234
233
 
235
- To use thredded with your application layout, you also need to ensure that your layout and thredded don't load different
236
- versions of jQuery. By default, Thredded loads jQuery v3 (via `//= require jquery3` from [jquery-rails]),
237
- but if you want to use jQuery v1 or v2, then you need to create a file at
238
- `app/assets/javascripts/thredded/dependencies/jquery.js` which contains just `//= require jquery`
239
- or `// require jquery2`. If you are not loading jQuery in your app, or if you are already using jQuery v3, then you don't need to do do this (You also don't need to worry about this if you are using the default ["Standalone" layout](#standalone-layout))
234
+ By default, thredded loads `rails-ujs`. If you're using Rails before v5.1, you need to add `rails-ujs` to
235
+ your Gemfile.
240
236
 
241
- [jquery-rails]: https://github.com/rails/jquery-rails
237
+ If you'd like it to use `jquery_ujs` instead, run this command from your app directory:
242
238
 
243
- #### Rails UJS version
239
+ ```bash
240
+ mkdir -p app/assets/javascripts/thredded/dependencies/
241
+ printf '//= require jquery3\n//= require jquery_ujs\n' > app/assets/javascripts/thredded/dependencies/ujs.js
242
+ ```
244
243
 
245
- By default, thredded requires `jquery_ujs`. If you'd like to change that to `rails-ujs` (default since Rails v5.1),
246
- run this command from your app directory:
244
+ ##### Timeago version
245
+
246
+ By default, thredded loads `timeago.js`.
247
+
248
+ If you'd like to use `jquery.timeago` or `rails-timeago` instead, run this command from your app directory:
247
249
 
248
250
  ```bash
249
251
  mkdir -p app/assets/javascripts/thredded/dependencies/
250
- echo '//= require rails-ujs' > app/assets/javascripts/thredded/dependencies/ujs.js
252
+ echo '//= require jquery.timeago' > app/assets/javascripts/thredded/dependencies/timeago.js
251
253
  ```
252
254
 
253
- The default UJS framework will change from `jquery_ujs` to `rails-ujs` in the upcoming Thredded v0.13.0 release.
255
+ You will also need to adjust the `//= require` statements for timeago locales if your site is translated into multiple
256
+ languages. For `jquery.timeago`, these need to be require after `thredded/dependencies` but before `thredded/thredded`.
257
+ E.g. for Brazilian Portuguese with jquery.timeago:
258
+
259
+ ```js
260
+ //= require thredded/dependencies
261
+ //= require locales/jquery.timeago.pt-br
262
+ //= require thredded/thredded
263
+ ```
254
264
 
255
265
  #### Thredded page title and ID
256
266
 
@@ -334,21 +344,11 @@ $thredded-brand: #9c27b0;
334
344
  @import "thredded";
335
345
  ```
336
346
 
337
- The `@import "thredded"` directive above will import thredded styles and the [dependencies][thredded-scss-dependencies]
338
- (currently just "select2" from [select2-rails]). If you already include your own styles for any of thredded
339
- dependencies, you can import just the thredded styles alone like this:
340
-
341
- ```scss
342
- // application.scss
343
- @import "thredded/thredded";
344
- ```
345
-
346
347
  If you are writing a Thredded plugin, import the [`thredded/base`][thredded-scss-base] Sass package instead.
347
348
  The `base` package only defines variables, mixins, and %-placeholders, so it can be imported safely without producing
348
349
  any duplicate CSS.
349
350
 
350
351
  [thredded-scss-dependencies]: https://github.com/thredded/thredded/blob/master/app/assets/stylesheets/thredded/_dependencies.scss
351
- [select2-rails]: https://github.com/argerim/select2-rails
352
352
  [thredded-scss-base]: https://github.com/thredded/thredded/blob/master/app/assets/stylesheets/thredded/_base.scss
353
353
 
354
354
  ### Email and other notifications
@@ -384,13 +384,10 @@ Here are the steps to ensure the best support for your language if it isn't Engl
384
384
 
385
385
  1. Add `rails-i18n` and `kaminari-i18n` to your Gemfile.
386
386
 
387
- 2. Require the translations for rails-timeago in your JavaScript before `thredded` but after `jquery.timeago`
388
- (included in `thredded/dependencies`). E.g. for Brazilian Portuguese:
387
+ 2. Require the translations for timeago.js in your JavaScript. E.g. for Brazilian Portuguese:
389
388
 
390
389
  ```js
391
- //= require thredded/dependencies
392
- //= require locales/jquery.timeago.pt-br
393
- //= require thredded/thredded
390
+ //= require timeago/locales/pt_BR
394
391
  ```
395
392
 
396
393
  3. To generate URL slugs for messageboards, categories, and topics with support for more language than English,
@@ -640,8 +637,8 @@ To achieve the above, all the Thredded code must register onload via
640
637
 
641
638
  ```js
642
639
  window.Thredded.onPageLoad(() => {
643
- // Initialize widgets:
644
- $('[data-thredded-select2]').select2();
640
+ // Initialize widgets
641
+ autosize('textarea');
645
642
  });
646
643
  ```
647
644
 
@@ -657,8 +654,8 @@ e.g.:
657
654
 
658
655
  ```js
659
656
  document.addEventListener('turbolinks:before-cache', () => {
660
- // Destroy widgets:
661
- $('[data-thredded-select2]').select2('destroy');
657
+ // Destroy widgets
658
+ autosize.destroy('textarea');
662
659
  });
663
660
  ```
664
661
 
@@ -1,25 +1,32 @@
1
- (($) => {
1
+ //= require thredded/core/on_page_load
2
+
3
+ (() => {
4
+ const Thredded = window.Thredded;
5
+
2
6
  const COMPONENT_SELECTOR = '[data-thredded-currently-online]';
7
+ const EXPANDED_CLASS = 'thredded--is-expanded';
8
+
9
+ const handleMouseEnter = (evt) => {
10
+ evt.target.classList.add(EXPANDED_CLASS);
11
+ };
12
+
13
+ const handleMouseLeave = (evt) => {
14
+ evt.target.classList.remove(EXPANDED_CLASS);
15
+ };
16
+
17
+ const handleTouchStart = (evt) => {
18
+ evt.target.classList.toggle(EXPANDED_CLASS);
19
+ };
3
20
 
4
- class ThreddedCurrentlyOnline {
5
- init($nodes) {
6
- $($nodes).
7
- on('mouseenter', function(e) {
8
- $(this).addClass('thredded--is-expanded');
9
- }).
10
- on('mouseleave', function(e) {
11
- $(this).removeClass('thredded--is-expanded');
12
- }).
13
- on('touchstart', function(e) {
14
- $(this).toggleClass('thredded--is-expanded');
15
- });
16
- }
17
- }
21
+ const initCurrentlyOnline = (node) => {
22
+ node.addEventListener('mouseenter', handleMouseEnter);
23
+ node.addEventListener('mouseleave', handleMouseLeave);
24
+ node.addEventListener('touchstart', handleTouchStart);
25
+ };
18
26
 
19
- window.Thredded.onPageLoad(() => {
20
- const $nodes = $(COMPONENT_SELECTOR);
21
- if ($nodes.length) {
22
- new ThreddedCurrentlyOnline().init($nodes);
23
- }
27
+ Thredded.onPageLoad(() => {
28
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
29
+ initCurrentlyOnline(node);
30
+ });
24
31
  });
25
- })(jQuery);
32
+ })();
@@ -1,11 +1,9 @@
1
- (($) => {
1
+ (() => {
2
2
  const COMPONENT_SELECTOR = '[data-thredded-flash-message]';
3
3
 
4
- const destroy = () => {
5
- $(COMPONENT_SELECTOR).remove();
6
- };
7
-
8
4
  document.addEventListener('turbolinks:before-cache', () => {
9
- destroy()
5
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
6
+ node.parentNode.removeChild(node);
7
+ });
10
8
  });
11
- })(jQuery);
9
+ })();
@@ -0,0 +1,39 @@
1
+ //= require thredded/components/user_textcomplete
2
+
3
+ const ThreddedMentionAutocompletion = {
4
+ MATCH_RE: /(^@|\s@)"?([\w.,\- ()]+)$/,
5
+ DROPDOWN_MAX_COUNT: 6,
6
+
7
+ init(form, textarea) {
8
+ const editor = new Textcomplete.editors.Textarea(textarea);
9
+ const textcomplete = new Textcomplete(editor, {
10
+ dropdown: {
11
+ className: Thredded.UserTextcomplete.DROPDOWN_CLASS_NAME,
12
+ maxCount: ThreddedMentionAutocompletion.DROPDOWN_MAX_COUNT
13
+ },
14
+ });
15
+ textcomplete.on('rendered', function() {
16
+ if (textcomplete.dropdown.items.length) {
17
+ textcomplete.dropdown.items[0].activate();
18
+ }
19
+ });
20
+ textcomplete.register([{
21
+ match: ThreddedMentionAutocompletion.MATCH_RE,
22
+ search: Thredded.UserTextcomplete.searchFn({
23
+ url: form.getAttribute('data-autocomplete-url'),
24
+ autocompleteMinLength: parseInt(form.getAttribute('data-autocomplete-min-length'), 10)
25
+ }),
26
+ template: Thredded.UserTextcomplete.formatUser,
27
+ replace ({name, match}) {
28
+ let prefix = match[1];
29
+ if (/[., ()]/.test(name)) {
30
+ return `${prefix}"${name}" `
31
+ } else {
32
+ return `${prefix}${name} `
33
+ }
34
+ }
35
+ }]);
36
+ }
37
+ };
38
+
39
+ window.ThreddedMentionAutocompletion = ThreddedMentionAutocompletion;
@@ -1,42 +1,37 @@
1
- //= require ./preview_area
1
+ //= require autosize
2
+ //= require thredded/core/on_page_load
3
+ //= require thredded/components/mention_autocompletion
4
+ //= require thredded/components/preview_area
2
5
 
3
- (($, autosize) => {
4
- const COMPONENT_SELECTOR = '[data-thredded-post-form]';
5
-
6
- class ThreddedPostForm {
7
- constructor() {
8
- this.textareaSelector = 'textarea';
9
- }
6
+ (() => {
7
+ const Thredded = window.Thredded;
8
+ const ThreddedMentionAutocompletion = window.ThreddedMentionAutocompletion;
9
+ const ThreddedPreviewArea = window.ThreddedPreviewArea;
10
+ const autosize = window.autosize;
10
11
 
11
- init($nodes) {
12
- let $textarea = $nodes.find(this.textareaSelector);
13
- this.autosize($textarea);
14
- $nodes.each(function() {
15
- new ThreddedPreviewArea($(this));
16
- });
17
- new ThreddedMentionAutocompletion($).init($nodes);
18
- }
12
+ const COMPONENT_SELECTOR = '[data-thredded-post-form]';
13
+ const CONTENT_TEXTAREA_SELECTOR = 'textarea[name$="[content]"]';
19
14
 
20
- autosize($textarea) {
21
- autosize($textarea)
22
- }
15
+ const initPostForm = (form) => {
16
+ const textarea = form.querySelector(CONTENT_TEXTAREA_SELECTOR);
17
+ autosize(textarea);
18
+ new ThreddedPreviewArea(form, textarea);
19
+ ThreddedMentionAutocompletion.init(form, textarea);
20
+ };
23
21
 
24
- destroy($nodes) {
25
- autosize.destroy($nodes.find(this.textareaSelector));
26
- }
27
- }
22
+ const destroyPostForm = (form) => {
23
+ autosize.destroy(form.querySelector(CONTENT_TEXTAREA_SELECTOR));
24
+ };
28
25
 
29
- window.Thredded.onPageLoad(() => {
30
- const $nodes = $(COMPONENT_SELECTOR);
31
- if ($nodes.length) {
32
- new ThreddedPostForm().init($nodes);
33
- }
26
+ Thredded.onPageLoad(() => {
27
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
28
+ initPostForm(node);
29
+ });
34
30
  });
35
31
 
36
32
  document.addEventListener('turbolinks:before-cache', () => {
37
- const $nodes = $(COMPONENT_SELECTOR);
38
- if ($nodes.length) {
39
- new ThreddedPostForm().destroy($nodes);
40
- }
33
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
34
+ destroyPostForm(node);
35
+ });
41
36
  });
42
- })(jQuery, window.autosize);
37
+ })();
@@ -1,28 +1,28 @@
1
- //= require ./preview_area
1
+ //= require thredded/core/serialize_form
2
2
 
3
- (function($) {
3
+ (() => {
4
4
  const PREVIEW_AREA_SELECTOR = '[data-thredded-preview-area]';
5
5
  const PREVIEW_AREA_POST_SELECTOR = '[data-thredded-preview-area-post]';
6
6
 
7
7
  class ThreddedPreviewArea {
8
8
 
9
- constructor($form) {
10
- const $preview = $form.find(PREVIEW_AREA_SELECTOR);
11
- if (!$preview.length) return;
12
- this.$form = $form;
13
- const $textarea = $form.find('textarea');
14
- this.textarea = $textarea.get(0);
15
- this.preview = $preview.get(0);
16
- this.previewPost = $form.find(PREVIEW_AREA_POST_SELECTOR).get(0);
9
+ constructor(form, textarea) {
10
+ const preview = form.querySelector(PREVIEW_AREA_SELECTOR);
11
+ if (!preview || !textarea) return;
12
+ this.form = form;
13
+ this.preview = preview;
14
+ this.previewPost = form.querySelector(PREVIEW_AREA_POST_SELECTOR);
17
15
  this.previewUrl = this.preview.getAttribute('data-thredded-preview-url');
18
16
 
17
+ let prevValue = null;
19
18
  const onChange = Thredded.debounce(() => {
20
- this.updatePreview()
19
+ if (prevValue !== textarea.value) {
20
+ this.updatePreview();
21
+ prevValue = textarea.value;
22
+ }
21
23
  }, 200, false);
22
24
 
23
- this.textarea.addEventListener('input', onChange, false);
24
- // Listen to the jQuery change event as that's what is triggered by plugins such as jQuery.textcomplete.
25
- $textarea.on('change', onChange);
25
+ textarea.addEventListener('input', onChange, false);
26
26
 
27
27
  this.requestId = 0;
28
28
  }
@@ -30,16 +30,20 @@
30
30
  updatePreview() {
31
31
  this.requestId++;
32
32
  const requestId = this.requestId;
33
- $.ajax({
34
- type: this.$form.attr('method'),
35
- url: this.previewUrl,
36
- data: this.$form.serialize(),
37
- }).done((data) => {
38
- if (requestId == this.requestId) {
33
+ const request = new XMLHttpRequest();
34
+ request.open(this.form.method, this.previewUrl, /* async */ true);
35
+ request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
36
+ request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
37
+ request.onload = () => {
38
+ if (
39
+ // Ignore server errors
40
+ request.status >= 200 && request.status < 400 &&
39
41
  // Ignore older responses received out-of-order
40
- this.onPreviewResponse(data);
42
+ requestId === this.requestId) {
43
+ this.onPreviewResponse(request.responseText);
41
44
  }
42
- });
45
+ };
46
+ request.send(Thredded.serializeForm(this.form));
43
47
  }
44
48
 
45
49
  onPreviewResponse(data) {
@@ -49,4 +53,4 @@
49
53
  }
50
54
 
51
55
  window.ThreddedPreviewArea = ThreddedPreviewArea;
52
- })(jQuery);
56
+ })();
@@ -1,5 +1,9 @@
1
+ //= require thredded/core/on_page_load
2
+
1
3
  (function() {
2
- window.Thredded.onPageLoad(() => {
4
+ const Thredded = window.Thredded;
5
+
6
+ Thredded.onPageLoad(() => {
3
7
  Array.prototype.forEach.call(document.querySelectorAll('[data-thredded-quote-post]'), (el) => {
4
8
  el.addEventListener('click', onClick);
5
9
  });
@@ -1,10 +1,25 @@
1
- (($) => {
1
+ (() => {
2
2
  const COMPONENT_SELECTOR = '#thredded--container [data-time-ago]';
3
-
4
- window.Thredded.onPageLoad(() => {
5
- const allowFutureWas = jQuery.timeago.settings.allowFuture;
6
- $.timeago.settings.allowFuture = true;
7
- $(COMPONENT_SELECTOR).timeago();
8
- $.timeago.settings.allowFuture = allowFutureWas;
9
- });
10
- })(jQuery);
3
+ const Thredded = window.Thredded;
4
+ if ('timeago' in window) {
5
+ const timeago = window.timeago;
6
+ Thredded.onPageLoad(() => {
7
+ const threddedContainer = document.querySelector('#thredded--container');
8
+ if (!threddedContainer) return;
9
+ timeago().render(
10
+ document.querySelectorAll(COMPONENT_SELECTOR),
11
+ threddedContainer.getAttribute('data-thredded-locale'));
12
+ });
13
+ document.addEventListener('turbolinks:before-cache', () => {
14
+ timeago.cancel();
15
+ });
16
+ } else if ('jQuery' in window && 'timeago' in jQuery.fn) {
17
+ const $ = window.jQuery;
18
+ Thredded.onPageLoad(() => {
19
+ const allowFutureWas = $.timeago.settings.allowFuture;
20
+ $.timeago.settings.allowFuture = true;
21
+ $(COMPONENT_SELECTOR).timeago();
22
+ $.timeago.settings.allowFuture = allowFutureWas;
23
+ });
24
+ }
25
+ })();
@@ -1,69 +1,87 @@
1
- (($, autosize) => {
1
+ //= require autosize
2
+ //= require thredded/core/on_page_load
3
+ //= require thredded/components/mention_autocompletion
4
+ //= require thredded/components/preview_area
5
+
6
+ (() => {
7
+ const Thredded = window.Thredded;
8
+ const ThreddedMentionAutocompletion = window.ThreddedMentionAutocompletion;
9
+ const ThreddedPreviewArea = window.ThreddedPreviewArea;
10
+ const autosize = window.autosize;
11
+
2
12
  const COMPONENT_SELECTOR = '[data-thredded-topic-form]';
3
- class ThreddedTopicForm {
4
- constructor() {
5
- this.titleSelector = '[name$="topic[title]"]';
6
- this.textareaSelector = 'textarea';
7
- this.compactSelector = 'form.thredded--is-compact';
8
- this.expandedSelector = 'form.thredded--is-expanded';
9
- this.escapeElements = 'input, textarea';
10
- this.escapeKeyCode = 27;
11
- }
13
+ const TITLE_SELECTOR = '[name$="topic[title]"]';
14
+ const CONTENT_TEXTAREA_SELECTOR = 'textarea[name$="[content]"]';
15
+ const COMPACT_CLASS = 'thredded--is-compact';
16
+ const EXPANDED_CLASS = 'thredded--is-expanded';
17
+ const ESCAPE_KEY_CODE = 27;
12
18
 
13
- toggleExpanded(child, expanded) {
14
- jQuery(child).closest(expanded ? this.compactSelector : this.expandedSelector).toggleClass('thredded--is-compact thredded--is-expanded');
19
+ const initTopicForm = (form) => {
20
+ const textarea = form.querySelector(CONTENT_TEXTAREA_SELECTOR);
21
+ autosize(textarea);
22
+ new ThreddedPreviewArea(form, textarea);
23
+ ThreddedMentionAutocompletion.init(form, textarea);
24
+
25
+ if (!form.classList.contains(COMPACT_CLASS)) {
26
+ return;
15
27
  }
16
28
 
17
- init($nodes) {
18
- autosize($nodes.find(this.textareaSelector));
19
- $nodes.each(function() {
20
- new ThreddedPreviewArea($(this));
29
+ const title = form.querySelector(TITLE_SELECTOR);
30
+ title.addEventListener('focus', () => {
31
+ toggleExpanded(form, true);
32
+ });
33
+
34
+ [title, textarea].forEach((node) => {
35
+ // Un-expand on Escape key.
36
+ node.addEventListener('keydown', (evt) => {
37
+ if (evt.keyCode === ESCAPE_KEY_CODE) {
38
+ evt.target.blur();
39
+ toggleExpanded(form, false);
40
+ }
21
41
  });
22
- new ThreddedMentionAutocompletion($).init($nodes);
23
- $nodes.filter(this.compactSelector).
24
- on('focus', this.titleSelector, e => {
25
- this.toggleExpanded(e.target, true);
26
- }).
27
- on('keydown', this.escapeElements, e => {
28
- if (e.keyCode == this.escapeKeyCode) {
29
- this.toggleExpanded(e.target, false);
30
- e.target.blur();
42
+
43
+ // Un-expand on blur if the new focus element is outside of the same form and
44
+ // all the form inputs are empty.
45
+ node.addEventListener('blur', () => {
46
+ // This listener will be fired right after the blur event has finished.
47
+ const listener = (evt) => {
48
+ if (!form.contains(evt.target) && !title.value && !textarea.value) {
49
+ toggleExpanded(form, false);
31
50
  }
32
- }).
33
- on('blur', this.escapeElements, e => {
34
- var blurredEl = e.target;
35
- $(document.body).one('mouseup touchend', e => {
36
- var $blurredElForm = $(blurredEl).closest('form');
37
- // Un-expand if the new focus element is outside of the same form and
38
- // all the input elements are empty.
39
- if (!$(e.target).closest('form').is($blurredElForm) &&
40
- $blurredElForm.find(this.escapeElements).is(function() {
41
- return !this.value;
42
- })) {
43
- this.toggleExpanded(blurredEl, false);
44
- }
45
- })
46
- });
47
- }
51
+ document.body.removeEventListener('touchend', listener);
52
+ document.body.removeEventListener('mouseup', listener);
53
+ };
54
+ document.body.addEventListener('mouseup', listener);
55
+ document.body.addEventListener('touchend', listener);
56
+ })
57
+ });
58
+ };
48
59
 
49
- destroy($nodes) {
50
- autosize.destroy($nodes.find(this.textareaSelector));
60
+ const toggleExpanded = (form, expand) => {
61
+ if (expand) {
62
+ form.classList.remove(COMPACT_CLASS);
63
+ form.classList.add(EXPANDED_CLASS);
64
+ } else {
65
+ form.classList.remove(EXPANDED_CLASS);
66
+ form.classList.add(COMPACT_CLASS);
51
67
  }
52
- }
68
+ };
53
69
 
54
- window.Thredded.onPageLoad(() => {
55
- const $nodes = $(COMPONENT_SELECTOR);
56
- if ($nodes.length) {
57
- new ThreddedTopicForm().init($nodes);
58
- }
70
+ const destroyTopicForm = (form) => {
71
+ autosize.destroy(form.querySelector(CONTENT_TEXTAREA_SELECTOR));
72
+ };
73
+
74
+ Thredded.onPageLoad(() => {
75
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
76
+ initTopicForm(node);
77
+ });
59
78
  });
60
79
 
61
80
  document.addEventListener('turbolinks:before-cache', () => {
62
- const $nodes = $(COMPONENT_SELECTOR);
63
- if ($nodes.length) {
64
- new ThreddedTopicForm().destroy($nodes);
65
- }
81
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
82
+ destroyTopicForm(node);
83
+ });
66
84
  });
67
- })(jQuery, window.autosize);
85
+ })();
68
86
 
69
87