added jstore path
[bookreader.git] / BookReader / BookReader.js
index f2cc8a8..4b3e686 100644 (file)
@@ -18,7 +18,6 @@ This file is part of BookReader.
     
     The BookReader source is hosted at http://github.com/openlibrary/bookreader/
 
-    archive.org cvs $Revision: 1.2 $ $Date: 2009-06-22 18:42:51 $
 */
 
 // BookReader()
@@ -80,9 +79,8 @@ function BookReader() {
     
     this.lastDisplayableIndex2up = null;
     
-    // We link to index.php to avoid redirect which breaks back button
     // Should be overriden (before init) by custom implmentations.
-    this.logoURL = 'http://openlibrary.org';
+    this.logoURL = 'http://www.archive.org';
     
     // Base URL for UI images - should be overriden (before init) by
     // custom implementations.
@@ -119,7 +117,7 @@ function BookReader() {
     this.twoPage = {
         coverInternalPadding: 0, // Width of cover
         coverExternalPadding: 0, // Padding outside of cover
-        bookSpineDivWidth: 0,    // Width of book spine  $$$ consider sizing based on book length
+        bookSpineDivWidth: 64,    // Width of book spine  $$$ consider sizing based on book length
         autofit: 'auto'
     };
     
@@ -145,6 +143,7 @@ function BookReader() {
     return this;
 };
 
+(function ($) {
 // init()
 //______________________________________________________________________________
 BookReader.prototype.init = function() {
@@ -153,7 +152,16 @@ BookReader.prototype.init = function() {
     this.pageScale = this.reduce; // preserve current reduce
     
     // Find start index and mode if set in location hash
-    var params = this.paramsFromFragment(window.location.hash);
+    var params = {};
+    if (window.location.hash) {
+        // params explicitly set in URL
+        params = this.paramsFromFragment(window.location.hash);
+    } else {
+        // params not explicitly set, use defaults if we have them
+        if ('defaults' in this) {
+            params = this.paramsFromFragment(this.defaults);
+        }
+    }
     
     // Sanitize/process parameters
 
@@ -189,20 +197,11 @@ BookReader.prototype.init = function() {
     document.title = this.shortTitle(50);
     
     $("#BookReader").empty();
-        
-    this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
     
-    $("#BookReader").append("<div id='BRcontainer'></div>");
+    this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
+    $("#BookReader").append("<div id='BRcontainer' dir='ltr'></div>");
     $("#BRcontainer").append("<div id='BRpageview'></div>");
         
-    // Autohide nav after showing for awhile
-    var self = this;
-    if (this.uiAutoHide) {
-        $(window).bind('load', function() {
-            setTimeout(function() { self.hideNavigation(); }, 3000);
-        });
-    };
-
     $("#BRcontainer").bind('scroll', this, function(e) {
         e.data.loadLeafs();
     });
@@ -250,6 +249,21 @@ BookReader.prototype.init = function() {
         }
     });
     
+    if (this.protected) {
+        $('.BRpagediv1up').live('contextmenu dragstart', this, function(e) {
+            return false;
+        });
+        
+        $('.BRpageimage').live('contextmenu dragstart', this, function(e) {
+            return false;
+        });
+
+        $('.BRpagedivthumb').live('contextmenu dragstart', this, function(e) {
+            return false;
+        });
+        
+    }
+    
     $('.BRpagediv1up').bind('mousedown', this, function(e) {
         // $$$ the purpose of this is to disable selection of the image (makes it turn blue)
         //     but this also interferes with right-click.  See https://bugs.edge.launchpad.net/gnubook/+bug/362626
@@ -280,8 +294,15 @@ BookReader.prototype.init = function() {
 
     // We init the nav bar after the params processing so that the nav slider knows where
     // it should start (doesn't jump after init)
-    this.initNavbar();
+    if (this.ui == "embed") {
+        this.initEmbedNavbar();
+    } else {
+        this.initNavbar();
+    }
     this.bindNavigationHandlers();
+    
+    // Set strings in the UI
+    this.initUIStrings();
 
     // Start AJAX request for OL data
     if (this.getOpenLibraryRecord) {
@@ -703,11 +724,9 @@ BookReader.prototype.drawLeafsThumbnail = function( seekIndex ) {
                 $('#pagediv'+index).remove();
             }
         } else {
-            /*
-            var mRow = this.displayedRows[i];
-            var mLeafs = '[' +  [leaf.num for each (leaf in leafMap[mRow].leafs)] + ']';
-            console.log('NOT Removing row ' + mRow + ' ' + mLeafs);
-            */
+            // var mRow = this.displayedRows[i];
+            // var mLeafs = '[' +  [leaf.num for each (leaf in leafMap[mRow].leafs)] + ']';
+            // console.log('NOT Removing row ' + mRow + ' ' + mLeafs);
         }
     }
     
@@ -794,9 +813,17 @@ BookReader.prototype.lazyLoadImage = function (dummyImage) {
             // Remove class so we no longer count as loading
             $(this).removeClass('BRlazyloading');
         })
-        .attr( { width: $(dummyImage).width(),
-                   height: $(dummyImage).height(),
-                   src: $(dummyImage).data('srcURL')
+
+        //the width set with .attr is ignored by Internet Explorer, causing it to show the image at its original size
+        //but with this one line of css, even IE shows the image at the proper size
+        .css({
+            'width': $(dummyImage).width()+'px',
+            'height': $(dummyImage).height()+'px'
+        })
+        .attr({
+            'width': $(dummyImage).width(),
+            'height': $(dummyImage).height(),
+            'src': $(dummyImage).data('srcURL')
         });
                  
     // replace with the new img
@@ -1228,7 +1255,21 @@ BookReader.prototype._reduceSort = function(a, b) {
 // Attempts to jump to page.  Returns true if page could be found, false otherwise.
 BookReader.prototype.jumpToPage = function(pageNum) {
 
-    var pageIndex = this.getPageIndex(pageNum);
+    var pageIndex;
+    
+    // Check for special "leaf"
+    var re = new RegExp('^leaf(\\d+)');
+    leafMatch = re.exec(pageNum);
+    if (leafMatch) {
+        console.log(leafMatch[1]);
+        pageIndex = this.leafNumToIndex(parseInt(leafMatch[1],10));
+        if (pageIndex === null) {
+            pageIndex = undefined; // to match return type of getPageIndex
+        }
+        
+    } else {
+        pageIndex = this.getPageIndex(pageNum);
+    }
 
     if ('undefined' != typeof(pageIndex)) {
         var leafTop = 0;
@@ -1527,10 +1568,10 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc
     }).appendTo('#BRtwopageview');
 
     div = document.createElement('div');
-    $(div).attr('id', 'BRbookspine').css({
+    $(div).attr('id', 'BRgutter').css({
         width:           this.twoPage.bookSpineDivWidth+'px',
         height:          this.twoPage.bookSpineDivHeight+'px',
-        left:            this.twoPage.bookSpineDivLeft+'px',
+        left:            (this.twoPage.gutter - this.twoPage.bookSpineDivWidth*0.5)+'px',
         top:             this.twoPage.bookSpineDivTop+'px'
     }).appendTo('#BRtwopageview');
     
@@ -1812,6 +1853,20 @@ BookReader.prototype.twoPageGetAutofitReduce = function() {
     return spreadSize.reduce;
 }
 
+// twoPageIsZoomedIn
+//______________________________________________________________________________
+// Returns true if the pages extend past the edge of the view
+BookReader.prototype.twoPageIsZoomedIn = function() {
+    var autofitReduce = this.twoPageGetAutofitReduce();
+    var isZoomedIn = false;
+    if (this.twoPage.autofit != 'auto') {
+        if (this.reduce < this.twoPageGetAutofitReduce()) {                
+            isZoomedIn = true;
+        }
+    }
+    return isZoomedIn;
+}
+
 BookReader.prototype.onePageGetAutofitWidth = function() {
     var widthPadding = 20;
     return (this.getMedianPageSize().width + 0.0) / ($('#BRcontainer').attr('clientWidth') - widthPadding * 2);
@@ -2181,10 +2236,16 @@ BookReader.prototype.flipLeftToRight = function(newIndexL, newIndexR) {
     
         //console.log('     and now leafEdgeTmp to left: gutter+newWidthR ' + (gutter + newWidthR));
         $(self.leafEdgeTmp).animate({left: gutter+newWidthR+'px'}, self.flipSpeed, 'easeOutSine');
+        
+        $('#BRgutter').css({left: (gutter - self.twoPage.bookSpineDivWidth*0.5)+'px'});        
 
         //console.log('  animating newIndexR ' + newIndexR + ' to ' + newWidthR + ' from ' + $(self.prefetchedImgs[newIndexR]).width());
         $(self.prefetchedImgs[newIndexR]).animate({width: newWidthR+'px'}, self.flipSpeed, 'easeOutSine', function() {
             $(self.prefetchedImgs[newIndexL]).css('zIndex', 2);
+
+            //jquery adds display:block to the element style, which interferes with our print css
+            $(self.prefetchedImgs[newIndexL]).css('display', '');
+            $(self.prefetchedImgs[newIndexR]).css('display', '');
             
             $(self.leafEdgeR).css({
                 // Moves the right leaf edge
@@ -2202,7 +2263,7 @@ BookReader.prototype.flipLeftToRight = function(newIndexL, newIndexR) {
             $(self.twoPage.coverDiv).css({
                 width: self.twoPageCoverWidth(newWidthL+newWidthR)+'px',
                 left: gutter-newWidthL-newLeafEdgeWidthL-self.twoPage.coverInternalPadding+'px'
-            });
+            });            
             
             $(self.leafEdgeTmp).remove();
             self.leafEdgeTmp = null;
@@ -2281,7 +2342,6 @@ BookReader.prototype.willChangeToIndex = function(index)
 {
     // Update navbar position icon - leads page change animation
     this.updateNavIndex(index);
-
 }
 
 // flipRightToLeft(nextL, nextR, gutter)
@@ -2330,9 +2390,14 @@ BookReader.prototype.flipRightToLeft = function(newIndexL, newIndexR) {
     
     $(this.leafEdgeTmp).animate({left: gutter}, speed, 'easeInSine');    
     $(this.prefetchedImgs[this.twoPage.currentIndexR]).animate({width: '0px'}, speed, 'easeInSine', function() {
+        $('#BRgutter').css({left: (gutter - self.twoPage.bookSpineDivWidth*0.5)+'px'});
         $(self.leafEdgeTmp).animate({left: gutter-newWidthL-leafEdgeTmpW+'px'}, speed, 'easeOutSine');    
         $(self.prefetchedImgs[newIndexL]).animate({width: newWidthL+'px'}, speed, 'easeOutSine', function() {
             $(self.prefetchedImgs[newIndexR]).css('zIndex', 2);
+
+            //jquery adds display:block to the element style, which interferes with our print css
+            $(self.prefetchedImgs[newIndexL]).css('display', '');
+            $(self.prefetchedImgs[newIndexR]).css('display', '');
             
             $(self.leafEdgeL).css({
                 width: newLeafEdgeWidthL+'px', 
@@ -2343,8 +2408,8 @@ BookReader.prototype.flipRightToLeft = function(newIndexL, newIndexR) {
             $(self.twoPage.coverDiv).css({
                 width: self.twoPageCoverWidth(newWidthL+newWidthR)+'px',
                 left: gutter - newWidthL - newLeafEdgeWidthL - self.twoPage.coverInternalPadding + 'px'
-            });
-            
+            });            
+    
             $(self.leafEdgeTmp).remove();
             self.leafEdgeTmp = null;
             
@@ -2382,14 +2447,15 @@ BookReader.prototype.setMouseHandlers2UP = function() {
     this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexL],
         { self: this },
         function(e) {
-            if (e.button == 2) {
+            if (e.which == 3) {
                 // right click
-                return;
+                if (e.data.self.protected) {
+                    return false;
+                }
+                return true;
             }
-            
-            var autofitReduce = e.data.self.twoPageGetAutofitReduce();
-            // Don't trigger if zoomed in
-            if (e.data.self.reduce >= e.data.self.twoPageGetAutofitReduce()) {                
+                        
+             if (! e.data.self.twoPageIsZoomedIn()) {
                 e.data.self.ttsStop();
                 e.data.self.left();                
             }
@@ -2400,16 +2466,17 @@ BookReader.prototype.setMouseHandlers2UP = function() {
     this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR],
         { self: this },
         function(e) {
-            if (e.button == 2) {
+            if (e.which == 3) {
                 // right click
-                return;
+                if (e.data.self.protected) {
+                    return false;
+                }
+                return true;
             }
-
-            var autofitReduce = e.data.self.twoPageGetAutofitReduce();
-            // Don't trigger if zoomed in
-            if (e.data.self.reduce >= e.data.self.twoPageGetAutofitReduce()) {                
+            
+            if (! e.data.self.twoPageIsZoomedIn()) {
                 e.data.self.ttsStop();
-                e.data.self.right();
+                e.data.self.right();                
             }
             e.preventDefault();
         }
@@ -2475,14 +2542,13 @@ BookReader.prototype.prepareFlipLeftToRight = function(prevL, prevR) {
     //console.log('    prevL.left: ' + (gutter - scaledW) + 'px');
     //console.log('    changing prevL ' + prevL + ' to left: ' + (gutter-scaledW) + ' width: ' + scaledW);
     
-    leftCSS = {
+    var leftCSS = {
         position: 'absolute',
         left: gutter-scaledW+'px',
         right: '', // clear right property
         top:    top+'px',
         height: this.twoPage.height,
         width:  scaledW+'px',
-        borderRight: '1px solid black', // XXXmang check
         zIndex: 1
     }
     
@@ -2492,13 +2558,12 @@ BookReader.prototype.prepareFlipLeftToRight = function(prevL, prevR) {
 
     //console.log('    changing prevR ' + prevR + ' to left: ' + gutter + ' width: 0');
 
-    rightCSS = {
+    var rightCSS = {
         position: 'absolute',
         left:   gutter+'px',
         right: '',
         top:    top+'px',
         height: this.twoPage.height,
-        borderLeft: '1px solid black', // XXXmang check
         width:  '0',
         zIndex: 2
     }
@@ -2674,7 +2739,7 @@ BookReader.prototype.search = function(term) {
     
     this.removeSearchResults();
     this.showProgressPopup('<img id="searchmarker" src="'+this.imagesBaseURL + 'marker_srch-on.png'+'"> Search results will appear below...');
-    this.ttsAjax = $.ajax({url:url, dataType:'jsonp', jsonpCallback:'br.BRSearchCallback'});    
+    $.ajax({url:url, dataType:'jsonp', jsonpCallback:'br.BRSearchCallback'});    
 }
 
 // BRSearchCallback()
@@ -2686,12 +2751,18 @@ BookReader.prototype.BRSearchCallback = function(results) {
     //console.log(br.searchResults);
     
     if (0 == results.matches.length) {
-        $(br.popup).text('No matches were found.');
+        var errStr  = 'No matches were found.';
+        var timeout = 1000;
+        if (false === results.indexed) {
+            errStr  = "<p>This book hasn't been indexed for searching yet. We've just started indexing it, so search should be available soon. Please try again later. Thanks!</p>";
+            timeout = 5000;
+        }
+        $(br.popup).html(errStr);
         setTimeout(function(){
             $(br.popup).fadeOut('slow', function() {
                 br.removeProgressPopup();
             })        
-        },1000);
+        },timeout);
         return;
     }
     
@@ -2987,56 +3058,9 @@ BookReader.prototype.getPrintURI = function() {
     return '/bookreader/print.php?' + options;
 }
 
-/* iframe implementation
-BookReader.prototype.getPrintFrameContent = function(index) {    
-    // We fit the image based on an assumed A4 aspect ratio.  A4 is a bit taller aspect than
-    // 8.5x11 so we should end up not overflowing on either paper size.
-    var paperAspect = 8.5 / 11;
-    var imageAspect = this._getPageWidth(index) / this._getPageHeight(index);
-    
-    var rotate = 0;
-    
-    // Rotate if possible and appropriate, to get larger image size on printed page
-    if (this.canRotatePage(index)) {
-        if (imageAspect > 1 && imageAspect > paperAspect) {
-            // more wide than square, and more wide than paper
-            rotate = 90;
-            imageAspect = 1/imageAspect;
-        }
-    }
-    
-    var fitAttrs;
-    if (imageAspect > paperAspect) {
-        // wider than paper, fit width
-        fitAttrs = 'width="95%"';
-    } else {
-        // taller than paper, fit height
-        fitAttrs = 'height="95%"';
-    }
-
-    var imageURL = this._getPageURI(index, 1, rotate);
-    var iframeStr = '<html style="padding: 0; border: 0; margin: 0"><head><title>' + this.bookTitle + '</title></head><body style="padding: 0; border:0; margin: 0">';
-    iframeStr += '<div style="text-align: center; width: 99%; height: 99%; overflow: hidden;">';
-    iframeStr +=   '<img src="' + imageURL + '" ' + fitAttrs + ' />';
-    iframeStr += '</div>';
-    iframeStr += '</body></html>';
-    
-    return iframeStr;
-}
-
-BookReader.prototype.updatePrintFrame = function(delta) {
-    var newIndex = this.indexToPrint + delta;
-    newIndex = BookReader.util.clamp(newIndex, 0, this.numLeafs - 1);
-    if (newIndex == this.indexToPrint) {
-        return;
-    }
-    this.indexToPrint = newIndex;
-    var doc = BookReader.util.getIFrameDocument($('#printFrame')[0]);
-    $('body', doc).html(this.getPrintFrameContent(this.indexToPrint));
-}
-*/
-
 // showEmbedCode()
+//
+// Note: Has been replaced by the share dialog
 //______________________________________________________________________________
 BookReader.prototype.showEmbedCode = function() {
     if (null != this.embedPopup) { // check if already showing
@@ -3283,8 +3307,7 @@ BookReader.prototype.jumpIndexForRightEdgePageX = function(pageX) {
 BookReader.prototype.initNavbar = function() {
     // Setup nav / chapter / search results bar
     
-    // $$$ should make this work inside the BookReader div (self-contained), rather than after
-    $('#BookReader').after(
+    $('#BookReader').append(
         '<div id="BRnav">'
         +     '<div id="BRpage">'   // Page turn buttons
         +         '<button class="BRicon onepg"></button>'
@@ -3309,61 +3332,6 @@ BookReader.prototype.initNavbar = function() {
         + '</div>'
     );
     
-/*
-        <div class="searchChap" style="left:49%;" title="Search result">
-            <div class="query">
-            A related distinction is between the emotion and the results of the emotion, principally behaviors and <strong><a href="">emotional</a></strong> expressions. People often behave in certain ways as a direct result of their <strong><a href="">emotional</a></strong> state, such as crying, fighting or fleeing. <span>Page 163</span>
-                <div class="queryChap">IV. The Witch <span>|</span> Page 163</div>
-            </div>
-        </div>
-*/
-    
-    /* $$$mang search results and chapters should automatically coalesce
-    $('.searchChap').bt({
-        contentSelector: '$(this).find(".query")',
-        trigger: 'click',
-        closeWhenOthersOpen: true,
-        cssStyles: {
-            width: '250px',
-            padding: '10px 10px 15px',
-            backgroundColor: '#fff',
-            border: '3px solid #e2dcc5',
-            borderBottom: 'none',
-            fontFamily: '"Lucida Grande","Arial",sans-serif',
-            fontSize: '12px',
-            lineHeight: '18px',
-            color: '#615132'
-        },
-        shrinkToFit: false,
-        width: '230px',
-        padding: 0,
-        spikeGirth: 0,
-        spikeLength: 0,
-        overlap: '10px',
-        overlay: false,
-        killTitle: true, 
-        textzIndex: 9999,
-        boxzIndex: 9998,
-        wrapperzIndex: 9997,
-        offsetParent: null,
-        positions: ['top'],
-        fill: 'white',
-        windowMargin: 10,
-        strokeWidth: 3,
-        strokeStyle: '#e2dcc5',
-        cornerRadius: 0,
-        centerPointX: 0,
-        centerPointY: 0,
-        shadow: false
-    });
-    $('.searchChap').each(function(){
-        $(this).hover(function(){
-            $(this).addClass('front');
-        },function(){
-            $(this).removeClass('front');
-        });
-    });
-    */
     var self = this;
     $('#BRpager').slider({    
         animate: true,
@@ -3371,12 +3339,13 @@ BookReader.prototype.initNavbar = function() {
         max: this.numLeafs - 1,
         value: this.currentIndex()
     })
-    .bind('slide', function(event, ui){
+    .bind('slide', function(event, ui) {
         self.updateNavPageNum(ui.value);
         $("#pagenum").show();
         return true;
     })
     .bind('slidechange', function(event, ui) {
+        self.updateNavPageNum(ui.value); // hiding now but will show later
         $("#pagenum").hide();
         
         // recursion prevention for jumpToIndex
@@ -3397,35 +3366,46 @@ BookReader.prototype.initNavbar = function() {
     
     //append icon to handle
     var handleHelper = $('#BRpager .ui-slider-handle')
-    // $$$mang update logic for setting the page number label -- use page numbers if available
     .append('<div id="pagenum"><span class="currentpage"></span></div>');
     //.wrap('<div class="ui-handle-helper-parent"></div>').parent(); // XXXmang is this used for hiding the tooltip?
     
-    // $$$mang, why are these set both here and in bindNavigationHandlers?
-    $('.BRicon.book_left').bind('click', function() {
-        self.ttsStop();
-        self.left();
-    });
-    $('.BRicon.book_right').bind('click', function() {
-        self.ttsStop();
-        self.right();
-    });
-    
     this.updateNavPageNum(this.currentIndex());
 
     $("#BRzoombtn").draggable({axis:'y',containment:'parent'});
     
-    //XXXmang remove once done testing
-    //this.addSearchResult("There is a place where the <strong>sidewalk</strong> ends And before the street begins, And there the grass grows soft and white, And there the sun burns crimson bright,And there the moon-bird rests from his flight To cool in the peppermint wind.", "20", 31);
-    //this.addSearchResult("There is a place where the <strong>sidewalk</strong> BEGINS And there the moon-bird rests from his flight To cool in the peppermint wind.", "60", 71);
+    /* Initial hiding
+        $('#BRtoolbar').delay(3000).animate({top:-40});
+        $('#BRnav').delay(3000).animate({bottom:-53});
+        changeArrow();
+        $('.BRnavCntl').delay(3000).animate({height:'43px'}).delay(1000).animate({opacity:.25},1000);
+    */
+}
+
+// initEmbedNavbar
+//______________________________________________________________________________
+// Initialize the navigation bar when embedded
+BookReader.prototype.initEmbedNavbar = function() {
+    var thisLink = (window.location + '').replace('?ui=embed',''); // IA-specific
     
+    $('#BookReader').append(
+        '<div id="BRnav">'
+        +   "<span id='BRtoolbarbuttons'>"        
+        +         '<button class="BRicon full"></button>'
+        +         '<button class="BRicon book_left"></button>'
+        +         '<button class="BRicon book_right"></button>'
+        +   "</span>"
+        +   "<span><a class='logo' href='" + this.logoURL + "' 'target='_blank' ></a></span>"
+        +   "<span id='BRembedreturn'><a href='" + thisLink + "' target='_blank' ></a></span>"
+        + '</div>'
+    );
+    $('#BRembedreturn a').text(this.bookTitle);
 }
 
 BookReader.prototype.updateNavPageNum = function(index) {
     var pageNum = this.getPageNum(index);
     var pageStr;
     if (pageNum[0] == 'n') { // funny index
-        pageStr = index + ' / ' + this.numLeafs;
+        pageStr = index + 1 + ' / ' + this.numLeafs; // Accessible index starts at 0 (alas) so we add 1 to make human
     } else {
         pageStr = 'Page ' + pageNum;
     }
@@ -3630,14 +3610,17 @@ BookReader.prototype.addChapterFromEntry = function(tocEntryObject) {
 }
 
 BookReader.prototype.initToolbar = function(mode, ui) {
+    if (ui == "embed") {
+        return; // No toolbar at top in embed mode
+    }
 
     // $$$mang should be contained within the BookReader div instead of body
     var readIcon = '';
     if (!navigator.userAgent.match(/mobile/i)) {
         readIcon = "<button class='BRicon read modal'></button>";
     }
-
-    $("body").append(
+    
+    $("#BookReader").append(
           "<div id='BRtoolbar'>"
         +   "<span id='BRtoolbarbuttons'>"
         +     "<form action='javascript:br.search($(\"#textSrch\").val());' id='booksearch'><input type='search' id='textSrch' name='textSrch' val='' placeholder='Search inside'/><button type='submit' id='btnSrch' name='btnSrch'>GO</button></form>"
@@ -3649,8 +3632,8 @@ BookReader.prototype.initToolbar = function(mode, ui) {
         //+     "<button class='BRicon full'></button>"
         +   "</span>"
         +   "<span><a class='logo' href='" + this.logoURL + "'></a></span>"
-        +   "<span id='BRreturn'><span>Back to</span><a href='" + this.bookUrl + "'>" + this.bookTitle + "</a></span>"
-        +   "<div id='BRnavCntlTop' class='BRnavCntl BRup'></div>"
+        +   "<span id='BRreturn'><a></a></span>"
+        +   "<div id='BRnavCntlTop' class='BRnabrbuvCntl'></div>"
         + "</div>"
         /*
         + "<div id='BRzoomer'>"
@@ -3666,6 +3649,15 @@ BookReader.prototype.initToolbar = function(mode, ui) {
         */
         );
 
+    // Browser hack - bug with colorbox on iOS 3 see https://bugs.launchpad.net/bookreader/+bug/686220
+    if ( navigator.userAgent.match(/ipad/i) && $.browser.webkit && (parseInt($.browser.version, 10) <= 531) ) {
+       $('#BRtoolbarbuttons .info').hide();
+       $('#BRtoolbarbuttons .share').hide();
+    }
+
+    $('#BRreturn a').attr('href', this.bookUrl).text(this.bookTitle);
+
+    $('#BRtoolbar .BRnavCntl').addClass('BRup');
     $('#BRtoolbar .pause').hide();    
     
     this.updateToolbarZoom(this.reduce); // Pretty format
@@ -3679,44 +3671,7 @@ BookReader.prototype.initToolbar = function(mode, ui) {
     
     // We build in mode 2
     jToolbar.append();
-    
-    // Navigation handlers will be bound after all UI is in place -- makes moving icons between
-    // the toolbar and nav bar easier
-    
-    // Setup tooltips -- later we could load these from a file for i18n
-    var titles = { '.logo': 'Go to Archive.org',
-                   '.zoom_in': 'Zoom in',
-                   '.zoom_out': 'Zoom out',
-                   '.onepg': 'One-page view',
-                   '.twopg': 'Two-page view',
-                   '.thumb': 'Thumbnail view',
-                   '.print': 'Print this page',
-                   '.embed': 'Embed BookReader',
-                   '.link': 'Link to this book (and page)',
-                   '.bookmark': 'Bookmark this page',
-                   '.read': 'Read this book aloud',
-                   '.full': 'Show fullscreen',
-                   '.book_left': 'Flip left',
-                   '.book_right': 'Flip right',
-                   '.book_up': 'Page up',
-                   '.book_down': 'Page down',
-                   '.play': 'Play',
-                   '.pause': 'Pause',
-                   '.book_top': 'First page',
-                   '.book_bottom': 'Last page'
-                  };
-    if ('rl' == this.pageProgression) {
-        titles['.book_leftmost'] = 'Last page';
-        titles['.book_rightmost'] = 'First page';
-    } else { // LTR
-        titles['.book_leftmost'] = 'First page';
-        titles['.book_rightmost'] = 'Last page';
-    }
-                  
-    for (var icon in titles) {
-        jToolbar.find(icon).attr('title', titles[icon]);
-    }
-    
+        
     // Hide mode buttons and autoplay if 2up is not available
     // $$$ if we end up with more than two modes we should show the applicable buttons
     if ( !this.canSwitchToMode(this.constMode2up) ) {
@@ -3731,26 +3686,57 @@ BookReader.prototype.initToolbar = function(mode, ui) {
         jToolbar.find('.one_page_mode').hide();
     }
     
-        // $$$ Don't hardcode ids
-    jToolbar.find('.share').colorbox({inline: true, opacity: "0.5", href: "#shareThis"});
-    jToolbar.find('.info').colorbox({inline: true, opacity: "0.5", href: "#aboutThis"});
-    
-    $("body").append(
-        [
-            '<div style="display: none;">',
-                this.makeShareDiv(),
-                this.makeAboutDiv(),
-            '</div>'
-        ].join('\n')
-    );
-    
+    // $$$ Don't hardcode ids
+    var self = this;
+    jToolbar.find('.share').colorbox({inline: true, opacity: "0.5", href: "#BRshare", onLoad: function() { self.autoStop(); self.ttsStop(); } });
+    jToolbar.find('.info').colorbox({inline: true, opacity: "0.5", href: "#BRinfo", onLoad: function() { self.autoStop(); self.ttsStop(); } });
+
+    $('<div style="display: none;"></div>').append(this.blankShareDiv()).append(this.blankInfoDiv()).appendTo($('body'));
 
+    $('#BRinfo .BRfloatTitle a').attr( {'href': this.bookUrl} ).text(this.bookTitle).addClass('title');
+    
+    // These functions can be overridden
+    this.buildInfoDiv($('#BRinfo'));
+    this.buildShareDiv($('#BRshare'));
     
     // Switch to requested mode -- binds other click handlers
     //this.switchToolbarMode(mode);
     
 }
 
+BookReader.prototype.blankInfoDiv = function() {
+    return $([
+        '<div class="BRfloat" id="BRinfo">',
+            '<div class="BRfloatHead">About this book',
+                '<a class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="shift">Close</span></a>',
+            '</div>',
+            '<div class="BRfloatBody">',
+                '<div class="BRfloatCover">',
+                '</div>',
+                '<div class="BRfloatMeta">',
+                    '<div class="BRfloatTitle">',
+                        '<h2><a/></h2>',
+                    '</div>',
+                '</div>',
+            '</div>',
+            '<div class="BRfloatFoot">',
+                '<a href="http://openlibrary.org/dev/docs/bookreader">About the BookReader</a>',
+            '</div>',
+        '</div>'].join('\n')
+    );
+}
+
+BookReader.prototype.blankShareDiv = function() {
+    return $([
+        '<div class="BRfloat" id="BRshare">',
+            '<div class="BRfloatHead">',
+                'Share',
+                '<a class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="shift">Close</span></a>',
+            '</div>',
+        '</div>'].join('\n')
+    );
+}
+
 
 // switchToolbarMode
 //______________________________________________________________________________
@@ -3829,11 +3815,13 @@ BookReader.prototype.bindNavigationHandlers = function() {
     });
 
     jIcons.filter('.book_left').click(function(e) {
+        self.ttsStop();
         self.left();
         return false;
     });
          
     jIcons.filter('.book_right').click(function(e) {
+        self.ttsStop();
         self.right();
         return false;
     });
@@ -3860,7 +3848,8 @@ BookReader.prototype.bindNavigationHandlers = function() {
         self.printPage();
         return false;
     });
-        
+    
+    // Note: Functionality has been replaced by .share
     jIcons.filter('.embed').click(function(e) {
         self.showEmbedCode();
         return false;
@@ -3918,6 +3907,57 @@ BookReader.prototype.bindNavigationHandlers = function() {
         return false;
     });
     
+    jIcons.filter('.full').bind('click', function() {
+        if (self.ui == 'embed') {
+            // $$$ bit of a hack, IA-specific
+            var url = (window.location + '').replace("?ui=embed","");
+            window.open(url);
+        }
+        
+        // Not implemented
+    });
+    
+    $('.BRnavCntl').click(
+        function(){
+            if ($('#BRnavCntlBtm').hasClass('BRdn')) {
+                $('#BRtoolbar').animate({top:-40});
+                $('#BRnav').animate({bottom:-55});
+                $('#BRnavCntlBtm').addClass('BRup').removeClass('BRdn');
+                $('#BRnavCntlTop').addClass('BRdn').removeClass('BRup');
+                $('#BRnavCntlBtm.BRnavCntl').animate({height:'45px'});
+                $('.BRnavCntl').delay(1000).animate({opacity:.25},1000);
+            } else {
+                $('#BRtoolbar').animate({top:0});
+                $('#BRnav').animate({bottom:0});
+                $('#BRnavCntlBtm').addClass('BRdn').removeClass('BRup');
+                $('#BRnavCntlTop').addClass('BRup').removeClass('BRdn');
+                $('#BRnavCntlBtm.BRnavCntl').animate({height:'30px'});
+                $('.BRvavCntl').animate({opacity:1})
+            };
+        }
+    );
+    $('#BRnavCntlBtm').mouseover(function(){
+        if ($(this).hasClass('BRup')) {
+            $('.BRnavCntl').animate({opacity:1},250);
+        };
+    });
+    $('#BRnavCntlBtm').mouseleave(function(){
+        if ($(this).hasClass('BRup')) {
+            $('.BRnavCntl').animate({opacity:.25},250);
+        };
+    });
+    $('#BRnavCntlTop').mouseover(function(){
+        if ($(this).hasClass('BRdn')) {
+            $('.BRnavCntl').animate({opacity:1},250);
+        };
+    });
+    $('#BRnavCntlTop').mouseleave(function(){
+        if ($(this).hasClass('BRdn')) {
+            $('.BRnavCntl').animate({opacity:.25},250);
+        };
+    });
+
+    
     this.initSwipeData();
     $('#BookReader').die('mousemove.navigation').live('mousemove.navigation',
         { 'br': this },
@@ -3927,14 +3967,6 @@ BookReader.prototype.bindNavigationHandlers = function() {
     $('.BRpageimage').die('mousedown.swipe').live('mousedown.swipe',
         { 'br': this },
         this.swipeMousedownHandler
-    )
-    .die('mousemove.swipe').live('mousemove.swipe',
-        { 'br': this },
-        this.swipeMousemoveHandler
-    )
-    .die('mouseup.swipe').live('mouseup.swipe',
-        { 'br': this },
-        this.swipeMouseupHandler
     );
     
     this.bindMozTouchHandlers();
@@ -3987,17 +4019,37 @@ BookReader.prototype.initSwipeData = function(clientX, clientY) {
 BookReader.prototype.swipeMousedownHandler = function(event) {
     //console.log('swipe mousedown');
     //console.log(event);
-    
+
     var self = event.data['br'];
+
+    // We should be the last bubble point for the page images
+    // Disable image drag and select, but keep right-click
+    if (event.which == 3) {
+        if (self.protected) {
+            return false;
+        }
+        return true;
+    }
+    
+    $(event.target).bind('mouseout.swipe',
+        { 'br': self},
+        self.swipeMouseupHandler
+    ).bind('mouseup.swipe',
+        { 'br': self},
+        self.swipeMouseupHandler
+    ).bind('mousemove.swipe',
+        { 'br': self },
+        self.swipeMousemoveHandler
+    );    
+    
     self.initSwipeData(event.clientX, event.clientY);
     self._swipe.mightBeSwiping = true;
     self._swipe.mightBeDragging = true;
     
-    // We should be the last bubble point for the page images
-    // Disable image drag and select, but keep right-click
-    if ($(event.originalTarget).hasClass('BRpageimage') && event.button != 2) {
-        event.preventDefault();
-    }
+    event.preventDefault();
+    event.returnValue  = false;  
+    event.cancelBubble = true;          
+    return false;
 }
 
 BookReader.prototype.swipeMousemoveHandler = function(event) {
@@ -4046,19 +4098,29 @@ BookReader.prototype.swipeMousemoveHandler = function(event) {
     }
     _swipe.lastX = event.clientX;
     _swipe.lastY = event.clientY;
+    
+    event.preventDefault();
+    event.returnValue  = false;
+    event.cancelBubble = true;         
+    return false;
 }
 BookReader.prototype.swipeMouseupHandler = function(event) {
     var _swipe = event.data['br']._swipe;
     //console.log('swipe mouseup - did swipe ' + _swipe.didSwipe);
     _swipe.mightBeSwiping = false;
     _swipe.mightBeDragging = false;
+
+    $(event.target).unbind('mouseout.swipe').unbind('mouseup.swipe').unbind('mousemove.swipe');
+    
     if (_swipe.didSwipe || _swipe.didDrag) {
         // Swallow event if completed swipe gesture
         event.preventDefault();
-        event.stopPropagation();
+        event.returnValue  = false;
+        event.cancelBubble = true;         
+        return false;
     }
+    return true;
 }
-
 BookReader.prototype.bindMozTouchHandlers = function() {
     var self = this;
     
@@ -4121,6 +4183,16 @@ BookReader.prototype.showNavigation = function() {
     }
 }
 
+// changeArrow
+//______________________________________________________________________________
+// Change the nav bar arrow
+function changeArrow(){
+    setTimeout(function(){
+        $('#BRnavCntlBtm').removeClass('BRdn').addClass('BRup');
+    },3000);
+};
+
+
 // firstDisplayableIndex
 //______________________________________________________________________________
 // Returns the index of the first visible page, dependent on the mode.
@@ -4223,7 +4295,7 @@ BookReader.prototype.updateFromParams = function(params) {
             this.jumpToPage(params.page);
         }
     }
-    
+
     // $$$ process /region
     // $$$ process /highlight
 }
@@ -4326,8 +4398,10 @@ BookReader.prototype.fragmentFromParams = function(params) {
     if ('undefined' != typeof(params.page)) {
         fragments.push('page', params.page);
     } else {
-        // Don't have page numbering -- use index instead
-        fragments.push('page', 'n' + params.index);
+        if ('undefined' != typeof(params.index)) {
+            // Don't have page numbering but we do have the index
+            fragments.push('page', 'n' + params.index);
+        }
     }
     
     // $$$ highlight
@@ -4562,8 +4636,44 @@ BookReader.prototype.gotOpenLibraryRecord = function(self, olObject) {
         }
 
         // $$$mang cleanup
-        this.bookUrl = 'http://openlibrary.org' + olObject.key;
-        $('#BRreturn a').attr('href', this.bookUrl);
+        self.bookUrl = self.olHost + olObject.key;
+        self.bookTitle = olObject['title'];
+        $('#BRreturn a').attr( {'href': self.bookUrl, 'title': "Go to this book's page on Open Library" } );
+        $('#BRreturn a').text(self.bookTitle);
+        
+        $('#BRinfo').remove();
+        $('#BRshare').after(self.blankInfoDiv());
+        self.buildInfoDiv($('#BRinfo'));
+        
+        // Check for borrowed book
+        if (self.olAuth) {
+            var returnUrl = self.olHost + olObject.key + '/do_return/borrow';
+            var borrowUrl = self.olHost + olObject.key + '/borrow';
+            
+            /*
+            $('<a/>')
+                .attr('href', borrowUrl)
+                .text('Return this book')
+                .click(function(event) {
+                    event.preventDefault();
+                    $('#BRreturnform').trigger('submit');
+                })
+                .appendTo('#BRreturn');
+            */
+            
+            $('<form id="BRreturnform" action="' + returnUrl + '" method="post"><input type="submit" value="Return book" onclick="olAuth.deleteCookies();"/><input type="hidden" name="action" value="return" /></form>')
+                .appendTo('#BRreturn');
+
+        } else {
+            $('<a/>').attr( { 'href': self.bookUrl, 'title': 'Go to this book\'s page on Open Library' } )
+                .text('On openlibrary.org')
+                .appendTo('#BRreturn');
+        }
+        
+        $('#BRreturn').css({ 'line-height': '19px'} );
+        $('#BRreturn a').css( {'height': '18px' } );
+
+        
     }
 }
 
@@ -4706,15 +4816,17 @@ BookReader.prototype.ttsStartCB = function (data) {
     
     this.showProgressPopup('Loading audio...');
     
-    ///// whileloading: broken on safari
-    ///// onload fires on safari, but *after* the sound starts playing..
+    ///// Many soundManger2 callbacks are broken when using HTML5 audio.
+    ///// whileloading: broken on safari, worked in FF4, but broken on FireFox 5
+    ///// onload: fires on safari, but *after* the sound starts playing, and does not fire in FF or IE9
+    ///// onbufferchange: fires in FF5 using HTML5 audio, but not in safari using flash audio
+    ///// whileplaying: fires everywhere
     this.ttsPosition = -1;    
     var snd = soundManager.createSound({
      id: 'chunk'+this.ttsIndex+'-0',
-     //url: 'http://home.us.archive.org/~rkumar/arctic.ogg',
      url: 'http://'+this.server+'/BookReader/BookReaderGetTTS.php?string=' + escape(data[0][0]) + '&format=.'+this.ttsFormat, //the .ogg is to trick SoundManager2 to use the HTML5 audio player
-     whileloading: function(){if (this.bytesLoaded == this.bytesTotal) this.br.removeProgressPopup();}, //onload never fires in FF...
-     onload: function(){this.br.removeProgressPopup();} //whileloading never fires in safari...
+     onload: function(){this.br.removeProgressPopup();}, //fires in safari...
+     onbufferchange: function(){if (false == this.isBuffering) this.br.removeProgressPopup();} //fires in FF and IE9
     });    
     snd.br = this;
     snd.load();
@@ -4725,7 +4837,7 @@ BookReader.prototype.ttsStartCB = function (data) {
 // showProgressPopup
 //______________________________________________________________________________
 BookReader.prototype.showProgressPopup = function(msg) {
-    if (soundManager.debugMode) console.log('showProgressPopup index='+this.ttsIndex+' pos='+this.ttsPosition);
+    //if (soundManager.debugMode) console.log('showProgressPopup index='+this.ttsIndex+' pos='+this.ttsPosition);
     if (this.popup) return;
     
     this.popup = document.createElement("div");
@@ -5045,147 +5157,123 @@ BookReader.prototype.ttsStartPolling = function () {
         self.ttsNextChunk();
     },500);    
 }
-//FADING, ETC.
-    function changeArrow(){
-        setTimeout(function(){
-            $('#BRnavCntlBtm').removeClass('BRdn').addClass('BRup');
-        },3000);
-    };
-    $().ready(function(){
-    /*
-        $('#BRtoolbar').delay(3000).animate({top:-40});
-        $('#BRnav').delay(3000).animate({bottom:-53});
-        changeArrow();
-        $('.BRnavCntl').delay(3000).animate({height:'43px'}).delay(1000).animate({opacity:.25},1000);
-    */
-        $('.BRnavCntl').click(
-            function(){
-                if ($('#BRnavCntlBtm').hasClass('BRdn')) {
-                    $('#BRtoolbar').animate({top:-40});
-                    $('#BRnav').animate({bottom:-55});
-                    $('#BRnavCntlBtm').addClass('BRup').removeClass('BRdn');
-                    $('#BRnavCntlTop').addClass('BRdn').removeClass('BRup');
-                    $('#BRnavCntlBtm.BRnavCntl').animate({height:'45px'});
-                    $('.BRnavCntl').delay(1000).animate({opacity:.25},1000);
-                } else {
-                    $('#BRtoolbar').animate({top:0});
-                    $('#BRnav').animate({bottom:0});
-                    $('#BRnavCntlBtm').addClass('BRdn').removeClass('BRup');
-                    $('#BRnavCntlTop').addClass('BRup').removeClass('BRdn');
-                    $('#BRnavCntlBtm.BRnavCntl').animate({height:'30px'});
-                    $('.BRvavCntl').animate({opacity:1})
-                };
-            }
-        );
-        $('#BRnavCntlBtm').mouseover(function(){
-            if ($(this).hasClass('BRup')) {
-                $('.BRnavCntl').animate({opacity:1},250);
-            };
-        });
-        $('#BRnavCntlBtm').mouseleave(function(){
-            if ($(this).hasClass('BRup')) {
-                $('.BRnavCntl').animate({opacity:.25},250);
-            };
-        });
-        $('#BRnavCntlTop').mouseover(function(){
-            if ($(this).hasClass('BRdn')) {
-                $('.BRnavCntl').animate({opacity:1},250);
-            };
-        });
-        $('#BRnavCntlTop').mouseleave(function(){
-            if ($(this).hasClass('BRdn')) {
-                $('.BRnavCntl').animate({opacity:.25},250);
-            };
-        });
-    });
 
-BookReader.prototype.makeShareDiv = function()
+BookReader.prototype.buildShareDiv = function(jShareDiv)
 {
-    var html = [
-        '<div class="BRfloat" id="shareThis">',
-            '<div class="BRfloatHead">',
-                'Share',
-                '<a class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="shift">Close</span></a>',
-            '</div>',
-            '<p>Copy and paste one of these options to share this book elsewhere.</p>',
-            '<form method="post" action="">',
-                '<fieldset>',
-                    '<label for="pageview">Link to this page view:</label>',
-                    '<input type="text" name="pageview" id="pageview" value="http://thisisthelinktothispageview"/>',
-                '</fieldset>',
-                '<fieldset>',
-                    '<label for="booklink">Link to the book:</label>',
-                    '<input type="text" name="booklink" id="booklink" value="http://thisisthelinktothisbook"/>',
-                '</fieldset>',
-                '<fieldset>',
-                    '<label for="iframe">Embed a mini Book Reader:</label>',
-                    '<fieldset class="sub">',
-                        '<label class="sub">',
-                            '<input type="radio" name="pages" id="1page" checked="checked"/>',
-                            '1 page',
-                        '</label>',
-                        '<label class="sub">',
-                            '<input type="radio" name="pages" id="2page"/>',
-                            '2 pages',
-                        '</label>',
-                        '<label class="sub">',
-                            '<input type="checkbox" name="thispage" id="thispage"/>',
-                            'Open to this page?',
-                        '</label>',
-                    '</fieldset>',
-                    '<textarea cols="30" rows="4" name="iframe" id="iframe"><iframe src="http://thisisthestreamlink" width="480" height="480"></iframe></textarea>',
-                    '<p class="meta"><strong>NOTE:</strong> We\'ve tested EMBED on blogspot.com blogs as well as self-hosted Wordpress blogs. This feature will NOT work on wordpress.com blogs.</p>',
-                '</fieldset>',
-                '<fieldset class="center">',
-                    '<button type="button" onclick="$.fn.colorbox.close();">Finished</button>',
+    var pageView = document.location + '';
+    var bookView = (pageView + '').replace(/#.*/,'');
+    var self = this;
+    
+    var jForm = $([
+        '<p>Copy and paste one of these options to share this book elsewhere.</p>',
+        '<form method="post" action="">',
+            '<fieldset>',
+                '<label for="pageview">Link to this page view:</label>',
+                '<input type="text" name="pageview" id="pageview" value="' + pageView + '"/>',
+            '</fieldset>',
+            '<fieldset>',
+                '<label for="booklink">Link to the book:</label>',
+                '<input type="text" name="booklink" id="booklink" value="' + bookView + '"/>',
+            '</fieldset>',
+            '<fieldset>',
+                '<label for="iframe">Embed a mini Book Reader:</label>',
+                '<fieldset class="sub">',
+                    '<label class="sub">',
+                        '<input type="radio" name="pages" value="' + this.constMode1up + '" checked="checked"/>',
+                        '1 page',
+                    '</label>',
+                    '<label class="sub">',
+                        '<input type="radio" name="pages" value="' + this.constMode2up + '"/>',
+                        '2 pages',
+                    '</label>',
+                    '<label class="sub">',
+                        '<input type="checkbox" name="thispage" value="thispage"/>',
+                        'Open to this page?',
+                    '</label>',
                 '</fieldset>',
-            '</form>',
-        '</div>'
-    ].join('\n');
+                '<textarea cols="30" rows="4" name="iframe" class="BRframeEmbed"></textarea>',
+                '<p class="meta"><strong>NOTE:</strong> We\'ve tested EMBED on blogspot.com blogs as well as self-hosted Wordpress blogs. This feature will NOT work on wordpress.com blogs.</p>',
+            '</fieldset>',
+            '<fieldset class="center">',
+                '<button type="button" onclick="$.fn.colorbox.close();">Finished</button>',
+            '</fieldset>',
+        '</form>'].join('\n'));
+        
+    jForm.appendTo(jShareDiv);
+      
+    jForm.find('input').bind('change', function() {
+        var form = $(this).parents('form:first');
+        var params = {};
+        params.mode = $(form.find('input[name=pages]:checked')).val();
+        if (form.find('input[name=thispage]').attr('checked')) {
+            params.page = self.getPageNum(self.currentIndex());
+        }
+        
+        // $$$ changeable width/height to be added to share UI
+        var frameWidth = "480px";
+        var frameHeight = "430px";
+        form.find('.BRframeEmbed').val(self.getEmbedCode(frameWidth, frameHeight, params));
+    })
+    jForm.find('input[name=thispage]').trigger('change');
+    jForm.find('input, textarea').bind('focus', function() {
+        this.select();
+    });
     
-    return html;
+    jForm.appendTo(jShareDiv);
+    jForm = ''; // closure
+        
 }
 
-BookReader.prototype.makeAboutDiv = function() 
+// Should be overridden
+BookReader.prototype.buildInfoDiv = function(jInfoDiv) 
 {
-    var html = [
-        '<div class="BRfloat" id="aboutThis">',
-            '<div class="BRfloatHead">About this book',
-                '<a class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="shift">Close</span></a>',
-            '</div>',
-            '<div class="BRfloatBody">'
-    ];
-    
-    // Use 3rd-party provided function if available
-    if (this.getInfoDiv) {
-        html.push(this.getInfoDiv());
-    } else {
-        html = html.concat([
-                '<div class="BRfloatMeta">',
-                    '<div class="BRfloatTitle">',
-                        '<h2><a href="', br.bookUrl, '" class="title">', BookReader.util.escapeHTML(br.bookTitle), '</a></h2>',
-                    '</div>',
-                '</div>',
-        ]);
+    jInfoDiv.find('.BRfloatTitle a').attr({'href': this.bookUrl, 'alt': this.bookTitle}).text(this.bookTitle);
+}
+
+// Can be overriden
+BookReader.prototype.initUIStrings = function()
+{
+    // Navigation handlers will be bound after all UI is in place -- makes moving icons between
+    // the toolbar and nav bar easier
+        
+    // Setup tooltips -- later we could load these from a file for i18n
+    var titles = { '.logo': 'Go to Archive.org', // $$$ update after getting OL record
+                   '.zoom_in': 'Zoom in',
+                   '.zoom_out': 'Zoom out',
+                   '.onepg': 'One-page view',
+                   '.twopg': 'Two-page view',
+                   '.thumb': 'Thumbnail view',
+                   '.print': 'Print this page',
+                   '.embed': 'Embed BookReader',
+                   '.link': 'Link to this book (and page)',
+                   '.bookmark': 'Bookmark this page',
+                   '.read': 'Read this book aloud',
+                   '.share': 'Share this book',
+                   '.info': 'About this book',
+                   '.full': 'Show fullscreen',
+                   '.book_left': 'Flip left',
+                   '.book_right': 'Flip right',
+                   '.book_up': 'Page up',
+                   '.book_down': 'Page down',
+                   '.play': 'Play',
+                   '.pause': 'Pause',
+                   '.BRdn': 'Show/hide nav bar', // Would have to keep updating on state change to have just "Hide nav bar"
+                   '.BRup': 'Show/hide nav bar',
+                   '.book_top': 'First page',
+                   '.book_bottom': 'Last page'
+                  };
+    if ('rl' == this.pageProgression) {
+        titles['.book_leftmost'] = 'Last page';
+        titles['.book_rightmost'] = 'First page';
+    } else { // LTR
+        titles['.book_leftmost'] = 'First page';
+        titles['.book_rightmost'] = 'Last page';
     }
-    
-    html = html.concat([
-            '</div>', // BRfloatBody
-            '<div class="BRfloatFoot">'
-    ]);
-    
-    if (this.getInfoFooter) {
-        html.push(this.getInfoFooter());
-    } else {
-        html.push(
-                '<a href="http://openlibrary.org/dev/docs/bookreader">About the BookReader</a>'
-        );
+                  
+    for (var icon in titles) {
+        if (titles.hasOwnProperty(icon)) {
+            $('#BookReader').find(icon).attr('title', titles[icon]);
+        }
     }
-    
-    html = html.concat([
-            '</div>', // BRfloatfoot
-        '</div>' // BRfloat
-    ]);
-    
-    return html.join('\n');
 }
+})(jQuery);