diff --git a/module/VuFind/src/VuFind/Theme/Initializer.php b/module/VuFind/src/VuFind/Theme/Initializer.php
index 315c5d06401b6dd1dd4ade7bb68f79fca8d253e1..5fec8bab79a4f4346530c627f2a68756f5cd27a1 100644
--- a/module/VuFind/src/VuFind/Theme/Initializer.php
+++ b/module/VuFind/src/VuFind/Theme/Initializer.php
@@ -47,6 +47,7 @@ class Initializer
     protected $autoLoader;
     protected $config;
     protected $event;
+    protected $resourceContainer;
     protected $serviceManager;
     protected $session;
 
@@ -75,6 +76,10 @@ class Initializer
         // Grab the service manager for convenience:
         $this->serviceManager = $this->event->getApplication()->getServiceManager();
 
+        // Grab the resource manager for tracking CSS, JS, etc.:
+        $this->resourceContainer
+            = call_user_func(array($tools, 'getResourceContainer'));
+
         // Set up a session namespace for storing theme settings:
         $this->session = call_user_func(array($tools, 'getPersistenceContainer'));
     }
@@ -332,49 +337,6 @@ class Initializer
         }
     }
 
-    /**
-     * Support method for setUpThemes -- set up CSS for the current theme.
-     *
-     * @param array $css CSS files to load.
-     *
-     * @return void
-     */
-    protected function setUpThemeCss($css)
-    {
-        /* TODO:
-        foreach ($css as $current) {
-            $parts = explode(':', $current);
-            $this->view->headLink()->appendStylesheet(
-                trim($parts[0]),
-                isset($parts[1]) ? trim($parts[1]) : 'all',
-                isset($parts[2]) ? trim($parts[2]) : false
-            );
-        }
-         */
-    }
-
-    /**
-     * Support method for setUpThemes -- set up Javascript for the current theme.
-     *
-     * @param array $js Javascript files to load.
-     *
-     * @return void
-     */
-    protected function setUpThemeJs($js)
-    {
-        /* TODO:
-        foreach ($js as $current) {
-            $parts =  explode(':', $current);
-            $this->view->headScript()->appendFile(
-                trim($parts[0]),
-                'text/javascript',
-                isset($parts[1])
-                ? array('conditional' => trim($parts[1])) : array()
-            );
-        }
-         */
-    }
-
     /**
      * Support method for init() -- set up theme once current settings are known.
      *
@@ -399,16 +361,15 @@ class Initializer
 
             // Add CSS and JS dependencies:
             if ($css = $currentThemeInfo->get('css')) {
-                $this->setUpThemeCss($css);
+                $this->resourceContainer->addCss($css);
             }
             if ($js = $currentThemeInfo->get('js')) {
-                $this->setUpThemeJs($js);
+                $this->resourceContainer->addJs($js);
             }
 
-            // Select favicon (we only want one, so we'll pick the best available
-            // one inside this loop and actually load it later outside the loop):
+            // Select favicon:
             if ($favicon = $currentThemeInfo->get('favicon')) {
-                $bestFavicon = $favicon;
+                $this->resourceContainer->setFavicon($favicon);
             }
         }
 
@@ -422,18 +383,5 @@ class Initializer
                 $current->setPaths($templatePathStack);
             }
         }
-
-        /* TODO:
-        // If we found a favicon above, load it now:
-        if (isset($bestFavicon)) {
-            $this->view->headLink(
-                array(
-                    'href' => $this->view->imageLink($bestFavicon),
-                    'type' => 'image/x-icon',
-                    'rel' => 'shortcut icon'
-                )
-            );
-        }
-         */
     }
 }
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/Theme/ResourceContainer.php b/module/VuFind/src/VuFind/Theme/ResourceContainer.php
new file mode 100644
index 0000000000000000000000000000000000000000..482fe5fdd92d2cfe336fafe7efceccbe7b44d2b7
--- /dev/null
+++ b/module/VuFind/src/VuFind/Theme/ResourceContainer.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * VuFind Theme Public Resource Handler (for CSS, JS, etc.)
+ *
+ * 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   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org   Main Site
+ */
+namespace VuFind\Theme;
+
+/**
+ * VuFind Theme Public Resource Handler (for CSS, JS, etc.)
+ *
+ * @category VuFind2
+ * @package  Support_Classes
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org   Main Site
+ */
+class ResourceContainer
+{
+    protected $css = array();
+    protected $js = array();
+    protected $favicon = null;
+
+    /**
+     * Add a CSS file.
+     *
+     * @param array|string $css CSS file (or array of CSS files) to add (possibly
+     * with extra settings from theme.ini appended to each filename string).
+     *
+     * @return void
+     */
+    public function addCss($css)
+    {
+        if (!is_array($css) && !is_a($css, '\Traversable')) {
+            $css = array($css);
+        }
+        foreach ($css as $current) {
+            $this->css[] = $current;
+        }
+    }
+
+    /**
+     * Add a Javascript file.
+     *
+     * @param array|string $js Javascript file (or array of files) to add (possibly
+     * with extra settings from theme.ini appended to each filename string).
+     *
+     * @return void
+     */
+    public function addJs($js)
+    {
+        if (!is_array($js) && !is_a($js, '\Traversable')) {
+            $js = array($js);
+        }
+        foreach ($js as $current) {
+            $this->js[] = $current;
+        }
+    }
+
+    /**
+     * Get CSS files.
+     *
+     * @return array
+     */
+    public function getCss()
+    {
+        return array_unique($this->css);
+    }
+
+    /**
+     * Get Javascript files.
+     *
+     * @return array
+     */
+    public function getJs()
+    {
+        return array_unique($this->js);
+    }
+
+    /**
+     * Set the favicon.
+     *
+     * @param string $favicon New favicon path.
+     *
+     * @return void
+     */
+    public function setFavicon($favicon)
+    {
+        $this->favicon = $favicon;
+    }
+
+    /**
+     * Get the favicon (null for none).
+     *
+     * @return string
+     */
+    public function getFavicon($favicon)
+    {
+        return $this->favicon;
+    }
+}
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/Theme/Tools.php b/module/VuFind/src/VuFind/Theme/Tools.php
index 9e06b70a065befd95ea86e42aff62e001c3a610d..c6d1a3a5c029c418123dad442105172c53240ad0 100644
--- a/module/VuFind/src/VuFind/Theme/Tools.php
+++ b/module/VuFind/src/VuFind/Theme/Tools.php
@@ -50,7 +50,23 @@ class Tools
     }
 
     /**
-     * Get the container used for persisting session-related settings.
+     * Get the container used for handling public resources for themes
+     * (CSS, JS, etc.)
+     *
+     * @return Context
+     */
+    public static function getResourceContainer()
+    {
+        static $container = false;
+        if (!$container) {
+            $container = new ResourceContainer();
+        }
+        return $container;
+    }
+
+    /**
+     * Get the container used for persisting theme-related settings from
+     * page to page.
      *
      * @return SessionContainer
      */
