The GnuBook source is hosted at http://github.com/openlibrary/bookreader/
- archive.org cvs $Revision: 1.68 $ $Date: 2009-03-04 22:00:31 $
+ archive.org cvs $Revision: 1.2 $ $Date: 2009-06-22 18:42:51 $
*/
// GnuBook()
this.reduce = 4;
this.padding = 10;
this.mode = 1; //1 or 2
+ this.ui = 'full'; // UI mode
this.displayedLeafs = [];
//this.leafsToDisplay = [];
this.lastDisplayableIndex2up = null;
+ // We link to index.php to avoid redirect which breaks back button
+ this.logoURL = 'http://www.archive.org/index.php';
+
+ // Base URL for images
+ this.imagesBaseURL = '/bookreader/images/';
+
+ // Mode constants
+ this.constMode1up = 1;
+ this.constMode2up = 2;
+
};
// init()
//______________________________________________________________________________
GnuBook.prototype.init = function() {
- var startLeaf = window.location.hash;
- //console.log("startLeaf from location.hash: %s", startLeaf);
- if ('' == startLeaf) {
- if (this.titleLeaf) {
- startLeaf = "#" + this.leafNumToIndex(this.titleLeaf);
- }
+
+ var startIndex = undefined;
+
+ // Find start index and mode if set in location hash
+ var params = this.paramsFromFragment(window.location.hash);
+
+ if ('undefined' != typeof(params.index)) {
+ startIndex = params.index;
+ } else if ('undefined' != typeof(params.page)) {
+ startIndex = this.getPageIndex(params.page);
}
- // Ideally this would be set in the HTML/PHP for better search engine visibility but
- // it takes some time to locate the item and retrieve the metadata
+ if ('undefined' == typeof(startIndex)) {
+ startIndex = this.leafNumToIndex(this.titleLeaf);
+ }
+
+ if ('undefined' == typeof(startIndex)) {
+ startIndex = 0;
+ }
+
+ if ('undefined' != typeof(params.mode)) {
+ this.mode = params.mode;
+ }
+
+ // Set document title -- may have already been set in enclosing html for
+ // search engine visibility
document.title = this.shortTitle(50);
$("#GnuBook").empty();
- this.initToolbar(this.mode); // Build inside of toolbar div
+ this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
$("#GnuBook").append("<div id='GBcontainer'></div>");
$("#GBcontainer").append("<div id='GBpageview'></div>");
});
this.setupKeyListeners();
- this.setupRollovers();
+ this.startLocationPolling();
$(window).bind('resize', this, function(e) {
//console.log('resize!');
e.data.prepareTwoPageView();
}
});
+
+ $('.GBpagediv1up').bind('mousedown', this, function(e) {
+ //console.log('mousedown!');
+ });
if (1 == this.mode) {
this.resizePageView();
-
- if ('' != startLeaf) { // Jump to the leaf specified in the URL
- this.jumpToIndex(parseInt(startLeaf.substr(1)));
- //console.log('jump to ' + parseInt(startLeaf.substr(1)));
- }
+ this.firstIndex = startIndex;
+ this.jumpToIndex(startIndex);
} else {
//this.resizePageView();
this.displayedLeafs=[0];
- if ('' != startLeaf) {
- this.displayedLeafs = [parseInt(startLeaf.substr(1))];
- }
+ this.firstIndex = startIndex;
+ this.displayedLeafs = [this.firstIndex];
//console.log('titleLeaf: %d', this.titleLeaf);
//console.log('displayedLeafs: %s', this.displayedLeafs);
this.prepareTwoPageView();
//if (this.auto) this.nextPage();
}
+
+ // Enact other parts of initial params
+ this.updateFromParams(params);
}
GnuBook.prototype.setupKeyListeners = function() {
}
break;
case KEY_END:
- self.end();
+ self.last();
break;
case KEY_HOME:
- self.home();
+ self.first();
break;
case KEY_LEFT:
if (self.keyboardNavigationIsDisabled(e)) {
}
}
+// setDragHandler1up()
+//______________________________________________________________________________
+GnuBook.prototype.setDragHandler1up = function(div) {
+ div.dragging = false;
+
+ $(div).bind('mousedown', function(e) {
+ //console.log('mousedown at ' + e.pageY);
+
+ this.dragging = true;
+ this.prevMouseX = e.pageX;
+ this.prevMouseY = e.pageY;
+
+ var startX = e.pageX;
+ var startY = e.pageY;
+ var startTop = $('#GBcontainer').attr('scrollTop');
+ var startLeft = $('#GBcontainer').attr('scrollLeft');
+
+ return false;
+ });
+
+ $(div).bind('mousemove', function(ee) {
+ //console.log('mousemove ' + startY);
+
+ var offsetX = ee.pageX - this.prevMouseX;
+ var offsetY = ee.pageY - this.prevMouseY;
+
+ if (this.dragging) {
+ $('#GBcontainer').attr('scrollTop', $('#GBcontainer').attr('scrollTop') - offsetY);
+ $('#GBcontainer').attr('scrollLeft', $('#GBcontainer').attr('scrollLeft') - offsetX);
+ }
+
+ this.prevMouseX = ee.pageX;
+ this.prevMouseY = ee.pageY;
+
+ return false;
+ });
+
+ $(div).bind('mouseup', function(ee) {
+ //console.log('mouseup');
+
+ this.dragging = false;
+ return false;
+ });
+
+ $(div).bind('mouseleave', function(e) {
+ //console.log('mouseleave');
+
+ //$(this).unbind('mousemove mouseup');
+ this.dragging = false;
+
+ });
+}
// drawLeafsOnePage()
//______________________________________________________________________________
var bottomInView = (leafBottom >= scrollTop) && (leafBottom <= scrollBottom);
var middleInView = (leafTop <=scrollTop) && (leafBottom>=scrollBottom);
if (topInView | bottomInView | middleInView) {
+ //console.log('displayed: ' + this.displayedLeafs);
//console.log('to display: ' + i);
leafsToDisplay.push(i);
}
}
var firstLeafToDraw = leafsToDisplay[0];
- window.location.replace('#' + firstLeafToDraw);
this.firstIndex = firstLeafToDraw;
+
+ // Update hash, but only if we're currently displaying a leaf
+ // Hack that fixes #365790
+ if (this.displayedLeafs.length > 0) {
+ this.updateLocationHash();
+ }
if ((0 != firstLeafToDraw) && (1 < this.reduce)) {
firstLeafToDraw--;
$(div).css('height', height+'px');
//$(div).text('loading...');
+ this.setDragHandler1up(div);
+
$('#GBpageview').append(div);
var img = document.createElement("img");
this.updateSearchHilites();
if (null != this.getPageNum(firstLeafToDraw)) {
- $("#GBpagenum").val(this.getPageNum(firstLeafToDraw));
+ $("#GBpagenum").val(this.getPageNum(this.currentIndex()));
} else {
$("#GBpagenum").val('');
}
} else {
$("#GBpagenum").val('');
}
- window.location.replace('#' + this.currentLeafL);
+ this.updateLocationHash();
}
// loadLeafs()
$('#GBzoom').text(100/this.reduce);
}
-
// resizePageView()
//______________________________________________________________________________
GnuBook.prototype.resizePageView = function() {
// jumpToPage()
//______________________________________________________________________________
+// Attempts to jump to page. Returns true if page could be found, false otherwise.
GnuBook.prototype.jumpToPage = function(pageNum) {
- var i;
- var foundPage = false;
- var foundLeaf = null;
- for (i=0; i<this.numLeafs; i++) {
- if (this.getPageNum(i) == pageNum) {
- foundPage = true;
- foundLeaf = i;
- break;
- }
- }
-
- if (foundPage) {
+ var pageIndex = this.getPageIndex(pageNum);
+
+ if ('undefined' != typeof(pageIndex)) {
var leafTop = 0;
var h;
- this.jumpToIndex(foundLeaf);
+ this.jumpToIndex(pageIndex);
$('#GBcontainer').attr('scrollTop', leafTop);
- } else {
- alert('Page not found. This book might not have pageNumbers in scandata.');
+ return true;
}
+
+ // Page not found
+ return false;
}
// jumpToIndex()
leafTop += h + this.padding;
}
//$('#GBcontainer').attr('scrollTop', leafTop);
- $('#GBcontainer').animate({scrollTop: leafTop },'fast');
+ $('#GBcontainer').animate({scrollTop: leafTop },'fast');
}
}
//______________________________________________________________________________
GnuBook.prototype.prepareOnePageView = function() {
- var startLeaf = this.displayedLeafs[0];
+ // var startLeaf = this.displayedLeafs[0];
+ var startLeaf = this.currentIndex();
$('#GBcontainer').empty();
$('#GBcontainer').css({
overflowX: 'auto'
});
- $("#GBcontainer").append("<div id='GBpageview'></div>");
+ var gbPageView = $("#GBcontainer").append("<div id='GBpageview'></div>");
this.resizePageView();
+
this.jumpToIndex(startLeaf);
- this.displayedLeafs = [];
+ this.displayedLeafs = [];
+
this.drawLeafsOnePage();
- $('#GBzoom').text(100/this.reduce);
+ $('#GBzoom').text(100/this.reduce);
+
+ // Bind mouse handlers
+ // Disable mouse click to avoid selected/highlighted page images - bug 354239
+ gbPageView.bind('mousedown', function(e) {
+ return false;
+ })
+ // Special hack for IE7
+ gbPageView[0].onselectstart = function(e) { return false; };
}
// prepareTwoPageView()
// one side of the spread because it is the first/last leaf,
// foldouts, missing pages, etc
- var targetLeaf = this.displayedLeafs[0];
-
+ //var targetLeaf = this.displayedLeafs[0];
+ var targetLeaf = this.firstIndex;
+
if (targetLeaf < this.firstDisplayableIndex()) {
targetLeaf = this.firstDisplayableIndex();
}
var currentSpreadIndices = this.getSpreadIndices(targetLeaf);
this.currentLeafL = currentSpreadIndices[0];
this.currentLeafR = currentSpreadIndices[1];
+ this.firstIndex = this.currentLeafL;
this.calculateSpreadSize(); //sets this.twoPageW, twoPageH, and twoPageRatio
borderStyle: 'solid solid solid none',
borderColor: 'rgb(51, 51, 34)',
borderWidth: '1px 1px 1px 0px',
- background: 'transparent url(images/right-edges.png) repeat scroll 0% 0%',
+ background: 'transparent url(' + this.imagesBaseURL + 'right_edges.png) repeat scroll 0% 0%',
width: leafEdgeWidthR + 'px',
height: this.twoPageH-1 + 'px',
/*right: '10px',*/
borderStyle: 'solid none solid solid',
borderColor: 'rgb(51, 51, 34)',
borderWidth: '1px 0px 1px 1px',
- background: 'transparent url(images/left-edges.png) repeat scroll 0% 0%',
+ background: 'transparent url(' + this.imagesBaseURL + 'left_edges.png) repeat scroll 0% 0%',
width: leafEdgeWidthL + 'px',
height: this.twoPageH-1 + 'px',
left: bookCoverDivLeft+10+'px',
}
+// currentIndex()
+//______________________________________________________________________________
+// 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;
+ } else {
+ throw 'currentIndex called for unimplemented mode ' + this.mode;
+ }
+}
+
// right()
//______________________________________________________________________________
// Flip the right page over onto the left
}
}
+// rightmost()
+//______________________________________________________________________________
+// Flip to the rightmost page
+GnuBook.prototype.rightmost = function() {
+ if ('rl' != this.pageProgression) {
+ gb.last();
+ } else {
+ gb.first();
+ }
+}
+
// left()
//______________________________________________________________________________
// Flip the left page over onto the right.
}
}
+// leftmost()
+//______________________________________________________________________________
+// Flip to the leftmost page
+GnuBook.prototype.leftmost = function() {
+ if ('rl' != this.pageProgression) {
+ gb.first();
+ } else {
+ gb.last();
+ }
+}
+
// next()
//______________________________________________________________________________
GnuBook.prototype.next = function() {
}
}
-GnuBook.prototype.home = function() {
+GnuBook.prototype.first = function() {
if (2 == this.mode) {
this.jumpToIndex(2);
}
}
}
-GnuBook.prototype.end = function() {
+GnuBook.prototype.last = function() {
if (2 == this.mode) {
this.jumpToIndex(this.lastDisplayableIndex());
}
borderStyle: 'solid none solid solid',
borderColor: 'rgb(51, 51, 34)',
borderWidth: '1px 0px 1px 1px',
- background: 'transparent url(images/left-edges.png) repeat scroll 0% 0%',
+ background: 'transparent url(' + this.imagesBaseURL + 'left_edges.png) repeat scroll 0% 0%',
width: leafEdgeTmpW + 'px',
height: this.twoPageH-1 + 'px',
left: leftEdgeTmpLeft + 'px',
self.currentLeafL = newIndexL;
self.currentLeafR = newIndexR;
+ self.firstIndex = self.currentLeafL;
self.displayedLeafs = [newIndexL, newIndexR];
self.setClickHandlers();
self.pruneUnusedImgs();
- self.prefetch();
+ self.prefetch();
self.animating = false;
self.updateSearchHilites2UP();
self.updatePageNumBox2UP();
//$('#GBzoom').text((self.twoPageH/self.getPageHeight(newIndexL)).toString().substr(0,4));
+
+ if (self.animationFinishedCallback) {
+ self.animationFinishedCallback();
+ self.animationFinishedCallback = null;
+ }
});
});
borderStyle: 'solid none solid solid',
borderColor: 'rgb(51, 51, 34)',
borderWidth: '1px 0px 1px 1px',
- background: 'transparent url(images/left-edges.png) repeat scroll 0% 0%',
+ background: 'transparent url(' + this.imagesBaseURL + 'left_edges.png) repeat scroll 0% 0%',
width: leafEdgeTmpW + 'px',
height: this.twoPageH-1 + 'px',
left: currGutter+scaledW+'px',
self.currentLeafL = newIndexL;
self.currentLeafR = newIndexR;
+ self.firstIndex = self.currentLeafL;
self.displayedLeafs = [newIndexL, newIndexR];
self.setClickHandlers();
self.pruneUnusedImgs();
self.prefetch();
self.animating = false;
+
self.updateSearchHilites2UP();
self.updatePageNumBox2UP();
//$('#GBzoom').text((self.twoPageH/self.getPageHeight(newIndexL)).toString().substr(0,4));
+
+ if (self.animationFinishedCallback) {
+ self.animationFinishedCallback();
+ self.animationFinishedCallback = null;
+ }
});
});
}
}).appendTo('#GnuBook');
htmlStr = '<p style="text-align:center;"><b>Embed Bookreader in your blog!</b></p>';
- htmlStr += '<p><b>Note:</b> The bookreader is still in beta testing. URLs may change in the future, breaking embedded books. This feature is just for testing!</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="<iframe src=\'http://www.us.archive.org/GnuBook/GnuBookEmbed.php?id='+this.bookId+'\' width=\'430px\' height=\'430px\'></iframe>"></p>';
+ htmlStr += '<p>Embed Code: <input type="text" size="40" value="' + this.getEmbedCode() + '"></p>';
htmlStr += '<p style="text-align:center;"><a href="" onclick="gb.embedPopup = null; $(this.parentNode.parentNode).remove(); return false">Close popup</a></p>';
- this.embedPopup.innerHTML = htmlStr;
+ this.embedPopup.innerHTML = htmlStr;
+ $(this.embedPopup).find('input').bind('click', function() {
+ this.select();
+ })
}
// autoToggle()
}
}
-GnuBook.prototype.initToolbar = function(mode) {
- // XXXmang hook up logo to url action -- change buttons to image links? -- don't hardcode URL
- $("#GnuBook").append("<div id='GBtoolbar'><span style='float:left;'><button class='GBicon logo' onclick='window.location = \"http://www.archive.org\";' />"
+GnuBook.prototype.initToolbar = function(mode, ui) {
+ $("#GnuBook").append("<div id='GBtoolbar'><span style='float:left;'>"
+ + "<a class='GBicon logo rollover' href='" + this.logoURL + "'> </a>"
+ " <button class='GBicon rollover zoom_out' onclick='gb.zoom1up(-1); return false;'/>"
+ "<button class='GBicon rollover zoom_in' onclick='gb.zoom1up(1); return false;'/>"
- + " <span class='label'>Zoom: <span id='GBzoom'>25</span>%</span>"
- + " <button class='GBicon rollover script' onclick='gb.switchMode(1); return false;'/>"
- + " <button class='GBicon rollover book_open' onclick='gb.switchMode(2); return false;'/>"
- + " <a class='GBblack title' href='"+this.bookUrl+"' target='_blank'>"+this.shortTitle(50)+"</a>"
+ + " <span class='label'>Zoom: <span id='GBzoom'>"+100/this.reduce+"</span>%</span>"
+ + " <button class='GBicon rollover one_page_mode' onclick='gb.switchMode(1); return false;'/>"
+ + " <button class='GBicon rollover two_page_mode' onclick='gb.switchMode(2); return false;'/>"
+ + " <a class='GBblack title' href='"+this.bookUrl+"' target='_blank'>"+this.shortTitle(50)+"</a>"
+ "</span></div>");
+
+ if (ui == "embed") {
+ $("#GnuBook a.logo").attr("target","_blank");
+ }
// $$$ turn this into a member variable
var jToolbar = $('#GBtoolbar'); // j prefix indicates jQuery object
// We build in mode 2
jToolbar.append("<span id='GBtoolbarbuttons' style='float: right'>"
- + "<button class='GBicon rollover page_code' />"
+ + "<button class='GBicon rollover embed' />"
+ "<form class='GBpageform' action='javascript:' onsubmit='gb.jumpToPage(this.elements[0].value)'> <span class='label'>Page:<input id='GBpagenum' type='text' size='3' onfocus='gb.autoStop();'></input></span></form>"
- + "<div class='GBtoolbarmode2' style='display: inline'><button class='GBicon rollover book_left' /><button class='GBicon rollover book_right' /></div>"
- + "<div class='GBtoolbarmode1' style='display: hidden'><button class='GBicon rollover book_up' /> <button class='GBicon rollover book_down' /></div>"
+ + "<div class='GBtoolbarmode2' style='display: inline'><button class='GBicon rollover book_leftmost' /><button class='GBicon rollover book_left' /><button class='GBicon rollover book_right' /><button class='GBicon rollover book_rightmost' /></div>"
+ + "<div class='GBtoolbarmode1' style='display: hidden'><button class='GBicon rollover book_top' /><button class='GBicon rollover book_up' /> <button class='GBicon rollover book_down' /><button class='GBicon rollover book_bottom' /></div>"
+ "<button class='GBicon rollover play' /><button class='GBicon rollover pause' style='display: none' /></span>");
- // Bind the non-changing click handlers
- jToolbar.find('.page_code').bind('click', function(e) {
- gb.showEmbedCode();
- return false;
- });
- jToolbar.find('.play').bind('click', function(e) {
- gb.autoToggle();
- return false;
- });
- jToolbar.find('.pause').bind('click', function(e) {
- gb.autoToggle();
- return false;
- });
+ this.bindToolbarNavHandlers(jToolbar);
+
+ // Setup tooltips -- later we could load these from a file for i18n
+ 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',
+ '.embed': 'Embed bookreader',
+ '.book_left': 'Flip left',
+ '.book_right': 'Flip right',
+ '.book_up': 'Page up',
+ '.book_down': 'Page down',
+ '.play': 'Play',
+ '.pause': 'Pause',
+ '.book_top': 'First page',
+ '.book_bottom': 'Last page'
+ };
+ if ('rl' == this.pageProgression) {
+ titles['.book_leftmost'] = 'Last page';
+ titles['.book_rightmost'] = 'First page';
+ } else { // LTR
+ titles['.book_leftmost'] = 'First page';
+ titles['.book_rightmost'] = 'Last page';
+ }
+
+ for (var icon in titles) {
+ jToolbar.find(icon).attr('title', titles[icon]);
+ }
// Switch to requested mode -- binds other click handlers
this.switchToolbarMode(mode);
$('#GBtoolbar .GBtoolbarmode1').hide();
$('#GBtoolbar .GBtoolbarmode2').css('display', 'inline').show();
}
-
- this.bindToolbarNavHandlers($('#GBtoolbar'));
}
// bindToolbarNavHandlers
// Binds the toolbar handlers
GnuBook.prototype.bindToolbarNavHandlers = function(jToolbar) {
- jToolbar.find('.book_left').unbind('click')
- .bind('click', function(e) {
- gb.left();
- return false;
- });
+ jToolbar.find('.book_left').bind('click', function(e) {
+ gb.left();
+ return false;
+ });
- jToolbar.find('.book_right').unbind('click')
- .bind('click', function(e) {
- gb.right();
- return false;
- });
+ jToolbar.find('.book_right').bind('click', function(e) {
+ gb.right();
+ return false;
+ });
+
+ jToolbar.find('.book_up').bind('click', function(e) {
+ gb.prev();
+ return false;
+ });
- jToolbar.find('.book_up').unbind('click')
- .bind('click', function(e) {
- gb.prev();
- return false;
- });
+ jToolbar.find('.book_down').bind('click', function(e) {
+ gb.next();
+ return false;
+ });
- jToolbar.find('.book_down').unbind('click')
- .bind('click', function(e) {
- gb.next();
- return false;
- });
+ jToolbar.find('.embed').bind('click', function(e) {
+ gb.showEmbedCode();
+ return false;
+ });
+
+ jToolbar.find('.play').bind('click', function(e) {
+ gb.autoToggle();
+ return false;
+ });
+
+ jToolbar.find('.pause').bind('click', function(e) {
+ gb.autoToggle();
+ return false;
+ });
+
+ jToolbar.find('.book_top').bind('click', function(e) {
+ gb.first();
+ return false;
+ });
+
+ jToolbar.find('.book_bottom').bind('click', function(e) {
+ gb.last();
+ return false;
+ });
+
+ jToolbar.find('.book_leftmost').bind('click', function(e) {
+ gb.leftmost();
+ return false;
+ });
+
+ jToolbar.find('.book_rightmost').bind('click', function(e) {
+ gb.rightmost();
+ return false;
+ });
}
// firstDisplayableIndex
}
}
-// setupRollovers
-//______________________________________________________________________________
-// Set up rollover behaviour for icons
-GnuBook.prototype.setupRollovers = function() {
-
- // TODO precache
-
- // On hover we change the base to rollover. We switch back on off-hover.
- $('#GnuBook .rollover').hover( function() {
- $(this).css('backgroundImage', $(this).css('backgroundImage').replace('_base', '_rollover'));
- }, function () {
- $(this).css('backgroundImage', $(this).css('backgroundImage').replace('_rollover', '_base'));
- });
-}
-
// shortTitle(maximumCharacters)
//________
// Returns a shortened version of the title with the maximum number of characters
var title = this.bookTitle.substr(0, maximumCharacters - 3);
title += '...';
return title;
+}
+
+
+
+// Parameter related functions
+
+// updateFromParams(params)
+//________
+// Update ourselves from the params object.
+//
+// e.g. this.updateFromParams(this.paramsFromFragment(window.location.hash))
+GnuBook.prototype.updateFromParams = function(params) {
+ if ('undefined' != typeof(params.mode)) {
+ this.switchMode(params.mode);
+ }
+
+ // $$$ process /search
+ // $$$ process /zoom
+
+ // We only respect page if index is not set
+ if ('undefined' != typeof(params.index)) {
+ if (params.index != this.currentIndex()) {
+ this.jumpToIndex(params.index);
+ }
+ } else if ('undefined' != typeof(params.page)) {
+ if (params.page != this.getPageNum(this.currentIndex())) {
+ this.jumpToPage(params.page);
+ }
+ }
+
+ // $$$ process /region
+ // $$$ process /highlight
+}
+
+// paramsFromFragment(urlFragment)
+//________
+// Returns a object with configuration parametes from a URL fragment.
+//
+// E.g paramsFromFragment(window.location.hash)
+GnuBook.prototype.paramsFromFragment = function(urlFragment) {
+ // URL fragment syntax specification: http://openlibrary.org/dev/docs/bookurls
+
+ var params = {};
+
+ // For convenience we allow an initial # character (as from window.location.hash)
+ // but don't require it
+ if (urlFragment.substr(0,1) == '#') {
+ urlFragment = urlFragment.substr(1);
+ }
+
+ // Simple #nn syntax
+ var oldStyleLeafNum = parseInt( /^\d+$/.exec(urlFragment) );
+ if ( !isNaN(oldStyleLeafNum) ) {
+ params.index = oldStyleLeafNum;
+
+ // Done processing if using old-style syntax
+ return params;
+ }
+
+ // Split into key-value pairs
+ var urlArray = urlFragment.split('/');
+ var urlHash = {};
+ for (var i = 0; i < urlArray.length; i += 2) {
+ urlHash[urlArray[i]] = urlArray[i+1];
+ }
+
+ // Mode
+ if ('1up' == urlHash['mode']) {
+ params.mode = this.constMode1up;
+ } else if ('2up' == urlHash['mode']) {
+ params.mode = this.constMode2up;
+ }
+
+ // Index and page
+ if ('undefined' != typeof(urlHash['page'])) {
+ // page was set -- may not be int
+ params.page = urlHash['page'];
+ }
+
+ // $$$ process /region
+ // $$$ process /search
+ // $$$ process /highlight
+
+ return params;
+}
+
+// paramsFromCurrent()
+//________
+// Create a params object from the current parameters.
+GnuBook.prototype.paramsFromCurrent = function() {
+
+ var params = {};
+
+ var pageNum = this.getPageNum(this.currentIndex());
+ if ((pageNum === 0) || pageNum) {
+ params.page = pageNum;
+ }
+
+ params.index = this.currentIndex();
+ params.mode = this.mode;
+
+ // $$$ highlight
+ // $$$ region
+ // $$$ search
+
+ return params;
+}
+
+// fragmentFromParams(params)
+//________
+// Create a fragment string from the params object.
+// See http://openlibrary.org/dev/docs/bookurls for an explanation of the fragment syntax.
+GnuBook.prototype.fragmentFromParams = function(params) {
+ var separator = '/';
+
+ var fragments = [];
+
+ if ('undefined' != typeof(params.page)) {
+ fragments.push('page', params.page);
+ } else {
+ // Don't have page numbering -- use index instead
+ fragments.push('page', 'n' + params.index);
+ }
+
+ // $$$ highlight
+ // $$$ region
+ // $$$ search
+
+ // mode
+ if ('undefined' != typeof(params.mode)) {
+ if (params.mode == this.constMode1up) {
+ fragments.push('mode', '1up');
+ } else if (params.mode == this.constMode2up) {
+ fragments.push('mode', '2up');
+ } else {
+ throw 'fragmentFromParams called with unknown mode ' + params.mode;
+ }
+ }
+
+ return fragments.join(separator);
+}
+
+// getPageIndex(pageNum)
+//________
+// Returns the index of the given page number, or undefined
+GnuBook.prototype.getPageIndex = function(pageNum) {
+ var pageIndex = undefined;
+
+ // Check for special "nXX" page number
+ if (pageNum.slice(0,1) == 'n') {
+ try {
+ var pageIntStr = pageNum.slice(1, pageNum.length);
+ pageIndex = parseInt(pageIntStr);
+ return pageIndex;
+ } catch(err) {
+ // Do nothing... will run through page names and see if one matches
+ }
+ }
+
+ var i;
+ for (i=0; i<this.numLeafs; i++) {
+ if (this.getPageNum(i) == pageNum) {
+ pageIndex = i;
+ return pageIndex;
+ }
+ }
+
+ return pageIndex;
+}
+
+// updateLocationHash
+//________
+// Update the location hash from the current parameters. Call this instead of manually
+// using window.location.replace
+GnuBook.prototype.updateLocationHash = function() {
+ var newHash = '#' + this.fragmentFromParams(this.paramsFromCurrent());
+ window.location.replace(newHash);
+
+ // This is the variable checked in the timer. Only user-generated changes
+ // to the URL will trigger the event.
+ this.oldLocationHash = newHash;
+}
+
+// startLocationPolling
+//________
+// Starts polling of window.location to see hash fragment changes
+GnuBook.prototype.startLocationPolling = function() {
+ var self = this; // remember who I am
+ self.oldLocationHash = window.location.hash;
+
+ if (this.locationPollId) {
+ clearInterval(this.locationPollID);
+ this.locationPollId = null;
+ }
+
+ this.locationPollId = setInterval(function() {
+ var newHash = window.location.hash;
+ if (newHash != self.oldLocationHash) {
+ if (newHash != self.oldUserHash) { // Only process new user hash once
+ //console.log('url change detected ' + self.oldLocationHash + " -> " + newHash);
+
+ // Queue change if animating
+ if (self.animating) {
+ self.autoStop();
+ self.animationFinishedCallback = function() {
+ self.updateFromParams(self.paramsFromFragment(newHash));
+ }
+ } else { // update immediately
+ self.updateFromParams(self.paramsFromFragment(newHash));
+ }
+ self.oldUserHash = newHash;
+ }
+ }
+ }, 500);
+}
+
+// getEmbedURL
+//________
+// Returns a URL for an embedded version of the current book
+GnuBook.prototype.getEmbedURL = function() {
+ // We could generate a URL hash fragment here but for now we just leave at defaults
+ return 'http://' + window.location.host + '/stream/'+this.bookId + '?ui=embed';
+}
+
+// getEmbedCode
+//________
+// Returns the embed code HTML fragment suitable for copy and paste
+GnuBook.prototype.getEmbedCode = function() {
+ return "<iframe src='" + this.getEmbedURL() + "' width='480px' height='430px'></iframe>";
}
\ No newline at end of file