blacklight-spotlight 5.3.0 → 5.3.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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -1
  3. data/app/assets/javascripts/spotlight/spotlight.esm.js +1679 -1228
  4. data/app/assets/javascripts/spotlight/spotlight.esm.js.map +1 -1
  5. data/app/assets/javascripts/spotlight/spotlight.js +1682 -1231
  6. data/app/assets/javascripts/spotlight/spotlight.js.map +1 -1
  7. data/app/components/spotlight/analytics/aggregation_component.html.erb +1 -1
  8. data/app/components/spotlight/document_admin_table_component.html.erb +2 -2
  9. data/app/components/spotlight/edit_view_links_component.html.erb +1 -1
  10. data/app/components/spotlight/header_component.html.erb +1 -1
  11. data/app/components/spotlight/tag_list_form_component.html.erb +1 -1
  12. data/app/components/spotlight/tag_selector_component.html.erb +2 -2
  13. data/app/components/spotlight/translations/subheading_component.html.erb +1 -1
  14. data/app/controllers/spotlight/resources_controller.rb +1 -1
  15. data/app/javascript/spotlight/admin/add_another.js +22 -15
  16. data/app/javascript/spotlight/admin/add_image_selector.js +27 -25
  17. data/app/javascript/spotlight/admin/add_new_button.js +47 -43
  18. data/app/javascript/spotlight/admin/blacklight_configuration.js +52 -38
  19. data/app/javascript/spotlight/admin/block_mixins/autocompleteable.js +68 -60
  20. data/app/javascript/spotlight/admin/block_mixins/formable.js +55 -47
  21. data/app/javascript/spotlight/admin/block_mixins/plustextable.js +19 -20
  22. data/app/javascript/spotlight/admin/blocks/block.js +20 -16
  23. data/app/javascript/spotlight/admin/blocks/browse_block.js +42 -37
  24. data/app/javascript/spotlight/admin/blocks/browse_group_categories_block.js +33 -31
  25. data/app/javascript/spotlight/admin/blocks/iframe_block.js +14 -11
  26. data/app/javascript/spotlight/admin/blocks/link_to_search_block.js +4 -7
  27. data/app/javascript/spotlight/admin/blocks/oembed_block.js +13 -10
  28. data/app/javascript/spotlight/admin/blocks/pages_block.js +20 -13
  29. data/app/javascript/spotlight/admin/blocks/resources_block.js +150 -117
  30. data/app/javascript/spotlight/admin/blocks/rule_block.js +11 -10
  31. data/app/javascript/spotlight/admin/blocks/search_result_block.js +16 -17
  32. data/app/javascript/spotlight/admin/blocks/solr_documents_base_block.js +123 -64
  33. data/app/javascript/spotlight/admin/blocks/solr_documents_block.js +8 -8
  34. data/app/javascript/spotlight/admin/blocks/solr_documents_carousel_block.js +63 -43
  35. data/app/javascript/spotlight/admin/blocks/solr_documents_embed_block.js +9 -9
  36. data/app/javascript/spotlight/admin/blocks/solr_documents_features_block.js +17 -16
  37. data/app/javascript/spotlight/admin/blocks/solr_documents_grid_block.js +6 -7
  38. data/app/javascript/spotlight/admin/blocks/uploaded_items_block.js +84 -64
  39. data/app/javascript/spotlight/admin/crop.js +203 -156
  40. data/app/javascript/spotlight/admin/croppable.js +4 -4
  41. data/app/javascript/spotlight/admin/croppable_modal.js +59 -37
  42. data/app/javascript/spotlight/admin/edit_in_place.js +50 -47
  43. data/app/javascript/spotlight/admin/exhibits.js +52 -41
  44. data/app/javascript/spotlight/admin/form_observer.js +59 -53
  45. data/app/javascript/spotlight/admin/iiif.js +26 -25
  46. data/app/javascript/spotlight/admin/index.js +44 -47
  47. data/app/javascript/spotlight/admin/locks.js +14 -14
  48. data/app/javascript/spotlight/admin/multi_image_selector.js +127 -111
  49. data/app/javascript/spotlight/admin/pages.js +21 -21
  50. data/app/javascript/spotlight/admin/progress_monitor.js +67 -65
  51. data/app/javascript/spotlight/admin/readonly_checkbox.js +3 -3
  52. data/app/javascript/spotlight/admin/search_typeahead.js +55 -43
  53. data/app/javascript/spotlight/admin/select_related_input.js +17 -17
  54. data/app/javascript/spotlight/admin/sir-trevor/block_controls.js +100 -84
  55. data/app/javascript/spotlight/admin/sir-trevor/block_limits.js +36 -31
  56. data/app/javascript/spotlight/admin/sir-trevor/locales.js +52 -39
  57. data/app/javascript/spotlight/admin/spotlight_nestable.js +112 -90
  58. data/app/javascript/spotlight/admin/tabs.js +16 -3
  59. data/app/javascript/spotlight/admin/translation_progress.js +13 -7
  60. data/app/javascript/spotlight/admin/users.js +41 -41
  61. data/app/javascript/spotlight/admin/visibility_toggle.js +4 -4
  62. data/app/javascript/spotlight/controllers/clipboard_controller.js +14 -0
  63. data/app/javascript/spotlight/controllers/index.js +4 -2
  64. data/app/javascript/spotlight/controllers/tag_selector_controller.js +116 -79
  65. data/app/javascript/spotlight/core.js +18 -15
  66. data/app/javascript/spotlight/index.js +4 -4
  67. data/app/javascript/spotlight/user/browse_group_categories.js +24 -22
  68. data/app/javascript/spotlight/user/carousel.js +23 -19
  69. data/app/javascript/spotlight/user/clear_form_button.js +15 -15
  70. data/app/javascript/spotlight/user/index.js +4 -4
  71. data/app/javascript/spotlight/user/zpr_links.js +18 -18
  72. data/app/models/spotlight/analytics/ga.rb +0 -1
  73. data/app/models/spotlight/resources/iiif_manifest_v3.rb +2 -2
  74. data/app/services/spotlight/exhibit_import_export_service.rb +0 -2
  75. data/app/views/blacklight/nav/_bookmark.html.erb +1 -1
  76. data/app/views/catalog/_remove_tags.html.erb +1 -1
  77. data/app/views/layouts/spotlight/base.html.erb +1 -1
  78. data/app/views/shared/_header_navbar.html.erb +1 -1
  79. data/app/views/shared/_masthead.html.erb +2 -2
  80. data/app/views/shared/_site_sidebar.html.erb +1 -1
  81. data/app/views/shared/_user_util_links.html.erb +2 -2
  82. data/app/views/spotlight/about_pages/_contacts.html.erb +1 -1
  83. data/app/views/spotlight/accessibility/alt_text.html.erb +1 -1
  84. data/app/views/spotlight/admin_users/index.html.erb +11 -9
  85. data/app/views/spotlight/appearances/edit.html.erb +1 -1
  86. data/app/views/spotlight/browse/_search_box.html.erb +1 -1
  87. data/app/views/spotlight/bulk_updates/_download.html.erb +1 -1
  88. data/app/views/spotlight/bulk_updates/_progress_panel.html.erb +6 -6
  89. data/app/views/spotlight/bulk_updates/edit.html.erb +1 -1
  90. data/app/views/spotlight/catalog/_admin_header.html.erb +1 -1
  91. data/app/views/spotlight/catalog/_admin_index_header_default.html.erb +1 -1
  92. data/app/views/spotlight/catalog/_curation_mode_toggle_default.html.erb +1 -1
  93. data/app/views/spotlight/catalog/_document.html.erb +2 -2
  94. data/app/views/spotlight/catalog/_document_admin_table.html.erb +1 -1
  95. data/app/views/spotlight/catalog/_edit_sidecar.html.erb +1 -1
  96. data/app/views/spotlight/catalog/_index_compact_default.html.erb +1 -1
  97. data/app/views/spotlight/catalog/_reindex_progress_panel.html.erb +6 -6
  98. data/app/views/spotlight/catalog/select_image.html.erb +1 -1
  99. data/app/views/spotlight/contact_mailer/report_problem.html.erb +2 -2
  100. data/app/views/spotlight/exhibits/_contact.html.erb +2 -2
  101. data/app/views/spotlight/exhibits/_delete.html.erb +2 -2
  102. data/app/views/spotlight/exhibits/_exhibit_card.html.erb +1 -1
  103. data/app/views/spotlight/exhibits/_export.html.erb +1 -1
  104. data/app/views/spotlight/exhibits/_form.html.erb +2 -2
  105. data/app/views/spotlight/exhibits/_languages.html.erb +7 -7
  106. data/app/views/spotlight/exhibits/_missing_exhibits.html.erb +1 -1
  107. data/app/views/spotlight/locks/_lock.html.erb +2 -2
  108. data/app/views/spotlight/metadata_configurations/edit.html.erb +1 -1
  109. data/app/views/spotlight/pages/_order_pages.html.erb +3 -3
  110. data/app/views/spotlight/pages/preview.html.erb +1 -1
  111. data/app/views/spotlight/pages/show.html.erb +2 -2
  112. data/app/views/spotlight/resources/_form.html.erb +1 -1
  113. data/app/views/spotlight/resources/csv_upload/_form.html.erb +1 -1
  114. data/app/views/spotlight/resources/upload/_form.html.erb +1 -1
  115. data/app/views/spotlight/roles/index.html.erb +5 -5
  116. data/app/views/spotlight/search_configurations/_search_fields.html.erb +1 -1
  117. data/app/views/spotlight/search_configurations/edit.html.erb +1 -1
  118. data/app/views/spotlight/searches/index.html.erb +2 -2
  119. data/app/views/spotlight/shared/_configuration_sidebar.html.erb +1 -1
  120. data/app/views/spotlight/shared/_curation_sidebar.html.erb +1 -1
  121. data/app/views/spotlight/shared/_dd3_item.html.erb +1 -1
  122. data/app/views/spotlight/sir_trevor/_sir_trevor_block_array.html.erb +1 -1
  123. data/app/views/spotlight/sir_trevor/blocks/_browse_block.html.erb +1 -1
  124. data/app/views/spotlight/sir_trevor/blocks/_browse_group_categories_block.html.erb +1 -1
  125. data/app/views/spotlight/sir_trevor/blocks/_featured_pages_block.html.erb +2 -2
  126. data/app/views/spotlight/sir_trevor/blocks/_link_to_search_block.html.erb +1 -1
  127. data/app/views/spotlight/sir_trevor/blocks/_rule_block.html.erb +1 -1
  128. data/app/views/spotlight/sir_trevor/blocks/_search_results_block.html.erb +1 -1
  129. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_block.html.erb +1 -1
  130. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_carousel_block.html.erb +1 -1
  131. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_features_block.html.erb +1 -1
  132. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_grid_block.html.erb +1 -1
  133. data/app/views/spotlight/sites/edit_exhibits.html.erb +3 -3
  134. data/app/views/spotlight/translations/_browse_categories.html.erb +2 -2
  135. data/app/views/spotlight/translations/_general.html.erb +21 -21
  136. data/app/views/spotlight/translations/_groups.html.erb +1 -1
  137. data/app/views/spotlight/translations/_metadata.html.erb +8 -8
  138. data/app/views/spotlight/translations/_pages_table.html.erb +2 -2
  139. data/app/views/spotlight/translations/_search_fields.html.erb +13 -13
  140. data/app/views/spotlight/translations/edit.html.erb +5 -5
  141. data/config/importmap.rb +0 -1
  142. data/lib/generators/spotlight/assets/propshaft_generator.rb +0 -1
  143. data/lib/generators/spotlight/templates/solr/conf/admin-extra.html +31 -31
  144. data/lib/spotlight/version.rb +1 -1
  145. data/spec/factories/contact_images.rb +1 -1
  146. data/spec/factories/exhibit_thumbnails.rb +1 -1
  147. data/spec/factories/featured_images.rb +1 -1
  148. data/spec/support/features/test_features_helpers.rb +10 -1
  149. metadata +4 -61
  150. data/app/javascript/spotlight/admin/copy_email_addresses.js +0 -7
  151. data/vendor/assets/javascripts/jquery.waitforimages.min.js +0 -2
