From d338ad907ed06bc9e121cc1cbda32828a60793df Mon Sep 17 00:00:00 2001
From: Demian Katz <demian.katz@villanova.edu>
Date: Tue, 9 Dec 2014 12:46:09 -0500
Subject: [PATCH] Added config setting for cart cookie domain.

---
 config/vufind/config.ini                      |   3 +
 module/VuFind/src/VuFind/Cart.php             |  36 +-
 module/VuFind/src/VuFind/Service/Factory.php  |   4 +-
 .../unit-tests/src/VuFindTest/CartTest.php    |  18 +-
 themes/blueprint/js/cart.js                   |  18 +-
 .../blueprint/templates/layout/layout.phtml   |   4 +
 themes/bootstrap3/js/cart.js                  | 496 +++++++++---------
 .../bootstrap3/templates/layout/layout.phtml  |   4 +
 8 files changed, 319 insertions(+), 264 deletions(-)

diff --git a/config/vufind/config.ini b/config/vufind/config.ini
index 652cfefb57c..2108807f00f 100644
--- a/config/vufind/config.ini
+++ b/config/vufind/config.ini
@@ -84,6 +84,9 @@ sidebarOnLeft = false
 showBookBag = false
 ; Set the maximum amount of items allowed in the Book Bag - Default is 100
 bookBagMaxSize = 100
+; Set the domain used for cart-related cookies (sometimes useful for sharing the
+; cookies across subdomains)
+;bookBagCookieDomain = ".example.edu"
 ; Generator value to display in an HTML header <meta> tag:
 generator = "VuFind 2.3.1"
 
diff --git a/module/VuFind/src/VuFind/Cart.php b/module/VuFind/src/VuFind/Cart.php
index 4c9c9faf325..b7606b3e335 100644
--- a/module/VuFind/src/VuFind/Cart.php
+++ b/module/VuFind/src/VuFind/Cart.php
@@ -68,6 +68,13 @@ class Cart
      */
     protected $recordLoader;
 
+    /**
+     * Domain context for cookies (null for default)
+     *
+     * @var string
+     */
+    protected $cookieDomain;
+
     const CART_COOKIE =  'vufind_cart';
     const CART_COOKIE_SOURCES = 'vufind_cart_src';
     const CART_COOKIE_DELIM = "\t";
@@ -75,18 +82,21 @@ class Cart
     /**
      * Constructor
      *
-     * @param \VuFind\Record\Loader $loader  Object for loading records
-     * @param int                   $maxSize Maximum size of cart contents
-     * @param bool                  $active  Is cart enabled?
-     * @param array                 $cookies Current cookie values (leave null
+     * @param \VuFind\Record\Loader $loader       Object for loading records
+     * @param int                   $maxSize      Maximum size of cart contents
+     * @param bool                  $active       Is cart enabled?
+     * @param array                 $cookies      Current cookie values (leave null
      * to use $_COOKIE superglobal)
+     * @param string                $cookieDomain Domain context for cookies
+     * (optional)
      */
     public function __construct(\VuFind\Record\Loader $loader,
-        $maxSize = 100, $active = true, $cookies = null
+        $maxSize = 100, $active = true, $cookies = null, $cookieDomain = null
     ) {
         $this->recordLoader = $loader;
         $this->maxSize = $maxSize;
         $this->active = $active;
+        $this->cookieDomain = $cookieDomain;
 
         // Initialize contents
         $this->init(null === $cookies ? $_COOKIE : $cookies);
@@ -282,9 +292,11 @@ class Cart
 
         // Save the cookies:
         $cookie = implode(self::CART_COOKIE_DELIM, $ids);
-        $this->setCookie(self::CART_COOKIE, $cookie, 0, '/');
+        $this->setCookie(self::CART_COOKIE, $cookie, 0, '/', $this->cookieDomain);
         $cookie = implode(self::CART_COOKIE_DELIM, $sources);
-        $this->setCookie(self::CART_COOKIE_SOURCES, $cookie, 0, '/');
+        $this->setCookie(
+            self::CART_COOKIE_SOURCES, $cookie, 0, '/', $this->cookieDomain
+        );
     }
 
     /**
@@ -300,6 +312,16 @@ class Cart
         // @codeCoverageIgnoreEnd
     }
 
+    /**
+     * Get cookie domain context (null if unset).
+     *
+     * @return string
+     */
+    public function getCookieDomain()
+    {
+        return $this->cookieDomain;
+    }
+
     /**
      * Process parameters and return the cart content.
      *
diff --git a/module/VuFind/src/VuFind/Service/Factory.php b/module/VuFind/src/VuFind/Service/Factory.php
index b0a64735232..cd3180022c3 100644
--- a/module/VuFind/src/VuFind/Service/Factory.php
+++ b/module/VuFind/src/VuFind/Service/Factory.php
@@ -93,8 +93,10 @@ class Factory
             ? (bool)$config->Site->showBookBag : false;
         $size = isset($config->Site->bookBagMaxSize)
             ? $config->Site->bookBagMaxSize : 100;
+        $domain = isset($config->Site->bookBagCookieDomain)
+            ? $config->Site->bookBagCookieDomain : null;
         return new \VuFind\Cart(
-            $sm->get('VuFind\RecordLoader'), $size, $active
+            $sm->get('VuFind\RecordLoader'), $size, $active, $_COOKIE, $domain
         );
     }
 
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php
index 904dcbe0147..f63ffabc36a 100644
--- a/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php
@@ -70,14 +70,26 @@ class CartTest extends \PHPUnit_Framework_TestCase
      *
      * @return \VuFind\Cart
      */
