Fix typo in CSS
[bookreader.git] / BookReaderIA / datanode / BookReaderJSIA.php
old mode 100755 (executable)
new mode 100644 (file)
index f65fb7b..b5d9c18
@@ -18,24 +18,32 @@ This file is part of BookReader.
     along with BookReader.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+header('Content-Type: application/javascript');
+
 $id = $_REQUEST['id'];
 $itemPath = $_REQUEST['itemPath'];
 $subPrefix = $_REQUEST['subPrefix'];
 $server = $_REQUEST['server'];
 
+// $$$mang this code has been refactored into BookReaderMeta.inc.php for use e.g. by
+//         BookReaderPreview.php and BookReaderImages.php.  The code below should be
+//         taken out and replaced by calls into BookReaderMeta
+
 // Check if we're on a dev vhost and point to JSIA in the user's public_html on the datanode
+
 // $$$ TODO consolidate this logic
 if (strpos($_SERVER["REQUEST_URI"], "/~mang") === 0) { // Serving out of home dir
     $server .= ':80/~mang';
+} else if (strpos($_SERVER["REQUEST_URI"], "/~rkumar") === 0) { // Serving out of home dir
+    $server .= ':80/~rkumar';
 } else if (strpos($_SERVER["REQUEST_URI"], "/~testflip") === 0) { // Serving out of home dir
     $server .= ':80/~testflip';
 }
 
