active_scaffold 4.1.6 → 4.2.1

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +32 -0
  3. data/README.md +8 -6
  4. data/app/assets/javascripts/jquery/active_scaffold.js +103 -51
  5. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +35 -2
  6. data/app/assets/stylesheets/active_scaffold_images.scss +6 -0
  7. data/app/assets/stylesheets/{active_scaffold_layout.css → active_scaffold_layout.scss} +104 -4
  8. data/app/assets/stylesheets/tiny_mce_bridge.scss +11 -0
  9. data/app/views/active_scaffold_overrides/_base_form.html.erb +3 -2
  10. data/app/views/active_scaffold_overrides/_field_search.html.erb +2 -2
  11. data/app/views/active_scaffold_overrides/_form.html.erb +14 -4
  12. data/app/views/active_scaffold_overrides/_form_association.html.erb +1 -1
  13. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +5 -11
  14. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +1 -1
  15. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +1 -3
  16. data/app/views/active_scaffold_overrides/_new_record.js.erb +3 -1
  17. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +1 -1
  18. data/app/views/active_scaffold_overrides/_render_field.js.erb +67 -36
  19. data/app/views/active_scaffold_overrides/_update_field_on_create.js.erb +41 -6
  20. data/app/views/active_scaffold_overrides/action_links_menu.js.erb +1 -0
  21. data/config/locales/de.yml +9 -0
  22. data/config/locales/en.yml +11 -0
  23. data/config/locales/es.yml +8 -0
  24. data/config/locales/fr.yml +8 -0
  25. data/config/locales/hu.yml +8 -0
  26. data/config/locales/ja.yml +8 -0
  27. data/config/locales/ru.yml +8 -0
  28. data/lib/active_scaffold/actions/common_search.rb +2 -0
  29. data/lib/active_scaffold/actions/core.rb +47 -23
  30. data/lib/active_scaffold/actions/create.rb +2 -0
  31. data/lib/active_scaffold/actions/delete.rb +6 -0
  32. data/lib/active_scaffold/actions/field_search.rb +36 -11
  33. data/lib/active_scaffold/actions/list.rb +26 -8
  34. data/lib/active_scaffold/actions/mark.rb +6 -0
  35. data/lib/active_scaffold/actions/nested.rb +2 -0
  36. data/lib/active_scaffold/actions/search.rb +7 -0
  37. data/lib/active_scaffold/actions/show.rb +6 -0
  38. data/lib/active_scaffold/actions/subform.rb +2 -0
  39. data/lib/active_scaffold/actions/update.rb +8 -1
  40. data/lib/active_scaffold/active_record_permissions.rb +3 -3
  41. data/lib/active_scaffold/attribute_params.rb +35 -17
  42. data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +2 -0
  43. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +10 -9
  44. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +10 -3
  45. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +2 -0
  46. data/lib/active_scaffold/bridges/active_storage.rb +2 -0
  47. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +2 -0
  48. data/lib/active_scaffold/bridges/ancestry.rb +2 -0
  49. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +2 -0
  50. data/lib/active_scaffold/bridges/bitfields/list_ui.rb +2 -0
  51. data/lib/active_scaffold/bridges/bitfields.rb +2 -0
  52. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +9 -6
  53. data/lib/active_scaffold/bridges/cancan.rb +2 -0
  54. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +2 -0
  55. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge_helpers.rb +2 -0
  56. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +3 -1
  57. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -0
  58. data/lib/active_scaffold/bridges/carrierwave.rb +2 -0
  59. data/lib/active_scaffold/bridges/chosen/helpers.rb +13 -4
  60. data/lib/active_scaffold/bridges/chosen.rb +2 -0
  61. data/lib/active_scaffold/bridges/country_select/country_select_bridge_helper.rb +2 -0
  62. data/lib/active_scaffold/bridges/country_select.rb +2 -0
  63. data/lib/active_scaffold/bridges/date_picker/ext.rb +6 -0
  64. data/lib/active_scaffold/bridges/date_picker/helper.rb +7 -3
  65. data/lib/active_scaffold/bridges/date_picker.rb +2 -0
  66. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge.rb +2 -0
  67. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge_helpers.rb +2 -0
  68. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +3 -1
  69. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +2 -0
  70. data/lib/active_scaffold/bridges/dragonfly.rb +2 -0
  71. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +2 -0
  72. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +10 -9
  73. data/lib/active_scaffold/bridges/file_column/form_ui.rb +2 -0
  74. data/lib/active_scaffold/bridges/file_column/list_ui.rb +2 -0
  75. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +2 -0
  76. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +2 -0
  77. data/lib/active_scaffold/bridges/file_column.rb +2 -0
  78. data/lib/active_scaffold/bridges/logical_query_parser/tokens_grammar.rb +65 -0
  79. data/lib/active_scaffold/bridges/logical_query_parser/tokens_grammar.treetop +31 -0
  80. data/lib/active_scaffold/bridges/logical_query_parser.rb +9 -0
  81. data/lib/active_scaffold/bridges/paper_trail/actions.rb +2 -0
  82. data/lib/active_scaffold/bridges/paper_trail/config.rb +2 -0
  83. data/lib/active_scaffold/bridges/paper_trail/helper.rb +2 -0
  84. data/lib/active_scaffold/bridges/paper_trail/paper_trail_bridge.rb +2 -0
  85. data/lib/active_scaffold/bridges/paper_trail.rb +2 -0
  86. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +3 -1
  87. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +2 -0
  88. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +2 -0
  89. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +12 -12
  90. data/lib/active_scaffold/bridges/paperclip.rb +2 -0
  91. data/lib/active_scaffold/bridges/record_select/helpers.rb +19 -11
  92. data/lib/active_scaffold/bridges/record_select.rb +2 -0
  93. data/lib/active_scaffold/bridges/semantic_attributes/column.rb +2 -0
  94. data/lib/active_scaffold/bridges/semantic_attributes.rb +2 -0
  95. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
  96. data/lib/active_scaffold/bridges/tiny_mce.rb +6 -0
  97. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +2 -0
  98. data/lib/active_scaffold/bridges/usa_state_select.rb +2 -0
  99. data/lib/active_scaffold/bridges.rb +2 -0
  100. data/lib/active_scaffold/config/base.rb +12 -7
  101. data/lib/active_scaffold/config/core.rb +26 -23
  102. data/lib/active_scaffold/config/create.rb +2 -0
  103. data/lib/active_scaffold/config/delete.rb +2 -0
  104. data/lib/active_scaffold/config/field_search.rb +2 -0
  105. data/lib/active_scaffold/config/form.rb +11 -1
  106. data/lib/active_scaffold/config/list.rb +7 -7
  107. data/lib/active_scaffold/config/mark.rb +2 -0
  108. data/lib/active_scaffold/config/nested.rb +28 -0
  109. data/lib/active_scaffold/config/search.rb +2 -0
  110. data/lib/active_scaffold/config/show.rb +2 -0
  111. data/lib/active_scaffold/config/subform.rb +2 -0
  112. data/lib/active_scaffold/config/update.rb +3 -1
  113. data/lib/active_scaffold/configurable.rb +4 -2
  114. data/lib/active_scaffold/constraints.rb +2 -0
  115. data/lib/active_scaffold/core.rb +14 -4
  116. data/lib/active_scaffold/data_structures/action_columns.rb +3 -1
  117. data/lib/active_scaffold/data_structures/action_link.rb +10 -0
  118. data/lib/active_scaffold/data_structures/action_link_separator.rb +2 -0
  119. data/lib/active_scaffold/data_structures/action_links.rb +32 -21
  120. data/lib/active_scaffold/data_structures/actions.rb +4 -2
  121. data/lib/active_scaffold/data_structures/association/abstract.rb +4 -2
  122. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +4 -2
  123. data/lib/active_scaffold/data_structures/association/active_record.rb +3 -9
  124. data/lib/active_scaffold/data_structures/association/mongoid.rb +4 -2
  125. data/lib/active_scaffold/data_structures/association.rb +2 -0
  126. data/lib/active_scaffold/data_structures/bridge.rb +3 -1
  127. data/lib/active_scaffold/data_structures/column.rb +37 -3
  128. data/lib/active_scaffold/data_structures/columns.rb +4 -2
  129. data/lib/active_scaffold/data_structures/filter.rb +3 -3
  130. data/lib/active_scaffold/data_structures/filter_option.rb +2 -0
  131. data/lib/active_scaffold/data_structures/filters.rb +3 -3
  132. data/lib/active_scaffold/data_structures/nested_info.rb +4 -2
  133. data/lib/active_scaffold/data_structures/set.rb +8 -10
  134. data/lib/active_scaffold/data_structures/sorting.rb +5 -7
  135. data/lib/active_scaffold/engine.rb +3 -4
  136. data/lib/active_scaffold/extensions/action_controller_rendering.rb +2 -0
  137. data/lib/active_scaffold/extensions/action_controller_rescueing.rb +2 -0
  138. data/lib/active_scaffold/extensions/action_view_rendering.rb +2 -0
  139. data/lib/active_scaffold/extensions/connection_adapter.rb +2 -0
  140. data/lib/active_scaffold/extensions/ice_nine.rb +2 -0
  141. data/lib/active_scaffold/extensions/localize.rb +2 -0
  142. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +2 -0
  143. data/lib/active_scaffold/extensions/paginator_extensions.rb +3 -1
  144. data/lib/active_scaffold/extensions/routing_mapper.rb +2 -0
  145. data/lib/active_scaffold/extensions/to_label.rb +2 -0
  146. data/lib/active_scaffold/extensions/unsaved_associated.rb +10 -8
  147. data/lib/active_scaffold/extensions/unsaved_record.rb +2 -0
  148. data/lib/active_scaffold/finder.rb +57 -18
  149. data/lib/active_scaffold/helpers/action_link_helpers.rb +112 -37
  150. data/lib/active_scaffold/helpers/association_helpers.rb +4 -2
  151. data/lib/active_scaffold/helpers/controller_helpers.rb +2 -0
  152. data/lib/active_scaffold/helpers/filter_helpers.rb +11 -3
  153. data/lib/active_scaffold/helpers/form_column_helpers.rb +145 -110
  154. data/lib/active_scaffold/helpers/human_condition_helpers.rb +2 -0
  155. data/lib/active_scaffold/helpers/id_helpers.rb +2 -0
  156. data/lib/active_scaffold/helpers/list_column_helpers.rb +9 -5
  157. data/lib/active_scaffold/helpers/pagination_helpers.rb +3 -1
  158. data/lib/active_scaffold/helpers/search_column_helpers.rb +19 -9
  159. data/lib/active_scaffold/helpers/show_column_helpers.rb +4 -2
  160. data/lib/active_scaffold/helpers/tabs_helpers.rb +5 -3
  161. data/lib/active_scaffold/helpers/view_helpers.rb +3 -3
  162. data/lib/active_scaffold/marked_model.rb +6 -5
  163. data/lib/active_scaffold/orm_checks.rb +2 -0
  164. data/lib/active_scaffold/paginator.rb +4 -1
  165. data/lib/active_scaffold/registry.rb +2 -0
  166. data/lib/active_scaffold/responds_to_parent.rb +2 -0
  167. data/lib/active_scaffold/tableless.rb +23 -13
  168. data/lib/active_scaffold/version.rb +4 -2
  169. data/lib/active_scaffold.rb +10 -2
  170. data/lib/generators/active_scaffold/controller/USAGE +19 -0
  171. data/lib/generators/active_scaffold/controller/controller_generator.rb +29 -0
  172. data/lib/generators/active_scaffold/install/USAGE +2 -0
  173. data/lib/generators/active_scaffold/{install_generator.rb → install/install_generator.rb} +10 -6
  174. data/lib/generators/active_scaffold/resource/USAGE +29 -0
  175. data/lib/generators/active_scaffold/resource/resource_generator.rb +30 -0
  176. data/lib/tasks/brakeman.rake +2 -0
  177. data/shoulda_macros/macros.rb +2 -0
  178. metadata +19 -11
  179. data/lib/generators/active_scaffold/controller_generator.rb +0 -49
  180. data/lib/generators/active_scaffold/resource_generator.rb +0 -56
  181. /data/lib/generators/{templates → active_scaffold/controller/templates}/controller.rb +0 -0
  182. /data/lib/generators/{templates → active_scaffold/controller/templates}/helper.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32a8692802d39053416d7af7e631ab4c83ba65ebeca2e1f5e8c61d36f5290521
