X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=GnuBook%2FGnuBook.js;h=1700035fab08d05ec0dd3f84c82d2033d7c5959a;hb=adb879b083b8a3fb1f982b099a94c37aa1544125;hp=e29edbdb4793c84b256eb05706dc30d5d2dc312f;hpb=5ba3d031e339695c3ca70ed85e6577075916c59a;p=bookreader.git diff --git a/GnuBook/GnuBook.js b/GnuBook/GnuBook.js index e29edbd..1700035 100644 --- a/GnuBook/GnuBook.js +++ b/GnuBook/GnuBook.js @@ -30,18 +30,15 @@ This file is part of GnuBook. // - getPageURI() // You must also add a numLeafs property before calling init(). -//XXX -if (typeof(console) == 'undefined') { - console = { log: function(msg) { } }; -} - function GnuBook() { this.reduce = 4; this.padding = 10; - this.mode = 1; //1 or 2 + this.mode = 1; //1, 2, 3 this.ui = 'full'; // UI mode - + this.thumbScale = 10; // thumbnail default + this.displayedIndices = []; + this.displayedRows=[]; //this.indicesToDisplay = []; this.imgs = {}; this.prefetchedImgs = {}; //an object with numeric keys cooresponding to page index @@ -55,7 +52,7 @@ function GnuBook() { this.twoPagePopUp = null; this.leafEdgeTmp = null; this.embedPopup = null; - + this.searchResults = {}; this.firstIndex = null; @@ -71,7 +68,11 @@ function GnuBook() { // Mode constants this.constMode1up = 1; this.constMode2up = 2; + this.constModeThumb = 3; + // 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 @@ -86,6 +87,7 @@ function GnuBook() { GnuBook.prototype.init = function() { var startIndex = undefined; + this.pageScale = this.reduce; // preserve current reduce // Find start index and mode if set in location hash var params = this.paramsFromFragment(window.location.hash); @@ -140,21 +142,47 @@ GnuBook.prototype.init = function() { e.data.displayedIndices = []; e.data.updateSearchHilites(); //deletes hilights but does not call remove() e.data.loadLeafs(); + } else if (3 == e.data.mode){ + e.data.prepareThumbnailView(); } 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); + } + } } }); $('.GBpagediv1up').bind('mousedown', this, function(e) { - //console.log('mousedown!'); + // $$$ the purpose of this is to disable selection of the image (makes it turn blue) + // but this also interferes with right-click. See https://bugs.edge.launchpad.net/gnubook/+bug/362626 }); if (1 == this.mode) { this.resizePageView(); this.firstIndex = startIndex; this.jumpToIndex(startIndex); - } else { + } else if (3 == this.mode) { + this.firstIndex = startIndex; + this.prepareThumbnailView(); + this.jumpToIndex(startIndex); + } else { //this.resizePageView(); this.displayedIndices=[0]; @@ -163,7 +191,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 @@ -172,7 +199,7 @@ GnuBook.prototype.init = function() { GnuBook.prototype.setupKeyListeners = function() { var self = this; - + var KEY_PGUP = 33; var KEY_PGDOWN = 34; var KEY_END = 35; @@ -185,41 +212,48 @@ GnuBook.prototype.setupKeyListeners = function() { // We use document here instead of window to avoid a bug in jQuery on IE7 $(document).keydown(function(e) { - // Keyboard navigation switch(e.keyCode) { case KEY_PGUP: case KEY_UP: // In 1up mode page scrolling is handled by browser if (2 == self.mode) { + e.preventDefault(); self.prev(); } break; case KEY_DOWN: case KEY_PGDOWN: if (2 == self.mode) { + e.preventDefault(); self.next(); } break; case KEY_END: + e.preventDefault(); self.last(); break; case KEY_HOME: + e.preventDefault(); self.first(); break; case KEY_LEFT: if (self.keyboardNavigationIsDisabled(e)) { + e.preventDefault(); break; } if (2 == self.mode) { + e.preventDefault(); self.left(); } break; case KEY_RIGHT: if (self.keyboardNavigationIsDisabled(e)) { + e.preventDefault(); break; } if (2 == self.mode) { + e.preventDefault(); self.right(); } break; @@ -232,17 +266,21 @@ GnuBook.prototype.setupKeyListeners = function() { GnuBook.prototype.drawLeafs = function() { if (1 == this.mode) { this.drawLeafsOnePage(); + } else if(3 == this.mode) { + this.drawLeafsThumbnail(); } else { this.drawLeafsTwoPage(); } } -// setDragHandler1up() +// setDragHandler() //______________________________________________________________________________ -GnuBook.prototype.setDragHandler1up = function(div) { +GnuBook.prototype.setDragHandler = function(div) { div.dragging = false; - $(div).bind('mousedown', function(e) { + $(div).unbind('mousedown').bind('mousedown', function(e) { + e.preventDefault(); + //console.log('mousedown at ' + e.pageY); this.dragging = true; @@ -254,11 +292,12 @@ GnuBook.prototype.setDragHandler1up = function(div) { var startTop = $('#GBcontainer').attr('scrollTop'); var startLeft = $('#GBcontainer').attr('scrollLeft'); - return false; }); - $(div).bind('mousemove', function(ee) { - //console.log('mousemove ' + startY); + $(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; @@ -271,22 +310,125 @@ GnuBook.prototype.setDragHandler1up = function(div) { this.prevMouseX = ee.pageX; this.prevMouseY = ee.pageY; - return false; }); - $(div).bind('mouseup', function(ee) { + $(div).unbind('mouseup').bind('mouseup', function(ee) { + ee.preventDefault(); //console.log('mouseup'); this.dragging = false; - return false; }); - $(div).bind('mouseleave', function(e) { + $(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() +//______________________________________________________________________________ +GnuBook.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 = $('#GBcontainer').attr('scrollTop'); + var startLeft = $('#GBcontainer').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) { + $('#GBcontainer').attr('scrollTop', $('#GBcontainer').attr('scrollTop') - offsetY); + $('#GBcontainer').attr('scrollLeft', $('#GBcontainer').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; + this.mouseDown = false; + }); + */ + + + $(div).unbind('mouseleave').bind('mouseleave', function(e) { + e.preventDefault(); //console.log('mouseleave'); - //$(this).unbind('mousemove mouseup'); + this.dragging = false; + this.mouseDown = false; + }); + + $(div).unbind('mouseenter').bind('mouseenter', function(e) { + e.preventDefault(); + //console.log('mouseenter'); + this.dragging = false; + this.mouseDown = false; + }); +} + +GnuBook.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; }); } @@ -371,7 +513,7 @@ GnuBook.prototype.drawLeafsOnePage = function() { $(div).css('height', height+'px'); //$(div).text('loading...'); - this.setDragHandler1up(div); + this.setDragHandler(div); $('#GBpageview').append(div); @@ -413,17 +555,182 @@ GnuBook.prototype.drawLeafsOnePage = function() { } +// drawLeafsThumbnail() +//______________________________________________________________________________ +GnuBook.prototype.drawLeafsThumbnail = function() { + //alert('drawing leafs!'); + this.timer = null; + + var viewWidth = $('#GBcontainer').attr('scrollWidth') - 20; // width minus buffer + + //console.log('top=' + scrollTop + ' bottom='+scrollBottom); + + var i; + var leafWidth; + var leafHeight; + var rightPos = 0; + var bottomPos = 0; + var maxRight = 0; + var currentRow = 0; + var leafIndex = 0; + var leafMap = []; + + for (i=0; i viewWidth){ + currentRow++; + rightPos = 0; + leafIndex = 0; + } + + if (leafMap[currentRow]===undefined) { leafMap[currentRow] = {}; } + if (leafMap[currentRow].leafs===undefined) { + leafMap[currentRow].leafs = []; + leafMap[currentRow].height = 0; + leafMap[currentRow].top = 0; + } + leafMap[currentRow].leafs[leafIndex] = {}; + leafMap[currentRow].leafs[leafIndex].num = i; + leafMap[currentRow].leafs[leafIndex].left = rightPos; + + leafHeight = parseInt(this.getPageHeight(leafMap[currentRow].leafs[leafIndex].num)/this.reduce, 10); + if (leafHeight > leafMap[currentRow].height) { leafMap[currentRow].height = leafHeight; } + if (leafIndex===0) { bottomPos += this.padding + leafMap[currentRow].height; } + rightPos += leafWidth + this.padding; + if (rightPos > maxRight) { maxRight = rightPos; } + leafIndex++; + } + + // reset the bottom position based on thumbnails + $('#GBpageview').height(bottomPos); + + var pageViewBuffer = Math.floor(($('#GBcontainer').attr('scrollWidth') - maxRight) / 2) - 14; + var scrollTop = $('#GBcontainer').attr('scrollTop'); + var scrollBottom = scrollTop + $('#GBcontainer').height(); + + var leafTop = 0; + var leafBottom = 0; + var rowsToDisplay = []; + + for (i=0; i= scrollTop) && (leafTop <= scrollBottom); + var bottomInView = (leafBottom >= scrollTop) && (leafBottom <= scrollBottom); + var middleInView = (leafTop <=scrollTop) && (leafBottom>=scrollBottom); + if (topInView | bottomInView | middleInView) { + //console.log('row to display: ' + j); + rowsToDisplay.push(i); + } + if(leafTop > leafMap[i].top) { leafMap[i].top = leafTop; } + leafTop = leafBottom; + } + + var firstRow = rowsToDisplay[0]; + var lastRow = rowsToDisplay[rowsToDisplay.length-1]; + var rowBuffer = 4; + for (i=1; i= 0) { rowsToDisplay.unshift(firstRow-i); } + if (lastRow+i < leafMap.length) { rowsToDisplay.push(lastRow+i); } + } + + // Update hash, but only if we're currently displaying a leaf + // Hack that fixes #365790 + if (this.displayedRows.length > 0) { + this.updateLocationHash(); + } + + var j; + var row; + var left; + var index; + var div; + var link; + var img; + for (i=0; i0) { + div = $('.GBpagedivthumb_highlight') + div.attr({className: 'GBpagedivthumb' }); + } + // highlight current page + $('#pagediv'+this.currentIndex()).attr({className: 'GBpagedivthumb_highlight' }); + + var k; + for (i=0; i= 8) return; this.reduce*=2; //zoom out } - + + this.pageScale = this.reduce; // preserve current reduce this.resizePageView(); $('#GBpageview').empty() @@ -635,20 +944,102 @@ 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; + + // Hard stop autoplay + this.stopFlipAnimations(); + + // 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; + this.pageScale = this.reduce; // preserve current reduce + + // Preserve view center position + var oldCenter = this.twoPageGetViewCenter(); + + // If zooming in, reload imgs. DOM elements will be removed by prepareTwoPageView + // $$$ An improvement would be to use the low res image until the larger one is loaded. + if (1 == direction) { + for (var img in this.prefetchedImgs) { + delete this.prefetchedImgs[img]; + } + } + + // 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 - this.reductionFactors[i]); + if (newDistance < distance) { + distance = newDistance; + quantized = this.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; + } } - this.prepareTwoPageView(); + return result; } // jumpToPage() @@ -685,7 +1076,38 @@ GnuBook.prototype.jumpToIndex = function(index) { this.flipFwdToIndex(index); } - } else { + } else if (3 == this.mode){ + var viewWidth = $('#GBcontainer').attr('scrollWidth') - 20; // width minus buffer + var i; + var leafWidth = 0; + var leafHeight = 0; + var rightPos = 0; + var bottomPos = 0; + var rowHeight = 0; + var leafTop = 0; + var leafIndex = 0; + + for (i=0; i<(index+1); i++) { + leafWidth = parseInt(this.getPageWidth(i)/this.reduce, 10); + if (rightPos + (leafWidth + this.padding) > viewWidth){ + rightPos = 0; + rowHeight = 0; + leafIndex = 0; + } + leafHeight = parseInt(this.getPageHeight(i)/this.reduce, 10); + if(leafHeight > rowHeight) { rowHeight = leafHeight; } + if (leafIndex==0) { leafTop = bottomPos; } + if (leafIndex==0) { bottomPos += this.padding + rowHeight; } + rightPos += leafWidth + this.padding; + leafIndex++; + } + this.firstIndex=index; + if ($('#GBcontainer').attr('scrollTop') == leafTop) { + this.loadLeafs(); + } else { + $('#GBcontainer').animate({scrollTop: leafTop },'fast'); + } + } else { var i; var leafTop = 0; var h; @@ -716,13 +1138,26 @@ GnuBook.prototype.switchMode = function(mode) { this.removeSearchHilites(); this.mode = mode; - this.switchToolbarMode(mode); + + // reinstate scale if moving from thumbnail view + if (this.pageScale != this.reduce) this.reduce = this.pageScale; + // $$$ TODO preserve center of view when switching between mode + // See https://bugs.edge.launchpad.net/gnubook/+bug/416682 + if (1 == mode) { + this.reduce = this.quantizeReduce(this.reduce); this.prepareOnePageView(); + } else if (3 == mode) { + this.reduce = this.quantizeReduce(this.reduce); + this.prepareThumbnailView(); + this.jumpToIndex(this.currentIndex()); } else { + this.twoPage.autofit = false; // Take zoom level from other mode + this.reduce = this.quantizeReduce(this.reduce); this.prepareTwoPageView(); + this.twoPageCenterView(0.5, 0.5); // $$$ TODO preserve center } } @@ -752,6 +1187,40 @@ GnuBook.prototype.prepareOnePageView = function() { // Bind mouse handlers // Disable mouse click to avoid selected/highlighted page images - bug 354239 gbPageView.bind('mousedown', function(e) { + // $$$ check here for right-click and don't disable. Also use jQuery style + // for stopping propagation. See https://bugs.edge.launchpad.net/gnubook/+bug/362626 + return false; + }) + // Special hack for IE7 + gbPageView[0].onselectstart = function(e) { return false; }; +} + +//prepareThumbnailView() +//______________________________________________________________________________ +GnuBook.prototype.prepareThumbnailView = function() { + + // var startLeaf = this.displayedIndices[0]; + var startLeaf = this.currentIndex(); + this.reduce = this.thumbScale; + + $('#GBcontainer').empty(); + $('#GBcontainer').css({ + overflowY: 'scroll', + overflowX: 'auto' + }); + + var gbPageView = $("#GBcontainer").append("
"); + + this.resizePageView(); + + this.displayedRows = []; + this.drawLeafsThumbnail(); + + // Bind mouse handlers + // Disable mouse click to avoid selected/highlighted page images - bug 354239 + gbPageView.bind('mousedown', function(e) { + // $$$ check here for right-click and don't disable. Also use jQuery style + // for stopping propagation. See https://bugs.edge.launchpad.net/gnubook/+bug/362626 return false; }) // Special hack for IE7 @@ -770,20 +1239,10 @@ GnuBook.prototype.prepareOnePageView = function() { // 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() { +GnuBook.prototype.prepareTwoPageView = function(centerPercentageX, centerPercentageY) { $('#GBcontainer').empty(); $('#GBcontainer').css('overflow', 'auto'); - - // Add the two page view - $('#GBcontainer').append('
'); - // Explicitly set sizes the same - // $$$ calculate first then set - $('#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 @@ -799,22 +1258,44 @@ GnuBook.prototype.prepareTwoPageView = function() { targetLeaf = this.lastDisplayableIndex(); } - this.twoPage.currentIndexL = null; - this.twoPage.currentIndexR = null; - this.pruneUnusedImgs(); + //this.twoPage.currentIndexL = null; + //this.twoPage.currentIndexR = null; + //this.pruneUnusedImgs(); var currentSpreadIndices = this.getSpreadIndices(targetLeaf); this.twoPage.currentIndexL = currentSpreadIndices[0]; this.twoPage.currentIndexR = currentSpreadIndices[1]; this.firstIndex = this.twoPage.currentIndexL; - this.calculateSpreadSize(); //sets twoPage.width, twoPage.height - - console.dir(this.twoPage); // XXX + this.calculateSpreadSize(); //sets twoPage.width, twoPage.height and others + + this.pruneUnusedImgs(); + this.prefetch(); // Preload images or reload if scaling has changed + + //console.dir(this.twoPage); - // $$$ May need to account for scroll bars here - $('#GBtwopageview').width(this.twoPage.totalWidth).height(this.twoPage.totalHeight); + // Add the two page view + // $$$ Can we get everything set up and then append? + $('#GBcontainer').append('
'); + // $$$ 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); + this.twoPage.coverDiv = document.createElement('div'); $(this.twoPage.coverDiv).attr('id', 'GBbookcover').css({ border: '1px solid rgb(68, 25, 17)', @@ -870,9 +1351,47 @@ GnuBook.prototype.prepareTwoPageView = function() { left: this.twoPage.bookSpineDivLeft+'px', top: this.twoPage.bookSpineDivTop+'px' }).appendTo('#GBtwopageview'); - + + var self = this; // for closure + + /* Flip areas no longer used + this.twoPage.leftFlipArea = document.createElement('div'); + this.twoPage.leftFlipArea.className = 'GBfliparea'; + $(this.twoPage.leftFlipArea).attr('id', 'GBleftflip').css({ + border: '0', + width: this.twoPageFlipAreaWidth() + 'px', + height: this.twoPageFlipAreaHeight() + 'px', + position: 'absolute', + left: this.twoPageLeftFlipAreaLeft() + 'px', + top: this.twoPageFlipAreaTop() + 'px', + cursor: 'w-resize', + zIndex: 100 + }).bind('click', function(e) { + self.left(); + }).bind('mousedown', function(e) { + e.preventDefault(); + }).appendTo('#GBtwopageview'); + + this.twoPage.rightFlipArea = document.createElement('div'); + this.twoPage.rightFlipArea.className = 'GBfliparea'; + $(this.twoPage.rightFlipArea).attr('id', 'GBrightflip').css({ + border: '0', + width: this.twoPageFlipAreaWidth() + 'px', + height: this.twoPageFlipAreaHeight() + 'px', + position: 'absolute', + left: this.twoPageRightFlipAreaLeft() + 'px', + top: this.twoPageFlipAreaTop() + 'px', + cursor: 'e-resize', + zIndex: 100 + }).bind('click', function(e) { + self.right(); + }).bind('mousedown', function(e) { + e.preventDefault(); + }).appendTo('#GBtwopageview'); + */ + this.prepareTwoPagePopUp(); - + this.displayedIndices = []; //this.indicesToDisplay=[firstLeaf, firstLeaf+1]; @@ -960,12 +1479,7 @@ GnuBook.prototype.prepareTwoPagePopUp = function() { // This function sets this.twoPage.height, twoPage.width GnuBook.prototype.calculateSpreadSize = function() { - console.log('calculateSpreadSize ' + this.twoPage.currentIndexL); // XXX - // $$$ TODO Calculate the spread size based on the reduction factor. If we are using - // fit mode we recalculate the reduction factor based on the current page sizes - // and display size first. - var firstIndex = this.twoPage.currentIndexL; var secondIndex = this.twoPage.currentIndexR; //console.log('first page is ' + firstIndex); @@ -988,9 +1502,9 @@ GnuBook.prototype.calculateSpreadSize = function() { this.twoPage.scaledWR = this.getPageWidth2UP(secondIndex); // Leaf edges + this.twoPage.edgeWidth = spreadSize.totalLeafEdgeWidth; // The combined width of both 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 // Book cover @@ -1004,8 +1518,8 @@ GnuBook.prototype.calculateSpreadSize = function() { // 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 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); @@ -1068,9 +1582,14 @@ GnuBook.prototype.getIdealSpreadSize = function(firstIndex, secondIndex) { var maxLeafEdgeWidth = parseInt($('#GBcontainer').attr('clientWidth') * 0.1); ideal.totalLeafEdgeWidth = Math.min(totalLeafEdgeWidth, maxLeafEdgeWidth); - 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); + var widthOutsidePages = 2 * (this.twoPage.coverInternalPadding + this.twoPage.coverExternalPadding) + ideal.totalLeafEdgeWidth; + var heightOutsidePages = 2* (this.twoPage.coverInternalPadding + this.twoPage.coverExternalPadding); + + ideal.width = ($('#GBcontainer').width() - widthOutsidePages) >> 1; + ideal.width -= 10; // $$$ fudge factor + ideal.height = $('#GBcontainer').height() - heightOutsidePages; + ideal.height -= 20; // fudge factor + //console.log('init idealWidth='+ideal.width+' idealHeight='+ideal.height + ' ratio='+ratio); if (ideal.height/ratio <= ideal.width) { //use height @@ -1080,38 +1599,61 @@ GnuBook.prototype.getIdealSpreadSize = function(firstIndex, secondIndex) { ideal.height = parseInt(ideal.width*ratio); } - // XXX check this logic with large spreads + // $$$ check this logic with large spreads 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); // XXX update + var maxLeafEdgeWidth = parseInt($('#GBcontainer').attr('clientWidth') * 0.1); // $$$ Assumes leaf edge width constant at all zoom levels spreadSize.totalLeafEdgeWidth = Math.min(totalLeafEdgeWidth, maxLeafEdgeWidth); - // XXX incorrect -- we should make height "dominant" + // $$$ 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; +} + +// twoPageSetCursor() +//______________________________________________________________________________ +// Set the cursor for two page view +GnuBook.prototype.twoPageSetCursor = function() { + // console.log('setting cursor'); + if ( ($('#GBtwopageview').width() > $('#GBcontainer').attr('clientWidth')) || + ($('#GBtwopageview').height() > $('#GBcontainer').attr('clientHeight')) ) { + $(this.prefetchedImgs[this.twoPage.currentIndexL]).css('cursor','move'); + $(this.prefetchedImgs[this.twoPage.currentIndexR]).css('cursor','move'); + } else { + $(this.prefetchedImgs[this.twoPage.currentIndexL]).css('cursor',''); + $(this.prefetchedImgs[this.twoPage.currentIndexR]).css('cursor',''); + } +} + // currentIndex() //______________________________________________________________________________ // Returns the currently active index. GnuBook.prototype.currentIndex = function() { // $$$ we should be cleaner with our idea of which index is active in 1up/2up - if (this.mode == this.constMode1up || this.mode == this.constMode2up) { + if (this.mode == this.constMode1up || this.mode == this.constMode2up || this.mode == this.constModeThumb) { return this.firstIndex; } else { throw 'currentIndex called for unimplemented mode ' + this.mode; @@ -1327,8 +1869,8 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR) { // Left gets the offset of the current left leaf from the document var left = $(this.prefetchedImgs[leftLeaf]).offset().left; // $$$ This seems very similar to the gutter. May be able to consolidate the logic. - // XXX need to recalc 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({ @@ -1336,10 +1878,6 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR) { 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'); @@ -1356,7 +1894,7 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR) { //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', @@ -1369,21 +1907,25 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR) { left: gutter-newWidthL-newLeafEdgeWidthL+'px' }); - // XXX set values in this.twoPage here(?) + // Resizes the brown border div $(self.twoPage.coverDiv).css({ - // Resizes the brown border div - width: newWidthL+newWidthR+self.twoPage.edgeWidth+20+'px', + width: self.twoPageCoverWidth(newWidthL+newWidthR)+'px', left: gutter-newWidthL-newLeafEdgeWidthL-self.twoPage.coverInternalPadding+'px' }); $(self.leafEdgeTmp).remove(); self.leafEdgeTmp = null; + + // $$$ TODO refactor with opposite direction flip self.twoPage.currentIndexL = newIndexL; self.twoPage.currentIndexR = newIndexR; + self.twoPage.scaledWL = newWidthL; + self.twoPage.scaledWR = newWidthR; + self.twoPage.gutter = gutter; + self.firstIndex = self.twoPage.currentIndexL; self.displayedIndices = [newIndexL, newIndexR]; - self.setClickHandlers(); self.pruneUnusedImgs(); self.prefetch(); self.animating = false; @@ -1391,6 +1933,10 @@ GnuBook.prototype.flipLeftToRight = function(newIndexL, newIndexR) { self.updateSearchHilites2UP(); self.updatePageNumBox2UP(); + // self.twoPagePlaceFlipAreas(); // No longer used + self.setMouseHandlers2UP(); + self.twoPageSetCursor(); + if (self.animationFinishedCallback) { self.animationFinishedCallback(); self.animationFinishedCallback = null; @@ -1474,7 +2020,7 @@ GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR) { var currWidthR = this.getPageWidth2UP(this.twoPage.currentIndexR); var newWidthL = this.getPageWidth2UP(newIndexL); var newWidthR = this.getPageWidth2UP(newIndexR); - + $(this.leafEdgeR).css({width: newLeafEdgeWidthR+'px', left: gutter+newWidthR+'px' }); var self = this; // closure-tastic! @@ -1488,7 +2034,7 @@ GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR) { $(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' @@ -1497,7 +2043,7 @@ GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR) { // Resizes the book cover $(self.twoPage.coverDiv).css({ width: self.twoPageCoverWidth(newWidthL+newWidthR)+'px', - left: self.coverExternalPadding+'px' + left: gutter - newWidthL - newLeafEdgeWidthL - self.twoPage.coverInternalPadding + 'px' }); $(self.leafEdgeTmp).remove(); @@ -1505,9 +2051,12 @@ GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR) { self.twoPage.currentIndexL = newIndexL; self.twoPage.currentIndexR = newIndexR; + self.twoPage.scaledWL = newWidthL; + self.twoPage.scaledWR = newWidthR; + self.twoPage.gutter = gutter; + self.firstIndex = self.twoPage.currentIndexL; self.displayedIndices = [newIndexL, newIndexR]; - self.setClickHandlers(); self.pruneUnusedImgs(); self.prefetch(); self.animating = false; @@ -1516,6 +2065,10 @@ GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR) { self.updateSearchHilites2UP(); self.updatePageNumBox2UP(); + // self.twoPagePlaceFlipAreas(); // No longer used + self.setMouseHandlers2UP(); + self.twoPageSetCursor(); + if (self.animationFinishedCallback) { self.animationFinishedCallback(); self.animationFinishedCallback = null; @@ -1524,30 +2077,59 @@ GnuBook.prototype.flipRightToLeft = function(newIndexL, newIndexR) { }); } -// setClickHandlers +// setMouseHandlers2UP //______________________________________________________________________________ -GnuBook.prototype.setClickHandlers = function() { - var self = this; - // $$$ TODO don't set again if already set - $(this.prefetchedImgs[this.twoPage.currentIndexL]).click(function() { +GnuBook.prototype.setMouseHandlers2UP = function() { + /* + $(this.prefetchedImgs[this.twoPage.currentIndexL]).bind('dblclick', function() { //self.prevPage(); self.autoStop(); self.left(); }); - $(this.prefetchedImgs[this.twoPage.currentIndexR]).click(function() { + $(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(); + } + ); + + this.setDragHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR] ); + this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR], + { self: this }, + function(e) { + e.data.self.right(); + } + ); } // prefetchImg() //______________________________________________________________________________ GnuBook.prototype.prefetchImg = function(index) { - if (undefined == this.prefetchedImgs[index]) { + var pageURI = this.getPageURI(index); + + // Load image if not loaded or URI has changed (e.g. due to scaling) + var loadImage = false; + if (undefined == this.prefetchedImgs[index]) { + //console.log('no image for ' + index); + loadImage = true; + } else if (pageURI != this.prefetchedImgs[index].uri) { + //console.log('uri changed for ' + index); + loadImage = true; + } + + if (loadImage) { //console.log('prefetching ' + index); var img = document.createElement("img"); - img.src = this.getPageURI(index); + img.src = pageURI; + img.uri = pageURI; // browser may rewrite src so we stash raw URI here this.prefetchedImgs[index] = img; } } @@ -1576,8 +2158,7 @@ GnuBook.prototype.prepareFlipLeftToRight = function(prevL, prevR) { // It is offset from the middle to create the illusion of thickness to the pages var gutter = middle + this.gutterOffsetForIndex(prevL); - // XXX - console.log(' gutter for ' + prevL + ' is ' + gutter); + //console.log(' gutter for ' + prevL + ' is ' + gutter); //console.log(' prevL.left: ' + (gutter - scaledW) + 'px'); //console.log(' changing prevL ' + prevL + ' to left: ' + (gutter-scaledW) + ' width: ' + scaledW); @@ -1613,7 +2194,7 @@ GnuBook.prototype.prepareFlipLeftToRight = function(prevL, prevR) { } -// XXXmang we're adding an extra pixel in the middle +// $$$ mang we're adding an extra pixel in the middle. See https://bugs.edge.launchpad.net/gnubook/+bug/411667 // prepareFlipRightToLeft() //______________________________________________________________________________ GnuBook.prototype.prepareFlipRightToLeft = function(nextL, nextR) { @@ -1631,9 +2212,7 @@ GnuBook.prototype.prepareFlipRightToLeft = function(nextL, nextR) { var scaledW = this.twoPage.height*width/height; var gutter = middle + this.gutterOffsetForIndex(nextL); - - console.log('right to left to %d gutter is %d', nextL, gutter); // XXX - + //console.log(' prepareRTL changing nextR ' + nextR + ' to left: ' + gutter); $(this.prefetchedImgs[nextR]).css({ position: 'absolute', @@ -1711,6 +2290,31 @@ GnuBook.prototype.pruneUnusedImgs = function() { //______________________________________________________________________________ GnuBook.prototype.prefetch = function() { + // prefetch visible pages first + this.prefetchImg(this.twoPage.currentIndexL); + this.prefetchImg(this.twoPage.currentIndexR); + + var adjacentPagesToLoad = 3; + + var lowCurrent = Math.min(this.twoPage.currentIndexL, this.twoPage.currentIndexR); + var highCurrent = Math.max(this.twoPage.currentIndexL, this.twoPage.currentIndexR); + + var start = Math.max(lowCurrent - adjacentPagesToLoad, 0); + var end = Math.min(highCurrent + adjacentPagesToLoad, this.numLeafs - 1); + + // Load images spreading out from current + for (var i = 1; i <= adjacentPagesToLoad; i++) { + var goingDown = lowCurrent - i; + if (goingDown >= start) { + this.prefetchImg(goingDown); + } + var goingUp = highCurrent + i; + if (goingUp <= end) { + this.prefetchImg(goingUp); + } + } + + /* var lim = this.twoPage.currentIndexL-4; var i; lim = Math.max(lim, 0); @@ -1724,6 +2328,7 @@ GnuBook.prototype.prefetch = function() { this.prefetchImg(i); } } + */ } // getPageWidth2UP() @@ -1853,19 +2458,132 @@ GnuBook.prototype.updateSearchHilites1UP = function() { } } -// XXX move, clean up, use everywhere +// twoPageGutter() +//______________________________________________________________________________ +// Returns the position of the gutter (line between the page images) GnuBook.prototype.twoPageGutter = function() { return this.twoPage.middle + this.gutterOffsetForIndex(this.twoPage.currentIndexL); } -// XXX move, clean up, use everywhere +// twoPageTop() +//______________________________________________________________________________ +// Returns the offset for the top of the page images GnuBook.prototype.twoPageTop = function() { return this.twoPage.coverExternalPadding + this.twoPage.coverInternalPadding; // $$$ + border? } - + +// twoPageCoverWidth() +//______________________________________________________________________________ +// Returns the width of the cover div given the total page width GnuBook.prototype.twoPageCoverWidth = function(totalPageWidth) { return totalPageWidth + this.twoPage.edgeWidth + 2*this.twoPage.coverInternalPadding; } + +// twoPageGetViewCenter() +//______________________________________________________________________________ +// Returns the percentage offset into twopageview div at the center of container div +// { percentageX: float, percentageY: float } +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)); + } +} + +// twoPageFlipAreaHeight +//______________________________________________________________________________ +// Returns the integer height of the click-to-flip areas at the edges of the book +GnuBook.prototype.twoPageFlipAreaHeight = function() { + return parseInt(this.twoPage.height); +} + +// twoPageFlipAreaWidth +//______________________________________________________________________________ +// Returns the integer width of the flip areas +GnuBook.prototype.twoPageFlipAreaWidth = function() { + var max = 100; // $$$ TODO base on view width? + var min = 10; + + var width = this.twoPage.width * 0.15; + return parseInt(GnuBook.util.clamp(width, min, max)); +} + +// twoPageFlipAreaTop +//______________________________________________________________________________ +// Returns integer top offset for flip areas +GnuBook.prototype.twoPageFlipAreaTop = function() { + return parseInt(this.twoPage.bookCoverDivTop + this.twoPage.coverInternalPadding); +} + +// twoPageLeftFlipAreaLeft +//______________________________________________________________________________ +// Left offset for left flip area +GnuBook.prototype.twoPageLeftFlipAreaLeft = function() { + return parseInt(this.twoPage.gutter - this.twoPage.scaledWL); +} + +// twoPageRightFlipAreaLeft +//______________________________________________________________________________ +// Left offset for right flip area +GnuBook.prototype.twoPageRightFlipAreaLeft = function() { + return parseInt(this.twoPage.gutter + this.twoPage.scaledWR - this.twoPageFlipAreaWidth()); +} + +// twoPagePlaceFlipAreas +//______________________________________________________________________________ +// Readjusts position of flip areas based on current layout +GnuBook.prototype.twoPagePlaceFlipAreas = function() { + // We don't set top since it shouldn't change relative to view + $(this.twoPage.leftFlipArea).css({ + left: this.twoPageLeftFlipAreaLeft() + 'px', + width: this.twoPageFlipAreaWidth() + 'px' + }); + $(this.twoPage.rightFlipArea).css({ + left: this.twoPageRightFlipAreaLeft() + 'px', + width: this.twoPageFlipAreaWidth() + 'px' + }); +} // showSearchHilites2UP() //______________________________________________________________________________ @@ -1965,6 +2683,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) { @@ -1999,6 +2722,7 @@ GnuBook.prototype.autoToggle = function() { // autoStop() //______________________________________________________________________________ +// Stop autoplay mode, allowing animations to finish GnuBook.prototype.autoStop = function() { if (null != this.autoTimer) { clearInterval(this.autoTimer); @@ -2009,6 +2733,31 @@ GnuBook.prototype.autoStop = function() { } } +// stopFlipAnimations +//______________________________________________________________________________ +// Immediately stop flip animations. Callbacks are triggered. +GnuBook.prototype.stopFlipAnimations = function() { + + this.autoStop(); // Clear timers + + // Stop animation, clear queue, trigger callbacks + if (this.leafEdgeTmp) { + $(this.leafEdgeTmp).stop(false, true); + } + 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); + } + jQuery.each(this.prefetchedImgs, function() { + $(this).stop(false, true); + }); + +} + // keyboardNavigationIsDisabled(event) // - returns true if keyboard navigation should be disabled for the event //______________________________________________________________________________ @@ -2091,11 +2840,14 @@ GnuBook.prototype.initToolbar = function(mode, ui) { + "" + "