threddedDANIEL 0.14.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (367) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +744 -0
  4. data/app/assets/images/favicons/README.md +3 -0
  5. data/app/assets/images/favicons/amazon.png +0 -0
  6. data/app/assets/images/favicons/github.png +0 -0
  7. data/app/assets/images/favicons/google_branding/logo_calendar_128px.png +0 -0
  8. data/app/assets/images/favicons/google_branding/logo_docs_48px.png +0 -0
  9. data/app/assets/images/favicons/google_branding/logo_drive_48px.png +0 -0
  10. data/app/assets/images/favicons/google_branding/logo_forms_48px.png +0 -0
  11. data/app/assets/images/favicons/google_branding/logo_sheets_48px.png +0 -0
  12. data/app/assets/images/favicons/google_branding/logo_slides_48px.png +0 -0
  13. data/app/assets/images/favicons/stackexchange.png +0 -0
  14. data/app/assets/images/favicons/twitter.png +0 -0
  15. data/app/assets/images/favicons/wikipedia.png +0 -0
  16. data/app/assets/images/thredded/breadcrumb-chevron.svg +1 -0
  17. data/app/assets/images/thredded/follow.svg +1 -0
  18. data/app/assets/images/thredded/lock.svg +1 -0
  19. data/app/assets/images/thredded/moderation.svg +1 -0
  20. data/app/assets/images/thredded/private-messages.svg +1 -0
  21. data/app/assets/images/thredded/settings.svg +1 -0
  22. data/app/assets/images/thredded/three-dot-menu.svg +3 -0
  23. data/app/assets/images/thredded/unfollow.svg +1 -0
  24. data/app/assets/javascripts/thredded.es6 +2 -0
  25. data/app/assets/javascripts/thredded/components/currently_online.es6 +32 -0
  26. data/app/assets/javascripts/thredded/components/flash_messages.es6 +9 -0
  27. data/app/assets/javascripts/thredded/components/mention_autocompletion.es6 +40 -0
  28. data/app/assets/javascripts/thredded/components/post_form.es6 +37 -0
  29. data/app/assets/javascripts/thredded/components/preview_area.es6 +56 -0
  30. data/app/assets/javascripts/thredded/components/quote_post.es6 +49 -0
  31. data/app/assets/javascripts/thredded/components/time_stamps.es6 +25 -0
  32. data/app/assets/javascripts/thredded/components/topic_form.es6 +94 -0
  33. data/app/assets/javascripts/thredded/components/topics.es6 +46 -0
  34. data/app/assets/javascripts/thredded/components/turboforms.es6 +25 -0
  35. data/app/assets/javascripts/thredded/components/user_preferences_form.es6 +66 -0
  36. data/app/assets/javascripts/thredded/components/user_textcomplete.es6 +50 -0
  37. data/app/assets/javascripts/thredded/components/users_select.es6 +122 -0
  38. data/app/assets/javascripts/thredded/core/csrf_tokens.es6 +9 -0
  39. data/app/assets/javascripts/thredded/core/debounce.es6 +32 -0
  40. data/app/assets/javascripts/thredded/core/escape_html.es6 +7 -0
  41. data/app/assets/javascripts/thredded/core/hide_soft_keyboard.es6 +7 -0
  42. data/app/assets/javascripts/thredded/core/on_page_load.es6 +54 -0
  43. data/app/assets/javascripts/thredded/core/serialize_form.es6 +9 -0
  44. data/app/assets/javascripts/thredded/core/thredded.es6 +1 -0
  45. data/app/assets/javascripts/thredded/dependencies.js +8 -0
  46. data/app/assets/javascripts/thredded/dependencies/autosize.js +1 -0
  47. data/app/assets/javascripts/thredded/dependencies/textcomplete.js +1 -0
  48. data/app/assets/javascripts/thredded/dependencies/timeago.js +1 -0
  49. data/app/assets/javascripts/thredded/dependencies/ujs.js +1 -0
  50. data/app/assets/javascripts/thredded/thredded.es6 +2 -0
  51. data/app/assets/stylesheets/thredded.scss +3 -0
  52. data/app/assets/stylesheets/thredded/_base.scss +13 -0
  53. data/app/assets/stylesheets/thredded/_dependencies.scss +0 -0
  54. data/app/assets/stylesheets/thredded/_email.scss +51 -0
  55. data/app/assets/stylesheets/thredded/_thredded.scss +33 -0
  56. data/app/assets/stylesheets/thredded/base/_alerts.scss +44 -0
  57. data/app/assets/stylesheets/thredded/base/_buttons.scss +73 -0
  58. data/app/assets/stylesheets/thredded/base/_dropdown-menu.scss +83 -0
  59. data/app/assets/stylesheets/thredded/base/_forms.scss +84 -0
  60. data/app/assets/stylesheets/thredded/base/_grid.scss +58 -0
  61. data/app/assets/stylesheets/thredded/base/_lists.scss +31 -0
  62. data/app/assets/stylesheets/thredded/base/_nav.scss +67 -0
  63. data/app/assets/stylesheets/thredded/base/_tables.scss +17 -0
  64. data/app/assets/stylesheets/thredded/base/_typography.scss +72 -0
  65. data/app/assets/stylesheets/thredded/base/_variables.scss +141 -0
  66. data/app/assets/stylesheets/thredded/components/_alerts.scss +19 -0
  67. data/app/assets/stylesheets/thredded/components/_base.scss +25 -0
  68. data/app/assets/stylesheets/thredded/components/_currently-online.scss +54 -0
  69. data/app/assets/stylesheets/thredded/components/_empty.scss +11 -0
  70. data/app/assets/stylesheets/thredded/components/_flash-message.scss +19 -0
  71. data/app/assets/stylesheets/thredded/components/_following.scss +14 -0
  72. data/app/assets/stylesheets/thredded/components/_form-list.scss +39 -0
  73. data/app/assets/stylesheets/thredded/components/_icons.scss +3 -0
  74. data/app/assets/stylesheets/thredded/components/_main-section.scss +9 -0
  75. data/app/assets/stylesheets/thredded/components/_mention-autocomplete.scss +57 -0
  76. data/app/assets/stylesheets/thredded/components/_messageboard.scss +155 -0
  77. data/app/assets/stylesheets/thredded/components/_onebox.scss +275 -0
  78. data/app/assets/stylesheets/thredded/components/_pagination.scss +35 -0
  79. data/app/assets/stylesheets/thredded/components/_post-form.scss +27 -0
  80. data/app/assets/stylesheets/thredded/components/_post.scss +138 -0
  81. data/app/assets/stylesheets/thredded/components/_preferences.scss +19 -0
  82. data/app/assets/stylesheets/thredded/components/_preview_area.scss +11 -0
  83. data/app/assets/stylesheets/thredded/components/_topic-delete.scss +8 -0
  84. data/app/assets/stylesheets/thredded/components/_topic-header.scss +111 -0
  85. data/app/assets/stylesheets/thredded/components/_topics.scss +186 -0
  86. data/app/assets/stylesheets/thredded/layout/_main-container.scss +16 -0
  87. data/app/assets/stylesheets/thredded/layout/_main-navigation.scss +79 -0
  88. data/app/assets/stylesheets/thredded/layout/_moderation.scss +97 -0
  89. data/app/assets/stylesheets/thredded/layout/_navigation.scss +87 -0
  90. data/app/assets/stylesheets/thredded/layout/_search-navigation.scss +126 -0
  91. data/app/assets/stylesheets/thredded/layout/_user-navigation.scss +20 -0
  92. data/app/assets/stylesheets/thredded/layout/_user.scss +10 -0
  93. data/app/assets/stylesheets/thredded/utilities/_is-compact.scss +26 -0
  94. data/app/assets/stylesheets/thredded/utilities/_is-expanded.scss +16 -0
  95. data/app/commands/thredded/at_notification_extractor.rb +23 -0
  96. data/app/commands/thredded/autofollow_users.rb +62 -0
  97. data/app/commands/thredded/create_messageboard.rb +42 -0
  98. data/app/commands/thredded/mark_all_read.rb +22 -0
  99. data/app/commands/thredded/moderate_post.rb +48 -0
  100. data/app/commands/thredded/notify_following_users.rb +78 -0
  101. data/app/commands/thredded/notify_private_topic_users.rb +34 -0
  102. data/app/controllers/concerns/thredded/new_post_params.rb +21 -0
  103. data/app/controllers/concerns/thredded/new_private_post_params.rb +21 -0
  104. data/app/controllers/concerns/thredded/new_private_topic_params.rb +25 -0
  105. data/app/controllers/concerns/thredded/new_topic_params.rb +19 -0
  106. data/app/controllers/concerns/thredded/render_preview.rb +16 -0
  107. data/app/controllers/thredded/application_controller.rb +145 -0
  108. data/app/controllers/thredded/autocomplete_users_controller.rb +52 -0
  109. data/app/controllers/thredded/messageboard_groups_controller.rb +31 -0
  110. data/app/controllers/thredded/messageboards_controller.rb +53 -0
  111. data/app/controllers/thredded/moderation_controller.rb +103 -0
  112. data/app/controllers/thredded/post_permalinks_controller.rb +11 -0
  113. data/app/controllers/thredded/post_previews_controller.rb +29 -0
  114. data/app/controllers/thredded/posts_controller.rb +97 -0
  115. data/app/controllers/thredded/preferences_controller.rb +42 -0
  116. data/app/controllers/thredded/private_post_permalinks_controller.rb +12 -0
  117. data/app/controllers/thredded/private_post_previews_controller.rb +29 -0
  118. data/app/controllers/thredded/private_posts_controller.rb +96 -0
  119. data/app/controllers/thredded/private_topic_previews_controller.rb +15 -0
  120. data/app/controllers/thredded/private_topics_controller.rb +102 -0
  121. data/app/controllers/thredded/read_states_controller.rb +13 -0
  122. data/app/controllers/thredded/theme_previews_controller.rb +34 -0
  123. data/app/controllers/thredded/topic_previews_controller.rb +15 -0
  124. data/app/controllers/thredded/topics_controller.rb +205 -0
  125. data/app/forms/thredded/edit_topic_form.rb +51 -0
  126. data/app/forms/thredded/post_form.rb +54 -0
  127. data/app/forms/thredded/private_post_form.rb +50 -0
  128. data/app/forms/thredded/private_topic_form.rb +160 -0
  129. data/app/forms/thredded/topic_form.rb +95 -0
  130. data/app/forms/thredded/user_preferences_form.rb +110 -0
  131. data/app/helpers/thredded/application_helper.rb +129 -0
  132. data/app/helpers/thredded/nav_helper.rb +42 -0
  133. data/app/helpers/thredded/render_helper.rb +15 -0
  134. data/app/helpers/thredded/urls_helper.rb +134 -0
  135. data/app/jobs/thredded/activity_updater_job.rb +21 -0
  136. data/app/jobs/thredded/auto_follow_and_notify_job.rb +14 -0
  137. data/app/jobs/thredded/notify_private_topic_users_job.rb +12 -0
  138. data/app/mailer_previews/thredded/base_mailer_preview.rb +118 -0
  139. data/app/mailer_previews/thredded/post_mailer_preview.rb +13 -0
  140. data/app/mailer_previews/thredded/private_topic_mailer_preview.rb +13 -0
  141. data/app/mailers/thredded/base_mailer.rb +18 -0
  142. data/app/mailers/thredded/post_mailer.rb +21 -0
  143. data/app/mailers/thredded/private_topic_mailer.rb +21 -0
  144. data/app/models/concerns/thredded/content_moderation_state.rb +67 -0
  145. data/app/models/concerns/thredded/friendly_id_reserved_words_and_pagination.rb +17 -0
  146. data/app/models/concerns/thredded/moderation_state.rb +14 -0
  147. data/app/models/concerns/thredded/notifier_preference.rb +19 -0
  148. data/app/models/concerns/thredded/post_common.rb +76 -0
  149. data/app/models/concerns/thredded/search_parser.rb +41 -0
  150. data/app/models/concerns/thredded/topic_common.rb +94 -0
  151. data/app/models/concerns/thredded/topics_search.rb +67 -0
  152. data/app/models/concerns/thredded/user_topic_read_state_common.rb +42 -0
  153. data/app/models/thredded/category.rb +18 -0
  154. data/app/models/thredded/messageboard.rb +120 -0
  155. data/app/models/thredded/messageboard_group.rb +19 -0
  156. data/app/models/thredded/messageboard_notifications_for_followed_topics.rb +30 -0
  157. data/app/models/thredded/messageboard_user.rb +14 -0
  158. data/app/models/thredded/notifications_for_followed_topics.rb +25 -0
  159. data/app/models/thredded/notifications_for_private_topics.rb +22 -0
  160. data/app/models/thredded/null_user.rb +51 -0
  161. data/app/models/thredded/null_user_topic_read_state.rb +17 -0
  162. data/app/models/thredded/post.rb +77 -0
  163. data/app/models/thredded/post_moderation_record.rb +56 -0
  164. data/app/models/thredded/private_post.rb +63 -0
  165. data/app/models/thredded/private_topic.rb +89 -0
  166. data/app/models/thredded/private_user.rb +8 -0
  167. data/app/models/thredded/stats.rb +25 -0
  168. data/app/models/thredded/topic.rb +179 -0
  169. data/app/models/thredded/topic_category.rb +8 -0
  170. data/app/models/thredded/user_detail.rb +27 -0
  171. data/app/models/thredded/user_extender.rb +75 -0
  172. data/app/models/thredded/user_messageboard_preference.rb +45 -0
  173. data/app/models/thredded/user_permissions/admin/if_admin_column_true.rb +14 -0
  174. data/app/models/thredded/user_permissions/admin/none.rb +14 -0
  175. data/app/models/thredded/user_permissions/message/readers_of_writeable_boards.rb +15 -0
  176. data/app/models/thredded/user_permissions/moderate/if_moderator_column_true.rb +16 -0
  177. data/app/models/thredded/user_permissions/moderate/none.rb +16 -0
  178. data/app/models/thredded/user_permissions/read/all.rb +27 -0
  179. data/app/models/thredded/user_permissions/write/all.rb +16 -0
  180. data/app/models/thredded/user_permissions/write/none.rb +16 -0
  181. data/app/models/thredded/user_post_notification.rb +29 -0
  182. data/app/models/thredded/user_preference.rb +26 -0
  183. data/app/models/thredded/user_private_topic_read_state.rb +13 -0
  184. data/app/models/thredded/user_topic_follow.rb +34 -0
  185. data/app/models/thredded/user_topic_read_state.rb +13 -0
  186. data/app/notifiers/thredded/email_notifier.rb +25 -0
  187. data/app/policies/thredded/messageboard_group_policy.rb +16 -0
  188. data/app/policies/thredded/messageboard_policy.rb +49 -0
  189. data/app/policies/thredded/post_policy.rb +64 -0
  190. data/app/policies/thredded/private_post_policy.rb +38 -0
  191. data/app/policies/thredded/private_topic_policy.rb +24 -0
  192. data/app/policies/thredded/topic_policy.rb +49 -0
  193. data/app/view_hooks/thredded/all_view_hooks.rb +125 -0
  194. data/app/view_models/thredded/base_topic_view.rb +43 -0
  195. data/app/view_models/thredded/messageboard_group_view.rb +27 -0
  196. data/app/view_models/thredded/post_view.rb +89 -0
  197. data/app/view_models/thredded/posts_page_view.rb +27 -0
  198. data/app/view_models/thredded/private_topic_view.rb +20 -0
  199. data/app/view_models/thredded/private_topics_page_view.rb +31 -0
  200. data/app/view_models/thredded/topic_email_view.rb +18 -0
  201. data/app/view_models/thredded/topic_posts_page_view.rb +17 -0
  202. data/app/view_models/thredded/topic_view.rb +68 -0
  203. data/app/view_models/thredded/topics_page_view.rb +38 -0
  204. data/app/views/layouts/thredded/application.html.erb +18 -0
  205. data/app/views/thredded/categories/_category.html.erb +1 -0
  206. data/app/views/thredded/error_pages/forbidden.html.erb +6 -0
  207. data/app/views/thredded/error_pages/not_found.html.erb +6 -0
  208. data/app/views/thredded/kaminari/_first_page.html.erb +11 -0
  209. data/app/views/thredded/kaminari/_gap.html.erb +8 -0
  210. data/app/views/thredded/kaminari/_last_page.html.erb +11 -0
  211. data/app/views/thredded/kaminari/_next_page.html.erb +11 -0
  212. data/app/views/thredded/kaminari/_page.html.erb +12 -0
  213. data/app/views/thredded/kaminari/_paginator.html.erb +23 -0
  214. data/app/views/thredded/kaminari/_prev_page.html.erb +11 -0
  215. data/app/views/thredded/messageboard_groups/new.html.erb +28 -0
  216. data/app/views/thredded/messageboards/_form.html.erb +30 -0
  217. data/app/views/thredded/messageboards/_messageboard.html.erb +20 -0
  218. data/app/views/thredded/messageboards/_messageboard_meta.html.erb +13 -0
  219. data/app/views/thredded/messageboards/edit.html.erb +15 -0
  220. data/app/views/thredded/messageboards/index.html.erb +34 -0
  221. data/app/views/thredded/messageboards/new.html.erb +15 -0
  222. data/app/views/thredded/moderation/_nav.html.erb +19 -0
  223. data/app/views/thredded/moderation/_post.html.erb +19 -0
  224. data/app/views/thredded/moderation/_post_moderation_actions.html.erb +12 -0
  225. data/app/views/thredded/moderation/_post_moderation_record.html.erb +46 -0
  226. data/app/views/thredded/moderation/_user_moderation_state.html.erb +3 -0
  227. data/app/views/thredded/moderation/_user_post.html.erb +12 -0
  228. data/app/views/thredded/moderation/_users_search_form.html.erb +13 -0
  229. data/app/views/thredded/moderation/activity.html.erb +20 -0
  230. data/app/views/thredded/moderation/history.html.erb +13 -0
  231. data/app/views/thredded/moderation/pending.html.erb +24 -0
  232. data/app/views/thredded/moderation/user.html.erb +54 -0
  233. data/app/views/thredded/moderation/users.html.erb +41 -0
  234. data/app/views/thredded/post_mailer/post_notification.html.erb +26 -0
  235. data/app/views/thredded/post_mailer/post_notification.text.erb +14 -0
  236. data/app/views/thredded/post_previews/preview.html.erb +1 -0
  237. data/app/views/thredded/post_previews/update.html.erb +1 -0
  238. data/app/views/thredded/posts/_content.html.erb +1 -0
  239. data/app/views/thredded/posts/_form.html.erb +5 -0
  240. data/app/views/thredded/posts/_post.html.erb +13 -0
  241. data/app/views/thredded/posts/_user.html.erb +3 -0
  242. data/app/views/thredded/posts/edit.html.erb +16 -0
  243. data/app/views/thredded/posts/new.html.erb +15 -0
  244. data/app/views/thredded/posts_common/_actions.html.erb +28 -0
  245. data/app/views/thredded/posts_common/_content.html.erb +3 -0
  246. data/app/views/thredded/posts_common/_form.html.erb +22 -0
  247. data/app/views/thredded/posts_common/_header.html.erb +8 -0
  248. data/app/views/thredded/posts_common/_header_with_topic.html.erb +15 -0
  249. data/app/views/thredded/posts_common/_header_with_user_and_topic.html.erb +18 -0
  250. data/app/views/thredded/posts_common/actions/_delete.html.erb +4 -0
  251. data/app/views/thredded/posts_common/actions/_edit.html.erb +3 -0
  252. data/app/views/thredded/posts_common/actions/_mark_as_unread.html.erb +2 -0
  253. data/app/views/thredded/posts_common/actions/_quote.html.erb +4 -0
  254. data/app/views/thredded/posts_common/form/_after_content.html.erb +8 -0
  255. data/app/views/thredded/posts_common/form/_before_content.html.erb +8 -0
  256. data/app/views/thredded/posts_common/form/_content.html.erb +7 -0
  257. data/app/views/thredded/posts_common/form/_content_field.html.erb +8 -0
  258. data/app/views/thredded/posts_common/form/_preview_area.html.erb +16 -0
  259. data/app/views/thredded/preferences/_form.html.erb +95 -0
  260. data/app/views/thredded/preferences/_messageboards_nav.html.erb +8 -0
  261. data/app/views/thredded/preferences/_messageboards_nav_item.html.erb +2 -0
  262. data/app/views/thredded/preferences/edit.html.erb +20 -0
  263. data/app/views/thredded/private_post_previews/preview.html.erb +1 -0
  264. data/app/views/thredded/private_post_previews/update.html.erb +1 -0
  265. data/app/views/thredded/private_posts/_content.html.erb +1 -0
  266. data/app/views/thredded/private_posts/_form.html.erb +6 -0
  267. data/app/views/thredded/private_posts/_private_post.html.erb +6 -0
  268. data/app/views/thredded/private_posts/edit.html.erb +16 -0
  269. data/app/views/thredded/private_posts/new.html.erb +11 -0
  270. data/app/views/thredded/private_topic_mailer/message_notification.html.erb +26 -0
  271. data/app/views/thredded/private_topic_mailer/message_notification.text.erb +15 -0
  272. data/app/views/thredded/private_topic_previews/preview.html.erb +1 -0
  273. data/app/views/thredded/private_topics/_breadcrumbs.html.erb +4 -0
  274. data/app/views/thredded/private_topics/_form.html.erb +39 -0
  275. data/app/views/thredded/private_topics/_header.html.erb +17 -0
  276. data/app/views/thredded/private_topics/_no_private_topics.html.erb +6 -0
  277. data/app/views/thredded/private_topics/_private_topic.html.erb +23 -0
  278. data/app/views/thredded/private_topics/edit.html.erb +35 -0
  279. data/app/views/thredded/private_topics/header/_participant.html.erb +1 -0
  280. data/app/views/thredded/private_topics/index.html.erb +32 -0
  281. data/app/views/thredded/private_topics/new.html.erb +11 -0
  282. data/app/views/thredded/private_topics/private_topic/_participant.html.erb +1 -0
  283. data/app/views/thredded/private_topics/show.html.erb +28 -0
  284. data/app/views/thredded/search/_form.html.erb +13 -0
  285. data/app/views/thredded/shared/_breadcrumbs.html.erb +6 -0
  286. data/app/views/thredded/shared/_content_moderation_blocked_state.html.erb +8 -0
  287. data/app/views/thredded/shared/_currently_online.html.erb +6 -0
  288. data/app/views/thredded/shared/_flash_messages.html.erb +7 -0
  289. data/app/views/thredded/shared/_header.html.erb +3 -0
  290. data/app/views/thredded/shared/_nav.html.erb +19 -0
  291. data/app/views/thredded/shared/_page.html.erb +15 -0
  292. data/app/views/thredded/shared/currently_online/_header.html.erb +5 -0
  293. data/app/views/thredded/shared/currently_online/_user_list.html.erb +3 -0
  294. data/app/views/thredded/shared/currently_online/_user_list_item.html.erb +6 -0
  295. data/app/views/thredded/shared/nav/_moderation.html.erb +14 -0
  296. data/app/views/thredded/shared/nav/_notification_preferences.html.erb +8 -0
  297. data/app/views/thredded/shared/nav/_private_topics.html.erb +12 -0
  298. data/app/views/thredded/shared/nav/_standalone.html.erb +12 -0
  299. data/app/views/thredded/shared/nav/_standalone_profile.html.erb +3 -0
  300. data/app/views/thredded/shared/preview.html.erb +10 -0
  301. data/app/views/thredded/theme_previews/_section_title.html.erb +3 -0
  302. data/app/views/thredded/theme_previews/show.html.erb +99 -0
  303. data/app/views/thredded/topic_previews/preview.html.erb +1 -0
  304. data/app/views/thredded/topics/_followers.html.erb +12 -0
  305. data/app/views/thredded/topics/_form.html.erb +32 -0
  306. data/app/views/thredded/topics/_header.html.erb +32 -0
  307. data/app/views/thredded/topics/_sticky_topics_divider.html.erb +1 -0
  308. data/app/views/thredded/topics/_topic.html.erb +47 -0
  309. data/app/views/thredded/topics/_topic_form_admin_options.html.erb +12 -0
  310. data/app/views/thredded/topics/edit.html.erb +50 -0
  311. data/app/views/thredded/topics/index.html.erb +35 -0
  312. data/app/views/thredded/topics/new.html.erb +12 -0
  313. data/app/views/thredded/topics/search.html.erb +39 -0
  314. data/app/views/thredded/topics/show.html.erb +46 -0
  315. data/app/views/thredded/users/_link.html.erb +14 -0
  316. data/app/views/thredded/users/_post.html.erb +6 -0
  317. data/app/views/thredded/users/_posts.html.erb +7 -0
  318. data/bin/rails +6 -0
  319. data/config/i18n-tasks.yml +16 -0
  320. data/config/locales/de.yml +257 -0
  321. data/config/locales/en.yml +253 -0
  322. data/config/locales/es.yml +257 -0
  323. data/config/locales/fr.yml +255 -0
  324. data/config/locales/it.yml +257 -0
  325. data/config/locales/pl.yml +257 -0
  326. data/config/locales/pt-BR.yml +258 -0
  327. data/config/locales/ru.yml +255 -0
  328. data/config/locales/zh-CN.yml +246 -0
  329. data/config/routes.rb +87 -0
  330. data/db/migrate/20160329231848_create_thredded.rb +257 -0
  331. data/db/seeds.rb +4 -0
  332. data/db/upgrade_migrations/20160611094616_upgrade_v0_5_to_v0_6.rb +28 -0
  333. data/db/upgrade_migrations/20160723012349_upgrade_v0_6_to_v0_7.rb +46 -0
  334. data/db/upgrade_migrations/20161019150201_upgrade_v0_7_to_v0_8.rb +34 -0
  335. data/db/upgrade_migrations/20161113161801_upgrade_v0_8_to_v0_9.rb +60 -0
  336. data/db/upgrade_migrations/20170125033319_upgrade_v0_9_to_v0_10.rb +36 -0
  337. data/db/upgrade_migrations/20170312131417_upgrade_thredded_v0_10_to_v0_11.rb +23 -0
  338. data/db/upgrade_migrations/20170420163138_upgrade_thredded_v0_11_to_v0_12.rb +28 -0
  339. data/db/upgrade_migrations/20170811090735_upgrade_thredded_v0_13_to_v_014.rb +21 -0
  340. data/lib/generators/thredded/install/USAGE +8 -0
  341. data/lib/generators/thredded/install/install_generator.rb +20 -0
  342. data/lib/generators/thredded/install/templates/initializer.rb +160 -0
  343. data/lib/tasks/thredded_tasks.rake +14 -0
  344. data/lib/thredded.rb +190 -0
  345. data/lib/thredded/base_migration.rb +14 -0
  346. data/lib/thredded/base_notifier.rb +28 -0
  347. data/lib/thredded/collection_to_strings_with_cache_renderer.rb +86 -0
  348. data/lib/thredded/content_formatter.rb +129 -0
  349. data/lib/thredded/database_seeder.rb +290 -0
  350. data/lib/thredded/db_tools.rb +103 -0
  351. data/lib/thredded/email_transformer.rb +22 -0
  352. data/lib/thredded/email_transformer/base.rb +47 -0
  353. data/lib/thredded/email_transformer/onebox.rb +21 -0
  354. data/lib/thredded/engine.rb +28 -0
  355. data/lib/thredded/errors.rb +68 -0
  356. data/lib/thredded/formatting_demo_content.rb +30 -0
  357. data/lib/thredded/html_pipeline/at_mention_filter.rb +78 -0
  358. data/lib/thredded/html_pipeline/autolink_filter.rb +15 -0
  359. data/lib/thredded/html_pipeline/kramdown_filter.rb +39 -0
  360. data/lib/thredded/html_pipeline/onebox_filter.rb +143 -0
  361. data/lib/thredded/html_pipeline/wrap_iframes_filter.rb +13 -0
  362. data/lib/thredded/version.rb +5 -0
  363. data/lib/thredded/view_hooks/config.rb +37 -0
  364. data/lib/thredded/view_hooks/renderer.rb +30 -0
  365. data/vendor/assets/javascripts/autosize.min.js +6 -0
  366. data/vendor/assets/javascripts/textcomplete.min.js +2 -0
  367. metadata +1035 -0
