text: /*! rangeslider.js - v2.0.2 | (c) 2015 @andreruffert | MIT license | https://github.com/andreruffert/rangeslider.js */
  (function(factory) {
      'use strict';
  
      if (typeof define === 'function' && define.amd) {
          // AMD. Register as an anonymous module.
          define(['jquery'], factory);
      }
      else if (typeof exports === 'object') {
          // CommonJS
          factory(require('jquery'));
      } else {
          // Browser globals
          factory(jQuery);
      }
  }(function($) {
      'use strict';
  
      // Polyfill Number.isNaN(value)
      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
      Number.isNaN = Number.isNaN || function(value) {
          return typeof value === 'number' && value !== value;
      };
  
      /**
       * Range feature detection
       * @return {Boolean}
       */
      function supportsRange() {
          var input = document.createElement('input');
          input.setAttribute('type', 'range');
          return input.type !== 'text';
      }
  
      var pluginName = 'rangeslider',
          pluginIdentifier = 0,
          hasInputRangeSupport = supportsRange(),
          defaults = {
              polyfill: true,
              orientation: 'horizontal',
              rangeClass: 'rangeslider',
              disabledClass: 'rangeslider--disabled',
              horizontalClass: 'rangeslider--horizontal',
              verticalClass: 'rangeslider--vertical',
              fillClass: 'rangeslider__fill',
              handleClass: 'rangeslider__handle',
              startEvent: ['mousedown', 'touchstart', 'pointerdown'],
              moveEvent: ['mousemove', 'touchmove', 'pointermove'],
              endEvent: ['mouseup', 'touchend', 'pointerup']
          },
          constants = {
              orientation: {
                  horizontal: {
                      dimension: 'width',
                      direction: 'left',
                      directionStyle: 'left',
                      coordinate: 'x'
                  },
                  vertical: {
                      dimension: 'height',
                      direction: 'top',
                      directionStyle: 'bottom',
                      coordinate: 'y'
                  }
              }
          };
  
      /**
       * Delays a function for the given number of milliseconds, and then calls
       * it with the arguments supplied.
       *
       * @param  {Function} fn   [description]
       * @param  {Number}   wait [description]
       * @return {Function}
       */
      function delay(fn, wait) {
          var args = Array.prototype.slice.call(arguments, 2);
          return setTimeout(function(){ return fn.apply(null, args); }, wait);
      }
  
      /**
       * Returns a debounced function that will make sure the given
       * function is not triggered too much.
       *
       * @param  {Function} fn Function to debounce.
       * @param  {Number}   debounceDuration OPTIONAL. The amount of time in milliseconds for which we will debounce the function. (defaults to 100ms)
       * @return {Function}
       */
      function debounce(fn, debounceDuration) {
          debounceDuration = debounceDuration || 100;
          return function() {
              if (!fn.debouncing) {
                  var args = Array.prototype.slice.apply(arguments);
                  fn.lastReturnVal = fn.apply(window, args);
                  fn.debouncing = true;
              }
              clearTimeout(fn.debounceTimeout);
              fn.debounceTimeout = setTimeout(function(){
                  fn.debouncing = false;
              }, debounceDuration);
              return fn.lastReturnVal;
          };
      }
  
      /**
       * Check if a `element` is visible in the DOM
       *
       * @param  {Element}  element
       * @return {Boolean}
       */
      function isHidden(element) {
          return (
              element && (
                  element.offsetWidth === 0 ||
                  element.offsetHeight === 0 ||
                  // Also Consider native `<details>` elements.
                  element.open === false
              )
          );
      }
  
      /**
       * Get hidden parentNodes of an `element`
       *
       * @param  {Element} element
       * @return {[type]}
       */
      function getHiddenParentNodes(element) {
          var parents = [],
              node    = element.parentNode;
  
          while (isHidden(node)) {
              parents.push(node);
              node = node.parentNode;
          }
          return parents;
      }
  
      /**
       * Returns dimensions for an element even if it is not visible in the DOM.
       *
       * @param  {Element} element
       * @param  {String}  key     (e.g. offsetWidth …)
       * @return {Number}
       */
      function getDimension(element, key) {
          var hiddenParentNodes       = getHiddenParentNodes(element),
              hiddenParentNodesLength = hiddenParentNodes.length,
              inlineStyle             = [],
              dimension               = element[key];
  
          // Used for native `<details>` elements
          function toggleOpenProperty(element) {
              if (typeof element.open !== 'undefined') {
                  element.open = (element.open) ? false : true;
              }
          }
  
          if (hiddenParentNodesLength) {
              for (var i = 0; i < hiddenParentNodesLength; i++) {
  
                  // Cache style attribute to restore it later.
                  inlineStyle[i] = hiddenParentNodes[i].style.cssText;
  
                  // visually hide
                  hiddenParentNodes[i].style.display = 'block';
                  hiddenParentNodes[i].style.height = '0';
                  hiddenParentNodes[i].style.overflow = 'hidden';
                  hiddenParentNodes[i].style.visibility = 'hidden';
                  toggleOpenProperty(hiddenParentNodes[i]);
              }
  
              // Update dimension
              dimension = element[key];
  
              for (var j = 0; j < hiddenParentNodesLength; j++) {
  
                  // Restore the style attribute
                  hiddenParentNodes[j].style.cssText = inlineStyle[j];
                  toggleOpenProperty(hiddenParentNodes[j]);
              }
          }
          return dimension;
      }
  
      /**
       * Returns the parsed float or the default if it failed.
       *
       * @param  {String}  str
       * @param  {Number}  defaultValue
       * @return {Number}
       */
      function tryParseFloat(str, defaultValue) {
          var value = parseFloat(str);
          return Number.isNaN(value) ? defaultValue : value;
      }
  
      /**
       * Capitalize the first letter of string
       *
       * @param  {String} str
       * @return {String}
       */
      function ucfirst(str) {
          return str.charAt(0).toUpperCase() + str.substr(1);
      }
  
      /**
       * Plugin
       * @param {String} element
       * @param {Object} options
       */
      function Plugin(element, options) {
          this.$window            = $(window);
          this.$document          = $(document);
          this.$element           = $(element);
          this.options            = $.extend( {}, defaults, options );
          this.polyfill           = this.options.polyfill;
          this.orientation        = this.$element[0].getAttribute('data-orientation') || this.options.orientation;
          this.onInit             = this.options.onInit;
          this.onSlide            = this.options.onSlide;
          this.onSlideEnd         = this.options.onSlideEnd;
          this.DIMENSION          = constants.orientation[this.orientation].dimension;
          this.DIRECTION          = constants.orientation[this.orientation].direction;
          this.DIRECTION_STYLE    = constants.orientation[this.orientation].directionStyle;
          this.COORDINATE         = constants.orientation[this.orientation].coordinate;
  
          // Plugin should only be used as a polyfill
          if (this.polyfill) {
              // Input range support?
              if (hasInputRangeSupport) { return false; }
          }
  
          this.identifier = 'js-' + pluginName + '-' +(pluginIdentifier++);
          this.startEvent = this.options.startEvent.join('.' + this.identifier + ' ') + '.' + this.identifier;
          this.moveEvent  = this.options.moveEvent.join('.' + this.identifier + ' ') + '.' + this.identifier;
          this.endEvent   = this.options.endEvent.join('.' + this.identifier + ' ') + '.' + this.identifier;
          this.toFixed    = (this.step + '').replace('.', '').length - 1;
          this.$fill      = $('<div class="' + this.options.fillClass + '" />');
          this.$handle    = $('<div class="' + this.options.handleClass + '" />');
          this.$range     = $('<div class="' + this.options.rangeClass + ' ' + this.options[this.orientation + 'Class'] + '" id="' + this.identifier + '" />').insertAfter(this.$element).prepend(this.$fill, this.$handle);
  
          // visually hide the input
          this.$element.css({
              'position': 'absolute',
              'width': '1px',
              'height': '1px',
              'overflow': 'hidden',
              'opacity': '0'
          });
  
          // Store context
          this.handleDown = $.proxy(this.handleDown, this);
          this.handleMove = $.proxy(this.handleMove, this);
          this.handleEnd  = $.proxy(this.handleEnd, this);
  
          this.init();
  
          // Attach Events
          var _this = this;
          this.$window.on('resize.' + this.identifier, debounce(function() {
              // Simulate resizeEnd event.
              delay(function() { _this.update(); }, 300);
          }, 20));
  
          this.$document.on(this.startEvent, '#' + this.identifier + ':not(.' + this.options.disabledClass + ')', this.handleDown);
  
          // Listen to programmatic value changes
          this.$element.on('change.' + this.identifier, function(e, data) {
              if (data && data.origin === _this.identifier) {
                  return;
              }
  
              var value = e.target.value,
                  pos = _this.getPositionFromValue(value);
              _this.setPosition(pos);
          });
      }
  
      Plugin.prototype.init = function() {
          this.update(true, false);
  
          // Set initial value just in case it is not set already.
          // Prevents trouble if we call `update(true)`
          this.$element[0].value = this.value;
  
          if (this.onInit && typeof this.onInit === 'function') {
              this.onInit();
          }
      };
  
      Plugin.prototype.update = function(updateAttributes, triggerSlide) {
          updateAttributes = updateAttributes || false;
  
          if (updateAttributes) {
              this.min    = tryParseFloat(this.$element[0].getAttribute('min'), 0);
              this.max    = tryParseFloat(this.$element[0].getAttribute('max'), 100);
              this.value  = tryParseFloat(this.$element[0].value, this.min + (this.max-this.min)/2);
              this.step   = tryParseFloat(this.$element[0].getAttribute('step'), 1);
          }
  
          this.handleDimension    = getDimension(this.$handle[0], 'offset' + ucfirst(this.DIMENSION));
          this.rangeDimension     = getDimension(this.$range[0], 'offset' + ucfirst(this.DIMENSION));
          this.maxHandlePos       = this.rangeDimension - this.handleDimension;
          this.grabPos            = this.handleDimension / 2;
          this.position           = this.getPositionFromValue(this.value);
  
          // Consider disabled state
          if (this.$element[0].disabled) {
              this.$range.addClass(this.options.disabledClass);
          } else {
              this.$range.removeClass(this.options.disabledClass);
          }
  
          this.setPosition(this.position, triggerSlide);
      };
  
      Plugin.prototype.handleDown = function(e) {
          e.preventDefault();
          this.$document.on(this.moveEvent, this.handleMove);
          this.$document.on(this.endEvent, this.handleEnd);
  
          // If we click on the handle don't set the new position
          if ((' ' + e.target.className + ' ').replace(/[\n\t]/g, ' ').indexOf(this.options.handleClass) > -1) {
              return;
          }
  
          var pos         = this.getRelativePosition(e),
              rangePos    = this.$range[0].getBoundingClientRect()[this.DIRECTION],
              handlePos   = this.getPositionFromNode(this.$handle[0]) - rangePos,
              setPos      = (this.orientation === 'vertical') ? (this.maxHandlePos - (pos - this.grabPos)) : (pos - this.grabPos);
  
          this.setPosition(setPos);
  
          if (pos >= handlePos && pos < handlePos + this.handleDimension) {
              this.grabPos = pos - handlePos;
          }
      };
  
      Plugin.prototype.handleMove = function(e) {
          e.preventDefault();
          var pos = this.getRelativePosition(e);
          var setPos = (this.orientation === 'vertical') ? (this.maxHandlePos - (pos - this.grabPos)) : (pos - this.grabPos);
          this.setPosition(setPos);
      };
  
      Plugin.prototype.handleEnd = function(e) {
          e.preventDefault();
          this.$document.off(this.moveEvent, this.handleMove);
          this.$document.off(this.endEvent, this.handleEnd);
  
          // Ok we're done fire the change event
          this.$element.trigger('change', { origin: this.identifier });
  
          if (this.onSlideEnd && typeof this.onSlideEnd === 'function') {
              this.onSlideEnd(this.position, this.value);
          }
      };
  
      Plugin.prototype.cap = function(pos, min, max) {
          if (pos < min) { return min; }
          if (pos > max) { return max; }
          return pos;
      };
  
      Plugin.prototype.setPosition = function(pos, triggerSlide) {
          var value, newPos;
  
          if (triggerSlide === undefined) {
              triggerSlide = true;
          }
  
          // Snapping steps
          value = this.getValueFromPosition(this.cap(pos, 0, this.maxHandlePos));
          newPos = this.getPositionFromValue(value);
  
          // Update ui
          this.$fill[0].style[this.DIMENSION] = (newPos + this.grabPos) + 'px';
          this.$handle[0].style[this.DIRECTION_STYLE] = newPos + 'px';
          this.setValue(value);
  
          // Update globals
          this.position = newPos;
          this.value = value;
  
          if (triggerSlide && this.onSlide && typeof this.onSlide === 'function') {
              this.onSlide(newPos, value);
          }
      };
  
      // Returns element position relative to the parent
      Plugin.prototype.getPositionFromNode = function(node) {
          var i = 0;
          while (node !== null) {
              i += node.offsetLeft;
              node = node.offsetParent;
          }
          return i;
      };
  
      Plugin.prototype.getRelativePosition = function(e) {
          // Get the offset DIRECTION relative to the viewport
          var ucCoordinate = ucfirst(this.COORDINATE),
              rangePos = this.$range[0].getBoundingClientRect()[this.DIRECTION],
              pageCoordinate = 0;
  
          if (typeof e['page' + ucCoordinate] !== 'undefined') {
              pageCoordinate = e['client' + ucCoordinate];
          }
          else if (typeof e.originalEvent['client' + ucCoordinate] !== 'undefined') {
              pageCoordinate = e.originalEvent['client' + ucCoordinate];
          }
          else if (e.originalEvent.touches && e.originalEvent.touches[0] && typeof e.originalEvent.touches[0]['client' + ucCoordinate] !== 'undefined') {
              pageCoordinate = e.originalEvent.touches[0]['client' + ucCoordinate];
          }
          else if(e.currentPoint && typeof e.currentPoint[this.COORDINATE] !== 'undefined') {
              pageCoordinate = e.currentPoint[this.COORDINATE];
          }
  
          return pageCoordinate - rangePos;
      };
  
      Plugin.prototype.getPositionFromValue = function(value) {
          var percentage, pos;
          percentage = (value - this.min)/(this.max - this.min);
          pos = (!Number.isNaN(percentage)) ? percentage * this.maxHandlePos : 0;
          return pos;
      };
  
      Plugin.prototype.getValueFromPosition = function(pos) {
          var percentage, value;
          percentage = ((pos) / (this.maxHandlePos || 1));
          value = this.step * Math.round(percentage * (this.max - this.min) / this.step) + this.min;
          return Number((value).toFixed(this.toFixed));
      };
  
      Plugin.prototype.setValue = function(value) {
          if (value === this.value) {
              return;
          }
  
          // Set the new value and fire the `input` event
          this.$element
              .val(value)
              .trigger('input', { origin: this.identifier });
      };
  
      Plugin.prototype.destroy = function() {
          this.$document.off('.' + this.identifier);
          this.$window.off('.' + this.identifier);
  
          this.$element
              .off('.' + this.identifier)
              .removeAttr('style')
              .removeData('plugin_' + pluginName);
  
          // Remove the generated markup
          if (this.$range && this.$range.length) {
              this.$range[0].parentNode.removeChild(this.$range[0]);
          }
      };
  
      // A really lightweight plugin wrapper around the constructor,
      // preventing against multiple instantiations
      $.fn[pluginName] = function(options) {
          var args = Array.prototype.slice.call(arguments, 1);
  
          return this.each(function() {
              var $this = $(this),
                  data  = $this.data('plugin_' + pluginName);
  
              // Create a new instance.
              if (!data) {
                  $this.data('plugin_' + pluginName, (data = new Plugin(this, options)));
              }
  
              // Make it possible to access methods from public.
              // e.g `$element.rangeslider('method');`
              if (typeof options === 'string') {
                  data[options].apply(data, args);
              }
          });
      };
  
  }));
  