this.mode = this.constMode1up;
this.ui = 'full'; // UI mode
- this.uiAutoHide = true; // Controls whether nav/toolbar will autohide
+ this.uiAutoHide = false; // Controls whether nav/toolbar will autohide
// thumbnail mode
this.thumbWidth = 100; // will be overridden during prepareThumbnailView
// 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.
$("#BookReader").append("<div id='BRcontainer'></div>");
$("#BRcontainer").append("<div id='BRpageview'></div>");
-
- this.initNavbar();
- this.bindNavigationHandlers();
-
+
// Autohide nav after showing for awhile
var self = this;
if (this.uiAutoHide) {
// 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) {
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
// 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);
}
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()
//______________________________________________________________________________
//______________________________________________________________________________
BookReader.prototype.jumpToIndex = function(index, pageX, pageY) {
+ this.willChangeToIndex(index);
+
if (this.constMode2up == this.mode) {
this.autoStop();
}
} 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;
}
});
$("#BRcontainer").append("<div id='BRpageview'></div>");
-
+
// Attaches to first child - child must be present
$('#BRcontainer').dragscrollable();
this.bindGestures($('#BRcontainer'));
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;
}
//if (index<0) return;
+ this.willChangeToIndex(index);
+
var previousIndices = this.getSpreadIndices(index);
if (previousIndices[0] < this.firstDisplayableIndex() || previousIndices[1] < this.firstDisplayableIndex()) {
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
//______________________________________________________________________________
$('#BookReaderSearchResults').html('Searching...');
}
+// searchNew()
+//______________________________________________________________________________
+BookReader.prototype.searchNew = function(term) {
+ //console.log('searchNew called with term=' + term);
+ var url = 'http://'+this.server.replace(/:.+/, ''); //remove the port and userdir
+ url += '/~edward/inside_jsonp.php?item_id='+this.bookId;
+ url += '&doc='+this.subPrefix; //TODO: test with subitem
+ url += '&path='+this.bookPath.replace(new RegExp('/'+this.subPrefix+'$'), ''); //remove subPrefix from end of path
+ url += '&q='+escape(term);
+ //console.log('search url='+url);
+ this.ttsAjax = $.ajax({url:url, dataType:'jsonp', jsonpCallback:'BRSearchCallbackNew'});
+}
+
+// Unfortunately, we can't pass 'br.searchCallback' to our search service,
+// because it can't handle the '.'
+function BRSearchCallbackNew(results) {
+ //console.log('got ' + results.matches.length + ' results');
+ var i;
+ for (i=0; i<results.matches.length; i++) {
+ br.addSearchResult(results.matches[i].text, br.leafNumToIndex(results.matches[i].par[0].page));
+ }
+}
+
// BRSearchCallback()
//______________________________________________________________________________
BookReader.prototype.BRSearchCallback = function(txt) {
// $$$ should make this work inside the BookReader div (self-contained), rather than after
$('#BookReader').after(
'<div id="BRnav">'
- + '<div id="BRpage">'
+ + '<div id="BRpage">' // Page turn buttons
+ '<button class="BRicon book_left"></button>'
+ '<button class="BRicon book_right"></button>'
+ '</div>'
- + '<div id="BRnavpos">'
- + '<div id="BRfiller"></div>'
- + '<div id="BRpager">'
- + '<div id="BRslider">'
- + '<div id="slider"></div>'
- + '<div id="pager"></div>'
- // XXXmang update code to update pagenum
- + '<div id="pagenum"><span>n141</span> / 325</div>'
- + '</div>'
- + '</div>'
- + '<div id="BRnavline">'
+ + '<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>'
});
});
*/
+ 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.addSearchResult = function(queryString, pageNumber, pageIndex) {
+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);
+ var percentThrough = BookReader.util.cssPercentage(pageIndex, this.numLeafs - 1);
+ var pageDisplayString = '';
+ if (pageNumber) {
+ pageDisplayString = uiStringPage + ' ' + pageNumber;
+ }
- // $$$mang add click-through to page
+ var re = new RegExp('{{{(.+?)}}}', 'g');
+ queryString = queryString.replace(re, '<a href="#" onclick="br.jumpToIndex('+pageIndex+'); return false;">$1</a>')
+
$('<div class="search" style="left:' + percentThrough + ';" title="' + uiStringSearch + '"><div class="query">'
+ queryString + '<span>' + uiStringPage + ' ' + pageNumber + '</span></div>')
- .appendTo('#BRnavpos').bt({
+ .data({'self': this, 'pageIndex': pageIndex })
+ .appendTo('#BRnavline').bt({
contentSelector: '$(this).find(".query")',
- trigger: 'click',
+ trigger: 'hover',
closeWhenOthersOpen: true,
cssStyles: {
- padding: '10px 10px 15px',
+ padding: '12px 14px',
backgroundColor: '#fff',
- border: '3px solid #e2dcc5',
- borderBottom: 'none',
+ border: '4px solid #e2dcc5',
fontFamily: '"Lucida Grande","Arial",sans-serif',
- fontSize: '12px',
- lineHeight: '18px',
+ fontSize: '13px',
+ //lineHeight: '18px',
color: '#615132'
},
shrinkToFit: false,
padding: 0,
spikeGirth: 0,
spikeLength: 0,
- overlap: '10px',
+ overlap: '22px',
overlay: false,
killTitle: false,
textzIndex: 9999,
positions: ['top'],
fill: 'white',
windowMargin: 10,
- strokeWidth: 3,
- strokeStyle: '#e2dcc5',
+ strokeWidth: 0,
cornerRadius: 0,
centerPointX: 0,
centerPointY: 0,
shadow: false
})
- .hover(function(){
- $(this).addClass('front');
- },function(){
- $(this).removeClass('front');
- }
- );
+ .hover( function() {
+ $(this).addClass('front');
+ }, function() {
+ $(this).removeClass('front');
+ }
+ )
+ .bind('click', function() {
+ $(this).data('self').jumpToIndex($(this).data('pageIndex'));
+ });
+
}
BookReader.prototype.removeSearchResults = function() {
BookReader.prototype.addChapter = function(chapterTitle, pageNumber, pageIndex) {
var uiStringPage = 'Page'; // i18n
- var percentThrough = BookReader.util.cssPercentage(pageIndex, this.numLeafs);
+ 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('#BRnavpos')
+ .appendTo('#BRnavline')
.data({'self': this, 'pageIndex': pageIndex })
.bt({
contentSelector: '$(this).find(".title")',
trigger: 'hover',
closeWhenOthersOpen: true,
cssStyles: {
- backgroundColor: '#000',
- border: '2px solid #e2dcc5',
- borderBottom: 'none',
- padding: '5px 10px',
+ padding: '12px 14px',
+ //backgroundColor: '#000',
+ backgroundColor: '#444', // To set it off slightly from the chapter marker
+ border: '4px solid #e2dcc5',
+ //borderBottom: 'none',
fontFamily: '"Arial", sans-serif',
- fontSize: '11px',
+ fontSize: '12px',
fontWeight: '700',
color: '#fff',
whiteSpace: 'nowrap'
padding: 0,
spikeGirth: 0,
spikeLength: 0,
- overlap: '16px',
+ overlap: '21px',
overlay: false,
killTitle: true,
textzIndex: 9999,
* }
*/
BookReader.prototype.addChapterFromEntry = function(tocEntryObject) {
- console.log(tocEntryObject);
var pageIndex = this.getPageIndex(tocEntryObject['pagenum']);
// Only add if we know where it is
if (pageIndex) {
$(this).removeClass('front');
});
});
- $("#BRslider").draggable({axis:'x',containment:'parent'});
- $("#BRzoombtn").draggable({axis:'y',containment:'parent'});
- $("#BRslider").hover(
- function(){
- $("#pagenum").show();
- },function(){
- $("#pagenum").hide();
- });
}
BookReader.prototype.initToolbar = function(mode, ui) {
// $$$mang should be contained within the BookReader div instead of body
- $("body").append("<div id='BRtoolbar'>"
- + "<span id='BRtoolbarbuttons'>"
+ var readIcon = ''
+ if (!navigator.userAgent.match(/mobile/i)) {
+ readIcon = "<button class='BRicon read modal'></button>";
+ }
+
+ $("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>"
+ + "<form action='javascript:' 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' onclick='br.switchMode(3); return false;'></button>"
- + "<button class='BRicon share' onclick='br.switchMode(2); return false;'></button>"
- + "<button class='BRicon read modal' onclick='br.ttsToggle(); return false;'></button>"
- + "<button class='BRicon full'></button>"
- + "</span>"
-
- + "<span>"
- + "<a class='logo' href='" + this.logoURL + "'></a>"
- // XXXmang update
- + "<div id='BRreturn'><span>Back to</span><a href='BOOK URL'>Book Title</a></div>"
- + "</span>"
-
+ + "<button class='BRicon info'></button>"
+ + "<button class='BRicon share'></button>"
+ + readIcon
+ + "<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 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
self.rightmost();
return false;
});
+
+ jToolbar.find('.read').click(function(e) {
+ self.ttsToggle();
+ return false;
+ });
+
+ // $$$mang cleanup
+ $('#BRzoomer .zoom_in').bind('click', function() {
+ self.zoom(1);
+ return false;
+ });
+
+ $('#BRzoomer .zoom_out').bind('click', function() {
+ self.zoom(-1);
+ return false;
+ });
+
+ $('#booksearch').bind('submit', function() {
+ self.searchNew($('#textSrch').val());
+ });
+
}
// updateToolbarZoom(reduce)
self.updateTOC(olObject['table_of_contents']);
}
}
+
+ // $$$mang cleanup
+ $('#BRreturn a').attr('href', 'http://openlibrary.org' + olObject.key);
+
}
// Library functions
// Given value and maximum, calculate a percentage suitable for CSS
cssPercentage: function(value, max) {
- return parseInt(((value + 0.0) / max) * 100) + '%';
+ return (((value + 0.0) / max) * 100) + '%';
},
notInArray: function(value, array) {
// ttsToggle()
//______________________________________________________________________________
BookReader.prototype.ttsToggle = function () {
- if (false == this.ttsPlaying) {
+ if (false == this.ttsPlaying) {
+ this.ttsPlaying = true;
+ this.ttsShowPopup();
if(soundManager.supported()) {
this.ttsStart();
} else {
if (oStatus.success) {
this.ttsStart();
} else {
- alert('Could not load soundManger2, possibly due to FlashBlock. Audio playback is disabled');
+ alert('Could not load soundManager2, possibly due to FlashBlock. Audio playback is disabled');
}
}, this);
}
if (soundManager.debugMode) console.log('starting readAloud');
if (this.constModeThumb == this.mode) this.switchMode(this.constMode1up);
- this.ttsPlaying = true;
+ //this.ttsPlaying = true; //set this in ttsToggle()
this.ttsIndex = this.currentIndex();
this.ttsFormat = 'mp3';
if ($.browser.mozilla) {
//______________________________________________________________________________
BookReader.prototype.ttsShowPopup = function() {
if (soundManager.debugMode) console.log('ttsShowPopup index='+this.ttsIndex+' pos='+this.ttsPosition);
+ if (this.popup) return;
this.popup = document.createElement("div");
$(this.popup).css({