geoblacklight 6.0.0.pre.alpha.7 → 6.0.0.pre.beta.2
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/.github/workflows/ruby.yml +14 -3
- data/README.md +2 -2
- data/Rakefile +1 -17
- data/app/assets/images/geoblacklight/logo-dark.svg +31 -0
- data/app/assets/images/geoblacklight/logo.svg +10 -10
- data/app/assets/stylesheets/geoblacklight/global.css +44 -7
- data/app/assets/stylesheets/geoblacklight/index_maps.css +0 -7
- data/app/assets/stylesheets/geoblacklight/record.css +33 -9
- data/app/assets/stylesheets/geoblacklight/search.css +66 -17
- data/app/assets/stylesheets/geoblacklight/viewers.css +18 -111
- data/app/assets/stylesheets/geoblacklight.css +0 -5
- data/app/components/blacklight/icons/georeferenced_component.rb +23 -0
- data/app/components/geoblacklight/document/download_link_component.html.erb +1 -1
- data/app/components/geoblacklight/document/preview_component.html.erb +0 -2
- data/app/components/geoblacklight/document_component.html.erb +0 -4
- data/app/components/geoblacklight/facets/bbox_field_component.html.erb +2 -1
- data/app/components/geoblacklight/georeferenced_header_badge_component.rb +16 -0
- data/app/components/geoblacklight/header_badge_component.html.erb +4 -2
- data/app/components/geoblacklight/header_badge_component.rb +1 -1
- data/app/components/geoblacklight/header_badges_component.html.erb +5 -0
- data/app/components/geoblacklight/header_badges_component.rb +34 -0
- data/app/components/geoblacklight/iiif_drag_drop_component.html.erb +1 -1
- data/app/components/geoblacklight/item_map_viewer_component.html.erb +0 -58
- data/app/components/geoblacklight/item_map_viewer_component.rb +19 -70
- data/app/components/geoblacklight/locator_map_component.rb +31 -0
- data/app/components/geoblacklight/login_link_component.html.erb +1 -1
- data/app/components/geoblacklight/login_link_component.rb +17 -1
- data/app/components/geoblacklight/metadata_description_markdown_component.rb +3 -9
- data/app/components/geoblacklight/overview_map_component.html.erb +2 -0
- data/app/components/geoblacklight/overview_map_component.rb +86 -0
- data/app/components/geoblacklight/relations/relation_component.html.erb +12 -0
- data/app/components/geoblacklight/relations/relation_component.rb +30 -0
- data/app/components/geoblacklight/relations/relations_component.html.erb +15 -0
- data/app/components/geoblacklight/relations/relations_component.rb +59 -0
- data/app/components/geoblacklight/resource_header_badge_component.rb +1 -1
- data/app/components/geoblacklight/search_result_component.html.erb +14 -33
- data/app/components/geoblacklight/search_result_component.rb +2 -14
- data/app/components/geoblacklight/static_map_component.rb +3 -14
- data/app/components/geoblacklight/truncatable_metadata_field_layout_component.html.erb +7 -0
- data/app/components/geoblacklight/truncatable_metadata_field_layout_component.rb +45 -0
- data/app/controllers/{relation_controller.rb → relations_controller.rb} +4 -2
- data/app/controllers/viewer_record_controller.rb +28 -0
- data/app/helpers/geoblacklight_helper.rb +57 -6
- data/app/javascript/geoblacklight/controllers/overview_map_controller.js +143 -0
- data/app/javascript/geoblacklight/core.js +8 -10
- data/app/javascript/geoblacklight/index.js +9 -12
- data/app/javascript/geoblacklight/initializers/field_truncation.js +62 -0
- data/app/javascript/geoblacklight/initializers/metadata_download.js +3 -8
- data/app/javascript/geoblacklight/initializers/truncation.js +7 -3
- data/app/javascript/geoblacklight/initializers/viewer_requests.js +61 -0
- data/app/javascript/geoblacklight/initializers/viewer_theme.js +28 -0
- data/app/models/concerns/geoblacklight/solr_document/thumbnail.rb +38 -0
- data/app/models/concerns/geoblacklight/solr_document.rb +2 -0
- data/app/models/concerns/geoblacklight/suppressed_records_search_behavior.rb +1 -1
- data/app/views/catalog/_document_split.html.erb +10 -10
- data/app/views/catalog/_home_text.html.erb +1 -1
- data/app/views/catalog/metadata.html.erb +1 -1
- data/app/views/{relation → relations}/index.html.erb +1 -1
- data/app/views/relations/index.json.jbuilder +9 -0
- data/config/importmap.rb +8 -8
- data/config/locales/geoblacklight.en.yml +48 -99
- data/geoblacklight.gemspec +3 -3
- data/lib/generators/geoblacklight/assets_generator.rb +2 -2
- data/lib/generators/geoblacklight/install_generator.rb +3 -6
- data/lib/generators/geoblacklight/templates/assets/customizations.css +5 -1
- data/lib/generators/geoblacklight/templates/catalog_controller.rb +17 -27
- data/lib/generators/geoblacklight/templates/settings.yml +15 -95
- data/lib/geoblacklight/configuration/display_note_shown_config.rb +3 -0
- data/lib/geoblacklight/configuration/relationship_config.rb +7 -1
- data/lib/geoblacklight/configuration/relationships_config.rb +15 -15
- data/lib/geoblacklight/configuration/settings_attributes.rb +85 -0
- data/lib/geoblacklight/configuration/settings_builder.rb +3 -14
- data/lib/geoblacklight/configuration.rb +23 -62
- data/lib/geoblacklight/exceptions.rb +4 -0
- data/lib/geoblacklight/reference.rb +0 -2
- data/lib/geoblacklight/references.rb +17 -5
- data/lib/geoblacklight/relations/ancestors.rb +12 -0
- data/lib/geoblacklight/relations/descendants.rb +11 -0
- data/lib/geoblacklight/relations/query.rb +39 -0
- data/lib/geoblacklight/{relation → relations}/relation_response.rb +5 -4
- data/lib/geoblacklight/routes/exportable.rb +2 -1
- data/lib/geoblacklight/version.rb +1 -1
- data/lib/geoblacklight/viewer_record.rb +39 -0
- data/lib/geoblacklight.rb +1 -1
- data/lib/tasks/geoblacklight.rake +1 -17
- data/lib/tasks/solr.rb +37 -0
- data/schema/readme.md +1 -1
- data/script/wait-for-solr +36 -0
- data/solr/conf/solrconfig.xml +10 -16
- data/spec/components/geoblacklight/document/sidebar_component_spec.rb +3 -3
- data/spec/components/geoblacklight/document_component_spec.rb +8 -34
- data/spec/components/geoblacklight/{header_icons_component_spec.rb → header_badges_component_spec.rb} +20 -2
- data/spec/components/geoblacklight/item_map_viewer_component_spec.rb +65 -15
- data/spec/components/geoblacklight/locator_map_component_spec.rb +54 -0
- data/spec/components/geoblacklight/login_link_component_spec.rb +20 -12
- data/spec/components/geoblacklight/metadata_description_markdown_component_spec.rb +1 -6
- data/spec/components/geoblacklight/overview_map_component_spec.rb +136 -0
- data/spec/components/geoblacklight/relations/relation_component_spec.rb +98 -0
- data/spec/components/geoblacklight/relations/relations_component_spec.rb +146 -0
- data/spec/components/geoblacklight/search_result_component_spec.rb +66 -39
- data/spec/components/geoblacklight/static_map_component_spec.rb +8 -2
- data/spec/components/geoblacklight/truncatable_metadata_field_layout_component_spec.rb +83 -0
- data/spec/controllers/catalog_controller_spec.rb +22 -7
- data/spec/controllers/{relation_controller_spec.rb → relations_controller_spec.rb} +1 -1
- data/spec/factories/user.rb +6 -1
- data/spec/features/axe_spec.rb +5 -3
- data/spec/features/home_page_spec.rb +8 -7
- data/spec/features/index_view_spec.rb +6 -20
- data/spec/features/layer_preview_spec.rb +4 -11
- data/spec/features/locator_map_spec.rb +31 -0
- data/spec/features/relations_spec.rb +22 -11
- data/spec/features/restricted_viewer_spec.rb +14 -0
- data/spec/features/search_results_map_spec.rb +89 -13
- data/spec/features/search_spec.rb +6 -2
- data/spec/features/sign_in_spec.rb +26 -0
- data/spec/features/tilejson_spec.rb +1 -3
- data/spec/features/tms_spec.rb +2 -2
- data/spec/features/truncated_metadata_spec.rb +77 -0
- data/spec/features/viewer_requests_spec.rb +40 -0
- data/spec/features/viewer_theme_spec.rb +51 -0
- data/spec/features/wmts_spec.rb +6 -6
- data/spec/features/xyz_spec.rb +3 -3
- data/spec/fixtures/solr_documents/README.md +18 -14
- data/spec/fixtures/solr_documents/actual-polygon1.json +22 -18
- data/spec/fixtures/solr_documents/b1g_wabash_child_15.json +14 -15
- data/spec/fixtures/solr_documents/b1g_wabash_child_16.json +14 -15
- data/spec/fixtures/solr_documents/b1g_wabash_child_17.json +14 -15
- data/spec/fixtures/solr_documents/b1g_wabash_child_18.json +14 -15
- data/spec/fixtures/solr_documents/b1g_wabash_parent.json +14 -15
- data/spec/fixtures/solr_documents/esri-dynamic-layer-single-layer.json +2 -2
- data/spec/fixtures/solr_documents/esri-feature-layer.json +1 -1
- data/spec/fixtures/solr_documents/esri-image-map-layer.json +20 -20
- data/spec/fixtures/solr_documents/esri-wms-layer.json +22 -24
- data/spec/fixtures/solr_documents/index_map_point.json +3 -3
- data/spec/fixtures/solr_documents/public_polygon.json +28 -0
- data/spec/helpers/geoblacklight_helper_spec.rb +103 -25
- data/spec/lib/geoblacklight/bounding_box_spec.rb +16 -0
- data/spec/lib/geoblacklight/configuration/relationships_config_spec.rb +121 -0
- data/spec/lib/geoblacklight/configuration/settings_attributes_spec.rb +113 -0
- data/spec/lib/geoblacklight/configuration/settings_builder_spec.rb +33 -4
- data/spec/lib/geoblacklight/references_spec.rb +35 -0
- data/spec/lib/geoblacklight/{relation → relations}/ancestors_spec.rb +17 -6
- data/spec/lib/geoblacklight/{relation → relations}/descendants_spec.rb +14 -7
- data/spec/lib/geoblacklight/{relation → relations}/relation_response_spec.rb +31 -17
- data/spec/lib/geoblacklight/viewer_record_spec.rb +69 -0
- data/spec/models/concerns/geoblacklight/solr_document/thumbnail_spec.rb +149 -0
- data/spec/models/concerns/geoblacklight/solr_document_spec.rb +1 -1
- data/spec/requests/bookmarks_spec.rb +2 -1
- data/spec/requests/catalog_show_spec.rb +24 -2
- data/spec/requests/index_view_spec.rb +50 -2
- data/spec/requests/missing_metadata_spec.rb +1 -1
- data/spec/requests/relations_spec.rb +9 -2
- data/spec/requests/search_results_spec.rb +6 -6
- data/spec/requests/viewer_record_spec.rb +37 -0
- data/spec/requests/viewer_requests_spec.rb +52 -0
- data/spec/solr_config_spec.rb +41 -0
- data/spec/solr_fixtures_spec.rb +23 -0
- data/spec/spec_helper.rb +13 -3
- data/spec/support/authentication.rb +37 -0
- data/spec/support/features/map_helpers.rb +68 -0
- data/spec/support/features/session_helpers.rb +18 -13
- data/spec/support/features/viewer_helpers.rb +20 -0
- data/spec/support/features.rb +4 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
- data/template.rb +1 -1
- metadata +72 -84
- data/app/components/geoblacklight/accordion_component.html.erb +0 -16
- data/app/components/geoblacklight/accordion_component.rb +0 -15
- data/app/components/geoblacklight/attribute_table_component.html.erb +0 -15
- data/app/components/geoblacklight/attribute_table_component.rb +0 -17
- data/app/components/geoblacklight/header_icons_component.html.erb +0 -3
- data/app/components/geoblacklight/header_icons_component.rb +0 -27
- data/app/components/geoblacklight/index_map_inspect_component.html.erb +0 -12
- data/app/components/geoblacklight/index_map_inspect_component.rb +0 -15
- data/app/components/geoblacklight/index_map_legend_component.html.erb +0 -14
- data/app/components/geoblacklight/index_map_legend_component.rb +0 -24
- data/app/components/geoblacklight/location_leaflet_map_component.rb +0 -68
- data/app/components/geoblacklight/relations_component.html.erb +0 -23
- data/app/components/geoblacklight/relations_component.rb +0 -25
- data/app/components/geoblacklight/viewer_help_text_component.html.erb +0 -1
- data/app/components/geoblacklight/viewer_help_text_component.rb +0 -46
- data/app/controllers/wms_controller.rb +0 -17
- data/app/javascript/geoblacklight/controllers/leaflet_viewer_controller.js +0 -240
- data/app/javascript/geoblacklight/controllers/openlayers_viewer_controller.js +0 -74
- data/app/javascript/geoblacklight/controllers/search_results_controller.js +0 -50
- data/app/javascript/geoblacklight/initializers/popovers.js +0 -11
- data/app/javascript/geoblacklight/initializers/tooltips.js +0 -51
- data/app/javascript/geoblacklight/leaflet/basemaps.js +0 -73
- data/app/javascript/geoblacklight/leaflet/constants.js +0 -18
- data/app/javascript/geoblacklight/leaflet/controls/geosearch.js +0 -168
- data/app/javascript/geoblacklight/leaflet/controls/layer_opacity.js +0 -121
- data/app/javascript/geoblacklight/leaflet/controls/sidebar.js +0 -49
- data/app/javascript/geoblacklight/leaflet/controls/sleep.js +0 -101
- data/app/javascript/geoblacklight/leaflet/inspection.js +0 -210
- data/app/javascript/geoblacklight/leaflet/layer_index_map.js +0 -128
- data/app/javascript/geoblacklight/leaflet/layer_wmts.js +0 -57
- data/app/javascript/geoblacklight/leaflet/layers.js +0 -146
- data/app/javascript/geoblacklight/leaflet/utils.js +0 -83
- data/app/javascript/geoblacklight/openlayers/basemaps.js +0 -55
- data/app/javascript/geoblacklight/openlayers/inspection.js +0 -42
- data/app/javascript/geoblacklight/openlayers/layers.js +0 -44
- data/app/views/relation/index.json.jbuilder +0 -8
- data/lib/geoblacklight/configuration/layer_config.rb +0 -28
- data/lib/geoblacklight/configuration/leaflet_config.rb +0 -73
- data/lib/geoblacklight/configuration/leaflet_layers_config.rb +0 -33
- data/lib/geoblacklight/configuration/leaflet_sleep_config.rb +0 -20
- data/lib/geoblacklight/relation/ancestors.rb +0 -31
- data/lib/geoblacklight/relation/descendants.rb +0 -31
- data/lib/geoblacklight/routes/wms.rb +0 -15
- data/lib/geoblacklight/wms_layer/feature_info_response.rb +0 -37
- data/lib/geoblacklight/wms_layer.rb +0 -34
- data/spec/components/geoblacklight/attribute_table_component_spec.rb +0 -36
- data/spec/components/geoblacklight/index_map_legend_component_spec.rb +0 -29
- data/spec/components/geoblacklight/viewer_help_text_component_spec.rb +0 -27
- data/spec/controllers/wms_controller_spec.rb +0 -34
- data/spec/features/configurable_basemap_spec.rb +0 -40
- data/spec/features/full_screen_control_spec.rb +0 -15
- data/spec/features/layer_inspection_spec.rb +0 -12
- data/spec/features/layer_opacity_spec.rb +0 -16
- data/spec/fixtures/solr_documents/harvard_raster.json +0 -39
- data/spec/fixtures/solr_documents/public_polygon_mit.json +0 -23
- data/spec/fixtures/solr_documents/uva_slug_colon.json +0 -29
- data/spec/javascripts/geoblacklight_spec.js +0 -11
- data/spec/lib/geoblacklight/configuration/layer_config_spec.rb +0 -71
- data/spec/lib/geoblacklight/configuration/leaflet_config_spec.rb +0 -129
- data/spec/lib/geoblacklight/configuration/leaflet_layers_config_spec.rb +0 -63
- data/spec/lib/geoblacklight/configuration_spec.rb +0 -48
- data/spec/lib/geoblacklight/wms_layer/feature_info_response_spec.rb +0 -54
- data/spec/lib/geoblacklight/wms_layer_spec.rb +0 -115
- /data/app/components/geoblacklight/{location_leaflet_map_component.html.erb → locator_map_component.html.erb} +0 -0
|
@@ -19,21 +19,10 @@ module Geoblacklight
|
|
|
19
19
|
@label ||= t("geoblacklight.location")
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# The card is the label and the border; the map inside it is a locator, which is the viewer's own
|
|
23
|
+
# component for where a single record is.
|
|
22
24
|
def viewer_tag
|
|
23
|
-
|
|
24
|
-
leaflet_options.sleep.sleep = false
|
|
25
|
-
tag.div(nil,
|
|
26
|
-
id: "static-map",
|
|
27
|
-
class: "viewer border",
|
|
28
|
-
data: {
|
|
29
|
-
"controller" => "leaflet-viewer",
|
|
30
|
-
"leaflet-viewer-basemap-value" => Geoblacklight.configuration.basemap_provider,
|
|
31
|
-
"leaflet-viewer-dark-basemap-value" => Geoblacklight.configuration.dark_basemap_provider,
|
|
32
|
-
"leaflet-viewer-page-value" => "STATIC_MAP",
|
|
33
|
-
"leaflet-viewer-map-geom-value" => @document.geometry.geojson,
|
|
34
|
-
"leaflet-viewer-options-value" => leaflet_options.to_h,
|
|
35
|
-
"leaflet-viewer-draw-initial-bounds-value" => true
|
|
36
|
-
})
|
|
25
|
+
render Geoblacklight::LocatorMapComponent.new(document: @document)
|
|
37
26
|
end
|
|
38
27
|
end
|
|
39
28
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Geoblacklight
|
|
4
|
+
# Lays out a metadata field so that it shows only the first few of its values, with a
|
|
5
|
+
# button to reveal the rest. The label and the values are wrapped in a div so that
|
|
6
|
+
# app/javascript/geoblacklight/initializers/field_truncation.js can tell which values
|
|
7
|
+
# belong to the field; it reads the data attributes set here.
|
|
8
|
+
#
|
|
9
|
+
# Configure a show field to use it, optionally overriding the limit. Because this is a
|
|
10
|
+
# layout rather than a field component, it composes with whatever component the field
|
|
11
|
+
# already uses to render its values:
|
|
12
|
+
#
|
|
13
|
+
# config.add_show_field field_config.subject, label: "Subject", link_to_facet: true,
|
|
14
|
+
# layout_component: Geoblacklight::TruncatableMetadataFieldLayoutComponent, limit: 3
|
|
15
|
+
class TruncatableMetadataFieldLayoutComponent < Blacklight::MetadataFieldLayoutComponent
|
|
16
|
+
DEFAULT_LIMIT = 5
|
|
17
|
+
|
|
18
|
+
# @param read_more_text [String] label for the button that reveals the rest of the values
|
|
19
|
+
# @param close_text [String] label for the button once the values are revealed
|
|
20
|
+
# @param limit [Integer] number of values to show before hiding the rest
|
|
21
|
+
def initialize(read_more_text: nil, close_text: nil, limit: nil, **kwargs)
|
|
22
|
+
@read_more_text = read_more_text
|
|
23
|
+
@close_text = close_text
|
|
24
|
+
@limit = limit
|
|
25
|
+
super(**kwargs)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def truncate_data
|
|
31
|
+
{
|
|
32
|
+
read_more_text: @read_more_text || t("geoblacklight.truncate.read_more"),
|
|
33
|
+
close_text: @close_text || t("geoblacklight.truncate.close"),
|
|
34
|
+
button_label: t("geoblacklight.truncate.button_label"),
|
|
35
|
+
limit: @limit || @field.field_config[:limit] || DEFAULT_LIMIT
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# A <dl> cannot hold a bare <button>, so the initializer moves the button into this
|
|
40
|
+
# empty <dd>. It takes the same columns as the values so that it lines up with them.
|
|
41
|
+
def read_more_slot_class
|
|
42
|
+
["read-more-slot", @offset_class, @value_class]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
class
|
|
3
|
+
class RelationsController < ApplicationController
|
|
4
4
|
include Blacklight::Configurable
|
|
5
5
|
|
|
6
6
|
copy_blacklight_config_from(CatalogController)
|
|
7
7
|
|
|
8
|
+
allow_unauthenticated_access raise: false if respond_to?(:allow_unauthenticated_access)
|
|
9
|
+
|
|
8
10
|
def index
|
|
9
|
-
@relations = Geoblacklight::
|
|
11
|
+
@relations = Geoblacklight::Relations::RelationResponse.new(params[:id], repository)
|
|
10
12
|
render layout: !(request.xhr? || request.headers["Turbo-Frame"].present?)
|
|
11
13
|
end
|
|
12
14
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Serves the version of the Solr document that is used by viewers.
|
|
4
|
+
class ViewerRecordController < ApplicationController
|
|
5
|
+
include Blacklight::Configurable
|
|
6
|
+
|
|
7
|
+
copy_blacklight_config_from(CatalogController)
|
|
8
|
+
|
|
9
|
+
allow_unauthenticated_access raise: false if respond_to?(:allow_unauthenticated_access)
|
|
10
|
+
|
|
11
|
+
rescue_from Blacklight::Exceptions::RecordNotFound do
|
|
12
|
+
head :not_found
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def show
|
|
16
|
+
render json: Geoblacklight::ViewerRecord.new(document, available: helpers.document_available?(document))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def document
|
|
22
|
+
@document ||= repository.find(params[:id]).documents.first
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def repository
|
|
26
|
+
@repository ||= blacklight_config.repository_class.new(blacklight_config)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -24,15 +24,28 @@ module GeoblacklightHelper
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def document_available?(document)
|
|
27
|
-
document.public? || (document.same_institution? &&
|
|
27
|
+
document.public? || (document.same_institution? && current_user_signed_in?)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def current_user_signed_in?
|
|
31
|
+
controller.respond_to?(:current_user) && current_user.present?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# If you turn off dark mode support, Geoblacklight will display in light mode,
|
|
35
|
+
# but ogm-viewer will still try to honor a system dark mode preference. This
|
|
36
|
+
# ensures that everything stays light until you explicitly enable dark mode.
|
|
37
|
+
def geoblacklight_viewer_theme
|
|
38
|
+
return if blacklight_config.dark_mode_support
|
|
39
|
+
|
|
40
|
+
"light"
|
|
28
41
|
end
|
|
29
42
|
|
|
30
43
|
##
|
|
31
|
-
#
|
|
32
|
-
# @param [
|
|
33
|
-
# @return [
|
|
34
|
-
def
|
|
35
|
-
|
|
44
|
+
# Transform markdown into HTML
|
|
45
|
+
# @param [String] value markdown to transform
|
|
46
|
+
# @return [ActiveSupport::SafeBuffer] rendered HTML
|
|
47
|
+
def markdown_to_html(value)
|
|
48
|
+
Commonmarker.to_html(value).html_safe
|
|
36
49
|
end
|
|
37
50
|
|
|
38
51
|
##
|
|
@@ -76,4 +89,42 @@ module GeoblacklightHelper
|
|
|
76
89
|
"index"
|
|
77
90
|
end
|
|
78
91
|
end
|
|
92
|
+
|
|
93
|
+
##
|
|
94
|
+
# Blacklight view_config.thumbnail_method contract. Renders the document's
|
|
95
|
+
# configured thumbnail_url as an <img>, or nil if unavailable/disabled.
|
|
96
|
+
# @param [SolrDocument] document
|
|
97
|
+
# @param [Hash] image_options
|
|
98
|
+
# @return [String, nil]
|
|
99
|
+
def geoblacklight_thumbnail(document, image_options = {})
|
|
100
|
+
return unless Geoblacklight.configuration.thumbnails_enabled
|
|
101
|
+
|
|
102
|
+
url = document.thumbnail_url
|
|
103
|
+
return if url.blank?
|
|
104
|
+
|
|
105
|
+
image_tag url, {loading: "lazy"}.merge(image_options)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
##
|
|
109
|
+
# Blacklight view_config.default_thumbnail (Symbol) contract. Falls back to
|
|
110
|
+
# a placeholder icon based on the document's resource/data type.
|
|
111
|
+
# @param [SolrDocument] document
|
|
112
|
+
# @param [Hash] _image_options unused; icons aren't rendered as <img>
|
|
113
|
+
# @return [String, nil]
|
|
114
|
+
def geoblacklight_default_thumbnail(document, _image_options = {})
|
|
115
|
+
return unless Geoblacklight.configuration.thumbnails_enabled
|
|
116
|
+
|
|
117
|
+
default_thumbnail_icon(document)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
##
|
|
123
|
+
# @param [SolrDocument] document
|
|
124
|
+
# @return [String]
|
|
125
|
+
def default_thumbnail_icon(document)
|
|
126
|
+
dataset_type = document.resource_type&.find { |type| type.include?(" data") }&.gsub(" data", "")
|
|
127
|
+
name = [dataset_type, document.resource_class&.first].compact.first
|
|
128
|
+
geoblacklight_icon(name)
|
|
129
|
+
end
|
|
79
130
|
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// Which row is being hovered
|
|
4
|
+
const RESULTS = "#documents[data-overview-map-results]"
|
|
5
|
+
const ROW = `${RESULTS} [data-document-counter]`
|
|
6
|
+
|
|
7
|
+
// Controller used for the search results map
|
|
8
|
+
// NOTE: the map is turbo-permanent to prevent flashing on reload
|
|
9
|
+
export default class OverviewMapController extends Controller {
|
|
10
|
+
static values = {
|
|
11
|
+
catalogUrl: String,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
connect() {
|
|
15
|
+
this.element.searchBounds = this.boundsMarker()?.dataset.bounds || undefined
|
|
16
|
+
this.load()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
disconnect() {
|
|
20
|
+
this.unwatchResults()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
boundsMarker() {
|
|
24
|
+
return document.getElementById(`${this.element.id}-bounds`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async load() {
|
|
28
|
+
// Imported here rather than at the top of the file: this library re-exports everything the
|
|
29
|
+
// viewer can draw, including the previewers the components themselves load only on demand, so
|
|
30
|
+
// naming it statically would put all of that on every page that loads GeoBlacklight. Here it
|
|
31
|
+
// arrives with the map, and only on the pages that have one.
|
|
32
|
+
const [library] = await Promise.all([import("ogm-viewer/lib"), customElements.whenDefined("ogm-overview")])
|
|
33
|
+
|
|
34
|
+
// Taken off the page while we waited, by a Turbo visit to somewhere else
|
|
35
|
+
if (!this.element.isConnected) return
|
|
36
|
+
|
|
37
|
+
// Build previewable objects from every result's geometry
|
|
38
|
+
this.element.previewers = this.locations(library, this.results())
|
|
39
|
+
|
|
40
|
+
// Bind event handling for highlighting search result rows
|
|
41
|
+
if (this.rows().length) this.watchResults()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Build a resource and previewer (container for geometry) for each search result.
|
|
45
|
+
// NOTE: results with no geometry still have to take up a number, because the
|
|
46
|
+
// numbering has to match the search result list.
|
|
47
|
+
locations({ LocationPreviewer, LocationResource }, results) {
|
|
48
|
+
const places = results.map(({ place }) => Number(place)).filter((place) => place)
|
|
49
|
+
const locations = Array.from({ length: places.length ? Math.max(...places) : 0 })
|
|
50
|
+
|
|
51
|
+
results.forEach(({ id, place, geometry }) => {
|
|
52
|
+
if (!place || !geometry) return
|
|
53
|
+
|
|
54
|
+
locations[place - 1] = new LocationPreviewer(new LocationResource(id, geometry))
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
return locations
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Parse data coming in from GBL in the template. This gets reset on each
|
|
61
|
+
// load of search results and doesn't use turbo.
|
|
62
|
+
results() {
|
|
63
|
+
const json = document.querySelector(RESULTS)?.dataset.overviewMapResults
|
|
64
|
+
return json ? JSON.parse(json) : []
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Execute the search when you drag a box on the map. The event that gets
|
|
68
|
+
// emitted by the map contains the bounding box as its `detail`.
|
|
69
|
+
search({ detail: bounds }) {
|
|
70
|
+
const url = new URL(this.catalogUrlValue, window.location)
|
|
71
|
+
|
|
72
|
+
// Go back to page 1, and replace the existing query, if there is one
|
|
73
|
+
const params = new URL(window.location).searchParams
|
|
74
|
+
params.delete("page")
|
|
75
|
+
params.set("bbox", bounds.join(" "))
|
|
76
|
+
url.search = params.toString()
|
|
77
|
+
|
|
78
|
+
this.visit(url)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// When you hover over a number on the map, also highlight that row in the
|
|
82
|
+
// search results (apply .highlighted class)
|
|
83
|
+
highlight({ detail }) {
|
|
84
|
+
this.rows().forEach((row) => {
|
|
85
|
+
const marked = detail && (this.layerId(row) === detail.id || this.place(row) === detail.place)
|
|
86
|
+
row.classList.toggle("highlighted", Boolean(marked))
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Reverse of highlight(): when you hover over a row in the search results,
|
|
91
|
+
// focus that number on the map
|
|
92
|
+
handlePointer = (event) => {
|
|
93
|
+
const row = event.target instanceof Element ? event.target.closest(ROW) : null
|
|
94
|
+
this.element.highlighted = row ? this.layerId(row) : undefined
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
watchResults() {
|
|
98
|
+
document.addEventListener("mouseover", this.handlePointer)
|
|
99
|
+
document.addEventListener("focusin", this.handlePointer)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
unwatchResults() {
|
|
103
|
+
document.removeEventListener("mouseover", this.handlePointer)
|
|
104
|
+
document.removeEventListener("focusin", this.handlePointer)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Rows are consulted only for the two-way pointer/focus highlight. Geometry, ids and numbering used
|
|
108
|
+
// to build the map all come from results() instead.
|
|
109
|
+
rows() {
|
|
110
|
+
return Array.from(document.querySelectorAll(ROW))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// The number shown beside a result, which is the number the map draws for it
|
|
114
|
+
place(row) {
|
|
115
|
+
return Number(row.dataset.documentCounter) || undefined
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
layerId(row) {
|
|
119
|
+
return row.dataset.mapId
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Turbo when it's there, and in place when the reader is already on the page they're searching: the
|
|
123
|
+
// page renders again around them, so their scroll position - and the map they are working in - stays
|
|
124
|
+
// where it was. A search from anywhere else is a navigation like any other, and lands at the top.
|
|
125
|
+
visit(url) {
|
|
126
|
+
if (typeof Turbo === "undefined") {
|
|
127
|
+
window.location.href = url
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (url.pathname === window.location.pathname) {
|
|
132
|
+
document.addEventListener(
|
|
133
|
+
"turbo:before-render",
|
|
134
|
+
() => {
|
|
135
|
+
if (Turbo.navigator.currentVisit) Turbo.navigator.currentVisit.scrolled = true
|
|
136
|
+
},
|
|
137
|
+
{ once: true },
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
Turbo.visit(url)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
// Initializers
|
|
2
|
-
import initializePopovers from "geoblacklight/initializers/popovers"
|
|
3
|
-
import initializeTooltips from "geoblacklight/initializers/tooltips"
|
|
4
2
|
import initializeTruncation from "geoblacklight/initializers/truncation"
|
|
3
|
+
import initializeFieldTruncation from "geoblacklight/initializers/field_truncation"
|
|
5
4
|
import initializeMetadataDownload from "geoblacklight/initializers/metadata_download"
|
|
5
|
+
import initializeViewerTheme from "geoblacklight/initializers/viewer_theme"
|
|
6
|
+
import initializeViewerRequests from "geoblacklight/initializers/viewer_requests"
|
|
6
7
|
|
|
7
8
|
// Stimulus controllers
|
|
8
9
|
import ClipboardController from "geoblacklight/controllers/clipboard_controller"
|
|
9
|
-
import LeafletViewerController from "geoblacklight/controllers/leaflet_viewer_controller"
|
|
10
10
|
import OembedViewerController from "geoblacklight/controllers/oembed_viewer_controller"
|
|
11
|
-
import
|
|
12
|
-
import SearchResultsController from "geoblacklight/controllers/search_results_controller"
|
|
11
|
+
import OverviewMapController from "geoblacklight/controllers/overview_map_controller"
|
|
13
12
|
|
|
14
13
|
// Inspired by Blacklight's javascript/blacklight/core.js
|
|
15
14
|
const Geoblacklight = (function () {
|
|
@@ -41,17 +40,16 @@ Geoblacklight.listeners().forEach((listener) =>
|
|
|
41
40
|
)
|
|
42
41
|
|
|
43
42
|
// Register our initializers
|
|
44
|
-
Geoblacklight.onLoad(initializePopovers)
|
|
45
|
-
Geoblacklight.onLoad(initializeTooltips)
|
|
46
43
|
Geoblacklight.onLoad(initializeTruncation)
|
|
44
|
+
Geoblacklight.onLoad(initializeFieldTruncation)
|
|
47
45
|
Geoblacklight.onLoad(initializeMetadataDownload)
|
|
46
|
+
Geoblacklight.onLoad(initializeViewerTheme)
|
|
47
|
+
Geoblacklight.onLoad(initializeViewerRequests)
|
|
48
48
|
|
|
49
49
|
// Register our Stimulus controllers
|
|
50
50
|
if (typeof Stimulus !== "undefined") {
|
|
51
|
-
Stimulus.register("openlayers-viewer", OpenlayersViewerController)
|
|
52
51
|
Stimulus.register("oembed-viewer", OembedViewerController)
|
|
53
|
-
Stimulus.register("
|
|
54
|
-
Stimulus.register("search-results", SearchResultsController)
|
|
52
|
+
Stimulus.register("overview-map", OverviewMapController)
|
|
55
53
|
Stimulus.register("clipboard", ClipboardController)
|
|
56
54
|
} else {
|
|
57
55
|
console.error(
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
// Stimulus controllers
|
|
2
|
-
import OpenlayersViewerController from "geoblacklight/controllers/openlayers_viewer_controller"
|
|
3
2
|
import OembedViewerController from "geoblacklight/controllers/oembed_viewer_controller"
|
|
4
|
-
import
|
|
5
|
-
import SearchResultsController from "geoblacklight/controllers/search_results_controller"
|
|
3
|
+
import OverviewMapController from "geoblacklight/controllers/overview_map_controller"
|
|
6
4
|
import ClipboardController from "geoblacklight/controllers/clipboard_controller"
|
|
7
5
|
|
|
6
|
+
// OpenGeoMetadata web components (each import registers the custom element it is named for)
|
|
7
|
+
import "ogm-viewer"
|
|
8
|
+
import "ogm-overview"
|
|
9
|
+
import "ogm-locator"
|
|
10
|
+
|
|
8
11
|
// GBL core
|
|
9
12
|
import Core from "geoblacklight/core"
|
|
10
|
-
|
|
11
|
-
// Other modules
|
|
12
|
-
import openLayersBasemaps from "geoblacklight/openlayers/basemaps"
|
|
13
|
-
import leafletBasemaps from "geoblacklight/leaflet/basemaps"
|
|
13
|
+
import { onViewerRequest } from "geoblacklight/initializers/viewer_requests"
|
|
14
14
|
|
|
15
15
|
export default {
|
|
16
|
-
OpenlayersViewerController,
|
|
17
16
|
OembedViewerController,
|
|
18
|
-
|
|
19
|
-
SearchResultsController,
|
|
17
|
+
OverviewMapController,
|
|
20
18
|
ClipboardController,
|
|
21
|
-
openLayersBasemaps,
|
|
22
|
-
leafletBasemaps,
|
|
23
19
|
Core,
|
|
24
20
|
onLoad: Core.onLoad,
|
|
21
|
+
onViewerRequest,
|
|
25
22
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Limit how many values a metadata field shows on the show page
|
|
2
|
+
|
|
3
|
+
// Class that hides a field's values past its limit and adds a button to reveal them.
|
|
4
|
+
// The hidden values are only hidden visually, so assistive technology is given the whole
|
|
5
|
+
// list up front. That leaves the button useful to sighted users alone, so it is marked
|
|
6
|
+
// aria-disabled and its label says why.
|
|
7
|
+
class FieldTruncator {
|
|
8
|
+
constructor(element) {
|
|
9
|
+
this.slot = element.querySelector(":scope > dd.read-more-slot")
|
|
10
|
+
if (!this.slot) return
|
|
11
|
+
|
|
12
|
+
// this field was already truncated on an earlier turbo:load/turbo:frame-load
|
|
13
|
+
if (this.slot.firstElementChild) return
|
|
14
|
+
|
|
15
|
+
// if the field is already short enough, don't truncate it
|
|
16
|
+
const values = element.querySelectorAll(":scope > dd:not(.read-more-slot)")
|
|
17
|
+
const limit = parseInt(element.dataset.limit, 10) || 5
|
|
18
|
+
if (values.length <= limit) return
|
|
19
|
+
|
|
20
|
+
// the value at the limit is left in place but clipped, so that the list visibly runs
|
|
21
|
+
// on past the values it is showing; everything after it is hidden outright
|
|
22
|
+
this.preview = values[limit]
|
|
23
|
+
this.hidden = Array.from(values).slice(limit + 1)
|
|
24
|
+
this.readMoreText = element.dataset.readMoreText
|
|
25
|
+
this.closeText = element.dataset.closeText
|
|
26
|
+
|
|
27
|
+
// add the button
|
|
28
|
+
this.button = document.createElement("button")
|
|
29
|
+
this.button.classList.add("btn", "btn-link", "p-0", "border-0", "read-more")
|
|
30
|
+
this.button.setAttribute("aria-disabled", "true")
|
|
31
|
+
this.button.setAttribute("aria-label", element.dataset.buttonLabel)
|
|
32
|
+
this.button.addEventListener("click", this.toggle.bind(this))
|
|
33
|
+
|
|
34
|
+
// start collapsed
|
|
35
|
+
this.collapse()
|
|
36
|
+
this.slot.append(this.button)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
toggle() {
|
|
40
|
+
if (this.expanded) this.collapse()
|
|
41
|
+
else this.expand()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
collapse() {
|
|
45
|
+
this.preview.classList.add("truncate-preview")
|
|
46
|
+
this.hidden.forEach((value) => value.classList.add("visually-hidden"))
|
|
47
|
+
this.button.textContent = this.readMoreText
|
|
48
|
+
this.expanded = false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
expand() {
|
|
52
|
+
this.preview.classList.remove("truncate-preview")
|
|
53
|
+
this.hidden.forEach((value) => value.classList.remove("visually-hidden"))
|
|
54
|
+
this.button.textContent = this.closeText
|
|
55
|
+
this.expanded = true
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Initialize truncation for every metadata field that limits its values
|
|
60
|
+
export default function initializeFieldTruncation() {
|
|
61
|
+
document.querySelectorAll(".truncate-field").forEach((element) => new FieldTruncator(element))
|
|
62
|
+
}
|
|
@@ -28,10 +28,8 @@ export class GeoBlacklightMetadataDownloadButton {
|
|
|
28
28
|
export default function initializeMetadataDownload() {
|
|
29
29
|
const modal = document.getElementById("blacklight-modal")
|
|
30
30
|
|
|
31
|
-
modal
|
|
32
|
-
|
|
33
|
-
return
|
|
34
|
-
}
|
|
31
|
+
// Use Blacklight's dialog-based modal dispatches these custom events
|
|
32
|
+
modal.addEventListener("show.blacklight.blacklight-modal", (e) => {
|
|
35
33
|
e.target.querySelectorAll(".metadata-body").forEach((el) => {
|
|
36
34
|
el.closest(".modal-content").classList.add("metadata-modal")
|
|
37
35
|
})
|
|
@@ -41,10 +39,7 @@ export default function initializeMetadataDownload() {
|
|
|
41
39
|
})
|
|
42
40
|
})
|
|
43
41
|
|
|
44
|
-
modal.addEventListener("
|
|
45
|
-
if (Array.from(e.target.classList).includes("show")) {
|
|
46
|
-
return
|
|
47
|
-
}
|
|
42
|
+
modal.addEventListener("hide.blacklight.blacklight-modal", (e) => {
|
|
48
43
|
e.target.querySelectorAll(".metadata-body").forEach((el) => {
|
|
49
44
|
el.closest(".modal-content").classList.remove("metadata-modal")
|
|
50
45
|
})
|
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
// Uses bootstrap's built-in collapse functionality; CSS is used to do the truncation
|
|
5
5
|
class Truncator {
|
|
6
6
|
constructor(element, { lines, id }) {
|
|
7
|
+
// this element was already processed on an earlier turbo:load/turbo:frame-load
|
|
8
|
+
if (element.classList.contains("collapse")) return
|
|
9
|
+
|
|
7
10
|
// if the element is already small enough, don't truncate it
|
|
8
|
-
const minHeight = lines * parseFloat(window.getComputedStyle(element).
|
|
11
|
+
const minHeight = lines * parseFloat(window.getComputedStyle(element).lineHeight)
|
|
9
12
|
if (element.getBoundingClientRect().height <= minHeight) return
|
|
10
13
|
|
|
11
14
|
// set a unique ID for the element if it doesn't have one
|
|
@@ -13,10 +16,11 @@ class Truncator {
|
|
|
13
16
|
this.element.id ||= id
|
|
14
17
|
this.readMoreText = element.dataset.readMoreText
|
|
15
18
|
this.closeText = element.dataset.closeText
|
|
19
|
+
this.element.style.setProperty("--max-lines", lines)
|
|
16
20
|
|
|
17
21
|
// add the button
|
|
18
22
|
this.button = document.createElement("button")
|
|
19
|
-
this.button.classList.add("btn", "btn-link", "p-0", "border-0")
|
|
23
|
+
this.button.classList.add("btn", "btn-link", "p-0", "border-0", "read-more")
|
|
20
24
|
this.button.dataset.bsToggle = "collapse"
|
|
21
25
|
this.button.dataset.bsTarget = `#${this.element.id}`
|
|
22
26
|
this.button.setAttribute("aria-expanded", "false")
|
|
@@ -40,7 +44,7 @@ class Truncator {
|
|
|
40
44
|
export default function initializeTruncation() {
|
|
41
45
|
document.querySelectorAll(".truncate-abstract").forEach((element, i) => {
|
|
42
46
|
new Truncator(element, {
|
|
43
|
-
lines:
|
|
47
|
+
lines: parseInt(element.dataset.maxLines, 10) || 8,
|
|
44
48
|
id: `truncate-${i}`,
|
|
45
49
|
})
|
|
46
50
|
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Initializer for helping the viewer make authenticated requests for restricted data.
|
|
2
|
+
|
|
3
|
+
// List of registered request transform functions
|
|
4
|
+
const registered = []
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register a transform, from a built GBL app's JavaScript entrypoint:
|
|
8
|
+
*
|
|
9
|
+
* Geoblacklight.onViewerRequest((url, resourceType) => {
|
|
10
|
+
* if (!url.startsWith("https://geo.example.edu/restricted/")) return
|
|
11
|
+
* return { headers: { Authorization: `Bearer ${token}` } }
|
|
12
|
+
* })
|
|
13
|
+
*
|
|
14
|
+
* Answer with `{ headers }`, `{ credentials: "include" }`, or `{ url }` to send the request
|
|
15
|
+
* somewhere else - a proxy on this application's own origin, say, which can authenticate with the
|
|
16
|
+
* service using credentials the browser never sees. Return nothing to leave a request alone.
|
|
17
|
+
*
|
|
18
|
+
* Always check the URL first; the viewer uses the transform for tile and basemap requests too,
|
|
19
|
+
* and you probably don't want to redirect or send cookies to those.
|
|
20
|
+
*
|
|
21
|
+
* Answer synchronously: this runs inline as the reader pans and zooms, so anything it needs - a
|
|
22
|
+
* token, say - has to already be in hand rather than fetched here.
|
|
23
|
+
*
|
|
24
|
+
* @param {(url: string, resourceType: "metadata" | "tile") => object | undefined} transform
|
|
25
|
+
*/
|
|
26
|
+
export function onViewerRequest(transform) {
|
|
27
|
+
registered.push(transform)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// URLs where we know we need to send credentials. This gets set via GBL's
|
|
31
|
+
// configuration as a data attribute on the viewer.
|
|
32
|
+
function restrictedOrigins(viewer) {
|
|
33
|
+
if (!viewer.dataset.restrictedOrigins) return []
|
|
34
|
+
return JSON.parse(viewer.dataset.restrictedOrigins)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Pick the transform to use for a given request. The first registered transform
|
|
38
|
+
// that returns a value will be used.
|
|
39
|
+
//
|
|
40
|
+
// If there weren't any registered transforms but we know the request is to a
|
|
41
|
+
// restricted origin, use a default transform that sends credentials (cookies).
|
|
42
|
+
function transformFor(origins) {
|
|
43
|
+
return (url, resourceType) => {
|
|
44
|
+
for (const transform of registered) {
|
|
45
|
+
const transformed = transform(url, resourceType)
|
|
46
|
+
if (transformed) return transformed
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (origins.some((origin) => url.startsWith(origin))) return { credentials: "include" }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// On load, find all the viewers in the page and set up request transforms for them.
|
|
54
|
+
export default function initializeViewerRequests() {
|
|
55
|
+
document.querySelectorAll("ogm-viewer").forEach((viewer) => {
|
|
56
|
+
const origins = restrictedOrigins(viewer)
|
|
57
|
+
if (!registered.length && !origins.length) return
|
|
58
|
+
|
|
59
|
+
viewer.requestTransform = transformFor(origins)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Keep any viewers on the page in the same color mode as the rest of the page.
|
|
2
|
+
// Bootstrap's mode lives in data-bs-theme on <html>, set by Blacklight's theme switcher; the viewer
|
|
3
|
+
// takes a "theme" attribute. Blacklight resolves "auto" against prefers-color-scheme before writing
|
|
4
|
+
// the attribute, so the value we read is always either "light" or "dark".
|
|
5
|
+
|
|
6
|
+
// Apply the current color mode to every viewer and overview on the page
|
|
7
|
+
function syncViewerTheme() {
|
|
8
|
+
const theme = document.documentElement.getAttribute("data-bs-theme")
|
|
9
|
+
|
|
10
|
+
// No attribute means the host app has dark mode support turned off
|
|
11
|
+
if (!theme) return
|
|
12
|
+
|
|
13
|
+
// Every kind of map the viewer offers: the previewer, the map of where a set of records is, and
|
|
14
|
+
// the map of where one of them is. All three take the same attribute.
|
|
15
|
+
document
|
|
16
|
+
.querySelectorAll("ogm-viewer, ogm-overview, ogm-locator")
|
|
17
|
+
.forEach((viewer) => viewer.setAttribute("theme", theme))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Follow the switcher after load. <html> outlives Turbo navigation, so one observer covers every
|
|
21
|
+
// page: re-observing the same node with the same options replaces the registration instead of
|
|
22
|
+
// stacking up another one, which is what makes this safe to call on each turbo:load.
|
|
23
|
+
const observer = new MutationObserver(syncViewerTheme)
|
|
24
|
+
|
|
25
|
+
export default function initializeViewerTheme() {
|
|
26
|
+
syncViewerTheme()
|
|
27
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-bs-theme"] })
|
|
28
|
+
}
|