-    protected function getCart($maxSize = 100, $active = true, $cookies = array())
-    {
+    protected function getCart($maxSize = 100, $active = true, $cookies = array(),
+        $domain = null
+    ) {
         return $this->getMock(
             'VuFind\Cart', array('setCookie'),
-            array($this->loader, $maxSize, $active, $cookies)
+            array($this->loader, $maxSize, $active, $cookies, $domain)
         );
     }
 
+    /**
+     * Test cookie domain setting.
+     *
+     * @return void
+     */
+    public function testCookieDomain()
+    {
+        $cart = $this->getCart(100, true, array(), '.example.com');
+        $this->assertEquals('.example.com', $cart->getCookieDomain());
+    }
+
     /**
      * Check that the cart is empty by default.
      *
diff --git a/themes/blueprint/js/cart.js b/themes/blueprint/js/cart.js
index d25930ca758..d29f65c15bc 100644
--- a/themes/blueprint/js/cart.js
+++ b/themes/blueprint/js/cart.js
@@ -1,4 +1,4 @@
-/*global contextHelp, vufindString*/
+/*global cartCookieDomain, contextHelp, vufindString*/
 
 var _CART_COOKIE = 'vufind_cart';
 var _CART_COOKIE_SOURCES = 'vufind_cart_src';
@@ -51,11 +51,19 @@ function uniqueValues(array) {
     return r;
 }
 
+function getCartCookieParams() {
+    if (cartCookieDomain) {
+        return { path: '/', domain: cartCookieDomain }
+    } else {
+        return { path: '/' };
+    }
+}
+
 function saveCartCookie(items) {
     // No items?  Clear cookies:
     if (items.length == 0) {
-        $.cookie(_CART_COOKIE, null, { path: '/' });
-        $.cookie(_CART_COOKIE_SOURCES, null, { path: '/' });
+        $.cookie(_CART_COOKIE, null, getCartCookieParams());
+        $.cookie(_CART_COOKIE_SOURCES, null, getCartCookieParams());
         return;
     }
 
@@ -83,8 +91,8 @@ function saveCartCookie(items) {
     }
 
     // Save the cookies:
-    $.cookie(_CART_COOKIE, ids.join(_CART_COOKIE_DELIM), { path: '/' });
-    $.cookie(_CART_COOKIE_SOURCES, sources.join(_CART_COOKIE_DELIM), { path: '/' });
+    $.cookie(_CART_COOKIE, ids.join(_CART_COOKIE_DELIM), getCartCookieParams());
+    $.cookie(_CART_COOKIE_SOURCES, sources.join(_CART_COOKIE_DELIM), getCartCookieParams());
 }
 
 function addItemToCartCookie(item) {
diff --git a/themes/blueprint/templates/layout/layout.phtml b/themes/blueprint/templates/layout/layout.phtml
index cdd5a16b870..5d0f99455f3 100644
--- a/themes/blueprint/templates/layout/layout.phtml
+++ b/themes/blueprint/templates/layout/layout.phtml
@@ -38,6 +38,10 @@
             if ($cart->isActive()) {
                 $this->headScript()->appendFile("jquery.cookie.js");
                 $this->headScript()->appendFile("cart.js");
+                $domain = $cart->getCookieDomain();
+                $this->headScript()->appendScript(
+                  'var cartCookieDomain = ' . (!empty($domain) ? "'$domain'" : 'false') . ';'
+                );
                 $this->jsTranslations()->addStrings(
                     array(
                         'bulk_noitems_advice' => 'bulk_noitems_advice',
diff --git a/themes/bootstrap3/js/cart.js b/themes/bootstrap3/js/cart.js
index 84572198cdb..c18c4d70632 100644
--- a/themes/bootstrap3/js/cart.js
+++ b/themes/bootstrap3/js/cart.js
@@ -1,248 +1,248 @@
-/*global bulkActionSubmit, Cookies, newAccountHandler, path, vufindString, Lightbox, updatePageForLogin */
-
-var _CART_COOKIE = 'vufind_cart';
-var _CART_COOKIE_SOURCES = 'vufind_cart_src';
-var _CART_COOKIE_DELIM = "\t";
-
-var currentId,currentSource;
-var lastCartSubmit = false;
-
-function getCartItems() {
-  var items = Cookies.getItem(_CART_COOKIE);
-  if(items) {
-    return items.split(_CART_COOKIE_DELIM);
-  }
-  return [];
-}
-function getCartSources() {
-  var items = Cookies.getItem(_CART_COOKIE_SOURCES);
-  if(items) {
-    return items.split(_CART_COOKIE_DELIM);
-  }
-  return [];
-}
-function getFullCartItems() {
-  var items = getCartItems();
-  var sources = getCartSources();
-  var full = [];
-  if(items.length == 0) {
-    return [];
-  }
-  for(var i=items.length;i--;) {
-    full[full.length] = sources[items[i].charCodeAt(0)-65]+'|'+items[i].substr(1);
-  }
-  return full;
-}
-
-function addItemToCart(id,source) {
-  if(!source) {
-    source = 'VuFind';
-  }
-  var cartItems = getCartItems();
-  var cartSources = getCartSources();
-  var sIndex = cartSources.indexOf(source);
-  if(sIndex < 0) {
-    // Add source to source cookie
-    cartItems[cartItems.length] = String.fromCharCode(65+cartSources.length) + id;
-    cartSources[cartSources.length] = source;
-    Cookies.setItem(_CART_COOKIE_SOURCES, cartSources.join(_CART_COOKIE_DELIM), false, '/');
-  } else {
-    cartItems[cartItems.length] = String.fromCharCode(65+sIndex) + id;
-  }
-  Cookies.setItem(_CART_COOKIE, $.unique(cartItems).join(_CART_COOKIE_DELIM), false, '/');
-  $('#cartItems strong').html(parseInt($('#cartItems strong').html(), 10)+1);
-  return true;
-}
-function uniqueArray(op) {
-  var ret = [];
-  for(var i=0;i<op.length;i++) {
-    if(ret.indexOf(op[i]) < 0) {
-      ret.push(op[i]);
-    }
-  }
-  return ret;
-}
-function removeItemFromCart(id,source) {
-  var cartItems = getCartItems();
-  var cartSources = getCartSources();
-  // Find
-  var cartIndex = cartItems.indexOf(String.fromCharCode(65+cartSources.indexOf(source))+id);
-  if(cartIndex > -1) {
-    var sourceIndex = cartItems[cartIndex].charCodeAt(0)-65;
-    var cartItem = cartItems[cartIndex];
-    var saveSource = false;
-    for(var i=cartItems.length;i--;) {
-      if(i==cartIndex) {
-        continue;
-      }
-      // If this source is shared by another, keep it
-      if(cartItems[i].charCodeAt(0)-65 == sourceIndex) {
-        saveSource = true;
-        break;
-      }
-    }
-    cartItems.splice(cartIndex,1);
-    // Remove unused sources
-    if(!saveSource) {
-      var oldSources = cartSources.slice(0);
-      cartSources.splice(sourceIndex,1);
-      // Adjust source index characters
-      for(var j=cartItems.length;j--;) {
-        var si = cartItems[j].charCodeAt(0)-65;
-        var ni = cartSources.indexOf(oldSources[si]);
-        cartItems[j] = String.fromCharCode(65+ni)+cartItems[j].substring(1);
-      }
-    }
-    if(cartItems.length > 0) {
-      Cookies.setItem(_CART_COOKIE, uniqueArray(cartItems).join(_CART_COOKIE_DELIM), false, '/');
-      Cookies.setItem(_CART_COOKIE_SOURCES, uniqueArray(cartSources).join(_CART_COOKIE_DELIM), false, '/');
-    } else {
-      Cookies.removeItem(_CART_COOKIE, '/');
-      Cookies.removeItem(_CART_COOKIE_SOURCES, '/');
-    }
-    $('#cartItems strong').html(parseInt($('#cartItems strong').html(), 10)-1);
-    return true;
-  }
-  return false;
-}
-var cartNotificationTimeout = false;
-function registerUpdateCart($form) {
-  if($form) {
-    $("#updateCart, #bottom_updateCart").unbind('click').click(function(){
-      var elId = this.id;
-      var selectedBoxes = $("input[name='ids[]']:checked", $form);
-      var selected = [];
-      $(selectedBoxes).each(function(i) {
-        selected[i] = this.value;
-      });
-      if (selected.length > 0) {
-        var inCart = 0;
-        var msg = "";
-        var orig = getFullCartItems();
-        $(selected).each(function(i) {
-          for (var x in orig) {
-            if (this == orig[x]) {
-              inCart++;
-              return;
-            }
-          }
-          var data = this.split('|');
-          addItemToCart(data[1], data[0]);
-        });
-        var updated = getFullCartItems();
-        var added = updated.length - orig.length;
-        msg += added + " " + vufindString.itemsAddBag;
-        if (inCart > 0 && orig.length > 0) {
-          msg += "<br/>" + inCart + " " + vufindString.itemsInBag;
-        }
-        if (updated.length >= vufindString.bookbagMax) {
-          msg += "<br/>" + vufindString.bookbagFull;
-        }
-        $('#'+elId).data('bs.popover').options.content = msg;
-        $('#cartItems strong').html(updated.length);
-      } else {
-        $('#'+elId).data('bs.popover').options.content = vufindString.bulk_noitems_advice;
-      }
-      $('#'+elId).popover('show');
-      if (cartNotificationTimeout !== false) {
-        clearTimeout(cartNotificationTimeout);
-      }
-      cartNotificationTimeout = setTimeout(function() {
-        $('#'+elId).popover('hide');
-      }, 5000);
-      return false;
-    });
-  }
-}
-
-$(document).ready(function() {
-  // Record buttons
-  var cartId = $('#cartId');
-  if(cartId.length > 0) {
-    cartId = cartId.val().split('|');
-    currentId = cartId[1];
-    currentSource = cartId[0];
-    $('#cart-add.correct,#cart-remove.correct').removeClass('correct hidden');
-    $('#cart-add').click(function() {
-      addItemToCart(currentId,currentSource);
-      $('#cart-add,#cart-remove').toggleClass('hidden');
-    });
-    $('#cart-remove').click(function() {
-      removeItemFromCart(currentId,currentSource);
-      $('#cart-add,#cart-remove').toggleClass('hidden');
-    });
-  } else {
-    // Search results
-    var $form = $('form[name="bulkActionForm"]');
-    registerUpdateCart($form);
-  }
-  $("#updateCart, #bottom_updateCart").popover({content:'', html:true, trigger:'manual'});
-
-  // Setup lightbox behavior
-  // Cart lightbox
-  $('#cartItems').click(function() {
-    return Lightbox.get('Cart','Cart');
-  });
-  // Overwrite
-  Lightbox.addFormCallback('accountForm', function(html) {
-    updatePageForLogin();
-    if (lastCartSubmit !== false) {
-      bulkActionSubmit(lastCartSubmit);
-      lastCartSubmit = false;
-    } else {
-      newAccountHandler(html);
-    }
-  });
-  Lightbox.addFormHandler('cartForm', function(evt) {
-    lastCartSubmit = $(evt.target);
-    bulkActionSubmit($(evt.target));
-    return false;
-  });
-  Lightbox.addFormCallback('bulkEmail', function(html) {
-    Lightbox.confirm(vufindString['bulk_email_success']);
-  });
-  Lightbox.addFormCallback('bulkSave', function(html) {
-    // After we close the lightbox, redirect to list view
-    Lightbox.addCloseAction(function() {
-      document.location.href = path+'/MyResearch/MyList/'+Lightbox.lastPOST['list'];
-    });
-    Lightbox.confirm(vufindString['bulk_save_success']);
-  });
-  Lightbox.addFormHandler('exportForm', function(evt) {
-    $.ajax({
-      url: path + '/AJAX/JSON?' + $.param({method:'exportFavorites'}),
-      type:'POST',
-      dataType:'json',
-      data:Lightbox.getFormData($(evt.target)),
-      success:function(data) {
-        if(data.data.needs_redirect) {
-          document.location.href = data.data.result_url;
-        } else {
-          Lightbox.changeContent(data.data.result_additional);
-        }
-      },
-      error:function(d,e) {
-        //console.log(d,e); // Error reporting
-      }
-    });
-    return false;
-  });
-  $('#modal').on('hidden.bs.modal', function() {
-    // Update cart items (add to cart, remove from cart, cart lightbox interface)
-    var cartCount = $('#cartItems strong');
-    if(cartCount.length > 0) {
-      var cart = getFullCartItems();
-      var id = $('#cartId');
-      if(id.length > 0) {
-        id = id.val();
-        $('#cart-add,#cart-remove').addClass('hidden');
-        if(cart.indexOf(id) > -1) {
-          $('#cart-remove').removeClass('hidden');
-        } else {
-          $('#cart-add').removeClass('hidden');
-        }
-      }
-      cartCount.html(cart.length);
-    }
-  });
-});
+/*global bulkActionSubmit, cartCookieDomain, Cookies, newAccountHandler, path, vufindString, Lightbox, updatePageForLogin */
+
+var _CART_COOKIE = 'vufind_cart';
+var _CART_COOKIE_SOURCES = 'vufind_cart_src';
+var _CART_COOKIE_DELIM = "\t";
+
+var currentId,currentSource;
+var lastCartSubmit = false;
+
+function getCartItems() {
+  var items = Cookies.getItem(_CART_COOKIE);
+  if(items) {
+    return items.split(_CART_COOKIE_DELIM);
+  }
+  return [];
+}
+function getCartSources() {
+  var items = Cookies.getItem(_CART_COOKIE_SOURCES);
+  if(items) {
+    return items.split(_CART_COOKIE_DELIM);
+  }
+  return [];
+}
+function getFullCartItems() {
+  var items = getCartItems();
+  var sources = getCartSources();
+  var full = [];
+  if(items.length == 0) {
+    return [];
+  }
+  for(var i=items.length;i--;) {
+    full[full.length] = sources[items[i].charCodeAt(0)-65]+'|'+items[i].substr(1);
+  }
+  return full;
+}
+
+function addItemToCart(id,source) {
+  if(!source) {
+    source = 'VuFind';
+  }
+  var cartItems = getCartItems();
+  var cartSources = getCartSources();
+  var sIndex = cartSources.indexOf(source);
+  if(sIndex < 0) {
+    // Add source to source cookie
+    cartItems[cartItems.length] = String.fromCharCode(65+cartSources.length) + id;
+    cartSources[cartSources.length] = source;
+    Cookies.setItem(_CART_COOKIE_SOURCES, cartSources.join(_CART_COOKIE_DELIM), false, '/', cartCookieDomain);
+  } else {
+    cartItems[cartItems.length] = String.fromCharCode(65+sIndex) + id;
+  }
+  Cookies.setItem(_CART_COOKIE, $.unique(cartItems).join(_CART_COOKIE_DELIM), false, '/', cartCookieDomain);
+  $('#cartItems strong').html(parseInt($('#cartItems strong').html(), 10)+1);
+  return true;
+}
+function uniqueArray(op) {
+  var ret = [];
+  for(var i=0;i<op.length;i++) {
+    if(ret.indexOf(op[i]) < 0) {
+      ret.push(op[i]);
+    }
+  }
+  return ret;
+}
+function removeItemFromCart(id,source) {
+  var cartItems = getCartItems();
+  var cartSources = getCartSources();
+  // Find
+  var cartIndex = cartItems.indexOf(String.fromCharCode(65+cartSources.indexOf(source))+id);
+  if(cartIndex > -1) {
+    var sourceIndex = cartItems[cartIndex].charCodeAt(0)-65;
+    var cartItem = cartItems[cartIndex];
+    var saveSource = false;
+    for(var i=cartItems.length;i--;) {
+      if(i==cartIndex) {
+        continue;
+      }
+      // If this source is shared by another, keep it
+      if(cartItems[i].charCodeAt(0)-65 == sourceIndex) {
+        saveSource = true;
+        break;
+      }
+    }
+    cartItems.splice(cartIndex,1);
+    // Remove unused sources
+    if(!saveSource) {
+      var oldSources = cartSources.slice(0);
+      cartSources.splice(sourceIndex,1);
+      // Adjust source index characters
+      for(var j=cartItems.length;j--;) {
+        var si = cartItems[j].charCodeAt(0)-65;
+        var ni = cartSources.indexOf(oldSources[si]);
+        cartItems[j] = String.fromCharCode(65+ni)+cartItems[j].substring(1);
+      }
+    }
+    if(cartItems.length > 0) {
+      Cookies.setItem(_CART_COOKIE, uniqueArray(cartItems).join(_CART_COOKIE_DELIM), false, '/', cartCookieDomain);
+      Cookies.setItem(_CART_COOKIE_SOURCES, uniqueArray(cartSources).join(_CART_COOKIE_DELIM), false, '/', cartCookieDomain);
+    } else {
+      Cookies.removeItem(_CART_COOKIE, '/', cartCookieDomain);
+      Cookies.removeItem(_CART_COOKIE_SOURCES, '/', cartCookieDomain);
+    }
+    $('#cartItems strong').html(parseInt($('#cartItems strong').html(), 10)-1);
+    return true;
+  }
+  return false;
+}
+var cartNotificationTimeout = false;
+function registerUpdateCart($form) {
+  if($form) {
+    $("#updateCart, #bottom_updateCart").unbind('click').click(function(){
+      var elId = this.id;
+      var selectedBoxes = $("input[name='ids[]']:checked", $form);
+      var selected = [];
+      $(selectedBoxes).each(function(i) {
+        selected[i] = this.value;
+      });
+      if (selected.length > 0) {
+        var inCart = 0;
+        var msg = "";
+        var orig = getFullCartItems();
+        $(selected).each(function(i) {
+          for (var x in orig) {
+            if (this == orig[x]) {
+              inCart++;
+              return;
+            }
+          }
+          var data = this.split('|');
+          addItemToCart(data[1], data[0]);
+        });
+        var updated = getFullCartItems();
+        var added = updated.length - orig.length;
+        msg += added + " " + vufindString.itemsAddBag;
+        if (inCart > 0 && orig.length > 0) {
+          msg += "<br/>" + inCart + " " + vufindString.itemsInBag;
+        }
+        if (updated.length >= vufindString.bookbagMax) {
+          msg += "<br/>" + vufindString.bookbagFull;
+        }
+        $('#'+elId).data('bs.popover').options.content = msg;
+        $('#cartItems strong').html(updated.length);
+      } else {
+        $('#'+elId).data('bs.popover').options.content = vufindString.bulk_noitems_advice;
+      }
+      $('#'+elId).popover('show');
+      if (cartNotificationTimeout !== false) {
+        clearTimeout(cartNotificationTimeout);
+      }
+      cartNotificationTimeout = setTimeout(function() {
+        $('#'+elId).popover('hide');
+      }, 5000);
+      return false;
+    });
+  }
+}
+
+$(document).ready(function() {
+  // Record buttons
+  var cartId = $('#cartId');
+  if(cartId.length > 0) {
+    cartId = cartId.val().split('|');
+    currentId = cartId[1];
+    currentSource = cartId[0];
+    $('#cart-add.correct,#cart-remove.correct').removeClass('correct hidden');
+    $('#cart-add').click(function() {
+      addItemToCart(currentId,currentSource);
+      $('#cart-add,#cart-remove').toggleClass('hidden');
+    });
+    $('#cart-remove').click(function() {
+      removeItemFromCart(currentId,currentSource);
+      $('#cart-add,#cart-remove').toggleClass('hidden');
+    });
+  } else {
+    // Search results
+    var $form = $('form[name="bulkActionForm"]');
+    registerUpdateCart($form);
+  }
+  $("#updateCart, #bottom_updateCart").popover({content:'', html:true, trigger:'manual'});
+
+  // Setup lightbox behavior
+  // Cart lightbox
+  $('#cartItems').click(function() {
+    return Lightbox.get('Cart','Cart');
+  });
+  // Overwrite
+  Lightbox.addFormCallback('accountForm', function(html) {
+    updatePageForLogin();
+    if (lastCartSubmit !== false) {
+      bulkActionSubmit(lastCartSubmit);
+      lastCartSubmit = false;
+    } else {
+      newAccountHandler(html);
+    }
+  });
+  Lightbox.addFormHandler('cartForm', function(evt) {
+    lastCartSubmit = $(evt.target);
+    bulkActionSubmit($(evt.target));
+    return false;
+  });
+  Lightbox.addFormCallback('bulkEmail', function(html) {
+    Lightbox.confirm(vufindString['bulk_email_success']);
+  });
+  Lightbox.addFormCallback('bulkSave', function(html) {
+    // After we close the lightbox, redirect to list view
+    Lightbox.addCloseAction(function() {
+      document.location.href = path+'/MyResearch/MyList/'+Lightbox.lastPOST['list'];
+    });
+    Lightbox.confirm(vufindString['bulk_save_success']);
+  });
+  Lightbox.addFormHandler('exportForm', function(evt) {
+    $.ajax({
+      url: path + '/AJAX/JSON?' + $.param({method:'exportFavorites'}),
+      type:'POST',
+      dataType:'json',
+      data:Lightbox.getFormData($(evt.target)),
+      success:function(data) {
+        if(data.data.needs_redirect) {
+          document.location.href = data.data.result_url;
+        } else {
+          Lightbox.changeContent(data.data.result_additional);
+        }
+      },
+      error:function(d,e) {
+        //console.log(d,e); // Error reporting
+      }
+    });
+    return false;
+  });
+  $('#modal').on('hidden.bs.modal', function() {
+    // Update cart items (add to cart, remove from cart, cart lightbox interface)
+    var cartCount = $('#cartItems strong');
+    if(cartCount.length > 0) {
+      var cart = getFullCartItems();
+      var id = $('#cartId');
+      if(id.length > 0) {
+        id = id.val();
+        $('#cart-add,#cart-remove').addClass('hidden');
+        if(cart.indexOf(id) > -1) {
+          $('#cart-remove').removeClass('hidden');
+        } else {
+          $('#cart-add').removeClass('hidden');
+        }
+      }
+      cartCount.html(cart.length);
+    }
+  });
+});
diff --git a/themes/bootstrap3/templates/layout/layout.phtml b/themes/bootstrap3/templates/layout/layout.phtml
index f7f53f437fb..a749ed23b16 100644
--- a/themes/bootstrap3/templates/layout/layout.phtml
+++ b/themes/bootstrap3/templates/layout/layout.phtml
@@ -39,6 +39,10 @@
         if ($cart->isActive()) {
           $this->headScript()->appendFile("vendor/cookies.js");
           $this->headScript()->appendFile("cart.js");
+          $domain = $cart->getCookieDomain();
+          $this->headScript()->appendScript(
+            'var cartCookieDomain = ' . (!empty($domain) ? "'$domain'" : 'false') . ';'
+          );
           $this->jsTranslations()->addStrings(
             array(
               'bulk_noitems_advice' => 'bulk_noitems_advice',
-- 
GitLab