X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=BookReader%2FBookReader.js;h=d4dc4339f9902aa8df3cbffc0849340005be7a19;hb=c3e4e27fc6b55977153ad3c50645eae3f8216ec2;hp=c3481ef2650cd0263eb30b71b5bf2b4144282aba;hpb=896d529d83fb6266ff5d4b220c721edf87492a85;p=bookreader.git diff --git a/BookReader/BookReader.js b/BookReader/BookReader.js index c3481ef..d4dc433 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 = []; @@ -65,7 +74,7 @@ function BookReader() { this.printPopup = null; this.searchTerm = ''; - this.searchResults = {}; + this.searchResults = null; this.firstIndex = null; @@ -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; }; @@ -160,15 +184,14 @@ BookReader.prototype.init = function() { $("#BookReader").append("
"); $("#BRcontainer").append("
"); - - this.initNavbar(); - this.bindNavigationHandlers(); - + // Autohide nav after showing for awhile var self = this; - $(window).bind('load', function() { - setTimeout(function() { self.hideNavigation(); }, 3000); - }); + if (this.uiAutoHide) { + $(window).bind('load', function() { + setTimeout(function() { self.hideNavigation(); }, 3000); + }); + }; $("#BRcontainer").bind('scroll', this, function(e) { e.data.loadLeafs(); @@ -245,8 +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(); - //XXXmang window.setTimeout(self.hideNavigation, 3000); + // Start AJAX request for OL data + if (this.getOpenLibraryRecord) { + this.getOpenLibraryRecord(this.gotOpenLibraryRecord); + } } BookReader.prototype.setupKeyListeners = function() { @@ -333,14 +364,13 @@ BookReader.prototype.bindGestures = function(jElement) { br.zoom(-1); } }); - } BookReader.prototype.setClickHandler2UP = function( element, data, handler) { //console.log('setting handler'); //console.log(element.tagName); - $(element).unbind('tap').bind('tap', data, function(e) { + $(element).unbind('click').bind('click', data, function(e) { handler(e); }); } @@ -377,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 @@ -496,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; @@ -516,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; } } @@ -549,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); @@ -607,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'); @@ -672,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); } @@ -926,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() ); @@ -936,6 +973,7 @@ BookReader.prototype.resizePageView = function() { } } +// Resize the current one page view BookReader.prototype.resizePageView1up = function() { var i; var viewHeight = 0; @@ -944,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 @@ -965,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); @@ -991,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() //______________________________________________________________________________ @@ -1089,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); } @@ -1178,6 +1237,10 @@ BookReader.prototype.jumpToPage = function(pageNum) { //______________________________________________________________________________ BookReader.prototype.jumpToIndex = function(index, pageX, pageY) { + this.willChangeToIndex(index); + + this.ttsStop(); + if (this.constMode2up == this.mode) { this.autoStop(); @@ -1202,7 +1265,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; @@ -1211,8 +1274,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; @@ -1223,14 +1286,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')); @@ -1421,7 +1473,7 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc $('#BRcontainer').append('
'); // Attaches to first child, so must come after we add the page view - $('#BRcontainer').dragscrollable(); + //$('#BRcontainer').dragscrollable(); this.bindGestures($('#BRcontainer')); // $$$ calculate first then set @@ -1446,7 +1498,7 @@ BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPerc $(this.twoPage.coverDiv).attr('id', 'BRbookcover').css({ width: this.twoPage.bookCoverDivWidth + 'px', height: this.twoPage.bookCoverDivHeight+'px', - visibility: 'visible', + visibility: 'visible' }).appendTo('#BRtwopageview'); this.leafEdgeR = document.createElement('div'); @@ -1555,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); }); @@ -1762,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); @@ -2197,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 //______________________________________________________________________________ @@ -2295,7 +2377,13 @@ BookReader.prototype.setMouseHandlers2UP = function() { this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexL], { self: this }, function(e) { + if (e.button == 2) { + // right click + return; + } + e.data.self.ttsStop(); e.data.self.left(); + e.preventDefault(); } ); @@ -2303,6 +2391,11 @@ BookReader.prototype.setMouseHandlers2UP = function() { this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR], { self: this }, function(e) { + if (e.button == 2) { + // right click + return; + } + e.data.self.ttsStop(); e.data.self.right(); e.preventDefault(); } @@ -2327,11 +2420,11 @@ BookReader.prototype.prefetchImg = function(index) { if (loadImage) { //console.log('prefetching ' + index); var img = document.createElement("img"); - img.className = 'BRpageimage'; + $(img).addClass('BRpageimage').addClass('BRnoselect'); if (index < 0 || index > (this.numLeafs - 1) ) { // Facing page at beginning or end, or beyond $(img).css({ - 'background-color': 'transparent' + 'background-color': '#efefef' }); } img.src = pageURI; @@ -2551,92 +2644,49 @@ BookReader.prototype.getPageWidth2UP = function(index) { // search() //______________________________________________________________________________ BookReader.prototype.search = function(term) { - term = term.replace(/\//g, ' '); // strip slashes + //console.log('search called with term=' + term); + var url = 'http://'+this.server.replace(/:.+/, ''); //remove the port and userdir + url += '/fulltext/inside.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); + + term = term.replace(/\//g, ' '); // strip slashes, since this goes in the url this.searchTerm = term; - $('#BookReaderSearchScript').remove(); - var script = document.createElement("script"); - script.setAttribute('id', 'BookReaderSearchScript'); - script.setAttribute("type", "text/javascript"); - script.setAttribute("src", 'http://'+this.server+'/BookReader/flipbook_search_br.php?url='+escape(this.bookPath + '_djvu.xml')+'&term='+term+'&format=XML&callback=br.BRSearchCallback'); - document.getElementsByTagName('head')[0].appendChild(script); - $('#BookReaderSearchBox').val(term); - $('#BookReaderSearchResults').html('Searching...'); + + this.removeSearchResults(); + this.showProgressPopup(' Search results will appear below...'); + this.ttsAjax = $.ajax({url:url, dataType:'jsonp', jsonpCallback:'br.BRSearchCallback'}); } // BRSearchCallback() //______________________________________________________________________________ -BookReader.prototype.BRSearchCallback = function(txt) { - //alert(txt); - if (jQuery.browser.msie) { - var dom=new ActiveXObject("Microsoft.XMLDOM"); - dom.async="false"; - dom.loadXML(txt); - } else { - var parser = new DOMParser(); - var dom = parser.parseFromString(txt, "text/xml"); - } - - $('#BookReaderSearchResults').empty(); - $('#BookReaderSearchResults').append('
    '); - - for (var key in this.searchResults) { - if (null != this.searchResults[key].div) { - $(this.searchResults[key].div).remove(); - } - delete this.searchResults[key]; +BookReader.prototype.BRSearchCallback = function(results) { + //console.log('got ' + results.matches.length + ' results'); + br.removeSearchResults(); + br.searchResults = results; + //console.log(br.searchResults); + + if (0 == results.matches.length) { + $(br.popup).text('No matches were found.'); + setTimeout(function(){ + $(br.popup).fadeOut('slow', function() { + br.removeProgressPopup(); + }) + },1000); + return; } - var pages = dom.getElementsByTagName('PAGE'); - - if (0 == pages.length) { - // $$$ it would be nice to echo the (sanitized) search result here - $('#BookReaderSearchResults').append('
  • No search results found
  • '); - } else { - for (var i = 0; i < pages.length; i++){ - //console.log(pages[i].getAttribute('file').substr(1) +'-'+ parseInt(pages[i].getAttribute('file').substr(1), 10)); - - - var re = new RegExp (/_(\d{4})\.djvu/); - var reMatch = re.exec(pages[i].getAttribute('file')); - var index = parseInt(reMatch[1], 10); - //var index = parseInt(pages[i].getAttribute('file').substr(1), 10); - - var children = pages[i].childNodes; - var context = ''; - for (var j=0; j'; - - var index = this.leafNumToIndex(index); - if (null != index) { - //coordinates are [left, bottom, right, top, [baseline]] - //we'll skip baseline for now... - var coords = children[j].getAttribute('coords').split(',',4); - if (4 == coords.length) { - this.searchResults[index] = {'l':parseInt(coords[0]), 'b':parseInt(coords[1]), 'r':parseInt(coords[2]), 't':parseInt(coords[3]), 'div':null}; - } - } - } - } - var pageName = this.getPageName(index); - var middleX = (this.searchResults[index].l + this.searchResults[index].r) >> 1; - var middleY = (this.searchResults[index].t + this.searchResults[index].b) >> 1; - //TODO: remove hardcoded instance name - $('#BookReaderSearchResults').append('
  • ' + pageName + ' - ' + context + '
  • '); - } + var i; + for (i=0; i'); - - // $$$ update again for case of loading search URL in new browser window (search box may not have been ready yet) - $('#BookReaderSearchBox').val(this.searchTerm); - - this.updateSearchHilites(); + br.updateSearchHilites(); + br.removeProgressPopup(); } + // updateSearchHilites() //______________________________________________________________________________ BookReader.prototype.updateSearchHilites = function() { @@ -2650,30 +2700,39 @@ BookReader.prototype.updateSearchHilites = function() { // showSearchHilites1UP() //______________________________________________________________________________ BookReader.prototype.updateSearchHilites1UP = function() { - - for (var key in this.searchResults) { - - if (jQuery.inArray(parseInt(key), this.displayedIndices) >= 0) { - var result = this.searchResults[key]; - if (null == result.div) { - result.div = document.createElement('div'); - $(result.div).attr('className', 'BookReaderSearchHilite').appendTo('#pagediv'+key); - //console.log('appending ' + key); - } - $(result.div).css({ - width: (result.r-result.l)/this.reduce + 'px', - height: (result.b-result.t)/this.reduce + 'px', - left: (result.l)/this.reduce + 'px', - top: (result.t)/this.reduce +'px' - }); - - } else { - //console.log(key + ' not displayed'); - this.searchResults[key].div=null; + var results = this.searchResults; + if (null == results) return; + var i, j; + for (i=0; i= 0) { + if (null == box.div) { + //create a div for the search highlight, and stash it in the box object + box.div = document.createElement('div'); + $(box.div).attr('className', 'BookReaderSearchHilite').appendTo('#pagediv'+pageIndex); + } + $(box.div).css({ + width: (box.r-box.l)/this.reduce + 'px', + height: (box.b-box.t)/this.reduce + 'px', + left: (box.l)/this.reduce + 'px', + top: (box.t)/this.reduce +'px' + }); + } else { + if (null != box.div) { + //console.log('removing search highlight div'); + $(box.div).remove(); + box.div=null; + } + } } } + } + // twoPageGutter() //______________________________________________________________________________ // Returns the position of the gutter (line between the page images) @@ -2801,65 +2860,86 @@ BookReader.prototype.twoPagePlaceFlipAreas = function() { }); } -// showSearchHilites2UP() +// showSearchHilites2UPNew() //______________________________________________________________________________ BookReader.prototype.updateSearchHilites2UP = function() { - - for (var key in this.searchResults) { - key = parseInt(key, 10); - if (jQuery.inArray(key, this.displayedIndices) >= 0) { - var result = this.searchResults[key]; - if (null == result.div) { - result.div = document.createElement('div'); - $(result.div).attr('className', 'BookReaderSearchHilite').css('zIndex', 3).appendTo('#BRtwopageview'); - //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; + //console.log('updateSearchHilites2UP results = ' + this.searchResults); + var results = this.searchResults; + if (null == results) return; + var i, j; + for (i=0; i= 0) { + if (null == box.div) { + //create a div for the search highlight, and stash it in the box object + box.div = document.createElement('div'); + $(box.div).attr('className', 'BookReaderSearchHilite').css('zIndex', 3).appendTo('#BRtwopageview'); + //console.log('appending new div'); + } + this.setHilightCss2UP(box.div, pageIndex, box.l, box.r, box.t, box.b); } 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' - }); - - } else { - //console.log(key + ' not displayed'); - if (null != this.searchResults[key].div) { - //console.log('removing ' + key); - $(this.searchResults[key].div).remove(); + if (null != box.div) { + //console.log('removing search highlight div'); + $(box.div).remove(); + box.div=null; + } } - this.searchResults[key].div=null; } } + +} + +// 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() { - for (var key in this.searchResults) { - if (null != this.searchResults[key].div) { - $(this.searchResults[key].div).remove(); - this.searchResults[key].div=null; - } - } + var results = this.searchResults; + if (null == results) return; + var i, j; + for (i=0; i' + + '
    ' // 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: { - backgroundColor: '#000', - border: '2px solid #e2dcc5', + width: '250px', + padding: '10px 10px 15px', + backgroundColor: '#fff', + border: '3px solid #e2dcc5', borderBottom: 'none', - padding: '5px 10px', - fontFamily: '"Arial", sans-serif', - fontSize: '11px', - fontWeight: '700', - color: '#fff', - whiteSpace: 'nowrap' + fontFamily: '"Lucida Grande","Arial",sans-serif', + fontSize: '12px', + lineHeight: '18px', + color: '#615132' }, - shrinkToFit: true, - width: '200px', + shrinkToFit: false, + width: '230px', padding: 0, spikeGirth: 0, spikeLength: 0, - overlap: '16px', + overlap: '10px', overlay: false, killTitle: true, textzIndex: 9999, @@ -3205,26 +3329,129 @@ BookReader.prototype.initNavbar = function() { wrapperzIndex: 9997, offsetParent: null, positions: ['top'], - fill: 'black', + fill: 'white', windowMargin: 10, - strokeWidth: 0, + strokeWidth: 3, + strokeStyle: '#e2dcc5', cornerRadius: 0, centerPointX: 0, centerPointY: 0, shadow: false }); - $('.search').bt({ + $('.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? + + // $$$mang, why are these set both here and in bindNavigationHandlers? + $('.BRicon.book_left').bind('click', function() { + self.ttsStop(); + self.left(); + }); + $('.BRicon.book_right').bind('click', function() { + self.ttsStop(); + 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') + + var marker = $('