Skip to content

Commit

Permalink
add image selection for item embed viewers
Browse files Browse the repository at this point in the history
  • Loading branch information
dnoneill committed Dec 18, 2024
1 parent 3bdfdab commit 878590d
Show file tree
Hide file tree
Showing 16 changed files with 241 additions and 7 deletions.
93 changes: 93 additions & 0 deletions app/assets/javascripts/request_viewer_state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* eslint-disable camelcase */
/* global Blacklight */

(function (global) {
var RequestViewerState;

RequestViewerState = {
init: function() {
this.setupIframeMessageListener();
this.setupRequestButton();
$('[data-embed-url]').oEmbed();
},

setupRequestButton: function() {
$('#request-state').on('click', (event) => {
this.formId = event.target.dataset.formId;
this.itemId = event.target.dataset.itemId;
this.requestState();
});
},

setupIframeMessageListener: function() {
window.addEventListener('message', (event) => {
if (event && event.data) {
// avoid development issues
if (event.data == "recaptcha-setup" || event.data.source == "react-devtools-content-script") { return; }
let parsedData;
try {
parsedData = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
} catch (error) {
console.error('Failed to parse event data:', error);
return; // Exit if parsing fails
}

if (parsedData.type === "stateResponse" && parsedData.source === "sul-embed-m3") {

console.log('Exhibits: received state:', parsedData.data);
let { viewers, windows, iiif_images, canvas_index } = parsedData.data;
if (document.querySelector('#page-2-placeholder')) document.querySelector('#page-2-placeholder').remove();
const viewer = Object.keys(viewers)[0];
const canvas_id = windows[viewer]['canvasId'];
const iiif_initial_viewer_config = JSON.stringify(viewers[viewer]);
const canvasField = document.querySelector(`#${this.formId} > input[name="item[${this.itemId}][iiif_canvas_id]"]`)
const configInput = document.querySelector(`#${this.formId} > input[name="item[${this.itemId}][iiif_initial_viewer_config]"]`)
canvasField.value = canvas_id;
configInput.value = iiif_initial_viewer_config;
document.querySelector(`#${this.formId} > input[name="item[${this.itemId}][full_image_url]"]`).value = iiif_images[0];
const thumbnail_size = iiif_images.length > 1 ? '!33,100' : '!100,100';
const thumbnail = iiif_images.map(image => image.replace('full', thumbnail_size))
document.querySelector(`#${this.formId} > input[name="item[${this.itemId}][thumbnail_image_url]"]`).value = thumbnail[0];
document.querySelector(`#${this.formId} img`).src = `${thumbnail[0]}?${new Date().getTime()}`;
if (thumbnail.length > 1){
if (!document.querySelector('#page-2')) {
document.querySelector(`#${this.formId} img`).insertAdjacentHTML('afterend', `<img id="page-2" src="${thumbnail[1]}?${new Date().getTime()}"/>`);
} else {
document.querySelector('#page-2').src = `${thumbnail[1]}?${new Date().getTime()}`;
}
} else if (document.querySelector('#page-2')) {
document.querySelector('#page-2').src = ''
}
let modal_link_element = document.querySelector(`#${this.formId} #select-image-area`);
let modal_link = this.updateQueryParameters(modal_link_element.href, canvas_id, iiif_initial_viewer_config);
const multi_page = document.querySelector('[data-current-image]');
if (multi_page) multi_page.innerText = canvas_index;
modal_link_element.href = modal_link;
}
}
});
},

updateQueryParameters: function(url, canvas_id, iiif_initial_viewer_config) {
const urlObj = new URL(url);
urlObj.searchParams.set('canvas_id', canvas_id);
urlObj.searchParams.set('iiif_initial_viewer_config', iiif_initial_viewer_config);
return urlObj.href;
},

requestState: function() {
const iframe = document.querySelector('.oembed-widget iframe, iframe.mirador-embed-wrapper');
iframe.contentWindow.postMessage(JSON.stringify({ type: 'requestState' }), '*'); // Change '*' to a specific origin for security?
}
};

global.RequestViewerState = RequestViewerState;
}(this));


Blacklight.onLoad(function () {
'use strict';
document.addEventListener('show.blacklight.blacklight-modal', function(event) {
RequestViewerState.init()
})
});
61 changes: 61 additions & 0 deletions app/assets/javascripts/select_image_area.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* eslint-disable camelcase */
/* global Blacklight */

