X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=BookReader%2FBookReader.js;h=4838ce06fe4542d2acb362de7ef1742ac79f4a41;hb=6d88872cb24a0a81fae18df38fbb2a94ddf5da55;hp=31e5e474e164f9a5a0a18458791083148e5e2b80;hpb=1388e09c0e72e81427413f3533e5b2cdb1efcc7c;p=bookreader.git diff --git a/BookReader/BookReader.js b/BookReader/BookReader.js index 31e5e47..4838ce0 100644 --- a/BookReader/BookReader.js +++ b/BookReader/BookReader.js @@ -36,16 +36,25 @@ This file is part of BookReader. // You must also add a numLeafs property before calling init(). function BookReader() { + + // Mode constants + this.constMode1up = 1; + this.constMode2up = 2; + this.constModeThumb = 3; + this.reduce = 4; - this.padding = 10; - this.mode = 1; //1, 2, 3 - this.ui = 'full'; // UI mode + this.padding = 10; // Padding in 1up + + this.mode = this.constMode1up; + this.ui = 'full'; // UI mode + this.uiAutoHide = false; // Controls whether nav/toolbar will autohide // thumbnail mode this.thumbWidth = 100; // will be overridden during prepareThumbnailView this.thumbRowBuffer = 2; // number of rows to pre-cache out a view this.thumbColumns = 6; // default this.thumbMaxLoading = 4; // number of thumbnails to load at once + this.thumbPadding = 10; // spacing between thumbnails this.displayedRows=[]; this.displayedIndices = []; @@ -62,7 +71,7 @@ function BookReader() { this.twoPagePopUp = null; this.leafEdgeTmp = null; this.embedPopup = null; - this.popup = null; + this.printPopup = null; this.searchTerm = ''; this.searchResults = {}; @@ -72,15 +81,15 @@ function BookReader() { this.lastDisplayableIndex2up = null; // We link to index.php to avoid redirect which breaks back button - this.logoURL = 'http://www.archive.org/index.php'; + // Should be overriden (before init) by custom implmentations. + this.logoURL = 'http://openlibrary.org'; - // Base URL for images + // Base URL for UI images - should be overriden (before init) by + // custom implementations. + // $$$ This is the same directory as the images referenced by relative + // path in the CSS. Would be better to automagically find that path. this.imagesBaseURL = '/bookreader/images/'; - // Mode constants - this.constMode1up = 1; - this.constMode2up = 2; - this.constModeThumb = 3; // Zoom levels // $$$ provide finer grained zooming @@ -98,18 +107,30 @@ function BookReader() { // 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 + coverInternalPadding: 0, // Width of cover + coverExternalPadding: 0, // Padding outside of cover + bookSpineDivWidth: 0, // Width of book spine $$$ consider sizing based on book length autofit: 'auto' }; + // This object/dictionary controls which optional features are enabled + // XXXmang in progress + this.features = { + // search + // read aloud + // open library entry + // table of contents + // embed/share ui + // info ui + }; + // Text-to-Speech params this.ttsPlaying = false; this.ttsIndex = null; //leaf index this.ttsPosition = -1; //chunk (paragraph) number this.ttsBuffering = false; this.ttsPoller = null; + this.ttsFormat = null; return this; }; @@ -123,16 +144,25 @@ BookReader.prototype.init = function() { // Find start index and mode if set in location hash var params = this.paramsFromFragment(window.location.hash); + + // Sanitize/process parameters + + if ( !this.canSwitchToMode( this.mode ) ) { + this.mode = this.constMode1up; + } if ('undefined' != typeof(params.index)) { startIndex = params.index; } else if ('undefined' != typeof(params.page)) { startIndex = this.getPageIndex(params.page); } - + if ('undefined' == typeof(startIndex)) { if ('undefined' != typeof(this.titleLeaf)) { - startIndex = this.leafNumToIndex(this.titleLeaf); + // title leaf is known - but only use as default if book has a few pages + if (this.numLeafs > 2) { + startIndex = this.leafNumToIndex(this.titleLeaf); + } } } @@ -148,21 +178,25 @@ BookReader.prototype.init = function() { // search engine visibility document.title = this.shortTitle(50); - // Sanitize parameters - if ( !this.canSwitchToMode( this.mode ) ) { - this.mode = this.constMode1up; - } - $("#BookReader").empty(); this.initToolbar(this.mode, this.ui); // Build inside of toolbar div + $("#BookReader").append("
"); $("#BRcontainer").append("
"); + + // Autohide nav after showing for awhile + var self = this; + if (this.uiAutoHide) { + $(window).bind('load', function() { + setTimeout(function() { self.hideNavigation(); }, 3000); + }); + }; $("#BRcontainer").bind('scroll', this, function(e) { e.data.loadLeafs(); }); - + this.setupKeyListeners(); this.startLocationPolling(); @@ -234,6 +268,16 @@ BookReader.prototype.init = function() { // Enact other parts of initial params this.updateFromParams(params); + + // We init the nav bar after the params processing so that the nav slider knows where + // it should start (doesn't jump after init) + this.initNavbar(); + this.bindNavigationHandlers(); + + // Start AJAX request for OL data + if (this.getOpenLibraryRecord) { + this.getOpenLibraryRecord(this.gotOpenLibraryRecord); + } } BookReader.prototype.setupKeyListeners = function() { @@ -320,7 +364,6 @@ BookReader.prototype.bindGestures = function(jElement) { br.zoom(-1); } }); - } BookReader.prototype.setClickHandler2UP = function( element, data, handler) { @@ -364,9 +407,14 @@ BookReader.prototype.drawLeafsOnePage = function() { leafTop += height +10; leafBottom += 10; } - + + // Based of the pages displayed in the view we set the current index + // $$$ we should consider the page in the center of the view to be the current one var firstIndexToDraw = indicesToDisplay[0]; - this.firstIndex = firstIndexToDraw; + if (firstIndexToDraw != this.firstIndex) { + this.willChangeToIndex(firstIndexToDraw); + } + this.firstIndex = firstIndexToDraw; // Update hash, but only if we're currently displaying a leaf // Hack that fixes #365790 @@ -483,7 +531,7 @@ BookReader.prototype.drawLeafsThumbnail = function( seekIndex ) { // Calculate the position of every thumbnail. $$$ cache instead of calculating on every draw for (i=0; i viewWidth){ + if (rightPos + (leafWidth + this.thumbPadding) > viewWidth){ currentRow++; rightPos = 0; leafIndex = 0; @@ -503,13 +551,13 @@ BookReader.prototype.drawLeafsThumbnail = function( seekIndex ) { if (leafHeight > leafMap[currentRow].height) { leafMap[currentRow].height = leafHeight; } - if (leafIndex===0) { bottomPos += this.padding + leafMap[currentRow].height; } - rightPos += leafWidth + this.padding; + if (leafIndex===0) { bottomPos += this.thumbPadding + leafMap[currentRow].height; } + rightPos += leafWidth + this.thumbPadding; if (rightPos > maxRight) { maxRight = rightPos; } leafIndex++; if (i == seekIndex) { - seekTop = bottomPos - this.padding - leafMap[currentRow].height; + seekTop = bottomPos - this.thumbPadding - leafMap[currentRow].height; } } @@ -536,7 +584,7 @@ BookReader.prototype.drawLeafsThumbnail = function( seekIndex ) { // Determine the thumbnails in view for (i=0; i= scrollTop) && (leafTop <= scrollBottom); var bottomInView = (leafBottom >= scrollTop) && (leafBottom <= scrollBottom); var middleInView = (leafTop <=scrollTop) && (leafBottom>=scrollBottom); @@ -594,7 +642,7 @@ BookReader.prototype.drawLeafsThumbnail = function( seekIndex ) { div.style.position = "absolute"; div.className = "BRpagedivthumb"; - left += this.padding; + left += this.thumbPadding; $(div).css('top', leafTop + 'px'); $(div).css('left', left+'px'); $(div).css('width', leafWidth+'px'); @@ -659,8 +707,10 @@ BookReader.prototype.drawLeafsThumbnail = function( seekIndex ) { // console.log('current ' + currentIndex); // console.log('least visible ' + leastVisible + ' most visible ' + mostVisible); if (currentIndex < leastVisible) { + this.willChangeToIndex(leastVisible); this.setCurrentIndex(leastVisible); } else if (currentIndex > mostVisible) { + this.willChangeToIndex(mostVisible); this.setCurrentIndex(mostVisible); } @@ -781,7 +831,6 @@ BookReader.prototype.drawLeafsTwoPage = function() { top: top+'px', height: this.twoPage.height +'px', // $$$ height forced the same for both pages width: this.twoPage.scaledWL + 'px', - borderRight: '1px solid black', zIndex: 2 }).appendTo('#BRtwopageview'); @@ -800,7 +849,6 @@ BookReader.prototype.drawLeafsTwoPage = function() { top: top+'px', height: this.twoPage.height + 'px', // $$$ height forced the same for both pages width: this.twoPage.scaledWR + 'px', - borderLeft: '1px solid black', zIndex: 2 }).appendTo('#BRtwopageview'); @@ -915,7 +963,7 @@ BookReader.prototype.resizePageView = function() { switch (this.mode) { case this.constMode1up: case this.constMode2up: - this.resizePageView1up(); + this.resizePageView1up(); // $$$ necessary in non-1up mode? break; case this.constModeThumb: this.prepareThumbnailView( this.currentIndex() ); @@ -925,6 +973,7 @@ BookReader.prototype.resizePageView = function() { } } +// Resize the current one page view BookReader.prototype.resizePageView1up = function() { var i; var viewHeight = 0; @@ -933,16 +982,30 @@ BookReader.prototype.resizePageView1up = function() { var oldScrollTop = $('#BRcontainer').attr('scrollTop'); var oldScrollLeft = $('#BRcontainer').attr('scrollLeft'); + var oldPageViewHeight = $('#BRpageview').height(); var oldPageViewWidth = $('#BRpageview').width(); - var oldCenterY = this.centerY1up(); - var oldCenterX = this.centerX1up(); - - if (0 != oldPageViewHeight) { - var scrollRatio = oldCenterY / oldPageViewHeight; + // May have come here after preparing the view, in which case the scrollTop and view height are not set + + var scrollRatio = 0; + if (oldScrollTop > 0) { + // We have scrolled - implies view has been set up + var oldCenterY = this.centerY1up(); + var oldCenterX = this.centerX1up(); + scrollRatio = oldCenterY / oldPageViewHeight; } else { - var scrollRatio = 0; + // Have not scrolled, e.g. because in new container + + // We set the scroll ratio so that the current index will still be considered the + // current index in drawLeafsOnePage after we create the new view container + + // Make sure this will count as current page after resize + // console.log('fudging for index ' + this.currentIndex() + ' (page ' + this.getPageNum(this.currentIndex()) + ')'); + var fudgeFactor = (this.getPageHeight(this.currentIndex()) / this.reduce) * 0.6; + var oldLeafTop = this.onePageGetPageTop(this.currentIndex()) + fudgeFactor; + var oldViewDimensions = this.onePageCalculateViewDimensions(this.reduce, this.padding); + scrollRatio = oldLeafTop / oldViewDimensions.height; } // Recalculate 1up reduction factors @@ -954,15 +1017,11 @@ BookReader.prototype.resizePageView1up = function() { this.reduce = reductionFactor.reduce; } - for (i=0; iviewWidth) viewWidth=width; - } - $('#BRpageview').height(viewHeight); - $('#BRpageview').width(viewWidth); + var viewDimensions = this.onePageCalculateViewDimensions(this.reduce, this.padding); + $('#BRpageview').height(viewDimensions.height); + $('#BRpageview').width(viewDimensions.width); - var newCenterY = scrollRatio*viewHeight; + var newCenterY = scrollRatio*viewDimensions.height; var newTop = Math.max(0, Math.floor( newCenterY - $('#BRcontainer').height()/2 )); $('#BRcontainer').attr('scrollTop', newTop); @@ -980,6 +1039,17 @@ BookReader.prototype.resizePageView1up = function() { this.updateSearchHilites(); } +// Calculate the dimensions for a one page view with images at the given reduce and padding +BookReader.prototype.onePageCalculateViewDimensions = function(reduce, padding) { + var viewWidth = 0; + var viewHeight = 0; + for (i=0; iviewWidth) viewWidth=width; + } + return { width: viewWidth, height: viewHeight } +} // centerX1up() //______________________________________________________________________________ @@ -1078,7 +1148,7 @@ BookReader.prototype.zoomThumb = function(direction) { // Note: #BRpageview must already exist since its width is used to calculate the // thumbnail width BookReader.prototype.getThumbnailWidth = function(thumbnailColumns) { - var padding = (thumbnailColumns + 1) * this.padding; + var padding = (thumbnailColumns + 1) * this.thumbPadding; var width = ($('#BRpageview').width() - padding) / (thumbnailColumns + 0.5); // extra 0.5 is for some space at sides return parseInt(width); } @@ -1167,6 +1237,8 @@ BookReader.prototype.jumpToPage = function(pageNum) { //______________________________________________________________________________ BookReader.prototype.jumpToIndex = function(index, pageX, pageY) { + this.willChangeToIndex(index); + if (this.constMode2up == this.mode) { this.autoStop(); @@ -1191,7 +1263,7 @@ BookReader.prototype.jumpToIndex = function(index, pageX, pageY) { for (i=0; i<(index+1); i++) { leafWidth = this.thumbWidth; - if (rightPos + (leafWidth + this.padding) > viewWidth){ + if (rightPos + (leafWidth + this.thumbPadding) > viewWidth){ rightPos = 0; rowHeight = 0; leafIndex = 0; @@ -1200,8 +1272,8 @@ BookReader.prototype.jumpToIndex = function(index, pageX, pageY) { 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; } - rightPos += leafWidth + this.padding; + if (leafIndex==0) { bottomPos += this.thumbPadding + rowHeight; } + rightPos += leafWidth + this.thumbPadding; leafIndex++; } this.firstIndex=index; @@ -1212,14 +1284,7 @@ BookReader.prototype.jumpToIndex = function(index, pageX, pageY) { } } else { // 1up - var i; - var leafTop = 0; - var leafLeft = 0; - var h; - for (i=0; i"); - + // Attaches to first child - child must be present $('#BRcontainer').dragscrollable(); this.bindGestures($('#BRcontainer')); @@ -1432,16 +1499,13 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc width: this.twoPage.bookCoverDivWidth + 'px', height: this.twoPage.bookCoverDivHeight+'px', visibility: 'visible', - position: 'absolute', - left: this.twoPage.bookCoverDivLeft + 'px', - top: this.twoPage.bookCoverDivTop+'px' }).appendTo('#BRtwopageview'); this.leafEdgeR = document.createElement('div'); this.leafEdgeR.className = 'BRleafEdgeR'; $(this.leafEdgeR).css({ width: this.twoPage.leafEdgeWidthR + 'px', - height: this.twoPage.height-1 + 'px', + height: this.twoPage.height + 'px', left: this.twoPage.gutter+this.twoPage.scaledWR+'px', top: this.twoPage.bookCoverDivTop+this.twoPage.coverInternalPadding+'px' }).appendTo('#BRtwopageview'); @@ -1450,7 +1514,7 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc this.leafEdgeL.className = 'BRleafEdgeL'; $(this.leafEdgeL).css({ width: this.twoPage.leafEdgeWidthL + 'px', - height: this.twoPage.height-1 + 'px', + height: this.twoPage.height + 'px', left: this.twoPage.bookCoverDivLeft+this.twoPage.coverInternalPadding+'px', top: this.twoPage.bookCoverDivTop+this.twoPage.coverInternalPadding+'px' }).appendTo('#BRtwopageview'); @@ -1477,7 +1541,7 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc top: this.twoPageFlipAreaTop() + 'px', cursor: 'w-resize', zIndex: 100 - }).bind('click', function(e) { + }).click(function(e) { self.left(); }).bind('mousedown', function(e) { e.preventDefault(); @@ -1494,7 +1558,7 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc top: this.twoPageFlipAreaTop() + 'px', cursor: 'e-resize', zIndex: 100 - }).bind('click', function(e) { + }).click(function(e) { self.right(); }).bind('mousedown', function(e) { e.preventDefault(); @@ -1543,12 +1607,14 @@ BookReader.prototype.prepareTwoPagePopUp = function() { $(this.leafEdgeL).bind('click', this, function(e) { e.data.autoStop(); + e.data.ttsStop(); var jumpIndex = e.data.jumpIndexForLeftEdgePageX(e.pageX); e.data.jumpToIndex(jumpIndex); }); $(this.leafEdgeR).bind('click', this, function(e) { e.data.autoStop(); + e.data.ttsStop(); var jumpIndex = e.data.jumpIndexForRightEdgePageX(e.pageX); e.data.jumpToIndex(jumpIndex); }); @@ -1561,7 +1627,7 @@ BookReader.prototype.prepareTwoPagePopUp = function() { // $$$ TODO: Make sure popup is positioned so that it is in view // (https://bugs.edge.launchpad.net/gnubook/+bug/327456) $(e.data.twoPagePopUp).css({ - left: e.pageX- $('#BRcontainer').offset().left + $('#BRcontainer').scrollLeft() + 20 + 'px', + left: e.pageX- $('#BRcontainer').offset().left + $('#BRcontainer').scrollLeft() - 100 + 'px', top: e.pageY - $('#BRcontainer').offset().top + $('#BRcontainer').scrollTop() + 'px' }); }); @@ -1574,7 +1640,7 @@ BookReader.prototype.prepareTwoPagePopUp = function() { // $$$ TODO: Make sure popup is positioned so that it is in view // (https://bugs.edge.launchpad.net/gnubook/+bug/327456) $(e.data.twoPagePopUp).css({ - left: e.pageX - $('#BRcontainer').offset().left + $('#BRcontainer').scrollLeft() - $(e.data.twoPagePopUp).width() - 25 + 'px', + left: e.pageX - $('#BRcontainer').offset().left + $('#BRcontainer').scrollLeft() - $(e.data.twoPagePopUp).width() + 100 + 'px', top: e.pageY-$('#BRcontainer').offset().top + $('#BRcontainer').scrollTop() + 'px' }); }); @@ -1618,7 +1684,7 @@ BookReader.prototype.calculateSpreadSize = function() { // 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; + this.twoPage.bookCoverDivWidth = this.twoPageCoverWidth(this.twoPage.scaledWL + this.twoPage.scaledWR); // The height of the book cover div this.twoPage.bookCoverDivHeight = this.twoPage.height + 2 * this.twoPage.coverInternalPadding; @@ -1750,6 +1816,20 @@ BookReader.prototype.onePageGetAutofitHeight = function() { return (this.getMedianPageSize().height + 0.0) / ($('#BRcontainer').attr('clientHeight') - this.padding * 2); // make sure a little of adjacent pages show } +// Returns where the top of the page with given index should be in one page view +BookReader.prototype.onePageGetPageTop = function(index) +{ + var i; + var leafTop = 0; + var leafLeft = 0; + var h; + for (i=0; i this.lastDisplayableIndex()) return; + this.willChangeToIndex(index); + this.animating = true; var nextIndices = this.getSpreadIndices(index); @@ -2189,6 +2269,16 @@ BookReader.prototype.flipFwdToIndex = function(index) { } } +/* + * Put handlers here for when we will navigate to a new page + */ +BookReader.prototype.willChangeToIndex = function(index) +{ + // Update navbar position icon - leads page change animation + this.updateNavIndex(index); + +} + // flipRightToLeft(nextL, nextR, gutter) // $$$ better not to have to pass gutter in //______________________________________________________________________________ @@ -2211,7 +2301,7 @@ BookReader.prototype.flipRightToLeft = function(newIndexL, newIndexR) { this.leafEdgeTmp.className = 'BRleafEdgeTmp'; $(this.leafEdgeTmp).css({ width: leafEdgeTmpW + 'px', - height: this.twoPage.height-1 + 'px', + height: this.twoPage.height + 'px', left: gutter+scaledW+'px', top: top+'px', zIndex:1000 @@ -2287,6 +2377,7 @@ BookReader.prototype.setMouseHandlers2UP = function() { this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexL], { self: this }, function(e) { + e.data.self.ttsStop(); e.data.self.left(); e.preventDefault(); } @@ -2295,6 +2386,7 @@ BookReader.prototype.setMouseHandlers2UP = function() { this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR], { self: this }, function(e) { + e.data.self.ttsStop(); e.data.self.right(); e.preventDefault(); } @@ -2367,7 +2459,7 @@ BookReader.prototype.prepareFlipLeftToRight = function(prevL, prevR) { top: top+'px', height: this.twoPage.height, width: scaledW+'px', - borderRight: '1px solid black', + borderRight: '1px solid black', // XXXmang check zIndex: 1 } @@ -2383,8 +2475,8 @@ BookReader.prototype.prepareFlipLeftToRight = function(prevL, prevR) { right: '', top: top+'px', height: this.twoPage.height, - width: '0px', - borderLeft: '1px solid black', + borderLeft: '1px solid black', // XXXmang check + width: '0', zIndex: 2 } @@ -2420,7 +2512,6 @@ BookReader.prototype.prepareFlipRightToLeft = function(nextL, nextR) { top: top+'px', height: this.twoPage.height, width: scaledW+'px', - borderLeft: '1px solid black', zIndex: 1 }); @@ -2437,7 +2528,6 @@ BookReader.prototype.prepareFlipRightToLeft = function(nextL, nextR) { top: top+'px', height: this.twoPage.height, width: 0+'px', // Start at 0 width, then grow to the left - borderRight: '1px solid black', zIndex: 2 }); @@ -2557,6 +2647,29 @@ BookReader.prototype.search = function(term) { $('#BookReaderSearchResults').html('Searching...'); } +// searchNew() +//______________________________________________________________________________ +BookReader.prototype.searchNew = function(term) { + //console.log('searchNew called with term=' + term); + var url = 'http://'+this.server.replace(/:.+/, ''); //remove the port and userdir + url += '/~edward/inside_jsonp.php?item_id='+this.bookId; + url += '&doc='+this.subPrefix; //TODO: test with subitem + url += '&path='+this.bookPath.replace(new RegExp('/'+this.subPrefix+'$'), ''); //remove subPrefix from end of path + url += '&q='+escape(term); + //console.log('search url='+url); + this.ttsAjax = $.ajax({url:url, dataType:'jsonp', jsonpCallback:'BRSearchCallbackNew'}); +} + +// Unfortunately, we can't pass 'br.searchCallback' to our search service, +// because it can't handle the '.' +function BRSearchCallbackNew(results) { + //console.log('got ' + results.matches.length + ' results'); + var i; + for (i=0; i