@@ -0,0 +1,3 @@
1
+ These icons are copied from the Discourse repo:
2
+
3
+ https://github.com/discourse/discourse/tree/a43ec88f46b1698cb2fbd08983b715011f882c45/app/assets/images/favicons
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 7 11" xmlns="http://www.w3.org/2000/svg"><path d="M1.96.19a.47.47 0 0 0-.685 0L.592.91a.52.52 0 0 0 0 .715L3.887 5.03.564 8.462a.52.52 0 0 0 0 .716l.684.717a.47.47 0 0 0 .684 0l4.35-4.508a.528.528 0 0 0 0-.717L1.958.19" fill="#B0AEBC" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 116 121" xmlns="http://www.w3.org/2000/svg"><path d="M57.071 113.714c0-.714-.357-1.071-1.071-1.071-2.634 0-4.9-.949-6.797-2.846-1.897-1.897-2.846-4.163-2.846-6.797 0-.714-.357-1.071-1.071-1.071-.715 0-1.072.357-1.072 1.071 0 3.259 1.15 6.038 3.449 8.337 2.3 2.3 5.078 3.449 8.337 3.449.714 0 1.071-.357 1.071-1.072zM12.473 94.43h87.054C87.652 81.036 81.714 62.464 81.714 38.714c0-2.277-.535-4.62-1.607-7.031-1.071-2.41-2.611-4.71-4.62-6.897-2.01-2.188-4.721-3.985-8.137-5.39-3.415-1.407-7.198-2.11-11.35-2.11-4.152 0-7.935.703-11.35 2.11-3.416 1.405-6.128 3.202-8.137 5.39-2.009 2.187-3.549 4.486-4.62 6.897-1.072 2.41-1.607 4.754-1.607 7.031 0 23.75-5.938 42.322-17.813 55.715zm99.241 0c0 2.321-.848 4.33-2.544 6.026-1.697 1.697-3.706 2.545-6.027 2.545h-30c0 4.732-1.674 8.772-5.022 12.12-3.349 3.349-7.389 5.023-12.121 5.023s-8.772-1.674-12.12-5.022c-3.349-3.349-5.023-7.389-5.023-12.121h-30c-2.321 0-4.33-.848-6.027-2.545C1.134 98.76.286 96.75.286 94.43a58.82 58.82 0 0 0 6.093-5.893c1.83-2.054 3.728-4.721 5.692-8.003 1.965-3.28 3.628-6.819 4.99-10.613 1.36-3.795 2.477-8.393 3.347-13.795.871-5.402 1.306-11.205 1.306-17.41 0-6.786 2.612-13.092 7.835-18.918 5.223-5.826 12.076-9.364 20.558-10.614a6.67 6.67 0 0 1-.536-2.612c0-1.785.625-3.303 1.875-4.553S54.214.143 56 .143c1.786 0 3.304.625 4.554 1.875 1.25 1.25 1.875 2.768 1.875 4.553a6.67 6.67 0 0 1-.536 2.612c8.482 1.25 15.335 4.788 20.558 10.614 5.223 5.826 7.835 12.132 7.835 18.917 0 6.206.435 12.01 1.306 17.411.87 5.402 1.986 10 3.348 13.795 1.361 3.794 3.024 7.332 4.989 10.613 1.964 3.282 3.861 5.95 5.692 8.003a58.82 58.82 0 0 0 6.093 5.893z" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M10 16c0-1.104.896-2 2-2s2 .896 2 2c0 .738-.404 1.376-1 1.723v2.277h-2v-2.277c-.596-.347-1-.985-1-1.723zm11-6v14h-18v-14h3v-4c0-3.313 2.687-6 6-6s6 2.687 6 6v4h3zm-13 0h8v-4c0-2.206-1.795-4-4-4s-4 1.794-4 4v4zm11 2h-14v10h14v-10z"/></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 6c0-.55-.45-1-1-1H5.82l.66-3.18.02-.23c0-.31-.13-.59-.33-.8L5.38 0 .44 4.94C.17 5.21 0 5.59 0 6v6.5c0 .83.67 1.5 1.5 1.5h6.75c.62 0 1.15-.38 1.38-.91l2.26-5.29c.07-.17.11-.36.11-.55V6zm10.5 4h-6.75c-.62 0-1.15.38-1.38.91l-2.26 5.29c-.07.17-.11.36-.11.55V18c0 .55.45 1 1 1h5.18l-.66 3.18-.02.24c0 .31.13.59.33.8l.79.78 4.94-4.94c.27-.27.44-.65.44-1.06v-6.5c0-.83-.67-1.5-1.5-1.5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V8l8 5 8-5v10zm-8-7L4 6h16l-8 5z"/></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.488.488 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/></svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="408px" height="408px" viewBox="0 0 408 408">
2
+ <path d="M204,102c28.05,0,51-22.95,51-51S232.05,0,204,0s-51,22.95-51,51S175.95,102,204,102z M204,153c-28.05,0-51,22.95-51,51 s22.95,51,51,51s51-22.95,51-51S232.05,153,204,153z M204,306c-28.05,0-51,22.95-51,51s22.95,51,51,51s51-22.95,51-51 S232.05,306,204,306z"/>
3
+ </svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 138 121" xmlns="http://www.w3.org/2000/svg"><path d="M69.643 113.714c0-.714-.357-1.071-1.072-1.071-2.634 0-4.9-.949-6.796-2.846-1.898-1.897-2.846-4.163-2.846-6.797 0-.714-.358-1.071-1.072-1.071-.714 0-1.071.357-1.071 1.071 0 3.259 1.15 6.038 3.448 8.337 2.3 2.3 5.078 3.449 8.337 3.449.715 0 1.072-.357 1.072-1.072zm-35.96-31.808l58.728-50.893c-1.875-3.928-4.833-7.198-8.873-9.81-4.04-2.611-9.029-3.917-14.967-3.917-4.151 0-7.935.703-11.35 2.11-3.415 1.405-6.127 3.202-8.136 5.39-2.01 2.187-3.55 4.486-4.62 6.897-1.072 2.41-1.608 4.754-1.608 7.031 0 17.143-3.058 31.54-9.174 43.192zm90.603 12.523c0 2.321-.848 4.33-2.545 6.026-1.696 1.697-3.705 2.545-6.027 2.545h-30c0 4.732-1.674 8.772-5.022 12.12-3.348 3.349-7.388 5.023-12.12 5.023-4.733 0-8.762-1.663-12.088-4.989-3.326-3.326-5.01-7.355-5.055-12.087l9.977-8.638h50.692c-7.41-8.349-12.477-18.594-15.2-30.737l7.432-6.496c2.724 15.893 9.375 28.304 19.956 37.233zm5.759-93.215l5.625 6.429c.357.446.524.97.502 1.574a2.052 2.052 0 0 1-.703 1.506L10.112 119.34c-.447.357-.971.514-1.574.469-.603-.045-1.083-.29-1.44-.737l-5.625-6.428c-.357-.447-.524-.971-.502-1.574.022-.602.257-1.082.703-1.44L14.13 98.85c-.848-1.43-1.272-2.903-1.272-4.42a58.82 58.82 0 0 0 6.094-5.893c1.83-2.054 3.728-4.721 5.692-8.003 1.964-3.28 3.627-6.819 4.989-10.613 1.361-3.795 2.477-8.393 3.348-13.795.87-5.402 1.306-11.205 1.306-17.41 0-6.786 2.611-13.092 7.835-18.918 5.223-5.826 12.075-9.364 20.558-10.614a6.67 6.67 0 0 1-.536-2.612c0-1.785.625-3.303 1.875-4.553S66.786.143 68.57.143c1.786 0 3.304.625 4.554 1.875C74.375 3.268 75 4.786 75 6.57a6.67 6.67 0 0 1-.536 2.612c5.536.804 10.424 2.645 14.665 5.525 4.242 2.88 7.545 6.395 9.911 10.546L127.031.946c.447-.357.971-.513 1.574-.468.603.044 1.082.29 1.44.736z" fill-rule="evenodd"/></svg>
@@ -0,0 +1,2 @@
1
+ //= require thredded/dependencies
2
+ //= require thredded/thredded
@@ -0,0 +1,32 @@
1
+ //= require thredded/core/on_page_load
2
+
3
+ (() => {
4
+ const Thredded = window.Thredded;
5
+
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
+ };
20
+
21
+ const initCurrentlyOnline = (node) => {
22
+ node.addEventListener('mouseenter', handleMouseEnter);
23
+ node.addEventListener('mouseleave', handleMouseLeave);
24
+ node.addEventListener('touchstart', handleTouchStart);
25
+ };
26
+
27
+ Thredded.onPageLoad(() => {
28
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
29
+ initCurrentlyOnline(node);
30
+ });
31
+ });
32
+ })();
@@ -0,0 +1,9 @@
1
+ (() => {
2
+ const COMPONENT_SELECTOR = '[data-thredded-flash-message]';
3
+
4
+ document.addEventListener('turbolinks:before-cache', () => {
5
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
6
+ node.parentNode.removeChild(node);
7
+ });
8
+ });
9
+ })();
@@ -0,0 +1,40 @@
1
+ //= require thredded/components/user_textcomplete
2
+
3
+ const ThreddedMentionAutocompletion = {
4
+ MATCH_RE: /(^@|\s@)"?([\w., \-()]+[\w.,\-()])$/,
5
+ // the last letter has to not be a space so it doesn't match after replacement
6
+ DROPDOWN_MAX_COUNT: 6,
7
+
8
+ init(form, textarea) {
9
+ const editor = new Textcomplete.editors.Textarea(textarea);
10
+ const textcomplete = new Textcomplete(editor, {
11
+ dropdown: {
12
+ className: Thredded.UserTextcomplete.DROPDOWN_CLASS_NAME,
13
+ maxCount: ThreddedMentionAutocompletion.DROPDOWN_MAX_COUNT
14
+ },
15
+ });
16
+ textcomplete.on('rendered', function() {
17
+ if (textcomplete.dropdown.items.length) {
18
+ textcomplete.dropdown.items[0].activate();
19
+ }
20
+ });
21
+ textcomplete.register([{
22
+ match: ThreddedMentionAutocompletion.MATCH_RE,
23
+ search: Thredded.UserTextcomplete.searchFn({
24
+ url: form.getAttribute('data-autocomplete-url'),
25
+ autocompleteMinLength: parseInt(form.getAttribute('data-autocomplete-min-length'), 10)
26
+ }),
27
+ template: Thredded.UserTextcomplete.formatUser,
28
+ replace ({name, match}) {
29
+ let prefix = match[1];
30
+ if (/[., ()]/.test(name)) {
31
+ return `${prefix}"${name}" `
32
+ } else {
33
+ return `${prefix}${name} `
34
+ }
35
+ }
36
+ }]);
37
+ }
38
+ };
39
+
40
+ window.ThreddedMentionAutocompletion = ThreddedMentionAutocompletion;
@@ -0,0 +1,37 @@
1
+ //= require thredded/dependencies/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
+
12
+ const COMPONENT_SELECTOR = '[data-thredded-post-form]';
13
+ const CONTENT_TEXTAREA_SELECTOR = 'textarea[name$="[content]"]';
14
+
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
+ };
21
+
22
+ const destroyPostForm = (form) => {
23
+ autosize.destroy(form.querySelector(CONTENT_TEXTAREA_SELECTOR));
24
+ };
25
+
26
+ Thredded.onPageLoad(() => {
27
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
28
+ initPostForm(node);
29
+ });
30
+ });
31
+
32
+ document.addEventListener('turbolinks:before-cache', () => {
33
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
34
+ destroyPostForm(node);
35
+ });
36
+ });
37
+ })();
@@ -0,0 +1,56 @@
1
+ //= require thredded/core/serialize_form
2
+
3
+ (() => {
4
+ const PREVIEW_AREA_SELECTOR = '[data-thredded-preview-area]';
5
+ const PREVIEW_AREA_POST_SELECTOR = '[data-thredded-preview-area-post]';
6
+
7
+ class ThreddedPreviewArea {
8
+
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);
15
+ this.previewUrl = this.preview.getAttribute('data-thredded-preview-url');
16
+
17
+ let prevValue = null;
18
+ const onChange = Thredded.debounce(() => {
19
+ if (prevValue !== textarea.value) {
20
+ this.updatePreview();
21
+ prevValue = textarea.value;
22
+ }
23
+ }, 200, false);
24
+
25
+ textarea.addEventListener('input', onChange, false);
26
+
27
+ this.requestId = 0;
28
+ }
29
+
30
+ updatePreview() {
31
+ this.requestId++;
32
+ const 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 &&
41
+ // Ignore older responses received out-of-order
42
+ requestId === this.requestId) {
43
+ this.onPreviewResponse(request.responseText);
44
+ }
45
+ };
46
+ request.send(Thredded.serializeForm(this.form));
47
+ }
48
+
49
+ onPreviewResponse(data) {
50
+ this.preview.style.display = 'block';
51
+ this.previewPost.innerHTML = data;
52
+ }
53
+ }
54
+
55
+ window.ThreddedPreviewArea = ThreddedPreviewArea;
56
+ })();
@@ -0,0 +1,49 @@
1
+ //= require thredded/core/on_page_load
2
+
3
+ (function() {
4
+ const Thredded = window.Thredded;
5
+
6
+ Thredded.onPageLoad(() => {
7
+ Array.prototype.forEach.call(document.querySelectorAll('[data-thredded-quote-post]'), (el) => {
8
+ el.addEventListener('click', onClick);
9
+ });
10
+ });
11
+
12
+ function onClick(evt) {
13
+ // Handle only left clicks with no modifier keys
14
+ if (evt.button !== 0 || evt.ctrlKey || evt.altKey || evt.metaKey || evt.shiftKey) return;
15
+ evt.preventDefault();
16
+ const target = document.getElementById('post_content');
17
+ target.scrollIntoView();
18
+ target.value = '...';
19
+ fetchReply(evt.target.getAttribute('data-thredded-quote-post'), (replyText) => {
20
+ if (!target.ownerDocument.body.contains(target)) return;
21
+ target.focus();
22
+ target.value = replyText;
23
+
24
+ const autosizeUpdateEvent = document.createEvent('Event');
25
+ autosizeUpdateEvent.initEvent('autosize:update', true, false);
26
+ target.dispatchEvent(autosizeUpdateEvent);
27
+ // Scroll into view again as the size might have changed.
28
+ target.scrollIntoView();
29
+ }, (errorMessage) => {
30
+ target.value = errorMessage;
31
+ });
32
+ }
33
+
34
+ function fetchReply(url, onSuccess, onError) {
35
+ const request = new XMLHttpRequest();
36
+ request.open('GET', url, /* async */ true);
37
+ request.onload = () => {
38
+ if (request.status >= 200 && request.status < 400) {
39
+ onSuccess(request.responseText);
40
+ } else {
41
+ onError(`Error (${request.status}): ${request.statusText} ${request.responseText}`);
42
+ }
43
+ };
44
+ request.onerror = () => {
45
+ onError('Network Error');
46
+ };
47
+ request.send();
48
+ }
49
+ })();
@@ -0,0 +1,25 @@
1
+ (() => {
2
+ const COMPONENT_SELECTOR = '#thredded--container [data-time-ago]';
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').replace('-', '_'));
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
+ })();
@@ -0,0 +1,94 @@
1
+ //= require thredded/dependencies/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
+
12
+ const COMPONENT_SELECTOR = '[data-thredded-topic-form]';
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;
18
+
19
+ const initTopicForm = (form) => {
20
+ const textarea = form.querySelector(CONTENT_TEXTAREA_SELECTOR);
21
+ if (!textarea) {
22
+ return;
23
+ }
24
+ autosize(textarea);
25
+ new ThreddedPreviewArea(form, textarea);
26
+ ThreddedMentionAutocompletion.init(form, textarea);
27
+
28
+ if (!form.classList.contains(COMPACT_CLASS)) {
29
+ return;
30
+ }
31
+
32
+ const title = form.querySelector(TITLE_SELECTOR);
33
+ title.addEventListener('focus', () => {
34
+ toggleExpanded(form, true);
35
+ });
36
+
37
+ [title, textarea].forEach((node) => {
38
+ // Un-expand on Escape key.
39
+ node.addEventListener('keydown', (evt) => {
40
+ if (evt.keyCode === ESCAPE_KEY_CODE) {
41
+ evt.target.blur();
42
+ toggleExpanded(form, false);
43
+ }
44
+ });
45
+
46
+ // Un-expand on blur if the new focus element is outside of the same form and
47
+ // all the form inputs are empty.
48
+ node.addEventListener('blur', () => {
49
+ // This listener will be fired right after the blur event has finished.
50
+ const listener = (evt) => {
51
+ if (!form.contains(evt.target) && !title.value && !textarea.value) {
52
+ toggleExpanded(form, false);
53
+ }
54
+ document.body.removeEventListener('touchend', listener);
55
+ document.body.removeEventListener('mouseup', listener);
56
+ };
57
+ document.body.addEventListener('mouseup', listener);
58
+ document.body.addEventListener('touchend', listener);
59
+ })
60
+ });
61
+ };
62
+
63
+ const toggleExpanded = (form, expand) => {
64
+ if (expand) {
65
+ form.classList.remove(COMPACT_CLASS);
66
+ form.classList.add(EXPANDED_CLASS);
67
+ } else {
68
+ form.classList.remove(EXPANDED_CLASS);
69
+ form.classList.add(COMPACT_CLASS);
70
+ }
71
+ };
72
+
73
+ const destroyTopicForm = (form) => {
74
+ const textarea = form.querySelector(CONTENT_TEXTAREA_SELECTOR);
75
+ if (!textarea) {
76
+ return;
77
+ }
78
+ autosize.destroy(textarea);
79
+ };
80
+
81
+ Thredded.onPageLoad(() => {
82
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
83
+ initTopicForm(node);
84
+ });
85
+ });
86
+
87
+ document.addEventListener('turbolinks:before-cache', () => {
88
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
89
+ destroyTopicForm(node);
90
+ });
91
+ });
92
+ })();
93
+
94
+
@@ -0,0 +1,46 @@
1
+ //= require thredded/core/on_page_load
2
+ //= require thredded/core/serialize_form
3
+
4
+ // Makes topics in the list appear read as soon as the topic link is clicked,
5
+ // iff the topic link leads to the last page of the topic.
6
+ (() => {
7
+ const Thredded = window.Thredded;
8
+
9
+ const COMPONENT_SELECTOR = '[data-thredded-topics]';
10
+ const TOPIC_UNREAD_CLASS = 'thredded--topic-unread';
11
+ const TOPIC_READ_CLASS = 'thredded--topic-read';
12
+ const POSTS_COUNT_SELECTOR = '.thredded--topics--posts-count';
13
+ const POSTS_PER_PAGE = 50;
14
+
15
+ function pageNumber(url) {
16
+ const match = url.match(/\/page-(\d)$/);
17
+ return match ? +match[1] : 1;
18
+ }
19
+
20
+ function totalPages(numPosts) {
21
+ return Math.ceil(numPosts / POSTS_PER_PAGE);
22
+ }
23
+
24
+ function getTopicNode(node) {
25
+ do {
26
+ node = node.parentNode;
27
+ } while (node && node.tagName !== 'ARTICLE');
28
+ return node;
29
+ }
30
+
31
+ function initTopicsList(topicsList) {
32
+ topicsList.addEventListener('click', (evt) => {
33
+ const link = evt.target;
34
+ if (link.tagName !== 'A' || link.parentNode.tagName !== 'H1') return;
35
+ const topic = getTopicNode(link);
36
+ if (pageNumber(link.href) === totalPages(+topic.querySelector(POSTS_COUNT_SELECTOR).textContent)) {
37
+ topic.classList.add(TOPIC_READ_CLASS);
38
+ topic.classList.remove(TOPIC_UNREAD_CLASS);
39
+ }
40
+ });
41
+ }
42
+
43
+ Thredded.onPageLoad(() => {
44
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), initTopicsList);
45
+ });
46
+ })();