diff --git a/config/vufind/config.ini b/config/vufind/config.ini
index dc070497b2800758d512d0925d98adef6bf8d11b..dd0eebe0be9d9c91534916b4df83714df221edb9 100644
--- a/config/vufind/config.ini
+++ b/config/vufind/config.ini
@@ -613,7 +613,9 @@ coverimagesCache = true
 
 ; These settings control the image to display when no book cover is available.
 ; If makeDynamicCovers is not false and the GD library is installed, VuFind will draw
-; cover images on the fly. Possible values: false, solid, grid
+; cover images on the fly. See [DynamicCovers] below for more settings. If set to
+; a non-Boolean value, for legacy reasons, the makeDynamicCovers setting will
+; be used as the backgroundMode setting of [DynamicCovers] if that setting is unset.
 ;makeDynamicCovers = true
 
 ; Otherwise, you can use noCoverAvailableImage to specify a
@@ -662,14 +664,84 @@ authors         = Wikipedia
 ; You can select from Google, OpenLibrary, HathiTrust.  You should consult
 ; http://code.google.com/apis/books/branding.html before using Google Book Search.
 ; previews       = Google,OpenLibrary,HathiTrust
- 
+
+; This section controls the behavior of the cover generator when makeDynamicCovers
+; above is non-false.
+[DynamicCovers]
+; This controls the background layer of the generated image; options:
+; - solid: display a solid color
+; - grid: display a symmetrical random pattern seeded by title/callnumber
+;backgroundMode = grid
+
+; This controls the text layer of the generated image; options:
+; - default: display a title at the top and an author at the bottom
+; - initial: display only the first letter of the title as a stylized initial
+;textMode = default
+
+; Font files specified here should exist in the css/font subdirectory of a theme.
+; Some options are available by default inside the root theme.
+;authorFont = "Roboto-Light.ttf"
+;titleFont = "RobotoCondensed-Bold.ttf"
+
+; In 'default' textMode, covers are generated using title and author name; VuFind
+; will try to display everything by doing the following: break the title into
+; lines, and if the title is too long (more than maxTitleLines lines), it will
+; display ellipses at the last line.
+;
+; All text will be drawn using the specified textAlign alignment value using the
+; relevant titleFontSize or authorFontSize setting, except that author names will
+; be reduced to the minAuthorFontSize option if needed, and if that doesn't make
+; it fit, text will be aligned left and truncated.
+;
+; When using 'initial' textMode, maxTitleLines and author-related settings are
+; ignored as they do not apply.
+;textAlign = center
+;titleFontSize = 9
+;authorFontSize = 8
+;minAuthorFontSize = 7
+;maxTitleLines = 4
+
+; All color options support the same basic set of values:
+; - The 16 named colors from HTML4
+; - Arbitrary HTML hex colors in the form #RRGGBB (e.g. #FFFF00 for yellow)
+; Some color options also support additional options.
+; - authorFillColor,titleFillColor: the main color used
+; - authorBorderColor,titleBorderColor: the color used to make a border; "none" is
+;   a legal option in addition to colors.
+; - baseColor: When using grid backgrounds, you may also choose a base color drawn
+;   beneath the grid. Default is white.
+; - accentColor: When using solid backgrounds, this is the background color; when
+;   using grid backgrounds, this is the color of the grid pattern beneath the text.
+;   You may set this to "random" to select a random color seeded with text from
+;   the cover and adjusted with the "lightness" and "saturation" settings below.
+;titleFillColor = black
+;titleBorderColor = none
+;authorFillColor = white
+;authorBorderColor = black
+;baseColor = white
+;accentColor = random
+; Note: lightness and saturation are only used when accentColor = random. Legal
+; ranges are 0-255 for each value.
+;lightness = 220
+;saturation = 80
+
+; These settings control the size of the image -- if size is a single number, a
+; square will be created; if it is a string containing an "x" (i.e. 160x190) it
+; defines a WxH rectangle. wrapWidth constrains the text size (and must be no
+; larger than the width of the canvas). topPadding and bottomPadding push the
+; text away from the edges of the canvas.
+;size = 128
+;topPadding = 19
+;bottomPadding = 3
+;wrapWidth = 110
+
 ; This section is needed for Buchhandel.de cover loading. You need an authentication
 ; token. It may also be necessary to customize your templates in order to comply with
 ; terms of service; please look at http://info.buchhandel.de/handbuch_links for
 ; details before turning this on.
 [Buchhandel]
 url = "https://api.vlb.de/api/v1/cover/"
-; token = "XXXXXX-XXXX-XXXXX-XXXXXXXXXXXX" 
+; token = "XXXXXX-XXXX-XXXXX-XXXXXXXXXXXX"
 
 ; Possible HathiRights options = pd,ic,op,orph,und,umall,ic-world,nobody,pdus,cc-by,cc-by-nd,
 ; cc-by-nc-nd,cc-by-nc,cc-by-nc-sa,cc-by-sa,orphcand,cc-zero,und-world,icus
@@ -1041,7 +1113,7 @@ era             = true      ; allow browsing of era subdivisions
 ; <result_limit> most popular entries -- it only affects display order.
 ;alphabetical_order = true
 
-; This section controls the availability of export methods. 
+; This section controls the availability of export methods.
 ;
 ; Each entry may be a comma-separated list of contexts in which the export
 ; option will be presented. Valid options:
diff --git a/module/VuFind/src/VuFind/Cover/Generator.php b/module/VuFind/src/VuFind/Cover/Generator.php
index f75268d4b13a9246d2319ad2e5f93f382b85f83f..0e571df7eff4eebd7c817186773a0de8ab1ea1be 100644
--- a/module/VuFind/src/VuFind/Cover/Generator.php
+++ b/module/VuFind/src/VuFind/Cover/Generator.php
@@ -26,7 +26,6 @@
  * @link     http://vufind.org/wiki/use_of_external_content Wiki
  */
 namespace VuFind\Cover;
-use VuFindCode\ISBN, Zend\Log\LoggerInterface, ZendService\Amazon\Amazon;
 
 /**
  * Dynamic Book Cover Generator
@@ -45,14 +44,62 @@ class Generator
      * @var array
      */
     protected $settings = [];
+
+    /**
+     * Base color used to fill initially created image.
+     *
+     * @var int
+     */
+    protected $baseColor;
+
+    /**
+     * Title's fill color
+     *
+     * @var int
+     */
+    protected $titleFillColor;
+
+    /**
+     * Title's border color
+     *
+     * @var int
+     */
+    protected $titleBorderColor;
+
+    /**
+     * Author's fill color
+     *
+     * @var int
+     */
+    protected $authorFillColor;
+
+    /**
+     * Author's border color
+     *
+     * @var int
+     */
+    protected $authorBorderColor;
+
+    /**
+     * Base for image
+     *
+     * @var resource
+     */
+    protected $im;
+
     /**
-     * Reserved color
+     * Width of image (pixels)
+     *
+     * @var int
      */
-    protected $black;
+    protected $width;
+
     /**
-     * Reserved color
+     * Height of image (pixels)
+     *
+     * @var int
      */
-    protected $white;
+    protected $height;
 
     /**
      * Constructor
@@ -64,18 +111,27 @@ class Generator
     {
         $this->themeTools = $themeTools;
         $default = [
-            'mode'         => 'grid',
+            'backgroundMode' => 'grid',
+            'textMode' => 'default',
             'authorFont'   => 'DroidSerif-Bold.ttf',
-            'fontSize'     => 7,
+            'titleFontSize' => 7,
+            'authorFontSize' => 6,
             'lightness'    => 220,
-            'maxLines'     => 5,
-            'minFontSize'  => 5,
+            'maxTitleLines' => 5,
+            'minAuthorFontSize' => 5,
             'saturation'   => 80,
             'size'         => 84,
             'textAlign'    => 'center',
             'titleFont'    => 'DroidSerif-Bold.ttf',
             'topPadding'   => 19,
+            'bottomPadding' => 3,
             'wrapWidth'    => 80,
+            'titleFillColor' => 'black',
+            'titleBorderColor' => 'none',
+            'authorFillColor' => 'white',
+            'authorBorderColor' => 'black',
+            'baseColor' => 'white',
+            'accentColor' => 'random',
         ];
         foreach ($settings as $i => $setting) {
             $default[$i] = $setting;
@@ -83,146 +139,293 @@ class Generator
         $default['authorFont'] = $this->fontPath($default['authorFont']);
         $default['titleFont']  = $this->fontPath($default['titleFont']);
         $this->settings = (object) $default;
+        $this->initImage();
+        $this->initColors();
     }
 
     /**
-     * Generates a dynamic cover image from elements of the book
+     * Initialize colors to be used in the image.
      *
-     * @param string $title      Title of the book
-     * @param string $author     Author of the book
-     * @param string $callnumber Callnumber of the book
+     * @return void
+     */
+    protected function initColors()
+    {
+        $this->baseColor = $this->getColor($this->settings->baseColor);
+        $this->titleFillColor = $this->getColor($this->settings->titleFillColor);
+        $this->titleBorderColor = $this->getColor($this->settings->titleBorderColor);
+        $this->authorFillColor = $this->getColor($this->settings->authorFillColor);
+        $this->authorBorderColor = $this->getColor(
+            $this->settings->authorBorderColor
+        );
+    }
+
+    /**
+     * Initialize the image in the object.
      *
-     * @return string contents of image file
+     * @return void
      */