-if ($subPrefix) {
-    $subItemPath = $itemPath . '/' . $subPrefix;
-} else {
-    $subItemPath = $itemPath . '/' . $id;
+if (! $subPrefix) {
+    $subPrefix = $id;
 }
+$subItemPath = $itemPath . '/' . $subPrefix;
 
 if ("" == $id) {
     BRFatal("No identifier specified!");
@@ -55,22 +63,32 @@ if (!preg_match("|^/\d+/items/{$id}$|", $itemPath)) {
 
 // XXX check here that subitem is okay
 
-$imageFormat = 'unknown';
-$zipFile = "${subItemPath}_jp2.zip";
+$filesDataFile = "$itemPath/${id}_files.xml";
 
-if (file_exists($zipFile)) {
-    $imageFormat = 'jp2';
+if (file_exists($filesDataFile)) {
+    $filesData = simplexml_load_file("$itemPath/${id}_files.xml");
 } else {
-  $zipFile = "${subItemPath}_tif.zip";
-  if (file_exists($zipFile)) {
-    $imageFormat = 'tif';
-  }
+    BRfatal("File metadata not found!");
 }
 
+$imageStackInfo = findImageStack($subPrefix, $filesData);
+if ($imageStackInfo['imageFormat'] == 'unknown') {
+    BRfatal('Couldn\'t find image stack');
+}
+
+$imageFormat = $imageStackInfo['imageFormat'];
+$archiveFormat = $imageStackInfo['archiveFormat'];
+$imageStackFile = $itemPath . "/" . $imageStackInfo['imageStackFile'];
+
 if ("unknown" == $imageFormat) {
   BRfatal("Unknown image format");
 }
 
+if ("unknown" == $archiveFormat) {
+  BRfatal("Unknown archive format");
+}
+
+
 $scanDataFile = "${subItemPath}_scandata.xml";
 $scanDataZip  = "$itemPath/scandata.zip";
 if (file_exists($scanDataFile)) {
@@ -100,6 +118,44 @@ $metaData = simplexml_load_file($metaDataFile);
 //$firstLeaf = $scanData->pageData->page[0]['leafNum'];
 ?>
 
+// Error reporting - this helps us fix errors quickly
+function logError(description,page,line) {
+    if (typeof(archive_analytics) != 'undefined') {
+        var values = {
+            'bookreader': 'error',
+            'description': description,
+            'page': page,
+            'line': line,
+            'itemid': '<?echo $id;?>',
+            'subPrefix': '<?echo $subPrefix;?>',
+            'server': '<?echo $server;?>',
+            'bookPath': '<?echo $subItemPath;?>'
+        };
+
+        // if no referrer set '-' as referrer
+        if (document.referrer == '') {
+            values['referrer'] = '-';
+        } else {
+            values['referrer'] = document.referrer;
+        }
+        
+        if (typeof(br) != 'undefined') {
+            values['itemid'] = br.bookId;
+            values['subPrefix'] = br.subPrefix;
+            values['server'] = br.server;
+            values['bookPath'] = br.bookPath;
+        }
+        
+        var qs = archive_analytics.format_bug(values);
+
+        var error_img = new Image(100,25);
+        error_img.src = archive_analytics.img_src + "?" + qs;
+    }
+
+    return false; // allow browser error handling so user sees there was a problem
+}
+window.onerror=logError;
+
 br = new BookReader();
 
 <?
@@ -150,34 +206,7 @@ br.getPageURI = function(index, reduce, rotate) {
     var file = this._getPageFile(index);
         
     // $$$ add more image stack formats here
-    if (1==this.mode) {
-        var url = 'http://'+this.server+'/BookReader/BookReaderImages.php?zip='+this.zip+'&file='+file+'&scale='+_reduce+'&rotate='+_rotate;
-    } else {
-        if ('undefined' == typeof(reduce)) {
-            // reduce not passed in
-            var ratio = this.getPageHeight(index) / this.twoPage.height;
-            var scale;
-            // $$$ we make an assumption here that the scales are available pow2 (like kakadu)
-            if (ratio < 2) {
-                scale = 1;
-            } else if (ratio < 4) {
-                scale = 2;
-            } else if (ratio < 8) {
-                scale = 4;
-            } else if (ratio < 16) {
-                scale = 8;
-            } else  if (ratio < 32) {
-                scale = 16;
-            } else {
-                scale = 32;
-            }
-            _reduce = scale;
-        }
-    
-        var url = 'http://'+this.server+'/BookReader/BookReaderImages.php?zip='+this.zip+'&file='+file+'&scale='+_reduce+'&rotate='+_rotate;
-        
-    }
-    return url;
+    return 'http://'+this.server+'/BookReader/BookReaderImages.php?zip='+this.zip+'&file='+file+'&scale='+_reduce+'&rotate='+_rotate;
 }
 
 br._getPageFile = function(index) {
@@ -242,13 +271,24 @@ br.getPageNum = function(index) {
     }
 }
 
+// Single images in the Internet Archive scandata.xml metadata are (somewhat incorrectly)
+// given a "leaf" number.  Some of these images from the scanning process should not
+// be displayed in the BookReader (for example colour calibration cards).  Since some
+// of the scanned images will not be displayed in the BookReader (those marked with
+// addToAccessFormats false in the scandata.xml) leaf numbers and BookReader page
+// indexes are generally not the same.  This function returns the BookReader page
+// index given a scanned leaf number.
+//
+// This function is used, for example, to map between search results (that use the
+// leaf numbers) and the displayed pages in the BookReader.
 br.leafNumToIndex = function(leafNum) {
-    var index = jQuery.inArray(leafNum, this.leafMap);
-    if (-1 == index) {
-        return null;
-    } else {
-        return index;
+    for (var index = 0; index < this.leafMap.length; index++) {
+        if (this.leafMap[index] == leafNum) {
+            return index;
+        }
     }
+    
+    return null;
 }
 
 // This function returns the left and right indices for the user-visible
@@ -311,24 +351,104 @@ br.cleanupMetadata = function() {
 // getEmbedURL
 //________
 // Returns a URL for an embedded version of the current book
-br.getEmbedURL = function() {
+br.getEmbedURL = function(viewParams) {
     // We could generate a URL hash fragment here but for now we just leave at defaults
     var url = 'http://' + window.location.host + '/stream/'+this.bookId;
     if (this.subPrefix != this.bookId) { // Only include if needed
         url += '/' + this.subPrefix;
     }
     url += '?ui=embed';
+    if (typeof(viewParams) != 'undefined') {
+        url += '#' + this.fragmentFromParams(viewParams);
+    }
     return url;
 }
 
 // getEmbedCode
 //________
 // Returns the embed code HTML fragment suitable for copy and paste
-br.getEmbedCode = function() {
-    return "<iframe src='" + this.getEmbedURL() + "' width='480px' height='430px'></iframe>";
+br.getEmbedCode = function(frameWidth, frameHeight, viewParams) {
+    return "<iframe src='" + this.getEmbedURL(viewParams) + "' width='" + frameWidth + "' height='" + frameHeight + "' frameborder='0' ></iframe>";
+}
+
+// getOpenLibraryRecord
+br.getOpenLibraryRecord = function(callback) {
+    // Try looking up by ocaid first, then by source_record
+    
+    var jsonURL = this.olHost + '/query.json?type=/type/edition&*=&ocaid=' + br.bookId;
+    $.ajax({
+        url: jsonURL,
+        success: function(data) {
+            if (data && data.length > 0) {
+                callback(br, data[0]);
+            } else {
+                // try sourceid
+                jsonURL = this.olHost + '/query.json?type=/type/edition&*=&source_records=ia:' + br.bookId;
+                $.ajax({
+                    url: jsonURL,
+                    success: function(data) {
+                        if (data && data.length > 0) {
+                            callback(br, data[0]);
+                        }
+                    },
+                    dataType: 'jsonp'
+                });
+            }
+        },
+        dataType: 'jsonp'
+    });
 }
 
-br.pageW =             [
+br.buildInfoDiv = function(jInfoDiv) {
+    // $$$ it might make more sense to have a URL on openlibrary.org that returns this info
+
+    var escapedTitle = BookReader.util.escapeHTML(this.bookTitle);
+    var domainRe = /(\w+\.(com|org))/;
+    var domainMatch = domainRe.exec(this.bookUrl);
+    var domain = this.bookUrl;
+    if (domainMatch) {
+        domain = domainMatch[1];
+    }
+       
+    // $$$ cover looks weird before it loads
+    jInfoDiv.find('.BRfloatCover').append([
+                    '<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('')
+    );
+
+    jInfoDiv.find('.BRfloatMeta').append([
+                    // $$$ description
+                    //'<p>Published ', this.bookPublished,
+                    //, <a href="Open Library Publisher Page">Publisher name</a>',
+                    //'</p>',
+                    //'<p>Written in <a href="Open Library Language page">Language</a></p>',
+                    '<h3>Other Formats</h3>',
+                    '<ul class="links">',
+                        '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '.pdf">PDF</a><span>|</span></li>',
+                        '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '_djvu.txt">Plain Text</a><span>|</span></li>',
+                        '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '_daisy.zip">DAISY</a><span>|</span></li>',
+                        '<li><a href="http://www.archive.org/download/', this.bookId, '/', this.subPrefix, '.epub">ePub</a><span>|</span></li>',
+                        '<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>',
+                    '</ul>',
+                    '<p class="moreInfo"><span></span>More information on <a href="'+ this.bookUrl + '">' + domain + '</a>  </p>'].join('\n'));
+                    
+    jInfoDiv.find('.BRfloatFoot').append([
+                '<span>|</span>',                
+                '<a href="http://openlibrary.org/contact" class="problem">Report a problem</a>',
+    ].join('\n'));
+                
+    if (domain == 'archive.org') {
+        jInfoDiv.find('.BRfloatMeta p.moreInfo span').css(
+            {'background': 'url(http://www.archive.org/favicon.ico) no-repeat', 'width': 22, 'height': 18 }
+        );
+    }
+    
+    jInfoDiv.find('.BRfloatTitle a').attr({'href': this.bookUrl, 'alt': this.bookTitle}).text(this.bookTitle);
+    var bookPath = (window.location + '').replace('#','%23');
+    jInfoDiv.find('a.problem').attr('href','http://openlibrary.org/contact?path=' + bookPath);
+
+}
+
+br.pageW =  [
             <?
             $i=0;
             foreach ($scanData->pageData->page as $page) {
@@ -341,7 +461,7 @@ br.pageW =          [
             ?>
             ];
 
-br.pageH =             [
+br.pageH =  [
             <?
             $totalHeight = 0;
             $i=0;            
@@ -389,22 +509,39 @@ br.pageNums = [
 br.numLeafs = br.pageW.length;
 
 br.bookId   = '<?echo $id;?>';
-br.zip      = '<?echo $zipFile;?>';
+br.zip      = '<?echo $imageStackFile;?>';
 br.subPrefix = '<?echo $subPrefix;?>';
 br.server   = '<?echo $server;?>';
 br.bookTitle= '<?echo preg_replace("/\'/", "\\'", $metaData->title);?>';
 br.bookPath = '<?echo $subItemPath;?>';
 br.bookUrl  = '<?echo "http://www.archive.org/details/$id";?>';
 br.imageFormat = '<?echo $imageFormat;?>';
+br.archiveFormat = '<?echo $archiveFormat;?>';
 
 <?
 
 # Load some values from meta.xml
 if ('' != $metaData->{'page-progression'}) {
-  echo "br.pageProgression = '" . $metaData->{"page-progression"} . "';";
+  echo "br.pageProgression = '" . $metaData->{"page-progression"} . "';\n";
 } else {
   // Assume page progression is Left To Right
-  echo "br.pageProgression = 'lr';";
+  echo "br.pageProgression = 'lr';\n";
+}
+
+$useOLAuth = false;
+foreach ($metaData->xpath('//collection') as $collection) {
+    if('browserlending' == $collection) {
+        $useOLAuth = true;
+    }
+}
+
+#echo "br.olHost = 'http://openlibrary.org'\n";
+echo "br.olHost = 'http://ol-mang:8080'\n";
+
+if ($useOLAuth) {
+    echo "br.olAuth = true;\n";
+} else {
+    echo "br.olAuth = false;\n";
 }
 
 # Special cases
@@ -428,28 +565,179 @@ if (typeof(brConfig) != 'undefined') {
             br.reduce = brConfig['reduce'];
         }
     } else if (brConfig['mode'] == 2) {
-        br.mode = 2;
-      
-<?
-        //$$$mang hack to override request for 2up for books with attribution page
-        //   as first page until we can display that page in 2up
-        $needle = 'goog';
-        if (strrpos($id, $needle) === strlen($id)-strlen($needle)) {
-            print "// override for books with attribution page\n";
-            print "br.mode = 1;\n";
-        }
-?>
+        br.mode = 2;      
     }
 } // brConfig
 
-br.cleanupMetadata();
-br.init();
 
+function OLAuth() {
+    this.authUrl = br.olHost + '/ia_auth/' + br.bookId;
+    this.olConnect = false;
+    this.loanUUID = false;
+    this.loanToken = false;
+    
+    var cookieRe = /;\s*/;
+    var cookies = document.cookie.split(cookieRe);
+    var length = cookies.length;
+    var i;
+    for (i=0; i<length; i++) {
+        if (0 == cookies[i].indexOf('br-loan-' + br.bookId)) {
+            this.loanUUID = cookies[i].split('=')[1];
+        }
+        if (0 == cookies[i].indexOf('loan-' + br.bookId)) {
+            this.loanToken = cookies[i].split('=')[1];
+        }
+    }
+
+    return this;
+}
+
+OLAuth.prototype.init = function() {
+    var htmlStr =  'Checking loan status with Open Library';
+
+    this.showPopup("#ddd", "#000", htmlStr, 'Please wait as we check the status of this book...');
+    var authUrl = this.authUrl+'?rand='+Math.random();
+    if (false !== this.loanUUID) {
+        authUrl += '&loan='+this.loanUUID
+    }
+    if (false !== this.loanToken) {
+        authUrl += '&token='+this.loanToken
+    }
+    $.ajax({url:authUrl, dataType:'jsonp', jsonpCallback:'olAuth.initCallback'});
+}
+
+OLAuth.prototype.showPopup = function(bgColor, textColor, msg, resolution) {
+    this.popup = document.createElement("div");
+    $(this.popup).css({
+        position: 'absolute',
+        top:      '20px',
+        left:     ($('#BookReader').attr('clientWidth')-400)/2 + 'px',
+        width:    '400px',
+        padding:  "20px",
+        border:   "3px double #999999",
+        zIndex:   3,
+        textAlign: 'center',
+        backgroundColor: bgColor,
+        color: textColor
+    }).appendTo('#BookReader');
+
+    this.setPopupMsg(msg, resolution);
+
+}
+
+OLAuth.prototype.setPopupMsg = function(msg, resolution) {
+    this.popup.innerHTML = ['<p><strong>', msg, '</strong></p><p>', resolution, '</p>'].join('\n');
+}
+
+OLAuth.prototype.initCallback = function(obj) {
+    if (false == obj.success) {
+        $(this.popup).css({
+            backgroundColor: "#fff",
+            color: "#000"
+        });
+
+        this.setPopupMsg(obj.msg, obj.resolution);
+        return;
+    }
+    
+    //user is authenticated
+    this.setCookie(obj.token);
+    this.olConnect = true;
+    this.startPolling();    
+    br.init();
+}
+
+OLAuth.prototype.callback = function(obj) {
+    if (false == obj.success) {
+        this.showPopup("#fff", "#000", obj.msg, obj.resolution);
+        clearInterval(this.poller);
+        this.ttsPoller = null;
+    } else {
+        this.olConnect = true;
+        this.setCookie(obj.token);
+    }
+}
+
+OLAuth.prototype.setCookie = function(value) {
+    var date = new Date();
+    date.setTime(date.getTime()+(10*60*1000));  //10 min expiry
+    var expiry = date.toGMTString();
+    var cookie = 'loan-'+br.bookId+'='+value;
+    cookie    += '; expires='+expiry;
+    cookie    += '; path=/; domain=.archive.org;';
+    document.cookie = cookie;
+    
+    //refresh the br-loan uuid cookie with current expiry, if needed
+    if (false !== this.loanUUID) {
+        cookie = 'br-loan-'+br.bookId+'='+this.loanUUID;
+        cookie    += '; expires='+expiry;
+        cookie    += '; path=/; domain=.archive.org;';
+        document.cookie = cookie;
+    }
+}
+
+OLAuth.prototype.startPolling = function () {    
+    var self = this;
+    this.poller=setInterval(function(){
+        if (!self.olConnect) {
+          self.showPopup("#f00", "#fff", '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 or try again later.');
+          clearInterval(self.poller);
+          self.ttsPoller = null;        
+        } else {
+          self.olConnect = false;
+          //be sure to add random param to authUrl to avoid stale cache
+          var authUrl = self.authUrl+'?rand='+Math.random();
+          if (false !== self.loanUUID) {
+              authUrl += '&loan='+self.loanUUID
+          }
+          if (false !== self.loanToken) {
+              authUrl += '&token='+self.loanToken
+          }
+
+          $.ajax({url:authUrl, dataType:'jsonp', jsonpCallback:'olAuth.callback'});
+        }
+    },300000);   //five minute interval
+}
+
+br.cleanupMetadata();
+if (br.olAuth) {
+    var olAuth = new OLAuth();
+    olAuth.init();
+} else {
+    br.init();
+}
 <?
 
 
 function BRFatal($string) {
-    echo "alert('$string')\n";
+    // log error
+    ?>
+    
+    if (typeof(archive_analytics) != 'undefined') {
+        var values = {
+            'bookreader': 'fatal',
+            'description': "<? echo $string; ?>",
+            'itemid': "<? echo $_REQUEST['id']; ?>",
+            'server': "<? echo $_REQUEST['server']; ?>",
+            'request_uri': "<? echo $_SERVER["REQUEST_URI"]; ?>"
+        }
+        
+        if (document.referrer == '') {
+            values['referrer'] = '-';
+        } else {
+            values['referrer'] = document.referrer;
+        }
+        
+        var qs = archive_analytics.format_bug(values);
+
+        var error_img = new Image(100,25);
+        error_img.src = archive_analytics.img_src + "?" + qs;
+    }
+
+    alert("<? echo $string;?>");
+    
+    <?
+    
     die(-1);
 }
 
@@ -467,4 +755,35 @@ function shouldAddPage($page) {
     return true;
 }
 
+// Returns { 'imageFormat' => , 'archiveFormat' => '} given a sub-item prefix and loaded xml data
+function findImageStack($subPrefix, $filesData) {
+
+    // $$$ The order of the image formats determines which will be returned first
+    $imageFormats = array('JP2' => 'jp2', 'TIFF' => 'tif', 'JPEG' => 'jpg');
+    $archiveFormats = array('ZIP' => 'zip', 'Tar' => 'tar');
+    $imageGroup = implode('|', array_keys($imageFormats));
+    $archiveGroup = implode('|', array_keys($archiveFormats));
+    // $$$ Currently only return processed images
+    $imageStackRegex = "/Single Page (Processed) (${imageGroup}) (${archiveGroup})/";
+        
+    foreach ($filesData->file as $file) {        
+        if (strpos($file['name'], $subPrefix) === 0) { // subprefix matches beginning
+            if (preg_match($imageStackRegex, $file->format, $matches)) {
+            
+                // Make sure we have a regular image stack
+                $imageFormat = $imageFormats[$matches[2]];
+                if (strpos($file['name'], $subPrefix . '_' . $imageFormat) === 0) {            
+                    return array('imageFormat' => $imageFormat,
+                                 'archiveFormat' => $archiveFormats[$matches[3]],
+                                 'imageStackFile' => $file['name']);
+                }
+            }
+        }
+    }
+    
+    return array('imageFormat' => 'unknown', 'archiveFormat' => 'unknown', 'imageStackFile' => 'unknown');
+        
+}
+
 ?>
+