blacklight 7.42.0 → 7.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52a04abd135541f36cb96f7e8538c190b034ca08d2a66a52b46bc7ac22c1ee21
4
- data.tar.gz: 26250b77f584c4b7d7316dd51a86fb465444e30c91f3c3bcd6ff5899a8012b65
3
+ metadata.gz: a6c3e69551f3ec30627ee9af6650769e67b2a3a218e8565ba4f3932ef02847c9
4
+ data.tar.gz: 0a6d3a9136156ede88c35a280452e5f2c93b62e94c8b0f242eaa97bff800354e
5
5
  SHA512:
6
- metadata.gz: b9c4b32a75d4bbefe63e2cb4e54d2e221d110a152014b7150c124950c8a457da8059ab5ed87a0bbe2a7e0d754914abad0457745366a4060b62878284e95076b6
7
- data.tar.gz: aa0a5810de6e1b1eb19a611061b310e093f57179261c3eff3f8e6a8830cb03ef47f5ce22800f2a9a3653a1867842d12786a1d805ee3d3e26e856c5e85d753574
6
+ metadata.gz: f7fec0f4025fa11b4ae1f966f78be6bb7ec3ef3c11226e16741e821fc27d2107e3d33ba251c7158b5c8573d27af97c9eaf7e17fc38e2dd58c01a3729ee32a1ba
7
+ data.tar.gz: e38e2cd95019ca902fe5e257d7b9511811c63b646eb919383b807ae7a1d478ae6a6b365361b072f34f2d37187c6cd352afe895ab6c923e6b19f52a18e2e1157f
data/VERSION CHANGED
@@ -1 +1 @@
1
- 7.42.0
1
+ 7.43.0
@@ -0,0 +1,596 @@
1
+ /* Converts a "toggle" form, with single submit button to add/remove
2
+ something, like used for Bookmarks, into an AJAXy checkbox instead.
3
+ Apply to a form. Does require certain assumption about the form:
4
+ 1) The same form 'action' href must be used for both ADD and REMOVE
5
+ actions, with the different being the hidden input name="_method"
6
+ being set to "put" or "delete" -- that's the Rails method to pretend
7
+ to be doing a certain HTTP verb. So same URL, PUT to add, DELETE
8
+ to remove. This plugin assumes that.
9
+ Plus, the form this is applied to should provide a data-doc-id
10
+ attribute (HTML5-style doc-*) that contains the id/primary key
11
+ of the object in question -- used by plugin for a unique value for
12
+ DOM id's.
13
+ Uses HTML for a checkbox compatible with Bootstrap.
14
+ new CheckboxSubmit(document.querySelector('form.something')).render()
15
+ */
16
+ class CheckboxSubmit {
17
+ constructor(form) {
18
+ this.form = form;
19
+ }
20
+
21
+ clicked(evt) {
22
+ this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress');
23
+ this.labelTarget.setAttribute('disabled', 'disabled');
24
+ this.checkboxTarget.setAttribute('disabled', 'disabled');
25
+ fetch(this.formTarget.getAttribute('action'), {
26
+ body: new FormData(this.formTarget),
27
+ method: this.formTarget.getAttribute('method').toUpperCase(),
28
+ headers: {
29
+ 'Accept': 'application/json',
30
+ 'X-Requested-With': 'XMLHttpRequest',
31
+ 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content
32
+ }
33
+ }).then((response) => {
34
+ if (response.ok) return response.json();
35
+ return Promise.reject('response was not ok')
36
+ }).then((json) => {
37
+ this.labelTarget.removeAttribute('disabled');
38
+ this.checkboxTarget.removeAttribute('disabled');
39
+ // For accessibility return keyboard focus
40
+ // back to the checkbox after form submission
41
+ this.checkboxTarget.focus();
42
+ this.updateStateFor(!this.checked);
43
+ this.bookmarksCounter().forEach(counter => {
44
+ counter.innerHTML = json.bookmarks.count;
45
+ });
46
+
47
+ var e = new CustomEvent('bookmark.blacklight', { detail: { checked: this.checked }, bubbles: true });
48
+ this.formTarget.dispatchEvent(e);
49
+ }).catch((error) => {
50
+ this.handleError(error);
51
+ });
52
+ }
53
+
54
+ get checked() {
55
+ return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)
56
+ }
57
+
58
+ get formTarget() {
59
+ return this.form
60
+ }
61
+
62
+ get labelTarget() {
63
+ return this.form.querySelector('[data-checkboxsubmit-target="label"]')
64
+ }
65
+
66
+ get checkboxTarget() {
67
+ return this.form.querySelector('[data-checkboxsubmit-target="checkbox"]')
68
+ }
69
+
70
+ get spanTarget() {
71
+ return this.form.querySelector('[data-checkboxsubmit-target="span"]')
72
+ }
73
+
74
+ bookmarksCounter() {
75
+ return document.querySelectorAll('[data-role="bookmark-counter"]')
76
+ }
77
+
78
+ handleError() {
79
+ alert("Unable to save the bookmark at this time.");
80
+ }
81
+
82
+ updateStateFor(state) {
83
+ if (state) { this.checkboxTarget.setAttribute('checked', state); }
84
+ else { this.checkboxTarget.removeAttribute('checked'); }
85
+
86
+ if (state) {
87
+ this.labelTarget.classList.add('checked');
88
+ //Set the Rails hidden field that fakes an HTTP verb
89
+ //properly for current state action.
90
+ this.formTarget.querySelector('input[name=_method]').value = 'delete';
91
+ this.spanTarget.innerHTML = this.form.getAttribute('data-present');
92
+ } else {
93
+ this.labelTarget.classList.remove('checked');
94
+ this.formTarget.querySelector('input[name=_method]').value = 'put';
95
+ this.spanTarget.innerHTML = this.form.getAttribute('data-absent');
96
+ }
97
+ }
98
+ }
99
+
100
+ const BookmarkToggle = (e) => {
101
+ const elementType = e.target.getAttribute('data-checkboxsubmit-target');
102
+ if (elementType == 'checkbox' || elementType == 'label') {
103
+ const form = e.target.closest('form');
104
+ if (form) new CheckboxSubmit(form).clicked(e);
105
+ if (e.code == 'Space') e.preventDefault();
106
+ }
107
+ };
108
+
109
+ document.addEventListener('click', BookmarkToggle);
110
+ document.addEventListener('keydown', function (e) {
111
+ if (e.key === 'Enter' || e.code == 'Space') { BookmarkToggle(e); } }
112
+ );
113
+
114
+ const ButtonFocus = (e) => {
115
+ // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
116
+ // Safari both do not set focus to a button on button click.
117
+ // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
118
+ if (e.target.matches('[data-bs-toggle="collapse"]')) {
119
+ e.target.focus();
120
+ }
121
+ };
122
+
123
+ document.addEventListener('click', ButtonFocus);
124
+
125
+ const Core = function() {
126
+ const buffer = new Array;
127
+ return {
128
+ onLoad: function(func) {
129
+ buffer.push(func);
130
+ },
131
+
132
+ activate: function() {
133
+ for(let i = 0; i < buffer.length; i++) {
134
+ buffer[i].call();
135
+ }
136
+ },
137
+
138
+ listeners: function () {
139
+ const listeners = [];
140
+ if (typeof Turbo !== 'undefined') {
141
+ listeners.push('turbo:load', 'turbo:frame-load');
142
+ } else {
143
+ listeners.push('DOMContentLoaded');
144
+ }
145
+
146
+ return listeners;
147
+ }
148
+ };
149
+ }();
150
+
151
+ // turbo triggers turbo:load events on page transition
152
+ // If app isn't using turbo, this event will never be triggered, no prob.
153
+ Core.listeners().forEach(function(listener) {
154
+ document.addEventListener(listener, function() {
155
+ Core.activate();
156
+ });
157
+ });
158
+
159
+ Core.onLoad(function () {
160
+ const elem = document.querySelector('.no-js');
161
+
162
+ // The "no-js" class may already have been removed because this function is
163
+ // run on every turbo:load event, in that case, it won't find an element.
164
+ if (!elem) return;
165
+
166
+ elem.classList.remove('no-js');
167
+ elem.classList.add('js');
168
+ });
169
+
170
+ /*!
171
+ * Color mode toggler for Blacklight
172
+ * Based on Bootstrap's color mode toggler (https://getbootstrap.com/docs/5.3/customize/color-modes/#javascript)
173
+ */
174
+
175
+
176
+ const ColorThemeSwitcher = (() => {
177
+
178
+ const getStoredTheme = () => localStorage.getItem('theme');
179
+ const setStoredTheme = theme => localStorage.setItem('theme', theme);
180
+
181
+ const getPreferredTheme = () => {
182
+ const storedTheme = getStoredTheme();
183
+ if (storedTheme) {
184
+ return storedTheme
185
+ }
186
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
187
+ };
188
+
189
+ const setTheme = theme => {
190
+ if (theme === 'auto') {
191
+ document.documentElement.setAttribute('data-bs-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
192
+ } else {
193
+ document.documentElement.setAttribute('data-bs-theme', theme);
194
+ }
195
+ };
196
+
197
+ const showActiveTheme = (theme, focus = false) => {
198
+ const themeSwitcher = document.querySelector('#bl-theme-switcher');
199
+ if (!themeSwitcher) return
200
+
201
+ // Reset all dropdown items
202
+ document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
203
+ element.classList.remove('active');
204
+ element.setAttribute('aria-pressed', 'false');
205
+ const check = element.querySelector('.bl-theme-check');
206
+ if (check) check.classList.add('d-none');
207
+ });
208
+
209
+ // Activate the selected item
210
+ const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`);
211
+ if (btnToActive) {
212
+ btnToActive.classList.add('active');
213
+ btnToActive.setAttribute('aria-pressed', 'true');
214
+ const check = btnToActive.querySelector('.bl-theme-check');
215
+ if (check) check.classList.remove('d-none');
216
+ }
217
+
218
+ // Swap the toggle button icon
219
+ themeSwitcher.querySelectorAll('.bl-theme-icon').forEach(icon => icon.classList.add('d-none'));
220
+ const activeIcon = themeSwitcher.querySelector(`.bl-theme-icon[data-bl-theme-icon="${theme}"]`);
221
+ if (activeIcon) activeIcon.classList.remove('d-none');
222
+
223
+ themeSwitcher.setAttribute('aria-label', `Toggle theme (${theme})`);
224
+
225
+ if (focus) {
226
+ themeSwitcher.focus();
227
+ }
228
+ };
229
+
230
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
231
+ const storedTheme = getStoredTheme();
232
+ if (storedTheme !== 'light' && storedTheme !== 'dark') {
233
+ setTheme(getPreferredTheme());
234
+ }
235
+ });
236
+
237
+ document.addEventListener('click', e => {
238
+ const btn = e.target.closest('[data-bs-theme-value]');
239
+ if (!btn) return
240
+
241
+ const theme = btn.getAttribute('data-bs-theme-value');
242
+ setStoredTheme(theme);
243
+ setTheme(theme);
244
+ showActiveTheme(theme, true);
245
+ });
246
+
247
+ Core.onLoad(() => showActiveTheme(getPreferredTheme()));
248
+
249
+ return { setTheme, getPreferredTheme, showActiveTheme }
250
+ })();
251
+
252
+ // Usage:
253
+ // ```
254
+ // const basicFunction = (entry) => console.log(entry)
255
+ // const debounced = debounce(basicFunction("I should only be called once"));
256
+ //
257
+ // debounced // does NOT print to the screen because it is invoked again less than 200 milliseconds later
258
+ // debounced // does print to the screen
259
+ // ```
260
+ function debounce(func, timeout = 200) {
261
+ let timer;
262
+ return (...args) => {
263
+ clearTimeout(timer);
264
+ timer = setTimeout(() => { func.apply(this, args); }, timeout);
265
+ };
266
+ }
267
+
268
+ const FacetSuggest = async (e) => {
269
+ if (e.target.matches('.facet-suggest')) {
270
+ const queryFragment = e.target.value?.trim();
271
+ const facetField = e.target.dataset.facetField;
272
+ const facetArea = document.querySelector('.facet-extended-list');
273
+ const prevNextLinks = document.querySelectorAll('.prev_next_links');
274
+
275
+ if (!facetField) { return; }
276
+
277
+ // Get the search params from the current query so the facet suggestions
278
+ // can retain that context.
279
+ const facetSearchContext = e.target.dataset.facetSearchContext;
280
+ const url = new URL(facetSearchContext, window.location.origin);
281
+
282
+ // Drop facet.page so a filtered suggestion list will always start on page 1
283
+ url.searchParams.delete('facet.page');
284
+ // add our queryFragment for facet filtering
285
+ url.searchParams.append('query_fragment', queryFragment);
286
+
287
+ const facetSearchParams = url.searchParams.toString();
288
+ const basePathComponent = url.pathname.split('/')[1];
289
+
290
+ const urlToFetch = `/${basePathComponent}/facet_suggest/${facetField}?${facetSearchParams}`;
291
+
292
+ const response = await fetch(urlToFetch);
293
+ if (response.ok) {
294
+ const blob = await response.blob();
295
+ const text = await blob.text();
296
+
297
+ if (text && facetArea) {
298
+ facetArea.innerHTML = text;
299
+ }
300
+ }
301
+
302
+ // Hide the prev/next links when a user enters text in the facet
303
+ // suggestion input. They don't work with a filtered list.
304
+ prevNextLinks.forEach(element => {
305
+ element.classList.toggle('invisible', !!queryFragment);
306
+ });
307
+
308
+ // Add a class to distinguish suggested facet values vs. regular.
309
+ facetArea.classList.toggle('facet-suggestions', !!queryFragment);
310
+ }
311
+ };
312
+
313
+ document.addEventListener('input', debounce(FacetSuggest));
314
+
315
+ /*
316
+ The blacklight modal plugin can display some interactions inside a Bootstrap
317
+ modal window, including some multi-page interactions.
318
+
319
+ It supports unobtrusive Javascript, where a link or form that would have caused
320
+ a new page load is changed to display it's results inside a modal dialog,
321
+ by this plugin. The plugin assumes there is a Bootstrap modal div
322
+ on the page with id #blacklight-modal to use as the modal -- the standard Blacklight
323
+ layout provides this.
324
+
325
+ To make a link or form have their results display inside a modal, add
326
+ `data-blacklight-modal="trigger"` to the link or form. (Note, form itself not submit input)
327
+ With Rails link_to helper, you'd do that like:
328
+
329
+ link_to something, link, data: { blacklight_modal: "trigger" }
330
+
331
+ The results of the link href or form submit will be displayed inside
332
+ a modal -- they should include the proper HTML markup for a bootstrap modal's
333
+ contents. Also, you ordinarily won't want the Rails template with wrapping
334
+ navigational elements to be used. The Rails controller could suppress
335
+ the layout when a JS AJAX request is detected, OR the response
336
+ can include a `<div data-blacklight-modal="container">` -- only the contents
337
+ of the container will be placed inside the modal, the rest of the
338
+ page will be ignored.
339
+
340
+ Link or forms inside the modal will ordinarily cause page loads
341
+ when they are triggered. However, if you'd like their results
342
+ to stay within the modal, just add `data-blacklight-modal="preserve"`
343
+ to the link or form.
344
+
345
+ Here's an example of what might be returned, demonstrating most of the devices available:
346
+
347
+ <div data-blacklight-modal="container">
348
+ <div class="modal-header">
349
+ <button type="button" class="btn-close" data-bl-dismiss="modal" aria-hidden="true">×</button>
350
+ <h3 class="modal-title">Request Placed</h3>
351
+ </div>
352
+
353
+ <div class="modal-body">
354
+ <p>Some message</p>
355
+ <%= link_to "This result will still be within modal", some_link, data: { blacklight_modal: "preserve" } %>
356
+ </div>
357
+
358
+
359
+ <div class="modal-footer">
360
+ <button type="button" class="btn btn-secondary" data-bl-dismiss="modal">Close</button>
361
+ </div>
362
+ </div>
363
+
364
+
365
+ One additional feature. If the content returned from the AJAX form submission
366
+ can be a turbo-stream that defines some HTML fragementsand where on the page to put them:
367
+ https://turbo.hotwired.dev/handbook/streams
368
+ */
369
+
370
+ const Modal = (() => {
371
+ const modal = {};
372
+
373
+ // a Bootstrap modal div that should be already on the page hidden
374
+ modal.modalSelector = '#blacklight-modal';
375
+
376
+ // Trigger selectors identify forms or hyperlinks that should open
377
+ // inside a modal dialog.
378
+ modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';
379
+
380
+ // preserve selectors identify forms or hyperlinks that, if activated already
381
+ // inside a modal dialog, should have destinations remain inside the modal -- but
382
+ // won't trigger a modal if not already in one.
383
+ //
384
+ // No need to repeat selectors from trigger selectors, those will already
385
+ // be preserved. MUST be manually prefixed with the modal selector,
386
+ // so they only apply to things inside a modal.
387
+ modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';
388
+
389
+ modal.containerSelector = '[data-blacklight-modal~=container]';
390
+
391
+ // Called on fatal failure of ajax load, function returns content
392
+ // to show to user in modal. Right now called only for network errors.
393
+ modal.onFailure = function (error) {
394
+ console.error('Server error:', this.url, error);
395
+
396
+ const contents = `<div class="modal-header">
397
+ <div class="modal-title">There was a problem with your request.</div>
398
+ <button type="button" class="blacklight-modal-close btn-close" data-bl-dismiss="modal" aria-label="Close">
399
+ </button>
400
+ </div>
401
+ <div class="modal-body">
402
+ <p>Expected a successful response from the server, but got an error</p>
403
+ <pre>${this.url}\n${error}</pre>
404
+ </div>`;
405
+
406
+ modal.target().querySelector('.modal-content').innerHTML = contents;
407
+
408
+ modal.show();
409
+ };
410
+
411
+ // Add the passed in contents to the modal and display it.
412
+ // We have specific handling so that scripts returned from the ajax call are executed.
413
+ // This enables adding a script like recaptcha to prevent bots from sending emails.
414
+ modal.receiveAjax = function (contents) {
415
+ const domparser = new DOMParser();
416
+ const dom = domparser.parseFromString(contents, "text/html");
417
+ // If there is a containerSelector on the document, use its children.
418
+ let elements = dom.querySelectorAll(`${modal.containerSelector} > *`);
419
+ const frag = document.createDocumentFragment();
420
+ if (elements.length == 0) {
421
+ // If the containerSelector wasn't found, use the whole document
422
+ elements = dom.body.childNodes;
423
+ }
424
+ elements.forEach((el) => frag.appendChild(el));
425
+ modal.activateScripts(frag);
426
+
427
+ modal.target().querySelector('.modal-content').replaceChildren(frag);
428
+
429
+ // send custom event with the modal dialog div as the target
430
+ var e = new CustomEvent('loaded.blacklight.blacklight-modal', { bubbles: true, cancelable: true });
431
+ modal.target().dispatchEvent(e);
432
+
433
+ // if they did preventDefault, don't show the dialog
434
+ if (e.defaultPrevented) return;
435
+ modal.show();
436
+ };
437
+
438
+ // DOMParser doesn't allow scripts to be executed. This fixes that.
439
+ modal.activateScripts = function (frag) {
440
+ frag.querySelectorAll('script').forEach((script) => {
441
+ const fixedScript = document.createElement('script');
442
+ fixedScript.src = script.src;
443
+ fixedScript.async = false;
444
+ script.parentNode.replaceChild(fixedScript, script);
445
+ });
446
+ };
447
+
448
+ modal.modalAjaxLinkClick = function(e) {
449
+ e.preventDefault();
450
+ const href = e.target.closest('a').getAttribute('href');
451
+ fetch(href, { headers: { 'X-Requested-With': 'XMLHttpRequest' }})
452
+ .then(response => {
453
+ if (!response.ok) {
454
+ throw new TypeError("Request failed");
455
+ }
456
+ return response.text();
457
+ })
458
+ .then(data => modal.receiveAjax(data))
459
+ .catch(error => modal.onFailure(error));
460
+ };
461
+
462
+ modal.setupModal = function() {
463
+ // Register several click handlers in ONE event handler for efficiency
464
+ //
465
+ // * close button OR click on backdrop (modal.modalSelector) closes modal
466
+ // * trigger and preserve link in modal functionality -- if somethign matches both trigger and
467
+ // preserve, still only called once.
468
+ document.addEventListener('click', (e) => {
469
+ if (e.target.closest(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))
470
+ modal.modalAjaxLinkClick(e);
471
+ else if (e.target.matches(`${modal.modalSelector}`) || e.target.closest('[data-bl-dismiss="modal"]'))
472
+ modal.hide();
473
+ });
474
+
475
+ // Make sure user-agent dismissal of html 'dialog', etc `esc` key, triggers
476
+ // our hide logic, including events and scroll restoration.
477
+ const modalDom = modal.target();
478
+ if (modalDom) {
479
+ modal.target().addEventListener('cancel', (e) => {
480
+ e.preventDefault(); // 'hide' will close the modal unless cancelled
481
+
482
+ modal.hide();
483
+ });
484
+ }
485
+ };
486
+
487
+ modal.hide = function (el) {
488
+ const dom = modal.target();
489
+
490
+ if (!dom.open) return
491
+
492
+ var e = new CustomEvent('hide.blacklight.blacklight-modal', { bubbles: true, cancelable: true });
493
+ dom.dispatchEvent(e);
494
+
495
+ dom.close();
496
+
497
+ // Turn body scrolling back to what it was
498
+ document.body.style["overflow"] = modal.originalBodyOverflow;
499
+ document.body.style["padding-right"] = modal.originalBodyPaddingRight;
500
+ modal.originalBodyOverflow = undefined;
501
+ modal.originalBodyPaddingRight = undefined;
502
+ };
503
+
504
+ modal.show = function(el) {
505
+ const dom = modal.target();
506
+
507
+ if (dom.open) return
508
+
509
+ var e = new CustomEvent('show.blacklight.blacklight-modal', { bubbles: true, cancelable: true });
510
+ dom.dispatchEvent(e);
511
+
512
+ dom.showModal();
513
+
514
+ // Turn off body scrolling
515
+ modal.originalBodyOverflow = document.body.style['overflow'];
516
+ modal.originalBodyPaddingRight = document.body.style['padding-right'];
517
+ document.body.style["overflow"] = "hidden";
518
+ document.body.style["padding-right"] = "0px";
519
+ };
520
+
521
+ modal.target = function() {
522
+ return document.querySelector(modal.modalSelector);
523
+ };
524
+
525
+ modal.setupModal();
526
+
527
+ return modal;
528
+ })();
529
+
530
+ const SearchContext = (e) => {
531
+ const contextLink = e.target.closest('[data-context-href]');
532
+ if (contextLink) {
533
+ SearchContext.handleSearchContextMethod.call(contextLink, e);
534
+ }
535
+ };
536
+
537
+ SearchContext.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content;
538
+ SearchContext.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content;
539
+
540
+ // this is the Rails.handleMethod with a couple adjustments, described inline:
541
+ // first, we're attaching this directly to the event handler, so we can check for meta-keys
542
+ SearchContext.handleSearchContextMethod = function(event) {
543
+ const link = this;
544
+
545
+ // instead of using the normal href, we need to use the context href instead
546
+ let href = link.getAttribute('data-context-href');
547
+ let target = link.getAttribute('target');
548
+ let csrfToken = SearchContext.csrfToken();
549
+ let csrfParam = SearchContext.csrfParam();
550
+ let form = document.createElement('form');
551
+ form.method = 'post';
552
+ form.action = href;
553
+
554
+
555
+ let formContent = `<input name="_method" value="post" type="hidden" />
556
+ <input name="redirect" value="${link.getAttribute('href')}" type="hidden" />`;
557
+
558
+ // check for meta keys.. if set, we should open in a new tab
559
+ if(event.metaKey || event.ctrlKey) {
560
+ form.dataset.turbo = "false";
561
+ target = '_blank';
562
+ }
563
+
564
+ if (csrfParam !== undefined && csrfToken !== undefined) {
565
+ formContent += `<input name="${csrfParam}" value="${csrfToken}" type="hidden" />`;
566
+ }
567
+
568
+ // Must trigger submit by click on a button, else "submit" event handler won't work!
569
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
570
+ formContent += '<input type="submit" />';
571
+
572
+ if (target) { form.setAttribute('target', target); }
573
+
574
+ form.style.display = 'none';
575
+ form.innerHTML = formContent;
576
+ document.body.appendChild(form);
577
+ form.querySelector('[type="submit"]').click();
578
+
579
+ event.preventDefault();
580
+ };
581
+
582
+ document.addEventListener('click', SearchContext);
583
+
584
+ const index = {
585
+ BookmarkToggle,
586
+ ButtonFocus,
587
+ ColorThemeSwitcher,
588
+ FacetSuggest,
589
+ Modal,
590
+ SearchContext,
591
+ Core,
592
+ onLoad: Core.onLoad
593
+ };
594
+
595
+ export { index as default };
596
+ //# sourceMappingURL=blacklight.esm.js.map