From c90c0a367039e1a5b85b0e045fbfa00408806be4 Mon Sep 17 00:00:00 2001 From: Benjamin Armintor Date: Tue, 3 Jan 2023 13:41:08 -0500 Subject: [PATCH 01/11] move all render? logic for SearchContextComponent into component --- .../blacklight/search_context_component.rb | 5 +++-- app/views/catalog/_show_main_content.html.erb | 2 +- .../blacklight/search_context_component_spec.rb | 16 +++++++++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/components/blacklight/search_context_component.rb b/app/components/blacklight/search_context_component.rb index 6b20d7be8f..1aa986be9e 100644 --- a/app/components/blacklight/search_context_component.rb +++ b/app/components/blacklight/search_context_component.rb @@ -4,13 +4,14 @@ module Blacklight class SearchContextComponent < Blacklight::Component with_collection_parameter :search_context - def initialize(search_context:, search_session:) + def initialize(search_context:, search_session:, current_document:) @search_context = search_context @search_session = search_session + @current_document_id = current_document.id end def render? - @search_context.present? && (@search_context[:prev] || @search_context[:next]) + @search_context.present? && (@search_context[:prev] || @search_context[:next]) && (@search_session['document_id'] == @current_document_id) end ## diff --git a/app/views/catalog/_show_main_content.html.erb b/app/views/catalog/_show_main_content.html.erb index aa26aea16b..cbc4c642ec 100644 --- a/app/views/catalog/_show_main_content.html.erb +++ b/app/views/catalog/_show_main_content.html.erb @@ -1,4 +1,4 @@ -<%= render(Blacklight::SearchContextComponent.new(search_context: @search_context, search_session: search_session)) if search_session['document_id'] == @document.id %> +<%= render(Blacklight::SearchContextComponent.new(search_context: @search_context, search_session: search_session, current_document: @document)) %> <% @page_title = t('blacklight.search.show.title', document_title: document_presenter(@document).html_title, application_name: application_name).html_safe %> <% content_for(:head) { render_link_rel_alternates } %> diff --git a/spec/components/blacklight/search_context_component_spec.rb b/spec/components/blacklight/search_context_component_spec.rb index b2544a1dcd..a6f15b9e23 100644 --- a/spec/components/blacklight/search_context_component_spec.rb +++ b/spec/components/blacklight/search_context_component_spec.rb @@ -5,11 +5,13 @@ RSpec.describe Blacklight::SearchContextComponent, type: :component do subject(:render) { render_inline(instance) } - let(:search_session) { {} } - let(:instance) { described_class.new(search_context: search_context, search_session: search_session) } + let(:current_document_id) { 9 } + let(:current_document) { SolrDocument.new(id: current_document_id) } + let(:search_session) { { 'document_id' => current_document_id } } + let(:instance) { described_class.new(search_context: search_context, search_session: search_session, current_document: current_document) } before do - allow(controller).to receive(:current_search_session).and_return(double(id: 9)) + allow(controller).to receive(:current_search_session).and_return(double(id: current_document_id)) end context 'when there is no next or previous' do @@ -36,5 +38,13 @@ it "renders content" do expect(render.css('.pagination-search-widgets').to_html).not_to be_blank end + + context "session and document are out of sync" do + let(:current_document) { SolrDocument.new(id: current_document_id + 1) } + + it "does not render content" do + expect(render.to_html).to be_blank + end + end end end From 56fc4a3842d04d5957ae8239846b39b09f99669f Mon Sep 17 00:00:00 2001 From: Benjamin Armintor Date: Tue, 3 Jan 2023 14:18:19 -0500 Subject: [PATCH 02/11] add a page_links action to Blacklight::Searchable - allows client-side management of show-view result set navigation --- .../concerns/blacklight/search_context.rb | 32 +++++++++++++++++++ lib/blacklight/routes/searchable.rb | 1 + spec/controllers/catalog_controller_spec.rb | 15 +++++++++ spec/routing/catalog_routing_spec.rb | 4 +++ 4 files changed, 52 insertions(+) diff --git a/app/controllers/concerns/blacklight/search_context.rb b/app/controllers/concerns/blacklight/search_context.rb index bb5d1f8f4b..336e7e129b 100644 --- a/app/controllers/concerns/blacklight/search_context.rb +++ b/app/controllers/concerns/blacklight/search_context.rb @@ -28,6 +28,28 @@ def searches_from_history session[:history].blank? ? ::Search.none : ::Search.where(id: session[:history]).order("updated_at desc") end + # GET previous and next document json for the document specified by + # the counter param in current search + def page_links + counter_param = params.delete(:counter) + @page_link_data = {} + if counter_param + index = counter_param.to_i - 1 + response, documents = search_service.previous_and_next_documents_for_search index, search_state.reset_search + if documents.detect(&:present?) + @page_link_data[:prev] = page_links_document_path(documents.first, index) + @page_link_data[:next] = page_links_document_path(documents.last, index + 2) + end + if response&.total && response.total.positive? + @page_link_data[:counterRaw] = counter_param + @page_link_data[:counterDelimited] = helpers.number_with_delimiter(counter_param.to_i) + @page_link_data[:totalRaw] = response.total + @page_link_data[:totalDelimited] = helpers.number_with_delimiter(response.total) + end + end + render json: @page_link_data + end + private # sets up the session[:search] hash if it doesn't already exist @@ -138,4 +160,14 @@ def setup_next_and_previous_documents logger&.warn "Unable to setup next and previous documents: #{e}" nil end + + def page_links_document_path(document, counter) + return nil unless document + + if blacklight_config.view_config(:show).route + search_state.url_for_document(document, counter: counter) + else + solr_document_path(document, counter: counter) + end + end end diff --git a/lib/blacklight/routes/searchable.rb b/lib/blacklight/routes/searchable.rb index 46d610ddf9..bab55b888a 100644 --- a/lib/blacklight/routes/searchable.rb +++ b/lib/blacklight/routes/searchable.rb @@ -10,6 +10,7 @@ def initialize(defaults = {}) def call(mapper, _options = {}) mapper.match '/', action: 'index', as: 'search', via: [:get, :post] mapper.get '/advanced', action: 'advanced_search', as: 'advanced_search' + mapper.get '/page_links', action: 'page_links', as: 'page_links' mapper.post ":id/track", action: 'track', as: 'track' mapper.get ":id/raw", action: 'raw', as: 'raw', defaults: { format: 'json' } diff --git a/spec/controllers/catalog_controller_spec.rb b/spec/controllers/catalog_controller_spec.rb index 14439e4640..ef72999c2d 100644 --- a/spec/controllers/catalog_controller_spec.rb +++ b/spec/controllers/catalog_controller_spec.rb @@ -797,6 +797,21 @@ def export_as_mock expect(controller.send(:search_facet_path, id: "some_facet", page: 5)).to eq facet_catalog_path(id: "some_facet") end end + + describe "page_links" do + it "has prev/next docs and result set data for non-empty result sets", integration: true do + get :page_links, params: { f: { "format" => 'Book' }, counter: 2 } + expect(assigns(:page_link_data)).not_to be_empty + expect(assigns(:page_link_data).fetch(:prev, nil)).to end_with('counter=1') + expect(assigns(:page_link_data).fetch(:next, nil)).to end_with('counter=3') + expect(assigns(:page_link_data).fetch(:totalRaw, nil)).to be 30 + end + + it "is empty for empty result sets", integration: true do + get :page_links, params: { f: { "format" => 'empty-result-set' }, counter: 1 } + expect(assigns(:page_link_data)).to be_empty + end + end end # there must be at least one facet, and each facet must have at least one value diff --git a/spec/routing/catalog_routing_spec.rb b/spec/routing/catalog_routing_spec.rb index d2ee1f19d2..d88622fdc6 100644 --- a/spec/routing/catalog_routing_spec.rb +++ b/spec/routing/catalog_routing_spec.rb @@ -24,6 +24,10 @@ it "maps { :controller => 'catalog', :action => 'show', :id => 666 } to /catalog/666" do expect(get: "/catalog/666").to route_to(controller: 'catalog', action: 'show', id: "666") end + + it "maps GET {:controller => 'catalog', :action => 'prev_next_documents'} to /catalog/page_links" do + expect(get: "/catalog/page_links").to route_to(controller: 'catalog', action: 'page_links') + end end describe "solr_document_path for SolrDocument", test: true do From 973edf7f3dd6bb315d54e2d2842d5a683f02437d Mon Sep 17 00:00:00 2001 From: Benjamin Armintor Date: Tue, 3 Jan 2023 14:34:03 -0500 Subject: [PATCH 03/11] make Blacklight::Configuration track_search_session a string property rather than boolean --- lib/blacklight/configuration.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/blacklight/configuration.rb b/lib/blacklight/configuration.rb index 3c4e4e4aa7..ec64ef0e3a 100644 --- a/lib/blacklight/configuration.rb +++ b/lib/blacklight/configuration.rb @@ -268,9 +268,12 @@ def initialized_default_configuration? property :raw_endpoint, default: OpenStructWithHashAccess.new(enabled: false) # @!attribute track_search_session + # 'server': use server-side tracking + # 'client': delegate search tracking and prev/next navigation to client + # falsy value: do no tracking # @since v7.1.0 - # @return [Boolean] - property :track_search_session, default: true + # @return [] + property :track_search_session, default: 'server' # @!attribute advanced_search # @since v7.15.0 From 4ccdaa6584438bdbe0db0976dec32482c5c87b9e Mon Sep 17 00:00:00 2001 From: Benjamin Armintor Date: Tue, 3 Jan 2023 15:25:58 -0500 Subject: [PATCH 04/11] extract applied_params html into a component --- .../server_applied_params_component.html.erb | 4 +++ .../server_applied_params_component.rb | 13 +++++++++ app/views/catalog/show.html.erb | 7 +---- .../server_applied_params_component_spec.rb | 29 +++++++++++++++++++ 4 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 app/components/blacklight/search_context/server_applied_params_component.html.erb create mode 100644 app/components/blacklight/search_context/server_applied_params_component.rb create mode 100644 spec/components/blacklight/search_context/server_applied_params_component_spec.rb diff --git a/app/components/blacklight/search_context/server_applied_params_component.html.erb b/app/components/blacklight/search_context/server_applied_params_component.html.erb new file mode 100644 index 0000000000..7254cce572 --- /dev/null +++ b/app/components/blacklight/search_context/server_applied_params_component.html.erb @@ -0,0 +1,4 @@ +
+ <%= render 'start_over' %> + <%= link_back_to_catalog class: 'btn btn-outline-secondary' %> +
diff --git a/app/components/blacklight/search_context/server_applied_params_component.rb b/app/components/blacklight/search_context/server_applied_params_component.rb new file mode 100644 index 0000000000..d7783c714d --- /dev/null +++ b/app/components/blacklight/search_context/server_applied_params_component.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Blacklight + module SearchContext + class ServerAppliedParamsComponent < Blacklight::Component + delegate :current_search_session, :link_back_to_catalog, to: :helpers + + def render? + current_search_session + end + end + end +end diff --git a/app/views/catalog/show.html.erb b/app/views/catalog/show.html.erb index 39533c8543..8356996771 100644 --- a/app/views/catalog/show.html.erb +++ b/app/views/catalog/show.html.erb @@ -1,9 +1,4 @@ -<% if current_search_session %> -
- <%= render 'start_over' %> - <%= link_back_to_catalog class: 'btn btn-outline-secondary' %> -
-<% end %> +<%= render Blacklight::SearchContext::ServerAppliedParamsComponent.new %> <%= render 'show_main_content' %> diff --git a/spec/components/blacklight/search_context/server_applied_params_component_spec.rb b/spec/components/blacklight/search_context/server_applied_params_component_spec.rb new file mode 100644 index 0000000000..ff21468e7f --- /dev/null +++ b/spec/components/blacklight/search_context/server_applied_params_component_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Blacklight::SearchContext::ServerAppliedParamsComponent, type: :component do + subject(:render) { instance.render_in(view_context) } + + let(:instance) { described_class.new } + let(:current_search_session) { nil } + let(:view_context) { controller.view_context } + + before do + view_context.view_paths.unshift(RSpec::Rails::ViewExampleGroup::StubResolverCache.resolver_for('application/_start_over.html.erb' => 'start over')) + allow(view_context).to receive(:current_search_session).and_return current_search_session + allow(view_context).to receive(:link_back_to_catalog).with(any_args) + end + + it 'is blank without current session' do + expect(render).to be_blank + end + + context 'with current session' do + let(:current_search_session) { double(query_params: { q: 'abc' }) } + + it 'is not blank' do + expect(render).not_to be_blank + end + end +end From 2f40e51f8acece693592e9a30627badd2d0df74e Mon Sep 17 00:00:00 2001 From: Benjamin Armintor Date: Tue, 3 Jan 2023 16:47:03 -0500 Subject: [PATCH 05/11] implement components to support client-side applied params and pagination in show view --- .../client_applied_params_component.html.erb | 4 ++ .../client_applied_params_component.rb | 12 +++++ .../client_item_pagination_component.html.erb | 10 ++++ .../client_item_pagination_component.rb | 51 +++++++++++++++++++ .../client_applied_params_component_spec.rb | 29 +++++++++++ .../client_item_pagination_component_spec.rb | 37 ++++++++++++++ 6 files changed, 143 insertions(+) create mode 100644 app/components/blacklight/search_context/client_applied_params_component.html.erb create mode 100644 app/components/blacklight/search_context/client_applied_params_component.rb create mode 100644 app/components/blacklight/search_context/client_item_pagination_component.html.erb create mode 100644 app/components/blacklight/search_context/client_item_pagination_component.rb create mode 100644 spec/components/blacklight/search_context/client_applied_params_component_spec.rb create mode 100644 spec/components/blacklight/search_context/client_item_pagination_component_spec.rb diff --git a/app/components/blacklight/search_context/client_applied_params_component.html.erb b/app/components/blacklight/search_context/client_applied_params_component.html.erb new file mode 100644 index 0000000000..9feac6c81a --- /dev/null +++ b/app/components/blacklight/search_context/client_applied_params_component.html.erb @@ -0,0 +1,4 @@ +
+ <%= render 'start_over' %> + <%= link_to t('blacklight.back_to_search'), search_action_path(only_path: true), class: 'btn btn-outline-secondary back-to-catalog d-none', aria: { live: "off" } %> +
diff --git a/app/components/blacklight/search_context/client_applied_params_component.rb b/app/components/blacklight/search_context/client_applied_params_component.rb new file mode 100644 index 0000000000..f2878f23b4 --- /dev/null +++ b/app/components/blacklight/search_context/client_applied_params_component.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Blacklight + module SearchContext + class ClientAppliedParamsComponent < Blacklight::Component + delegate :search_action_path, to: :helpers + def render? + controller.params.permit(:counter)[:counter] + end + end + end +end diff --git a/app/components/blacklight/search_context/client_item_pagination_component.html.erb b/app/components/blacklight/search_context/client_item_pagination_component.html.erb new file mode 100644 index 0000000000..e527b565fc --- /dev/null +++ b/app/components/blacklight/search_context/client_item_pagination_component.html.erb @@ -0,0 +1,10 @@ +
+ + +
diff --git a/app/components/blacklight/search_context/client_item_pagination_component.rb b/app/components/blacklight/search_context/client_item_pagination_component.rb new file mode 100644 index 0000000000..e71f212def --- /dev/null +++ b/app/components/blacklight/search_context/client_item_pagination_component.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module Blacklight + module SearchContext + class ClientItemPaginationComponent < Blacklight::Component + delegate :search_action_path, to: :helpers + + def initialize(counter:, **_args) + @counter = counter + end + + def render? + @counter.present? + end + + def page_links_url + search_action_path action: 'page_links', counter: @counter + end + + ## + # Displays "showing X of Y items" message. + # + # @return [String] + def item_page_entry_info + # rubocop:disable Rails/OutputSafety + t('blacklight.search.entry_pagination_info.other', current: "", + total: "", + count: "").html_safe + # rubocop:enable Rails/OutputSafety + end + + def link_to_previous_document(classes: 'previous', **link_opts) + # rubocop:disable Rails/OutputSafety + link_opts = { class: classes, rel: 'prev' }.merge(link_opts) + link_to '#', link_opts do + tag.span raw(t('views.pagination.previous')), class: 'previous' + end + # rubocop:enable Rails/OutputSafety + end + + def link_to_next_document(classes: 'next', **link_opts) + # rubocop:disable Rails/OutputSafety + link_opts = { class: classes, rel: 'next' }.merge(link_opts) + link_to '#', link_opts do + tag.span raw(t('views.pagination.next')), class: 'next' + end + # rubocop:enable Rails/OutputSafety + end + end + end +end diff --git a/spec/components/blacklight/search_context/client_applied_params_component_spec.rb b/spec/components/blacklight/search_context/client_applied_params_component_spec.rb new file mode 100644 index 0000000000..15b25f5b05 --- /dev/null +++ b/spec/components/blacklight/search_context/client_applied_params_component_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Blacklight::SearchContext::ClientAppliedParamsComponent, type: :component do + subject(:render) { instance.render_in(view_context) } + + let(:instance) { described_class.new } + let(:view_context) { controller.view_context } + + before do + view_context.view_paths.unshift(RSpec::Rails::ViewExampleGroup::StubResolverCache.resolver_for('application/_start_over.html.erb' => 'start over')) + allow(view_context).to receive(:link_back_to_catalog).with(any_args) + end + + it 'is blank without counter param' do + expect(render).to be_blank + end + + context 'with counter param' do + before do + controller.params[:counter] = 1 + end + + it 'is not blank' do + expect(render).not_to be_blank + end + end +end diff --git a/spec/components/blacklight/search_context/client_item_pagination_component_spec.rb b/spec/components/blacklight/search_context/client_item_pagination_component_spec.rb new file mode 100644 index 0000000000..4864fd06a2 --- /dev/null +++ b/spec/components/blacklight/search_context/client_item_pagination_component_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Blacklight::SearchContext::ClientItemPaginationComponent, type: :component do + subject(:render) { instance.render_in(view_context) } + + let(:rendered) do + Capybara::Node::Simple.new(render) + end + + let(:counter) { nil } + let(:instance) { described_class.new(counter: counter) } + let(:view_context) { controller.view_context } + + before do + view_context.view_paths.unshift(RSpec::Rails::ViewExampleGroup::StubResolverCache.resolver_for('application/_start_over.html.erb' => 'start over')) + allow(view_context).to receive(:link_back_to_catalog).with(any_args) + end + + it 'is blank without counter param' do + expect(render).to be_blank + end + + context 'with counter param' do + let(:counter) { 1 } + + it 'is not blank' do + expect(render).not_to be_blank + end + + it 'has spans that will be populated dynamically by the client' do + expect(rendered).to have_selector 'span[class="pagination-counter-delimited"]' + expect(rendered).to have_selector 'span[class="pagination-total-delimited"]' + end + end +end From bbfbb27ce793b29001d96b68ca2e5e20aadaaae5 Mon Sep 17 00:00:00 2001 From: Benjamin Armintor Date: Tue, 3 Jan 2023 16:55:48 -0500 Subject: [PATCH 06/11] only build server-side search tracking data if using server-side tracking --- .../concerns/blacklight/search_context.rb | 2 ++ app/helpers/blacklight/url_helper_behavior.rb | 12 ++++++++++-- spec/helpers/blacklight/url_helper_behavior_spec.rb | 6 ++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/controllers/concerns/blacklight/search_context.rb b/app/controllers/concerns/blacklight/search_context.rb index 336e7e129b..68fda1b4f8 100644 --- a/app/controllers/concerns/blacklight/search_context.rb +++ b/app/controllers/concerns/blacklight/search_context.rb @@ -107,6 +107,8 @@ def agent_is_crawler? end def find_or_initialize_search_session_from_params params + return unless blacklight_config.track_search_session == 'server' + params_copy = params.reject { |k, v| nonpersisted_search_session_params.include?(k.to_sym) || v.blank? } return if params_copy.reject { |k, _v| [:action, :controller].include? k.to_sym }.blank? diff --git a/app/helpers/blacklight/url_helper_behavior.rb b/app/helpers/blacklight/url_helper_behavior.rb index 917bae27af..f5a2ce8e19 100644 --- a/app/helpers/blacklight/url_helper_behavior.rb +++ b/app/helpers/blacklight/url_helper_behavior.rb @@ -43,10 +43,16 @@ def document_link_params(doc, opts) # session_tracking_params(SolrDocument.new(id: 123), 7) # => { data: { context_href: '/catalog/123/track?counter=7&search_id=999' } } def session_tracking_params document, counter, per_page: search_session['per_page'], search_id: current_search_session&.id - path = session_tracking_path(document, per_page: params.fetch(:per_page, per_page), counter: counter, search_id: search_id, document_id: document&.id) + path_params = { per_page: params.fetch(:per_page, per_page), counter: counter, search_id: search_id } + if blacklight_config&.track_search_session == 'server' + path_params[:document_id] = document&.id + path_params[:search_id] = search_id + end + path = session_tracking_path(document, path_params) return {} if path.nil? - { data: { context_href: path } } + context_method = blacklight_config&.track_search_session == 'client' ? 'get' : 'post' + { data: { context_href: path, context_method: context_method } } end ## @@ -63,6 +69,8 @@ def session_tracking_path document, params = {} end def controller_tracking_method + return "solr_document_path" if blacklight_config&.track_search_session == 'client' + "track_#{controller_name}_path" end diff --git a/spec/helpers/blacklight/url_helper_behavior_spec.rb b/spec/helpers/blacklight/url_helper_behavior_spec.rb index 0898ec0267..12765e8b84 100644 --- a/spec/helpers/blacklight/url_helper_behavior_spec.rb +++ b/spec/helpers/blacklight/url_helper_behavior_spec.rb @@ -190,5 +190,11 @@ blacklight_config.track_search_session = false expect(helper.session_tracking_path(document, x: 1)).to be_nil end + + it "uses solr_document_path if tracking is done on the client" do + blacklight_config.track_search_session = 'client' + allow(helper.main_app).to receive(:solr_document_path).with({ id: have_attributes(id: 1), x: 1 }).and_return('x') + expect(helper.session_tracking_path(document, x: 1)).to eq 'x' + end end end From c097179073bb0ccc9c5c24d6e449aa8c0f1814aa Mon Sep 17 00:00:00 2001 From: Benjamin Armintor Date: Tue, 3 Jan 2023 17:00:39 -0500 Subject: [PATCH 07/11] use sessionStorage to track search and fetch pageLinks data on client in show views - use client SearchContext components when blacklight_config.track_search_session is 'client' --- .../javascripts/blacklight/blacklight.js | 90 ++++++++++++++++--- .../javascripts/blacklight/blacklight.js.map | 2 +- app/javascript/blacklight/search_context.js | 90 ++++++++++++++++--- app/views/catalog/_show_main_content.html.erb | 7 +- app/views/catalog/index.html.erb | 3 + app/views/catalog/show.html.erb | 2 +- 6 files changed, 170 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/blacklight/blacklight.js b/app/assets/javascripts/blacklight/blacklight.js index c51d1c4c38..7b800548b5 100644 --- a/app/assets/javascripts/blacklight/blacklight.js +++ b/app/assets/javascripts/blacklight/blacklight.js @@ -333,6 +333,7 @@ Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content; Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content; + Blacklight.searchStorage = () => document.querySelector('meta[name=blacklight-search-storage]')?.content; // this is the Rails.handleMethod with a couple adjustments, described inline: // first, we're attaching this directly to the event handler, so we can check for meta-keys @@ -340,27 +341,34 @@ const link = this; // instead of using the normal href, we need to use the context href instead - let href = link.getAttribute('data-context-href'); + let contextUrl = new URL(link.getAttribute('data-context-href'), window.location); let target = link.getAttribute('target'); let csrfToken = Blacklight.csrfToken(); let csrfParam = Blacklight.csrfParam(); let form = document.createElement('form'); - form.method = 'post'; - form.action = href; + form.action = contextUrl.pathname; + const formMethod = link.getAttribute('data-context-method') || 'post'; + form.method = (formMethod == 'get') ? formMethod : 'post'; + let formContent = ''; + for (const [paramName, paramValue] of contextUrl.searchParams.entries()) { + formContent += ``; + } + if (formMethod != 'get' && formMethod != 'post') formContent += ``; - let formContent = ` - `; - + if (Blacklight.searchStorage() == 'client') { + sessionStorage.setItem("blacklightSearch", new URLSearchParams(new URL(window.location).search)); + } else { + formContent += ``; + if (csrfParam !== undefined && csrfToken !== undefined) { + formContent += ``; + } + } // check for meta keys.. if set, we should open in a new tab if(event.metaKey || event.ctrlKey) { target = '_blank'; } - if (csrfParam !== undefined && csrfToken !== undefined) { - formContent += ``; - } - // Must trigger submit by click on a button, else "submit" event handler won't work! // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit formContent += ''; @@ -376,6 +384,68 @@ }; Blacklight.doSearchContextBehavior(); + + const clientAppliedParams = function(storedSearch) { + if (storedSearch) { + let backToCatalogParams = new URLSearchParams(); + for (const [paramName, paramValue] of storedSearch.entries()) { + backToCatalogParams.append(paramName, paramValue); + } + const appliedParams = document.querySelector('#appliedParams'); + if (appliedParams) { + const backToCatalogEle = appliedParams.querySelector('.back-to-catalog'); + if (backToCatalogEle) { + const backToCatalogUrl = new URL(backToCatalogEle.href); + backToCatalogEle.href = `${backToCatalogUrl.pathname}?${backToCatalogParams.toString()}`; + backToCatalogEle.classList.remove('d-none'); + } + } + } + }; + const clientPrevNext = function(prevNextPath, storedSearch) { + const prevNextUrl = new URL(prevNextPath, window.location); + // prevNextUrl should have a counter param, but needs search params + let prevNextParams = new URLSearchParams(prevNextUrl.search); + if (storedSearch) { + for (const [paramName, paramValue] of storedSearch.entries()) { + prevNextParams.append(paramName, paramValue); + } + } + const setHrefOrDelete = function(linkEle, url) { + if (!linkEle) return; + if (url) { + linkEle.href = url; + } else { + linkEle.remove(); + } + }; + const setContent = function(ele, content) { + if (!ele) return; + ele.innerHTML = content; + }; + fetch(`${prevNextUrl.pathname}?${prevNextParams.toString()}`) + .then((response) => response.json()) + .then(function(responseData) { + if (!responseData.prev && !responseData.next) return; + document.querySelectorAll('.page-links').forEach(function(pageLinks) { + setContent(pageLinks.querySelector('.pagination-counter-raw'), responseData.counterRaw); + setContent(pageLinks.querySelector('.pagination-counter-delimited'), responseData.counterDelimited); + setContent(pageLinks.querySelector('.pagination-total-raw'), responseData.totalRaw); + setContent(pageLinks.querySelector('.pagination-total-delimited'), responseData.totalDelimited); + setHrefOrDelete(pageLinks.querySelector("a[rel='prev']"), responseData.prev); + setHrefOrDelete(pageLinks.querySelector("a[rel='next']"), responseData.next); + pageLinks.classList.remove('d-none'); + }); + }); + }; + Blacklight.onLoad(function(){ + let clientPageLinks = document.querySelectorAll('.page-links[data-page-links-url]'); + if (clientPageLinks[0]) { + const storedSearch = new URLSearchParams(sessionStorage.getItem("blacklightSearch")); + clientAppliedParams(storedSearch); + clientPrevNext(clientPageLinks[0].getAttribute('data-page-links-url'), storedSearch); + } + }); })(); const index = { diff --git a/app/assets/javascripts/blacklight/blacklight.js.map b/app/assets/javascripts/blacklight/blacklight.js.map index 8667933c43..2f2c9c2aac 100644 --- a/app/assets/javascripts/blacklight/blacklight.js.map +++ b/app/assets/javascripts/blacklight/blacklight.js.map @@ -1 +1 @@ -{"version":3,"file":"blacklight.js","sources":["../../../javascript/blacklight/core.js","../../../javascript/blacklight/checkbox_submit.js","../../../javascript/blacklight/bookmark_toggle.js","../../../javascript/blacklight/button_focus.js","../../../javascript/blacklight/modal.js","../../../javascript/blacklight/search_context.js","../../../javascript/blacklight/index.js"],"sourcesContent":["const Blacklight = function() {\n const buffer = new Array;\n return {\n onLoad: function(func) {\n buffer.push(func);\n },\n\n activate: function() {\n for(let i = 0; i < buffer.length; i++) {\n buffer[i].call();\n }\n },\n\n listeners: function () {\n const listeners = [];\n if (typeof Turbo !== 'undefined') {\n listeners.push('turbo:load', 'turbo:frame-load');\n } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {\n // Turbolinks 5\n if (Turbolinks.BrowserAdapter) {\n listeners.push('turbolinks:load');\n } else {\n // Turbolinks < 5\n listeners.push('page:load', 'DOMContentLoaded');\n }\n } else {\n listeners.push('DOMContentLoaded');\n }\n\n return listeners;\n }\n };\n}();\n\n// turbolinks triggers page:load events on page transition\n// If app isn't using turbolinks, this event will never be triggered, no prob.\nBlacklight.listeners().forEach(function(listener) {\n document.addEventListener(listener, function() {\n Blacklight.activate()\n })\n})\n\nBlacklight.onLoad(function () {\n const elem = document.querySelector('.no-js');\n\n // The \"no-js\" class may already have been removed because this function is\n // run on every turbo:load event, in that case, it won't find an element.\n if (!elem) return;\n\n elem.classList.remove('no-js')\n elem.classList.add('js')\n})\n\n\nexport default Blacklight\n","/* Converts a \"toggle\" form, with single submit button to add/remove\n something, like used for Bookmarks, into an AJAXy checkbox instead.\n Apply to a form. Does require certain assumption about the form:\n 1) The same form 'action' href must be used for both ADD and REMOVE\n actions, with the different being the hidden input name=\"_method\"\n being set to \"put\" or \"delete\" -- that's the Rails method to pretend\n to be doing a certain HTTP verb. So same URL, PUT to add, DELETE\n to remove. This plugin assumes that.\n Plus, the form this is applied to should provide a data-doc-id\n attribute (HTML5-style doc-*) that contains the id/primary key\n of the object in question -- used by plugin for a unique value for\n DOM id's.\n Uses HTML for a checkbox compatible with Bootstrap 4.\n new CheckboxSubmit(document.querySelector('form.something')).render()\n*/\nexport default class CheckboxSubmit {\n constructor(form) {\n this.form = form\n }\n\n async clicked(evt) {\n this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress')\n this.labelTarget.setAttribute('disabled', 'disabled');\n this.checkboxTarget.setAttribute('disabled', 'disabled');\n const response = await fetch(this.formTarget.getAttribute('action'), {\n body: new FormData(this.formTarget),\n method: this.formTarget.getAttribute('method').toUpperCase(),\n headers: {\n 'Accept': 'application/json',\n 'X-Requested-With': 'XMLHttpRequest',\n 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content\n }\n })\n this.labelTarget.removeAttribute('disabled')\n this.checkboxTarget.removeAttribute('disabled')\n if (response.ok) {\n const json = await response.json()\n this.updateStateFor(!this.checked)\n document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count\n } else {\n alert('Error')\n }\n }\n\n get checked() {\n return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)\n }\n\n get formTarget() {\n return this.form\n }\n\n get labelTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"label\"]')\n }\n\n get checkboxTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"checkbox\"]')\n }\n\n get spanTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"span\"]')\n }\n\n updateStateFor(state) {\n this.checkboxTarget.checked = state\n\n if (state) {\n this.labelTarget.classList.add('checked')\n //Set the Rails hidden field that fakes an HTTP verb\n //properly for current state action.\n this.formTarget.querySelector('input[name=_method]').value = 'delete'\n this.spanTarget.innerHTML = this.form.getAttribute('data-present')\n } else {\n this.labelTarget.classList.remove('checked')\n this.formTarget.querySelector('input[name=_method]').value = 'put'\n this.spanTarget.innerHTML = this.form.getAttribute('data-absent')\n }\n }\n}\n","import Blacklight from './core'\nimport CheckboxSubmit from './checkbox_submit'\n\nconst BookmarkToggle = (() => {\n // change form submit toggle to checkbox\n Blacklight.doBookmarkToggleBehavior = function() {\n document.addEventListener('click', (e) => {\n if (e.target.matches('[data-checkboxsubmit-target=\"checkbox\"]')) {\n const form = e.target.closest('form')\n if (form) new CheckboxSubmit(form).clicked(e);\n }\n });\n };\n Blacklight.doBookmarkToggleBehavior.selector = 'form.bookmark-toggle';\n\n Blacklight.doBookmarkToggleBehavior();\n})()\n\nexport default BookmarkToggle\n","const ButtonFocus = (() => {\n document.addEventListener('click', (e) => {\n // Button clicks should change focus. As of 10/3/19, Firefox for Mac and\n // Safari both do not set focus to a button on button click.\n // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information\n if (e.target.matches('[data-toggle=\"collapse\"]') || e.target.matches('[data-bs-toggle=\"collapse\"]')) {\n e.target.focus()\n }\n })\n})()\n\nexport default ButtonFocus\n","/*\n The blacklight modal plugin can display some interactions inside a Bootstrap\n modal window, including some multi-page interactions.\n\n It supports unobtrusive Javascript, where a link or form that would have caused\n a new page load is changed to display it's results inside a modal dialog,\n by this plugin. The plugin assumes there is a Bootstrap modal div\n on the page with id #blacklight-modal to use as the modal -- the standard Blacklight\n layout provides this.\n\n To make a link or form have their results display inside a modal, add\n `data-blacklight-modal=\"trigger\"` to the link or form. (Note, form itself not submit input)\n With Rails link_to helper, you'd do that like:\n\n link_to something, link, data: { blacklight_modal: \"trigger\" }\n\n The results of the link href or form submit will be displayed inside\n a modal -- they should include the proper HTML markup for a bootstrap modal's\n contents. Also, you ordinarily won't want the Rails template with wrapping\n navigational elements to be used. The Rails controller could suppress\n the layout when a JS AJAX request is detected, OR the response\n can include a `
` -- only the contents\n of the container will be placed inside the modal, the rest of the\n page will be ignored.\n\n Link or forms inside the modal will ordinarily cause page loads\n when they are triggered. However, if you'd like their results\n to stay within the modal, just add `data-blacklight-modal=\"preserve\"`\n to the link or form.\n\n Here's an example of what might be returned, demonstrating most of the devices available:\n\n
\n
\n \n

