Merge branch 'newui' of git@github.com:openlibrary/bookreader into newui
[bookreader.git] / BookReader / BookReader.js
index 5855e56..f2cc8a8 100644 (file)
@@ -93,12 +93,22 @@ function BookReader() {
     
     // Zoom levels
     // $$$ provide finer grained zooming
+    /*
     this.reductionFactors = [ {reduce: 0.5, autofit: null},
                               {reduce: 1, autofit: null},
                               {reduce: 2, autofit: null},
                               {reduce: 4, autofit: null},
                               {reduce: 8, autofit: null},
                               {reduce: 16, autofit: null} ];
+    */
+    /* The autofit code ensures that fit to width and fit to height will be available */
+    this.reductionFactors = [ {reduce: 0.5, autofit: null},
+                          {reduce: 1, autofit: null},
+                          {reduce: 2, autofit: null},
+                          {reduce: 3, autofit: null},
+                          {reduce: 4, autofit: null},
+                          {reduce: 6, autofit: null} ];
+
 
     // Object to hold parameters related to 1up mode
     this.onePage = {
@@ -202,6 +212,7 @@ BookReader.prototype.init = function() {
 
     $(window).bind('resize', this, function(e) {
         //console.log('resize!');
+
         if (1 == e.data.mode) {
             //console.log('centering 1page view');
             if (e.data.autofit) {
@@ -255,9 +266,7 @@ BookReader.prototype.init = function() {
         this.firstIndex = startIndex;
         this.prepareThumbnailView();
         this.jumpToIndex(startIndex);
-    } else {
-        //this.resizePageView();
-        
+    } else {        
         this.displayedIndices=[0];
         this.firstIndex = startIndex;
         this.displayedIndices = [this.firstIndex];
@@ -273,11 +282,12 @@ BookReader.prototype.init = function() {
     // it should start (doesn't jump after init)
     this.initNavbar();
     this.bindNavigationHandlers();
-    
+
     // Start AJAX request for OL data
     if (this.getOpenLibraryRecord) {
         this.getOpenLibraryRecord(this.gotOpenLibraryRecord);
     }
+
 }
 
 BookReader.prototype.setupKeyListeners = function() {
@@ -370,7 +380,7 @@ BookReader.prototype.setClickHandler2UP = function( element, data, handler) {
     //console.log('setting handler');
     //console.log(element.tagName);
     
-    $(element).unbind('tap').bind('tap', data, function(e) {
+    $(element).unbind('click').bind('click', data, function(e) {
         handler(e);
     });
 }
@@ -465,6 +475,7 @@ BookReader.prototype.drawLeafsOnePage = function() {
 
             var img = document.createElement("img");
             img.src = this._getPageURI(index, this.reduce, 0);
+            $(img).addClass('BRnoselect');
             $(img).css('width', width+'px');
             $(img).css('height', height+'px');
             $(div).append(img);
@@ -656,10 +667,8 @@ BookReader.prototype.drawLeafsThumbnail = function( seekIndex ) {
                     self.firstIndex = $(this).data('leaf');
                     self.switchMode(self.constMode1up);
                     event.preventDefault();
-                });
-                
-                // $$$ we don't actually go to this URL (click is handled in handler above)
-                link.href = '#page/' + (this.getPageNum(leaf)) +'/mode/1up' ;
+                    event.stopPropagation();
+                });                
                 $(div).append(link);
                 
                 $('#BRpageview').append(div);
@@ -1239,6 +1248,8 @@ BookReader.prototype.jumpToIndex = function(index, pageX, pageY) {
 
     this.willChangeToIndex(index);
 
+    this.ttsStop();
+
     if (this.constMode2up == this.mode) {
         this.autoStop();
         
@@ -1351,8 +1362,6 @@ BookReader.prototype.switchMode = function(mode) {
         // this.twoPage.autofit = null; // Take zoom level from other mode
         this.twoPageCalculateReductionFactors();
         this.reduce = this.quantizeReduce(this.reduce, this.twoPage.reductionFactors);
-        $('button.thumb').show();
-        $('button.twopg').hide();
         this.prepareTwoPageView();
         this.twoPageCenterView(0.5, 0.5); // $$$ TODO preserve center
     }
@@ -1471,7 +1480,7 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc
     $('#BRcontainer').append('<div id="BRtwopageview"></div>');
     
     // Attaches to first child, so must come after we add the page view
-    $('#BRcontainer').dragscrollable();
+    //$('#BRcontainer').dragscrollable();
     this.bindGestures($('#BRcontainer'));
 
     // $$$ calculate first then set
@@ -1496,7 +1505,7 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc
     $(this.twoPage.coverDiv).attr('id', 'BRbookcover').css({
         width:  this.twoPage.bookCoverDivWidth + 'px',
         height: this.twoPage.bookCoverDivHeight+'px',
-        visibility: 'visible',
+        visibility: 'visible'
     }).appendTo('#BRtwopageview');
     
     this.leafEdgeR = document.createElement('div');
@@ -1569,7 +1578,7 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc
     
     //this.indicesToDisplay=[firstLeaf, firstLeaf+1];
     //console.log('indicesToDisplay: ' + this.indicesToDisplay[0] + ' ' + this.indicesToDisplay[1]);
-    
+        
     this.drawLeafsTwoPage();
     this.updateToolbarZoom(this.reduce);
     
@@ -1664,7 +1673,7 @@ BookReader.prototype.calculateSpreadSize = function() {
         // set based on reduction factor
         spreadSize = this.getSpreadSizeFromReduce(firstIndex, secondIndex, this.reduce);
     }
-    
+        
     // Both pages together
     this.twoPage.height = spreadSize.height;
     this.twoPage.width = spreadSize.width;
@@ -1736,7 +1745,7 @@ BookReader.prototype.getIdealSpreadSize = function(firstIndex, secondIndex) {
         height: this._getPageHeight(secondIndex),
         width: this._getPageWidth(secondIndex)
     }
-    
+        
     var firstIndexRatio  = first.height / first.width;
     var secondIndexRatio = second.height / second.width;
     //console.log('firstIndexRatio = ' + firstIndexRatio + ' secondIndexRatio = ' + secondIndexRatio);
@@ -1744,10 +1753,8 @@ BookReader.prototype.getIdealSpreadSize = function(firstIndex, secondIndex) {
     var ratio;
     if (Math.abs(firstIndexRatio - canon5Dratio) < Math.abs(secondIndexRatio - canon5Dratio)) {
         ratio = firstIndexRatio;
-        //console.log('using firstIndexRatio ' + ratio);
     } else {
         ratio = secondIndexRatio;
-        //console.log('using secondIndexRatio ' + ratio);
     }
 
     var totalLeafEdgeWidth = parseInt(this.numLeafs * 0.1);
@@ -2375,8 +2382,17 @@ BookReader.prototype.setMouseHandlers2UP = function() {
     this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexL],
         { self: this },
         function(e) {
-            e.data.self.ttsStop();
-            e.data.self.left();
+            if (e.button == 2) {
+                // right click
+                return;
+            }
+            
+            var autofitReduce = e.data.self.twoPageGetAutofitReduce();
+            // Don't trigger if zoomed in
+            if (e.data.self.reduce >= e.data.self.twoPageGetAutofitReduce()) {                
+                e.data.self.ttsStop();
+                e.data.self.left();                
+            }
             e.preventDefault();
         }
     );
@@ -2384,8 +2400,17 @@ BookReader.prototype.setMouseHandlers2UP = function() {
     this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR],
         { self: this },
         function(e) {
-            e.data.self.ttsStop();
-            e.data.self.right();
+            if (e.button == 2) {
+                // right click
+                return;
+            }
+
+            var autofitReduce = e.data.self.twoPageGetAutofitReduce();
+            // Don't trigger if zoomed in
+            if (e.data.self.reduce >= e.data.self.twoPageGetAutofitReduce()) {                
+                e.data.self.ttsStop();
+                e.data.self.right();
+            }
             e.preventDefault();
         }
     );
@@ -2409,7 +2434,7 @@ BookReader.prototype.prefetchImg = function(index) {
     if (loadImage) {
         //console.log('prefetching ' + index);
         var img = document.createElement("img");
-        img.className = 'BRpageimage';
+        $(img).addClass('BRpageimage').addClass('BRnoselect');
         if (index < 0 || index > (this.numLeafs - 1) ) {
             // Facing page at beginning or end, or beyond
             $(img).css({
@@ -2634,8 +2659,11 @@ BookReader.prototype.getPageWidth2UP = function(index) {
 //______________________________________________________________________________
 BookReader.prototype.search = function(term) {
     //console.log('search called with term=' + term);
+    
+    $('#textSrch').blur(); //cause mobile safari to hide the keyboard     
+    
     var url = 'http://'+this.server.replace(/:.+/, ''); //remove the port and userdir
-    url    += '/~edward/inside_jsonp.php?item_id='+this.bookId;
+    url    += '/fulltext/inside.php?item_id='+this.bookId;
     url    += '&doc='+this.subPrefix;   //TODO: test with subitem
     url    += '&path='+this.bookPath.replace(new RegExp('/'+this.subPrefix+'$'), ''); //remove subPrefix from end of path
     url    += '&q='+escape(term);
@@ -2646,14 +2674,12 @@ 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:'BRSearchCallback'});    
+    this.ttsAjax = $.ajax({url:url, dataType:'jsonp', jsonpCallback:'br.BRSearchCallback'});    
 }
 
 // BRSearchCallback()
 //______________________________________________________________________________
-// Unfortunately, we can't pass 'br.searchCallback' to our search service,
-// because it can't handle the '.'
-function BRSearchCallback(results) {    
+BookReader.prototype.BRSearchCallback = function(results) {
     //console.log('got ' + results.matches.length + ' results');
     br.removeSearchResults();
     br.searchResults = results; 
@@ -2860,9 +2886,10 @@ BookReader.prototype.updateSearchHilites2UP = function() {
     var i, j;
     for (i=0; i<results.matches.length; i++) {
         //console.log(results.matches[i].par[0]);
+        //TODO: loop over all par objects
+        var pageIndex = this.leafNumToIndex(results.matches[i].par[0].page);        
         for (j=0; j<results.matches[i].par[0].boxes.length; j++) {
             var box = results.matches[i].par[0].boxes[j];
-            var pageIndex = this.leafNumToIndex(box.page);
             if (jQuery.inArray(pageIndex, this.displayedIndices) >= 0) {
                 if (null == box.div) {
                     //create a div for the search highlight, and stash it in the box object
@@ -3090,6 +3117,8 @@ BookReader.prototype.showBookmarkCode = function() {
 //______________________________________________________________________________
 BookReader.prototype.autoToggle = function() {
 
+    this.ttsStop();
+
     var bComingFrom1up = false;
     if (2 != this.mode) {
         bComingFrom1up = true;
@@ -3261,7 +3290,8 @@ BookReader.prototype.initNavbar = function() {
         +         '<button class="BRicon onepg"></button>'
         +         '<button class="BRicon twopg"></button>'
         +         '<button class="BRicon thumb"></button>'
-        +         '<button class="BRicon fit"></button>'
+        // $$$ not yet implemented
+        //+         '<button class="BRicon fit"></button>'
         +         '<button class="BRicon zoom_in"></button>'
         +         '<button class="BRicon zoom_out"></button>'
         +         '<button class="BRicon book_left"></button>'
@@ -3371,10 +3401,13 @@ BookReader.prototype.initNavbar = function() {
     .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();
     });
     
@@ -3461,6 +3494,9 @@ BookReader.prototype.addSearchResult = function(queryString, pageIndex) {
         shadow: false
     })
     .hover( function() {
+                // remove from other markers then turn on just for this
+                // XXX should be done when nav slider moves
+                $('.search,.chapter').removeClass('front');
                 $(this).addClass('front');
             }, function() {
                 $(this).removeClass('front');
@@ -3525,6 +3561,8 @@ BookReader.prototype.addChapter = function(chapterTitle, pageNumber, pageIndex)
         shadow: false
     })
     .hover( function() {
+            // remove hover effect from other markers then turn on just for this
+            $('.search,.chapter').removeClass('front');
                 $(this).addClass('front');
             }, function() {
                 $(this).removeClass('front');
@@ -3594,7 +3632,7 @@ BookReader.prototype.addChapterFromEntry = function(tocEntryObject) {
 BookReader.prototype.initToolbar = function(mode, ui) {
 
     // $$$mang should be contained within the BookReader div instead of body
-    var readIcon = ''
+    var readIcon = '';
     if (!navigator.userAgent.match(/mobile/i)) {
         readIcon = "<button class='BRicon read modal'></button>";
     }
@@ -3602,16 +3640,17 @@ BookReader.prototype.initToolbar = function(mode, ui) {
     $("body").append(
           "<div id='BRtoolbar'>"
         +   "<span id='BRtoolbarbuttons'>"
-        /* XXXmang integrate search */
-        +     "<form action='javascript:' id='booksearch'><input type='search' id='textSrch' name='textSrch' val='' placeholder='Search inside'/><button type='submit' id='btnSrch' name='btnSrch'>GO</button></form>"
-        // XXXmang icons incorrect or handlers wrong
+        +     "<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>"
+        +     "<button class='BRicon play'></button>"
+        +     "<button class='BRicon pause'></button>"
         +     "<button class='BRicon info'></button>"
         +     "<button class='BRicon share'></button>"
         +     readIcon
-        +     "<button class='BRicon full'></button>"
+        //+     "<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>"
         + "</div>"
         /*
         + "<div id='BRzoomer'>"
@@ -3626,6 +3665,8 @@ BookReader.prototype.initToolbar = function(mode, ui) {
         + "</div>"
         */
         );
+
+    $('#BRtoolbar .pause').hide();    
     
     this.updateToolbarZoom(this.reduce); // Pretty format
         
@@ -3653,7 +3694,7 @@ BookReader.prototype.initToolbar = function(mode, ui) {
                    '.embed': 'Embed BookReader',
                    '.link': 'Link to this book (and page)',
                    '.bookmark': 'Bookmark this page',
-                   '.read': 'Allow BookReader to read this aloud',
+                   '.read': 'Read this book aloud',
                    '.full': 'Show fullscreen',
                    '.book_left': 'Flip left',
                    '.book_right': 'Flip right',
@@ -3689,7 +3730,22 @@ BookReader.prototype.initToolbar = function(mode, ui) {
     if ( ! (this.canSwitchToMode(this.constMode2up) || this.canSwitchToMode(this.constModeThumb)) ) {
         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')
+    );
+    
 
+    
     // Switch to requested mode -- binds other click handlers
     //this.switchToolbarMode(mode);
     
@@ -3851,24 +3907,37 @@ BookReader.prototype.bindNavigationHandlers = function() {
     });
     
     jIcons.filter('.zoom_in').bind('click', function() {
+        self.ttsStop();
         self.zoom(1);
         return false;
     });
     
     jIcons.filter('.zoom_out').bind('click', function() {
+        self.ttsStop();
         self.zoom(-1);
         return false;
     });
     
-    // XXX fix integration
-    $('#booksearch').bind('submit', function() {
-        self.search($('#textSrch').val());
-    });
-
+    this.initSwipeData();
     $('#BookReader').die('mousemove.navigation').live('mousemove.navigation',
         { 'br': this },
         this.navigationMousemoveHandler
     );
+    
+    $('.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();
 }
 
 // unbindNavigationHandlers
@@ -3895,6 +3964,125 @@ BookReader.prototype.navigationMousemoveHandler = function(event) {
     }
 }
 
+BookReader.prototype.initSwipeData = function(clientX, clientY) {
+    /*
+     * Based on the really quite awesome "Today's Guardian" at http://guardian.gyford.com/
+     */
+    this._swipe = {
+        mightBeSwiping: false,
+        didSwipe: false,
+        mightBeDraggin: false,
+        didDrag: false,
+        startTime: (new Date).getTime(),
+        startX: clientX,
+        startY: clientY,
+        lastX: clientX,
+        lastY: clientY,
+        deltaX: 0,
+        deltaY: 0,
+        deltaT: 0
+    }
+}
+
+BookReader.prototype.swipeMousedownHandler = function(event) {
+    //console.log('swipe mousedown');
+    //console.log(event);
+    
+    var self = event.data['br'];
+    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();
+    }
+}
+
+BookReader.prototype.swipeMousemoveHandler = function(event) {
+    //console.log('swipe move ' + event.clientX + ',' + event.clientY);
+
+    var _swipe = event.data['br']._swipe;
+    if (! _swipe.mightBeSwiping) {
+        return;
+    }
+    
+    // Update swipe data
+    _swipe.deltaX = event.clientX - _swipe.startX;
+    _swipe.deltaY = event.clientY - _swipe.startY;
+    _swipe.deltaT = (new Date).getTime() - _swipe.startTime;
+    
+    var absX = Math.abs(_swipe.deltaX);
+    var absY = Math.abs(_swipe.deltaY);
+    
+    // Minimum distance in the amount of tim to trigger the swipe
+    var minSwipeLength = Math.min($('#BookReader').width() / 5, 80);
+    var maxSwipeTime = 400;
+    
+    // Check for horizontal swipe
+    if (absX > absY && (absX > minSwipeLength) && _swipe.deltaT < maxSwipeTime) {
+        //console.log('swipe! ' + _swipe.deltaX + ',' + _swipe.deltaY + ' ' + _swipe.deltaT + 'ms');
+        
+        _swipe.mightBeSwiping = false; // only trigger once
+        _swipe.didSwipe = true;
+        if (event.data['br'].mode == event.data['br'].constMode2up) {
+            if (_swipe.deltaX < 0) {
+                event.data['br'].right();
+            } else {
+                event.data['br'].left();
+            }
+        }
+    }
+    
+    if ( _swipe.deltaT > maxSwipeTime && !_swipe.didSwipe) {
+        if (_swipe.mightBeDragging) {        
+            // Dragging
+            _swipe.didDrag = true;
+            $('#BRcontainer')
+            .scrollTop($('#BRcontainer').scrollTop() - event.clientY + _swipe.lastY)
+            .scrollLeft($('#BRcontainer').scrollLeft() - event.clientX + _swipe.lastX);            
+        }
+    }
+    _swipe.lastX = event.clientX;
+    _swipe.lastY = event.clientY;
+}
+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;
+    if (_swipe.didSwipe || _swipe.didDrag) {
+        // Swallow event if completed swipe gesture
+        event.preventDefault();
+        event.stopPropagation();
+    }
+}
+
+BookReader.prototype.bindMozTouchHandlers = function() {
+    var self = this;
+    
+    // Currently only want touch handlers in 2up
+    $('#BookReader').bind('MozTouchDown', function(event) {
+        //console.log('MozTouchDown ' + event.originalEvent.streamId + ' ' + event.target + ' ' + event.clientX + ',' + event.clientY);
+        if (this.mode == this.constMode2up) {
+            event.preventDefault();
+        }
+    })
+    .bind('MozTouchMove', function(event) {
+        //console.log('MozTouchMove - ' + event.originalEvent.streamId + ' ' + event.target + ' ' + event.clientX + ',' + event.clientY)
+        if (this.mode == this.constMode2up) { 
+            event.preventDefault();
+        }
+    })
+    .bind('MozTouchUp', function(event) {
+        //console.log('MozTouchUp - ' + event.originalEvent.streamId + ' ' + event.target + ' ' + event.clientX + ',' + event.clientY);
+        if (this.mode = this.constMode2up) {
+            event.preventDefault();
+        }
+    });
+}
+
 // navigationIsVisible
 //______________________________________________________________________________
 // Returns true if the navigation elements are currently visible
@@ -4367,15 +4555,16 @@ BookReader.prototype._getPageURI = function(index, reduce, rotate) {
 BookReader.prototype.gotOpenLibraryRecord = function(self, olObject) {
     // $$$ could refactor this so that 'this' is available
     if (olObject) {
+        // console.log(olObject);
         if (olObject['table_of_contents']) {
             // XXX check here that TOC is valid
             self.updateTOC(olObject['table_of_contents']);
         }
-    }
-    
-    // $$$mang cleanup
-    $('#BRreturn a').attr('href', 'http://openlibrary.org' + olObject.key);
 
+        // $$$mang cleanup
+        this.bookUrl = 'http://openlibrary.org' + olObject.key;
+        $('#BRreturn a').attr('href', this.bookUrl);
+    }
 }
 
 // Library functions
@@ -4412,6 +4601,15 @@ BookReader.util = {
         return (outer.document || outer);
     },
     
+    escapeHTML: function (str) {
+        return(
+            str.replace(/&/g,'&amp;').
+                replace(/>/g,'&gt;').
+                replace(/</g,'&lt;').
+                replace(/"/g,'&quot;')
+        );
+    },
+    
     decodeURIComponentPlus: function(value) {
         // Decodes a URI component and converts '+' to ' '
         return decodeURIComponent(value).replace(/\+/g, ' ');
@@ -4428,6 +4626,9 @@ BookReader.util = {
 // ttsToggle()
 //______________________________________________________________________________
 BookReader.prototype.ttsToggle = function () {
+
+    this.autoStop();
+
     if (false == this.ttsPlaying) {
         this.ttsPlaying = true;
         this.showProgressPopup('Loading audio...');    
@@ -4530,9 +4731,7 @@ BookReader.prototype.showProgressPopup = function(msg) {
     this.popup = document.createElement("div");
     $(this.popup).css({
         top:      ($('#BookReader').height()*0.5-100) + 'px',
-        left:     ($('#BookReader').width()-300)*0.5 + 'px',
-        width:    '300px',
-        border:   '2px solid black'
+        left:     ($('#BookReader').width()-300)*0.5 + 'px'
     }).attr('className', 'BRprogresspopup');
 
     var bar = document.createElement("div");
@@ -4853,22 +5052,28 @@ BookReader.prototype.ttsStartPolling = function () {
         },3000);
     };
     $().ready(function(){
-        $('#BRtoolbar').animate({top:0},3000).animate({top:-40});
-        $('#BRnav').animate({bottom:0},3000).animate({bottom:-53});
+    /*
+        $('#BRtoolbar').delay(3000).animate({top:-40});
+        $('#BRnav').delay(3000).animate({bottom:-53});
         changeArrow();
-        $('.BRnavCntl').animate({opacity:1},3000).animate({height:'43px'}).animate({opacity:1},1000).animate({opacity:.25},1000);
+        $('.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:-53});
+                    $('#BRnav').animate({bottom:-55});
                     $('#BRnavCntlBtm').addClass('BRup').removeClass('BRdn');
-                    $('.BRnavCntl').animate({height:'43px'}).animate({opacity:1},1000).animate({opacity:.25},1000);
+                    $('#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');
-                    $('.BRnavCntl').animate({opacity:1,height:'30px'});
+                    $('#BRnavCntlTop').addClass('BRup').removeClass('BRdn');
+                    $('#BRnavCntlBtm.BRnavCntl').animate({height:'30px'});
+                    $('.BRvavCntl').animate({opacity:1})
                 };
             }
         );
@@ -4882,5 +5087,105 @@ BookReader.prototype.ttsStartPolling = function () {
                 $('.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()
+{
+    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>',
+                '</fieldset>',
+            '</form>',
+        '</div>'
+    ].join('\n');
+    
+    return html;
+}
+
+BookReader.prototype.makeAboutDiv = function() 
+{
+    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>',
+        ]);
+    }
+    
+    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>'
+        );
+    }
+    
+    html = html.concat([
+            '</div>', // BRfloatfoot
+        '</div>' // BRfloat
+    ]);
+    
+    return html.join('\n');
+}