text: 'use strict';
  
  var Shuffle = window.Shuffle;
  
  var Demo = function (element) {
    this.element = element;
    this.initShuffle();
    this.setupEvents();
  };
  
  // Column width and gutter width options can be functions
  Demo.prototype.initShuffle = function () {
    this.shuffle = new Shuffle(this.element, {
      itemSelector: '.box',
      speed: 250,
      easing: 'ease',
      columnWidth: function (containerWidth) {
        // .box's have a width of 18%
        return 0.18 * containerWidth;
      },
  
      gutterWidth: function (containerWidth) {
        // .box's have a margin-left of 2.5%
        return 0.025 * containerWidth;
      },
    });
  };
  
  Demo.prototype.setupEvents = function () {
    document.querySelector('#append').addEventListener('click', this.onAppendBoxes.bind(this));
    document.querySelector('#prepend').addEventListener('click', this.onPrependBoxes.bind(this));
    document.querySelector('#randomize').addEventListener('click', this.onRandomize.bind(this));
    document.querySelector('#remove').addEventListener('click', this.onRemoveClick.bind(this));
    document.querySelector('#sorter').addEventListener('change', this.onSortChange.bind(this));
    document.querySelector('#filterer').addEventListener('change', this.onFilterChange.bind(this));
    this.shuffle.element.addEventListener('click', this.onContainerClick.bind(this));
  
    // Show off some shuffle events
    this.shuffle.on(Shuffle.EventType.REMOVED, function (data) {
      console.log(data);
    });
  };
  
  /**
   * Generate random DOM elements.
   * @param {number} itemsToCreate Number of items to create.
   * @return {Array.<Element>} Array of elements.
   */
  Demo.prototype._generateBoxes = function (itemsToCreate) {
    // Creating random elements. You could use an ajax request or clone elements instead.
    var items = [];
    var modifierClasses = ['w2', 'h2', 'w3'];
    var i = 0;
  
    for (i = 0; i < itemsToCreate; i++) {
      var random = Math.random();
      var box = document.createElement('div');
      box.className = 'box';
      box.style.backgroundColor = this.getRandomColor();
      box.setAttribute('data-reviews', this.getRandomInt(1, 150));
  
      // Randomly add a class
      if (random > 0.8) {
        var randomClass = Math.floor(Math.random() * 3);
        box.className = box.className + ' ' + modifierClasses[randomClass];
      }
  
      items.push(box);
    }
  
    return items;
  };
  
  /**
   * Return an array of elements which have already been added to the DOM.
   * @return {Array.<Element>}
   */
  Demo.prototype._getArrayOfElementsToAdd = function () {
    return this._generateBoxes(5);
  };
  
  /**
   * Create an HTML string to insert. This could, for example, come from an XHR request.
   * @return {string} A mock HTML string.
   */
  Demo.prototype._getHtmlMarkupToAdd = function () {
    var fragment = document.createDocumentFragment();
    this._generateBoxes(5).forEach(function (item) {
      fragment.appendChild(item);
    });
  
    var dummy = document.createElement('div');
    dummy.appendChild(fragment);
    return dummy.innerHTML;
  };
  
  /**
   * Create some DOM elements, append them to the shuffle container, then notify
   * shuffle about the new items. You could also insert the HTML as a string.
   */
  Demo.prototype.onAppendBoxes = function () {
    var elements = this._getArrayOfElementsToAdd();
  
    elements.forEach(function (element) {
      this.shuffle.element.appendChild(element);
    }, this);
  
    // Tell shuffle items have been appended.
    // It expects an array of elements as the parameter.
    this.shuffle.add(elements);
  };
  
  /**
   * Show that you can prepend elements by inserting before other elements. You
   * can either insert a string like in this method or prepend real elements like
   * the `onAppendBoxes` method.
   */
  Demo.prototype.onPrependBoxes = function () {
    var markup = this._getHtmlMarkupToAdd();
  
    // Prepend HTML string.
    this.element.insertAdjacentHTML('afterbegin', markup);
  
    // Get the first 5 children of the container (we are inserting 5 items).
    var items = Array.prototype.slice.call(this.element.children, 0, 5);
  
    // Notify the instance.
    this.shuffle.add(items);
  };
  
  Demo.prototype.getRandomInt = function (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  };
  
  Demo.prototype.getRandomColor = function () {
    return '#' + Math.random().toString(16).slice(2, 8);
  };
  
  // Randomly choose some elements to remove
  Demo.prototype.onRemoveClick = function () {
    var total = this.shuffle.visibleItems;
  
    // None left
    if (!total) {
      return;
    }
  
    var numberToRemove = Math.min(3, total);
    var indiciesToRemove = [];
  
    // This has the possibility to choose the same index for more than
    // one in the array, meaning sometimes less than 3 will be removed
    for (var i = 0; i < numberToRemove; i++) {
      indiciesToRemove.push(this.getRandomInt(0, total - 1));
    }
  
    // Make an array of elements to remove.
    var collection = indiciesToRemove.map(function (index) {
      return this.shuffle.items[index].element;
    }, this);
  
    // Tell shuffle to remove them
    this.shuffle.remove(collection);
  };
  
  Demo.prototype.onRandomize = function () {
    var label = document.getElementById('sorter').querySelector('label.btn.active');
    if (label) {
      var radio = label.querySelector('input');
      radio.checked = false;
      label.classList.remove('active');
    }
  
    this.sortBy('random');
  };
  
  Demo.prototype.toggleActiveClasses = function (event) {
    // Add and remove `active` class from buttons.
    var buttons = Array.from(event.currentTarget.children);
    buttons.forEach(function (button) {
      if (button.querySelector('input').value === event.target.value) {
        button.classList.add('active');
      } else {
        button.classList.remove('active');
      }
    });
  }
  
  Demo.prototype.onSortChange = function (evt) {
    this.toggleActiveClasses(evt);
    this.sortBy(evt.target.value);
  };
  
  Demo.prototype.sortBy = function (value) {
    var sortOptions;
  
    if (value === 'most-reviews') {
      sortOptions = {
        reverse: true,
        by: this.getReviews,
      };
  
    } else if (value === 'least-reviews') {
      sortOptions = {
        by: this.getReviews,
      };
  
    } else if (value === 'random') {
      sortOptions = { randomize: true };
  
    } else {
      sortOptions = {};
    }
  
    // Filter elements
    this.shuffle.sort(sortOptions);
  };
  
  Demo.prototype.getReviews = function (element) {
    return parseInt(element.getAttribute('data-reviews'), 10);
  }
  
  Demo.prototype.onFilterChange = function (event) {
    this.toggleActiveClasses(event);
    this.filterBy(event.target.value);
  };
  
  Demo.prototype.filterBy = function (value) {
    var filterBy;
    var _this = this;
  
    if (value === 'none') {
      filterBy = Shuffle.ALL_ITEMS;
    } else if (value === 'odd-reviews') {
      filterBy = function (element) {
        return _this.getReviews(element) % 2 === 1;
      };
    } else {
      filterBy = function (element) {
        return _this.getReviews(element) % 2 === 0;
      };
    }
  
    this.shuffle.filter(filterBy);
  };
  
  /**
   * Remove a shuffle item when it's clicked.
   * @param {Object} event Event object.
   */
  Demo.prototype.onContainerClick = function (event) {
    // Bail in older browsers. https://caniuse.com/#feat=element-closest
    if (typeof event.target.closest !== 'function') {
      return;
    }
  
    var element = event.target.closest('.box');
    if (element !== null) {
      this.shuffle.remove([element]);
    }
  };
  
  document.addEventListener('DOMContentLoaded', function () {
    window.demo = new Demo(document.getElementById('my-shuffle'));
  });
  