Request Placed

\n
\n\n
\n

Some message

\n <%= link_to \"This result will still be within modal\", some_link, data: { blacklight_modal: \"preserve\" } %>\n
\n\n\n
\n \n
\n
\n\n\n One additional feature. If the content returned from the AJAX form submission\n can be a turbo-stream that defines some HTML fragementsand where on the page to put them:\n https://turbo.hotwired.dev/handbook/streams\n*/\nimport Blacklight from './core'\nimport ModalForm from './modalForm'\n\nconst Modal = (() => {\n // We keep all our data in Blacklight.modal object.\n // Create lazily if someone else created first.\n if (Blacklight.modal === undefined) {\n Blacklight.modal = {};\n }\n\n const modal = Blacklight.modal\n\n // a Bootstrap modal div that should be already on the page hidden\n modal.modalSelector = '#blacklight-modal';\n\n // Trigger selectors identify forms or hyperlinks that should open\n // inside a modal dialog.\n modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';\n\n // preserve selectors identify forms or hyperlinks that, if activated already\n // inside a modal dialog, should have destinations remain inside the modal -- but\n // won't trigger a modal if not already in one.\n //\n // No need to repeat selectors from trigger selectors, those will already\n // be preserved. MUST be manually prefixed with the modal selector,\n // so they only apply to things inside a modal.\n modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';\n\n modal.containerSelector = '[data-blacklight-modal~=container]';\n\n // Called on fatal failure of ajax load, function returns content\n // to show to user in modal. Right now called only for extreme\n // network errors.\n modal.onFailure = function (jqXHR, textStatus, errorThrown) {\n console.error('Server error:', this.url, jqXHR.status, errorThrown);\n\n const contents = `
\n
There was a problem with your request.
\n \n
\n
\n

