changeset 800:65e70c03870b stream

merge from juqery branch 12f790cb30de0ac42ff62fb9921d7a3215243b7b
author robcast
date Sat, 19 Feb 2011 09:56:18 +0100
parents 4568e539abd2 (current diff) 12f790cb30de (diff)
children 72662bb585ba
files client/digitallibrary/jquery/img/fullscreen/mirror-horizontal-on.png client/digitallibrary/jquery/img/fullscreen/mirror-vertical-on.png
diffstat 23 files changed, 3030 insertions(+), 348 deletions(-) [+]
line wrap: on
line diff
Binary file client/digitallibrary/jquery/combiner-0.0.1.jar has changed
Binary file client/digitallibrary/jquery/img/fullscreen/addregion.png has changed
Binary file client/digitallibrary/jquery/img/fullscreen/delregion.png has changed
Binary file client/digitallibrary/jquery/img/fullscreen/mirror-horizontal-on.png has changed
Binary file client/digitallibrary/jquery/img/fullscreen/mirror-vertical-on.png has changed
Binary file client/digitallibrary/jquery/img/fullscreen/regioninfo.png has changed
Binary file client/digitallibrary/jquery/img/fullscreen/regions.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/digitallibrary/jquery/jquery-digilib-plugins.txt	Sat Feb 19 09:56:18 2011 +0100
@@ -0,0 +1,42 @@
+
+The plugin API of jquery.digilib
+--------------------------------
+
+* The plugin Javascript file has to be loaded after jquery.digilib.js.
+
+* The plugin should be written as a closure on jQuery (like a jquery plugin) exporting a plugin object as its interface:
+
+  var plugin = {
+            name : 'mydigilibplugin',
+            install : install,
+            init : init,
+            buttons : {},
+            actions : {},
+            fn : {},
+            plugins : {},
+            defaults : {}
+  };
+
+  The "name" string, "install" function and "init" function are provided by the plugin and called by digilib. 
+  "buttons", "actions", "fn" and "plugins" are shared objects provided by digilib.
+  "buttons" is the array of button objects. The plugin can add its own buttons to this array.
+  "actions" is the array of action objects. The plugin can add its own actions to this array.
+  "fn" is an object with functions from digilib.
+  "plugins" is an object with all digilib plugin objects.
+  "defaults" is an object with all digilib defaults.
+
+* the plugin object is passed to digilib in the "plugin" action on loading the plugin javascript file:
+
+  $.fn.digilib('plugin', plugin);
+
+  digilib then calls the "install" function on the populated plugin object (i.e. this = plugin) with the plugin object as parameter.
+  
+* digilib calls the "init" function from the plugin object with the data object instance as a parameter in digilibs init phase.
+
+* further actions should be implemented as custom events on the data object.
+
+* events triggered by digilib on the data object:
+  "setup": in the init phase, after scalerDiv and buttons have been set up.
+  "update", updateDisplay(): after small updates in the display, e.g. when the scaler-img finished loading.
+  "redisplay", redisplay(): after changes in the display, e.g. after changing zoom factor.
+  "dragZoom(newZoomArea)": while dragging the zoom area (with parameter newZoomArea).
--- a/client/digitallibrary/jquery/jquery-test-embedded-rc.html	Fri Feb 11 10:12:41 2011 +0100
+++ b/client/digitallibrary/jquery/jquery-test-embedded-rc.html	Sat Feb 19 09:56:18 2011 +0100
@@ -47,9 +47,10 @@
         </style>
 
         <script type="text/javascript" src="jquery-1.4.4.js"></script>
-        <script type="text/javascript" src="dlGeometry.js"></script>
         <script type="text/javascript" src="jquery.cookie.js"></script>
         <script type="text/javascript" src="jquery.digilib.js"></script>
+        <script type="text/javascript" src="jquery.digilib.geometry.js"></script>
+        <script type="text/javascript" src="jquery.digilib.birdseye.js"></script>
         <link rel="stylesheet" type="text/css" href="jquery.digilib.css" />
 
 
--- a/client/digitallibrary/jquery/jquery-test-embedded.html	Fri Feb 11 10:12:41 2011 +0100
+++ b/client/digitallibrary/jquery/jquery-test-embedded.html	Sat Feb 19 09:56:18 2011 +0100
@@ -52,9 +52,10 @@
         </style>
 
         <script type="text/javascript" src="jquery-1.4.4.js"></script>
-        <script type="text/javascript" src="dlGeometry.js"></script>
         <script type="text/javascript" src="jquery.cookie.js"></script>
         <script type="text/javascript" src="jquery.digilib.js"></script>
+        <script type="text/javascript" src="jquery.digilib.geometry.js"></script>
+        <script type="text/javascript" src="jquery.digilib.birdseye.js"></script>
         <link rel="stylesheet" type="text/css" href="jquery.digilib.css" />
 
 
--- a/client/digitallibrary/jquery/jquery-test-full-rc.html	Fri Feb 11 10:12:41 2011 +0100
+++ b/client/digitallibrary/jquery/jquery-test-full-rc.html	Sat Feb 19 09:56:18 2011 +0100
@@ -12,9 +12,10 @@
         </style>
 
         <script type="text/javascript" src="jquery-1.4.4.js"></script>
-        <script type="text/javascript" src="dlGeometry.js"></script>
         <script type="text/javascript" src="jquery.cookie.js"></script>
         <script type="text/javascript" src="jquery.digilib.js"></script>
+        <script type="text/javascript" src="jquery.digilib.geometry.js"></script>
+        <script type="text/javascript" src="jquery.digilib.birdseye.js"></script>
         <link rel="stylesheet" type="text/css" href="jquery.digilib.css" />
 
 
--- a/client/digitallibrary/jquery/jquery-test-full.html	Fri Feb 11 10:12:41 2011 +0100
+++ b/client/digitallibrary/jquery/jquery-test-full.html	Sat Feb 19 09:56:18 2011 +0100
@@ -6,7 +6,7 @@
 
         <style type="text/css">
             body {
-                 background: silver;
+                 background: grey;
             }
 
             div.digilib {
@@ -56,9 +56,11 @@
         </style>
 
         <script type="text/javascript" src="jquery-1.4.4.js"></script>
-        <script type="text/javascript" src="dlGeometry.js"></script>
         <script type="text/javascript" src="jquery.cookie.js"></script>
         <script type="text/javascript" src="jquery.digilib.js"></script>
+        <script type="text/javascript" src="jquery.digilib.geometry.js"></script>
+        <script type="text/javascript" src="jquery.digilib.birdseye.js"></script>
+        <script type="text/javascript" src="jquery.digilib.regions.js"></script>
         <link rel="stylesheet" type="text/css" href="jquery.digilib.css" />
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/digitallibrary/jquery/jquery-test-svg.html	Sat Feb 19 09:56:18 2011 +0100
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <title>jQuery.digilibSVG test</title>
+
+        <style type="text/css">
+        </style>
+
+        <script type="text/javascript" src="jquery-1.4.4.js"></script>
+        <script type="text/javascript" src="jquery.cookie.js"></script>
+        <script type="text/javascript" src="jquery.digilib.js"></script>
+        <script type="text/javascript" src="jquery.digilib.geometry.js"></script>
+        <script type="text/javascript" src="svg/jquery.svg.js"></script>
+        <script type="text/javascript" src="svg/jquery.digilibSVG.js"></script>
+        <link rel="stylesheet" type="text/css" href="jquery.digilib.css" />
+        <link rel="stylesheet" type="text/css" href="svg/jquery.digilibSVG.css" />
+
+
+        <script type="text/javascript">
+            $(document).ready(function(){
+                var opts = {
+                    interactionMode : 'fullscreen',
+                    scalerBaseUrl : 'http://digilib.biblhertz.it/digilib04/servlet/Scaler'
+                    };
+                var $div = $('div.digilib');
+                $div.digilib(opts);
+                var svgopts = {};
+                $div.digilibSVG(svgopts);
+            });
+
+        </script>
+    </head>
+
+    <body>
+
+        <div id="digilib-1" class="digilib">
+            <img src="http://digilib.biblhertz.it/digilib04/servlet/Scaler?dw=200&amp;dh=200&amp;fn=/digisprint/jquery/FransHals-WillemVanHeythuysen" />
+        </div>
+        <div id="debug">DEBUG</div>
+    </body>
+</html>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/digitallibrary/jquery/jquery.digilib.birdseye.js	Sat Feb 19 09:56:18 2011 +0100
@@ -0,0 +1,323 @@
+/**
+digilib bird's eye view plugin
+ */
+
+(function($) {
+
+    // affine geometry plugin stub
+    var geom;
+
+    var FULL_AREA;
+
+    var buttons = {
+            bird : {
+                onclick : "showBirdDiv",
+                tooltip : "show bird's eye view",
+                icon : "birds-eye.png"
+                }
+    };
+
+    var defaults = {
+            // is birdView shown?
+            'isBirdDivVisible' : false,
+            // dimensions of bird's eye div
+            'birdDivWidth' : 200, 
+            'birdDivHeight' : 200,
+            // parameters used by bird's eye div
+            'birdDivParams' : ['fn','pn','dw','dh']
+    };
+
+    var actions = {
+            // event handler: toggles the visibility of the bird's eye window 
+            showBirdDiv : function (data, show) {
+                var settings = data.settings;
+                if (data.$birdDiv == null) {
+                    // no bird div -> create
+                    setupBirdDiv(data);
+                }
+                var on = digilib.fn.showDiv(settings.isBirdDivVisible, data.$birdDiv, show);
+                settings.isBirdDivVisible = on;
+                digilib.fn.highlightButtons(data, 'bird', on);
+                updateBirdDiv(data);
+                digilib.fn.storeOptions(data);
+            }
+    };
+
+    // plugin installation called by digilib on plugin object.
+    var install = function(digilib) {
+        // import geometry classes
+        geom = digilib.fn.geometry;
+        FULL_AREA = geom.rectangle(0,0,1,1);
+        // add defaults
+        $.extend(digilib.defaults, defaults);
+        // add actions
+        $.extend(digilib.actions, actions);
+        // add buttons
+        $.extend(digilib.buttons, buttons);
+        // insert in button list -- not elegant
+        digilib.defaults.buttonSettings.fullscreen.standardSet.splice(9, 0, 'bird');
+        digilib.defaults.buttonSettings.embedded.standardSet.splice(5, 0, 'bird');
+    };
+
+    // plugin initialization
+    var init = function (data) {
+        var $data = $(data);
+        // install event handler
+        $data.bind('setup', handleSetup);
+        $data.bind('update', handleUpdate);
+        $data.bind('redisplay', handleRedisplay);
+        $data.bind('dragZoom', handleDragZoom);
+    };
+
+
+    var handleSetup = function (evt) {
+        console.debug("birdseye: handleSetup");
+        data = this;
+        // bird's eye view creation
+        if (data.settings.isBirdDivVisible) {
+            setupBirdDiv(data);
+            data.$birdDiv.show();
+        }
+    };
+
+    var handleUpdate = function (evt) {
+        console.debug("birdseye: handleUpdate");
+        data = this;
+        if (data.settings.isBirdDivVisible) {
+            renderBirdArea(data);
+            setupBirdDrag(data);
+        }
+    };
+
+    var handleRedisplay = function (evt) {
+        console.debug("birdseye: handleRedisplay");
+        data = this;
+        if (data.settings.isBirdDivVisible) {
+            updateBirdDiv(data);
+        }
+    };
+
+    var handleDragZoom = function (evt, zoomArea) {
+        //console.debug("birdseye: handleDragZoom za="+zoomArea);
+        data = this;
+        if (data.settings.isBirdDivVisible) {
+            setBirdZoom(data, zoomArea);
+        }
+    };
+
+    // returns URL for bird's eye view image
+    var getBirdImgUrl = function (data) {
+        var settings = data.settings;
+        var birdDivOptions = {
+                dw : settings.birdDivWidth,
+                dh : settings.birdDivHeight
+        };
+        var birdSettings = $.extend({}, settings, birdDivOptions);
+        // use only the relevant parameters
+        var params = digilib.fn.getParamString(birdSettings, settings.birdDivParams, digilib.defaults);
+        var url = settings.scalerBaseUrl + '?' + params;
+        return url;
+    };
+
+    // creates HTML structure for the bird's eye view in elem
+    var setupBirdDiv = function (data) {
+        var $elem = data.$elem;
+        // the bird's eye div
+        var $birdDiv = $('<div class="birdview" style="display:none"/>');
+        // the detail indicator frame
+        var $birdZoom = $('<div class="birdzoom" style="display:none; background-color:transparent;"/>');
+        // the small image
+        var $birdImg = $('<img class="birdimg"/>');
+        data.$birdDiv = $birdDiv;
+        data.$birdZoom = $birdZoom;
+        data.$birdImg = $birdImg;
+        $elem.append($birdDiv);
+        $birdDiv.append($birdZoom);
+        $birdDiv.append($birdImg);
+        // $birdZoom.css(data.settings.birdIndicatorStyle);
+        var birdUrl = getBirdImgUrl(data);
+        $birdImg.load(birdImgLoadedHandler(data));
+        $birdImg.error(function () {console.error("error loading birdview image");});
+        $birdImg.attr('src', birdUrl);
+    };
+
+    // update bird's eye view
+    var updateBirdDiv = function (data) {
+        if (!data.settings.isBirdDivVisible) return;
+        var $birdImg = data.$birdImg;
+        var oldsrc = $birdImg.attr('src');
+        var newsrc = getBirdImgUrl(data);
+        if (oldsrc !== newsrc) {
+            $birdImg.attr('src', newsrc);
+            // onload handler re-renders
+        } else {
+            // re-render
+            renderBirdArea(data);
+            // enable click and drag
+            setupBirdDrag(data);
+        }
+    };
+
+    // returns function for load event of bird's eye view img
+    var birdImgLoadedHandler = function (data) {
+        return function () {
+            var $birdImg = $(this);
+            var birdRect = geom.rectangle($birdImg);
+            console.debug("birdImg loaded!", $birdImg, "rect=", birdRect, "data=", data);
+            if (birdRect.width === 0) {
+                // malheureusement IE7 calls load handler when there is no size info yet 
+                setTimeout(function () { $birdImg.triggerHandler('load'); }, 200);
+                }
+            // update display (zoom area indicator)
+            digilib.fn.updateDisplay(data);
+        };
+    };
+
+    // show zoom area indicator on bird's eye view
+    var renderBirdArea = function (data) {
+        if (data.$birdImg == null || ! data.$birdImg.get(0).complete) return;
+        var $birdZoom = data.$birdZoom;
+        var zoomArea = data.zoomArea;
+        var normalSize = digilib.fn.isFullArea(zoomArea);
+        if (normalSize) {
+            $birdZoom.hide();
+            return;
+        } else {
+            $birdZoom.show();
+        }
+        // create Transform from current area and picsize
+        data.birdTrafo = digilib.fn.getImgTrafo(data.$birdImg, FULL_AREA);
+        var zoomRect = data.birdTrafo.transform(zoomArea);
+        console.debug("renderBirdArea:", zoomRect, "zoomArea:", zoomArea, "$birdTrafo:", data.birdTrafo);
+        // acount for border width
+        var bw = digilib.fn.getBorderWidth($birdZoom);
+        zoomRect.addPosition({x : -bw, y : -bw});
+        if (data.settings.interactionMode === 'fullscreen') {
+            // no animation for fullscreen
+            zoomRect.adjustDiv($birdZoom);
+        } else {
+            // nice animation for embedded mode :-)
+            // correct offsetParent because animate doesn't use offset
+            var ppos = $birdZoom.offsetParent().offset();
+            var dest = {
+                left : (zoomRect.x - ppos.left) + 'px',
+                top : (zoomRect.y - ppos.top) + 'px',
+                width : zoomRect.width,
+                height : zoomRect.height
+                };
+            $birdZoom.animate(dest);
+        }
+    };
+
+    // bird's eye view zoom area click and drag handler
+    var setupBirdDrag = function(data) {
+        var $birdImg = data.$birdImg;
+        var $birdZoom = data.$birdZoom;
+        var $document = $(document);
+        var $scaler = data.$scaler;
+        var startPos, newRect, birdImgRect, birdZoomRect, fullRect, scalerPos;
+        var bw = digilib.fn.getBorderWidth($birdZoom);
+
+        // mousedown handler: start dragging bird zoom to a new position
+        var birdZoomStartDrag = function(evt) {
+            startPos = geom.position(evt);
+            // position may have changed
+            data.birdTrafo = digilib.fn.getImgTrafo($birdImg, FULL_AREA);
+            birdImgRect = geom.rectangle($birdImg);
+            birdZoomRect = geom.rectangle($birdZoom);
+            scalerPos = geom.position($scaler);
+            newRect = null;
+            fullRect = digilib.fn.setZoomBG(data); // setup zoom background image
+            $document.bind("mousemove.dlBirdMove", birdZoomMove);
+            $document.bind("mouseup.dlBirdMove", birdZoomEndDrag);
+            return false;
+        };
+
+        // mousemove handler: drag
+        var birdZoomMove = function(evt) {
+            var pos = geom.position(evt);
+            var delta = startPos.delta(pos);
+            // move birdZoom div, keeping size
+            newRect = birdZoomRect.copy();
+            newRect.addPosition(delta);
+            newRect.stayInside(birdImgRect);
+            // reflect birdview zoom position in scaler image
+            var area = data.birdTrafo.invtransform(newRect);
+            var imgArea = data.imgTrafo.transform(area);
+            var offset = imgArea.getPosition().neg();
+            offset.add(scalerPos);
+            if (fullRect) {
+                var bgPos = fullRect.getPosition().add(offset);
+            } else {
+                var bgPos = offset;
+            }
+            // move the background image to the new position
+            data.$scaler.css({
+                'background-position' : bgPos.x + "px " + bgPos.y + "px"
+                });
+            // acount for border width
+            newRect.addPosition({x : -bw, y : -bw});
+            newRect.adjustDiv($birdZoom);
+            return false;
+        };
+
+        // mouseup handler: reload page
+        var birdZoomEndDrag = function(evt) {
+            var settings = data.settings;
+            $document.unbind("mousemove.dlBirdMove", birdZoomMove);
+            $document.unbind("mouseup.dlBirdMove", birdZoomEndDrag);
+            if (newRect == null) { 
+                // no movement happened - set center to click position
+                startPos = birdZoomRect.getCenter();
+                birdZoomMove(evt); 
+                }
+            // ugly, but needed to prevent double border width compensation
+            newRect.addPosition({x : bw, y : bw});
+            var newArea = data.birdTrafo.invtransform(newRect);
+            data.zoomArea = newArea;
+            digilib.fn.redisplay(data);
+            return false;
+        };
+
+        // clear old handler
+        $document.unbind(".dlBirdMove");
+        $birdImg.unbind(".dlBirdMove");
+        $birdZoom.unbind(".dlBirdMove");
+        if (! digilib.fn.isFullArea(data.zoomArea)) {
+            // set new handler
+            $birdImg.bind("mousedown.dlBirdMove", birdZoomStartDrag);
+            $birdZoom.bind("mousedown.dlBirdMove", birdZoomStartDrag);
+        }
+    };
+
+    // move bird zoom indicator to reflect zoomed detail area
+    var setBirdZoom = function(data, rect) {
+        var part = data.imgTrafo.invtransform(rect);
+        // area = FULL_AREA.fit(part); // no, we want to see where we transcend the borders
+        birdTrafo = digilib.fn.getImgTrafo(data.$birdImg, FULL_AREA);
+        var birdRect = birdTrafo.transform(part);
+        var $birdZoom = data.$birdZoom;
+        // acount for border width
+        var bw = digilib.fn.getBorderWidth($birdZoom);
+        birdRect.addPosition({x : -bw, y : -bw});
+        birdRect.adjustDiv(data.$birdZoom);
+    };
+
+    // plugin object with name and init
+    // shared objects filled by digilib on registration
+    var digilib = {
+            name : 'birdseye',
+            install : install,
+            init : init,
+            buttons : {},
+            actions : {},
+            fn : {},
+            plugins : {}
+    };
+
+    if ($.fn.digilib == null) {
+        $.error("jquery.digilib.birdview must be loaded after jquery.digilib!");
+    } else {
+        $.fn.digilib('plugin', digilib);
+    }
+})(jQuery);
--- a/client/digitallibrary/jquery/jquery.digilib.css	Fri Feb 11 10:12:41 2011 +0100
+++ b/client/digitallibrary/jquery/jquery.digilib.css	Sat Feb 19 09:56:18 2011 +0100
@@ -56,6 +56,11 @@
 	z-index: 1000;
 }
 
