/**
* @author Leandro Silva
* @copyright 2017 Leandro Silva (http://grafluxe.com)
* @license MIT
*
* @classdesc Create unique loading text, HTML sequences, and even sprite animations.
*/
//jshint esversion: 6, node: true, browser: true
class SequenceRunner {
/**
* Creates a new SequenceRunner instance.
* @throws {Error} Throws if you attempt to define a setting that does not exist.
* @param {Object} [settings] The sequence settings.
* @param {String} [settings.selector=".sequence-runner"] The HTML container to write your content to. It can be an id, class, tag, etc.
* @param {String|Array} [settings.content="."] The content to add to your selected HTML container(s). If set to a string, the
* content will be duplicated based on the amount set in the 'duplicate' settings
* property. If set to an array, the duplicate property will be auto set based on
* the length of the array.
* @param {Number} [settings.duplicate=3] The number of times to duplicate your content.
* @param {Number} [settings.delay=500] The delay between changes.
* @param {Number} [settings.loop=NaN] The amount of times to loop between changes. If set to 'NaN,' the loop will
* be infinite.
* @returns {SequenceRunner}
*/
constructor(settings) {
let defaults = {
selector: ".sequence-runner",
content: ".",
duplicate: 3,
delay: 500,
loop: NaN
};
for (let key in settings) {
if (!defaults.hasOwnProperty(key)) {
throw new Error("You're attempting to define a setting that does not exist.");
}
}
this._settings = Object.assign(defaults, settings);
this._elements = document.querySelectorAll(this._settings.selector);
if (this._settings.content instanceof Array) {
if (settings.duplicate) {
console.warn("The 'duplicate' property should not be used when 'content' is an array.");
}
this._isArray = true;
this._settings.duplicate = this._settings.content.length;
}
this.stop();
return this;
}
/**
* Gets the current settings.
* @type {Object}
*/
get settings() {
return this._settings;
}
/**
* Starts the sequence.
* @returns {SequenceRunner}
*/
start() {
this._currContent = "";
this._count = 0;
this._loop = 0;
this.stop();
this._onInterate();
this._intr = setInterval(this._onInterate.bind(this), this._settings.delay);
return this;
}
_onInterate() {
if (this._count >= this._settings.duplicate) {
this._count = 0;
if (!this._isArray) {
this._currContent = "";
}
}
if (this._isArray) {
this._currContent = this._settings.content[this._count];
} else {
this._currContent += this._settings.content;
}
this._elements.forEach(el => el.innerHTML = this._currContent);
if (this._onChangeFn) {
this._onChangeFn(this._currContent, this._count, this._loop);
}
if (this._settings.loop) {
if (this._count >= this._settings.duplicate - 1) {
this._loop++;
}
if(this._loop >= this._settings.loop) {
this.pause();
if (this._onCompleteFn) {
this._onCompleteFn(this._currContent, this._count, this._loop);
}
}
}
this._count++;
}
/**
* Pauses the sequence. This method clears the interval, but does not empty the HTML container(s).
* @returns {SequenceRunner}
*/
pause() {
clearInterval(this._intr);
return this;
}
/**
* Stops the sequence. This method clears the interval and empties the HTML container(s).
* @returns {SequenceRunner}
*/
stop() {
clearInterval(this._intr);
this._elements.forEach(el => el.innerHTML = "");
return this;
}
/**
* Calls your function on every change.
* @param {SequenceRunner~onChangeCallback} callback The callback function.
* @returns {SequenceRunner}
*/
onChange(callback) {
this._onChangeFn = callback;
return this;
}
/**
* The callback used by the 'onChange' method.
* @callback SequenceRunner~onChangeCallback
* @param {*} content The current content.
* @param {Number} count The current tick/count.
* @param {Number} loop The current loop.
*/
/**
* Calls your function at the end of the loop (assuming you set a loop).
* @param {SequenceRunner~onCompleteCallback} callback The callback function.
* @returns {SequenceRunner}
*/
onComplete(callback) {
this._onCompleteFn = callback;
return this;
}
/**
* The callback used by the 'onComplete' method.
* @callback SequenceRunner~onCompleteCallback
* @param {*} content The current content.
* @param {Number} count The current tick/count.
* @param {Number} loop The current loop.
*/
}
//Support CJS/Node
if (typeof module === "object" && module.exports) {
module.exports = SequenceRunner;
}