From 576c00b37dd0bc8ae6487ba33d60286ad07e669d Mon Sep 17 00:00:00 2001
From: thatcher <thatcher.christopher@gmail.com>
Date: Fri, 8 Feb 2013 09:21:28 -0500
Subject: [PATCH] adding support for several new tile sources including iiif,
 osm and tms (osm and tms are thanks to seajax-utils project)

---
 build.properties         |   2 +-
 build.xml                |   5 +
 openseadragon.js         | 521 ++++++++++++++++++++++++++++++++++++++-
 src/drawer.js            |  18 +-
 src/viewer.js            |   3 +-
 www/base.html            |  13 +-
 www/tilesource-iiif.html | 170 +++++++++++--
 7 files changed, 693 insertions(+), 39 deletions(-)

diff --git a/build.properties b/build.properties
index 7c375c5b..d629ef5c 100644
--- a/build.properties
+++ b/build.properties
@@ -6,7 +6,7 @@
 PROJECT: openseadragon
 BUILD_MAJOR: 0
 BUILD_MINOR: 9
-BUILD_ID: 98
+BUILD_ID: 101
 BUILD: ${PROJECT}.${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID}
 VERSION: ${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID}
 
diff --git a/build.xml b/build.xml
index 0a308132..ab826346 100644
--- a/build.xml
+++ b/build.xml
@@ -31,6 +31,9 @@
             <!--file name="src/profiler.js" /-->
             <file name="src/tilesource.js" />
             <file name="src/dzitilesource.js" />
+            <file name="src/iiiftilesource.js" />
+            <file name="src/osmtilesource.js" />
+            <file name="src/tmstilesource.js" />
             <file name="src/legacytilesource.js" />
             <file name="src/tilesourcecollection.js" />
             <file name="src/button.js" />
@@ -63,6 +66,8 @@
 
         <publish page='tilesource-custom' title='Custom Tile Source | '/>
         <publish page='tilesource-dzi' title='DZI Tile Source | '/>
+        <publish page='tilesource-osm' title='Open Street Maps Tile Source | '/>
+        <publish page='tilesource-tms' title='Tiled Map Service Tile Source | '/>
         <publish page='tilesource-iiif' title='IIIF Tile Source | '/>
         <publish page='tilesource-legacy' title='Legacy Tile Sources | '/>
         <publish page='tilesource-sequence' title='Tile Source Sequence | '/>