+div.digilib div.region {
+	background-color: red;
+	opacity: 0.5;
+}
+
 /* special definitions for fullscreen */
 div.digilib.dl_fullscreen div.buttons {
 	position: fixed;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/digitallibrary/jquery/jquery.digilib.geometry.js	Sat Feb 19 09:56:18 2011 +0100
@@ -0,0 +1,507 @@
+/** required digilib geometry plugin
+ */
+
+(function($) {
+//var dlGeometry = function() {
+    /*
+     * Size class
+     */
+    var size = function(w, h) {
+        var that;
+        if (typeof w === "object") {
+            // assume its size
+            that = {
+                width : w.width,
+                height : w.height
+            };
+        } else {
+            that = {
+                width : parseFloat(w),
+                height : parseFloat(h)
+            };
+        }
+        that.equals = function(other) {
+            return (this.width === other.width && this.height === other.height);
+        };
+        that.toString = function() {
+            return (this.width + "x" + this.height);
+        };
+        return that;
+    };
+
+    /*
+     * Position class
+     */
+    var position = function(x, y) {
+        var that;
+        if (typeof x === "object") {
+            if (x instanceof jQuery) {
+                // jQuery object
+                var pos = x.offset();
+                that = {
+                    x : pos.left,
+                    y : pos.top
+                };
+            } else {
+                if (x.x != null) {
+                    // position object
+                    that = {
+                        x : x.x,
+                        y : x.y
+                    };
+                }
+                if (x.pageX != null) {
+                    // event object
+                    that = {
+                        x : x.pageX,
+                        y : x.pageY
+                    };
+                }
+            }
+        } else {
+            that = {
+                x : parseFloat(x),
+                y : parseFloat(y)
+            };
+        }
+        that.equals = function(other) {
+            return (this.x === other.x && this.y === other.y);
+        };
+        // add position other to this
+        that.add = function(other) {
+            this.x += other.x;
+            this.y += other.y;
+            return this;
+        };
+        // returns negative position
+        that.neg = function() {
+            return position({
+                x : -this.x,
+                y : -this.y
+            });
+        };
+        // returns new position that is the difference between this and other
+        that.delta = function(other) {
+            return position({
+                x : other.x - this.x,
+                y : other.y - this.y
+            });
+        };
+        // adjusts position $elem to this position
+        that.adjustDiv = function($elem) {
+            $elem.offset({
+                left : this.x,
+                top : this.y
+            });
+        };
+        // returns distance of this position to pos (length if pos == null)
+        that.distance = function(pos) {
+            if (pos == null) {
+                pos = {
+                    x : 0,
+                    y : 0
+                };
+            }
+            var dx = pos.x - this.x;
+            var dy = pos.y - this.y;
+            return Math.sqrt(dx * dx + dy * dy);
+        };
+        that.toString = function() {
+            return (this.x + "," + this.y);
+        };
+        return that;
+    };
+    /*
+     * Rectangle class
+     */
+    var rectangle = function(x, y, w, h) {
+        var that = {};
+        if (typeof x === "object") {
+            if (x instanceof jQuery) {
+                // jQuery object
+                var pos = x.offset();
+                that = {
+                    x : pos.left,
+                    y : pos.top,
+                    width : x.width(),
+                    height : x.height()
+                };
+            } else if (y == null) {
+                // assume x is rectangle
+                that = {
+                    x : x.x,
+                    y : x.y,
+                    width : x.width,
+                    height : x.height
+                };
+            } else {
+                // assume x and y are Position
+                that = {
+                    x : Math.min(x.x, y.x),
+                    y : Math.min(x.y, y.y),
+                    width : Math.abs(y.x - x.x),
+                    height : Math.abs(y.y - x.y)
+                };
+            }
+        } else {
+            that = {
+                x : parseFloat(x),
+                y : parseFloat(y),
+                width : parseFloat(w),
+                height : parseFloat(h)
+            };
+        }
+        // returns a copy of this Rectangle
+        that.copy = function() {
+            return rectangle(this);
+        };
+        // returns the position of this Rectangle
+        that.getPosition = function() {
+            return position(this);
+        };
+        // returns the size of this Rectangle
+        that.getSize = function() {
+            return size(this);
+        };
+        // returns the upper left corner position
+        that.getPt1 = that.getPosition;
+        // returns the lower right corner position of this Rectangle
+        that.getPt2 = function() {
+            return position({
+                x : this.x + this.width,
+                y : this.y + this.height
+            });
+        };
+        // sets the upper left corner position to pos
+        that.setPosition = function(pos) {
+            this.x = pos.x;
+            this.y = pos.y;
+            return this;
+        };
+        that.setPt1 = that.setPosition; // TODO: not really the same
+        // adds pos to the position
+        that.addPosition = function(pos) {
+            this.x += pos.x;
+            this.y += pos.y;
+            return this;
+        };
+        // sets the lower right corner to position pos
+        that.setPt2 = function(pos) {
+            this.width = pos.x - this.x;
+            this.height = pos.y - this.y;
+            return this;
+        };
+        // returns the center position of this Rectangle
+        that.getCenter = function() {
+            return position({
+                x : this.x + this.width / 2,
+                y : this.y + this.height / 2
+            });
+        };
+        // moves this Rectangle's center to position pos
+        that.setCenter = function(pos) {
+            this.x = pos.x - this.width / 2;
+            this.y = pos.y - this.height / 2;
+            return this;
+        };
+        that.equals = function(other) {
+            // equal props
+            var eq = (this.x === other.x && this.y === other.y && this.width === other.width);
+            return eq;
+        };
+        // returns the area of this Rectangle
+        that.getArea = function() {
+            return (this.width * this.height);
+        };
+        // eliminates negative width and height
+        that.normalize = function() {
+            var p = this.getPt2();
+            this.x = Math.min(this.x, p.x);
+            this.y = Math.min(this.y, p.y);
+            this.width = Math.abs(this.width);
+            this.height = Math.abs(this.height);
+            return this;
+        };
+        // returns if Position "pos" lies inside of this rectangle
+        that.containsPosition = function(pos) {
+            var ct = ((pos.x >= this.x) && (pos.y >= this.y)
+                    && (pos.x <= this.x + this.width) && (pos.y <= this.y
+                    + this.height));
+            return ct;
+        };
+        // returns if rectangle "rect" is contained in this rectangle
+        that.containsRect = function(rect) {
+            return (this.containsPosition(rect.getPt1()) && this
+                    .containsPosition(rect.getPt2()));
+        };
+        // changes this rectangle's x/y values so it stays inside of rectangle
+        // rect
+        // keeping the proportions
+        that.stayInside = function(rect) {
+            if (this.x < rect.x) {
+                this.x = rect.x;
+            }
+            if (this.y < rect.y) {
+                this.y = rect.y;
+            }
+            if (this.x + this.width > rect.x + rect.width) {
+                this.x = rect.x + rect.width - this.width;
+            }
+            if (this.y + this.height > rect.y + rect.height) {
+                this.y = rect.y + rect.height - this.height;
+            }
+            return this;
+        };
+        // clips this rectangle so it stays inside of rectangle rect
+        that.clipTo = function(rect) {
+            var p1 = rect.getPt1();
+            var p2 = rect.getPt2();
+            var this2 = this.getPt2();
+            this.setPosition(position(Math.max(this.x, p1.x), Math.max(this.y, p1.y)));
+            this.setPt2(position(Math.min(this2.x, p2.x), Math.min(this2.y, p2.y)));
+            return this;
+        };
+        // returns the intersection of the given Rectangle and this one
+        that.intersect = function(rect) {
+            // FIX ME: not really, it should return null if there is no overlap
+            var sec = rect.copy();
+            if (sec.x < this.x) {
+                sec.width = sec.width - (this.x - sec.x);
+                sec.x = this.x;
+            }
+            if (sec.y < this.y) {
+                sec.height = sec.height - (this.y - sec.y);
+                sec.y = this.y;
+            }
+            if (sec.x + sec.width > this.x + this.width) {
+                sec.width = (this.x + this.width) - sec.x;
+            }
+            if (sec.y + sec.height > this.y + this.height) {
+                sec.height = (this.y + this.height) - sec.y;
+            }
+            return sec;
+        };
+        // returns a Rectangle that fits into this one (by moving first)
+        that.fit = function(rect) {
+            var sec = rect.copy();
+            sec.x = Math.max(sec.x, this.x);
+            sec.y = Math.max(sec.y, this.x);
+            if (sec.x + sec.width > this.x + this.width) {
+                sec.x = this.x + this.width - sec.width;
+            }
+            if (sec.y + sec.height > this.y + this.height) {
+                sec.y = this.y + this.height - sec.height;
+            }
+            return sec.intersect(this);
+        };
+        // adjusts position and size of $elem to this rectangle
+        that.adjustDiv = function($elem) {
+            $elem.offset({
+                left : this.x,
+                top : this.y
+            });
+            $elem.width(this.width).height(this.height);
+        };
+        // returns size and position in css-compatible format
+        that.getAsCss = function() {
+            return {
+                left : this.x,
+                top : this.y,
+                width : this.width,
+                height : this.height
+            };
+        };
+        that.toString = function() {
+            return this.width + "x" + this.height + "@" + this.x + "," + this.y;
+        };
+        return that;
+    };
+
+    /*
+     * Transform class
+     * 
+     * defines a class of affine transformations
+     */
+    var transform = function(spec) {
+        var that = {
+            m00 : 1.0,
+            m01 : 0.0,
+            m02 : 0.0,
+            m10 : 0.0,
+            m11 : 1.0,
+            m12 : 0.0,
+            m20 : 0.0,
+            m21 : 0.0,
+            m22 : 1.0
+        };
+        if (spec) {
+            jQuery.extend(that, spec);
+        }
+        ;
+        that.concat = function(trafA) {
+            // add Transform trafA to this Transform (i.e. this = trafC = trafA
+            // * this)
+            var trafC = {};
+            for ( var i = 0; i < 3; i++) {
+                for ( var j = 0; j < 3; j++) {
+                    var c = 0.0;
+                    for ( var k = 0; k < 3; k++) {
+                        c += trafA["m" + i + k] * this["m" + k + j];
+                    }
+                    trafC["m" + i + j] = c;
+                }
+            }
+            jQuery.extend(this, trafC);
+            return this;
+        };
+        that.transform = function(rect) {
+            // returns transformed Rectangle or Position with this Transform
+            // applied
+            var x = this.m00 * rect.x + this.m01 * rect.y + this.m02;
+            var y = this.m10 * rect.x + this.m11 * rect.y + this.m12;
+            var pt = position(x, y);
+            if (rect.width) {
+                // transform the other corner point
+                var pt2 = this.transform(rect.getPt2());
+                return rectangle(pt, pt2);
+            }
+            return pt;
+        };
+        that.invtransform = function(rect) {
+            // returns transformed Rectangle or Position with the inverse of
+            // this Transform applied
+            var det = this.m00 * this.m11 - this.m01 * this.m10;
+            var x = (this.m11 * rect.x - this.m01 * rect.y - this.m11
+                    * this.m02 + this.m01 * this.m12)
+                    / det;
+            var y = (-this.m10 * rect.x + this.m00 * rect.y + this.m10
+                    * this.m02 - this.m00 * this.m12)
+                    / det;
+            var pt = position(x, y);
+            if (rect.width) {
+                // transform the other corner point
+                var pt2 = this.invtransform(rect.getPt2());
+                return rectangle(pt, pt2);
+            }
+            return pt;
+        };
+        that.toString = function(pretty) {
+            var s = '[';
+            if (pretty)
+                s += '\n';
+            for ( var i = 0; i < 3; ++i) {
+                s += '[';
+                for ( var j = 0; j < 3; ++j) {
+                    if (j)
+                        s += ',';
+                    s += this['m' + i + j];
+                }
+                s += ']';
+                if (pretty)
+                    s += '\n';
+            }
+            s += ']';
+            if (pretty)
+                s += '\n';
+            return s;
+        };
+        // add class methods to instance
+        that.getRotation = transform.getRotation;
+        that.getRotationAround = transform.getRotationAround;
+        that.getTranslation = transform.getTranslation;
+        that.getMirror = transform.getMirror;
+        that.getScale = transform.getScale;
+
+        return that;
+    };
+
+    transform.getRotation = function(angle) {
+        // returns a Transform that is a rotation by angle degrees around [0,0]
+        if (angle !== 0) {
+            var t = Math.PI * parseFloat(angle) / 180.0;
+            var cost = Math.cos(t);
+            var sint = Math.sin(t);
+            var traf = {
+                m00 : cost,
+                m01 : -sint,
+                m10 : sint,
+                m11 : cost
+            };
+            return transform(traf);
+        }
+        return transform();
+    };
+
+    transform.getRotationAround = function(angle, pos) {
+        // returns a Transform that is a rotation by angle degrees around pos
+        var traf = transform.getTranslation(pos.neg());
+        traf.concat(transform.getRotation(angle));
+        traf.concat(transform.getTranslation(pos));
+        return traf;
+    };
+
+    transform.getTranslation = function(pos) {
+        // returns a Transform that is a translation by [pos.x, pos,y]
+        var traf = {
+            m02 : pos.x,
+            m12 : pos.y
+        };
+        return transform(traf);
+    };
+
+    transform.getMirror = function(type) {
+        // returns a Transform that is a mirror about the axis type
+        if (type === 'x') {
+            var traf = {
+                m00 : 1,
+                m11 : -1
+            };
+        } else {
+            var traf = {
+                m00 : -1,
+                m11 : 1
+            };
+        }
+        return transform(traf);
+    };
+
+    transform.getScale = function(size) {
+        // returns a Transform that is a scale by [size.width, size.height]
+        var traf = {
+            m00 : size.width,
+            m11 : size.height
+        };
+        return transform(traf);
+    };
+
+    // export constructor functions to digilib plugin
+    var geometry = {
+            size : size,
+            position : position,
+            rectangle : rectangle,
+            transform : transform
+    };
+    // install function called by digilib on plugin object
+    var install = function() {
+        // add constructor object to fn
+        this.fn.geometry = geometry;
+    };
+    // digilib plugin object
+    var plugin = {
+            name : 'geometry',
+            install : install,
+            fn : {},
+            // TODO: remove old init
+            init : init
+    };
+    // TODO: remove old version of init returning contructor
+    var init = function () {
+        return geometry;
+    };
+    // plug into digilib
+    if ($.fn.digilib == null) {
+        $.error("jquery.digilib.geometry must be loaded after jquery.digilib!");
+    } else {
+        $.fn.digilib('plugin', plugin);
+    }
+})(jQuery);
--- a/client/digitallibrary/jquery/jquery.digilib.js	Fri Feb 11 10:12:41 2011 +0100
+++ b/client/digitallibrary/jquery/jquery.digilib.js	Sat Feb 19 09:56:18 2011 +0100
@@ -21,18 +21,17 @@
  * digilib jQuery plugin
 **/ 
 
-
 /*jslint browser: true, debug: true, forin: true
 */
 
 // fallback for console.log calls
-if (typeof(console) === 'undefined') {
+if (typeof console === 'undefined') {
     var console = {
         log : function(){}, 
         debug : function(){}, 
         error : function(){}
         };
-    var customConsole = true;
+    var customConsole = false; // set to true if debugging for MS IE
 }
 
 (function($) {
@@ -82,11 +81,6 @@
             tooltip : "goto image number",
             icon : "page.png"
             },
-        bird : {
-            onclick : "showBirdDiv",
-            tooltip : "show bird's eye view",
-            icon : "birds-eye.png"
-            },
         help : {
             onclick : "showAboutDiv",
             tooltip : "about Digilib",
@@ -223,46 +217,55 @@
             'fullscreen' : {
                 // path to button images (must end with a slash)
                 'imagePath' : 'img/fullscreen/',
-                'standardSet' : ["reference","zoomin","zoomout","zoomarea","zoomfull","pagewidth","back","fwd","page","bird","help","reset","toggleoptions"],
+                'standardSet' : ["reference","zoomin","zoomout","zoomarea","zoomfull","pagewidth","back","fwd","page","help","reset","toggleoptions"],
                 'specialSet' : ["mark","delmark","hmir","vmir","rot","brgt","cont","rgb","quality","size","calibrationx","scale","toggleoptions"],
                 'buttonSets' : ['standardSet', 'specialSet']
                 },
             'embedded' : {
                 'imagePath' : 'img/embedded/16/',
-                'standardSet' : ["reference","zoomin","zoomout","zoomarea","zoomfull","bird","help","reset","toggleoptions"],
+                'standardSet' : ["reference","zoomin","zoomout","zoomarea","zoomfull","help","reset","toggleoptions"],
                 'specialSet' : ["mark","delmark","hmir","vmir","rot","brgt","cont","rgb","quality","scale","toggleoptions"],
                 'buttonSets' : ['standardSet', 'specialSet']
                 }
         },
-        
         // number of visible button groups
         'visibleButtonSets' : 1,
-        // is birdView shown?
-        'isBirdDivVisible' : false,
-        // dimensions of bird's eye div
-        'birdDivWidth' : 200, 
-        'birdDivHeight' : 200,
-        // parameters used by bird's eye div
-        'birdDivParams' : ['fn','pn','dw','dh'],
-        // style of the zoom area indicator in the bird's eye div 
-        'birdIndicatorStyle' : {'border' : '2px solid #ff0000' },
-        // style of zoom area "rubber band"
-        'zoomrectStyle' : {'border' : '2px solid #ff0000' },
         // is the "about" window shown?
         'isAboutDivVisible' : false,
-        // maximum width of background image for drag-scroll
-        'maxBgSize' : 10000
-
+        // default size of background image for drag-scroll (same as Bird's Eye View image)
+        'bgImgWidth' : 200,
+        'bgImgHeight' : 200,
+        // maximum width or height of background image for drag-scroll
+        'maxBgSize' : 10000,
+        // parameters used by background image
+        'bgImgParams' : ['fn','pn','dw','dh','mo','rot'],
+        // space to be left free in full page display, default value is for scrollbar
+        'scalerInset' : 10
         };
 
-    // affine geometry classes
-    var geom = dlGeometry();
+    // list of plugins
+    var plugins = {};
+    // object to export functions to plugins
+    var fn;
+    // affine geometry plugin stub
+    var geom;
 
-    var FULL_AREA = geom.rectangle(0, 0, 1, 1);
+    var FULL_AREA;
 
     var actions = {
         // init: digilib initialization
         init : function(options) {
+            // import geometry classes
+            if (plugins.geometry == null) {
+                $.error("jquery.digilib.geometry plugin not found!");
+                // last straw: old version
+                geom = dlGeometry();
+            } else {
+                // geometry plugin puts classes in the shared fn
+                geom = fn.geometry;
+            }
+            FULL_AREA  = geom.rectangle(0, 0, 1, 1);
+
             // settings for this digilib instance are merged from defaults and options
             var settings = $.extend({}, defaults, options);
             var isFullscreen = settings.interactionMode === 'fullscreen';
@@ -294,28 +297,30 @@
                         params = queryParams;
                     } else {
                         params = parseImgParams($elem);
-                        if (jQuery.cookie) {
+                        if ($.cookie) {
                             // retrieve params from cookie
                             var ck = "digilib-embed:fn:" + escape(params.fn) + ":pn:" + (params.pn || '1');
-                            var cs = jQuery.cookie(ck);
+                            var cs = $.cookie(ck);
                             console.debug("get cookie=", ck, " value=", cs);
                             if (cs) {
                                 var cp = parseQueryString(cs);
                                 // ignore fn and pn from cookie TODO: should we keep pn?
                                 delete cp.fn;
                                 delete cp.pn;
-                                jQuery.extend(params, cp);
+                                $.extend(params, cp);
                             }
                         }
                     }
-                    // store $(this) element in the settings
-                    elemSettings = jQuery.extend({}, settings, params);
+                    // store $(this) element in data
+                    elemSettings = $.extend({}, settings, params);
                     data = {
                             $elem : $elem,
                             settings : elemSettings,
-                            queryParams : params
+                            queryParams : params,
+                            // TODO: move plugins reference out of data
+                            plugins : plugins
                     };
-                    // store in data element
+                    // store in jQuery data element
                     $elem.data('digilib', data);
                 }
                 unpackParams(data);
@@ -343,26 +348,29 @@
                         }
                     }
                 }
