X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=BookReader%2FBookReader.js;h=36379a126d77b251fcde88a8f45dc2c324022ef4;hb=32dcce1c6c33c22670b8c651356595c6a8c2a0ad;hp=c8b36de09041f63c6c25d36c86a4265abe3bae41;hpb=4aa498933124c7ba3702fcfe39d33d89924ac1e7;p=bookreader.git diff --git a/BookReader/BookReader.js b/BookReader/BookReader.js index c8b36de..36379a1 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 = 0; - 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 = []; @@ -73,7 +82,7 @@ function BookReader() { // We link to index.php to avoid redirect which breaks back button // Should be overriden (before init) by custom implmentations. - this.logoURL = 'http://www.archive.org/index.php'; + this.logoURL = 'http://openlibrary.org'; // Base URL for UI images - should be overriden (before init) by // custom implementations. @@ -81,10 +90,6 @@ function BookReader() { // 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 @@ -108,6 +113,25 @@ function BookReader() { 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; }; @@ -157,13 +181,22 @@ BookReader.prototype.init = function() { $("#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(); @@ -235,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() { @@ -321,7 +364,6 @@ BookReader.prototype.bindGestures = function(jElement) { br.zoom(-1); } }); - } BookReader.prototype.setClickHandler2UP = function( element, data, handler) { @@ -484,7 +526,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; @@ -504,13 +546,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; } } @@ -537,7 +579,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); @@ -595,7 +637,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'); @@ -1077,7 +1119,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); } @@ -1166,6 +1208,8 @@ BookReader.prototype.jumpToPage = function(pageNum) { //______________________________________________________________________________ BookReader.prototype.jumpToIndex = function(index, pageX, pageY) { + this.updateNavIndex(index); + if (this.constMode2up == this.mode) { this.autoStop(); @@ -1190,7 +1234,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; @@ -1199,8 +1243,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; @@ -1261,6 +1305,7 @@ BookReader.prototype.switchMode = function(mode) { } this.autoStop(); + this.ttsStop(); this.removeSearchHilites(); this.mode = mode; @@ -1282,8 +1327,8 @@ BookReader.prototype.switchMode = function(mode) { } else if (3 == mode) { $('button.thumb').hide(); $('button.twopg').show(); + this.reduce = this.quantizeReduce(this.reduce, this.reductionFactors); this.prepareThumbnailView(); - this.reduce = this.quantizeReduce(this.reduce); } else { // $$$ why don't we save autofit? // this.twoPage.autofit = null; // Take zoom level from other mode @@ -1435,21 +1480,13 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc width: this.twoPage.bookCoverDivWidth + 'px', height: this.twoPage.bookCoverDivHeight+'px', visibility: 'visible', - position: 'absolute', - backgroundColor: 'transparent', - backgroundImage: 'url(back_pages.png)', - left: this.twoPage.bookCoverDivLeft + 'px', - top: this.twoPage.bookCoverDivTop+'px', - MozBoxShadow: '0 0 2px #000', - WebkitBoxShadow: '0 0 2px #000' }).appendTo('#BRtwopageview'); this.leafEdgeR = document.createElement('div'); this.leafEdgeR.className = 'BRleafEdgeR'; $(this.leafEdgeR).css({ - background: 'transparent url(back_pages.png) repeat scroll 0% 0%', 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'); @@ -1458,7 +1495,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'); @@ -1537,20 +1574,7 @@ BookReader.prototype.prepareTwoPagePopUp = function() { this.twoPagePopUp = document.createElement('div'); this.twoPagePopUp.className = 'BRtwoPagePopUp'; $(this.twoPagePopUp).css({ - zIndex: '1000', - // XXXmang move to CSS - padding: '6px', - position: 'absolute', - fontFamily: 'Arial,sans-serif', - fontSize: '11px', - color: 'white', - zIndex: '1000', - backgroundColor: '#939598', - opacity: 0.85, - webkitBorderRadius: '4px', - mozBorderRadius: '4px', - borderRadius: '4px', - whiteSpace: 'nowrap' + zIndex: '1000' }).appendTo('#BRcontainer'); $(this.twoPagePopUp).hide(); @@ -1564,12 +1588,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); }); @@ -1639,7 +1665,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; @@ -2006,6 +2032,8 @@ BookReader.prototype.flipBackToIndex = function(index) { } //if (index<0) return; + this.updateNavIndex(index); + var previousIndices = this.getSpreadIndices(index); if (previousIndices[0] < this.firstDisplayableIndex() || previousIndices[1] < this.firstDisplayableIndex()) { @@ -2079,7 +2107,7 @@ BookReader.prototype.flipLeftToRight = 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: leftEdgeTmpLeft + 'px', top: top+'px', zIndex:1000 @@ -2178,7 +2206,7 @@ BookReader.prototype.flipLeftToRight = function(newIndexL, newIndexR) { BookReader.prototype.flipFwdToIndex = function(index) { if (this.animating) return; - + if (null != this.leafEdgeTmp) { alert('error: leafEdgeTmp should be null!'); return; @@ -2189,6 +2217,8 @@ BookReader.prototype.flipFwdToIndex = function(index) { } if (index > this.lastDisplayableIndex()) return; + this.updateNavIndex(index); + this.animating = true; var nextIndices = this.getSpreadIndices(index); @@ -2228,7 +2258,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 @@ -2304,6 +2334,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(); } @@ -2312,6 +2343,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(); } @@ -2824,28 +2856,7 @@ BookReader.prototype.updateSearchHilites2UP = function() { //console.log('appending ' + key); } - // We calculate the reduction factor for the specific page because it can be different - // for each page in the spread - var height = this._getPageHeight(key); - var width = this._getPageWidth(key) - var reduce = this.twoPage.height/height; - var scaledW = parseInt(width*reduce); - - var gutter = this.twoPageGutter(); - var pageL; - if ('L' == this.getPageSide(key)) { - pageL = gutter-scaledW; - } else { - pageL = gutter; - } - var pageT = this.twoPageTop(); - - $(result.div).css({ - width: (result.r-result.l)*reduce + 'px', - height: (result.b-result.t)*reduce + 'px', - left: pageL+(result.l)*reduce + 'px', - top: pageT+(result.t)*reduce +'px' - }); + this.setHilightCss2UP(result.div, key, result.l, result.r, result.t, result.b); } else { //console.log(key + ' not displayed'); @@ -2858,6 +2869,35 @@ BookReader.prototype.updateSearchHilites2UP = function() { } } +// setHilightCss2UP() +//______________________________________________________________________________ +//position calculation shared between search and text-to-speech functions +BookReader.prototype.setHilightCss2UP = function(div, index, left, right, top, bottom) { + + // We calculate the reduction factor for the specific page because it can be different + // for each page in the spread + var height = this._getPageHeight(index); + var width = this._getPageWidth(index) + var reduce = this.twoPage.height/height; + var scaledW = parseInt(width*reduce); + + var gutter = this.twoPageGutter(); + var pageL; + if ('L' == this.getPageSide(index)) { + pageL = gutter-scaledW; + } else { + pageL = gutter; + } + var pageT = this.twoPageTop(); + + $(div).css({ + width: (right-left)*reduce + 'px', + height: (bottom-top)*reduce + 'px', + left: pageL+left*reduce + 'px', + top: pageT+top*reduce +'px' + }); +} + // removeSearchHilites() //______________________________________________________________________________ BookReader.prototype.removeSearchHilites = function() { @@ -2951,6 +2991,12 @@ BookReader.prototype.updatePrintFrame = function(delta) { // showEmbedCode() //______________________________________________________________________________ BookReader.prototype.showEmbedCode = function() { + if (null != this.embedPopup) { // check if already showing + return; + } + this.autoStop(); + this.ttsStop(); + this.embedPopup = document.createElement("div"); $(this.embedPopup).css({ position: 'absolute', @@ -3179,30 +3225,353 @@ 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( + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + ); + +/* +
+
+ 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() { + // $$$ not working on iPad + $("#pagenum").show(); + },function(){ + $("#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'}); +} + +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, pageNumber, pageIndex) { + var uiStringSearch = "Search result"; // i18n + var uiStringPage = "Page"; // i18n + + var percentThrough = BookReader.util.cssPercentage(pageIndex, this.numLeafs); + + // $$$mang add click-through to page + $('