X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=GnuBook%2FGnuBook.js;h=ee9401befcdf3e5dec3fcfb889433a132bb9812c;hb=0fd1bd905b41cda83354f82d9e08b57a17ab7bc3;hp=2dbb993b271762e9ab102e7c76d6c7776fcda3ea;hpb=84f05132e20cf13758b1c17a1b75b44e39c9ba8f;p=bookreader.git diff --git a/GnuBook/GnuBook.js b/GnuBook/GnuBook.js index 2dbb993..ee9401b 100644 --- a/GnuBook/GnuBook.js +++ b/GnuBook/GnuBook.js @@ -72,8 +72,14 @@ function GnuBook() { this.constMode1up = 1; this.constMode2up = 2; + // Zoom levels + this.reductionFactors = [0.5, 1, 2, 4, 8, 16]; + // Object to hold parameters related to 2up mode this.twoPage = { + coverInternalPadding: 10, // Width of cover + coverExternalPadding: 10, // Padding outside of cover + bookSpineDivWidth: 30, // Width of book spine $$$ consider sizing based on book length autofit: true }; }; @@ -139,7 +145,26 @@ GnuBook.prototype.init = function() { e.data.loadLeafs(); } else { //console.log('drawing 2 page view'); - e.data.prepareTwoPageView(); + + // We only need to prepare again in autofit (size of spread changes) + if (e.data.twoPage.autofit) { + e.data.prepareTwoPageView(); + } else { + // Re-center if the scrollbars have disappeared + var center = e.data.twoPageGetViewCenter(); + var doRecenter = false; + if (e.data.twoPage.totalWidth < $('#GBcontainer').attr('clientWidth')) { + center.percentageX = 0.5; + doRecenter = true; + } + if (e.data.twoPage.totalHeight < $('#GBcontainer').attr('clientHeight')) { + center.percentageY = 0.5; + doRecenter = true; + } + if (doRecenter) { + e.data.twoPageCenterView(center.percentageX, center.percentageY); + } + } } }); @@ -160,7 +185,6 @@ GnuBook.prototype.init = function() { //console.log('titleLeaf: %d', this.titleLeaf); //console.log('displayedIndices: %s', this.displayedIndices); this.prepareTwoPageView(); - //if (this.auto) this.nextPage(); } // Enact other parts of initial params @@ -405,11 +429,8 @@ GnuBook.prototype.drawLeafsOnePage = function() { } else { $("#GBpagenum").val(''); } - - //var centerY = this.centerY1up(); - //var centerX = this.centerX1up(); - //console.log('draw center ' + centerY + ',' + centerX); - //console.log('scroll left ' + $('#GBcontainer').attr('scrollLeft')); + + this.updateToolbarZoom(this.reduce); } @@ -417,39 +438,30 @@ GnuBook.prototype.drawLeafsOnePage = function() { //______________________________________________________________________________ GnuBook.prototype.drawLeafsTwoPage = function() { console.log('drawing two leafs!'); // XXX - + var scrollTop = $('#GBtwopageview').attr('scrollTop'); var scrollBottom = scrollTop + $('#GBtwopageview').height(); console.log('drawLeafsTwoPage: this.currrentLeafL ' + this.twoPage.currentIndexL); // XXX + // XXX we should use calculated values in this.twoPage (recalc if necessary) + var indexL = this.twoPage.currentIndexL; var heightL = this.getPageHeight(indexL); var widthL = this.getPageWidth(indexL); var leafEdgeWidthL = this.leafEdgeWidth(indexL); var leafEdgeWidthR = this.twoPage.edgeWidth - leafEdgeWidthL; - var bookCoverDivWidth = this.twoPage.width*2+20 + this.twoPage.edgeWidth; // $$$ hardcoded cover width + //var bookCoverDivWidth = this.twoPage.width*2 + 20 + this.twoPage.edgeWidth; // $$$ hardcoded cover width + var bookCoverDivWidth = this.twoPage.bookCoverDivWidth; //console.log(leafEdgeWidthL); - var middle, top, bookCoverDivLeft; - if (this.twoPage.autofit) { - middle = ($('#GBtwopageview').attr('clientWidth') >> 1); - top = ($('#GBtwopageview').attr('clientHeight') - this.twoPage.height) >> 1; - bookCoverDivLeft = ($('#GBcontainer').attr('clientWidth') - bookCoverDivWidth) >> 1; - } else { - // $$$ add external padding - middle = this.twoPage.width / 2; - top = 0; - bookCoverDivLeft = 0; - - // XXX add in padding - $('#GBtwopageview').width(this.twoPage.width + this.twoPage.totalLeafEdgeWidth); - $('#GBtwopageview').height(this.twoPage.height + 20); - } + var middle = this.twoPage.middle; // $$$ getter instead? + var top = this.twoPageTop(); + var bookCoverDivLeft = this.twoPage.bookCoverDivLeft; - var scaledWL = parseInt(this.twoPage.height*widthL/heightL); - var gutter = middle + this.gutterOffsetForIndex(this.twoPage.currentIndexL); + var scaledWL = this.getPageWidth2UP(indexL); + var gutter = this.twoPageGutter(); this.prefetchImg(indexL); $(this.prefetchedImgs[indexL]).css({ @@ -469,7 +481,9 @@ GnuBook.prototype.drawLeafsTwoPage = function() { var heightR = this.getPageHeight(indexR); var widthR = this.getPageWidth(indexR); - var scaledWR = this.twoPage.height*widthR/heightR; + // $$$ should use getwidth2up? + //var scaledWR = this.twoPage.height*widthR/heightR; + var scaledWR = this.getPageWidth2UP(indexR); this.prefetchImg(indexR); $(this.prefetchedImgs[indexR]).css({ position: 'absolute', @@ -488,6 +502,7 @@ GnuBook.prototype.drawLeafsTwoPage = function() { this.setClickHandlers(); this.updatePageNumBox2UP(); + this.updateToolbarZoom(this.reduce); } // updatePageNumBox2UP @@ -552,7 +567,7 @@ GnuBook.prototype.zoom1up = function(dir) { this.displayedIndices = []; this.loadLeafs(); - $('#GBzoom').text(100/this.reduce); + this.updateToolbarZoom(this.reduce); } // resizePageView() @@ -639,20 +654,94 @@ GnuBook.prototype.centerPageView = function() { // zoom2up(direction) //______________________________________________________________________________ GnuBook.prototype.zoom2up = function(direction) { - // $$$ this is where we can e.g. snap to %2 sizes - if (0 == direction) { // autofit mode - this.twoPage.autofit = true;; - } else if (1 == direction) { - if (this.reduce <= 0.5) return; - this.reduce*=0.5; //zoom in - this.twoPage.autofit = false; - } else { - if (this.reduce >= 8) return; - this.reduce *= 2; // zoom out - this.twoPage.autofit = false; + + // Stop autoplay + this.autoStop(); + this.stopFlipAnimations(); // hard stop animations + + // Get new zoom state + var newZoom = this.twoPageNextReduce(this.reduce, direction); + if ((this.reduce == newZoom.reduce) && (this.twoPage.autofit == newZoom.autofit)) { + return; } + this.twoPage.autofit = newZoom.autofit; + this.reduce = newZoom.reduce; + + // Preserve view center position + var oldCenter = this.twoPageGetViewCenter(); - this.prepareTwoPageView(); + // Prepare view with new center to minimize visual glitches + this.prepareTwoPageView(oldCenter.percentageX, oldCenter.percentageY); +} + + +// quantizeReduce(reduce) +//______________________________________________________________________________ +// Quantizes the given reduction factor to closest power of two from set from 12.5% to 200% +GnuBook.prototype.quantizeReduce = function(reduce) { + var quantized = this.reductionFactors[0]; + var distance = Math.abs(reduce - quantized); + for (var i = 1; i < this.reductionFactors.length; i++) { + newDistance = Math.abs(reduce - reductionFactors[i]); + if (newDistance < distance) { + distance = newDistance; + quantized = reductionFactors[i]; + } + } + + return quantized; +} + +// twoPageNextReduce() +//______________________________________________________________________________ +// Returns the next reduction level +GnuBook.prototype.twoPageNextReduce = function(reduce, direction) { + var result = {}; + var autofitReduce = this.twoPageGetAutofitReduce(); + + if (0 == direction) { // autofit + result.autofit = true; + result.reduce = autofitReduce; + + } else if (1 == direction) { // zoom in + var newReduce = this.reductionFactors[0]; + + for (var i = 1; i < this.reductionFactors.length; i++) { + if (this.reductionFactors[i] < reduce) { + newReduce = this.reductionFactors[i]; + } + } + + if (!this.twoPage.autofit && (autofitReduce < reduce && autofitReduce > newReduce)) { + // use autofit + result.autofit = true; + result.reduce = autofitReduce; + } else { + result.autofit = false; + result.reduce = newReduce; + } + + } else { // zoom out + var lastIndex = this.reductionFactors.length - 1; + var newReduce = this.reductionFactors[lastIndex]; + + for (var i = lastIndex; i >= 0; i--) { + if (this.reductionFactors[i] > reduce) { + newReduce = this.reductionFactors[i]; + } + } + + if (!this.twoPage.autofit && (autofitReduce > reduce && autofitReduce < newReduce)) { + // use autofit + result.autofit = true; + result.reduce = autofitReduce; + } else { + result.autofit = false; + result.reduce = newReduce; + } + } + + return result; } // jumpToPage() @@ -724,9 +813,11 @@ GnuBook.prototype.switchMode = function(mode) { this.switchToolbarMode(mode); if (1 == mode) { + this.reduce = this.twoPageQuantizeReduce(this.reduce); this.prepareOnePageView(); } else { this.prepareTwoPageView(); + this.twoPageCenterView(0.5, 0.5); } } @@ -752,7 +843,6 @@ GnuBook.prototype.prepareOnePageView = function() { this.displayedIndices = []; this.drawLeafsOnePage(); - $('#GBzoom').text(100/this.reduce); // Bind mouse handlers // Disable mouse click to avoid selected/highlighted page images - bug 354239 @@ -765,20 +855,20 @@ GnuBook.prototype.prepareOnePageView = function() { // prepareTwoPageView() //______________________________________________________________________________ -GnuBook.prototype.prepareTwoPageView = function() { +// Some decisions about two page view: +// +// Both pages will be displayed at the same height, even if they were different physical/scanned +// sizes. This simplifies the animation (from a design as well as technical standpoint). We +// examine the page aspect ratios (in calculateSpreadSize) and use the page with the most "normal" +// aspect ratio to determine the height. +// +// The two page view div is resized to keep the middle of the book in the middle of the div +// even as the page sizes change. To e.g. keep the middle of the book in the middle of the GBcontent +// div requires adjusting the offset of GBtwpageview and/or scrolling in GBcontent. +GnuBook.prototype.prepareTwoPageView = function(centerPercentageX, centerPercentageY) { $('#GBcontainer').empty(); $('#GBcontainer').css('overflow', 'auto'); - - - // Add the two page view - $('#GBcontainer').append('
'); - // Explicitly set sizes the same - $('#GBtwopageview').css( { - height: $('#GBcontainer').height(), - width: $('#GBcontainer').width(), - position: 'absolute' - }); - + // We want to display two facing pages. We may be missing // one side of the spread because it is the first/last leaf, // foldouts, missing pages, etc @@ -803,59 +893,47 @@ GnuBook.prototype.prepareTwoPageView = function() { this.twoPage.currentIndexR = currentSpreadIndices[1]; this.firstIndex = this.twoPage.currentIndexL; - this.calculateSpreadSize(); //sets twoPage.width, twoPage.height, and twoPage.ratio - - // We want to minimize the unused space in two-up mode (maximize the amount of page - // shown). We give width to the leaf edges and these widths change (though the sum - // of the two remains constant) as we flip through the book. With the book - // cover centered and fixed in the GBcontainer div the page images will meet - // at the "gutter" which is generally offset from the center. - var middle = ($('#GBcontainer').attr('clientWidth') >> 1); // Middle of the GBcontainer div - //var gutter = middle+parseInt((2*this.twoPage.currentIndexL - this.numLeafs)*this.twoPage.edgeWidth/this.numLeafs/2); - - var gutter = middle + this.gutterOffsetForIndex(this.twoPage.currentIndexL); + this.calculateSpreadSize(); //sets twoPage.width, twoPage.height + + //console.dir(this.twoPage); - var scaledWL = this.getPageWidth2UP(this.twoPage.currentIndexL); - var scaledWR = this.getPageWidth2UP(this.twoPage.currentIndexR); - var leafEdgeWidthL = this.leafEdgeWidth(this.twoPage.currentIndexL); - var leafEdgeWidthR = this.twoPage.edgeWidth - leafEdgeWidthL; + // Add the two page view + // $$$ Can we get everything set up and then append? + $('#GBcontainer').append('
'); - //console.log('idealWidth='+idealWidth+' idealHeight='+idealHeight); - //var bookCoverDivWidth = this.twoPage.width*2+20 + this.twoPage.edgeWidth; - - // The width of the book cover div. The combined width of both pages, twice the width - // of the book cover internal padding (2*10) and the page edges - var bookCoverDivWidth = scaledWL + scaledWR + 20 + this.twoPage.edgeWidth; - - // The height of the book cover div - var bookCoverDivHeight = this.twoPage.height+20; + // $$$ calculate first then set + $('#GBtwopageview').css( { + height: this.twoPage.totalHeight + 'px', + width: this.twoPage.totalWidth + 'px', + position: 'absolute' + }); + + // If there will not be scrollbars (e.g. when zooming out) we center the book + // since otherwise the book will be stuck off-center + if (this.twoPage.totalWidth < $('#GBcontainer').attr('clientWidth')) { + centerPercentageX = 0.5; + } + if (this.twoPage.totalHeight < $('#GBcontainer').attr('clientHeight')) { + centerPercentageY = 0.5; + } + + this.twoPageCenterView(centerPercentageX, centerPercentageY); - //var bookCoverDivLeft = ($('#GBcontainer').width() - bookCoverDivWidth) >> 1; - var bookCoverDivLeft = gutter-scaledWL-leafEdgeWidthL-10; - var bookCoverDivTop = ($('#GBcontainer').height() - bookCoverDivHeight) >> 1; - //console.log('bookCoverDivWidth='+bookCoverDivWidth+' bookCoverDivHeight='+bookCoverDivHeight+ ' bookCoverDivLeft='+bookCoverDivLeft+' bookCoverDivTop='+bookCoverDivTop); - - this.twoPageDiv = document.createElement('div'); - $(this.twoPageDiv).attr('id', 'GBbookcover').css({ + this.twoPage.coverDiv = document.createElement('div'); + $(this.twoPage.coverDiv).attr('id', 'GBbookcover').css({ border: '1px solid rgb(68, 25, 17)', - width: bookCoverDivWidth + 'px', - height: bookCoverDivHeight+'px', + width: this.twoPage.bookCoverDivWidth + 'px', + height: this.twoPage.bookCoverDivHeight+'px', visibility: 'visible', position: 'absolute', backgroundColor: '#663929', - left: bookCoverDivLeft + 'px', - top: bookCoverDivTop+'px', + left: this.twoPage.bookCoverDivLeft + 'px', + top: this.twoPage.bookCoverDivTop+'px', MozBorderRadiusTopleft: '7px', MozBorderRadiusTopright: '7px', MozBorderRadiusBottomright: '7px', MozBorderRadiusBottomleft: '7px' }).appendTo('#GBtwopageview'); - //$('#GBcontainer').append('
'); - - - var height = this.getPageHeight(this.twoPage.currentIndexR); - var width = this.getPageWidth(this.twoPage.currentIndexR); - var scaledW = this.twoPage.height*width/height; this.leafEdgeR = document.createElement('div'); this.leafEdgeR.className = 'leafEdgeR'; // $$$ the static CSS should be moved into the .css file @@ -864,11 +942,11 @@ GnuBook.prototype.prepareTwoPageView = function() { borderColor: 'rgb(51, 51, 34)', borderWidth: '1px 1px 1px 0px', background: 'transparent url(' + this.imagesBaseURL + 'right_edges.png) repeat scroll 0% 0%', - width: leafEdgeWidthR + 'px', + width: this.twoPage.leafEdgeWidthR + 'px', height: this.twoPage.height-1 + 'px', /*right: '10px',*/ - left: gutter+scaledW+'px', - top: bookCoverDivTop+10+'px', + left: this.twoPage.gutter+this.twoPage.scaledWR+'px', + top: this.twoPage.bookCoverDivTop+this.twoPage.coverInternalPadding+'px', position: 'absolute' }).appendTo('#GBtwopageview'); @@ -879,37 +957,23 @@ GnuBook.prototype.prepareTwoPageView = function() { borderColor: 'rgb(51, 51, 34)', borderWidth: '1px 0px 1px 1px', background: 'transparent url(' + this.imagesBaseURL + 'left_edges.png) repeat scroll 0% 0%', - width: leafEdgeWidthL + 'px', + width: this.twoPage.leafEdgeWidthL + 'px', height: this.twoPage.height-1 + 'px', - left: bookCoverDivLeft+10+'px', - top: bookCoverDivTop+10+'px', + left: this.twoPage.bookCoverDivLeft+this.twoPage.coverInternalPadding+'px', + top: this.twoPage.bookCoverDivTop+this.twoPage.coverInternalPadding+'px', position: 'absolute' }).appendTo('#GBtwopageview'); - - - bookCoverDivWidth = 30; - bookCoverDivHeight = this.twoPage.height+20; - bookCoverDivLeft = ($('#GBcontainer').attr('clientWidth') - bookCoverDivWidth) >> 1; - bookCoverDivTop = ($('#GBcontainer').height() - bookCoverDivHeight) >> 1; - div = document.createElement('div'); $(div).attr('id', 'GBbookspine').css({ border: '1px solid rgb(68, 25, 17)', - width: bookCoverDivWidth+'px', - height: bookCoverDivHeight+'px', + width: this.twoPage.bookSpineDivWidth+'px', + height: this.twoPage.bookSpineDivHeight+'px', position: 'absolute', backgroundColor: 'rgb(68, 25, 17)', - left: bookCoverDivLeft+'px', - top: bookCoverDivTop+'px' + left: this.twoPage.bookSpineDivLeft+'px', + top: this.twoPage.bookSpineDivTop+'px' }).appendTo('#GBtwopageview'); - //$('#GBcontainer').append('
'); - - bookCoverDivWidth = this.twoPage.width*2; - bookCoverDivHeight = this.twoPage.height; - bookCoverDivLeft = ($('#GBcontainer').attr('clientWidth') - bookCoverDivWidth) >> 1; - bookCoverDivTop = ($('#GBcontainer').height() - bookCoverDivHeight) >> 1; - this.prepareTwoPagePopUp(); @@ -920,9 +984,9 @@ GnuBook.prototype.prepareTwoPageView = function() { this.drawLeafsTwoPage(); this.updateSearchHilites2UP(); + this.updateToolbarZoom(this.reduce); this.prefetch(); - $('#GBzoom').text((100*this.twoPage.height/this.getPageHeight(this.twoPage.currentIndexL)).toString().substr(0,4)); } // prepareTwoPagePopUp() @@ -997,7 +1061,7 @@ GnuBook.prototype.prepareTwoPagePopUp = function() { //______________________________________________________________________________ // Calculates 2-page spread dimensions based on this.twoPage.currentIndexL and // this.twoPage.currentIndexR -// This function sets this.twoPage.height, twoPage.width, and twoPage.ratio +// This function sets this.twoPage.height, twoPage.width GnuBook.prototype.calculateSpreadSize = function() { console.log('calculateSpreadSize ' + this.twoPage.currentIndexL); // XXX @@ -1010,27 +1074,75 @@ GnuBook.prototype.calculateSpreadSize = function() { var secondIndex = this.twoPage.currentIndexR; //console.log('first page is ' + firstIndex); - // $$$ Right now we just use the ideal size + // Calculate page sizes and total leaf width var spreadSize; if ( this.twoPage.autofit) { spreadSize = this.getIdealSpreadSize(firstIndex, secondIndex); } else { // set based on reduction factor spreadSize = this.getSpreadSizeFromReduce(firstIndex, secondIndex, this.reduce); - // XXX - console.dir(spreadSize); } + // Both pages together this.twoPage.height = spreadSize.height; this.twoPage.width = spreadSize.width; - this.twoPage.ratio = spreadSize.ratio; + + // Individual pages + this.twoPage.scaledWL = this.getPageWidth2UP(firstIndex); + this.twoPage.scaledWR = this.getPageWidth2UP(secondIndex); + + // Leaf edges + this.twoPage.leafEdgeWidthL = this.leafEdgeWidth(this.twoPage.currentIndexL); + this.twoPage.leafEdgeWidthR = this.twoPage.edgeWidth - this.twoPage.leafEdgeWidthL; this.twoPage.edgeWidth = spreadSize.totalLeafEdgeWidth; // The combined width of both edges - this.reduce = spreadSize.reduce; + + + // Book cover + // The width of the book cover div. The combined width of both pages, twice the width + // of the book cover internal padding (2*10) and the page edges + this.twoPage.bookCoverDivWidth = this.twoPage.scaledWL + this.twoPage.scaledWR + 2 * this.twoPage.coverInternalPadding + this.twoPage.edgeWidth; + // The height of the book cover div + this.twoPage.bookCoverDivHeight = this.twoPage.height + 2 * this.twoPage.coverInternalPadding; + + + // We calculate the total width and height for the div so that we can make the book + // spine centered + var leftGutterOffset = this.gutterOffsetForIndex(firstIndex); + var leftWidthFromCenter = this.twoPage.scaledWL + leftGutterOffset + this.twoPage.leafEdgeWidthL; + var rightWidthFromCenter = this.twoPage.scaledWR - leftGutterOffset + this.twoPage.leafEdgeWidthR; + var largestWidthFromCenter = Math.max( leftWidthFromCenter, rightWidthFromCenter ); + this.twoPage.totalWidth = 2 * (largestWidthFromCenter + this.twoPage.coverInternalPadding + this.twoPage.coverExternalPadding); + this.twoPage.totalHeight = this.twoPage.height + 2 * (this.twoPage.coverInternalPadding + this.twoPage.coverExternalPadding); + + // We want to minimize the unused space in two-up mode (maximize the amount of page + // shown). We give width to the leaf edges and these widths change (though the sum + // of the two remains constant) as we flip through the book. With the book + // cover centered and fixed in the GBcontainer div the page images will meet + // at the "gutter" which is generally offset from the center. + this.twoPage.middle = this.twoPage.totalWidth >> 1; + this.twoPage.gutter = this.twoPage.middle + this.gutterOffsetForIndex(firstIndex); + + // The left edge of the book cover moves depending on the width of the pages + // $$$ change to getter + this.twoPage.bookCoverDivLeft = this.twoPage.gutter - this.twoPage.scaledWL - this.twoPage.leafEdgeWidthL - this.twoPage.coverInternalPadding; + // The top edge of the book cover stays a fixed distance from the top + this.twoPage.bookCoverDivTop = this.twoPage.coverExternalPadding; + + // Book spine + this.twoPage.bookSpineDivHeight = this.twoPage.height + 2*this.twoPage.coverInternalPadding; + this.twoPage.bookSpineDivLeft = this.twoPage.middle - (this.twoPage.bookSpineDivWidth >> 1); + this.twoPage.bookSpineDivTop = this.twoPage.bookCoverDivTop; + + + this.reduce = spreadSize.reduce; // $$$ really set this here? } GnuBook.prototype.getIdealSpreadSize = function(firstIndex, secondIndex) { var ideal = {}; + // We check which page is closest to a "normal" page and use that to set the height + // for both pages. This means that foldouts and other odd size pages will be displayed + // smaller than the nominal zoom amount. var canon5Dratio = 1.5; var first = { @@ -1047,11 +1159,12 @@ GnuBook.prototype.getIdealSpreadSize = function(firstIndex, secondIndex) { var secondIndexRatio = second.height / second.width; //console.log('firstIndexRatio = ' + firstIndexRatio + ' secondIndexRatio = ' + secondIndexRatio); + var ratio; if (Math.abs(firstIndexRatio - canon5Dratio) < Math.abs(secondIndexRatio - canon5Dratio)) { - ideal.ratio = firstIndexRatio; + ratio = firstIndexRatio; //console.log('using firstIndexRatio ' + ratio); } else { - ideal.ratio = secondIndexRatio; + ratio = secondIndexRatio; //console.log('using secondIndexRatio ' + ratio); } @@ -1061,42 +1174,50 @@ GnuBook.prototype.getIdealSpreadSize = function(firstIndex, secondIndex) { ideal.width = ($('#GBcontainer').attr('clientWidth') - 30 - ideal.totalLeafEdgeWidth)>>1; ideal.height = $('#GBcontainer').height() - 30; // $$$ why - 30? book edge width? - //console.log('init idealWidth='+idealWidth+' idealHeight='+idealHeight + ' ratio='+ratio); + //console.log('init idealWidth='+ideal.width+' idealHeight='+ideal.height + ' ratio='+ratio); - if (ideal.height/ideal.ratio <= ideal.width) { + if (ideal.height/ratio <= ideal.width) { //use height - ideal.width = parseInt(ideal.height/ideal.ratio); + ideal.width = parseInt(ideal.height/ratio); } else { //use width - ideal.height = parseInt(ideal.width*ideal.ratio); + ideal.height = parseInt(ideal.width*ratio); } // XXX check this logic with large spreads - ideal.reduce = ((first.width + second.width) / 2) / ideal.width; + ideal.reduce = ((first.height + second.height) / 2) / ideal.height; return ideal; } +// getSpreadSizeFromReduce() +//______________________________________________________________________________ +// Returns the spread size calculated from the reduction factor for the given pages GnuBook.prototype.getSpreadSizeFromReduce = function(firstIndex, secondIndex, reduce) { var spreadSize = {}; // $$$ Scale this based on reduce? var totalLeafEdgeWidth = parseInt(this.numLeafs * 0.1); - var maxLeafEdgeWidth = parseInt($('#GBcontainer').attr('clientWidth') * 0.1); + var maxLeafEdgeWidth = parseInt($('#GBcontainer').attr('clientWidth') * 0.1); // XXX update spreadSize.totalLeafEdgeWidth = Math.min(totalLeafEdgeWidth, maxLeafEdgeWidth); - // $$$ this isn't quite right when the pages are different sizes + // $$$ Possibly incorrect -- we should make height "dominant" var nativeWidth = this.getPageWidth(firstIndex) + this.getPageWidth(secondIndex); var nativeHeight = this.getPageHeight(firstIndex) + this.getPageHeight(secondIndex); spreadSize.height = parseInt( (nativeHeight / 2) / this.reduce ); spreadSize.width = parseInt( (nativeWidth / 2) / this.reduce ); spreadSize.reduce = reduce; - // XXX - console.log('spread size: ' + firstIndex + ',' + secondIndex + ',' + reduce); - return spreadSize; } +// twoPageGetAutofitReduce() +//______________________________________________________________________________ +// Returns the current ideal reduction factor +GnuBook.prototype.twoPageGetAutofitReduce = function() { + var spreadSize = this.getIdealSpreadSize(this.twoPage.currentIndexL, this.twoPage.currentIndexR); + return spreadSize.reduce; +} + // currentIndex() //______________________________________________________________________________ // Returns the currently active index. @@ -1236,9 +1357,8 @@ GnuBook.prototype.flipBackToIndex = function(index) { if ('rl' != this.pageProgression) { // Assume LTR and we are going backward - var gutter = this.prepareFlipLeftToRight(previousIndices[0], previousIndices[1]); - this.flipLeftToRight(previousIndices[0], previousIndices[1], gutter); - + this.prepareFlipLeftToRight(previousIndices[0], previousIndices[1]); + this.flipLeftToRight(previousIndices[0], previousIndices[1]); } else { // RTL and going backward var gutter = this.prepareFlipRightToLeft(previousIndices[0], previousIndices[1]); @@ -1249,7 +1369,7 @@ GnuBook.prototype.flipBackToIndex = function(index) { // flipLeftToRight() //______________________________________________________________________________ // Flips the page on the left towards the page on the right -GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR, gutter) { +GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR) { var leftLeaf = this.twoPage.currentIndexL; @@ -1261,8 +1381,9 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR, gutter) { var newWidthL = this.getPageWidth2UP(newIndexL); var newWidthR = this.getPageWidth2UP(newIndexR); - var top = ($('#GBtwopageview').height() - this.twoPage.height) >> 1; - + var top = this.twoPageTop(); + var gutter = this.twoPage.middle + this.gutterOffsetForIndex(newIndexL); + //console.log('leftEdgeTmpW ' + leafEdgeTmpW); //console.log(' gutter ' + gutter + ', scaledWL ' + scaledWL + ', newLeafEdgeWL ' + newLeafEdgeWidthL); @@ -1283,7 +1404,7 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR, gutter) { // and width=twoPage.edgeWidth-newLeafEdgeWidthL. // - resize and move the left leaf edge (leafEdgeL) to left=gutter-newWidthL-newLeafEdgeWidthL // and width=newLeafEdgeWidthL. - // - resize the back cover (twoPageDiv) to left=gutter-newWidthL-newLeafEdgeWidthL-10 + // - resize the back cover (twoPage.coverDiv) to left=gutter-newWidthL-newLeafEdgeWidthL-10 // and width=newWidthL+newWidthR+twoPage.edgeWidth+20 // - move new left leaf (newIndexL) forward to zindex=2 so it can receive clicks. // - remove old left and right leafs from the dom [pruneUnusedImgs()]. @@ -1319,16 +1440,14 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR, gutter) { var left = $(this.prefetchedImgs[leftLeaf]).offset().left; // $$$ This seems very similar to the gutter. May be able to consolidate the logic. var right = $('#GBtwopageview').attr('clientWidth')-left-$(this.prefetchedImgs[leftLeaf]).width()+$('#GBtwopageview').offset().left-2+'px'; + // We change the left leaf to right positioning + // $$$ This causes animation glitches during resize. See https://bugs.edge.launchpad.net/gnubook/+bug/328327 $(this.prefetchedImgs[leftLeaf]).css({ right: right, left: '' }); - left = $(this.prefetchedImgs[leftLeaf]).offset().left - $('#book_div_1').offset().left; - - right = left+$(this.prefetchedImgs[leftLeaf]).width()+'px'; - $(this.leafEdgeTmp).animate({left: gutter}, this.flipSpeed, 'easeInSine'); //$(this.prefetchedImgs[leftLeaf]).animate({width: '0px'}, 'slow', 'easeInSine'); @@ -1345,7 +1464,7 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR, gutter) { //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); - + $(self.leafEdgeR).css({ // Moves the right leaf edge width: self.twoPage.edgeWidth-newLeafEdgeWidthL+'px', @@ -1358,11 +1477,10 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR, gutter) { left: gutter-newWidthL-newLeafEdgeWidthL+'px' }); - - $(self.twoPageDiv).css({ - // Resizes the brown border div - width: newWidthL+newWidthR+self.twoPage.edgeWidth+20+'px', - left: gutter-newWidthL-newLeafEdgeWidthL-10+'px' + // Resizes the brown border div + $(self.twoPage.coverDiv).css({ + width: self.twoPageCoverWidth(newWidthL+newWidthR)+'px', + left: gutter-newWidthL-newLeafEdgeWidthL-self.twoPage.coverInternalPadding+'px' }); $(self.leafEdgeTmp).remove(); @@ -1379,7 +1497,6 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR, gutter) { self.updateSearchHilites2UP(); self.updatePageNumBox2UP(); - //$('#GBzoom').text((self.twoPage.height/self.getPageHeight(newIndexL)).toString().substr(0,4)); if (self.animationFinishedCallback) { self.animationFinishedCallback(); @@ -1421,7 +1538,7 @@ GnuBook.prototype.flipFwdToIndex = function(index) { } else { // RTL var gutter = this.prepareFlipLeftToRight(nextIndices[0], nextIndices[1]); - this.flipLeftToRight(nextIndices[0], nextIndices[1], gutter); + this.flipLeftToRight(nextIndices[0], nextIndices[1]); } } @@ -1429,7 +1546,7 @@ GnuBook.prototype.flipFwdToIndex = function(index) { // $$$ better not to have to pass gutter in //______________________________________________________________________________ // Flip from left to right and show the nextL and nextR indices on those sides -GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR, gutter) { +GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR) { var oldLeafEdgeWidthL = this.leafEdgeWidth(this.twoPage.currentIndexL); var oldLeafEdgeWidthR = this.twoPage.edgeWidth-oldLeafEdgeWidthL; var newLeafEdgeWidthL = this.leafEdgeWidth(newIndexL); @@ -1437,16 +1554,12 @@ GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR, gutter) { var leafEdgeTmpW = oldLeafEdgeWidthR - newLeafEdgeWidthR; - var top = ($('#GBtwopageview').height() - this.twoPage.height) >> 1; - + var top = this.twoPageTop(); var scaledW = this.getPageWidth2UP(this.twoPage.currentIndexR); - var middle = ($('#GBtwopageview').attr('clientWidth') >> 1); - var currGutter = middle + this.gutterOffsetForIndex(this.twoPage.currentIndexL); + var middle = this.twoPage.middle; + var gutter = middle + this.gutterOffsetForIndex(newIndexL); - // XXX - console.log('R->L middle(' + middle +') currGutter(' + currGutter + ') gutter(' + gutter + ')'); - this.leafEdgeTmp = document.createElement('div'); $(this.leafEdgeTmp).css({ borderStyle: 'solid none solid solid', @@ -1455,7 +1568,7 @@ GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR, gutter) { background: 'transparent url(' + this.imagesBaseURL + 'left_edges.png) repeat scroll 0% 0%', width: leafEdgeTmpW + 'px', height: this.twoPage.height-1 + 'px', - left: currGutter+scaledW+'px', + left: gutter+scaledW+'px', top: top+'px', position: 'absolute', zIndex:1000 @@ -1482,15 +1595,16 @@ GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR, gutter) { $(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); - + $(self.leafEdgeL).css({ width: newLeafEdgeWidthL+'px', left: gutter-newWidthL-newLeafEdgeWidthL+'px' }); - $(self.twoPageDiv).css({ - width: newWidthL+newWidthR+self.twoPage.edgeWidth+20+'px', - left: gutter-newWidthL-newLeafEdgeWidthL-10+'px' + // Resizes the book cover + $(self.twoPage.coverDiv).css({ + width: self.twoPageCoverWidth(newWidthL+newWidthR)+'px', + left: gutter - newWidthL - newLeafEdgeWidthL - self.coverInternalPadding + 'px' }); $(self.leafEdgeTmp).remove(); @@ -1508,7 +1622,6 @@ GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR, gutter) { self.updateSearchHilites2UP(); self.updatePageNumBox2UP(); - //$('#GBzoom').text((self.twoPage.height/self.getPageHeight(newIndexL)).toString().substr(0,4)); if (self.animationFinishedCallback) { self.animationFinishedCallback(); @@ -1562,9 +1675,9 @@ GnuBook.prototype.prepareFlipLeftToRight = function(prevL, prevR) { var height = this.getPageHeight(prevL); var width = this.getPageWidth(prevL); - var middle = ($('#GBtwopageview').attr('clientWidth') >> 1); - var top = ($('#GBtwopageview').height() - this.twoPage.height) >> 1; - var scaledW = this.twoPage.height*width/height; + var middle = this.twoPage.middle; + var top = this.twoPageTop(); + var scaledW = this.twoPage.height*width/height; // $$$ assumes height of page is dominant // The gutter is the dividing line between the left and right pages. // It is offset from the middle to create the illusion of thickness to the pages @@ -1577,9 +1690,8 @@ GnuBook.prototype.prepareFlipLeftToRight = function(prevL, prevR) { $(this.prefetchedImgs[prevL]).css({ position: 'absolute', - /*right: middle+'px',*/ left: gutter-scaledW+'px', - right: '', + right: '', // clear right property top: top+'px', backgroundColor: 'rgb(234, 226, 205)', height: this.twoPage.height, @@ -1605,9 +1717,6 @@ GnuBook.prototype.prepareFlipLeftToRight = function(prevL, prevR) { }); $('#GBtwopageview').append(this.prefetchedImgs[prevR]); - - - return gutter; } @@ -1618,13 +1727,14 @@ GnuBook.prototype.prepareFlipRightToLeft = function(nextL, nextR) { //console.log(' preparing left<-right for ' + nextL + ',' + nextR); + // Prefetch images this.prefetchImg(nextL); this.prefetchImg(nextR); var height = this.getPageHeight(nextR); var width = this.getPageWidth(nextR); - var middle = ($('#GBtwopageview').attr('clientWidth') >> 1); - var top = ($('#GBtwopageview').height() - this.twoPage.height) >> 1; + var middle = this.twoPage.middle; + var top = this.twoPageTop(); var scaledW = this.twoPage.height*width/height; var gutter = middle + this.gutterOffsetForIndex(nextL); @@ -1656,14 +1766,12 @@ GnuBook.prototype.prepareFlipRightToLeft = function(nextL, nextR) { top: top+'px', backgroundColor: 'rgb(234, 226, 205)', height: this.twoPage.height, - width: 0+'px', + width: 0+'px', // Start at 0 width, then grow to the left borderRight: '1px solid black', zIndex: 2 }); $('#GBtwopageview').append(this.prefetchedImgs[nextL]); - - return gutter; } @@ -1728,9 +1836,10 @@ GnuBook.prototype.prefetch = function() { // getPageWidth2UP() //______________________________________________________________________________ GnuBook.prototype.getPageWidth2UP = function(index) { + // We return the width based on the dominant height var height = this.getPageHeight(index); var width = this.getPageWidth(index); - return Math.floor(this.twoPage.height*width/height); + return Math.floor(this.twoPage.height*width/height); // $$$ we assume width is relative to current spread } // search() @@ -1851,12 +1960,72 @@ GnuBook.prototype.updateSearchHilites1UP = function() { } } +// XXX move, clean up, use everywhere +GnuBook.prototype.twoPageGutter = function() { + return this.twoPage.middle + this.gutterOffsetForIndex(this.twoPage.currentIndexL); +} + +// XXX move, clean up, use everywhere +GnuBook.prototype.twoPageTop = function() { + return this.twoPage.coverExternalPadding + this.twoPage.coverInternalPadding; // $$$ + border? +} + +GnuBook.prototype.twoPageCoverWidth = function(totalPageWidth) { + return totalPageWidth + this.twoPage.edgeWidth + 2*this.twoPage.coverInternalPadding; +} + +GnuBook.prototype.twoPageGetViewCenter = function() { + var center = {}; + + var containerOffset = $('#GBcontainer').offset(); + var viewOffset = $('#GBtwopageview').offset(); + center.percentageX = (containerOffset.left - viewOffset.left + ($('#GBcontainer').attr('clientWidth') >> 1)) / this.twoPage.totalWidth; + center.percentageY = (containerOffset.top - viewOffset.top + ($('#GBcontainer').attr('clientHeight') >> 1)) / this.twoPage.totalHeight; + + return center; +} + +// twoPageCenterView(percentageX, percentageY) +//______________________________________________________________________________ +// Centers the point given by percentage from left,top of twopageview +GnuBook.prototype.twoPageCenterView = function(percentageX, percentageY) { + if ('undefined' == typeof(percentageX)) { + percentageX = 0.5; + } + if ('undefined' == typeof(percentageY)) { + percentageY = 0.5; + } + + var viewWidth = $('#GBtwopageview').width(); + var containerClientWidth = $('#GBcontainer').attr('clientWidth'); + var intoViewX = percentageX * viewWidth; + + var viewHeight = $('#GBtwopageview').height(); + var containerClientHeight = $('#GBcontainer').attr('clientHeight'); + var intoViewY = percentageY * viewHeight; + + if (viewWidth < containerClientWidth) { + // Can fit width without scrollbars - center by adjusting offset + $('#GBtwopageview').css('left', (containerClientWidth >> 1) - intoViewX + 'px'); + } else { + // Need to scroll to center + $('#GBtwopageview').css('left', 0); + $('#GBcontainer').scrollLeft(intoViewX - (containerClientWidth >> 1)); + } + + if (viewHeight < containerClientHeight) { + // Fits with scrollbars - add offset + $('#GBtwopageview').css('top', (containerClientHeight >> 1) - intoViewY + 'px'); + } else { + $('#GBtwopageview').css('top', 0); + $('#GBcontainer').scrollTop(intoViewY - (containerClientHeight >> 1)); + } +} + // showSearchHilites2UP() //______________________________________________________________________________ GnuBook.prototype.updateSearchHilites2UP = function() { - var middle = ($('#GBtwopageview').attr('clientWidth') >> 1); - for (var key in this.searchResults) { key = parseInt(key, 10); if (-1 != jQuery.inArray(key, this.displayedIndices)) { @@ -1867,20 +2036,22 @@ GnuBook.prototype.updateSearchHilites2UP = function() { //console.log('appending ' + key); } + // We calculate the reduction factor for the specific page because it can be different + // for each page in the spread var height = this.getPageHeight(key); var width = this.getPageWidth(key) var reduce = this.twoPage.height/height; var scaledW = parseInt(width*reduce); - var gutter = middle + this.gutterOffsetForIndex(this.twoPage.currentIndexL); - + var gutter = this.twoPageGutter(); + var pageL; if ('L' == this.getPageSide(key)) { - var pageL = gutter-scaledW; + pageL = gutter-scaledW; } else { - var pageL = gutter; + pageL = gutter; } - var pageT = ($('#GBtwopagview').height() - this.twoPage.height) >> 1; - + var pageT = this.twoPageTop(); + $(result.div).css({ width: (result.r-result.l)*reduce + 'px', height: (result.b-result.t)*reduce + 'px', @@ -1949,6 +2120,11 @@ GnuBook.prototype.autoToggle = function() { bComingFrom1up = true; this.switchMode(2); } + + // Change to autofit if book is too large + if (this.reduce < this.twoPageGetAutofitReduce()) { + this.zoom2up(0); + } var self = this; if (null == this.autoTimer) { @@ -1993,6 +2169,29 @@ GnuBook.prototype.autoStop = function() { } } +// $$$ document +GnuBook.prototype.stopFlipAnimations = function() { + + // Stop animation, clear queue, trigger callbacks + if (this.leafEdgeTmp) { + $(this.leafEdgeTmp).stop(false, true); + } + console.log(this.leafEdgeTmp); + jQuery.each(this.prefetchedImgs, function() { + $(this).stop(false, true); + }); + + // And again since animations also queued in callbacks + if (this.leafEdgeTmp) { + $(this.leafEdgeTmp).stop(false, true); + } + console.log(this.leafEdgeTmp); + jQuery.each(this.prefetchedImgs, function() { + $(this).stop(false, true); + }); + +} + // keyboardNavigationIsDisabled(event) // - returns true if keyboard navigation should be disabled for the event //______________________________________________________________________________ @@ -2075,7 +2274,7 @@ GnuBook.prototype.initToolbar = function(mode, ui) { + "" + "