@@ -1,314 +1,355 @@
1
- import { addImageSelector } from 'spotlight/admin/add_image_selector'
2
- import Core from 'spotlight/core'
1
+ import { addImageSelector } from "spotlight/admin/add_image_selector"
2
+ import Core from "spotlight/core"
3
3
 
4
4
  export default class Crop {
5
5
  constructor(cropArea, preserveAspectRatio = true) {
6
- this.cropArea = cropArea;
7
- this.cropArea.data('iiifCropper', this);
6
+ this.cropArea = cropArea
7
+ this.cropArea.data("iiifCropper", this)
8
8
  // This element will also have the IIIF input elements contained
9
9
  // There may be multiple elements with data-cropper attributes, but
10
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);
11
+ this.cropSelector = '[data-cropper="' + cropArea.data("cropperKey") + '"]'
12
+ this.cropTool = $(this.cropSelector)
13
13
  // Exhibit and masthead cropping requires the ratio between image width and height
14
- // to be consistent, whereas item widget cropping allows any combination of
14
+ // to be consistent, whereas item widget cropping allows any combination of
15
15
  // image width and height.
16
- this.preserveAspectRatio = preserveAspectRatio;
16
+ this.preserveAspectRatio = preserveAspectRatio
17
17
  // Get the IIIF input elements used to store/reference IIIF information
18
- this.inputPrefix = this.cropTool.data('input-prefix');
19
- this.iiifUrlField = this.iiifInputElement(this.inputPrefix, 'iiif_tilesource', this.cropTool);
20
- this.iiifRegionField = this.iiifInputElement(this.inputPrefix, 'iiif_region', this.cropTool);
21
- this.iiifManifestField = this.iiifInputElement(this.inputPrefix, 'iiif_manifest_url', this.cropTool);
22
- this.iiifCanvasField = this.iiifInputElement(this.inputPrefix, 'iiif_canvas_id', this.cropTool);
23
- this.iiifImageField = this.iiifInputElement(this.inputPrefix, 'iiif_image_id', this.cropTool);
18
+ this.inputPrefix = this.cropTool.data("input-prefix")
19
+ this.iiifUrlField = this.iiifInputElement(
20
+ this.inputPrefix,
21
+ "iiif_tilesource",
22
+ this.cropTool,
23
+ )
24
+ this.iiifRegionField = this.iiifInputElement(
25
+ this.inputPrefix,
26
+ "iiif_region",
27
+ this.cropTool,
28
+ )
29
+ this.iiifManifestField = this.iiifInputElement(
30
+ this.inputPrefix,
31
+ "iiif_manifest_url",
32
+ this.cropTool,
33
+ )
34
+ this.iiifCanvasField = this.iiifInputElement(
35
+ this.inputPrefix,
36
+ "iiif_canvas_id",
37
+ this.cropTool,
38
+ )
39
+ this.iiifImageField = this.iiifInputElement(
40
+ this.inputPrefix,
41
+ "iiif_image_id",
42
+ this.cropTool,
43
+ )
24
44
  // Get the closest form element
25
- this.form = cropArea.closest('form');
26
- this.tileSource = null;
45
+ this.form = cropArea.closest("form")
46
+ this.tileSource = null
27
47
  }
