Handling of non-jp2 image formats using ImageMagick's convert
[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://mang-dev.us.archive.org: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 # Default options for BookReader
556 if ('' != $metaData->{'bookreader-defaults'}) {
557     echo "br.defaults = '" . $metaData->{'bookreader-defaults'} . "';\n";
558 }
559
560 ?>
561
562 // Check for config object
563 // $$$ change this to use the newer params object
564 if (typeof(brConfig) != 'undefined') {
565     if (typeof(brConfig["ui"]) != 'undefined') {
566         br.ui = brConfig["ui"];
567     }
568
569     if (brConfig['mode'] == 1) {
570         br.mode = 1;
571         if (typeof(brConfig['reduce'] != 'undefined')) {
572             br.reduce = brConfig['reduce'];
573         }
574     } else if (brConfig['mode'] == 2) {
575         br.mode = 2;      
576     }
577     
578     if (typeof(brConfig["isAdmin"]) != 'undefined') {
579         br.isAdmin = brConfig["isAdmin"];
580     } else {
581         br.isAdmin = false;
582     }
583 } // brConfig
584
585
586 function OLAuth() {
587     this.authUrl = br.olHost + '/ia_auth/' + br.bookId;
588     this.olConnect = false;
589     this.loanUUID = false;
590     this.permsToken = false;
591     
592     var cookieRe = /;\s*/;
593     var cookies = document.cookie.split(cookieRe);
594     var length = cookies.length;
595     var i;
596     for (i=0; i<length; i++) {
597         if (0 == cookies[i].indexOf('br-loan-' + br.bookId)) {
598             this.loanUUID = cookies[i].split('=')[1];
599         }
600         if (0 == cookies[i].indexOf('loan-' + br.bookId)) {
601             this.permsToken = cookies[i].split('=')[1];
602         }
603     }
604
605     return this;
606 }
607
608 OLAuth.prototype.init = function() {
609     var htmlStr =  'Checking loan status with Open Library';
610
611     this.showPopup("#F0EEE2", "#000", htmlStr, 'Please wait as we check the status of this book...');
612     var authUrl = this.authUrl+'?rand='+Math.random();
613     if (false !== this.loanUUID) {
614         authUrl += '&loan='+this.loanUUID
615     }
616     if (false !== this.permsToken) {
617         authUrl += '&token='+this.permsToken
618     }
619     $.ajax({url:authUrl, dataType:'jsonp', jsonpCallback:'olAuth.initCallback'});
620 }
621
622 OLAuth.prototype.showPopup = function(bgColor, textColor, msg, resolution) {
623     this.popup = document.createElement("div");
624     $(this.popup).css({
625         position: 'absolute',
626         top:      '50px',
627         left:     ($('#BookReader').attr('clientWidth')-400)/2 + 'px',
628         width:    '400px',
629         padding:  "15px",
630         border:   "3px double #999999",
631         zIndex:   3,
632         textAlign: 'center',
633         backgroundColor: bgColor,
634         color: textColor
635     }).appendTo('#BookReader');
636
637     this.setPopupMsg(msg, resolution);
638
639 }
640
641 OLAuth.prototype.setPopupMsg = function(msg, resolution) {
642     this.popup.innerHTML = ['<p><strong>', msg, '</strong></p><p>', resolution, '</p>'].join('\n');
643 }
644
645 OLAuth.prototype.showError = function(msg, resolution) {
646    $(this.popup).css({
647         backgroundColor: "#fff",
648         color: "#000"
649     });
650
651     this.setPopupMsg(msg, resolution);
652 }
653
654 OLAuth.prototype.initCallback = function(obj) {
655     if (false == obj.success) {
656         if (br.isAdmin) {
657             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?");
658             if (!ret) {
659                 this.showError(obj.msg, obj.resolution)
660             } else {
661                 br.init();
662             }
663         } else {
664             this.showError(obj.msg, obj.resolution)
665         }       
666     } else {    
667         //user is authenticated
668         this.setCookie(obj.token);
669         this.olConnect = true;
670         this.startPolling();    
671         br.init();
672     }
673 }
674
675 OLAuth.prototype.callback = function(obj) {
676     if (false == obj.success) {
677         this.showPopup("#F0EEE2", "#000", obj.msg, obj.resolution);
678         clearInterval(this.poller);
679         this.ttsPoller = null;
680     } else {
681         this.olConnect = true;
682         this.setCookie(obj.token);
683     }
684 }
685
686 OLAuth.prototype.setCookie = function(value) {
687     var date = new Date();
688     date.setTime(date.getTime()+(10*60*1000));  //10 min expiry
689     var expiry = date.toGMTString();
690     var cookie = 'loan-'+br.bookId+'='+value;
691     cookie    += '; expires='+expiry;
692     cookie    += '; path=/; domain=.archive.org;';
693     document.cookie = cookie;
694     this.permsToken = value;
695     
696     //refresh the br-loan uuid cookie with current expiry, if needed
697     if (false !== this.loanUUID) {
698         cookie = 'br-loan-'+br.bookId+'='+this.loanUUID;
699         cookie    += '; expires='+expiry;
700         cookie    += '; path=/; domain=.archive.org;';
701         document.cookie = cookie;
702     }
703 }
704
705 OLAuth.prototype.deleteCookies = function() {
706     var date = new Date();
707     date.setTime(date.getTime()-(24*60*60*1000));  //one day ago
708     var expiry = date.toGMTString();
709     var cookie = 'loan-'+br.bookId+'=""';
710     cookie    += '; expires='+expiry;
711     cookie    += '; path=/; domain=.archive.org;';
712     document.cookie = cookie;
713     
714     cookie = 'br-loan-'+br.bookId+'=""';
715     cookie    += '; expires='+expiry;
716     cookie    += '; path=/; domain=.archive.org;';
717     document.cookie = cookie;
718 }
719
720 OLAuth.prototype.startPolling = function () {    
721     var self = this;
722     this.poller=setInterval(function(){
723         if (!self.olConnect) {
724           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.');
725           clearInterval(self.poller);
726           self.ttsPoller = null;        
727         } else {
728           self.olConnect = false;
729           //be sure to add random param to authUrl to avoid stale cache
730           var authUrl = self.authUrl+'?rand='+Math.random();
731           if (false !== self.loanUUID) {
732               authUrl += '&loan='+self.loanUUID
733           }
734           if (false !== self.permsToken) {
735               authUrl += '&token='+self.permsToken
736           }
737
738           $.ajax({url:authUrl, dataType:'jsonp', jsonpCallback:'olAuth.callback'});
739         }
740     },300000);   //five minute interval
741 }
742
743 br.cleanupMetadata();
744 if (br.olAuth) {
745     var olAuth = new OLAuth();
746     olAuth.init();
747 } else {
748     br.init();
749 }
750 <?
751
752
753 function BRFatal($string) {
754     // log error
755     ?>
756     
757     if (typeof(archive_analytics) != 'undefined') {
758         var values = {
759             'bookreader': 'fatal',
760             'description': "<? echo $string; ?>",
761             'itemid': "<? echo $_REQUEST['id']; ?>",
762             'server': "<? echo $_REQUEST['server']; ?>",
763             'request_uri': "<? echo $_SERVER["REQUEST_URI"]; ?>"
764         }
765         
766         if (document.referrer == '') {
767             values['referrer'] = '-';
768         } else {
769             values['referrer'] = document.referrer;
770         }
771         
772         var qs = archive_analytics.format_bug(values);
773
774         var error_img = new Image(100,25);
775         error_img.src = archive_analytics.img_src + "?" + qs;
776     }
777
778     alert("<? echo $string;?>");
779     
780     <?
781     
782     die(-1);
783 }
784
785 // Returns true if a page should be added based on it's information in
786 // the metadata
787 function shouldAddPage($page) {
788     // Return false only if the page is marked addToAccessFormats false.
789     // If there is no assertion we assume it should be added.
790     if (isset($page->addToAccessFormats)) {
791         if ("false" == strtolower(trim($page->addToAccessFormats))) {
792             return false;
793         }
794     }
795     
796     return true;
797 }
798
799 // Returns { 'imageFormat' => , 'archiveFormat' => '} given a sub-item prefix and loaded xml data
800 function findImageStack($subPrefix, $filesData) {
801
802     // $$$ The order of the image formats determines which will be returned first
803     $imageFormats = array('JP2' => 'jp2', 'TIFF' => 'tif', 'JPEG' => 'jpg');
804     $archiveFormats = array('ZIP' => 'zip', 'Tar' => 'tar');
805     $imageGroup = implode('|', array_keys($imageFormats));
806     $archiveGroup = implode('|', array_keys($archiveFormats));
807     // $$$ Currently only return processed images
808     $imageStackRegex = "/Single Page (Processed) (${imageGroup}) (${archiveGroup})/";
809         
810     foreach ($filesData->file as $file) {        
811         if (strpos($file['name'], $subPrefix) === 0) { // subprefix matches beginning
812             if (preg_match($imageStackRegex, $file->format, $matches)) {
813             
814                 // Make sure we have a regular image stack
815                 $imageFormat = $imageFormats[$matches[2]];
816                 if (strpos($file['name'], $subPrefix . '_' . $imageFormat) === 0) {            
817                     return array('imageFormat' => $imageFormat,
818                                  'archiveFormat' => $archiveFormats[$matches[3]],
819                                  'imageStackFile' => $file['name']);
820                 }
821             }
822         }
823     }
824     
825     return array('imageFormat' => 'unknown', 'archiveFormat' => 'unknown', 'imageStackFile' => 'unknown');
826         
827 }
828
829 ?>
830