diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php
index a516c4181ea0bb4242cf8b41640020d668805b4e..826526bd2fd9e13f5d00059c521f1cb337966f2e 100644
--- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php
+++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php
@@ -74,15 +74,17 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase
      * Set up the record page for OpenURL testing.
      *
      * @param array $openUrlExtras Extra settings for the [OpenURL] config section.
+     * @param array $extraConfigs  Top-level config.ini overrides
      *
      * @return Element
      */
-    protected function setupRecordPage($openUrlExtras = [])
+    protected function setupRecordPage($openUrlExtras = [], $extraConfigs = [])
     {
         // Set up configs
         $this->changeConfigs(
             [
-                'config' => $this->getConfigIniOverrides($openUrlExtras),
+                'config' =>
+                    $extraConfigs + $this->getConfigIniOverrides($openUrlExtras),
             ]
         );
 
@@ -95,14 +97,17 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase
     /**
      * Click an OpenURL on the page and assert the expected results.
      *
-     * @param Element $page Current page object
+     * @param Element $page  Current page object
+     * @param bool    $click Should we click the link (true), or is it autoloading?
      *
      * @return void
      */
-    protected function assertOpenUrl(Element $page)
+    protected function assertOpenUrl(Element $page, $click = true)
     {
         // Click the OpenURL link:
-        $this->findCss($page, '.fulltext')->click();
+        if ($click) {
+            $this->findCss($page, '.fulltext')->click();
+        }
         $this->snooze();
 
         // Confirm that the expected fake demo driver data is there:
@@ -132,7 +137,7 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase
     }
 
     /**
-     * Test a link in the search results.
+     * Test a link in the search results (default behavior, click required).
      *
      * @return void
      */
@@ -158,7 +163,35 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase
     }
 
     /**
-     * Test a link on the record page.
+     * Test a link in the search results (optional autoloading enabled).
+     *
+     * @return void
+     */
+    public function testLinkInSearchResultsWithAutoloading()
+    {
+        // Set up configs
+        $this->changeConfigs(
+            [
+                'config' => $this->getConfigIniOverrides(
+                    ['embed_auto_load' => true]
+                ),
+            ]
+        );
+
+        // Search for a known record:
+        $session = $this->getMinkSession();
+        $session->visit($this->getVuFindUrl() . '/Search/Home');
+        $page = $session->getPage();
+        $this->findCss($page, '.searchForm [name="lookfor"]')
+            ->setValue('id:testsample1');
+        $this->findCss($page, '.btn.btn-primary')->click();
+
+        // Verify the OpenURL
+        $this->assertOpenUrl($page, false /* do not click link */);
+    }
+
+    /**
+     * Test that link is missing from the record page by default.
      *
      * @return void
      */
@@ -171,7 +204,7 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase
     }
 
     /**
-     * Test a link on the record page.
+     * Test a link on the record page (in core metadata).
      *
      * @return void
      */
@@ -183,7 +216,7 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase
     }
 
     /**
-     * Test a link on the record page.
+     * Test a link on the record page (in holdings tab).
      *
      * @return void
      */
@@ -193,4 +226,19 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase
         $page = $this->setupRecordPage(['show_in_holdings' => true]);
         $this->assertOpenUrl($page);
     }
+
+    /**
+     * Test a link on the record page (in holdings tab w/ AJAX loading).
+     *
+     * @return void
+     */
+    public function testLinkOnRecordPageWithLinkInHoldingsAndAjaxTabLoading()
+    {
+        // By default, no OpenURL on record page:
+        $page = $this->setupRecordPage(
+            ['show_in_holdings' => true],
+            ['Site' => ['loadInitialTabWithAjax' => true]]
+        );
+        $this->assertOpenUrl($page);
+    }
 }