28
48
 
29
49
  // Return the iiif input element based on the fieldname.
30
- // Multiple input fields with the same name on the page may be related
31
- // to a cropper. We thus need to pass in a parent element.
50
+ // Multiple input fields with the same name on the page may be related
51
+ // to a cropper. We thus need to pass in a parent element.
32
52
  iiifInputElement(inputPrefix, fieldName, inputParentElement) {
33
- return $('input[name="' + inputPrefix + '[' + fieldName + ']"]', inputParentElement);
53
+ return $(
54
+ 'input[name="' + inputPrefix + "[" + fieldName + ']"]',
55
+ inputParentElement,
56
+ )
34
57
  }
35
58
 
36
59
  // Render the cropper environment and add hooks into the autocomplete and upload forms
37
60
  render() {
38
- this.setupAutoCompletes();
39
- this.setupAjaxFileUpload();
40
- this.setupExistingIiifCropper();
61
+ this.setupAutoCompletes()
62
+ this.setupAjaxFileUpload()
63
+ this.setupExistingIiifCropper()
41
64
  }
42
65
 
43
66
  // Setup the cropper on page load if the field
44
67
  // that holds the IIIF url is populated
45
68
  setupExistingIiifCropper() {
46
- if(this.iiifUrlField.val() === '') {
47
- return;
69
+ if (this.iiifUrlField.val() === "") {
70
+ return
48
71
  }
49
72
 
50
- this.addImageSelectorToExistingCropTool();
51
- this.setTileSource(this.iiifUrlField.val());
73
+ this.addImageSelectorToExistingCropTool()
74
+ this.setTileSource(this.iiifUrlField.val())
52
75
  }
53
76
 
54
77
  // Display the IIIF Cropper map with the current IIIF Layer (and cropbox, once the layer is available)
55
78
  setupIiifCropper() {
56
- this.loaded = false;
79
+ this.loaded = false
57
80
 
58
- this.renderCropperMap();
81
+ this.renderCropperMap()
59
82
 
60
83
  if (this.imageLayer) {
61
84
  // Force a broken layer's container to be an element before removing.
62
85
  // Code in leaflet-iiif land calls delete on the image layer's container when removing,
63
86
  // which errors if there is an issue fetching the info.json and stops further necessary steps to execute.
64
- if(!this.imageLayer._container) {
65
- this.imageLayer._container = $('<div></div>');
87
+ if (!this.imageLayer._container) {
88
+ this.imageLayer._container = $("<div></div>")
66
89
  }
67
- this.cropperMap.removeLayer(this.imageLayer);
90
+ this.cropperMap.removeLayer(this.imageLayer)
68
91
  }
69
92
 
70
- this.imageLayer = L.tileLayer.iiif(this.tileSource).addTo(this.cropperMap);
93
+ this.imageLayer = L.tileLayer.iiif(this.tileSource).addTo(this.cropperMap)
71
94
 
72
- var self = this;
73
- this.imageLayer.on('load', function() {
95
+ var self = this
96
+ this.imageLayer.on("load", function () {
74
97
  if (!self.loaded) {
75
- var region = self.getCropRegion();
76
- self.positionIiifCropBox(region);
77
- self.loaded = true;
98
+ var region = self.getCropRegion()
99
+ self.positionIiifCropBox(region)
100
+ self.loaded = true
78
101
  }
79
- });
102
+ })
80
103
 
81
- this.cropArea.data('initiallyVisible', this.cropArea.is(':visible'));
104
+ this.cropArea.data("initiallyVisible", this.cropArea.is(":visible"))
82
105
  }
83
106
 
84
107
  // Get (or initialize) the current crop region from the form data
85
108
  getCropRegion() {
86
- var regionFieldValue = this.iiifRegionField.val();
87
- if(!regionFieldValue || regionFieldValue === '') {
88
- var region = this.defaultCropRegion();
89
- this.iiifRegionField.val(region);
90
- return region;
109
+ var regionFieldValue = this.iiifRegionField.val()
110
+ if (!regionFieldValue || regionFieldValue === "") {
111
+ var region = this.defaultCropRegion()
112
+ this.iiifRegionField.val(region)
113
+ return region
91
114
  } else {
92
- return regionFieldValue.split(',');
115
+ return regionFieldValue.split(",")
93
116
  }
94
117
  }
95
118
 
96
119
  // Calculate a default crop region in the center of the image using the correct aspect ratio
97
120
  defaultCropRegion() {
98
- var imageWidth = this.imageLayer.x;
99
- var imageHeight = this.imageLayer.y;
121
+ var imageWidth = this.imageLayer.x
122
+ var imageHeight = this.imageLayer.y
100
123
 
101
- var boxWidth = Math.floor(imageWidth / 2);
102
- var boxHeight = Math.floor(boxWidth / this.aspectRatio());
124
+ var boxWidth = Math.floor(imageWidth / 2)
125
+ var boxHeight = Math.floor(boxWidth / this.aspectRatio())
103
126
 
104
127
  return [
105
128
  Math.floor((imageWidth - boxWidth) / 2),
106
129
  Math.floor((imageHeight - boxHeight) / 2),
107
130
  boxWidth,
108
- boxHeight
109
- ];
131
+ boxHeight,
132
+ ]
110
133
  }
111
134
 
112
135
  // Calculate the required aspect ratio for the crop area
113
136
  aspectRatio() {
114
- var cropWidth = parseInt(this.cropArea.data('crop-width'));
115
- var cropHeight = parseInt(this.cropArea.data('crop-height'));
116
- return cropWidth / cropHeight;
137
+ var cropWidth = parseInt(this.cropArea.data("crop-width"))
138
+ var cropHeight = parseInt(this.cropArea.data("crop-height"))
139
+ return cropWidth / cropHeight
117
140
  }
118
141
 
119
142
  // Position the IIIF Crop Box at the given IIIF region
120
143
  positionIiifCropBox(region) {
121
- var bounds = this.unprojectIIIFRegionToBounds(region);
144
+ var bounds = this.unprojectIIIFRegionToBounds(region)
122
145
 
123
146
  if (!this.cropBox) {
124
- this.renderCropBox(bounds);
147
+ this.renderCropBox(bounds)
125
148
  }
126
149
 
127
- this.cropBox.setBounds(bounds);
128
- this.cropperMap.invalidateSize();
129
- this.cropperMap.fitBounds(bounds);
150
+ this.cropBox.setBounds(bounds)
151
+ this.cropperMap.invalidateSize()
152
+ this.cropperMap.fitBounds(bounds)
130
153
 
131
- this.cropBox.editor.editLayer.clearLayers();
132
- this.cropBox.editor.refresh();
133
- this.cropBox.editor.initVertexMarkers();
154
+ this.cropBox.editor.editLayer.clearLayers()
155
+ this.cropBox.editor.refresh()
156
+ this.cropBox.editor.initVertexMarkers()
134
157
  }
135
158
 
136
159
  // Set all of the various input fields to
137
160
  // the appropriate IIIF URL or identifier
138
161
  setIiifFields(iiifObject) {
139
- this.setTileSource(iiifObject.tilesource);
140
- this.iiifManifestField.val(iiifObject.manifest);
141
- this.iiifCanvasField.val(iiifObject.canvasId);
142
- this.iiifImageField.val(iiifObject.imageId);
162
+ this.setTileSource(iiifObject.tilesource)
163
+ this.iiifManifestField.val(iiifObject.manifest)
164
+ this.iiifCanvasField.val(iiifObject.canvasId)
165
+ this.iiifImageField.val(iiifObject.imageId)
143
166
  }
144
167
 
145
168
  // Set the Crop tileSource and setup the cropper
146
169
  setTileSource(source) {
147
170
  if (source == this.tileSource) {
148
- return;
171
+ return
149
172
  }
150
173
 
151
174
  if (source === null || source === undefined) {
152
- console.error('No tilesource provided when setting up IIIF Cropper');
153
- return;
175
+ console.error("No tilesource provided when setting up IIIF Cropper")
176
+ return
154
177
  }
155
178
 
156
179
  if (this.cropBox) {
157
- this.iiifRegionField.val("");
180
+ this.iiifRegionField.val("")
158
181
  }
159
182
 
160
- this.tileSource = source;
161
- this.iiifUrlField.val(source);
162
- this.setupIiifCropper();
183
+ this.tileSource = source
184
+ this.iiifUrlField.val(source)
185
+ this.setupIiifCropper()
163
186
  }
164
187
 
165
188
  // Render the Leaflet Map into the crop area
166
189
  renderCropperMap() {
167
190
  if (this.cropperMap) {
168
- return;
191
+ return
169
192
  }
170
193
 
171
194
  var cropperOptions = {
172
195
  editable: true,
173
196
  center: [0, 0],
174
197
  crs: L.CRS.Simple,
175
- zoom: 0
198
+ zoom: 0,
176
199
  }
177
200
 
178
- if(this.preserveAspectRatio) {
179
- cropperOptions['editOptions'] = {
180
- rectangleEditorClass: this.aspectRatioPreservingRectangleEditor(this.aspectRatio())
181
- };
201
+ if (this.preserveAspectRatio) {
202
+ cropperOptions["editOptions"] = {
203
+ rectangleEditorClass: this.aspectRatioPreservingRectangleEditor(
204
+ this.aspectRatio(),
205
+ ),
206
+ }
182
207
  }
183
208
 
184
- this.cropperMap = L.map(this.cropArea.attr('id'), cropperOptions);
185
- this.invalidateMapSizeOnTabToggle();
209
+ this.cropperMap = L.map(this.cropArea.attr("id"), cropperOptions)
210
+ this.invalidateMapSizeOnTabToggle()
186
211
  }
187
212
 
188
213
  // Render the crop box (a Leaflet editable rectangle) onto the canvas
189
214
  renderCropBox(initialBounds) {
190
- this.cropBox = L.rectangle(initialBounds);
191
- this.cropBox.addTo(this.cropperMap);
192
- this.cropBox.enableEdit();
193
- this.cropBox.on('dblclick', L.DomEvent.stop).on('dblclick', this.cropBox.toggleEdit);
194
-
195
- var self = this;
196
- this.cropperMap.on('editable:dragend editable:vertex:dragend', function(e) {
197
- var bounds = e.layer.getBounds();
198
- var region = self.projectBoundsToIIIFRegion(bounds);
199
-
200
- self.iiifRegionField.val(region.join(','));
201
- });
215
+ this.cropBox = L.rectangle(initialBounds)
216
+ this.cropBox.addTo(this.cropperMap)
217
+ this.cropBox.enableEdit()
218
+ this.cropBox
219
+ .on("dblclick", L.DomEvent.stop)
220
+ .on("dblclick", this.cropBox.toggleEdit)
221
+
222
+ var self = this
223
+ this.cropperMap.on(
224
+ "editable:dragend editable:vertex:dragend",
225
+ function (e) {
226
+ var bounds = e.layer.getBounds()
227
+ var region = self.projectBoundsToIIIFRegion(bounds)
228
+
229
+ self.iiifRegionField.val(region.join(","))
230
+ },
231
+ )
202
232
  }
203
233
 
204
234
  // Get the maximum zoom level for the IIIF Layer (always 1:1 image pixel to canvas?)
205
235
  maxZoom() {
206
- if(this.imageLayer) {
207
- return this.imageLayer.maxZoom;
236
+ if (this.imageLayer) {
237
+ return this.imageLayer.maxZoom
208
238
  }
209
239
  }
210
240
 
211
241
  // Take a Leaflet LatLngBounds object and transform it into a IIIF [x, y, w, h] region
212
242
  projectBoundsToIIIFRegion(bounds) {
213
- var min = this.cropperMap.project(bounds.getNorthWest(), this.maxZoom());
214
- var max = this.cropperMap.project(bounds.getSouthEast(), this.maxZoom());
243
+ var min = this.cropperMap.project(bounds.getNorthWest(), this.maxZoom())
244
+ var max = this.cropperMap.project(bounds.getSouthEast(), this.maxZoom())
215
245
  return [
216
246
  Math.max(Math.floor(min.x), 0),
217
247
  Math.max(Math.floor(min.y), 0),
218
248
  Math.floor(max.x - min.x),
219
- Math.floor(max.y - min.y)
220
- ];
249
+ Math.floor(max.y - min.y),
250
+ ]
221
251
  }
222
252
 
223
253
  // Take a IIIF [x, y, w, h] region and transform it into a Leaflet LatLngBounds
224
254
  unprojectIIIFRegionToBounds(region) {
225
- var minPoint = L.point(parseInt(region[0]), parseInt(region[1]));
226
- var maxPoint = L.point(parseInt(region[0]) + parseInt(region[2]), parseInt(region[1]) + parseInt(region[3]));
227
-
228
- var min = this.cropperMap.unproject(minPoint, this.maxZoom());
229
- var max = this.cropperMap.unproject(maxPoint, this.maxZoom());
230
- return L.latLngBounds(min, max);
255
+ var minPoint = L.point(parseInt(region[0]), parseInt(region[1]))
256
+ var maxPoint = L.point(
257
+ parseInt(region[0]) + parseInt(region[2]),
258
+ parseInt(region[1]) + parseInt(region[3]),
259
+ )
260
+
261
+ var min = this.cropperMap.unproject(minPoint, this.maxZoom())
262
+ var max = this.cropperMap.unproject(maxPoint, this.maxZoom())
263
+ return L.latLngBounds(min, max)
231
264
  }
232
265
 
233
266
  // TODO: Add accessors to update hidden inputs with IIIF uri/ids?
234
267
 
235
268
  // Setup autocomplete inputs to have the iiif_cropper context
236
269
  setupAutoCompletes() {
237
- var input = $('[data-behavior="autocomplete"]', this.cropTool);
238
- input.data('iiifCropper', this);
270
+ var input = $('[data-behavior="autocomplete"]', this.cropTool)
271
+ input.data("iiifCropper", this)
239
272
  }
240
273
 
241
274
  setupAjaxFileUpload() {
242
- this.fileInput = $('input[type="file"]', this.cropTool);
243
- this.fileInput.change(() => this.uploadFile());
275
+ this.fileInput = $('input[type="file"]', this.cropTool)
276
+ this.fileInput.change(() => this.uploadFile())
244
277
  }
245
278
 
246
279
  addImageSelectorToExistingCropTool() {
247
- if(this.iiifManifestField.val() === '') {
248
- return;
280
+ if (this.iiifManifestField.val() === "") {
281
+ return
249
282
  }
250
283
 
251
- var input = $('[data-behavior="autocomplete"]', this.cropTool);
252
-
284
+ var input = $('[data-behavior="autocomplete"]', this.cropTool)
285
+
253
286
  // Not every page which uses this module has autocomplete linked directly to the cropping tool
254
- if(input.length) {
255
- var panel = $(input.data('target-panel'));
256
- addImageSelector(input, panel, this.iiifManifestField.val(), !this.iiifImageField.val());
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
+ )
257
295
  }
258
296
  }
259
297
 
260
298
  invalidateMapSizeOnTabToggle() {
261
- var tabs = $('[role="tablist"]', this.form);
262
- var self = this;
263
- tabs.on('shown.bs.tab', function() {
264
- if(self.cropArea.data('initiallyVisible') === false && self.cropArea.is(':visible')) {
265
- self.cropperMap.invalidateSize();
299
+ var tabs = $('[role="tablist"]', this.form)
300
+ var self = this
301
+ tabs.on("shown.bs.tab", function () {
302
+ if (
303
+ self.cropArea.data("initiallyVisible") === false &&
304
+ self.cropArea.is(":visible")
305
+ ) {
306
+ self.cropperMap.invalidateSize()
266
307
  // Because the map size is 0,0 when image is loading (not visible) we need to refit the bounds of the layer
267
- self.imageLayer._fitBounds();
268
- self.cropArea.data('initiallyVisible', null);
308
+ self.imageLayer._fitBounds()
309
+ self.cropArea.data("initiallyVisible", null)
269
310
  }
270
- });
311
+ })
271
312
  }
272
313
 
273
314
  // Get all the form data with the exception of the _method field.
274
315
  getData() {
275
- var data = new FormData(this.form[0]);
276
- data.append('_method', null);
277
- return data;
316
+ var data = new FormData(this.form[0])
317
+ data.append("_method", null)
318
+ return data
278
319
  }
279
320
 
280
321
  uploadFile() {
281
- var url = this.fileInput.data('endpoint')
322
+ var url = this.fileInput.data("endpoint")
282
323
  // Every post creates a new image/masthead.
283
324
  // Because they create IIIF urls which are heavily cached.
284
325
  $.ajax({
285
- url: url, //Server script to process data
286
- type: 'POST',
326
+ url: url, //Server script to process data
327
+ type: "POST",
287
328
  success: (data, stat, xhr) => this.successHandler(data, stat, xhr),
288
329
  error: (xhr, stat, error) => this.errorHandler(xhr, stat, error),
289
330
  // Form data
290
331
  data: this.getData(),
291
332
  headers: {
292
- 'X-CSRF-Token': Core.csrfToken() || ''
333
+ "X-CSRF-Token": Core.csrfToken() || "",
293
334
  },
294
335
  //Options to tell jQuery not to process data or worry about content-type.
295
336
  cache: false,
296
337
  contentType: false,
297
- processData: false
298
- });
338
+ processData: false,
339
+ })
299
340
  }
300
341
 
301
- successHandler(data, stat, xhr) {
302
- this.setIiifFields({ tilesource: data.tilesource });
303
- this.setUploadId(data.id);
342
+ successHandler(data, _stat, _xhr) {
343
+ this.setIiifFields({ tilesource: data.tilesource })
344
+ this.setUploadId(data.id)
304
345
  this.clearUploadErrors()
305
346
  }
306
347
 
307
- errorHandler(xhr, stat, error) {
348
+ errorHandler(xhr, _stat, _error) {
308
349
  let errorMessage = "Upload failed"
309
350
  if (xhr.responseJSON) {
310
351
  if (xhr.responseJSON.errors) {
311
- errorMessage = xhr.responseJSON.errors.join(', ')
352
+ errorMessage = xhr.responseJSON.errors.join(", ")
312
353
  } else if (xhr.responseJSON.error) {
313
354
  errorMessage = xhr.responseJSON.error
314
355
  }
@@ -325,7 +366,7 @@ export default class Crop {
325
366
  if (errorsElement) {
326
367
  errorsElement.text(errorMessage).show()
327
368
  } else {
328
- console.error("uploadFile", error, errorMessage)
369
+ console.error("uploadFile", errorMessage)
329
370
  }
330
371
  }
331
372
 
@@ -339,34 +380,40 @@ export default class Crop {
339
380
  setUploadId(id) {
340
381
  // This input is currently used for exhibit masthead or thumbnail image upload.
341
382
  // The name should be sufficient in this case, as we don't use this part of the
342
- // code for solr document widgets where we enable cropping.
343
- // If we require more specificity, we can scope this to this.cropTool.
344
- $('input[name="' + this.inputPrefix + '[upload_id]"]').val(id);
383
+ // code for solr document widgets where we enable cropping.
384
+ // If we require more specificity, we can scope this to this.cropTool.
385
+ $('input[name="' + this.inputPrefix + '[upload_id]"]').val(id)
345
386
  }
346
387
 
347
388
  aspectRatioPreservingRectangleEditor(aspect) {
348
389
  return L.Editable.RectangleEditor.extend({
349
390
  extendBounds: function (e) {
350
391
  var index = e.vertex.getIndex(),
351
- next = e.vertex.getNext(),
352
- previous = e.vertex.getPrevious(),
353
- oppositeIndex = (index + 2) % 4,
354
- opposite = e.vertex.latlngs[oppositeIndex];
392
+ next = e.vertex.getNext(),
393
+ previous = e.vertex.getPrevious(),
394
+ oppositeIndex = (index + 2) % 4,
395
+ opposite = e.vertex.latlngs[oppositeIndex]
355
396
 
356
- if ((index % 2) == 1) {
397
+ if (index % 2 == 1) {
357
398
  // calculate horiz. displacement
358
- e.latlng.update([opposite.lat + ((1 / aspect) * (opposite.lng - e.latlng.lng)), e.latlng.lng]);
399
+ e.latlng.update([
400
+ opposite.lat + (1 / aspect) * (opposite.lng - e.latlng.lng),
401
+ e.latlng.lng,
402
+ ])
359
403
  } else {
360
404
  // calculate vert. displacement
361
- e.latlng.update([e.latlng.lat, (opposite.lng - (aspect * (opposite.lat - e.latlng.lat)))]);
405
+ e.latlng.update([
406
+ e.latlng.lat,
407
+ opposite.lng - aspect * (opposite.lat - e.latlng.lat),
408
+ ])
362
409
  }
363
- var bounds = new L.LatLngBounds(e.latlng, opposite);
410
+ var bounds = new L.LatLngBounds(e.latlng, opposite)
364
411
  // Update latlngs by hand to preserve order.
365
- previous.latlng.update([e.latlng.lat, opposite.lng]);
366
- next.latlng.update([opposite.lat, e.latlng.lng]);
367
- this.updateBounds(bounds);
368
- this.refreshVertexMarkers();
369
- }
370
- });
412
+ previous.latlng.update([e.latlng.lat, opposite.lng])
413
+ next.latlng.update([opposite.lat, e.latlng.lng])
414
+ this.updateBounds(bounds)
415
+ this.refreshVertexMarkers()
416
+ },
417
+ })
371
418
  }
372
419
  }
@@ -1,17 +1,17 @@
1
- import Crop from 'spotlight/admin/crop';
2
- import CroppableModal from 'spotlight/admin/croppable_modal';
1
+ import Crop from "spotlight/admin/crop"
2
+ import CroppableModal from "spotlight/admin/croppable_modal"
3
3
 
4
4
  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() {
8
+ $('[data-behavior="iiif-cropper"]').each(function () {
9
9
  var cropElement = $(this)
10
10
  new Crop(cropElement).render()
11
11
  })
12
12
 
13
13
  // In the case of individual document thumbnails, selection
14
14
  // of the image is through a modal. Here we attach the event
15
- new CroppableModal().attachModalHandlers();
15
+ new CroppableModal().attachModalHandlers()
16
16
  }
17
17
  }