alchemy_cms 7.3.4 → 7.4.0

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 (134) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -1
  3. data/Gemfile +10 -3
  4. data/README.md +2 -2
  5. data/Rakefile +2 -0
  6. data/alchemy_cms.gemspec +1 -4
  7. data/app/assets/builds/alchemy/admin.css +9 -1
  8. data/app/assets/builds/alchemy/admin.css.map +1 -1
  9. data/app/assets/builds/alchemy/custom-properties.css +1 -1
  10. data/app/assets/builds/alchemy/custom-properties.css.map +1 -1
  11. data/app/assets/builds/alchemy/preview.min.js +1 -0
  12. data/app/assets/builds/alchemy/welcome.css +1 -1
  13. data/app/assets/builds/alchemy/welcome.css.map +1 -1
  14. data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -1
  15. data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css.map +1 -1
  16. data/app/assets/config/alchemy_manifest.js +0 -4
  17. data/app/assets/javascripts/alchemy/admin.js +8 -6
  18. data/app/assets/stylesheets/alchemy/admin/elements.scss +43 -7
  19. data/app/assets/stylesheets/alchemy/admin/forms.scss +4 -0
  20. data/app/assets/stylesheets/alchemy/admin/image_library.scss +40 -26
  21. data/app/assets/stylesheets/alchemy/admin/navigation.scss +9 -1
  22. data/app/assets/stylesheets/alchemy/admin/preview_window.scss +22 -17
  23. data/app/assets/stylesheets/alchemy/admin.scss +1 -1
  24. data/app/assets/stylesheets/alchemy/custom-properties.css +2 -1
  25. data/app/components/alchemy/ingredients/link_view.rb +7 -1
  26. data/app/components/alchemy/ingredients/picture_view.rb +5 -2
  27. data/app/components/alchemy/ingredients/text_view.rb +4 -1
  28. data/app/components/concerns/alchemy/ingredients/link_target.rb +18 -0
  29. data/app/controllers/alchemy/admin/base_controller.rb +34 -5
  30. data/app/controllers/alchemy/admin/elements_controller.rb +2 -2
  31. data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
  32. data/app/controllers/alchemy/admin/layoutpages_controller.rb +1 -0
  33. data/app/controllers/alchemy/admin/pages_controller.rb +6 -6
  34. data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
  35. data/app/controllers/alchemy/elements_controller.rb +3 -0
  36. data/app/helpers/alchemy/admin/form_helper.rb +1 -1
  37. data/app/helpers/alchemy/admin/navigation_helper.rb +22 -1
  38. data/app/javascript/alchemy_admin/components/action.js +2 -1
  39. data/app/javascript/alchemy_admin/components/dialog_link.js +3 -18
  40. data/app/javascript/alchemy_admin/components/element_editor.js +9 -0
  41. data/app/javascript/alchemy_admin/components/elements_window.js +34 -0
  42. data/app/javascript/alchemy_admin/components/elements_window_handle.js +65 -0
  43. data/app/javascript/alchemy_admin/components/icon.js +2 -2
  44. data/app/javascript/alchemy_admin/components/index.js +1 -0
  45. data/app/javascript/alchemy_admin/components/preview_window.js +5 -5
  46. data/app/javascript/alchemy_admin/components/uploader/file_upload.js +1 -1
  47. data/app/javascript/alchemy_admin/confirm_dialog.js +9 -11
  48. data/app/javascript/alchemy_admin/dialog.js +329 -0
  49. data/app/javascript/alchemy_admin/hotkeys.js +3 -2
  50. data/app/javascript/alchemy_admin/image_cropper.js +57 -40
  51. data/app/javascript/alchemy_admin/image_overlay.js +73 -0
  52. data/app/javascript/alchemy_admin/initializer.js +51 -2
  53. data/app/javascript/alchemy_admin/link_dialog.js +2 -1
  54. data/app/javascript/alchemy_admin/node_tree.js +3 -1
  55. data/app/javascript/alchemy_admin/page_sorter.js +1 -1
  56. data/app/javascript/alchemy_admin/picture_selector.js +2 -1
  57. data/app/javascript/alchemy_admin/shoelace_theme.js +2 -2
  58. data/app/javascript/alchemy_admin/templates/compiled.js +1 -0
  59. data/app/javascript/alchemy_admin.js +10 -6
  60. data/app/javascript/preview.js +117 -0
  61. data/app/models/alchemy/image_cropper_settings.rb +3 -4
  62. data/app/views/alchemy/_menubar.html.erb +1 -1
  63. data/app/views/alchemy/_preview_mode_code.html.erb +1 -1
  64. data/app/views/alchemy/admin/crop.html.erb +19 -16
  65. data/app/views/alchemy/admin/dashboard/info.html.erb +1 -1
  66. data/app/views/alchemy/admin/elements/_add_nested_element_form.html.erb +9 -8
  67. data/app/views/alchemy/admin/elements/_clipboard_button.html.erb +14 -0
  68. data/app/views/alchemy/admin/elements/_element.html.erb +2 -0
  69. data/app/views/alchemy/admin/elements/_form.html.erb +15 -13
  70. data/app/views/alchemy/admin/elements/create.turbo_stream.erb +34 -0
  71. data/app/views/alchemy/admin/elements/index.html.erb +3 -15
  72. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +1 -1
  73. data/app/views/alchemy/admin/layoutpages/edit.html.erb +7 -5
  74. data/app/views/alchemy/admin/nodes/_form.html.erb +1 -1
  75. data/app/views/alchemy/admin/pages/_current_page.html.erb +1 -1
  76. data/app/views/alchemy/admin/pages/_form.html.erb +43 -40
  77. data/app/views/alchemy/admin/pages/_locked_page.html.erb +1 -1
  78. data/app/views/alchemy/admin/pages/_page_layout_filter.html.erb +1 -1
  79. data/app/views/alchemy/admin/pages/_sitemap.html.erb +1 -1
  80. data/app/views/alchemy/admin/pages/_table.html.erb +2 -2
  81. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  82. data/app/views/alchemy/admin/pages/update.turbo_stream.erb +39 -0
  83. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +3 -4
  84. data/app/views/alchemy/admin/pictures/_picture_description_field.html.erb +7 -5
  85. data/app/views/alchemy/admin/pictures/index.html.erb +13 -9
  86. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +1 -1
  87. data/app/views/layouts/alchemy/admin.html.erb +8 -4
  88. data/bun.lockb +0 -0
  89. data/bundles/tinymce.js +2 -0
  90. data/config/alchemy/config.yml +3 -3
  91. data/config/alchemy/modules.yml +7 -6
  92. data/config/importmap.rb +4 -0
  93. data/config/routes.rb +1 -1
  94. data/lib/alchemy/engine.rb +6 -0
  95. data/lib/alchemy/modules.rb +0 -27
  96. data/lib/alchemy/resource.rb +14 -4
  97. data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +10 -10
  98. data/lib/alchemy/tinymce.rb +2 -1
  99. data/lib/alchemy/upgrader/seven_point_four.rb +26 -0
  100. data/lib/alchemy/version.rb +1 -1
  101. data/lib/alchemy.rb +14 -0
  102. data/lib/alchemy_cms.rb +0 -2
  103. data/lib/generators/alchemy/ingredient/ingredient_generator.rb +5 -0
  104. data/lib/generators/alchemy/ingredient/templates/view.html.erb +1 -1
  105. data/lib/generators/alchemy/ingredient/templates/view_component.rb.tt +10 -0
  106. data/lib/generators/alchemy/install/install_generator.rb +0 -1
  107. data/lib/generators/alchemy/install/templates/elements.yml.tt +1 -1
  108. data/lib/tasks/alchemy/upgrade.rake +19 -20
  109. data/rollup.config.mjs +44 -1
  110. data/vendor/javascript/cropperjs.min.js +10 -0
  111. data/vendor/javascript/handlebars.min.js +29 -0
  112. data/vendor/javascript/jquery.min.js +2 -0
  113. data/vendor/javascript/select2.min.js +23 -0
  114. data/vendor/javascript/tinymce.min.js +1 -1
  115. metadata +40 -94
  116. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +0 -271
  117. data/app/assets/javascripts/alchemy/alchemy.image_overlay.coffee +0 -54
  118. data/app/assets/javascripts/alchemy/alchemy.preview.js.coffee +0 -97
  119. data/app/assets/javascripts/alchemy/preview.js +0 -1
  120. data/app/assets/javascripts/alchemy/templates/index.js +0 -2
  121. data/app/javascript/alchemy_admin/gui.js +0 -12
  122. data/app/views/alchemy/admin/elements/create.js.erb +0 -35
  123. data/app/views/alchemy/admin/pages/update.js.erb +0 -43
  124. data/lib/alchemy/upgrader/seven_point_zero.rb +0 -36
  125. data/vendor/assets/images/Jcrop.gif +0 -0
  126. data/vendor/assets/javascripts/jquery_plugins/jquery.Jcrop.min.js +0 -7
  127. data/vendor/assets/javascripts/jquery_plugins/select2.js +0 -3729
  128. data/vendor/assets/stylesheets/jquery.Jcrop.min.css +0 -2
  129. data/vendor/assets/stylesheets/tinymce/skins/content/default/content.min.css +0 -1
  130. /data/app/{assets/javascripts/alchemy → javascript/alchemy_admin}/templates/node_folder.hbs +0 -0
  131. /data/app/{assets/javascripts/alchemy → javascript/alchemy_admin}/templates/page_folder.hbs +0 -0
  132. /data/app/{assets/javascripts/tinymce/icons/remixicons/icons.js → javascript/tinymce/icons/remixicons/index.js} +0 -0
  133. /data/app/{assets/javascripts/tinymce/plugins/alchemy_link/plugin.min.js → javascript/tinymce/plugins/alchemy_link/index.js} +0 -0
  134. /data/vendor/assets/{fonts → images}/remixicon.symbol.svg +0 -0