@@ -62,4 +78,28 @@ class Tools
         }
         return $container;
     }
+
+    /**
+     * Search the themes for a particular file.  If it exists, return the
+     * first matching theme name; otherwise, return null.
+     *
+     * @param string $relativePath Relative path to search within themes
+     *
+     * @return string
+     */
+    public static function findContainingTheme($relativePath)
+    {
+        $session = static::getPersistenceContainer();
+        $basePath = static::getBaseDir();
+
+        $currentTheme = $session->currentTheme;
+
+        while (!empty($currentTheme)
+            && !file_exists("$basePath/$currentTheme/$relativePath")
+        ) {
+            $currentTheme = $session->allThemeInfo[$currentTheme]->extends;
+        }
+
+        return empty($currentTheme) ? null : $currentTheme;
+    }
 }
\ No newline at end of file
diff --git a/themes/vufind/blueprint/templates/layout/layout.phtml b/themes/vufind/blueprint/templates/layout/layout.phtml
index 1e3bf8ecbd24ec19ba04bb3ed1fab344bb87a31f..c9c28664620298a11f310a1bccfa3d20136d19aa 100644
--- a/themes/vufind/blueprint/templates/layout/layout.phtml
+++ b/themes/vufind/blueprint/templates/layout/layout.phtml
@@ -1,6 +1,7 @@
 <?=$this->doctype('XHTML1_TRANSITIONAL')?>
 <html xmlns="http://www.w3.org/1999/xhtml" lang="<?=$this->userLang?>" xml:lang="en">
   <head>