Expected a successful response from the server, but got an error

\n
${this.type} ${this.url}\\n${jqXHR.status}: ${errorThrown}
\n
`\n\n document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents\n\n modal.show();\n }\n\n // Add the passed in contents to the modal and display it.\nmodal.receiveAjax = function (contents) {\n const domparser = new DOMParser();\n const dom = domparser.parseFromString(contents, \"text/html\")\n const elements = dom.querySelectorAll(`${modal.containerSelector} > *`)\n document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements)\n\n modal.show();\n };\n\n\n modal.modalAjaxLinkClick = function(e) {\n e.preventDefault();\n const href = e.target.getAttribute('href')\n fetch(href)\n .then(response => {\n if (!response.ok) {\n throw new TypeError(\"Request failed\");\n }\n return response.text();\n })\n .then(data => modal.receiveAjax(data))\n .catch(error => modal.onFailure(error))\n };\n\n modal.setupModal = function() {\n // Register both trigger and preserve selectors in ONE event handler, combining\n // into one selector with a comma, so if something matches BOTH selectors, it\n // still only gets the event handler called once.\n document.addEventListener('click', (e) => {\n if (e.target.matches(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))\n modal.modalAjaxLinkClick(e)\n else if (e.target.matches('[data-bl-dismiss=\"modal\"]'))\n modal.hide()\n })\n };\n\n modal.hide = function (el) {\n const dom = document.querySelector(Blacklight.modal.modalSelector)\n\n if (!dom.open) return\n dom.close()\n }\n\n modal.show = function(el) {\n const dom = document.querySelector(Blacklight.modal.modalSelector)\n\n if (dom.open) return\n dom.showModal()\n }\n\n modal.setupModal()\n})()\n\nexport default Modal\n","import Blacklight from './core'\n\nconst SearchContext = (() => {\n Blacklight.doSearchContextBehavior = function() {\n document.addEventListener('click', (e) => {\n if (e.target.matches('[data-context-href]')) {\n Blacklight.handleSearchContextMethod.call(e.target, e)\n }\n })\n };\n\n Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content\n Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content\n\n // this is the Rails.handleMethod with a couple adjustments, described inline:\n // first, we're attaching this directly to the event handler, so we can check for meta-keys\n Blacklight.handleSearchContextMethod = function(event) {\n const link = this\n\n // instead of using the normal href, we need to use the context href instead\n let href = link.getAttribute('data-context-href')\n let target = link.getAttribute('target')\n let csrfToken = Blacklight.csrfToken()\n let csrfParam = Blacklight.csrfParam()\n let form = document.createElement('form')\n form.method = 'post'\n form.action = href\n\n\n let formContent = `\n `\n\n // check for meta keys.. if set, we should open in a new tab\n if(event.metaKey || event.ctrlKey) {\n target = '_blank';\n }\n\n if (csrfParam !== undefined && csrfToken !== undefined) {\n formContent += ``\n }\n\n // Must trigger submit by click on a button, else \"submit\" event handler won't work!\n // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit\n formContent += ''\n\n if (target) { form.setAttribute('target', target); }\n\n form.style.display = 'none'\n form.innerHTML = formContent\n document.body.appendChild(form)\n form.querySelector('[type=\"submit\"]').click()\n\n event.preventDefault()\n };\n\n Blacklight.doSearchContextBehavior();\n})()\n\nexport default SearchContext\n","import BookmarkToggle from './bookmark_toggle'\nimport ButtonFocus from './button_focus'\nimport Modal from './modal'\nimport SearchContext from './search_context'\nimport Core from './core'\n\nexport default {\n BookmarkToggle,\n ButtonFocus,\n Modal,\n SearchContext,\n onLoad: Core.onLoad\n}\n"],"names":["Core"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"blacklight.js","sources":["../../../javascript/blacklight/core.js","../../../javascript/blacklight/checkbox_submit.js","../../../javascript/blacklight/bookmark_toggle.js","../../../javascript/blacklight/button_focus.js","../../../javascript/blacklight/modal.js","../../../javascript/blacklight/search_context.js","../../../javascript/blacklight/index.js"],"sourcesContent":["const Blacklight = function() {\n const buffer = new Array;\n return {\n onLoad: function(func) {\n buffer.push(func);\n },\n\n activate: function() {\n for(let i = 0; i < buffer.length; i++) {\n buffer[i].call();\n }\n },\n\n listeners: function () {\n const listeners = [];\n if (typeof Turbo !== 'undefined') {\n listeners.push('turbo:load', 'turbo:frame-load');\n } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {\n // Turbolinks 5\n if (Turbolinks.BrowserAdapter) {\n listeners.push('turbolinks:load');\n } else {\n // Turbolinks < 5\n listeners.push('page:load', 'DOMContentLoaded');\n }\n } else {\n listeners.push('DOMContentLoaded');\n }\n\n return listeners;\n }\n };\n}();\n\n// turbolinks triggers page:load events on page transition\n// If app isn't using turbolinks, this event will never be triggered, no prob.\nBlacklight.listeners().forEach(function(listener) {\n document.addEventListener(listener, function() {\n Blacklight.activate()\n })\n})\n\nBlacklight.onLoad(function () {\n const elem = document.querySelector('.no-js');\n\n // The \"no-js\" class may already have been removed because this function is\n // run on every turbo:load event, in that case, it won't find an element.\n if (!elem) return;\n\n elem.classList.remove('no-js')\n elem.classList.add('js')\n})\n\n\nexport default Blacklight\n","/* Converts a \"toggle\" form, with single submit button to add/remove\n something, like used for Bookmarks, into an AJAXy checkbox instead.\n Apply to a form. Does require certain assumption about the form:\n 1) The same form 'action' href must be used for both ADD and REMOVE\n actions, with the different being the hidden input name=\"_method\"\n being set to \"put\" or \"delete\" -- that's the Rails method to pretend\n to be doing a certain HTTP verb. So same URL, PUT to add, DELETE\n to remove. This plugin assumes that.\n Plus, the form this is applied to should provide a data-doc-id\n attribute (HTML5-style doc-*) that contains the id/primary key\n of the object in question -- used by plugin for a unique value for\n DOM id's.\n Uses HTML for a checkbox compatible with Bootstrap 4.\n new CheckboxSubmit(document.querySelector('form.something')).render()\n*/\nexport default class CheckboxSubmit {\n constructor(form) {\n this.form = form\n }\n\n async clicked(evt) {\n this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress')\n this.labelTarget.setAttribute('disabled', 'disabled');\n this.checkboxTarget.setAttribute('disabled', 'disabled');\n const response = await fetch(this.formTarget.getAttribute('action'), {\n body: new FormData(this.formTarget),\n method: this.formTarget.getAttribute('method').toUpperCase(),\n headers: {\n 'Accept': 'application/json',\n 'X-Requested-With': 'XMLHttpRequest',\n 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content\n }\n })\n this.labelTarget.removeAttribute('disabled')\n this.checkboxTarget.removeAttribute('disabled')\n if (response.ok) {\n const json = await response.json()\n this.updateStateFor(!this.checked)\n document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count\n } else {\n alert('Error')\n }\n }\n\n get checked() {\n return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)\n }\n\n get formTarget() {\n return this.form\n }\n\n get labelTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"label\"]')\n }\n\n get checkboxTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"checkbox\"]')\n }\n\n get spanTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"span\"]')\n }\n\n updateStateFor(state) {\n this.checkboxTarget.checked = state\n\n if (state) {\n this.labelTarget.classList.add('checked')\n //Set the Rails hidden field that fakes an HTTP verb\n //properly for current state action.\n this.formTarget.querySelector('input[name=_method]').value = 'delete'\n this.spanTarget.innerHTML = this.form.getAttribute('data-present')\n } else {\n this.labelTarget.classList.remove('checked')\n this.formTarget.querySelector('input[name=_method]').value = 'put'\n this.spanTarget.innerHTML = this.form.getAttribute('data-absent')\n }\n }\n}\n","import Blacklight from './core'\nimport CheckboxSubmit from './checkbox_submit'\n\nconst BookmarkToggle = (() => {\n // change form submit toggle to checkbox\n Blacklight.doBookmarkToggleBehavior = function() {\n document.addEventListener('click', (e) => {\n if (e.target.matches('[data-checkboxsubmit-target=\"checkbox\"]')) {\n const form = e.target.closest('form')\n if (form) new CheckboxSubmit(form).clicked(e);\n }\n });\n };\n Blacklight.doBookmarkToggleBehavior.selector = 'form.bookmark-toggle';\n\n Blacklight.doBookmarkToggleBehavior();\n})()\n\nexport default BookmarkToggle\n","const ButtonFocus = (() => {\n document.addEventListener('click', (e) => {\n // Button clicks should change focus. As of 10/3/19, Firefox for Mac and\n // Safari both do not set focus to a button on button click.\n // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information\n if (e.target.matches('[data-toggle=\"collapse\"]') || e.target.matches('[data-bs-toggle=\"collapse\"]')) {\n e.target.focus()\n }\n })\n})()\n\nexport default ButtonFocus\n","/*\n The blacklight modal plugin can display some interactions inside a Bootstrap\n modal window, including some multi-page interactions.\n\n It supports unobtrusive Javascript, where a link or form that would have caused\n a new page load is changed to display it's results inside a modal dialog,\n by this plugin. The plugin assumes there is a Bootstrap modal div\n on the page with id #blacklight-modal to use as the modal -- the standard Blacklight\n layout provides this.\n\n To make a link or form have their results display inside a modal, add\n `data-blacklight-modal=\"trigger\"` to the link or form. (Note, form itself not submit input)\n With Rails link_to helper, you'd do that like:\n\n link_to something, link, data: { blacklight_modal: \"trigger\" }\n\n The results of the link href or form submit will be displayed inside\n a modal -- they should include the proper HTML markup for a bootstrap modal's\n contents. Also, you ordinarily won't want the Rails template with wrapping\n navigational elements to be used. The Rails controller could suppress\n the layout when a JS AJAX request is detected, OR the response\n can include a `
` -- only the contents\n of the container will be placed inside the modal, the rest of the\n page will be ignored.\n\n Link or forms inside the modal will ordinarily cause page loads\n when they are triggered. However, if you'd like their results\n to stay within the modal, just add `data-blacklight-modal=\"preserve\"`\n to the link or form.\n\n Here's an example of what might be returned, demonstrating most of the devices available:\n\n
\n
\n \n