@@ -1 +1 @@
1
- {"version":3,"sourceRoot":"","sources":["../../stylesheets/alchemy/custom-properties.css","../../stylesheets/alchemy/_fonts.scss","../../stylesheets/alchemy/welcome.scss","../../stylesheets/alchemy/_variables.scss"],"names":[],"mappings":"AAAA,MAEE,iBACA,iBACA,iBACA,kBACA,kBACA,kBACA,kBACA,kBACA,kBAGA,wBACA,yBACA,qBACA,uBACA,uBACA,wBAGA,8FAEA,6GAIA,wBACA,yBACA,wBAEA,0BACA,wBAGA,4BAGA,+CACA,0CACA,2CACA,yCAEA,+CACA,2CACA,4CACA,0CAEA,2CACA,4CACA,0CAEA,6CACA,0CACA,+CAEA,4CACA,uCACA,wCACA,sCAEA,4CACA,uCACA,wCACA,sCACA,2CAEA,mCAEA,0CACA,gDACA,2CAGF,eACE,0CAEA,6CACA,wCAEA,iDACA,4CACA,0CAEA,yDACA,sEACA,iEACA,+DAEA,0EACA,uEACA,wEACA,6EACA,iFC7FF,WACE,wBACA,ylxFACA,gBACA,kBAGF,WACE,wBACA,i7wFACA,gBACA,kBCPF,KACE,iBCgImB,uBD/HnB,yBACA,YCiBoB,0FDhBpB,mBACA,eACA,iBAGF,GACE,iBACA,eACA,gBACA,mBACA,iBAGF,WACE,kBACA,QACA,SACA,YACA,iBACA,8CAGF,GACE,gBACA,mBACA,UACA,kBACA,WACA,4BAEA,MACE,yBACA,mBACA,sBACA,qBACA,iBAIJ,EACE,yBACA,0BACA,+BAEA,QACE,yBACA","file":"welcome.css"}
1
+ {"version":3,"sourceRoot":"","sources":["../../stylesheets/alchemy/custom-properties.css","../../stylesheets/alchemy/_fonts.scss","../../stylesheets/alchemy/welcome.scss","../../stylesheets/alchemy/_variables.scss"],"names":[],"mappings":"AAAA,MAEE,iBACA,iBACA,iBACA,kBACA,kBACA,kBACA,kBACA,kBACA,kBAGA,wBACA,yBACA,qBACA,uBACA,uBACA,wBAGA,8FAEA,6GAIA,wBACA,yBACA,wBAEA,0BACA,wBAGA,4BACA,0BAGA,+CACA,0CACA,2CACA,yCAEA,+CACA,2CACA,4CACA,0CAEA,2CACA,4CACA,0CAEA,6CACA,0CACA,+CAEA,4CACA,uCACA,wCACA,sCAEA,4CACA,uCACA,wCACA,sCACA,2CAEA,mCAEA,0CACA,gDACA,2CAGF,eACE,0CAEA,6CACA,wCAEA,iDACA,4CACA,0CAEA,yDACA,sEACA,iEACA,+DAEA,0EACA,uEACA,wEACA,6EACA,iFC9FF,WACE,wBACA,ylxFACA,gBACA,kBAGF,WACE,wBACA,i7wFACA,gBACA,kBCPF,KACE,iBCgImB,uBD/HnB,yBACA,YCiBoB,0FDhBpB,mBACA,eACA,iBAGF,GACE,iBACA,eACA,gBACA,mBACA,iBAGF,WACE,kBACA,QACA,SACA,YACA,iBACA,8CAGF,GACE,gBACA,mBACA,UACA,kBACA,WACA,4BAEA,MACE,yBACA,mBACA,sBACA,qBACA,iBAIJ,EACE,yBACA,0BACA,+BAEA,QACE,yBACA","file":"welcome.css"}
@@ -1 +1 @@
1
- :root{--spacing-0: 2px;--spacing-1: 4px;--spacing-2: 8px;--spacing-3: 12px;--spacing-4: 16px;--spacing-5: 20px;--spacing-6: 24px;--spacing-7: 28px;--spacing-8: 32px;--icon-size-xs: 0.75rem;--icon-size-sm: 0.875rem;--icon-size-1x: 1rem;--icon-size-md: 1.3rem;--icon-size-xl: 1.5rem;--icon-size-xxl: 1.6rem;--font-mono: Menlo, Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;--font-sans: "Open Sans", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, Tahoma, sans-serif;--font-size_small: 10px;--font-size_medium: 12px;--font-size_large: 16px;--font-weigth_normal: 500;--font-weigth_bold: 700;--border-radius_medium: 3px;--color-blue_very_light: hsl(203deg, 32%, 97%);--color-blue_light: hsl(203deg, 32%, 85%);--color-blue_medium: hsl(212deg, 52%, 36%);--color-blue_dark: hsl(212deg, 52%, 26%);--color-green_very_light: hsl(88deg, 47%, 88%);--color-green_light: hsl(127deg, 25%, 69%);--color-green_medium: hsl(127deg, 25%, 48%);--color-green_dark: hsl(128deg, 32%, 26%);--color-yellow_light: hsl(60deg, 81%, 92%);--color-yellow_medium: hsl(56deg, 68%, 85%);--color-yellow_dark: hsl(56deg, 53%, 29%);--color-orange_medium: hsl(42deg, 100%, 74%);--color-orange_dark: hsl(28deg, 77%, 68%);--color-orange_very_dark: hsl(28deg, 77%, 48%);--color-red_very_light: hsl(0deg, 47%, 88%);--color-red_light: hsl(0deg, 25%, 69%);--color-red_medium: hsl(0deg, 51%, 42%);--color-red_dark: hsl(0deg, 51%, 25%);--color-grey_very_light: hsl(0deg, 0%, 97%);--color-grey_light: hsl(0deg, 0%, 94%);--color-grey_medium: hsl(0deg, 0%, 78%);--color-grey_dark: hsl(0deg, 0%, 40%);--color-grey_very_dark: hsl(0deg, 0%, 20%);--color-white: hsl(0deg, 0%, 100%);--color-text: hsla(224deg, 23%, 26%, 0.8);--color-text_muted: hsla(224deg, 23%, 26%, 0.5);--color-icon: hsla(224deg, 23%, 26%, 0.75)}.alchemy-light{--outline-color: var(--color-orange_dark);--font-color_failed: var(--color-red_medium);--font-color_default: var(--color-text);--tabs_indicator-color: var(--color-orange_dark);--tabs_track-color: var(--color-grey_light);--sl-input-label-color: var(--color-text);--file-upload_background-color: hsla(0deg, 0%, 80%, 0.8);--file-upload_single-upload-background-color: var(--color-grey_light);--file-upload_progress-track-color: var(--color-blue_very_light);--file-upload_progress-indicator-color: var(--color-blue_dark);--file-upload_progress-indicator-color-canceled: hsla(0deg, 0%, 60%, 0.8);--file-upload_progress-indicator-color-failed: var(--color-red_medium);--file-upload_progress-indicator-color-invalid: var(--color-red_medium);--file-upload_progress-indicator-color-successful: var(--color-green_medium);--file-upload_progress-indicator-color-upload-finished: var( --color-blue_dark )}html{font-size:13px}body{font-family:"Open Sans","Lucida Grande","Lucida Sans Unicode","Lucida Sans",Verdana,Tahoma,sans-serif;line-height:1.4;margin:1em;color:var(--color-text)}a{color:var(--color-blue_dark)}table{border-collapse:collapse}table td,table th{border:1px solid rgba(176,176,176,.5);padding:.4rem}figure{display:table;margin:1rem auto}figure figcaption{color:var(--color-grey_dark);display:block;margin-top:.25rem;text-align:center}hr{border-color:rgba(176,176,176,.5);border-style:solid;border-width:1px 0 0 0}code{background-color:var(--color-grey_very_light);border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid rgba(176,176,176,.5);margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid rgba(176,176,176,.5);margin-right:1.5rem;padding-right:1rem}/*# sourceMappingURL=content.min.css.map */
1
+ :root{--spacing-0: 2px;--spacing-1: 4px;--spacing-2: 8px;--spacing-3: 12px;--spacing-4: 16px;--spacing-5: 20px;--spacing-6: 24px;--spacing-7: 28px;--spacing-8: 32px;--icon-size-xs: 0.75rem;--icon-size-sm: 0.875rem;--icon-size-1x: 1rem;--icon-size-md: 1.3rem;--icon-size-xl: 1.5rem;--icon-size-xxl: 1.6rem;--font-mono: Menlo, Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;--font-sans: "Open Sans", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, Tahoma, sans-serif;--font-size_small: 10px;--font-size_medium: 12px;--font-size_large: 16px;--font-weigth_normal: 500;--font-weigth_bold: 700;--border-radius_medium: 3px;--border-width_small: 1px;--color-blue_very_light: hsl(203deg, 32%, 97%);--color-blue_light: hsl(203deg, 32%, 85%);--color-blue_medium: hsl(212deg, 52%, 36%);--color-blue_dark: hsl(212deg, 52%, 26%);--color-green_very_light: hsl(88deg, 47%, 88%);--color-green_light: hsl(127deg, 25%, 69%);--color-green_medium: hsl(127deg, 25%, 48%);--color-green_dark: hsl(128deg, 32%, 26%);--color-yellow_light: hsl(60deg, 81%, 92%);--color-yellow_medium: hsl(56deg, 68%, 85%);--color-yellow_dark: hsl(56deg, 53%, 29%);--color-orange_medium: hsl(42deg, 100%, 74%);--color-orange_dark: hsl(28deg, 77%, 68%);--color-orange_very_dark: hsl(28deg, 77%, 48%);--color-red_very_light: hsl(0deg, 47%, 88%);--color-red_light: hsl(0deg, 25%, 69%);--color-red_medium: hsl(0deg, 51%, 42%);--color-red_dark: hsl(0deg, 51%, 25%);--color-grey_very_light: hsl(0deg, 0%, 97%);--color-grey_light: hsl(0deg, 0%, 94%);--color-grey_medium: hsl(0deg, 0%, 78%);--color-grey_dark: hsl(0deg, 0%, 40%);--color-grey_very_dark: hsl(0deg, 0%, 20%);--color-white: hsl(0deg, 0%, 100%);--color-text: hsla(224deg, 23%, 26%, 0.8);--color-text_muted: hsla(224deg, 23%, 26%, 0.5);--color-icon: hsla(224deg, 23%, 26%, 0.75)}.alchemy-light{--outline-color: var(--color-orange_dark);--font-color_failed: var(--color-red_medium);--font-color_default: var(--color-text);--tabs_indicator-color: var(--color-orange_dark);--tabs_track-color: var(--color-grey_light);--sl-input-label-color: var(--color-text);--file-upload_background-color: hsla(0deg, 0%, 80%, 0.8);--file-upload_single-upload-background-color: var(--color-grey_light);--file-upload_progress-track-color: var(--color-blue_very_light);--file-upload_progress-indicator-color: var(--color-blue_dark);--file-upload_progress-indicator-color-canceled: hsla(0deg, 0%, 60%, 0.8);--file-upload_progress-indicator-color-failed: var(--color-red_medium);--file-upload_progress-indicator-color-invalid: var(--color-red_medium);--file-upload_progress-indicator-color-successful: var(--color-green_medium);--file-upload_progress-indicator-color-upload-finished: var( --color-blue_dark )}html{font-size:13px}body{font-family:"Open Sans","Lucida Grande","Lucida Sans Unicode","Lucida Sans",Verdana,Tahoma,sans-serif;line-height:1.4;margin:1em;color:var(--color-text)}a{color:var(--color-blue_dark)}table{border-collapse:collapse}table td,table th{border:1px solid rgba(176,176,176,.5);padding:.4rem}figure{display:table;margin:1rem auto}figure figcaption{color:var(--color-grey_dark);display:block;margin-top:.25rem;text-align:center}hr{border-color:rgba(176,176,176,.5);border-style:solid;border-width:1px 0 0 0}code{background-color:var(--color-grey_very_light);border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid rgba(176,176,176,.5);margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid rgba(176,176,176,.5);margin-right:1.5rem;padding-right:1rem}/*# sourceMappingURL=content.min.css.map */
@@ -1 +1 @@
1
- {"version":3,"sourceRoot":"","sources":["../../../../../stylesheets/alchemy/custom-properties.css","../../../../../stylesheets/tinymce/skins/content/alchemy/content.scss","../../../../../stylesheets/alchemy/_variables.scss"],"names":[],"mappings":"AAAA,MAEE,iBACA,iBACA,iBACA,kBACA,kBACA,kBACA,kBACA,kBACA,kBAGA,wBACA,yBACA,qBACA,uBACA,uBACA,wBAGA,8FAEA,6GAIA,wBACA,yBACA,wBAEA,0BACA,wBAGA,4BAGA,+CACA,0CACA,2CACA,yCAEA,+CACA,2CACA,4CACA,0CAEA,2CACA,4CACA,0CAEA,6CACA,0CACA,+CAEA,4CACA,uCACA,wCACA,sCAEA,4CACA,uCACA,wCACA,sCACA,2CAEA,mCAEA,0CACA,gDACA,2CAGF,eACE,0CAEA,6CACA,wCAEA,iDACA,4CACA,0CAEA,yDACA,sEACA,iEACA,+DAEA,0EACA,uEACA,wEACA,6EACA,iFCnFF,KACE,eAGF,KACE,YCSoB,0FDRpB,gBACA,WACA,wBAGF,EACE,6BAGF,MACE,yBAGF,kBAEE,sCACA,cAGF,OACE,cACA,iBAGF,kBACE,6BACA,cACA,kBACA,kBAGF,GACE,aCFqB,qBDGrB,mBACA,uBAGF,KACE,8CACA,cCHsB,IDItB,oBAGF,4CACE,2CACA,mBACA,kBAGF,sCACE,4CACA,oBACA","file":"content.min.css"}
1
+ {"version":3,"sourceRoot":"","sources":["../../../../../stylesheets/alchemy/custom-properties.css","../../../../../stylesheets/tinymce/skins/content/alchemy/content.scss","../../../../../stylesheets/alchemy/_variables.scss"],"names":[],"mappings":"AAAA,MAEE,iBACA,iBACA,iBACA,kBACA,kBACA,kBACA,kBACA,kBACA,kBAGA,wBACA,yBACA,qBACA,uBACA,uBACA,wBAGA,8FAEA,6GAIA,wBACA,yBACA,wBAEA,0BACA,wBAGA,4BACA,0BAGA,+CACA,0CACA,2CACA,yCAEA,+CACA,2CACA,4CACA,0CAEA,2CACA,4CACA,0CAEA,6CACA,0CACA,+CAEA,4CACA,uCACA,wCACA,sCAEA,4CACA,uCACA,wCACA,sCACA,2CAEA,mCAEA,0CACA,gDACA,2CAGF,eACE,0CAEA,6CACA,wCAEA,iDACA,4CACA,0CAEA,yDACA,sEACA,iEACA,+DAEA,0EACA,uEACA,wEACA,6EACA,iFCpFF,KACE,eAGF,KACE,YCSoB,0FDRpB,gBACA,WACA,wBAGF,EACE,6BAGF,MACE,yBAGF,kBAEE,sCACA,cAGF,OACE,cACA,iBAGF,kBACE,6BACA,cACA,kBACA,kBAGF,GACE,aCFqB,qBDGrB,mBACA,uBAGF,KACE,8CACA,cCHsB,IDItB,oBAGF,4CACE,2CACA,mBACA,kBAGF,sCACE,4CACA,oBACA","file":"content.min.css"}
@@ -1,11 +1,7 @@
1
1
  //= link alchemy/admin/all.js