+                // initialise plugins
+                for (n in plugins) {
+                    var p = plugins[n];
+                    if (typeof p.init === 'function') {
+                        p.init(data);
+                    }
+                }
                 // get image info from server if needed
                 if (data.scaleMode === 'pixel' || data.scaleMode === 'size') {
-                    loadImageInfo(data, updateDisplay); // updateDisplay(data) on completion
+                    $(data).bind('imageInfo', handleImageInfo);
+                    loadImageInfo(data); // triggers "imageInfo" on completion
                 }
-                // create HTML structure for scaler
-                setupScalerDiv(data);
-                // add buttons
+                // create buttons before scaler 
                 for (var i = 0; i < elemSettings.visibleButtonSets; ++i) {
                     showButtons(data, true, i);
                     }
+                // create HTML structure for scaler, taking width of buttons div into account
+                setupScalerDiv(data);
                 highlightButtons(data);
-                // bird's eye view creation
-                if (elemSettings.isBirdDivVisible) {
-                    setupBirdDiv(data);
-                    data.$birdDiv.show();
-                    }
                 // about window creation - TODO: could be deferred? restrict to only one item?
                 setupAboutDiv(data);
-                // drag zoom area around in scaler div 
-                // setupZoomDrag(data); // is done in scalerImgLoadedHandler()
+                // send setup event
+                $(data).trigger('setup');
             });
         },
 
@@ -383,20 +391,6 @@
             highlightButtons(data, 'help', on);
         },
 
-        // event handler: toggles the visibility of the bird's eye window 
-        showBirdDiv : function (data, show) {
-            var settings = data.settings;
-            if (data.$birdDiv == null) {
-                // no bird div -> create
-                setupBirdDiv(data);
-            }
-            var on = showDiv(settings.isBirdDivVisible, data.$birdDiv, show);
-            settings.isBirdDivVisible = on;
-            highlightButtons(data, 'bird', on);
-            updateBirdDiv(data);
-            storeOptions(data);
-        },
-
         // goto given page nr (+/-: relative)
         gotoPage : function (data, pageNr) {
             var settings = data.settings;
@@ -614,7 +608,7 @@
                 redisplay(data);
             }
         },
-        
+
         // set image scale mode
         setScaleMode : function (data, mode) {
             var oldM = getScaleMode(data);
@@ -627,7 +621,7 @@
                 redisplay(data);
             }
         }
-        
+
     // end of actions
     };
 
@@ -698,31 +692,25 @@
     };
 
     // returns URL for bird's eye view image