'; - htmlStr += '

Close popup

'; + htmlStr = '

Embed Bookreader

'; + htmlStr += '

The bookreader uses iframes for embedding. It will not work on web hosts that block iframes. The embed feature has been tested on blogspot.com blogs as well as self-hosted Wordpress blogs. This feature will NOT work on wordpress.com blogs.

'; + htmlStr += ''; + htmlStr += 'Close'; this.embedPopup.innerHTML = htmlStr; - $(this.embedPopup).find('input').bind('click', function() { + $('#BookReader').append('
'); + $(this.embedPopup).find('textarea').click(function() { + this.select(); + }) + $(this.embedPopup).addClass("popped"); +} + +// showBookmarkCode() +//______________________________________________________________________________ +BookReader.prototype.showBookmarkCode = function() { + this.bookmarkPopup = document.createElement("div"); + $(this.bookmarkPopup).css({ + position: 'absolute', + top: ($('#BRcontainer').attr('clientHeight')-250)/2 + 'px', + left: ($('#BRcontainer').attr('clientWidth')-400)/2 + 'px', + width: '400px', + height: '250px', + padding: '0', + fontSize: '12px', + color: '#333', + zIndex: 300, + border: '10px solid #615132', + backgroundColor: "#fff", + MozBorderRadius: '8px', + MozBoxShadow: '0 0 6px #000', + WebkitBorderRadius: '8px', + WebkitBoxShadow: '0 0 6px #000' + }).appendTo('#BookReader'); + + htmlStr = '