diff --git a/openseadragon.js b/openseadragon.js
index 9e7df3df..7bac1e01 100644
--- a/openseadragon.js
+++ b/openseadragon.js
@@ -1,7 +1,7 @@
 /*globals OpenSeadragon */
 
 /**
- * @version  OpenSeadragon 0.9.98
+ * @version  OpenSeadragon 0.9.101
  *
  * @fileOverview 
  * <h2>
@@ -3410,7 +3410,8 @@ $.Viewer = function( options ) {
         //These are originally not part options but declared as members
         //in initialize.  Its still considered idiomatic to put them here
         source:         null,
-        drawer:         null,
+        drawer:         null, 
+        drawers:        [],
         viewport:       null,
         navigator:      null, 
 
@@ -5987,7 +5988,503 @@ function configureFromObject( tileSource, configuration ){
 };
 
 }( OpenSeadragon ));
-/*globals OpenSeadragon */
+(function( $ ){
+    
+/**
+ * A client implementation of the International Image Interoperability 
+ * Format: Image API Draft 0.2 - Please read more about the specification
+ * at 
+ *
+ * The getTileUrl implementation is based on the gist from:
+ * https://gist.github.com/jpstroop/4624253
+ *
+ * @class
+ * @name OpenSeadragon.IIIFTileSource
+ * @see http://library.stanford.edu/iiif/image-api/
+ */
+$.IIIFTileSource = function( options ){
+
+    $.extend( true, this, options );
+
+    if( !(this.height && this.width && this.identifier && this.tilesUrl ) ){
+        throw new Error('IIIF required parameters not provided.');
+    }
+
+    //TODO: at this point the base tile source implementation assumes
+    //      a tile is a square and so only has one property tileSize
+    //      to store it.  It may be possible to make tileSize a vector
+    //      OpenSeadraon.Point but would require careful implementation
+    //      to preserve backward compatibility.
+    options.tileSize = this.tile_width;
+
+    options.maxLevel = options.maxLevel ? options.maxLevel : Number( 
+        Math.ceil( Math.log( Math.max( this.width, this.height ), 2 ) )
+    );
+    
+    $.TileSource.apply( this, [ options ] );
+};
+
+$.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, {
+    
+
+    /**
+     * Determine if the data and/or url imply the image service is supported by
+     * this tile source.
+     * @function
+     * @name OpenSeadragon.IIIFTileSource.prototype.supports
+     * @param {Object|Array} data
+     * @param {String} optional - url
+     */
+    supports: function( data, url ){
+        return ( 
+            data.ns && 
+            "http://library.stanford.edu/iiif/image-api/ns/" == data.ns
+        ) || (
+            data.profile && (
+                "http://library.stanford.edu/iiif/image-api/compliance.html#level1" == data.profile ||
+                "http://library.stanford.edu/iiif/image-api/compliance.html#level2" == data.profile ||
+                "http://library.stanford.edu/iiif/image-api/compliance.html#level3" == data.profile ||
+                "http://library.stanford.edu/iiif/image-api/compliance.html" == data.profile 
+            )
+        ) || (
+            data.documentElement &&
+            "info" == data.documentElement.tagName &&
+            "http://library.stanford.edu/iiif/image-api/ns/" ==
+                data.documentElement.namespaceURI
+        );
+    },
+
+    /**
+     * 
+     * @function
+     * @name OpenSeadragon.IIIFTileSource.prototype.configure
+     * @param {Object|XMLDocument} data - the raw configuration
+     * @param {String} url - the url the data was retreived from if any.
+     * @return {Object} options - A dictionary of keyword arguments sufficient 
+     *      to configure this tile source via it's constructor.
+     */
+    configure: function( data, url ){
+        var service,
+            identifier,
+            options,
+            host;
+
+        if( !$.isPlainObject(data) ){
+
+            options = configureFromXML( this, data );
+
+        }else{
+
+            options = configureFromObject( this, data );
+        }
+
+        if( url && !options.tilesUrl ){
+            service = url.split('/');
+            service.pop(); //info.json or info.xml
+            service = service.join('/');
+            if( !( 'http' == url.substring( 0, 4 ) ) ){
+                host = location.protocol + '//' + location.host;
+                service = host + service;
+            }
+            options.tilesUrl = service.replace(
+                data.identifier,
+                ''
+            );
+        }
+
+        return options;
+    },
+
+    /**
+     * Responsible for retreiving the url which will return an image for the 
+     * region speified by the given x, y, and level components.
+     * @function
+     * @name OpenSeadragon.IIIFTileSource.prototype.getTileUrl
+     * @param {Number} level - z index
+     * @param {Number} x
+     * @param {Number} y
+     * @throws {Error}
+     */
+    getTileUrl: function( level, x, y ){
+         
+        //# constants
+        var IIIF_ROTATION = '0',
+            IIIF_QUALITY = 'native.jpg',
+         
+            //## get the scale (level as a decimal)
+            scale = Math.pow( 0.5, this.maxLevel - level ),
+         
+            //## get iiif size
+            iiif_size = 'pct:' + ( scale * 100 ),
+
+            //# image dimensions at this level
+            level_width = Math.ceil( this.width * scale ),
+            level_height = Math.ceil( this.height * scale ),
+         
+            //## iiif region
+            iiif_tile_size_width = Math.ceil( this.tileSize / scale ),
+            iiif_tile_size_height = Math.ceil( this.tileSize / scale ),
+            iiif_region,
+            iiif_tile_x,
+            iiif_tile_y,
+            iiif_tile_w,
+            iiif_tile_h;
+         
+         
+        if ( level_width < this.tile_width || level_height < this.tile_height ){
+            iiif_region = 'full';
+        } else {
+            iiif_tile_x = x * iiif_tile_size_width;
+            iiif_tile_y = y * iiif_tile_size_height;
+            iiif_tile_w = Math.min( iiif_tile_size_width, this.width - iiif_tile_x );
+            iiif_tile_h = Math.min( iiif_tile_size_height, this.height - iiif_tile_y );
+            iiif_region = [ iiif_tile_x, iiif_tile_y, iiif_tile_w, iiif_tile_h ].join(',');
+        }
+         
+        return [ 
+            this.tilesUrl, 
+            this.identifier, 
+            iiif_region, 
+            iiif_size, 
+            IIIF_ROTATION, 
+            IIIF_QUALITY 
+        ].join('/');
+    }
+
+
+});
+
+/**
+ * @private
+ * @inner
+ * @function
+ * 
+    <?xml version="1.0" encoding="UTF-8"?>
+    <info xmlns="http://library.stanford.edu/iiif/image-api/ns/">
+      <identifier>1E34750D-38DB-4825-A38A-B60A345E591C</identifier>
+      <width>6000</width>
+      <height>4000</height>
+      <scale_factors>
+        <scale_factor>1</scale_factor>
+        <scale_factor>2</scale_factor>
+        <scale_factor>4</scale_factor>
+      </scale_factors>
+      <tile_width>1024</tile_width>
+      <tile_height>1024</tile_height>
+      <formats>
+        <format>jpg</format>
+        <format>png</format>
+      </formats>
+      <qualities>
+        <quality>native</quality>
+        <quality>grey</quality>
+      </qualities>
+    </info>
+ */
+function configureFromXml( tileSource, xmlDoc ){
+    var configuration = {};
+
+    //parse the xml
+    if ( !xmlDoc || !xmlDoc.documentElement ) {
+        throw new Error( $.getString( "Errors.Xml" ) );
+    }
+
+    var root            = xmlDoc.documentElement,
+        rootName        = root.tagName,
+        configuration   = null,
+        scale_factors,
+        formats,
+        qualities,
+        i;
+
+    if ( rootName == "info" ) {
+        
+        try {
+
+            configuration = {
+                "ns":            root.namespaceURI,
+                "identifier":    root.getElement('identifier').innerHTML,
+                "width":         root.getElement('width').innerHTML,
+                "height":        root.getElement('height').innerHTML,
+                "scale_factors": null,
+                "tile_width":    root.getElement('tile_width').innerHTML,
+                "tile_height":   root.getElement('tile_height').innerHTML,
+                "formats":       [ "jpg", "png" ],
+                "quality":       [ "native", "grey" ]
+            };
+
+            scale_factors = root.getElement('scale_factors');
+            if( scale_factors ){
+                scale_factors = scale_factors.getElementsByTagName('scale_factor');
+                configuration.scale_factors = [];
+                for( i = 0; i < scale_factors.length; i++ ){
+                    configuration.scale_factors.push(
+                        scale_factors[ i ].innerHTML
+                    );
+                }
+            }
+
+            formats = root.getElement('formats');
+            if( formats ){
+                formats = formats.getElementsByTagName('format');
+                configuration.formats = [];
+                for( i = 0; i < formats.length; i++ ){
+                    configuration.formats.push(
+                        formats[ i ].innerHTML
+                    );
+                }
+            }
+
+            qualities = root.getElement('qualities');
+            if( qualities ){
+                qualities = formats.getElementsByTagName('quality');
+                configuration.quality = [];
+                for( i = 0; i < qualities.length; i++ ){
+                    configuration.quality.push(
+                        qualities[ i ].innerHTML
+                    );
+                }
+            }
+
+            return configureFromObject( tileSource, configuration );
+
+        } catch ( e ) {
+            throw (e instanceof Error) ? 
+                e : 
+                new Error( $.getString("Errors.IIIF") );
+        }
+    }
+
+    throw new Error( $.getString( "Errors.IIIF" ) );
+
+};
+
+
+/**
+ * @private
+ * @inner
+ * @function
+ * 
+    { 
+        "profile" : "http://library.stanford.edu/iiif/image-api/compliance.html#level1",
+        "identifier" : "1E34750D-38DB-4825-A38A-B60A345E591C",
+        "width" : 6000,
+        "height" : 4000,
+        "scale_factors" : [ 1, 2, 4 ],
+        "tile_width" : 1024,
+        "tile_height" : 1024,
+        "formats" : [ "jpg", "png" ],
+        "quality" : [ "native", "grey" ]
+    } 
+ */
+function configureFromObject( tileSource, configuration ){
+    return configuration;
+};
+
+}( OpenSeadragon ));(function( $ ){
+    
+/**
+ * A tilesource implementation for OpenStreetMap. Adopted from Rainer Simon
+ * project http://github.com/rsimon/seajax-utils.
+ *
+ * Note 1. Zoomlevels. Deep Zoom and OSM define zoom levels differently. In  Deep 
+ * Zoom, level 0 equals an image of 1x1 pixels. In OSM, level 0 equals an image of
+ * 256x256 levels (see http://gasi.ch/blog/inside-deep-zoom-2). I.e. there is a 
+ * difference of log2(256)=8 levels.
+ *
+ * Note 2. Image dimension. According to the OSM Wiki 
+ * (http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels)
+ * the highest Mapnik zoom level has 256.144x256.144 tiles, with a 256x256
+ * pixel size. I.e. the Deep Zoom image dimension is 65.572.864x65.572.864
+ * pixels.
+ *
+ * @class
+ * @extends OpenSeadragon.TileSource
+ * @param {Number|Object} width - the pixel width of the image or the idiomatic
+ *      options object which is used instead of positional arguments.
+ * @param {Number} height
+ * @param {Number} tileSize
+ * @param {Number} tileOverlap
+ * @param {String} tilesUrl
+ */ 
+$.OsmTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) {
+    var options;
+
+    if( $.isPlainObject( width ) ){
+        options = width;
+    }else{
+        options = {
+            width: arguments[0],
+            height: arguments[1],
+            tileSize: arguments[2],
+            tileOverlap: arguments[3],
+            tilesUrl: arguments[4]
+        };
+    }
+    //apply default setting for standard public OpenStreatMaps service
+    //but allow them to be specified so fliks can host there own instance
+    //or apply against other services supportting the same standard
+    if( !options.width || !options.height ){
+        options.width = 65572864;
+        options.height = 65572864;
+    }
+    if( !options.tileSize ){
+        options.tileSize = 256;
+        options.tileOverlap = 0;
+    }
+    if( !options.tilesUrl ){
+        options.tilesUrl = "http://tile.openstreetmap.org/";
+    }
+    options.minLevel = 8;
+    
+    $.TileSource.apply( this, [ options ] );
+
+};
+
+$.extend( $.OsmTileSource.prototype, $.TileSource.prototype, {
+
+
+    /**
+     * Determine if the data and/or url imply the image service is supported by
+     * this tile source.
+     * @function
+     * @name OpenSeadragon.DziTileSource.prototype.supports
+     * @param {Object|Array} data
+     * @param {String} optional - url
+     */
+    supports: function( data, url ){
+        return ( 
+            data.type && 
+            "openstreetmaps" == data.type
+        )
+    },
+
+    /**
+     * 
+     * @function
+     * @name OpenSeadragon.OsmTileSource.prototype.configure
+     * @param {Object} data - the raw configuration
+     * @param {String} url - the url the data was retreived from if any.
+     * @return {Object} options - A dictionary of keyword arguments sufficient 
+     *      to configure this tile sources constructor.
+     */
+    configure: function( data, url ){
+        return data;
+    },
+
+
+    /**
+     * @function
+     * @name OpenSeadragon.OsmTileSource.prototype.getTileUrl
+     * @param {Number} level
+     * @param {Number} x
+     * @param {Number} y
+     */
+    getTileUrl: function( level, x, y ) {
+        return this.tilesUrl + (level - 8) + "/" + x + "/" + y + ".png";
+    }
+});
+
+
+}( OpenSeadragon ));
+(function( $ ){
+    
+/**
+ * A tilesource implementation for Tiled Map Services (TMS). Adopted from Rainer Simon
+ * project http://github.com/rsimon/seajax-utils. TMS tile
+ * scheme ( [ as supported by OpenLayers ] is described here 
+ * ( http://openlayers.org/dev/examples/tms.html ) )
+ *
+ * @class
+ * @extends OpenSeadragon.TileSource
+ * @param {Number|Object} width - the pixel width of the image or the idiomatic
+ *      options object which is used instead of positional arguments.
+ * @param {Number} height
+ * @param {Number} tileSize
+ * @param {Number} tileOverlap
+ * @param {String} tilesUrl
+ */ 
+$.TmsTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) {
+    var options;
+
+    if( $.isPlainObject( width ) ){
+        options = width;
+    }else{
+        options = {
+            width: arguments[0],
+            height: arguments[1],
+            tileSize: arguments[2],
+            tileOverlap: arguments[3],
+            tilesUrl: arguments[4]
+        };
+    }
+    // TMS has integer multiples of 256 for width/height and adds buffer
+    // if necessary -> account for this!
+    var bufferedWidth = Math.ceil(options.width / 256) * 256,
+        bufferedHeight = Math.ceil(options.height / 256) * 256,
+        max;
+
+    // Compute number of zoomlevels in this tileset
+    if (bufferedWidth > bufferedHeight) {
+        max = bufferedWidth / 256;
+    } else {
+        max = bufferedHeight / 256;
+    }
+    options.maxLevel = Math.ceil(Math.log(max)/Math.log(2)) - 1;
+    options.tileSize = 256;
+    options.width = bufferedWidth;
+    options.height = bufferedHeight;
+    
+    $.TileSource.apply( this, [ options ] );
+
+};
+
+$.extend( $.TmsTileSource.prototype, $.TileSource.prototype, {
+
+
+    /**
+     * Determine if the data and/or url imply the image service is supported by
+     * this tile source.
+     * @function
+     * @name OpenSeadragon.TmsTileSource.prototype.supports
+     * @param {Object|Array} data
+     * @param {String} optional - url
+     */
+    supports: function( data, url ){
+        return ( data.type && "tiledmapservice" == data.type );
+    },
+
+    /**
+     * 
+     * @function
+     * @name OpenSeadragon.TmsTileSource.prototype.configure
+     * @param {Object} data - the raw configuration
+     * @param {String} url - the url the data was retreived from if any.
+     * @return {Object} options - A dictionary of keyword arguments sufficient 
+     *      to configure this tile sources constructor.
+     */
+    configure: function( data, url ){
+        return data;
+    },
+
+
+    /**
+     * @function
+     * @name OpenSeadragon.TmsTileSource.prototype.getTileUrl
+     * @param {Number} level
+     * @param {Number} x
+     * @param {Number} y
+     */
+    getTileUrl: function( level, x, y ) {
+        // Convert from Deep Zoom definition to TMS zoom definition
+        var yTiles = this.getNumTiles( level ).y - 1;
+
+        return this.tilesUrl + level + "/" + x + "/" +  (yTiles - y) + ".png";
+    }
+});
+
+
+}( OpenSeadragon ));/*globals OpenSeadragon */
 
 (function( $ ){
 
@@ -8343,7 +8840,7 @@ function updateViewport( drawer ) {
                 Math.log( 2 )
             )
         ),
-        highestLevel    = Math.min(
+        highestLevel    = Math.max(
             Math.abs(drawer.source.maxLevel),
             Math.abs(Math.floor( 
                 Math.log( zeroRatioC / drawer.minPixelRatio ) / 
@@ -8366,11 +8863,10 @@ function updateViewport( drawer ) {
     //TODO
     drawer.canvas.innerHTML   = "";
     if ( USE_CANVAS ) {
-        if( drawer.canvas.width != viewportSize.x ||
-            drawer.canvas.height != viewportSize.y 
-        ){
-            drawer.canvas.width   = viewportSize.x;
-            drawer.canvas.height  = viewportSize.y;
+        if( drawer.canvas.width  != viewportSize.x ||
+            drawer.canvas.height != viewportSize.y ){
+            drawer.canvas.width  = viewportSize.x;
+            drawer.canvas.height = viewportSize.y;
         }
         drawer.context.clearRect( 0, 0, viewportSize.x, viewportSize.y );
     }
@@ -8647,8 +9143,10 @@ function onTileLoad( drawer, tile, time, image ) {
         return;
     } else if ( !image  && !drawer.viewport.collectionMode ) {
         $.console.log( "Tile %s failed to load: %s", tile, tile.url );
-        tile.exists = false;
-        return;
+        if( !drawer.debugMode ){
+            tile.exists = false;
+            return;
+        }
     } else if ( time < drawer.lastResetTime ) {
         $.console.log( "Ignoring tile %s loaded before reset: %s", tile, tile.url );
         return;
@@ -8657,6 +9155,7 @@ function onTileLoad( drawer, tile, time, image ) {
     tile.loaded = true;
     tile.image  = image;
 
+
     insertionIndex = drawer.tilesLoaded.length;
 
     if ( drawer.tilesLoaded.length >= drawer.maxImageCacheCount ) {
diff --git a/src/drawer.js b/src/drawer.js
index 7331f07b..99f7bcd1 100644
--- a/src/drawer.js
+++ b/src/drawer.js
@@ -365,7 +365,7 @@ function updateViewport( drawer ) {
                 Math.log( 2 )
             )
         ),
-        highestLevel    = Math.min(
+        highestLevel    = Math.max(
             Math.abs(drawer.source.maxLevel),
             Math.abs(Math.floor( 
                 Math.log( zeroRatioC / drawer.minPixelRatio ) / 
@@ -388,11 +388,10 @@ function updateViewport( drawer ) {
     //TODO
     drawer.canvas.innerHTML   = "";
     if ( USE_CANVAS ) {
-        if( drawer.canvas.width != viewportSize.x ||
-            drawer.canvas.height != viewportSize.y 
-        ){
-            drawer.canvas.width   = viewportSize.x;
-            drawer.canvas.height  = viewportSize.y;
+        if( drawer.canvas.width  != viewportSize.x ||
+            drawer.canvas.height != viewportSize.y ){
+            drawer.canvas.width  = viewportSize.x;
+            drawer.canvas.height = viewportSize.y;
         }
         drawer.context.clearRect( 0, 0, viewportSize.x, viewportSize.y );
     }
@@ -669,8 +668,10 @@ function onTileLoad( drawer, tile, time, image ) {
         return;
     } else if ( !image  && !drawer.viewport.collectionMode ) {
         $.console.log( "Tile %s failed to load: %s", tile, tile.url );
-        tile.exists = false;
-        return;
+        if( !drawer.debugMode ){
+            tile.exists = false;
+            return;
+        }
     } else if ( time < drawer.lastResetTime ) {
         $.console.log( "Ignoring tile %s loaded before reset: %s", tile, tile.url );
         return;
@@ -679,6 +680,7 @@ function onTileLoad( drawer, tile, time, image ) {
     tile.loaded = true;
     tile.image  = image;
 
+
     insertionIndex = drawer.tilesLoaded.length;
 
     if ( drawer.tilesLoaded.length >= drawer.maxImageCacheCount ) {
diff --git a/src/viewer.js b/src/viewer.js
index 28d5a05f..93933008 100644
--- a/src/viewer.js
+++ b/src/viewer.js
@@ -91,7 +91,8 @@ $.Viewer = function( options ) {
         //These are originally not part options but declared as members
         //in initialize.  Its still considered idiomatic to put them here
         source:         null,
-        drawer:         null,
+        drawer:         null, 
+        drawers:        [],
         viewport:       null,
         navigator:      null, 
 
diff --git a/www/base.html b/www/base.html
index 9c61d96b..ebe3ec25 100644
--- a/www/base.html
+++ b/www/base.html
@@ -67,11 +67,20 @@
             via XMLHTTPRequest, JSONP, as well as direct inline configuration.
             </p>
             <ul>
+                <li><a href='/openseadragon/examples/tilesource-legacy/'>
+                    <span>Legacy Image Pyramids</span>
+                </a></li>
+                <!--<li><a href='/openseadragon/examples/tilesource-iiif/'>
+                    <span>IIIF (International Image Interchange Format)</span>
+                </a></li>-->
                 <li><a href='/openseadragon/examples/tilesource-dzi/'>
                     <span>DZI (Deep Zoom Images)</span>
                 </a></li>
-                <li><a href='/openseadragon/examples/tilesource-legacy/'>
-                    <span>Legacy Image Pyramids</span>
+                <li><a href='/openseadragon/examples/tilesource-osm/'>
+                    <span>OSM (Open Street Maps)</span>
+                </a></li>
+                <li><a href='/openseadragon/examples/tilesource-tms/'>
+                    <span>TMS (Tiled Map Service)</span>
                 </a></li>
                 <li><a href='/openseadragon/examples/tilesource-custom/'>
                     <span>Custom Tile Sources</span>
diff --git a/www/tilesource-iiif.html b/www/tilesource-iiif.html
index 94981000..4bad843b 100644
--- a/www/tilesource-iiif.html
+++ b/www/tilesource-iiif.html
@@ -1,16 +1,154 @@
-<!-- 
-/opt/djakota/target/lib/kdu_compress \
-	-quiet \
-	-i /opt/djakota/src/test/resources/images/01967u.tif \
-	-o /opt/djakota/target/images/01967u.jp2 \
-	-slope 51651,51337,51186,50804,50548,50232 \
-	Clevels=7 \
-	Cprecincts={256,256},{256,256},{128,128} \
-	Clayers=6 \
-	Corder=RPCL \
-	ORGtparts=R \
-	Cblk={32,32} \
-	ORGgen_plt=yes \
-	Creversible=yes \
-	-jp2_space sRGB
--->
\ No newline at end of file
+<h2>
+    example: iiif tiles support
+</h2>
+<p>
+    The International Image Interoperability Format is described formally here 
+    <a href='http://library.stanford.edu/iiif/image-api/'
+        >http://library.stanford.edu/iiif/image-api/</a>.
+</p>
+<p>
+    The IIIF API specifies a web service that returns an image in response to a standard http or https request. 
+    The URL can specify the region, size, rotation, quality characteristics and format of the requested image. A 
+    URL can also be constructed to request basic technical information about the image to support client 
+    applications.  The IIIF API was conceived of to facilitate systematic reuse of image resources in digital 
+    image repositories maintained by cultural heritage organizations. The API could be adopted by any image 
+    repository or service, and can be used to retrieve static images in response to a properly constructed URL.
+</p>
+<p>
+    OpenSeadragon has added support for IIIF thanks to several of its specifications authors
+    as well as the gist provided by <a href='http://github.com/jstroop'>Jon Stroop</a> at 
+    <a href='https://gist.github.com/jpstroop/4624253'>this gist</a>.
+    <em>The tile source reference in this page are not meant for general public consumption
+    without individual consideration of potential copyright infringement.</em>
+</p>
+
+
+<div class="description">
+    <h3>Inline Configuration for IIIF tile sources.</h3>
+    <p>
+        Inline configuration is very straight forward.  The tile source type is
+        identified by it's <strong>profile</strong> attribute an must specify the 
+        url of the tile service with the <strong>tilesUrl</strong> attribute.
+    </p>
+</div>
+<div class="demoarea">
+    <div class="demoheading">
+        Example Inline Configuration for IIIF
+    </div>
+    <div id="example-inline-configuration-for-iiif" 
+         class="openseadragon">
+    </div>
+    <p>
+        In this example we also specified the 'preserveViewport' option to retain the 
+        viewport zoom and position when changing pages.
+
+    </p>
+<pre>
+OpenSeadragon({
+    ...
+    preserveViewport: true,
+    tileSources:   [{
+        "tilesUrl":     "http://img.princeton.edu/loris",
+        "identifier":   "pudl0001/4609321/s42/00000001",   
+        "width":        2617,   
+        "height":       3600,   
+        "scale_factors": [1, 2, 3, 4, 5],   
+        "tile_width":   256,   
+        "tile_height":  256,   
+        "formats":      [ "jpg", "png" ],   
+        "qualities":    ["native", "bitonal", "grey", "color"],   
+        "profile":      "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
+    }...,{
+
+    ...//more tile sources
+
+    }]
+    ...
+});</pre>
+</div>
+<script type="text/javascript">
+    OpenSeadragon({
+        id:                 "example-inline-configuration-for-iiif",
+        prefixUrl:          "/openseadragon/images/",
+        preserveViewport: true,
+        tileSources:   [{
+            "tilesUrl":     "http://img.princeton.edu/loris",
+            "identifier":   "pudl0001/4609321/s42/00000001",   
+            "width":        2617,   
+            "height":       3600,   
+            "scale_factors": [1, 2, 3, 4, 5],   
+            "tile_width":   256,   
+            "tile_height":  256,   
+            "formats":      [ "jpg", "png" ],   
+            "qualities":    ["native", "bitonal", "grey", "color"],   
+            "profile":      "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
+        },{
+            "tilesUrl":     "http://img.princeton.edu/loris",
+            "identifier":   "pudl0001/4609321/s42/00000002",   
+            "width":        2547,   
+            "height":       3600,   
+            "scale_factors": [1, 2, 3, 4, 5],   
+            "tile_width":   256,   
+            "tile_height":  256,   
+            "formats":      [ "jpg", "png" ],   
+            "qualities":    ["native", "bitonal", "grey", "color"],   
+            "profile":      "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
+        },{
+            "tilesUrl":     "http://img.princeton.edu/loris",
+            "identifier":   "pudl0001/4609321/s42/00000003",   
+            "width":        2694,   
+            "height":       3600,   
+            "scale_factors": [1, 2, 3, 4, 5],   
+            "tile_width":   256,   
+            "tile_height":  256,   
+            "formats":      [ "jpg", "png" ],   
+            "qualities":    ["native", "bitonal", "grey", "color"],   
+            "profile":      "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
+        },{
+            "tilesUrl":     "http://img.princeton.edu/loris",
+            "identifier":   "pudl0001/4609321/s42/00000004",   
+            "width":        2717,   
+            "height":       3600,   
+            "scale_factors": [1, 2, 3, 4, 5],   
+            "tile_width":   256,   
+            "tile_height":  256,   
+            "formats":      [ "jpg", "png" ],   
+            "qualities":    ["native", "bitonal", "grey", "color"],   
+            "profile":      "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
+        },{
+            "tilesUrl":     "http://img.princeton.edu/loris",
+            "identifier":   "pudl0001/4609321/s42/00000005",   
+            "width":        2694,   
+            "height":       3600,   
+            "scale_factors": [1, 2, 3, 4, 5],   
+            "tile_width":   256,   
+            "tile_height":  256,   
+            "formats":      [ "jpg", "png" ],   
+            "qualities":    ["native", "bitonal", "grey", "color"],   
+            "profile":      "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
+        },{
+            "tilesUrl":     "http://img.princeton.edu/loris",
+            "identifier":   "pudl0001/4609321/s42/00000006",   
+            "width":        2717,   
+            "height":       3600,   
+            "scale_factors": [1, 2, 3, 4, 5],   
+            "tile_width":   256,   
+            "tile_height":  256,   
+            "formats":      [ "jpg", "png" ],   
+            "qualities":    ["native", "bitonal", "grey", "color"],   
+            "profile":      "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
+        },{
+            "tilesUrl":     "http://img.princeton.edu/loris",
+            "identifier":   "pudl0001/4609321/s42/00000007",   
+            "width":        2694,   
+            "height":       3600,   
+            "scale_factors": [1, 2, 3, 4, 5],   
+            "tile_width":   256,   
+            "tile_height":  256,   
+            "formats":      [ "jpg", "png" ],   
+            "qualities":    ["native", "bitonal", "grey", "color"],   
+            "profile":      "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
+        }]
+    });
+</script>
+