Add autoPlay feature, simple.js, and more icons.
[bookreader.git] / GnuBook / GnuBook.js
1 /*
2 Copyright(c)2008 Internet Archive. Software license AGPL version 3.
3
4 This file is part of GnuBook.
5
6     GnuBook is free software: you can redistribute it and/or modify
7     it under the terms of the GNU Affero General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10
11     GnuBook is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU Affero General Public License for more details.
15
16     You should have received a copy of the GNU Affero General Public License
17     along with GnuBook.  If not, see <http://www.gnu.org/licenses/>.
18     
19 archive.org cvs $Revision: 1.57 $ $Date: 2008-10-27 05:47:40 $
20 */
21
22 // GnuBook()
23 //______________________________________________________________________________
24 // After you instantiate this object, you must supply the following
25 // book-specific functions, before calling init():
26 //  - getPageWidth()
27 //  - getPageHeight()
28 //  - getPageURI()
29 // You must also add a numLeafs property before calling init().
30
31 function GnuBook() {
32     this.reduce  = 4;
33     this.padding = 10;
34     this.mode    = 1; //1 or 2
35     
36     this.displayedLeafs = [];   
37     //this.leafsToDisplay = [];
38     this.imgs = {};
39     this.prefetchedImgs = {}; //an object with numeric keys cooresponding to leafNum
40     
41     this.timer     = null;
42     this.animating = false;
43     this.auto      = false;
44     this.autoTimer = null;
45     this.flipSpeed = 'fast';
46
47     this.twoPagePopUp = null;
48     this.leafEdgeTmp  = null;
49     
50     this.searchResults = {};
51     
52     this.firstIndex = null;
53     
54 };
55
56 // init()
57 //______________________________________________________________________________
58 GnuBook.prototype.init = function() {
59
60     var startLeaf = window.location.hash;
61     var title = this.bookTitle.substr(0,50);
62     if (this.bookTitle.length>50) title += '...';
63     
64     $("#GnuBook").empty();
65     $("#GnuBook").append("<div id='GBtoolbar'><span style='float:left;'><img class='GBicon' src='http://www.us.archive.org/GnuBook/images/zoom_out.png' onclick='gb.zoom1up(-1); return false;'> <img class='GBicon' src='http://www.us.archive.org/GnuBook/images/zoom_in.png' onclick='gb.zoom1up(1); return false;'> zoom: <span id='GBzoom'>25</span>% <img class='GBicon' src='http://www.us.archive.org/GnuBook/images/script.png' onclick='gb.switchMode(1); return false;'> <img class='GBicon' src='http://www.us.archive.org/GnuBook/images/book_open.png' onclick='gb.switchMode(2); return false;'>  &nbsp;&nbsp; <a href='"+this.bookUrl+"' target='_blank'>"+title+"</a></span></div>");
66     $("#GBtoolbar").append("<form class='GBpageform' action='javascript:' onsubmit='gb.jumpToPage(this.elements[0].value)'><img class='GBicon' src='http://www.us.archive.org/GnuBook/images/page_code.png' onclick='gb.showEmbedCode(); return false;'> page:<input id='GBpagenum' type='text' size='3' onfocus='gb.autoStop();'></input> <img class='GBicon' src='http://www.us.archive.org/GnuBook/images/book_previous.png' onclick='gb.prev(); return false;'> <img class='GBicon' src='http://www.us.archive.org/GnuBook/images/book_next.png' onclick='gb.next(); return false;'> <img class='GBicon' id='autoImg' src='http://www.us.archive.org/GnuBook/images/control_play_blue.png' onclick='gb.autoToggle(); return false;'></form>");
67     $("#GnuBook").append("<div id='GBcontainer'></div>");
68     $("#GBcontainer").append("<div id='GBpageview'></div>");
69
70     $("#GBcontainer").bind('scroll', this, function(e) {
71         e.data.loadLeafs();
72     });
73
74     $(window).bind('resize', this, function(e) {
75         //console.log('resize!');
76         if (1 == e.data.mode) {
77             //console.log('centering 1page view');
78             e.data.centerPageView();
79             $('#GBpageview').empty()
80             e.data.displayedLeafs = [];
81             e.data.updateSearchHilites(); //deletes hilights but does not call remove()            
82             e.data.loadLeafs();
83         } else {
84             //console.log('drawing 2 page view');
85             e.data.prepareTwoPageView();
86         }
87     });
88
89     if (1 == this.mode) {
90         this.resizePageView();
91     
92         if ('' != startLeaf) {
93             this.jumpToIndex(parseInt(startLeaf.substr(1)));
94             //alert('jump to ' + startLeaf);
95         }
96     } else {
97         this.displayedLeafs=[0];
98         this.prepareTwoPageView();
99         //if (this.auto) this.nextPage();
100     }
101 }
102
103 // drawLeafs()
104 //______________________________________________________________________________
105 GnuBook.prototype.drawLeafs = function() {
106     if (1 == this.mode) {
107         this.drawLeafsOnePage();
108     } else {
109         this.drawLeafsTwoPage();
110     }
111 }
112
113
114 // drawLeafsOnePage()
115 //______________________________________________________________________________
116 GnuBook.prototype.drawLeafsOnePage = function() {
117     //alert('drawing leafs!');
118     this.timer = null;
119
120
121     var scrollTop = $('#GBcontainer').attr('scrollTop');
122     var scrollBottom = scrollTop + $('#GBcontainer').height();
123     //console.log('top=' + scrollTop + ' bottom='+scrollBottom);
124     
125     var leafsToDisplay = [];
126     
127     var i;
128     var leafTop = 0;
129     var leafBottom = 0;
130     for (i=0; i<this.numLeafs; i++) {
131         var height  = parseInt(this.getPageHeight(i)/this.reduce); 
132     
133         leafBottom += height;
134         //console.log('leafTop = '+leafTop+ ' pageH = ' + this.pageH[i] + 'leafTop>=scrollTop=' + (leafTop>=scrollTop));
135         var topInView    = (leafTop >= scrollTop) && (leafTop <= scrollBottom);
136         var bottomInView = (leafBottom >= scrollTop) && (leafBottom <= scrollBottom);
137         var middleInView = (leafTop <=scrollTop) && (leafBottom>=scrollBottom);
138         if (topInView | bottomInView | middleInView) {
139             //console.log('to display: ' + i);
140             leafsToDisplay.push(i);
141         }
142         leafTop += height +10;      
143         leafBottom += 10;
144     }
145
146     var firstLeafToDraw  = leafsToDisplay[0];
147     window.location.hash = firstLeafToDraw;    
148     this.firstIndex      = firstLeafToDraw;
149
150     if ((0 != firstLeafToDraw) && (1 < this.reduce)) {
151         firstLeafToDraw--;
152         leafsToDisplay.unshift(firstLeafToDraw);
153     }
154     
155     var lastLeafToDraw = leafsToDisplay[leafsToDisplay.length-1];
156     if ( ((this.numLeafs-1) != lastLeafToDraw) && (1 < this.reduce) ) {
157         leafsToDisplay.push(lastLeafToDraw+1);
158     }
159     
160     leafTop = 0;
161     var i;
162     for (i=0; i<firstLeafToDraw; i++) {
163         leafTop += parseInt(this.getPageHeight(i)/this.reduce) +10;
164     }
165
166     //var viewWidth = $('#GBpageview').width(); //includes scroll bar width
167     var viewWidth = $('#GBcontainer').attr('scrollWidth');
168
169
170     for (i=0; i<leafsToDisplay.length; i++) {
171         var leafNum = leafsToDisplay[i];    
172         var height  = parseInt(this.getPageHeight(leafNum)/this.reduce); 
173
174         if(-1 == jQuery.inArray(leafsToDisplay[i], this.displayedLeafs)) {            
175             var width   = parseInt(this.getPageWidth(leafNum)/this.reduce); 
176             //console.log("displaying leaf " + leafsToDisplay[i] + ' leafTop=' +leafTop);
177             var div = document.createElement("div");
178             div.className = 'GBpagediv1up';
179             div.id = 'pagediv'+leafNum;
180             div.style.position = "absolute";
181             $(div).css('top', leafTop + 'px');
182             var left = (viewWidth-width)>>1;
183             if (left<0) left = 0;
184             $(div).css('left', left+'px');
185             $(div).css('width', width+'px');
186             $(div).css('height', height+'px');
187             //$(div).text('loading...');
188             
189             $('#GBpageview').append(div);
190
191             var img = document.createElement("img");
192             img.src = this.getPageURI(leafNum);
193             $(img).css('width', width+'px');
194             $(img).css('height', height+'px');
195             $(div).append(img);
196
197         } else {
198             //console.log("not displaying " + leafsToDisplay[i] + ' score=' + jQuery.inArray(leafsToDisplay[i], this.displayedLeafs));            
199         }
200
201         leafTop += height +10;
202
203     }
204     
205     for (i=0; i<this.displayedLeafs.length; i++) {
206         if (-1 == jQuery.inArray(this.displayedLeafs[i], leafsToDisplay)) {
207             var leafNum = this.displayedLeafs[i];
208             //console.log('Removing leaf ' + leafNum);
209             //console.log('id='+'#pagediv'+leafNum+ ' top = ' +$('#pagediv'+leafNum).css('top'));
210             $('#pagediv'+leafNum).remove();
211         } else {
212             //console.log('NOT Removing leaf ' + this.displayedLeafs[i]);
213         }
214     }
215     
216     this.displayedLeafs = leafsToDisplay.slice();
217     this.updateSearchHilites();
218     
219     if (null != this.getPageNum(firstLeafToDraw))  {
220         $("#GBpagenum").val(this.getPageNum(firstLeafToDraw));
221     } else {
222         $("#GBpagenum").val('');
223     }
224 }
225
226 // drawLeafsTwoPage()
227 //______________________________________________________________________________
228 GnuBook.prototype.drawLeafsTwoPage = function() {
229     //alert('drawing two leafs!');
230
231     var scrollTop = $('#GBcontainer').attr('scrollTop');
232     var scrollBottom = scrollTop + $('#GBcontainer').height();
233     var leafNum = this.currentLeafL;
234     var height  = this.getPageHeight(leafNum); 
235     var width   = this.getPageWidth(leafNum);
236     var handSide= this.getPageSide(leafNum);
237
238     var leafEdgeWidthL = parseInt( (leafNum/this.numLeafs)*this.twoPageEdgeW );
239     var leafEdgeWidthR = this.twoPageEdgeW - leafEdgeWidthL;
240     var divWidth = this.twoPageW*2+20 + this.twoPageEdgeW;
241     var divLeft = ($('#GBcontainer').width() - divWidth) >> 1;
242     //console.log(leafEdgeWidthL);
243
244     var middle = ($('#GBcontainer').width() >> 1);            
245     var left = middle - this.twoPageW;
246     var top  = ($('#GBcontainer').height() - this.twoPageH) >> 1;                
247
248     var scaledW = parseInt(this.twoPageH*width/height);
249     left = 10+leafEdgeWidthL;
250     //var right = left+scaledW;
251     var right = $(this.twoPageDiv).width()-11-$(this.leafEdgeL).width()-scaledW;
252
253     var gutter = middle+parseInt((2*this.currentLeafL - this.numLeafs)*this.twoPageEdgeW/this.numLeafs/2);
254     
255     
256     this.prefetchImg(leafNum);
257     $(this.prefetchedImgs[leafNum]).css({
258         position: 'absolute',
259         /*right:   gutter+'px',*/
260         left: gutter-scaledW+'px',
261         top:    top+'px',
262         backgroundColor: 'rgb(234, 226, 205)',
263         height: this.twoPageH +'px',
264         width:  scaledW + 'px',
265         borderRight: '1px solid black',
266         zIndex: 2
267     }).appendTo('#GBcontainer');
268     //$('#GBcontainer').append(this.prefetchedImgs[leafNum]);
269
270
271     var leafNum = this.currentLeafR;
272     var height  = this.getPageHeight(leafNum); 
273     var width   = this.getPageWidth(leafNum);
274     //    var left = ($('#GBcontainer').width() >> 1);
275     left += scaledW;
276
277     var scaledW = this.twoPageH*width/height;
278     this.prefetchImg(leafNum);
279     $(this.prefetchedImgs[leafNum]).css({
280         position: 'absolute',
281         left:   gutter+'px',
282         top:    top+'px',
283         backgroundColor: 'rgb(234, 226, 205)',
284         height: this.twoPageH + 'px',
285         width:  scaledW + 'px',
286         borderLeft: '1px solid black',
287         zIndex: 2
288     }).appendTo('#GBcontainer');
289     //$('#GBcontainer').append(this.prefetchedImgs[leafNum]);
290         
291
292     this.displayedLeafs = [this.currentLeafL, this.currentLeafR];
293     this.setClickHandlers();
294
295     this.updatePageNumBox2UP();
296 }
297
298 // updatePageNumBox2UP
299 //______________________________________________________________________________
300 GnuBook.prototype.updatePageNumBox2UP = function() {
301     if (null != this.getPageNum(this.currentLeafL))  {
302         $("#GBpagenum").val(this.getPageNum(this.currentLeafL));
303     } else {
304         $("#GBpagenum").val('');
305     }
306     window.location.hash = this.currentLeafL; 
307 }
308
309 // loadLeafs()
310 //______________________________________________________________________________
311 GnuBook.prototype.loadLeafs = function() {
312
313
314     var self = this;
315     if (null == this.timer) {
316         this.timer=setTimeout(function(){self.drawLeafs()},250);
317     } else {
318         clearTimeout(this.timer);
319         this.timer=setTimeout(function(){self.drawLeafs()},250);    
320     }
321 }
322
323
324 // zoom1up()
325 //______________________________________________________________________________
326 GnuBook.prototype.zoom1up = function(dir) {
327     if (2 == this.mode) {     //can only zoom in 1-page mode
328         this.switchMode(1);
329         return;
330     }
331     
332     if (1 == dir) {
333         if (this.reduce <= 0.5) return;
334         this.reduce*=0.5;           //zoom in
335     } else {
336         if (this.reduce >= 8) return;
337         this.reduce*=2;             //zoom out
338     }
339     
340     this.resizePageView();
341
342     $('#GBpageview').empty()
343     this.displayedLeafs = [];
344     this.loadLeafs();
345     
346     $('#GBzoom').text(100/this.reduce);
347 }
348
349
350 // resizePageView()
351 //______________________________________________________________________________
352 GnuBook.prototype.resizePageView = function() {
353     var i;
354     var viewHeight = 0;
355     //var viewWidth  = $('#GBcontainer').width(); //includes scrollBar
356     var viewWidth  = $('#GBcontainer').attr('clientWidth');   
357
358     var oldScrollTop  = $('#GBcontainer').attr('scrollTop');
359     var oldViewHeight = $('#GBpageview').height();
360     if (0 != oldViewHeight) {
361         var scrollRatio = oldScrollTop / oldViewHeight;
362     } else {
363         var scrollRatio = 0;
364     }
365     
366     for (i=0; i<this.numLeafs; i++) {
367         viewHeight += parseInt(this.getPageHeight(i)/this.reduce) + this.padding; 
368         var width = parseInt(this.getPageWidth(i)/this.reduce);
369         if (width>viewWidth) viewWidth=width;
370     }
371     $('#GBpageview').height(viewHeight);
372     $('#GBpageview').width(viewWidth);    
373
374     $('#GBcontainer').attr('scrollTop', Math.floor(scrollRatio*viewHeight));
375     
376     this.centerPageView();
377     this.loadLeafs();
378     
379 }
380
381 // centerPageView()
382 //______________________________________________________________________________
383 GnuBook.prototype.centerPageView = function() {
384
385     var scrollWidth  = $('#GBcontainer').attr('scrollWidth');
386     var clientWidth  =  $('#GBcontainer').attr('clientWidth');
387     //console.log('sW='+scrollWidth+' cW='+clientWidth);
388     if (scrollWidth > clientWidth) {
389         $('#GBcontainer').attr('scrollLeft', (scrollWidth-clientWidth)/2);
390     }
391
392 }
393
394 // jumpToPage()
395 //______________________________________________________________________________
396 GnuBook.prototype.jumpToPage = function(pageNum) {
397     //if (2 == this.mode) return;
398     
399     var i;
400     var foundPage = false;
401     var foundLeaf = null;
402     for (i=0; i<this.numLeafs; i++) {
403         if (this.getPageNum(i) == pageNum) {
404             foundPage = true;
405             foundLeaf = i;
406             break;
407         }
408     }
409     
410     if (foundPage) {
411         var leafTop = 0;
412         var h;
413         this.jumpToIndex(foundLeaf);
414         $('#GBcontainer').attr('scrollTop', leafTop);
415     } else {
416         alert('Page not found. This book might not have pageNumbers in scandata.');
417     }
418 }
419
420 // jumpToIndex()
421 //______________________________________________________________________________
422 GnuBook.prototype.jumpToIndex = function(index) {
423     if (2 == this.mode) {
424         this.autoStop();
425         if (index<this.currentLeafL) {
426             if ('L' == this.getPageSide(index)) {
427                 this.flipBackToIndex(index);
428             } else {
429                 this.flipBackToIndex(index-1);
430             }
431         } else if (index>this.currentLeafR) {
432             if ('R' == this.getPageSide(index)) {
433                 this.flipFwdToIndex(index);
434             } else {
435                 this.flipFwdToIndex(index+1);
436             }        
437         }
438
439     } else {        
440         var i;
441         var leafTop = 0;
442         var h;
443         for (i=0; i<index; i++) {
444             h = parseInt(this.getPageHeight(i)/this.reduce); 
445             leafTop += h + this.padding;
446         }
447         //$('#GBcontainer').attr('scrollTop', leafTop);
448         $('#GBcontainer').animate({scrollTop: leafTop}, 'fast');    
449     }
450 }
451
452
453
454 // switchMode()
455 //______________________________________________________________________________
456 GnuBook.prototype.switchMode = function(mode) {
457     if (mode == this.mode) return;
458
459     this.autoStop();
460     this.removeSearchHilites();
461
462     this.mode = mode;
463     if (1 == mode) {
464         this.prepareOnePageView();
465     } else {
466         this.prepareTwoPageView();
467     }
468
469 }
470
471 //prepareOnePageView()
472 //______________________________________________________________________________
473 GnuBook.prototype.prepareOnePageView = function() {
474
475     var startLeaf = this.displayedLeafs[0];
476     
477     $('#GBcontainer').empty();
478     $('#GBcontainer').css({
479         overflowY: 'scroll',
480         overflowX: 'auto'
481     });
482     
483     $("#GBcontainer").append("<div id='GBpageview'></div>");
484     this.resizePageView();
485     this.jumpToIndex(startLeaf);
486     this.displayedLeafs = [];    
487     this.drawLeafsOnePage();
488     $('#GBzoom').text(100/this.reduce);    
489 }
490
491 // prepareTwoPageView()
492 //______________________________________________________________________________
493 GnuBook.prototype.prepareTwoPageView = function() {
494     $('#GBcontainer').empty();
495
496     var firstLeaf = this.displayedLeafs[0];
497     if ('R' == this.getPageSide(firstLeaf)) {
498         if (0 == firstLeaf) {
499             firstLeaf++;
500         } else {
501             firstLeaf--;
502         }
503     }
504
505     this.currentLeafL = null;
506     this.currentLeafR = null;
507     this.pruneUnusedImgs();
508     
509     this.currentLeafL = firstLeaf;
510     this.currentLeafR = firstLeaf + 1;
511     
512     this.calculateSpreadSize(); //sets this.twoPageW, twoPageH, and twoPageRatio
513
514     var middle = ($('#GBcontainer').width() >> 1);
515     var gutter = middle+parseInt((2*this.currentLeafL - this.numLeafs)*this.twoPageEdgeW/this.numLeafs/2);
516     var scaledWL = this.getPageWidth2UP(this.currentLeafL);
517     var scaledWR = this.getPageWidth2UP(this.currentLeafR);
518     var leafEdgeWidthL = parseInt( (firstLeaf/this.numLeafs)*this.twoPageEdgeW );
519     var leafEdgeWidthR = this.twoPageEdgeW - leafEdgeWidthL;
520
521     //console.log('idealWidth='+idealWidth+' idealHeight='+idealHeight);
522     //var divWidth = this.twoPageW*2+20 + this.twoPageEdgeW;
523     var divWidth = scaledWL + scaledWR + 20 + this.twoPageEdgeW;
524     var divHeight = this.twoPageH+20;
525     //var divLeft = ($('#GBcontainer').width() - divWidth) >> 1;
526     var divLeft = gutter-scaledWL-leafEdgeWidthL-10;
527     var divTop = ($('#GBcontainer').height() - divHeight) >> 1;
528     //console.log('divWidth='+divWidth+' divHeight='+divHeight+ ' divLeft='+divLeft+' divTop='+divTop);
529
530     this.twoPageDiv = document.createElement('div');
531     $(this.twoPageDiv).attr('id', 'book_div_1').css({
532         border: '1px solid rgb(68, 25, 17)',
533         width:  divWidth + 'px',
534         height: divHeight+'px',
535         visibility: 'visible',
536         position: 'absolute',
537         backgroundColor: 'rgb(136, 51, 34)',
538         left: divLeft + 'px',
539         top: divTop+'px',
540         MozBorderRadiusTopleft: '7px',
541         MozBorderRadiusTopright: '7px',
542         MozBorderRadiusBottomright: '7px',
543         MozBorderRadiusBottomleft: '7px'
544     }).appendTo('#GBcontainer');
545     //$('#GBcontainer').append('<div id="book_div_1" style="border: 1px solid rgb(68, 25, 17); width: ' + divWidth + 'px; height: '+divHeight+'px; visibility: visible; position: absolute; background-color: rgb(136, 51, 34); left: ' + divLeft + 'px; top: '+divTop+'px; -moz-border-radius-topleft: 7px; -moz-border-radius-topright: 7px; -moz-border-radius-bottomright: 7px; -moz-border-radius-bottomleft: 7px;"/>');
546
547
548     var height  = this.getPageHeight(this.currentLeafR); 
549     var width   = this.getPageWidth(this.currentLeafR);    
550     var scaledW = this.twoPageH*width/height;
551     
552     this.leafEdgeR = document.createElement('div');
553     $(this.leafEdgeR).css({
554         borderStyle: 'solid solid solid none',
555         borderColor: 'rgb(51, 51, 34)',
556         borderWidth: '1px 1px 1px 0px',
557         background: 'transparent url(http://www.us.archive.org/GnuBook/images/right-edges.png) repeat scroll 0% 0%',
558         width: leafEdgeWidthR + 'px',
559         height: this.twoPageH-1 + 'px',
560         /*right: '10px',*/
561         left: gutter+scaledW+'px',
562         top: divTop+10+'px',
563         position: 'absolute'
564     }).appendTo('#GBcontainer');
565     
566     this.leafEdgeL = document.createElement('div');
567     $(this.leafEdgeL).css({
568         borderStyle: 'solid none solid solid',
569         borderColor: 'rgb(51, 51, 34)',
570         borderWidth: '1px 0px 1px 1px',
571         background: 'transparent url(http://www.us.archive.org/GnuBook/images/left-edges.png) repeat scroll 0% 0%',
572         width: leafEdgeWidthL + 'px',
573         height: this.twoPageH-1 + 'px',
574         left: divLeft+10+'px',
575         top: divTop+10+'px',    
576         position: 'absolute'
577     }).appendTo('#GBcontainer');
578
579
580
581     divWidth = 30;
582     divHeight = this.twoPageH+20;
583     divLeft = ($('#GBcontainer').width() - divWidth) >> 1;
584     divTop = ($('#GBcontainer').height() - divHeight) >> 1;
585
586     div = document.createElement('div');
587     $(div).attr('id', 'book_div_2').css({
588         border:          '1px solid rgb(68, 25, 17)',
589         width:           divWidth+'px',
590         height:          divHeight+'px',
591         position:        'absolute',
592         backgroundColor: 'rgb(68, 25, 17)',
593         left:            divLeft+'px',
594         top:             divTop+'px'
595     }).appendTo('#GBcontainer');
596     //$('#GBcontainer').append('<div id="book_div_2" style="border: 1px solid rgb(68, 25, 17); width: '+divWidth+'px; height: '+divHeight+'px; visibility: visible; position: absolute; background-color: rgb(68, 25, 17); left: '+divLeft+'px; top: '+divTop+'px;"/>');
597
598     divWidth = this.twoPageW*2;
599     divHeight = this.twoPageH;
600     divLeft = ($('#GBcontainer').width() - divWidth) >> 1;
601     divTop = ($('#GBcontainer').height() - divHeight) >> 1;
602
603
604     this.prepareTwoPagePopUp();
605
606     this.displayedLeafs = [];
607     //this.leafsToDisplay=[firstLeaf, firstLeaf+1];
608     //console.log('leafsToDisplay: ' + this.leafsToDisplay[0] + ' ' + this.leafsToDisplay[1]);
609     this.drawLeafsTwoPage();
610     this.updateSearchHilites2UP();
611     
612     this.prefetch();
613     $('#GBzoom').text((100*this.twoPageH/this.getPageHeight(firstLeaf)).toString().substr(0,4));
614 }
615
616 // prepareTwoPagePopUp()
617 //______________________________________________________________________________
618 GnuBook.prototype.prepareTwoPagePopUp = function() {
619     this.twoPagePopUp = document.createElement('div');
620     $(this.twoPagePopUp).css({
621         border: '1px solid black',
622         padding: '2px 6px',
623         position: 'absolute',
624         fontFamily: 'sans-serif',
625         fontSize: '14px',
626         zIndex: '1000',
627         backgroundColor: 'rgb(255, 255, 238)',
628         opacity: 0.85
629     }).appendTo('#GBcontainer');
630     $(this.twoPagePopUp).hide();
631     
632     $(this.leafEdgeL).add(this.leafEdgeR).bind('mouseenter', this, function(e) {
633         $(e.data.twoPagePopUp).show();
634     });
635
636     $(this.leafEdgeL).add(this.leafEdgeR).bind('mouseleave', this, function(e) {
637         $(e.data.twoPagePopUp).hide();
638     });
639
640     $(this.leafEdgeL).bind('click', this, function(e) { 
641         e.data.autoStop();
642         var jumpIndex = e.data.currentLeafL - ($(e.data.leafEdgeL).offset().left + $(e.data.leafEdgeL).width() - e.pageX) * 10;
643         jumpIndex = Math.max(jumpIndex, 0);
644         e.data.flipBackToIndex(jumpIndex);
645     
646     });
647
648     $(this.leafEdgeR).bind('click', this, function(e) { 
649         e.data.autoStop();
650         var jumpIndex = e.data.currentLeafR + (e.pageX - $(e.data.leafEdgeR).offset().left) * 10;
651         jumpIndex = Math.max(jumpIndex, 0);
652         e.data.flipFwdToIndex(jumpIndex);
653     
654     });
655
656     $(this.leafEdgeR).bind('mousemove', this, function(e) {
657
658         var jumpLeaf = e.data.currentLeafR + (e.pageX - $(e.data.leafEdgeR).offset().left) * 10;
659         jumpLeaf = Math.min(jumpLeaf, e.data.numLeafs-1);        
660         $(e.data.twoPagePopUp).text('View Leaf '+jumpLeaf);
661         
662         $(e.data.twoPagePopUp).css({
663             left: e.pageX +5+ 'px',
664             top: e.pageY-$('#GBcontainer').offset().top+ 'px'
665         });
666     });
667
668     $(this.leafEdgeL).bind('mousemove', this, function(e) {
669         var jumpLeaf = e.data.currentLeafL - ($(e.data.leafEdgeL).offset().left + $(e.data.leafEdgeL).width() - e.pageX) * 10;
670         jumpLeaf = Math.max(jumpLeaf, 0);
671         $(e.data.twoPagePopUp).text('View Leaf '+jumpLeaf);
672         
673         $(e.data.twoPagePopUp).css({
674             left: e.pageX - $(e.data.twoPagePopUp).width() - 30 + 'px',
675             top: e.pageY-$('#GBcontainer').offset().top+ 'px'
676         });
677     });
678 }
679
680 // calculateSpreadSize()
681 //______________________________________________________________________________
682 // Calculates 2-page spread dimensions based on this.currentLeafL and
683 // this.currentLeafR
684 // This function sets this.twoPageH, twoPageW, and twoPageRatio
685
686 GnuBook.prototype.calculateSpreadSize = function() {
687     var firstLeaf  = this.currentLeafL;
688     var secondLeaf = this.currentLeafR;
689     //console.log('first page is ' + firstLeaf);
690
691     var canon5Dratio = 1.5;
692     
693     var firstLeafRatio  = this.getPageHeight(firstLeaf) / this.getPageWidth(firstLeaf);
694     var secondLeafRatio = this.getPageHeight(secondLeaf) / this.getPageWidth(secondLeaf);
695     //console.log('firstLeafRatio = ' + firstLeafRatio + ' secondLeafRatio = ' + secondLeafRatio);
696
697     var ratio;
698     if (Math.abs(firstLeafRatio - canon5Dratio) < Math.abs(secondLeafRatio - canon5Dratio)) {
699         ratio = firstLeafRatio;
700         //console.log('using firstLeafRatio ' + ratio);
701     } else {
702         ratio = secondLeafRatio;
703         //console.log('using secondLeafRatio ' + ratio);
704     }
705
706     var totalLeafEdgeWidth = parseInt(this.numLeafs * 0.1);
707     var maxLeafEdgeWidth   = parseInt($('#GBcontainer').width() * 0.1);
708     totalLeafEdgeWidth     = Math.min(totalLeafEdgeWidth, maxLeafEdgeWidth);
709     
710     $('#GBcontainer').css('overflow', 'hidden');
711
712     var idealWidth  = ($('#GBcontainer').width() - 30 - totalLeafEdgeWidth)>>1;
713     var idealHeight = $('#GBcontainer').height() - 30;
714     //console.log('init idealWidth='+idealWidth+' idealHeight='+idealHeight + ' ratio='+ratio);
715
716     if (idealHeight/ratio <= idealWidth) {
717         //use height
718         idealWidth = parseInt(idealHeight/ratio);
719     } else {
720         //use width
721         idealHeight = parseInt(idealWidth*ratio);
722     }
723
724     this.twoPageH     = idealHeight;
725     this.twoPageW     = idealWidth;
726     this.twoPageRatio = ratio;
727     this.twoPageEdgeW = totalLeafEdgeWidth;    
728
729 }
730
731 // next()
732 //______________________________________________________________________________
733 GnuBook.prototype.next = function() {
734     if (2 == this.mode) {
735         this.autoStop();
736         this.flipFwdToIndex(null);
737     } else {
738         if (this.firstIndex <= (this.numLeafs - 2)) {
739             this.jumpToIndex(this.firstIndex+1);
740         }
741     }
742 }
743
744 // prev()
745 //______________________________________________________________________________
746 GnuBook.prototype.prev = function() {
747     if (2 == this.mode) {
748         this.autoStop();
749         this.flipBackToIndex(null);
750     } else {
751         if (this.firstIndex >= 1) {
752             this.jumpToIndex(this.firstIndex-1);
753         }    
754     }
755 }
756
757
758 // flipBackToIndex()
759 //______________________________________________________________________________
760 // to flip back one spread, pass index=null
761 GnuBook.prototype.flipBackToIndex = function(index) {
762     if (1 == this.mode) return;
763
764     var leftIndex = this.currentLeafL;
765     if (leftIndex <= 2) return;
766     if (this.animating) return;
767
768     if (null != this.leafEdgeTmp) {
769         alert('error: leafEdgeTmp should be null!');
770         return;
771     }
772     
773     if (null == index) {
774         index = leftIndex-2;
775     }
776     if (index<0) return;
777
778     if ('L' !=  this.getPageSide(index)) {
779         alert('img with index ' + index + ' is not a left-hand page');
780         return;
781     }
782
783     this.animating = true;
784     
785     var prevL = index;
786     var prevR = index+1;
787
788     var gutter= this.prepareFlipBack(prevL, prevR);
789
790     var leftLeaf = this.currentLeafL;
791
792     var oldLeafEdgeWidthL = parseInt( (this.currentLeafL/this.numLeafs)*this.twoPageEdgeW );
793     var newLeafEdgeWidthL = parseInt( (index            /this.numLeafs)*this.twoPageEdgeW );    
794     var leafEdgeTmpW = oldLeafEdgeWidthL - newLeafEdgeWidthL;
795
796     var scaledWL = this.getPageWidth2UP(prevL);
797
798     var top  = ($('#GBcontainer').height() - this.twoPageH) >> 1;                
799
800     this.leafEdgeTmp = document.createElement('div');
801     $(this.leafEdgeTmp).css({
802         borderStyle: 'solid none solid solid',
803         borderColor: 'rgb(51, 51, 34)',
804         borderWidth: '1px 0px 1px 1px',
805         background: 'transparent url(http://www.us.archive.org/GnuBook/images/left-edges.png) repeat scroll 0% 0%',
806         width: leafEdgeTmpW + 'px',
807         height: this.twoPageH-1 + 'px',
808         left: gutter-scaledWL+10+newLeafEdgeWidthL+'px',
809         top: top+'px',    
810         position: 'absolute',
811         zIndex:1000
812     }).appendTo('#GBcontainer');
813     
814     //$(this.leafEdgeL).css('width', newLeafEdgeWidthL+'px');
815     $(this.leafEdgeL).css({
816         width: newLeafEdgeWidthL+'px', 
817         left: gutter-scaledWL-newLeafEdgeWidthL+'px'
818     });   
819
820     var left = $(this.prefetchedImgs[leftLeaf]).offset().left;
821     var right = $('#GBcontainer').width()-left-$(this.prefetchedImgs[leftLeaf]).width()+$('#GBcontainer').offset().left-2+'px';
822     $(this.prefetchedImgs[leftLeaf]).css({
823         right: right,
824         left: null
825     });
826
827      left = $(this.prefetchedImgs[leftLeaf]).offset().left - $('#book_div_1').offset().left;
828      right = left+$(this.prefetchedImgs[leftLeaf]).width()+'px';
829
830     $(this.leafEdgeTmp).animate({left: gutter}, this.flipSpeed, 'easeInSine');    
831     //$(this.prefetchedImgs[leftLeaf]).animate({width: '0px'}, 'slow', 'easeInSine');
832
833     var scaledWR = this.getPageWidth2UP(prevR);
834     
835     var self = this;
836
837     this.removeSearchHilites();
838
839     $(this.prefetchedImgs[leftLeaf]).animate({width: '0px'}, self.flipSpeed, 'easeInSine', function() {
840         $(self.leafEdgeTmp).animate({left: gutter+scaledWR+'px'}, self.flipSpeed, 'easeOutSine');    
841         $(self.prefetchedImgs[prevR]).animate({width: scaledWR+'px'}, self.flipSpeed, 'easeOutSine', function() {
842             $(self.prefetchedImgs[prevL]).css('zIndex', 2);
843
844             $(self.leafEdgeR).css({
845                 width: self.twoPageEdgeW-newLeafEdgeWidthL+'px',
846                 left:  gutter+scaledWR+'px'
847             });
848             
849             $(self.twoPageDiv).css({
850                 width: scaledWL+scaledWR+self.twoPageEdgeW+20+'px',
851                 left: gutter-scaledWL-newLeafEdgeWidthL-10+'px'
852             });
853             
854             $(self.leafEdgeTmp).remove();
855             self.leafEdgeTmp = null;
856             
857             self.currentLeafL = prevL;
858             self.currentLeafR = prevR;
859             self.displayedLeafs = [prevL, prevR];
860             self.setClickHandlers();
861             self.pruneUnusedImgs();
862             self.prefetch();
863             self.animating = false;
864             
865             self.updateSearchHilites2UP();
866             self.updatePageNumBox2UP();
867             //$('#GBzoom').text((self.twoPageH/self.getPageHeight(prevL)).toString().substr(0,4));            
868         });
869     });        
870     
871 }
872
873 // flipFwdToIndex()
874 //______________________________________________________________________________
875 // to flip forward one spread, pass index=null
876 GnuBook.prototype.flipFwdToIndex = function(index) {
877     var rightLeaf = this.currentLeafR;
878     if (rightLeaf >= this.numLeafs-3) return;
879
880     if (this.animating) return;
881
882     if (null != this.leafEdgeTmp) {
883         alert('error: leafEdgeTmp should be null!');
884         return;
885     }
886
887     
888     if (null == index) {
889         index = rightLeaf+2;
890     }
891     if (index>=this.numLeafs-3) return;
892
893     if ('R' !=  this.getPageSide(index)) {
894         alert('img with index ' + index + ' is not a right-hand page');
895         return;
896     }
897
898     this.animating = true;
899
900     var nextL = index-1;
901     var nextR = index;
902
903     var gutter= this.prepareFlipFwd(nextL, nextR);
904
905     var oldLeafEdgeWidthL = parseInt( (this.currentLeafL/this.numLeafs)*this.twoPageEdgeW );
906     var oldLeafEdgeWidthR = this.twoPageEdgeW-oldLeafEdgeWidthL;
907     var newLeafEdgeWidthL = parseInt( (nextL            /this.numLeafs)*this.twoPageEdgeW );    
908     var newLeafEdgeWidthR = this.twoPageEdgeW-newLeafEdgeWidthL;
909
910     var leafEdgeTmpW = oldLeafEdgeWidthR - newLeafEdgeWidthR;
911
912     var top  = ($('#GBcontainer').height() - this.twoPageH) >> 1;                
913
914     var height  = this.getPageHeight(rightLeaf); 
915     var width   = this.getPageWidth(rightLeaf);    
916     var scaledW = this.twoPageH*width/height;
917
918     var middle     = ($('#GBcontainer').width() >> 1);
919     var currGutter = middle+parseInt((2*this.currentLeafL - this.numLeafs)*this.twoPageEdgeW/this.numLeafs/2);    
920
921     this.leafEdgeTmp = document.createElement('div');
922     $(this.leafEdgeTmp).css({
923         borderStyle: 'solid none solid solid',
924         borderColor: 'rgb(51, 51, 34)',
925         borderWidth: '1px 0px 1px 1px',
926         background: 'transparent url(http://www.us.archive.org/GnuBook/images/left-edges.png) repeat scroll 0% 0%',
927         width: leafEdgeTmpW + 'px',
928         height: this.twoPageH-1 + 'px',
929         left: currGutter+scaledW+'px',
930         top: top+'px',    
931         position: 'absolute',
932         zIndex:1000
933     }).appendTo('#GBcontainer');
934
935     var scaledWR = this.getPageWidth2UP(nextR);
936     $(this.leafEdgeR).css({width: newLeafEdgeWidthR+'px', left: gutter+scaledWR+'px' });
937
938     var scaledWL = this.getPageWidth2UP(nextL);
939     
940     var self = this;
941
942     var speed = this.flipSpeed;
943
944     this.removeSearchHilites();
945     
946     $(this.leafEdgeTmp).animate({left: gutter}, speed, 'easeInSine');    
947     $(this.prefetchedImgs[rightLeaf]).animate({width: '0px'}, speed, 'easeInSine', function() {
948         $(self.leafEdgeTmp).animate({left: gutter-scaledWL-leafEdgeTmpW+'px'}, speed, 'easeOutSine');    
949         $(self.prefetchedImgs[nextL]).animate({width: scaledWL+'px'}, speed, 'easeOutSine', function() {
950             $(self.prefetchedImgs[nextR]).css('zIndex', 2);
951
952             $(self.leafEdgeL).css({
953                 width: newLeafEdgeWidthL+'px', 
954                 left: gutter-scaledWL-newLeafEdgeWidthL+'px'
955             });
956             
957             $(self.twoPageDiv).css({
958                 width: scaledWL+scaledWR+self.twoPageEdgeW+20+'px',
959                 left: gutter-scaledWL-newLeafEdgeWidthL-10+'px'
960             });
961             
962             $(self.leafEdgeTmp).remove();
963             self.leafEdgeTmp = null;
964             
965             self.currentLeafL = nextL;
966             self.currentLeafR = nextR;
967             self.displayedLeafs = [nextL, nextR];
968             self.setClickHandlers();            
969             self.pruneUnusedImgs();
970             self.prefetch();
971             self.animating = false;
972
973             self.updateSearchHilites2UP();
974             self.updatePageNumBox2UP();
975             //$('#GBzoom').text((self.twoPageH/self.getPageHeight(nextL)).toString().substr(0,4));
976         });
977     });
978     
979 }
980
981 // setClickHandlers
982 //______________________________________________________________________________
983 GnuBook.prototype.setClickHandlers = function() {
984     var self = this;
985     $(this.prefetchedImgs[this.currentLeafL]).click(function() {
986         //self.prevPage();
987         self.autoStop();
988         self.flipBackToIndex(null);
989     });
990     $(this.prefetchedImgs[this.currentLeafR]).click(function() {
991         //self.nextPage();'
992         self.autoStop();
993         self.flipFwdToIndex(null);        
994     });
995 }
996
997 // prefetchImg()
998 //______________________________________________________________________________
999 GnuBook.prototype.prefetchImg = function(leafNum) {
1000     if (undefined == this.prefetchedImgs[leafNum]) {        
1001         var img = document.createElement("img");
1002         img.src = this.getPageURI(leafNum);
1003         this.prefetchedImgs[leafNum] = img;
1004     }
1005 }
1006
1007
1008 // prepareFlipBack()
1009 //______________________________________________________________________________
1010 GnuBook.prototype.prepareFlipBack = function(prevL, prevR) {
1011
1012     this.prefetchImg(prevL);
1013     this.prefetchImg(prevR);
1014     
1015     var height  = this.getPageHeight(prevL); 
1016     var width   = this.getPageWidth(prevL);    
1017     var middle = ($('#GBcontainer').width() >> 1);
1018     var top  = ($('#GBcontainer').height() - this.twoPageH) >> 1;                
1019     var scaledW = this.twoPageH*width/height;
1020
1021     var gutter = middle+parseInt((2*prevL - this.numLeafs)*this.twoPageEdgeW/this.numLeafs/2);    
1022
1023     $(this.prefetchedImgs[prevL]).css({
1024         position: 'absolute',
1025         /*right:   middle+'px',*/
1026         left: gutter-scaledW+'px',
1027         top:    top+'px',
1028         backgroundColor: 'rgb(234, 226, 205)',
1029         height: this.twoPageH,
1030         width:  scaledW+'px',
1031         borderRight: '1px solid black',
1032         zIndex: 1
1033     });
1034
1035     $('#GBcontainer').append(this.prefetchedImgs[prevL]);
1036
1037     $(this.prefetchedImgs[prevR]).css({
1038         position: 'absolute',
1039         left:   gutter+'px',
1040         top:    top+'px',
1041         backgroundColor: 'rgb(234, 226, 205)',
1042         height: this.twoPageH,
1043         width:  '0px',
1044         borderLeft: '1px solid black',
1045         zIndex: 2
1046     });
1047
1048     $('#GBcontainer').append(this.prefetchedImgs[prevR]);
1049
1050
1051     return gutter;
1052             
1053 }
1054
1055 // prepareFlipFwd()
1056 //______________________________________________________________________________
1057 GnuBook.prototype.prepareFlipFwd = function(nextL, nextR) {
1058
1059     this.prefetchImg(nextL);
1060     this.prefetchImg(nextR);
1061
1062     var height  = this.getPageHeight(nextR); 
1063     var width   = this.getPageWidth(nextR);    
1064     var middle = ($('#GBcontainer').width() >> 1);
1065     var top  = ($('#GBcontainer').height() - this.twoPageH) >> 1;                
1066     var scaledW = this.twoPageH*width/height;
1067
1068     var gutter = middle+parseInt((2*nextL - this.numLeafs)*this.twoPageEdgeW/this.numLeafs/2);    
1069     
1070     $(this.prefetchedImgs[nextR]).css({
1071         position: 'absolute',
1072         left:   gutter+'px',
1073         top:    top+'px',
1074         backgroundColor: 'rgb(234, 226, 205)',
1075         height: this.twoPageH,
1076         width:  scaledW+'px',
1077         borderLeft: '1px solid black',
1078         zIndex: 1
1079     });
1080
1081     $('#GBcontainer').append(this.prefetchedImgs[nextR]);
1082
1083     height  = this.getPageHeight(nextL); 
1084     width   = this.getPageWidth(nextL);      
1085     scaledW = this.twoPageH*width/height;
1086
1087     $(this.prefetchedImgs[nextL]).css({
1088         position: 'absolute',
1089         right:   $('#GBcontainer').width()-gutter+'px',
1090         top:    top+'px',
1091         backgroundColor: 'rgb(234, 226, 205)',
1092         height: this.twoPageH,
1093         width:  0+'px',
1094         borderRight: '1px solid black',
1095         zIndex: 2
1096     });
1097
1098     $('#GBcontainer').append(this.prefetchedImgs[nextL]);    
1099
1100     return gutter;
1101             
1102 }
1103
1104 // getNextLeafs()
1105 //______________________________________________________________________________
1106 GnuBook.prototype.getNextLeafs = function(o) {
1107     //TODO: we might have two left or two right leafs in a row (damaged book)
1108     //For now, assume that leafs are contiguous.
1109     
1110     //return [this.currentLeafL+2, this.currentLeafL+3];
1111     o.L = this.currentLeafL+2;
1112     o.R = this.currentLeafL+3;
1113 }
1114
1115 // getprevLeafs()
1116 //______________________________________________________________________________
1117 GnuBook.prototype.getPrevLeafs = function(o) {
1118     //TODO: we might have two left or two right leafs in a row (damaged book)
1119     //For now, assume that leafs are contiguous.
1120     
1121     //return [this.currentLeafL-2, this.currentLeafL-1];
1122     o.L = this.currentLeafL-2;
1123     o.R = this.currentLeafL-1;
1124 }
1125
1126 // pruneUnusedImgs()
1127 //______________________________________________________________________________
1128 GnuBook.prototype.pruneUnusedImgs = function() {
1129     //console.log('current: ' + this.currentLeafL + ' ' + this.currentLeafR);
1130     for (var key in this.prefetchedImgs) {
1131         //console.log('key is ' + key);
1132         if ((key != this.currentLeafL) && (key != this.currentLeafR)) {
1133             //console.log('removing key '+ key);
1134             $(this.prefetchedImgs[key]).remove();
1135         }
1136         if ((key < this.currentLeafL-4) || (key > this.currentLeafR+4)) {
1137             //console.log('deleting key '+ key);
1138             delete this.prefetchedImgs[key];
1139         }
1140     }
1141 }
1142
1143 // prefetch()
1144 //______________________________________________________________________________
1145 GnuBook.prototype.prefetch = function() {
1146     var lim = this.currentLeafL-4;
1147     var i;
1148     lim = Math.max(lim, 0);
1149     for (i = lim; i < this.currentLeafL; i++) {
1150         this.prefetchImg(i);
1151     }
1152     
1153     if (this.numLeafs > (this.currentLeafR+1)) {
1154         lim = Math.min(this.currentLeafR+4, this.numLeafs-1);
1155         for (i=this.currentLeafR+1; i<=lim; i++) {
1156             this.prefetchImg(i);
1157         }
1158     }
1159 }
1160
1161 // getPageWidth2UP()
1162 //______________________________________________________________________________
1163 GnuBook.prototype.getPageWidth2UP = function(index) {
1164     var height  = this.getPageHeight(index); 
1165     var width   = this.getPageWidth(index);    
1166     return Math.floor(this.twoPageH*width/height);
1167 }    
1168
1169 // search()
1170 //______________________________________________________________________________
1171 GnuBook.prototype.search = function(term) {
1172     $('#GnuBookSearchScript').remove();
1173         var script  = document.createElement("script");
1174         script.setAttribute('id', 'GnuBookSearchScript');
1175         script.setAttribute("type", "text/javascript");
1176         script.setAttribute("src", 'http://'+this.server+'/GnuBook/flipbook_search_gb.php?url='+escape(this.bookPath+'/'+this.bookId+'_djvu.xml')+'&term='+term+'&format=XML&callback=gb.GBSearchCallback');
1177         document.getElementsByTagName('head')[0].appendChild(script);
1178 }
1179
1180 // GBSearchCallback()
1181 //______________________________________________________________________________
1182 GnuBook.prototype.GBSearchCallback = function(txt) {
1183     //alert(txt);
1184     if (jQuery.browser.msie) {
1185         var dom=new ActiveXObject("Microsoft.XMLDOM");
1186         dom.async="false";
1187         dom.loadXML(txt);    
1188     } else {
1189         var parser = new DOMParser();
1190         var dom = parser.parseFromString(txt, "text/xml");    
1191     }
1192     
1193     $('#GnuBookSearchResults').empty();    
1194     $('#GnuBookSearchResults').append('<ul>');
1195     
1196     for (var key in this.searchResults) {
1197         if (null != this.searchResults[key].div) {
1198             $(this.searchResults[key].div).remove();
1199         }
1200         delete this.searchResults[key];
1201     }
1202     
1203     var pages = dom.getElementsByTagName('PAGE');
1204     for (var i = 0; i < pages.length; i++){
1205         //console.log(pages[i].getAttribute('file').substr(1) +'-'+ parseInt(pages[i].getAttribute('file').substr(1), 10));
1206
1207         
1208         var re = new RegExp (/_(\d{4})/);
1209         var reMatch = re.exec(pages[i].getAttribute('file'));
1210         var leafNum = parseInt(reMatch[1], 10);
1211         //var leafNum = parseInt(pages[i].getAttribute('file').substr(1), 10);
1212         
1213         var children = pages[i].childNodes;
1214         var context = '';
1215         for (var j=0; j<children.length; j++) {
1216             //console.log(j + ' - ' + children[j].nodeName);
1217             //console.log(children[j].firstChild.nodeValue);
1218             if ('CONTEXT' == children[j].nodeName) {
1219                 context += children[j].firstChild.nodeValue;
1220             } else if ('WORD' == children[j].nodeName) {
1221                 context += '<b>'+children[j].firstChild.nodeValue+'</b>';
1222                 
1223                 var index = this.leafNumToIndex(leafNum);
1224                 if (null != index) {
1225                     //coordinates are [left, bottom, right, top, [baseline]]
1226                     //we'll skip baseline for now...
1227                     var coords = children[j].getAttribute('coords').split(',',4);
1228                     if (4 == coords.length) {
1229                         this.searchResults[index] = {'l':coords[0], 'b':coords[1], 'r':coords[2], 't':coords[3], 'div':null};
1230                     }
1231                 }
1232             }
1233         }
1234         //TODO: remove hardcoded instance name
1235         $('#GnuBookSearchResults').append('<li><b><a href="javascript:gb.jumpToIndex('+index+');">Leaf ' + leafNum + '</a></b> - ' + context+'</li>');
1236     }
1237     $('#GnuBookSearchResults').append('</ul>');
1238
1239     this.updateSearchHilites();
1240 }
1241
1242 // updateSearchHilites()
1243 //______________________________________________________________________________
1244 GnuBook.prototype.updateSearchHilites = function() {
1245     if (2 == this.mode) {
1246         this.updateSearchHilites2UP();
1247     } else {
1248         this.updateSearchHilites1UP();
1249     }
1250 }
1251
1252 // showSearchHilites1UP()
1253 //______________________________________________________________________________
1254 GnuBook.prototype.updateSearchHilites1UP = function() {
1255
1256     for (var key in this.searchResults) {
1257         
1258         if (-1 != jQuery.inArray(parseInt(key), this.displayedLeafs)) {
1259             var result = this.searchResults[key];
1260             if(null == result.div) {
1261                 result.div = document.createElement('div');
1262                 $(result.div).attr('className', 'GnuBookSearchHilite').appendTo('#pagediv'+key);
1263                 //console.log('appending ' + key);
1264             }    
1265             $(result.div).css({
1266                 width:  (result.r-result.l)/this.reduce + 'px',
1267                 height: (result.b-result.t)/this.reduce + 'px',
1268                 left:   (result.l)/this.reduce + 'px',
1269                 top:    (result.t)/this.reduce +'px'
1270             });
1271
1272         } else {
1273             //console.log(key + ' not displayed');
1274             this.searchResults[key].div=null;
1275         }
1276     }
1277 }
1278
1279 // showSearchHilites2UP()
1280 //______________________________________________________________________________
1281 GnuBook.prototype.updateSearchHilites2UP = function() {
1282
1283     var middle = ($('#GBcontainer').width() >> 1);
1284
1285     for (var key in this.searchResults) {
1286         key = parseInt(key, 10);
1287         if (-1 != jQuery.inArray(key, this.displayedLeafs)) {
1288             var result = this.searchResults[key];
1289             if(null == result.div) {
1290                 result.div = document.createElement('div');
1291                 $(result.div).attr('className', 'GnuBookSearchHilite').css('zIndex', 3).appendTo('#GBcontainer');
1292                 //console.log('appending ' + key);
1293             }
1294
1295             var height = this.getPageHeight(key);
1296             var width  = this.getPageWidth(key)
1297             var reduce = this.twoPageH/height;
1298             var scaledW = parseInt(width*reduce);
1299             
1300             var gutter = middle+parseInt((2*this.currentLeafL - this.numLeafs)*this.twoPageEdgeW/this.numLeafs/2);
1301             
1302             if ('L' == this.getPageSide(key)) {
1303                 var pageL = gutter-scaledW;
1304             } else {
1305                 var pageL = gutter;
1306             }
1307             var pageT  = ($('#GBcontainer').height() - this.twoPageH) >> 1;                
1308                         
1309             $(result.div).css({
1310                 width:  (result.r-result.l)*reduce + 'px',
1311                 height: (result.b-result.t)*reduce + 'px',
1312                 left:   pageL+(result.l)*reduce + 'px',
1313                 top:    pageT+(result.t)*reduce +'px'
1314             });
1315
1316         } else {
1317             //console.log(key + ' not displayed');
1318             if (null != this.searchResults[key].div) {
1319                 //console.log('removing ' + key);
1320                 $(this.searchResults[key].div).remove();
1321             }
1322             this.searchResults[key].div=null;
1323         }
1324     }
1325 }
1326
1327 // removeSearchHilites()
1328 //______________________________________________________________________________
1329 GnuBook.prototype.removeSearchHilites = function() {
1330     for (var key in this.searchResults) {
1331         if (null != this.searchResults[key].div) {
1332             $(this.searchResults[key].div).remove();
1333             this.searchResults[key].div=null;
1334         }        
1335     }
1336 }
1337
1338 // showEmbedCode()
1339 //______________________________________________________________________________
1340 GnuBook.prototype.showEmbedCode = function() {
1341     this.autoStop();
1342     var overlay = document.createElement("div");
1343     $(overlay).css({
1344         position: 'absolute',
1345         top:      '20px',
1346         left:     ($('#GBcontainer').width()-400)/2 + 'px',
1347         width:    '400px',
1348         padding:  "20px",
1349         border:   "3px double #999999",
1350         zIndex:   3,
1351         backgroundColor: "#fff",
1352     }).appendTo('#GnuBook');
1353
1354     htmlStr =  '<p style="text-align:center;"><b>Embed Bookreader in your blog!</b></p>';
1355     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>';
1356     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>';
1357     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>';
1358     htmlStr += '<p style="text-align:center;"><a href="" onclick="$(this.parentNode.parentNode).remove(); return false;">Close popup</a></p>';    
1359
1360     overlay.innerHTML = htmlStr;    
1361 }
1362
1363 // autoToggle()
1364 //______________________________________________________________________________
1365 GnuBook.prototype.autoToggle = function() {
1366     if (2 != this.mode) {
1367         this.switchMode(2);
1368     }
1369
1370     var self = this;
1371     if (null == this.autoTimer) {
1372         this.flipSpeed = 2000;
1373         this.flipFwdToIndex();
1374
1375         $('#autoImg').attr('src', 'http://www.us.archive.org/GnuBook/images/control_pause_blue.png');
1376         this.autoTimer=setInterval(function(){
1377             if (self.animating) {return;}
1378
1379             if (self.currentLeafR >= self.numLeafs-5) {
1380                 self.flipBackToIndex(1);
1381             } else {
1382                 self.flipFwdToIndex();
1383             }
1384         },5000);
1385     } else {
1386         this.autoStop();
1387     }
1388 }
1389
1390 // autoStop()
1391 //______________________________________________________________________________
1392 GnuBook.prototype.autoStop = function() {
1393     if (null != this.autoTimer) {
1394         clearInterval(this.autoTimer);
1395         this.flipSpeed = 'fast';
1396         $('#autoImg').attr('src', 'http://www.us.archive.org/GnuBook/images/control_play_blue.png');
1397         this.autoTimer = null;
1398     }
1399 }