Merge branch 'master' of git@github.com:openlibrary/bookreader into browserlending
[bookreader.git] / BookReaderIA / datanode / BookReaderJSIA.php
1 <?
2 /*
3 Copyright(c)2008 Internet Archive. Software license AGPL version 3.
4
5 This file is part of BookReader.
6
7     BookReader is free software: you can redistribute it and/or modify
8     it under the terms of the GNU Affero General Public License as published by
9     the Free Software Foundation, either version 3 of the License, or
10     (at your option) any later version.
11
12     BookReader is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU Affero General Public License for more details.
16
17     You should have received a copy of the GNU Affero General Public License
18     along with BookReader.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 header('Content-Type: application/javascript');
22
23 $id = $_REQUEST['id'];
24 $itemPath = $_REQUEST['itemPath'];
25 $subPrefix = $_REQUEST['subPrefix'];
26 $server = $_REQUEST['server'];
27
28 // $$$mang this code has been refactored into BookReaderMeta.inc.php for use e.g. by
29 //         BookReaderPreview.php and BookReaderImages.php.  The code below should be
30 //         taken out and replaced by calls into BookReaderMeta
31
32 // Check if we're on a dev vhost and point to JSIA in the user's public_html on the datanode
33
34 // $$$ TODO consolidate this logic
35 if (strpos($_SERVER["REQUEST_URI"], "/~mang") === 0) { // Serving out of home dir
36     $server .= ':80/~mang';
37 } else if (strpos($_SERVER["REQUEST_URI"], "/~rkumar") === 0) { // Serving out of home dir
38     $server .= ':80/~rkumar';
39 } else if (strpos($_SERVER["REQUEST_URI"], "/~testflip") === 0) { // Serving out of home dir
40     $server .= ':80/~testflip';
41 }
42
43 if (! $subPrefix) {
44     $subPrefix = $id;
45 }
46 $subItemPath = $itemPath . '/' . $subPrefix;
47
48 if ("" == $id) {
49     BRFatal("No identifier specified!");
50 }
51
52 if ("" == $itemPath) {
53     BRFatal("No itemPath specified!");
54 }
55
56 if ("" == $server) {
57     BRFatal("No server specified!");
58 }
59
60 if (!preg_match("|^/\d+/items/{$id}$|", $itemPath)) {
61     BRFatal("Bad id!");
62 }
63
64 // XXX check here that subitem is okay
65
66 $filesDataFile = "$itemPath/${id}_files.xml";
67
68 if (file_exists($filesDataFile)) {
69     $filesData = simplexml_load_file("$itemPath/${id}_files.xml");
70 } else {
71     BRfatal("File metadata not found!");
72 }
73
74 $imageStackInfo = findImageStack($subPrefix, $filesData);
75 if ($imageStackInfo['imageFormat'] == 'unknown') {
76     BRfatal('Couldn\'t find image stack');
77 }
78
79 $imageFormat = $imageStackInfo['imageFormat'];
80 $archiveFormat = $imageStackInfo['archiveFormat'];
81 $imageStackFile = $itemPath . "/" . $imageStackInfo['imageStackFile'];
82
83 if ("unknown" == $imageFormat) {
84   BRfatal("Unknown image format");
85 }
86
87 if ("unknown" == $archiveFormat) {
88   BRfatal("Unknown archive format");
89 }
90
91
92 $scanDataFile = "${subItemPath}_scandata.xml";
93 $scanDataZip  = "$itemPath/scandata.zip";
94 if (file_exists($scanDataFile)) {
95     $scanData = simplexml_load_file($scanDataFile);
96 } else if (file_exists($scanDataZip)) {
97     $cmd  = 'unzip -p ' . escapeshellarg($scanDataZip) . ' scandata.xml';
98     exec($cmd, $output, $retval);
99     if ($retval != 0) BRFatal("Could not unzip ScanData!");
100     
101     $dump = join("\n", $output);
102     $scanData = simplexml_load_string($dump);
103 } else if (file_exists("$itemPath/scandata.xml")) {
104     // For e.g. Scribe v.0 books!
105     $scanData = simplexml_load_file("$itemPath/scandata.xml");
106 } else {
107     BRFatal("ScanData file not found!");
108 }
109
110 $metaDataFile = "$itemPath/{$id}_meta.xml";
111 if (!file_exists($metaDataFile)) {
112     BRFatal("MetaData file not found!");
113 }
114
115
116 $metaData = simplexml_load_file($metaDataFile);
117
118 //$firstLeaf = $scanData->pageData->page[0]['leafNum'];
119 ?>
120
121 // Error reporting - this helps us fix errors quickly
122 function logError(description,page,line) {
123     if (typeof(archive_analytics) != 'undefined') {
124         var values = {
125             'bookreader': 'error',
126             'description': description,
127             'page': page,
128             'line': line,
129             'itemid': '<?echo $id;?>',
130             'subPrefix': '<?echo $subPrefix;?>',
131             'server': '<?echo $server;?>',
132             'bookPath': '<?echo $subItemPath;?>'
133         };
134
135         // if no referrer set '-' as referrer
136         if (document.referrer == '') {
137             values['referrer'] = '-';
138         } else {
139             values['referrer'] = document.referrer;
140         }
141         
142         if (typeof(br) != 'undefined') {
143             values['itemid'] = br.bookId;
144             values['subPrefix'] = br.subPrefix;
145             values['server'] = br.server;
146             values['bookPath'] = br.bookPath;
147         }
148         
149         var qs = archive_analytics.format_bug(values);
150
151         var error_img = new Image(100,25);
152         error_img.src = archive_analytics.img_src + "?" + qs;
153     }
154
155     return false; // allow browser error handling so user sees there was a problem
156 }
157 window.onerror=logError;
158
159 br = new BookReader();
160
161 <?
162 /* Output title leaf if marked */
163 $titleLeaf = '';
164 foreach ($scanData->pageData->page as $page) {
165     if (("Title Page" == $page->pageType) || ("Title" == $page->pageType)) {
166         $titleLeaf = "{$page['leafNum']}";
167         break;
168     }
169 }
170     
171 if ('' != $titleLeaf) {
172     printf("br.titleLeaf = %d;\n", $titleLeaf);
173 }
174 ?>
175
176 br.getPageWidth = function(index) {
177     return this.pageW[index];
178 }
179
180 br.getPageHeight = function(index) {
181     return this.pageH[index];
182 }
183
184 // Returns true if page image is available rotated
185 br.canRotatePage = function(index) {
186     return 'jp2' == this.imageFormat; // Assume single format for now
187 }
188
189 // reduce defaults to 1 (no reduction)
190 // rotate defaults to 0 (no rotation)
191 br.getPageURI = function(index, reduce, rotate) {
192     var _reduce;
193     var _rotate;
194
195     if ('undefined' == typeof(reduce)) {
196         _reduce = 1;
197     } else {
198         _reduce = reduce;
199     }
200     if ('undefined' == typeof(rotate)) {
201         _rotate = 0;
202     } else {
203         _rotate = rotate;
204     }
205     
206     var file = this._getPageFile(index);
207         
208     // $$$ add more image stack formats here
209     return 'http://'+this.server+'/BookReader/BookReaderImages.php?zip='+this.zip+'&file='+file+'&scale='+_reduce+'&rotate='+_rotate;
210 }
211
212 br._getPageFile = function(index) {
213     var leafStr = '0000';
214     var imgStr = this.leafMap[index].toString();
215     var re = new RegExp("0{"+imgStr.length+"}$");
216     
217     var insideZipPrefix = this.subPrefix.match('[^/]+$');
218     var file = insideZipPrefix + '_' + this.imageFormat + '/' + insideZipPrefix + '_' + leafStr.replace(re, imgStr) + '.' + this.imageFormat;
219     
220     return file;
221 }
222
223 br.getPageSide = function(index) {
224     //assume the book starts with a cover (right-hand leaf)
225     //we should really get handside from scandata.xml
226     
227     <? // Use special function if we should infer the page sides based off the title page index
228     if (preg_match('/goog$/', $id) && ('' != $titleLeaf)) {
229     ?>
230     // assume page side based on title pagex
231     var titleIndex = br.leafNumToIndex(br.titleLeaf);
232     // assume title page is RHS
233     var delta = titleIndex - index;
234     if (0 == (delta & 0x1)) {
235         // even delta
236         return 'R';
237     } else {
238         return 'L';
239     }
240     <?
241     }
242     ?>
243     
244     // $$$ we should get this from scandata instead of assuming the accessible
245     //     leafs are contiguous
246     if ('rl' != this.pageProgression) {
247         // If pageProgression is not set RTL we assume it is LTR
248         if (0 == (index & 0x1)) {
249             // Even-numbered page
250             return 'R';
251         } else {
252             // Odd-numbered page
253             return 'L';
254         }
255     } else {
256         // RTL
257         if (0 == (index & 0x1)) {
258             return 'L';
259         } else {
260             return 'R';
261         }
262     }
263 }
264
265 br.getPageNum = function(index) {
266     var pageNum = this.pageNums[index];
267     if (pageNum) {
268         return pageNum;
269     } else {
270         return 'n' + index;
271     }
272 }
273
274 // Single images in the Internet Archive scandata.xml metadata are (somewhat incorrectly)
275 // given a "leaf" number.  Some of these images from the scanning process should not
276 // be displayed in the BookReader (for example colour calibration cards).  Since some
277 // of the scanned images will not be displayed in the BookReader (those marked with
278 // addToAccessFormats false in the scandata.xml) leaf numbers and BookReader page
279 // indexes are generally not the same.  This function returns the BookReader page
280 // index given a scanned leaf number.
281 //
282 // This function is used, for example, to map between search results (that use the
283 // leaf numbers) and the displayed pages in the BookReader.
284 br.leafNumToIndex = function(leafNum) {
285     for (var index = 0; index < this.leafMap.length; index++) {
286         if (this.leafMap[index] == leafNum) {
287             return index;
288         }
289     }
290     
291     return null;
292 }
293
294 // This function returns the left and right indices for the user-visible
295 // spread that contains the given index.  The return values may be
296 // null if there is no facing page or the index is invalid.
297 br.getSpreadIndices = function(pindex) {
298     // $$$ we could make a separate function for the RTL case and
299     //      only bind it if necessary instead of always checking
300     // $$$ we currently assume there are no gaps
301     
302     var spreadIndices = [null, null]; 
303     if ('rl' == this.pageProgression) {
304         // Right to Left
305         if (this.getPageSide(pindex) == 'R') {
306             spreadIndices[1] = pindex;
307             spreadIndices[0] = pindex + 1;
308         } else {
309             // Given index was LHS
310             spreadIndices[0] = pindex;
311             spreadIndices[1] = pindex - 1;
312         }
313     } else {
314         // Left to right
315         if (this.getPageSide(pindex) == 'L') {
316             spreadIndices[0] = pindex;
317             spreadIndices[1] = pindex + 1;
318         } else {
319             // Given index was RHS
320             spreadIndices[1] = pindex;
321             spreadIndices[0] = pindex - 1;
322         }
323     }
324     
325     //console.log("   index %d mapped to spread %d,%d", pindex, spreadIndices[0], spreadIndices[1]);
326     
327     return spreadIndices;
328 }
329
330 // Remove the page number assertions for all but the highest index page with
331 // a given assertion.  Ensures there is only a single page "{pagenum}"
332 // e.g. the last page asserted as page 5 retains that assertion.
333 br.uniquifyPageNums = function() {
334     var seen = {};
335     
336     for (var i = br.pageNums.length - 1; i--; i >= 0) {
337         var pageNum = br.pageNums[i];
338         if ( !seen[pageNum] ) {
339             seen[pageNum] = true;
340         } else {
341             br.pageNums[i] = null;
342         }
343     }
344
345 }
346
347 br.cleanupMetadata = function() {
348     br.uniquifyPageNums();
349 }
350
351 // getEmbedURL
352 //________
353 // Returns a URL for an embedded version of the current book
354 br.getEmbedURL = function(viewParams) {
355     // We could generate a URL hash fragment here but for now we just leave at defaults
356     var url = 'http://' + window.location.host + '/stream/'+this.bookId;
357     if (this.subPrefix != this.bookId) { // Only include if needed
358         url += '/' + this.subPrefix;
359     }
360     url += '?ui=embed';
361     if (typeof(viewParams) != 'undefined') {
362         url += '#' + this.fragmentFromParams(viewParams);
363     }
364     return url;
365 }
366
367 // getEmbedCode
368 //________
369 // Returns the embed code HTML fragment suitable for copy and paste
370 br.getEmbedCode = function(frameWidth, frameHeight, viewParams) {
371     return "<iframe src='" + this.getEmbedURL(viewParams) + "' width='" + frameWidth + "' height='" + frameHeight + "' frameborder='0' ></iframe>";
372 }
373
374 // getOpenLibraryRecord
375 br.getOpenLibraryRecord = function(callback) {
376     // Try looking up by ocaid first, then by source_record
377     
378     var self = this; // closure
379     
380     var jsonURL = self.olHost + '/query.json?type=/type/edition&*=&ocaid=' + self.bookId;
381     $.ajax({
382         url: jsonURL,
383         success: function(data) {
384             if (data && data.length > 0) {
385                 callback(self, data[0]);
386             } else {
387                 // try sourceid
388                 jsonURL = self.olHost + '/query.json?type=/type/edition&*=&source_records=ia:' + self.bookId;
389                 $.ajax({
390                     url: jsonURL,
391                     success: function(data) {
392                         if (data && data.length > 0) {
393                             callback(self, data[0]);
394                         }
395                     },
396                     dataType: 'jsonp'
397                 });
398             }
399         },
400         dataType: 'jsonp'
401     });
402 }
403
404 br.buildInfoDiv = function(jInfoDiv) {
405     // $$$ it might make more sense to have a URL on openlibrary.org that returns this info
406
407     var escapedTitle = BookReader.util.escapeHTML(this.bookTitle);
408     var domainRe = /(\w+\.(com|org))/;
409     var domainMatch = domainRe.exec(this.bookUrl);
410     var domain = this.bookUrl;
411     if (domainMatch) {
412         domain = domainMatch[1];
413     }
414        
415     // $$$ cover looks weird before it loads
416     jInfoDiv.find('.BRfloatCover').append([
417                     '<div style="height: 140px; min-width: 80px; padding: 0; margin: 0;"><a href="', this.bookUrl, '"><img src="http://www.archive.org/download/', this.bookId, '/page/cover_t.jpg" alt="' + escapedTitle + '" height="140px" /></a></div>'].join('')
418     );
419
420     jInfoDiv.find('.BRfloatMeta').append([
421                     // $$$ description
422                     //'<p>Published ', this.bookPublished,
423                     //, <a href="Open Library Publisher Page">Publisher name</a>',
424                     //'</p>',
425                     //'<p>Written in <a href="Open Library Language page">Language</a></p>',
426                     '<h3>Other Formats</h3>',
427                     '<ul class="links">',
428                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '.pdf">PDF</a><span>|</span></li>',
429                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '_djvu.txt">Plain Text</a><span>|</span></li>',
430                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '_daisy.zip">DAISY</a><span>|</span></li>',
431                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '.epub">ePub</a><span>|</span></li>',
432                         '<li><a href="https://www.amazon.com/gp/digital/fiona/web-to-kindle?clientid=IA&itemid=', this.bookId, '&docid=', this.subPrefix, '">Send to Kindle</a></li>',
433                     '</ul>',
434                     '<p class="moreInfo"><span></span>More information on <a href="'+ this.bookUrl + '">' + domain + '</a>  </p>'].join('\n'));
435                     
436     jInfoDiv.find('.BRfloatFoot').append([
437                 '<span>|</span>',                
438                 '<a href="http://openlibrary.org/contact" class="problem">Report a problem</a>',
439     ].join('\n'));
440                 
441     if (domain == 'archive.org') {
442         jInfoDiv.find('.BRfloatMeta p.moreInfo span').css(
443             {'background': 'url(http://www.archive.org/favicon.ico) no-repeat', 'width': 22, 'height': 18 }
444         );
445     }
446     
447     jInfoDiv.find('.BRfloatTitle a').attr({'href': this.bookUrl, 'alt': this.bookTitle}).text(this.bookTitle);
448     var bookPath = (window.location + '').replace('#','%23');
449     jInfoDiv.find('a.problem').attr('href','http://openlibrary.org/contact?path=' + bookPath);
450
451 }
452
453 br.pageW =  [
454             <?
455             $i=0;
456             foreach ($scanData->pageData->page as $page) {
457                 if (shouldAddPage($page)) {
458                     if(0 != $i) echo ",";   //stupid IE
459                     echo "{$page->cropBox->w}";
460                     $i++;
461                 }
462             }
463             ?>
464             ];
465
466 br.pageH =  [
467             <?
468             $totalHeight = 0;
469             $i=0;            
470             foreach ($scanData->pageData->page as $page) {
471                 if (shouldAddPage($page)) {
472                     if(0 != $i) echo ",";   //stupid IE                
473                     echo "{$page->cropBox->h}";
474                     $totalHeight += intval($page->cropBox->h/4) + 10;
475                     $i++;
476                 }
477             }
478             ?>
479             ];
480 br.leafMap = [
481             <?
482             $i=0;
483             foreach ($scanData->pageData->page as $page) {
484                 if (shouldAddPage($page)) {
485                     if(0 != $i) echo ",";   //stupid IE
486                     echo "{$page['leafNum']}";
487                     $i++;
488                 }
489             }
490             ?>    
491             ];
492
493 br.pageNums = [
494             <?
495             $i=0;
496             foreach ($scanData->pageData->page as $page) {
497                 if (shouldAddPage($page)) {
498                     if(0 != $i) echo ",";   //stupid IE                
499                     if (array_key_exists('pageNumber', $page) && ('' != $page->pageNumber)) {
500                         echo "'{$page->pageNumber}'";
501                     } else {
502                         echo "null";
503                     }
504                     $i++;
505                 }
506             }
507             ?>    
508             ];
509             
510       
511 br.numLeafs = br.pageW.length;
512
513 br.bookId   = '<?echo $id;?>';
514 br.zip      = '<?echo $imageStackFile;?>';
515 br.subPrefix = '<?echo $subPrefix;?>';
516 br.server   = '<?echo $server;?>';
517 br.bookTitle= '<?echo preg_replace("/\'/", "\\'", $metaData->title);?>';
518 br.bookPath = '<?echo $subItemPath;?>';
519 br.bookUrl  = '<?echo "http://www.archive.org/details/$id";?>';
520 br.imageFormat = '<?echo $imageFormat;?>';
521 br.archiveFormat = '<?echo $archiveFormat;?>';
522
523 <?
524
525 # Load some values from meta.xml
526 if ('' != $metaData->{'page-progression'}) {
527   echo "br.pageProgression = '" . $metaData->{"page-progression"} . "';\n";
528 } else {
529   // Assume page progression is Left To Right
530   echo "br.pageProgression = 'lr';\n";
531 }
532
533 $useOLAuth = false;
534 $protected = false;
535 foreach ($metaData->xpath('//collection') as $collection) {
536     if('browserlending' == $collection) {
537         $useOLAuth = true;
538         $protected = true;
539     }
540 }
541
542 echo "br.olHost = 'http://openlibrary.org'\n";
543 #echo "br.olHost = 'http://ol-mang:8080'\n";
544
545 if ($useOLAuth) {
546     echo "br.olAuth = true;\n";
547 } else {
548     echo "br.olAuth = false;\n";
549 }
550
551 if ($protected) {
552     echo "br.protected = true;\n";
553 }
554
555 # Special cases
556 if ('bandersnatchhsye00scarrich' == $id) {
557     echo "br.mode     = 2;\n";
558     echo "br.auto     = true;\n";
559 }
560
561 ?>
562
563 // Check for config object
564 // $$$ change this to use the newer params object
565 if (typeof(brConfig) != 'undefined') {
566     if (typeof(brConfig["ui"]) != 'undefined') {
567         br.ui = brConfig["ui"];
568     }
569
570     if (brConfig['mode'] == 1) {
571         br.mode = 1;
572         if (typeof(brConfig['reduce'] != 'undefined')) {
573             br.reduce = brConfig['reduce'];
574         }
575     } else if (brConfig['mode'] == 2) {
576         br.mode = 2;      
577     }
578     
579     if (typeof(brConfig["isAdmin"]) != 'undefined') {
580         br.isAdmin = brConfig["isAdmin"];
581     } else {
582         br.isAdmin = false;
583     }
584 } // brConfig
585
586
587 function OLAuth() {
588     this.authUrl = br.olHost + '/ia_auth/' + br.bookId;
589     this.olConnect = false;
590     this.loanUUID = false;
591     this.permsToken = false;
592     
593     var cookieRe = /;\s*/;
594     var cookies = document.cookie.split(cookieRe);
595     var length = cookies.length;
596     var i;
597     for (i=0; i<length; i++) {
598         if (0 == cookies[i].indexOf('br-loan-' + br.bookId)) {
599             this.loanUUID = cookies[i].split('=')[1];
600         }
601         if (0 == cookies[i].indexOf('loan-' + br.bookId)) {
602             this.permsToken = cookies[i].split('=')[1];
603         }
604     }
605
606     return this;
607 }
608
609 OLAuth.prototype.init = function() {
610     var htmlStr =  'Checking loan status with Open Library';
611
612     this.showPopup("#F0EEE2", "#000", htmlStr, 'Please wait as we check the status of this book...');
613     var authUrl = this.authUrl+'?rand='+Math.random();
614     if (false !== this.loanUUID) {
615         authUrl += '&loan='+this.loanUUID
616     }
617     if (false !== this.permsToken) {
618         authUrl += '&token='+this.permsToken
619     }
620     $.ajax({url:authUrl, dataType:'jsonp', jsonpCallback:'olAuth.initCallback'});
621 }
622
623 OLAuth.prototype.showPopup = function(bgColor, textColor, msg, resolution) {
624     this.popup = document.createElement("div");
625     $(this.popup).css({
626         position: 'absolute',
627         top:      '50px',
628         left:     ($('#BookReader').attr('clientWidth')-400)/2 + 'px',
629         width:    '400px',
630         padding:  "15px",
631         border:   "3px double #999999",
632         zIndex:   3,
633         textAlign: 'center',
634         backgroundColor: bgColor,
635         color: textColor
636     }).appendTo('#BookReader');
637
638     this.setPopupMsg(msg, resolution);
639
640 }
641
642 OLAuth.prototype.setPopupMsg = function(msg, resolution) {
643     this.popup.innerHTML = ['<p><strong>', msg, '</strong></p><p>', resolution, '</p>'].join('\n');
644 }
645
646 OLAuth.prototype.showError = function(msg, resolution) {
647    $(this.popup).css({
648         backgroundColor: "#fff",
649         color: "#000"
650     });
651
652     this.setPopupMsg(msg, resolution);
653 }
654
655 OLAuth.prototype.initCallback = function(obj) {
656     if (false == obj.success) {
657         if (br.isAdmin) {
658             ret = confirm("We couldn't authenticate your loan with Open Library, but since you are an administrator or uploader of this book, you can access this book for QA purposes. Would you like to QA this book?");
659             if (!ret) {
660                 this.showError(obj.msg, obj.resolution)
661             } else {
662                 br.init();
663             }
664         } else {
665             this.showError(obj.msg, obj.resolution)
666         }       
667     } else {    
668         //user is authenticated
669         this.setCookie(obj.token);
670         this.olConnect = true;
671         this.startPolling();    
672         br.init();
673     }
674 }
675
676 OLAuth.prototype.callback = function(obj) {
677     if (false == obj.success) {
678         this.showPopup("#F0EEE2", "#000", obj.msg, obj.resolution);
679         clearInterval(this.poller);
680         this.ttsPoller = null;
681     } else {
682         this.olConnect = true;
683         this.setCookie(obj.token);
684     }
685 }
686
687 OLAuth.prototype.setCookie = function(value) {
688     var date = new Date();
689     date.setTime(date.getTime()+(10*60*1000));  //10 min expiry
690     var expiry = date.toGMTString();
691     var cookie = 'loan-'+br.bookId+'='+value;
692     cookie    += '; expires='+expiry;
693     cookie    += '; path=/; domain=.archive.org;';
694     document.cookie = cookie;
695     this.permsToken = value;
696     
697     //refresh the br-loan uuid cookie with current expiry, if needed
698     if (false !== this.loanUUID) {
699         cookie = 'br-loan-'+br.bookId+'='+this.loanUUID;
700         cookie    += '; expires='+expiry;
701         cookie    += '; path=/; domain=.archive.org;';
702         document.cookie = cookie;
703     }
704 }
705
706 OLAuth.prototype.deleteCookies = function() {
707     var date = new Date();
708     date.setTime(date.getTime()-(24*60*60*1000));  //one day ago
709     var expiry = date.toGMTString();
710     var cookie = 'loan-'+br.bookId+'=""';
711     cookie    += '; expires='+expiry;
712     cookie    += '; path=/; domain=.archive.org;';
713     document.cookie = cookie;
714     
715     cookie = 'br-loan-'+br.bookId+'=""';
716     cookie    += '; expires='+expiry;
717     cookie    += '; path=/; domain=.archive.org;';
718     document.cookie = cookie;
719 }
720
721 OLAuth.prototype.startPolling = function () {    
722     var self = this;
723     this.poller=setInterval(function(){
724         if (!self.olConnect) {
725           self.showPopup("#F0EEE2", "#000", 'Connection error', 'The BookReader cannot reach Open Library. This might mean that you are offline or that Open Library is down. Please check your Internet connection and refresh this page or try again later.');
726           clearInterval(self.poller);
727           self.ttsPoller = null;        
728         } else {
729           self.olConnect = false;
730           //be sure to add random param to authUrl to avoid stale cache
731           var authUrl = self.authUrl+'?rand='+Math.random();
732           if (false !== self.loanUUID) {
733               authUrl += '&loan='+self.loanUUID
734           }
735           if (false !== self.permsToken) {
736               authUrl += '&token='+self.permsToken
737           }
738
739           $.ajax({url:authUrl, dataType:'jsonp', jsonpCallback:'olAuth.callback'});
740         }
741     },300000);   //five minute interval
742 }
743
744 br.cleanupMetadata();
745 if (br.olAuth) {
746     var olAuth = new OLAuth();
747     olAuth.init();
748 } else {
749     br.init();
750 }
751 <?
752
753
754 function BRFatal($string) {
755     // log error
756     ?>
757     
758     if (typeof(archive_analytics) != 'undefined') {
759         var values = {
760             'bookreader': 'fatal',
761             'description': "<? echo $string; ?>",
762             'itemid': "<? echo $_REQUEST['id']; ?>",
763             'server': "<? echo $_REQUEST['server']; ?>",
764             'request_uri': "<? echo $_SERVER["REQUEST_URI"]; ?>"
765         }
766         
767         if (document.referrer == '') {
768             values['referrer'] = '-';
769         } else {
770             values['referrer'] = document.referrer;
771         }
772         
773         var qs = archive_analytics.format_bug(values);
774
775         var error_img = new Image(100,25);
776         error_img.src = archive_analytics.img_src + "?" + qs;
777     }
778
779     alert("<? echo $string;?>");
780     
781     <?
782     
783     die(-1);
784 }
785
786 // Returns true if a page should be added based on it's information in
787 // the metadata
788 function shouldAddPage($page) {
789     // Return false only if the page is marked addToAccessFormats false.
790     // If there is no assertion we assume it should be added.
791     if (isset($page->addToAccessFormats)) {
792         if ("false" == strtolower(trim($page->addToAccessFormats))) {
793             return false;
794         }
795     }
796     
797     return true;
798 }
799
800 // Returns { 'imageFormat' => , 'archiveFormat' => '} given a sub-item prefix and loaded xml data
801 function findImageStack($subPrefix, $filesData) {
802
803     // $$$ The order of the image formats determines which will be returned first
804     $imageFormats = array('JP2' => 'jp2', 'TIFF' => 'tif', 'JPEG' => 'jpg');
805     $archiveFormats = array('ZIP' => 'zip', 'Tar' => 'tar');
806     $imageGroup = implode('|', array_keys($imageFormats));
807     $archiveGroup = implode('|', array_keys($archiveFormats));
808     // $$$ Currently only return processed images
809     $imageStackRegex = "/Single Page (Processed) (${imageGroup}) (${archiveGroup})/";
810         
811     foreach ($filesData->file as $file) {        
812         if (strpos($file['name'], $subPrefix) === 0) { // subprefix matches beginning
813             if (preg_match($imageStackRegex, $file->format, $matches)) {
814             
815                 // Make sure we have a regular image stack
816                 $imageFormat = $imageFormats[$matches[2]];
817                 if (strpos($file['name'], $subPrefix . '_' . $imageFormat) === 0) {            
818                     return array('imageFormat' => $imageFormat,
819                                  'archiveFormat' => $archiveFormats[$matches[3]],
820                                  'imageStackFile' => $file['name']);
821                 }
822             }
823         }
824     }
825     
826     return array('imageFormat' => 'unknown', 'archiveFormat' => 'unknown', 'imageStackFile' => 'unknown');
827         
828 }
829
830 ?>
831