From: scollett Date: Tue, 9 Mar 2010 17:50:23 +0000 (-0800) Subject: Merged changes upstream X-Git-Url: http://git.rot13.org/?a=commitdiff_plain;h=dfb5a3ab7ef6be1a92ecbfd65935534980e6ff7d;hp=cc12edaf80ae1d20c92da04e89cb98591789afe0;p=bookreader.git Merged changes upstream --- diff --git a/BookReader/BookReader.css b/BookReader/BookReader.css index 24191c4..c81dac9 100644 --- a/BookReader/BookReader.css +++ b/BookReader/BookReader.css @@ -157,9 +157,9 @@ a.BRicon { vertical-align: middle; } -a.BRwhite { color: #fff } -a.BRwhite:hover { text-decoration: none; } -a.BRwhite:visited { color: #fff } +a.BRwhite { color: #fff } +a.BRwhite:hover { text-decoration: none; } +a.BRwhite:visited { color: #fff } a.BRblack { color: #000000 } a.BRblack:hover { text-decoration: none; } diff --git a/BookReader/BookReader.js b/BookReader/BookReader.js index b24d57e..0c101a3 100644 --- a/BookReader/BookReader.js +++ b/BookReader/BookReader.js @@ -42,9 +42,8 @@ function BookReader() { this.ui = 'full'; // UI mode this.thumbWidth = 100; this.thumbRowBuffer = 3; // number of rows to pre-cache out a view - - this.displayedIndices = []; this.displayedRows=[]; + this.displayedIndices = []; //this.indicesToDisplay = []; this.imgs = {}; this.prefetchedImgs = {}; //an object with numeric keys cooresponding to page index @@ -2379,13 +2378,13 @@ BookReader.prototype.search = function(term) { term = term.replace(/\//g, ' '); // strip slashes this.searchTerm = term; $('#BookReaderSearchScript').remove(); - var script = document.createElement("script"); - script.setAttribute('id', 'BookReaderSearchScript'); - script.setAttribute("type", "text/javascript"); - script.setAttribute("src", 'http://'+this.server+'/BookReader/flipbook_search_br.php?url='+escape(this.bookPath + '_djvu.xml')+'&term='+term+'&format=XML&callback=br.BRSearchCallback'); - document.getElementsByTagName('head')[0].appendChild(script); - $('#BookReaderSearchBox').val(term); - $('#BookReaderSearchResults').html('Searching...'); + var script = document.createElement("script"); + script.setAttribute('id', 'BookReaderSearchScript'); + script.setAttribute("type", "text/javascript"); + script.setAttribute("src", 'http://'+this.server+'/BookReader/flipbook_search_br.php?url='+escape(this.bookPath + '_djvu.xml')+'&term='+term+'&format=XML&callback=br.BRSearchCallback'); + document.getElementsByTagName('head')[0].appendChild(script); + $('#BookReaderSearchBox').val(term); + $('#BookReaderSearchResults').html('Searching...'); } // BRSearchCallback() @@ -2457,7 +2456,7 @@ BookReader.prototype.BRSearchCallback = function(txt) { $('#BookReaderSearchResults').append(''); // $$$ update again for case of loading search URL in new browser window (search box may not have been ready yet) - $('#BookReaderSearchBox').val(this.searchTerm); + $('#BookReaderSearchBox').val(this.searchTerm); this.updateSearchHilites(); } @@ -3603,6 +3602,27 @@ BookReader.prototype._getPageURI = function(index, reduce, rotate) { return this.imagesBaseURL + "/transparent.png"; } + 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; + } + return this.getPageURI(index, reduce, rotate); } diff --git a/BookReaderIA/Makefile b/BookReaderIA/Makefile new file mode 100644 index 0000000..350913b --- /dev/null +++ b/BookReaderIA/Makefile @@ -0,0 +1,6 @@ +test: + @ echo Made tests + + + + diff --git a/BookReaderIA/datanode/BookReaderImages.php b/BookReaderIA/datanode/BookReaderImages.php index 8f4e7fe..2895724 100644 --- a/BookReaderIA/datanode/BookReaderImages.php +++ b/BookReaderIA/datanode/BookReaderImages.php @@ -1,9 +1,14 @@ . */ -$MIMES = array('jpg' => 'image/jpeg', - 'png' => 'image/png'); +$MIMES = array('gif' => 'image/gif', + 'jp2' => 'image/jp2', + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'png' => 'image/png', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff'); + +$EXTENSIONS = array('gif' => 'gif', + 'jp2' => 'jp2', + 'jpeg' => 'jpeg', + 'jpg' => 'jpeg', + 'png' => 'png', + 'tif' => 'tiff', + 'tiff' => 'tiff'); + +$exiftool = '/petabox/sw/books/exiftool/exiftool'; +// Process some of the request parameters $zipPath = $_REQUEST['zip']; $file = $_REQUEST['file']; +if (isset($_REQUEST['ext'])) { + $ext = $_REQUEST['ext']; +} else { + // Default to jpg + $ext = 'jpeg'; +} +if (isset($_REQUEST['callback'])) { + // validate callback is valid JS identifier (only) + $callback = $_REQUEST['callback']; + $identifierPatt = '/^[[:alpha:]$_]([[:alnum:]$_])*$/'; + if (! preg_match($identifierPatt, $callback)) { + BRfatal('Invalid callback'); + } +} else { + $callback = null; +} + +/* + * Approach: + * + * Get info about requested image (input) + * Get info about requested output format + * Determine processing parameters + * Process image + * Return image data + * Clean up temporary files + */ + +function getUnarchiveCommand($archivePath, $file) +{ + $lowerPath = strtolower($archivePath); + if (preg_match('/\.([^\.]+)$/', $lowerPath, $matches)) { + $suffix = $matches[1]; + + if ($suffix == 'zip') { + return 'unzip -p ' + . escapeshellarg($archivePath) + . ' ' . escapeshellarg($file); + } else if ($suffix == 'tar') { + return '7z e -so ' + . escapeshellarg($archivePath) + . ' ' . escapeshellarg($file); + } else { + BRfatal('Incompatible archive format'); + } + + } else { + BRfatal('Bad image stack path'); + } + + BRfatal('Bad image stack path or archive format'); + +} + +/* + * Returns the image type associated with the file extension. + */ +function imageExtensionToType($extension) +{ + global $EXTENSIONS; + + if (array_key_exists($extension, $EXTENSIONS)) { + return $EXTENSIONS[$extension]; + } else { + BRfatal('Unknown image extension'); + } +} + +/* + * Get the image width, height and depth from a jp2 file in zip. + */ +function getImageInfo($zipPath, $file) +{ + global $exiftool; + + $fileExt = strtolower(pathinfo($file, PATHINFO_EXTENSION)); + $type = imageExtensionToType($fileExt); + + // We look for all the possible tags of interest then act on the + // ones presumed present based on the file type + $tagsToGet = ' -ImageWidth -ImageHeight -FileType' // all formats + . ' -BitsPerComponent -ColorSpace' // jp2 + . ' -BitDepth' // png + . ' -BitsPerSample'; // tiff + + $cmd = getUnarchiveCommand($zipPath, $file) + . ' | '. $exiftool . ' -S -fast' . $tagsToGet . ' -'; + exec($cmd, $output); + + $tags = Array(); + foreach ($output as $line) { + $keyValue = explode(": ", $line); + $tags[$keyValue[0]] = $keyValue[1]; + } + + $width = intval($tags["ImageWidth"]); + $height = intval($tags["ImageHeight"]); + $type = strtolower($tags["FileType"]); + + switch ($type) { + case "jp2": + $bits = intval($tags["BitsPerComponent"]); + break; + case "tiff": + $bits = intval($tags["BitsPerSample"]); + break; + case "jpeg": + $bits = 8; + break; + case "png": + $bits = intval($tags["BitDepth"]); + break; + default: + BRfatal("Unsupported image type"); + break; + } + + + $retval = Array('width' => $width, 'height' => $height, + 'bits' => $bits, 'type' => $type); + + return $retval; +} + +/* + * Output JSON given the imageInfo associative array + */ +function outputJSON($imageInfo, $callback) +{ + header('Content-type: text/plain'); + $jsonOutput = json_encode($imageInfo); + if ($callback) { + $jsonOutput = $callback . '(' . $jsonOutput . ');'; + } + echo $jsonOutput; +} + +// Get the image size and depth +$imageInfo = getImageInfo($zipPath, $file); + +// Output json if requested +if ('json' == $ext) { + // $$$ we should determine the output size first based on requested scale + outputJSON($imageInfo, $callback); + exit; +} // Unfortunately kakadu requires us to know a priori if the // output file should be .ppm or .pgm. By decompressing to @@ -38,13 +205,6 @@ if ($decompressToBmp) { $stdoutLink = '/tmp/stdout.ppm'; } -if (isset($_REQUEST['ext'])) { - $ext = $_REQUEST['ext']; -} else { - // Default to jpg - $ext = 'jpg'; -} - $fileExt = strtolower(pathinfo($file, PATHINFO_EXTENSION)); // Rotate is currently only supported for jp2 since it does not add server load @@ -60,7 +220,7 @@ $jpegOptions = '-quality 75'; // The pbmreduce reduction factor produces an image with dimension 1/n // The kakadu reduction factor produceds an image with dimension 1/(2^n) - +// $$$ handle continuous values for scale if (isset($_REQUEST['height'])) { $ratio = floatval($_REQUEST['origHeight']) / floatval($_REQUEST['height']); if ($ratio <= 2) { @@ -97,6 +257,22 @@ if (isset($_REQUEST['height'])) { } } +// Override depending on source image format +// $$$ consider doing a 302 here instead, to make better use of the browser cache +// Limit scaling for 1-bit images. See https://bugs.edge.launchpad.net/bookreader/+bug/486011 +if (1 == $imageInfo['bits']) { + if ($scale > 1) { + $scale /= 2; + $powReduce -= 1; + + // Hard limit so there are some black pixels to use! + if ($scale > 4) { + $scale = 4; + $powReduce = 2; + } + } +} + if (!file_exists($stdoutLink)) { system('ln -s /dev/stdout ' . $stdoutLink); @@ -105,39 +281,40 @@ if (!file_exists($stdoutLink)) putenv('LD_LIBRARY_PATH=/petabox/sw/lib/kakadu'); -$unzipCmd = 'unzip -p ' . - escapeshellarg($zipPath) . - ' ' . escapeshellarg($file); - -if ('jp2' == $fileExt) { - $decompressCmd = - " | /petabox/sw/bin/kdu_expand -no_seek -quiet -reduce $powReduce -rotate $rotate -i /dev/stdin -o " . $stdoutLink; - if ($decompressToBmp) { - $decompressCmd .= ' | bmptopnm '; - } - -} else if ('tif' == $fileExt) { - // We need to create a temporary file for tifftopnm since it cannot - // work on a pipe (the file must be seekable). - // We use the BookReaderTiff prefix to give a hint in case things don't - // get cleaned up. - $tempFile = tempnam("/tmp", "BookReaderTiff"); - - if (1 != $scale) { - if (onPowerNode()) { - $pbmReduce = ' | pnmscale -reduce ' . $scale; - } else { - $pbmReduce = ' | pnmscale -nomix -reduce ' . $scale; +$unzipCmd = getUnarchiveCommand($zipPath, $file); + +switch ($imageInfo['type']) { + case 'jp2': + $decompressCmd = + " | /petabox/sw/bin/kdu_expand -no_seek -quiet -reduce $powReduce -rotate $rotate -i /dev/stdin -o " . $stdoutLink; + if ($decompressToBmp) { + $decompressCmd .= ' | bmptopnm '; } - } else { - $pbmReduce = ''; - } + break; + + case 'tiff': + // We need to create a temporary file for tifftopnm since it cannot + // work on a pipe (the file must be seekable). + // We use the BookReaderTiff prefix to give a hint in case things don't + // get cleaned up. + $tempFile = tempnam("/tmp", "BookReaderTiff"); - $decompressCmd = - ' > ' . $tempFile . ' ; tifftopnm ' . $tempFile . ' 2>/dev/null' . $pbmReduce; + // $$$ look at bit depth when reducing + $decompressCmd = + ' > ' . $tempFile . ' ; tifftopnm ' . $tempFile . ' 2>/dev/null' . reduceCommand($scale); + break; + + case 'jpeg': + $decompressCmd = ' | jpegtopnm ' . reduceCommand($scale); + break; -} else { - BRfatal('Unknown source file extension: ' . $fileExt); + case 'png': + $decompressCmd = ' | pngtopnm ' . reduceCommand($scale); + break; + + default: + BRfatal('Unknown source file extension: ' . $fileExt); + break; } // Non-integer scaling is currently disabled on the cluster @@ -145,27 +322,41 @@ if ('jp2' == $fileExt) { // $cmd .= " | pnmscale -height {$_REQUEST['height']} "; // } -if ('jpg' == $ext) { - $compressCmd = ' | pnmtojpeg ' . $jpegOptions; -} else if ('png' == $ext) { - $compressCmd = ' | pnmtopng ' . $pngOptions; +switch ($ext) { + case 'png': + $compressCmd = ' | pnmtopng ' . $pngOptions; + break; + + case 'jpeg': + case 'jpg': + default: + $compressCmd = ' | pnmtojpeg ' . $jpegOptions; + $ext = 'jpeg'; // for matching below + break; + +} + +if (($ext == $fileExt) && ($scale == 1) && ($rotate === "0")) { + // Just pass through original data if same format and size + $cmd = $unzipCmd; +} else { + $cmd = $unzipCmd . $decompressCmd . $compressCmd; } -$cmd = $unzipCmd . $decompressCmd . $compressCmd; +# print $cmd; -//print $cmd; +// $$$ investigate how to flush cache when this file is changed header('Content-type: ' . $MIMES[$ext]); header('Cache-Control: max-age=15552000'); - -passthru ($cmd); +passthru ($cmd); # cmd returns image data if (isset($tempFile)) { - unlink($tempFile); + unlink($tempFile); } function BRFatal($string) { - echo "alert('$string')\n"; + echo "alert('$string');\n"; die(-1); } @@ -183,6 +374,18 @@ function onPowerNode() { return false; } +function reduceCommand($scale) { + if (1 != $scale) { + if (onPowerNode()) { + return ' | pnmscale -reduce ' . $scale; + } else { + return ' | pnmscale -nomix -reduce ' . $scale; + } + } else { + return ''; + } +} + ?> diff --git a/BookReaderIA/datanode/BookReaderJSIA.php b/BookReaderIA/datanode/BookReaderJSIA.php index 7a29e83..730249a 100755 --- a/BookReaderIA/datanode/BookReaderJSIA.php +++ b/BookReaderIA/datanode/BookReaderJSIA.php @@ -31,11 +31,10 @@ if (strpos($_SERVER["REQUEST_URI"], "/~mang") === 0) { // Serving out of home di $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!"); @@ -49,28 +48,38 @@ if ("" == $server) { BRFatal("No server specified!"); } -if (!preg_match("|^/[0-3]/items/{$id}$|", $itemPath)) { +if (!preg_match("|^/\d+/items/{$id}$|", $itemPath)) { BRFatal("Bad id!"); } // 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)) { @@ -150,34 +159,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) { @@ -243,12 +225,13 @@ br.getPageNum = function(index) { } 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 @@ -328,7 +311,7 @@ br.getEmbedCode = function() { return ""; } -br.pageW = [ +br.pageW = [ pageData->page as $page) { @@ -341,7 +324,7 @@ br.pageW = [ ?> ]; -br.pageH = [ +br.pageH = [ '; -br.zip = ''; +br.zip = ''; br.subPrefix = ''; br.server = ''; br.bookTitle= 'title);?>'; br.bookPath = ''; br.bookUrl = ''; br.imageFormat = ''; +br.archiveFormat = ''; , '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'); + +} + ?> diff --git a/BookReaderIA/inc/BookReader.inc b/BookReaderIA/inc/BookReader.inc index d61de5e..1b2606f 100644 --- a/BookReaderIA/inc/BookReader.inc +++ b/BookReaderIA/inc/BookReader.inc @@ -19,12 +19,12 @@ class BookReader // $$$ TODO add support for jpg and tar stacks // https://bugs.edge.launchpad.net/gnubook/+bug/323003 // https://bugs.edge.launchpad.net/gnubook/+bug/385397 - $imageFormatRegex = '@' . preg_quote($prefix, '@') . '_(jp2|tif)\.zip$@'; + $imageFormatRegex = '@' . preg_quote($prefix, '@') . '_(jp2|tif|jpg)\.(zip|tar)$@'; $baseLength = strlen($item->metadataGrabber->mainDir . '/'); foreach ($item->getFiles() as $location => $fileInfo) { $filename = substr($location, $baseLength); - + if ($checkOldScandata) { if ($filename == 'scandata.xml' || $filename == 'scandata.zip') { $foundScandata = $filename; @@ -39,7 +39,7 @@ class BookReader $foundImageStack = $filename; } } - + if ($foundScandata && $foundImageStack) { return true; } @@ -81,7 +81,7 @@ class BookReader // manually update with Launchpad version number at each checkin so that browsers // do not use old cached version // see https://bugs.launchpad.net/gnubook/+bug/330748 - $version = "0.9.18"; + $version = "0.9.21"; if ("" == $id) { echo "No identifier specified!"; @@ -163,22 +163,29 @@ class BookReader $identifier, 'itemPath' => $mainDir, 'server' => $server ); if ($subPrefix) { $params['subPrefix'] = $subPrefix; diff --git a/BookReaderIA/inc/FlipSearchMap.inc b/BookReaderIA/inc/FlipSearchMap.inc index e9345ca..182572b 100644 --- a/BookReaderIA/inc/FlipSearchMap.inc +++ b/BookReaderIA/inc/FlipSearchMap.inc @@ -138,9 +138,9 @@ class FlipSearchMap { $url = urldecode($searchUrl); //Another way is for the javascript code to pass in server, itemdir, and identifier directly //For now we'll parse the $url passed us. - #if (!preg_match('|http://\w+.archive.org(/[0-9]/items/\w+)/(\w+)_djvu.xml$|', $url, $match)) + #if (!preg_match('|http://\w+.archive.org(/[0-9]+/items/\w+)/(\w+)_djvu.xml$|', $url, $match)) #if (!preg_match('|(\w+)/(\w+)_djvu.xml$|', $url, $match)) - if (!preg_match('|(/[0-9]/items/[\w-]+)/([\w-]+)_djvu.xml$|', $url, $match)) + if (!preg_match('|(/[0-9]+/items/[\w-]+)/([\w-]+)_djvu.xml$|', $url, $match)) fatal("Can't get server and identifier from url $url"); $bookDir = $match[1]; $identifier = $match[2]; diff --git a/BookReaderIA/sendtoswarm.pl b/BookReaderIA/sendtoswarm.pl new file mode 100755 index 0000000..0485106 --- /dev/null +++ b/BookReaderIA/sendtoswarm.pl @@ -0,0 +1,207 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +# CONFIGURE + +# The location of the TestSwarm that you're going to run against. + +my $SWARM = "http://dev01.sf.archive.org:8080"; +my $SWARM_INJECT = "/js/inject.js"; + +# Your TestSwarm username. + +my $USER = "testflip"; + +# Your authorization token. + +# my $AUTH_TOKEN = ""; +open(AUTHFILE, "/home/testflip/.testswarm"); +my $AUTH_TOKEN = ; +chomp($AUTH_TOKEN); + +# The maximum number of times you want the tests to be run. + +my $MAX_RUNS = 5; + +# The type of revision control system being used. +# Currently "svn" or "git" are supported. + +my $RCS_TYPE = "git"; + +# The URL from which a copy will be checked out. + +my $RCS_URL = "/home/testflip/bookreader/.git"; + +# The directory in which the checkouts will occur. + +my $BASE_DIR = "/home/testflip/public_html/changeset"; + +# A script tag loading in the TestSwarm injection script will +# be added at the bottom of the in the following file. + +my $INJECT_FILE = "BookReaderIA/test/index.html"; + +# Any build commands that need to happen. + +my $BUILD = "(cd BookReaderIA; make test)"; + +# The name of the job that will be submitted +# (pick a descriptive, but short, name to make it easy to search) + +# Note: The string {REV} will be replaced with the current +# commit number/hash. + +my $JOB_NAME = "BookReader www-testflip #{REV}"; + +# The browsers you wish to run against. Options include: +# - "all" all available browsers. +# - "popular" the most popular browser (99%+ of all browsers in use) +# - "current" the current release of all the major browsers +# - "gbs" the browsers currently supported in Yahoo's Graded Browser Support +# - "beta" upcoming alpha/beta of popular browsers +# - "popularbeta" the most popular browser and their upcoming releases + +my $BROWSERS = "popularbeta"; + +# All the suites that you wish to run within this job +# (can be any number of suites) + +#my %SUITES = ('first' => 'http://test.archive.org/changeset/{REV}/test/index.html'); +my %SUITES = (); + +# Comment these out if you wish to define a custom set of SUITES above +#my $SUITE = "http://dev.jquery.com/~john/changeset/{REV}"; +my $SUITE = "http://home.us.archive.org/~testflip/changeset/{REV}/BookReaderIA"; +sub BUILD_SUITES { + %SUITES = map { /(\w+).js$/; $1 => "$SUITE/test/?$1%20module"; } glob("BookReaderIA/test/unit/*.js"); +} + +########### NO NEED TO CONFIGURE BELOW HERE ############ + +my $DEBUG = 1; +my $curdate = time; +my $co_dir = "tmp-$curdate"; + +print "chdir $BASE_DIR\n" if ( $DEBUG ); +chdir( $BASE_DIR ); + +# Check out a specific revision +if ( $RCS_TYPE eq "svn" ) { + print "svn co $RCS_URL $co_dir\n" if ( $DEBUG ); + `svn co $RCS_URL $co_dir`; +} elsif ( $RCS_TYPE eq "git" ) { + print "git clone $RCS_URL $co_dir\n" if ( $DEBUG ); + `git clone $RCS_URL $co_dir`; +} + +if ( ! -e $co_dir ) { + die "Problem checking out source."; +} + +print "chdir $co_dir\n" if ( $DEBUG ); +chdir( $co_dir ); + +my $rev; + +# Figure out the revision of the checkout +if ( $RCS_TYPE eq "svn" ) { + print "svn info | grep Revision\n" if ( $DEBUG ); + $rev = `svn info | grep Revision`; + $rev =~ s/Revision: //; +} elsif ( $RCS_TYPE eq "git" ) { + my $cmd = "git rev-parse --short HEAD"; + print "$cmd\n" if ( $DEBUG ); + $rev = `$cmd`; +} + +$rev =~ s/\s*//g; + +print "Revision: $rev\n" if ( $DEBUG ); + +if ( ! $rev ) { + remove_tmp(); + die "Revision information not found."; + +} elsif ( ! -e "../$rev" ) { + print "chdir $BASE_DIR\n" if ( $DEBUG ); + chdir( $BASE_DIR ); + + print "rename $co_dir $rev\n" if ( $DEBUG ); + rename( $co_dir, $rev ); + + print "chdir $rev\n" if ( $DEBUG ); + chdir ( $rev ); + + if ( $BUILD ) { + print "$BUILD\n" if ( $DEBUG ); + `$BUILD`; + } + + if ( exists &BUILD_SUITES ) { + &BUILD_SUITES(); + } + + foreach my $file ( glob($INJECT_FILE) ) { + my $inject_file = `cat $file`; + + # Inject the TestSwarm injection script into the test suite + $inject_file =~ s/<\/head>/ + + + + + + + +