4
- data.tar.gz: 352724ef265132cdcbc025700ef45b4d5699d16f25ceefbd84e6e9890911b2d3
3
+ metadata.gz: 0ce255799e0bff6d3854d55afdf9278d3a283f68f6d0b1a639acf5bd229b359a
4
+ data.tar.gz: 62761ad0b3530526880f7d8ac464f1f61623f76a6cbdd55512ab6e6102b6f963
5
5
  SHA512:
6
- metadata.gz: 2e28a6a5db3582b4a7175320f799b1fcf1c69e9cfcc4c6740937be291d5c86b111e93e544caad63bc6c1cc9089dd1c3d965fd899cb5c74a96172147b24be5191
7
- data.tar.gz: eae22752d95d2b141396d8f1c78421fee01f5bacca00c834012d42ce024f98ac0098c540ef17554ac4fd901c40a5fdff017221e508a6c703190213fe5f576025
6
+ metadata.gz: ad75a5aef07ab0ed9a6997847249ebfd30b749ffe6502472a74066e86ff08fd196b85579bdec112108a71d149f7918af79cfeec64cdee23d095440594a33dba0
7
+ data.tar.gz: 6bcb9392ee5554b46ebb708d957b061c890b23fc613298c8296370276480b27bae10c4dc7fbb683aeed615aec9bbb1c83de7ac11ada6e1714fcf1d5b6958813b
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,35 @@
1
+ = 4.2.1
2
+ - Fix replacing tinymce fields with update_columns
3
+ - Support refresh_link in radio form UI
4
+ - Rewrite refresh_link JS event handler to find the field without relying in the html structure
5
+
6
+ = 4.2.0
7
+ - Integrate LogicalQueryParser, supporting full logical search, or simplified searches with 'all keywords' and 'any keyword' operators.
8
+ - Display unauthorized column in form as show action, so show_ui or show override is used if exists.
9
+ - Display unauthorized subform with show_ui as show action, to display readonly subform tables.
10
+ - Add subform_includes to column, and preload_for_form method to preload associations on edit action, speed up on big forms with long subforms or many nested subforms.
11
+ - Speedup on refreshing columns and saving big forms.
12
+ - Support add_new with chosen bridge.
13
+ - Added translations for millisec and microsec prompts in jquery UI datetime picker.
14
+ - Speed up validating big forms (e.g. nested subforms with many records on each subform).
15
+ - Support hiding field descriptions by default and show them with hover or click.
16
+ - Fix for usage with frozen literal strings enabled, add magic comments to enable frozen string literals in ActiveScaffold.
17
+ - Update code for tabs to work with bootstrap 5.
18
+ - Add ActiveScaffold.setup_callbacks array, so functions can be added to be called when ActiveScaffold.setup is called (form open, page load).
19
+ - Add add_new_link to config.nested, adding a link to create form on an association, refreshing this record instead of adding new row in the list.
20
+ - Support hashes in update_columns, keys are association names or :__root__ to refresh fields on subforms or at top of the form, values are list of columns or nested hashes.
21
+ - Add custom_modules, in active_scaffold_config and global, to add modules which will be included after ActiveScaffold action modules, to customize the controllers.
22
+ - Add active_scaffold_subform_record_actions helper to render the destroy action on the rows of a subform, so it can be used to add more actions.
23
+ - Improve ActiveStorage bridge to keep existing files in has_many_attachments.
24
+ - Helpers used by file upload bridges have been renamed and moved the JS code to active_scaffold.js.
25
+ - Support action link groups to open with click, like actions showing dynamic action link group.
26
+ - Open dynamic action link group nested in the menu tree, under the action opening it.
27
+ - Add sort_joins to column, defaults to includes value, so it's possible to set sort by SQL without joining the includes, or join with some associations, and preload others.
28
+ - Fix when textarea is required and uses tinymce, show the validation error if it's empty and sync the content before submitting.
29
+ - Support add_new in collection associations too, only with popup mode (add_new: true will default to popup in such associations)
30
+ - Support for rails 8.0 and 8.1
31
+ _ Cache action link generation to speed up list rendering
32
+
1
33
  = 4.1.6
