WaBis

walter.bislins.ch

Datei: CP: slider.js

Inhalt der Datei: ./javascript/ControlPanel/src/slider.js
/**
 * Dragdealer  (Dgd) JS v0.9.5
 * http://skidding.github.io/dragdealer/
 *
 * Copyright (c) 2010, Ovidiu Chereches
 * MIT License
 * http://legal.ovidiu.ch/licenses/MIT
 *
 * Dependencies:
 * x.js
 *
 * 2014-11-24 wabis: LayoutChange events integrated via x-modules xAddEventLayoutChange() (see x.js)
 * 2015-10-12 wabis: New Formats, Context Menu disabled
 * 2016-01-08 wabis: Fixed: disabling Context Menu error on IE8 (addEventListener not implemented)
 * 2016-08-14 wabis: use of addEventListener/removeEventListener;
 *                   new: free() to release event listeners and delete slider DOM
 *                   new: option autoUpdateLayout=false to suppress handling of layout/resize changes here
 */

/* Cursor */

var DgdCursor = {
  x: 0, y: 0,

  init: function() {
    var mouseHandler = function CB_OnMouse(e) {
      //console.log( 'DgdCursor.on'+type+'move' );
      DgdCursor.refresh(e);
    }
    document.addEventListener( 'mousemove', mouseHandler, false );
    document.addEventListener( 'touchmove', mouseHandler, false );
  },

  refresh: function(e) {
    if (e.type == 'mousemove') {
      this.set(e);
    }
    else if (e.touches) {
      this.set(e.touches[0]);
    }
  },

  set: function(e) {
    if(e.pageX || e.pageY) {
      this.x = e.pageX;
      this.y = e.pageY;
    } else if(e.clientX || e.clientY) {
      this.x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
      this.y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }
  }
};

DgdCursor.init();

/* Position */

var DgdPosition = {
  get: function(obj) {
    var curleft = 0;
    var curtop = 0;
    if(obj.offsetParent) {
      do {
        curleft += obj.offsetLeft;
        curtop += obj.offsetTop;
      } while((obj = obj.offsetParent));
    }
    return [curleft, curtop];
  }
};

/* Dragdealer Slider */

function DgdSliderHtml( aID, aCaption, aHandleColor, aSliderColor ) {
  aCaption = xDefStr( aCaption, '⇔' );
  aHandleColor = xDefStr( aHandleColor, '' );
  aSliderColor = xDefStr( aSliderColor, '' );
  var s = '';
  var style = '';
  if (aSliderColor != '') style = ' style="background-color: ' + aSliderColor + ';"';
  s += '<div id="' + aID + '" class="Slider"' + style + '>';
  style = '';
  if (aHandleColor != '') style = ' style="background-color: ' + aHandleColor + ';"';
  s += '<div id="' + aID + '-Handle" class="Handle"' + style + '>' + aCaption + '</div></div>';
  return s;
}

var DgdSlider = function(wrapper, options) {
  if (!(wrapper = xElement( wrapper ))) return;
  var handle = xGetByTag( 'div', wrapper )[0];
  if(!xHasClass(handle,'Handle')) return;
  this.init(wrapper, handle, options || {});
  this.addListeners();
  this.setup(false);
  this.IsLife = true;
};

