comfortable_mexican_sofa 2.0.12 → 2.0.13

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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +5 -2
  4. data/Gemfile +5 -0
  5. data/Rakefile +2 -0
  6. data/app/assets/javascripts/comfy/admin/cms/application.js +40 -0
  7. data/app/assets/javascripts/comfy/admin/cms/base.js +46 -0
  8. data/app/assets/javascripts/comfy/admin/cms/categories.js +17 -0
  9. data/app/assets/javascripts/comfy/admin/cms/codemirror.js +31 -0
  10. data/app/assets/javascripts/comfy/admin/cms/custom.js +1 -0
  11. data/app/assets/javascripts/comfy/admin/cms/diff.js +10 -0
  12. data/app/assets/javascripts/comfy/admin/cms/file_link.js +67 -0
  13. data/app/assets/javascripts/comfy/admin/cms/file_upload.js +194 -0
  14. data/app/assets/javascripts/comfy/admin/cms/files_modal.js +40 -0
  15. data/app/assets/javascripts/comfy/admin/cms/page_fragments.js +22 -0
  16. data/app/assets/javascripts/comfy/admin/cms/slugify.js +34 -0
  17. data/app/assets/javascripts/comfy/admin/cms/sortable_list.js +40 -0
  18. data/app/assets/javascripts/comfy/admin/cms/timepicker.js +30 -0
  19. data/app/assets/javascripts/comfy/admin/cms/wysiwyg.js +65 -0
  20. data/app/assets/javascripts/comfy/vendor/redactor.js +12 -6
  21. data/app/assets/stylesheets/comfy/admin/cms/base.sass +32 -35
  22. data/app/controllers/application_controller.rb +2 -0
  23. data/app/controllers/comfy/admin/base_controller.rb +3 -1
  24. data/app/controllers/comfy/admin/cms/base_controller.rb +9 -1
  25. data/app/controllers/comfy/admin/cms/categories_controller.rb +2 -0
  26. data/app/controllers/comfy/admin/cms/files_controller.rb +3 -1
  27. data/app/controllers/comfy/admin/cms/layouts_controller.rb +2 -0
  28. data/app/controllers/comfy/admin/cms/pages_controller.rb +4 -1
  29. data/app/controllers/comfy/admin/cms/revisions/base_controller.rb +2 -0
  30. data/app/controllers/comfy/admin/cms/revisions/layout_controller.rb +2 -0
  31. data/app/controllers/comfy/admin/cms/revisions/page_controller.rb +2 -0
  32. data/app/controllers/comfy/admin/cms/revisions/snippet_controller.rb +2 -0
  33. data/app/controllers/comfy/admin/cms/revisions/translation_controller.rb +2 -0
  34. data/app/controllers/comfy/admin/cms/sites_controller.rb +2 -0
  35. data/app/controllers/comfy/admin/cms/snippets_controller.rb +2 -0
  36. data/app/controllers/comfy/admin/cms/translations_controller.rb +8 -0
  37. data/app/controllers/comfy/cms/assets_controller.rb +2 -0
  38. data/app/controllers/comfy/cms/base_controller.rb +4 -2
  39. data/app/controllers/comfy/cms/content_controller.rb +3 -1
  40. data/app/controllers/concerns/comfy/paginate.rb +2 -0
  41. data/app/controllers/concerns/comfy/reorder_action.rb +2 -0
  42. data/app/helpers/comfy/admin/cms_helper.rb +19 -0
  43. data/app/helpers/comfy/cms_helper.rb +4 -2
  44. data/app/models/comfy/cms/categorization.rb +2 -0
  45. data/app/models/comfy/cms/category.rb +2 -0
  46. data/app/models/comfy/cms/file.rb +2 -0
  47. data/app/models/comfy/cms/fragment.rb +3 -1
  48. data/app/models/comfy/cms/layout.rb +3 -1
  49. data/app/models/comfy/cms/page.rb +3 -1
  50. data/app/models/comfy/cms/revision.rb +2 -0
  51. data/app/models/comfy/cms/site.rb +5 -1
  52. data/app/models/comfy/cms/snippet.rb +2 -0
  53. data/app/models/comfy/cms/translation.rb +2 -0
  54. data/app/models/concerns/comfy/cms/with_categories.rb +2 -0
  55. data/app/models/concerns/comfy/cms/with_fragments.rb +2 -0
  56. data/app/views/comfy/admin/cms/categories/_index.html.haml +3 -3
  57. data/app/views/comfy/admin/cms/categories/create.js.erb +1 -1
  58. data/app/views/comfy/admin/cms/categories/destroy.js.erb +8 -3
  59. data/app/views/comfy/admin/cms/files/_file.html.haml +9 -10
  60. data/app/views/comfy/admin/cms/files/_modal.html.haml +1 -2
  61. data/app/views/comfy/admin/cms/files/destroy.js.erb +4 -0
  62. data/app/views/comfy/admin/cms/files/index.html.haml +10 -10
  63. data/app/views/comfy/admin/cms/fragments/_form_fragment_attachments.html.haml +15 -6
  64. data/app/views/comfy/admin/cms/fragments/_form_fragments.html.haml +17 -21
  65. data/app/views/comfy/admin/cms/pages/_form.html.haml +3 -2
  66. data/app/views/comfy/admin/cms/pages/toggle_branch.js.erb +3 -1
  67. data/app/views/comfy/admin/cms/translations/_form.html.haml +3 -2
  68. data/app/views/layouts/comfy/admin/cms.html.haml +0 -1
  69. data/app/views/layouts/comfy/admin/cms/_body.html.haml +1 -0
  70. data/app/views/layouts/comfy/admin/cms/_left.html.haml +1 -1
  71. data/comfortable_mexican_sofa.gemspec +3 -2
  72. data/config.ru +2 -0
  73. data/config/application.rb +2 -0
  74. data/config/boot.rb +2 -0
  75. data/config/cms_routes.rb +2 -0
  76. data/config/environment.rb +2 -0
  77. data/config/environments/development.rb +2 -0
  78. data/config/environments/test.rb +2 -0
  79. data/config/initializers/comfortable_mexican_sofa.rb +2 -0
  80. data/lib/comfortable_mexican_sofa.rb +2 -0
  81. data/lib/comfortable_mexican_sofa/access_control/admin_authentication.rb +2 -0
  82. data/lib/comfortable_mexican_sofa/access_control/admin_authorization.rb +2 -0
  83. data/lib/comfortable_mexican_sofa/access_control/public_authentication.rb +2 -0
  84. data/lib/comfortable_mexican_sofa/access_control/public_authorization.rb +2 -0
  85. data/lib/comfortable_mexican_sofa/configuration.rb +3 -0
  86. data/lib/comfortable_mexican_sofa/content.rb +3 -0
  87. data/lib/comfortable_mexican_sofa/content/block.rb +2 -0
  88. data/lib/comfortable_mexican_sofa/content/params_parser.rb +59 -41
  89. data/lib/comfortable_mexican_sofa/content/renderer.rb +18 -4
  90. data/lib/comfortable_mexican_sofa/content/tag.rb +20 -5
  91. data/lib/comfortable_mexican_sofa/content/tags/asset.rb +2 -0
  92. data/lib/comfortable_mexican_sofa/content/tags/checkbox.rb +10 -3
  93. data/lib/comfortable_mexican_sofa/content/tags/date.rb +3 -1
  94. data/lib/comfortable_mexican_sofa/content/tags/datetime.rb +3 -1
  95. data/lib/comfortable_mexican_sofa/content/tags/file.rb +21 -27
  96. data/lib/comfortable_mexican_sofa/content/tags/file_link.rb +23 -24
  97. data/lib/comfortable_mexican_sofa/content/tags/files.rb +11 -9
  98. data/lib/comfortable_mexican_sofa/content/tags/fragment.rb +11 -1
  99. data/lib/comfortable_mexican_sofa/content/tags/helper.rb +3 -1
  100. data/lib/comfortable_mexican_sofa/content/tags/markdown.rb +3 -1
  101. data/lib/comfortable_mexican_sofa/content/tags/mixins/file_content.rb +38 -0
  102. data/lib/comfortable_mexican_sofa/content/tags/number.rb +3 -1
  103. data/lib/comfortable_mexican_sofa/content/tags/page_file_link.rb +82 -0
  104. data/lib/comfortable_mexican_sofa/content/tags/partial.rb +3 -1
  105. data/lib/comfortable_mexican_sofa/content/tags/snippet.rb +2 -0
  106. data/lib/comfortable_mexican_sofa/content/tags/template.rb +3 -1
  107. data/lib/comfortable_mexican_sofa/content/tags/text.rb +3 -1
  108. data/lib/comfortable_mexican_sofa/content/tags/textarea.rb +3 -1
  109. data/lib/comfortable_mexican_sofa/content/tags/wysiwyg.rb +3 -1
  110. data/lib/comfortable_mexican_sofa/engine.rb +2 -1
  111. data/lib/comfortable_mexican_sofa/error.rb +2 -0
  112. data/lib/comfortable_mexican_sofa/extensions/acts_as_tree.rb +2 -0
  113. data/lib/comfortable_mexican_sofa/extensions/has_revisions.rb +2 -0
  114. data/lib/comfortable_mexican_sofa/form_builder.rb +24 -17
  115. data/lib/comfortable_mexican_sofa/render_methods.rb +2 -0
  116. data/lib/comfortable_mexican_sofa/routes/cms.rb +2 -0
  117. data/lib/comfortable_mexican_sofa/routes/cms_admin.rb +2 -0
  118. data/lib/comfortable_mexican_sofa/routing.rb +2 -0
  119. data/lib/comfortable_mexican_sofa/seeds.rb +3 -1
  120. data/lib/comfortable_mexican_sofa/seeds/file/exporter.rb +2 -0
  121. data/lib/comfortable_mexican_sofa/seeds/file/importer.rb +2 -0
  122. data/lib/comfortable_mexican_sofa/seeds/layout/exporter.rb +2 -0
  123. data/lib/comfortable_mexican_sofa/seeds/layout/importer.rb +2 -0
  124. data/lib/comfortable_mexican_sofa/seeds/page/exporter.rb +2 -0
  125. data/lib/comfortable_mexican_sofa/seeds/page/importer.rb +2 -0
  126. data/lib/comfortable_mexican_sofa/seeds/snippet/exporter.rb +2 -0
  127. data/lib/comfortable_mexican_sofa/seeds/snippet/importer.rb +2 -0
  128. data/lib/comfortable_mexican_sofa/version.rb +3 -1
  129. data/lib/comfortable_mexican_sofa/view_hooks.rb +2 -0
  130. data/lib/generators/comfy/cms/assets_generator.rb +2 -0
  131. data/lib/generators/comfy/cms/cms_generator.rb +6 -4
  132. data/lib/generators/comfy/cms/controllers_generator.rb +2 -0
  133. data/lib/generators/comfy/cms/models_generator.rb +2 -0
  134. data/lib/generators/comfy/cms/views_generator.rb +2 -0
  135. data/lib/generators/comfy/scaffold/scaffold_generator.rb +6 -2
  136. data/lib/tasks/cms_seeds.rake +2 -0
  137. metadata +20 -26
  138. data/app/assets/javascripts/comfy/admin/cms/application.js.coffee +0 -30
  139. data/app/assets/javascripts/comfy/admin/cms/base.js.coffee +0 -224
  140. data/app/assets/javascripts/comfy/admin/cms/custom.js.coffee +0 -1
  141. data/app/assets/javascripts/comfy/admin/cms/files.js.coffee +0 -29
  142. data/app/assets/javascripts/comfy/admin/cms/uploader.js.coffee +0 -140
  143. data/app/views/comfy/admin/cms/files/create.js.erb +0 -1
  144. data/app/views/comfy/admin/cms/files/destroy.js.coffee +0 -2
  145. data/app/views/comfy/admin/cms/pages/form_fragments.js.erb +0 -1
  146. data/app/views/comfy/admin/cms/translations/form_fragments.js.erb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab2c176d43e3783a684bd7245eb28f889af9ad8d1bd38665e4847e0fdefdbf08
