Source: BKGWebMap/Control/Legend.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 von Layerlegenden.
 *
 * Sofern ein Layer über ein Attribut legendURL verfügt, wird dieses als Bild
 * in den Container eingefügt.
 *
 * @constructor BKGWebMap.Control.Legend
 * @param {object} options - Optionen für das Controlelement
 */
BKGWebMap.Control.Legend = OpenLayers.Class(OpenLayers.Control, {

    /**
     * Eventhandler
     * @memberOf BKGWebMap.Control.Legend
     * @type OpenLayers.Events
     */
    events: null,

    /**
     * Legenden-Titel
     * @memberOf BKGWebMap.Control.Legend
     * @type HTMLElement
     */
    legendTitle: null,

    /**
     * Container für Legenden
     * @memberOf BKGWebMap.Control.Legend
     * @type HTMLElement
     */
    legendsContainer: null,

    /**
     * Höhe für Legendengrafik. -1 für automatisch vom Server
     * @memberOf BKGWebMap.Control.Legend
     * @type int
     */
    legendHeight: -1,

    /**
     * Breite für Legendengrafik. -1 für automatisch vom Server
     * @memberOf BKGWebMap.Control.Legend
     * @type int
     */
    legendWidth: -1,

    /**
     * Texte für Labels
     * @memberOf BKGWebMap.Control.Legend
     * @type object
     */
    labels: {
    	/** Titel der Legende. */
    	title : 'Legende',
        /** Prefix für Layername in Legende */
        layerPrefix: 'Layer: '
    },

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

    destroy: function() {
        this.map.events.un({
            buttonclick: this.onButtonClick,
            addlayer: this.redraw,
            changelayer: this.redraw,
            removelayer: this.redraw,
            changebaselayer: this.redraw,
            scope: this
        });
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },

    /**
     * Registriert Control für Layer-Map-Events
     * @param {OpenLayers.Map}map
     * @memberOf BKGWebMap.Control.Legend
     */
    setMap: function(map) {
        OpenLayers.Control.prototype.setMap.apply(this, arguments);

        this.map.events.on({
            addlayer: this.redraw,
            changelayer: this.redraw,
            removelayer: this.redraw,
            changebaselayer: this.redraw,
            scope: this
        });
    },

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

        this.legendTitle = document.createElement('h2');
        this.legendTitle.innerHTML = this.labels.title;
        OpenLayers.Element.addClass(this.legendTitle, 'closed');
        div.appendChild(this.legendTitle);

        // Click auf Titel öffnet Legende
        OpenLayers.Event.observe(this.legendTitle, "click", OpenLayers.Function.bindAsEventListener(this.toggle, this));

        // get legend graphics
        this.legendsContainer = document.createElement('dl');
        OpenLayers.Element.addClass(this.legendsContainer, this.displayClass + 'Legends');
        OpenLayers.Element.addClass(this.legendsContainer, 'hidden');

        div.appendChild(this.legendsContainer);

        this.redraw();
        this.registerEvents();
        return div;
    },

    /**
     * Überprüft ob sich der Status der Layer seit dem letzten redraw-Aufruf geändert hat.
     *
     * @return {boolean} <code>true</code> Bei Änderung des Status.
     * @memberOf BKGWebMap.Control.Legend
     */
    checkRedraw: function() {
        if ( !this.layerStates.length || (this.map.layers.length != this.layerStates.length) ) {
            return true;
        }

        for (var i=0, len=this.layerStates.length; i<len; i++) {
            var layerState = this.layerStates[i];
            var layer = this.map.layers[i];

            if ( (layerState.name != layer.name) ||
                 (layerState.id != layer.id) ||
                 (layerState.visibility != layer.visibility) ) {
                return true;
            }
        }
        return false;
    },

    /**
     * Erstellt den dynamischen Inhalt, der abhängig von Layeränderungen, etc. ist.
     * @memberOf BKGWebMap.Control.Legend
     */
    redraw: function () {
        // Sofern sich der Status der Layer nicht geändert hat, ist nichts zu tun.
        if (!this.checkRedraw()) {
            return;
        }

        // Ermittle die aktuellen Stati der Layer
        this.layerStates = BKGWebMap.Util.map(function(layer) {
            return { name: layer.name, id: layer.id, visibility:layer.visibility };
        }, this.map.layers);

        // Redraw
        this.legendsContainer.innerHTML = "";

        BKGWebMap.Util.each(this.map.layers, OpenLayers.Function.bind(
            function(index, layer) {
                if(!layer.legendURL && !(layer instanceof OpenLayers.Layer.WMS))
                    return;
                if(!layer.visibility)
                    return;

                var dt = document.createElement('dt');
                dt.innerHTML = layer.name;
                this.legendsContainer.appendChild(dt);

                var legendWrapper = document.createElement('dd');
                this.legendsContainer.appendChild(legendWrapper);

                var legendGraphics = this.createLegendGraphics(layer);
                BKGWebMap.Util.each(legendGraphics, OpenLayers.Function.bind( function(index, item) { this.appendChild(item); }, legendWrapper) );
            }, this
        ));
    },

    /**
     * Erzeugt die Legendenvisualisierungen für den gegebenen Layer. Dazu muss im Layer legendURL gegeben sein, oder
     * es handelt sich um einen WMS-Layer. Dann wird mit GetLegendGraphic gearbeitet.
     *
     * @param layer
     * @return {Array<HTMLElement>} Liste an Legendengraphiken
     * @memberOf BKGWebMap.Control.Legend
     */
    createLegendGraphics: function(layer) {
        var imgSources;

        var baseURL = layer.url + '?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&FORMAT=image/png';
        if(this.legendHeight > 0)
            baseURL += '&HEIGH=' + this.legendHeight;
        if(this.legendWidth > 0)
            baseURL += '&WIDTH=' + this.legendWidth;
        baseURL += '&LAYER=';

        if (layer.legendURL) {
          // prefer a given legendURL
          imgSources = [{url: layer.legendURL, name: layer.name}];
        } else if ( layer instanceof BKGWebMap.Layer.WMS ) {
            var layers = BKGWebMap.Util.grep( layer.layers, function(item, i) { return item.active } );
            imgSources = BKGWebMap.Util.map( function(layer) {
               var url = baseURL + layer.name;
               if(layer.style)
                   url = url + '&STYLE=' + layer.style;
               return {url: url, name: layer.title, description: layer.title };
            }, layers );
        } else if ( layer instanceof OpenLayers.Layer.WMS ) {
            var layers = layer.params.LAYERS.split(',');
            // TODO: add var styles = layer.params.STYLES.split(',');
            var layerPrefix = this.labels.layerPrefix;
            imgSources = BKGWebMap.Util.map( function(layer) {
               return {url: baseURL + layer, name: layer, description: layerPrefix + layer };
            }, layers );
        }

        return  BKGWebMap.Util.map( function(source) {
            var img = document.createElement('img');
            img.src = source.url;
            img.alt = source.name;

            var imgWrapper = document.createElement('div');

            if(source.description) {
                var span = document.createElement('span');
                span.innerHTML = source.description;
                imgWrapper.appendChild(span);
                imgWrapper.appendChild(document.createElement('br'));
            }

            imgWrapper.appendChild(img);

            return imgWrapper;
        }, imgSources );
    },

    /**
     * Blendet die Legende ein oder aus.
     * @memberOf BKGWebMap.Control.Legend
     */
    toggle: function (evt) {
        // noch nicht erzeugt?
        if(this.div === null) return;
        var visible = OpenLayers.Element.hasClass(this.legendsContainer, "hidden");

        OpenLayers.Element.removeClass(this.legendTitle, visible ? "closed" : "opened");
        OpenLayers.Element.addClass(this.legendTitle, visible ? "open" : "closed");

        OpenLayers.Element.removeClass(this.legendsContainer, visible ? "hidden" : "visible");
        OpenLayers.Element.addClass(this.legendsContainer, visible ? "visible" : "hidden");
    },

    /**
     * Registers events on the div.
     * @memberOf BKGWebMap.Control.Legend
     */
     registerEvents:function() {
        this.events = new OpenLayers.Events(this, this.div, null, true);

        function onTouchstart(evt) {
            OpenLayers.Event.stop(evt, true);
        }
        this.events.on({
            "mousedown": this.onmousedown,
            "mousemove": this.onmousemove,
            "mouseup": this.onmouseup,
            "click": this.onclick,
            "mouseout": this.onmouseout,
            "dblclick": this.ondblclick,
            "touchstart": onTouchstart,
            scope: this
        });

     },

    /**
     * @param {Event} evt
     * @memberOf BKGWebMap.Control.Legend
     */
    onmousedown: function (evt) {
        this.mousedown = true;
        OpenLayers.Event.stop(evt, true);
    },

    /**
     * @param {Event} evt
     * @memberOf BKGWebMap.Control.Legend
     */
    onmousemove: function (evt) {
        if (this.mousedown) {
            OpenLayers.Event.stop(evt, true);
        }
    },

    /**
     * @param {Event} evt
     * @memberOf BKGWebMap.Control.Legend
     */
    onmouseup: function (evt) {
        if (this.mousedown) {
            this.mousedown = false;
            OpenLayers.Event.stop(evt, true);
        }
    },

    /**
     * Ignore clicks, but allowing default browser handling
     * @param {Event} evt
     * @memberOf BKGWebMap.Control.Legend
     */
    onclick: function (evt) {
        OpenLayers.Event.stop(evt, true);
    },

    /**
     * @param {Event} evt
     * @memberOf BKGWebMap.Control.Legend
     */
    onmouseout: function (evt) {
        this.mousedown = false;
    },

    /**
     * Ignore double-clicks, but allowing default browser handling
     * @param {Event} evt
     * @memberOf BKGWebMap.Control.Legend
     */
    ondblclick: function (evt) {
        OpenLayers.Event.stop(evt, true);
    },

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

/**
 * Factory-Funktion zur Generierung eines Legend Steuerelement.
 * @param {Array<OpenLayers.Control>} controls - Liste der Steuerelemente, in die die neue erzeugten Steuerelemente
 *                                               eingefügt werden sollen.
 * @param {boolean} enabled -<code>true</code> wenn Legend erstellt werden soll.
 */
BKGWebMap.Control.FACTORIES['legend'] = function(controls, enabled) {
    if (!enabled) return;
    controls.push(new BKGWebMap.Control.Legend());
};

/*
FÜR WMS:

function erzeugeLegende() {
  var legendDiv = OpenLayers.Util.getElement('legend');
  // Legendenelemente entfernen:
  if ( legendDiv.hasChildNodes() ) {
    while ( legendDiv.childNodes.length >= 1 ) {
      legendDiv.removeChild( legendDiv.firstChild );
    }
  }
  // Alle neu erzeugen
  for (layer_idx in map.layers) {
    var layer = map.layers[layer_idx];
    if ( layer instanceof OpenLayers.Layer.WMS && layer.visibility ) {
      // Ein WMS-Layer in OpenLayers kann aus
      // mehreren kommagetrennten WMS-Layern bestehen
      var url_layers_string = layer.params.LAYERS;
      var url_layers = url_layers_string.split(',');
      for(part_idx in url_layers) {
        singlelayer = url_layers[part_idx];
        // hole legende
	    var url = layer.url;
	    url += ( url.indexOf('?') === -1 ) ? '?' : '';
	    url += '&SERVICE=WMS';
	    url += '&VERSION=1.1.1';
	    url += '&REQUEST=GetLegendGraphic';
	    url += '&FORMAT=image/png';
	    url += '&LAYER=' + singlelayer;
	    var img = document.createElement("img");
	    img.src = url;
	    legendDiv.appendChild(img);
      }
    }
  }
}

...

lyr.events.register('visibilitychanged', window, erzeugeLegende);

*/