DgdSlider.prototype = {
  sliderList: [],
  animationFrameId: null,

  init: function(wrapper, handle, options)   {
    DgdSlider.prototype.sliderList.push( this );
    this.isSetup = false;
    this.wrapper = wrapper;
    this.handle = handle;
    this.options = options;

    this.disabled = this.getOption('disabled', false);
    this.horizontal = this.getOption('horizontal', true);
    this.vertical = this.getOption('vertical', false);
    this.slide = this.getOption('slide', true);
    this.steps = this.getOption('steps', 0);
    this.snap = this.getOption('snap', false);
    this.loose = this.getOption('loose', false);
    this.speed = this.getOption('speed', 10) / 100;
    this.xPrecision = this.getOption('xPrecision', 0);
    this.yPrecision = this.getOption('yPrecision', 0);
    this.autoUpdateLayout = this.getOption('autoUpdateLayout', true);

    this.callback = options.callback || null;
    this.animationCallback = options.animationCallback || null;

    this.bounds = {
      left: options.left || 0, right: -(options.right || 0),
      top: options.top || 0, bottom: -(options.bottom || 0),
      x0: 0, x1: 0, xRange: 0,
      y0: 0, y1: 0, yRange: 0
    };
    this.value = {
      prev: [-1, -1],
      current: [options.x || 0, options.y || 0],
      target: [options.x || 0, options.y || 0]
    };
    this.offset = {
      wrapper: [0, 0],
      mouse: [0, 0],
      prev: [-999999, -999999],
      current: [0, 0],
      target: [0, 0]
    };
    this.change = [0, 0];

    this.activity = false;
    this.dragging = false;
    this.tapping = false;

    // event handler --------------------------------------
    var self = this;
    this.selectstartHandler = function CB_OnSelectStart() {
      //console.log( 'DgdSlider.wrapper.onselectstart' );
      return false;
    };
    this.mousedownHandler = function CB_OnMouseDown(e) {
      //console.log( 'DgdSlider.handle.onmousedown' );
      self.handleDownHandler(e);
    };
    this.contextmenuHandler = function CB_OnContextmenu(e){
      //e.stopPropagation();
      e.preventDefault();
      //e.cancelBubble = true;
      return false;
    };
    this.wrapperMousedownHandler = function CB_OnMouseDown(e) {
      //console.log( 'DgdSlider.wrapper.onmousedown' );
      self.wrapperDownHandler(e);
    };
    this.mouseupHandler = function CB_OnMouseUp(e) {
      //console.log( 'DgdSlider.document.onmouseup' );
      self.documentUpHandler(e);
    };
    this.touchendHandler = function CB_OnTouchEnd(e) {
      //console.log( 'DgdSlider.document.ontouchend' );
      self.documentUpHandler(e);
    };
    this.windowResizeHandler = function CB_OnWindowResize() {
      //console.log( 'DgdSlider.window.onresize' );
      self.documentResizeHandler();
    };
    this.mousemoveHandler = function CB_OnMouseMove() {
      //console.log( 'DgdSlider.wrapper.onmousemove' );
      self.activity = true;
    }
    this.clickHandler = function CB_OnClick() {
      //console.log( 'DgdSlider.wrapper.onclick' );
      return !self.activity;
    }
    this.IsLife = false;
  },

  free: function( deleteDom ) {
    deleteDom = xDefBool( deleteDom, false );
    // remove this.slider from sliderList
    xArrRemove(
      DgdSlider.prototype.sliderList,
      function CB_Compare_Slider(slider){
        return slider == this;
      },
      this
    );
    // if last slider is removed, cancel animation
    if (DgdSlider.prototype.sliderList.length == 0) {
      cancelAnimationFrame( DgdSlider.prototype.animationFrameId );
      DgdSlider.prototype.animationFrameId = null;
    }
    this.removeListeners();
    if (deleteDom) {
      var domObj = this.wrapper;
      if (domObj) xRemoveChild( xParent( domObj ), domObj );
    }
    this.IsLife = false;
  },

  updateLayout: function() {
    this.documentResizeHandler();
  },

  getOption: function(name, defaultValue) {
    return xDefAnyOrNull( this.options[name], defaultValue );
  },

  setup: function(reset) {
    if (!reset && (this.isSetup || !xIsDisplayed(this.wrapper))) return false;
    this.setWrapperOffset();
    this.setBoundsPadding();
    this.setBounds();
    this.isSetup = true;
    return true;
  },

  setWrapperOffset: function() {
    this.offset.wrapper = DgdPosition.get(this.wrapper);
  },

  setBoundsPadding: function() {
    if(!this.bounds.left && !this.bounds.right) {
      this.bounds.left = DgdPosition.get(this.handle)[0] - this.offset.wrapper[0];
      this.bounds.right = -this.bounds.left;
    }
    if(!this.bounds.top && !this.bounds.bottom) {
      this.bounds.top = DgdPosition.get(this.handle)[1] - this.offset.wrapper[1];
      this.bounds.bottom = -this.bounds.top;
    }
  },

  setBounds: function() {
    this.bounds.x0 = this.bounds.left;
    this.bounds.x1 = this.wrapper.offsetWidth + this.bounds.right;
    this.bounds.xRange = (this.bounds.x1 - this.bounds.x0) - this.handle.offsetWidth;

    this.bounds.y0 = this.bounds.top;
    this.bounds.y1 = this.wrapper.offsetHeight + this.bounds.bottom;
    this.bounds.yRange = (this.bounds.y1 - this.bounds.y0) - this.handle.offsetHeight;

    this.bounds.xStep = 1 / (this.xPrecision || Math.max(this.wrapper.offsetWidth, this.handle.offsetWidth));
    this.bounds.yStep = 1 / (this.yPrecision || Math.max(this.wrapper.offsetHeight, this.handle.offsetHeight));
  },

  addListeners: function() {
    this.wrapper.addEventListener( 'selectstart', this.selectstartHandler );
    this.wrapper.addEventListener( 'mousedown',   this.wrapperMousedownHandler );
    this.handle.addEventListener(  'mousedown',   this.mousedownHandler );
    document.addEventListener(     'mouseup',     this.mouseupHandler );
    this.wrapper.addEventListener( 'touchstart',  this.wrapperMousedownHandler );
    this.handle.addEventListener(  'touchstart',  this.mousedownHandler );
    document.addEventListener(     'touchend',    this.touchendHandler );
    this.wrapper.addEventListener( 'mousemove',   this.mousemoveHandler );
    this.wrapper.addEventListener( 'click',       this.clickHandler );

    this.handle.addEventListener(  'contextmenu', this.contextmenuHandler );
    this.wrapper.addEventListener( 'contextmenu', this.contextmenuHandler );
    if (this.autoUpdateLayout) {
      window.addEventListener(     'resize',      this.windowResizeHandler );
      xAddEventLayoutChange( this.windowResizeHandler );
    }
    // start animation frames if first slider is created
    if (DgdSlider.prototype.sliderList.length == 1) {
      this.startAnimation();
    }
  },

  removeListeners: function() {
    this.wrapper.removeEventListener( 'selectstart', this.selectstartHandler );
    this.wrapper.removeEventListener( 'mousedown',   this.wrapperMousedownHandler );
    this.handle.removeEventListener(  'mousedown',   this.mousedownHandler );
    document.removeEventListener(     'mouseup',     this.mouseupHandler );
    this.wrapper.removeEventListener( 'touchstart',  this.wrapperMousedownHandler );
    this.handle.removeEventListener(  'touchstart',  this.mousedownHandler );
    document.removeEventListener(     'touchend',    this.touchendHandler );
    this.wrapper.removeEventListener( 'mousemove',   this.mousemoveHandler );
    this.wrapper.removeEventListener( 'click',       this.clickHandler );

    this.handle.removeEventListener(  'contextmenu', this.contextmenuHandler );
    this.wrapper.removeEventListener( 'contextmenu', this.contextmenuHandler );
    if (this.autoUpdateLayout) {
      window.removeEventListener(     'resize',      this.windowResizeHandler );
      xRemoveEventLayoutChange( this.windowResizeHandler );
    }
  },

  handleDownHandler: function(e) {
    this.activity = false;
    DgdCursor.refresh(e);

    this.preventDefaults(e, true);
    this.startDrag();
    this.cancelEvent(e);
  },

  wrapperDownHandler: function(e) {
    DgdCursor.refresh(e);
    this.preventDefaults(e, true);
    this.startTap();
  },

  documentUpHandler: function(e) {
    this.stopDrag();
    this.stopTap();
    //this.cancelEvent(e);
  },

  documentResizeHandler: function() {
    if (!this.setup(true)) return;
    this.update();
  },

  enable: function() {
    this.disabled = false;
    xRemoveClass( this.handle, 'Disabled' );
  },

  disable: function() {
    this.disabled = true;
    xAddClass( this.handle, 'Disabled' );
  },

  readonly: function() {
    this.disabled = true;
  },

  setStep: function(x, y, snap) {
    this.setValue(
      this.steps && x > 1 ? (x - 1) / (this.steps - 1) : 0,
      this.steps && y > 1 ? (y - 1) / (this.steps - 1) : 0,
      snap
    );
  },

  setValue: function(x, y, snap) {
    this.setTargetValue([x, y || 0]);
    if(snap) {
      this.groupCopy(this.value.current, this.value.target);
      this.update(); // wabis
    }
  },

  startTap: function(target) {
    if(this.disabled) {
      return;
    }
    this.tapping = true;

    if(target === undefined) {
      target = [
        DgdCursor.x - this.offset.wrapper[0] - (this.handle.offsetWidth / 2),
        DgdCursor.y - this.offset.wrapper[1] - (this.handle.offsetHeight / 2)
      ];
    }
    this.setTargetOffset(target);
  },

  stopTap: function() {
    if(this.disabled || !this.tapping) {
      return;
    }
    this.tapping = false;

    this.setTargetValue(this.value.current);
    this.result();
  },

  startDrag: function() {
    if(this.disabled) {
      return;
    }
    this.offset.mouse = [
      DgdCursor.x - DgdPosition.get(this.handle)[0],
      DgdCursor.y - DgdPosition.get(this.handle)[1]
    ];

    this.dragging = true;
  },

  stopDrag: function() {
    if(this.disabled || !this.dragging) {
      return;
    }
    this.dragging = false;

    var target = this.groupClone(this.value.current);
    if(this.slide) {
      var ratioChange = this.change;
      target[0] += ratioChange[0] * 4;
      target[1] += ratioChange[1] * 4;
    }
    this.setTargetValue(target);
    this.result();
  },

  feedback: function() {
    var value = this.value.current;
    if(this.snap && this.steps > 1) {
      value = this.getClosestSteps(value);
    }
    if(!this.groupCompare(value, this.value.prev)) {
      if(xFunc(this.animationCallback)) {
        //console.log( 'DgdSlider.animationCallback' );
        this.animationCallback(value[0], value[1]);
      }
      this.groupCopy(this.value.prev, value);
    }
  },

  result: function() {
    if(xFunc(this.callback)) {
      //console.log( 'DgdSlider.callback' );
      this.callback(this.value.target[0], this.value.target[1]);
    }
  },

  startAnimation: function() {
    DgdSlider.prototype.animationFrameId = requestAnimationFrame( DgdSlider.prototype.onAnimationFrame );
    this.animate(false, true);
  },

  onAnimationFrame: function( time ) {
    if (DgdSlider.prototype.sliderList.length > 0) {
      DgdSlider.prototype.animationFrameId = requestAnimationFrame( DgdSlider.prototype.onAnimationFrame );
      DgdSlider.prototype.animateAll();
    } else {
      DgdSlider.prototype.animationFrameId = null;
    }
  },

  animateAll: function() {
    var sl = DgdSlider.prototype.sliderList;
    var last = sl.length;
    for (var i = 0; i < last; i++) {
      if (sl[i].IsLife) sl[i].animate();
    }
  },

  animate: function(direct, first) {
    //console.log( 'DgdSlider.animate' );
    if(direct && !this.dragging) {
      return;
    }
    if(this.dragging) {
      var prevTarget = this.groupClone(this.value.target);

      var offset = [
        DgdCursor.x - this.offset.wrapper[0] - this.offset.mouse[0],
        DgdCursor.y - this.offset.wrapper[1] - this.offset.mouse[1]
      ];
      this.setTargetOffset(offset, this.loose);

      this.change = [
        this.value.target[0] - prevTarget[0],
        this.value.target[1] - prevTarget[1]
      ];
    }
    if(this.dragging || first) {
      this.groupCopy(this.value.current, this.value.target);
    }
    if(this.dragging || this.glide() || first) {
      this.update();
      this.feedback();
    }
  },

  glide: function() {
    var diff = [
      this.value.target[0] - this.value.current[0],
      this.value.target[1] - this.value.current[1]
    ];
    if(!diff[0] && !diff[1]) {
      return false;
    }
    if(Math.abs(diff[0]) > this.bounds.xStep || Math.abs(diff[1]) > this.bounds.yStep) {
      this.value.current[0] += diff[0] * this.speed;
      this.value.current[1] += diff[1] * this.speed;
    } else {
      this.groupCopy(this.value.current, this.value.target);
    }
    return true;
  },

  update: function() {
    if(!this.snap) {
      this.offset.current = this.getOffsetsByRatios(this.value.current);
    } else {
      this.offset.current = this.getOffsetsByRatios(
        this.getClosestSteps(this.value.current)
      );
    }
    this.show();
  },

  show: function() {
    //console.log( 'DgdSlider.show' );
    if(!this.groupCompare(this.offset.current, this.offset.prev)) {
      if(this.horizontal) {
        this.handle.style.left = String(this.offset.current[0]) + 'px';
      }
      if(this.vertical) {
        this.handle.style.top = String(this.offset.current[1]) + 'px';
      }
      this.groupCopy(this.offset.prev, this.offset.current);
    }
  },

  setTargetValue: function(value, loose) {
    var target = loose ? this.getLooseValue(value) : this.getProperValue(value);

    this.groupCopy(this.value.target, target);
    this.offset.target = this.getOffsetsByRatios(target);
  },

  setTargetOffset: function(offset, loose) {
    var value = this.getRatiosByOffsets(offset);
    var target = loose ? this.getLooseValue(value) : this.getProperValue(value);

    this.groupCopy(this.value.target, target);
    this.offset.target = this.getOffsetsByRatios(target);
  },

  getLooseValue: function(value) {
    var proper = this.getProperValue(value);
    return [
      proper[0] + ((value[0] - proper[0]) / 4),
      proper[1] + ((value[1] - proper[1]) / 4)
    ];
  },

  getProperValue: function(value) {
    var proper = this.groupClone(value);

    proper[0] = Math.max(proper[0], 0);
    proper[1] = Math.max(proper[1], 0);
    proper[0] = Math.min(proper[0], 1);
    proper[1] = Math.min(proper[1], 1);

    if((!this.dragging && !this.tapping) || this.snap) {
      if(this.steps > 1) {
        proper = this.getClosestSteps(proper);
      }
    }
    return proper;
  },

  getRatiosByOffsets: function(group) {
    return [
      this.getRatioByOffset(group[0], this.bounds.xRange, this.bounds.x0),
      this.getRatioByOffset(group[1], this.bounds.yRange, this.bounds.y0)
    ];
  },

  getRatioByOffset: function(offset, range, padding) {
    return range ? (offset - padding) / range : 0;
  },

  getOffsetsByRatios: function(group) {
    return [
      this.getOffsetByRatio(group[0], this.bounds.xRange, this.bounds.x0),
      this.getOffsetByRatio(group[1], this.bounds.yRange, this.bounds.y0)
    ];
  },

  getOffsetByRatio: function(ratio, range, padding) {
    return Math.round(ratio * range) + padding;
  },

  getClosestSteps: function(group) {
    return [
      this.getClosestStep(group[0]),
      this.getClosestStep(group[1])
    ];
  },

  getClosestStep: function(value) {
    if (this.steps <= 1) return value;
    var n = this.steps - 1;
    return Math.round( value * n ) / n;
  },

  groupCompare: function(a, b) {
    return a[0] == b[0] && a[1] == b[1];
  },

  groupCopy: function(a, b) {
    a[0] = b[0];
    a[1] = b[1];
  },

  groupClone: function(a) {
    return [a[0], a[1]];
  },

  preventDefaults: function(e, selection) {
    if(!e) {
      e = window.event;
    }
    if(e.preventDefault) {
      e.preventDefault();
    }
    e.returnValue = false;

    if(selection && document.selection) {
      document.selection.empty();
    }
  },

  cancelEvent: function(e) {
    if(!e) {
      e = window.event;
    }
    if(e.stopPropagation) {
      e.stopPropagation();
    }
    e.cancelBubble = true;
  }
};


More Page Infos / Sitemap
Created Sonntag, 7. August 2016
Scroll to Top of Page
Changed Sonntag, 8. Oktober 2017