blacklight-spotlight 5.3.2 → 6.0.0.rc2

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 (211) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -4
  3. data/Rakefile +1 -2
  4. data/app/assets/javascripts/spotlight/spotlight.esm.js +2168 -4560
  5. data/app/assets/javascripts/spotlight/spotlight.esm.js.map +1 -1
  6. data/app/assets/javascripts/spotlight/spotlight.js +2167 -4559
  7. data/app/assets/javascripts/spotlight/spotlight.js.map +1 -1
  8. data/app/assets/stylesheets/spotlight/_blacklight_overrides.scss +4 -0
  9. data/app/assets/stylesheets/spotlight/_browse.scss +0 -4
  10. data/app/assets/stylesheets/spotlight/{_collapse_toggle.scss → _collapse_toggle.css} +2 -2
  11. data/app/assets/stylesheets/spotlight/_exhibit_admin.scss +8 -0
  12. data/app/assets/stylesheets/spotlight/_exhibits_index.scss +2 -1
  13. data/app/assets/stylesheets/spotlight/_footer.scss +2 -2
  14. data/app/assets/stylesheets/spotlight/_header.scss +8 -8
  15. data/app/assets/stylesheets/spotlight/_pages.scss +1 -1
  16. data/app/assets/stylesheets/spotlight/_slideshow_block.scss +15 -0
  17. data/app/assets/stylesheets/spotlight/_spotlight.scss +1 -1
  18. data/app/assets/stylesheets/spotlight/_translations.scss +3 -3
  19. data/app/assets/stylesheets/spotlight/_uploaded_items_block.css +7 -0
  20. data/app/assets/stylesheets/spotlight/_variables.css +15 -0
  21. data/app/assets/stylesheets/spotlight/browse_group_categories_block.scss +0 -76
  22. data/app/components/blacklight/icons/pause_component.rb +15 -0
  23. data/app/components/blacklight/icons/play_arrow_component.rb +15 -0
  24. data/app/components/spotlight/analytics/dashboard_component.html.erb +1 -1
  25. data/app/components/spotlight/custom_field_form_group_component.rb +40 -0
  26. data/app/components/spotlight/document_admin_table_component.html.erb +2 -2
  27. data/app/components/spotlight/edit_sidecar_component.html.erb +18 -0
  28. data/app/components/spotlight/edit_sidecar_component.rb +41 -0
  29. data/app/components/spotlight/masthead_component.html.erb +18 -0
  30. data/app/components/spotlight/masthead_component.rb +46 -0
  31. data/app/components/spotlight/save_search_component.html.erb +1 -1
  32. data/app/components/spotlight/sidecar_form_field_component.html.erb +18 -0
  33. data/app/components/spotlight/sidecar_form_field_component.rb +71 -0
  34. data/app/components/spotlight/solr_document_legacy_embed_component.html.erb +0 -4
  35. data/app/components/spotlight/solr_document_legacy_embed_component.rb +2 -2
  36. data/app/components/spotlight/tag_selector_component.html.erb +1 -1
  37. data/app/components/spotlight/tag_selector_component.rb +3 -2
  38. data/app/components/spotlight/upload_field_form_group_component.rb +33 -0
  39. data/app/controllers/concerns/spotlight/controller.rb +15 -14
  40. data/app/controllers/concerns/spotlight/search_helper.rb +2 -5
  41. data/app/controllers/spotlight/browse_controller.rb +1 -6
  42. data/app/controllers/spotlight/catalog_controller.rb +2 -3
  43. data/app/controllers/spotlight/concerns/application_controller.rb +4 -6
  44. data/app/controllers/spotlight/exhibits_controller.rb +22 -10
  45. data/app/controllers/spotlight/lock_controller.rb +1 -1
  46. data/app/controllers/spotlight/pages_controller.rb +11 -11
  47. data/app/controllers/spotlight/resources/upload_controller.rb +1 -1
  48. data/app/controllers/spotlight/resources_controller.rb +1 -1
  49. data/app/controllers/spotlight/solr_controller.rb +5 -1
  50. data/app/helpers/spotlight/application_helper.rb +14 -26
  51. data/app/helpers/spotlight/crud_link_helpers.rb +0 -12
  52. data/app/helpers/spotlight/main_app_helpers.rb +2 -2
  53. data/app/javascript/spotlight/admin/add_another.js +37 -25
  54. data/app/javascript/spotlight/admin/add_image_selector.js +43 -26
  55. data/app/javascript/spotlight/admin/add_new_button.js +51 -36
  56. data/app/javascript/spotlight/admin/blacklight_configuration.js +76 -54
  57. data/app/javascript/spotlight/admin/block_mixins/autocompleteable.js +102 -101
  58. data/app/javascript/spotlight/admin/block_mixins/formable.js +91 -75
  59. data/app/javascript/spotlight/admin/block_mixins/plustextable.js +46 -48
  60. data/app/javascript/spotlight/admin/blocks/block.js +27 -28
  61. data/app/javascript/spotlight/admin/blocks/browse_block.js +14 -10
  62. data/app/javascript/spotlight/admin/blocks/browse_group_categories_block.js +14 -10
  63. data/app/javascript/spotlight/admin/blocks/pages_block.js +5 -4
  64. data/app/javascript/spotlight/admin/blocks/resources_block.js +61 -41
  65. data/app/javascript/spotlight/admin/blocks/search_result_block.js +24 -7
  66. data/app/javascript/spotlight/admin/blocks/solr_documents_base_block.js +103 -50
  67. data/app/javascript/spotlight/admin/blocks/solr_documents_carousel_block.js +50 -26
  68. data/app/javascript/spotlight/admin/blocks/solr_documents_features_block.js +47 -23
  69. data/app/javascript/spotlight/admin/blocks/uploaded_items_block.js +35 -19
  70. data/app/javascript/spotlight/admin/crop.js +146 -66
  71. data/app/javascript/spotlight/admin/croppable.js +5 -4
  72. data/app/javascript/spotlight/admin/croppable_modal.js +67 -32
  73. data/app/javascript/spotlight/admin/edit_in_place.js +69 -54
  74. data/app/javascript/spotlight/admin/exhibits.js +92 -45
  75. data/app/javascript/spotlight/admin/form_observer.js +92 -78
  76. data/app/javascript/spotlight/admin/multi_image_selector.js +201 -153
  77. data/app/javascript/spotlight/admin/pages.js +13 -7
  78. data/app/javascript/spotlight/admin/progress_monitor.js +65 -50
  79. data/app/javascript/spotlight/admin/readonly_checkbox.js +7 -3
  80. data/app/javascript/spotlight/admin/search_typeahead.js +20 -11
  81. data/app/javascript/spotlight/admin/select_related_input.js +23 -23
  82. data/app/javascript/spotlight/admin/serialize_json.js +120 -0
  83. data/app/javascript/spotlight/admin/sir-trevor/block_controls.js +24 -11
  84. data/app/javascript/spotlight/admin/sir-trevor/block_limits.js +9 -6
  85. data/app/javascript/spotlight/admin/sir-trevor/locales.js +2 -2
  86. data/app/javascript/spotlight/admin/spotlight_nestable.js +2 -5
  87. data/app/javascript/spotlight/admin/translation_progress.js +18 -13
  88. data/app/javascript/spotlight/admin/users.js +151 -64
  89. data/app/javascript/spotlight/user/carousel.js +104 -32
  90. data/app/javascript/spotlight/user/clear_form_button.js +24 -14
  91. data/app/javascript/spotlight/user/index.js +0 -2
  92. data/app/javascript/spotlight/user/zpr_links.js +99 -40
  93. data/app/jobs/concerns/spotlight/gather_documents.rb +1 -1
  94. data/app/jobs/spotlight/add_uploads_from_csv.rb +31 -8
  95. data/app/jobs/spotlight/cleanup_job_trackers_job.rb +5 -3
  96. data/app/models/concerns/spotlight/access_controls_enforcement_search_builder.rb +13 -2
  97. data/app/models/concerns/spotlight/browse_category_search_builder.rb +3 -3
  98. data/app/models/concerns/spotlight/exhibit_documents.rb +2 -2
  99. data/app/models/concerns/spotlight/sitemap.rb +2 -2
  100. data/app/models/concerns/spotlight/solr_document/finder.rb +2 -2
  101. data/app/models/concerns/spotlight/solr_document.rb +1 -47
  102. data/app/models/sir_trevor_rails/blocks/browse_block.rb +0 -4
  103. data/app/models/sir_trevor_rails/blocks/solr_documents_block.rb +2 -1
  104. data/app/models/spotlight/blacklight_configuration.rb +5 -3
  105. data/app/models/spotlight/contact_email.rb +2 -2
  106. data/app/models/spotlight/custom_field.rb +17 -0
  107. data/app/models/spotlight/exhibit.rb +28 -0
  108. data/app/models/spotlight/featured_image.rb +1 -1
  109. data/app/models/spotlight/field_metadata.rb +10 -5
  110. data/app/models/spotlight/job_tracker.rb +0 -18
  111. data/app/models/spotlight/main_navigation.rb +2 -2
  112. data/app/models/spotlight/page.rb +3 -3
  113. data/app/models/spotlight/resources/iiif_harvester.rb +7 -4
  114. data/app/models/spotlight/resources/iiif_manifest.rb +0 -4
  115. data/app/models/spotlight/resources/iiif_manifest_metadata.rb +1 -3
  116. data/app/models/spotlight/resources/iiif_service.rb +21 -6
  117. data/app/models/spotlight/resources/upload.rb +6 -0
  118. data/app/models/spotlight/search.rb +5 -9
  119. data/app/models/spotlight/solr_document_sidecar.rb +18 -0
  120. data/app/services/spotlight/bulk_updates_csv_template_service.rb +1 -1
  121. data/app/services/spotlight/etl/pipeline.rb +2 -2
  122. data/app/services/spotlight/etl/step.rb +2 -2
  123. data/app/views/catalog/_remove_tags.html.erb +2 -2
  124. data/app/views/layouts/spotlight/base.html.erb +3 -6
  125. data/app/views/layouts/spotlight/spotlight.html.erb +2 -2
  126. data/app/views/shared/_analytics.html.erb +4 -3
  127. data/app/views/shared/_masthead.html.erb +2 -18
  128. data/app/views/spotlight/about_pages/_empty.html.erb +1 -1
  129. data/app/views/spotlight/about_pages/_sidebar.html.erb +10 -8
  130. data/app/views/spotlight/browse/_search.html.erb +2 -2
  131. data/app/views/spotlight/browse/_search_box.html.erb +6 -14
  132. data/app/views/spotlight/browse/index.html.erb +1 -1
  133. data/app/views/spotlight/browse/show.html.erb +1 -1
  134. data/app/views/spotlight/catalog/_document_admin_table.html.erb +1 -1
  135. data/app/views/spotlight/catalog/_edit_default.html.erb +1 -1
  136. data/app/views/spotlight/catalog/edit.html.erb +1 -1
  137. data/app/views/spotlight/dashboards/_page.html.erb +1 -1
  138. data/app/views/spotlight/exhibits/_exhibit_card.html.erb +3 -1
  139. data/app/views/spotlight/exhibits/_export.html.erb +17 -1
  140. data/app/views/spotlight/exhibits/_form.html.erb +20 -4
  141. data/app/views/spotlight/exhibits/_missing_exhibits.html.erb +3 -3
  142. data/app/views/spotlight/exhibits/index.html.erb +21 -8
  143. data/app/views/spotlight/feature_pages/_empty.html.erb +1 -1
  144. data/app/views/spotlight/feature_pages/_sidebar.html.erb +17 -15
  145. data/app/views/spotlight/home_pages/_empty.html.erb +7 -7
  146. data/app/views/spotlight/pages/show.html.erb +1 -0
  147. data/app/views/spotlight/resources/upload/_form.html.erb +2 -2
  148. data/app/views/spotlight/sir_trevor/blocks/_browse_group_categories_block.html.erb +5 -16
  149. data/app/views/spotlight/sir_trevor/blocks/_embedded_document.html.erb +3 -5
  150. data/app/views/spotlight/sir_trevor/blocks/_link_to_search_block.html.erb +1 -1
  151. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_carousel_block.html.erb +11 -2
  152. data/app/views/spotlight/sites/_exhibit.html.erb +1 -1
  153. data/config/i18n-tasks.yml +1 -1
  154. data/config/locales/spotlight.ar.yml +1 -2
  155. data/config/locales/spotlight.de.yml +1 -2
  156. data/config/locales/spotlight.en.yml +39 -10
  157. data/config/locales/spotlight.es.yml +1 -2
  158. data/config/locales/spotlight.fr.yml +1 -2
  159. data/config/locales/spotlight.it.yml +1 -2
  160. data/config/locales/spotlight.ja.yml +39 -0
  161. data/config/locales/spotlight.zh.yml +1 -2
  162. data/db/migrate/20261007133748_add_discovery_enabled_to_spotlight_exhibits.rb +5 -0
  163. data/lib/generators/spotlight/assets/importmap_generator.rb +2 -10
  164. data/lib/generators/spotlight/assets/propshaft_generator.rb +5 -4
  165. data/lib/generators/spotlight/install_generator.rb +2 -4
  166. data/lib/generators/spotlight/templates/catalog_controller.rb +0 -1
  167. data/lib/generators/spotlight/templates/config/initializers/spotlight_initializer.rb +26 -4
  168. data/lib/generators/spotlight/templates/solr/conf/schema.xml +4 -3
  169. data/lib/generators/spotlight/templates/solr/conf/solrconfig.xml +3 -8
  170. data/lib/migration/page_language.rb +0 -2
  171. data/lib/spotlight/engine.rb +28 -9
  172. data/lib/spotlight/search_state.rb +7 -11
  173. data/lib/spotlight/upload_field_config.rb +19 -1
  174. data/lib/spotlight/version.rb +1 -1
  175. data/lib/spotlight.rb +1 -1
  176. data/spec/support/features/capybara_wait_metadata_helper.rb +3 -1
  177. data/spec/support/features/test_features_helpers.rb +24 -1
  178. data/spec/support/iiif_manifest_fixtures.rb +33 -0
  179. data/spec/support/stub_dns_resolution.rb +12 -0
  180. metadata +50 -80
  181. data/app/assets/stylesheets/spotlight/_uploaded_items_block.scss +0 -7
  182. data/app/assets/stylesheets/spotlight/_variables.scss +0 -13
  183. data/app/components/blacklight/icons/arrow_alt_circle_left_component.rb +0 -15
  184. data/app/components/blacklight/icons/arrow_alt_circle_right_component.rb +0 -15
  185. data/app/components/blacklight/icons/info_component.rb +0 -15
  186. data/app/components/blacklight/icons/zoom_in_component.rb +0 -15
  187. data/app/components/blacklight/icons/zoom_out_component.rb +0 -15
  188. data/app/components/spotlight/icon_component.rb +0 -13
  189. data/app/javascript/spotlight/user/browse_group_categories.js +0 -54
  190. data/app/mailers/spotlight/invitation_mailer.rb +0 -21
  191. data/app/models/concerns/spotlight/resources/web.rb +0 -38
  192. data/app/services/spotlight/iiif_resource_resolver.rb +0 -78
  193. data/app/views/catalog/_image_default.html.erb +0 -6
  194. data/app/views/shared/_exhibit_navbar.html.erb +0 -2
  195. data/app/views/shared/_header_navbar.html.erb +0 -6
  196. data/app/views/spotlight/catalog/_admin_index_header_default.html.erb +0 -23
  197. data/app/views/spotlight/catalog/_admin_thumbnail_default.html.erb +0 -7
  198. data/app/views/spotlight/catalog/_curation_mode_toggle_default.html.erb +0 -1
  199. data/app/views/spotlight/catalog/_document_row.html.erb +0 -2
  200. data/app/views/spotlight/catalog/_edit_sidecar.html.erb +0 -24
  201. data/app/views/spotlight/catalog/_index_compact_default.html.erb +0 -21
  202. data/app/views/spotlight/custom_fields/form_group/_text.html.erb +0 -23
  203. data/app/views/spotlight/custom_fields/form_group/_vocab.html.erb +0 -23
  204. data/app/views/spotlight/invitation_mailer/exhibit_invitation_notification.html.erb +0 -12
  205. data/lib/generators/spotlight/templates/javascript/jquery-shim.js +0 -1
  206. data/vendor/assets/javascripts/tiny-slider.js +0 -3221
  207. data/vendor/assets/stylesheets/tiny-slider.css +0 -1
  208. /data/app/assets/stylesheets/spotlight/{_breadcrumbs.scss → _breadcrumbs.css} +0 -0
  209. /data/app/assets/stylesheets/spotlight/{_croppable.scss → _croppable.css} +0 -0
  210. /data/app/assets/stylesheets/spotlight/{_modals.scss → _modals.css} +0 -0
  211. /data/app/assets/stylesheets/spotlight/{_utilities.scss → _utilities.css} +0 -0
