
$.fn.dragInput = function(cfg){
  return this.each(function(){

    this.repeating = false;
    // Apply specified options or defaults:
    // (Ought to refactor this some day to use $.extend() instead)
    this.dragCfg = {
      min: cfg && !isNaN(parseFloat(cfg.min)) ? Number(cfg.min) : null, // Fixes bug with min:0
      max: cfg && !isNaN(parseFloat(cfg.max)) ? Number(cfg.max) : null,
      step: cfg && Number(cfg.step) ? cfg.step : 1,
      stepfunc: cfg && cfg.stepfunc ? cfg.stepfunc : false,
      dragAdjust: cfg && cfg.dragAdjust ? cfg.dragAdjust : 1,
      height: 70,
      cursor: cfg && cfg.cursor ? Boolean(cfg.cursor) : false,
      start: cfg && cfg.start ? Number(cfg.start) : 0,
      _btn_width: 20,
      _direction: null,
      _delay: null,
      _repeat: null,
      callback: cfg && cfg.callback ? cfg.callback : null
    };
    // if a smallStep isn't supplied, use half the regular step
    this.dragCfg.smallStep = cfg && cfg.smallStep ? cfg.smallStep : this.dragCfg.step/2;
    var dragAdjust = this.dragCfg.dragAdjust;
    var $label = $(this).parent();
    var $input = $(this);
    var cursorHeight = this.dragCfg.height;
    var min = this.dragCfg.min;
    var max = this.dragCfg.max
    var step = this.dragCfg.step
    var area = (max - min > 0) ?  (max - min) / step : 200;
    var scale = area/cursorHeight * step;
    var lastY = 0;
    var attr = this.getAttribute("data-attr");
    var canvas = methodDraw.canvas
    var isTouch = svgedit.browser.isTouch();
    var completed = true //for mousewheel
    var $cursor = (area && this.dragCfg.cursor)
      ? $("<div class='draginput_cursor' />").appendTo($label)
      : false
    $input.attr("readonly", "readonly")
    if ($cursor && !isNaN(this.dragCfg.start)) $cursor.css("top", (this.dragCfg.start*-1)/scale+cursorHeight)

    //this is where all the magic happens
    this.adjustValue = function(i, completed){
      var v;
      i = parseFloat(i);
      if(isNaN(this.value)) {
        v = this.dragCfg.reset;
      } else if($.isFunction(this.dragCfg.stepfunc)) {
        v = this.dragCfg.stepfunc(this, i);
      } else {
        v = Number((Number(this.value) + Number(i)).toFixed(5));
      }
      if (max !== null) v = Math.min(v, max);
      if (min !== null) v = Math.max(v, min);
      if ($cursor) this.updateCursor(v);
      this.value = v;
      $label.attr("data-value", v)
      if ($.isFunction(this.dragCfg.callback)) this.dragCfg.callback(this, completed)
    };

    $label.toggleClass("draginput", $label.is("label"))

    // when the mouse is down and moving
    this.move = function(e, oy, val) {
      if (isTouch) {
        e = e.originalEvent.touches[0]
      }
      // just got started let's save for undo purposes
      if (lastY === 0) {
        lastY = oy;
      }
      var deltaY = (e.pageY - lastY) *-1
      lastY = e.pageY;
      val = (deltaY * scale) * dragAdjust
      var fixed = (step < 1) ? 1 : 0
      this.adjustValue(val.toFixed(fixed))  //no undo true
    };

    //when the mouse is released
    this.stop = function() {
      var selectedElems = canvas.getSelectedElems();
      $('body').removeClass('dragging');
      $label.removeClass("active");
      completed = true;
      if (isTouch) {
          $(window).unbind("touchmove.draginput touchend.draginput");
      }else{
          $(window).unbind("mousemove.draginput mouseup.draginput");
      }
      //
      lastY = 0;
      if (selectedElems[0]) {
        var batchCmd = canvas.undoMgr.finishUndoableChange();
        if (!batchCmd.isEmpty()) canvas.undoMgr.addCommandToHistory(batchCmd);
      }
      this.adjustValue(0, completed)
    }

    this.updateCursor = function(){
      var value = parseFloat(this.value);
      var pos = (value*-1)/scale+cursorHeight;
      $cursor.css("top", pos);
    }

    this.launch = function(e) {
      var selectedElems = canvas.getSelectedElems();
      if (isTouch) e = e.originalEvent.touches[0];
      var oy = e.pageY;
      var val = this.value;
      var el = this;
      canvas.undoMgr.beginUndoableChange(attr, selectedElems)
      $('body').addClass('dragging');

      $label.addClass('active');
      if (isTouch) {
          $(window).bind("touchmove.draginput", function(e){ el.move(e, oy, parseFloat(val)); })
          $(window).bind("touchend.draginput", function(e){el.focus();  el.stop(); })
      }else{
          $(window).bind("mousemove.draginput", function(e){ el.move(e, oy, parseFloat(val)); })
          $(window).bind("mouseup.draginput", function(e){el.focus();   el.stop();
          })
      }

    }
// .attr("readonly", "readonly")
    $(this)
      .attr("data-scale", scale)
      .attr("data-domain", cursorHeight)
      .attr("data-cursor", ($cursor != false))

    .bind("mousedown touchstart", function(e){ //touchstart
    //  e.preventDefault(); e.stopPropagation();
      this.blur();
      this.launch(e);
     // e.preventDefault();
    })

    .bind("dblclick taphold", function(e) {
      this.removeAttribute("readonly", "readonly");
      this.focus();
      this.select();
    })

    .keydown(function(e){
      // Respond to up/down arrow keys.
      switch(e.keyCode){
        case 13: this.adjustValue(0); this.blur();  break; // Enter
      }
    })

    .focus(function(e){
      if (this.getAttribute("readonly") === "readonly") this.blur()
    })

    .blur(function(e){
      this.setAttribute("readonly", "readonly")
    })

    .bind("mousewheel", function(e, delta, deltaX, deltaY){
      var selectedElems = canvas.getSelectedElems();
      if (completed) canvas.undoMgr.beginUndoableChange(attr, selectedElems)
      completed = false
      clearTimeout(window.undoTimeout)
      window.undoTimeout = setTimeout(function(){
        wheel_input.stop()
      },200)

      var wheel_input = this;
      if (deltaY > 0)
        this.adjustValue(this.dragCfg.step);
      else if (deltaY < 0)
        this.adjustValue(-this.dragCfg.step);
      e.preventDefault();

    })

  });

};

// public function
$.fn.dragInput.updateCursor = function(el){
  var value = parseFloat(el.value);
  var scale = parseFloat(el.getAttribute("data-scale"));
  var domain = parseFloat(el.getAttribute("data-domain"));
  var pos = ((value*-1)/scale+domain) + "px";
  var cursor = el.parentNode.lastChild
  if (cursor.className == "draginput_cursor") cursor.style.top = pos;
}

