Add rounded border and shadow to book cover on Safari. Bring more static CSS out...
[bookreader.git] / BookReader / BookReader.js
index 88e7924..707f007 100644 (file)
@@ -83,6 +83,7 @@ function BookReader() {
     this.constModeThumb = 3;
     
     // Zoom levels
+    // $$$ provide finer grained zooming
     this.reductionFactors = [0.5, 1, 2, 4, 8, 16];
 
     // Object to hold parameters related to 2up mode
@@ -140,6 +141,7 @@ BookReader.prototype.init = function() {
     }
     
     $("#BookReader").empty();
+        
     this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
     $("#BookReader").append("<div id='BRcontainer'></div>");
     $("#BRcontainer").append("<div id='BRpageview'></div>");
@@ -147,7 +149,7 @@ BookReader.prototype.init = function() {
     $("#BRcontainer").bind('scroll', this, function(e) {
         e.data.loadLeafs();
     });
-
+    
     this.setupKeyListeners();
     this.startLocationPolling();
 
@@ -196,8 +198,8 @@ BookReader.prototype.init = function() {
     // $$$ refactor this so it's enough to set the first index and call preparePageView
     //     (get rid of mode-specific logic at this point)
     if (1 == this.mode) {
-        this.resizePageView();
         this.firstIndex = startIndex;
+        this.prepareOnePageView();
         this.jumpToIndex(startIndex);
     } else if (3 == this.mode) {
         this.firstIndex = startIndex;
@@ -290,162 +292,27 @@ BookReader.prototype.drawLeafs = function() {
     
 }
 
-// setDragHandler()
+// bindGestures(jElement)
 //______________________________________________________________________________
-BookReader.prototype.setDragHandler = function(div) {
-    div.dragging = false;
+BookReader.prototype.bindGestures = function(jElement) {
 
-    $(div).unbind('mousedown').bind('mousedown', function(e) {
+    jElement.unbind('gesturechange').bind('gesturechange', function(e) {
         e.preventDefault();
-        
-        //console.log('mousedown at ' + e.pageY);
-
-        this.dragging = true;
-        this.prevMouseX = e.pageX;
-        this.prevMouseY = e.pageY;
-    
-        var startX    = e.pageX;
-        var startY    = e.pageY;
-        var startTop  = $('#BRcontainer').attr('scrollTop');
-        var startLeft =  $('#BRcontainer').attr('scrollLeft');
-
-    });
-        
-    $(div).unbind('mousemove').bind('mousemove', function(ee) {
-        ee.preventDefault();
-
-        // console.log('mousemove ' + ee.pageX + ',' + ee.pageY);
-        
-        var offsetX = ee.pageX - this.prevMouseX;
-        var offsetY = ee.pageY - this.prevMouseY;
-        
-        if (this.dragging) {
-            $('#BRcontainer').attr('scrollTop', $('#BRcontainer').attr('scrollTop') - offsetY);
-            $('#BRcontainer').attr('scrollLeft', $('#BRcontainer').attr('scrollLeft') - offsetX);
-        }
-        
-        this.prevMouseX = ee.pageX;
-        this.prevMouseY = ee.pageY;
-        
-    });
-    
-    $(div).unbind('mouseup').bind('mouseup', function(ee) {
-        ee.preventDefault();
-        //console.log('mouseup');
-
-        this.dragging = false;
-    });
-    
-    $(div).unbind('mouseleave').bind('mouseleave', function(e) {
-        e.preventDefault();
-        //console.log('mouseleave');
-
-        this.dragging = false;        
-    });
-    
-    $(div).unbind('mouseenter').bind('mouseenter', function(e) {
-        e.preventDefault();
-        //console.log('mouseenter');
-        
-        this.dragging = false;
-    });
-}
-
-// setDragHandler2UP()
-//______________________________________________________________________________
-BookReader.prototype.setDragHandler2UP = function(div) {
-    div.dragging = false;
-    
-    $(div).unbind('mousedown').bind('mousedown', function(e) {
-        e.preventDefault();
-        
-        //console.log('mousedown at ' + e.pageY);
-
-        this.dragStart = {x: e.pageX, y: e.pageY };
-        this.mouseDown = true;
-        this.dragging = false; // wait until drag distance
-        this.prevMouseX = e.pageX;
-        this.prevMouseY = e.pageY;
-    
-        var startX    = e.pageX;
-        var startY    = e.pageY;
-        var startTop  = $('#BRcontainer').attr('scrollTop');
-        var startLeft =  $('#BRcontainer').attr('scrollLeft');
-
-    });
-        
-    $(div).unbind('mousemove').bind('mousemove', function(ee) {
-        ee.preventDefault();
-
-        // console.log('mousemove ' + ee.pageX + ',' + ee.pageY);
-        
-        var offsetX = ee.pageX - this.prevMouseX;
-        var offsetY = ee.pageY - this.prevMouseY;
-        
-        var minDragDistance = 5; // $$$ constant
-
-        var distance = Math.max(Math.abs(offsetX), Math.abs(offsetY));
-                
-        if (this.mouseDown && (distance > minDragDistance)) {
-            //console.log('drag start!');
-            
-            this.dragging = true;
-        }
-        
-        if (this.dragging) {        
-            $('#BRcontainer').attr('scrollTop', $('#BRcontainer').attr('scrollTop') - offsetY);
-            $('#BRcontainer').attr('scrollLeft', $('#BRcontainer').attr('scrollLeft') - offsetX);
-            this.prevMouseX = ee.pageX;
-            this.prevMouseY = ee.pageY;
+        if (e.originalEvent.scale > 1.5) {
+            br.zoom(1);
+        } else if (e.originalEvent.scale < 0.6) {
+            br.zoom(-1);
         }
-        
-        
-    });
-    
-    /*
-    $(div).unbind('mouseup').bind('mouseup', function(ee) {
-        ee.preventDefault();
-        //console.log('mouseup');
-
-        this.dragging = false;
-        this.mouseDown = false;
-    });
-    */
-    
-    
-    $(div).unbind('mouseleave').bind('mouseleave', function(e) {
-        e.preventDefault();
-        //console.log('mouseleave');
-
-        this.dragging = false;  
-        this.mouseDown = false;
     });
-    
-    $(div).unbind('mouseenter').bind('mouseenter', function(e) {
-        e.preventDefault();
-        //console.log('mouseenter');
         
-        this.dragging = false;
-        this.mouseDown = false;
-    });
 }
 
 BookReader.prototype.setClickHandler2UP = function( element, data, handler) {
     //console.log('setting handler');
     //console.log(element.tagName);
     
-    $(element).unbind('click').bind('click', data, function(e) {
-        e.preventDefault();
-        
-        //console.log('click!');
-        
-        if (this.mouseDown && (!this.dragging)) {
-            //console.log('click not dragging!');
-            handler(e);
-        }
-        
-        this.dragging = false;
-        this.mouseDown = false;
+    $(element).unbind('tap').bind('tap', data, function(e) {
+        handler(e);
     });
 }
 
@@ -481,7 +348,7 @@ BookReader.prototype.drawLeafsOnePage = function() {
         leafTop += height +10;      
         leafBottom += 10;
     }
-
+    
     var firstIndexToDraw  = indicesToDisplay[0];
     this.firstIndex      = firstIndexToDraw;
     
@@ -530,8 +397,6 @@ BookReader.prototype.drawLeafsOnePage = function() {
             $(div).css('height', height+'px');
             //$(div).text('loading...');
             
-            this.setDragHandler(div);
-            
             $('#BRpageview').append(div);
 
             var img = document.createElement("img");
@@ -574,10 +439,12 @@ BookReader.prototype.drawLeafsOnePage = function() {
 
 // drawLeafsThumbnail()
 //______________________________________________________________________________
-BookReader.prototype.drawLeafsThumbnail = function() {
+// If seekIndex is defined, the view will be drawn with that page visible (without any
+// animated scrolling)
+BookReader.prototype.drawLeafsThumbnail = function( seekIndex ) {
     //alert('drawing leafs!');
     this.timer = null;
-
+    
     var viewWidth = $('#BRcontainer').attr('scrollWidth') - 20; // width minus buffer
 
     //console.log('top=' + scrollTop + ' bottom='+scrollBottom);
@@ -593,6 +460,9 @@ BookReader.prototype.drawLeafsThumbnail = function() {
     var leafMap = [];
     
     var self = this;
+    
+    // Will be set to top of requested seek index, if set
+    var seekTop;
 
     // Calculate the position of every thumbnail.  $$$ cache instead of calculating on every draw
     for (i=0; i<this.numLeafs; i++) {
@@ -621,12 +491,22 @@ BookReader.prototype.drawLeafsThumbnail = function() {
         rightPos += leafWidth + this.padding;
         if (rightPos > maxRight) { maxRight = rightPos; }
         leafIndex++;
+        
+        if (i == seekIndex) {
+            seekTop = bottomPos - this.padding - leafMap[currentRow].height;
+        }
     }
 
     // reset the bottom position based on thumbnails
     $('#BRpageview').height(bottomPos);
 
     var pageViewBuffer = Math.floor(($('#BRcontainer').attr('scrollWidth') - maxRight) / 2) - 14;
+
+    // If seekTop is defined, seeking was requested and target found
+    if (typeof(seekTop) != 'undefined') {
+        $('#BRcontainer').scrollTop( seekTop );
+    }
+        
     var scrollTop = $('#BRcontainer').attr('scrollTop');
     var scrollBottom = scrollTop + $('#BRcontainer').height();
 
@@ -634,6 +514,10 @@ BookReader.prototype.drawLeafsThumbnail = function() {
     var leafBottom = 0;
     var rowsToDisplay = [];
 
+    // Visible leafs with least/greatest index
+    var leastVisible = this.numLeafs - 1;
+    var mostVisible = 0;
+    
     // Determine the thumbnails in view
     for (i=0; i<leafMap.length; i++) {
         leafBottom += this.padding + leafMap[i].height;
@@ -643,6 +527,12 @@ BookReader.prototype.drawLeafsThumbnail = function() {
         if (topInView | bottomInView | middleInView) {
             //console.log('row to display: ' + j);
             rowsToDisplay.push(i);
+            if (leafMap[i].leafs[0].num < leastVisible) {
+                leastVisible = leafMap[i].leafs[0].num;
+            }
+            if (leafMap[i].leafs[leafMap[i].leafs.length - 1].num > mostVisible) {
+                mostVisible = leafMap[i].leafs[leafMap[i].leafs.length - 1].num;
+            }
         }
         if (leafTop > leafMap[i].top) { leafMap[i].top = leafTop; }
         leafTop = leafBottom;
@@ -658,12 +548,6 @@ BookReader.prototype.drawLeafsThumbnail = function() {
         if (firstRow-i >= 0) { rowsToDisplay.push(firstRow-i); }
     }
 
-    // Update hash, but only if we're currently displaying a leaf
-    // Hack that fixes #365790
-    if (this.displayedRows.length > 0) {
-        this.updateLocationHash();
-    }
-
     // Create the thumbnail divs and images (lazy loaded)
     var j;
     var row;
@@ -704,7 +588,7 @@ BookReader.prototype.drawLeafsThumbnail = function() {
                 // link to page in single page mode
                 link = document.createElement("a");
                 $(link).data('leaf', leaf);
-                $(link).bind('click', function(event) {
+                $(link).bind('tap', function(event) {
                     self.firstIndex = $(this).data('leaf');
                     self.switchMode(self.constMode1up);
                     event.preventDefault();
@@ -729,12 +613,6 @@ BookReader.prototype.drawLeafsThumbnail = function() {
             }   
         }
     }
-
-    // remove previous highlights
-    $('.BRpagedivthumb_highlight').removeClass('BRpagedivthumb_highlight');
-    
-    // highlight current page
-    $('#pagediv'+this.currentIndex()).addClass('BRpagedivthumb_highlight');
     
     // Remove thumbnails that are not to be displayed
     var k;
@@ -760,8 +638,30 @@ BookReader.prototype.drawLeafsThumbnail = function() {
         }
     }
     
+    // Update which page is considered current to make sure a visible page is the current one
+    var currentIndex = this.currentIndex();
+    // console.log('current ' + currentIndex);
+    // console.log('least visible ' + leastVisible + ' most visible ' + mostVisible);
+    if (currentIndex < leastVisible) {
+        this.setCurrentIndex(leastVisible);
+    } else if (currentIndex > mostVisible) {
+        this.setCurrentIndex(mostVisible);
+    }
+
     this.displayedRows = rowsToDisplay.slice();
     
+    // Update hash, but only if we're currently displaying a leaf
+    // Hack that fixes #365790
+    if (this.displayedRows.length > 0) {
+        this.updateLocationHash();
+    }
+
+    // remove previous highlights
+    $('.BRpagedivthumb_highlight').removeClass('BRpagedivthumb_highlight');
+    
+    // highlight current page
+    $('#pagediv'+this.currentIndex()).addClass('BRpagedivthumb_highlight');
+    
     this.lazyLoadThumbnails();
 
     // Update page number box.  $$$ refactor to function
@@ -984,6 +884,20 @@ BookReader.prototype.resizePageView = function() {
     //     e.g. does not preserve position in thumbnail mode
     //     See http://bugs.launchpad.net/bookreader/+bug/552972
     
+    switch (this.mode) {
+        case this.constMode1up:
+        case this.constMode2up:
+            this.resizePageView1up();
+            break;
+        case this.constModeThumb:
+            this.prepareThumbnailView( this.currentIndex() );
+            break;
+        default:
+            alert('Resize not implemented for this mode');
+    }
+}
+
+BookReader.prototype.resizePageView1up = function() {
     var i;
     var viewHeight = 0;
     //var viewWidth  = $('#BRcontainer').width(); //includes scrollBar
@@ -1009,7 +923,7 @@ BookReader.prototype.resizePageView = function() {
         if (width>viewWidth) viewWidth=width;
     }
     $('#BRpageview').height(viewHeight);
-    $('#BRpageview').width(viewWidth);    
+    $('#BRpageview').width(viewWidth);
 
     var newCenterY = scrollRatio*viewHeight;
     var newTop = Math.max(0, Math.floor( newCenterY - $('#BRcontainer').height()/2 ));
@@ -1024,14 +938,12 @@ BookReader.prototype.resizePageView = function() {
     
     //this.centerPageView();
     this.loadLeafs();
-    
-    // $$$ jump to index here? index is not preserved when resizing in thumb mode
-    
-    // Not really needed until there is 1up autofit
+        
     this.removeSearchHilites();
     this.updateSearchHilites();
 }
 
+
 // centerX1up()
 //______________________________________________________________________________
 // Returns the current offset of the viewport center in scaled document coordinates.
@@ -1118,7 +1030,6 @@ BookReader.prototype.zoomThumb = function(direction) {
     
     if (this.thumbColumns != oldColumns) {
         this.prepareThumbnailView();
-        this.jumpToIndex(this.currentIndex());
     }
 }
 
@@ -1252,7 +1163,8 @@ BookReader.prototype.jumpToIndex = function(index, pageX, pageY) {
                 rowHeight = 0;
                 leafIndex = 0;
             }
-            leafHeight = parseInt((this.getPageHeight(leaf)*this.thumbWidth)/this.getPageWidth(leaf), 10);
+
+            leafHeight = parseInt((this.getPageHeight(leafIndex)*this.thumbWidth)/this.getPageWidth(leafIndex), 10);
             if (leafHeight > rowHeight) { rowHeight = leafHeight; }
             if (leafIndex==0) { leafTop = bottomPos; }
             if (leafIndex==0) { bottomPos += this.padding + rowHeight; }
@@ -1329,7 +1241,6 @@ BookReader.prototype.switchMode = function(mode) {
     } else if (3 == mode) {
         this.reduce = this.quantizeReduce(this.reduce);
         this.prepareThumbnailView();
-        this.jumpToIndex(this.currentIndex());
     } else {
         // $$$ why don't we save autofit?
         this.twoPage.autofit = false; // Take zoom level from other mode
@@ -1346,19 +1257,24 @@ BookReader.prototype.prepareOnePageView = function() {
 
     // var startLeaf = this.displayedIndices[0];
     var startLeaf = this.currentIndex();
-    
+        
     $('#BRcontainer').empty();
     $('#BRcontainer').css({
         overflowY: 'scroll',
         overflowX: 'auto'
     });
-    
+        
     $("#BRcontainer").append("<div id='BRpageview'></div>");
+
+    // Attaches to first child - child must be present
+    $('#BRcontainer').dragscrollable();
+    this.bindGestures($('#BRcontainer'));
+
     // $$$ keep select enabled for now since disabling it breaks keyboard
     //     nav in FF 3.6 (https://bugs.edge.launchpad.net/bookreader/+bug/544666)
     // BookReader.util.disableSelect($('#BRpageview'));
     
-    this.resizePageView();
+    this.resizePageView();    
     
     this.jumpToIndex(startLeaf);
     this.displayedIndices = [];
@@ -1375,23 +1291,24 @@ BookReader.prototype.prepareThumbnailView = function() {
         overflowY: 'scroll',
         overflowX: 'auto'
     });
-    
+        
     $("#BRcontainer").append("<div id='BRpageview'></div>");
+    
+    $('#BRcontainer').dragscrollable();
+    this.bindGestures($('#BRcontainer'));
 
     // $$$ keep select enabled for now since disabling it breaks keyboard
     //     nav in FF 3.6 (https://bugs.edge.launchpad.net/bookreader/+bug/544666)
     // BookReader.util.disableSelect($('#BRpageview'));
     
-    var startLeaf = this.currentIndex();
     this.thumbWidth = this.getThumbnailWidth(this.thumbColumns);
     this.reduce = this.getPageWidth(0)/this.thumbWidth;
 
-    this.resizePageView();
-    
     this.displayedRows = [];
+
+    // Draw leafs with current index directly in view (no animating to the index)
+    this.drawLeafsThumbnail( this.currentIndex() );
     
-    // $$$ resizePageView will do a delayed load -- this will make it happen faster
-    this.drawLeafsThumbnail();
 }
 
 // prepareTwoPageView()
@@ -1444,6 +1361,10 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc
     // Add the two page view
     // $$$ Can we get everything set up and then append?
     $('#BRcontainer').append('<div id="BRtwopageview"></div>');
+    
+    // Attaches to first child, so must come after we add the page view
+    $('#BRcontainer').dragscrollable();
+    this.bindGestures($('#BRcontainer'));
 
     // $$$ calculate first then set
     $('#BRtwopageview').css( {
@@ -1465,26 +1386,17 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc
     
     this.twoPage.coverDiv = document.createElement('div');
     $(this.twoPage.coverDiv).attr('id', 'BRbookcover').css({
-        border: '1px solid rgb(68, 25, 17)',
         width:  this.twoPage.bookCoverDivWidth + 'px',
         height: this.twoPage.bookCoverDivHeight+'px',
         visibility: 'visible',
         position: 'absolute',
-        backgroundColor: '#663929',
         left: this.twoPage.bookCoverDivLeft + 'px',
         top: this.twoPage.bookCoverDivTop+'px',
-        MozBorderRadiusTopleft: '7px',
-        MozBorderRadiusTopright: '7px',
-        MozBorderRadiusBottomright: '7px',
-        MozBorderRadiusBottomleft: '7px'
     }).appendTo('#BRtwopageview');
     
     this.leafEdgeR = document.createElement('div');
     this.leafEdgeR.className = 'leafEdgeR'; // $$$ the static CSS should be moved into the .css file
     $(this.leafEdgeR).css({
-        borderStyle: 'solid solid solid none',
-        borderColor: 'rgb(51, 51, 34)',
-        borderWidth: '1px 1px 1px 0px',
         background: 'transparent url(' + this.imagesBaseURL + 'right_edges.png) repeat scroll 0% 0%',
         width: this.twoPage.leafEdgeWidthR + 'px',
         height: this.twoPage.height-1 + 'px',
@@ -1496,10 +1408,7 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc
     
     this.leafEdgeL = document.createElement('div');
     this.leafEdgeL.className = 'leafEdgeL';
-    $(this.leafEdgeL).css({ // $$$ static CSS should be moved to file
-        borderStyle: 'solid none solid solid',
-        borderColor: 'rgb(51, 51, 34)',
-        borderWidth: '1px 0px 1px 1px',
+    $(this.leafEdgeL).css({
         background: 'transparent url(' + this.imagesBaseURL + 'left_edges.png) repeat scroll 0% 0%',
         width: this.twoPage.leafEdgeWidthL + 'px',
         height: this.twoPage.height-1 + 'px',
@@ -1510,11 +1419,9 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc
 
     div = document.createElement('div');
     $(div).attr('id', 'BRbookspine').css({
-        border:          '1px solid rgb(68, 25, 17)',
         width:           this.twoPage.bookSpineDivWidth+'px',
         height:          this.twoPage.bookSpineDivHeight+'px',
         position:        'absolute',
-        backgroundColor: 'rgb(68, 25, 17)',
         left:            this.twoPage.bookSpineDivLeft+'px',
         top:             this.twoPage.bookSpineDivTop+'px'
     }).appendTo('#BRtwopageview');
@@ -1833,26 +1740,13 @@ BookReader.prototype.currentIndex = function() {
     }
 }
 
-/*
-// Returns the current index if visible, or the logically current visible
-// thumbnail
-BookReader.prototype.currentIndexOrVisibleThumb = function() {
-    // XXX finish implementation
-    var index = this.currentIndex();
-    if (!this.indexIsVisbleThumb(this.currentIndex()) {
-        // XXX search for visible
-    }
-    return index;
-}
-
-// Returns true if the given index is visible
-BookReader.prototype.indexIsVisibleThumb = function(index, onlyCompletelyVisible = true) {
-    // XXX implement
-    // $$$ I'd prefer to go through a stored leaf map instead of DOM
-    
-    
+// setCurrentIndex(index)
+//______________________________________________________________________________
+// Sets the idea of current index without triggering other actions such as animation.
+// Compare to jumpToIndex which animates to that index
+BookReader.prototype.setCurrentIndex = function(index) {
+    this.firstIndex = index;
 }
-*/
 
 
 // right()
@@ -2292,32 +2186,19 @@ BookReader.prototype.flipRightToLeft = function(newIndexL, newIndexR) {
 // setMouseHandlers2UP
 //______________________________________________________________________________
 BookReader.prototype.setMouseHandlers2UP = function() {
-    /*
-    $(this.prefetchedImgs[this.twoPage.currentIndexL]).bind('dblclick', function() {
-        //self.prevPage();
-        self.autoStop();
-        self.left();
-    });
-    $(this.prefetchedImgs[this.twoPage.currentIndexR]).bind('dblclick', function() {
-        //self.nextPage();'
-        self.autoStop();
-        self.right();        
-    });
-    */
-    
-    this.setDragHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexL] );
     this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexL],
         { self: this },
         function(e) {
             e.data.self.left();
+            e.preventDefault();
         }
     );
         
-    this.setDragHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR] );
     this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR],
         { self: this },
         function(e) {
             e.data.self.right();
+            e.preventDefault();
         }
     );
 }
@@ -3226,7 +3107,7 @@ BookReader.prototype.initToolbar = function(mode, ui) {
     
     this.updateToolbarZoom(this.reduce); // Pretty format
         
-    if (ui == "embed") {
+    if (ui == "embed" || ui == "touch") {
         $("#BookReader a.logo").attr("target","_blank");
     }