ProgressIndicator

function
 ProgressIndicator() 

Option name Type Description
el Element
params Object

ProgressIndicator constructor.

var ProgressIndicator = function(el, params) {

  if (!el) {
    return;
  }

  this._setParams(this.defaults, true);
  this._cacheElements(el);
  this._setParams(params || {});
  this._bindEventListenerCallbacks();
  this._addEventListeners();
};

ProgressIndicator.prototype = {

_setParams

property
 _setParams 

Include common functionality.

_setParams: Base.setParams,
_toggleClass: Base.toggleClass,
_hasClass: Base.hasClass,
_round: Base.round,
remove: Base.remove,

_whitelistedParams

property
 _whitelistedParams 

Whitelisted parameters which can be set on construction.

_whitelistedParams: ['precision'],

defaults

property
 defaults 

Default values for internal properties we will be setting.
These are set on each construction so we don't leak properties
into the prototype chain.

defaults: {
  el: null,
  progressEl: null,
  statusEl: null,
  noteEls: null,
  meterEl: null,
  fillEl: null,
  meterHeight: 0,
  meterWidth: 0,
  notes: null,
  isDeterminate: false,
  value: null,
  precision: 0,
  lastDOMUpdateTime: 0,
  _onResizeBound: null
},

set

method
 set() 

Option name Type Description
val Number

Set the value of the indicator.

set: function(val) {

  if (val === this.value) {
    return;
  }

  if (val > 1) {
    val = 1;
  }

  this.value = val;

  if (this.isDeterminate && this.progressEl) {
    this.progressEl.setAttribute('value', Math.round(val * 100) / 100);
  }

  this._updateDOM();
},

_cacheElements

method
 _cacheElements() 

Option name Type Description
el Element

Store a reference to all the needed elements.

_cacheElements: function(el) {

  this.el = el;
  this.progressEl = this.el.querySelector('progress');
  this.statusEl = this.el.querySelector('.spark-progress__value-status, [role="status"]');
  this.noteEl = this.el.querySelector('.spark-progress__states');
  this.meterEl = this.el.querySelector('.spark-progress__meter');

  this.isDeterminate = this.progressEl.getAttribute('value') !== null;
  this.size = this._determineSize();

  // If this is a determinate value, replace the meter with the SVG.
  if (this.isDeterminate) {

    var svg = this._buildSVG();
    svg.setAttribute('class', this.meterEl.className);

    this.meterEl.parentNode.replaceChild(svg, this.meterEl);
    this.meterEl = svg;
    this.fillEl = this.meterEl.querySelector('.spark-progress__fill');
  }

  if (this.noteEl) {
    this._parseNotes(this.noteEl);
  }

  if (this.progressEl) {
    this.value = this.progressEl.value;
  }

  this._cacheSize();

  this._updateDOM();
},

_cacheSize

method
 _cacheSize() 

Cache the size of the meter.

_cacheSize: function() {
  this.meterHeight = this.meterEl.clientHeight;
  this.meterWidth = this.meterEl.clientWidth;
},

_determineSize

method
 _determineSize() 

Determine the size of the indicator.

_determineSize: function() {

  if (this.el.className.indexOf('progress--sm') !== -1) {
    return 'small';
  } else if (this.el.className.indexOf('progress--xs') !== -1) {
    return 'extraSmall';
  }

  return 'large';
},

_buildSVG

method
 _buildSVG() 

Build the proper SVG element for this size indicator.

_buildSVG: function() {
  var size = sizes[this.size];
  var template = '<svg viewBox="0 0 ' + size.diameter + ' ' + size.diameter + '" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="' + size.track + '" stroke-width="' + size.stroke + '" class="spark-progress__track"/><path d="' + (size.fill || size.track) + '" stroke-width="' + size.stroke + '" class="spark-progress__fill"/><path d="' + size.border + '" class="spark-progress__border"/></g></g></svg>';
  var div = document.createElement('div');
  div.innerHTML = template;
  return div.children[0];
},

_parseNotes

method
 _parseNotes() 

Option name Type Description
el Element
return Array

Take an unordered list of notes and determine the ranges for
when to show a given note.

_parseNotes: function(el) {

  this.notes = this.notes || [];

  var len = el.children.length;
  var i = len - 1;

  for (; i >= 0; i--) {
    this.notes.push({
      min: parseInt(el.children[i].getAttribute('data-value'), 10),
      max: el.children[i + 1] ? parseInt(el.children[i + 1].getAttribute('data-value'), 10) - 1 : 100,
      el: el.children[i]
    });
  }
},

_updateDOM

method
 _updateDOM() 

Update the text visible based on the value. Also adjust the SVG.

_updateDOM: function() {

  if (!this.isDeterminate) {
    return;
  }

  var updateTime = Date.now();
  var val = this._round(this.value * 100, this.precision);

  // Don't animate if we're animating back to 0 or it's been less than 150ms since our last update.
  var noAnimation = val === 0 || this.lastDOMUpdateTime + 150 > updateTime;
  this._toggleClass(this.fillEl, 'no-animation', noAnimation);

  this.statusEl.innerHTML = val + '%';

  var dashArray = (sizes[this.size].diameter - sizes[this.size].stroke) * Math.PI;
  var dashOffset = dashArray - (dashArray * (val / 100));

  this.fillEl.setAttribute('style', 'stroke-dasharray: ' + dashArray + '; stroke-dashoffset: ' + dashOffset);

  this.lastDOMUpdateTime = updateTime;

  if (!this.notes) {
    return;
  }

  var i = 0;
  var len = this.notes.length;

  for (; i < len; i++) {
    this._toggleClass(this.notes[i].el, 'active', (this.notes[i].min <= val && this.notes[i].max >= val));
  }
},

_bindEventListenerCallbacks

method
 _bindEventListenerCallbacks() 

Create bound versions of event listener callbacks and store them.
Otherwise we can't unbind from these events later because the
function signatures won't match.

_bindEventListenerCallbacks: function() {
  this._onResizeBound = this._onResize.bind(this);
},

_addEventListeners

method
 _addEventListeners() 

Add event listeners for DOM events.

_addEventListeners: function() {
  window.addEventListener('resize', this._onResizeBound);
},

_removeEventListeners

method
 _removeEventListeners() 

Remove event listeners for DOM events..

_removeEventListeners: function() {
  window.removeEventListener('resize', this._onResizeBound);
},

_onResize

method
 _onResize() 

Option name Type Description
e Object

When the window resizes, cache the dimensions.

_onResize: function() {
  this._cacheSize();
}
  };

  Base.exportjQuery(ProgressIndicator, 'ProgressIndicator');

  return ProgressIndicator;
}));