Merge branch 'browserlending' of git@github.com:openlibrary/bookreader into browserle...
[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 jsonURL = this.olHost + '/query.json?type=/type/edition&*=&ocaid=' + br.bookId;
379     $.ajax({
380         url: jsonURL,
381         success: function(data) {
382             if (data && data.length > 0) {
383                 callback(br, data[0]);
384             } else {
385                 // try sourceid
386                 jsonURL = this.olHost + '/query.json?type=/type/edition&*=&source_records=ia:' + br.bookId;
387                 $.ajax({
388                     url: jsonURL,
389                     success: function(data) {
390                         if (data && data.length > 0) {
391                             callback(br, data[0]);
392                         }
393                     },
394                     dataType: 'jsonp'
395                 });
396             }
397         },
398         dataType: 'jsonp'
399     });
400 }
401
402 br.buildInfoDiv = function(jInfoDiv) {
403     // $$$ it might make more sense to have a URL on openlibrary.org that returns this info
404
405     var escapedTitle = BookReader.util.escapeHTML(this.bookTitle);
406     var domainRe = /(\w+\.(com|org))/;
407     var domainMatch = domainRe.exec(this.bookUrl);
408     var domain = this.bookUrl;
409     if (domainMatch) {
410         domain = domainMatch[1];
411     }
412        
413     // $$$ cover looks weird before it loads
414     jInfoDiv.find('.BRfloatCover').append([
415                     '<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('')
416     );
417
418     jInfoDiv.find('.BRfloatMeta').append([
419                     // $$$ description
420                     //'<p>Published ', this.bookPublished,
421                     //, <a href="Open Library Publisher Page">Publisher name</a>',
422                     //'</p>',
423                     //'<p>Written in <a href="Open Library Language page">Language</a></p>',
424                     '<h3>Other Formats</h3>',
425                     '<ul class="links">',
426                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '.pdf">PDF</a><span>|</span></li>',
427                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '_djvu.txt">Plain Text</a><span>|</span></li>',
428                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '_daisy.zip">DAISY</a><span>|</span></li>',
429                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '.epub">ePub</a><span>|</span></li>',
430                         '<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>',
431                     '</ul>',
432                     '<p class="moreInfo"><span></span>More information on <a href="'+ this.bookUrl + '">' + domain + '</a>  </p>'].join('\n'));
433                     
434     jInfoDiv.find('.BRfloatFoot').append([
435                 '<span>|</span>',                
436                 '<a href="http://openlibrary.org/contact" class="problem">Report a problem</a>',
437     ].join('\n'));
438                 
439     if (domain == 'archive.org') {
440         jInfoDiv.find('.BRfloatMeta p.moreInfo span').css(
441             {'background': 'url(http://www.archive.org/favicon.ico) no-repeat', 'width': 22, 'height': 18 }
442         );
443     }
444     
445     jInfoDiv.find('.BRfloatTitle a').attr({'href': this.bookUrl, 'alt': this.bookTitle}).text(this.bookTitle);
446     var bookPath = (window.location + '').replace('#','%23');
447     jInfoDiv.find('a.problem').attr('href','http://openlibrary.org/contact?path=' + bookPath);
448
449 }
450
451 br.pageW =  [
452             <?
453             $i=0;
454             foreach ($scanData->pageData->page as $page) {
455                 if (shouldAddPage($page)) {
456                     if(0 != $i) echo ",";   //stupid IE
457                     echo "{$page->cropBox->w}";
458                     $i++;
459                 }
460             }
461             ?>
462             ];
463
464 br.pageH =  [
465             <?
466             $totalHeight = 0;
467             $i=0;            
468             foreach ($scanData->pageData->page as $page) {
469                 if (shouldAddPage($page)) {
470                     if(0 != $i) echo ",";   //stupid IE                
471                     echo "{$page->cropBox->h}";
472                     $totalHeight += intval($page->cropBox->h/4) + 10;
473                     $i++;
474                 }
475             }
476             ?>
477             ];
478 br.leafMap = [
479             <?
480             $i=0;
481             foreach ($scanData->pageData->page as $page) {
482                 if (shouldAddPage($page)) {
483                     if(0 != $i) echo ",";   //stupid IE
484                     echo "{$page['leafNum']}";
485                     $i++;
486                 }
487             }
488             ?>    
489             ];
490
491 br.pageNums = [
492             <?
493             $i=0;
494             foreach ($scanData->pageData->page as $page) {
495                 if (shouldAddPage($page)) {
496                     if(0 != $i) echo ",";   //stupid IE                
497                     if (array_key_exists('pageNumber', $page) && ('' != $page->pageNumber)) {
498                         echo "'{$page->pageNumber}'";
499                     } else {
500                         echo "null";
501                     }
502                     $i++;
503                 }
504             }
505             ?>    
506             ];
507             
508       
509 br.numLeafs = br.pageW.length;
510
511 br.bookId   = '<?echo $id;?>';
512 br.zip      = '<?echo $imageStackFile;?>';
513 br.subPrefix = '<?echo $subPrefix;?>';
514 br.server   = '<?echo $server;?>';
515 br.bookTitle= '<?echo preg_replace("/\'/", "\\'", $metaData->title);?>';
516 br.bookPath = '<?echo $subItemPath;?>';
517 br.bookUrl  = '<?echo "http://www.archive.org/details/$id";?>';
518 br.imageFormat = '<?echo $imageFormat;?>';
519 br.archiveFormat = '<?echo $archiveFormat;?>';
520
521 <?
522
523 # Load some values from meta.xml
524 if ('' != $metaData->{'page-progression'}) {
525   echo "br.pageProgression = '" . $metaData->{"page-progression"} . "';\n";
526 } else {
527   // Assume page progression is Left To Right
528   echo "br.pageProgression = 'lr';\n";
529 }
530
531 $useOLAuth = false;
532 $protected = false;
533 foreach ($metaData->xpath('//collection') as $collection) {
534     if('browserlending' == $collection) {
535         $useOLAuth = true;
536         $protected = true;
537     }
538 }
539
540 echo "br.olHost = 'http://openlibrary.org'\n";
541 #echo "br.olHost = 'http://ol-mang:8080'\n";
542
543 if ($useOLAuth) {
544     echo "br.olAuth = true;\n";
545 } else {
546     echo "br.olAuth = false;\n";
547 }
548
549 if ($protected) {
550     echo "br.protected = true;\n";
551 }
552
553 # Special cases
554 if ('bandersnatchhsye00scarrich' == $id) {
555     echo "br.mode     = 2;\n";
556     echo "br.auto     = true;\n";
557 }
558
559 ?>
560
561 // Check for config object
562 // $$$ change this to use the newer params object
563 if (typeof(brConfig) != 'undefined') {
564     if (typeof(brConfig["ui"]) != 'undefined') {
565         br.ui = brConfig["ui"];
566     }
567
568     if (brConfig['mode'] == 1) {
569         br.mode = 1;
570         if (typeof(brConfig['reduce'] != 'undefined')) {
571             br.reduce = brConfig['reduce'];
572         }
573     } else if (brConfig['mode'] == 2) {
574         br.mode = 2;      
575     }
576 } // brConfig
577
578
579 function OLAuth() {
580     this.authUrl = br.olHost + '/ia_auth/' + br.bookId;
581     this.olConnect = false;
582     this.loanUUID = false;
583     this.permsToken = false;
584     
585     var cookieRe = /;\s*/;
586     var cookies = document.cookie.split(cookieRe);
587     var length = cookies.length;
588     var i;
589     for (i=0; i<length; i++) {
590         if (0 == cookies[i].indexOf('br-loan-' + br.bookId)) {
591             this.loanUUID = cookies[i].split('=')[1];
592         }
593         if (0 == cookies[i].indexOf('loan-' + br.bookId)) {
594             this.permsToken = cookies[i].split('=')[1];
595         }
596     }
597
598     return this;
599 }
600
601 OLAuth.prototype.init = function() {
602     var htmlStr =  'Checking loan status with Open Library';
603
604     this.showPopup("#F0EEE2", "#000", htmlStr, 'Please wait as we check the status of this book...');
605     var authUrl = this.authUrl+'?rand='+Math.random();
606     if (false !== this.loanUUID) {
607         authUrl += '&loan='+this.loanUUID
608     }
609     if (false !== this.permsToken) {
610         authUrl += '&token='+this.permsToken
611     }
612     $.ajax({url:authUrl, dataType:'jsonp', jsonpCallback:'olAuth.initCallback'});
613 }
614
615 OLAuth.prototype.showPopup = function(bgColor, textColor, msg, resolution) {
616     this.popup = document.createElement("div");
617     $(this.popup).css({
618         position: 'absolute',
619         top:      '50px',
620         left:     ($('#BookReader').attr('clientWidth')-400)/2 + 'px',
621         width:    '400px',
622         padding:  "15px",
623         border:   "3px double #999999",
624         zIndex:   3,
625         textAlign: 'center',
626         backgroundColor: bgColor,
627         color: textColor
628     }).appendTo('#BookReader');
629
630     this.setPopupMsg(msg, resolution);
631
632 }
633
634 OLAuth.prototype.setPopupMsg = function(msg, resolution) {
635     this.popup.innerHTML = ['<p><strong>', msg, '</strong></p><p>', resolution, '</p>'].join('\n');
636 }
637
638 OLAuth.prototype.isAdmin = function() {
639     var isAdmin = false;
640     
641     $.ajax({url:'/bookreader/BookReaderAdmin.php?id='+br.bookId, 
642         success:function(data){isAdmin=data.isAdmin;}, 
643         async:false
644     });
645     return isAdmin;
646 }
647
648 OLAuth.prototype.showError = function(msg, resolution) {
649    $(this.popup).css({
650         backgroundColor: "#fff",
651         color: "#000"
652     });
653
654     this.setPopupMsg(msg, resolution);
655 }
656
657 OLAuth.prototype.initCallback = function(obj) {
658     if (false == obj.success) {
659         if (this.isAdmin()) {
660             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?");
661             if (!ret) {
662                 this.showError(obj.msg, obj.resolution)
663             } else {
664                 br.init();
665             }
666         } else {
667             this.showError(obj.msg, obj.resolution)
668         }       
669     } else {    
670         //user is authenticated
671         this.setCookie(obj.token);
672         this.olConnect = true;
673         this.startPolling();    
674         br.init();
675     }
676 }
677
678 OLAuth.prototype.callback = function(obj) {
679     if (false == obj.success) {
680         this.showPopup("#F0EEE2", "#000", obj.msg, obj.resolution);
681         clearInterval(this.poller);
682         this.ttsPoller = null;
683     } else {
684         this.olConnect = true;
685         this.setCookie(obj.token);
686     }
687 }
688
689 OLAuth.prototype.setCookie = function(value) {
690     var date = new Date();
691     date.setTime(date.getTime()+(10*60*1000));  //10 min expiry
692     var expiry = date.toGMTString();
693     var cookie = 'loan-'+br.bookId+'='+value;
694     cookie    += '; expires='+expiry;
695     cookie    += '; path=/; domain=.archive.org;';
696     document.cookie = cookie;
697     this.permsToken = value;
698     
699     //refresh the br-loan uuid cookie with current expiry, if needed
700     if (false !== this.loanUUID) {
701         cookie = 'br-loan-'+br.bookId+'='+this.loanUUID;
702         cookie    += '; expires='+expiry;
703         cookie    += '; path=/; domain=.archive.org;';
704         document.cookie = cookie;
705     }
706 }
707
708 OLAuth.prototype.deleteCookies = function() {
709     var date = new Date();
710     date.setTime(date.getTime()-(24*60*60*1000));  //one day ago
711     var expiry = date.toGMTString();
712     var cookie = 'loan-'+br.bookId+'=""';
713     cookie    += '; expires='+expiry;
714     cookie    += '; path=/; domain=.archive.org;';
715     document.cookie = cookie;
716     
717     cookie = 'br-loan-'+br.bookId+'=""';
718     cookie    += '; expires='+expiry;
719     cookie    += '; path=/; domain=.archive.org;';
720     document.cookie = cookie;
721 }
722
723 OLAuth.prototype.startPolling = function () {    
724     var self = this;
725     this.poller=setInterval(function(){
726         if (!self.olConnect) {
727           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.');
728           clearInterval(self.poller);
729           self.ttsPoller = null;        
730         } else {
731           self.olConnect = false;
732           //be sure to add random param to authUrl to avoid stale cache
733           var authUrl = self.authUrl+'?rand='+Math.random();
734           if (false !== self.loanUUID) {
735               authUrl += '&loan='+self.loanUUID
736           }
737           if (false !== self.permsToken) {
738               authUrl += '&token='+self.permsToken
739           }
740
741           $.ajax({url:authUrl, dataType:'jsonp', jsonpCallback:'olAuth.callback'});
742         }
743     },300000);   //five minute interval
744 }
745
746 br.cleanupMetadata();
747 if (br.olAuth) {
748     var olAuth = new OLAuth();
749     olAuth.init();
750 } else {
751     br.init();
752 }
753 <?
754
755
756 function BRFatal($string) {
757     // log error
758     ?>
759     
760     if (typeof(archive_analytics) != 'undefined') {
761         var values = {
762             'bookreader': 'fatal',
763             'description': "<? echo $string; ?>",
764             'itemid': "<? echo $_REQUEST['id']; ?>",
765             'server': "<? echo $_REQUEST['server']; ?>",
766             'request_uri': "<? echo $_SERVER["REQUEST_URI"]; ?>"
767         }
768         
769         if (document.referrer == '') {
770             values['referrer'] = '-';
771         } else {
772             values['referrer'] = document.referrer;
773         }
774         
775         var qs = archive_analytics.format_bug(values);
776
777         var error_img = new Image(100,25);
778         error_img.src = archive_analytics.img_src + "?" + qs;
779     }
780
781     alert("<? echo $string;?>");
782     
783     <?
784     
785     die(-1);
786 }
787
788 // Returns true if a page should be added based on it's information in
789 // the metadata
790 function shouldAddPage($page) {
791     // Return false only if the page is marked addToAccessFormats false.
792     // If there is no assertion we assume it should be added.
793     if (isset($page->addToAccessFormats)) {
794         if ("false" == strtolower(trim($page->addToAccessFormats))) {
795             return false;
796         }
797     }
798     
799     return true;
800 }
801
802 // Returns { 'imageFormat' => , 'archiveFormat' => '} given a sub-item prefix and loaded xml data
803 function findImageStack($subPrefix, $filesData) {
804
805     // $$$ The order of the image formats determines which will be returned first
806     $imageFormats = array('JP2' => 'jp2', 'TIFF' => 'tif', 'JPEG' => 'jpg');
807     $archiveFormats = array('ZIP' => 'zip', 'Tar' => 'tar');
808     $imageGroup = implode('|', array_keys($imageFormats));
809     $archiveGroup = implode('|', array_keys($archiveFormats));
810     // $$$ Currently only return processed images
811     $imageStackRegex = "/Single Page (Processed) (${imageGroup}) (${archiveGroup})/";
812         
813     foreach ($filesData->file as $file) {        
814         if (strpos($file['name'], $subPrefix) === 0) { // subprefix matches beginning
815             if (preg_match($imageStackRegex, $file->format, $matches)) {
816             
817                 // Make sure we have a regular image stack
818                 $imageFormat = $imageFormats[$matches[2]];
819                 if (strpos($file['name'], $subPrefix . '_' . $imageFormat) === 0) {            
820                     return array('imageFormat' => $imageFormat,
821                                  'archiveFormat' => $archiveFormats[$matches[3]],
822                                  'imageStackFile' => $file['name']);
823                 }
824             }
825         }
826     }
827     
828     return array('imageFormat' => 'unknown', 'archiveFormat' => 'unknown', 'imageStackFile' => 'unknown');
829         
830 }
831
832 ?>
833