Source: BKGWebMap/Util/Slider.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/Util.js
 * @requires BKGWebMap/Util.js
 */

/**
 * @classdesc Element zur Erzeugung eines Schiebereglers.
 *
 * @constructor BKGWebMap.Util.Slider
 * @param {object} options - Optionen zur Modifikation der Parameter
 */
BKGWebMap.Util.Slider = OpenLayers.Class({

	/**
	 * HTML-Element, welches den Slider beinhaltet
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {node}
	 */
	div: null,
	
	/**
	 * HTML-Element, für den Slider
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {node}
	 */
	slider: null,	
	
  /**
   * Events Instanz zur Überwachung von Slider-Spezifischen Events
	 *
   * Registrierung via:
   * <code>
   * slider.events.register(type, obj, listener);
   * </code>
   *
   * Folgende Events stehen zur Verfügung:
   * valuechanged: Änderung des aktuellen Sliderwertes
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {OpenLayers.Events}
   */
  events: null,
	
	/**
	 * CSS-Klasse für Container 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {string}
	 */
	style: 'wmSlider',

	/**
	 * Richtung für Slider: 'h' für horizontal oder 'v' für vertikal 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {string}
	 */
	direction: 'h',
	
	/**
	 * Flag für dragmodus 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {boolean}
	 */
	mousedown: false,
	
	/**
	 * Breite der Skala, auf der sich der Slider bewegt. 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {int}
	 */
	scaleSize: 150,
	
	/**
	 * Breite des Sliders, auf der sich der Slider bewegt. 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {int}
	 */
	sliderSize: 16,
	
	/**
	 * Minimalpixelwert für Sliderposition 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {int}
	 */
	minPx: 0,
	
	/**
	 * Maximalpixelwert für Sliderposition. Bei <code>-1</code> berechnet sich dies aus scaleWidth. 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {int}
	 */
	maxPx: -1,
	
	/**
	 * aktuelle Pixelposition des Sliders 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {int}
	 */
	px: 0,
	
	/**
	 * aktuelle Wert des Sliders 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {number}
	 */
	value: 0,
	
	/**
	 * minimale Wert des Sliders 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {number}
	 */
	min: 0,
	
	/**
	 * maximale Wert des Sliders 
	 * @memberOf BKGWebMap.Util.Slider
	 * @type {number}
	 */
	max: 100,
	
  initialize: function(options) {
    OpenLayers.Util.extend(this, options);
    this.events = new OpenLayers.Events(this);
    this.draw();
  },

  destroy: function() {
    if(this.events) {
      this.events.destroy();
      this.events = null;
    }

    OpenLayers.Event.stopObserving(this.slider, "mousedown");
    OpenLayers.Event.stopObserving(this.slider, "touchstart");
    // TODO: stopObserving mouseup and mousemove

    this.div = null;
    this.slider = null;
  },
    
  /**
   * Zeichnet alle HTML-Elemente für den Slider
	 * @memberOf BKGWebMap.Util.Slider
	 * @return {node} Gibt das HTML-Element welches den Slider beinhaltet zurück
   */
  draw: function() {
    // Wrapper
    if(!this.div) {
      this.div = document.createElement("div");
		}
		OpenLayers.Element.addClass(this.div, this.style);
		
		// slider
		this.slider = document.createElement("slider");
		OpenLayers.Element.addClass(this.slider, this.style + 'Knob');
		this.div.appendChild(this.slider);
		
		// Events
    OpenLayers.Event.observe(this.slider, "mousedown", OpenLayers.Function.bindAsEventListener(this.onmousedown, this));
    OpenLayers.Event.observe(document, "mouseup", OpenLayers.Function.bindAsEventListener(this.onmouseup, this));
    OpenLayers.Event.observe(document, "mousemove", OpenLayers.Function.bindAsEventListener(this.onmousemove, this));
    // TODO: Auch Touch-Geräte unterstützen!
    //OpenLayers.Event.observe(this.slider, "touchstart", OpenLayers.Function.bindAsEventListener(this.onmousedown, this));
    //OpenLayers.Event.observe(document, "touchend", OpenLayers.Function.bindAsEventListener(this.onmouseup, this));
    //OpenLayers.Event.observe(document, "touchmove", OpenLayers.Function.bindAsEventListener(this.onmousemove, this));
    // TODO: click in Wrapper für direkte Positionierung
    	
    // Positionen
    // TODO: Breiten/Höhen dynamisch berechnen...
    if(this.maxPx == -1) {
      this.maxPx = this.scaleSize - this.sliderSize;
    }
    this.setValue(this.value);
        
    return this.div;
  },
    
    
  getPxByValue : function(value) {
    var p = value / (this.max - this.min);
    return (this.scaleSize - this.sliderSize) * p;
  },

  getValueByPx : function(px) {
    var p = px / (this.scaleSize - this.sliderSize);
    return (this.max - this.min) * p + this.min;
  },

  /**
   * Aktualisiert die Sliderposition anhand des Wertes.
   * @param {number} val - Der neue Wert für den Slider
	 * @memberOf BKGWebMap.Util.Slider
   */
  setValue: function(val) {
    val = Math.max(this.min, val);
    val = Math.min(this.max, val);
    this.value = val;

    var px = this.getPxByValue(this.value - this.min);

    this.setSliderPos(px);
    this.events.triggerEvent("valuechanged");
  },
    
  /**
   * Aktualisiert die Sliderposition im Wrapper.
   * @param {int} px - Die Pixelposition des Sliders
	 * @memberOf BKGWebMap.Util.Slider
   */
  setSliderPos: function(px) {
    px = Math.max(this.minPx, px);
    px = Math.min(this.maxPx, px);

    this.px = px;
    if(this.direction == 'h') {
      this.slider.style.left = this.px + 'px';
    } else {
      this.slider.style.top = this.px + 'px';
    }
  },
    
    
  /**
   * Beginnt den Dragmodus für das Fenster
   * @param {Event} evt
   * @memberOf BKGWebMap.Util.Slider
   */
  onmousedown: function (evt) {
    this.mousedown = true;

    var posStyle = OpenLayers.Element.getStyle(this.slider, this.direction == 'h' ? 'left' : 'top');
    this._sliderOriPx = parseInt(posStyle.replace('px',''));

    this._clientOriPx = this.direction == 'h' ? evt.clientX : evt.clientY;

    OpenLayers.Element.addClass(this.slider, 'dragging');
    OpenLayers.Event.stop(evt, true);
  },

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

    OpenLayers.Event.stop(evt, true);

    var curClientPx = this.direction == 'h' ? evt.clientX : evt.clientY;
    var dx = curClientPx - this._clientOriPx;
		this.setSliderPos(this._sliderOriPx + dx);
		this.value = this.getValueByPx(this.px);
		this.events.triggerEvent("valuechanged");
  },

  /**
   * Beendet den Dragmodus für das Fenster
   * @param {Event} evt
	 * @memberOf BKGWebMap.Util.Slider
   */
  onmouseup: function (evt) {
    if (!this.mousedown) return;

    this.mousedown = false;
    OpenLayers.Element.removeClass(this.slider, 'dragging');
    OpenLayers.Event.stop(evt, true);
  },
    
  CLASS_NAME: "BKGWebMap.Util.Slider"
});