2
34
  - Fix add_existing
3
35
 
data/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
  Overview
3
3
  ========
4
4
  [![Build status](https://github.com/activescaffold/active_scaffold/actions/workflows/ci.yml/badge.svg)](https://github.com/activescaffold/active_scaffold/actions/workflows/ci.yml)
5
- [![Code Climate](https://codeclimate.com/github/activescaffold/active_scaffold/badges/gpa.svg)](https://codeclimate.com/github/activescaffold/active_scaffold)
6
- [![Test Coverage](https://codeclimate.com/github/activescaffold/active_scaffold/badges/coverage.svg)](https://codeclimate.com/github/activescaffold/active_scaffold)
5
+ [![Maintainability](https://qlty.sh/gh/activescaffold/projects/active_scaffold/maintainability.svg)](https://qlty.sh/gh/activescaffold/projects/active_scaffold)
6
+ [![Code Coverage](https://qlty.sh/gh/activescaffold/projects/active_scaffold/coverage.svg)](https://qlty.sh/gh/activescaffold/projects/active_scaffold)
7
7
  [![Gem Version](https://badge.fury.io/rb/active_scaffold.svg)](https://badge.fury.io/rb/active_scaffold)
8
8
  [![Inline docs](https://inch-ci.org/github/activescaffold/active_scaffold.svg?branch=master)](https://inch-ci.org/github/activescaffold/active_scaffold)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
@@ -12,11 +12,13 @@ ActiveScaffold provides a quick and powerful user interfaces for CRUD (create, r
12
12
 
13
13
  Branch Details
14
14
  --------------
15
- master supports rails >= 7.0.x and <= 7.2.x, and ruby >= 3.1.0
16
- 4-0-stable supports rails >= 6.1.x and <= 7.2.x, and ruby >= 3.0.0
15
+ master supports rails >= 7.2.x and ruby >= 3.2.0
16
+ 4-2-stable supports rails >= 7.2.x and ruby >= 3.2.0
17
17
 
18
18
  These versions are not supported anymore:
19
- 3-7-stable supports rails >= 5.2.x and <= 7.1.x, and ruby >= 2.5.0
19
+ 4-1-stable supports rails >= 7.0.x and <= 7.2.x, and ruby >= 3.1.0
20
+ 4-0-stable supports rails >= 6.1.x and <= 7.2.x, and ruby >= 2.5.0
21
+ 3-7-stable supports rails >= 5.2.x and <= 7.1.x, and ruby >= 2.5.0
20
22
  3-6-stable supports rails >= 4.2.x and <= 6.1.x, and ruby >= 2.3.0
21
23
  3-5-stable supports rails >= 4.0.x and <= 5.1.x, and ruby >= 2.0.0
22
24
  3-4-stable supports rails >= 3.2.x and <= 4.2.x, and ruby >= 1.9.3
@@ -69,7 +71,7 @@ When upgrading from 3.x, add `active_scaffold/manifest.js` to `app/assets/config
69
71
 
70
72
  Changing column settings on a request has changed, it must use `active_scaffold_config.columns.override(:name)` at least the first time. After calling `columns.override(:name)`, calling it again or calling `columns[:name]` will return the overrided column. It also supports a block. See [Per Request Configuration](https://github.com/activescaffold/active_scaffold/wiki/Per-Request-Configuration) for examples and more comprehensive explanation.
71
73
 
72
- Changing columns for an action (e.g. add or exclude) on a request must use active_scaffold_config.actions.override_columns, the first time, or use assignment.
74
+ Changing columns for an action (e.g. add or exclude) on a request must use active_scaffold_config.action.override_columns, e.g. active_scaffold_config.list.override_columns, the first time, or use assignment.
73
75
 
74
76
  If you have a `_form_association_record` partial view overrided, use `record` local variable instead of `form_association_record`.
75
77
 
@@ -40,7 +40,11 @@
40
40
  jQuery(document).on('focus', ':input', function() { ActiveScaffold.last_focus = this; });
41
41
  jQuery(document).on('blur', ':input', function(e) { ActiveScaffold.last_focus = e.relatedTarget; });
42
42
  jQuery(document).click(function(event) {
43
- jQuery('.action_group.dyn > ul').hide(); // only hide so action links loading still work
43
+ if (!jQuery(event.target).closest('.action_group.dyn').length) {
44
+ jQuery('.action_group.dyn > .dynamic-menu').each(function() {
45
+ ActiveScaffold.remove_dynamic_action_group(this);
46
+ });
47
+ }
44
48
  });
45
49
  jQuery(document).on('ajax:beforeSend', 'form.as_form', function(event) {
46
50
  var as_form = jQuery(this).closest("form");
@@ -115,6 +119,7 @@
115
119
  if (action_link.is_disabled()) {
116
120
  return false;
117
121
  } else {
122
+ if (action_link.position == 'popup') ActiveScaffold.open_popup('<div class="loading"></div>', action_link);
118
123
  if (action_link.loading_indicator) action_link.loading_indicator.css('visibility','visible');
119
124
  action_link.disable();
120
125
  }
@@ -150,6 +155,8 @@
150
155
  var action_link = ActiveScaffold.ActionLink.get(jQuery(this));
151
156
  if (action_link) {
152
157
  ActiveScaffold.report_500_response(action_link.scaffold_id(), xhr);
158
+ if (action_link.position == 'popup') action_link.close();
159
+ if (action_link.loading_indicator) action_link.loading_indicator.css('visibility','hidden');
153
160
  action_link.enable();
154
161
  }
155
162
  return true;
@@ -241,6 +248,22 @@
241
248
  if (span.data('editInPlace')) span.trigger('click.editInPlace');
242
249
  else ActiveScaffold.in_place_editor_field_clicked(span);
243
250
  });
251
+ jQuery(document).on('click', '.file-input-controls .remove-file-btn', function(event) {
252
+ event.preventDefault();
253
+ var btn = jQuery(this), file_line = btn.closest('.file-input-controls');
254
+ if (file_line.find('.remove_file').val('true').length) {
255
+ btn.parent().hide();
256
+ file_line.find('input').show();
257
+ if (file_line.attr('required')) file_line.find('input').attr('required', 'required');
258
+ } else {
259
+ file_line.remove();
260
+ }
261
+ return false;
262
+ });
263
+ jQuery(document).on('change', '.file-input-controls input[type=file]', function(event) {
264
+ var file_line = jQuery(this).closest('.file-input-controls');
265
+ file_line.find('.remove_file').val('false');
266
+ });
244
267
  jQuery(document).on('ajax:before', 'a.as_paginate',function(event) {
245
268
  var as_paginate = jQuery(this);
246
269
  as_paginate.prevAll('img.loading-indicator').css('visibility','visible');
@@ -270,10 +293,14 @@
270
293
  return true;
271
294
  } else return false;
272
295
  });
273
- jQuery(document).on('ajax:complete', '.action_group.dyn > ul a', function(event) {
274
- var action_link = ActiveScaffold.find_action_link(event.target);
275
- if (action_link && action_link.loading_indicator) action_link.loading_indicator.css('visibility','hidden');
276
- jQuery(event.target).closest('.action_group.dyn > ul').remove();
296
+ jQuery(document).on('ajax:complete', '.action_group.dyn > .dynamic-menu a', function(event) {
297
+ var action_link = ActiveScaffold.find_action_link(event.target), link = jQuery(event.target);
298
+ if (action_link && action_link.loading_indicator) action_link.loading_indicator.css('visibility', 'hidden');
299
+ setTimeout(function() {
300
+ if (!link.parent().is('.action_group.dyn')) {
301
+ ActiveScaffold.remove_dynamic_action_group(link.closest('.action_group.dyn > .dynamic-menu'));
302
+ }
303
+ }, 100);
277
304
  });
278
305
 
279
306
  jQuery(document).on('change', 'input.update_form:not(.recordselect), textarea.update_form, select.update_form, .checkbox-list.update_form input:checkbox', function(event, additional_params) {
@@ -294,14 +321,15 @@
294
321
  });
295
322
  jQuery(document).on('click', 'a.refresh-link', function(event) {
296
323
  event.preventDefault();
297
- var element = jQuery(this);
298
- var form_element = element.prev();
299
- var value;
324
+ var element = jQuery(this), selector = element.data('field-selector'), value, form_element;
325
+ form_element = selector.startsWith('#') ? jQuery(selector) : element.closest('.form-element').find(selector);
326
+ if (form_element.is('.draggable-list')) form_element = form_element.closest('.draggable-lists-container');
300
327
  if (form_element.is(".field_with_errors")) form_element = form_element.children().last();
301
328
  if (form_element.is(".checkbox-list")) {
302
329
  value = form_element.find(':checked').map(function(item) { return jQuery(this).val(); }).toArray();
303
330
  form_element = form_element.parent().find("input:checkbox"); // parent is needed for draggable-list, checked list may be empty
304
- } else value = form_element.is("input:checkbox:not(:checked)") ? null : form_element.val();
331
+ } else if (form_element.is(':radio, :checkbox')) value = form_element.filter(':checked').val() || null;
332
+ else value = form_element.val();
305
333
  ActiveScaffold.update_column(form_element, element.attr('href'), element.data('update_send_form'), form_element.attr('id'), value);
306
334
  });
307
335
  jQuery(document).on('click', 'a.visibility-toggle', function(e) {
@@ -334,7 +362,7 @@
334
362
 
335
363
  jQuery(document).on('click', '.active-scaffold .sub-form a.destroy', function(event) {
336
364
  event.preventDefault();
337
- ActiveScaffold.delete_subform_record(jQuery(this).data('delete-id'));
365
+ ActiveScaffold.remove(jQuery(this).closest('.sub-form-record'));
338
366
  });
339
367
 
340
368
  jQuery(document).on("click", '.hover_click', function(event) {
@@ -367,8 +395,8 @@
367
395
  });
368
396
 
369
397
  jQuery(document).on('click', '.hide-new-subform, .show-new-subform', function(e) {
370
- var $this = jQuery(this), line = $this.closest('.form-element, td'),
371
- subform = line.find('#' + $this.data('subform-id')), radio = false, hide, select;
398
+ var $this = jQuery(this), line = $this.closest('.form-element'),
399
+ subform = line.find('#' + $this.data('subform-id')), radio = false, hide, field;
372
400
  if ($this.is('[type=radio]')) {
373
401
  radio = true;
374
402
  hide = $this.is('.hide-new-subform');
@@ -377,17 +405,16 @@
377
405
  hide = subform.is(':visible');
378
406
  }
379
407
  if ($this.data('select-id')) {
380
- select = line.find('#' + $this.data('select-id'));
381
- if (select.hasClass('recordselect') || select.is('.no-options')) select = select.next(':hidden').addBack();
408
+ field = line.find('#' + $this.data('select-id')).closest('.select-field');
382
409
  }
383
410
  if (hide) {
384
411
  subform.hide().find("input:enabled,select:enabled,textarea:enabled").prop('disabled', true);
385
- if (select) select.show().prop('disabled', false);
412
+ if (field) field.show().find('input:disabled,select:disabled,textarea:disabled').prop('disabled', false);
386
413
  if (radio) {
387
414
  $this.closest('.form-element').find('[name="' + $this.attr('name') + '"].show-new-subform').prop('disabled', false);
388
415
  } else $this.html($this.data('select-text'));
389
416
  } else {
390
- if (select) select.hide().prop('disabled', true);
417
+ if (field) field.hide().find('input:enabled,select:enabled,textarea:enabled').prop('disabled', true);
391
418
  subform.show().find("input:disabled,select:disabled,textarea:disabled").prop('disabled', false);
392
419
  if (radio) $this.prop('disabled', true);
393
420
  else {
@@ -401,7 +428,7 @@
401
428
  jQuery(this).parent().find('[type=submit]').click();
402
429
  }));
403
430
 
404
- jQuery(document).on('click', 'form.search .reset[data-reset]', function(e){
431
+ jQuery(document).on('click', 'form.search [data-reset]', function(e){
405
432
  e.preventDefault();
406
433
  var form = jQuery(this).closest('form.search');
407
434
  form.find(
@@ -468,6 +495,14 @@
468
495
  e.preventDefault();
469
496
  ActiveScaffold.update_all_checkboxes(jQuery(this), false);
470
497
  });
498
+ jQuery(document).on('click', '.descriptions-click .description:not(.visible)', function(e) {
499
+ e.preventDefault();
500
+ $(this).addClass('visible');
501
+ });
502
+ jQuery(document).on('click', '.descriptions-click .description.visible .close', function(e) {
503
+ e.preventDefault();
504
+ $(this).closest('.description').removeClass('visible');
505
+ });
471
506
  });
472
507
 
473
508
  jQuery(document).on('turbolinks:load turbo:load', function($) {
@@ -556,7 +591,7 @@
556
591
  ActiveScaffold.auto_paginate(container);
557
592
  ActiveScaffold.draggable_lists('.draggable-lists', container);
558
593
  ActiveScaffold.sliders(container);
559
- ActiveScaffold.disable_optional_subforms(container);
594
+ setTimeout(function() { ActiveScaffold.disable_optional_subforms(container) }); // delay hiding fields, in case some field need to be visible, e.g. chosen
560
595
  ActiveScaffold.update_floating_form_footer(); // check other forms too, state may change
561
596
  if (container != document) {
562
597
  jQuery('[data-rs-type]', container).each(function() { RecordSelect.from_attributes(jQuery(this)); });
@@ -612,9 +647,8 @@
612
647
  var line = $this.closest('.form-element'), toggle = line.find('.show-new-subform[data-subform-id="' + $this.attr('id') + '"]').first();
613
648
  if (toggle.is('[type=radio]')) toggle.prop('disabled', true);
614
649
  else if (toggle.data('select-id')) {
615
- var select = line.find('#' + toggle.data('select-id'));
616
- if (select.hasClass('recordselect') || select.is('.no-options')) select = select.next(':hidden').addBack();
617
- select.hide().prop('disabled', true);
650
+ var field = line.find('#' + toggle.data('select-id')).closest('.select-field');
651
+ field.hide().find('input:enabled,select:enabled,textarea:enabled').prop('disabled', true);
618
652
  }
619
653
  } else $this.find("input:enabled,select:enabled,textarea:enabled").prop('disabled', true);
620
654
  });
@@ -885,12 +919,6 @@
885
919
  });
886
920
  },
887
921
 
888
- delete_subform_record: function(record) {
889
- if (typeof(record) == 'string') record = '#' + record;
890
- record = jQuery(record).closest('.sub-form-record');
891
- this.remove(record);
892
- },
893
-
894
922
  report_500_response: function(active_scaffold_id, xhr) {
895
923
  var server_error = jQuery(active_scaffold_id).find('.messages-container p.server-error').first();
896
924
  if (server_error.is(':visible')) {
@@ -911,16 +939,21 @@
911
939
  display_dynamic_action_group: function(link, html) {
912
940
  var container;
913
941
  if (typeof(link) == 'string') link = jQuery('#' + link);
914
- if (link.closest('td.actions').length) {
915
- container = link.closest('td').addClass('action_group dyn');
916
- } else {
917
- if (link.parent('div.actions').length) link.wrap(jQuery('<div>'));
918
- container = link.parent().addClass('action_group dyn');
942
+ if (link.parent('.actions').length) link.wrap(jQuery('<div>'));
943
+ container = link.parent().addClass('action_group dyn');
944
+ if (ActiveScaffold.config.dynamic_group_parent_class) {
945
+ container.addClass(ActiveScaffold.config.dynamic_group_parent_class);
919
946
  }
920
- container.find('> ul').remove();
947
+ container.find('> .dynamic-menu').remove();
921
948
  container.append(html);
922
949
  },
923
950
 
951
+ remove_dynamic_action_group: function(group) {
952
+ group = jQuery(group);
953
+ group.closest('.action_group.dyn').removeClass(ActiveScaffold.config.dynamic_group_parent_class);
954
+ group.remove();
955
+ },
956
+
924
957
  scroll_to: function(element, checkInViewport) {
925
958
  if (typeof checkInViewport == 'undefined') checkInViewport = true;
926
959
  if (typeof(element) == 'string') element = '#' + element;
@@ -1004,7 +1037,14 @@
1004
1037
  var element, container = source.closest('.sub-form-record'), selector = '';
1005
1038
  if (container.length === 0) {
1006
1039
  container = source.closest('form > ol.form');
1007
- selector = 'li';
1040
+ selector = '>';
1041
+ }
1042
+ if (options.traverse) {
1043
+ selector = '';
1044
+ options.traverse.forEach(function(step) {
1045
+ if (step === '__root__') container = container.closest('form');
1046
+ else container = container.find(step);
1047
+ });
1008
1048
  }
1009
1049
  // find without entering new subforms
1010
1050
  element = container.find(selector + ':not(.sub-form) .' + options.field_class);
@@ -1012,17 +1052,17 @@
1012
1052
  element = element.filter(function() { return jQuery(this).closest('.sub-form-record').get(0) === container.get(0); });
1013
1053
  else element = element.filter(function() { return jQuery(this).closest('.sub-form-record').length === 0; });
1014
1054
  if (element.length)
1015
- element = element.first().closest('dl');
1055
+ element = element.first().closest('.form-element');
1016
1056
  else if (options.subform_class)
1017
1057
  element = container.find(selector + '.' + options.subform_class).first();
1018
1058
 
1019
1059
  if (element.length) {
1020
- var parent = element.is('li, td') ? element : element.parent('li, td');
1021
- if (element.is('li'))
1060
+ var parent = element.is('.sub-form, .form-element') ? element : element.parent('.form-element, .sub-form');
1061
+ if (element.is('.sub-form, .form-element'))
1022
1062
  this.replace_html(element, content);
1023
1063
  else
1024
1064
  this.replace(element, content);
1025
- if (parent.is('li')) {
1065
+ if (parent.is('.form-element, .sub-form')) {
1026
1066
  for (var attr in options.attrs)
1027
1067
  parent.attr(attr, options.attrs[attr]);
1028
1068
  }
@@ -1262,9 +1302,13 @@
1262
1302
  },
1263
1303
 
1264
1304
  open_popup: function(content, link) {
1265
- var element = jQuery(content).filter(function(){ return this.tagName; }).dialog({
1305
+ var element = jQuery(content).filter(function(){ return this.tagName; })
1306
+ if (link.adapter) link.adapter.dialog('close');
1307
+ element.dialog({
1266
1308
  modal: true,
1267
- close: function() { link.close(); },
1309
+ close: function () {
1310
+ if (!link.adapter.is('.loading')) link.close();
1311
+ },
1268
1312
  width: ActiveScaffold.config.popup_width || '80%'
1269
1313
  });
1270
1314
  link.set_adapter(element);
@@ -1273,6 +1317,7 @@
1273
1317
  close_popup: function(link, callback) {
1274
1318
  link.adapter.dialog('close');
1275
1319
  ActiveScaffold.remove(link.adapter, callback);
1320
+ link.set_adapter(null);
1276
1321
  }
1277
1322
  }
1278
1323
 
@@ -1444,9 +1489,11 @@
1444
1489
 
1445
1490
  set_adapter: function(element) {
1446
1491
  this.adapter = element;
1447
- this.adapter.addClass('as_adapter');
1448
- this.adapter.data('action_link', this);
1449
- if (this.refresh_url) jQuery('.as_cancel', this.adapter).attr('href', this.refresh_url);
1492
+ if (element) {
1493
+ this.adapter.addClass('as_adapter');
1494
+ this.adapter.data('action_link', this);
1495
+ if (this.refresh_url) jQuery('.as_cancel', this.adapter).attr('href', this.refresh_url);
1496
+ }
1450
1497
  },
1451
1498
 
1452
1499
  keep_open: function() {
@@ -1519,14 +1566,19 @@
1519
1566
  close: function(refreshed_content_or_reload) {
1520
1567
  this._super();
1521
1568
  if (refreshed_content_or_reload) {
1522
- if (typeof refreshed_content_or_reload == 'string') {
1523
- ActiveScaffold.update_row(this.target, refreshed_content_or_reload);
1524
- } else if (this.refresh_url) {
1525
- var target = this.target;
1526
- jQuery.get(this.refresh_url, function(e, status, xhr) {
1527
- ActiveScaffold.update_row(target, xhr.responseText);
1528
- });
1529
- }
1569
+ if (typeof refreshed_content_or_reload == 'string') this.refresh(refreshed_content_or_reload);
1570
+ else this.refresh();
1571
+ }
1572
+ },
1573
+
1574
+ refresh: function(content) {
1575
+ if (content) {
1576
+ ActiveScaffold.update_row(this.target, content);
1577
+ } else if (this.refresh_url) {
1578
+ var target = this.target;
1579
+ jQuery.get(this.refresh_url, function(e, status, xhr) {
1580
+ ActiveScaffold.update_row(target, xhr.responseText);
1581
+ });
1530
1582
  }
1531
1583
  },
1532
1584
 
@@ -7,6 +7,26 @@
7
7
  action_link_close.apply(this);
8
8
  };
9
9
 
10
+ ActiveScaffold.remove_tinymce = function(element) {
11
+ if (typeof(element) == 'string') element = '#' + element;
12
+ element = jQuery(element);
13
+ element.find('textarea.mceEditor').each(function(index, elem) {
14
+ tinymce.remove('#' + elem.id);
15
+ });
16
+ };
17
+
18
+ var as_replace = ActiveScaffold.replace,
19
+ as_replace_html = ActiveScaffold.replace_html;
20
+
21
+ ActiveScaffold.replace = function(element) {
22
+ this.remove_tinymce(element);
23
+ return as_replace.apply(this, arguments);
24
+ };
25
+ ActiveScaffold.replace_html = function(element) {
26
+ this.remove_tinymce(element);
27
+ return as_replace_html.apply(this, arguments);
28
+ };
29
+
10
30
  function loadTinyMCE() {
11
31
  var global_settings = ActiveScaffold.config.tiny_mce_settings || {};
12
32
  var local_settings = jQuery(this).data('tinymce');
@@ -20,14 +40,27 @@
20
40
  var id = jQuery(this).attr('id');
21
41
  if (tinymce && tinymce.majorVersion >= 6) {
22
42
  settings.selector = '#' + id;
23
- if (settings.setup) settings.setup = eval(settings.setup);
43
+ var userSetup = settings.setup ? eval(settings.setup) : function () {};
44
+ settings.setup = function(editor) {
45
+ userSetup(editor); // run what the user already had
46
+
47
+ /* keep textarea in sync */
48
+ var sync = function() { editor.save(); }; // same as triggerSave()
49
+ editor.on('change input NodeChange Undo Redo', sync);
50
+ };
24
51
  tinymce.init(settings);
25
52
  } else { // tinyMCE.majorVersion < 6
53
+ settings.init_instance_callback = function(editor) {
54
+ var sync = function () { editor.save(); }; // same as triggerSave()
55
+ editor.on('change input NodeChange Undo Redo', sync);
56
+ if (userInit) userInit(editor); // run user’s callback too
57
+ };
26
58
  tinyMCE.settings = settings;
27
59
  tinyMCE.execCommand('mceAddEditor', false, id);
28
60
  }
29
61
  }
30
-
62
+ /*const sync = () => editor.save(); // same as triggerSave for this editor
63
+ editor.on('change input NodeChange Undo Redo', sync);*/
31
64
  jQuery(document).on('submit', 'form.as_form', function() {
32
65
  tinymce.triggerSave();
33
66
  jQuery('textarea.mceEditor', this).each(function() { tinymce.remove('#'+jQuery(this).attr('id')); });
@@ -57,3 +57,9 @@ background-image: image-url('active_scaffold/close_touch.png');
57
57
  .active-scaffold .refresh-link {
58
58
  background-image: image-url('active_scaffold/refresh.png');
59
59
  }
60
+
61
+ .ui-dialog .loading::after {
62
+ background: image-url('active_scaffold/indicator.gif') no-repeat;
63
+ content: 'Loading...';
64
+ padding-left: 20px;
65
+ }
@@ -751,6 +751,14 @@ float: left;
751
751
  margin-top: 6px;
752
752
  }
753
753
 
754
+ .active-scaffold li.form-element dd .select-field:not(.draggable-lists-container) {
755
+ display: inline-block;
756
+ }
757
+ .active-scaffold li.form-element dd .select-field ~ .as_action {
758
+ vertical-align: top;
759
+ margin-left: 5px;
760
+ }
761
+
754
762
  .active-scaffold .form dd {
755
763
  margin: 0;
756
764
  }
@@ -761,6 +769,95 @@ display: inline-block;
761
769
  font-size: 10px;
762
770
  margin-left: 5px;
763
771
  }
772
+ .active-scaffold .description .close {
773
+ display: none;
774
+ }
775
+
776
+ .active-scaffold .descriptions-hover, .active-scaffold .descriptions-click {
777
+ .horizontal-sub-form td .description {
778
+ display: none;
779
+ }
780
+ dd > .description {
781
+ position: absolute;
782
+ }
783
+ .horizontal-sub-form th, dd {
784
+ position: relative;
785
+ .description {
786
+ display: inline-block;
787
+ width: 1.5em;
788
+ height: 1.5em;
789
+ overflow: hidden;
790
+ white-space: nowrap;
791
+ color: blue;
792
+ top: 2px;
793
+ font-weight: normal;
794
+
795
+ &::before {
796
+ display: flex;
797
+ content: '?';
798
+ border-radius: 0.75em;
799
+ background: lightgray;
800
+ height: 1.5em;
801
+ width: 1.5em;
802
+ align-items: center;
803
+ justify-content: center;
804
+ }
805
+ }
806
+ }
807
+ .horizontal-sub-form th .description {
808
+ left: 0;
809
+ }
810
+ dd > .description {
811
+ right: -1em;
812
+ }
813
+ }
814
+
815
+ .active-scaffold .descriptions-hover .horizontal-sub-form th .description:hover,
816
+ .active-scaffold .descriptions-hover dd .description:hover,
817
+ .active-scaffold .descriptions-click .horizontal-sub-form th .description.visible,
818
+ .active-scaffold .descriptions-click dd .description.visible {
819
+ position: absolute;
820
+ width: auto;
821
+ height: auto;
822
+ white-space: normal;
823
+ background: white;
824
+ padding: 2px 5px;
825
+ z-index: 100; /* Ensure it's above other content */
826
+ display: block;
827
+ &::before {
828
+ display: none;
829
+ }
830
+ }
831
+
832
+ .active-scaffold .descriptions-click {
833
+ .horizontal-sub-form th .description, dd .description {
834
+ cursor: pointer;
835
+ &.visible {
836
+ overflow: visible;
837
+ cursor: auto;
838
+ padding-right: 10px;
839
+ }
840
+ &.visible .close {
841
+ &::before {
842
+ content: 'x';
843
+ }
844
+ position: absolute;
845
+ top: -10px;
846
+ right: -5px;
847
+ background: lightgray;
848
+ border-radius: 10px;
849
+ width: 20px;
850
+ height: 20px;
851
+ display: flex;
852
+ align-items: center;
853
+ justify-content: center;
854
+ }
855
+ }
856
+ }
857
+
858
+ .active-scaffold .sub-form.optional[style=""] ~ .description {
859
+ display: none;
860
+ }
764
861
 
765
862
  .active-scaffold .required label {
766
863
  font-weight: bold;
@@ -961,7 +1058,7 @@ clear: left;
961
1058
 
962
1059
  .active-scaffold .sub-form .association-record a.destroy {
963
1060
  font-weight: bold;
964
- display: block;
1061
+ display: inline-block;
965
1062
  height: 16px;
966
1063
  padding: 0;
967
1064
  width: 16px;
@@ -969,6 +1066,9 @@ text-indent: -4000px;
969
1066
  background: 0 0 no-repeat;
970
1067
  overflow: hidden;
971
1068
  }
1069
+ .active-scaffold .sub-form .association-record * + a.destroy {
1070
+ margin-left: 5px;
1071
+ }
972
1072
 
973
1073
  .active-scaffold .sub-form .locked a.destroy {
974
1074
  display: none;
@@ -1052,9 +1152,9 @@ font-size: 100%;
1052
1152
  text-decoration: none;
1053
1153
  background-color: #eee;
1054
1154
  }
1055
- .active-scaffold .tabbed .nav-tabs > li.active > a,
1056
- .active-scaffold .tabbed .nav-tabs > li.active > a:focus,
1057
- .active-scaffold .tabbed .nav-tabs > li.active > a:hover {
1155
+ .active-scaffold .tabbed .nav-tabs > li > a.active,
1156
+ .active-scaffold .tabbed .nav-tabs > li > a.active:focus,
1157
+ .active-scaffold .tabbed .nav-tabs > li > a.active:hover {
1058
1158
  border-color: #ddd;
1059
1159
  border-bottom-color: transparent;
1060
1160
  cursor: default;
@@ -0,0 +1,11 @@
1
+ .active-scaffold textarea.mceEditor {
2
+ opacity: 0;
3
+ pointer-events: none;
4
+ display: inline !important;
5
+ height: 0;
6
+ margin: 0;
7
+ padding: 0;
8
+ width: 0 !important;
9
+ float: left;
10
+ margin-left: -5px;
11
+ }
@@ -7,6 +7,7 @@
7
7
  multipart ||= action_config.multipart? unless local_assigns.key? :multipart
8
8
  columns ||= action_config.columns unless local_assigns.key? :columns
9
9
  persistent ||= action_config.persistent unless local_assigns.key? :persistent
10
+ field_descriptions ||= action_config.field_descriptions
10
11
  if !local_assigns.key?(:floating_footer) && action_config.respond_to?(:floating_footer)
11
12
  floating_footer = action_config.floating_footer
12
13
  end
@@ -17,6 +18,7 @@
17
18
  multipart ||= false
18
19
  columns ||= nil
19
20
  persistent ||= false
21
+ field_descriptions ||= :show
20
22
  end
21
23
  floating_footer ||= false
22
24
  method ||= :post
@@ -29,11 +31,10 @@
29
31
  <%=
30
32
  options = {id: form_id,
31
33
  multipart: multipart,
32
- class: "as_form #{form_action}",
34
+ class: "as_form #{form_action} #{'floating-footer' if floating_footer} #{"descriptions-#{field_descriptions}" if field_descriptions != :show}",
33
35
  method: method,
34
36
  'data-loading' => defined?(loading) ? loading : true}
35
37
  cancel_options = {class: 'as_cancel'}
36
- options[:class] << ' floating-footer' if floating_footer
37
38
  cancel_options[:remote] = true if xhr # cancel link does nt have to care about multipart forms
38
39
  if xhr && multipart # file_uploads
39
40
  form_remote_upload_tag url_options.merge(iframe: true), options