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.
- checksums.yaml +4 -4
- data/README.md +41 -4
- data/Rakefile +1 -2
- data/app/assets/javascripts/spotlight/spotlight.esm.js +2168 -4560
- data/app/assets/javascripts/spotlight/spotlight.esm.js.map +1 -1
- data/app/assets/javascripts/spotlight/spotlight.js +2167 -4559
- data/app/assets/javascripts/spotlight/spotlight.js.map +1 -1
- data/app/assets/stylesheets/spotlight/_blacklight_overrides.scss +4 -0
- data/app/assets/stylesheets/spotlight/_browse.scss +0 -4
- data/app/assets/stylesheets/spotlight/{_collapse_toggle.scss → _collapse_toggle.css} +2 -2
- data/app/assets/stylesheets/spotlight/_exhibit_admin.scss +8 -0
- data/app/assets/stylesheets/spotlight/_exhibits_index.scss +2 -1
- data/app/assets/stylesheets/spotlight/_footer.scss +2 -2
- data/app/assets/stylesheets/spotlight/_header.scss +8 -8
- data/app/assets/stylesheets/spotlight/_pages.scss +1 -1
- data/app/assets/stylesheets/spotlight/_slideshow_block.scss +15 -0
- data/app/assets/stylesheets/spotlight/_spotlight.scss +1 -1
- data/app/assets/stylesheets/spotlight/_translations.scss +3 -3
- data/app/assets/stylesheets/spotlight/_uploaded_items_block.css +7 -0
- data/app/assets/stylesheets/spotlight/_variables.css +15 -0
- data/app/assets/stylesheets/spotlight/browse_group_categories_block.scss +0 -76
- data/app/components/blacklight/icons/pause_component.rb +15 -0
- data/app/components/blacklight/icons/play_arrow_component.rb +15 -0
- data/app/components/spotlight/analytics/dashboard_component.html.erb +1 -1
- data/app/components/spotlight/custom_field_form_group_component.rb +40 -0
- data/app/components/spotlight/document_admin_table_component.html.erb +2 -2
- data/app/components/spotlight/edit_sidecar_component.html.erb +18 -0
- data/app/components/spotlight/edit_sidecar_component.rb +41 -0
- data/app/components/spotlight/masthead_component.html.erb +18 -0
- data/app/components/spotlight/masthead_component.rb +46 -0
- data/app/components/spotlight/save_search_component.html.erb +1 -1
- data/app/components/spotlight/sidecar_form_field_component.html.erb +18 -0
- data/app/components/spotlight/sidecar_form_field_component.rb +71 -0
- data/app/components/spotlight/solr_document_legacy_embed_component.html.erb +0 -4
- data/app/components/spotlight/solr_document_legacy_embed_component.rb +2 -2
- data/app/components/spotlight/tag_selector_component.html.erb +1 -1
- data/app/components/spotlight/tag_selector_component.rb +3 -2
- data/app/components/spotlight/upload_field_form_group_component.rb +33 -0
- data/app/controllers/concerns/spotlight/controller.rb +15 -14
- data/app/controllers/concerns/spotlight/search_helper.rb +2 -5
- data/app/controllers/spotlight/browse_controller.rb +1 -6
- data/app/controllers/spotlight/catalog_controller.rb +2 -3
- data/app/controllers/spotlight/concerns/application_controller.rb +4 -6
- data/app/controllers/spotlight/exhibits_controller.rb +22 -10
- data/app/controllers/spotlight/lock_controller.rb +1 -1
- data/app/controllers/spotlight/pages_controller.rb +11 -11
- data/app/controllers/spotlight/resources/upload_controller.rb +1 -1
- data/app/controllers/spotlight/resources_controller.rb +1 -1
- data/app/controllers/spotlight/solr_controller.rb +5 -1
- data/app/helpers/spotlight/application_helper.rb +14 -26
- data/app/helpers/spotlight/crud_link_helpers.rb +0 -12
- data/app/helpers/spotlight/main_app_helpers.rb +2 -2
- data/app/javascript/spotlight/admin/add_another.js +37 -25
- data/app/javascript/spotlight/admin/add_image_selector.js +43 -26
- data/app/javascript/spotlight/admin/add_new_button.js +51 -36
- data/app/javascript/spotlight/admin/blacklight_configuration.js +76 -54
- data/app/javascript/spotlight/admin/block_mixins/autocompleteable.js +102 -101
- data/app/javascript/spotlight/admin/block_mixins/formable.js +91 -75
- data/app/javascript/spotlight/admin/block_mixins/plustextable.js +46 -48
- data/app/javascript/spotlight/admin/blocks/block.js +27 -28
- data/app/javascript/spotlight/admin/blocks/browse_block.js +14 -10
- data/app/javascript/spotlight/admin/blocks/browse_group_categories_block.js +14 -10
- data/app/javascript/spotlight/admin/blocks/pages_block.js +5 -4
- data/app/javascript/spotlight/admin/blocks/resources_block.js +61 -41
- data/app/javascript/spotlight/admin/blocks/search_result_block.js +24 -7
- data/app/javascript/spotlight/admin/blocks/solr_documents_base_block.js +103 -50
- data/app/javascript/spotlight/admin/blocks/solr_documents_carousel_block.js +50 -26
- data/app/javascript/spotlight/admin/blocks/solr_documents_features_block.js +47 -23
- data/app/javascript/spotlight/admin/blocks/uploaded_items_block.js +35 -19
- data/app/javascript/spotlight/admin/crop.js +146 -66
- data/app/javascript/spotlight/admin/croppable.js +5 -4
- data/app/javascript/spotlight/admin/croppable_modal.js +67 -32
- data/app/javascript/spotlight/admin/edit_in_place.js +69 -54
- data/app/javascript/spotlight/admin/exhibits.js +92 -45
- data/app/javascript/spotlight/admin/form_observer.js +92 -78
- data/app/javascript/spotlight/admin/multi_image_selector.js +201 -153
- data/app/javascript/spotlight/admin/pages.js +13 -7
- data/app/javascript/spotlight/admin/progress_monitor.js +65 -50
- data/app/javascript/spotlight/admin/readonly_checkbox.js +7 -3
- data/app/javascript/spotlight/admin/search_typeahead.js +20 -11
- data/app/javascript/spotlight/admin/select_related_input.js +23 -23
- data/app/javascript/spotlight/admin/serialize_json.js +120 -0
- data/app/javascript/spotlight/admin/sir-trevor/block_controls.js +24 -11
- data/app/javascript/spotlight/admin/sir-trevor/block_limits.js +9 -6
- data/app/javascript/spotlight/admin/sir-trevor/locales.js +2 -2
- data/app/javascript/spotlight/admin/spotlight_nestable.js +2 -5
- data/app/javascript/spotlight/admin/translation_progress.js +18 -13
- data/app/javascript/spotlight/admin/users.js +151 -64
- data/app/javascript/spotlight/user/carousel.js +104 -32
- data/app/javascript/spotlight/user/clear_form_button.js +24 -14
- data/app/javascript/spotlight/user/index.js +0 -2
- data/app/javascript/spotlight/user/zpr_links.js +99 -40
- data/app/jobs/concerns/spotlight/gather_documents.rb +1 -1
- data/app/jobs/spotlight/add_uploads_from_csv.rb +31 -8
- data/app/jobs/spotlight/cleanup_job_trackers_job.rb +5 -3
- data/app/models/concerns/spotlight/access_controls_enforcement_search_builder.rb +13 -2
- data/app/models/concerns/spotlight/browse_category_search_builder.rb +3 -3
- data/app/models/concerns/spotlight/exhibit_documents.rb +2 -2
- data/app/models/concerns/spotlight/sitemap.rb +2 -2
- data/app/models/concerns/spotlight/solr_document/finder.rb +2 -2
- data/app/models/concerns/spotlight/solr_document.rb +1 -47
- data/app/models/sir_trevor_rails/blocks/browse_block.rb +0 -4
- data/app/models/sir_trevor_rails/blocks/solr_documents_block.rb +2 -1
- data/app/models/spotlight/blacklight_configuration.rb +5 -3
- data/app/models/spotlight/contact_email.rb +2 -2
- data/app/models/spotlight/custom_field.rb +17 -0
- data/app/models/spotlight/exhibit.rb +28 -0
- data/app/models/spotlight/featured_image.rb +1 -1
- data/app/models/spotlight/field_metadata.rb +10 -5
- data/app/models/spotlight/job_tracker.rb +0 -18
- data/app/models/spotlight/main_navigation.rb +2 -2
- data/app/models/spotlight/page.rb +3 -3
- data/app/models/spotlight/resources/iiif_harvester.rb +7 -4
- data/app/models/spotlight/resources/iiif_manifest.rb +0 -4
- data/app/models/spotlight/resources/iiif_manifest_metadata.rb +1 -3
- data/app/models/spotlight/resources/iiif_service.rb +21 -6
- data/app/models/spotlight/resources/upload.rb +6 -0
- data/app/models/spotlight/search.rb +5 -9
- data/app/models/spotlight/solr_document_sidecar.rb +18 -0
- data/app/services/spotlight/bulk_updates_csv_template_service.rb +1 -1
- data/app/services/spotlight/etl/pipeline.rb +2 -2
- data/app/services/spotlight/etl/step.rb +2 -2
- data/app/views/catalog/_remove_tags.html.erb +2 -2
- data/app/views/layouts/spotlight/base.html.erb +3 -6
- data/app/views/layouts/spotlight/spotlight.html.erb +2 -2
- data/app/views/shared/_analytics.html.erb +4 -3
- data/app/views/shared/_masthead.html.erb +2 -18
- data/app/views/spotlight/about_pages/_empty.html.erb +1 -1
- data/app/views/spotlight/about_pages/_sidebar.html.erb +10 -8
- data/app/views/spotlight/browse/_search.html.erb +2 -2
- data/app/views/spotlight/browse/_search_box.html.erb +6 -14
- data/app/views/spotlight/browse/index.html.erb +1 -1
- data/app/views/spotlight/browse/show.html.erb +1 -1
- data/app/views/spotlight/catalog/_document_admin_table.html.erb +1 -1
- data/app/views/spotlight/catalog/_edit_default.html.erb +1 -1
- data/app/views/spotlight/catalog/edit.html.erb +1 -1
- data/app/views/spotlight/dashboards/_page.html.erb +1 -1
- data/app/views/spotlight/exhibits/_exhibit_card.html.erb +3 -1
- data/app/views/spotlight/exhibits/_export.html.erb +17 -1
- data/app/views/spotlight/exhibits/_form.html.erb +20 -4
- data/app/views/spotlight/exhibits/_missing_exhibits.html.erb +3 -3
- data/app/views/spotlight/exhibits/index.html.erb +21 -8
- data/app/views/spotlight/feature_pages/_empty.html.erb +1 -1
- data/app/views/spotlight/feature_pages/_sidebar.html.erb +17 -15
- data/app/views/spotlight/home_pages/_empty.html.erb +7 -7
- data/app/views/spotlight/pages/show.html.erb +1 -0
- data/app/views/spotlight/resources/upload/_form.html.erb +2 -2
- data/app/views/spotlight/sir_trevor/blocks/_browse_group_categories_block.html.erb +5 -16
- data/app/views/spotlight/sir_trevor/blocks/_embedded_document.html.erb +3 -5
- data/app/views/spotlight/sir_trevor/blocks/_link_to_search_block.html.erb +1 -1
- data/app/views/spotlight/sir_trevor/blocks/_solr_documents_carousel_block.html.erb +11 -2
- data/app/views/spotlight/sites/_exhibit.html.erb +1 -1
- data/config/i18n-tasks.yml +1 -1
- data/config/locales/spotlight.ar.yml +1 -2
- data/config/locales/spotlight.de.yml +1 -2
- data/config/locales/spotlight.en.yml +39 -10
- data/config/locales/spotlight.es.yml +1 -2
- data/config/locales/spotlight.fr.yml +1 -2
- data/config/locales/spotlight.it.yml +1 -2
- data/config/locales/spotlight.ja.yml +39 -0
- data/config/locales/spotlight.zh.yml +1 -2
- data/db/migrate/20261007133748_add_discovery_enabled_to_spotlight_exhibits.rb +5 -0
- data/lib/generators/spotlight/assets/importmap_generator.rb +2 -10
- data/lib/generators/spotlight/assets/propshaft_generator.rb +5 -4
- data/lib/generators/spotlight/install_generator.rb +2 -4
- data/lib/generators/spotlight/templates/catalog_controller.rb +0 -1
- data/lib/generators/spotlight/templates/config/initializers/spotlight_initializer.rb +26 -4
- data/lib/generators/spotlight/templates/solr/conf/schema.xml +4 -3
- data/lib/generators/spotlight/templates/solr/conf/solrconfig.xml +3 -8
- data/lib/migration/page_language.rb +0 -2
- data/lib/spotlight/engine.rb +28 -9
- data/lib/spotlight/search_state.rb +7 -11
- data/lib/spotlight/upload_field_config.rb +19 -1
- data/lib/spotlight/version.rb +1 -1
- data/lib/spotlight.rb +1 -1
- data/spec/support/features/capybara_wait_metadata_helper.rb +3 -1
- data/spec/support/features/test_features_helpers.rb +24 -1
- data/spec/support/iiif_manifest_fixtures.rb +33 -0
- data/spec/support/stub_dns_resolution.rb +12 -0
- metadata +50 -80
- data/app/assets/stylesheets/spotlight/_uploaded_items_block.scss +0 -7
- data/app/assets/stylesheets/spotlight/_variables.scss +0 -13
- data/app/components/blacklight/icons/arrow_alt_circle_left_component.rb +0 -15
- data/app/components/blacklight/icons/arrow_alt_circle_right_component.rb +0 -15
- data/app/components/blacklight/icons/info_component.rb +0 -15
- data/app/components/blacklight/icons/zoom_in_component.rb +0 -15
- data/app/components/blacklight/icons/zoom_out_component.rb +0 -15
- data/app/components/spotlight/icon_component.rb +0 -13
- data/app/javascript/spotlight/user/browse_group_categories.js +0 -54
- data/app/mailers/spotlight/invitation_mailer.rb +0 -21
- data/app/models/concerns/spotlight/resources/web.rb +0 -38
- data/app/services/spotlight/iiif_resource_resolver.rb +0 -78
- data/app/views/catalog/_image_default.html.erb +0 -6
- data/app/views/shared/_exhibit_navbar.html.erb +0 -2
- data/app/views/shared/_header_navbar.html.erb +0 -6
- data/app/views/spotlight/catalog/_admin_index_header_default.html.erb +0 -23
- data/app/views/spotlight/catalog/_admin_thumbnail_default.html.erb +0 -7
- data/app/views/spotlight/catalog/_curation_mode_toggle_default.html.erb +0 -1
- data/app/views/spotlight/catalog/_document_row.html.erb +0 -2
- data/app/views/spotlight/catalog/_edit_sidecar.html.erb +0 -24
- data/app/views/spotlight/catalog/_index_compact_default.html.erb +0 -21
- data/app/views/spotlight/custom_fields/form_group/_text.html.erb +0 -23
- data/app/views/spotlight/custom_fields/form_group/_vocab.html.erb +0 -23
- data/app/views/spotlight/invitation_mailer/exhibit_invitation_notification.html.erb +0 -12
- data/lib/generators/spotlight/templates/javascript/jquery-shim.js +0 -1
- data/vendor/assets/javascripts/tiny-slider.js +0 -3221
- data/vendor/assets/stylesheets/tiny-slider.css +0 -1
- /data/app/assets/stylesheets/spotlight/{_breadcrumbs.scss → _breadcrumbs.css} +0 -0
- /data/app/assets/stylesheets/spotlight/{_croppable.scss → _croppable.css} +0 -0
- /data/app/assets/stylesheets/spotlight/{_modals.scss → _modals.css} +0 -0
- /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
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
85
|
+
if (this.iiifUrlField.value === "") {
|
|
70
86
|
return
|
|
71
87
|
}
|
|
72
88
|
|
|
73
89
|
this.addImageSelectorToExistingCropTool()
|
|
74
|
-
this.setTileSource(this.iiifUrlField.
|
|
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 =
|
|
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.
|
|
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.
|
|
134
|
+
var regionFieldValue = this.iiifRegionField.value
|
|
110
135
|
if (!regionFieldValue || regionFieldValue === "") {
|
|
111
136
|
var region = this.defaultCropRegion()
|
|
112
|
-
this.iiifRegionField.
|
|
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
|
-
|
|
138
|
-
var
|
|
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.
|
|
164
|
-
this.iiifCanvasField.
|
|
165
|
-
this.iiifImageField.
|
|
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.
|
|
212
|
+
this.iiifRegionField.value = ""
|
|
181
213
|
}
|
|
182
214
|
|
|
183
215
|
this.tileSource = source
|
|
184
|
-
this.iiifUrlField.
|
|
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(
|
|
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.
|
|
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
|
-
|
|
271
|
-
input.data
|
|
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
|
-
|
|
276
|
-
this.fileInput
|
|
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.
|
|
321
|
+
if (this.iiifManifestField.value === "") {
|
|
281
322
|
return
|
|
282
323
|
}
|
|
283
324
|
|
|
284
|
-
|
|
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 (
|
|
288
|
-
var
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
351
|
+
if (!this.form) return
|
|
352
|
+
var tabs = this.form.querySelectorAll('[role="tablist"]')
|
|
300
353
|
var self = this
|
|
301
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
326
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
return
|
|
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
|
-
|
|
35
|
+
const context = this
|
|
36
36
|
|
|
37
37
|
document.addEventListener("show.blacklight.blacklight-modal", function () {
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
48
|
-
|
|
50
|
+
const context = this
|
|
51
|
+
const dataCropperDiv = document.querySelector(
|
|
52
|
+
'#blacklight-modal [data-behavior="iiif-cropper"]',
|
|
53
|
+
)
|
|
49
54
|
|
|
50
55
|
if (dataCropperDiv) {
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
+
const thumbnailSaveField = context.iiifInputField(
|
|
57
71
|
itemIndex,
|
|
58
72
|
"thumbnail_image_url",
|
|
59
73
|
itemElement,
|
|
60
74
|
)
|
|
61
|
-
|
|
75
|
+
const fullimageSaveField = context.iiifInputField(
|
|
62
76
|
itemIndex,
|
|
63
77
|
"full_image_url",
|
|
64
78
|
itemElement,
|
|
65
79
|
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
87
|
-
itemImage
|
|
119
|
+
const itemImage = itemElement.querySelector("img.img-thumbnail")
|
|
120
|
+
if (itemImage) {
|
|
121
|
+
itemImage.setAttribute("src", thumbnailUrl)
|
|
122
|
+
}
|
|
88
123
|
}
|
|
89
124
|
}
|
|
90
125
|
}
|