Request Placed

\n
\n\n
\n

Some message

\n <%= link_to \"This result will still be within modal\", some_link, data: { blacklight_modal: \"preserve\" } %>\n
\n\n\n
\n \n
\n
\n\n\n One additional feature. If the content returned from the AJAX form submission\n can be a turbo-stream that defines some HTML fragementsand where on the page to put them:\n https://turbo.hotwired.dev/handbook/streams\n*/\nimport Blacklight from './core'\nimport ModalForm from './modalForm'\n\nconst Modal = (() => {\n // We keep all our data in Blacklight.modal object.\n // Create lazily if someone else created first.\n if (Blacklight.modal === undefined) {\n Blacklight.modal = {};\n }\n\n const modal = Blacklight.modal\n\n // a Bootstrap modal div that should be already on the page hidden\n modal.modalSelector = '#blacklight-modal';\n\n // Trigger selectors identify forms or hyperlinks that should open\n // inside a modal dialog.\n modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';\n\n // preserve selectors identify forms or hyperlinks that, if activated already\n // inside a modal dialog, should have destinations remain inside the modal -- but\n // won't trigger a modal if not already in one.\n //\n // No need to repeat selectors from trigger selectors, those will already\n // be preserved. MUST be manually prefixed with the modal selector,\n // so they only apply to things inside a modal.\n modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';\n\n modal.containerSelector = '[data-blacklight-modal~=container]';\n\n // Called on fatal failure of ajax load, function returns content\n // to show to user in modal. Right now called only for extreme\n // network errors.\n modal.onFailure = function (jqXHR, textStatus, errorThrown) {\n console.error('Server error:', this.url, jqXHR.status, errorThrown);\n\n const contents = `
\n
There was a problem with your request.
\n \n
\n
\n

Expected a successful response from the server, but got an error