BookReader QUnit Tests

+

+

+
    + + diff --git a/BookReaderIA/test/unit/Images.js b/BookReaderIA/test/unit/Images.js new file mode 100644 index 0000000..e2fabb9 --- /dev/null +++ b/BookReaderIA/test/unit/Images.js @@ -0,0 +1,208 @@ +// Tests for BookReaderImages.php + +// $$$ TODO -- make the test host configurable/automagic + +module("Images"); + +// $$$ set to test host +var testHost = 'http://www-testflip.archive.org'; + +// Returns locator URL for the given id +function jsLocateURL(identifier, book) { + var bookURL = testHost + '/bookreader/BookReaderJSLocate.php?id=' + identifier; + if (book) { + bookURL += '&book=' + book; + } + return bookURL; +} + +// Set up dummy BookReader class for JSLocate +function BookReader() { +}; + +BookReader.prototype.init = function() { + return true; +}; + + +// Test image info - jpeg +asyncTest("JSLocate for armageddonafter00couruoft - jpeg", function() { + expect(1); + $.getScript( jsLocateURL('armageddonafter00couruoft'), function() { + equals(br.bookTitle, 'Armageddon and after', 'Title'); + start(); + }); +}); + +asyncTest("Image info for jpeg", function() { + expect(3); + var expected = {"width":1349,"height":2105,"bits":8,"type":"jpeg"}; + var imageInfoURL = br.getPageURI(8) + '&ext=json&callback=?'; + + $.getJSON(imageInfoURL, function(data) { + equals(data != null, true, 'data is not null'); + if (data != null) { + equals(data.width, expected.width, 'Image width'); + same(data, expected, 'Image info object'); + } + start(); + }); +}); + + + +// Test image info +asyncTest("JSLocate for zc-f-c-b-4 - 1-bit jp2", function() { + expect(1); + $.getScript( jsLocateURL('zc-f-c-b-4', 'concept-of-infection'), function() { + equals(br.numLeafs, 13, 'numLeafs'); + start(); + }); +}); + +asyncTest("Image info for 1-bit jp2", function() { + expect(3); + var expected = {"width":3295,"height":2561,"bits":1,"type":"jp2"}; + var imageInfoURL = br.getPageURI(0) + '&ext=json&callback=?'; + + $.getJSON(imageInfoURL, function(data) { + equals(data != null, true, 'data is not null'); + if (data != null) { + equals(data.width, expected.width, 'Image width'); + same(data, expected, 'Image info object'); + } + start(); + }); +}); + +/// windwavesatseabr00bige - jp2 zip +asyncTest("JSLocate for windwavesatseabr00bige - Scribe jp2.zip book", function() { + expect(1); + $.getScript( jsLocateURL('windwavesatseabr00bige'), function(data, textStatus) { + equals(br.numLeafs, 224, 'JSLocate successful. numLeafs'); + start(); + }); +}); + +test("Image URI for windwavesatseabr00bige page index 5", function() { + expect(1); + var index = 5; + var expectedEnding = "file=windwavesatseabr00bige_jp2/windwavesatseabr00bige_0006.jp2&scale=1&rotate=0"; + var pageURI = br.getPageURI(index); + var reg = new RegExp('file=.*$'); + var actualEnding = reg.exec(pageURI); + equals(actualEnding, expectedEnding, 'URI for page index 5 ends with'); +}); + +asyncTest("Load windwavesatseabr00bige image 5", function() { + var pageURI = br.getPageURI(5); + var img = new Image(); + $(img).bind( 'load error', function(eventObj) { + equals(eventObj.type, 'load', 'Load image (' + pageURI + '). Event handler called'); + start(); + }) + // Actually load the image + .attr('src', pageURI); +}); + + +/// nybc200109 - 1-bit tiff zip +asyncTest("JSLocate for nybc200109 - 1-bit tiff.zip book", function() { + expect(1); + $.getScript( jsLocateURL('nybc200109'), function() { + equals(br.numLeafs, + 694, + 'Number of pages'); + start(); + }); +}); + +asyncTest("Image info for 1-bit tiff", function() { + expect(3); + var expected = {"width":5081,"height":6592,"bits":1,"type":"tiff"}; + var imageInfoURL = br.getPageURI(0) + '&ext=json&callback=?'; + + $.getJSON(imageInfoURL, function(data) { + equals(data != null, true, 'data is not null'); + if (data != null) { + equals(data.width, expected.width, 'Image width'); + same(data, expected, 'Image info object'); + } + start(); + }); +}); + +asyncTest("Load 1-bit tiff image from zip", function() { + expect(2); + var pageURI = br.getPageURI(6, 16); + var img = new Image(); + $(img).bind( 'load error', function(eventObj) { + equals(eventObj.type, 'load', 'Load image (' + pageURI + '). Event handler called'); + equals(this.width, 1272, 'Image width'); + start(); + }) + .attr('src', pageURI); +}); + + + +/// asamoandictiona00pragoog - tiff zip +asyncTest("JSLocate for asamoandictiona00pragoog - tiff.zip book", function() { + expect(1); + $.getScript( jsLocateURL('asamoandictiona00pragoog'), function() { + equals(br.bookTitle, + 'A Samoan dictionary: English and Samoan, and Samoan and English;', + 'Book title'); + start(); + }); +}); + +asyncTest("Image info for 8-bit tiff", function() { + expect(3); + var expected = {"width":1275,"height":1650,"bits":8,"type":"tiff"}; + var imageInfoURL = br.getPageURI(0) + '&ext=json&callback=?'; + + $.getJSON(imageInfoURL, function(data) { + equals(data != null, true, 'data is not null'); + if (data != null) { + equals(data.width, expected.width, 'Image width'); + same(data, expected, 'Image info object'); + } + start(); + }); +}); + +asyncTest("Load tiff image from zip", function() { + expect(2); + var pageURI = br.getPageURI(23, 8); + var img = new Image(); + $(img).bind( 'load error', function(eventObj) { + equals(eventObj.type, 'load', 'Load image (' + pageURI + '). Event handler called'); + equals(this.width, 702, 'Image width'); + start(); + }) + .attr('src', pageURI); +}); + + +/// hccapp56191900uoft - jpeg tar +asyncTest("JSLocate for hccapp56191900uoft - jpg.tar", function() { + expect(1); + $.getScript( jsLocateURL('hccapp56191900uoft'), function() { + equals(br.numLeafs, 1101, 'Number of pages'); + start(); + }); +}); + +asyncTest('Load jpg image from tar file - https://bugs.launchpad.net/bookreader/+bug/323003', function() { + expect(2); + var pageURI = br.getPageURI(6, 8); + var img = new Image(); + $(img).bind( 'load error', function(eventObj) { + equals(eventObj.type, 'load', 'Load image (' + pageURI + '). Event handler called'); + equals(this.width, 244, 'Image width'); + start(); + }) + .attr('src', pageURI); +}); + diff --git a/BookReaderIA/test/unit/JSLocate.js b/BookReaderIA/test/unit/JSLocate.js new file mode 100644 index 0000000..17136c7 --- /dev/null +++ b/BookReaderIA/test/unit/JSLocate.js @@ -0,0 +1,50 @@ +// Tests for BookReaderJSLocate.php + +// $$$ TODO -- make the test host configurable/automagic + +module("JSLocate"); + +testHost = 'http://www-testflip.archive.org'; + +// Returns locator URL for the given id +function jsLocateURL(bookId) { + return testHost + '/bookreader/BookReaderJSLocate.php?id=' + bookId; +} + +// Set up dummy BookReader class for JSLocate +function BookReader() { +}; + +BookReader.prototype.init = function() { + return true; +}; + +asyncTest("JSLocate for notesonsubmarine00grea", function() { + expect(1); + $.getScript( jsLocateURL('notesonsubmarine00grea'), + function(data, textStatus) { + equals(window.br.titleLeaf, 5, 'Metadata loaded. See https://bugs.launchpad.net/bookreader/+bug/517424. Title leaf'); + start(); + } + ); +}); + +asyncTest("JSLocate for photographingclo00carprich", function() { + expect(1); + $.getScript( jsLocateURL('photographingclo00carprich'), + function(data, textStatus) { + equals(window.br.bookTitle, 'Photographing clouds from an airplane', 'Title of book'); + start(); + } + ); +}); + +asyncTest("JSLocate for salmoncookbookho00panaiala", function() { + expect(1); + $.getScript( jsLocateURL('salmoncookbookho00panaiala'), + function(data, textStatus) { + equals(window.br.numLeafs, 40, 'Number of pages'); + start(); + } + ); +}); diff --git a/BookReaderIA/www/BookReaderJSLocate.php b/BookReaderIA/www/BookReaderJSLocate.php index 0b18e2e..c71ffeb 100644 --- a/BookReaderIA/www/BookReaderJSLocate.php +++ b/BookReaderIA/www/BookReaderJSLocate.php @@ -21,6 +21,7 @@ This file is part of BookReader. require_once '/petabox/setup.inc'; $id = $_REQUEST['id']; +$book = $_REQUEST['book']; // support multiple books within an item if ("" == $id) { echo "No identifier specified!"; @@ -31,20 +32,13 @@ $locator = new Locator(); $results = $locator->locateUDP($id, 1, false); -$serverBaseURL = $results[0][0]; - -// Check if we're on a dev vhost and point to JSIA in the user's public_html on the datanode -if (preg_match("/^www-(\w+)/", $_SERVER["SERVER_NAME"], $match)) { - // $$$ the remapping isn't totally automatic yet and requires user to - // ln -s ~/petabox/www/datanode/BookReader ~/public_html/BookReader - // so we enable it only for known hosts - $devhosts = array('mang', 'testflip', 'rkumar'); - if (in_array($match[1], $devhosts)) { - $serverBaseURL = $serverBaseURL . ":81/~" . $match[1]; - } -} +$server = $results[0][0]; +$serverBaseURL = BookReader::serverBaseURL($server); -$url = "http://{$serverBaseURL}/BookReader/BookReaderJSIA.php?id={$id}&itemPath={$results[0][1]}&server={$serverBaseURL}"; +$url = "http://{$serverBaseURL}/BookReader/BookReaderJSIA.php?id=" . urlencode($id) . "&itemPath={$results[0][1]}&server={$server}"; +if ($book) { + $url .= "&subPrefix=" . urlencode($book); +} if (("" != $results[0][0]) && ("" != $results[0][1])) { diff --git a/BookReaderIA/www/browserunsupported.php b/BookReaderIA/www/browserunsupported.php index 1e8680e..c098789 100644 --- a/BookReaderIA/www/browserunsupported.php +++ b/BookReaderIA/www/browserunsupported.php @@ -1,4 +1,4 @@ -', + '', true); ?> - - - - Unsupported browser - - - - - - - - - - - - - -
    - - (logo) - - - - - - - -
    -
    -
    - (navigation image)
    - - - - - - - - - -
    - Home - - Donate | - -Forums | -FAQs | -Contributions | -Terms, Privacy, & Copyright | -Contact | -Jobs | - -Bios -
    @@ -89,9 +35,9 @@ Sorry, but our new viewer does not work in this browser yet.

    -Either go to our old viewer, +Either go to our old viewer, or download Firefox or -IE7 (or higher). + IE7 (or higher).

    @@ -100,10 +46,4 @@ You will be automatically redirected to the older viewer in 10 seconds.

    -

    - Terms of Use (10 Mar 2001) - -

    - - - + diff --git a/README.txt b/README.txt index 83b6a25..b081997 100644 --- a/README.txt +++ b/README.txt @@ -1 +1,10 @@ -openlibrary book reader +The Internet Archive BookReader is used to view books from the Internet Archive +online and can also be used to view other books. + +Developer documentation: +http://openlibrary.org/dev/docs/bookreader + +Hosted source code: +http://github.com/openlibrary/bookreader + +The source code license is AGPL v3, as described in the LICENSE file.