-    public function generate($title, $author, $callnumber = null)
+    protected function initImage()
     {
-        if ($this->settings->mode == 'solid') {
-            return $this->generateSolid($title, $author, $callnumber);
+        // Create image
+        $parts = explode('x', strtolower($this->settings->size));
+        if (count($parts) < 2) {
+            $this->width = $this->height = $parts[0];
         } else {
-            return $this->generateGrid($title, $author, $callnumber);
+            list($this->width, $this->height) = $parts;
+        }
+        if (!($this->im = imagecreate($this->width, $this->height))) {
+            throw new \Exception("Cannot Initialize new GD image stream");
         }
     }
 
     /**
-     * Generates a solid color background, ala Google
+     * Clear the resources associated with the image in the object.
+     *
+     * @return void
+     */
+    protected function destroyImage()
+    {
+        imagedestroy($this->im);
+    }
+
+    /**
+     * Render the contents of the image in the object to a PNG; return as string.
+     *
+     * @return string
+     */
+    protected function renderPng()
+    {
+        ob_start();
+        imagepng($this->im);
+        $img = ob_get_contents();
+        ob_end_clean();
+        return $img;
+    }
+
+    /**
+     * Check and allocates color
+     *
+     * @param string $color Legal color name from HTML4
+     *
+     * @return allocated color
+     */
+    protected function getColor($color)
+    {
+        switch (strtolower($color)){
+        case 'black':
+            return imagecolorallocate($this->im, 0, 0, 0);
+        case 'silver':
+            return imagecolorallocate($this->im, 192, 192, 192);
+        case 'gray':
+            return imagecolorallocate($this->im, 128, 128, 128);
+        case 'white':
+            return imagecolorallocate($this->im, 255, 255, 255);
+        case 'maroon':
+            return imagecolorallocate($this->im, 128, 0, 0);
+        case 'red':
+            return imagecolorallocate($this->im, 255, 0, 0);
+        case 'purple':
+            return imagecolorallocate($this->im, 128, 0, 128);
+        case 'fuchsia':
+            return imagecolorallocate($this->im, 255, 0, 255);
+        case 'green':
+            return imagecolorallocate($this->im, 0, 128, 0);
+        case 'lime':
+            return imagecolorallocate($this->im, 0, 255, 0);
+        case 'olive':
+            return imagecolorallocate($this->im, 128, 128, 0);
+        case 'yellow':
+            return imagecolorallocate($this->im, 255, 255, 0);
+        case 'navy':
+            return imagecolorallocate($this->im, 0, 0, 128);
+        case 'blue':
+            return imagecolorallocate($this->im, 0, 0, 255);
+        case 'teal':
+            return imagecolorallocate($this->im, 0, 128, 128);
+        case 'aqua':
+            return imagecolorallocate($this->im, 0, 255, 255);
+        default:
+            if (substr($color, 0, 1) == '#' && strlen($color) == 7) {
+                $r = hexdec(substr($color, 1, 2));
+                $g = hexdec(substr($color, 3, 2));
+                $b = hexdec(substr($color, 5, 2));
+                return imagecolorallocate($this->im, $r, $g, $b);
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Generates a dynamic cover image from elements of the item
      *
      * @param string $title      Title of the book
      * @param string $author     Author of the book
      * @param string $callnumber Callnumber of the book
      *
      * @return string contents of image file
+     */
+    public function generate($title, $author, $callnumber = null)
+    {
+        // Generate seed from callnumber, title back up
+        $seed = $this->createSeed($title, $callnumber);
+
+        // Build the image
+        $this->drawBackgroundLayer($seed);
+        $this->drawTextLayer($title, $author);
+
+        // Render the image
+        $png = $this->renderPng();
+        $this->destroyImage();
+        return $png;
+    }
+
+    /**
+     * Draw the background behind the text.
+     *
+     * @param int $seed Seed value
      *
-     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     * @return void
      */
-    protected function generateSolid($title, $author, $callnumber)
+    protected function drawBackgroundLayer($seed)
     {
-        $half = $this->settings->size / 2;
-        // Create image
-        if (!($im = imagecreate($this->settings->size, $this->settings->size))) {
-            throw new \Exception("Cannot Initialize new GD image stream");
+        // Construct a method name using the mode setting; if the method is not
+        // defined, use the default drawGridBackground().
+        $mode = ucwords(strtolower($this->settings->backgroundMode));
+        $method = "draw{$mode}Background";
+        return method_exists($this, $method)
+            ? $this->$method($seed) : $this->drawGridBackground($seed);
+    }
+
+    /**
+     * Position the text on the image.
+     *
+     * @param string $title  Title of the book
+     * @param string $author Author of the book
+     *
+     * @return void
+     */
+    protected function drawTextLayer($title, $author)
+    {
+        // Construct a method name using the mode setting; if the method is not
+        // defined, use the default drawGridBackground().
+        $mode = ucwords(strtolower($this->settings->textMode));
+        $method = "draw{$mode}Text";
+        return method_exists($this, $method)
+            ? $this->$method($title, $author)
+            : $this->drawDefaultText($title, $author);
+    }
+
+    /**
+     * Position the text on the image using default rules.
+     *
+     * @param string $title  Title of the book
+     * @param string $author Author of the book
+     *
+     * @return void
+     */
+    protected function drawDefaultText($title, $author)
+    {
+        if (null !== $title) {
+            $this->drawTitle($title, $this->height / 8);
+        }
+        if (null !== $author) {
+            $this->drawAuthor($author);
         }
-        // this->white backdrop
-        $this->white = imagecolorallocate($im, 255, 255, 255);
-        // this->black
-        $this->black = imagecolorallocate($im, 0, 0, 0);
+    }
 
-        // Generate seed from callnumber, title back up
-        $seed = $this->createSeed($title, $callnumber);
-        // Number to color, hsb to control saturation and lightness
-        $color = $this->makeHSBColor(
-            $im,
-            $seed % 256,
-            $this->settings->saturation,
-            $this->settings->lightness
+    /**
+     * Position the text on the image using "initials" rules.
+     *
+     * @param string $title  Title of the book
+     * @param string $author Author of the book
+     *
+     * @return void
+     */
+    protected function drawInitialText($title, $author)
+    {
+        // Get the first letter of title or author...
+        $initial = mb_substr($title . $author, 0, 1, 'UTF-8');
+
+        // Get the height of a character with no descenders:
+        $heightWithoutDescenders = $this->textHeight(
+            'O', $this->settings->titleFont, $this->settings->titleFontSize
         );
 
-        // Fill solid color
-        imagefilledrectangle(
-            $im,
-            0,
-            0,
-            $this->settings->size,
-            $this->settings->size,
-            $color
+        // Get the height of the currently selected character:
+        $textHeight = $this->textHeight(
+            $initial, $this->settings->titleFont, $this->settings->titleFontSize
         );
 
+        // Draw the letter... Note that the way we are using $textHeight and
+        // $heightWithoutDescenders is something of a fudge driven by the fact
+        // that PHP measures text in total pixels, but positions text using the
+        // baseline (thus not accounting for descenders). To truly vertically
+        // center something, we need more information than we can get without
+        // using an extension or library to read more information from the font
+        // file. The formula here is not particularly well-informed but seems
+        // to produce acceptable results for many scenarios.
         $this->drawText(
-            $im,
-            strtoupper($title[0]),
-            $half,
-            $half + 28,
+            $initial,
+            $heightWithoutDescenders + ($this->height - $textHeight) / 2,
             $this->settings->titleFont,
-            60,
-            $this->white,
-            false,
-            'center'
+            $this->settings->titleFontSize,
+            $this->titleFillColor,
+            $this->titleBorderColor,
+            $this->settings->textAlign
         );
-
-        // Output png CHECK THE PARAM
-        ob_start();
-        imagepng($im);
-        $img = ob_get_contents();
-        ob_end_clean();
-
-        // Clear memory
-        imagedestroy($im);
-        // GTFO
-        return $img;
     }
 
     /**
-     * Generates a grid of colors as primary feature
+     * Generate an accent color from a seed value.
      *
-     * @param string $title      Title of the book
-     * @param string $author     Author of the book
-     * @param string $callnumber Callnumber of the book
+     * @param int $seed Seed value
      *
-     * @return string contents of image file
+     * @return int
      */
-    protected function generateGrid($title, $author, $callnumber)
+    protected function getAccentColor($seed)
     {
-        // Set up common variables
-        $half = $this->settings->size / 2;
-        $box  = $this->settings->size / 8;
-
-        // Create image
-        if (!($im = imagecreate($this->settings->size, $this->settings->size))) {
-            throw new \Exception("Cannot Initialize new GD image stream");
+        // Number to color, hsb to control saturation and lightness
+        if ($this->settings->accentColor == 'random') {
+            return $this->makeHSBColor(
+                $seed % 256,
+                $this->settings->saturation,
+                $this->settings->lightness
+            );
         }
-        // this->white backdrop
-        $this->white = imagecolorallocate($im, 255, 255, 255);
-        // this->black
-        $this->black = imagecolorallocate($im, 0, 0, 0);
+        return $this->getColor($this->settings->accentColor);
+    }
 
-        // Generate seed from callnumber, title back up
-        $seed = $this->createSeed($title, $callnumber);
-        // Number to color, hsb to control saturation and lightness
-        $grid_color = $this->makeHSBColor(
-            $im,
-            $seed % 256,
-            $this->settings->saturation,
-            $this->settings->lightness
+    /**
+     * Generates a solid color background, ala Google
+     *
+     * @param int $seed Seed value
+     *
+     * @return void
+     */
+    protected function drawSolidBackground($seed)
+    {
+        // Fill solid color
+        imagefilledrectangle(
+            $this->im,
+            0,
+            0,
+            $this->width,
+            $this->height,
+            $this->getAccentColor($seed)
         );
-        // Render the grid
-        $pattern = $this->createPattern($seed);
-        $this->render($pattern, $im, $grid_color, $half, $box);
-
-        if (null !== $title) {
-            $this->drawTitle($im, $title, $box);
-        }
-        if (null !== $author) {
-            $this->drawAuthor($im, $author);
-        }
-        // Output png CHECK THE PARAM
-        ob_start();
-        imagepng($im);
-        $img = ob_get_contents();
-        ob_end_clean();
+    }
 
-        // Clear memory
-        imagedestroy($im);
-        // GTFO
-        return $img;
+    /**
+     * Generates a grid of colors as primary feature
+     *
+     * @param int $seed Seed value
+     *
+     * @return void
+     */
+    protected function drawGridBackground($seed)
+    {
+        // Render the grid
+        $this->renderGrid($this->createPattern($seed), $this->getAccentColor($seed));
     }
 
     /**
@@ -231,7 +434,7 @@ class Generator
      * @param string $title      Title of the book
      * @param string $callnumber Callnumber of the book
      *
-     * @return integer unique number for this record
+     * @return int unique number for this record
      */
     protected function createSeed($title, $callnumber)
     {
@@ -254,7 +457,7 @@ class Generator
     /**
      * Turn number into pattern
      *
-     * @param integer $seed Seed used to generate the pattern
+     * @param int $seed Seed used to generate the pattern
      *
      * @return string binary string describing a quarter of the pattern
      */
@@ -283,13 +486,12 @@ class Generator
     /**
      * Render title in wrapped, black text with white border
      *
-     * @param GCImage $im         Image object to render to
-     * @param string  $title      Title to write
-     * @param integer $lineHeight Pixels we move down each line
+     * @param string $title      Title to write
+     * @param int    $lineHeight Pixels we move down each line
      *
      * @return void
      */
-    protected function drawTitle($im, $title, $lineHeight)
+    protected function drawTitle($title, $lineHeight)
     {
         $words = explode(' ', $title);
         // Wrap words into image
@@ -297,56 +499,52 @@ class Generator
         $line = '';
         $lineCount = 0;
         $i = 0;
-        while ($i < count($words) && $lineCount < $this->settings->maxLines - 1) {
+        while ($i < count($words)
+            && $lineCount < $this->settings->maxTitleLines - 1
+        ) {
             $pline = $line;
             // Format
-            $text = strtoupper($words[$i]);
+            $text = $words[$i];
             $line .= $text . ' ';
             $textWidth = $this->textWidth(
-                $line,
+                rtrim($line, ' '),
                 $this->settings->titleFont,
-                $this->settings->fontSize
+                $this->settings->titleFontSize
             );
             if ($textWidth > $this->settings->wrapWidth) {
                 // Print black with white border
                 $this->drawText(
-                    $im,
-                    $pline,
-                    3,
+                    rtrim($pline, ' '),
                     $this->settings->topPadding + $lineHeight * $lineCount,
                     $this->settings->titleFont,
-                    $this->settings->fontSize,
-                    $this->black,
-                    $this->white
+                    $this->settings->titleFontSize,
+                    $this->titleFillColor,
+                    $this->titleBorderColor
                 );
-                $line = $text . " ";
+                $line = $text . ' ';
                 $lineCount++;
             }
             $i++;
         }
         // Print the last words
         $this->drawText(
-            $im,
-            $line,
-            3,
+            rtrim($line, ' '),
             $this->settings->topPadding + $lineHeight * $lineCount,
             $this->settings->titleFont,
-            $this->settings->fontSize,
-            $this->black,
-            $this->white
+            $this->settings->titleFontSize,
+            $this->titleFillColor,
+            $this->titleBorderColor
         );
         // Add ellipses if we've truncated
         if ($i < count($words) - 1) {
             $this->drawText(
-                $im,
                 '...',
-                5,
                 $this->settings->topPadding
-                + $this->settings->maxLines * $lineHeight,
+                + $this->settings->maxTitleLines * $lineHeight,
                 $this->settings->titleFont,
-                $this->settings->fontSize + 1,
-                $this->black,
-                $this->white
+                $this->settings->titleFontSize + 1,
+                $this->titleFillColor,
+                $this->titleBorderColor
             );
         }
     }
@@ -354,41 +552,39 @@ class Generator
     /**
      * Render author at bottom in wrapped, white text with black border
      *
-     * @param GCImage $im     Image object to render to
-     * @param string  $author Author to write
+     * @param string $author Author to write
      *
      * @return void
      */
-    protected function drawAuthor($im, $author)
+    protected function drawAuthor($author)
     {
         // Scale author to fit by incrementing fontsizes down
-        $fontSize = $this->settings->fontSize;
+        $fontSize = $this->settings->authorFontSize + 1;
         do {
-            $txtWidth = $this->textWidth(
+            $fontSize--;
+            $textWidth = $this->textWidth(
                 $author,
-                $this->settings->titleFont,
+                $this->settings->authorFont,
                 $fontSize
             );
-            $fontSize--;
-        } while ($txtWidth > $this->settings->wrapWidth);
-        // white text, black outline
-        $fontSize = ++$fontSize < $this->settings->minFontSize
-            ? $this->settings->fontSize
-            : $fontSize;
+        } while ($textWidth > $this->settings->wrapWidth &&
+              $fontSize > $this->settings->minAuthorFontSize
+          );
         // Too small to read? Align left
-        $alignment = $fontSize < $this->settings->minFontSize
-            ? 'left'
-            : null;
+        $textWidth = $this->textWidth(
+            $author,
+            $this->settings->authorFont,
+            $fontSize
+        );
+        $align = $textWidth > $this->width ? 'left' : null;
         $this->drawText(
-            $im,
             $author,
-            5,
-            $this->settings->size - 3,
+            $this->height - $this->settings->bottomPadding,
             $this->settings->authorFont,
             $fontSize,
-            $this->white,
-            $this->black,
-            $alignment
+            $this->authorFillColor,
+            $this->authorBorderColor,
+            $align
         );
     }
 
@@ -414,137 +610,163 @@ class Generator
      * @param string $font Full font path
      * @param string $size Size of the font
      *
-     * @return string file path
+     * @return int
      */
     protected function textWidth($text, $font, $size)
     {
         $p = imagettfbbox($size, 0, $font, $text);
-        return $p[2] - $p[0] - 4;
+        return $p[2] - $p[0];
+    }
+
+    /**
+     * Returns the height a string would render to
+     *
+     * @param string $text Text to test
+     * @param string $font Full font path
+     * @param string $size Size of the font
+     *
+     * @return int
+     */
+    protected function textHeight($text, $font, $size)
+    {
+        $p = imagettfbbox($size, 0, $font, $text);
+        return $p[1] - $p[5];
     }
 
     /**
      * Simulate outlined text
      *
-     * @param GCImage $im       Image object
-     * @param string  $text     Text to render
-     * @param integer $x        Left position
-     * @param integer $y        Top position
-     * @param string  $font     Full path to font
-     * @param integer $fontSize Size of the font
-     * @param GCColor $mcolor   Main text color
-     * @param GCColor $scolor   Secondary border color
-     * @param string  $align    'left','center','right'
+     * @param string $text     Text to render
+     * @param int    $y        Top position
+     * @param string $font     Full path to font
+     * @param int    $fontSize Size of the font
+     * @param int    $mcolor   Main text color
+     * @param int    $scolor   Secondary border color
+     * @param string $align    'left','center','right'
      *
      * @return void
      */
-    protected function drawText($im, $text, $x, $y,
-        $font, $fontSize, $mcolor, $scolor = false, $align = null
+    protected function drawText($text, $y, $font, $fontSize, $mcolor,
+        $scolor = false, $align = null
     ) {
-        $txtWidth = $this->textWidth(
+        // If the wrap width is smaller than the image width, we want to account
+        // for this when right or left aligning to maintain padding on the image.
+        $wrapGap = ($this->width - $this->settings->wrapWidth) / 2;
+
+        $textWidth = $this->textWidth(
             $text,
-            $this->settings->titleFont,
-            $this->settings->fontSize
+            $font,
+            $fontSize
         );
-        if ($txtWidth > $this->settings->size) {
+        if ($textWidth > $this->width) {
             $align = 'left';
-            $x = 0;
+            $wrapGap = 0; // kill wrap gap to maximize text fit
         }
         if (null == $align) {
             $align = $this->settings->textAlign;
         }
+        if ($align == 'left') {
+            $x = $wrapGap;
+        }
         if ($align == 'center') {
-            $p = imagettfbbox($fontSize, 0, $this->settings->titleFont, $text);
-            $txtWidth = $p[2] - $p[0] - 4;
-            $x = ($this->settings->size - $txtWidth) / 2;
+            $x = ($this->width - $textWidth) / 2;
         }
         if ($align == 'right') {
-            $p = imagettfbbox($fontSize, 0, $this->settings->titleFont, $text);
-            $txtWidth = $p[2] - $p[0] - 4;
-            $x = $this->settings->size - $txtWidth - $x;
+            $x = $this->width - ($textWidth + $wrapGap);
         }
 
         // Generate 5 lines of text, 4 offset in a border color
         if ($scolor) {
-            imagettftext($im, $fontSize, 0, $x,   $y + 1, $scolor, $font, $text);
-            imagettftext($im, $fontSize, 0, $x,   $y - 1, $scolor, $font, $text);
-            imagettftext($im, $fontSize, 0, $x + 1, $y,   $scolor, $font, $text);
-            imagettftext($im, $fontSize, 0, $x - 1, $y,   $scolor, $font, $text);
+            imagettftext(
+                $this->im, $fontSize, 0, $x,   $y + 1, $scolor, $font, $text
+            );
+            imagettftext(
+                $this->im, $fontSize, 0, $x,   $y - 1, $scolor, $font, $text
+            );
+            imagettftext(
+                $this->im, $fontSize, 0, $x + 1, $y,   $scolor, $font, $text
+            );
+            imagettftext(
+                $this->im, $fontSize, 0, $x - 1, $y,   $scolor, $font, $text
+            );
         }
         // 1 centered in main color
-        imagettftext($im, $fontSize, 0, $x,   $y,   $mcolor, $font, $text);
+        imagettftext($this->im, $fontSize, 0, $x,   $y,   $mcolor, $font, $text);
     }
 
     /**
      * Convert 16 long binary string to 8x8 color grid
      * Reflects vertically and horizontally
      *
-     * @param string  $bc    Binary string of pattern
-     * @param GCImage $im    Image object
-     * @param GCColor $color Fill color
-     * @param integer $half  Half the size, shortcut for math
-     * @param integer $box   Box size
+     * @param string $bc    Binary string of pattern
+     * @param int    $color Fill color
      *
      * @return void
      */
-    protected function render($bc, $im, $color, $half, $box)
+    protected function renderGrid($bc, $color)
     {
+        $halfWidth = $this->width / 2;
+        $halfHeight = $this->height / 2;
+        $boxWidth  = $this->width / 8;
+        $boxHeight = $this->height / 8;
+
         $bc = str_split($bc);
         for ($k = 0;$k < 4;$k++) {
-            $x = $k % 2   ? $half : $half - $box;
-            $y = $k / 2 < 1 ? $half : $half - $box;
-            $u = $k % 2   ? $box : -$box;
-            $v = $k / 2 < 1 ? $box : -$box;
+            $x = $k % 2   ? $halfWidth : $halfWidth - $boxWidth;
+            $y = $k / 2 < 1 ? $halfHeight : $halfHeight - $boxHeight;
+            $u = $k % 2   ? $boxWidth : -$boxWidth;
+            $v = $k / 2 < 1 ? $boxHeight : -$boxHeight;
             for ($i = 0;$i < 16;$i++) {
                 if ($bc[$i] == "1") {
                     imagefilledrectangle(
-                        $im, $x, $y, $x + $box - 1, $y + $box - 1, $color
+                        $this->im, $x, $y,
+                        $x + $boxWidth - 1, $y + $boxHeight - 1, $color
                     );
                 }
                 $x += $u;
-                if ($x >= $this->settings->size || $x < 0) {
-                    $x = $k % 2 ? $half : $half - $box;
+                if ($x >= $this->width || $x < 0) {
+                    $x = $k % 2 ? $halfWidth : $halfWidth - $boxWidth;
                     $y += $v;
                 }
             }
         }
-        //imagefilledrectangle($im,0,$size-11,$size-1,$size,$color);
+        //imagefilledrectangle($this->im,0,$size-11,$size-1,$size,$color);
     }
 
     /**
      * Using HSB allows us to control the contrast while allowing randomness
      *
-     * @param GCImage $im Image object
-     * @param integer $h  Hue (0-255)
-     * @param integer $s  Saturation (0-100)
-     * @param integer $v  Lightness (0-100)
+     * @param int $h Hue (0-255)
+     * @param int $s Saturation (0-255)
+     * @param int $v Lightness (0-255)
      *
-     * @return GCColor
+     * @return int
      */
-    protected function makeHSBColor($im, $h, $s, $v)
+    protected function makeHSBColor($h, $s, $v)
     {
         $s /= 256.0;
         if ($s == 0.0) {
-            return imagecolorallocate($im, $v, $v, $v);
+            return imagecolorallocate($this->im, $v, $v, $v);
         }
         $h /= (256.0 / 6.0);
         $i = floor($h);
         $f = $h - $i;
-        $p = (integer)($v * (1.0 - $s));
-        $q = (integer)($v * (1.0 - $s * $f));
-        $t = (integer)($v * (1.0 - $s * (1.0 - $f)));
+        $p = (int)($v * (1.0 - $s));
+        $q = (int)($v * (1.0 - $s * $f));
+        $t = (int)($v * (1.0 - $s * (1.0 - $f)));
         switch($i) {
         case 0:
-            return imagecolorallocate($im, $v, $t, $p);
+            return imagecolorallocate($this->im, $v, $t, $p);
         case 1:
-            return imagecolorallocate($im, $q, $v, $p);
+            return imagecolorallocate($this->im, $q, $v, $p);
         case 2:
-            return imagecolorallocate($im, $p, $v, $t);
+            return imagecolorallocate($this->im, $p, $v, $t);
         case 3:
-            return imagecolorallocate($im, $p, $q, $v);
+            return imagecolorallocate($this->im, $p, $q, $v);
         case 4:
-            return imagecolorallocate($im, $t, $p, $v);
+            return imagecolorallocate($this->im, $t, $p, $v);
         default:
-            return imagecolorallocate($im, $v, $p, $q);
+            return imagecolorallocate($this->im, $v, $p, $q);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/module/VuFind/src/VuFind/Cover/Loader.php b/module/VuFind/src/VuFind/Cover/Loader.php
index 15fe1242e1ef20683b236d65f7faf06ce5953284..a22682899556706d12935a850bbeff4355602ede 100644
--- a/module/VuFind/src/VuFind/Cover/Loader.php
+++ b/module/VuFind/src/VuFind/Cover/Loader.php
@@ -156,10 +156,14 @@ class Loader extends \VuFind\ImageLoader
      */
     public function getCoverGenerator()
     {
-        return new \VuFind\Cover\Generator(
-            $this->themeTools,
-            ['mode' => $this->config->Content->makeDynamicCovers]
-        );
+        $settings = isset($this->config->DynamicCovers)
+            ? $this->config->DynamicCovers->toArray() : [];
+        if (!isset($settings['backgroundMode'])
+            && isset($this->config->Content->makeDynamicCovers)
+        ) {
+            $settings['backgroundMode'] = $this->config->Content->makeDynamicCovers;
+        }
+        return new \VuFind\Cover\Generator($this->themeTools, $settings);
     }
 
     /**
diff --git a/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php b/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php
index 150c7136e2ad66d33b4ab29d66b7bf07f3414f37..5801b1098c9238286fd49de0af8cbc2c57bdf841 100644
--- a/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php
+++ b/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php
@@ -186,9 +186,9 @@ class DeduplicationListener
         $config = $this->serviceLocator->get('VuFind\Config');
         $searchConfig = $config->get($this->searchConfig);
         $dataSourceConfig = $config->get($this->dataSourceConfig);
-        $recordSources = isset($searchConfig->Records->sources)
-            ? $searchConfig->Records->sources
-            : '';
+        $recordSources = !empty($searchConfig->Records->sources)
+            ? explode(',', $searchConfig->Records->sources)
+            : [];
         $sourcePriority = $this->determineSourcePriority($recordSources);
         $params = $event->getParam('params');
         $buildingPriority = $this->determineBuildingPriority($params);
@@ -212,7 +212,7 @@ class DeduplicationListener
                 $localPriority = null;
                 list($source) = explode('.', $localId, 2);
                 // Ignore ID if source is not in the list of allowed record sources:
-                if (!empty($sourcePriority) && !isset($sourcePriority[$source])) {
+                if ($recordSources && !in_array($source, $recordSources)) {
                     continue;
                 }
                 if (!empty($buildingPriority)) {
@@ -304,10 +304,10 @@ class DeduplicationListener
      * two parameters are unused in this default method, but they may be useful for
      * custom behavior in subclasses.
      *
-     * @param array  $localRecordData Local record data
-     * @param array  $dedupRecordData Dedup record data
-     * @param string $recordSources   List of active record sources, empty if all
-     * @param array  $sourcePriority  Array of source priorities keyed by source id
+     * @param array $localRecordData Local record data
+     * @param array $dedupRecordData Dedup record data
+     * @param array $recordSources   List of active record sources, empty if all
+     * @param array $sourcePriority  Array of source priorities keyed by source id
      *
      * @return array Local record data
      *
@@ -323,7 +323,7 @@ class DeduplicationListener
     /**
      * Function that determines the priority for sources
      *
-     * @param object $recordSources Record sources defined in searches.ini
+     * @param array $recordSources Record sources defined in searches.ini
      *
      * @return array Array keyed by source with priority as the value
      */
@@ -332,7 +332,7 @@ class DeduplicationListener
         if (empty($recordSources)) {
             return [];
         }
-        return array_flip(explode(',', $recordSources));
+        return array_flip($recordSources);
     }
 
     /**
diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php
index 140fbb6c0dbdc4c5fcf2a0dc626500b1491241ef..3efcb69f04d5502d924241250e78835d6fded8c4 100644
--- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php
+++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php
@@ -354,16 +354,6 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase
             $this->findCss($page, 'a.title')->getText()
         );
 
-        /* TODO: uncomment this test when Bootstrap bug stops making it fail...
-         * Specifically, at the time of this writing, if you click the dropdown
-         * menu to get "Yes" and "No" options, then click "No," then the next
-         * time you attempt to pop down the dropdown, it quickly closes itself
-         * before "Yes" can be clicked. This appears to be a bug on the Bootstrap
-         * side affecting Firefox only. Once it is resolved, we should add this
-         * check to the test to prevent regressions... but for now better to leave
-         * this commented out so a bug beyond our control does not break VuFind's
-         * test suite.
-         * 
         // Click cancel but bail out with no... item should still be there.
         $this->findCss($page, '#cancelAll')->click();
         $this->clickButtonGroupLink($page, 'No');
@@ -373,7 +363,6 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase
             . ' the journal of the Institute for Rational-Emotive Therapy.',
             $this->findCss($page, 'a.title')->getText()
         );
-         */
 
         // Now cancel for real:
         $this->findCss($page, '#cancelAll')->click();
diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php
index 7e09d0a965af7c3580cdae421cb61e1ac597f29e..667ab20db9aaccb2ff16a65c3f98ff08ed0df36d 100644
--- a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php
+++ b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php
@@ -303,10 +303,7 @@ class Connector implements \Zend\Log\LoggerAwareInterface
             }
 
             // QUERYSTRING: indx (start record)
-            $recordStart = $args["pageNumber"];
-            if ($recordStart != 1) {
-                $recordStart = ($recordStart * 10) + 1;
-            }
+            $recordStart = ($args["pageNumber"] - 1) * $args['limit'] + 1;
             $qs[] = "indx=$recordStart";
 
             // TODO: put bulksize in conf file?  set a reasonable cap...
diff --git a/themes/bootstrap3/css/vendor/bootstrap-accessibility.css b/themes/bootstrap3/css/vendor/bootstrap-accessibility.css
index 651d16877cd8b519f9f71e6bb0304e33612e902d..1f00c0b52e785b4350bd5ebe85fda6f7f0106efc 100644
--- a/themes/bootstrap3/css/vendor/bootstrap-accessibility.css
+++ b/themes/bootstrap3/css/vendor/bootstrap-accessibility.css
@@ -1 +1 @@
-.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-indicators li,.carousel-indicators li.active{height:18px;width:18px;border-width:2px;position:relative;box-shadow:0px 0px 0px 1px #808080;-moz-boxshadow:0px 0px 0px 1px #808080;-webkit-boxshadow:0px 0px 0px 1px #808080}.carousel-indicators.active li{background-color:rgba(100,149,253,0.6)}.carousel-indicators.active li.active{background-color:white}.carousel-tablist-highlight{display:block;position:absolute;outline:2px solid transparent;background-color:transparent;box-shadow:0px 0px 0px 1px transparent;-moz-boxshadow:0px 0px 0px 1px transparent;-webkit-boxshadow:0px 0px 0px 1px transparent}.carousel-tablist-highlight.focus{outline:2px solid #6495ED;background-color:rgba(0,0,0,0.4)}a.carousel-control:focus{outline:2px solid #6495ED;background-image:linear-gradient(to right, transparent 0px, rgba(0,0,0,0.5) 100%);box-shadow:0px 0px 0px 1px #000000;-moz-boxshadow:0px 0px 0px 1px #000000;-webkit-boxshadow:0px 0px 0px 1px #000000}.carousel-pause-button{position:absolute;top:-30em;left:-300em;display:block}.carousel-pause-button.focus{top:0.5em;left:0.5em}.carousel:hover .carousel-caption,.carousel.contrast .carousel-caption{background-color:rgba(0,0,0,0.5);z-index:10}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824}
\ No newline at end of file
+.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-indicators li,.carousel-indicators li.active{height:18px;width:18px;border-width:2px;position:relative;box-shadow:0px 0px 0px 1px #808080}.carousel-indicators.active li{background-color:rgba(100,149,253,0.6)}.carousel-indicators.active li.active{background-color:white}.carousel-tablist-highlight{display:block;position:absolute;outline:2px solid transparent;background-color:transparent;box-shadow:0px 0px 0px 1px transparent}.carousel-tablist-highlight.focus{outline:2px solid #6495ED;background-color:rgba(0,0,0,0.4)}a.carousel-control:focus{outline:2px solid #6495ED;background-image:linear-gradient(to right, transparent 0px, rgba(0,0,0,0.5) 100%);box-shadow:0px 0px 0px 1px #000000}.carousel-pause-button{position:absolute;top:-30em;left:-300em;display:block}.carousel-pause-button.focus{top:0.5em;left:0.5em}.carousel:hover .carousel-caption,.carousel.contrast .carousel-caption{background-color:rgba(0,0,0,0.5);z-index:10}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824}
\ No newline at end of file
diff --git a/themes/bootstrap3/js/vendor/bootstrap-accessibility.min.js b/themes/bootstrap3/js/vendor/bootstrap-accessibility.min.js
index 05cdd444ec549bfa415db2dd20dd638fa3b35f3b..9d28a6d69c0c50e68a1aca6cc1052f311ea4d8dc 100644
--- a/themes/bootstrap3/js/vendor/bootstrap-accessibility.min.js
+++ b/themes/bootstrap3/js/vendor/bootstrap-accessibility.min.js
@@ -1,4 +1,4 @@
-/*! bootstrap-accessibility-plugin - v1.0.5 - 2015-12-01
+/*! bootstrap-accessibility-plugin - v1.0.5 - 2016-02-09
 * https://github.com/paypal/bootstrap-accessibility-plugin
-* Copyright (c) 2015 PayPal Accessibility Team; Licensed BSD */
-!function($){"use strict";var uniqueId=function(prefix){return(prefix||"ui-id")+"-"+Math.floor(1e3*Math.random()+1)},focusable=function(element,isTabIndexNotNaN){var map,mapName,img,nodeName=element.nodeName.toLowerCase();return"area"===nodeName?(map=element.parentNode,mapName=map.name,element.href&&mapName&&"map"===map.nodeName.toLowerCase()?(img=$("img[usemap='#"+mapName+"']")[0],!!img&&visible(img)):!1):(/input|select|textarea|button|object/.test(nodeName)?!element.disabled:"a"===nodeName?element.href||isTabIndexNotNaN:isTabIndexNotNaN)&&visible(element)},visible=function(element){return $.expr.filters.visible(element)&&!$(element).parents().addBack().filter(function(){return"hidden"===$.css(this,"visibility")}).length};$.extend($.expr[":"],{data:$.expr.createPseudo?$.expr.createPseudo(function(dataName){return function(elem){return!!$.data(elem,dataName)}}):function(elem,i,match){return!!$.data(elem,match[3])},focusable:function(element){return focusable(element,!isNaN($.attr(element,"tabindex")))},tabbable:function(element){var tabIndex=$.attr(element,"tabindex"),isTabIndexNaN=isNaN(tabIndex);return(isTabIndexNaN||tabIndex>=0)&&focusable(element,!isTabIndexNaN)}}),$(".modal-dialog").attr({role:"document"});var modalhide=$.fn.modal.Constructor.prototype.hide;$.fn.modal.Constructor.prototype.hide=function(){modalhide.apply(this,arguments),$(document).off("keydown.bs.modal")};var modalfocus=$.fn.modal.Constructor.prototype.enforceFocus;$.fn.modal.Constructor.prototype.enforceFocus=function(){var focEls=this.$element.find(":tabbable"),lastEl=focEls[focEls.length-1];$(document).on("keydown.bs.modal",$.proxy(function(ev){!this.$element.has(ev.target).length&&ev.shiftKey&&9===ev.keyCode&&(lastEl.focus(),ev.preventDefault())},this)),modalfocus.apply(this,arguments)};var $par,firstItem,toggle="[data-toggle=dropdown]",focusDelay=200,menus=$(toggle).parent().find("ul").attr("role","menu"),lis=menus.find("li").attr("role","presentation");lis.find("a").attr({role:"menuitem",tabIndex:"-1"}),$(toggle).attr({"aria-haspopup":"true","aria-expanded":"false"}),$(toggle).parent().on("shown.bs.dropdown",function(e){$par=$(this);var $toggle=$par.find(toggle);$toggle.attr("aria-expanded","true"),$toggle.on("keydown.bs.dropdown",$.proxy(function(ev){setTimeout(function(){firstItem=$(".dropdown-menu [role=menuitem]:visible",$par)[0];try{firstItem.focus()}catch(ex){}},focusDelay)},this))}),$(toggle).parent().on("hidden.bs.dropdown",function(e){$par=$(this);var $toggle=$par.find(toggle);$toggle.attr("aria-expanded","false")}),$(document).on("focusout.dropdown.data-api",".dropdown-menu",function(e){var $this=$(this),that=this;setTimeout(function(){$.contains(that,document.activeElement)||($this.parent().removeClass("open"),$this.parent().find("[data-toggle=dropdown]").attr("aria-expanded","false"))},150)}).on("keydown.bs.dropdown.data-api",toggle+", [role=menu]",$.fn.dropdown.Constructor.prototype.keydown);var $tablist=$(".nav-tabs, .nav-pills"),$lis=$tablist.children("li"),$tabs=$tablist.find('[data-toggle="tab"], [data-toggle="pill"]');$tabs&&($tablist.attr("role","tablist"),$lis.attr("role","presentation"),$tabs.attr("role","tab")),$tabs.each(function(index){var tabpanel=$($(this).attr("href")),tab=$(this),tabid=tab.attr("id")||uniqueId("ui-tab");tab.attr("id",tabid),tab.parent().hasClass("active")?(tab.attr({tabIndex:"0","aria-selected":"true","aria-controls":tab.attr("href").substr(1)}),tabpanel.attr({role:"tabpanel",tabIndex:"0","aria-hidden":"false","aria-labelledby":tabid})):(tab.attr({tabIndex:"-1","aria-selected":"false","aria-controls":tab.attr("href").substr(1)}),tabpanel.attr({role:"tabpanel",tabIndex:"-1","aria-hidden":"true","aria-labelledby":tabid}))}),$.fn.tab.Constructor.prototype.keydown=function(e){var $items,index,$this=$(this),$ul=$this.closest("ul[role=tablist] "),k=e.which||e.keyCode;if($this=$(this),/(37|38|39|40)/.test(k)){$items=$ul.find("[role=tab]:visible"),index=$items.index($items.filter(":focus")),(38==k||37==k)&&index--,(39==k||40==k)&&index++,0>index&&(index=$items.length-1),index==$items.length&&(index=0);var nextTab=$items.eq(index);"tab"===nextTab.attr("role")&&nextTab.tab("show").focus(),e.preventDefault(),e.stopPropagation()}},$(document).on("keydown.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',$.fn.tab.Constructor.prototype.keydown);var tabactivate=$.fn.tab.Constructor.prototype.activate;$.fn.tab.Constructor.prototype.activate=function(element,container,callback){var $active=container.find("> .active");$active.find("[data-toggle=tab], [data-toggle=pill]").attr({tabIndex:"-1","aria-selected":!1}),$active.filter(".tab-pane").attr({"aria-hidden":!0,tabIndex:"-1"}),tabactivate.apply(this,arguments),element.addClass("active"),element.find("[data-toggle=tab], [data-toggle=pill]").attr({tabIndex:"0","aria-selected":!0}),element.filter(".tab-pane").attr({"aria-hidden":!1,tabIndex:"0"})};var $colltabs=$('[data-toggle="collapse"]');$colltabs.each(function(index){var colltab=$(this),collpanel=$(colltab.attr("data-target")?colltab.attr("data-target"):colltab.attr("href")),parent=colltab.attr("data-parent"),collparent=parent&&$(parent),collid=colltab.attr("id")||uniqueId("ui-collapse");colltab.attr("id",collid),collparent&&(colltab.attr({role:"tab","aria-selected":"false","aria-expanded":"false"}),$(collparent).find("div:not(.collapse,.panel-body), h4").attr("role","presentation"),collparent.attr({role:"tablist","aria-multiselectable":"true"}),collpanel.hasClass("in")?(colltab.attr({"aria-controls":collpanel.attr("id"),"aria-selected":"true","aria-expanded":"true",tabindex:"0"}),collpanel.attr({role:"tabpanel",tabindex:"0","aria-labelledby":collid,"aria-hidden":"false"})):(colltab.attr({"aria-controls":collpanel.attr("id"),tabindex:"-1"}),collpanel.attr({role:"tabpanel",tabindex:"-1","aria-labelledby":collid,"aria-hidden":"true"})))});var collToggle=$.fn.collapse.Constructor.prototype.toggle;$.fn.collapse.Constructor.prototype.toggle=function(){var href,prevTab=this.$parent&&this.$parent.find('[aria-expanded="true"]');if(prevTab){var curTab,prevPanel=prevTab.attr("data-target")||(href=prevTab.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,""),$prevPanel=$(prevPanel),$curPanel=this.$element;this.$parent;this.$parent&&(curTab=this.$parent.find('[data-toggle=collapse][href="#'+this.$element.attr("id")+'"]')),collToggle.apply(this,arguments),$.support.transition&&this.$element.one($.support.transition.end,function(){prevTab.attr({"aria-selected":"false","aria-expanded":"false",tabIndex:"-1"}),$prevPanel.attr({"aria-hidden":"true",tabIndex:"-1"}),curTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:"0"}),$curPanel.hasClass("in")?$curPanel.attr({"aria-hidden":"false",tabIndex:"0"}):(curTab.attr({"aria-selected":"false","aria-expanded":"false"}),$curPanel.attr({"aria-hidden":"true",tabIndex:"-1"}))})}else collToggle.apply(this,arguments)},$.fn.collapse.Constructor.prototype.keydown=function(e){var $items,index,$this=$(this),$tablist=$this.closest("div[role=tablist] "),k=e.which||e.keyCode;$this=$(this),/(32|37|38|39|40)/.test(k)&&(32==k&&$this.click(),$items=$tablist.find("[role=tab]"),index=$items.index($items.filter(":focus")),(38==k||37==k)&&index--,(39==k||40==k)&&index++,0>index&&(index=$items.length-1),index==$items.length&&(index=0),$items.eq(index).focus(),e.preventDefault(),e.stopPropagation())},$(document).on("keydown.collapse.data-api",'[data-toggle="collapse"]',$.fn.collapse.Constructor.prototype.keydown),$(".carousel").each(function(index){function setTablistHighlightBox(){var $tab,offset,height,width,highlightBox={};highlightBox.top=0,highlightBox.left=32e3,highlightBox.height=0,highlightBox.width=0;for(var i=0;i<$tabs.length;i++){$tab=$tabs[i],offset=$($tab).offset(),height=$($tab).height(),width=$($tab).width(),highlightBox.top<offset.top&&(highlightBox.top=Math.round(offset.top)),highlightBox.height<height&&(highlightBox.height=Math.round(height)),highlightBox.left>offset.left&&(highlightBox.left=Math.round(offset.left));var w=offset.left-highlightBox.left+Math.round(width);highlightBox.width<w&&(highlightBox.width=w)}$tablistHighlight.style.top=highlightBox.top-2+"px",$tablistHighlight.style.left=highlightBox.left-2+"px",$tablistHighlight.style.height=highlightBox.height+7+"px",$tablistHighlight.style.width=highlightBox.width+8+"px"}var $tabpanel,$tablistHighlight,$pauseCarousel,$complementaryLandmark,$tab,i,$this=$(this),$prev=$this.find('[data-slide="prev"]'),$next=$this.find('[data-slide="next"]'),$tablist=$this.find(".carousel-indicators"),$tabs=$this.find(".carousel-indicators li"),$tabpanels=$this.find(".item"),$is_paused=!1,id_title="id_title",id_desc="id_desc";for($tablist.attr("role","tablist"),$tabs.focus(function(){$this.carousel("pause"),$is_paused=!0,$pauseCarousel.innerHTML="Play Carousel",$(this).parent().addClass("active"),setTablistHighlightBox(),$($tablistHighlight).addClass("focus"),$(this).parents(".carousel").addClass("contrast")}),$tabs.blur(function(event){$(this).parent().removeClass("active"),$($tablistHighlight).removeClass("focus"),$(this).parents(".carousel").removeClass("contrast")}),i=0;i<$tabpanels.length;i++)$tabpanel=$tabpanels[i],$tabpanel.setAttribute("role","tabpanel"),$tabpanel.setAttribute("id","tabpanel-"+index+"-"+i),$tabpanel.setAttribute("aria-labelledby","tab-"+index+"-"+i);for("string"!=typeof $this.attr("role")&&($this.attr("role","complementary"),$this.attr("aria-labelledby",id_title),$this.attr("aria-describedby",id_desc),$this.prepend('<p  id="'+id_desc+'" class="sr-only">A carousel is a rotating set of images, rotation stops on keyboard focus on carousel tab controls or hovering the mouse pointer over images.  Use the tabs or the previous and next buttons to change the displayed slide.</p>'),$this.prepend('<h2 id="'+id_title+'" class="sr-only">Carousel content with '+$tabpanels.length+" slides.</h2>")),i=0;i<$tabs.length;i++){$tab=$tabs[i],$tab.setAttribute("role","tab"),$tab.setAttribute("id","tab-"+index+"-"+i),$tab.setAttribute("aria-controls","tabpanel-"+index+"-"+i);var tpId="#tabpanel-"+index+"-"+i,caption=$this.find(tpId).find("h1").text();("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h3").text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h4").text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h5").text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h6").text()),("string"!=typeof caption||0===caption.length)&&(caption="no title");var tabName=document.createElement("span");tabName.setAttribute("class","sr-only"),tabName.innerHTML="Slide "+(i+1),caption&&(tabName.innerHTML+=": "+caption),$tab.appendChild(tabName)}$tablistHighlight=document.createElement("div"),$tablistHighlight.className="carousel-tablist-highlight",document.body.appendChild($tablistHighlight),$complementaryLandmark=document.createElement("aside"),$complementaryLandmark.setAttribute("aria-label","carousel pause/play control"),$(document.body).prepend($complementaryLandmark),$pauseCarousel=document.createElement("button"),$pauseCarousel.className="carousel-pause-button",$pauseCarousel.innerHTML="Pause Carousel",$pauseCarousel.setAttribute("title","Pause/Play carousel button can be used by screen reader users to stop carousel animations"),$($complementaryLandmark).append($pauseCarousel),$($pauseCarousel).click(function(){$is_paused?($pauseCarousel.innerHTML="Pause Carousel",$this.carousel("cycle"),$is_paused=!1):($pauseCarousel.innerHTML="Play Carousel",$this.carousel("pause"),$is_paused=!0)}),$($pauseCarousel).focus(function(){$(this).addClass("focus")}),$($pauseCarousel).blur(function(){$(this).removeClass("focus")}),setTablistHighlightBox(),$(window).resize(function(){setTablistHighlightBox()}),$prev.attr("aria-label","Previous Slide"),$prev.keydown(function(e){var k=e.which||e.keyCode;/(13|32)/.test(k)&&(e.preventDefault(),e.stopPropagation(),$prev.trigger("click"))}),$prev.focus(function(){$(this).parents(".carousel").addClass("contrast")}),$prev.blur(function(){$(this).parents(".carousel").removeClass("contrast")}),$next.attr("aria-label","Next Slide"),$next.keydown(function(e){var k=e.which||e.keyCode;/(13|32)/.test(k)&&(e.preventDefault(),e.stopPropagation(),$next.trigger("click"))}),$next.focus(function(){$(this).parents(".carousel").addClass("contrast")}),$next.blur(function(){$(this).parents(".carousel").removeClass("contrast")}),$(".carousel-inner a").focus(function(){$(this).parents(".carousel").addClass("contrast")}),$(".carousel-inner a").blur(function(){$(this).parents(".carousel").removeClass("contrast")}),$tabs.each(function(){var item=$(this);item.hasClass("active")?item.attr({"aria-selected":"true",tabindex:"0"}):item.attr({"aria-selected":"false",tabindex:"-1"})})});var slideCarousel=$.fn.carousel.Constructor.prototype.slide;$.fn.carousel.Constructor.prototype.slide=function(type,next){var $id,$element=this.$element,$active=$element.find("[role=tabpanel].active"),$next=next||$active[type](),$tab_count=$element.find("[role=tabpanel]").size(),$prev_side=$element.find('[data-slide="prev"]'),$next_side=$element.find('[data-slide="next"]'),$index=0,$prev_index=$tab_count-1,$next_index=1;$next&&$next.attr("id")&&($id=$next.attr("id"),$index=$id.lastIndexOf("-"),$index>=0&&($index=parseInt($id.substring($index+1),10)),$prev_index=$index-1,1>$prev_index&&($prev_index=$tab_count-1),$next_index=$index+1,$next_index>=$tab_count&&($next_index=0)),$prev_side.attr("aria-label","Show slide "+($prev_index+1)+" of "+$tab_count),$next_side.attr("aria-label","Show slide "+($next_index+1)+" of "+$tab_count),slideCarousel.apply(this,arguments),$active.one("bsTransitionEnd",function(){var $tab;$tab=$element.find('li[aria-controls="'+$active.attr("id")+'"]'),$tab&&$tab.attr({"aria-selected":!1,tabIndex:"-1"}),$tab=$element.find('li[aria-controls="'+$next.attr("id")+'"]'),$tab&&$tab.attr({"aria-selected":!0,tabIndex:"0"})})};var $this;$.fn.carousel.Constructor.prototype.keydown=function(e){function selectTab(index){index>=$tabs.length||0>index||($carousel.carousel(index),setTimeout(function(){$tabs[index].focus()},150))}$this=$this||$(this),this instanceof Node&&($this=$(this));var index,$carousel=$(e.target).closest(".carousel"),$tabs=$carousel.find("[role=tab]"),k=e.which||e.keyCode;/(37|38|39|40)/.test(k)&&(index=$tabs.index($tabs.filter(".active")),(37==k||38==k)&&(index--,selectTab(index)),(39==k||40==k)&&(index++,selectTab(index)),e.preventDefault(),e.stopPropagation())},$(document).on("keydown.carousel.data-api","li[role=tab]",$.fn.carousel.Constructor.prototype.keydown)}(jQuery);
\ No newline at end of file
+* Copyright (c) 2016 PayPal Accessibility Team; Licensed BSD */
+!function($){"use strict";var uniqueId=function(prefix){return(prefix||"ui-id")+"-"+Math.floor(1e3*Math.random()+1)},focusable=function(element,isTabIndexNotNaN){var map,mapName,img,nodeName=element.nodeName.toLowerCase();return"area"===nodeName?(map=element.parentNode,mapName=map.name,element.href&&mapName&&"map"===map.nodeName.toLowerCase()?(img=$("img[usemap='#"+mapName+"']")[0],!!img&&visible(img)):!1):(/input|select|textarea|button|object/.test(nodeName)?!element.disabled:"a"===nodeName?element.href||isTabIndexNotNaN:isTabIndexNotNaN)&&visible(element)},visible=function(element){return $.expr.filters.visible(element)&&!$(element).parents().addBack().filter(function(){return"hidden"===$.css(this,"visibility")}).length};$.extend($.expr[":"],{data:$.expr.createPseudo?$.expr.createPseudo(function(dataName){return function(elem){return!!$.data(elem,dataName)}}):function(elem,i,match){return!!$.data(elem,match[3])},focusable:function(element){return focusable(element,!isNaN($.attr(element,"tabindex")))},tabbable:function(element){var tabIndex=$.attr(element,"tabindex"),isTabIndexNaN=isNaN(tabIndex);return(isTabIndexNaN||tabIndex>=0)&&focusable(element,!isTabIndexNaN)}}),$(".modal-dialog").attr({role:"document"});var modalhide=$.fn.modal.Constructor.prototype.hide;$.fn.modal.Constructor.prototype.hide=function(){modalhide.apply(this,arguments),$(document).off("keydown.bs.modal")};var modalfocus=$.fn.modal.Constructor.prototype.enforceFocus;$.fn.modal.Constructor.prototype.enforceFocus=function(){var focEls=this.$element.find(":tabbable"),lastEl=focEls[focEls.length-1];$(document).on("keydown.bs.modal",$.proxy(function(ev){!this.$element.has(ev.target).length&&ev.shiftKey&&9===ev.keyCode&&(lastEl.focus(),ev.preventDefault())},this)),modalfocus.apply(this,arguments)};var $par,firstItem,toggle="[data-toggle=dropdown]",focusDelay=200,menus=$(toggle).parent().find("ul").attr("role","menu"),lis=menus.find("li").attr("role","presentation");lis.find("a").attr({role:"menuitem",tabIndex:"-1"}),$(toggle).attr({"aria-haspopup":"true","aria-expanded":"false"}),$(toggle).parent().on("shown.bs.dropdown",function(e){$par=$(this);var $toggle=$par.find(toggle);$toggle.attr("aria-expanded","true"),$toggle.on("keydown.bs.dropdown",$.proxy(function(ev){setTimeout(function(){firstItem=$(".dropdown-menu [role=menuitem]:visible",$par)[0];try{firstItem.focus()}catch(ex){}},focusDelay)},this))}).on("hidden.bs.dropdown",function(e){$par=$(this);var $toggle=$par.find(toggle);$toggle.attr("aria-expanded","false")}),$(document).on("focusout.dropdown.data-api",".dropdown-menu",function(e){var $this=$(this),that=this;$this.parent().hasClass("open")&&setTimeout(function(){$.contains(that,document.activeElement)||$this.parent().find("[data-toggle=dropdown]").dropdown("toggle")},150)}).on("keydown.bs.dropdown.data-api",toggle+", [role=menu]",$.fn.dropdown.Constructor.prototype.keydown);var $tablist=$(".nav-tabs, .nav-pills"),$lis=$tablist.children("li"),$tabs=$tablist.find('[data-toggle="tab"], [data-toggle="pill"]');$tabs&&($tablist.attr("role","tablist"),$lis.attr("role","presentation"),$tabs.attr("role","tab")),$tabs.each(function(index){var tabpanel=$($(this).attr("href")),tab=$(this),tabid=tab.attr("id")||uniqueId("ui-tab");tab.attr("id",tabid),tab.parent().hasClass("active")?(tab.attr({tabIndex:"0","aria-selected":"true","aria-controls":tab.attr("href").substr(1)}),tabpanel.attr({role:"tabpanel",tabIndex:"0","aria-hidden":"false","aria-labelledby":tabid})):(tab.attr({tabIndex:"-1","aria-selected":"false","aria-controls":tab.attr("href").substr(1)}),tabpanel.attr({role:"tabpanel",tabIndex:"-1","aria-hidden":"true","aria-labelledby":tabid}))}),$.fn.tab.Constructor.prototype.keydown=function(e){var $items,index,$this=$(this),$ul=$this.closest("ul[role=tablist] "),k=e.which||e.keyCode;if($this=$(this),/(37|38|39|40)/.test(k)){$items=$ul.find("[role=tab]:visible"),index=$items.index($items.filter(":focus")),(38==k||37==k)&&index--,(39==k||40==k)&&index++,0>index&&(index=$items.length-1),index==$items.length&&(index=0);var nextTab=$items.eq(index);"tab"===nextTab.attr("role")&&nextTab.tab("show").focus(),e.preventDefault(),e.stopPropagation()}},$(document).on("keydown.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',$.fn.tab.Constructor.prototype.keydown);var tabactivate=$.fn.tab.Constructor.prototype.activate;$.fn.tab.Constructor.prototype.activate=function(element,container,callback){var $active=container.find("> .active");$active.find("[data-toggle=tab], [data-toggle=pill]").attr({tabIndex:"-1","aria-selected":!1}),$active.filter(".tab-pane").attr({"aria-hidden":!0,tabIndex:"-1"}),tabactivate.apply(this,arguments),element.addClass("active"),element.find("[data-toggle=tab], [data-toggle=pill]").attr({tabIndex:"0","aria-selected":!0}),element.filter(".tab-pane").attr({"aria-hidden":!1,tabIndex:"0"})};var $colltabs=$('[data-toggle="collapse"]');$colltabs.each(function(index){var colltab=$(this),collpanel=$(colltab.attr("data-target")?colltab.attr("data-target"):colltab.attr("href")),parent=colltab.attr("data-parent"),collparent=parent&&$(parent),collid=colltab.attr("id")||uniqueId("ui-collapse");colltab.attr("id",collid),collparent&&(colltab.attr({role:"tab","aria-selected":"false","aria-expanded":"false"}),$(collparent).find("div:not(.collapse,.panel-body), h4").attr("role","presentation"),collparent.attr({role:"tablist","aria-multiselectable":"true"}),collpanel.hasClass("in")?(colltab.attr({"aria-controls":collpanel.attr("id"),"aria-selected":"true","aria-expanded":"true",tabindex:"0"}),collpanel.attr({role:"tabpanel",tabindex:"0","aria-labelledby":collid,"aria-hidden":"false"})):(colltab.attr({"aria-controls":collpanel.attr("id"),tabindex:"-1"}),collpanel.attr({role:"tabpanel",tabindex:"-1","aria-labelledby":collid,"aria-hidden":"true"})))});var collToggle=$.fn.collapse.Constructor.prototype.toggle;$.fn.collapse.Constructor.prototype.toggle=function(){var href,prevTab=this.$parent&&this.$parent.find('[aria-expanded="true"]');if(prevTab){var curTab,prevPanel=prevTab.attr("data-target")||(href=prevTab.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,""),$prevPanel=$(prevPanel),$curPanel=this.$element;this.$parent;this.$parent&&(curTab=this.$parent.find('[data-toggle=collapse][href="#'+this.$element.attr("id")+'"]')),collToggle.apply(this,arguments),$.support.transition&&this.$element.one($.support.transition.end,function(){prevTab.attr({"aria-selected":"false","aria-expanded":"false",tabIndex:"-1"}),$prevPanel.attr({"aria-hidden":"true",tabIndex:"-1"}),curTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:"0"}),$curPanel.hasClass("in")?$curPanel.attr({"aria-hidden":"false",tabIndex:"0"}):(curTab.attr({"aria-selected":"false","aria-expanded":"false"}),$curPanel.attr({"aria-hidden":"true",tabIndex:"-1"}))})}else collToggle.apply(this,arguments)},$.fn.collapse.Constructor.prototype.keydown=function(e){var $items,index,$this=$(this),$tablist=$this.closest("div[role=tablist] "),k=e.which||e.keyCode;$this=$(this),/(32|37|38|39|40)/.test(k)&&(32==k&&$this.click(),$items=$tablist.find("[role=tab]"),index=$items.index($items.filter(":focus")),(38==k||37==k)&&index--,(39==k||40==k)&&index++,0>index&&(index=$items.length-1),index==$items.length&&(index=0),$items.eq(index).focus(),e.preventDefault(),e.stopPropagation())},$(document).on("keydown.collapse.data-api",'[data-toggle="collapse"]',$.fn.collapse.Constructor.prototype.keydown),$(".carousel").each(function(index){function setTablistHighlightBox(){var $tab,offset,height,width,highlightBox={};highlightBox.top=0,highlightBox.left=32e3,highlightBox.height=0,highlightBox.width=0;for(var i=0;i<$tabs.length;i++){$tab=$tabs[i],offset=$($tab).offset(),height=$($tab).height(),width=$($tab).width(),highlightBox.top<offset.top&&(highlightBox.top=Math.round(offset.top)),highlightBox.height<height&&(highlightBox.height=Math.round(height)),highlightBox.left>offset.left&&(highlightBox.left=Math.round(offset.left));var w=offset.left-highlightBox.left+Math.round(width);highlightBox.width<w&&(highlightBox.width=w)}$tablistHighlight.style.top=highlightBox.top-2+"px",$tablistHighlight.style.left=highlightBox.left-2+"px",$tablistHighlight.style.height=highlightBox.height+7+"px",$tablistHighlight.style.width=highlightBox.width+8+"px"}var $tabpanel,$tablistHighlight,$pauseCarousel,$complementaryLandmark,$tab,i,$this=$(this),$prev=$this.find('[data-slide="prev"]'),$next=$this.find('[data-slide="next"]'),$tablist=$this.find(".carousel-indicators"),$tabs=$this.find(".carousel-indicators li"),$tabpanels=$this.find(".item"),$is_paused=!1,id_title="id_title",id_desc="id_desc";for($tablist.attr("role","tablist"),$tabs.focus(function(){$this.carousel("pause"),$is_paused=!0,$pauseCarousel.innerHTML="Play Carousel",$(this).parent().addClass("active"),setTablistHighlightBox(),$($tablistHighlight).addClass("focus"),$(this).parents(".carousel").addClass("contrast")}),$tabs.blur(function(event){$(this).parent().removeClass("active"),$($tablistHighlight).removeClass("focus"),$(this).parents(".carousel").removeClass("contrast")}),i=0;i<$tabpanels.length;i++)$tabpanel=$tabpanels[i],$tabpanel.setAttribute("role","tabpanel"),$tabpanel.setAttribute("id","tabpanel-"+index+"-"+i),$tabpanel.setAttribute("aria-labelledby","tab-"+index+"-"+i);for("string"!=typeof $this.attr("role")&&($this.attr("role","complementary"),$this.attr("aria-labelledby",id_title),$this.attr("aria-describedby",id_desc),$this.prepend('<p  id="'+id_desc+'" class="sr-only">A carousel is a rotating set of images, rotation stops on keyboard focus on carousel tab controls or hovering the mouse pointer over images.  Use the tabs or the previous and next buttons to change the displayed slide.</p>'),$this.prepend('<h2 id="'+id_title+'" class="sr-only">Carousel content with '+$tabpanels.length+" slides.</h2>")),i=0;i<$tabs.length;i++){$tab=$tabs[i],$tab.setAttribute("role","tab"),$tab.setAttribute("id","tab-"+index+"-"+i),$tab.setAttribute("aria-controls","tabpanel-"+index+"-"+i);var tpId="#tabpanel-"+index+"-"+i,caption=$this.find(tpId).find("h1").text();("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h3").text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h4").text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h5").text()),("string"!=typeof caption||0===caption.length)&&(caption=$this.find(tpId).find("h6").text()),("string"!=typeof caption||0===caption.length)&&(caption="no title");var tabName=document.createElement("span");tabName.setAttribute("class","sr-only"),tabName.innerHTML="Slide "+(i+1),caption&&(tabName.innerHTML+=": "+caption),$tab.appendChild(tabName)}$tablistHighlight=document.createElement("div"),$tablistHighlight.className="carousel-tablist-highlight",document.body.appendChild($tablistHighlight),$complementaryLandmark=document.createElement("aside"),$complementaryLandmark.setAttribute("aria-label","carousel pause/play control"),$(document.body).prepend($complementaryLandmark),$pauseCarousel=document.createElement("button"),$pauseCarousel.className="carousel-pause-button",$pauseCarousel.innerHTML="Pause Carousel",$pauseCarousel.setAttribute("title","Pause/Play carousel button can be used by screen reader users to stop carousel animations"),$($complementaryLandmark).append($pauseCarousel),$($pauseCarousel).click(function(){$is_paused?($pauseCarousel.innerHTML="Pause Carousel",$this.carousel("cycle"),$is_paused=!1):($pauseCarousel.innerHTML="Play Carousel",$this.carousel("pause"),$is_paused=!0)}),$($pauseCarousel).focus(function(){$(this).addClass("focus")}),$($pauseCarousel).blur(function(){$(this).removeClass("focus")}),setTablistHighlightBox(),$(window).resize(function(){setTablistHighlightBox()}),$prev.attr("aria-label","Previous Slide"),$prev.keydown(function(e){var k=e.which||e.keyCode;/(13|32)/.test(k)&&(e.preventDefault(),e.stopPropagation(),$prev.trigger("click"))}),$prev.focus(function(){$(this).parents(".carousel").addClass("contrast")}),$prev.blur(function(){$(this).parents(".carousel").removeClass("contrast")}),$next.attr("aria-label","Next Slide"),$next.keydown(function(e){var k=e.which||e.keyCode;/(13|32)/.test(k)&&(e.preventDefault(),e.stopPropagation(),$next.trigger("click"))}),$next.focus(function(){$(this).parents(".carousel").addClass("contrast")}),$next.blur(function(){$(this).parents(".carousel").removeClass("contrast")}),$(".carousel-inner a").focus(function(){$(this).parents(".carousel").addClass("contrast")}),$(".carousel-inner a").blur(function(){$(this).parents(".carousel").removeClass("contrast")}),$tabs.each(function(){var item=$(this);item.hasClass("active")?item.attr({"aria-selected":"true",tabindex:"0"}):item.attr({"aria-selected":"false",tabindex:"-1"})})});var slideCarousel=$.fn.carousel.Constructor.prototype.slide;$.fn.carousel.Constructor.prototype.slide=function(type,next){var $id,$element=this.$element,$active=$element.find("[role=tabpanel].active"),$next=next||$active[type](),$tab_count=$element.find("[role=tabpanel]").size(),$prev_side=$element.find('[data-slide="prev"]'),$next_side=$element.find('[data-slide="next"]'),$index=0,$prev_index=$tab_count-1,$next_index=1;$next&&$next.attr("id")&&($id=$next.attr("id"),$index=$id.lastIndexOf("-"),$index>=0&&($index=parseInt($id.substring($index+1),10)),$prev_index=$index-1,1>$prev_index&&($prev_index=$tab_count-1),$next_index=$index+1,$next_index>=$tab_count&&($next_index=0)),$prev_side.attr("aria-label","Show slide "+($prev_index+1)+" of "+$tab_count),$next_side.attr("aria-label","Show slide "+($next_index+1)+" of "+$tab_count),slideCarousel.apply(this,arguments),$active.one("bsTransitionEnd",function(){var $tab;$tab=$element.find('li[aria-controls="'+$active.attr("id")+'"]'),$tab&&$tab.attr({"aria-selected":!1,tabIndex:"-1"}),$tab=$element.find('li[aria-controls="'+$next.attr("id")+'"]'),$tab&&$tab.attr({"aria-selected":!0,tabIndex:"0"})})};var $this;$.fn.carousel.Constructor.prototype.keydown=function(e){function selectTab(index){index>=$tabs.length||0>index||($carousel.carousel(index),setTimeout(function(){$tabs[index].focus()},150))}$this=$this||$(this),this instanceof Node&&($this=$(this));var index,$carousel=$(e.target).closest(".carousel"),$tabs=$carousel.find("[role=tab]"),k=e.which||e.keyCode;/(37|38|39|40)/.test(k)&&(index=$tabs.index($tabs.filter(".active")),(37==k||38==k)&&(index--,selectTab(index)),(39==k||40==k)&&(index++,selectTab(index)),e.preventDefault(),e.stopPropagation())},$(document).on("keydown.carousel.data-api","li[role=tab]",$.fn.carousel.Constructor.prototype.keydown)}(jQuery);
\ No newline at end of file
diff --git a/themes/root/css/font/Roboto-Light.ttf b/themes/root/css/font/Roboto-Light.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..664e1b2f9dbafbf6280305123d2df7fa0c66cee9
Binary files /dev/null and b/themes/root/css/font/Roboto-Light.ttf differ
diff --git a/themes/root/css/font/RobotoCondensed-Bold.ttf b/themes/root/css/font/RobotoCondensed-Bold.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..48dd63534bae7019b7a4e48ec648e29ce153ccf0
Binary files /dev/null and b/themes/root/css/font/RobotoCondensed-Bold.ttf differ
diff --git a/themes/root/css/font/RobotoMono-Regular.ttf b/themes/root/css/font/RobotoMono-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..495a82ce92ede816ffde602ade57dc02dc7b6314
Binary files /dev/null and b/themes/root/css/font/RobotoMono-Regular.ttf differ