\n
${this.type} ${this.url}\\n${jqXHR.status}: ${errorThrown}
\n
`\n\n document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents\n\n modal.show();\n }\n\n // Add the passed in contents to the modal and display it.\nmodal.receiveAjax = function (contents) {\n const domparser = new DOMParser();\n const dom = domparser.parseFromString(contents, \"text/html\")\n const elements = dom.querySelectorAll(`${modal.containerSelector} > *`)\n document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements)\n\n modal.show();\n };\n\n\n modal.modalAjaxLinkClick = function(e) {\n e.preventDefault();\n const href = e.target.getAttribute('href')\n fetch(href)\n .then(response => {\n if (!response.ok) {\n throw new TypeError(\"Request failed\");\n }\n return response.text();\n })\n .then(data => modal.receiveAjax(data))\n .catch(error => modal.onFailure(error))\n };\n\n modal.setupModal = function() {\n // Register both trigger and preserve selectors in ONE event handler, combining\n // into one selector with a comma, so if something matches BOTH selectors, it\n // still only gets the event handler called once.\n document.addEventListener('click', (e) => {\n if (e.target.matches(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))\n modal.modalAjaxLinkClick(e)\n else if (e.target.matches('[data-bl-dismiss=\"modal\"]'))\n modal.hide()\n })\n };\n\n modal.hide = function (el) {\n const dom = document.querySelector(Blacklight.modal.modalSelector)\n\n if (!dom.open) return\n dom.close()\n }\n\n modal.show = function(el) {\n const dom = document.querySelector(Blacklight.modal.modalSelector)\n\n if (dom.open) return\n dom.showModal()\n }\n\n modal.setupModal()\n})()\n\nexport default Modal\n","import Blacklight from './core'\n\nconst SearchContext = (() => {\n Blacklight.doSearchContextBehavior = function() {\n document.addEventListener('click', (e) => {\n if (e.target.matches('[data-context-href]')) {\n Blacklight.handleSearchContextMethod.call(e.target, e)\n }\n })\n };\n\n Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content\n Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content\n Blacklight.searchStorage = () => document.querySelector('meta[name=blacklight-search-storage]')?.content\n\n // this is the Rails.handleMethod with a couple adjustments, described inline:\n // first, we're attaching this directly to the event handler, so we can check for meta-keys\n Blacklight.handleSearchContextMethod = function(event) {\n const link = this\n\n // instead of using the normal href, we need to use the context href instead\n let contextUrl = new URL(link.getAttribute('data-context-href'), window.location)\n let target = link.getAttribute('target')\n let csrfToken = Blacklight.csrfToken()\n let csrfParam = Blacklight.csrfParam()\n let form = document.createElement('form')\n form.action = contextUrl.pathname;\n const formMethod = link.getAttribute('data-context-method') || 'post'\n form.method = (formMethod == 'get') ? formMethod : 'post'\n\n let formContent = ''\n for (const [paramName, paramValue] of contextUrl.searchParams.entries()) {\n formContent += ``\n }\n if (formMethod != 'get' && formMethod != 'post') formContent += ``\n\n if (Blacklight.searchStorage() == 'client') {\n sessionStorage.setItem(\"blacklightSearch\", new URLSearchParams(new URL(window.location).search))\n } else {\n formContent += ``\n if (csrfParam !== undefined && csrfToken !== undefined) {\n formContent += ``\n }\n }\n // check for meta keys.. if set, we should open in a new tab\n if(event.metaKey || event.ctrlKey) {\n target = '_blank';\n }\n\n // Must trigger submit by click on a button, else \"submit\" event handler won't work!\n // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit\n formContent += ''\n\n if (target) { form.setAttribute('target', target); }\n\n form.style.display = 'none'\n form.innerHTML = formContent\n document.body.appendChild(form)\n form.querySelector('[type=\"submit\"]').click()\n\n event.preventDefault()\n };\n\n Blacklight.doSearchContextBehavior();\n\n const clientAppliedParams = function(storedSearch) {\n if (storedSearch) {\n let backToCatalogParams = new URLSearchParams();\n for (const [paramName, paramValue] of storedSearch.entries()) {\n backToCatalogParams.append(paramName, paramValue);\n }\n const appliedParams = document.querySelector('#appliedParams')\n if (appliedParams) {\n const backToCatalogEle = appliedParams.querySelector('.back-to-catalog')\n if (backToCatalogEle) {\n const backToCatalogUrl = new URL(backToCatalogEle.href)\n backToCatalogEle.href = `${backToCatalogUrl.pathname}?${backToCatalogParams.toString()}`\n backToCatalogEle.classList.remove('d-none')\n }\n }\n }\n }\n const clientPrevNext = function(prevNextPath, storedSearch) {\n const prevNextUrl = new URL(prevNextPath, window.location)\n // prevNextUrl should have a counter param, but needs search params\n let prevNextParams = new URLSearchParams(prevNextUrl.search);\n if (storedSearch) {\n for (const [paramName, paramValue] of storedSearch.entries()) {\n prevNextParams.append(paramName, paramValue);\n }\n }\n const setHrefOrDelete = function(linkEle, url) {\n if (!linkEle) return;\n if (url) {\n linkEle.href = url\n } else {\n linkEle.remove()\n }\n }\n const setContent = function(ele, content) {\n if (!ele) return;\n ele.innerHTML = content;\n }\n fetch(`${prevNextUrl.pathname}?${prevNextParams.toString()}`)\n .then((response) => response.json())\n .then(function(responseData) {\n if (!responseData.prev && !responseData.next) return;\n document.querySelectorAll('.page-links').forEach(function(pageLinks) {\n setContent(pageLinks.querySelector('.pagination-counter-raw'), responseData.counterRaw)\n setContent(pageLinks.querySelector('.pagination-counter-delimited'), responseData.counterDelimited)\n setContent(pageLinks.querySelector('.pagination-total-raw'), responseData.totalRaw)\n setContent(pageLinks.querySelector('.pagination-total-delimited'), responseData.totalDelimited)\n setHrefOrDelete(pageLinks.querySelector(\"a[rel='prev']\"), responseData.prev)\n setHrefOrDelete(pageLinks.querySelector(\"a[rel='next']\"), responseData.next)\n pageLinks.classList.remove('d-none')\n })\n })\n }\n Blacklight.onLoad(function(){\n let clientPageLinks = document.querySelectorAll('.page-links[data-page-links-url]')\n if (clientPageLinks[0]) {\n const storedSearch = new URLSearchParams(sessionStorage.getItem(\"blacklightSearch\"))\n clientAppliedParams(storedSearch)\n clientPrevNext(clientPageLinks[0].getAttribute('data-page-links-url'), storedSearch)\n }\n })\n})()\n\nexport default SearchContext\n","import BookmarkToggle from './bookmark_toggle'\nimport ButtonFocus from './button_focus'\nimport Modal from './modal'\nimport SearchContext from './search_context'\nimport Core from './core'\n\nexport default {\n BookmarkToggle,\n ButtonFocus,\n Modal,\n SearchContext,\n onLoad: Core.onLoad\n}\n"],"names":["Core"],"mappings":"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"} \ No newline at end of file diff --git a/app/javascript/blacklight/search_context.js b/app/javascript/blacklight/search_context.js index 0f7d229d6e..9d903fdcdd 100644 --- a/app/javascript/blacklight/search_context.js +++ b/app/javascript/blacklight/search_context.js @@ -11,6 +11,7 @@ const SearchContext = (() => { Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content + Blacklight.searchStorage = () => document.querySelector('meta[name=blacklight-search-storage]')?.content // this is the Rails.handleMethod with a couple adjustments, described inline: // first, we're attaching this directly to the event handler, so we can check for meta-keys @@ -18,27 +19,34 @@ const SearchContext = (() => { const link = this // instead of using the normal href, we need to use the context href instead - let href = link.getAttribute('data-context-href') + let contextUrl = new URL(link.getAttribute('data-context-href'), window.location) let target = link.getAttribute('target') let csrfToken = Blacklight.csrfToken() let csrfParam = Blacklight.csrfParam() let form = document.createElement('form') - form.method = 'post' - form.action = href + form.action = contextUrl.pathname; + const formMethod = link.getAttribute('data-context-method') || 'post' + form.method = (formMethod == 'get') ? formMethod : 'post' + let formContent = '' + for (const [paramName, paramValue] of contextUrl.searchParams.entries()) { + formContent += `` + } + if (formMethod != 'get' && formMethod != 'post') formContent += `` - let formContent = ` - ` - + if (Blacklight.searchStorage() == 'client') { + sessionStorage.setItem("blacklightSearch", new URLSearchParams(new URL(window.location).search)) + } else { + formContent += `` + if (csrfParam !== undefined && csrfToken !== undefined) { + formContent += `` + } + } // check for meta keys.. if set, we should open in a new tab if(event.metaKey || event.ctrlKey) { target = '_blank'; } - if (csrfParam !== undefined && csrfToken !== undefined) { - formContent += `` - } - // Must trigger submit by click on a button, else "submit" event handler won't work! // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit formContent += '' @@ -54,6 +62,68 @@ const SearchContext = (() => { }; Blacklight.doSearchContextBehavior(); + + const clientAppliedParams = function(storedSearch) { + if (storedSearch) { + let backToCatalogParams = new URLSearchParams(); + for (const [paramName, paramValue] of storedSearch.entries()) { + backToCatalogParams.append(paramName, paramValue); + } + const appliedParams = document.querySelector('#appliedParams') + if (appliedParams) { + const backToCatalogEle = appliedParams.querySelector('.back-to-catalog') + if (backToCatalogEle) { + const backToCatalogUrl = new URL(backToCatalogEle.href) + backToCatalogEle.href = `${backToCatalogUrl.pathname}?${backToCatalogParams.toString()}` + backToCatalogEle.classList.remove('d-none') + } + } + } + } + const clientPrevNext = function(prevNextPath, storedSearch) { + const prevNextUrl = new URL(prevNextPath, window.location) + // prevNextUrl should have a counter param, but needs search params + let prevNextParams = new URLSearchParams(prevNextUrl.search); + if (storedSearch) { + for (const [paramName, paramValue] of storedSearch.entries()) { + prevNextParams.append(paramName, paramValue); + } + } + const setHrefOrDelete = function(linkEle, url) { + if (!linkEle) return; + if (url) { + linkEle.href = url + } else { + linkEle.remove() + } + } + const setContent = function(ele, content) { + if (!ele) return; + ele.innerHTML = content; + } + fetch(`${prevNextUrl.pathname}?${prevNextParams.toString()}`) + .then((response) => response.json()) + .then(function(responseData) { + if (!responseData.prev && !responseData.next) return; + document.querySelectorAll('.page-links').forEach(function(pageLinks) { + setContent(pageLinks.querySelector('.pagination-counter-raw'), responseData.counterRaw) + setContent(pageLinks.querySelector('.pagination-counter-delimited'), responseData.counterDelimited) + setContent(pageLinks.querySelector('.pagination-total-raw'), responseData.totalRaw) + setContent(pageLinks.querySelector('.pagination-total-delimited'), responseData.totalDelimited) + setHrefOrDelete(pageLinks.querySelector("a[rel='prev']"), responseData.prev) + setHrefOrDelete(pageLinks.querySelector("a[rel='next']"), responseData.next) + pageLinks.classList.remove('d-none') + }) + }) + } + Blacklight.onLoad(function(){ + let clientPageLinks = document.querySelectorAll('.page-links[data-page-links-url]') + if (clientPageLinks[0]) { + const storedSearch = new URLSearchParams(sessionStorage.getItem("blacklightSearch")) + clientAppliedParams(storedSearch) + clientPrevNext(clientPageLinks[0].getAttribute('data-page-links-url'), storedSearch) + } + }) })() export default SearchContext diff --git a/app/views/catalog/_show_main_content.html.erb b/app/views/catalog/_show_main_content.html.erb index cbc4c642ec..438c8c4c44 100644 --- a/app/views/catalog/_show_main_content.html.erb +++ b/app/views/catalog/_show_main_content.html.erb @@ -1,5 +1,8 @@ -<%= render(Blacklight::SearchContextComponent.new(search_context: @search_context, search_session: search_session, current_document: @document)) %> - +<% if blacklight_config.track_search_session == 'client' -%> + <%= render Blacklight::SearchContext::ClientItemPaginationComponent.new(counter: params.permit(:counter)[:counter]) -%> +<% else -%> + <%= render Blacklight::SearchContextComponent.new(search_context: @search_context, search_session: search_session, current_document: @document) -%> +<% end -%> <% @page_title = t('blacklight.search.show.title', document_title: document_presenter(@document).html_title, application_name: application_name).html_safe %> <% content_for(:head) { render_link_rel_alternates } %> diff --git a/app/views/catalog/index.html.erb b/app/views/catalog/index.html.erb index 3b735b3c91..7d829c20e4 100644 --- a/app/views/catalog/index.html.erb +++ b/app/views/catalog/index.html.erb @@ -1,3 +1,6 @@ +<% content_for(:head) do %> + +<% end %> <% content_for(:sidebar) do %> <% conf = blacklight_config.view_config(document_index_view_type) %> <%= render conf.sidebar_component.new(blacklight_config: blacklight_config, diff --git a/app/views/catalog/show.html.erb b/app/views/catalog/show.html.erb index 8356996771..ef36062c96 100644 --- a/app/views/catalog/show.html.erb +++ b/app/views/catalog/show.html.erb @@ -1,4 +1,4 @@ -<%= render Blacklight::SearchContext::ServerAppliedParamsComponent.new %> +<%= render (blacklight_config.track_search_session == 'client' ? Blacklight::SearchContext::ClientAppliedParamsComponent : Blacklight::SearchContext::ServerAppliedParamsComponent).new %> <%= render 'show_main_content' %> From 4d3b762d51f0a01b422096a1c4367cd93bf4d7ca Mon Sep 17 00:00:00 2001 From: Benjamin Armintor Date: Wed, 4 Jan 2023 09:02:14 -0500 Subject: [PATCH 08/11] remove unnecessary permits of :counter param --- .../search_context/client_applied_params_component.rb | 2 +- app/views/catalog/_show_main_content.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/blacklight/search_context/client_applied_params_component.rb b/app/components/blacklight/search_context/client_applied_params_component.rb index f2878f23b4..f04aaa0a65 100644 --- a/app/components/blacklight/search_context/client_applied_params_component.rb +++ b/app/components/blacklight/search_context/client_applied_params_component.rb @@ -5,7 +5,7 @@ module SearchContext class ClientAppliedParamsComponent < Blacklight::Component delegate :search_action_path, to: :helpers def render? - controller.params.permit(:counter)[:counter] + controller.params[:counter] end end end diff --git a/app/views/catalog/_show_main_content.html.erb b/app/views/catalog/_show_main_content.html.erb index 438c8c4c44..3b4d3912e6 100644 --- a/app/views/catalog/_show_main_content.html.erb +++ b/app/views/catalog/_show_main_content.html.erb @@ -1,5 +1,5 @@ <% if blacklight_config.track_search_session == 'client' -%> - <%= render Blacklight::SearchContext::ClientItemPaginationComponent.new(counter: params.permit(:counter)[:counter]) -%> + <%= render Blacklight::SearchContext::ClientItemPaginationComponent.new(counter: params[:counter]) -%> <% else -%> <%= render Blacklight::SearchContextComponent.new(search_context: @search_context, search_session: search_session, current_document: @document) -%> <% end -%> From 69e527acbbc80613342193e1969992bb95107f33 Mon Sep 17 00:00:00 2001 From: Benjamin Armintor Date: Wed, 4 Jan 2023 16:38:41 -0500 Subject: [PATCH 09/11] refactor search_context.js for readability and documentation --- .../javascripts/blacklight/blacklight.js | 160 +++++++++++------- .../javascripts/blacklight/blacklight.js.map | 2 +- app/javascript/blacklight/search_context.js | 160 +++++++++++------- 3 files changed, 205 insertions(+), 117 deletions(-) diff --git a/app/assets/javascripts/blacklight/blacklight.js b/app/assets/javascripts/blacklight/blacklight.js index 7b800548b5..c82dc4cf50 100644 --- a/app/assets/javascripts/blacklight/blacklight.js +++ b/app/assets/javascripts/blacklight/blacklight.js @@ -324,106 +324,157 @@ const SearchContext = (() => { Blacklight.doSearchContextBehavior = function() { + // intercept clicks on search results to use search context behavior document.addEventListener('click', (e) => { if (e.target.matches('[data-context-href]')) { Blacklight.handleSearchContextMethod.call(e.target, e); } }); + // page-links dynamic content depends on a loaded document + Blacklight.onLoad(() => { + // if page-links container has a data URL, do client-side search context behaviors + let clientPageLinks = document.querySelectorAll('.page-links[data-page-links-url]'); + if (clientPageLinks[0]) { + const storedSearch = new URLSearchParams(sessionStorage.getItem("blacklightSearch")); + Blacklight.clientAppliedParams(storedSearch); + Blacklight.clientPageLinks(clientPageLinks[0].getAttribute('data-page-links-url'), storedSearch); + } + }); }; Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content; Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content; Blacklight.searchStorage = () => document.querySelector('meta[name=blacklight-search-storage]')?.content; - // this is the Rails.handleMethod with a couple adjustments, described inline: - // first, we're attaching this directly to the event handler, so we can check for meta-keys - Blacklight.handleSearchContextMethod = function(event) { - const link = this; + /** + * for a URL, iterate over searchParams and return a hidden form input for each pair + * @param {URL} searchUrl + * @returns {string} input element source + */ + const buildInputsFromSearchParams = function(searchUrl) { + let inputs = ''; + for (const [paramName, paramValue] of searchUrl.searchParams.entries()) { + inputs += ``; + } + return inputs + }; - // instead of using the normal href, we need to use the context href instead - let contextUrl = new URL(link.getAttribute('data-context-href'), window.location); - let target = link.getAttribute('target'); + /** + * build a submittable form to use in the onClick handler of a search result + * @param {string} formAction + * @param {?string} formTarget + * @param {string} formMethod + * @param {?string} redirectHref + */ + const buildSearchContextResultForm = function(formAction, formTarget, formMethod, redirectHref) { + let actionUrl = new URL(formAction, window.location); let csrfToken = Blacklight.csrfToken(); let csrfParam = Blacklight.csrfParam(); let form = document.createElement('form'); - form.action = contextUrl.pathname; - const formMethod = link.getAttribute('data-context-method') || 'post'; + form.action = actionUrl.pathname; form.method = (formMethod == 'get') ? formMethod : 'post'; - let formContent = ''; - for (const [paramName, paramValue] of contextUrl.searchParams.entries()) { - formContent += ``; - } + // check for meta keys.. if set, we should open in a new tab + const target = (event.metaKey || event.ctrlKey) ? '_blank' : formTarget; + if (target) form.target = target; + + form.style.display = 'none'; + + let formContent = buildInputsFromSearchParams(actionUrl); if (formMethod != 'get' && formMethod != 'post') formContent += ``; - if (Blacklight.searchStorage() == 'client') { - sessionStorage.setItem("blacklightSearch", new URLSearchParams(new URL(window.location).search)); - } else { - formContent += ``; + if (redirectHref) { + formContent += ``; if (csrfParam !== undefined && csrfToken !== undefined) { formContent += ``; } } - // check for meta keys.. if set, we should open in a new tab - if(event.metaKey || event.ctrlKey) { - target = '_blank'; - } // Must trigger submit by click on a button, else "submit" event handler won't work! // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit formContent += ''; - if (target) { form.setAttribute('target', target); } - - form.style.display = 'none'; form.innerHTML = formContent; + return form; + }; + + // this is the Rails.handleMethod with a couple adjustments, described inline: + // first, we're attaching this directly to the event handler, so we can check for meta-keys + Blacklight.handleSearchContextMethod = function(event) { + const link = this; + const clientSearchStorage = Blacklight.searchStorage() == 'client'; + if (clientSearchStorage) sessionStorage.setItem("blacklightSearch", new URLSearchParams(new URL(window.location).search)); + // instead of using the normal href, we need to use the context href instead + const contextHref = new URL(link.getAttribute('data-context-href'), window.location); + const linkTarget = link.getAttribute('target'); + const contextMethod = link.getAttribute('data-context-method') || 'post'; + const redirectHref = (clientSearchStorage) ? null : link.getAttribute('href'); + const form = buildSearchContextResultForm(contextHref, linkTarget, contextMethod, redirectHref); document.body.appendChild(form); form.querySelector('[type="submit"]').click(); event.preventDefault(); }; - Blacklight.doSearchContextBehavior(); - - const clientAppliedParams = function(storedSearch) { + /** + * if provided, iterate over searchParams and rebuild the back-to-catalog link to use them + * @param {?URLSearchParams} searchUrl + */ + Blacklight.clientAppliedParams = function(storedSearch) { if (storedSearch) { - let backToCatalogParams = new URLSearchParams(); - for (const [paramName, paramValue] of storedSearch.entries()) { - backToCatalogParams.append(paramName, paramValue); - } const appliedParams = document.querySelector('#appliedParams'); if (appliedParams) { const backToCatalogEle = appliedParams.querySelector('.back-to-catalog'); if (backToCatalogEle) { const backToCatalogUrl = new URL(backToCatalogEle.href); - backToCatalogEle.href = `${backToCatalogUrl.pathname}?${backToCatalogParams.toString()}`; + backToCatalogEle.href = `${backToCatalogUrl.pathname}?${storedSearch.toString()}`; backToCatalogEle.classList.remove('d-none'); } } } }; - const clientPrevNext = function(prevNextPath, storedSearch) { - const prevNextUrl = new URL(prevNextPath, window.location); - // prevNextUrl should have a counter param, but needs search params - let prevNextParams = new URLSearchParams(prevNextUrl.search); + + /** + * reassign a link element's href, or delete the element if it is not given + * remove the aria-disabled attribute if an href is assigned + * @param {Element} linkEle + * @param {?string} storedSearch + */ + const setHrefOrDelete = function(linkEle, url) { + if (!linkEle) return; + if (url) { + linkEle.href = url; + linkEle.removeAttribute('aria-disabled'); + } else { + linkEle.remove(); + } + }; + + /** + * reassign an element's HTML content if the element is given + * @param {?Element} linkEle + * @param {?string} storedSearch + */ + const setContent = function(ele, content) { + if (ele) ele.innerHTML = content; + }; + + /** + * fetch the JSON data at pageLinksPath for the given search + * use the fetched data to build the page-links labels and prev/next links + * @param {string} pageLinksPath + * @param {?URLSearchParams} storedSearch + */ + Blacklight.clientPageLinks = function(pageLinksPath, storedSearch) { + const pageLinksUrl = new URL(pageLinksPath, window.location); + // pageLinksUrl should already have a counter param, but needs search params + let prevNextParams = new URLSearchParams(pageLinksUrl.search); if (storedSearch) { for (const [paramName, paramValue] of storedSearch.entries()) { prevNextParams.append(paramName, paramValue); } } - const setHrefOrDelete = function(linkEle, url) { - if (!linkEle) return; - if (url) { - linkEle.href = url; - } else { - linkEle.remove(); - } - }; - const setContent = function(ele, content) { - if (!ele) return; - ele.innerHTML = content; - }; - fetch(`${prevNextUrl.pathname}?${prevNextParams.toString()}`) + fetch(`${pageLinksUrl.pathname}?${prevNextParams.toString()}`) .then((response) => response.json()) .then(function(responseData) { if (!responseData.prev && !responseData.next) return; @@ -434,18 +485,11 @@ setContent(pageLinks.querySelector('.pagination-total-delimited'), responseData.totalDelimited); setHrefOrDelete(pageLinks.querySelector("a[rel='prev']"), responseData.prev); setHrefOrDelete(pageLinks.querySelector("a[rel='next']"), responseData.next); - pageLinks.classList.remove('d-none'); }); }); }; - Blacklight.onLoad(function(){ - let clientPageLinks = document.querySelectorAll('.page-links[data-page-links-url]'); - if (clientPageLinks[0]) { - const storedSearch = new URLSearchParams(sessionStorage.getItem("blacklightSearch")); - clientAppliedParams(storedSearch); - clientPrevNext(clientPageLinks[0].getAttribute('data-page-links-url'), storedSearch); - } - }); + + Blacklight.doSearchContextBehavior(); })(); const index = { diff --git a/app/assets/javascripts/blacklight/blacklight.js.map b/app/assets/javascripts/blacklight/blacklight.js.map index 2f2c9c2aac..94c7055fa5 100644 --- a/app/assets/javascripts/blacklight/blacklight.js.map +++ b/app/assets/javascripts/blacklight/blacklight.js.map @@ -1 +1 @@ -{"version":3,"file":"blacklight.js","sources":["../../../javascript/blacklight/core.js","../../../javascript/blacklight/checkbox_submit.js","../../../javascript/blacklight/bookmark_toggle.js","../../../javascript/blacklight/button_focus.js","../../../javascript/blacklight/modal.js","../../../javascript/blacklight/search_context.js","../../../javascript/blacklight/index.js"],"sourcesContent":["const Blacklight = function() {\n const buffer = new Array;\n return {\n onLoad: function(func) {\n buffer.push(func);\n },\n\n activate: function() {\n for(let i = 0; i < buffer.length; i++) {\n buffer[i].call();\n }\n },\n\n listeners: function () {\n const listeners = [];\n if (typeof Turbo !== 'undefined') {\n listeners.push('turbo:load', 'turbo:frame-load');\n } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {\n // Turbolinks 5\n if (Turbolinks.BrowserAdapter) {\n listeners.push('turbolinks:load');\n } else {\n // Turbolinks < 5\n listeners.push('page:load', 'DOMContentLoaded');\n }\n } else {\n listeners.push('DOMContentLoaded');\n }\n\n return listeners;\n }\n };\n}();\n\n// turbolinks triggers page:load events on page transition\n// If app isn't using turbolinks, this event will never be triggered, no prob.\nBlacklight.listeners().forEach(function(listener) {\n document.addEventListener(listener, function() {\n Blacklight.activate()\n })\n})\n\nBlacklight.onLoad(function () {\n const elem = document.querySelector('.no-js');\n\n // The \"no-js\" class may already have been removed because this function is\n // run on every turbo:load event, in that case, it won't find an element.\n if (!elem) return;\n\n elem.classList.remove('no-js')\n elem.classList.add('js')\n})\n\n\nexport default Blacklight\n","/* Converts a \"toggle\" form, with single submit button to add/remove\n something, like used for Bookmarks, into an AJAXy checkbox instead.\n Apply to a form. Does require certain assumption about the form:\n 1) The same form 'action' href must be used for both ADD and REMOVE\n actions, with the different being the hidden input name=\"_method\"\n being set to \"put\" or \"delete\" -- that's the Rails method to pretend\n to be doing a certain HTTP verb. So same URL, PUT to add, DELETE\n to remove. This plugin assumes that.\n Plus, the form this is applied to should provide a data-doc-id\n attribute (HTML5-style doc-*) that contains the id/primary key\n of the object in question -- used by plugin for a unique value for\n DOM id's.\n Uses HTML for a checkbox compatible with Bootstrap 4.\n new CheckboxSubmit(document.querySelector('form.something')).render()\n*/\nexport default class CheckboxSubmit {\n constructor(form) {\n this.form = form\n }\n\n async clicked(evt) {\n this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress')\n this.labelTarget.setAttribute('disabled', 'disabled');\n this.checkboxTarget.setAttribute('disabled', 'disabled');\n const response = await fetch(this.formTarget.getAttribute('action'), {\n body: new FormData(this.formTarget),\n method: this.formTarget.getAttribute('method').toUpperCase(),\n headers: {\n 'Accept': 'application/json',\n 'X-Requested-With': 'XMLHttpRequest',\n 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content\n }\n })\n this.labelTarget.removeAttribute('disabled')\n this.checkboxTarget.removeAttribute('disabled')\n if (response.ok) {\n const json = await response.json()\n this.updateStateFor(!this.checked)\n document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count\n } else {\n alert('Error')\n }\n }\n\n get checked() {\n return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)\n }\n\n get formTarget() {\n return this.form\n }\n\n get labelTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"label\"]')\n }\n\n get checkboxTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"checkbox\"]')\n }\n\n get spanTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"span\"]')\n }\n\n updateStateFor(state) {\n this.checkboxTarget.checked = state\n\n if (state) {\n this.labelTarget.classList.add('checked')\n //Set the Rails hidden field that fakes an HTTP verb\n //properly for current state action.\n this.formTarget.querySelector('input[name=_method]').value = 'delete'\n this.spanTarget.innerHTML = this.form.getAttribute('data-present')\n } else {\n this.labelTarget.classList.remove('checked')\n this.formTarget.querySelector('input[name=_method]').value = 'put'\n this.spanTarget.innerHTML = this.form.getAttribute('data-absent')\n }\n }\n}\n","import Blacklight from './core'\nimport CheckboxSubmit from './checkbox_submit'\n\nconst BookmarkToggle = (() => {\n // change form submit toggle to checkbox\n Blacklight.doBookmarkToggleBehavior = function() {\n document.addEventListener('click', (e) => {\n if (e.target.matches('[data-checkboxsubmit-target=\"checkbox\"]')) {\n const form = e.target.closest('form')\n if (form) new CheckboxSubmit(form).clicked(e);\n }\n });\n };\n Blacklight.doBookmarkToggleBehavior.selector = 'form.bookmark-toggle';\n\n Blacklight.doBookmarkToggleBehavior();\n})()\n\nexport default BookmarkToggle\n","const ButtonFocus = (() => {\n document.addEventListener('click', (e) => {\n // Button clicks should change focus. As of 10/3/19, Firefox for Mac and\n // Safari both do not set focus to a button on button click.\n // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information\n if (e.target.matches('[data-toggle=\"collapse\"]') || e.target.matches('[data-bs-toggle=\"collapse\"]')) {\n e.target.focus()\n }\n })\n})()\n\nexport default ButtonFocus\n","/*\n The blacklight modal plugin can display some interactions inside a Bootstrap\n modal window, including some multi-page interactions.\n\n It supports unobtrusive Javascript, where a link or form that would have caused\n a new page load is changed to display it's results inside a modal dialog,\n by this plugin. The plugin assumes there is a Bootstrap modal div\n on the page with id #blacklight-modal to use as the modal -- the standard Blacklight\n layout provides this.\n\n To make a link or form have their results display inside a modal, add\n `data-blacklight-modal=\"trigger\"` to the link or form. (Note, form itself not submit input)\n With Rails link_to helper, you'd do that like:\n\n link_to something, link, data: { blacklight_modal: \"trigger\" }\n\n The results of the link href or form submit will be displayed inside\n a modal -- they should include the proper HTML markup for a bootstrap modal's\n contents. Also, you ordinarily won't want the Rails template with wrapping\n navigational elements to be used. The Rails controller could suppress\n the layout when a JS AJAX request is detected, OR the response\n can include a `
` -- only the contents\n of the container will be placed inside the modal, the rest of the\n page will be ignored.\n\n Link or forms inside the modal will ordinarily cause page loads\n when they are triggered. However, if you'd like their results\n to stay within the modal, just add `data-blacklight-modal=\"preserve\"`\n to the link or form.\n\n Here's an example of what might be returned, demonstrating most of the devices available:\n\n
\n
\n \n