+    <?$this->headThemeResources()?>
     <?=$this->headMeta()?>
     <?=$this->headTitle()?>
     <?=$this->headStyle()?>
diff --git a/themes/vufind/jquerymobile/templates/layout/layout.phtml b/themes/vufind/jquerymobile/templates/layout/layout.phtml
index c576baadeeccb5ad6193ecc78e112c1896305249..7d23610be4779229ab71f32809514b4a67ccb282 100644
--- a/themes/vufind/jquerymobile/templates/layout/layout.phtml
+++ b/themes/vufind/jquerymobile/templates/layout/layout.phtml
@@ -4,6 +4,7 @@
     <meta charset="utf-8"/>
     <meta name="format-detection" content="telephone=no"/>
     <meta name="viewport" content="width=device-width, minimum-scale=1, maximum-scale=1"/>
+    <?$this->headThemeResources()?>
     <?=$this->headMeta()?>
     <?=$this->headTitle()?>
     <?=$this->headStyle()?>
diff --git a/themes/vufind/root/helpers/HeadLink.php b/themes/vufind/root/helpers/HeadLink.php
index c48f335b2177c6e365c6f668a4563452745dab02..7f59b32e3b0ead2116c8e6cc5c97558318dd0a3b 100644
--- a/themes/vufind/root/helpers/HeadLink.php
+++ b/themes/vufind/root/helpers/HeadLink.php
@@ -25,6 +25,8 @@
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     http://vufind.org/wiki/building_a_recommendations_module Wiki
  */
+namespace VuFindThemes\Root\Helpers;
+use VuFind\Theme\Tools as ThemeTools;
 
 /**
  * Head link view helper (extended for VuFind's theme system)
@@ -35,7 +37,7 @@
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     http://vufind.org/wiki/building_a_recommendations_module Wiki
  */
-class VuFind_Theme_Root_Helper_HeadLink extends Zend_View_Helper_HeadLink
+class HeadLink extends \Zend\View\Helper\HeadLink
 {
     /**
      * Create HTML link element from data item
@@ -44,27 +46,17 @@ class VuFind_Theme_Root_Helper_HeadLink extends Zend_View_Helper_HeadLink
      *
      * @return string
      */
-    public function itemToString(stdClass $item)
+    public function itemToString(\stdClass $item)
     {
         // Normalize href to account for themes, then call the parent class:
-        $session = new Zend_Session_Namespace('Theme');
-        
-        $currentTheme = $session->currentTheme;
-        
-        while (!empty($currentTheme) &&
-            !file_exists(
-                APPLICATION_PATH .
-                "/themes/$currentTheme/css/{$item->href}"
-            )
-        ) {
-            $currentTheme = $session->allThemeInfo[$currentTheme]->extends;
-        }
+        $relPath = 'css/' . $item->href;
+        $currentTheme = ThemeTools::findContainingTheme($relPath);
 
         if (!empty($currentTheme)) {
-            $item->href = Zend_Controller_Front::getInstance()->getBaseUrl() .
-                "/themes/$currentTheme/css/" . $item->href;
+            $urlHelper = $this->getView()->plugin('url');
+            $item->href = $urlHelper('home') . "themes/$currentTheme/" . $relPath;
         }
-        
+
         return parent::itemToString($item);
     }
 }
