Source: BKGWebMap/Control/Window.js

/*
 * Copyright (c) 2013 Bundesamt für Kartographie und Geodäsie.
 * See license.txt in the BKG WebMap distribution or repository for the
 * full text of the license.
 *
 * Author: Dirk Thalheim
 */

/**
 * @requires OpenLayers/BaseTypes/Class.js
 * @requires OpenLayers/BaseTypes/Element.js
 * @requires OpenLayers/Util.js
 * @requires OpenLayers/Control.js
 * @requires OpenLayers/Events.js
 * @requires OpenLayers/Layer/WMS.js
 * @requires BKGWebMap/Control.js
 * @requires BKGWebMap/Util.js
 */

/**
 * @classdesc Control zur Anzeige eines Fensters.
 *
 * @constructor BKGWebMap.Control.Window
 * @param {object} options - Optionen für das Controlelement
 */
BKGWebMap.Control.Window = OpenLayers.Class(OpenLayers.Control, {

    // muss <code>true</code> sein, ansonsten klappt Textselektion nicht
    allowSelection: true,

    /**
     * Titel für Fenster
     * @memberOf BKGWebMap.Control.Window
     * @type string
     */
    title: null,

    /**
     * Inhalt des Fensters
     * @memberOf BKGWebMap.Control.Window
     * @type string
     */
    content: null,

    /**
     * Container für Inhalt. Kann alternativ zu content gesetzt werden, um DOM-Objekte direkt einzufügen.
     * @memberOf BKGWebMap.Control.Window
     * @type HTMLElement
     */
    contentDiv: null,

    /**
     * Größe des Bereichs für den Fensterinhalt
     * @memberOf BKGWebMap.Control.Window
     * @type OpenLayers.Size
     */
    size: new OpenLayers.Size(200, 200),

    /**
     * x-Positionsagabe bezieht sich auf Top. Wenn <code>false</code> dann wird bottom verwendet.
     * @memberOf BKGWebMap.Control.Window
     * @type boolean
     */
    positionTop: true,

    /**
     * y-Positionsagabe bezieht sich auf Left. Wenn <code>false</code> dann wird rigth verwendet.
     * @memberOf BKGWebMap.Control.Window
     * @type boolean
     */
    positionLeft: true,

    /**
     * X-Position des Fensters
     * @memberOf BKGWebMap.Control.Window
     * @type int
     */
    x: 0,

    /**
     * Y-Position des Fensters
     * @memberOf BKGWebMap.Control.Window
     * @type int
     */
    y: 0,

    /**
     * Gibt an, ob der Fensterinhalt angezeigt werden soll oder nicht.
     * @memberOf BKGWebMap.Control.Window
     * @type boolean
     */
    minimized: false,

    dragEvents: null,
    docMouseUpProxy: null,

    initialize: function(options) {
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
    },

    destroy: function() {
        this.dragEvents.unregister('mousedown', this, this.onmousedown);
        this.dragEvents.unregister('mouseup', this, this.onmouseup);
        this.dragEvents.unregister('mousemove', this, this.onmousemove);
        this.dragEvents.destroy();
        this.dragEvents = null;

        this.map.events.unregister('mousemove', this, this.onmousemove);

        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },

    /**
     * Erstellt die Legende für alle Layer in der Karte
     *
     * @return {HTMLElement} Eine Referenz zum DOMElement welches die Legende beinhaltet.
     * @memberOf BKGWebMap.Control.Window
     */
    draw: function() {
        var div = OpenLayers.Control.prototype.draw.apply(this);
        this.setWindowPos(this.x, this.y);

        this.titleDiv = this.createTitleDiv(this.title);
        div.appendChild(this.titleDiv);

        this.contentDiv = this.createContentDiv(this.content);
        div.appendChild(this.contentDiv);

        // Events
        this.dragEvents = new OpenLayers.Events(this, this.titleDiv, null, true);
        this.dragEvents.register('mousedown', this, this.onmousedown);
        this.dragEvents.register('mouseup', this, this.onmouseup);
        this.dragEvents.register('mousemove', this, this.onmousemove);
        // Define a function bound to this used to listen for
        // document mouseout events
        this.docMouseUpProxy = OpenLayers.Function.bind(this.onmouseup, this);


        // stoppe mousemove-Events falls nicht im Dragmodus, da sonst beim Scrollen Kartenpanning erfolgt
        OpenLayers.Event.observe(
                this.contentDiv, "mousemove",
                OpenLayers.Function.bindAsEventListener(
                        function(evt){ if (!this.mousedown) OpenLayers.Event.stop(evt, true); },
                        this
                )
        );
        // Minimierung/Maximierung Fenster
        OpenLayers.Event.observe(this.windowControl, "click", OpenLayers.Function.bindAsEventListener(this.onWindowControlClick, this));

        if(this.minimized) this.minimize();

        return div;
    },

    setMap: function(map) {
        OpenLayers.Control.prototype.setMap.apply(this, [map]);
        this.map.events.register('mousemove', this, this.onmousemove);
    },


    /**
     * Aktualisiert die Fensterposition.
     * @param {int} x - Die x-Position
     * @param {int} y - Die y-Position
     * @memberOf BKGWebMap.Control.Window
     */
    setWindowPos: function(x, y) {
        this.x = x;
        if(this.positionLeft) {
            this.div.style.left = x + 'px';
        } else {
            this.div.style.right = x + 'px';
        }

        this.y = y;
        if(this.positionTop) {
            this.div.style.top = y + 'px';
        } else {
            this.div.style.bottom = y + 'px';
        }
    },

    /**
     * Minimiert das Fenster
     * @memberOf BKGWebMap.Control.Window
     */
    minimize: function() {
        OpenLayers.Element.removeClass(this.windowControl, 'opened');
        OpenLayers.Element.addClass(this.windowControl, 'closed');
        OpenLayers.Element.toggle(this.contentDiv);
        this.minimized = true;
    },

    /**
     * Maximiert das Fenster
     * @memberOf BKGWebMap.Control.Window
     */
    maximize: function() {
        OpenLayers.Element.removeClass(this.windowControl, 'closed');
        OpenLayers.Element.addClass(this.windowControl, 'opened');
        OpenLayers.Element.toggle(this.contentDiv);
        this.minimized = false;
    },

    /**
     * Erzeugt die Titelleiste für das Fenster
     * @memberOf BKGWebMap.Control.Window
     * @param {string} titleHTML - Der HTML-Inhalt für die Titelleiste
     * @returns {HTMLElement}
     */
    createTitleDiv: function(titleHTML) {
        var title = document.createElement('h1');
        title.innerHTML = titleHTML;
        title.style.width = this.size.w + 'px';

        this.windowControl = document.createElement('span');
        OpenLayers.Element.addClass(this.windowControl, 'windowControl');
        OpenLayers.Element.addClass(this.windowControl, 'opened');
        title.appendChild(this.windowControl);
        return title;
    },

    /**
     * Generiert den Fensterinhalt
     * @memberOf BKGWebMap.Control.Window
     * @param {string} contentHTML - HTML-content für Container
     * @return HTMLElement
     */
    createContentDiv: function(contentHTML) {
        var div = (this.contentDiv) ? this.contentDiv : document.createElement('div');
        if(!this.contentDiv) {
            div.innerHTML = contentHTML;
        }

        OpenLayers.Element.addClass(div, 'windowContent');
        div.style.width = this.size.w + 'px';
        div.style.height = this.size.h + 'px';
        return div;
    },

    /**
     * Beginnt den Dragmodus für das Fenster
     * @param {Event} evt
     * @memberOf BKGWebMap.Control.Window
     */
    onmousedown: function (evt) {
        this.mousedown = true;

        this.divOriX = parseInt(OpenLayers.Element.getStyle(this.div, this.positionLeft ? 'left' : 'right').replace('px',''));
        this.divOriY = parseInt(OpenLayers.Element.getStyle(this.div, this.positionTop ? 'top' : 'bottom').replace('px',''));

        this.clientOriX = evt.clientX;
        this.clientOriY = evt.clientY;

        OpenLayers.Element.addClass(this.div, 'dragging');
        OpenLayers.Event.observe(document, 'mouseup', this.docMouseUpProxy);
        OpenLayers.Event.stop(evt, true);
    },

    /**
     * Beendet den Dragmodus für das Fenster
     * @param {Event} evt
     * @memberOf BKGWebMap.Control.Window
     */
    onmouseup: function (evt) {
        this.mousedown = false;
        OpenLayers.Element.removeClass(this.div, 'dragging');
        OpenLayers.Event.stopObserving(document, 'mouseup', this.docMouseUpProxy);
        OpenLayers.Event.stop(evt, true);
    },

    /**
     * Aktualisiert die Fensterposition im Dragmodus, wenn die Maus bewegt wird
     * @param {Event} evt
     * @memberOf BKGWebMap.Control.Legend
     */
    onmousemove: function (evt) {
        if (!this.mousedown) return;

        var dx = evt.clientX - this.clientOriX;
        dx = this.positionLeft ? dx : -dx;
        var dy = evt.clientY - this.clientOriY;
        dy = this.positionTop ? dy : -dy;
        this.setWindowPos(this.divOriX + dx, this.divOriY + dy);

        OpenLayers.Event.stop(evt, true);
    },

    /**
     * Click auf das Minimier-/Maximiersymbol in der Titelleiste
     * @param {Event} evt
     * @memberOf BKGWebMap.Control.Legend
     */
    onWindowControlClick: function(evt) {
        if(this.minimized) {
            this.maximize();
        } else {
            this.minimize();
        }
    },

    CLASS_NAME: "BKGWebMap.Control.Window"
});