X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=GnuBook%2FGnuBook.js;h=b9eb39bb95997e3702ad3fcfd4bcc7bc02388c08;hb=f2245dea527c994cda1924abbf35d272c4297fc2;hp=63700e4990aff76737d371df8a61db93e09ef4fd;hpb=2d4ae424d3945d9a9323912d888e15dc138a8931;p=bookreader.git diff --git a/GnuBook/GnuBook.js b/GnuBook/GnuBook.js index 63700e4..b9eb39b 100644 --- a/GnuBook/GnuBook.js +++ b/GnuBook/GnuBook.js @@ -24,19 +24,27 @@ This file is part of GnuBook. // GnuBook() //______________________________________________________________________________ // After you instantiate this object, you must supply the following -// book-specific functions, before calling init(): +// book-specific functions, before calling init(). Some of these functions +// can just be stubs for simple books. // - getPageWidth() // - getPageHeight() // - getPageURI() +// - getPageSide() +// - canRotatePage() +// - getPageNum() +// - getSpreadIndices() // You must also add a numLeafs property before calling init(). function GnuBook() { this.reduce = 4; this.padding = 10; - this.mode = 1; //1 or 2 + this.mode = 1; //1, 2, 3 this.ui = 'full'; // UI mode - + this.thumbScale = 10; // thumbnail default + this.thumbRowBuffer = 4; // number of rows to pre-cache out a view + this.displayedIndices = []; + this.displayedRows=[]; //this.indicesToDisplay = []; this.imgs = {}; this.prefetchedImgs = {}; //an object with numeric keys cooresponding to page index @@ -68,6 +76,7 @@ function GnuBook() { // Mode constants this.constMode1up = 1; this.constMode2up = 2; + this.constModeThumb = 3; // Zoom levels this.reductionFactors = [0.5, 1, 2, 4, 8, 16]; @@ -79,6 +88,10 @@ function GnuBook() { bookSpineDivWidth: 30, // Width of book spine $$$ consider sizing based on book length autofit: true }; + + // Background color for pages (e.g. when loading page image) + // $$$ TODO dynamically calculate based on page images + this.pageDefaultBackgroundColor = 'rgb(234, 226, 205)'; }; // init() @@ -86,6 +99,7 @@ function GnuBook() { GnuBook.prototype.init = function() { var startIndex = undefined; + this.pageScale = this.reduce; // preserve current reduce // Find start index and mode if set in location hash var params = this.paramsFromFragment(window.location.hash); @@ -140,6 +154,8 @@ GnuBook.prototype.init = function() { e.data.displayedIndices = []; e.data.updateSearchHilites(); //deletes hilights but does not call remove() e.data.loadLeafs(); + } else if (3 == e.data.mode){ + e.data.prepareThumbnailView(); } else { //console.log('drawing 2 page view'); @@ -174,6 +190,10 @@ GnuBook.prototype.init = function() { this.resizePageView(); this.firstIndex = startIndex; this.jumpToIndex(startIndex); + } else if (3 == this.mode) { + this.firstIndex = startIndex; + this.prepareThumbnailView(); + this.jumpToIndex(startIndex); } else { //this.resizePageView(); @@ -204,51 +224,46 @@ GnuBook.prototype.setupKeyListeners = function() { // We use document here instead of window to avoid a bug in jQuery on IE7 $(document).keydown(function(e) { + // Keyboard navigation - switch(e.keyCode) { - case KEY_PGUP: - case KEY_UP: - // In 1up mode page scrolling is handled by browser - if (2 == self.mode) { - e.preventDefault(); - self.prev(); - } - break; - case KEY_DOWN: - case KEY_PGDOWN: - if (2 == self.mode) { - e.preventDefault(); - self.next(); - } - break; - case KEY_END: - e.preventDefault(); - self.last(); - break; - case KEY_HOME: - e.preventDefault(); - self.first(); - break; - case KEY_LEFT: - if (self.keyboardNavigationIsDisabled(e)) { - e.preventDefault(); + if (!self.keyboardNavigationIsDisabled(e)) { + switch(e.keyCode) { + case KEY_PGUP: + case KEY_UP: + // In 1up mode page scrolling is handled by browser + if (2 == self.mode) { + e.preventDefault(); + self.prev(); + } break; - } - if (2 == self.mode) { - e.preventDefault(); - self.left(); - } - break; - case KEY_RIGHT: - if (self.keyboardNavigationIsDisabled(e)) { + case KEY_DOWN: + case KEY_PGDOWN: + if (2 == self.mode) { + e.preventDefault(); + self.next(); + } + break; + case KEY_END: e.preventDefault(); + self.last(); break; - } - if (2 == self.mode) { + case KEY_HOME: e.preventDefault(); - self.right(); - } - break; + self.first(); + break; + case KEY_LEFT: + if (2 == self.mode) { + e.preventDefault(); + self.left(); + } + break; + case KEY_RIGHT: + if (2 == self.mode) { + e.preventDefault(); + self.right(); + } + break; + } } }); } @@ -258,6 +273,8 @@ GnuBook.prototype.setupKeyListeners = function() { GnuBook.prototype.drawLeafs = function() { if (1 == this.mode) { this.drawLeafsOnePage(); + } else if(3 == this.mode) { + this.drawLeafsThumbnail(); } else { this.drawLeafsTwoPage(); } @@ -439,7 +456,7 @@ GnuBook.prototype.drawLeafsOnePage = function() { var leafTop = 0; var leafBottom = 0; for (i=0; i=scrollTop=' + (leafTop>=scrollTop)); @@ -477,7 +494,7 @@ GnuBook.prototype.drawLeafsOnePage = function() { leafTop = 0; var i; for (i=0; i= scrollTop) && (leafTop <= scrollBottom); + var bottomInView = (leafBottom >= scrollTop) && (leafBottom <= scrollBottom); + var middleInView = (leafTop <=scrollTop) && (leafBottom>=scrollBottom); + if (topInView | bottomInView | middleInView) { + //console.log('row to display: ' + j); + rowsToDisplay.push(i); + } + if(leafTop > leafMap[i].top) { leafMap[i].top = leafTop; } + leafTop = leafBottom; + } + + var firstRow = rowsToDisplay[0]; + var lastRow = rowsToDisplay[rowsToDisplay.length-1]; + for (i=1; i= 0) { rowsToDisplay.unshift(firstRow-i); } + if (lastRow+i < leafMap.length) { rowsToDisplay.push(lastRow+i); } +} + + // Update hash, but only if we're currently displaying a leaf + // Hack that fixes #365790 + if (this.displayedRows.length > 0) { + this.updateLocationHash(); + } + + var j; + var row; + var left; + var index; + var div; + var link; + var img; + var page; + for (i=0; i0) { + div = $('.GBpagedivthumb_highlight') + div.attr({className: 'GBpagedivthumb' }); + } + // highlight current page + $('#pagediv'+this.currentIndex()).attr({className: 'GBpagedivthumb_highlight' }); + + var k; + for (i=0; i= 8) return; this.reduce*=2; //zoom out } - + + this.pageScale = this.reduce; // preserve current reduce this.resizePageView(); $('#GBpageview').empty() @@ -678,6 +869,11 @@ GnuBook.prototype.zoom1up = function(dir) { this.loadLeafs(); this.updateToolbarZoom(this.reduce); + + // Recalculate search hilites + this.removeSearchHilites(); + this.updateSearchHilites(); + } // resizePageView() @@ -703,8 +899,8 @@ GnuBook.prototype.resizePageView = function() { } for (i=0; iviewWidth) viewWidth=width; } $('#GBpageview').height(viewHeight); @@ -724,6 +920,9 @@ GnuBook.prototype.resizePageView = function() { //this.centerPageView(); this.loadLeafs(); + // Not really needed until there is 1up autofit + this.removeSearchHilites(); + this.updateSearchHilites(); } // centerX1up() @@ -775,6 +974,7 @@ GnuBook.prototype.zoom2up = function(direction) { } this.twoPage.autofit = newZoom.autofit; this.reduce = newZoom.reduce; + this.pageScale = this.reduce; // preserve current reduce // Preserve view center position var oldCenter = this.twoPageGetViewCenter(); @@ -882,7 +1082,7 @@ GnuBook.prototype.jumpToPage = function(pageNum) { // jumpToIndex() //______________________________________________________________________________ -GnuBook.prototype.jumpToIndex = function(index) { +GnuBook.prototype.jumpToIndex = function(index, pageX, pageY) { if (2 == this.mode) { this.autoStop(); @@ -895,21 +1095,67 @@ GnuBook.prototype.jumpToIndex = function(index) { this.flipFwdToIndex(index); } - } else { + } else if (3 == this.mode){ + var viewWidth = $('#GBcontainer').attr('scrollWidth') - 20; // width minus buffer var i; + var leafWidth = 0; + var leafHeight = 0; + var rightPos = 0; + var bottomPos = 0; + var rowHeight = 0; var leafTop = 0; + var leafIndex = 0; + + for (i=0; i<(index+1); i++) { + leafWidth = parseInt(this.getPageWidth(i)/this.reduce, 10); + if (rightPos + (leafWidth + this.padding) > viewWidth){ + rightPos = 0; + rowHeight = 0; + leafIndex = 0; + } + leafHeight = parseInt(this.getPageHeight(i)/this.reduce, 10); + if(leafHeight > rowHeight) { rowHeight = leafHeight; } + if (leafIndex==0) { leafTop = bottomPos; } + if (leafIndex==0) { bottomPos += this.padding + rowHeight; } + rightPos += leafWidth + this.padding; + leafIndex++; + } + this.firstIndex=index; + if ($('#GBcontainer').attr('scrollTop') == leafTop) { + this.loadLeafs(); + } else { + $('#GBcontainer').animate({scrollTop: leafTop },'fast'); + } + } else { + var i; + var leafTop = 0; + var leafLeft = 0; var h; for (i=0; i> 1; + //console.log( 'jumping to ' + leafTop + ' ' + offset); + leafTop += offset; + } + + if (pageX) { + var offset = parseInt( (pageX) / this.reduce); + offset -= $('#GBcontainer').attr('clientWidth') >> 1; + leafLeft += offset; + } + //$('#GBcontainer').attr('scrollTop', leafTop); - $('#GBcontainer').animate({scrollTop: leafTop },'fast'); + $('#GBcontainer').animate({scrollTop: leafTop, scrollLeft: leafLeft },'fast'); } } - // switchMode() //______________________________________________________________________________ GnuBook.prototype.switchMode = function(mode) { @@ -926,15 +1172,21 @@ GnuBook.prototype.switchMode = function(mode) { this.removeSearchHilites(); this.mode = mode; - this.switchToolbarMode(mode); + + // reinstate scale if moving from thumbnail view + if (this.pageScale != this.reduce) this.reduce = this.pageScale; // $$$ TODO preserve center of view when switching between mode // See https://bugs.edge.launchpad.net/gnubook/+bug/416682 - + if (1 == mode) { this.reduce = this.quantizeReduce(this.reduce); this.prepareOnePageView(); + } else if (3 == mode) { + this.reduce = this.quantizeReduce(this.reduce); + this.prepareThumbnailView(); + this.jumpToIndex(this.currentIndex()); } else { this.twoPage.autofit = false; // Take zoom level from other mode this.reduce = this.quantizeReduce(this.reduce); @@ -977,6 +1229,38 @@ GnuBook.prototype.prepareOnePageView = function() { gbPageView[0].onselectstart = function(e) { return false; }; } +//prepareThumbnailView() +//______________________________________________________________________________ +GnuBook.prototype.prepareThumbnailView = function() { + + // var startLeaf = this.displayedIndices[0]; + var startLeaf = this.currentIndex(); + this.reduce = this.thumbScale; + + $('#GBcontainer').empty(); + $('#GBcontainer').css({ + overflowY: 'scroll', + overflowX: 'auto' + }); + + var gbPageView = $("#GBcontainer").append("
"); + + this.resizePageView(); + + this.displayedRows = []; + this.drawLeafsThumbnail(); + + // Bind mouse handlers + // Disable mouse click to avoid selected/highlighted page images - bug 354239 + gbPageView.bind('mousedown', function(e) { + // $$$ check here for right-click and don't disable. Also use jQuery style + // for stopping propagation. See https://bugs.edge.launchpad.net/gnubook/+bug/362626 + return false; + }) + // Special hack for IE7 + gbPageView[0].onselectstart = function(e) { return false; }; +} + // prepareTwoPageView() //______________________________________________________________________________ // Some decisions about two page view: @@ -1148,10 +1432,13 @@ GnuBook.prototype.prepareTwoPageView = function(centerPercentageX, centerPercent //console.log('indicesToDisplay: ' + this.indicesToDisplay[0] + ' ' + this.indicesToDisplay[1]); this.drawLeafsTwoPage(); - this.updateSearchHilites2UP(); this.updateToolbarZoom(this.reduce); this.prefetch(); + + this.removeSearchHilites(); + this.updateSearchHilites(); + } // prepareTwoPagePopUp() @@ -1306,13 +1593,13 @@ GnuBook.prototype.getIdealSpreadSize = function(firstIndex, secondIndex) { var canon5Dratio = 1.5; var first = { - height: this.getPageHeight(firstIndex), - width: this.getPageWidth(firstIndex) + height: this._getPageHeight(firstIndex), + width: this._getPageWidth(firstIndex) } var second = { - height: this.getPageHeight(secondIndex), - width: this.getPageWidth(secondIndex) + height: this._getPageHeight(secondIndex), + width: this._getPageWidth(secondIndex) } var firstIndexRatio = first.height / first.width; @@ -1366,8 +1653,8 @@ GnuBook.prototype.getSpreadSizeFromReduce = function(firstIndex, secondIndex, re spreadSize.totalLeafEdgeWidth = Math.min(totalLeafEdgeWidth, maxLeafEdgeWidth); // $$$ Possibly incorrect -- we should make height "dominant" - var nativeWidth = this.getPageWidth(firstIndex) + this.getPageWidth(secondIndex); - var nativeHeight = this.getPageHeight(firstIndex) + this.getPageHeight(secondIndex); + var nativeWidth = this._getPageWidth(firstIndex) + this._getPageWidth(secondIndex); + var nativeHeight = this._getPageHeight(firstIndex) + this._getPageHeight(secondIndex); spreadSize.height = parseInt( (nativeHeight / 2) / this.reduce ); spreadSize.width = parseInt( (nativeWidth / 2) / this.reduce ); spreadSize.reduce = reduce; @@ -1403,8 +1690,11 @@ GnuBook.prototype.twoPageSetCursor = function() { // Returns the currently active index. GnuBook.prototype.currentIndex = function() { // $$$ we should be cleaner with our idea of which index is active in 1up/2up - if (this.mode == this.constMode1up || this.mode == this.constMode2up) { - return this.firstIndex; + if (this.mode == this.constMode1up || this.mode == this.constModeThumb) { + return this.firstIndex; // $$$ TODO page in center of view would be better + } else if (this.mode == this.constMode2up) { + // Only allow indices that are actually present in book + return GnuBook.util.clamp(this.firstIndex, 0, this.numLeafs - 1); } else { throw 'currentIndex called for unimplemented mode ' + this.mode; } @@ -1485,34 +1775,22 @@ GnuBook.prototype.prev = function() { } GnuBook.prototype.first = function() { - if (2 == this.mode) { - this.jumpToIndex(2); - } - else { - this.jumpToIndex(0); - } + this.jumpToIndex(this.firstDisplayableIndex()); } GnuBook.prototype.last = function() { - if (2 == this.mode) { - this.jumpToIndex(this.lastDisplayableIndex()); - } - else { - this.jumpToIndex(this.lastDisplayableIndex()); - } + this.jumpToIndex(this.lastDisplayableIndex()); } // flipBackToIndex() //______________________________________________________________________________ // to flip back one spread, pass index=null GnuBook.prototype.flipBackToIndex = function(index) { + if (1 == this.mode) return; var leftIndex = this.twoPage.currentIndexL; - // $$$ Need to change this to be able to see first spread. - // See https://bugs.launchpad.net/gnubook/+bug/296788 - if (leftIndex <= 2) return; if (this.animating) return; if (null != this.leafEdgeTmp) { @@ -1527,12 +1805,10 @@ GnuBook.prototype.flipBackToIndex = function(index) { var previousIndices = this.getSpreadIndices(index); - if (previousIndices[0] < 0 || previousIndices[1] < 0) { + if (previousIndices[0] < this.firstDisplayableIndex() || previousIndices[1] < this.firstDisplayableIndex()) { return; } - //console.log("flipping back to " + previousIndices[0] + ',' + previousIndices[1]); - this.animating = true; if ('rl' != this.pageProgression) { @@ -1863,7 +2139,7 @@ GnuBook.prototype.setMouseHandlers2UP = function() { // prefetchImg() //______________________________________________________________________________ GnuBook.prototype.prefetchImg = function(index) { - var pageURI = this.getPageURI(index); + var pageURI = this._getPageURI(index); // Load image if not loaded or URI has changed (e.g. due to scaling) var loadImage = false; @@ -1898,8 +2174,8 @@ GnuBook.prototype.prepareFlipLeftToRight = function(prevL, prevR) { this.prefetchImg(prevL); this.prefetchImg(prevR); - var height = this.getPageHeight(prevL); - var width = this.getPageWidth(prevL); + var height = this._getPageHeight(prevL); + var width = this._getPageWidth(prevL); var middle = this.twoPage.middle; var top = this.twoPageTop(); var scaledW = this.twoPage.height*width/height; // $$$ assumes height of page is dominant @@ -1912,33 +2188,37 @@ GnuBook.prototype.prepareFlipLeftToRight = function(prevL, prevR) { //console.log(' prevL.left: ' + (gutter - scaledW) + 'px'); //console.log(' changing prevL ' + prevL + ' to left: ' + (gutter-scaledW) + ' width: ' + scaledW); - $(this.prefetchedImgs[prevL]).css({ + leftCSS = { position: 'absolute', left: gutter-scaledW+'px', right: '', // clear right property top: top+'px', - backgroundColor: 'rgb(234, 226, 205)', height: this.twoPage.height, width: scaledW+'px', + backgroundColor: this.getPageBackgroundColor(prevL), borderRight: '1px solid black', zIndex: 1 - }); + } + + $(this.prefetchedImgs[prevL]).css(leftCSS); $('#GBtwopageview').append(this.prefetchedImgs[prevL]); //console.log(' changing prevR ' + prevR + ' to left: ' + gutter + ' width: 0'); - $(this.prefetchedImgs[prevR]).css({ + rightCSS = { position: 'absolute', left: gutter+'px', right: '', top: top+'px', - backgroundColor: 'rgb(234, 226, 205)', height: this.twoPage.height, width: '0px', + backgroundColor: this.getPageBackgroundColor(prevR), borderLeft: '1px solid black', zIndex: 2 - }); + } + + $(this.prefetchedImgs[prevR]).css(rightCSS); $('#GBtwopageview').append(this.prefetchedImgs[prevR]); @@ -1955,8 +2235,8 @@ GnuBook.prototype.prepareFlipRightToLeft = function(nextL, nextR) { this.prefetchImg(nextL); this.prefetchImg(nextR); - var height = this.getPageHeight(nextR); - var width = this.getPageWidth(nextR); + var height = this._getPageHeight(nextR); + var width = this._getPageWidth(nextR); var middle = this.twoPage.middle; var top = this.twoPageTop(); var scaledW = this.twoPage.height*width/height; @@ -1968,7 +2248,7 @@ GnuBook.prototype.prepareFlipRightToLeft = function(nextL, nextR) { position: 'absolute', left: gutter+'px', top: top+'px', - backgroundColor: 'rgb(234, 226, 205)', + backgroundColor: this.getPageBackgroundColor(nextR), height: this.twoPage.height, width: scaledW+'px', borderLeft: '1px solid black', @@ -1977,8 +2257,8 @@ GnuBook.prototype.prepareFlipRightToLeft = function(nextL, nextR) { $('#GBtwopageview').append(this.prefetchedImgs[nextR]); - height = this.getPageHeight(nextL); - width = this.getPageWidth(nextL); + height = this._getPageHeight(nextL); + width = this._getPageWidth(nextL); scaledW = this.twoPage.height*width/height; //console.log(' prepareRTL changing nextL ' + nextL + ' to right: ' + $('#GBcontainer').width()-gutter); @@ -1986,7 +2266,7 @@ GnuBook.prototype.prepareFlipRightToLeft = function(nextL, nextR) { position: 'absolute', right: $('#GBtwopageview').attr('clientWidth')-gutter+'px', top: top+'px', - backgroundColor: 'rgb(234, 226, 205)', + backgroundColor: this.getPageBackgroundColor(nextL), height: this.twoPage.height, width: 0+'px', // Start at 0 width, then grow to the left borderRight: '1px solid black', @@ -2085,8 +2365,8 @@ GnuBook.prototype.prefetch = function() { //______________________________________________________________________________ GnuBook.prototype.getPageWidth2UP = function(index) { // We return the width based on the dominant height - var height = this.getPageHeight(index); - var width = this.getPageWidth(index); + var height = this._getPageHeight(index); + var width = this._getPageWidth(index); return Math.floor(this.twoPage.height*width/height); // $$$ we assume width is relative to current spread } @@ -2159,18 +2439,23 @@ GnuBook.prototype.GBSearchCallback = function(txt) { //we'll skip baseline for now... var coords = children[j].getAttribute('coords').split(',',4); if (4 == coords.length) { - this.searchResults[index] = {'l':coords[0], 'b':coords[1], 'r':coords[2], 't':coords[3], 'div':null}; + 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 - $('#GnuBookSearchResults').append('
  • ' + pageName + ' - ' + context + '
  • '); + $('#GnuBookSearchResults').append('
  • ' + pageName + ' - ' + context + '
  • '); } } $('#GnuBookSearchResults').append(''); + // $$$ update again for case of loading search URL in new browser window (search box may not have been ready yet) + $('#GnuBookSearchBox').val(this.searchTerm); + this.updateSearchHilites(); } @@ -2354,8 +2639,8 @@ GnuBook.prototype.updateSearchHilites2UP = function() { // 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 height = this._getPageHeight(key); + var width = this._getPageWidth(key) var reduce = this.twoPage.height/height; var scaledW = parseInt(width*reduce); @@ -2470,11 +2755,11 @@ GnuBook.prototype.getPrintURI = function() { var options = 'id=' + this.bookId + '&server=' + this.server + '&zip=' + this.zip + '&format=' + this.imageFormat + '&file=' + this._getPageFile(indexToPrint) - + '&width=' + this.getPageWidth(indexToPrint) + '&height=' + this.getPageHeight(indexToPrint); + + '&width=' + this._getPageWidth(indexToPrint) + '&height=' + this._getPageHeight(indexToPrint); if (this.constMode2up == this.mode) { - options += '&file2=' + this._getPageFile(this.twoPage.currentIndexR) + '&width2=' + this.getPageWidth(this.twoPage.currentIndexR); - options += '&height2=' + this.getPageHeight(this.twoPage.currentIndexR); + options += '&file2=' + this._getPageFile(this.twoPage.currentIndexR) + '&width2=' + this._getPageWidth(this.twoPage.currentIndexR); + options += '&height2=' + this._getPageHeight(this.twoPage.currentIndexR); options += '&title=' + encodeURIComponent(this.shortTitle(50) + ' - Pages ' + this.getPageNum(this.twoPage.currentIndexL) + ', ' + this.getPageNum(this.twoPage.currentIndexR)); } else { options += '&title=' + encodeURIComponent(this.shortTitle(50) + ' - Page ' + this.getPageNum(indexToPrint)); @@ -2488,7 +2773,7 @@ GnuBook.prototype.getPrintFrameContent = function(index) { // We fit the image based on an assumed A4 aspect ratio. A4 is a bit taller aspect than // 8.5x11 so we should end up not overflowing on either paper size. var paperAspect = 8.5 / 11; - var imageAspect = this.getPageWidth(index) / this.getPageHeight(index); + var imageAspect = this._getPageWidth(index) / this._getPageHeight(index); var rotate = 0; @@ -2510,7 +2795,7 @@ GnuBook.prototype.getPrintFrameContent = function(index) { fitAttrs = 'height="95%"'; } - var imageURL = this.getPageURI(index, 1, rotate); + var imageURL = this._getPageURI(index, 1, rotate); var iframeStr = '' + this.bookTitle + ''; iframeStr += '
    '; iframeStr += ''; @@ -2735,11 +3020,12 @@ GnuBook.prototype.initToolbar = function(mode, ui) { + "" + "" - + "
    " + "