(function (global) {
var SelectImageArea;

SelectImageArea = {
init: function(el) {
this.panel = $(el);
this.addSelectImageAreaLink();
},

addSelectImageAreaLink: function() {
const target = $('[data-panel-image-pagination]', this.panel);

const resourceId = this.panel.data('resource-id');
const itemId = this.panel.data('id');
const exhibit_path = this.panel.closest('form')[0].dataset.exhibitPath;
const iiif_initial_viewer_config = $(`input[name="item[${itemId}][iiif_initial_viewer_config]"]`, this.panel)[0].value;
const canvas_id = $(`input[name="item[${itemId}][iiif_canvas_id]"]`, this.panel)[0].value;
let href = `${exhibit_path}/select_image_area/${resourceId}?form_id=${this.panel[0].id}&item_id=${itemId}&canvas_id=${canvas_id}`
if (iiif_initial_viewer_config && iiif_initial_viewer_config != "undefined") href += `&iiif_initial_viewer_config=${encodeURIComponent(iiif_initial_viewer_config)}`
const selectImageAreaHtml = $(`<a id="select-image-area" data-blacklight-modal="trigger" href="${href}">Select image area</a>`);
const image_url = this.panel[0].querySelector('img');
if (image_url.src.includes('!33')) {
image_url.insertAdjacentHTML('afterend', '<span id="page-2-placeholder">This section spans two pages, we can not display the thumbnail for page 2.</span>');
}

target.before(selectImageAreaHtml);
}
};

global.SelectImageArea = SelectImageArea;
}(this));

Blacklight.onLoad(function () {
'use strict';

$('[data-type="solr_documents_embed"] .panels li').each(function (i, element) {
SelectImageArea.init(element);
});

// for if another embed widget is added after page load
const callback = function(mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && node.tagName === 'LI') {
SelectImageArea.init(node);
}
});
}
}
};

document.querySelectorAll('[data-type="solr_documents_embed"]').forEach(function(element, i) {
const observer = new MutationObserver(callback);
observer.observe(element, {childList: true, subtree: true});
})
});

13 changes: 12 additions & 1 deletion app/assets/javascripts/sir_trevor_block_overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,18 @@ SirTrevor.Blocks.SolrDocumentsEmbed = (function(){
`<label for="${formId}">Maximum height of viewer (in pixels)</label>`,
`<input id="${formId}" type="number" class="form-control" placeholder="600" name="maxheight" />`
].join(' ');
}
},
_itemPanelIiifFields: function(index, autocomplete_data) {
return [
'<input type="hidden" name="item[' + index + '][thumbnail_image_url]" value="' + (autocomplete_data.thumbnail_image_url || autocomplete_data.thumbnail || "") + '"/>',
'<input type="hidden" name="item[' + index + '][full_image_url]" value="' + (autocomplete_data.full_image_url || autocomplete_data.thumbnail_image_url || autocomplete_data.thumbnail || "") + '"/>',
'<input type="hidden" name="item[' + index + '][iiif_tilesource]" value="' + (autocomplete_data.iiif_tilesource) + '"/>',
'<input type="hidden" name="item[' + index + '][iiif_manifest_url]" value="' + (autocomplete_data.iiif_manifest_url) + '"/>',
'<input type="hidden" name="item[' + index + '][iiif_canvas_id]" value="' + (autocomplete_data.iiif_canvas_id) + '"/>',
"<input type='hidden' name='item[" + index + "][iiif_initial_viewer_config]' value='" + (autocomplete_data.iiif_initial_viewer_config) + "'/>",
'<input type="hidden" name="item[' + index + '][iiif_image_id]" value="' + (autocomplete_data.iiif_image_id) + '"/>',
].join("\n");
},
});
})();

Expand Down
2 changes: 2 additions & 0 deletions app/assets/javascripts/spotlight.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
//= require spotlight/application
//= require sir_trevor_block_overrides
//= require select_image_area
//= require request_viewer_state
2 changes: 1 addition & 1 deletion app/components/custom_viewer_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<% if document.external_iiif? %>
<%= render partial: "embedded_mirador3", locals: {document: document} %>
<%= render partial: "embedded_mirador3", locals: {document: document, block: block_context} %>
<% elsif document.uploaded_resource? %>
<%= render Blacklight::Gallery::OpenseadragonEmbedComponent.new(document: document, presenter: presenter, view_config: view_config) %>
<% else %>
Expand Down
19 changes: 19 additions & 0 deletions app/controllers/catalog_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,25 @@ def search_tips
end
end

def select_image_area
begin
result = search_service.fetch params[:id]
@document = if result.is_a?(Array)
result.last
else
result
end
rescue StandardError
@document = nil
end
respond_to do |format|
format.html do
return render layout: false if request.xhr?
# Otherwise draw the full page
end
end
end