-    var getBirdImgUrl = function (data, moreParams) {
+    var getBgImgUrl = function (data, moreParams) {
         var settings = data.settings;
-        var birdDivOptions = {
-                dw : settings.birdDivWidth,
-                dh : settings.birdDivHeight
+        var bgOptions = {
+                dw : settings.bgImgWidth,
+                dh : settings.bgImgHeight
         };
-        var birdSettings = jQuery.extend({}, settings, birdDivOptions);
-        // use only the relevant parameters
-        if (moreParams == null) {
-            var params = getParamString(birdSettings, settings.birdDivParams, defaults);
-        } else {
-            // filter scaler flags
-            if (birdSettings.mo != null) {
-                var mo = '';
-                if (data.scalerFlags.hmir != null) {
-                    mo += 'hmir,';
-                }
-                if (data.scalerFlags.vmir != null) {
-                    mo += 'vmir';
-                }
-                birdSettings.mo = mo;
+        var bgSettings = $.extend({}, settings, bgOptions);
+        // filter scaler flags
+        if (bgSettings.mo != null) {
+            var mo = '';
+            if (data.scalerFlags.hmir != null) {
+                mo += 'hmir,';
             }
-            var params = getParamString(birdSettings, 
-                    settings.birdDivParams.concat(moreParams), defaults);
+            if (data.scalerFlags.vmir != null) {
+                mo += 'vmir';
+            }
+            bgSettings.mo = mo;
         }
+        var params = getParamString(bgSettings, settings.bgImgParams, defaults);
         var url = settings.scalerBaseUrl + '?' + params;
         return url;
     };
@@ -735,22 +723,21 @@
         return settings.digilibBaseUrl + '?' + queryString;
     };
 
-    // loads image information from digilib server via HTTP (and calls complete-fn)
-    var loadImageInfo = function (data, complete) {
+    // loads image information from digilib server via HTTP
+    var loadImageInfo = function (data) {
         var settings = data.settings;
         var p = settings.scalerBaseUrl.indexOf('/servlet/Scaler');
         var url = settings.scalerBaseUrl.substring(0, p) + '/ImgInfo-json.jsp';
         url += '?' + getParamString(settings, ['fn', 'pn'], defaults);
         // TODO: better error handling
-        jQuery.getJSON(url, function (json) {
+        $.getJSON(url, function (json) {
             console.debug("got json data=", json);
             data.imgInfo = json;
-            if (complete != null) {
-                complete.call(this, data, json);
-            }
+            // send event
+            $(data).trigger('imageInfo', [json]);
         });
     };
-    
+
     // processes some parameters into objects and stuff
     var unpackParams = function (data) {
         var settings = data.settings;
@@ -837,18 +824,18 @@
                     }
                 clop += o + '=' + data.dlOpts[o];
                 }
-            if (jQuery.cookie) {
+            if ($.cookie) {
                 var ck = "digilib:fn:" + escape(settings.fn) + ":pn:" + settings.pn;
                 console.debug("set cookie=", ck, " value=", clop);
-                jQuery.cookie(ck, clop);
+                $.cookie(ck, clop);
                 }
         }
-        if (settings.interactionMode !== 'fullscreen' && jQuery.cookie) {
+        if (settings.interactionMode !== 'fullscreen' && $.cookie) {
             // store normal parameters in cookie for embedded mode
             var qs = getParamString(settings, settings.digilibParamNames, defaults);
             var ck = "digilib-embed:fn:" + escape(settings.fn) + ":pn:" + settings.pn;
             console.debug("set cookie=", ck, " value=", qs);
-            jQuery.cookie(ck, qs);
+            $.cookie(ck, qs);
         }
     };
 
@@ -856,10 +843,10 @@
         // clop (digilib options)
         var opts = {};
         var settings = data.settings;
-        if (jQuery.cookie) {
+        if ($.cookie) {
             // read from cookie
             var ck = "digilib:fn:" + escape(settings.fn) + ":pn:" + settings.pn;
-            var cp = jQuery.cookie(ck);
+            var cp = $.cookie(ck);
             console.debug("get cookie=", ck, " value=", cp);
             // in query string format
             opts = parseQueryString(cp);
@@ -882,17 +869,27 @@
             // update location.href (browser URL) in fullscreen mode
             var url = getDigilibUrl(data);
             var history = window.history;
-            if (typeof(history.pushState) === 'function') {
-                console.debug("we could modify history, but we don't...");
-                }
-            window.location = url;
+            if (typeof history.pushState === 'function') {
+                console.debug("faking reload to "+url);
+                // change url without reloading (stateObj, title, url)
+                history.pushState({}, '', url);
+                // change img src
+                var imgurl = getScalerUrl(data);
+                data.$img.attr('src', imgurl);
+                highlightButtons(data);
+                // send event
+                $(data).trigger('redisplay');
+            } else {
+                // reload window
+                window.location = url;
+            }
         } else {
             // embedded mode -- just change img src
             var url = getScalerUrl(data);
             data.$img.attr('src', url);
-            // redisplay bird img
-            updateBirdDiv(data);
             highlightButtons(data);
+            // send event
+            $(data).trigger('redisplay');
             }
     };
 
@@ -901,20 +898,29 @@
         updateImgTrafo(data);
         renderMarks(data);
         setupZoomDrag(data);
-        if (data.settings.isBirdDivVisible) {
-            renderBirdArea(data);
-            setupBirdDrag(data);
-        }
-        // TODO: update event subscriber?
+        // send event
+        $(data).trigger('update');
     };
-    
+
     // returns maximum size for scaler img in fullscreen mode
-    var getFullscreenImgSize = function ($elem) {
+    var getFullscreenImgSize = function (data) {
         var $win = $(window);
         var winH = $win.height();
         var winW = $win.width();
-        // TODO: account for borders?
-        return geom.size(winW, winH);
+        var $body = $('body');
+         // include standard body margins
+        var borderW = $body.outerWidth(true) - $body.width();
+        var borderH = $body.outerHeight(true) - $body.height();
+        // get width of first button div
+        var buttonsW = 0; 
+        if (data.$buttonSets) {
+            buttonsW = data.$buttonSets[0].outerWidth();
+        }
+        // account for left/right border, body margins and additional requirements
+        var calcW = winW - borderW - buttonsW - data.settings.scalerInset;
+        var calcH = winH - borderH;
+        console.debug(winW, winH, 'winW:', $win.width(), 'border:', borderW, 'buttonsW:', buttonsW, 'calc:', calcW);
+        return geom.size(calcW, calcH);
     };
 
     // creates HTML structure for digilib in elem
@@ -927,7 +933,7 @@
         if (settings.interactionMode === 'fullscreen') {
             // fullscreen
             $elem.addClass('dl_fullscreen');
-            var imgSize = getFullscreenImgSize($elem);
+            var imgSize = getFullscreenImgSize(data);
             // fitwidth/height omits destination height/width
             if (data.dlOpts.fitheight == null) {
                 settings.dw = imgSize.width;
@@ -954,10 +960,11 @@
                 $img = $('<img/>');
             }
         }
-        // create new html
-        $elem.empty(); // TODO: should we keep stuff for customization?
+        // create new inner html, keep buttons
+        $elem.contents(":not(div.buttons)").remove();
         var $scaler = $('<div class="scaler"/>');
-        $elem.append($scaler);
+        // scaler should be the first child element?
+        $elem.prepend($scaler);
         $scaler.append($img);
         $img.addClass('pic');
         data.$scaler = $scaler;
@@ -1033,45 +1040,6 @@
         return $buttonsDiv;
     };
 
-    // creates HTML structure for the bird's eye view in elem
-    var setupBirdDiv = function (data) {
-        var $elem = data.$elem;
-        // the bird's eye div
-        var $birdDiv = $('<div class="birdview" style="display:none"/>');
-        // the detail indicator frame
-        var $birdZoom = $('<div class="birdzoom" style="display:none; background-color:transparent;"/>');
-        // the small image
-        var $birdImg = $('<img class="birdimg"/>');
-        data.$birdDiv = $birdDiv;
-        data.$birdZoom = $birdZoom;
-        data.$birdImg = $birdImg;
-        $elem.append($birdDiv);
-        $birdDiv.append($birdZoom);
-        $birdDiv.append($birdImg);
-        $birdZoom.css(data.settings.birdIndicatorStyle);
-        var birdUrl = getBirdImgUrl(data);
-        $birdImg.load(birdImgLoadedHandler(data));
-        $birdImg.error(function () {console.error("error loading birdview image");});
-        $birdImg.attr('src', birdUrl);
-    };
-
-    // update bird's eye view
-    var updateBirdDiv = function (data) {
-        if (!data.settings.isBirdDivVisible) return;
-        var $birdImg = data.$birdImg;
-        var oldsrc = $birdImg.attr('src');
-        var newsrc = getBirdImgUrl(data);
-        if (oldsrc !== newsrc) {
-            $birdImg.attr('src', newsrc);
-            // onload handler re-renders
-        } else {
-            // re-render
-            renderBirdArea(data);
-            // enable click and drag
-            setupBirdDrag(data);
-        }
-    };
-
     // creates HTML structure for the about view in elem
     var setupAboutDiv = function (data) {
         var $elem = data.$elem;
@@ -1089,12 +1057,11 @@
         $logo.attr('src', settings.logoUrl);
         $link.attr('href', settings.homeUrl);
         $content.text('Version: ' + settings.version);
+        data.$aboutDiv = $aboutDiv;
         // click hides
-        $aboutDiv.bind('click.digilib', function () { 
-            settings.isAboutDivVisible = showDiv(settings.isAboutDivVisible, $aboutDiv, 0);
-            return false;
+        $aboutDiv.bind('click.digilib', function () {
+            actions['showAboutDiv'](data, false);
             });
-        data.$aboutDiv = $aboutDiv;
     };
 
     // shows some window e.g. 'about' (toggle visibility if show is null)
@@ -1130,6 +1097,7 @@
             if ($set == null) return false;
             // include border in calculation
             var btnWidth = $set.outerWidth();
+            // console.debug("btnWidth", btnWidth);
             // move remaining sets left and show new set
             if ($otherSets.length > 0) {
                     $otherSets.animate({right : '+='+btnWidth+'px'}, atime,
@@ -1141,7 +1109,7 @@
             // remove set
             var $set = data.$buttonSets[setIdx];
             if ($set == null) return false;
-            var btnWidth = $set.width();
+            var btnWidth = $set.outerWidth();
             // hide last set
             $set.hide();
             // take remaining sets and move right
@@ -1178,7 +1146,7 @@
         highlight('quality', flags.q1 || flags.q2);
         highlight('zoomin', ! isFullArea(data.zoomArea));
         };
-        
+
     // create Transform from area and $img
     var getImgTrafo = function ($img, area, rot, hmir, vmir, mode, data) {
         var picrect = geom.rectangle($img);
@@ -1245,8 +1213,8 @@
             // console.debug("imgTrafo=", data.imgTrafo);
         }
     };
-    
-    // returns function for load event of scaler img
+
+    // returns handler for load event of scaler img
     var scalerImgLoadedHandler = function (data) {
         return function () {
             var $img = $(this);
@@ -1263,21 +1231,12 @@
         };
     };
 
-    // returns function for load event of bird's eye view img
-    var birdImgLoadedHandler = function (data) {
-        return function () {
-            var $birdImg = $(this);
-            var birdRect = geom.rectangle($birdImg);
-            console.debug("birdImg loaded!", $birdImg, "rect=", birdRect, "data=", data);
-            if (birdRect.width === 0) {
-                // malheureusement IE7 calls load handler when there is no size info yet 
-                setTimeout(function () { $birdImg.triggerHandler('load'); }, 200);
-                }
-            // update display (zoom area indicator)
-            updateDisplay(data);
-        };
+    // handler for imageInfo loaded event
+    var handleImageInfo = function (evt, json) {
+        var data = this;
+        updateDisplay(data);
     };
-
+    
     // place marks on the image
     var renderMarks = function (data) {
         if (data.$img == null || data.imgTrafo == null) return;
@@ -1300,41 +1259,6 @@
             }
     };
 
-    // show zoom area indicator on bird's eye view
-    var renderBirdArea = function (data) {
-        if (data.$birdImg == null || ! data.$birdImg.get(0).complete) return;
-        var $birdZoom = data.$birdZoom;
-        var zoomArea = data.zoomArea;
-        var normalSize = isFullArea(zoomArea);
-        if (normalSize) {
-            $birdZoom.hide();
-            return;
-        } else {
-            $birdZoom.show();
-        }
-        // create Transform from current area and picsize
-        data.birdTrafo = getImgTrafo(data.$birdImg, FULL_AREA);
-        var zoomRect = data.birdTrafo.transform(zoomArea);
-        console.debug("renderBirdArea:", zoomRect, "zoomArea:", zoomArea, "$birdTrafo:", data.birdTrafo);
-        // acount for border width
-        zoomRect.addPosition({x : -2, y : -2});
-        if (data.settings.interactionMode === 'fullscreen') {
-            // no animation for fullscreen
-            zoomRect.adjustDiv($birdZoom);
-        } else {
-            // nice animation for embedded mode :-)
-            // correct offsetParent because animate doesn't use offset
-            var ppos = $birdZoom.offsetParent().offset();
-            var dest = {
-                left : (zoomRect.x - ppos.left) + 'px',
-                top : (zoomRect.y - ppos.top) + 'px',
-                width : zoomRect.width,
-                height : zoomRect.height
-                };
-            $birdZoom.animate(dest);
-        }
-    };
-
     // zooms by the given factor
     var zoomBy = function(data, factor) {
         var area = data.zoomArea;
@@ -1374,7 +1298,7 @@
         var pt1, pt2;
         var $zoomDiv = $('<div class="zoomrect" style="display:none"/>');
         $elem.append($zoomDiv);
-        $zoomDiv.css(data.settings.zoomrectStyle);
+        // $zoomDiv.css(data.settings.zoomrectStyle);
         var picRect = geom.rectangle($scaler);
         // FIX ME: is there a way to query the border width from CSS info?
         // rect.x -= 2; // account for overlay borders
@@ -1433,97 +1357,6 @@
         $scaler.one('mousedown.dlZoomArea', zoomStart);
     };
 
-    // bird's eye view zoom area click and drag handler
-    var setupBirdDrag = function(data) {
-        var $birdImg = data.$birdImg;
-        var $birdZoom = data.$birdZoom;
-        var $document = $(document);
-        var $scaler = data.$scaler;
-        var startPos, newRect, birdImgRect, birdZoomRect, fullRect, scalerPos;
-
-        // mousedown handler: start dragging bird zoom to a new position
-        var birdZoomStartDrag = function(evt) {
-            startPos = geom.position(evt);
-            // position may have changed
-            data.birdTrafo = getImgTrafo($birdImg, FULL_AREA);
-            birdImgRect = geom.rectangle($birdImg);
-            birdZoomRect = geom.rectangle($birdZoom);
-            scalerPos = geom.position($scaler);
-            newRect = null;
-            fullRect = setZoomBG(data); // setup zoom background image
-            $document.bind("mousemove.dlBirdMove", birdZoomMove);
-            $document.bind("mouseup.dlBirdMove", birdZoomEndDrag);
-            return false;
-        };
-
-        // mousemove handler: drag
-        var birdZoomMove = function(evt) {
-            var pos = geom.position(evt);
-            var delta = startPos.delta(pos);
-            // move birdZoom div, keeping size
-            newRect = birdZoomRect.copy();
-            newRect.addPosition(delta);
-            newRect.stayInside(birdImgRect);
-            // reflect birdview zoom position in scaler image
-            var area = data.birdTrafo.invtransform(newRect);
-            var imgArea = data.imgTrafo.transform(area);
-            var offset = imgArea.getPosition().neg();
-            offset.add(scalerPos);
-            if (fullRect) {
-                var bgPos = fullRect.getPosition().add(offset);
-            } else {
-                var bgPos = offset;
-            }
-            // move the background image to the new position
-            data.$scaler.css({
-                'background-position' : bgPos.x + "px " + bgPos.y + "px"
-                });
-            // acount for border width
-            newRect.addPosition({x : -2, y : -2});
-            newRect.adjustDiv($birdZoom);
-            return false;
-        };
-
-        // mouseup handler: reload page
-        var birdZoomEndDrag = function(evt) {
-            var settings = data.settings;
-            $document.unbind("mousemove.dlBirdMove", birdZoomMove);
-            $document.unbind("mouseup.dlBirdMove", birdZoomEndDrag);
-            if (newRect == null) { 
-                // no movement happened - set center to click position
-                startPos = birdZoomRect.getCenter();
-                birdZoomMove(evt); 
-                }
-            // ugly, but needed to prevent double border width compensation
-            newRect.addPosition({x : +2, y : +2});
-            var newArea = data.birdTrafo.invtransform(newRect);
-            data.zoomArea = newArea;
-            redisplay(data);
-            return false;
-        };
-
-        // clear old handler
-        $document.unbind(".dlBirdMove");
-        $birdImg.unbind(".dlBirdMove");
-        $birdZoom.unbind(".dlBirdMove");
-        if (! isFullArea(data.zoomArea)) {
-            // set new handler
-            $birdImg.bind("mousedown.dlBirdMove", birdZoomStartDrag);
-            $birdZoom.bind("mousedown.dlBirdMove", birdZoomStartDrag);
-        }
-    };
-
-    // move bird zoom indicator to reflect zoomed detail area
-    var setBirdZoom = function(data, rect) {
-        var part = data.imgTrafo.invtransform(rect);
-        // area = FULL_AREA.fit(part); // no, we want to see where we transcend the borders
-        birdTrafo = getImgTrafo(data.$birdImg, FULL_AREA);
-        var birdRect = birdTrafo.transform(part);
-        // acount for border width
-        birdRect.addPosition({x : -2, y : -2});
-        birdRect.adjustDiv(data.$birdZoom);
-    };
-
     // set zoom background
     var setZoomBG = function(data) {
         var $scaler = data.$scaler;
@@ -1547,7 +1380,7 @@
                 // correct offset because background is relative
                 var scalerPos = geom.position($scaler);
                 fullRect.addPosition(scalerPos.neg());
-                var url = getBirdImgUrl(data, ['rot', 'mo']);
+                var url = getBgImgUrl(data);
                 scalerCss['background-image'] = 'url(' + url + ')';
                 scalerCss[data.bgSizeName] = fullRect.width + 'px ' + fullRect.height + 'px';
                 scalerCss['background-position'] = fullRect.x + 'px '+ fullRect.y + 'px';
@@ -1565,6 +1398,7 @@
     var setupZoomDrag = function(data) {
         var startPos, delta, fullRect;
         var $document = $(document);
+        var $data = $(data);
         var $elem = data.$elem;
         var $scaler = data.$scaler;
         var $img = data.$img;
@@ -1599,7 +1433,8 @@
             // set birdview indicator to reflect new zoom position
             var za = geom.rectangle($img);
             za.addPosition(delta.neg());
-            setBirdZoom(data, za);
+            $data.trigger('dragZoom', [za]);
+            //TODO: setBirdZoom(data, za);
             return false;
             };
 
@@ -1663,7 +1498,7 @@
         // mo=fit is default
         return 'screen';
     };
-    
+
     // set image scale mode (screen, pixel, size)
     var setScaleMode = function (data, mode) {
         delete data.scalerFlags.fit;
@@ -1676,7 +1511,7 @@
         }
         // mo=fit is default
     };
-    
+
      // sets a key to a value (relative values with +/- if relative=true)
     var setNumValue = function(settings, key, value) {
         if (value == null) return null;
@@ -1697,13 +1532,19 @@
         return settings[key];
     };
 
+    // auxiliary function, assuming equal border width on all sides
+    var getBorderWidth = function($elem) {
+        var border = $elem.outerWidth() - $elem.width();
+        return border/2;
+    };
+
     // auxiliary function (from old dllib.js)
-    var isFullArea = function(area) {
+    var isFullArea = function (area) {
         return (area.width === 1.0) && (area.height === 1.0);
     };
 
     // auxiliary function (from Douglas Crockford, A.10)
-    var isNumber = function isNumber(value) {
+    var isNumber = function (value) {
         return typeof value === 'number' && isFinite(value);
     };
 
@@ -1731,28 +1572,50 @@
         console.error = logFunction('_error');
         }
 
-    // hook plugin into jquery
-    $.fn.digilib = function(action, obj) {
-        // plugin extension mechanism
-        if (action === 'extendPlugin') {
-            // for each digilib $elem extend data.settings with obj.options
-            if (obj.options) {
-                this.each(function() {
-                    var $elem = $(this);
-                    // console.debug('extending:', $elem);
-                    var data = $elem.data('digilib');
-                    if (!data) {
-                        return console.log('cannot extend digilib plugin, element not initialised!');
-                        }
-                    var settings = data.settings;
-                    $.extend(settings, obj.options);
-                    // console.log('settings:', settings);
-                    });
+    // functions to export to plugins
+    fn = {
+            geometry : geom,
+            parseQueryString : parseQueryString,
+            getScalerUrl : getScalerUrl,
+            getParamString : getParamString,
+            getDigilibUrl : getDigilibUrl,
+            unpackParams : unpackParams,
+            packParams : packParams,
+            storeOptions : storeOptions,
+            redisplay : redisplay,
+            updateDisplay : updateDisplay,
+            highlightButtons : highlightButtons,
+            showDiv : showDiv,
+            setZoomBG : setZoomBG,
+            getImgTrafo : getImgTrafo,
+            getQuality : getQuality,
+            setQuality : setQuality,
+            getScaleMode : getScaleMode,
+            setScaleMode : setScaleMode,
+            isFullArea : isFullArea,
+            getBorderWidth : getBorderWidth
+    };
+
+    // hook digilib plugin into jquery
+    $.fn.digilib = function (action) {
+        // plugin extension mechanism, called when the plugins' code is read 
+        if (action === 'plugin') {
+            var plugin = arguments[1];
+            // each plugin needs a name
+            if (plugin.name != null) {
+                plugins[plugin.name] = plugin;
+                // share common objects
+                plugin.defaults = defaults;
+                plugin.buttons = buttons;
+                plugin.actions = actions;
+                plugin.fn = fn;
+                plugin.plugins = plugins;
+                // and install
+                if (typeof plugin.install === 'function') {
+                    plugin.install(plugin);
                 }
-            delete(obj.options);
-            // extend the plugin actions (to make this useful, 
-            // maybe we need to expose some more internal functions)
-            $.extend(actions, obj);
+            }
+            // plugins will be initialised when action.init is called
         } else if (actions[action]) {
             // call action on this with the remaining arguments (inserting data as first argument)
             var $elem = $(this);
@@ -1760,8 +1623,8 @@
             var args = Array.prototype.slice.call(arguments, 1);
             args.unshift(data);
             return actions[action].apply(this, args);
-        } else if (typeof(action) === 'object' || !action) {
-            // call init on this
+        } else if (typeof action === 'object' || !action) {
+            // call init on the digilib jQuery object
             return actions.init.apply(this, arguments);
         } else {
             $.error('action ' + action + ' does not exist on jQuery.digilib');
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/digitallibrary/jquery/jquery.digilib.regions.js	Sat Feb 19 09:56:18 2011 +0100
@@ -0,0 +1,268 @@
+/** optional digilib regions plugin
+
+markup a digilib image with rectangular regions
+
+TODO:
+- store region in params/cookie, regarding zoom, mirror, rotation (like marks)
+- set regions programmatically
+- read regions from params/cookie and display
+- backlink mechanism
+- don't write to data.settings?
+*/
+
+(function($) {
+    // the data object passed by digilib
+    var data;
+    var buttons;
+    var fn;
+    // affine geometry plugin stub
+    var geom;
+
+    var FULL_AREA;
+
+    var buttons = {
+        addregion : {
+            onclick : "setRegion",
+            tooltip : "set a region",
+            icon : "addregion.png"
+            },
+        delregion : {
+            onclick : "removeRegion",
+            tooltip : "delete the last region",
+            icon : "delregion.png"
+            },
+        regions : {
+            onclick : "toggleRegions",
+            tooltip : "show or hide regions",
+            icon : "regions.png"
+            },
+        regioninfo : {
+            onclick : "infoRegions",
+            tooltip : "information about regions",
+            icon : "regioninfo.png"
+            }
+        };
+
+    var defaults = {
+        // are regions shown?
+        'isRegionVisible' : true,
+        // buttonset of this plugin
+        'regionSet' : ['addregion', 'delregion', 'regions', 'regioninfo', 'lessoptions'],
+        // array of defined regions
+        'regions' : []
+    };
+
+    var actions = { 
+        // define a region interactively with two clicked points
+        "setRegion" : function(data) {
+            var $elem = data.$elem;
+            var $scaler = data.$scaler;
+            var picRect = geom.rectangle($scaler);
+            var pt1, pt2;
+            // TODO: temporary rectangle only, pass values to "addRegion" factory
+            var $tempDiv = $('<div class="region" style="display:none"/>');
+            $elem.append($tempDiv);
+
+            var regionStart = function (evt) {
+                pt1 = geom.position(evt);
+                // setup and show zoom div
+                pt1.adjustDiv($tempDiv);
+                $tempDiv.width(0).height(0);
+                $tempDiv.show();
+                // register events
+                $elem.bind("mousemove.dlRegion", regionMove);
+                $elem.bind("mouseup.dlRegion", regionEnd);
+                return false;
+            };
+
+            // mouse move handler
+            var regionMove = function (evt) {
+                pt2 = geom.position(evt);
+                var rect = geom.rectangle(pt1, pt2);
+                rect.clipTo(picRect);
+                // update zoom div
+                rect.adjustDiv($tempDiv);
+                return false;
+            };
+
+            // mouseup handler: end moving
+            var regionEnd = function (evt) {
+                pt2 = geom.position(evt);
+                // assume a click and continue if the area is too small
+                var clickRect = geom.rectangle(pt1, pt2);
+                if (clickRect.getArea() <= 5) return false;
+                // unregister events
+                $elem.unbind("mousemove.dlRegion", regionMove);
+                $elem.unbind("mouseup.dlRegion", regionEnd);
+                // clip and transform
+                clickRect.clipTo(picRect);
+                clickRect.adjustDiv($tempDiv);
+                $tempDiv.remove();
+                data.settings.regions.push(clickRect);
+                // fn.redisplay(data);
+                return false;
+            };
+
+            // clear old handler (also ZoomDrag)
+            $scaler.unbind('.dlRegion');
+            $elem.unbind('.dlRegion');
+            // bind start zoom handler
+            $scaler.one('mousedown.dlRegion', regionStart);
+        },
+
+        // remove the last added region
+        "removeRegion" : function (data) {
+            var $regionDiv = data.settings.regions.pop();
+            $regionDiv.remove();
+            // fn.redisplay(data);
+        },
+
+        // add a region programmatically
+        "addRegion" : function(data, pos, url) {
+            // TODO: backlink mechanism
+            if (pos.length === 4) {
+                // TODO: trafo
+                var $regionDiv = $('<div class="region" style="display:none"/>');
+                $regionDiv.attr("id", "region" + i);
+                var regionRect = geom.rectangle(pos[0], pos[1], pos[2], pos[3]);
+                regionRect.adjustDiv($regionDiv);
+                if (!data.regions) {
+                    data.regions = [];
+                    }
+                data.regions.push($regionDiv);
+            }
+        }
+    };
+
+    var addRegion = actions.addRegion;
+
+    var realizeRegions = function (data) { 
+        // create regions from parameters
+        var settings = data.settings;
+        var rg = settings.rg;
+        var regions = rg.split(",");
+        for (var i = 0; i < regions.length ; i++) {
+            var pos = regions.split("/", 4);
+            // TODO: backlink mechanism
+            var url = paramString.match(/http.*$/);
+            addRegion(data, pos, url);
+            }
+    };
+
+    // display current regions
+    var renderRegions = function (data) { 
+        var regions = data.regions;
+        for (var i = 0; i < regions.length; i++) {
+            var region = regions[i];
+            if (data.zoomArea.containsPosition(region)) {
+                var rpos = data.imgTrafo.transform(region);
+                console.debug("renderRegions: rpos=", rpos);
+                // create region
+                var $regionDiv = $('<div class="region" style="display:none"/>');
+                $regionDiv.attr("id", "region" + data.regions.length);
+                $elem.append($regionDiv);
+                rpos.adjustDiv($regionDiv);
+                }
+            }
+    };
+
+    var serializeRegions = function (data) {
+        if (data.regions) {
+            settings.rg = '';
+            for (var i = 0; i < data.regions.length; i++) {
+                if (i) {
+                    settings.rg += ',';
+                    }
+                settings.rg +=
+                    cropFloat(data.regions[i].x).toString() + '/' + 
+                    cropFloat(data.regions[i].y).toString() + '/' +
+                    cropFloat(data.regions[i].width).toString() + '/' +
+                    cropFloat(data.regions[i].height).toString();
+                }
+            }
+    };
+
+    var handleSetup = function (evt) {
+        console.debug("regions: handleSetup");
+        data = this;
+//        if (data.settings.isBirdDivVisible) {
+//            setupBirdDiv(data);
+//            data.$birdDiv.show();
+//        }
+    };
+
+    var handleUpdate = function (evt) {
+        console.debug("regions: handleUpdate");
+        data = this;
+//        if (data.settings.isBirdDivVisible) {
+//            renderBirdArea(data);
+//            setupBirdDrag(data);
+//        }
+    };
+
+    var handleRedisplay = function (evt) {
+        console.debug("regions: handleRedisplay");
+        data = this;
+//        if (data.settings.isBirdDivVisible) {
+//            updateBirdDiv(data);
+//        }
+    };
+
+    var handleDragZoom = function (evt, zoomArea) {
+        console.debug("regions: handleDragZoom, zoomArea:", zoomArea);
+        data = this;
+//        if (data.settings.isBirdDivVisible) {
+//            setBirdZoom(data, zoomArea);
+//        }
+    };
+
+    // plugin installation called by digilib on plugin object.
+    var install = function(digilib) {
+        // import geometry classes
+        geom = digilib.fn.geometry;
+        FULL_AREA = geom.rectangle(0,0,1,1);
+        // add defaults
+        $.extend(digilib.defaults, defaults);
+        // add actions
+        $.extend(digilib.actions, actions);
+        // add buttons
+        $.extend(digilib.buttons, buttons);
+    };
+
+    // plugin initialization
+    var init = function (data) {
+        console.debug('initialising regions plugin. data:', data);
+        var $data = $(data);
+        var buttonSettings = data.settings.buttonSettings.fullscreen;
+        // configure buttons through digilib "regionSet" option
+        var buttonSet = data.settings.regionSet || regionSet; 
+        // set regionSet to [] or '' for no buttons (when showing regions only)
+        if (buttonSet.length && buttonSet.length > 0) {
+            buttonSettings['regionSet'] = buttonSet;
+            buttonSettings.buttonSets.push('regionSet');
+            }
+        // install event handler
+        $data.bind('setup', handleSetup);
+        $data.bind('update', handleUpdate);
+        $data.bind('redisplay', handleRedisplay);
+        $data.bind('dragZoom', handleDragZoom);
+    };
+
+    // plugin object with name and init
+    // shared objects filled by digilib on registration
+    var pluginProperties = {
+            name : 'region',
+            install : install,
+            init : init,
+            buttons : {},
+            actions : {},
+            fn : {},
+            plugins : {}
+    };
+
+    if ($.fn.digilib == null) {
+        $.error("jquery.digilib.birdview must be loaded after jquery.digilib!");
+    } else {
+        $.fn.digilib('plugin', pluginProperties);
+    }
+})(jQuery);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/digitallibrary/jquery/svg/archimedes.json	Sat Feb 19 09:56:18 2011 +0100
@@ -0,0 +1,1050 @@
+{
+"comment" : [
+  "Angaben nach:",
+  "Klimpert, Richard: Lexikon der Münzen, Maße, Gewichte, Zählarten und Zeitgrößen aller Länder der Erde 2) Berlin 1896 (Reprint Graz 1972)",
+  "Doursther, Horace: Dictionnaire universel des poids et mesures anciens et modernes. Paris 1840 (Reprint Amsterdam 1965)"
+  ],
+"section" : [
+  {
+    "name"  : "Längenmaße: metrisch",
+    "group" : "1",
+    "unit" : [
+      {
+      "name" : "m",
+      "factor" : "1"
+      },
+      {
+      "name" : "mm",
+      "factor" : "0.001"
+      },
+      {
+      "name" : "cm",
+      "factor" : "0.01"
+      },
+      {
+      "name" : "dm",
+      "factor" : "0.1"
+      },
+      {
+      "name" : "km",
+      "factor" : "1000"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Längenmaße: nautisch",
+    "group" : "1",
+    "unit" : [
+      {
+      "name" : "geographische Meile",
+      "factor" : "7420"
+      },
+      {
+      "name" : "Seemeile",
+      "factor" : "1854.965"
+      },
+      {
+      "name" : "fathom",
+      "factor" : "1.828782"
+      },
+      {
+      "name" : "cable",
+      "factor" : "182.8782"
+      },
+      {
+      "name" : "league",
+      "factor" : "5564.895"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Längenmaße: England",
+    "group" : "1",
+    "unit" : [
+      {
+      "name" : "foot",
+      "factor" : "0.304797",
+      "subunits" : "12"
+      },
+      {
+      "name" : "inch",
+      "factor" : "0.02539975"
+      },
+      {
+      "name" : "yard",
+      "factor" : "0.914391",
+      "subunits" : "3"
+      },
+      {
+      "name" : "pole",
+      "factor" : "5.0291505",
+      "subunits" : "11"
+      },
+      {
+      "name" : "chain",
+      "factor" : "20.116602",
+      "subunits" : "4"
+      },
+      {
+      "name" : "furlong",
+      "factor" : "201.16602"
+      },
+      {
+      "name" : "mile",
+      "factor" : "1609.32816",
+      "subunits" : "8"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Längenmaße: Italien",
+    "group" : "1",
+    "unit" : [
+      {
+      "name" : "palmo d'architetto (Rom)",
+      "factor" : "0.223425",
+      "subunits" : "12"
+      },
+      {
+      "name" : "braccio (Florenz)",
+      "factor" : "0.5836"
+      },
+      {
+      "name" : "braccio (Mailand)",
+      "factor" : "0.5949"
+      },
+      {
+      "name" : "canna d'architetto (Rom)",
+      "factor" : "2.23425"
+      },
+      {
+      "name" : "canna di commercio (Rom)",
+      "factor" : "1.9920"
+      },
+      {
+      "name" : "canna d'architetto (Florenz)",
+      "factor" : "2.9180"
+      },
+      {
+      "name" : "canna di commercio (Florenz)",
+      "factor" : "2.3344"
+      },
+      {
+      "name" : "canna (Neapel)",
+      "factor" : "2.0961"
+      },
+      {
+      "name" : "miglio (Lombardei)",
+      "factor" : "1784.808"
+      },
+      {
+      "name" : "miglio (Neapel)",
+      "factor" : "1855.110"
+      },
+      {
+      "name" : "miglio (Rom)",
+      "factor" : "1489.50"
+      },
+      {
+      "name" : "minuta (Rom)",
+      "factor" : "0.00372375"
+      },
+      {
+      "name" : "oncia (Rom)",
+      "factor" : "0.01861875"
+      },
+      {
+      "name" : "oncia (Mailand)",
+      "factor" : "0.49575"
+      },
+      {
+      "name" : "palmo di commercio (Rom)",
+      "factor" : "0.249"
+      },
+      {
+      "name" : "palmo (Florenz)",
+      "factor" : "0.2918"
+      },
+      {
+      "name" : "piede (Brescia)",
+      "factor" : "0.471"
+      },
+      {
+      "name" : "piede (Carrara)",
+      "factor" : "0.2933"
+      },
+      {
+      "name" : "piede (Como)",
+      "factor" : "0.4512"
+      },
+      {
+      "name" : "piede (Modena)",
+      "factor" : "0.523048"
+      },
+      {
+      "name" : "piede (Reggio Em.)",
+      "factor" : "0.530898"
+      },
+      {
+      "name" : "piede (Venedig)",
+      "factor" : "0.347735"
+      },
+      {
+      "name" : "piede (Vicenza)",
+      "factor" : "0.3574"
+      },
+      {
+      "name" : "piede (Verona)",
+      "factor" : "0.3429"
+      },
+      {
+      "name" : "piede (Rom)",
+      "factor" : "0.297587"
+      },
+      {
+      "name" : "piede Lombardo",
+      "factor" : "0.435185"
+      },
+      {
+      "name" : "piede liprando (Turin)",
+      "factor" : "0.51377"
+      },
+      {
+      "name" : "piede manuale (Turin)",
+      "factor" : "0.342511"
+      },
+      {
+      "name" : "piede (Neapel, 'palmo')",
+      "factor" : "0.26455"
+      },
+      {
+      "name" : "soldo (Florenz)",
+      "factor" : "0.2918"
+      },
+      {
+      "name" : "trabucco piemontese (Turin)",
+      "factor" : "3.08259"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Längenmaße: Niederlande",
+    "group" : "1",
+    "unit" : [
+      {
+      "name" : "voet (Amsterdam)",
+      "factor" : "0.283113"
+      },
+      {
+      "name" : "voet (Antwerpen)",
+      "factor" : "0.2868"
+      },
+      {
+      "name" : "voet (Aelst)",
+      "factor" : "0.2772"
+      },
+      {
+      "name" : "voet (Breda)",
+      "factor" : "0.28413"
+      },
+      {
+      "name" : "voet (Brügge)",
+      "factor" : "0.27439"
+      },
+      {
+      "name" : "voet (Brüssel)",
+      "factor" : "0.2757503"
+      },
+      {
+      "name" : "voet (Groningen)",
+      "factor" : "0.2922"
+      },
+      {
+      "name" : "voet (Haarlem)",
+      "factor" : "0.2858"
+      },
+      {
+      "name" : "voet (Kortrijk)",
+      "factor" : "0.2977"
+      },
+      {
+      "name" : "voet (Tournai)",
+      "factor" : "0.2977"
+      },
+      {
+      "name" : "voet (Utrecht)",
+      "factor" : "0.2683"
+      },
+      {
+      "name" : "voet (Ypern)",
+      "factor" : "0.2739"
+      },
+      {
+      "name" : "pied (Hainaut)",
+      "factor" : "0.2934"
+      },
+      {
+      "name" : "pied St. Hubert (Lüttich)",
+      "factor" : "0.294698"
+      },
+      {
+      "name" : "pied St. Lambert (Lüttich)",
+      "factor" : "0.291796"
+      },
+      {
+      "name" : "pied Ste. Gertrude (Nivelles)",
+      "factor" : "0.27709"
+      },
+      {
+      "name" : "steenvoet (Oudenaerde)",
+      "factor" : "0.2977"
+      },
+      {
+      "name" : "houtvoet (Oudenaerde)",
+      "factor" : "0.292"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Längenmaße: Frankreich",
+    "group" : "1",
+    "unit" : [
+      {
+      "name" : "pied du Roi (Paris)",
+      "factor" : "0.32483938497"
+      },
+      {
+      "name" : "pied (Arras)",
+      "factor" : "0.29777"
+      },
+      {
+      "name" : "pied (Cambrai)",
+      "factor" : "0.29777"
+      },
+      {
+      "name" : "Burgundischer Fuß",
+      "factor" : "0.33212"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Längenmaße: Südeuropa",
+    "group" : "1",
+    "unit" : [
+      {
+      "name" : "pié de Burgos (Spanien)",
+      "factor" : "0.278635"
+      },
+      {
+      "name" : "pé (Portugal)",
+      "factor" : "0.33"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Längenmaße: deutschspr. Länder",
+    "group" : "1",
+    "unit" : [
+      {
+      "name" : "Fuß (Basel)",
+      "factor" : "0.29820"
+      },
+      {
+      "name" : "Fuß (Bayern)",
+      "factor" : "0.2918592"
+      },
+      {
+      "name" : "Fuß (Braunschweig)",
+      "factor" : "0.2853624"
+      },
+      {
+      "name" : "Fuß (Gotha)",
+      "factor" : "0.287622"
+      },
+      {
+      "name" : "Fuß (Hamburg)",
+      "factor" : "0.286575"
+      },
+      {
+      "name" : "Fuß (Hessen)",
+      "factor" : "0.287669"
+      },
+      {
+      "name" : "Fuß (Köln)",
+      "factor" : "0.2876"
+      },
+      {
+      "name" : "Fuß (Mecklenburg)",
+      "factor" : "0.291006"
+      },
+      {
+      "name" : "Fuß (Münster)",
+      "factor" : "0.2908"
+      },
+      {
+      "name" : "Fuß (Pommern)",
+      "factor" : "0.2921"
+      },
+      {
+      "name" : "Fuß (rheinisch)",
+      "factor" : "0.3138535"
+      },
+      {
+      "name" : "Fuß (Sachsen)",
+      "factor" : "0.2831901"
+      },
+      {
+      "name" : "Fuß (Preußen)",
+      "factor" : "0.3138535"
+      },
+      {
+      "name" : "Fuß (Wien)",
+      "factor" : "0.3180807"
+      },
+      {
+      "name" : "Fuß (Württemberg)",
+      "factor" : "0.2864903"
+      },
+      {
+      "name" : "Werkschuh (Frankfurt)",
+      "factor" : "0.2846143"
+      },
+      {
+      "name" : "Meile (Preußen)",
+      "factor" : "7532.485"
+      },
+      {
+      "name" : "Postmeile (Österreich)",
+      "factor" : "7585.937"
+      },
+      {
+      "name" : "Dezimalfuß (Preußen)",
+      "factor" : "0.3766242"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Längenmaße: Osteuropa",
+    "group" : "1",
+    "unit" : [
+      {
+      "name" : "Fuß (Böhmen)",
+      "factor" : "0.2964"
+      },
+      {
+      "name" : "Fuß (Mähren)",
+      "factor" : "0.29596"
+      },
+      {
+      "name" : "stopa (Krakauer Fuß)",
+      "factor" : "0.3564"
+      },
+      {
+      "name" : "stopa (Warschauer Fuß)",
+      "factor" : "0.288"
+      },
+      {
+      "name" : "Fuß (Rußland)",
+      "factor" : "0.3556"
+      },
+      {
+      "name" : "arschin",
+      "factor" : "0.7112"
+      },
+      {
+      "name" : "saschen (Faden)",
+      "factor" : "2.133"
+      },
+      {
+      "name" : "werst",
+      "factor" : "1066.8"
+      },
+      {
+      "name" : "milja",
+      "factor" : "7468"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Längenmaße: Antike",
+    "group" : "1",
+    "unit" : [
+      {
+      "name" : "pes romanus",
+      "factor" : "0.2945"
+      },
+      {
+      "name" : "pollex (Zoll)",
+      "factor" : "0.0245416667"
+      },
+      {
+      "name" : "digitus (Fingerbreite)",
+      "factor" : "0.01840625"
+      },
+      {
+      "name" : "palmus (Handbreite)",
+      "factor" : "0.073625"
+      },
+      {
+      "name" : "cubitus (Elle)",
+      "factor" : "0.44175"
+      },
+      {
+      "name" : "passus (Doppelschritt)",
+      "factor" : "1.4725"
+      },
+      {
+      "name" : "pertica",
+      "factor" : "2.945"
+      },
+      {
+      "name" : "actus",
+      "factor" : "35.34"
+      },
+      {
+      "name" : "mille passus (Meile)",
+      "factor" : "1472.5"
+      },
+      {
+      "name" : "stadium (600 Fuß)",
+      "factor" : "176.7"
+      },
+      {
+      "name" : "stadium (1/8 Meile)",
+      "factor" : "184.0625"
+      },
+      {
+      "name" : "stadion (Olympia)",
+      "factor" : "192.25"
+      },
+      {
+      "name" : "Fuß (attisch)",
+      "factor" : "0.308"
+      },
+      {
+      "name" : "Fuß (Babylon)",
+      "factor" : "0.35"
+      },
+      {
+      "name" : "Fuß (Delphi)",
+      "factor" : "0.1848"
+      },
+      {
+      "name" : "Fuß (Olympia)",
+      "factor" : "0.32041667"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Zeitrechnung",
+    "group" : "3",
+    "unit" : [
+      {
+      "name" : "Anno Domini",
+      "factor" : "1",
+      "add" : "-1",
+      "subunits" : "x"
+      },
+      {
+      "name" : "ab urbe condita",
+      "factor" : "1",
+      "add" : "-753"
+      },
+      {
+      "name" : "jüdische Ära",
+      "factor" : "1",
+      "add" : "-3761"
+      },
+      {
+      "name" : "Olympiade",
+      "factor" : "4",
+      "add" : "-776"
+      },
+      {
+      "name" : "Indiktion",
+      "factor" : "15",
+      "add" : "3"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Münzen: Kirchenstaat",
+    "group" : "3",
+    "unit" : [
+      {
+      "name" : "Scudo",
+      "factor" : "1"
+      },
+      {
+      "name" : "Baiocco",
+      "factor" : "0.01"
+      },
+      {
+      "name" : "Paolo",
+      "factor" : "0.1"
+      },
+      {
+      "name" : "Quattrino",
+      "factor" : "0.2"
+      },
+      {
+      "name" : "Giulio",
+      "factor" : "0.1"
+      },
+      {
+      "name" : "Luitpold d'Or",
+      "factor" : "1.234"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Temperatur",
+    "group" : "5",
+    "unit" : [
+      {
+      "name" : "° Celsius",
+      "factor" : "1"
+      },
+      {
+      "name" : "° Fahrenheit",
+      "factor" : "0.5555555555556",
+      "add" : "-17.77777777778"
+      },
+      {
+      "name" : "° Reaumur",
+      "factor" : "1.25"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Gewichte: dezimal",
+    "group" : "6",
+    "unit" : [
+      {
+      "name" : "Gramm",
+      "factor" : "1"
+      },
+      {
+      "name" : "Milligramm",
+      "factor" : "0.001"
+      },
+      {
+      "name" : "Centigramm",
+      "factor" : "0.01"
+      },
+      {
+      "name" : "Pfund",
+      "factor" : "500"
+      },
+      {
+      "name" : "Kilogramm",
+      "factor" : "1000"
+      },
+      {
+      "name" : "Zentner",
+      "factor" : "50000"
+      },
+      {
+      "name" : "Doppelzentner",
+      "factor" : "100000"
+      },
+      {
+      "name" : "Tonne",
+      "factor" : "1000000"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Gewichte: England",
+    "group" : "6",
+    "unit" : [
+      {
+      "name" : "ounce",
+      "factor" : "31.104",
+      "subunits" : "8"
+      },
+      {
+      "name" : "grain",
+      "factor" : "0.065"
+      },
+      {
+      "name" : "scruple",
+      "factor" : "1.296",
+      "subunits" : "20"
+      },
+      {
+      "name" : "pennyweight",
+      "factor" : "1.555",
+      "subunits" : "24"
+      },
+      {
+      "name" : "dram",
+      "factor" : "3.888",
+      "subunits" : "3"
+      },
+      {
+      "name" : "pound",
+      "factor" : "373.24",
+      "subunits" : "12"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Gewichte: engl. Handel",
+    "group" : "6",
+    "unit" : [
+      {
+      "name" : "ounce",
+      "factor" : "28.349609375",
+      "subunits" : "16"
+      },
+      {
+      "name" : "dram",
+      "factor" : "1.771850585938"
+      },
+      {
+      "name" : "pound",
+      "factor" : "453.59375",
+      "subunits" : "16"
+      },
+      {
+      "name" : "stone",
+      "factor" : "6350.3125",
+      "subunits" : "14"
+      },
+      {
+      "name" : "quarter",
+      "factor" : "12700.625",
+      "subunits" : "28"
+      },
+      {
+      "name" : "hundredweight",
+      "factor" : "50802.5",
+      "subunits" : "4"
+      },
+      {
+      "name" : "long ton",
+      "factor" : "101604.16",
+      "subunits" : "20"
+      },
+      {
+      "name" : "cental",
+      "factor" : "45359.375"
+      },
+      {
+      "name" : "short ton",
+      "factor" : "907187.5",
+      "subunits" : "20"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Fläche",
+    "group" : "4",
+    "unit" : [
+      {
+      "name" : "qm",
+      "factor" : "1"
+      },
+      {
+      "name" : "qmm",
+      "factor" : "0.000001"
+      },
+      {
+      "name" : "qcm",
+      "factor" : "0.0001"
+      },
+      {
+      "name" : "qdm",
+      "factor" : "0.01"
+      },
+      {
+      "name" : "Ar",
+      "factor" : "100"
+      },
+      {
+      "name" : "Morgen",
+      "factor" : "2500"
+      },
+      {
+      "name" : "Hektar",
+      "factor" : "10000"
+      },
+      {
+      "name" : "qkm",
+      "factor" : "1000000"
+      },
+      {
+      "name" : "square inch",
+      "factor" : "0.0006452"
+      },
+      {
+      "name" : "square foot",
+      "factor" : "0.09288"
+      },
+      {
+      "name" : "square yard",
+      "factor" : "0.836",
+      "subunits" : "9"
+      },
+      {
+      "name" : "pole (rod, perch)",
+      "factor" : "25.289"
+      },
+      {
+      "name" : "rood",
+      "factor" : "1012",
+      "subunits" : "40"
+      },
+      {
+      "name" : "acre",
+      "factor" : "4048",
+      "subunits" : "4"
+      },
+      {
+      "name" : "square mile",
+      "factor" : "2590000"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Hohlmaße",
+    "group" : "2",
+    "unit" : [
+      {
+      "name" : "cbm",
+      "factor" : "1"
+      },
+      {
+      "name" : "cmm",
+      "factor" : "0.000000001"
+      },
+      {
+      "name" : "ccm",
+      "factor" : "0.000001"
+      },
+      {
+      "name" : "cdm (Liter)",
+      "factor" : "0.001"
+      },
+      {
+      "name" : "Hektoliter",
+      "factor" : "0.1"
+      },
+      {
+      "name" : "cubic inch",
+      "factor" : "0.000016387"
+      },
+      {
+      "name" : "cubic foot",
+      "factor" : "0.028317"
+      },
+      {
+      "name" : "cubic yard",
+      "factor" : "0.7646"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Flüssigkeit: England",
+    "group" : "2",
+    "unit" : [
+      {
+      "name" : "Liter",
+      "factor" : "1"
+      },
+      {
+      "name" : "gill",
+      "factor" : "0.142"
+      },
+      {
+      "name" : "pint",
+      "factor" : "0.568",
+      "subunits" : "4"
+      },
+      {
+      "name" : "quart",
+      "factor" : "1.136",
+      "subunits" : "8"
+      },
+      {
+      "name" : "gallon",
+      "factor" : "4.546",
+      "subunits" : "8"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Flüssigkeit: USA",
+    "group" : "2",
+    "unit" : [
+      {
+      "name" : "gill",
+      "factor" : "0.118"
+      },
+      {
+      "name" : "pint",
+      "factor" : "0.473",
+      "subunits" : "4"
+      },
+      {
+      "name" : "quart",
+      "factor" : "0.946",
+      "subunits" : "8"
+      },
+      {
+      "name" : "gallon",
+      "factor" : "3.787",
+      "subunits" : "8"
+      },
+      {
+      "name" : "barrel",
+      "factor" : "119.2"
+      },
+      {
+      "name" : "barrel of oil",
+      "factor" : "158.8",
+      "subunits" : "42"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Leistung",
+    "group" : "3",
+    "unit" : [
+      {
+      "name" : "Watt",
+      "factor" : "1"
+      },
+      {
+      "name" : "Milliwatt",
+      "factor" : "0.001"
+      },
+      {
+      "name" : "Kilowatt",
+      "factor" : "1000"
+      },
+      {
+      "name" : "Megawatt",
+      "factor" : "1000000"
+      },
+      {
+      "name" : "PS",
+      "factor" : "735.5"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Speicherplatz",
+    "group" : "3",
+    "unit" : [
+      {
+      "name" : "byte",
+      "factor" : "1"
+      },
+      {
+      "name" : "bit",
+      "factor" : "0.125"
+      },
+      {
+      "name" : "$100 byte",
+      "factor" : "256"
+      },
+      {
+      "name" : "kilobyte",
+      "factor" : "1000"
+      },
+      {
+      "name" : "KiloByte (Kb)",
+      "factor" : "1024"
+      },
+      {
+      "name" : "megabyte",
+      "factor" : "1000000"
+      },
+      {
+      "name" : "Megabyte (Mb)",
+      "factor" : "1048576"
+      },
+      {
+      "name" : "gigabyte",
+      "factor" : "1000000000"
+      },
+      {
+      "name" : "Gigabyte (Gb)",
+      "factor" : "1073741824"
+      }
+    ]
+  },
+
+  {
+    "name"  : "Sonstige",
+    "group" : "0",
+    "unit" : [
+      {
+      "name" : "Maßstab 1:200",
+      "factor" : "200"
+      },
+      {
+      "name" : "Maßstab",
+      "factor" : "1:100",
+      "add" : "100"
+      },
+      {
+      "name" : "Maßstab 1:75",
+      "factor" : "75"
+      },
+      {
+      "name" : "Maßstab 1:60",
+      "factor" : "60"
+      },
+      {
+      "name" : "Maßstab",
+      "factor" : "1:50",
+      "add" : "50"
+      },
+      {
+      "name" : "Maßstab 1:25",
+      "factor" : "25"
+      },
+      {
+      "name" : "Maßstab 1:20",
+      "factor" : "20"
+      },
+      {
+      "name" : "Maßstab 1:10",
+      "factor" : "10"
+      },
+      {
+      "name" : "Maßstab 1:5",
+      "factor" : "5"
+      },
+      {
+      "name" : "Maßstab 1:3",
+      "factor" : "3"
+      }
+    ]
+  }
+  ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/digitallibrary/jquery/svg/archimedes.xml	Sat Feb 19 09:56:18 2011 +0100
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<units>
+<comment>
+  <p>Data for measurement units taken from:</p>
+  <p>Klimpert, Richard: Lexikon der Münzen, Maße, Gewichte, Zählarten und Zeitgrößen aller Länder der Erde 2) Berlin 1896 (Reprint Graz 1972)</p>
+  <p>Doursther, Horace: Dictionnaire universel des poids et mesures anciens et modernes. Paris 1840 (Reprint Amsterdam 1965)</p>
+</comment>
+<section icon="ruler" name="length: metrical">
+  <unit name="m" factor="1"/>
+  <unit name="mm" factor="0.001"/>
+  <unit name="cm" factor="0.01"/>
+  <unit name="dm" factor="0.1"/>
+  <unit name="km" factor="1000"/>
+</section>
+<section icon="ruler" name="length: nautical">
+  <unit name="geographic mile" factor="7420"/>
+  <unit name="nautical mile" factor="1854.965"/>
+  <unit name="fathom" factor="1.828782"/>
+  <unit name="cable" factor="182.8782"/>
+  <unit name="league" factor="5564.895"/>
+</section>
+<section icon="ruler" name="length: England">
+  <unit name="foot" factor="0.304797" subunits="12"/>
+  <unit name="inch" factor="0.02539975"/>
+  <unit name="yard" factor="0.914391" subunits="3"/>
+  <unit name="pole" factor="5.0291505" subunits="11"/>
+  <unit name="chain" factor="20.116602" subunits="4"/>
+  <unit name="furlong" factor="201.16602"/>
+  <unit name="mile" factor="1609.32816" subunits="8"/>
+</section>
+<section icon="ruler" name="length: Italy">
+  <unit name="palmo d'architetto (Roma)" factor="0.223425" subunits="12"/>
+  <unit name="braccio (Firenze)" factor="0.5836"/>
+  <unit name="braccio (Milano)" factor="0.5949"/>
+  <unit name="canna d'architetto (Roma)" factor="2.23425"/>
+  <unit name="canna di commercio (Roma)" factor="1.9920"/>
+  <unit name="canna d'architetto (Firenze)" factor="2.9180"/>
+  <unit name="canna di commercio (Firenze)" factor="2.3344"/>
+  <unit name="canna (Napoli)" factor="2.0961"/>
+  <unit name="miglio (Lombardia)" factor="1784.808"/>
+  <unit name="miglio (Napoli)" factor="1855.110"/>
+  <unit name="miglio (Roma)" factor="1489.50"/>
+  <unit name="minuta (Roma)" factor="0.00372375"/>
+  <unit name="oncia (Roma)" factor="0.01861875"/>
+  <unit name="oncia (Mailand)" factor="0.49575"/>
+  <unit name="palmo di commercio (Roma)" factor="0.249"/>
+  <unit name="palmo (Firenze)" factor="0.2918"/>
+  <unit name="piede (Brescia)" factor="0.471"/>
+  <unit name="piede (Carrara)" factor="0.2933"/>
+  <unit name="piede (Como)" factor="0.4512"/>
+  <unit name="piede (Modena)" factor="0.523048"/>
+  <unit name="piede (Reggio Em.)" factor="0.530898"/>
+  <unit name="piede (Venezia)" factor="0.347735"/>
+  <unit name="piede (Vicenza)" factor="0.3574"/>
+  <unit name="piede (Verona)" factor="0.3429"/>
+  <unit name="piede (Roma)" factor="0.297587"/>
+  <unit name="piede Lombardo" factor="0.435185"/>
+  <unit name="piede liprando (Torino)" factor="0.51377"/>
+  <unit name="piede manuale (Torino)" factor="0.342511"/>
+  <unit name="piede (Napoli, 'palmo')" factor="0.26455"/>
+  <unit name="soldo (Firenze)" factor="0.2918"/>
+  <unit name="trabucco piemontese (Torino)" factor="3.08259"/>
+</section>
+<section icon="ruler" name="length: Netherlands">
+  <unit name="voet (Amsterdam)" factor="0.283113"/>
+  <unit name="voet (Antwerpen)" factor="0.2868"/>
+  <unit name="voet (Aelst)" factor="0.2772"/>
+  <unit name="voet (Breda)" factor="0.28413"/>
+  <unit name="voet (Brügge)" factor="0.27439"/>
+  <unit name="voet (Brüssel)" factor="0.2757503"/>
+  <unit name="voet (Groningen)" factor="0.2922"/>
+  <unit name="voet (Haarlem)" factor="0.2858"/>
+  <unit name="voet (Kortrijk)" factor="0.2977"/>
+  <unit name="voet (Tournai)" factor="0.2977"/>
+  <unit name="voet (Utrecht)" factor="0.2683"/>
+  <unit name="voet (Ypern)" factor="0.2739"/>
+  <unit name="pied (Hainaut)" factor="0.2934"/>
+  <unit name="pied St. Hubert (Lüttich)" factor="0.294698"/>
+  <unit name="pied St. Lambert (Lüttich)" factor="0.291796"/>
+  <unit name="pied Ste. Gertrude (Nivelles)" factor="0.27709"/>
+  <unit name="steenvoet (Oudenaerde)" factor="0.2977"/>
+  <unit name="houtvoet (Oudenaerde)" factor="0.292"/>
+</section>
+<section icon="ruler" name="length: France">
+  <unit name="pied du Roi (Paris)" factor="0.32483938497"/>
+  <unit name="pied (Arras)" factor="0.29777"/>
+  <unit name="pied (Cambrai)" factor="0.29777"/>
+  <unit name="pied de Bourgogne" factor="0.33212"/>
+</section>
+<section icon="ruler" name="length: Southern Europe">
+  <unit name="pié de Burgos (Spain)" factor="0.278635"/>
+  <unit name="pé (Portugal)" factor="0.33"/>
+</section>
+<section icon="ruler" name="length: German speaking countries">
+  <unit name="Fuß (Basel)" factor="0.29820"/>
+  <unit name="Fuß (Bayern)" factor="0.2918592"/>
+  <unit name="Fuß (Braunschweig)" factor="0.2853624"/>
+  <unit name="Fuß (Gotha)" factor="0.287622"/>
+  <unit name="Fuß (Hamburg)" factor="0.286575"/>
+  <unit name="Fuß (Hessen)" factor="0.287669"/>
+  <unit name="Fuß (Köln)" factor="0.2876"/>
+  <unit name="Fuß (Mecklenburg)" factor="0.291006"/>
+  <unit name="Fuß (Münster)" factor="0.2908"/>
+  <unit name="Fuß (Pommern)" factor="0.2921"/>
+  <unit name="Fuß (rheinisch)" factor="0.3138535"/>
+  <unit name="Fuß (Sachsen)" factor="0.2831901"/>
+  <unit name="Fuß (Preußen)" factor="0.3138535"/>
+  <unit name="Fuß (Wien)" factor="0.3180807"/>
+  <unit name="Fuß (Württemberg)" factor="0.2864903"/>
+  <unit name="Werkschuh (Frankfurt)" factor="0.2846143"/>
+  <unit name="Meile (Preußen)" factor="7532.485"/>
+  <unit name="Postmeile (Österreich)" factor="7585.937"/>
+  <unit name="Dezimalfuß (Preußen)" factor="0.3766242"/>
+</section>
+<section icon="ruler" name="length: Eastern Europe">
+  <unit name="foot (Bohemia)" factor="0.2964"/>
+  <unit name="foot (Moravia)" factor="0.29596"/>
+  <unit name="stopa (foot of Krakow)" factor="0.3564"/>
+  <unit name="stopa (foot of Warsaw)" factor="0.288"/>
+  <unit name="foot (Russia)" factor="0.3556"/>
+  <unit name="arshin" factor="0.7112"/>
+  <unit name="sashen (fathom)" factor="2.133"/>
+  <unit name="werst" factor="1066.8"/>
+  <unit name="milja" factor="7468"/>
+</section>
+<section icon="ruler" name="length: Antiquity">
+  <unit name="pes romanus (foot)" factor="0.2945"/>
+  <unit name="pollex (inch)" factor="0.0245416667"/>
+  <unit name="digitus (width of a finger)" factor="0.01840625"/>
+  <unit name="palmus (palm)" factor="0.073625"/>
+  <unit name="cubitus (cubit)" factor="0.44175"/>
+  <unit name="passus (double step)" factor="1.4725"/>
+  <unit name="pertica" factor="2.945"/>
+  <unit name="actus" factor="35.34"/>
+  <unit name="mille passus (mile)" factor="1472.5"/>
+  <unit name="stadium (600 feet)" factor="176.7"/>
+  <unit name="stadium (1/8 mile)" factor="184.0625"/>
+  <unit name="stadion (Olympia)" factor="192.25"/>
+  <unit name="foot (Attic)" factor="0.308"/>
+  <unit name="foot (Babylon)" factor="0.35"/>
+  <unit name="foot (Delphi)" factor="0.1848"/>
+  <unit name="foot (Olympia)" factor="0.32041667"/>
+</section>
+<section icon="other" name="other">
+  <unit name="scale 1:1000" factor="1000"/>
+  <unit name="scale 1:200" factor="200"/>
+  <unit name="scale 1:100" factor="100"/>
+  <unit name="scale 1:75" factor="75"/>
+  <unit name="scale 1:60" factor="60"/>
+  <unit name="scale 1:50" factor="50"/>
+  <unit name="scale 1:25" factor="25"/>
+  <unit name="scale 1:20" factor="20"/>
+  <unit name="scale 1:10" factor="10"/>
+  <unit name="scale 1:5" factor="5"/>
+  <unit name="scale 1:3" factor="3"/>
+</section>
+</units>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/digitallibrary/jquery/svg/jquery.digilibSVG.css	Sat Feb 19 09:56:18 2011 +0100
@@ -0,0 +1,112 @@
+/* 
+ * CSS style file for digilib SVG measuring tool
+ *
+ * Martin Raspe, Robert Casties, 11.1.2011
+ */
+body {
+    background: silver;
+    font-family: Tahoma, Helvetica, Arial;
+    }
+ 
+div#svg-toolbar {
+    color: grey;
+    background-color: grey;
+    position: fixed;
+    left: 8px;
+    bottom: 8px;
+    border: 1px solid black;
+    z-index: 1000;
+    }
+
+span.svg-label {
+    font-size: 12px;
+    padding: 0px 3px;
+    color: silver;
+    background-color: grey;
+    }
+
+span.svg-number {
+    font-size: 12px;
+    padding: 0px 3px;
+    display: inline-block;
+    width: 50px;
+    background-color: grey;
+    color: white;
+    text-align: right;
+    }
+
+select {
+    font-size: 12px;
+    padding: 0px 3px;
+    margin: 0px;
+    display: inline-block;
+    background-color: grey;
+    color: lightgreen;
+    border: none;
+    }
+
+option.section {
+    border-bottom: 1px dotted grey;
+    padding-left: 10px;
+}
+
+input.svg-input {
+    width: 50px;
+    font-size: 12px;
+    padding: 0px 3px;
+    margin: 0px;
+    display: inline-block;
+    background-color: grey;
+    color: lightgreen;
+    border: none;
+    text-align: right;
+    }
+
+select:hover, select:active, select:focus,
+input.svg-input:hover, input.svg-input:active, input.svg-input:focus {
+    background-color: white;
+    color: black;
+    }
+
+div.digilib {
+    margin: 0px;
+}
+
+div.svg {
+    position: absolute;
+    background: transparent;
+}
+
+td {
+    vertical-align: top;
+    }
+
+div#test:hover {
+    background-color: cornsilk;
+    }
+
+#debug {
+    background-color: beige;
+    position: absolute;
+    opacity: 50%;
+    top: 4px;
+    left: 300px;
+    width: 500px;
+    padding: 0px 5px;
+    font-family: Arial;
+    font-size: 9px;
+    display: none;
+    }
+
+div._log {
+    color: grey;
+    }
+
+div._debug {
+    color: darkgreen;
+    }
+
+div._error {
+    color: red;
+    }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/digitallibrary/jquery/svg/jquery.digilibSVG.js	Sat Feb 19 09:56:18 2011 +0100
@@ -0,0 +1,245 @@
+/* Copyright (c) 2011 Martin Raspe, Robert Casties
+ 
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ 
+Authors:
+  Martin Raspe, Robert Casties, 9.2.2011
+*/
+
+/**
+ * digilib SVG plugin (measuring tool for use within the digilib jQuery plugin)
+**/ 
+
+
+/* jslint browser: true, debug: true, forin: true
+*/
+
+// fallback for console.log calls
+if (typeof(console) === 'undefined') {
+    var console = {
+        log : function(){}, 
+        debug : function(){}, 
+        error : function(){}
+        };
+    var customConsole = true;
+}
+
+(function($) {
+    console.debug('installing jquery.digilibSVG');
+
+    var pluginName = 'digilibSVG';
+    var geom;
+
+    var defaults = {
+        // choice of colors offered by toolbar
+        lineColors : ['white', 'red', 'yellow', 'green', 'blue', 'black'],
+        // default color
+        lineColor : 'white',
+        // color while the line is drawn
+        drawColor : 'green',
+        // color of selected objects
+        selectColor : 'red',
+        // drawing shapes
+        shapes : ['line', 'polyline', 'rectangle', 'square', 'circle', 'arch',
+            'ratio', 'intercolumn', 'line grid'],
+        // default shape
+        shape : 'line',
+        // measuring unit (index into list)
+        unit : 1,
+        // converted unit (index into list)
+        converted : 2,
+        // last measured distance 
+        lastDistance : 0,
+        // last measured angle 
+        lastAngle : 0,
+        // maximal denominator for mixed fractions
+        maxDenominator : 20,
+        // number of decimal places for convert results
+        maxDecimals : 3,
+        // show convert result as mixed fraction?
+        showMixedFraction : false,
+        // show angle relative to last line?
+        showRelativeAngle : false,
+        // show distance numbers?
+        showDistanceNumbers : true,
+        // show ratio of rectangle sides?
+        showRectangleRatios : false,
+        // draw line ends as small crosses
+        drawEndPoints : true,
+        // draw mid points of lines
+        drawMidPoints : false,
+        // draw circle centers
+        drawCenters : false,
+        // draw rectangles from the diagonal and one point
+        drawFromDiagonal : false,
+        // draw circles from center
+        drawFromCenter : false,
+        // snap to endpoints
+        snapEndPoints : false,
+        // snap to mid points of lines
+        snapMidPoints : false,
+        // snap to circle centers
+        snapCenters : false,
+        // snap distance (in screen pixels)
+        snapDistance : 5,
+        // keep original object when moving/scaling/rotating
+        keepOriginal : false,
+        // number of copies when drawing grids
+        gridCopies : 10
+        };
+
+    // setup a div for accessing the main SVG functionality
+    var setupToolBar = function(settings) {
+        var $toolBar = $('<div id="svg-toolbar"/>');
+        // shapes select
+        var $shape = $('<select id="svg-shapes"/>');
+        for (var i = 0; i < settings.shapes.length; i++) {
+            var name = settings.shapes[i];
+            var $opt = $('<option value="' + i + '">' + name + '</option>');
+            $shape.append($opt);
+            }
+        // console.debug($xml);
+        var $xml = $(settings.xml);
+        // unit selects
+        var $unit1 = $('<select id="svg-convert1"/>');
+        var $unit2 = $('<select id="svg-convert2"/>');
+
+        $xml.find("section").each(function() {
+            var $section = $(this);
+            var name = $section.attr("name");
+            // append section name as option
+            var $opt = $('<option class="section" disabled="disabled">' + name + '</option>');
+            $unit1.append($opt);
+            $unit2.append($opt.clone());
+            $section.find("unit").each(function() {
+                var $unit = $(this);
+                var name = $unit.attr("name");
+                var factor = $unit.attr("factor"); 
+                var $opt = $('<option class="unit" value="' + factor + '">' + name + '</option>');
+                $opt.data(pluginName, {
+                    'name' : name,
+                    'factor' : factor, 
+                    'add' : $unit.attr("add"), 
+                    'subunits' : $unit.attr("subunits")
+                    });
+                $unit1.append($opt);
+                $unit2.append($opt.clone());
+                });
+            });
+        // settings.units = units;
+        // other elements
+        var $la1 = $('<span class="svg-label">pixel</span>');
+        var $la2 = $('<span class="svg-label">factor</span>');
+        var $la3 = $('<span class="svg-label">=</span>');
+        var $la4 = $('<span class="svg-label">=</span>');
+        var $px = $('<span id="svg-pixel" class="svg-number">0.0</span>');
+        var $factor = $('<span id="svg-factor" class="svg-number">0.0</span>');
+        var $result1 = $('<input id="svg-unit1" class="svg-input" value="0.0"/>');
+        var $result2 = $('<input id="svg-unit2" class="svg-input" value="0.0"/>');
+        var $angle = $('<span id="svg-angle" class="svg-number">0.0</span>');
+        $('body').append($toolBar);
+        $toolBar.append($shape);
+        $toolBar.append($la1);
+        $toolBar.append($px);
+        $toolBar.append($la2);
+        $toolBar.append($factor);
+        $toolBar.append($la3);
+        $toolBar.append($result1);
+        $toolBar.append($unit1);
+        $toolBar.append($la4);
+        $toolBar.append($result2);
+        $toolBar.append($unit2);
+        $toolBar.append($angle);
+        return $toolBar;
+        };
+
+    var drawInitial = function ($svg) {
+        console.debug('SVG is ready');
+        var $svgDiv = $(this);
+        var rect = geom.rectangle($svgDiv);
+        $svg.line(0, 0, rect.width, rect.height,
+            {stroke: 'white', strokeWidth: 2});
+        $svg.line(0, rect.height, rect.width, 0,
+            {stroke: 'red', strokeWidth: 2});
+        };
+
+    var actions = {
+        "init" : function(options) {
+            var $digilib = this;
+            var data = $digilib.data('digilib');
+            var plugins = data.plugins;
+            geom = plugins.geometry.init();
+            var settings = $.extend({}, defaults, options);
+            // prepare the AJAX callback
+            // TODO: return unless interactiveMode === 'fullscreen'?
+            var onLoadXML = function (xml) {
+                settings.xml = xml;
+                settings.$toolBar = setupToolBar(settings);
+                $digilib.each(function() {
+                    var $elem = $(this);
+                    $elem.data(pluginName, settings);
+                    });
+                };
+            var onLoadScalerImg = function () {
+                var $svgDiv = $('<div id="svg" />');
+                $('body').append($svgDiv);
+                // size SVG div like scaler img
+                var $scalerImg = $digilib.find('img.pic');
+                var scalerImgRect = geom.rectangle($scalerImg);
+                scalerImgRect.adjustDiv($svgDiv);
+                console.debug('$svgDiv', scalerImgRect);
+                var $svg = $svgDiv.svg({
+                        'onLoad' : drawInitial
+                    });
+                settings.$elem = $digilib;
+                settings.$svgDiv = $svgDiv;
+                settings.$svg = $svg;
+                // set SVG data 
+                $svg.data('digilib', data);
+                $svg.data(pluginName, settings);
+                };
+            // fetch the XML measuring unit list
+            $.ajax({
+                type : "GET",
+                url : "svg/archimedes.xml",
+                dataType : "xml",
+                success : onLoadXML
+                });
+            data.$img.load(onLoadScalerImg);
+            return this;
+            }
+        };
+
+ // hook plugin into jquery
+    $.fn[pluginName] = function(action) {
+        if (actions[action]) {
+            // call action on this with the remaining arguments (inserting data as first argument)
+            var $elem = $(this);
+            var data = $elem.data('digilib');
+            if (!data) {
+                return $.error(pluginName + ' action ' + action + ' needs a digilib element');
+                }
+            var args = Array.prototype.slice.call(arguments, 1);
+            args.unshift(data);
+            return actions[action].apply(this, args);
+        } else if (typeof(action) === 'object' || !action) {
+            // call init on this
+            return actions.init.apply(this, arguments);
+        } else {
+            $.error('action ' + action + ' does not exist on jQuery.' + pluginName);
+        }
+    };
+
+})(jQuery);
+
--- a/client/digitallibrary/jquery/svg/svgBasics.html	Fri Feb 11 10:12:41 2011 +0100
+++ b/client/digitallibrary/jquery/svg/svgBasics.html	Sat Feb 19 09:56:18 2011 +0100
@@ -5,17 +5,24 @@
     <head>
         <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
         <title>jQuery SVG Basics</title>
-        <style type="text/css">
+
+<style type="text/css">
+
 @import "jquery.svg.css";
 
 #svgbasics { position: absolute; border: 1px solid blue; }
+
 #img img {width: 400px; }
 
-        </style>
-        <script type="text/javascript" src="../dlGeometry.js"></script>
-        <script type="text/javascript" src="../jquery-1.4.4.js"></script>
-        <script type="text/javascript" src="jquery.svg.js"></script>
-        <script type="text/javascript">
+</style>
+
+<script type="text/javascript" src="../dlGeometry.js"></script>
+<script type="text/javascript" src="../jquery-1.4.4.js"></script>
+<script type="text/javascript" src="jquery.svg.js"></script>
+<script type="text/javascript">
+
+var $resDiv;
+
 $(function() {
     var geom = dlGeometry();
     var svg = $('#svgbasics');
@@ -24,18 +31,37 @@
     divrect.addPosition({x: -1, y: -1}).adjustDiv(svg);
     svg.svg({onLoad: drawInitial});
 	$('button').click(drawShape);
+	$resDiv = $('div#res');
+	console.log($resDiv);
 });
 
 function drawInitial(svg) {
+    // svg.configure({viewBox: '0 0 400 300' }, true);
+    svg.configure({transform: 'scale(0.5)' }, true);
 	svg.circle(75, 75, 50, {fill: 'none', stroke: 'red', 'stroke-width': 3});
 	var g = svg.group({stroke: 'black', 'stroke-width': 2});
 	svg.line(g, 15, 75, 135, 75);
 	svg.line(g, 75, 15, 75, 135);
-	svg.configure({viewPort: '0 0 100 100', overflow: 'visible' }, false);
+	var g = svg.group({stroke: 'yellow', 'stroke-width': 4});
+	svg.line(g, 0, 0, 400, 0);
+	svg.line(g, 0, 0, 0, 300);
+	svg.line(g, 400, 0, 400, 300);
+	svg.line(g, 400, 300, 0, 300);
+	// svg.configure({viewPort: '0 0 100 100', overflow: 'visible' }, false);
+
 }
 
 var colours = ['purple', 'red', 'orange', 'yellow', 'lime', 'green', 'blue', 'navy', 'black'];
 
+var x=0,y=0,w=400,h=300;
+
+function setViewBox(svg) {
+    var s = [x,y,w,h].join(' ');
+    svg.configure({viewBox: s }, true);
+    $resDiv.text(s);
+}
+
+
 function drawShape() {
 	var shape = this.id;
 	var svg = $('#svgbasics').svg('get');
@@ -61,9 +87,37 @@
 	else if (shape == 'clear') {
 		svg.clear();
 	}
-	else if (shape == 'zoom') {
-		svg.configure({viewBox: '0 0 800 1000'}, true);
-		//svg.animate({svgViewBox: '0, 0, 100, 100'}, 2000);
+	else if (shape == 'x+') {
+	    x+=10; 
+		setViewBox(svg);
+	}
+	else if (shape == 'x-') {
+	    x-=10; 
+		setViewBox(svg);
+	}
+	else if (shape == 'y+') {
+	    y+=10; 
+		setViewBox(svg);
+	}
+	else if (shape == 'y-') {
+	    y-=10; 
+		setViewBox(svg);
+	}
+	else if (shape == 'w+') {
+	    w+=10; 
+		setViewBox(svg);
+	}
+	else if (shape == 'w-') {
+	    w-=10; 
+		setViewBox(svg);
+	}
+	else if (shape == 'h+') {
+	    h+=10; 
+		setViewBox(svg);
+	}
+	else if (shape == 'h-') {
+	    h-=10; 
+		setViewBox(svg);
 	}
 	
 }
@@ -84,8 +138,16 @@
             <button id="line">Add line</button>
             <button id="circle">Add circle</button>
             <button id="ellipse">Add ellipse</button>
-            <button id="zoom">Zoom</button>
+            <button id="x+">x+</button>
+            <button id="x-">x-</button>
+            <button id="y+">y+</button>
+            <button id="y-">y-</button>
+            <button id="w+">w+</button>
+            <button id="w-">w-</button>
+            <button id="h+">h+</button>
+            <button id="h-">h-</button>
             <button id="clear">Clear</button>
         </p>
+        <div id="res"/>
     </body>
 </html>