2
- //= link alchemy/preview.js
3
- //= link tinymce/plugins/alchemy_link/plugin.min.js
4
- //= link tinymce/icons/remixicons/icons.js
5
2
  //= link_tree ../builds/alchemy/
6
3
  //= link_tree ../builds/tinymce/
7
4
  //= link_tree ../images/alchemy/
8
- //= link_tree ../../../vendor/assets/fonts/
9
5
  //= link_tree ../../../vendor/assets/images/
10
6
  //= link_tree ../../javascript .js
11
7
  //= link_tree ../../../vendor/javascript .js
@@ -1,8 +1,10 @@
1
1
  // Alchemy CMS Sprockets Manifest
2
2
  // ------------------------------
3
- //= require jquery3
4
- //= require_tree ../../../../vendor/assets/javascripts/jquery_plugins/
5
- //= require handlebars
6
- //= require alchemy/templates
7
- //= require alchemy/alchemy.dialog
8
- //= require alchemy/alchemy.image_overlay
3
+ //
4
+ // This manifest file is deprecated and will be removed in Alchemy 8.0.
5
+ //
6
+
7
+ console.warn(
8
+ "The 'alchemy/admin' Sprockets manifest is deprecated and will be removed in 8.0. " +
9
+ "Please remove 'require alchemy/admin' from your 'vendor/assets/javascripts/alchemy/admin/all.js' file."
10
+ )
@@ -1,10 +1,20 @@
1
+ :root {
2
+ --elements-window-width: 0px;
3
+ --elements-window-min-width: #{$elements-window-min-width};
4
+ }
5
+
1
6
  #alchemy_elements_window {