@@ -4,18 +4,28 @@ import Core from "spotlight/core"
4
4
  export default class Crop {
5
5
  constructor(cropArea, preserveAspectRatio = true) {
6
6
  this.cropArea = cropArea
7
- this.cropArea.data("iiifCropper", this)
8
- // This element will also have the IIIF input elements contained
9
- // There may be multiple elements with data-cropper attributes, but
10
- // there should only one element with this data-cropper attribute value.
11
- this.cropSelector = '[data-cropper="' + cropArea.data("cropperKey") + '"]'
12
- this.cropTool = $(this.cropSelector)
7
+ if (this.cropArea) {
8
+ this.cropArea.iiifCropper = this
9
+ }
10
+
11
+ // Get the cropper key and find the crop tool element
12
+ const cropperKey = this.cropArea
13
+ ? this.cropArea.dataset.cropperKey ||
14
+ this.cropArea.getAttribute("data-cropper-key")
15
+ : null
16
+ this.cropSelector = '[data-cropper="' + cropperKey + '"]'
17
+ this.cropTool = document.querySelector(this.cropSelector)
18
+
13
19
  // Exhibit and masthead cropping requires the ratio between image width and height
14
20
  // to be consistent, whereas item widget cropping allows any combination of
15
21
  // image width and height.
16
22
  this.preserveAspectRatio = preserveAspectRatio
23
+
17
24
  // Get the IIIF input elements used to store/reference IIIF information
18
- this.inputPrefix = this.cropTool.data("input-prefix")
25
+ this.inputPrefix = this.cropTool
26
+ ? this.cropTool.dataset.inputPrefix ||
27
+ this.cropTool.getAttribute("data-input-prefix")
28
+ : null
19
29
  this.iiifUrlField = this.iiifInputElement(
20
30
  this.inputPrefix,
21
31
  "iiif_tilesource",
@@ -41,8 +51,9 @@ export default class Crop {
41
51
  "iiif_image_id",
42
52
  this.cropTool,
43
53
  )
54
+
44
55
  // Get the closest form element
45
- this.form = cropArea.closest("form")
56
+ this.form = this.cropArea ? this.cropArea.closest("form") : null
46
57
  this.tileSource = null
47
58
  }
48
59
 
@@ -50,10 +61,15 @@ export default class Crop {
50
61
  // Multiple input fields with the same name on the page may be related
51
62
  // to a cropper. We thus need to pass in a parent element.
52
63
  iiifInputElement(inputPrefix, fieldName, inputParentElement) {
53
- return $(
54
- 'input[name="' + inputPrefix + "[" + fieldName + ']"]',
55
- inputParentElement,
56
- )
64
+ if (inputParentElement && inputPrefix) {
65
+ const selector = 'input[name="' + inputPrefix + "[" + fieldName + ']"]'
66
+ const element = inputParentElement.querySelector(selector)
67
+ if (element) {
68
+ return element
69
+ }
70
+ }
71
+ // Return a dummy object to prevent null-pointer exceptions
72
+ return { value: undefined }
57
73
  }
58
74
 
59
75
  // Render the cropper environment and add hooks into the autocomplete and upload forms
@@ -66,12 +82,12 @@ export default class Crop {
66
82
  // Setup the cropper on page load if the field
67
83
  // that holds the IIIF url is populated
68
84
  setupExistingIiifCropper() {
69
- if (this.iiifUrlField.val() === "") {
85
+ if (this.iiifUrlField.value === "") {
70
86
  return
71
87
  }
72
88
 
73
89
  this.addImageSelectorToExistingCropTool()
74
- this.setTileSource(this.iiifUrlField.val())
90
+ this.setTileSource(this.iiifUrlField.value)
75
91
  }
76
92
 
77
93
  // Display the IIIF Cropper map with the current IIIF Layer (and cropbox, once the layer is available)
@@ -85,7 +101,7 @@ export default class Crop {
85
101
  // Code in leaflet-iiif land calls delete on the image layer's container when removing,
86
102
  // which errors if there is an issue fetching the info.json and stops further necessary steps to execute.
87
103
  if (!this.imageLayer._container) {
88
- this.imageLayer._container = $("<div></div>")
104
+ this.imageLayer._container = document.createElement("div")
89
105
  }
90
106
  this.cropperMap.removeLayer(this.imageLayer)
91
107
  }
@@ -101,15 +117,24 @@ export default class Crop {
101
117
  }
102
118
  })
103
119
 
104
- this.cropArea.data("initiallyVisible", this.cropArea.is(":visible"))
120
+ this.cropAreaInitiallyVisible = this.isCropAreaVisible()
121
+ }
122
+
123
+ isCropAreaVisible() {
124
+ if (!this.cropArea) return false
125
+ return !!(
126
+ this.cropArea.offsetWidth ||
127
+ this.cropArea.offsetHeight ||
128
+ this.cropArea.getClientRects().length
129
+ )
105
130
  }
106
131
 
107
132
  // Get (or initialize) the current crop region from the form data
108
133
  getCropRegion() {
109
- var regionFieldValue = this.iiifRegionField.val()
134
+ var regionFieldValue = this.iiifRegionField.value
110
135
  if (!regionFieldValue || regionFieldValue === "") {
111
136
  var region = this.defaultCropRegion()
112
- this.iiifRegionField.val(region)
137
+ this.iiifRegionField.value = region
113
138
  return region
114
139
  } else {
115
140
  return regionFieldValue.split(",")
@@ -134,8 +159,15 @@ export default class Crop {
134
159
 
135
160
  // Calculate the required aspect ratio for the crop area
136
161
  aspectRatio() {
137
- var cropWidth = parseInt(this.cropArea.data("crop-width"))
138
- var cropHeight = parseInt(this.cropArea.data("crop-height"))
162
+ if (!this.cropArea) return 1
163
+ var cropWidth = parseInt(
164
+ this.cropArea.dataset.cropWidth ||
165
+ this.cropArea.getAttribute("data-crop-width"),
166
+ )
167
+ var cropHeight = parseInt(
168
+ this.cropArea.dataset.cropHeight ||
169
+ this.cropArea.getAttribute("data-crop-height"),
170
+ )
139
171
  return cropWidth / cropHeight
140
172
  }
141
173
 
@@ -160,9 +192,9 @@ export default class Crop {
160
192
  // the appropriate IIIF URL or identifier
161
193
  setIiifFields(iiifObject) {
162
194
  this.setTileSource(iiifObject.tilesource)
163
- this.iiifManifestField.val(iiifObject.manifest)
164
- this.iiifCanvasField.val(iiifObject.canvasId)
165
- this.iiifImageField.val(iiifObject.imageId)
195
+ this.iiifManifestField.value = iiifObject.manifest
196
+ this.iiifCanvasField.value = iiifObject.canvasId
197
+ this.iiifImageField.value = iiifObject.imageId
166
198
  }
167
199
 
168
200
  // Set the Crop tileSource and setup the cropper
@@ -177,17 +209,17 @@ export default class Crop {
177
209
  }
178
210
 
179
211
  if (this.cropBox) {
180
- this.iiifRegionField.val("")
212
+ this.iiifRegionField.value = ""
181
213
  }
182
214
 
183
215
  this.tileSource = source
184
- this.iiifUrlField.val(source)
216
+ this.iiifUrlField.value = source
185
217
  this.setupIiifCropper()
186
218
  }
187
219
 
188
220
  // Render the Leaflet Map into the crop area
189
221
  renderCropperMap() {
190
- if (this.cropperMap) {
222
+ if (this.cropperMap || !this.cropArea) {
191
223
  return
192
224
  }
193
225
 
@@ -206,7 +238,10 @@ export default class Crop {
206
238
  }
207
239
  }
208
240
 
209
- this.cropperMap = L.map(this.cropArea.attr("id"), cropperOptions)
241
+ this.cropperMap = L.map(
242
+ this.cropArea.getAttribute("id") || this.cropArea.id,
243
+ cropperOptions,
244
+ )
210
245
  this.invalidateMapSizeOnTabToggle()
211
246
  }
212
247
 
@@ -226,7 +261,7 @@ export default class Crop {
226
261
  var bounds = e.layer.getBounds()
227
262
  var region = self.projectBoundsToIIIFRegion(bounds)
228
263
 
229
- self.iiifRegionField.val(region.join(","))
264
+ self.iiifRegionField.value = region.join(",")
230
265
  },
231
266
  )
232
267
  }
@@ -267,76 +302,114 @@ export default class Crop {
267
302
 
268
303
  // Setup autocomplete inputs to have the iiif_cropper context
269
304
  setupAutoCompletes() {
270
- var input = $('[data-behavior="autocomplete"]', this.cropTool)
271
- input.data("iiifCropper", this)
305
+ if (!this.cropTool) return
306
+ var input = this.cropTool.querySelector('[data-behavior="autocomplete"]')
307
+ if (input) {
308
+ input.iiifCropper = this
309
+ }
272
310
  }
273
311
 
274
312
  setupAjaxFileUpload() {
275
- this.fileInput = $('input[type="file"]', this.cropTool)
276
- this.fileInput.change(() => this.uploadFile())
313
+ if (!this.cropTool) return
314
+ this.fileInput = this.cropTool.querySelector('input[type="file"]')
315
+ if (this.fileInput) {
316
+ this.fileInput.addEventListener("change", () => this.uploadFile())
317
+ }
277
318
  }
278
319
 
279
320
  addImageSelectorToExistingCropTool() {
280
- if (this.iiifManifestField.val() === "") {
321
+ if (this.iiifManifestField.value === "") {
281
322
  return
282
323
  }
283
324
 
284
- var input = $('[data-behavior="autocomplete"]', this.cropTool)
325
+ if (!this.cropTool) {
326
+ return
327
+ }
328
+
329
+ var inputElement = this.cropTool.querySelector(
330
+ '[data-behavior="autocomplete"]',
331
+ )
285
332
 
286
333
  // Not every page which uses this module has autocomplete linked directly to the cropping tool
287
- if (input.length) {
288
- var panel = $(input.data("target-panel"))
289
- addImageSelector(
290
- input,
291
- panel,
292
- this.iiifManifestField.val(),
293
- !this.iiifImageField.val(),
294
- )
334
+ if (inputElement) {
335
+ var targetPanel =
336
+ inputElement.dataset.targetPanel ||
337
+ inputElement.getAttribute("data-target-panel")
338
+ var panelElement = document.querySelector(targetPanel)
339
+ if (panelElement) {
340
+ addImageSelector(
341
+ inputElement,
342
+ panelElement,
343
+ this.iiifManifestField.value,
344
+ !this.iiifImageField.value,
345
+ )
346
+ }
295
347
  }
296
348
  }
297
349
 
298
350
  invalidateMapSizeOnTabToggle() {
299
- var tabs = $('[role="tablist"]', this.form)
351
+ if (!this.form) return
352
+ var tabs = this.form.querySelectorAll('[role="tablist"]')
300
353
  var self = this
301
- tabs.on("shown.bs.tab", function () {
302
- if (
303
- self.cropArea.data("initiallyVisible") === false &&
304
- self.cropArea.is(":visible")
305
- ) {
354
+ var onTabShown = function () {
355
+ if (self.cropAreaInitiallyVisible === false && self.isCropAreaVisible()) {
306
356
  self.cropperMap.invalidateSize()
307
357
  // Because the map size is 0,0 when image is loading (not visible) we need to refit the bounds of the layer
308
358
  self.imageLayer._fitBounds()
309
- self.cropArea.data("initiallyVisible", null)
359
+ self.cropAreaInitiallyVisible = null
310
360
  }
361
+ }
362
+
363
+ tabs.forEach((tab) => {
364
+ tab.addEventListener("shown.bs.tab", onTabShown)
311
365
  })
312
366
  }
313
367
 
314
368
  // Get all the form data with the exception of the _method field.
315
369
  getData() {
316
- var data = new FormData(this.form[0])
370
+ if (!this.form) return null
371
+ var data = new FormData(this.form)
317
372
  data.append("_method", null)
318
373
  return data
319
374
  }
320
375
 
321
376
  uploadFile() {
322
- var url = this.fileInput.data("endpoint")
377
+ if (!this.fileInput) return
378
+ var url =
379
+ this.fileInput.dataset.endpoint ||
380
+ this.fileInput.getAttribute("data-endpoint")
323
381
  // Every post creates a new image/masthead.
324
382
  // Because they create IIIF urls which are heavily cached.
325
- $.ajax({
326
- url: url, //Server script to process data
327
- type: "POST",
328
- success: (data, stat, xhr) => this.successHandler(data, stat, xhr),
329
- error: (xhr, stat, error) => this.errorHandler(xhr, stat, error),
330
- // Form data
331
- data: this.getData(),
383
+ fetch(url, {
384
+ method: "POST",
332
385
  headers: {
333
386
  "X-CSRF-Token": Core.csrfToken() || "",
387
+ Accept: "application/json",
334
388
  },
335
- //Options to tell jQuery not to process data or worry about content-type.
336
- cache: false,
337
- contentType: false,
338
- processData: false,
389
+ body: this.getData(),
339
390
  })
391
+ .then((response) => {
392
+ if (!response.ok) {
393
+ return response.json().then(
394
+ (json) => {
395
+ var fakeXhr = { responseJSON: json }
396
+ this.errorHandler(fakeXhr, "error", response.statusText)
397
+ },
398
+ () => {
399
+ this.errorHandler({}, "error", "Upload failed")
400
+ },
401
+ )
402
+ }
403
+ return response.json()
404
+ })
405
+ .then((data) => {
406
+ if (data) {
407
+ this.successHandler(data, "success", null)
408
+ }
409
+ })
410
+ .catch((error) => {
411
+ this.errorHandler({}, "error", error.message)
412
+ })
340
413
  }
341
414
 
342
415
  successHandler(data, _stat, _xhr) {
@@ -358,13 +431,15 @@ export default class Crop {
358
431
  }
359
432
 
360
433
  getUploadErrorsElement() {
361
- return this.cropTool.find(".featured-image.invalid-feedback")
434
+ if (!this.cropTool) return null
435
+ return this.cropTool.querySelector(".featured-image.invalid-feedback")
362
436
  }
363
437
 
364
438
  showUploadError(errorMessage) {
365
439
  const errorsElement = this.getUploadErrorsElement()
366
440
  if (errorsElement) {
367
- errorsElement.text(errorMessage).show()
441
+ errorsElement.textContent = errorMessage
442
+ errorsElement.style.display = "block"
368
443
  } else {
369
444
  console.error("uploadFile", errorMessage)
370
445
  }
@@ -373,7 +448,8 @@ export default class Crop {
373
448
  clearUploadErrors() {
374
449
  const errorsElement = this.getUploadErrorsElement()
375
450
  if (errorsElement) {
376
- errorsElement.text("").hide()
451
+ errorsElement.textContent = ""
452
+ errorsElement.style.display = "none"
377
453
  }
378
454
  }
379
455
 
@@ -382,7 +458,11 @@ export default class Crop {
382
458
  // The name should be sufficient in this case, as we don't use this part of the
383
459
  // code for solr document widgets where we enable cropping.
384
460
  // If we require more specificity, we can scope this to this.cropTool.
385
- $('input[name="' + this.inputPrefix + '[upload_id]"]').val(id)
461
+ const selector = 'input[name="' + this.inputPrefix + '[upload_id]"]'
462
+ const element = document.querySelector(selector)
463
+ if (element) {
464
+ element.value = id
465
+ }
386
466
  }
387
467
 
388
468
  aspectRatioPreservingRectangleEditor(aspect) {
@@ -5,10 +5,11 @@ export default class Croppable {
5
5
  connect() {
6
6
  // For exhibit masthead or thumbnail pages, where
7
7
  // the div exists on page load
8
- $('[data-behavior="iiif-cropper"]').each(function () {
9
- var cropElement = $(this)
10
- new Crop(cropElement).render()
11
- })
8
+ document
9
+ .querySelectorAll('[data-behavior="iiif-cropper"]')
10
+ .forEach((cropElement) => {
11
+ new Crop(cropElement).render()
12
+ })
12
13
 
13
14
  // In the case of individual document thumbnails, selection
14
15
  // of the image is through a modal. Here we attach the event
@@ -13,7 +13,7 @@ export default class CroppableModal {
13
13
  document.addEventListener(
14
14
  "loaded.blacklight.blacklight-modal",
15
15
  function () {
16
- var dataCropperDiv = $(
16
+ const dataCropperDiv = document.querySelector(
17
17
  '#blacklight-modal [data-behavior="iiif-cropper"]',
18
18
  )
19
19
 
@@ -26,65 +26,100 @@ export default class CroppableModal {
26
26
 
27
27
  // Field names are of the format item[item_0][iiif_image_id]
28
28
  iiifInputField(itemIndex, fieldName, parentElement) {
29
- var itemPrefix = "item[" + itemIndex + "]"
30
- var selector = 'input[name="' + itemPrefix + "[" + fieldName + ']"]'
31
- return $(selector, parentElement)
29
+ const itemPrefix = "item[" + itemIndex + "]"
30
+ const selector = 'input[name="' + itemPrefix + "[" + fieldName + ']"]'
31
+ return parentElement ? parentElement.querySelector(selector) : null
32
32
  }
33
33
 
34
34
  attachModalSaveHandler() {
35
- var context = this
35
+ const context = this
36
36
 
37
37
  document.addEventListener("show.blacklight.blacklight-modal", function () {
38
- $("#save-cropping-selection").on("click", () => {
39
- context.saveCroppedRegion()
40
- })
38
+ const saveBtn = document.getElementById("save-cropping-selection")
39
+ if (saveBtn) {
40
+ saveBtn.addEventListener("click", () => {
41
+ context.saveCroppedRegion()
42
+ })
43
+ }
41
44
  })
42
45
  }
43
46
 
44
47
  saveCroppedRegion() {
45
48
  //On hitting "save changes", we need to copy over the value
46
49
  //to the iiif thumbnail url input field as well as the image source itself
47
- var context = this
48
- var dataCropperDiv = $('#blacklight-modal [data-behavior="iiif-cropper"]')
50
+ const context = this
51
+ const dataCropperDiv = document.querySelector(
52
+ '#blacklight-modal [data-behavior="iiif-cropper"]',
53
+ )
49
54
 
50
55
  if (dataCropperDiv) {
51
- var dataCropperKey = dataCropperDiv.data("cropper-key")
52
- var itemIndex = dataCropperDiv.data("index-id")
56
+ const dataCropperKey =
57
+ dataCropperDiv.dataset.cropperKey ||
58
+ dataCropperDiv.getAttribute("data-cropper-key")
59
+ const itemIndex =
60
+ dataCropperDiv.dataset.indexId ||
61
+ dataCropperDiv.getAttribute("data-index-id")
62
+
53
63
  // Get the element on the main edit page whose select image link opened up the modal
54
- var itemElement = $('[data-cropper="' + dataCropperKey + '"]')
64
+ const itemElement = document.querySelector(
65
+ '[data-cropper="' + dataCropperKey + '"]',
66
+ )
67
+ if (!itemElement) return
68
+
55
69
  // Get the hidden input field on the main edit page corresponding to this item
56
- var thumbnailSaveField = context.iiifInputField(
70
+ const thumbnailSaveField = context.iiifInputField(
57
71
  itemIndex,
58
72
  "thumbnail_image_url",
59
73
  itemElement,
60
74
  )
61
- var fullimageSaveField = context.iiifInputField(
75
+ const fullimageSaveField = context.iiifInputField(
62
76
  itemIndex,
63
77
  "full_image_url",
64
78
  itemElement,
65
79
  )
66
- var iiifTilesource = context
67
- .iiifInputField(itemIndex, "iiif_tilesource", itemElement)
68
- .val()
69
- var regionValue = context
70
- .iiifInputField(itemIndex, "iiif_region", itemElement)
71
- .val()
72
- // Extract the region string to incorporate into the thumbnail URL
73
- var urlPrefix = iiifTilesource.substring(
74
- 0,
75
- iiifTilesource.lastIndexOf("/info.json"),
80
+
81
+ const iiifTilesourceField = context.iiifInputField(
82
+ itemIndex,
83
+ "iiif_tilesource",
84
+ itemElement,
76
85
  )
77
- var thumbnailUrl =
86
+ const regionValueField = context.iiifInputField(
87
+ itemIndex,
88
+ "iiif_region",
89
+ itemElement,
90
+ )
91
+
92
+ const iiifTilesource = iiifTilesourceField
93
+ ? iiifTilesourceField.value
94
+ : ""
95
+ const regionValue = regionValueField ? regionValueField.value : ""
96
+
97
+ // Extract the region string to incorporate into the thumbnail URL
98
+ const lastIndex = iiifTilesource.lastIndexOf("/info.json")
99
+ const urlPrefix =
100
+ lastIndex !== -1
101
+ ? iiifTilesource.substring(0, lastIndex)
102
+ : iiifTilesource
103
+ const thumbnailUrl =
78
104
  urlPrefix + "/" + regionValue + "/!400,400/0/default.jpg"
105
+
79
106
  // Set the hidden input value to the thumbnail URL
80
107
  // Also set the full image - which is used by widgets like carousel or slideshow
81
- thumbnailSaveField.val(thumbnailUrl)
82
- fullimageSaveField.val(
83
- urlPrefix + "/" + regionValue + "/!800,800/0/default.jpg",
84
- )
108
+ if (thumbnailSaveField) {
109
+ thumbnailSaveField.value = thumbnailUrl
110
+ thumbnailSaveField.dispatchEvent(new Event("change", { bubbles: true }))
111
+ }
112
+ if (fullimageSaveField) {
113
+ fullimageSaveField.value =
114
+ urlPrefix + "/" + regionValue + "/!800,800/0/default.jpg"
115
+ fullimageSaveField.dispatchEvent(new Event("change", { bubbles: true }))
116
+ }
117
+
85
118
  // Also change img url for thumbnail image
86
- var itemImage = $("img.img-thumbnail", itemElement)
87
- itemImage.attr("src", thumbnailUrl)
119
+ const itemImage = itemElement.querySelector("img.img-thumbnail")
120
+ if (itemImage) {
121
+ itemImage.setAttribute("src", thumbnailUrl)
122
+ }
88
123
  }
89
124
  }
90
125
  }