Add a bookmark

'; + htmlStr += '

You can add a bookmark to any page in any book. If you elect to make your bookmark public, other readers will be able to see it. You must be logged in to your Open Library account to add bookmarks.

'; + htmlStr += '



'; + htmlStr += 'Close'; + + this.bookmarkPopup.innerHTML = htmlStr; + $('#BookReader').append('
'); + $(this.bookmarkPopup).find('textarea').click(function() { this.select(); }) + $(this.bookmarkPopup).addClass("popped"); } + // autoToggle() //______________________________________________________________________________ BookReader.prototype.autoToggle = function() { @@ -3131,32 +3291,374 @@ BookReader.prototype.jumpIndexForRightEdgePageX = function(pageX) { } } +// initNavbar +//______________________________________________________________________________ +// Initialize the navigation bar. +// $$$ this could also add the base elements to the DOM, so disabling the nav bar +// could be as simple as not calling this function +BookReader.prototype.initNavbar = function() { + // Setup nav / chapter / search results bar + + // $$$ should make this work inside the BookReader div (self-contained), rather than after + $('#BookReader').after( + '
' + + '
' // Page turn buttons + + '' + + '' + + '
' + + '
' // Page slider and nav line + //+ '
' + + '
' // Page slider + + '
' // Nav line with e.g. chapter markers + + '
' + + '
' + + '
' + + '
' + + '
' + ); + +/* +
+
+ A related distinction is between the emotion and the results of the emotion, principally behaviors and emotional expressions. People often behave in certain ways as a direct result of their emotional state, such as crying, fighting or fleeing. Page 163 +
IV. The Witch | Page 163
+
+
+*/ + + /* $$$mang search results and chapters should automatically coalesce + $('.searchChap').bt({ + contentSelector: '$(this).find(".query")', + trigger: 'click', + closeWhenOthersOpen: true, + cssStyles: { + width: '250px', + padding: '10px 10px 15px', + backgroundColor: '#fff', + border: '3px solid #e2dcc5', + borderBottom: 'none', + fontFamily: '"Lucida Grande","Arial",sans-serif', + fontSize: '12px', + lineHeight: '18px', + color: '#615132' + }, + shrinkToFit: false, + width: '230px', + padding: 0, + spikeGirth: 0, + spikeLength: 0, + overlap: '10px', + overlay: false, + killTitle: true, + textzIndex: 9999, + boxzIndex: 9998, + wrapperzIndex: 9997, + offsetParent: null, + positions: ['top'], + fill: 'white', + windowMargin: 10, + strokeWidth: 3, + strokeStyle: '#e2dcc5', + cornerRadius: 0, + centerPointX: 0, + centerPointY: 0, + shadow: false + }); + $('.searchChap').each(function(){ + $(this).hover(function(){ + $(this).addClass('front'); + },function(){ + $(this).removeClass('front'); + }); + }); + */ + var self = this; + $('#BRpager').slider({ + animate: true, + min: 0, + max: this.numLeafs - 1, + value: this.currentIndex() + }) + .bind('slide', function(event, ui){ + self.updateNavPageNum(ui.value); + $("#pagenum").show(); + return true; + }) + .bind('slidechange', function(event, ui) { + $("#pagenum").hide(); + + // recursion prevention for jumpToIndex + if ( $(this).data('swallowchange') ) { + $(this).data('swallowchange', false); + } else { + self.jumpToIndex(ui.value); + } + return true; + }) + .hover(function() { + $("#pagenum").show(); + },function(){ + // XXXmang not triggering on iPad - probably due to touch event translation layer + $("#pagenum").hide(); + } + ); + + //append icon to handle + var handleHelper = $('#BRpager .ui-slider-handle') + // $$$mang update logic for setting the page number label -- use page numbers if available + .append('
'); + //.wrap('
').parent(); // XXXmang is this used for hiding the tooltip? + + $('.BRicon.book_left').bind('click', function() { + self.left(); + }); + $('.BRicon.book_right').bind('click', function() { + self.right(); + }); + + this.updateNavPageNum(this.currentIndex()); + + $("#BRzoombtn").draggable({axis:'y',containment:'parent'}); + + //XXXmang remove once done testing + //this.addSearchResult("There is a place where the sidewalk ends And before the street begins, And there the grass grows soft and white, And there the sun burns crimson bright,And there the moon-bird rests from his flight To cool in the peppermint wind.", "20", 31); + //this.addSearchResult("There is a place where the sidewalk BEGINS And there the moon-bird rests from his flight To cool in the peppermint wind.", "60", 71); + +} + +BookReader.prototype.updateNavPageNum = function(index) { + var pageNum = this.getPageNum(index); + var pageStr; + if (pageNum[0] == 'n') { // funny index + pageStr = index + ' / ' + this.numLeafs; + } else { + pageStr = 'Page ' + pageNum; + } + + $('#pagenum .currentpage').text(pageStr); +} + +/* + * Update the nav bar display - does not cause navigation. + */ +BookReader.prototype.updateNavIndex = function(index) { + // We want to update the value, but normally moving the slider + // triggers jumpToIndex which triggers this method + $('#BRpager').data('swallowchange', true).slider('value', index); +} + +BookReader.prototype.addSearchResult = function(queryString, pageIndex) { + var pageNumber = this.getPageNum(pageIndex); + var uiStringSearch = "Search result"; // i18n + var uiStringPage = "Page"; // i18n + + var percentThrough = BookReader.util.cssPercentage(pageIndex, this.numLeafs - 1); + var pageDisplayString = ''; + if (pageNumber) { + pageDisplayString = uiStringPage + ' ' + pageNumber; + } + + var re = new RegExp('{{{(.+?)}}}', 'g'); + queryString = queryString.replace(re, '$1') + + $('" + ""); this.updateToolbarZoom(this.reduce); // Pretty format @@ -3177,11 +3679,15 @@ BookReader.prototype.initToolbar = function(mode, ui) { var titles = { '.logo': 'Go to Archive.org', '.zoom_in': 'Zoom in', '.zoom_out': 'Zoom out', - '.one_page_mode': 'One-page view', - '.two_page_mode': 'Two-page view', - '.thumbnail_mode': 'Thumbnail view', + '.onepg': 'One-page view', + '.twopg': 'Two-page view', + '.thumb': 'Thumbnail view', '.print': 'Print this page', - '.embed': 'Embed bookreader', + '.embed': 'Embed BookReader', + '.link': 'Link to this book (and page)', + '.bookmark': 'Bookmark this page', + '.read': 'Allow BookReader to read this aloud', + '.full': 'Show fullscreen', '.book_left': 'Flip left', '.book_right': 'Flip right', '.book_up': 'Page up', @@ -3206,11 +3712,19 @@ BookReader.prototype.initToolbar = function(mode, ui) { // Hide mode buttons and autoplay if 2up is not available // $$$ if we end up with more than two modes we should show the applicable buttons if ( !this.canSwitchToMode(this.constMode2up) ) { - jToolbar.find('.one_page_mode, .two_page_mode, .play, .pause').hide(); + jToolbar.find('.two_page_mode, .play, .pause').hide(); + } + if ( !this.canSwitchToMode(this.constModeThumb) ) { + jToolbar.find('.thumbnail_mode').hide(); + } + + // Hide one page button if it is the only mode available + if ( ! (this.canSwitchToMode(this.constMode2up) || this.canSwitchToMode(this.constModeThumb)) ) { + jToolbar.find('.one_page_mode').hide(); } // Switch to requested mode -- binds other click handlers - this.switchToolbarMode(mode); + //this.switchToolbarMode(mode); } @@ -3248,12 +3762,12 @@ BookReader.prototype.bindToolbarNavHandlers = function(jToolbar) { var self = this; // closure - jToolbar.find('.book_left').bind('click', function(e) { + jToolbar.find('.book_left').click(function(e) { self.left(); return false; }); - jToolbar.find('.book_right').bind('click', function(e) { + jToolbar.find('.book_right').click(function(e) { self.right(); return false; }); @@ -3276,45 +3790,71 @@ BookReader.prototype.bindToolbarNavHandlers = function(jToolbar) { return false; }); - jToolbar.find('.print').bind('click', function(e) { + jToolbar.find('.print').click(function(e) { self.printPage(); return false; }); - jToolbar.find('.embed').bind('click', function(e) { + jToolbar.find('.embed').click(function(e) { self.showEmbedCode(); return false; }); - jToolbar.find('.play').bind('click', function(e) { + jToolbar.find('.bookmark').click(function(e) { + self.showBookmarkCode(); + return false; + }); + + jToolbar.find('.play').click(function(e) { self.autoToggle(); return false; }); - jToolbar.find('.pause').bind('click', function(e) { + jToolbar.find('.pause').click(function(e) { self.autoToggle(); return false; }); - jToolbar.find('.book_top').bind('click', function(e) { + jToolbar.find('.book_top').click(function(e) { self.first(); return false; }); - jToolbar.find('.book_bottom').bind('click', function(e) { + jToolbar.find('.book_bottom').click(function(e) { self.last(); return false; }); - jToolbar.find('.book_leftmost').bind('click', function(e) { + jToolbar.find('.book_leftmost').click(function(e) { self.leftmost(); return false; }); - jToolbar.find('.book_rightmost').bind('click', function(e) { + jToolbar.find('.book_rightmost').click(function(e) { self.rightmost(); return false; }); + + jToolbar.find('.read').click(function(e) { + self.ttsToggle(); + return false; + }); + + // $$$mang cleanup + $('#BRzoomer .zoom_in').bind('click', function() { + self.zoom(1); + return false; + }); + + $('#BRzoomer .zoom_out').bind('click', function() { + self.zoom(-1); + return false; + }); + + $('#booksearch').bind('submit', function() { + self.searchNew($('#textSrch').val()); + }); + } // updateToolbarZoom(reduce) @@ -3343,6 +3883,78 @@ BookReader.prototype.updateToolbarZoom = function(reduce) { $('#BRzoom').text(value); } +// bindNavigationHandlers +//______________________________________________________________________________ +// Bind navigation handlers +BookReader.prototype.bindNavigationHandlers = function() { + $('#BookReader').die('mousemove.navigation').live('mousemove.navigation', + { 'br': this }, + this.navigationMousemoveHandler + ); +} + +// unbindNavigationHandlers +//______________________________________________________________________________ +// Unbind navigation handlers +BookReader.prototype.unbindNavigationHandlers = function() { + $('#BookReader').die('mousemove.navigation'); +} + +// navigationMousemoveHandler +//______________________________________________________________________________ +// Handle mousemove related to navigation. Bind at #BookReader level to allow autohide. +BookReader.prototype.navigationMousemoveHandler = function(event) { + // $$$ possibly not great to be calling this for every mousemove + + if (event.data['br'].uiAutoHide) { + var navkey = $(document).height() - 75; + if ((event.pageY < 76) || (event.pageY > navkey)) { + // inside or near navigation elements + event.data['br'].hideNavigation(); + } else { + event.data['br'].showNavigation(); + } + } +} + +// navigationIsVisible +//______________________________________________________________________________ +// Returns true if the navigation elements are currently visible +BookReader.prototype.navigationIsVisible = function() { + // $$$ doesn't account for transitioning states, nav must be fully visible to return true + var toolpos = $('#BRtoolbar').offset(); + var tooltop = toolpos.top; + if (tooltop == 0) { + return true; + } + return false; +} + +// hideNavigation +//______________________________________________________________________________ +// Hide navigation elements, if visible +BookReader.prototype.hideNavigation = function() { + // Check if navigation is showing + if (this.navigationIsVisible()) { + // $$$ don't hardcode height + $('#BRtoolbar').animate({top:-60}); + $('#BRnav').animate({bottom:-60}); + $('#BRzoomer').animate({right:-26}); + } +} + +// showNavigation +//______________________________________________________________________________ +// Show navigation elements +BookReader.prototype.showNavigation = function() { + // Check if navigation is hidden + if (!this.navigationIsVisible()) { + $('#BRtoolbar').animate({top:0}); + $('#BRnav').animate({bottom:0}); + $('#BRzoomer').animate({right:0}); + } +} + // firstDisplayableIndex //______________________________________________________________________________ // Returns the index of the first visible page, dependent on the mode. @@ -3655,6 +4267,8 @@ BookReader.prototype.startLocationPolling = function() { if (newHash != self.oldUserHash) { // Only process new user hash once //console.log('url change detected ' + self.oldLocationHash + " -> " + newHash); + self.ttsStop(); + // Queue change if animating if (self.animating) { self.autoStop(); @@ -3674,11 +4288,11 @@ BookReader.prototype.startLocationPolling = function() { //________ // Returns true if we can switch to the requested mode BookReader.prototype.canSwitchToMode = function(mode) { - if (mode == this.constMode2up) { + if (mode == this.constMode2up || mode == this.constModeThumb) { // check there are enough pages to display // $$$ this is a workaround for the mis-feature that we can't display // short books in 2up mode - if (this.numLeafs < 6) { + if (this.numLeafs < 2) { return false; } } @@ -3755,6 +4369,22 @@ BookReader.prototype._getPageURI = function(index, reduce, rotate) { return this.getPageURI(index, reduce, rotate); } +/* + * Update based on received record from Open Library. + */ +BookReader.prototype.gotOpenLibraryRecord = function(self, olObject) { + // $$$ could refactor this so that 'this' is available + if (olObject) { + if (olObject['table_of_contents']) { + self.updateTOC(olObject['table_of_contents']); + } + } + + // $$$mang cleanup + $('#BRreturn a').attr('href', 'http://openlibrary.org' + olObject.key); + +} + // Library functions BookReader.util = { disableSelect: function(jObject) { @@ -3773,6 +4403,11 @@ BookReader.util = { return Math.min(Math.max(value, min), max); }, + // Given value and maximum, calculate a percentage suitable for CSS + cssPercentage: function(value, max) { + return (((value + 0.0) / max) * 100) + '%'; + }, + notInArray: function(value, array) { // inArray returns -1 or undefined if value not in array return ! (jQuery.inArray(value, array) >= 0); @@ -3797,27 +4432,53 @@ BookReader.util = { } -// ttsStart() +// ttsToggle() //______________________________________________________________________________ -BookReader.prototype.ttsStart = function () { - if (false == this.ttsPlaying) { - console.log('starting readAloud'); +BookReader.prototype.ttsToggle = function () { + if (false == this.ttsPlaying) { this.ttsPlaying = true; - this.ttsIndex = this.currentIndex(); - this.ttsGetText(this.ttsIndex, 'ttsStartCB'); + this.ttsShowPopup(); + if(soundManager.supported()) { + this.ttsStart(); + } else { + soundManager.onready(function(oStatus) { + if (oStatus.success) { + this.ttsStart(); + } else { + alert('Could not load soundManager2, possibly due to FlashBlock. Audio playback is disabled'); + } + }, this); + } } else { this.ttsStop(); } } +// ttsStart() +//______________________________________________________________________________ +BookReader.prototype.ttsStart = function () { + if (soundManager.debugMode) console.log('starting readAloud'); + if (this.constModeThumb == this.mode) this.switchMode(this.constMode1up); + + //this.ttsPlaying = true; //set this in ttsToggle() + this.ttsIndex = this.currentIndex(); + this.ttsFormat = 'mp3'; + if ($.browser.mozilla) { + this.ttsFormat = 'ogg'; + } + this.ttsGetText(this.ttsIndex, 'ttsStartCB'); +} + // ttsStop() //______________________________________________________________________________ BookReader.prototype.ttsStop = function () { if (false == this.ttsPlaying) return; - console.log('stopping readaloud'); + if (soundManager.debugMode) console.log('stopping readaloud'); soundManager.stopAll(); + soundManager.destroySound('chunk'+this.ttsIndex+'-'+this.ttsPosition); this.ttsRemoveHilites(); + this.ttsRemovePopup(); this.ttsPlaying = false; this.ttsIndex = null; //leaf index @@ -3829,38 +4490,37 @@ BookReader.prototype.ttsStop = function () { // ttsGetText() //______________________________________________________________________________ BookReader.prototype.ttsGetText = function(index, callback) { - var url = 'http://'+this.server+'/BookReader/BookReaderGetTextWrapper.php?path='+this.bookPath+'_djvu.xml&page='+index; + var url = 'http://'+this.server+'/BookReader/BookReaderGetTextWrapper.php?path='+this.bookPath+'_djvu.xml&page='+index; this.ttsAjax = $.ajax({url:url, dataType:'jsonp', jsonpCallback:callback}); } // ttsStartCB(): text-to-speech callback //______________________________________________________________________________ BookReader.prototype.ttsStartCB = function (data) { - console.log('ttsStartCB got data:'); - console.log(data); + if (soundManager.debugMode) console.log('ttsStartCB got data: ' + data); this.ttsChunks = data; this.ttsHilites = []; //deal with the page being blank if (0 == data.length) { - console.log('first page is blank!'); + if (soundManager.debugMode) console.log('first page is blank!'); if(this.ttsAdvance(true)) { this.ttsGetText(this.ttsIndex, 'ttsStartCB'); } return; - } else { - console.log('length = ' + data.length); - console.log((0 == data.length)); } this.ttsShowPopup(); + ///// whileloading: broken on safari + ///// onload fires on safari, but *after* the sound starts playing.. this.ttsPosition = -1; var snd = soundManager.createSound({ id: 'chunk'+this.ttsIndex+'-0', - //url: 'http://home.us.archive.org/~rkumar/arctic.ogg', - url: 'http://home.us.archive.org/~rkumar/getOgg.php?string=' + escape(data[0][0]) + '&f=.ogg', //the .ogg is to trick SoundManager2 to use the HTML5 audio player - whileloading: function(){if (this.bytesLoaded == this.bytesTotal) {$(br.popup).remove(); br.popup=null;}} //onload never fires... + //url: 'http://home.us.archive.org/~rkumar/arctic.ogg', + url: 'http://'+this.server+'/BookReader/BookReaderGetTTS.php?string=' + escape(data[0][0]) + '&format=.'+this.ttsFormat, //the .ogg is to trick SoundManager2 to use the HTML5 audio player + whileloading: function(){if (this.bytesLoaded == this.bytesTotal) this.br.ttsRemovePopup();}, //onload never fires in FF... + onload: function(){this.br.ttsRemovePopup();} //whileloading never fires in safari... }); snd.br = this; snd.load(); @@ -3871,35 +4531,37 @@ BookReader.prototype.ttsStartCB = function (data) { // ttsShowPopup //______________________________________________________________________________ BookReader.prototype.ttsShowPopup = function() { + if (soundManager.debugMode) console.log('ttsShowPopup index='+this.ttsIndex+' pos='+this.ttsPosition); + if (this.popup) return; + this.popup = document.createElement("div"); $(this.popup).css({ - position: 'absolute', - top: '20%', - left: ($('#BRcontainer').attr('clientWidth')-600)/2 + 'px', - width: '600px', - padding: "20px", - border: "3px double #999999", - zIndex: 3, - backgroundColor: "#f00", - color: "#fff", - fontSize: '1.875em' - }).appendTo('#BookReader'); + top: $('#BRtoolbar').height() + 'px', + left: $('#BookReader').width()-220 + 'px', + width: '220px', + height: '20px', + }).attr('className', 'BRttsPopUp').appendTo('#BookReader'); - htmlStr = '

Ever wanted to wait while audio loads?
'; - htmlStr += "It's OK. We all do.

"; + htmlStr = ' '; this.popup.innerHTML = htmlStr; } +// ttsRemovePopup +//______________________________________________________________________________ +BookReader.prototype.ttsRemovePopup = function() { + $(this.popup).remove(); + this.popup=null; +} + // ttsNextPageCB //______________________________________________________________________________ BookReader.prototype.ttsNextPageCB = function (data) { this.ttsNextChunks = data; - console.log('preloaded next chunks.. data is'); - console.log(data); + if (soundManager.debugMode) console.log('preloaded next chunks.. data is ' + data); if (true == this.ttsBuffering) { - console.log('ttsNextPageCB: ttsBuffering is true'); + if (soundManager.debugMode) console.log('ttsNextPageCB: ttsBuffering is true'); this.ttsBuffering = false; } } @@ -3909,7 +4571,7 @@ BookReader.prototype.ttsNextPageCB = function (data) { BookReader.prototype.ttsLoadChunk = function (page, pos, string) { var snd = soundManager.createSound({ id: 'chunk'+page+'-'+pos, - url: 'http://home.us.archive.org/~rkumar/getOgg.php?string=' + escape(string) + '&f=.ogg' //the .ogg is to trick SoundManager2 to use the HTML5 audio player + url: 'http://'+this.server+'/BookReader/BookReaderGetTTS.php?string=' + escape(string) + '&format=.'+this.ttsFormat //the .ogg is to trick SoundManager2 to use the HTML5 audio player }); snd.br = this; snd.load() @@ -3925,11 +4587,10 @@ BookReader.prototype.ttsLoadChunk = function (page, pos, string) { // continues after animation is finished. BookReader.prototype.ttsNextChunk = function () { - console.log(this); - console.log(this.ttsPosition); + if (soundManager.debugMode) console.log('nextchunk pos=' + this.ttsPosition); if (-1 != this.ttsPosition) { - soundManager.destroySound('chunk'+this.ttsIndex+'-'+this.ttsPosition); + soundManager.destroySound('chunk'+this.ttsIndex+'-'+this.ttsPosition); } this.ttsRemoveHilites(); //remove old hilights @@ -3949,14 +4610,18 @@ BookReader.prototype.ttsNextChunk = function () { //______________________________________________________________________________ // page flip animation has now completed BookReader.prototype.ttsNextChunkPhase2 = function () { + if (null == this.ttsChunks) { + alert('error: ttsChunks is null?'); //TODO + return; + } + if (0 == this.ttsChunks.length) { - console.log('ttsNextChunk2: ttsChunks.length is zero.. hacking...'); + if (soundManager.debugMode) console.log('ttsNextChunk2: ttsChunks.length is zero.. hacking...'); this.ttsStartCB(this.ttsChunks); return; } - console.log('next chunk is '); - console.log(this.ttsPosition); + if (soundManager.debugMode) console.log('next chunk is ' + this.ttsPosition); //prefetch next page of text if (0 == this.ttsPosition) { @@ -3984,29 +4649,39 @@ BookReader.prototype.ttsAdvance = function (starting) { if (this.ttsPosition >= this.ttsChunks.length) { if (this.ttsIndex == (this.numLeafs-1)) { - console.log('tts stop'); + if (soundManager.debugMode) console.log('tts stop'); return false; } else { if ((null != this.ttsNextChunks) || (starting)) { - console.log('moving to next page!'); + if (soundManager.debugMode) console.log('moving to next page!'); this.ttsIndex++; this.ttsPosition = 0; this.ttsChunks = this.ttsNextChunks; this.ttsNextChunks = null; - if ((this.ttsIndex != this.twoPage.currentIndexL) && (this.ttsIndex != this.twoPage.currentIndexR)) { - this.animationFinishedCallback = this.ttsNextChunkPhase2; - this.next(); - return false; - } else { - return true; + + //A page flip might be necessary. This code is confusing since + //ttsNextChunks might be null if we are starting on a blank page. + if (2 == this.mode) { + if ((this.ttsIndex != this.twoPage.currentIndexL) && (this.ttsIndex != this.twoPage.currentIndexR)) { + if (!starting) { + this.animationFinishedCallback = this.ttsNextChunkPhase2; + this.next(); + return false; + } else { + this.next(); + return true; + } + } else { + return true; + } } } else { - console.log('ttsAdvance: ttsNextChunks is null'); + if (soundManager.debugMode) console.log('ttsAdvance: ttsNextChunks is null'); return false; } } } - + return true; } @@ -4017,9 +4692,7 @@ BookReader.prototype.ttsPrefetchAudio = function () { if(false != this.ttsBuffering) { alert('TTS Error: prefetch() called while content still buffering!'); return; - } - - + } //preload next chunk var nextPos = this.ttsPosition+1; @@ -4027,16 +4700,15 @@ BookReader.prototype.ttsPrefetchAudio = function () { this.ttsLoadChunk(this.ttsIndex, nextPos, this.ttsChunks[nextPos][0]); } else { //for a short page, preload might nt have yet returned.. - console.log('preloading chunk 0 from next page, index='+(this.ttsIndex+1)) + if (soundManager.debugMode) console.log('preloading chunk 0 from next page, index='+(this.ttsIndex+1)); if (null != this.ttsNextChunks) { - console.log(this.ttsNextChunks); if (0 != this.ttsNextChunks.length) { this.ttsLoadChunk(this.ttsIndex+1, 0, this.ttsNextChunks[0][0]); } else { - console.log('prefetchAudio(): ttsNextChunks is zero length!'); + if (soundManager.debugMode) console.log('prefetchAudio(): ttsNextChunks is zero length!'); } } else { - console.log('ttsNextChunks is null, not preloading next page'); + if (soundManager.debugMode) console.log('ttsNextChunks is null, not preloading next page'); this.ttsBuffering = true; } } @@ -4048,31 +4720,91 @@ BookReader.prototype.ttsPrefetchAudio = function () { BookReader.prototype.ttsPlay = function () { var chunk = this.ttsChunks[this.ttsPosition]; - console.log('position = ' + this.ttsPosition); - console.log('chunk = ' + chunk); - console.log(this.ttsChunks); - + if (soundManager.debugMode) { + console.log('ttsPlay position = ' + this.ttsPosition); + console.log('chunk = ' + chunk); + console.log(this.ttsChunks); + } + //add new hilights if (2 == this.mode) { this.ttsHilite2UP(chunk); } else { - alert('only 2 page mode supported for TTS..'); + this.ttsHilite1UP(chunk); } + + this.ttsScrollToChunk(chunk); //play current chunk - if (false == this.ttsBuffering) { + if (false == this.ttsBuffering) { soundManager.play('chunk'+this.ttsIndex+'-'+this.ttsPosition,{onfinish:function(){br.ttsNextChunk();}}); } else { - console.log('playing current chunk, but next chunk is not buffered yet!'); soundManager.play('chunk'+this.ttsIndex+'-'+this.ttsPosition,{onfinish:function(){br.ttsStartPolling();}}); } } +// scrollToChunk() +//______________________________________________________________________________ +BookReader.prototype.ttsScrollToChunk = function(chunk) { + if (this.constMode1up != this.mode) return; + + var leafTop = 0; + var h; + var i; + for (i=0; i