Request Placed

\n
\n\n
\n

Some message

\n <%= link_to \"This result will still be within modal\", some_link, data: { blacklight_modal: \"preserve\" } %>\n
\n\n\n
\n \n
\n
\n\n\n One additional feature. If the content returned from the AJAX form submission\n can be a turbo-stream that defines some HTML fragementsand where on the page to put them:\n https://turbo.hotwired.dev/handbook/streams\n*/\nimport Blacklight from './core'\nimport ModalForm from './modalForm'\n\nconst Modal = (() => {\n // We keep all our data in Blacklight.modal object.\n // Create lazily if someone else created first.\n if (Blacklight.modal === undefined) {\n Blacklight.modal = {};\n }\n\n const modal = Blacklight.modal\n\n // a Bootstrap modal div that should be already on the page hidden\n modal.modalSelector = '#blacklight-modal';\n\n // Trigger selectors identify forms or hyperlinks that should open\n // inside a modal dialog.\n modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';\n\n // preserve selectors identify forms or hyperlinks that, if activated already\n // inside a modal dialog, should have destinations remain inside the modal -- but\n // won't trigger a modal if not already in one.\n //\n // No need to repeat selectors from trigger selectors, those will already\n // be preserved. MUST be manually prefixed with the modal selector,\n // so they only apply to things inside a modal.\n modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';\n\n modal.containerSelector = '[data-blacklight-modal~=container]';\n\n // Called on fatal failure of ajax load, function returns content\n // to show to user in modal. Right now called only for extreme\n // network errors.\n modal.onFailure = function (jqXHR, textStatus, errorThrown) {\n console.error('Server error:', this.url, jqXHR.status, errorThrown);\n\n const contents = `
\n
There was a problem with your request.
\n \n
\n
\n

Expected a successful response from the server, but got an error