# Action for parker to fetch bibliography references by ID
def documents_list
search_service = Blacklight::SearchService.new(config: blacklight_config)
Expand Down
7 changes: 6 additions & 1 deletion app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ def custom_render_oembed_tag_async(document, canvas_id, block)
content_tag :div, '', data: {
embed_url: blacklight_oembed_engine.embed_url(
url: url,
canvas_id: canvas_id,
iiif_initial_viewer_config: choose_initial_viewer_config(block) || params[:iiif_initial_viewer_config],
canvas_id: canvas_id || params[:canvas_id],
search: params[:search],
maxheight: block&.maxheight.presence || '600',
suggested_search: (current_search_session&.query_params || {})[:q]
Expand All @@ -70,6 +71,10 @@ def choose_canvas_id(sir_trevor_block)
sir_trevor_block&.items&.dig(0, 'iiif_canvas_id') if sir_trevor_block.respond_to? :items
end

def choose_initial_viewer_config(sir_trevor_block)
sir_trevor_block&.items&.dig(0, 'iiif_initial_viewer_config') if sir_trevor_block.respond_to? :items
end

def context_specific_oembed_url(document)
if feature_flags.uat_embed? && document['druid'].present?
format(Settings.purl.uat_url, druid: document['druid'])
Expand Down
2 changes: 1 addition & 1 deletion app/views/catalog/_embedded_mirador3.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<% if document.manifest_url.present? %>
<% manifest_url = document.manifest_url.starts_with?('/') ? root_url + document.manifest_url.slice(1..-1) : document.manifest_url %>
<%= content_tag :iframe, '',
src: "#{Settings.iiif_embed.url}?#{{ url: manifest_url }.to_query}",
src: "#{Settings.iiif_embed.url}?#{{ url: manifest_url, canvas_id: choose_canvas_id(block) || params[:canvas_id], iiif_initial_viewer_config: choose_initial_viewer_config(block) || params[:iiif_initial_viewer_config]}.to_query}",
allowfullscreen: true,
class: 'mirador-embed-wrapper',
frameborder: 0,
Expand Down
35 changes: 35 additions & 0 deletions app/views/catalog/select_image_area.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<%= render Blacklight::System::ModalComponent.new do |component| %>
<% component.with_title do %>
<div><h2 class="modal-title">Select image area</h2></div>
<% end %>
<% component.with_body do %>
<div class="p-4">
<% if @document %>
<p>
Adjust the image so that the viewer contains the area you want to display to exhibit visitors.
<ul>
<li>Click-and-drag to adjust position</li>
<li>Adjust zoom-level</li>
<li>Change page (for multi-page items)</li>
</ul>
Select Save changes to save the image area.
</p>
<%= render CustomViewerComponent.new(document: @document, presenter: document_presenter(@document), view_config: blacklight_config) %>
<% else %>
<p>
<%= link_to params[:id], spotlight.exhibit_solr_document_path(current_exhibit, params[:id]) %> does on exist in this exhibit.
</p>
<% end %>
</div>
<% end %>
<% component.with_footer do %>
<button type="button" class="btn btn-outline-primary me-2" data-bl-dismiss="modal">
Cancel
</button>
<% if @document %>
<button id="request-state" data-bl-dismiss="modal" data-form-id="<%= params[:form_id] %>" data-item-id="<%= params[:item_id] %>" class="btn btn-primary">
Save changes
</button>
<% end %>
<% end %>
<% end %>
2 changes: 1 addition & 1 deletion app/views/viewers/_mirador3.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<% if exhibit_specific_manifest.present? %>
<%= content_tag :iframe, '',
src: "#{Settings.iiif_embed.url}?#{{ url: exhibit_specific_manifest, image_tools: true, canvas_id: document.canvas.iiif_id }.to_query}",
src: "#{Settings.iiif_embed.url}?#{{ url: exhibit_specific_manifest, image_tools: true, canvas_id: choose_canvas_id(block) || params[:canvas_id], iiif_initial_viewer_config: choose_initial_viewer_config(block) || params[:iiif_initial_viewer_config] }.to_query}",
allowfullscreen: true,
class: 'mirador-embed-wrapper',
frameborder: 0,
Expand Down
2 changes: 1 addition & 1 deletion config/initializers/blacklight_oembed.rb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Blacklight::Oembed::Engine.config.additional_params = [:canvas_id, :search, :suggested_search, :maxheight]
Blacklight::Oembed::Engine.config.additional_params = [:canvas_id, :search, :suggested_search, :maxheight, :iiif_initial_viewer_config]
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
end
end

get '/:exhibit_id/select_image_area/:id' => 'catalog#select_image_area'

resources :solr_documents, only: [:show], path: '/catalog', controller: 'catalog'
resource :catalog, only: [:index], as: 'catalog', path: '/catalog', controller: 'catalog' do
concerns :searchable
Expand Down
Binary file added db/development.sqlite3-shm
Binary file not shown.
Empty file added db/development.sqlite3-wal
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddInitialViewerConfigToSpotlightFeaturedImages < ActiveRecord::Migration[7.2]
def change
add_column :spotlight_featured_images, :iiif_initial_viewer_config, :string
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.2].define(version: 2024_09_16_120405) do
ActiveRecord::Schema[7.2].define(version: 2024_12_12_200851) do
create_table "bibliography_services", force: :cascade do |t|
t.string "header"
t.string "api_id"
Expand Down Expand Up @@ -217,6 +217,7 @@
t.string "iiif_canvas_id"
t.string "iiif_image_id"
t.string "iiif_tilesource"
t.string "iiif_initial_viewer_config"
end

create_table "spotlight_filters", force: :cascade do |t|
Expand Down

0 comments on commit 878590d

Please sign in to comment.