7
+ --width: var(--elements-window-width, 100vw);
2
8
  position: absolute;
3
9
  right: 0;
4
10
  top: $top-menu-height;
5
11
  z-index: 20;
6
12
  display: block;
7
- width: calc(100vw - #{$collapsed-main-menu-width});
13
+ width: var(--width);
14
+ min-width: var(--elements-window-min-width);
15
+ max-width: calc(
16
+ 100vw - var(--main-menu-width) - var(--preview-window-min-width)
17
+ );
8
18
  height: calc(100vh - #{$top-menu-height});
9
19
  border-left: $default-border;
10
20
  background-color: var(--color-grey_very_light);
@@ -14,15 +24,41 @@
14
24
  .elements-window-visible & {
15
25
  transform: translate3d(0, 0, 0);
16
26
  }
27
+ }
17
28
 
18
- // Fix for Tinymce fullscreen window positioning issues (GH#1511)
19
- .mce-fullscreen & {
20
- width: calc(100vw - #{$collapsed-main-menu-width - $default-border-width});
21
- }
29
+ .elements-window-visible {
30
+ --elements-window-width: calc(100vw - var(--main-menu-width));
22
31
 
23
32
  @media screen and (min-width: $large-screen-break-point) {
24
- width: $elements-window-width;
25
- min-width: $elements-window-min-width;
33
+ --elements-window-width: var(--elements-window-min-width);
34
+ }
35
+
36
+ @media screen and (min-width: $xlarge-screen-break-point) {
37
+ --elements-window-min-width: 475px;
38
+ }
39
+
40
+ // 1778px * 0.225 = 400px (the min width of the elements window)
41
+ @media screen and (min-width: 1778px) {
42
+ --elements-window-width: #{$elements-window-width};
43
+ }
44
+ }
45
+
46
+ alchemy-elements-window-handle {
47
+ --width: var(--spacing-1);
48
+ position: absolute;
49
+ left: calc(-1 * var(--width) / 2);
50
+ top: 0;
51
+ z-index: 1500; // tinymce .tox-fullscreen has 1200
52
+ height: inherit;
53
+ width: var(--width);
54
+ transition-duration: $transition-duration;
55
+ transition-property: background-color width left;
56
+ transition-timing-function: ease-in-out;
57
+
58
+ &:hover,
59
+ &.is-dragged {
60
+ background: var(--color-blue_dark);
61
+ cursor: ew-resize;
26
62
  }
27
63
  }
28
64
 
@@ -43,6 +43,10 @@ form {
43
43
  float: right;
44
44
  }
45
45
 
46
+ textarea {
47
+ padding-top: 7px;
48
+ }
49
+
46
50
  .input > select,
47
51
  .input > .select2-container {
48
52
  width: 100%;
@@ -1,8 +1,3 @@
1
- $picture-overlay-handle-width: 24px;
2
- $image-overlay-form-width: 350px - $picture-overlay-handle-width;
3
- $image-overlay-transition-duration: $transition-duration;
4
- $image-overlay-transition-easing: ease-in;
5
-
6
1
  .alchemy-image-overlay {
7
2
  &.open {
8
3
  background-color: rgba(0, 0, 0, 0.6);
@@ -17,10 +12,21 @@ $image-overlay-transition-easing: ease-in;
17
12
  max-width: 100%;
18
13
  }
19
14
 
15
+ .alchemy-image-overlay-container {
16
+ --picture-overlay-handle-width: 24px;
17
+ --image-overlay-form-width: calc(350px - var(--picture-overlay-handle-width));
18
+ --image-overlay-transition-duration: 250ms;
19
+ --image-overlay-transition-easing: ease-in;
20
+
21
+ &.open {
22
+ overflow: hidden;
23
+ }
24
+ }
25
+
20
26
  .alchemy-image-overlay-dialog {
21
27
  &.hide-form {
22
28
  .picture-details-overlay {
23
- right: -$image-overlay-form-width;
29
+ right: calc(-1 * var(--image-overlay-form-width));
24
30
  }
25
31
 
26
32
  .zoomed-picture-background {
@@ -28,11 +34,11 @@ $image-overlay-transition-easing: ease-in;
28
34
  }
29
35
 
30
36
  .alchemy-image-overlay-close {
31
- right: $picture-overlay-handle-width + var(--spacing-2);
37
+ right: calc(var(--picture-overlay-handle-width) + var(--spacing-2));
32
38
  }
33
39
 
34
40
  .next-picture {
35
- right: $picture-overlay-handle-width;
41
+ right: var(--picture-overlay-handle-width);
36
42
  }
37
43
 
38
44
  .picture-overlay-handle {
@@ -64,17 +70,18 @@ $image-overlay-transition-easing: ease-in;
64
70
  height: 32px;
65
71
  top: var(--spacing-2);
66
72
  right: calc(
67
- var(--spacing-2) + #{$picture-overlay-handle-width} + #{$image-overlay-form-width}
73
+ var(--spacing-2) + var(--picture-overlay-handle-width) +
74
+ var(--image-overlay-form-width)
68
75
  );
69
76
  cursor: pointer;
70
- transition: right $image-overlay-transition-duration
71
- $image-overlay-transition-easing;
77
+ transition: right var(--image-overlay-transition-duration)
78
+ var(--image-overlay-transition-easing);
72
79
 
73
80
  .icon {
74
81
  font-size: 2em;
75
82
  color: var(--color-grey_light);
76
83
  text-shadow: 0 0 4px var(--color-text);
77
- transition: color $image-overlay-transition-duration linear;
84
+ transition: color var(--image-overlay-transition-duration) linear;
78
85
 
79
86
  &:hover {
80
87
  color: var(--color-white);
@@ -88,13 +95,13 @@ $image-overlay-transition-easing: ease-in;
88
95
  top: 0;
89
96
  background-color: var(--color-grey_light);
90
97
  box-shadow: -2px 0 4px -2px var(--color-text);
91
- transition: right $image-overlay-transition-duration
92
- $image-overlay-transition-easing;
98
+ transition: right var(--image-overlay-transition-duration)
99
+ var(--image-overlay-transition-easing);
93
100
  }
94
101
 
95
102
  .picture-details-overlay {
96
103
  right: 0;
97
- width: $image-overlay-form-width;
104
+ width: var(--image-overlay-form-width);
98
105
  height: 100%;
99
106
  padding: var(--spacing-2) var(--spacing-4) var(--spacing-2) var(--spacing-1);
100
107
  overflow: auto;
@@ -117,9 +124,9 @@ $image-overlay-transition-easing: ease-in;
117
124
  }
118
125
 
119
126
  .picture-overlay-handle {
120
- width: $picture-overlay-handle-width;
127
+ width: var(--picture-overlay-handle-width);
121
128
  height: 100%;
122
- right: $image-overlay-form-width;
129
+ right: var(--image-overlay-form-width);
123
130
  cursor: pointer;
124
131
 
125
132
  .icon {
@@ -129,8 +136,8 @@ $image-overlay-transition-easing: ease-in;
129
136
  transform: translate(-50%, -50%);
130
137
  font-size: 1.2em;
131
138
  color: var(--color-icon);
132
- transition: transform $image-overlay-transition-duration
133
- $image-overlay-transition-easing;
139
+ transition: transform var(--image-overlay-transition-duration)
140
+ var(--image-overlay-transition-easing);
134
141
  }
135
142
 
136
143
  &:hover {
@@ -143,15 +150,16 @@ $image-overlay-transition-easing: ease-in;
143
150
  height: 100%;
144
151
  padding-top: var(--spacing-2);
145
152
  padding-right: calc(
146
- #{$image-overlay-form-width} + var(--spacing-2) + #{$picture-overlay-handle-width}
153
+ var(--image-overlay-form-width) + var(--spacing-2) +
154
+ var(--picture-overlay-handle-width)
147
155
  );
148
156
  padding-bottom: var(--spacing-2);
149
157
  padding-left: var(--spacing-2);
150
158
  margin: 0 auto;
151
159
  text-align: center;
152
160
  cursor: pointer;
153
- transition: padding-right $image-overlay-transition-duration
154
- $image-overlay-transition-easing;
161
+ transition: padding-right var(--image-overlay-transition-duration)
162
+ var(--image-overlay-transition-easing);
155
163
 
156
164
  &:before {
157
165
  content: "";
@@ -233,7 +241,7 @@ $image-overlay-transition-easing: ease-in;
233
241
  justify-content: center;
234
242
  align-items: center;
235
243
  text-decoration: none;
236
- transition: background-color $image-overlay-transition-duration linear;
244
+ transition: background-color var(--image-overlay-transition-duration) linear;
237
245
 
238
246
  .icon {
239
247
  width: 32px;
@@ -246,16 +254,20 @@ $image-overlay-transition-easing: ease-in;
246
254
  background-color: rgba(0, 0, 0, 0.3);
247
255
 
248
256
  .icon {
249
- transition: fill $image-overlay-transition-duration linear;
257
+ transition: fill var(--image-overlay-transition-duration) linear;
250
258
  }
251
259
  }
252
260
  }
253
261
 
262
+ .next-picture {
263
+ transition-property: background-color, right;
264
+ }
265
+
254
266
  .icon {
255
267
  --icon-size: 4em;
256
268
  fill: var(--color-grey_light);
257
269
  filter: drop-shadow(0 0 4px var(--color-icon));
258
- transition: all $image-overlay-transition-duration linear;
270
+ transition: all var(--image-overlay-transition-duration) linear;
259
271
  vertical-align: middle;
260
272
  }
261
273
 
@@ -265,7 +277,9 @@ $image-overlay-transition-easing: ease-in;
265
277
  }
266
278
 
267
279
  .next-picture {
268
- right: $image-overlay-form-width + $picture-overlay-handle-width;
280
+ right: calc(
281
+ var(--image-overlay-form-width) + var(--picture-overlay-handle-width)
282
+ );
269
283
  @include border-left-radius($default-border-radius);
270
284
  }
271
285
  }
@@ -193,6 +193,14 @@
193
193
  }
194
194
  }
195
195
 
196
+ :root {
197
+ --main-menu-width: #{$main-menu-width};
198
+ }
199
+
200
+ .collapsed-menu {
201
+ --main-menu-width: #{$collapsed-main-menu-width};
202
+ }
203
+
196
204
  @media screen and (min-width: $large-screen-break-point) {
197
205
  body:not(.collapsed-menu) {
198
206
  .sub_navigation {
@@ -222,7 +230,7 @@
222
230
  display: flex;
223
231
  flex-grow: 1;
224
232
  flex-wrap: nowrap;
225
- height: $header-height;
233
+ height: $header-height + 1px;
226
234
 
227
235
  label {
228
236
  float: left;
@@ -1,28 +1,33 @@
1
+ :root {
2
+ --preview-window-min-width: 0px;
3
+
4
+ @media screen and (min-width: $large-screen-break-point) {
5
+ --preview-window-min-width: 240px;
6
+ }
7
+ }
8
+
1
9
  #alchemy_preview_window {
2
10
  position: absolute;
3
- left: $main-menu-width;
11
+ left: var(--main-menu-width);
4
12
  top: 75px;
5
- width: calc(100vw - #{$main-menu-width - $default-border-width});
13
+ width: calc(
14
+ 100vw - var(--main-menu-width) - var(--elements-window-width) +
15
+ var(--border-width_small)
16
+ );
17
+ min-width: var(--preview-window-min-width);
18
+ max-width: calc(100vw - var(--main-menu-width));
6
19
  height: calc(100vh - #{$top-menu-height});
7
20
  border: 0 none;
8
21
  background: #fff;
9
22
  border-right: $default-border;
10
- transition: width $transition-duration ease-in-out;
23
+ transition-duration: $transition-duration;
24
+ transition-timing-function: ease-in-out;
25
+ transition-property: width max-width;
11
26
 
12
- .collapsed-menu & {
13
- left: $collapsed-main-menu-width;
14
- width: calc(100vw - #{$collapsed-main-menu-width - $default-border-width});
15
- }
16
-
17
- .collapsed-menu.elements-window-visible & {
18
- width: calc(
19
- 100vw - #{$collapsed-main-menu-width - $default-border-width} - #{$elements-window-min-width}
27
+ .elements-window-visible & {
28
+ max-width: calc(
29
+ 100vw - var(--main-menu-width) - var(--elements-window-min-width) +
30
+ var(--border-width_small)
20
31
  );
21
-
22
- @media screen and (min-width: 1777px) {
23
- width: calc(
24
- 100vw - #{$collapsed-main-menu-width - $default-border-width} - #{$elements-window-width}
25
- );
26
- }
27
32
  }
28
33
  }
@@ -39,4 +39,4 @@
39
39
  @import "alchemy/admin/toolbar";
40
40
  @import "alchemy/admin/typography";
41
41
  @import "alchemy/admin/upload";
42
- @import "jquery.Jcrop.min";
42
+ @import "cropper.min";
@@ -32,8 +32,9 @@
32
32
  --font-weigth_normal: 500;
33
33
  --font-weigth_bold: 700;
34
34
 
35
- /* border-radius */
35
+ /* Borders */
36
36
  --border-radius_medium: 3px;
37
+ --border-width_small: 1px;
37
38
 
38
39
  /* Colors */
39
40
  --color-blue_very_light: hsl(203deg, 32%, 97%);
@@ -1,6 +1,8 @@
1
1
  module Alchemy
2
2
  module Ingredients
3
3
  class LinkView < BaseView
4
+ include LinkTarget
5
+
4
6
  attr_reader :link_text
5
7
 
6
8
  # @param ingredient [Alchemy::Ingredient]
@@ -12,7 +14,11 @@ module Alchemy
12
14
  end
13
15
 
14
16
  def call
15
- link_to(link_text, value, {target: ingredient.link_target.presence}.merge(html_options)).html_safe
17
+ target = ingredient.link_target.presence
18
+ link_to(link_text, value, {
19
+ target: link_target_value(target),
20
+ rel: link_rel_value(target)
21
+ }.merge(html_options)).html_safe
16
22
  end
17
23
  end
18
24
  end
@@ -4,6 +4,8 @@ module Alchemy
4
4
  module Ingredients
5
5
  # Renders a picture ingredient view
6
6
  class PictureView < BaseView
7
+ include LinkTarget
8
+
7
9
  attr_reader :ingredient,
8
10
  :show_caption,
9
11
  :disable_link,
@@ -46,10 +48,11 @@ module Alchemy
46
48
  output = caption ? img_tag + caption : img_tag
47
49
 
48
50
  if is_linked?
51
+ target = ingredient.link_target.presence
49
52
  output = link_to(output, url_for(ingredient.link), {
50
53
  title: ingredient.link_title.presence,
51
- target: (ingredient.link_target == "blank") ? "_blank" : nil,
52
- data: {link_target: ingredient.link_target.presence}
54
+ rel: link_rel_value(target),
55
+ target: link_target_value(target)
53
56
  })
54
57
  end
55
58
 
@@ -1,6 +1,8 @@
1
1
  module Alchemy
2
2
  module Ingredients
3
3
  class TextView < BaseView
4
+ include LinkTarget
5
+
4
6
  attr_reader :disable_link
5
7
 
6
8
  delegate :dom_id, :link, :link_title, :link_target,
@@ -21,7 +23,8 @@ module Alchemy
21
23
  link_to(value, url_for(link), {
22
24
  id: dom_id.presence,
23
25
  title: link_title,
24
- target: link_target
26
+ target: link_target_value(link_target),
27
+ rel: link_rel_value(link_target)
25
28
  }.merge(html_options))
26
29
  end.html_safe
27
30
  end
@@ -0,0 +1,18 @@
1
+ module Alchemy
2
+ module Ingredients
3
+ module LinkTarget
4
+ BLANK_VALUE = "_blank"
5
+ REL_VALUE = "noopener noreferrer"
6
+
7
+ def link_rel_value(target)
8
+ if link_target_value(target) == BLANK_VALUE
9
+ REL_VALUE
10
+ end
11
+ end
12
+
13
+ def link_target_value(target)
14
+ (target == "blank") ? BLANK_VALUE : target
15
+ end
16
+ end
17
+ end
18
+ end
@@ -31,6 +31,27 @@ module Alchemy
31
31
 
32
32
  private
33
33
 
34
+ def safe_redirect_path(path = params[:redirect_to], fallback: admin_path)
35
+ if is_safe_redirect_path?(path)
36
+ path
37
+ elsif is_safe_redirect_path?(fallback)
38
+ fallback
39
+ else
40
+ admin_path
41
+ end
42
+ end
43
+
44
+ def is_safe_redirect_path?(path)
45
+ mount_path = alchemy.root_path
46
+ path.to_s.match? %r{^#{mount_path}admin/}
47
+ end
48
+
49
+ def relative_referer_path(referer = request.referer)
50
+ return unless referer
51
+
52
+ URI(referer).path
53
+ end
54
+
34
55
  # Disable layout rendering for xhr requests.
35
56
  def set_layout
36
57
  (request.xhr? || turbo_frame_request?) ? false : "alchemy/admin"
@@ -105,15 +126,23 @@ module Alchemy
105
126
  end
106
127
  end
107
128
 
108
- # Does redirects for html and js requests
129
+ # Does redirects for html, turbo_stream and js requests
130
+ #
131
+ # Makes sure that the redirect path is safe.
109
132
  #
110
133
  def do_redirect_to(url_or_path)
134
+ redirect_path = safe_redirect_path(url_or_path)
111
135
  respond_to do |format|
112
- format.js {
113
- @redirect_url = url_or_path
136
+ format.js do
137
+ @redirect_url = redirect_path
114
138
  render :redirect
115
- }
116
- format.html { redirect_to url_or_path }
139
+ end
140
+ format.turbo_stream do
141
+ redirect_to(redirect_path, allow_other_host: false)
142
+ end
143
+ format.html do
144
+ redirect_to(redirect_path, allow_other_host: false)
145
+ end
117
146
  end
118
147
  end
119
148
 
@@ -41,12 +41,12 @@ module Alchemy
41
41
  end
42
42
  end
43
43
  if @element.save
44
- render :create
44
+ render :create, status: :created
45
45
  else
46
46
  @element.page_version = @page_version
47
47
  @elements = @page.available_element_definitions
48
48
  load_clipboard_items
49
- render :new
49
+ render :new, status: :unprocessable_entity
50
50
  end
51
51
  end
52
52
 
@@ -40,7 +40,7 @@ module Alchemy
40
40
  def switch
41
41
  @language = set_alchemy_language(params[:language_id])
42
42
  session[:alchemy_language_id] = @language.id
43
- do_redirect_to request.referer || alchemy.admin_dashboard_path
43
+ do_redirect_to relative_referer_path || alchemy.admin_dashboard_path
44
44
  end
45
45
 
46
46
  private
@@ -22,6 +22,7 @@ module Alchemy
22
22
  @page = Page.find(params[:id])
23
23
  if @page.update(page_params)
24
24
  @notice = Alchemy.t("Page saved", name: @page.name)
25
+ @while_page_edit = request.referer.include?("edit")
25
26
  render "alchemy/admin/pages/update"
26
27
  else
27
28
  render :edit, status: :unprocessable_entity
@@ -29,7 +29,7 @@ module Alchemy
29
29
  unless: -> { @page_root },
30
30
  only: [:index]
31
31
 
32
- before_action :set_view, only: [:index]
32
+ before_action :set_view, only: [:index, :update]
33
33
 
34
34
  before_action :set_page_version, only: [:show, :edit]
35
35
 
@@ -131,6 +131,10 @@ module Alchemy
131
131
  @notice = Alchemy.t("Page saved", name: @page.name)
132
132
  @while_page_edit = request.referer.include?("edit")
133
133
 
134
+ if @view == "list"
135
+ flash[:notice] = @notice
136
+ end
137
+
134
138
  unless @while_page_edit
135
139
  @tree = serialized_page_tree
136
140
  end
@@ -189,11 +193,7 @@ module Alchemy
189
193
  end
190
194
 
191
195
  def unlock_redirect_path
192
- if params[:redirect_to].to_s.match?(/\A\/admin\/(layout_)?pages/)
193
- params[:redirect_to]
194
- else
195
- admin_pages_path
196
- end
196
+ safe_redirect_path(fallback: admin_pages_path)
197
197
  end
198
198
 
199
199
  # Sets the page public and updates the published_at attribute that is used as cache_key
@@ -78,7 +78,7 @@ module Alchemy
78
78
  flash[:error] = resource_instance_variable.errors.full_messages.join(", ")
79
79
  end
80
80
  flash_notice_for_resource_action
81
- do_redirect_to resource_url_proxy.url_for(search_filter_params.merge(action: "index"))
81
+ do_redirect_to resource_url_proxy.url_for(search_filter_params.merge(action: "index", only_path: true))
82
82
  end
83
83
 
84
84
  def resource_handler
@@ -16,7 +16,10 @@ module Alchemy
16
16
  # * html
17
17
  # * js (Tries to replace a given +container_id+ with the elements view partial content via jQuery.)
18
18
  #
19
+ # @deprecated This controller action will be removed in Alchemy 8.0.
19
20
  def show
21
+ Alchemy::Deprecation.warn "The elements#show controller action is deprecated and will be removed in Alchemy 8.0."
22
+
20
23
  @page = @element.page
21
24
  @options = params[:options]
22
25
 
@@ -16,7 +16,7 @@ module Alchemy
16
16
  def alchemy_form_for(object, *args, &block)
17
17
  options = args.extract_options!
18
18
  options[:builder] = Alchemy::Forms::Builder
19
- options[:remote] = request.xhr?
19
+ options.key?(:remote) || options[:remote] = request.xhr?
20
20
  options[:html] = {
21
21
  id: options.delete(:id),
22
22
  class: ["alchemy", options.delete(:class)].compact.join(" ")