\n
${this.type} ${this.url}\\n${jqXHR.status}: ${errorThrown}
\n
`\n\n document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents\n\n modal.show();\n }\n\n // Add the passed in contents to the modal and display it.\nmodal.receiveAjax = function (contents) {\n const domparser = new DOMParser();\n const dom = domparser.parseFromString(contents, \"text/html\")\n const elements = dom.querySelectorAll(`${modal.containerSelector} > *`)\n document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements)\n\n modal.show();\n };\n\n\n modal.modalAjaxLinkClick = function(e) {\n e.preventDefault();\n const href = e.target.getAttribute('href')\n fetch(href)\n .then(response => {\n if (!response.ok) {\n throw new TypeError(\"Request failed\");\n }\n return response.text();\n })\n .then(data => modal.receiveAjax(data))\n .catch(error => modal.onFailure(error))\n };\n\n modal.setupModal = function() {\n // Register both trigger and preserve selectors in ONE event handler, combining\n // into one selector with a comma, so if something matches BOTH selectors, it\n // still only gets the event handler called once.\n document.addEventListener('click', (e) => {\n if (e.target.matches(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))\n modal.modalAjaxLinkClick(e)\n else if (e.target.matches('[data-bl-dismiss=\"modal\"]'))\n modal.hide()\n })\n };\n\n modal.hide = function (el) {\n const dom = document.querySelector(Blacklight.modal.modalSelector)\n\n if (!dom.open) return\n dom.close()\n }\n\n modal.show = function(el) {\n const dom = document.querySelector(Blacklight.modal.modalSelector)\n\n if (dom.open) return\n dom.showModal()\n }\n\n modal.setupModal()\n})()\n\nexport default Modal\n","import Blacklight from './core'\n\nconst SearchContext = (() => {\n Blacklight.doSearchContextBehavior = function() {\n document.addEventListener('click', (e) => {\n if (e.target.matches('[data-context-href]')) {\n Blacklight.handleSearchContextMethod.call(e.target, e)\n }\n })\n };\n\n Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content\n Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content\n Blacklight.searchStorage = () => document.querySelector('meta[name=blacklight-search-storage]')?.content\n\n // this is the Rails.handleMethod with a couple adjustments, described inline:\n // first, we're attaching this directly to the event handler, so we can check for meta-keys\n Blacklight.handleSearchContextMethod = function(event) {\n const link = this\n\n // instead of using the normal href, we need to use the context href instead\n let contextUrl = new URL(link.getAttribute('data-context-href'), window.location)\n let target = link.getAttribute('target')\n let csrfToken = Blacklight.csrfToken()\n let csrfParam = Blacklight.csrfParam()\n let form = document.createElement('form')\n form.action = contextUrl.pathname;\n const formMethod = link.getAttribute('data-context-method') || 'post'\n form.method = (formMethod == 'get') ? formMethod : 'post'\n\n let formContent = ''\n for (const [paramName, paramValue] of contextUrl.searchParams.entries()) {\n formContent += ``\n }\n if (formMethod != 'get' && formMethod != 'post') formContent += ``\n\n if (Blacklight.searchStorage() == 'client') {\n sessionStorage.setItem(\"blacklightSearch\", new URLSearchParams(new URL(window.location).search))\n } else {\n formContent += ``\n if (csrfParam !== undefined && csrfToken !== undefined) {\n formContent += ``\n }\n }\n // check for meta keys.. if set, we should open in a new tab\n if(event.metaKey || event.ctrlKey) {\n target = '_blank';\n }\n\n // Must trigger submit by click on a button, else \"submit\" event handler won't work!\n // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit\n formContent += ''\n\n if (target) { form.setAttribute('target', target); }\n\n form.style.display = 'none'\n form.innerHTML = formContent\n document.body.appendChild(form)\n form.querySelector('[type=\"submit\"]').click()\n\n event.preventDefault()\n };\n\n Blacklight.doSearchContextBehavior();\n\n const clientAppliedParams = function(storedSearch) {\n if (storedSearch) {\n let backToCatalogParams = new URLSearchParams();\n for (const [paramName, paramValue] of storedSearch.entries()) {\n backToCatalogParams.append(paramName, paramValue);\n }\n const appliedParams = document.querySelector('#appliedParams')\n if (appliedParams) {\n const backToCatalogEle = appliedParams.querySelector('.back-to-catalog')\n if (backToCatalogEle) {\n const backToCatalogUrl = new URL(backToCatalogEle.href)\n backToCatalogEle.href = `${backToCatalogUrl.pathname}?${backToCatalogParams.toString()}`\n backToCatalogEle.classList.remove('d-none')\n }\n }\n }\n }\n const clientPrevNext = function(prevNextPath, storedSearch) {\n const prevNextUrl = new URL(prevNextPath, window.location)\n // prevNextUrl should have a counter param, but needs search params\n let prevNextParams = new URLSearchParams(prevNextUrl.search);\n if (storedSearch) {\n for (const [paramName, paramValue] of storedSearch.entries()) {\n prevNextParams.append(paramName, paramValue);\n }\n }\n const setHrefOrDelete = function(linkEle, url) {\n if (!linkEle) return;\n if (url) {\n linkEle.href = url\n } else {\n linkEle.remove()\n }\n }\n const setContent = function(ele, content) {\n if (!ele) return;\n ele.innerHTML = content;\n }\n fetch(`${prevNextUrl.pathname}?${prevNextParams.toString()}`)\n .then((response) => response.json())\n .then(function(responseData) {\n if (!responseData.prev && !responseData.next) return;\n document.querySelectorAll('.page-links').forEach(function(pageLinks) {\n setContent(pageLinks.querySelector('.pagination-counter-raw'), responseData.counterRaw)\n setContent(pageLinks.querySelector('.pagination-counter-delimited'), responseData.counterDelimited)\n setContent(pageLinks.querySelector('.pagination-total-raw'), responseData.totalRaw)\n setContent(pageLinks.querySelector('.pagination-total-delimited'), responseData.totalDelimited)\n setHrefOrDelete(pageLinks.querySelector(\"a[rel='prev']\"), responseData.prev)\n setHrefOrDelete(pageLinks.querySelector(\"a[rel='next']\"), responseData.next)\n pageLinks.classList.remove('d-none')\n })\n })\n }\n Blacklight.onLoad(function(){\n let clientPageLinks = document.querySelectorAll('.page-links[data-page-links-url]')\n if (clientPageLinks[0]) {\n const storedSearch = new URLSearchParams(sessionStorage.getItem(\"blacklightSearch\"))\n clientAppliedParams(storedSearch)\n clientPrevNext(clientPageLinks[0].getAttribute('data-page-links-url'), storedSearch)\n }\n })\n})()\n\nexport default SearchContext\n","import BookmarkToggle from './bookmark_toggle'\nimport ButtonFocus from './button_focus'\nimport Modal from './modal'\nimport SearchContext from './search_context'\nimport Core from './core'\n\nexport default {\n BookmarkToggle,\n ButtonFocus,\n Modal,\n SearchContext,\n onLoad: Core.onLoad\n}\n"],"names":["Core"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"blacklight.js","sources":["../../../javascript/blacklight/core.js","../../../javascript/blacklight/checkbox_submit.js","../../../javascript/blacklight/bookmark_toggle.js","../../../javascript/blacklight/button_focus.js","../../../javascript/blacklight/modal.js","../../../javascript/blacklight/search_context.js","../../../javascript/blacklight/index.js"],"sourcesContent":["const Blacklight = function() {\n const buffer = new Array;\n return {\n onLoad: function(func) {\n buffer.push(func);\n },\n\n activate: function() {\n for(let i = 0; i < buffer.length; i++) {\n buffer[i].call();\n }\n },\n\n listeners: function () {\n const listeners = [];\n if (typeof Turbo !== 'undefined') {\n listeners.push('turbo:load', 'turbo:frame-load');\n } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {\n // Turbolinks 5\n if (Turbolinks.BrowserAdapter) {\n listeners.push('turbolinks:load');\n } else {\n // Turbolinks < 5\n listeners.push('page:load', 'DOMContentLoaded');\n }\n } else {\n listeners.push('DOMContentLoaded');\n }\n\n return listeners;\n }\n };\n}();\n\n// turbolinks triggers page:load events on page transition\n// If app isn't using turbolinks, this event will never be triggered, no prob.\nBlacklight.listeners().forEach(function(listener) {\n document.addEventListener(listener, function() {\n Blacklight.activate()\n })\n})\n\nBlacklight.onLoad(function () {\n const elem = document.querySelector('.no-js');\n\n // The \"no-js\" class may already have been removed because this function is\n // run on every turbo:load event, in that case, it won't find an element.\n if (!elem) return;\n\n elem.classList.remove('no-js')\n elem.classList.add('js')\n})\n\n\nexport default Blacklight\n","/* Converts a \"toggle\" form, with single submit button to add/remove\n something, like used for Bookmarks, into an AJAXy checkbox instead.\n Apply to a form. Does require certain assumption about the form:\n 1) The same form 'action' href must be used for both ADD and REMOVE\n actions, with the different being the hidden input name=\"_method\"\n being set to \"put\" or \"delete\" -- that's the Rails method to pretend\n to be doing a certain HTTP verb. So same URL, PUT to add, DELETE\n to remove. This plugin assumes that.\n Plus, the form this is applied to should provide a data-doc-id\n attribute (HTML5-style doc-*) that contains the id/primary key\n of the object in question -- used by plugin for a unique value for\n DOM id's.\n Uses HTML for a checkbox compatible with Bootstrap 4.\n new CheckboxSubmit(document.querySelector('form.something')).render()\n*/\nexport default class CheckboxSubmit {\n constructor(form) {\n this.form = form\n }\n\n async clicked(evt) {\n this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress')\n this.labelTarget.setAttribute('disabled', 'disabled');\n this.checkboxTarget.setAttribute('disabled', 'disabled');\n const response = await fetch(this.formTarget.getAttribute('action'), {\n body: new FormData(this.formTarget),\n method: this.formTarget.getAttribute('method').toUpperCase(),\n headers: {\n 'Accept': 'application/json',\n 'X-Requested-With': 'XMLHttpRequest',\n 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content\n }\n })\n this.labelTarget.removeAttribute('disabled')\n this.checkboxTarget.removeAttribute('disabled')\n if (response.ok) {\n const json = await response.json()\n this.updateStateFor(!this.checked)\n document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count\n } else {\n alert('Error')\n }\n }\n\n get checked() {\n return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)\n }\n\n get formTarget() {\n return this.form\n }\n\n get labelTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"label\"]')\n }\n\n get checkboxTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"checkbox\"]')\n }\n\n get spanTarget() {\n return this.form.querySelector('[data-checkboxsubmit-target=\"span\"]')\n }\n\n updateStateFor(state) {\n this.checkboxTarget.checked = state\n\n if (state) {\n this.labelTarget.classList.add('checked')\n //Set the Rails hidden field that fakes an HTTP verb\n //properly for current state action.\n this.formTarget.querySelector('input[name=_method]').value = 'delete'\n this.spanTarget.innerHTML = this.form.getAttribute('data-present')\n } else {\n this.labelTarget.classList.remove('checked')\n this.formTarget.querySelector('input[name=_method]').value = 'put'\n this.spanTarget.innerHTML = this.form.getAttribute('data-absent')\n }\n }\n}\n","import Blacklight from './core'\nimport CheckboxSubmit from './checkbox_submit'\n\nconst BookmarkToggle = (() => {\n // change form submit toggle to checkbox\n Blacklight.doBookmarkToggleBehavior = function() {\n document.addEventListener('click', (e) => {\n if (e.target.matches('[data-checkboxsubmit-target=\"checkbox\"]')) {\n const form = e.target.closest('form')\n if (form) new CheckboxSubmit(form).clicked(e);\n }\n });\n };\n Blacklight.doBookmarkToggleBehavior.selector = 'form.bookmark-toggle';\n\n Blacklight.doBookmarkToggleBehavior();\n})()\n\nexport default BookmarkToggle\n","const ButtonFocus = (() => {\n document.addEventListener('click', (e) => {\n // Button clicks should change focus. As of 10/3/19, Firefox for Mac and\n // Safari both do not set focus to a button on button click.\n // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information\n if (e.target.matches('[data-toggle=\"collapse\"]') || e.target.matches('[data-bs-toggle=\"collapse\"]')) {\n e.target.focus()\n }\n })\n})()\n\nexport default ButtonFocus\n","/*\n The blacklight modal plugin can display some interactions inside a Bootstrap\n modal window, including some multi-page interactions.\n\n It supports unobtrusive Javascript, where a link or form that would have caused\n a new page load is changed to display it's results inside a modal dialog,\n by this plugin. The plugin assumes there is a Bootstrap modal div\n on the page with id #blacklight-modal to use as the modal -- the standard Blacklight\n layout provides this.\n\n To make a link or form have their results display inside a modal, add\n `data-blacklight-modal=\"trigger\"` to the link or form. (Note, form itself not submit input)\n With Rails link_to helper, you'd do that like:\n\n link_to something, link, data: { blacklight_modal: \"trigger\" }\n\n The results of the link href or form submit will be displayed inside\n a modal -- they should include the proper HTML markup for a bootstrap modal's\n contents. Also, you ordinarily won't want the Rails template with wrapping\n navigational elements to be used. The Rails controller could suppress\n the layout when a JS AJAX request is detected, OR the response\n can include a `
` -- only the contents\n of the container will be placed inside the modal, the rest of the\n page will be ignored.\n\n Link or forms inside the modal will ordinarily cause page loads\n when they are triggered. However, if you'd like their results\n to stay within the modal, just add `data-blacklight-modal=\"preserve\"`\n to the link or form.\n\n Here's an example of what might be returned, demonstrating most of the devices available:\n\n
\n
\n \n

Request Placed

\n
\n\n
\n

Some message

\n <%= link_to \"This result will still be within modal\", some_link, data: { blacklight_modal: \"preserve\" } %>\n
\n\n\n
\n \n
\n
\n\n\n One additional feature. If the content returned from the AJAX form submission\n can be a turbo-stream that defines some HTML fragementsand where on the page to put them:\n https://turbo.hotwired.dev/handbook/streams\n*/\nimport Blacklight from './core'\nimport ModalForm from './modalForm'\n\nconst Modal = (() => {\n // We keep all our data in Blacklight.modal object.\n // Create lazily if someone else created first.\n if (Blacklight.modal === undefined) {\n Blacklight.modal = {};\n }\n\n const modal = Blacklight.modal\n\n // a Bootstrap modal div that should be already on the page hidden\n modal.modalSelector = '#blacklight-modal';\n\n // Trigger selectors identify forms or hyperlinks that should open\n // inside a modal dialog.\n modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';\n\n // preserve selectors identify forms or hyperlinks that, if activated already\n // inside a modal dialog, should have destinations remain inside the modal -- but\n // won't trigger a modal if not already in one.\n //\n // No need to repeat selectors from trigger selectors, those will already\n // be preserved. MUST be manually prefixed with the modal selector,\n // so they only apply to things inside a modal.\n modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';\n\n modal.containerSelector = '[data-blacklight-modal~=container]';\n\n // Called on fatal failure of ajax load, function returns content\n // to show to user in modal. Right now called only for extreme\n // network errors.\n modal.onFailure = function (jqXHR, textStatus, errorThrown) {\n console.error('Server error:', this.url, jqXHR.status, errorThrown);\n\n const contents = `
\n
There was a problem with your request.
\n \n
\n
\n

Expected a successful response from the server, but got an error