diff --git a/themes/bootstrap3/js/combined-search.js b/themes/bootstrap3/js/combined-search.js
index 611522efa9ed96784e22ff3d7edbad4e0e3fd4be..3477225688bc1b73be98cf8c6d03e669d223b371 100644
--- a/themes/bootstrap3/js/combined-search.js
+++ b/themes/bootstrap3/js/combined-search.js
@@ -1,11 +1,11 @@
-/*global VuFind, setupOpenUrlLinks, checkItemStatuses, checkSaveStatuses */
+/*global VuFind, checkItemStatuses, checkSaveStatuses */
 VuFind.combinedSearch = (function() {
   var init = function(container, url) {
     container.load(url, '', function(responseText) {
       if (responseText.length == 0) {
         container.hide();
       } else {
-        setupOpenUrlLinks(container);
+        VuFind.openurl.init(container);
         checkItemStatuses(container);
         checkSaveStatuses(container);
       }
diff --git a/themes/bootstrap3/js/openurl.js b/themes/bootstrap3/js/openurl.js
index 8719d819f2e8fab90f305b9a5e090b06e780ac7c..20a471541c534d9a08a52f2ce01d524e4f46d687 100644
--- a/themes/bootstrap3/js/openurl.js
+++ b/themes/bootstrap3/js/openurl.js
@@ -1,66 +1,63 @@
 /*global extractClassParams, VuFind */
+VuFind.register('openurl', function() {
+  var _loadResolverLinks = function($target, openUrl, searchClassId) {
+    $target.addClass('ajax_availability');
+    var url = VuFind.path + '/AJAX/JSON?' + $.param({method:'getResolverLinks',openurl:openUrl,searchClassId:searchClassId});
+    $.ajax({
+      dataType: 'json',
+      url: url
+    })
+    .done(function(response) {
+      $target.removeClass('ajax_availability').empty().append(response.data);
+    })
+    .fail(function(response, textStatus) {
+      $target.removeClass('ajax_availability').addClass('text-danger').empty();
+      if (textStatus == 'abort' || typeof response.responseJSON === 'undefined') { return; }
+      $target.append(response.responseJSON.data);
+    });
+  }
 
-function loadResolverLinks($target, openUrl, searchClassId) {
-  $target.addClass('ajax_availability');
-  var url = VuFind.path + '/AJAX/JSON?' + $.param({method:'getResolverLinks',openurl:openUrl,searchClassId:searchClassId});
-  $.ajax({
-    dataType: 'json',
-    url: url
-  })
-  .done(function(response) {
-    $target.removeClass('ajax_availability').empty().append(response.data);
-  })
-  .fail(function(response, textStatus) {
-    $target.removeClass('ajax_availability').addClass('text-danger').empty();
-    if (textStatus == 'abort' || typeof response.responseJSON === 'undefined') { return; }
-    $target.append(response.responseJSON.data);
-  });
-}
-
-function embedOpenUrlLinks(element) {
-  // Extract the OpenURL associated with the clicked element:
-  var openUrl = element.children('span.openUrl:first').attr('title');
+  var _embedOpenUrlLinks = function(element) {
+    // Extract the OpenURL associated with the clicked element:
+    var openUrl = element.children('span.openUrl:first').attr('title');
 
-  // Hide the controls now that something has been clicked:
-  var controls = element.parents('.openUrlControls');
-  controls.removeClass('openUrlEmbed').addClass('hidden');
+    // Hide the controls now that something has been clicked:
+    var controls = element.parents('.openUrlControls');
+    controls.removeClass('openUrlEmbed').addClass('hidden');
 
-  // Locate the target area for displaying the results:
-  var target = controls.next('div.resolver');
+    // Locate the target area for displaying the results:
+    var target = controls.next('div.resolver');
 
-  // If the target is already visible, a previous click has populated it;
-  // don't waste time doing redundant work.
-  if (target.hasClass('hidden')) {
-    loadResolverLinks(target.removeClass('hidden'), openUrl, element.data('search-class-id'));
+    // If the target is already visible, a previous click has populated it;
+    // don't waste time doing redundant work.
+    if (target.hasClass('hidden')) {
+      _loadResolverLinks(target.removeClass('hidden'), openUrl, element.data('search-class-id'));
+    }
   }
-}
 
-// Assign actions to the OpenURL links. This can be called with a container e.g. when 
-// combined results fetched with AJAX are loaded.
-function setupOpenUrlLinks(container)
-{
-  if (typeof(container) == 'undefined') {
-    container = $('body');
+  // Assign actions to the OpenURL links. This can be called with a container e.g. when 
+  // combined results fetched with AJAX are loaded.
+  var init = function(container)
+  {
+    if (typeof(container) == 'undefined') {
+      container = $('body');
+    }
+
+     // assign action to the openUrlWindow link class
+    container.find('a.openUrlWindow').unbind('click').click(function() {
+      var params = extractClassParams(this);
+      var settings = params.window_settings;
+      window.open($(this).attr('href'), 'openurl', settings);
+      return false;
+    });
+
+    // assign action to the openUrlEmbed link class
+    container.find('.openUrlEmbed a').unbind('click').click(function() {
+      _embedOpenUrlLinks($(this));
+      return false;
+    });
+
+    container.find('.openUrlEmbed.openUrlEmbedAutoLoad a').trigger('click');
   }
-  
-   // assign action to the openUrlWindow link class
-  container.find('a.openUrlWindow').click(function() {
-    var params = extractClassParams(this);
-    var settings = params.window_settings;
-    window.open($(this).attr('href'), 'openurl', settings);
-    return false;
-  });
- 
-  // assign action to the openUrlEmbed link class
-  container.find('.openUrlEmbed a').click(function() {
-    embedOpenUrlLinks($(this));
-    return false;
-  });
-
-  container.find('.openUrlEmbed.openUrlEmbedAutoLoad a').trigger('click');
-}
-
-$(document).ready(function() {
-  setupOpenUrlLinks();
+  return {init: init}
 });
-
diff --git a/themes/bootstrap3/templates/Helpers/openurl.phtml b/themes/bootstrap3/templates/Helpers/openurl.phtml
index 5cf61e1b5c450ffaa31b7fb7c8f9b9e9e0dafd30..f68528b4b61a21bea425e56512e188bcf81cd4c1 100644
--- a/themes/bootstrap3/templates/Helpers/openurl.phtml
+++ b/themes/bootstrap3/templates/Helpers/openurl.phtml
@@ -1,5 +1,5 @@
 <?
-  $this->headScript()->appendFile("openurl.js");
+  echo $this->inlineScript(\Zend\View\Helper\HeadScript::FILE, 'openurl.js', 'SET');
   $classes = '';
   if ($this->openUrlEmbed) {
     $classes = "fulltext";