view webapp/src/main/webapp/jquery/jquery.digilib.buttons.js @ 985:7f93dc476cdf

halfway up the slider
author hertzhaft
date Fri, 27 Jan 2012 16:00:50 +0100
parents 3b334a7d81ec
children b6261d3d68c2
line wrap: on
line source

/**
digilib buttons plugin
 */

(function($) {

    // plugin object with digilib data
    var digilib;
    // the functions made available by digilib
    var fn;
    // affine geometry plugin
    var geom;

    var buttons = {
            reference : {
                onclick : "reference",
                tooltip : "get a reference URL",
                icon : "reference.png"
                },
            zoomin : {
                onclick : ["zoomBy", 1.4],
                tooltip : "zoom in",
                icon : "zoom-in.png"
                },
            zoomout : {
                onclick : ["zoomBy", 0.7],
                tooltip : "zoom out",
                icon : "zoom-out.png"
                },
            zoomarea : {
                onclick : "zoomArea",
                tooltip : "zoom area",
                icon : "zoom-area.png"
                },
            zoomfull : {
                onclick : "zoomFull",
                tooltip : "view the whole image",
                icon : "zoom-full.png"
                },
            pagewidth : {
                onclick : ["zoomFull", "width"],
                tooltip : "page width",
                icon : "pagewidth.png"
                },
            back : {
                onclick : ["gotoPage", "-1"],
                tooltip : "goto previous image",
                icon : "back.png"
                },
            fwd : {
                onclick : ["gotoPage", "+1"],
                tooltip : "goto next image",
                icon : "fwd.png"
                },
            page : {
                onclick : "gotoPage",
                tooltip : "goto image number",
                icon : "page.png"
                },
            help : {
                onclick : "showAboutDiv",
                tooltip : "about Digilib",
                icon : "help.png"
                },
            reset : {
                onclick : "reset",
                tooltip : "reset image",
                icon : "reset.png"
                },
            mark : {
                onclick : "setMark",
                tooltip : "set a mark",
                icon : "mark.png"
                },
            delmark : {
                onclick : "removeMark",
                tooltip : "delete the last mark",
                icon : "delmark.png"
                },
            hmir : {
                onclick : ["mirror", "h"],
                tooltip : "mirror horizontally",
                icon : "mirror-horizontal.png"
                },
            vmir : {
                onclick : ["mirror", "v"],
                tooltip : "mirror vertically",
                icon : "mirror-vertical.png"
                },
            rot : {
                onclick : "rotate",
                //onclick : ["slider", 0, 360, "rotate-slider"],
                tooltip : "rotate image",
                icon : "rotate.png"
                },
            brgt : {
                onclick : "brightness",
                //onclick : ["slider", -255, 255, "brightness-slider"],
                tooltip : "set brightness",
                icon : "brightness.png"
                },
            cont : {
                onclick : "contrast",
                //onclick : ["slider", -8, 8, "contrast-slider"],
                tooltip : "set contrast",
                icon : "contrast.png"
                },
            rgb : {
                onclick : "javascript:setParamWin('rgb', '...')",
                tooltip : "set rgb values",
                icon : "rgb.png"
                },
            quality : {
                onclick : "setQuality",
                tooltip : "set image quality",
                icon : "quality.png"
                },
            size : {
                onclick : "javascript:toggleSizeMenu()",
                tooltip : "set page size",
                icon : "size.png"
                },
            calibrationx : {
                onclick : "showCalibrationDiv",
                tooltip : "calibrate screen resolution",
                icon : "calibration-x.png"
                },
            scale : {
                onclick : "showScaleModeSelector",
                tooltip : "change image scale",
                icon : "original-size.png"
                },
            toggleoptions : {
                onclick : "moreButtons",
                tooltip : "more options",
                icon : "options.png"
                },
            moreoptions : {
                onclick : ["moreButtons", "+1"],
                tooltip : "more options",
                icon : "options.png"
                },
            lessoptions : {
                onclick : ["moreButtons", "-1"],
                tooltip : "less options",
                icon : "options.png"
                },
            up : {
                onclick : ["moveZoomArea", 0, -1],
                tooltip : "move zoom area up",
                icon : "up.png"
                },
            down : {
                onclick : ["moveZoomArea", 0, 1],
                tooltip : "move zoom area down",
                icon : "down.png"
                },
            left : {
                onclick : ["moveZoomArea", -1, 0],
                tooltip : "move zoom area left",
                icon : "left.png"
                },
            right : {
                onclick : ["moveZoomArea", 1, 0],
                tooltip : "move zoom area right",
                icon : "right.png"
                },
            SEP : {
                icon : "sep.png"
                }
            };

    var modes = [
            {   name : "screen", 
                label : "fit to screen",
                tooltip : "scales the graphic file so that it fills the screen"
            },
            {   name : "pixel",
                label : "pixel by pixel",
                tooltip : "all pixels of the current part of the graphic file are shown"
            },
            {   name : "size",
                label : "original size",
                tooltip : "tries to display the current part of the graphic file in the size of the orginal resource (after screen calibration)" }
            ];

    var defaults = {
        // buttons (reference added later)
        'buttons' : null,
        // defaults for digilib buttons
        'buttonSettings' : {
            'fullscreen' : {
                // path to button images (must end with a slash)
                'imagePath' : 'img/fullscreen/',
                'buttonSetWidth' : 36,
                '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","lessoptions"],
                'arrowSet' : ["up", "down", "left", "right"],
                'buttonSets' : ['standardSet', 'specialSet']
                },
            'embedded' : {
                'imagePath' : 'img/embedded/16/',
                'buttonSetWidth' : 18,
                'standardSet' : ["reference","zoomin","zoomout","zoomarea","zoomfull","help","reset","toggleoptions"],
                'specialSet' : ["mark","delmark","hmir","vmir","rot","brgt","cont","rgb","quality","scale","lessoptions"],
                'arrowSet' : ["up", "down", "left", "right"],
                'buttonSets' : ['standardSet', 'specialSet']
                }
        },
        // number of visible button groups
        'visibleButtonSets' : 1
    };

    var actions = {
            // display more (or less) button sets
            moreButtons : function (data, more) {
                var settings = data.settings;
                if (more == null) {
                    // toggle more or less (only works for 2 sets)
                    var maxbtns = settings.buttonSettings[settings.interactionMode].buttonSets.length;
                    if (settings.visibleButtonSets >= maxbtns) {
                        more = '-1';
                    } else {
                        more = '+1';
                    }
                }
                if (more === '-1') {
                    // remove set
                    var setIdx = settings.visibleButtonSets - 1;
                    if (showButtons(data, false, setIdx, true)) {
                        settings.visibleButtonSets--;
                    }
                } else {
                    // add set
                    var setIdx = settings.visibleButtonSets;
                    if (showButtons(data, true, setIdx, true)) {
                        settings.visibleButtonSets++;
                    }
                }
                // persist setting
                fn.storeOptions(data);
            },
            // brightness slider
            slider : function (data, min, max, id) {
                var $elem = data.$elem;
                var settings = data.settings;
                var $div = $('<div/>');
                $div.attr('id', id);
                $div.slider({'min' : min, 'max' : max });
                $elem.append($div);
                $div.slider('show');
            },
            // shows Calibration Div
            showCalibrationDiv : function (data) {
                var $elem = data.$elem;
                var settings = data.settings;
                var $calDiv = $('#calibration');
                var $input = $('#calibration-input');
                $calDiv.fadeIn();
                $input.focus();
            },

            // shows ScaleModeSelector
            showScaleModeSelector : function (data) {
                var $elem = data.$elem;
                var $div = $("#scalemode");
                if ($div.is(":visible")) {
                    $div.fadeOut();
                    return;
                    }
                // select current mode
                var mode = data.scaleMode;
                $div.find('option').each(function () {
                	$this = $(this);
                	if ($this.attr('name') == mode) {
                		$this.prop('selected', true);
                	} else {
                		$this.prop('selected', false);
                	}
                });
                var $button = $elem.find('div.button-scale');
                var buttonRect = geom.rectangle($button);
                var divRect = geom.rectangle($div);
                $('body').on("click.scalemode", function(event) {
                        $div.fadeOut();
                        });
                $div.fadeIn();
                $div.offset({
                    left : Math.abs(buttonRect.x - divRect.width - 4),
                    top : buttonRect.y + 4
                    });
            }
        };

    // plugin installation called by digilib on plugin object.
    var install = function(plugin) {
        digilib = plugin;
        console.debug('installing buttons plugin. digilib:', digilib);
        fn = digilib.fn;
        // import geometry classes
        geom = fn.geometry;
        // add defaults, actions, buttons
        $.extend(digilib.defaults, defaults);
        $.extend(digilib.actions, actions);
        $.extend(digilib.buttons, buttons);
        // update buttons reference in defaults
        digilib.defaults.buttons = digilib.buttons;
        // export functions
        fn.createButton = createButton;
        fn.highlightButtons = highlightButtons;
    };

    // plugin initialization
    var init = function (data) {
        console.debug('initialising buttons plugin. data:', data);
        /* create buttons before scaler 
        for (var i = 0; i < data.settings.visibleButtonSets; ++i) {
            showButtons(data, true, i);
        } */
        // install event handler
        var $data = $(data);
        $data.bind('setup', handleSetup);
        sliderPlugin(jQuery, data);
    };

    var handleSetup = function (evt) {
        console.debug("buttons: handleSetup");
        var data = this;
        // create buttons before scaler 
        for (var i = 0; i < data.settings.visibleButtonSets; ++i) {
            showButtons(data, true, i);
        }
        // create ScaleMode selector;
        setupScaleModeDiv(data);
        // create Calibration div;
        setupCalibrationDiv(data);
    };

    var centerOnScreen = function (data, $div) {
        var r = geom.rectangle($div);
        var s = fn.getFullscreenRect(data);
        r.setCenter(s.getCenter());
        r.adjustDiv($div);
    };

    // slider
    var sliderPlugin = function($, digilibdata) {
        var defaults = {
            'id' : 'slider',
            'class' : 'slider',
            'size' : '10px',
            'direction' : 'x',
            'min' : 0,
            'max' : 100,
            'val' : 33
            };
        var methods = {
            init : function( options ) { 
                return this.each(function() {
                    var $this = $(this);
                    // var settings = data.settings;
                    var settings = $.extend( defaults, options);
                    console.debug('slider instance data:', settings);
                    $this.data('digilib', digilibdata);
                    var data = $this.data('settings');
                    if (!data) {
                        $this.data('settings', settings);
                        $this.addClass(settings.class);
                        var $sliderbutton = $('<div class="sliderbutton" />');
                        settings.$button = $sliderbutton;
                        $this.append($sliderbutton);
                        $this.slider('setpos');
                        }
                });
            },
            setval : function(data, val) {
                settings.val =
                    val < settings.min ? settings.min :
                    val > settings.max() ? settings.max :
                    val;
            },
            setpos : function() {
                var $this = $(this);
                var settings = $this.data('settings');
                var $sliderbutton = settings.$button;
                var delta = (settings.max - settings.min) / settings.max * settings.val;
                var r = geom.rectangle($this);
                var s = geom.rectangle($sliderbutton);
                var newpos, middle; 
                if (settings.direction == 'y') {
                    $sliderbutton.width(r.width).height(settings.size);
                    newpos = r.y + delta * r.height;
                    middle = r.x + r.width / 2;
                    s.setCenter(geom.position(middle, newpos));
                    s.adjustDiv($sliderbutton);
                    }
                else {
                    $sliderbutton.height(r.height).width(settings.size);
                    newpos = r.x + delta * r.width;
                    middle = r.y + r.height / 2;
                    s.setCenter(geom.position(newpos, middle));
                    s.adjustDiv($sliderbutton);
                    }
                return $this;
            },
            getval : function(data) {
                var $this = $(this);
                var settings = $this.data('settings');
            },
            show : function(data) {
                var $this = $(this);
                var settings = $this.data('slider');
                var $elem = data.$elem;
                $this.fadeIn();
                centerOnScreen(data, $this);
                return;
                var $body = $('body');
                var pt1, pt2;

                // mousedown handler: start sliding
                var sliderStart = function (event) {
                    pt1 = geom.position(event);
                    // overlay prevents other elements from reacting to mouse events 
                    // var $overlay = $('<div class="digilib-overlay" style="position:absolute"/>');
                    $body.on("mousemove.slider", sliderMove);
                    $body.on("mouseup.slider", sliderEnd);
                    return false;
                };

                // mousemove handler: move slider
                var sliderMove = function (event) {
                    pt2 = geom.position(event);
                    return false;
                };
    
                // mouseup handler: end sliding
                var sliderEnd = function (event) {
                    pt2 = geom.position(event);
                    $body.off("mousemove.slider");
                    $body.off("mouseup.slider");
                    // $overlay.remove();
                    settings.callback(settings.val);
                    redisplay(data);
                    return false;
                };

            // bind start zoom handler
            $overlay.one('mousedown.dlRegion', regionStart);
            fn.highlightButtons(data, 'addregion', 1);

            },
            hide : function( ) { 
            },
            destroy : function( ) { 
            },
            update : function( content ) { 
            }
        };
        // constructor
        $.fn.slider = function( method ) {
            if ( methods[method] ) {
                // call a method
                var $elem = $(this);
                var data = $elem.data('digilib');
                var args = Array.prototype.slice.call(arguments, 1);
                args.unshift(data);
                return methods[method].apply(this, args);
                }
            else if ( !method || typeof method === 'object' ) {
                // call init(), with an optional object containing options
                return methods.init.apply( this, arguments );
                }
            else {
                $.error( 'Method ' +  method + ' does not exist on digilib.slider!' );
                }
        };
    };

    /** creates HTML structure for the calibration div
     */
    var setupCalibrationDiv = function (data) {
        var $elem = data.$elem;
        var settings = data.settings;
        var html = '\
            <div id="calibration" class="calibration">\
                <div class="ruler">\
                    <div class="cm">Please enter the length of this scale on your screen</div>\
                    <div>\
                        <input id="calibration-input" size="5"/> cm\
                        <button class="calibration-button" id="calibrationOk">OK</button>\
                        <button class="calibration-button" id="calibrationCancel">Cancel</button>\
                    </div>\
                    <div class="calibration-error">Please enter a numeric value like this: 12.3</div>\
                </div>\
            </div>';
        var $calDiv = $(html);
        $elem.append($calDiv);
        var $input = $calDiv.find("input");
        var $ok = $calDiv.find("#calibrationOk");
        var $cancel = $calDiv.find("#calibrationCancel");
        data.calibrationDiv = $calDiv;
        data.calibrationErrorDiv = $calDiv.find("div.calibration-error");
        data.calibrationInput = $input;
        centerOnScreen(data, $calDiv);
        var handler = function(event) {
            var _data = data;
            if (event.keyCode == 27 || event.target.id == 'calibrationCancel') {
                $calDiv.fadeOut();
                return false;
                }
            if (event.keyCode == 13 || event.target.id == 'calibrationOk') {
                changeCalibration(_data);
                return false;
                }
            _data.calibrationInput.removeClass('error');
            };
        $ok.on("click", handler);
        $cancel.on("click", handler);
        $input.on("keypress", handler);
    };

    /** creates HTML structure for the scale mode menu
     */
    var setupScaleModeDiv = function (data) {
        var $elem = data.$elem;
        var settings = data.settings;
        var currentMode = digilib.fn.getScaleMode(data);
        var $scaleModeDiv = $('<div id="scalemode" style="display:none; z-index:9999; position:absolute"/>');
        data.scaleModeDiv = $scaleModeDiv;
        var $scaleModeSelect = $('<select class="scalemode" />');
        $elem.append($scaleModeDiv);
        $scaleModeDiv.append($scaleModeSelect);
        for (var i = 0; i < modes.length; i++) {
            var mode = modes[i];
            var select = (mode.name == currentMode) ? ' select="select"' : '';
            $scaleModeSelect.append($('<option name="'
                    + mode.name + '"' + select + '>' 
                    + mode.label + '</option>'));
        }
        $scaleModeDiv.on("click.scalemode", function(event) {
            return false;
            });
        $scaleModeSelect.on('change.scalemode', function(event) {
            var d = data;
            changeMode(event, d);
            });
    };

    /** event handler
     */
    var changeMode = function (event, data) {
        var $select = $(event.target);
        var newMode = $select.find("option:selected").attr("name");
        console.debug('setting mode to:', newMode);
        var $div = data.scaleModeDiv;
        $('body').off("click.scalemode");
        $div.fadeOut();
        digilib.actions.setScaleMode(data, newMode);
    };

    /** event handler
     */
    var changeCalibration = function (data) {
        var $calDiv = data.calibrationDiv;
        var $input = data.calibrationInput;
        var cm = $input.val();
        var w = $calDiv.width();
  		var dpi = fn.cropFloat(w / parseFloat(cm) * 2.54);
  		console.debug('width', w, 'cm', cm, 'input dpi:', dpi);
  		if(!fn.isNumber(dpi)) {
  		    $input.addClass('error');
  		    return;
  		    }
  		digilib.actions.calibrate(data, dpi);
        $calDiv.fadeOut();
    };

    // creates HTML structure for a single button
    var createButton = function (data, $div, buttonName) {
        var $elem = data.$elem;
        var settings = data.settings;
        var mode = settings.interactionMode;
        var imagePath = settings.buttonSettings[mode].imagePath;
        // make relative imagePath absolute
        if (imagePath.charAt(0) !== '/' && imagePath.substring(0,3) !== 'http') {
        	imagePath = settings.digilibBaseUrl + '/jquery/' + imagePath;
        }
        var buttonConfig = settings.buttons[buttonName];
        // button properties
        var action = buttonConfig.onclick;
        var tooltip = buttonConfig.tooltip;
        var icon = imagePath + buttonConfig.icon;
        // construct the button html
        var $button = $('<div class="button"></div>');
        var $a = $('<a/>');
        var $img = $('<img class="button"/>');
        $div.append($button);
        $button.append($a);
        $a.append($img);
        // add attributes and bindings
        $button.attr('title', tooltip);
        $button.addClass('button-' + buttonName);
        $img.attr('src', icon);
        // create handler for the buttons
        $button.on('click.digilib', (function () {
            // we create a new closure to capture the value of action
            if ($.isArray(action)) {
                // the handler function calls digilib with action and parameters
                return function (evt) {
                    console.debug('click action=', action, ' evt=', evt);
                    $elem.digilib.apply($elem, action);
                    return false;
                };
            } else {
                // the handler function calls digilib with action
                return function (evt) {
                    console.debug('click action=', action, ' evt=', evt);
                    $elem.digilib(action);
                    return false;
                };
            }
        })());
    };

    // creates HTML structure for buttons in elem
    var createButtons = function (data, buttonSetIdx) {
        var $elem = data.$elem;
        var settings = data.settings;
        var mode = settings.interactionMode;
        var buttonSettings = settings.buttonSettings[mode];
        var buttonGroup = buttonSettings.buttonSets[buttonSetIdx];
        if (buttonGroup == null) {
            // no buttons here
            return;
        }
        // button divs are marked with class "keep"
        var $buttonsDiv = $('<div class="keep buttons"/>');
        var buttonNames = buttonSettings[buttonGroup];
        for (var i = 0; i < buttonNames.length; i++) {
            var buttonName = buttonNames[i];
            createButton(data, $buttonsDiv, buttonName);
        }
        // make buttons div scroll if too large for window
        if ($buttonsDiv.height() > $(window).height() - 10) {
            $buttonsDiv.css('position', 'absolute');
        }
        // buttons hidden at first
        $buttonsDiv.hide();
        $elem.append($buttonsDiv);
        if (data.$buttonSets == null) {
            // first button set
            data.$buttonSets = [$buttonsDiv];
        } else {
            $elem.append($buttonsDiv);
            data.$buttonSets[buttonSetIdx] = $buttonsDiv;
        }
        return $buttonsDiv;
    };

    // display more (or less) button sets
    var showButtons = function (data, more, setIdx, animated) {
        var atime = animated ? 'fast': 0;
        // get button width from settings
        var mode = data.settings.interactionMode;
        var btnWidth = data.settings.buttonSettings[mode].buttonSetWidth;
        if (more) {
            // add set
            var $otherSets = data.$elem.find('div.buttons:visible');
            var $set;
            if (data.$buttonSets && data.$buttonSets[setIdx]) {
                // set exists
                $set = data.$buttonSets[setIdx];
            } else {
                $set = createButtons(data, setIdx);
                }
            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,
                            function () {$set.show();});
            } else {
                $set.show();
            }
        } else {
            // remove set
            var $set = data.$buttonSets[setIdx];
            if ($set == null) return false;
            //var btnWidth = $set.outerWidth();
            // hide last set
            $set.hide();
            // take remaining sets and move right
            var $otherSets = data.$elem.find('div.buttons:visible');
            $otherSets.animate({right : '-='+btnWidth+'px'}, atime);
        }
        return true;
    };

    // check for buttons to highlight TODO: improve this!
    var highlightButtons = function (data, name, on) {
        var $buttons = data.$elem.find('div.buttons:visible'); // include hidden?
        // add a class for highlighted button
        var highlight = function (name, on) {
            var $button = $buttons.find('div.button-' + name);
            if (on) {
                $button.addClass('button-on');
            } else {
                $button.removeClass('button-on');
            }
        };
        if (name != null) {
            return highlight(name, on);
            }
        var flags = data.scalerFlags;
        var settings = data.settings;
        highlight('rot', settings.rot);
        highlight('brgt', settings.brgt);
        highlight('cont', settings.cont);
        highlight('bird', settings.isBirdDivVisible);
        highlight('help', settings.isAboutDivVisible);
        highlight('hmir', flags.hmir);
        highlight('vmir', flags.vmir);
        highlight('quality', flags.q1 || flags.q2);
        highlight('zoomin', ! isFullArea(data.zoomArea));
        };

    // plugin object with name and init
    // shared objects filled by digilib on registration
    var plugin = {
            name : 'buttons',
            install : install,
            init : init,
            buttons : {},
            actions : {},
            fn : {},
            plugins : {}
    };

    if ($.fn.digilib == null) {
        $.error("jquery.digilib.buttons must be loaded after jquery.digilib!");
    } else {
        $.fn.digilib('plugin', plugin);
    }
})(jQuery);