\n
${this.type} ${this.url}\\n${jqXHR.status}: ${errorThrown}
\n
`\n\n document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents\n\n modal.show();\n }\n\n // Add the passed in contents to the modal and display it.\nmodal.receiveAjax = function (contents) {\n const domparser = new DOMParser();\n const dom = domparser.parseFromString(contents, \"text/html\")\n const elements = dom.querySelectorAll(`${modal.containerSelector} > *`)\n document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements)\n\n modal.show();\n };\n\n\n modal.modalAjaxLinkClick = function(e) {\n e.preventDefault();\n const href = e.target.getAttribute('href')\n fetch(href)\n .then(response => {\n if (!response.ok) {\n throw new TypeError(\"Request failed\");\n }\n return response.text();\n })\n .then(data => modal.receiveAjax(data))\n .catch(error => modal.onFailure(error))\n };\n\n modal.setupModal = function() {\n // Register both trigger and preserve selectors in ONE event handler, combining\n // into one selector with a comma, so if something matches BOTH selectors, it\n // still only gets the event handler called once.\n document.addEventListener('click', (e) => {\n if (e.target.matches(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))\n modal.modalAjaxLinkClick(e)\n else if (e.target.matches('[data-bl-dismiss=\"modal\"]'))\n modal.hide()\n })\n };\n\n modal.hide = function (el) {\n const dom = document.querySelector(Blacklight.modal.modalSelector)\n\n if (!dom.open) return\n dom.close()\n }\n\n modal.show = function(el) {\n const dom = document.querySelector(Blacklight.modal.modalSelector)\n\n if (dom.open) return\n dom.showModal()\n }\n\n modal.setupModal()\n})()\n\nexport default Modal\n","import Blacklight from './core'\n\nconst SearchContext = (() => {\n Blacklight.doSearchContextBehavior = function() {\n // intercept clicks on search results to use search context behavior\n document.addEventListener('click', (e) => {\n if (e.target.matches('[data-context-href]')) {\n Blacklight.handleSearchContextMethod.call(e.target, e)\n }\n })\n // page-links dynamic content depends on a loaded document\n Blacklight.onLoad(() => {\n // if page-links container has a data URL, do client-side search context behaviors\n let clientPageLinks = document.querySelectorAll('.page-links[data-page-links-url]')\n if (clientPageLinks[0]) {\n const storedSearch = new URLSearchParams(sessionStorage.getItem(\"blacklightSearch\"))\n Blacklight.clientAppliedParams(storedSearch)\n Blacklight.clientPageLinks(clientPageLinks[0].getAttribute('data-page-links-url'), storedSearch)\n }\n })\n };\n\n Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content\n Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content\n Blacklight.searchStorage = () => document.querySelector('meta[name=blacklight-search-storage]')?.content\n\n /**\n * for a URL, iterate over searchParams and return a hidden form input for each pair\n * @param {URL} searchUrl\n * @returns {string} input element source\n */\n const buildInputsFromSearchParams = function(searchUrl) {\n let inputs = ''\n for (const [paramName, paramValue] of searchUrl.searchParams.entries()) {\n inputs += ``\n }\n return inputs\n }\n\n /**\n * build a submittable form to use in the onClick handler of a search result\n * @param {string} formAction\n * @param {?string} formTarget\n * @param {string} formMethod\n * @param {?string} redirectHref\n */\n const buildSearchContextResultForm = function(formAction, formTarget, formMethod, redirectHref) {\n let actionUrl = new URL(formAction, window.location)\n let csrfToken = Blacklight.csrfToken()\n let csrfParam = Blacklight.csrfParam()\n let form = document.createElement('form')\n form.action = actionUrl.pathname\n form.method = (formMethod == 'get') ? formMethod : 'post'\n\n // check for meta keys.. if set, we should open in a new tab\n const target = (event.metaKey || event.ctrlKey) ? '_blank' : formTarget\n if (target) form.target = target\n\n form.style.display = 'none'\n\n let formContent = buildInputsFromSearchParams(actionUrl)\n if (formMethod != 'get' && formMethod != 'post') formContent += ``\n\n if (redirectHref) {\n formContent += ``\n if (csrfParam !== undefined && csrfToken !== undefined) {\n formContent += ``\n }\n }\n\n // Must trigger submit by click on a button, else \"submit\" event handler won't work!\n // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit\n formContent += ''\n\n form.innerHTML = formContent\n return form;\n }\n\n // this is the Rails.handleMethod with a couple adjustments, described inline:\n // first, we're attaching this directly to the event handler, so we can check for meta-keys\n Blacklight.handleSearchContextMethod = function(event) {\n const link = this\n const clientSearchStorage = Blacklight.searchStorage() == 'client'\n if (clientSearchStorage) sessionStorage.setItem(\"blacklightSearch\", new URLSearchParams(new URL(window.location).search))\n // instead of using the normal href, we need to use the context href instead\n const contextHref = new URL(link.getAttribute('data-context-href'), window.location)\n const linkTarget = link.getAttribute('target')\n const contextMethod = link.getAttribute('data-context-method') || 'post'\n const redirectHref = (clientSearchStorage) ? null : link.getAttribute('href')\n const form = buildSearchContextResultForm(contextHref, linkTarget, contextMethod, redirectHref)\n document.body.appendChild(form)\n form.querySelector('[type=\"submit\"]').click()\n\n event.preventDefault()\n };\n\n /**\n * if provided, iterate over searchParams and rebuild the back-to-catalog link to use them\n * @param {?URLSearchParams} searchUrl\n */\n Blacklight.clientAppliedParams = function(storedSearch) {\n if (storedSearch) {\n const appliedParams = document.querySelector('#appliedParams')\n if (appliedParams) {\n const backToCatalogEle = appliedParams.querySelector('.back-to-catalog')\n if (backToCatalogEle) {\n const backToCatalogUrl = new URL(backToCatalogEle.href)\n backToCatalogEle.href = `${backToCatalogUrl.pathname}?${storedSearch.toString()}`\n backToCatalogEle.classList.remove('d-none')\n }\n }\n }\n }\n\n /**\n * reassign a link element's href, or delete the element if it is not given\n * remove the aria-disabled attribute if an href is assigned\n * @param {Element} linkEle\n * @param {?string} storedSearch\n */\n const setHrefOrDelete = function(linkEle, url) {\n if (!linkEle) return;\n if (url) {\n linkEle.href = url\n linkEle.removeAttribute('aria-disabled')\n } else {\n linkEle.remove()\n }\n }\n\n /**\n * reassign an element's HTML content if the element is given\n * @param {?Element} linkEle\n * @param {?string} storedSearch\n */\n const setContent = function(ele, content) {\n if (ele) ele.innerHTML = content;\n }\n\n /**\n * fetch the JSON data at pageLinksPath for the given search\n * use the fetched data to build the page-links labels and prev/next links\n * @param {string} pageLinksPath\n * @param {?URLSearchParams} storedSearch\n */\n Blacklight.clientPageLinks = function(pageLinksPath, storedSearch) {\n const pageLinksUrl = new URL(pageLinksPath, window.location)\n // pageLinksUrl should already have a counter param, but needs search params\n let prevNextParams = new URLSearchParams(pageLinksUrl.search);\n if (storedSearch) {\n for (const [paramName, paramValue] of storedSearch.entries()) {\n prevNextParams.append(paramName, paramValue);\n }\n }\n fetch(`${pageLinksUrl.pathname}?${prevNextParams.toString()}`)\n .then((response) => response.json())\n .then(function(responseData) {\n if (!responseData.prev && !responseData.next) return;\n document.querySelectorAll('.page-links').forEach(function(pageLinks) {\n setContent(pageLinks.querySelector('.pagination-counter-raw'), responseData.counterRaw)\n setContent(pageLinks.querySelector('.pagination-counter-delimited'), responseData.counterDelimited)\n setContent(pageLinks.querySelector('.pagination-total-raw'), responseData.totalRaw)\n setContent(pageLinks.querySelector('.pagination-total-delimited'), responseData.totalDelimited)\n setHrefOrDelete(pageLinks.querySelector(\"a[rel='prev']\"), responseData.prev)\n setHrefOrDelete(pageLinks.querySelector(\"a[rel='next']\"), responseData.next)\n })\n })\n }\n\n Blacklight.doSearchContextBehavior();\n})()\n\nexport default SearchContext\n","import BookmarkToggle from './bookmark_toggle'\nimport ButtonFocus from './button_focus'\nimport Modal from './modal'\nimport SearchContext from './search_context'\nimport Core from './core'\n\nexport default {\n BookmarkToggle,\n ButtonFocus,\n Modal,\n SearchContext,\n onLoad: Core.onLoad\n}\n"],"names":["Core"],"mappings":"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"} \ No newline at end of file diff --git a/app/javascript/blacklight/search_context.js b/app/javascript/blacklight/search_context.js index 9d903fdcdd..7827bc7273 100644 --- a/app/javascript/blacklight/search_context.js +++ b/app/javascript/blacklight/search_context.js @@ -2,106 +2,157 @@ import Blacklight from './core' const SearchContext = (() => { Blacklight.doSearchContextBehavior = function() { + // intercept clicks on search results to use search context behavior document.addEventListener('click', (e) => { if (e.target.matches('[data-context-href]')) { Blacklight.handleSearchContextMethod.call(e.target, e) } }) + // page-links dynamic content depends on a loaded document + Blacklight.onLoad(() => { + // if page-links container has a data URL, do client-side search context behaviors + let clientPageLinks = document.querySelectorAll('.page-links[data-page-links-url]') + if (clientPageLinks[0]) { + const storedSearch = new URLSearchParams(sessionStorage.getItem("blacklightSearch")) + Blacklight.clientAppliedParams(storedSearch) + Blacklight.clientPageLinks(clientPageLinks[0].getAttribute('data-page-links-url'), storedSearch) + } + }) }; Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content Blacklight.searchStorage = () => document.querySelector('meta[name=blacklight-search-storage]')?.content - // this is the Rails.handleMethod with a couple adjustments, described inline: - // first, we're attaching this directly to the event handler, so we can check for meta-keys - Blacklight.handleSearchContextMethod = function(event) { - const link = this + /** + * for a URL, iterate over searchParams and return a hidden form input for each pair + * @param {URL} searchUrl + * @returns {string} input element source + */ + const buildInputsFromSearchParams = function(searchUrl) { + let inputs = '' + for (const [paramName, paramValue] of searchUrl.searchParams.entries()) { + inputs += `` + } + return inputs + } - // instead of using the normal href, we need to use the context href instead - let contextUrl = new URL(link.getAttribute('data-context-href'), window.location) - let target = link.getAttribute('target') + /** + * build a submittable form to use in the onClick handler of a search result + * @param {string} formAction + * @param {?string} formTarget + * @param {string} formMethod + * @param {?string} redirectHref + */ + const buildSearchContextResultForm = function(formAction, formTarget, formMethod, redirectHref) { + let actionUrl = new URL(formAction, window.location) let csrfToken = Blacklight.csrfToken() let csrfParam = Blacklight.csrfParam() let form = document.createElement('form') - form.action = contextUrl.pathname; - const formMethod = link.getAttribute('data-context-method') || 'post' + form.action = actionUrl.pathname form.method = (formMethod == 'get') ? formMethod : 'post' - let formContent = '' - for (const [paramName, paramValue] of contextUrl.searchParams.entries()) { - formContent += `` - } + // check for meta keys.. if set, we should open in a new tab + const target = (event.metaKey || event.ctrlKey) ? '_blank' : formTarget + if (target) form.target = target + + form.style.display = 'none' + + let formContent = buildInputsFromSearchParams(actionUrl) if (formMethod != 'get' && formMethod != 'post') formContent += `` - if (Blacklight.searchStorage() == 'client') { - sessionStorage.setItem("blacklightSearch", new URLSearchParams(new URL(window.location).search)) - } else { - formContent += `` + if (redirectHref) { + formContent += `` if (csrfParam !== undefined && csrfToken !== undefined) { formContent += `` } } - // check for meta keys.. if set, we should open in a new tab - if(event.metaKey || event.ctrlKey) { - target = '_blank'; - } // Must trigger submit by click on a button, else "submit" event handler won't work! // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit formContent += '' - if (target) { form.setAttribute('target', target); } - - form.style.display = 'none' form.innerHTML = formContent + return form; + } + + // this is the Rails.handleMethod with a couple adjustments, described inline: + // first, we're attaching this directly to the event handler, so we can check for meta-keys + Blacklight.handleSearchContextMethod = function(event) { + const link = this + const clientSearchStorage = Blacklight.searchStorage() == 'client' + if (clientSearchStorage) sessionStorage.setItem("blacklightSearch", new URLSearchParams(new URL(window.location).search)) + // instead of using the normal href, we need to use the context href instead + const contextHref = new URL(link.getAttribute('data-context-href'), window.location) + const linkTarget = link.getAttribute('target') + const contextMethod = link.getAttribute('data-context-method') || 'post' + const redirectHref = (clientSearchStorage) ? null : link.getAttribute('href') + const form = buildSearchContextResultForm(contextHref, linkTarget, contextMethod, redirectHref) document.body.appendChild(form) form.querySelector('[type="submit"]').click() event.preventDefault() }; - Blacklight.doSearchContextBehavior(); - - const clientAppliedParams = function(storedSearch) { + /** + * if provided, iterate over searchParams and rebuild the back-to-catalog link to use them + * @param {?URLSearchParams} searchUrl + */ + Blacklight.clientAppliedParams = function(storedSearch) { if (storedSearch) { - let backToCatalogParams = new URLSearchParams(); - for (const [paramName, paramValue] of storedSearch.entries()) { - backToCatalogParams.append(paramName, paramValue); - } const appliedParams = document.querySelector('#appliedParams') if (appliedParams) { const backToCatalogEle = appliedParams.querySelector('.back-to-catalog') if (backToCatalogEle) { const backToCatalogUrl = new URL(backToCatalogEle.href) - backToCatalogEle.href = `${backToCatalogUrl.pathname}?${backToCatalogParams.toString()}` + backToCatalogEle.href = `${backToCatalogUrl.pathname}?${storedSearch.toString()}` backToCatalogEle.classList.remove('d-none') } } } } - const clientPrevNext = function(prevNextPath, storedSearch) { - const prevNextUrl = new URL(prevNextPath, window.location) - // prevNextUrl should have a counter param, but needs search params - let prevNextParams = new URLSearchParams(prevNextUrl.search); + + /** + * reassign a link element's href, or delete the element if it is not given + * remove the aria-disabled attribute if an href is assigned + * @param {Element} linkEle + * @param {?string} storedSearch + */ + const setHrefOrDelete = function(linkEle, url) { + if (!linkEle) return; + if (url) { + linkEle.href = url + linkEle.removeAttribute('aria-disabled') + } else { + linkEle.remove() + } + } + + /** + * reassign an element's HTML content if the element is given + * @param {?Element} linkEle + * @param {?string} storedSearch + */ + const setContent = function(ele, content) { + if (ele) ele.innerHTML = content; + } + + /** + * fetch the JSON data at pageLinksPath for the given search + * use the fetched data to build the page-links labels and prev/next links + * @param {string} pageLinksPath + * @param {?URLSearchParams} storedSearch + */ + Blacklight.clientPageLinks = function(pageLinksPath, storedSearch) { + const pageLinksUrl = new URL(pageLinksPath, window.location) + // pageLinksUrl should already have a counter param, but needs search params + let prevNextParams = new URLSearchParams(pageLinksUrl.search); if (storedSearch) { for (const [paramName, paramValue] of storedSearch.entries()) { prevNextParams.append(paramName, paramValue); } } - const setHrefOrDelete = function(linkEle, url) { - if (!linkEle) return; - if (url) { - linkEle.href = url - } else { - linkEle.remove() - } - } - const setContent = function(ele, content) { - if (!ele) return; - ele.innerHTML = content; - } - fetch(`${prevNextUrl.pathname}?${prevNextParams.toString()}`) + fetch(`${pageLinksUrl.pathname}?${prevNextParams.toString()}`) .then((response) => response.json()) .then(function(responseData) { if (!responseData.prev && !responseData.next) return; @@ -112,18 +163,11 @@ const SearchContext = (() => { setContent(pageLinks.querySelector('.pagination-total-delimited'), responseData.totalDelimited) setHrefOrDelete(pageLinks.querySelector("a[rel='prev']"), responseData.prev) setHrefOrDelete(pageLinks.querySelector("a[rel='next']"), responseData.next) - pageLinks.classList.remove('d-none') }) }) } - Blacklight.onLoad(function(){ - let clientPageLinks = document.querySelectorAll('.page-links[data-page-links-url]') - if (clientPageLinks[0]) { - const storedSearch = new URLSearchParams(sessionStorage.getItem("blacklightSearch")) - clientAppliedParams(storedSearch) - clientPrevNext(clientPageLinks[0].getAttribute('data-page-links-url'), storedSearch) - } - }) + + Blacklight.doSearchContextBehavior(); })() export default SearchContext From d44f60e9522984e9c4ffb20d001da93510973320 Mon Sep 17 00:00:00 2001 From: Benjamin Armintor Date: Wed, 4 Jan 2023 17:04:11 -0500 Subject: [PATCH 10/11] implement a configuration object for Blacklight::Configuration.track_search_session - allow configuration of components for applied parameters and itewm pagination --- .../client_item_pagination_component.html.erb | 2 +- .../client_item_pagination_component.rb | 20 ++++---- .../concerns/blacklight/bookmarks.rb | 2 +- .../concerns/blacklight/search_context.rb | 30 +++++++----- app/helpers/blacklight/url_helper_behavior.rb | 8 ++-- app/views/catalog/_show_main_content.html.erb | 6 +-- app/views/catalog/index.html.erb | 2 +- app/views/catalog/show.html.erb | 2 +- lib/blacklight/configuration.rb | 9 ++-- .../configuration/session_tracking_config.rb | 46 +++++++++++++++++++ .../blacklight/document_component_spec.rb | 2 +- .../client_item_pagination_component_spec.rb | 2 +- spec/controllers/bookmarks_controller_spec.rb | 2 +- .../blacklight/url_helper_behavior_spec.rb | 4 +- .../session_tracking_config_spec.rb | 39 ++++++++++++++++ 15 files changed, 133 insertions(+), 43 deletions(-) create mode 100644 lib/blacklight/configuration/session_tracking_config.rb create mode 100644 spec/lib/blacklight/configuration/session_tracking_config_spec.rb diff --git a/app/components/blacklight/search_context/client_item_pagination_component.html.erb b/app/components/blacklight/search_context/client_item_pagination_component.html.erb index e527b565fc..1d02d6d589 100644 --- a/app/components/blacklight/search_context/client_item_pagination_component.html.erb +++ b/app/components/blacklight/search_context/client_item_pagination_component.html.erb @@ -1,6 +1,6 @@
-