Function.prototype.createDelegate = function(context, args, appendArguments) {
  var fn = this;

  if (!context) {
    context = window;
  }

  return function() {
    if (appendArguments) {
      args = Array.prototype.concat.apply(args, arguments);
    }

    return fn.apply(context, args);
  };
};

Function.prototype.createCallback = function(context) {
  return this.createDelegate(context, [], true);
};

Number.prototype.prettify = function(thousandsSeparator, decimalPoint) {
  thousandsSeparator = thousandsSeparator != null ? thousandsSeparator : ',';
  decimalPoint = decimalPoint != null ? decimalPoint : '.';

  var value = this.toString().split('.'),
      intPart = value[0],
      fractPart = value.length > 1 ? decimalPoint + value[1] : '';
  
  var re = /(\d+)(\d{3})/;

  while (re.test(intPart)) {
    intPart = intPart.replace(re, '$1' + thousandsSeparator + '$2');
  }

  return intPart + fractPart;
};

jQuery.fn.bindStepper = function(options) {
  var config = $.extend({
    minValue: 1,
    maxValue: 99
  }, options);

  var input = $(this),
      wrap = input.parent('.b-input-stepper'),
      up = wrap.children('.b-input-stepper-arrow.up'),
      down = wrap.children('.b-input-stepper-arrow.down'),
      mask = wrap.children('.b-input-stepper-mask');

  var getValue = function() {
    return parseInt(Number(input.val()));
  };

  var validateValue = function() {
    var val = getValue();

    if (val < config.minValue) {
      val = config.minValue;
    } else if (val > config.maxValue) {
      val = config.maxValue;
    }

    input.val(val);
  };

  var setValue = function(val) {
    input.val(val);

    validateValue();

    input.trigger('change');
  };

  var maskValue = function() {
    return $.isFunction(config.mask) ? config.mask(getValue()) : getValue();
  }

  var updateMaskValue = function() {
    mask.text(maskValue());
  };

  var stepUp = function() {
    setValue(getValue() + 1);
  };

  var stepDown = function() {
    setValue(getValue() - 1);
  };

  var handleControls = function(e) {
    var code = e.which;

    switch(code) {
      case 38: stepUp(); break;
      case 40: stepDown(); break;
      case 13: validateValue(); input.blur(); break;
    }
  };

  input.keydown(handleControls);

  input.focus(function(){
    mask.hide();
  });

  input.blur(function(){
    input.trigger('change');
    mask.show();
  });

  input.change(function(){
    updateMaskValue();
  });

  input.keypress(config.filter);

  up.click(function() {
    stepUp();
    return false;
  });

  down.click(function() {
    stepDown();
    return false;
  });

  setValue(config.value);

  updateMaskValue();

  return {

  };
};