4
- data.tar.gz: d214102f49f6965873628fac4e9aec36a486c537d6548d6f0e76a5ffe027e208
3
+ metadata.gz: b4189f8584e5fbfcc3083d72b892dee0d17407e1893674eed30927e6e5c6d336
4
+ data.tar.gz: '02964679fbddf7a9f1c46dc35482d11a6a3736409a32daa4a137d0600a921d3d'
5
5
  SHA512:
6
- metadata.gz: 301bd06b86f9d7e6080edeed4aa0c39add0e83a8ffdebc799ef03507fedd09dbf2ac4d28adb46a45656991e68b361f5f300e54dba24d06a5996f6f63a6cbe4cb
7
- data.tar.gz: 6768a98127dfde9834afd18e5085644db61d76e4e6629ddef4a50a480d588ada9c79cb56bf9958dc11f2693289737d3f227eadf2a266ec3a3dcb727aadd1ce41
6
+ metadata.gz: 47387e45ccebcad1bc3429c6b64633dfd9cc8f0810f7c28745dc578f391404e57a16fb8bd1ecd221acdd6701710aded9ee318eb2b110f7d7374e63063fc472b0
7
+ data.tar.gz: d16d2d2aaeb92e761c6b3559816f33e1339dd239f8691d6b8194bc5cf60d67ec0dbe34aae18ee2cc2a4f1bf24b15edc4105812bcd4427d04b048a850bc339819
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.2
2
+ TargetRubyVersion: 2.3
3
3
  Exclude:
