009a42e42e9d2443ee34187d7f9ea92bcf7ed4d7
[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 // Get a rectangular region out of a page
213 br.getRegionURI = function(index, reduce, rotate, sourceX, sourceY, sourceWidth, sourceHeight) {
214
215     // Map function arguments to the url keys
216     var urlKeys = ['n', 'r', 'rot', 'x', 'y', 'w', 'h'];
217     var page = '';
218     for (var i = 0; i < arguments.length; i++) {
219         if ('undefined' != typeof(arguments[i])) {
220             if (i > 0 ) {
221                 page += '_';
222             }
223             page += urlKeys[i] + argument[i];
224         }
225     }
226     
227     return 'http://'+this.server+'/BookReader/BookReaderImages.php?zip='+this.zip+'&page='+page;
228 }
229
230 br._getPageFile = function(index) {
231     var leafStr = '0000';
232     var imgStr = this.leafMap[index].toString();
233     var re = new RegExp("0{"+imgStr.length+"}$");
234     
235     var insideZipPrefix = this.subPrefix.match('[^/]+$');
236     var file = insideZipPrefix + '_' + this.imageFormat + '/' + insideZipPrefix + '_' + leafStr.replace(re, imgStr) + '.' + this.imageFormat;
237     
238     return file;
239 }
240
241 br.getPageSide = function(index) {
242     //assume the book starts with a cover (right-hand leaf)
243     //we should really get handside from scandata.xml
244     
245     <? // Use special function if we should infer the page sides based off the title page index
246     if (preg_match('/goog$/', $id) && ('' != $titleLeaf)) {
247     ?>
248     // assume page side based on title pagex
249     var titleIndex = br.leafNumToIndex(br.titleLeaf);
250     // assume title page is RHS
251     var delta = titleIndex - index;
252     if (0 == (delta & 0x1)) {
253         // even delta
254         return 'R';
255     } else {
256         return 'L';
257     }
258     <?
259     }
260     ?>
261     
262     // $$$ we should get this from scandata instead of assuming the accessible
263     //     leafs are contiguous
264     if ('rl' != this.pageProgression) {
265         // If pageProgression is not set RTL we assume it is LTR
266         if (0 == (index & 0x1)) {
267             // Even-numbered page
268             return 'R';
269         } else {
270             // Odd-numbered page
271             return 'L';
272         }
273     } else {
274         // RTL
275         if (0 == (index & 0x1)) {
276             return 'L';
277         } else {
278             return 'R';
279         }
280     }
281 }
282
283 br.getPageNum = function(index) {
284     var pageNum = this.pageNums[index];
285     if (pageNum) {
286         return pageNum;
287     } else {
288         return 'n' + index;
289     }
290 }
291
292 // Single images in the Internet Archive scandata.xml metadata are (somewhat incorrectly)
293 // given a "leaf" number.  Some of these images from the scanning process should not
294 // be displayed in the BookReader (for example colour calibration cards).  Since some
295 // of the scanned images will not be displayed in the BookReader (those marked with
296 // addToAccessFormats false in the scandata.xml) leaf numbers and BookReader page
297 // indexes are generally not the same.  This function returns the BookReader page
298 // index given a scanned leaf number.
299 //
300 // This function is used, for example, to map between search results (that use the
301 // leaf numbers) and the displayed pages in the BookReader.
302 br.leafNumToIndex = function(leafNum) {
303     for (var index = 0; index < this.leafMap.length; index++) {
304         if (this.leafMap[index] == leafNum) {
305             return index;
306         }
307     }
308     
309     return null;
310 }
311
312 // This function returns the left and right indices for the user-visible
313 // spread that contains the given index.  The return values may be
314 // null if there is no facing page or the index is invalid.
315 br.getSpreadIndices = function(pindex) {
316     // $$$ we could make a separate function for the RTL case and
317     //      only bind it if necessary instead of always checking
318     // $$$ we currently assume there are no gaps
319     
320     var spreadIndices = [null, null]; 
321     if ('rl' == this.pageProgression) {
322         // Right to Left
323         if (this.getPageSide(pindex) == 'R') {
324             spreadIndices[1] = pindex;
325             spreadIndices[0] = pindex + 1;
326         } else {
327             // Given index was LHS
328             spreadIndices[0] = pindex;
329             spreadIndices[1] = pindex - 1;
330         }
331     } else {
332         // Left to right
333         if (this.getPageSide(pindex) == 'L') {
334             spreadIndices[0] = pindex;
335             spreadIndices[1] = pindex + 1;
336         } else {
337             // Given index was RHS
338             spreadIndices[1] = pindex;
339             spreadIndices[0] = pindex - 1;
340         }
341     }
342     
343     //console.log("   index %d mapped to spread %d,%d", pindex, spreadIndices[0], spreadIndices[1]);
344     
345     return spreadIndices;
346 }
347
348 // Remove the page number assertions for all but the highest index page with
349 // a given assertion.  Ensures there is only a single page "{pagenum}"
350 // e.g. the last page asserted as page 5 retains that assertion.
351 br.uniquifyPageNums = function() {
352     var seen = {};
353     
354     for (var i = br.pageNums.length - 1; i--; i >= 0) {
355         var pageNum = br.pageNums[i];
356         if ( !seen[pageNum] ) {
357             seen[pageNum] = true;
358         } else {
359             br.pageNums[i] = null;
360         }
361     }
362
363 }
364
365 br.cleanupMetadata = function() {
366     br.uniquifyPageNums();
367 }
368
369 // getEmbedURL
370 //________
371 // Returns a URL for an embedded version of the current book
372 br.getEmbedURL = function(viewParams) {
373     // We could generate a URL hash fragment here but for now we just leave at defaults
374     var url = 'http://' + window.location.host + '/stream/'+this.bookId;
375     if (this.subPrefix != this.bookId) { // Only include if needed
376         url += '/' + this.subPrefix;
377     }
378     url += '?ui=embed';
379     if (typeof(viewParams) != 'undefined') {
380         url += '#' + this.fragmentFromParams(viewParams);
381     }
382     return url;
383 }
384
385 // getEmbedCode
386 //________
387 // Returns the embed code HTML fragment suitable for copy and paste
388 br.getEmbedCode = function(frameWidth, frameHeight, viewParams) {
389     return "<iframe src='" + this.getEmbedURL(viewParams) + "' width='" + frameWidth + "' height='" + frameHeight + "' frameborder='0' ></iframe>";
390 }
391
392 // getOpenLibraryRecord
393 br.getOpenLibraryRecord = function(callback) {
394     // Try looking up by ocaid first, then by source_record
395     
396     var self = this; // closure
397     
398     var jsonURL = self.olHost + '/query.json?type=/type/edition&*=&ocaid=' + self.bookId;
399     $.ajax({
400         url: jsonURL,
401         success: function(data) {
402             if (data && data.length > 0) {
403                 callback(self, data[0]);
404             } else {
405                 // try sourceid
406                 jsonURL = self.olHost + '/query.json?type=/type/edition&*=&source_records=ia:' + self.bookId;
407                 $.ajax({
408                     url: jsonURL,
409                     success: function(data) {
410                         if (data && data.length > 0) {
411                             callback(self, data[0]);
412                         }
413                     },
414                     dataType: 'jsonp'
415                 });
416             }
417         },
418         dataType: 'jsonp'
419     });
420 }
421
422 br.buildInfoDiv = function(jInfoDiv) {
423     // $$$ it might make more sense to have a URL on openlibrary.org that returns this info
424
425     var escapedTitle = BookReader.util.escapeHTML(this.bookTitle);
426     var domainRe = /(\w+\.(com|org))/;
427     var domainMatch = domainRe.exec(this.bookUrl);
428     var domain = this.bookUrl;
429     if (domainMatch) {
430         domain = domainMatch[1];
431     }
432        
433     // $$$ cover looks weird before it loads
434     jInfoDiv.find('.BRfloatCover').append([
435                     '<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('')
436     );
437
438     jInfoDiv.find('.BRfloatMeta').append([
439                     // $$$ description
440                     //'<p>Published ', this.bookPublished,
441                     //, <a href="Open Library Publisher Page">Publisher name</a>',
442                     //'</p>',
443                     //'<p>Written in <a href="Open Library Language page">Language</a></p>',
444                     '<h3>Other Formats</h3>',
445                     '<ul class="links">',
446                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '.pdf">PDF</a><span>|</span></li>',
447                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '_djvu.txt">Plain Text</a><span>|</span></li>',
448                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '_daisy.zip">DAISY</a><span>|</span></li>',
449                         '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '.epub">ePub</a><span>|</span></li>',
450                         '<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>',
451                     '</ul>',
452                     '<p class="moreInfo"><span></span>More information on <a href="'+ this.bookUrl + '">' + domain + '</a>  </p>'].join('\n'));
453                     
454     jInfoDiv.find('.BRfloatFoot').append([
455                 '<span>|</span>',                
456                 '<a href="http://openlibrary.org/contact" class="problem">Report a problem</a>',
457     ].join('\n'));
458                 
459     if (domain == 'archive.org') {
460         jInfoDiv.find('.BRfloatMeta p.moreInfo span').css(
461             {'background': 'url(http://www.archive.org/favicon.ico) no-repeat', 'width': 22, 'height': 18 }
462         );
463     }
464     
465     jInfoDiv.find('.BRfloatTitle a').attr({'href': this.bookUrl, 'alt': this.bookTitle}).text(this.bookTitle);
466     var bookPath = (window.location + '').replace('#','%23');
467     jInfoDiv.find('a.problem').attr('href','http://openlibrary.org/contact?path=' + bookPath);
468
469 }
470
471 br.pageW =  [
472             <?
473             $i=0;
474             foreach ($scanData->pageData->page as $page) {
475                 if (shouldAddPage($page)) {
476                     if(0 != $i) echo ",";   //stupid IE
477                     echo "{$page->cropBox->w}";
478                     $i++;
479                 }
480             }
481             ?>
482             ];
483
484 br.pageH =  [
485             <?
486             $totalHeight = 0;
487             $i=0;            
488             foreach ($scanData->pageData->page as $page) {
489                 if (shouldAddPage($page)) {
490                     if(0 != $i) echo ",";   //stupid IE                
491                     echo "{$page->cropBox->h}";
492                     $totalHeight += intval($page->cropBox->h/4) + 10;
493                     $i++;
494                 }
495             }
496             ?>
497             ];
498 br.leafMap = [
499             <?
500             $i=0;
501             foreach ($scanData->pageData->page as $page) {
502                 if (shouldAddPage($page)) {
503                     if(0 != $i) echo ",";   //stupid IE
504                     echo "{$page['leafNum']}";
505                     $i++;
506                 }
507             }
508             ?>    
509             ];
510
511 br.pageNums = [
512             <?
513             $i=0;
514             foreach ($scanData->pageData->page as $page) {
515                 if (shouldAddPage($page)) {
516                     if(0 != $i) echo ",";   //stupid IE                
517                     if (array_key_exists('pageNumber', $page) && ('' != $page->pageNumber)) {
518                         echo "'{$page->pageNumber}'";
519                     } else {
520                         echo "null";
521                     }
522                     $i++;
523                 }
524             }
525             ?>    
526             ];
527             
528       
529 br.numLeafs = br.pageW.length;
530
531 br.bookId   = '<?echo $id;?>';
532 br.zip      = '<?echo $imageStackFile;?>';
533 br.subPrefix = '<?echo $subPrefix;?>';
534 br.server   = '<?echo $server;?>';
535 br.bookTitle= '<?echo preg_replace("/\'/", "\\'", $metaData->title);?>';
536 br.bookPath = '<?echo $subItemPath;?>';
537 br.bookUrl  = '<?echo "http://www.archive.org/details/$id";?>';
538 br.imageFormat = '<?echo $imageFormat;?>';
539 br.archiveFormat = '<?echo $archiveFormat;?>';
540
541 <?
542
543 # Load some values from meta.xml
544 if ('' != $metaData->{'page-progression'}) {
545   echo "br.pageProgression = '" . $metaData->{"page-progression"} . "';\n";
546 } else {
547   // Assume page progression is Left To Right
548   echo "br.pageProgression = 'lr';\n";
549 }
550
551 $useOLAuth = false;
552 $protected = false;
553 foreach ($metaData->xpath('//collection') as $collection) {
554     if('browserlending' == $collection) {
555         $useOLAuth = true;
556         $protected = true;
557     }
558 }
559
560 echo "br.olHost = 'http://openlibrary.org';\n";
561 #echo "br.olHost = 'http://mang-dev.us.archive.org:8080';\n";
562
563 if ($useOLAuth) {
564     echo "br.olAuth = true;\n";
565 } else {
566     echo "br.olAuth = false;\n";
567 }
568
569 if ($protected) {
570     echo "br.protected = true;\n";
571 }
572
573 # Default options for BookReader
574 if ('' != $metaData->{'bookreader-defaults'}) {
575     echo "br.defaults = '" . $metaData->{'bookreader-defaults'} . "';\n";
576 }
577
578 ?>
579
580 // Check for config object
581 // $$$ change this to use the newer params object
582 if (typeof(brConfig) != 'undefined') {
583     if (typeof(brConfig["ui"]) != 'undefined') {
584         br.ui = brConfig["ui"];
585     }
586
587     if (brConfig['mode'] == 1) {
588         br.mode = 1;
589         if (typeof(brConfig['reduce'] != 'undefined')) {
590             br.reduce = brConfig['reduce'];
591         }
592     } else if (brConfig['mode'] == 2) {
593         br.mode = 2;      
594     }
595     
596     if (typeof(brConfig["isAdmin"]) != 'undefined') {
597         br.isAdmin = brConfig["isAdmin"];
598     } else {
599         br.isAdmin = false;
600     }
601 } // brConfig
602
603
604 function OLAuth() {
605     this.authUrl = br.olHost + '/ia_auth/' + br.bookId;
606     this.olConnect = false;
607     this.loanUUID = false;
608     this.permsToken = false;
609     
610     var cookieRe = /;\s*/;
611     var cookies = document.cookie.split(cookieRe);
612     var length = cookies.length;
613     var i;
614     for (i=0; i<length; i++) {
615         if (0 == cookies[i].indexOf('br-loan-' + br.bookId)) {
616             this.loanUUID = cookies[i].split('=')[1];
617         }
618         if (0 == cookies[i].indexOf('loan-' + br.bookId)) {
619             this.permsToken = cookies[i].split('=')[1];
620         }
621     }
622
623     return this;
624 }
625
626 OLAuth.prototype.init = function() {
627     var htmlStr =  'Checking loan status with Open Library';
628
629     this.showPopup("#F0EEE2", "#000", htmlStr, 'Please wait as we check the status of this book...');
630     var authUrl = this.authUrl+'?rand='+Math.random();
631     if (false !== this.loanUUID) {
632         authUrl += '&loan='+this.loanUUID
633     }
634     if (false !== this.permsToken) {
635         authUrl += '&token='+this.permsToken
636     }
637     $.ajax({url:authUrl, dataType:'jsonp', jsonpCallback:'olAuth.initCallback'});
638 }
639
640 OLAuth.prototype.showPopup = function(bgColor, textColor, msg, resolution) {
641     this.popup = document.createElement("div");
642     $(this.popup).css({
643         position: 'absolute',
644         top:      '50px',
645         left:     ($('#BookReader').attr('clientWidth')-400)/2 + 'px',
646         width:    '400px',
647         padding:  "15px",
648         border:   "3px double #999999",
649         zIndex:   3,
650         textAlign: 'center',
651         backgroundColor: bgColor,
652         color: textColor
653     }).appendTo('#BookReader');
654
655     this.setPopupMsg(msg, resolution);
656
657 }
658
659 OLAuth.prototype.setPopupMsg = function(msg, resolution) {
660     this.popup.innerHTML = ['<p><strong>', msg, '</strong></p><p>', resolution, '</p>'].join('\n');
661 }
662
663 OLAuth.prototype.showError = function(msg, resolution) {
664    $(this.popup).css({
665         backgroundColor: "#fff",
666         color: "#000"
667     });
668
669     this.setPopupMsg(msg, resolution);
670 }
671
672 OLAuth.prototype.initCallback = function(obj) {
673     if (false == obj.success) {
674         if (br.isAdmin) {
675             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?");
676             if (!ret) {
677                 this.showError(obj.msg, obj.resolution)
678             } else {
679                 br.init();
680             }
681         } else {
682             this.showError(obj.msg, obj.resolution)
683         }       
684     } else {    
685         //user is authenticated
686         this.setCookie(obj.token);
687         this.olConnect = true;
688         this.startPolling();    
689         br.init();
690     }
691 }
692
693 OLAuth.prototype.callback = function(obj) {
694     if (false == obj.success) {
695         this.showPopup("#F0EEE2", "#000", obj.msg, obj.resolution);
696         clearInterval(this.poller);
697         this.ttsPoller = null;
698     } else {
699         this.olConnect = true;
700         this.setCookie(obj.token);
701     }
702 }
703
704 OLAuth.prototype.setCookie = function(value) {
705     var date = new Date();
706     date.setTime(date.getTime()+(10*60*1000));  //10 min expiry
707     var expiry = date.toGMTString();
708     var cookie = 'loan-'+br.bookId+'='+value;
709     cookie    += '; expires='+expiry;
710     cookie    += '; path=/; domain=.archive.org;';
711     document.cookie = cookie;
712     this.permsToken = value;
713     
714     //refresh the br-loan uuid cookie with current expiry, if needed
715     if (false !== this.loanUUID) {
716         cookie = 'br-loan-'+br.bookId+'='+this.loanUUID;
717         cookie    += '; expires='+expiry;
718         cookie    += '; path=/; domain=.archive.org;';
719         document.cookie = cookie;
720     }
721 }
722
723 OLAuth.prototype.deleteCookies = function() {
724     var date = new Date();
725     date.setTime(date.getTime()-(24*60*60*1000));  //one day ago
726     var expiry = date.toGMTString();
727     var cookie = 'loan-'+br.bookId+'=""';
728     cookie    += '; expires='+expiry;
729     cookie    += '; path=/; domain=.archive.org;';
730     document.cookie = cookie;
731     
732     cookie = 'br-loan-'+br.bookId+'=""';
733     cookie    += '; expires='+expiry;
734     cookie    += '; path=/; domain=.archive.org;';
735     document.cookie = cookie;
736 }
737
738 OLAuth.prototype.startPolling = function () {    
739     var self = this;
740     this.poller=setInterval(function(){
741         if (!self.olConnect) {
742           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.');
743           clearInterval(self.poller);
744           self.ttsPoller = null;        
745         } else {
746           self.olConnect = false;
747           //be sure to add random param to authUrl to avoid stale cache
748           var authUrl = self.authUrl+'?rand='+Math.random();
749           if (false !== self.loanUUID) {
750               authUrl += '&loan='+self.loanUUID
751           }
752           if (false !== self.permsToken) {
753               authUrl += '&token='+self.permsToken
754           }
755
756           $.ajax({url:authUrl, dataType:'jsonp', jsonpCallback:'olAuth.callback'});
757         }
758     },300000);   //five minute interval
759 }
760
761 br.cleanupMetadata();
762 if (br.olAuth) {
763     var olAuth = new OLAuth();
764     olAuth.init();
765 } else {
766     br.init();
767 }
768 <?
769
770
771 function BRFatal($string) {
772     // log error
773     ?>
774     
775     if (typeof(archive_analytics) != 'undefined') {
776         var values = {
777             'bookreader': 'fatal',
778             'description': "<? echo $string; ?>",
779             'itemid': "<? echo $_REQUEST['id']; ?>",
780             'server': "<? echo $_REQUEST['server']; ?>",
781             'request_uri': "<? echo $_SERVER["REQUEST_URI"]; ?>"
782         }
783         
784         if (document.referrer == '') {
785             values['referrer'] = '-';
786         } else {
787             values['referrer'] = document.referrer;
788         }
789         
790         var qs = archive_analytics.format_bug(values);
791
792         var error_img = new Image(100,25);
793         error_img.src = archive_analytics.img_src + "?" + qs;
794     }
795
796     alert("<? echo $string;?>");
797     
798     <?
799     
800     die(-1);
801 }
802
803 // Returns true if a page should be added based on it's information in
804 // the metadata
805 function shouldAddPage($page) {
806     // Return false only if the page is marked addToAccessFormats false.
807     // If there is no assertion we assume it should be added.
808     if (isset($page->addToAccessFormats)) {
809         if ("false" == strtolower(trim($page->addToAccessFormats))) {
810             return false;
811         }
812     }
813     
814     return true;
815 }
816
817 // Returns { 'imageFormat' => , 'archiveFormat' => '} given a sub-item prefix and loaded xml data
818 function findImageStack($subPrefix, $filesData) {
819
820     // $$$ The order of the image formats determines which will be returned first
821     $imageFormats = array('JP2' => 'jp2', 'TIFF' => 'tif', 'JPEG' => 'jpg');
822     $archiveFormats = array('ZIP' => 'zip', 'Tar' => 'tar');
823     $imageGroup = implode('|', array_keys($imageFormats));
824     $archiveGroup = implode('|', array_keys($archiveFormats));
825     // $$$ Currently only return processed images
826     $imageStackRegex = "/Single Page (Processed) (${imageGroup}) (${archiveGroup})/";
827         
828     foreach ($filesData->file as $file) {        
829         if (strpos($file['name'], $subPrefix) === 0) { // subprefix matches beginning
830             if (preg_match($imageStackRegex, $file->format, $matches)) {
831             
832                 // Make sure we have a regular image stack
833                 $imageFormat = $imageFormats[$matches[2]];
834                 if (strpos($file['name'], $subPrefix . '_' . $imageFormat) === 0) {            
835                     return array('imageFormat' => $imageFormat,
836                                  'archiveFormat' => $archiveFormats[$matches[3]],
837                                  'imageStackFile' => $file['name']);
838                 }
839             }
840         }
841     }
842     
843     return array('imageFormat' => 'unknown', 'archiveFormat' => 'unknown', 'imageStackFile' => 'unknown');
844         
845 }
846
847 ?>
848