// 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 = [];
this.twoPagePopUp = null;
this.leafEdgeTmp = null;
this.embedPopup = null;
- this.popup = null;
+ this.printPopup = null;
this.searchTerm = '';
this.searchResults = {};
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
// 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
// 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);
+ }
}
}
// 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("<div id='BRcontainer'></div>");
$("#BRcontainer").append("<div id='BRpageview'></div>");
+
+ // 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();
// 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() {
br.zoom(-1);
}
});
-
}
BookReader.prototype.setClickHandler2UP = function( element, data, handler) {
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
// Calculate the position of every thumbnail. $$$ cache instead of calculating on every draw
for (i=0; i<this.numLeafs; i++) {
leafWidth = this.thumbWidth;
- if (rightPos + (leafWidth + this.padding) > viewWidth){
+ if (rightPos + (leafWidth + this.thumbPadding) > viewWidth){
currentRow++;
rightPos = 0;
leafIndex = 0;
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;
}
}
// Determine the thumbnails in view
for (i=0; i<leafMap.length; i++) {
- leafBottom += this.padding + leafMap[i].height;
+ leafBottom += this.thumbPadding + leafMap[i].height;
var topInView = (leafTop >= scrollTop) && (leafTop <= scrollBottom);
var bottomInView = (leafBottom >= scrollTop) && (leafBottom <= scrollBottom);
var middleInView = (leafTop <=scrollTop) && (leafBottom>=scrollBottom);
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');
// 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);
}
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');
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');
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() );
}
}
+// Resize the current one page view
BookReader.prototype.resizePageView1up = function() {
var i;
var viewHeight = 0;
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
this.reduce = reductionFactor.reduce;
}
- for (i=0; i<this.numLeafs; i++) {
- viewHeight += parseInt(this._getPageHeight(i)/this.reduce) + this.padding;
- var width = parseInt(this._getPageWidth(i)/this.reduce);
- if (width>viewWidth) 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);
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; i<this.numLeafs; i++) {
+ viewHeight += parseInt(this._getPageHeight(i)/this.reduce) + this.padding;
+ var width = parseInt(this._getPageWidth(i)/this.reduce);
+ if (width>viewWidth) viewWidth=width;
+ }
+ return { width: viewWidth, height: viewHeight }
+}
// centerX1up()
//______________________________________________________________________________
// 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);
}
//______________________________________________________________________________
BookReader.prototype.jumpToIndex = function(index, pageX, pageY) {
+ this.willChangeToIndex(index);
+
if (this.constMode2up == this.mode) {
this.autoStop();
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;
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;
}
} else {
// 1up
- var i;
- var leafTop = 0;
- var leafLeft = 0;
- var h;
- for (i=0; i<index; i++) {
- h = parseInt(this._getPageHeight(i)/this.reduce);
- leafTop += h + this.padding;
- }
+ var leafTop = this.onePageGetPageTop(index);
if (pageY) {
//console.log('pageY ' + pageY);
}
}
-
// switchMode()
//______________________________________________________________________________
BookReader.prototype.switchMode = function(mode) {
- //console.log(' asked to switch to mode ' + mode + ' from ' + this.mode);
-
if (mode == this.mode) {
return;
}
}
this.autoStop();
+ this.ttsStop();
this.removeSearchHilites();
this.mode = mode;
- this.switchToolbarMode(mode);
+ //this.switchToolbarMode(mode);
// reinstate scale if moving from thumbnail view
if (this.pageScale != this.reduce) {
this.reduce = this.quantizeReduce(this.reduce, this.onePage.reductionFactors);
this.prepareOnePageView();
} else if (3 == mode) {
+ $('button.thumb').hide();
+ $('button.twopg').show();
this.reduce = this.quantizeReduce(this.reduce, this.reductionFactors);
this.prepareThumbnailView();
} else {
// $$$ why don't we save autofit?
- this.twoPage.autofit = null; // Take zoom level from other mode
+ // this.twoPage.autofit = null; // Take zoom level from other mode
this.twoPageCalculateReductionFactors();
this.reduce = this.quantizeReduce(this.reduce, this.twoPage.reductionFactors);
+ $('button.thumb').show();
+ $('button.twopg').hide();
this.prepareTwoPageView();
this.twoPageCenterView(0.5, 0.5); // $$$ TODO preserve center
}
});
$("#BRcontainer").append("<div id='BRpageview'></div>");
-
+
// Attaches to first child - child must be present
$('#BRcontainer').dragscrollable();
this.bindGestures($('#BRcontainer'));
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');
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');
top: this.twoPageFlipAreaTop() + 'px',
cursor: 'w-resize',
zIndex: 100
- }).bind('click', function(e) {
+ }).click(function(e) {
self.left();
}).bind('mousedown', function(e) {
e.preventDefault();
top: this.twoPageFlipAreaTop() + 'px',
cursor: 'e-resize',
zIndex: 100
- }).bind('click', function(e) {
+ }).click(function(e) {
self.right();
}).bind('mousedown', function(e) {
e.preventDefault();
$(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);
});
// $$$ 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'
});
});
// $$$ 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'
});
});
// 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;
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<index; i++) {
+ h = parseInt(this._getPageHeight(i)/this.reduce);
+ leafTop += h + this.padding;
+ }
+ return leafTop;
+}
+
BookReader.prototype.getMedianPageSize = function() {
if (this._medianPageSize) {
return this._medianPageSize;
// next()
//______________________________________________________________________________
BookReader.prototype.next = function() {
- //this.ttsStop();
-
if (2 == this.mode) {
this.autoStop();
this.flipFwdToIndex(null);
// prev()
//______________________________________________________________________________
BookReader.prototype.prev = function() {
- //this.ttsStop();
-
if (2 == this.mode) {
this.autoStop();
this.flipBackToIndex(null);
}
//if (index<0) return;
+ this.willChangeToIndex(index);
+
var previousIndices = this.getSpreadIndices(index);
if (previousIndices[0] < this.firstDisplayableIndex() || previousIndices[1] < this.firstDisplayableIndex()) {
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
BookReader.prototype.flipFwdToIndex = function(index) {
if (this.animating) return;
-
+
if (null != this.leafEdgeTmp) {
alert('error: leafEdgeTmp should be null!');
return;
}
if (index > this.lastDisplayableIndex()) return;
+ this.willChangeToIndex(index);
+
this.animating = true;
var nextIndices = this.getSpreadIndices(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
//______________________________________________________________________________
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
this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexL],
{ self: this },
function(e) {
+ e.data.self.ttsStop();
e.data.self.left();
e.preventDefault();
}
this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR],
{ self: this },
function(e) {
+ e.data.self.ttsStop();
e.data.self.right();
e.preventDefault();
}
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;
top: top+'px',
height: this.twoPage.height,
width: scaledW+'px',
- borderRight: '1px solid black',
+ borderRight: '1px solid black', // XXXmang check
zIndex: 1
}
right: '',
top: top+'px',
height: this.twoPage.height,
- width: '0px',
- borderLeft: '1px solid black',
+ borderLeft: '1px solid black', // XXXmang check
+ width: '0',
zIndex: 2
}
top: top+'px',
height: this.twoPage.height,
width: scaledW+'px',
- borderLeft: '1px solid black',
zIndex: 1
});
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
});
return;
}
this.autoStop();
+ this.ttsStop();
+
this.embedPopup = document.createElement("div");
$(this.embedPopup).css({
position: 'absolute',
- top: '20px',
+ top: ($('#BRcontainer').attr('clientHeight')-250)/2 + 'px',
left: ($('#BRcontainer').attr('clientWidth')-400)/2 + 'px',
width: '400px',
- padding: "20px",
- border: "3px double #999999",
- zIndex: 3,
- backgroundColor: "#fff"
+ 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 = '<p style="text-align:center;"><b>Embed Bookreader in your blog!</b></p>';
- htmlStr += '<p>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.</p>';
- htmlStr += '<p>Embed Code: <input type="text" size="40" value="' + this.getEmbedCode() + '"></p>';
- htmlStr += '<p style="text-align:center;"><a href="" onclick="br.embedPopup = null; $(this.parentNode.parentNode).remove(); return false">Close popup</a></p>';
+ htmlStr = '<h3 style="background:#615132;padding:10px;margin:0 0 10px;color:#fff;">Embed Bookreader</h3>';
+ htmlStr += '<p style="padding:10px;line-height:18px;">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.</p>';
+ htmlStr += '<textarea rows="2" cols="40" style="margin-left:10px;width:368px;height:40px;color:#333;font-size:12px;border:2px inset #ccc;background:#efefef;padding:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;">' + this.getEmbedCode() + '</textarea>';
+ htmlStr += '<a href="javascript:;" class="popOff" onclick="$(this.parentNode).remove();$(\'.coverUp\').hide();return false" style="color:#999;"><span>Close</span></a>';
this.embedPopup.innerHTML = htmlStr;
- $(this.embedPopup).find('input').bind('click', function() {
+ $('#BookReader').append('<div class="coverUp" style="position:absolute;z-index:299;width:100%;height:100%;background:#000;opacity:.4;filter:alpha(opacity=40);" onclick="$(\'.popped\').hide();$(this).hide();"></div>');
+ $(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 = '<h3 style="background:#615132;padding:10px;margin:0 0 10px;color:#fff;">Add a bookmark</h3>';
+ htmlStr += '<p style="padding:10px;line-height:18px;">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. <em>You must be logged in to your <a href="">Open Library account</a> to add bookmarks.</em></p>';
+ htmlStr += '<form name="bookmark" id="bookmark" style="line-height:20px;margin-left:10px;"><label style="padding-bottom"10px;><input type="radio" name="privacy" id="p2" disabled="disabled" checked="checked"/> Make this bookmark public.</label><br/><label style="padding-bottom:10px;"><input type="radio" name="privacy" id="p1" disabled="disabled"/> Keep this bookmark private.</label><br/><br/><button type="submit" style="font-size:20px;" disabled="disabled">Add a bookmark</button></form>';
+ htmlStr += '<a href="javascript:;" class="popOff" onclick="$(this.parentNode).remove();$(\'.coverUp\').hide();return false;" style="color:#999;"><span>Close</span></a>';
+
+ this.bookmarkPopup.innerHTML = htmlStr;
+ $('#BookReader').append('<div class="coverUp" style="position:absolute;z-index:299;width:100%;height:100%;background:#000;opacity:.4;filter:alpha(opacity=40);" onclick="$(\'.popped\').hide();$(this).hide();"></div>');
+ $(this.bookmarkPopup).find('textarea').click(function() {
+ this.select();
+ })
+ $(this.bookmarkPopup).addClass("popped");
+}
+
+
// autoToggle()
//______________________________________________________________________________
BookReader.prototype.autoToggle = function() {
}
}
+// 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(
+ '<div id="BRnav">'
+ + '<div id="BRpage">' // Page turn buttons
+ + '<button class="BRicon onepg"></button>'
+ + '<button class="BRicon twopg"></button>'
+ + '<button class="BRicon thumb"></button>'
+ + '<button class="BRicon fit"></button>'
+ + '<button class="BRicon zoom_in"></button>'
+ + '<button class="BRicon zoom_out"></button>'
+ + '<button class="BRicon book_left"></button>'
+ + '<button class="BRicon book_right"></button>'
+ + '</div>'
+ + '<div id="BRnavpos">' // Page slider and nav line
+ //+ '<div id="BRfiller"></div>'
+ + '<div id="BRpager"></div>' // Page slider
+ + '<div id="BRnavline">' // Nav line with e.g. chapter markers
+ + '<div class="BRnavend" id="BRnavleft"></div>'
+ + '<div class="BRnavend" id="BRnavright"></div>'
+ + '</div>'
+ + '</div>'
+ + '<div id="BRnavCntlBtm" class="BRnavCntl BRdn"></div>'
+ + '</div>'
+ );
+
+/*
+ <div class="searchChap" style="left:49%;" title="Search result">
+ <div class="query">
+ A related distinction is between the emotion and the results of the emotion, principally behaviors and <strong><a href="">emotional</a></strong> expressions. People often behave in certain ways as a direct result of their <strong><a href="">emotional</a></strong> state, such as crying, fighting or fleeing. <span>Page 163</span>
+ <div class="queryChap">IV. The Witch <span>|</span> Page 163</div>
+ </div>
+ </div>
+*/
+
+ /* $$$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('<div id="pagenum"><span class="currentpage"></span></div>');
+ //.wrap('<div class="ui-handle-helper-parent"></div>').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 <strong>sidewalk</strong> 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 <strong>sidewalk</strong> 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, pageNumber, 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;
+ }
+
+ $('<div class="search" style="left:' + percentThrough + ';" title="' + uiStringSearch + '"><div class="query">'
+ + queryString + '<span>' + uiStringPage + ' ' + pageNumber + '</span></div>')
+ .data({'self': this, 'pageIndex': pageIndex })
+ .appendTo('#BRnavline').bt({
+ contentSelector: '$(this).find(".query")',
+ trigger: 'hover',
+ closeWhenOthersOpen: true,
+ cssStyles: {
+ padding: '12px 14px',
+ backgroundColor: '#fff',
+ border: '4px solid #e2dcc5',
+ fontFamily: '"Lucida Grande","Arial",sans-serif',
+ fontSize: '13px',
+ //lineHeight: '18px',
+ color: '#615132'
+ },
+ shrinkToFit: false,
+ width: '230px',
+ padding: 0,
+ spikeGirth: 0,
+ spikeLength: 0,
+ overlap: '22px',
+ overlay: false,
+ killTitle: false,
+ textzIndex: 9999,
+ boxzIndex: 9998,
+ wrapperzIndex: 9997,
+ offsetParent: null,
+ positions: ['top'],
+ fill: 'white',
+ windowMargin: 10,
+ strokeWidth: 0,
+ cornerRadius: 0,
+ centerPointX: 0,
+ centerPointY: 0,
+ shadow: false
+ })
+ .hover( function() {
+ $(this).addClass('front');
+ }, function() {
+ $(this).removeClass('front');
+ }
+ )
+ .bind('click', function() {
+ $(this).data('self').jumpToIndex($(this).data('pageIndex'));
+ });
+
+}
+
+BookReader.prototype.removeSearchResults = function() {
+ $('#BRnavpos .search').remove();
+}
+
+BookReader.prototype.addChapter = function(chapterTitle, pageNumber, pageIndex) {
+ var uiStringPage = 'Page'; // i18n
+
+ var percentThrough = BookReader.util.cssPercentage(pageIndex, this.numLeafs - 1);
+
+ $('<div class="chapter" style="left:' + percentThrough + ';"><div class="title">'
+ + chapterTitle + '<span>|</span> ' + uiStringPage + ' ' + pageNumber + '</div></div>')
+ .appendTo('#BRnavline')
+ .data({'self': this, 'pageIndex': pageIndex })
+ .bt({
+ contentSelector: '$(this).find(".title")',
+ trigger: 'hover',
+ closeWhenOthersOpen: true,
+ cssStyles: {
+ padding: '12px 14px',
+ backgroundColor: '#000',
+ border: '4px solid #e2dcc5',
+ //borderBottom: 'none',
+ fontFamily: '"Arial", sans-serif',
+ fontSize: '12px',
+ fontWeight: '700',
+ color: '#fff',
+ whiteSpace: 'nowrap'
+ },
+ shrinkToFit: true,
+ width: '200px',
+ padding: 0,
+ spikeGirth: 0,
+ spikeLength: 0,
+ overlap: '21px',
+ overlay: false,
+ killTitle: true,
+ textzIndex: 9999,
+ boxzIndex: 9998,
+ wrapperzIndex: 9997,
+ offsetParent: null,
+ positions: ['top'],
+ fill: 'black',
+ windowMargin: 10,
+ strokeWidth: 0,
+ cornerRadius: 0,
+ centerPointX: 0,
+ centerPointY: 0,
+ shadow: false
+ })
+ .hover( function() {
+ $(this).addClass('front');
+ }, function() {
+ $(this).removeClass('front');
+ }
+ )
+ .bind('click', function() {
+ $(this).data('self').jumpToIndex($(this).data('pageIndex'));
+ });
+}
+
+/*
+ * Remove all chapters.
+ */
+BookReader.prototype.removeChapters = function() {
+ $('#BRnavpos .chapter').remove();
+}
+
+/*
+ * Update the table of contents based on array of TOC entries.
+ */
+BookReader.prototype.updateTOC = function(tocEntries) {
+ this.removeChapters();
+ for (var i = 0; i < tocEntries.length; i++) {
+ this.addChapterFromEntry(tocEntries[i]);
+ }
+}
+
+/*
+ * Example table of contents entry - this format is defined by Open Library
+ * {
+ * "pagenum": "17",
+ * "level": 1,
+ * "label": "CHAPTER I",
+ * "type": {"key": "/type/toc_item"},
+ * "title": "THE COUNTRY AND THE MISSION"
+ * }
+ */
+BookReader.prototype.addChapterFromEntry = function(tocEntryObject) {
+ var pageIndex = this.getPageIndex(tocEntryObject['pagenum']);
+ // Only add if we know where it is
+ if (pageIndex) {
+ this.addChapter(tocEntryObject['title'], tocEntryObject['pagenum'], pageIndex);
+ }
+ $('.chapter').each(function(){
+ $(this).hover(function(){
+ $(this).addClass('front');
+ },function(){
+ $(this).removeClass('front');
+ });
+ });
+ $('.search').each(function(){
+ $(this).hover(function(){
+ $(this).addClass('front');
+ },function(){
+ $(this).removeClass('front');
+ });
+ });
+ $('.searchChap').each(function(){
+ $(this).hover(function(){
+ $(this).addClass('front');
+ },function(){
+ $(this).removeClass('front');
+ });
+ });
+}
+
BookReader.prototype.initToolbar = function(mode, ui) {
- $("#BookReader").append("<div id='BRtoolbar'>"
- + "<span id='BRtoolbarbuttons' style='float: right'>"
- + "<button class='BRicon rollover read_aloud' onclick='br.ttsToggle(); return false;'/>"
- + "<button class='BRicon print rollover' /> <button class='BRicon rollover embed' />"
- + "<form class='BRpageform' action='javascript:' onsubmit='br.jumpToPage(this.elements[0].value)'> <span class='label'>Page:<input id='BRpagenum' type='text' size='3' onfocus='br.autoStop();'></input></span></form>"
- + "<div class='BRtoolbarmode2' style='display: none'><button class='BRicon rollover book_leftmost' /><button class='BRicon rollover book_left' /><button class='BRicon rollover book_right' /><button class='BRicon rollover book_rightmost' /></div>"
- + "<div class='BRtoolbarmode1' style='display: none'><button class='BRicon rollover book_top' /><button class='BRicon rollover book_up' /> <button class='BRicon rollover book_down' /><button class='BRicon rollover book_bottom' /></div>"
- + "<div class='BRtoolbarmode3' style='display: none'><button class='BRicon rollover book_top' /><button class='BRicon rollover book_up' /> <button class='BRicon rollover book_down' /><button class='BRicon rollover book_bottom' /></div>"
- + "<button class='BRicon rollover play' /><button class='BRicon rollover pause' style='display: none' />"
- + "</span>"
-
- + "<span>"
- + "<a class='BRicon logo rollover' href='" + this.logoURL + "'> </a>"
- + " <button class='BRicon rollover zoom_out' onclick='br.zoom(-1); return false;'/>"
- + "<button class='BRicon rollover zoom_in' onclick='br.zoom(1); return false;'/>"
- + " <span class='label'>Zoom: <span id='BRzoom'>"+parseInt(100/this.reduce)+"</span></span>"
- + " <button class='BRicon rollover one_page_mode' onclick='br.switchMode(1); return false;'/>"
- + " <button class='BRicon rollover two_page_mode' onclick='br.switchMode(2); return false;'/>"
- + " <button class='BRicon rollover thumbnail_mode' onclick='br.switchMode(3); return false;'/>"
- + "</span>"
-
- + "<span id='#BRbooktitle'>"
- + " <a class='BRblack title' href='"+this.bookUrl+"' target='_blank'>"+this.bookTitle+"</a>"
- + "</span>"
- + "</div>");
+ // $$$mang should be contained within the BookReader div instead of body
+ $("body").append(
+ "<div id='BRtoolbar'>"
+ + "<span id='BRtoolbarbuttons'>"
+ /* XXXmang integrate search */
+ + "<form method='get' id='booksearch'><input type='search' id='textSrch' name='textSrch' val='' placeholder='Search inside'/><button type='submit' id='btnSrch' name='btnSrch'>GO</button></form>"
+ // XXXmang icons incorrect or handlers wrong
+ + "<button class='BRicon info'></button>"
+ + "<button class='BRicon share'></button>"
+ + "<button class='BRicon read modal'></button>"
+ + "<button class='BRicon full'></button>"
+ + "</span>"
+ + "<span><a class='logo' href='" + this.logoURL + "'></a></span>"
+ + "<span id='BRreturn'><span>Back to</span><a href='" + this.bookUrl + "'>" + this.bookTitle + "</a></span>"
+ + "</div>"
+ /*
+ + "<div id='BRzoomer'>"
+ + "<div id='BRzoompos'>"
+ + "<button class='BRicon zoom_out'></button>"
+ + "<div id='BRzoomcontrol'>"
+ + "<div id='BRzoomstrip'></div>"
+ + "<div id='BRzoombtn'></div>"
+ + "</div>"
+ + "<button class='BRicon zoom_in'></button>"
+ + "</div>"
+ + "</div>"
+ */
+ );
this.updateToolbarZoom(this.reduce); // Pretty format
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',
// 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);
}
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;
});
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
+ $('#BRpage .zoom_in').bind('click', function() {
+ self.zoom(1);
+ return false;
+ });
+
+ $('#BRpage .zoom_out').bind('click', function() {
+ self.zoom(-1);
+ return false;
+ });
}
// updateToolbarZoom(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.
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();
//________
// 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;
}
}
// Returns the page URI or transparent image if out of range
BookReader.prototype._getPageURI = function(index, reduce, rotate) {
if (index < 0 || index >= this.numLeafs) { // Synthesize page
- return this.imagesBaseURL + "/transparent.png";
+ return this.imagesBaseURL + "transparent.png";
}
if ('undefined' == typeof(reduce)) {
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) {
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);
//______________________________________________________________________________
BookReader.prototype.ttsStart = function () {
if (soundManager.debugMode) console.log('starting readAloud');
+ if (this.constModeThumb == this.mode) this.switchMode(this.constMode1up);
+
this.ttsPlaying = true;
this.ttsIndex = this.currentIndex();
this.ttsFormat = 'mp3';
soundManager.stopAll();
soundManager.destroySound('chunk'+this.ttsIndex+'-'+this.ttsPosition);
this.ttsRemoveHilites();
+ this.ttsRemovePopup();
this.ttsPlaying = false;
this.ttsIndex = null; //leaf index
var snd = soundManager.createSound({
id: 'chunk'+this.ttsIndex+'-0',
//url: 'http://home.us.archive.org/~rkumar/arctic.ogg',
- url: 'http://'+this.server+'/getTTS.php?string=' + escape(data[0][0]) + '&format=.'+this.ttsFormat, //the .ogg is to trick SoundManager2 to use the HTML5 audio player
+ 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...
});
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 = '<p style="text-align:center;"><b>Ever wanted to wait while audio loads?</b><br/>';
- htmlStr += "It's OK. We all do.</p>";
+ htmlStr = ' ';
this.popup.innerHTML = htmlStr;
}
BookReader.prototype.ttsLoadChunk = function (page, pos, string) {
var snd = soundManager.createSound({
id: 'chunk'+page+'-'+pos,
- url: 'http://'+this.server+'/getTTS.php?string=' + escape(string) + '&format=.'+this.ttsFormat //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()
//for a short page, preload might nt have yet returned..
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 {
self.ttsPrefetchAudio();
self.ttsNextChunk();
},500);
-}
\ No newline at end of file
+}
+//FADING, ETC.
+ function changeArrow(){
+ setTimeout(function(){
+ $('#BRnavCntlBtm').removeClass('BRdn').addClass('BRup');
+ },3000);
+ };
+ $().ready(function(){
+ $('#BRtoolbar').animate({top:0},3000).animate({top:-40});
+ $('#BRnav').animate({bottom:0},3000).animate({bottom:-40});
+ changeArrow();
+ $('.BRnavCntl').animate({opacity:1},4000).animate({opacity:.25},1000);
+ $('.BRnavCntl').click(
+ function(){
+ if ($('#BRnavCntlBtm').hasClass('BRdn')) {
+ $('#BRtoolbar').animate({top:-40});
+ $('#BRnav').animate({bottom:-40});
+ $('#BRnavCntlBtm').addClass('BRup').removeClass('BRdn');
+ $('.BRnavCntl').animate({opacity:1},1000).animate({opacity:.25},1000);
+ } else {
+ $('#BRtoolbar').animate({top:0});
+ $('#BRnav').animate({bottom:0});
+ $('#BRnavCntlBtm').addClass('BRdn').removeClass('BRup');
+ $('.BRnavCntl').animate({opacity:1});
+ };
+ }
+ );
+ $('#BRnavCntlBtm').mouseover(function(){
+ if ($(this).hasClass('BRup')) {
+ $('.BRnavCntl').animate({opacity:1},250);
+ };
+ });
+ $('#BRnavCntlBtm').mouseleave(function(){
+ if ($(this).hasClass('BRup')) {
+ $('.BRnavCntl').animate({opacity:.25},250);
+ };
+ });
+ });
+