thredded 0.16.13 → 0.16.14
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.
- checksums.yaml +4 -4
- data/README.md +57 -9
- data/app/assets/config/thredded_manifest.js +4 -0
- data/app/assets/javascripts/thredded/components/currently_online.es6 +1 -0
- data/app/assets/javascripts/thredded/components/post_form.es6 +1 -0
- data/app/assets/javascripts/thredded/components/preview_area.es6 +2 -0
- data/app/assets/javascripts/thredded/components/quote_post.es6 +1 -0
- data/app/assets/javascripts/thredded/components/spoilers.es6 +1 -0
- data/app/assets/javascripts/thredded/components/submit_hotkey.es6 +1 -0
- data/app/assets/javascripts/thredded/components/time_stamps.es6 +1 -0
- data/app/assets/javascripts/thredded/components/topic_form.es6 +1 -0
- data/app/assets/javascripts/thredded/components/topics.es6 +1 -0
- data/app/assets/javascripts/thredded/components/turboforms.es6 +8 -7
- data/app/assets/javascripts/thredded/components/user_preferences_form.es6 +1 -0
- data/app/assets/javascripts/thredded/components/user_textcomplete.es6 +1 -0
- data/app/assets/javascripts/thredded/components/users_select.es6 +1 -0
- data/app/assets/javascripts/thredded/dependencies/autosize.js +1 -1
- data/app/controllers/thredded/messageboard_groups_controller.rb +8 -0
- data/app/models/thredded/messageboard.rb +1 -0
- data/app/view_hooks/thredded/all_view_hooks.rb +21 -0
- data/app/view_models/thredded/messageboard_group_view.rb +1 -1
- data/app/views/thredded/messageboard_groups/show.html.erb +41 -0
- data/app/views/thredded/messageboards/index.html.erb +6 -1
- data/config/locales/de.yml +2 -0
- data/config/locales/en.yml +2 -0
- data/config/locales/es.yml +2 -0
- data/config/locales/fr.yml +2 -0
- data/config/locales/it.yml +2 -0
- data/config/locales/pl.yml +2 -0
- data/config/locales/pt-BR.yml +2 -0
- data/config/locales/ru.yml +2 -0
- data/config/locales/zh-CN.yml +2 -0
- data/config/routes.rb +1 -0
- data/lib/generators/thredded/install/templates/initializer.rb +3 -0
- data/lib/thredded.rb +6 -0
- data/lib/thredded/engine.rb +1 -3
- data/lib/thredded/version.rb +1 -1
- data/lib/thredded/webpack_assets.rb +110 -0
- data/vendor/assets/javascripts/autosize.js +280 -0
- metadata +9 -6
- data/vendor/assets/javascripts/autosize.min.js +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b96a82c0482d773cd074fdd77a4e67a408ba43e2e079bbbd2a4eefeb55085f37
|
4
|
+
data.tar.gz: 6949b12a03a39430340dbd8870cb6e5cc4dcddc583e093de576e2a3621becdb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4403418748bbb9b7bdcf5a503908fb526d60ff0f6100d11be98d64f625da8e9479c2be127adb2b88435686371d58cb75c39d1e4c73789e5e8297eabf9d01198d
|
7
|
+
data.tar.gz: 154ddbcd92f7e8f705bf4fe27bbc32f03fff0e3af7fdb81aca82143783dfd389eed1cbd516802356288af0c839cd3ddc61087209037c60bc521bee072939efa6
|
data/README.md
CHANGED
@@ -43,7 +43,8 @@ Table of Contents
|
|
43
43
|
* [Application layout](#application-layout)
|
44
44
|
* [Reference your paths so that Thredded can find them](#reference-your-paths-so-that-thredded-can-find-them)
|
45
45
|
* [Add Thredded styles](#add-thredded-styles)
|
46
|
-
* [Add Thredded JavaScripts](#add-thredded-javascripts)
|
46
|
+
* [Add Thredded JavaScripts (Sprockets)](#add-thredded-javascripts-sprockets)
|
47
|
+
* [Add Thredded JavaScripts (Webpack)](#add-thredded-javascripts-webpack)
|
47
48
|
* [User profile page](#user-profile-page)
|
48
49
|
* [Customizing views](#customizing-views)
|
49
50
|
* [View hooks](#view-hooks)
|
@@ -95,7 +96,7 @@ Then, see the rest of this Readme for more information about using and customizi
|
|
95
96
|
Add the gem to your Gemfile:
|
96
97
|
|
97
98
|
```ruby
|
98
|
-
gem 'thredded', '~> 0.16.
|
99
|
+
gem 'thredded', '~> 0.16.14'
|
99
100
|
```
|
100
101
|
|
101
102
|
Add the Thredded [initializer] to your parent app by running the install generator.
|
@@ -210,7 +211,7 @@ from the Thredded one by adding this Sass snippet after `@import "thredded";`:
|
|
210
211
|
|
211
212
|
See [below](#styles) for customizing the styles via Sass variables.
|
212
213
|
|
213
|
-
#### Add Thredded JavaScripts
|
214
|
+
#### Add Thredded JavaScripts (Sprockets)
|
214
215
|
|
215
216
|
Include thredded JavaScripts in your `application.js`:
|
216
217
|
|
@@ -220,6 +221,28 @@ Include thredded JavaScripts in your `application.js`:
|
|
220
221
|
|
221
222
|
Thredded is fully compatible with deferred and async script loading.
|
222
223
|
|
224
|
+
#### Add Thredded JavaScripts (Webpack)
|
225
|
+
|
226
|
+
You can also include Thredded JavaScript into your webpack pack.
|
227
|
+
|
228
|
+
First, run `bundle exec rails webpacker:install:erb`.
|
229
|
+
|
230
|
+
Then, add an `app/javascript/thredded_imports.js.erb` file with the following contents:
|
231
|
+
|
232
|
+
```erb
|
233
|
+
<%= Thredded::WebpackAssets.javascripts %>
|
234
|
+
```
|
235
|
+
|
236
|
+
Finally, add the following to your `app/javascript/packs/application.js`:
|
237
|
+
|
238
|
+
```js
|
239
|
+
require('thredded_imports.js');
|
240
|
+
```
|
241
|
+
|
242
|
+
Note that you must use `require` (not `import`) because Thredded JavaScript must be run after UJS/Turbolink `start()`
|
243
|
+
has been called. This is because Webpack places `import` calls before the code in the same file (unlike `require`,
|
244
|
+
which are placed in the same order as in the source).
|
245
|
+
|
223
246
|
##### Alternative JavaScript dependencies
|
224
247
|
|
225
248
|
<details><summary><b>Rails UJS version</b></summary>
|
@@ -381,15 +404,27 @@ Here are the steps to ensure the best support for your language if it isn't Engl
|
|
381
404
|
|
382
405
|
1. Add `rails-i18n` and `kaminari-i18n` to your Gemfile.
|
383
406
|
|
384
|
-
2. Require the translations for timeago.js in your JavaScript. E.g.
|
407
|
+
2. Require the translations for timeago.js in your JavaScript. E.g. if you want to add German and Brazilian Portuguese:
|
408
|
+
|
409
|
+
Sprockets:
|
385
410
|
|
386
411
|
```js
|
387
412
|
//= require thredded/dependencies/timeago
|
413
|
+
//= require timeago/locales/de
|
388
414
|
//= require timeago/locales/pt_BR
|
389
415
|
//= require thredded
|
390
|
-
```
|
391
|
-
|
392
|
-
|
416
|
+
```
|
417
|
+
|
418
|
+
Webpack:
|
419
|
+
|
420
|
+
```erb
|
421
|
+
<% timeago_root = File.join(Gem.loaded_specs['timeago_js'].full_gem_path, 'assets', 'javascripts') %>
|
422
|
+
import "<%= File.join(timeago_root, 'timeago.js') %>";
|
423
|
+
<%= %w[de pt_BR].map { |locale| %(import "#{File.join(timeago_root, "timeago/locales/#{locale}.js")}";) } * "\n" %>
|
424
|
+
<%= Thredded::WebpackAssets.javascripts %>
|
425
|
+
```
|
426
|
+
|
427
|
+
Note that it is important that timeago and its locales are required *before* Thredded.
|
393
428
|
|
394
429
|
3. To generate URL slugs for messageboards, categories, and topics with support for more language than English,
|
395
430
|
you can use a gem like [babosa](https://github.com/norman/babosa).
|
@@ -601,13 +636,26 @@ First, to get started, migrate and seed the database (SQLite by default):
|
|
601
636
|
```bash
|
602
637
|
bundle
|
603
638
|
# Create, migrate, and seed the development database with fake forum users, topics, and posts:
|
604
|
-
|
639
|
+
bin/rails db:create db:migrate db:seed
|
640
|
+
```
|
641
|
+
|
642
|
+
Install NPM dependencies for the dummy app:
|
643
|
+
|
644
|
+
```bash
|
645
|
+
cd spec/dummy && yarn && cd -
|
605
646
|
```
|
606
647
|
|
607
648
|
Then, start the dummy app server:
|
608
649
|
|
609
650
|
```bash
|
610
|
-
|
651
|
+
bin/rails s
|
652
|
+
```
|
653
|
+
|
654
|
+
By default, the dummy app server uses Webpack for JavaScript.
|
655
|
+
To use Sprockets instead, run:
|
656
|
+
|
657
|
+
```bash
|
658
|
+
THREDDED_TESTAPP_WEBPACK=1 bin/rails s
|
611
659
|
```
|
612
660
|
|
613
661
|
### Testing
|
@@ -1,3 +1,4 @@
|
|
1
|
+
//= require thredded/core/thredded
|
1
2
|
//= require thredded/core/on_page_load
|
2
3
|
//= require thredded/core/serialize_form
|
3
4
|
|
@@ -6,13 +7,6 @@
|
|
6
7
|
const Thredded = window.Thredded;
|
7
8
|
const Turbolinks = window.Turbolinks;
|
8
9
|
|
9
|
-
Thredded.onPageLoad(() => {
|
10
|
-
if (!Turbolinks || !Turbolinks.supported) return;
|
11
|
-
Array.prototype.forEach.call(document.querySelectorAll('[data-thredded-turboform]'), (form) => {
|
12
|
-
form.addEventListener('submit', handleSubmit);
|
13
|
-
});
|
14
|
-
});
|
15
|
-
|
16
10
|
const handleSubmit = (evt) => {
|
17
11
|
evt.preventDefault();
|
18
12
|
const form = evt.currentTarget;
|
@@ -22,4 +16,11 @@
|
|
22
16
|
// Turbolinks. Hide it:
|
23
17
|
Thredded.hideSoftKeyboard();
|
24
18
|
};
|
19
|
+
|
20
|
+
Thredded.onPageLoad(() => {
|
21
|
+
if (!Turbolinks || !Turbolinks.supported) return;
|
22
|
+
Array.prototype.forEach.call(document.querySelectorAll('[data-thredded-turboform]'), (form) => {
|
23
|
+
form.addEventListener('submit', handleSubmit);
|
24
|
+
});
|
25
|
+
});
|
25
26
|
})();
|
@@ -1 +1 @@
|
|
1
|
-
//= require autosize
|
1
|
+
//= require autosize
|
@@ -20,6 +20,14 @@ module Thredded
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
def show
|
24
|
+
@group = Thredded::MessageboardGroup.where(id: params[:id])
|
25
|
+
@groups = Thredded::MessageboardGroupView.grouped(
|
26
|
+
policy_scope(Thredded::Messageboard.where(group: params[:id])),
|
27
|
+
user: thredded_current_user
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
23
31
|
private
|
24
32
|
|
25
33
|
def messageboard_group_params
|
@@ -10,6 +10,8 @@ module Thredded
|
|
10
10
|
attr_reader :post_form
|
11
11
|
# @return [MessageboardsIndex]
|
12
12
|
attr_reader :messageboards_index
|
13
|
+
# @return [MessageboardGroupShow]
|
14
|
+
attr_reader :messageboard_group_show
|
13
15
|
# @return [ModerationUserPage]
|
14
16
|
attr_reader :moderation_user_page
|
15
17
|
# @return [TopicPage]
|
@@ -33,6 +35,7 @@ module Thredded
|
|
33
35
|
@post_form = PostForm.new
|
34
36
|
@moderation_user_page = ModerationUserPage.new
|
35
37
|
@messageboards_index = MessageboardsIndex.new
|
38
|
+
@messageboard_group_show = MessageboardGroupShow.new
|
36
39
|
@topic_page = TopicPage.new
|
37
40
|
end
|
38
41
|
|
@@ -95,6 +98,24 @@ module Thredded
|
|
95
98
|
end
|
96
99
|
end
|
97
100
|
|
101
|
+
class MessageboardGroupShow
|
102
|
+
# @return [Thredded::AllViewHooks::ViewHook]
|
103
|
+
attr_reader :container
|
104
|
+
# @return [Thredded::AllViewHooks::ViewHook]
|
105
|
+
attr_reader :list
|
106
|
+
# @return [Thredded::AllViewHooks::ViewHook]
|
107
|
+
attr_reader :group
|
108
|
+
# @return [Thredded::AllViewHooks::ViewHook]
|
109
|
+
attr_reader :messageboard
|
110
|
+
|
111
|
+
def initialize
|
112
|
+
@container = ViewHook.new
|
113
|
+
@list = ViewHook.new
|
114
|
+
@group = ViewHook.new
|
115
|
+
@messageboard = ViewHook.new
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
98
119
|
class ModerationUserPage
|
99
120
|
# @return [Thredded::AllViewHooks::ViewHook]
|
100
121
|
attr_reader :user_title
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Thredded
|
4
4
|
# A view model for a page of MessageboardGroupViews.
|
5
5
|
class MessageboardGroupView
|
6
|
-
delegate :name, to: :@group, allow_nil: true
|
6
|
+
delegate :name, :id, to: :@group, allow_nil: true
|
7
7
|
attr_reader :group, :messageboards
|
8
8
|
|
9
9
|
# @param [ActiveRecord::Relation<Thredded::Messageboard>] messageboards_scope
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<% content_for :thredded_page_title, t('thredded.messageboard_group.show.page_title', name: @group.name) %>
|
2
|
+
<% content_for :thredded_page_id, 'thredded--messageboard-groups--index' %>
|
3
|
+
<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %>
|
4
|
+
<%= thredded_page do %>
|
5
|
+
<%= define_svg_icons('thredded/follow.svg', 'thredded/lock.svg') %>
|
6
|
+
<%= view_hooks.messageboard_group_show.container.render self, groups: @groups do %>
|
7
|
+
<section class="thredded--main-section thredded--messageboards">
|
8
|
+
<%= view_hooks.messageboard_group_show.list.render self, groups: @groups do %>
|
9
|
+
<% @groups.each do |group| %>
|
10
|
+
<% if group.name.present? %>
|
11
|
+
<h3 class="thredded--messageboards-group--title">
|
12
|
+
<%= link_to group.name,
|
13
|
+
show_messageboard_group_path(group.id),
|
14
|
+
class: 'thredded--link' %>
|
15
|
+
</h3>
|
16
|
+
<% end %>
|
17
|
+
<div class="thredded--messageboards-group">
|
18
|
+
<%= view_hooks.messageboard_group_show.group.render self, group: group do %>
|
19
|
+
<%= render partial: 'thredded/messageboards/messageboard',
|
20
|
+
collection: group.messageboards %>
|
21
|
+
<% end %>
|
22
|
+
<%= render partial: 'thredded/messageboards/grid_sizers' %>
|
23
|
+
</div>
|
24
|
+
<% end %>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<div class="thredded--messageboards--actions">
|
28
|
+
<% if policy(Thredded::Messageboard.new).create? %>
|
29
|
+
<a class="thredded--button" href="<%= new_messageboard_path %>" rel="nofollow">
|
30
|
+
<%= t('thredded.messageboard.create') %>
|
31
|
+
</a>
|
32
|
+
<% end %>
|
33
|
+
<% if policy(Thredded::MessageboardGroup.new).create? %>
|
34
|
+
<a class="thredded--button" href="<%= new_messageboard_group_path %>" rel="nofollow">
|
35
|
+
<%= t('thredded.messageboard_group.create') %>
|
36
|
+
</a>
|
37
|
+
<% end %>
|
38
|
+
</div>
|
39
|
+
</section>
|
40
|
+
<% end %>
|
41
|
+
<% end %>
|
@@ -8,7 +8,12 @@
|
|
8
8
|
<%= view_hooks.messageboards_index.list.render self, groups: @groups do %>
|
9
9
|
<% @groups.each do |group| %>
|
10
10
|
<% if group.name.present? %>
|
11
|
-
<h3 class="thredded--messageboards-group--title"
|
11
|
+
<h3 class="thredded--messageboards-group--title">
|
12
|
+
<%= link_to_if Thredded.show_messageboard_group_page,
|
13
|
+
group.name,
|
14
|
+
show_messageboard_group_path(group.id),
|
15
|
+
class: 'thredded--link' %>
|
16
|
+
</h3>
|
12
17
|
<% end %>
|
13
18
|
<div class="thredded--messageboards-group">
|
14
19
|
<%= view_hooks.messageboards_index.group.render self, group: group do %>
|
data/config/locales/de.yml
CHANGED
data/config/locales/en.yml
CHANGED
data/config/locales/es.yml
CHANGED
data/config/locales/fr.yml
CHANGED
data/config/locales/it.yml
CHANGED
data/config/locales/pl.yml
CHANGED
data/config/locales/pt-BR.yml
CHANGED
data/config/locales/ru.yml
CHANGED
data/config/locales/zh-CN.yml
CHANGED
data/config/routes.rb
CHANGED
@@ -59,6 +59,7 @@ Thredded::Engine.routes.draw do # rubocop:disable Metrics/BlockLength
|
|
59
59
|
|
60
60
|
resource :preferences, only: %i[edit update], as: :global_preferences
|
61
61
|
resource :messageboard, path: 'messageboards', only: [:new]
|
62
|
+
get '/messageboard-groups/:id', action: :show, controller: 'messageboard_groups', as: :show_messageboard_group
|
62
63
|
resources :messageboards, only: %i[edit update destroy]
|
63
64
|
resources :messageboards, only: %i[index create], path: '' do
|
64
65
|
resource :preferences, only: %i[edit update]
|
@@ -58,6 +58,9 @@ Thredded.messageboards_order = :position
|
|
58
58
|
# Whether admin users see button to delete entire messageboards on the messageboard edit page.
|
59
59
|
Thredded.show_messageboard_delete_button = false
|
60
60
|
|
61
|
+
# Whether MessageboardGroup show page is enabled.
|
62
|
+
Thredded.show_messageboard_group_page = true
|
63
|
+
|
61
64
|
# Whether users that are following a topic are listed on the topic page.
|
62
65
|
Thredded.show_topic_followers = false
|
63
66
|
|
data/lib/thredded.rb
CHANGED
@@ -46,6 +46,8 @@ require 'thredded/base_notifier'
|
|
46
46
|
require 'thredded/arel_compat'
|
47
47
|
require 'thredded/collection_to_strings_with_cache_renderer'
|
48
48
|
|
49
|
+
require 'thredded/webpack_assets'
|
50
|
+
|
49
51
|
if Rails::VERSION::MAJOR < 5
|
50
52
|
begin
|
51
53
|
require 'where-or'
|
@@ -132,6 +134,9 @@ module Thredded # rubocop:disable Metrics/ModuleLength
|
|
132
134
|
# @return [Number] Minimum length to trigger username auto-completion for @-mentions and private message recipients.
|
133
135
|
attr_accessor :autocomplete_min_length
|
134
136
|
|
137
|
+
# @return [Boolean] Whether MessageboardGroup show page is enabled.
|
138
|
+
attr_accessor :show_messageboard_group_page
|
139
|
+
|
135
140
|
# @return [Thredded::AllViewHooks] View hooks configuration.
|
136
141
|
def view_hooks
|
137
142
|
Thredded::AllViewHooks.instance ||
|
@@ -282,6 +287,7 @@ module Thredded # rubocop:disable Metrics/ModuleLength
|
|
282
287
|
self.messageboards_order = :position
|
283
288
|
self.topics_per_page = 50
|
284
289
|
self.autocomplete_min_length = 2
|
290
|
+
self.show_messageboard_group_page = true
|
285
291
|
|
286
292
|
self.auto_follow_when_creating_topic = true
|
287
293
|
self.auto_follow_when_posting_in_topic = true
|
data/lib/thredded/engine.rb
CHANGED
data/lib/thredded/version.rb
CHANGED
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
module Thredded
|
7
|
+
# Lets you include Thredded JavaScripts into your Webpack "pack".
|
8
|
+
#
|
9
|
+
# To use this, first run `bundle exec rails webpacker:install:erb`.
|
10
|
+
# Then, rename `app/javascript/packs/application.js` to `app/javascript/packs/application.js.erb`
|
11
|
+
# Finally, add this line to `app/javascript/packs/application.js.erb`:
|
12
|
+
#
|
13
|
+
# <%= Thredded::WebpackAssets.javascripts %>
|
14
|
+
#
|
15
|
+
# To include additional timeago locales, add this *before* `Thredded::WebpackAssets.javascripts`:
|
16
|
+
#
|
17
|
+
# <% timeago_root = File.join(Gem.loaded_specs['timeago_js'].full_gem_path, 'assets', 'javascripts') %>
|
18
|
+
# import "<%= File.join(timeago_root, 'timeago.js') %>";
|
19
|
+
# <%= %w[de pt_BR].map { |locale| %(import "#{File.join(timeago_root, "timeago/locales/#{locale}.js")}";) } * "\n" %>
|
20
|
+
module WebpackAssets
|
21
|
+
JAVASCRIPT_EXTS = %w[.es6 .js].freeze
|
22
|
+
|
23
|
+
def self.javascripts
|
24
|
+
@javascripts ||= JavaScriptsResolver.new.resolve('thredded.es6')
|
25
|
+
end
|
26
|
+
|
27
|
+
class JavaScriptsResolver
|
28
|
+
def resolve(entry_point)
|
29
|
+
resolve_full_paths(entry_point).map do |dep|
|
30
|
+
%(import "#{dep}";)
|
31
|
+
end.join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def resolve_full_paths(entry_point)
|
37
|
+
deps = []
|
38
|
+
seen = Set.new
|
39
|
+
queue = [[:start, dep_to_path(entry_point)]]
|
40
|
+
until queue.empty?
|
41
|
+
state, path = queue.pop
|
42
|
+
if state == :visiting
|
43
|
+
deps << path
|
44
|
+
next
|
45
|
+
end
|
46
|
+
next unless seen.add?(path)
|
47
|
+
queue << [:visiting, path]
|
48
|
+
next if path.to_s.start_with?('@')
|
49
|
+
src = File.read(path)
|
50
|
+
src.scan(%r{//= require (\S+)}).each do |m|
|
51
|
+
queue << [:start, dep_to_path(m[0])]
|
52
|
+
end
|
53
|
+
tree_root = Pathname.new(File.dirname(path))
|
54
|
+
src.scan(%r{//= require_tree (\S+)}).each do |m|
|
55
|
+
tree_root.join(m[0]).each_child do |pn|
|
56
|
+
next unless JAVASCRIPT_EXTS.include?(pn.extname)
|
57
|
+
queue << [:start, pn.cleanpath.to_s]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
deps
|
62
|
+
end
|
63
|
+
|
64
|
+
def engine_js_root
|
65
|
+
@engine_js_root ||= Pathname.new(engine_js_prefix)
|
66
|
+
end
|
67
|
+
|
68
|
+
def engine_js_prefix
|
69
|
+
@engine_js_prefix ||=
|
70
|
+
File.expand_path('app/assets/javascripts', File.join(__dir__, '../..'))
|
71
|
+
end
|
72
|
+
|
73
|
+
def engine_vendor_js_prefix
|
74
|
+
@engine_vendor_js_prefix ||=
|
75
|
+
File.expand_path('vendor/assets/javascripts', File.join(__dir__, '../..'))
|
76
|
+
end
|
77
|
+
|
78
|
+
def engine_dep?(dep)
|
79
|
+
dep.start_with?('thredded/')
|
80
|
+
end
|
81
|
+
|
82
|
+
def engine_dep_to_path(dep)
|
83
|
+
find_dep_in_path(engine_js_prefix, dep) ||
|
84
|
+
find_dep_in_path(engine_vendor_js_prefix, dep) ||
|
85
|
+
fail("Failed to find #{dep}")
|
86
|
+
end
|
87
|
+
|
88
|
+
def find_dep_in_path(dir, dep)
|
89
|
+
path = File.join(dir, dep)
|
90
|
+
return path if File.file?(path)
|
91
|
+
JAVASCRIPT_EXTS.each do |ext|
|
92
|
+
path_with_ext = "#{path}#{ext}"
|
93
|
+
return path_with_ext if File.file?(path_with_ext)
|
94
|
+
end
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def dep_to_path(dep)
|
99
|
+
if dep == 'timeago'
|
100
|
+
path = File.join(Gem.loaded_specs['timeago_js'].full_gem_path, 'assets', 'javascripts', "#{dep}.js")
|
101
|
+
fail "Failed to find #{dep}" unless File.file?(path)
|
102
|
+
return path
|
103
|
+
elsif dep == 'rails-ujs'
|
104
|
+
return '@rails/ujs'
|
105
|
+
end
|
106
|
+
engine_dep_to_path(dep)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,280 @@
|
|
1
|
+
/** https://unpkg.com/autosize@4.0.2/dist/autosize.js **/
|
2
|
+
// Modified to always define the autosize global.
|
3
|
+
// This allows Thredded code to be compatible with both Webpack and Sprockets.
|
4
|
+
(function (global, factory) {
|
5
|
+
var mod = {
|
6
|
+
exports: {}
|
7
|
+
};
|
8
|
+
factory(mod, mod.exports);
|
9
|
+
global.autosize = mod.exports;
|
10
|
+
})(window, function (module, exports) {
|
11
|
+
'use strict';
|
12
|
+
|
13
|
+
var map = typeof Map === "function" ? new Map() : function () {
|
14
|
+
var keys = [];
|
15
|
+
var values = [];
|
16
|
+
|
17
|
+
return {
|
18
|
+
has: function has(key) {
|
19
|
+
return keys.indexOf(key) > -1;
|
20
|
+
},
|
21
|
+
get: function get(key) {
|
22
|
+
return values[keys.indexOf(key)];
|
23
|
+
},
|
24
|
+
set: function set(key, value) {
|
25
|
+
if (keys.indexOf(key) === -1) {
|
26
|
+
keys.push(key);
|
27
|
+
values.push(value);
|
28
|
+
}
|
29
|
+
},
|
30
|
+
delete: function _delete(key) {
|
31
|
+
var index = keys.indexOf(key);
|
32
|
+
if (index > -1) {
|
33
|
+
keys.splice(index, 1);
|
34
|
+
values.splice(index, 1);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
};
|
38
|
+
}();
|
39
|
+
|
40
|
+
var createEvent = function createEvent(name) {
|
41
|
+
return new Event(name, { bubbles: true });
|
42
|
+
};
|
43
|
+
try {
|
44
|
+
new Event('test');
|
45
|
+
} catch (e) {
|
46
|
+
// IE does not support `new Event()`
|
47
|
+
createEvent = function createEvent(name) {
|
48
|
+
var evt = document.createEvent('Event');
|
49
|
+
evt.initEvent(name, true, false);
|
50
|
+
return evt;
|
51
|
+
};
|
52
|
+
}
|
53
|
+
|
54
|
+
function assign(ta) {
|
55
|
+
if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has(ta)) return;
|
56
|
+
|
57
|
+
var heightOffset = null;
|
58
|
+
var clientWidth = null;
|
59
|
+
var cachedHeight = null;
|
60
|
+
|
61
|
+
function init() {
|
62
|
+
var style = window.getComputedStyle(ta, null);
|
63
|
+
|
64
|
+
if (style.resize === 'vertical') {
|
65
|
+
ta.style.resize = 'none';
|
66
|
+
} else if (style.resize === 'both') {
|
67
|
+
ta.style.resize = 'horizontal';
|
68
|
+
}
|
69
|
+
|
70
|
+
if (style.boxSizing === 'content-box') {
|
71
|
+
heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom));
|
72
|
+
} else {
|
73
|
+
heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
|
74
|
+
}
|
75
|
+
// Fix when a textarea is not on document body and heightOffset is Not a Number
|
76
|
+
if (isNaN(heightOffset)) {
|
77
|
+
heightOffset = 0;
|
78
|
+
}
|
79
|
+
|
80
|
+
update();
|
81
|
+
}
|
82
|
+
|
83
|
+
function changeOverflow(value) {
|
84
|
+
{
|
85
|
+
// Chrome/Safari-specific fix:
|
86
|
+
// When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
|
87
|
+
// made available by removing the scrollbar. The following forces the necessary text reflow.
|
88
|
+
var width = ta.style.width;
|
89
|
+
ta.style.width = '0px';
|
90
|
+
// Force reflow:
|
91
|
+
/* jshint ignore:start */
|
92
|
+
ta.offsetWidth;
|
93
|
+
/* jshint ignore:end */
|
94
|
+
ta.style.width = width;
|
95
|
+
}
|
96
|
+
|
97
|
+
ta.style.overflowY = value;
|
98
|
+
}
|
99
|
+
|
100
|
+
function getParentOverflows(el) {
|
101
|
+
var arr = [];
|
102
|
+
|
103
|
+
while (el && el.parentNode && el.parentNode instanceof Element) {
|
104
|
+
if (el.parentNode.scrollTop) {
|
105
|
+
arr.push({
|
106
|
+
node: el.parentNode,
|
107
|
+
scrollTop: el.parentNode.scrollTop
|
108
|
+
});
|
109
|
+
}
|
110
|
+
el = el.parentNode;
|
111
|
+
}
|
112
|
+
|
113
|
+
return arr;
|
114
|
+
}
|
115
|
+
|
116
|
+
function resize() {
|
117
|
+
if (ta.scrollHeight === 0) {
|
118
|
+
// If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
|
119
|
+
return;
|
120
|
+
}
|
121
|
+
|
122
|
+
var overflows = getParentOverflows(ta);
|
123
|
+
var docTop = document.documentElement && document.documentElement.scrollTop; // Needed for Mobile IE (ticket #240)
|
124
|
+
|
125
|
+
ta.style.height = '';
|
126
|
+
ta.style.height = ta.scrollHeight + heightOffset + 'px';
|
127
|
+
|
128
|
+
// used to check if an update is actually necessary on window.resize
|
129
|
+
clientWidth = ta.clientWidth;
|
130
|
+
|
131
|
+
// prevents scroll-position jumping
|
132
|
+
overflows.forEach(function (el) {
|
133
|
+
el.node.scrollTop = el.scrollTop;
|
134
|
+
});
|
135
|
+
|
136
|
+
if (docTop) {
|
137
|
+
document.documentElement.scrollTop = docTop;
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
function update() {
|
142
|
+
resize();
|
143
|
+
|
144
|
+
var styleHeight = Math.round(parseFloat(ta.style.height));
|
145
|
+
var computed = window.getComputedStyle(ta, null);
|
146
|
+
|
147
|
+
// Using offsetHeight as a replacement for computed.height in IE, because IE does not account use of border-box
|
148
|
+
var actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(computed.height)) : ta.offsetHeight;
|
149
|
+
|
150
|
+
// The actual height not matching the style height (set via the resize method) indicates that
|
151
|
+
// the max-height has been exceeded, in which case the overflow should be allowed.
|
152
|
+
if (actualHeight < styleHeight) {
|
153
|
+
if (computed.overflowY === 'hidden') {
|
154
|
+
changeOverflow('scroll');
|
155
|
+
resize();
|
156
|
+
actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(window.getComputedStyle(ta, null).height)) : ta.offsetHeight;
|
157
|
+
}
|
158
|
+
} else {
|
159
|
+
// Normally keep overflow set to hidden, to avoid flash of scrollbar as the textarea expands.
|
160
|
+
if (computed.overflowY !== 'hidden') {
|
161
|
+
changeOverflow('hidden');
|
162
|
+
resize();
|
163
|
+
actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(window.getComputedStyle(ta, null).height)) : ta.offsetHeight;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
if (cachedHeight !== actualHeight) {
|
168
|
+
cachedHeight = actualHeight;
|
169
|
+
var evt = createEvent('autosize:resized');
|
170
|
+
try {
|
171
|
+
ta.dispatchEvent(evt);
|
172
|
+
} catch (err) {
|
173
|
+
// Firefox will throw an error on dispatchEvent for a detached element
|
174
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=889376
|
175
|
+
}
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
var pageResize = function pageResize() {
|
180
|
+
if (ta.clientWidth !== clientWidth) {
|
181
|
+
update();
|
182
|
+
}
|
183
|
+
};
|
184
|
+
|
185
|
+
var destroy = function (style) {
|
186
|
+
window.removeEventListener('resize', pageResize, false);
|
187
|
+
ta.removeEventListener('input', update, false);
|
188
|
+
ta.removeEventListener('keyup', update, false);
|
189
|
+
ta.removeEventListener('autosize:destroy', destroy, false);
|
190
|
+
ta.removeEventListener('autosize:update', update, false);
|
191
|
+
|
192
|
+
Object.keys(style).forEach(function (key) {
|
193
|
+
ta.style[key] = style[key];
|
194
|
+
});
|
195
|
+
|
196
|
+
map.delete(ta);
|
197
|
+
}.bind(ta, {
|
198
|
+
height: ta.style.height,
|
199
|
+
resize: ta.style.resize,
|
200
|
+
overflowY: ta.style.overflowY,
|
201
|
+
overflowX: ta.style.overflowX,
|
202
|
+
wordWrap: ta.style.wordWrap
|
203
|
+
});
|
204
|
+
|
205
|
+
ta.addEventListener('autosize:destroy', destroy, false);
|
206
|
+
|
207
|
+
// IE9 does not fire onpropertychange or oninput for deletions,
|
208
|
+
// so binding to onkeyup to catch most of those events.
|
209
|
+
// There is no way that I know of to detect something like 'cut' in IE9.
|
210
|
+
if ('onpropertychange' in ta && 'oninput' in ta) {
|
211
|
+
ta.addEventListener('keyup', update, false);
|
212
|
+
}
|
213
|
+
|
214
|
+
window.addEventListener('resize', pageResize, false);
|
215
|
+
ta.addEventListener('input', update, false);
|
216
|
+
ta.addEventListener('autosize:update', update, false);
|
217
|
+
ta.style.overflowX = 'hidden';
|
218
|
+
ta.style.wordWrap = 'break-word';
|
219
|
+
|
220
|
+
map.set(ta, {
|
221
|
+
destroy: destroy,
|
222
|
+
update: update
|
223
|
+
});
|
224
|
+
|
225
|
+
init();
|
226
|
+
}
|
227
|
+
|
228
|
+
function destroy(ta) {
|
229
|
+
var methods = map.get(ta);
|
230
|
+
if (methods) {
|
231
|
+
methods.destroy();
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
function update(ta) {
|
236
|
+
var methods = map.get(ta);
|
237
|
+
if (methods) {
|
238
|
+
methods.update();
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
var autosize = null;
|
243
|
+
|
244
|
+
// Do nothing in Node.js environment and IE8 (or lower)
|
245
|
+
if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {
|
246
|
+
autosize = function autosize(el) {
|
247
|
+
return el;
|
248
|
+
};
|
249
|
+
autosize.destroy = function (el) {
|
250
|
+
return el;
|
251
|
+
};
|
252
|
+
autosize.update = function (el) {
|
253
|
+
return el;
|
254
|
+
};
|
255
|
+
} else {
|
256
|
+
autosize = function autosize(el, options) {
|
257
|
+
if (el) {
|
258
|
+
Array.prototype.forEach.call(el.length ? el : [el], function (x) {
|
259
|
+
return assign(x, options);
|
260
|
+
});
|
261
|
+
}
|
262
|
+
return el;
|
263
|
+
};
|
264
|
+
autosize.destroy = function (el) {
|
265
|
+
if (el) {
|
266
|
+
Array.prototype.forEach.call(el.length ? el : [el], destroy);
|
267
|
+
}
|
268
|
+
return el;
|
269
|
+
};
|
270
|
+
autosize.update = function (el) {
|
271
|
+
if (el) {
|
272
|
+
Array.prototype.forEach.call(el.length ? el : [el], update);
|
273
|
+
}
|
274
|
+
return el;
|
275
|
+
};
|
276
|
+
}
|
277
|
+
|
278
|
+
exports.default = autosize;
|
279
|
+
module.exports = exports['default'];
|
280
|
+
});
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thredded
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.16.
|
4
|
+
version: 0.16.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Oliveira
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-
|
12
|
+
date: 2019-10-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: active_record_union
|
@@ -295,14 +295,14 @@ dependencies:
|
|
295
295
|
requirements:
|
296
296
|
- - ">="
|
297
297
|
- !ruby/object:Gem::Version
|
298
|
-
version:
|
298
|
+
version: 3.0.2.2
|
299
299
|
type: :runtime
|
300
300
|
prerelease: false
|
301
301
|
version_requirements: !ruby/object:Gem::Requirement
|
302
302
|
requirements:
|
303
303
|
- - ">="
|
304
304
|
- !ruby/object:Gem::Version
|
305
|
-
version:
|
305
|
+
version: 3.0.2.2
|
306
306
|
- !ruby/object:Gem::Dependency
|
307
307
|
name: capybara
|
308
308
|
requirement: !ruby/object:Gem::Requirement
|
@@ -623,6 +623,7 @@ extra_rdoc_files: []
|
|
623
623
|
files:
|
624
624
|
- MIT-LICENSE
|
625
625
|
- README.md
|
626
|
+
- app/assets/config/thredded_manifest.js
|
626
627
|
- app/assets/images/favicons/README.md
|
627
628
|
- app/assets/images/favicons/amazon.png
|
628
629
|
- app/assets/images/favicons/github.png
|
@@ -845,6 +846,7 @@ files:
|
|
845
846
|
- app/views/thredded/kaminari/_paginator.html.erb
|
846
847
|
- app/views/thredded/kaminari/_prev_page.html.erb
|
847
848
|
- app/views/thredded/messageboard_groups/new.html.erb
|
849
|
+
- app/views/thredded/messageboard_groups/show.html.erb
|
848
850
|
- app/views/thredded/messageboards/_form.html.erb
|
849
851
|
- app/views/thredded/messageboards/_grid_sizers.html.erb
|
850
852
|
- app/views/thredded/messageboards/_messageboard.html.erb
|
@@ -1013,7 +1015,8 @@ files:
|
|
1013
1015
|
- lib/thredded/version.rb
|
1014
1016
|
- lib/thredded/view_hooks/config.rb
|
1015
1017
|
- lib/thredded/view_hooks/renderer.rb
|
1016
|
-
-
|
1018
|
+
- lib/thredded/webpack_assets.rb
|
1019
|
+
- vendor/assets/javascripts/autosize.js
|
1017
1020
|
- vendor/assets/javascripts/textcomplete.min.js
|
1018
1021
|
homepage: https://thredded.org
|
1019
1022
|
licenses:
|
@@ -1034,7 +1037,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1034
1037
|
- !ruby/object:Gem::Version
|
1035
1038
|
version: '0'
|
1036
1039
|
requirements: []
|
1037
|
-
rubygems_version: 3.0.
|
1040
|
+
rubygems_version: 3.0.6
|
1038
1041
|
signing_key:
|
1039
1042
|
specification_version: 4
|
1040
1043
|
summary: The best Rails forums engine ever.
|
@@ -1,2 +0,0 @@
|
|
1
|
-
/** https://unpkg.com/autosize@4.0.2/dist/autosize.min.js **/
|
2
|
-
!function(e,t){if("function"==typeof define&&define.amd)define(["module","exports"],t);else if("undefined"!=typeof exports)t(module,exports);else{var n={exports:{}};t(n,n.exports),e.autosize=n.exports}}(this,function(e,t){"use strict";var n,o,p="function"==typeof Map?new Map:(n=[],o=[],{has:function(e){return-1<n.indexOf(e)},get:function(e){return o[n.indexOf(e)]},set:function(e,t){-1===n.indexOf(e)&&(n.push(e),o.push(t))},delete:function(e){var t=n.indexOf(e);-1<t&&(n.splice(t,1),o.splice(t,1))}}),c=function(e){return new Event(e,{bubbles:!0})};try{new Event("test")}catch(e){c=function(e){var t=document.createEvent("Event");return t.initEvent(e,!0,!1),t}}function r(r){if(r&&r.nodeName&&"TEXTAREA"===r.nodeName&&!p.has(r)){var e,n=null,o=null,i=null,d=function(){r.clientWidth!==o&&a()},l=function(t){window.removeEventListener("resize",d,!1),r.removeEventListener("input",a,!1),r.removeEventListener("keyup",a,!1),r.removeEventListener("autosize:destroy",l,!1),r.removeEventListener("autosize:update",a,!1),Object.keys(t).forEach(function(e){r.style[e]=t[e]}),p.delete(r)}.bind(r,{height:r.style.height,resize:r.style.resize,overflowY:r.style.overflowY,overflowX:r.style.overflowX,wordWrap:r.style.wordWrap});r.addEventListener("autosize:destroy",l,!1),"onpropertychange"in r&&"oninput"in r&&r.addEventListener("keyup",a,!1),window.addEventListener("resize",d,!1),r.addEventListener("input",a,!1),r.addEventListener("autosize:update",a,!1),r.style.overflowX="hidden",r.style.wordWrap="break-word",p.set(r,{destroy:l,update:a}),"vertical"===(e=window.getComputedStyle(r,null)).resize?r.style.resize="none":"both"===e.resize&&(r.style.resize="horizontal"),n="content-box"===e.boxSizing?-(parseFloat(e.paddingTop)+parseFloat(e.paddingBottom)):parseFloat(e.borderTopWidth)+parseFloat(e.borderBottomWidth),isNaN(n)&&(n=0),a()}function s(e){var t=r.style.width;r.style.width="0px",r.offsetWidth,r.style.width=t,r.style.overflowY=e}function u(){if(0!==r.scrollHeight){var e=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push({node:e.parentNode,scrollTop:e.parentNode.scrollTop}),e=e.parentNode;return t}(r),t=document.documentElement&&document.documentElement.scrollTop;r.style.height="",r.style.height=r.scrollHeight+n+"px",o=r.clientWidth,e.forEach(function(e){e.node.scrollTop=e.scrollTop}),t&&(document.documentElement.scrollTop=t)}}function a(){u();var e=Math.round(parseFloat(r.style.height)),t=window.getComputedStyle(r,null),n="content-box"===t.boxSizing?Math.round(parseFloat(t.height)):r.offsetHeight;if(n<e?"hidden"===t.overflowY&&(s("scroll"),u(),n="content-box"===t.boxSizing?Math.round(parseFloat(window.getComputedStyle(r,null).height)):r.offsetHeight):"hidden"!==t.overflowY&&(s("hidden"),u(),n="content-box"===t.boxSizing?Math.round(parseFloat(window.getComputedStyle(r,null).height)):r.offsetHeight),i!==n){i=n;var o=c("autosize:resized");try{r.dispatchEvent(o)}catch(e){}}}}function i(e){var t=p.get(e);t&&t.destroy()}function d(e){var t=p.get(e);t&&t.update()}var l=null;"undefined"==typeof window||"function"!=typeof window.getComputedStyle?((l=function(e){return e}).destroy=function(e){return e},l.update=function(e){return e}):((l=function(e,t){return e&&Array.prototype.forEach.call(e.length?e:[e],function(e){return r(e)}),e}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],i),e},l.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],d),e}),t.default=l,e.exports=t.default});
|