diff --git a/module/VuFind/src/VuFind/Cart.php b/module/VuFind/src/VuFind/Cart.php
new file mode 100644
index 0000000000000000000000000000000000000000..64a885feac7540a20c9a43d15e003edb70703a23
--- /dev/null
+++ b/module/VuFind/src/VuFind/Cart.php
@@ -0,0 +1,290 @@
+<?php
+/**
+ * Cart Class
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  Support_Classes
+ * @author   Tuan Nguyen <tuan@yorku.ca>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/system_classes Wiki
+ */
+namespace VuFind;
+use VuFind\Config\Reader as ConfigReader;
+use VuFind\Record;
+
+/**
+ * Cart Class
+ *
+ * The data model object representing a user's book cart.
+ *
+ * @category VuFind2
+ * @package  Support_Classes
+ * @author   Tuan Nguyen <tuan@yorku.ca>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/system_classes Wiki
+ */
+class Cart
+{
+    protected static $singleton;
+    protected $items;
+    protected $maxSize = 100;
+    protected $active = false;
+
+    const CART_COOKIE =  'vufind_cart';
+    const CART_COOKIE_SOURCES = 'vufind_cart_src';
+    const CART_COOKIE_DELIM = "\t";
+
+    /**
+     * Protected constructor to ensure singleton pattern.
+     */
+    protected function __construct()
+    {
+        $config = ConfigReader::getConfig();
+        if (isset($config->Site->showBookBag)) {
+            $this->active = (bool)$config->Site->showBookBag;
+        }
+        if (isset($config->Site->bookBagMaxSize)) {
+            $this->maxSize = $config->Site->bookBagMaxSize;
+        }
+        $this->items = array();
+
+        // Initialize contents
+        $this->init();
+    }
+
+    /**
+     * Get the current instance of the user's cart, if
+     * it is not initialized, then one will be initialized.
+     *
+     * @return Cart_Model
+     */
+    static function getInstance()
+    {
+        if (!self::$singleton) {
+            self::$singleton = new Cart();
+        }
+        return self::$singleton;
+    }
+
+    /**
+     * Return the contents of the cart.
+     *
+     * @return array     array of items in the cart
+     */
+    public function getItems()
+    {
+        return $this->items;
+    }
+
+    /**
+     * Does the cart contain the specified item?
+     *
+     * @param string $item ID of item to check
+     *
+     * @return bool
+     */
+    public function contains($item)
+    {
+        return in_array($item, $this->items);
+    }
+
+    /**
+     * Empty the cart.
+     *
+     * @return void
+     */
+    public function emptyCart()
+    {
+        $this->items = array();
+        $this->save();
+    }
+
+    /**
+     * Add an item to the cart.
+     *
+     * @param string $item ID of item to remove
+     *
+     * @return array       Associative array with two keys: success (bool) and
+     * notAdded (array of IDs that were unable to be added to the cart)
+     */
+    public function addItem($item)
+    {
+        return $this->addItems(array($item));
+    }
+
+    /**
+     * Add an array of items to the cart.
+     *
+     * @param array $items IDs of items to add
+     *
+     * @return array       Associative array with two keys: success (bool) and
+     * notAdded (array of IDs that were unable to be added to the cart)
+     */
+    public function addItems($items)
+    {
+        $items = array_merge($this->items, $items);
+
+        $total = count($items);
+        $this->items = array_slice(array_unique($items), 0, $this->maxSize);
+        $this->save();
+        if ($total > $this->maxSize) {
+            $notAdded = $total-$this->maxSize;
+            return array('success' => false, 'notAdded' => $notAdded);
+        }
+        return array('success' => true);
+    }
+
+    /**
+     * Remove an item from the cart.
+     *
+     * @param array $items An array of item IDS
+     *
+     * @return void
+     */
+    public function removeItems($items)
+    {
+        $results = array();
+        foreach ($this->items as $id) {
+            if (!in_array($id, $items)) {
+                $results[] = $id;
+            }
+        }
+        $this->items = $results;
+        $this->save();
+    }
+
+    /**
+     * Get cart size.
+     *
+     * @return string The maximum cart size
+     */
+    public function getMaxSize()
+    {
+        return $this->maxSize;
+    }
+
+    /**
+     * Check whether the cart is full.
+     *
+     * @return bool      true if full, false otherwise
+     */
+    public function isFull()
+    {
+        return (count($this->items) >= $this->maxSize);
+    }
+
+    /**
+     * Check whether the cart is empty.
+     *
+     * @return bool      true if empty, false otherwise
+     */
+    public function isEmpty()
+    {
+        return empty($this->items);
+    }
+
+    /**
+     * Check whether cart is enabled.
+     *
+     * @return bool
+     */
+    public function isActive()
+    {
+        return $this->active;
+    }
+
+    /**
+     * Initialize the cart model.
+     *
+     * @return array   contents of the cart
+     */
+    protected function init()
+    {
+        $items = null;
+        if (isset($_COOKIE[self::CART_COOKIE])) {
+            $cookie = $_COOKIE[self::CART_COOKIE];
+            $items = explode(self::CART_COOKIE_DELIM, $cookie);
+
+            if (!isset($_COOKIE[self::CART_COOKIE_SOURCES])) {
+                // Backward compatibility with VuFind 1.x -- if no source cookie, all
+                // items come from the VuFind source:
+                for ($i = 0; $i < count($items); $i++) {
+                    $items[$i] = 'VuFind|' . $items[$i];
+                }
+            } else {
+                // Default case for VuFind 2.x carts -- decompress source data:
+                $sources = explode(
+                    self::CART_COOKIE_DELIM, $_COOKIE[self::CART_COOKIE_SOURCES]
+                );
+                for ($i = 0; $i < count($items); $i++) {
+                    $sourceIndex = ord(substr($items[$i], 0, 1)) - 65;
+                    $items[$i]
+                        = $sources[$sourceIndex] . '|' . substr($items[$i], 1);
+                }
+            }
+        }
+        $this->items = $items ? $items : array();
+    }
+
+    /**
+     * Save the state of the cart. This implementation uses cookie
+     * so the cart contents can be manipulated on the client side as well.
+     *
+     * @return void
+     */
+    protected function save()
+    {
+        $sources = array();
+        $ids = array();
+
+        foreach ($this->items as $item) {
+            // Break apart the source and the ID:
+            list($source, $id) = explode('|', $item, 2);
+
+            // Add the source to the source array if it is not already there:
+            $sourceIndex = array_search($source, $sources);
+            if ($sourceIndex === false) {
+                $sourceIndex = count($sources);
+                $sources[$sourceIndex] = $source;
+            }
+
+            // Encode the source into the ID as a single character:
+            $ids[] = chr(65 + $sourceIndex) . $id;
+        }
+
+        // Save the cookies:
+        $cookie = implode(self::CART_COOKIE_DELIM, $ids);
+        setcookie(self::CART_COOKIE, $cookie, 0, '/');
+        $cookie = implode(self::CART_COOKIE_DELIM, $sources);
+        setcookie(self::CART_COOKIE_SOURCES, $cookie, 0, '/');
+    }
+
+    /**
+     * Process parameters and return the cart content.
+     *
+     * @return array $records The cart content
+     */
+    public function getRecordDetails()
+    {
+        /* TODO:
+        return Record::loadBatch($this->items);
+         */
+    }
+}