4
4
  - bin/*
5
5
  - db/schema.rb
@@ -1,9 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.9
4
3
  - 2.3.6
5
4
  - 2.4.3
6
5
  - 2.5.0
6
+ - ruby-head
7
7
  gemfile:
8
8
  - test/gemfiles/5.2.gemfile
9
9
  branches:
@@ -19,7 +19,7 @@ before_install:
19
19
  - gem update --system
20
20
  - gem update bundler
21
21
  before_script:
22
- - wget http://chromedriver.storage.googleapis.com/2.34/chromedriver_linux64.zip
22
+ - wget http://chromedriver.storage.googleapis.com/2.36/chromedriver_linux64.zip
23
23
  - unzip chromedriver_linux64.zip
24
24
  - sudo apt-get install libnss3
25
25
  - sudo apt-get --only-upgrade install google-chrome-stable
@@ -33,3 +33,6 @@ script:
33
33
  - bundle exec rake test
34
34
  - SKIP_COV=true bundle exec rake test:system
35
35
  - bundle exec rubocop
36
+ matrix:
37
+ allow_failures:
38
+ - rvm: ruby-head
data/Gemfile CHANGED
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "http://rubygems.org"
2
4
 
3
5
  gemspec
4
6
 
5
7
  group :development, :test do
8
+ gem "autoprefixer-rails", "~> 8.1.0"
6
9
  gem "byebug", "~> 10.0.0", platforms: %i[mri mingw x64_mingw]
7
10
  gem "capybara", "~> 2.17.0"
8
11
  gem "kaminari", "~> 1.1.1"
@@ -19,6 +22,8 @@ end
19
22
 
20
23
  group :test do
21
24
  gem "coveralls", "~> 0.8.21", require: false
25
+ gem "diffy", "~> 3.2.0"
26
+ gem "equivalent-xml", "~> 0.6.0"
22
27
  gem "mocha", "~> 1.3.0", require: false
23
28
  gem "rails-controller-testing", "~> 1.0.2"
24
29
  end
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "config/application"
2
4
 
3
5
  Rails.application.load_tasks
@@ -0,0 +1,40 @@
1
+ //= require rails-ujs
2
+ //= require jquery3
3
+
4
+ //= require comfy/vendor/codemirror
5
+ //= require comfy/vendor/codemirror/mode/css/css
6
+ //= require comfy/vendor/codemirror/mode/htmlmixed/htmlmixed
7
+ //= require comfy/vendor/codemirror/mode/javascript/javascript
8
+ //= require comfy/vendor/codemirror/mode/markdown/markdown
9
+ //= require comfy/vendor/codemirror/mode/xml/xml
10
+ //= require comfy/vendor/codemirror/addon/edit/closetag
11
+ //= require comfy/vendor/sortable.min
12
+ //= require comfy/vendor/popper.min
13
+ //= require comfy/vendor/bootstrap.min
14
+ //= require comfy/vendor/diff/diff_match_patch.min
15
+ //= require comfy/vendor/diff/pretty_text_diff
16
+ //= require comfy/vendor/fontawesome
17
+ //= require comfy/vendor/redactor
18
+ //= require comfy/vendor/redactor/filemanager
19
+ //= require comfy/vendor/redactor/imagemanager
20
+ //= require comfy/vendor/redactor/definedlinks
21
+ //= require comfy/vendor/redactor/table
22
+ //= require comfy/vendor/redactor/video
23
+ //= require_directory ../../vendor/redactor/i18n/
24
+ //= require comfy/vendor/flatpickr.min
25
+ //= require_directory ../../vendor/flatpickr/i18n/
26
+
27
+ //= require comfy/admin/cms/base
28
+ //= require comfy/admin/cms/categories
29
+ //= require comfy/admin/cms/codemirror
30
+ //= require comfy/admin/cms/diff
31
+ //= require comfy/admin/cms/file_link
32
+ //= require comfy/admin/cms/file_upload
33
+ //= require comfy/admin/cms/files_modal
34
+ //= require comfy/admin/cms/page_fragments
35
+ //= require comfy/admin/cms/sortable_list
36
+ //= require comfy/admin/cms/slugify
37
+ //= require comfy/admin/cms/timepicker
38
+ //= require comfy/admin/cms/wysiwyg
39
+
40
+ //= require comfy/admin/cms/custom
@@ -0,0 +1,46 @@
1
+ (() => {
2
+ if (!window.CMS) window.CMS = {};
3
+ const CMS = window.CMS;
4
+
5
+ // TODO(glebm): Use the battle-tested universal onPageLoad code and enable Turbolinks+async in the demo app.
6
+ // See: https://gist.github.com/glebm/2496daf445877055447a6fac46509d9a
7
+ const isTurbolinks = 'Turbolinks' in window && window.Turbolinks.supported;
8
+ if (isTurbolinks) {
9
+ document.addEventListener('turbolinks:load', () => {
10
+ window.CMS.init();
11
+ });
12
+ document.addEventListener('turbolinks:before-cache', () => {
13
+ window.CMS.dispose();
14
+ });
15
+ } else {
16
+ document.addEventListener('DOMContentLoaded', () => {
17
+ window.CMS.init();
18
+ });
19
+ }
20
+
21
+ CMS.init = () => {
22
+ CMS.current_path = window.location.pathname;
23
+ CMS.slugify();
24
+ CMS.codemirror.init();
25
+ CMS.wysiwyg.init();
26
+ CMS.sortableList.init();
27
+ CMS.timepicker.init();
28
+ CMS.pageFragments();
29
+ CMS.categories();
30
+ CMS.files.init();
31
+ CMS.fileLinks();
32
+ CMS.fileUpload.init();
33
+ CMS.diff();
34
+ };
35
+
36
+ CMS.dispose = () => {
37
+ CMS.codemirror.dispose();
38
+ CMS.wysiwyg.dispose();
39
+ CMS.files.dispose();
40
+ CMS.fileUpload.dispose();
41
+ CMS.sortableList.dispose();
42
+ CMS.timepicker.dispose();
43
+ };
44
+
45
+ CMS.getLocale = () => document.querySelector('meta[name="cms-locale"]').content;
46
+ })();
@@ -0,0 +1,17 @@
1
+ (() => {
2
+ window.CMS.categories = (root = document) => {
3
+ const widget = root.querySelector('.categories-widget');
4
+ if (widget === null) return;
5
+ const readSection = widget.querySelector('.read');
6
+ const editSection = widget.querySelector('.editable');
7
+ widget.querySelector('.read button.toggle-cat-edit').addEventListener('click', () => {
8
+ readSection.style.display = 'none';
9
+ editSection.style.display = 'block';
10
+ });
11
+ widget.querySelector('.editable button.toggle-cat-edit').addEventListener('click', () => {
12
+ editSection.style.display = 'none';
13
+ readSection.style.display = 'block';
14
+ });
15
+ };
16
+ })();
17
+
@@ -0,0 +1,31 @@
1
+ (() => {
2
+ const codeMirrorInstances = [];
3
+ window.CMS.codemirror = {
4
+ init(root = document) {
5
+ for (const textarea of root.querySelectorAll('textarea[data-cms-cm-mode]')) {
6
+ const codemirror = CodeMirror.fromTextArea(textarea, {
7
+ mode: textarea.dataset.cmsCmMode,
8
+ tabSize: 2,
9
+ lineWrapping: true,
10
+ autoCloseTags: true,
11
+ lineNumbers: true,
12
+ viewportMargin: Infinity
13
+ });
14
+ codeMirrorInstances.push(codemirror);
15
+ }
16
+
17
+ const tabsRoot = root.id === 'form-fragments' ? root : root.querySelector('#form-fragments');
18
+ jQuery(tabsRoot).find('a[data-toggle="tab"]').on('shown.bs.tab', () => {
19
+ for (const codemirror of codeMirrorInstances) {
20
+ codemirror.refresh();
21
+ }
22
+ });
23
+ },
24
+ dispose() {
25
+ for (const codemirror of codeMirrorInstances) {
26
+ codemirror.toTextArea();
27
+ }
28
+ codeMirrorInstances.length = 0;
29
+ }
30
+ }
31
+ })();
@@ -0,0 +1 @@
1
+ // Custom JS for the admin area
@@ -0,0 +1,10 @@
1
+ (() => {
2
+ window.CMS.diff = () => {
3
+ jQuery('.revision').prettyTextDiff({
4
+ cleanup: true,
5
+ originalContainer: '.original',
6
+ changedContainer: '.current',
7
+ diffContainer: '.diff .content',
8
+ });
9
+ }
10
+ })();
@@ -0,0 +1,67 @@
1
+ (() => {
2
+ const isFirefox = /\bFirefox\//.test(navigator.userAgent);
3
+
4
+ class FileLink {
5
+ constructor(link) {
6
+ this.link = link;
7
+ this.isImage = !!link.dataset.cmsFileThumbUrl;
8
+
9
+ link.addEventListener('dragstart', (evt) => {
10
+ evt.dataTransfer.setData('text/plain', this.link.dataset.cmsFileLinkTag);
11
+ });
12
+
13
+ if (this.isImage) {
14
+ new bootstrap.Popover(link, {
15
+ container: link.parentElement,
16
+ trigger: 'hover',
17
+ placement: 'top',
18
+ content: this.buildFileThumbnail(),
19
+ html: true
20
+ });
21
+
22
+ link.addEventListener('dragstart', (evt) => {
23
+ evt.dataTransfer.setDragImage(this.buildFileThumbnail(), 4, 2);
24
+ this.getPopover().hide();
25
+ });
26
+
27
+ this.workAroundFirefoxPopoverGlitch();
28
+ }
29
+ }
30
+
31
+ buildFileThumbnail() {
32
+ const img = new Image();
33
+ img.src = this.link.dataset.cmsFileThumbUrl;
34
+ return img;
35
+ }
36
+
37
+ // To work around a Firefox bug causing the popover to re-appear after the drop:
38
+ // https://github.com/comfy/comfortable-mexican-sofa/pull/799#issuecomment-369124161
39
+ //
40
+ // Possibly related to:
41
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=505521
42
+ workAroundFirefoxPopoverGlitch() {
43
+ if (!isFirefox) return;
44
+ this.link.addEventListener('dragstart', () => {
45
+ this.getPopover().disable();
46
+ });
47
+ this.link.addEventListener('dragend', () => {
48
+ setTimeout(() => {
49
+ const popover = this.getPopover();
50
+ popover.enable();
51
+ popover.hide();
52
+ }, 300);
53
+ });
54
+ }
55
+
56
+ // We can't keep a reference to the Popover object, because Bootstrap re-creates it internally.
57
+ getPopover() {
58
+ return jQuery(this.link).data(bootstrap.Popover.DATA_KEY);
59
+ }
60
+ }
61
+
62
+ window.CMS.fileLinks = (root = document) => {
63
+ for (const link of root.querySelectorAll('[data-cms-file-link-tag]')) {
64
+ new FileLink(link);
65
+ }
66
+ };
67
+ })();
@@ -0,0 +1,194 @@
1
+ //= require comfy/vendor/moxie.min
2
+ //= require comfy/vendor/plupload.dev
3
+
4
+ (() => {
5
+ const DROP_TARGET_ACTIVE_CLASS = 'cms-uploader-drag-drop-target-active';
6
+
7
+ class FileUpload {
8
+ constructor(container, settings) {
9
+ if (!container.id) container.id = plupload.guid();
10
+ settings = Object.assign(FileUpload.defaultUploaderSettings(container.id), settings);
11
+ this.ui = {
12
+ container,
13
+ list: container.querySelector('.cms-uploader-filelist'),
14
+ dropElement: container.querySelector(`#${settings.drop_element}`),
15
+ };
16
+ this.cleanupFns = [];
17
+ this.uploader = new plupload.Uploader(settings);
18
+ this.uploader.bind('PostInit', () => this.onUploaderPostInit());
19
+ this.uploader.bind('Error', (_uploader, error) => this.onUploaderError(error));
20
+ this.uploader.bind('FilesAdded', (_uploader, files) => this.onUploaderFilesAdded(files));
21
+ this.uploader.bind('UploadProgress', (_uploader, file) => this.onUploaderUploadProgress(file));
22
+ this.uploader.bind('FileUploaded', (_uploader, file, info) => this.onUploaderFileUploaded(file, info));
23
+ this.uploader.bind('FilesRemoved', (_uploader, files) => this.onUploaderFilesRemoved(files));
24
+ this.uploader.init();
25
+ if (settings.setup) {
26
+ settings.setup(this.uploader);
27
+ }
28
+ }
29
+
30
+ destroy() {
31
+ this.uploader.destroy();
32
+ for (const cleanupFn of this.cleanupFns) {
33
+ cleanupFn();
34
+ }
35
+ }
36
+
37
+ addCleanup(cleanupFn) {
38
+ this.cleanupFns.push(cleanupFn);
39
+ }
40
+
41
+ onUploaderPostInit() {
42
+ // Show drag and drop info and attach events only if drag and drop is enabled and supported.
43
+ if (!this.uploader.settings.dragdrop || !this.uploader.features.dragdrop) {
44
+ this.ui.container.querySelector('.cms-uploader-drag-drop-info').style.display = 'none';
45
+ return;
46
+ }
47
+ // When dragging over the document add a class to the drop target that puts it on top of every element and remove
48
+ // that class when dropping or leaving the drop target. Otherwise the dragleave event would fire whenever we drag
49
+ // over a child element inside the drop target such as text nodes.
50
+ const onDragEnter = (e) => {
51
+ // Only react to drag'n'drops that contain a file. See:
52
+ // https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/types
53
+ if (e.dataTransfer.types.includes('Files')) {
54
+ this.ui.dropElement.classList.add(DROP_TARGET_ACTIVE_CLASS);
55
+ }
56
+ };
57
+ document.addEventListener('dragenter', onDragEnter);
58
+ this.addCleanup(() => {
59
+ document.removeEventListener('dragenter', onDragEnter);
60
+ });
61
+ for (const eventName of ['drop', 'dragleave']) {
62
+ this.ui.dropElement.addEventListener(eventName, () => {
63
+ this.ui.dropElement.classList.remove(DROP_TARGET_ACTIVE_CLASS);
64
+ });
65
+ }
66
+ }
67
+
68
+ onUploaderError(error) {
69
+ if (error.code === plupload.INIT_ERROR) {
70
+ window.alert('Error: Initialisation error. Reload to try again.');
71
+ return;
72
+ }
73
+ const file = error.file;
74
+ if (!file) return;
75
+ // Get error message from the server response. Not all runtimes support this.
76
+ let message = error.response;
77
+ // If no error message is in the server response get standard plupload error messages.
78
+ // This will have descriptive error message for something like file size or file format errors but for
79
+ // server errors it will only display a general error message.
80
+ if (!message) {
81
+ message = error.message;
82
+ if (error.details) message += ` (${error.details})`;
83
+ }
84
+ file.status = plupload.FAILED;
85
+ file.error_message = message;
86
+ this.updateFileStatus(file);
87
+ }
88
+
89
+ onUploaderFilesAdded(files) {
90
+ for (const file of files) {
91
+ this.addFile(file);
92
+ }
93
+ // Auto start upload when files are added.
94
+ this.uploader.start();
95
+ }
96
+
97
+ onUploaderUploadProgress(file) {
98
+ this.updateFileStatus(file);
99
+ }
100
+
101
+ onUploaderFileUploaded(file, info) {
102
+ // Replace the dummy file entry in the file list with the the entry from the server response.
103
+ const template = document.createElement('template');
104
+ template.innerHTML = info.response;
105
+ const newListItem = template.content.firstElementChild;
106
+ this.ui.list.replaceChild(newListItem, this.fileListItem(file));
107
+ window.CMS.fileLinks(newListItem);
108
+ }
109
+
110
+ onUploaderFilesRemoved(files) {
111
+ for (const file of files) {
112
+ this.removeFile(file);
113
+ }
114
+ }
115
+
116
+ addFile(file) {
117
+ this.ui.list.insertAdjacentHTML('afterbegin', FileUpload.buildListItemHTML(file));
118
+ this.fileListItem(file).querySelector('.cms-uploader-file-delete').addEventListener('click', (evt) => {
119
+ evt.preventDefault();
120
+ this.uploader.removeFile(file);
121
+ });
122
+ this.updateFileStatus(file);
123
+ }
124
+
125
+ removeFile(file) {
126
+ this.fileListItem(file).remove();
127
+ }
128
+
129
+ updateFileStatus(file) {
130
+ const progressBar = this.fileListItem(file).querySelector('.progress-bar');
131
+ switch (file.status) {
132
+ case plupload.UPLOADING:
133
+ progressBar.style.width = `${file.percent}%`;
134
+ break;
135
+ case plupload.FAILED:
136
+ progressBar.style.width = '100%';
137
+ progressBar.classList.add('progress-bar-danger');
138
+ progressBar.querySelector('span').innerHTML = file.error_message;
139
+ break;
140
+ }
141
+ }
142
+
143
+ fileListItem({id}) {
144
+ return this.ui.container.querySelector(`#${id}`);
145
+ }
146
+
147
+ static buildListItemHTML({id, name}) {
148
+ return `<li id='${id}' class='row temp'>
149
+ <div class='col-md-9 d-flex align-items-center'>
150
+ <div class='progress'>
151
+ <div class='progress-bar progress-bar-striped progress-bar-animated'>
152
+ <span>${name}</span>
153
+ </div>
154
+ </div>
155
+ </div>
156
+ <div class='col-md-3 d-flex align-items-center justify-content-md-end'>
157
+ <a class='btn btn-sm btn-danger float-right cms-uploader-file-delete' href='#'>Delete</a>
158
+ </div>
159
+ </li>`;
160
+ }
161
+
162
+ static defaultUploaderSettings(id) {
163
+ return {
164
+ runtimes: 'html5,browserplus,silverlight,flash,gears',
165
+ dragdrop: true,
166
+ drop_element: `${id}-drag-drop-target`,
167
+ browse_button: `${id}-browse`,
168
+ container: id,
169
+ file_data_name: 'file[file]',
170
+ };
171
+ }
172
+ }
173
+
174
+ const uploaders = [];
175
+ window.CMS.fileUpload = {
176
+ init(root = document) {
177
+ const el = root.querySelector('#cms-uploader');
178
+ if (el === null) return;
179
+ uploaders.push(new FileUpload(el, {
180
+ url: el.dataset.cmsUploaderUrl,
181
+ multipart_params: {
182
+ [el.dataset.cmsUploaderTokenName]: el.dataset.cmsUploaderTokenValue,
183
+ [el.dataset.cmsUploaderSessionName]: el.dataset.cmsUploaderSessionValue
184
+ }
185
+ }));
186
+ },
187
+ dispose() {
188
+ for (const uploader of uploader) {
189
+ uploader.dispose();
190
+ }
191
+ uploaders.length = 0;
192
+ }
193
+ };
194
+ })();