PageSourceSearch

https://www.columbiaortho.org/sites/default/files/js/js_0dzZys7APA39xPrRUChlMvKa85gB4-AJvR6u-Wi3u6w.js

js columbiaortho.org collected 2026-10-03 21:01:45 UTC 12,074 bytes, 295 lines download raw bytes

1/**
2 * @file
3 * Drupal behaviors attached to cola_search module.
4 *
5 */
6
7/**
8 * Cola Search scripts including Google Custom Search Engine Overrides.
9 *
10 * @type {Drupal~behavior}
11 *
12 * @prop {Drupal~behaviorAttach} attach
13 *   Adjusts markup generated by the Google Custom Search for accessibility.
14 */
15(function ($, Drupal, window, document, undefined) {
16  'use strict';
17
18  Drupal.behaviors.colaSearch = {
19    attach: function (context, settings) {
20      if (typeof settings.colaSearch === 'undefined') {
21        // Settings are required.
22        return;
23      }
24      if (typeof settings.colaSearch.searchEngineId === 'undefined') {
25        // Nothing to do if we don't have a search engine id.
26        return;
27      }
28      if (typeof settings.colaSearch.searchElements === 'undefined') {
29        // Nothing to do if we don't have any search elements to render.
30        return;
31      }
32      // Make sure refinements is defined, default to false (hidden);
33      settings.colaSearch.refinements = settings.colaSearch.refinements || false;
34
35      /**
36       * Theme GCSE search results list.
37       */
38      Drupal.theme.prototype.colaSearchGcseResults = function (items, baseClass, stateSuffix) {
39        if (items.length > 0) {
40          var $itemsContainer = $('<div></div>')
41            .addClass(baseClass)
42            .addClass(baseClass + '--' + stateSuffix);
43          var $itemsList = $('<ul></ul>', { class: baseClass + '__list' });
44          for (var i = 0; i < items.length; i++) {
45            var $item = $('<li></li>');
46            var $itemAnchor = $('<a/>', {
47              href: items[i].actualUrl
48            });
49            $itemAnchor.append('<h2>' + items[i].title + '</h2>');
50            $item.append($itemAnchor);
51            $itemsList.append($item);
52          }
53          $itemsContainer.append($itemsList);
54        }
55        return $itemsContainer;
56      }
57
58      /**
59       * Configuration for our mutation observers.
60       *
61       * The options childList and subtree configure the observer to watch for
62       * children and descendants of children of the observed node respectively.
63       *
64       * @see https://davidwalsh.name/mutationobserver-api
65       */
66      var observerConfig = { childList: true, subtree: true };
67
68      /**
69       * Function to make changes to markup when search elements change.
70       */
71      var reactToMutation = function (mutations, observer, container) {
72        // Remove observer while we make changes to the markup, so we don't
73        // trigger reactions to our own changes.
74        observer.disconnect();
75        for (var i = 0; i < mutations.length; i++) {
76          var $mutation = $(mutations[i].target);
77          var nodeAdded = mutations[i].addedNodes.length;
78          var addedNode = mutations[i].addedNodes[0];
79          var searchBoxOnly = $(container).hasClass('cb-gcse-container--searchbox-only');
80
81          // Add search role to search form.
82          // @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Search_role
83          if ($mutation.find('form.gsc-search-box').length) {
84            $mutation.find('form.gsc-search-box').attr('role', 'search');
85          }
86
87          // Add aria-labels to fix accessibility.
88          // Title attribute alone is not sufficient for a11y
89          // @see https://developer.paciellogroup.com/blog/2012/01/html5-accessibility-chops-title-attribute-use-and-abuse/
90          if ($mutation.find('.gsc-input').length) {
91            // Add aria label to form search input element.
92            $mutation.find('input.gsc-input')
93              .attr('aria-label', 'Search')
94              .attr('placeholder', 'Enter search terms');
95            // Add aria label to the clear search link.
96            $mutation.find('a.gsst_a').attr('aria-label', 'Reset search');
97          }
98
99          // Adjustments to search results markup.
100          if (!searchBoxOnly) {
101            // Hide refinements as soon as they are added, if site is not
102            // configured to show them.
103            if (!settings.colaSearch.refinements) {
104              if ($mutation.hasClass('gsc-tabsAreaInvisible')) {
105                $('.gsc-tabsAreaInvisible').hide();
106              }
107              if ($mutation.hasClass('gsc-tabsArea')) {
108                $('.gsc-tabsArea').hide();
109              }
110            }
111
112            // Announce result info if present and has just been added.
113            if ($mutation.hasClass('gsc-result-info') && mutations[i].addedNodes.length) {
114              var infoText = $('.gsc-result-info').text();
115              var announceText ='';
116              if ($('.gsc-cursor-current-page').length) {
117                announceText = 'You are on page ';
118                announceText += $('.gsc-cursor-current-page').text() + ' of ';
119                announceText += infoText;
120              }
121              else {
122                announceText = 'Listing ' + infoText;
123              }
124              Drupal.announce(Drupal.t(announceText));
125            }
126            // If the mutation record includes changes to the 'no result' div,
127            // check to see if nodes were added and if the added node was a
128            // no result message. Finally, announce it via drupal announce.
129            if (nodeAdded && addedNode.childNodes.length) {
130              if ($(addedNode.childNodes).hasClass('gs-no-results-result')) {
131                Drupal.announce(Drupal.t($('.gs-no-results-result').find('.gs-snippet').text()));
132              }
133            }
134
135            // Change pagination markup to match accessible pattern.
136            // @see https://www.a11ymatters.com/pattern/pagination/
137            if ($mutation.find('.gsc-cursor-box').length) {
138              var $pagination = $mutation.find('.gsc-cursor-box');
139
140              // Modify the pagination to fit the pattern if we haven't done so
141              // already, otherwise, just make sure thet the current page is
142              // marked properly.
143              if (!$pagination.find('nav').length) {
144                var $paginationWrappers = $('<nav/>', {
145                  'class': 'cb-gsce-pagination',
146                  'role': 'navigation',
147                  'aria-label': 'Pagination Navigation',
148                  'html': '<ul></ul>'
149                });
150                $pagination.find('.gsc-cursor > .gsc-cursor-page')
151                  .attr('role', 'button')
152                  .attr('aria-label', function() {return 'Go to page ' + $(this).text();})
153                  .wrap('<li class="cb-gsce-pagination__item">');
154                $pagination.find('.gsc-cursor .gsc-cursor-current-page')
155                  .attr('aria-label', function() {return 'Current Page, Page ' + $(this).text();})
156                  .attr('aria-current', 'true');
157                $pagination.find('.gsc-cursor > .cb-gsce-pagination__item')
158                  .wrapAll($paginationWrappers);
159              }
160              else {
161                $pagination.find('.gsc-cursor .gsc-cursor-page')
162                  .attr('aria-label', function() {return 'Go to page ' + $(this).text();})
163                  .removeAttr('aria-current', '');
164                $pagination.find('.gsc-cursor .gsc-cursor-current-page')
165                  .attr('aria-label', function() {return 'Current Page, Page ' + $(this).text();})
166                  .attr('aria-current', 'true');
167              }
168            }
169
170          }
171        }
172
173        // Re-enable observer after updating markup.
174        observer.observe(container, observerConfig);
175      };
176
177      /**
178       * Loop through the search elements and initialize them once.
179       */
180      $.each(settings.colaSearch.searchElements, function(elementId, gcseOptions) {
181        $('#' + elementId, context).once('cola-search-init', function() {
182          var searchElement = document.getElementById(elementId);
183          var observer = new MutationObserver(function(mutations, observer) {
184            reactToMutation(mutations, observer, searchElement);
185          });
186          observer.observe(searchElement, observerConfig);
187        });
188      });
189
190      /**
191       * GCSE initialization callback to render search elements.
192       */
193      var renderSearchElements = function() {
194        if (document.readyState == 'complete') {
195          // Document is ready when CSE element is initialized.
196          $.each(settings.colaSearch.searchElements, function(elementId, gcseOptions) {
197            google.search.cse.element.render(gcseOptions);
198          });
199        }
200        else {
201          // Document is not ready yet, when CSE element is initialized.
202          google.setOnLoadCallback(function() {
203            $.each(settings.colaSearch.searchElements, function(elementId, gcseOptions) {
204              google.search.cse.element.render(gcseOptions);
205            });
206          }, true);
207        }
208      };
209
210      /**
211       * Web search results have been rendered callback for GCSE.
212       *
213       * Invoked immediately before the Search Element JavaScript renders the
214       * page footer.
215       */
216      var webSearchResultsRendered = function(gname, query, promos, results) {
217        // @see https://developers.google.com/custom-search/docs/element#callbacks
218        var $promoImages = $(promos).find('.gs-promotion-table-snippet-with-image img');
219        var $resultImages = $(results).find('.gsc-thumbnail img');
220        var $allImages= $promoImages.add($resultImages);
221        var $resultTitleLinks = $(results).find('a.gs-title');
222
223        // Promoted results are limited to 60x60px.
224        // https://support.google.com/customsearch/answer/2631074?hl=en
225        // Let's retrieve a size equal to the size of the other search results.
226        $promoImages.each(function() {
227          var src = this.src;
228          if (typeof src === 'string') {
229            src = src.replace(/w\d+\-h\d+$/gi, 'w150-h150');
230            this.src = src;
231          }
232        });
233        // Remove anchors around images from tabindex and hide them from
234        // screen readers via attributes to avoid link duplication and empty
235        // anchors (the images don't have alt tags).
236        $allImages.each(function() {
237          if ($(this).parent().is('a')) {
238            $(this).parent().attr({
239              'aria-hidden': 'true',
240              'tabindex': '-1',
241              'role': 'presentation'
242            });
243          }
244          // Add a class to the result container for theming.
245          $(this).parents('.gsc-result').addClass('cb-gsc-has-thumbnail');
246        });
247
248        // Wrap result links in an h2. Idealy we would want the results to
249        // be in a list, but those changes seem too disruptive.
250        $resultTitleLinks.each(function() {
251          if (!$(this).parent().is('h2')) {
252            $(this).wrap('<h2></h2>');
253          }
254        });
255
256      };
257
258      /**
259       * Initialize GCSE on initial document load.
260       *
261       * Note this prevents search box rendering after editing with IPE. Since
262       * only developers should be modifying search panes we'll not worry
263       * about that right now.
264       */
265      $('body', context).once('cola-search-init', function() {
266
267        // Insert it before the CSE code snippet so that cse.js can take the
268        // script parameters, like parsetags, callbacks.
269        window.__gcse = {
270          parsetags: 'explicit',
271          initializationCallback: renderSearchElements,
272          searchCallbacks: {
273            web: {
274              rendered: webSearchResultsRendered
275            }
276          }
277        };
278        // CSE code snippet initializes google custom search.
279        (function() {
280          var cx = settings.colaSearch.searchEngineId;
281          var gcse = document.createElement('script');
282          gcse.type = 'text/javascript';
283          gcse.async = true;
284          gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
285          gcse.setAttribute('nonce', settings.colaSearch.nonce);
286          var s = document.getElementsByTagName('script')[0];
287          s.parentNode.insertBefore(gcse, s);
288        })();
289
290      });
291
292    }
293  };
294})(jQuery, Drupal, this, this.document);
295;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.