\ No newline at end of file
diff --git a/themes/vufind/root/helpers/HeadScript.php b/themes/vufind/root/helpers/HeadScript.php
index 40b1cdb908065162586eb142f7144bc82dc9c83b..de4a235bde32d0a59fd79f3b66c18687f2b2cc34 100644
--- a/themes/vufind/root/helpers/HeadScript.php
+++ b/themes/vufind/root/helpers/HeadScript.php
@@ -25,6 +25,8 @@
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     http://vufind.org/wiki/building_a_recommendations_module Wiki
  */
+namespace VuFindThemes\Root\Helpers;
+use VuFind\Theme\Tools as ThemeTools;
 
 /**
  * Head script view helper (extended for VuFind's theme system)
@@ -35,40 +37,28 @@
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     http://vufind.org/wiki/building_a_recommendations_module Wiki
  */
-class VuFind_Theme_Root_Helper_HeadScript extends Zend_View_Helper_HeadScript
+class HeadScript extends \Zend\View\Helper\HeadScript
 {
     /**
      * Create script HTML
      *
-     * @param string $item        item to be string-ified
-     * @param string $indent      string to put before the escaped content
-     * @param string $escapeStart string to put before the returned content
-     * @param string $escapeEnd   string to put after the returned content
+     * @param mixed  $item        Item to convert
+     * @param string $indent      String to add before the item
+     * @param string $escapeStart Starting sequence
+     * @param string $escapeEnd   Ending sequence
      *
      * @return string
      */
     public function itemToString($item, $indent, $escapeStart, $escapeEnd)
     {
-        // Normalize href to account for themes, then call the parent class:
-        $session = new Zend_Session_Namespace('Theme');
+        // Normalize href to account for themes:
+        $relPath = 'js/' . $item->attributes['src'];
+        $currentTheme = ThemeTools::findContainingTheme($relPath);
 
-        $currentTheme = $session->currentTheme;
-
-        if (isset($item->attributes['src'])) {
-            while (!empty($currentTheme) &&
-                !file_exists(
-                    APPLICATION_PATH .
-                    "/themes/$currentTheme/js/{$item->attributes['src']}"
-                )
-            ) {
-                $currentTheme = $session->allThemeInfo[$currentTheme]->extends;
-            }
-
-            if (!empty($currentTheme)) {
-                $item->attributes['src']
-                    = Zend_Controller_Front::getInstance()->getBaseUrl() .
-                    "/themes/$currentTheme/js/" . $item->attributes['src'];
-            }
+        if (!empty($currentTheme)) {
+            $urlHelper = $this->getView()->plugin('url');
+            $item->attributes['src']
+                = $urlHelper('home') . "themes/$currentTheme/" . $relPath;
         }
 
         return parent::itemToString($item, $indent, $escapeStart, $escapeEnd);
diff --git a/themes/vufind/root/helpers/HeadThemeResources.php b/themes/vufind/root/helpers/HeadThemeResources.php
new file mode 100644
index 0000000000000000000000000000000000000000..7c146c506cd74da9878e85bd3b0366c38d173cbc
--- /dev/null
+++ b/themes/vufind/root/helpers/HeadThemeResources.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * View helper for loading theme-related resources in the header.
+ *
+ * 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  View_Helpers
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/building_a_recommendations_module Wiki
+ */
+namespace VuFindThemes\Root\Helpers;
+use VuFind\Theme\Tools as ThemeTools,
+    Zend\View\Helper\AbstractHelper;
+
+/**
+ * View helper for loading theme-related resources in the header.
+ *
+ * @category VuFind2
+ * @package  View_Helpers
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/building_a_recommendations_module Wiki
+ */
+class HeadThemeResources extends AbstractHelper
+{
+    /**
+     * Set up header items based on contents of theme resource container.
+     *
+     * @return void
+     */
+    public function __invoke()
+    {
+        $resourceContainer = ThemeTools::getResourceContainer();
+
+        // Load CSS:
+        $headLink = $this->getView()->plugin('headlink');
+        foreach ($resourceContainer->getCss() as $current) {
+            $parts = explode(':', $current);
+            $headLink()->appendStylesheet(
+                trim($parts[0]),
+                isset($parts[1]) ? trim($parts[1]) : 'all',
+                isset($parts[2]) ? trim($parts[2]) : false
+            );
+        }
+
+        // Load Javascript:
+        $headScript = $this->getView()->plugin('headscript');
+        foreach ($resourceContainer->getJs() as $current) {
+            $parts =  explode(':', $current);
+            $headScript()->appendFile(
+                trim($parts[0]),
+                'text/javascript',
+                isset($parts[1])
+                ? array('conditional' => trim($parts[1])) : array()
+            );
+        }
+
+        // If we have a favicon, load it now:
+        $favicon = $resourceContainer->getFavicon();
+        if (!empty($favicon)) {
+            $imageLink = $this->getView()->plugin('imagelink');
+            $headLink(array(
+                'href' => $imageLink($favicon),
+                'type' => 'image/x-icon', 'rel' => 'shortcut icon'
+            ));
+        }
+    }
+}
\ No newline at end of file
diff --git a/themes/vufind/root/helpers/ImageLink.php b/themes/vufind/root/helpers/ImageLink.php
index e3fb506291baa9a6ed3c8775c7408dd7d0431e4c..425d6f93ed0da210253dd9edf6d1dfb585cf94d9 100644
--- a/themes/vufind/root/helpers/ImageLink.php
+++ b/themes/vufind/root/helpers/ImageLink.php
@@ -25,6 +25,9 @@
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     http://www.vufind.org  Main Page
  */
+namespace VuFindThemes\Root\Helpers;
+use VuFind\Theme\Tools as ThemeTools,
+    Zend\View\Helper\AbstractHelper;
 
 /**
  * Image link view helper (extended for VuFind's theme system)
@@ -35,7 +38,7 @@
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     http://www.vufind.org  Main Page
  */
-class VuFind_Theme_Root_Helper_ImageLink extends Zend_View_Helper_Abstract
+class ImageLink extends AbstractHelper
 {
     /**
      * Returns an image path according the configured theme
@@ -44,28 +47,17 @@ class VuFind_Theme_Root_Helper_ImageLink extends Zend_View_Helper_Abstract
      *
      * @return string path, null if image not found
      */
-    public function imageLink($image)
+    public function __invoke($image)
     {
-        // Normalize href to account for themes, then call the parent class:
-        $session = new Zend_Session_Namespace('Theme');
+        // Normalize href to account for themes:
+        $relPath = 'images/' . $image;
+        $currentTheme = ThemeTools::findContainingTheme($relPath);
 
-        $currentTheme = $session->currentTheme;
-
-        while (!empty($currentTheme) &&
-            !file_exists(
-                APPLICATION_PATH .
-                "/themes/$currentTheme/images/{$image}"
-            )
-        ) {
-            $currentTheme = $session->allThemeInfo[$currentTheme]->extends;
-        }
-
-        if (!empty($currentTheme)) {
-            return Zend_Controller_Front::getInstance()->getBaseUrl() .
-                "/themes/$currentTheme/images/" . $image;
+        if (is_null($currentTheme)) {
+            return null;
         }
 
-        // Image not found!
-        return null;
+        $urlHelper = $this->getView()->plugin('url');
+        return $urlHelper('home') . "themes/$currentTheme/" . $relPath;
     }
 }
\ No newline at end of file
diff --git a/themes/vufind/root/theme.ini b/themes/vufind/root/theme.ini
index f56efd36e2fe5ad6c8e94a81717e092d4f6085d2..852dcf465fe43dd55495a7c6e74b79bb6b9570d0 100644
--- a/themes/vufind/root/theme.ini
+++ b/themes/vufind/root/theme.ini
@@ -1,6 +1,10 @@
 extends = false
 
 helper_namespace = "VuFindThemes\Root\Helpers"
+helpers_to_register[] = "HeadLink"
+helpers_to_register[] = "HeadScript"
+helpers_to_register[] = "HeadThemeResources"
+helpers_to_register[] = "ImageLink"
 helpers_to_register[] = "MobileUrl"
 helpers_to_register[] = "TransEsc"
 helpers_to_register[] = "Translate"