UNPKG

vue-glitched-writer

Version:

Vue.js Single File Component. Wrapper for Glitched Writer: text-writing js module.

1,649 lines 64.8 kB
'use strict';var vue=require('vue');function ownKeys(object, enumerableOnly) {
  var keys = Object.keys(object);

  if (Object.getOwnPropertySymbols) {
    var symbols = Object.getOwnPropertySymbols(object);

    if (enumerableOnly) {
      symbols = symbols.filter(function (sym) {
        return Object.getOwnPropertyDescriptor(object, sym).enumerable;
      });
    }

    keys.push.apply(keys, symbols);
  }

  return keys;
}

function _objectSpread2(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i] != null ? arguments[i] : {};

    if (i % 2) {
      ownKeys(Object(source), true).forEach(function (key) {
        _defineProperty(target, key, source[key]);
      });
    } else if (Object.getOwnPropertyDescriptors) {
      Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
    } else {
      ownKeys(Object(source)).forEach(function (key) {
        Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
      });
    }
  }

  return target;
}

function _typeof(obj) {
  "@babel/helpers - typeof";

  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    _typeof = function (obj) {
      return typeof obj;
    };
  } else {
    _typeof = function (obj) {
      return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
    };
  }

  return _typeof(obj);
}

function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }

  return obj;
}

function _slicedToArray(arr, i) {
  return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}

function _arrayWithHoles(arr) {
  if (Array.isArray(arr)) return arr;
}

function _iterableToArrayLimit(arr, i) {
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];

  if (_i == null) return;
  var _arr = [];
  var _n = true;
  var _d = false;

  var _s, _e;

  try {
    for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
      _arr.push(_s.value);

      if (i && _arr.length === i) break;
    }
  } catch (err) {
    _d = true;
    _e = err;
  } finally {
    try {
      if (!_n && _i["return"] != null) _i["return"]();
    } finally {
      if (_d) throw _e;
    }
  }

  return _arr;
}

function _unsupportedIterableToArray(o, minLen) {
  if (!o) return;
  if (typeof o === "string") return _arrayLikeToArray(o, minLen);
  var n = Object.prototype.toString.call(o).slice(8, -1);
  if (n === "Object" && o.constructor) n = o.constructor.name;
  if (n === "Map" || n === "Set") return Array.from(o);
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}

function _arrayLikeToArray(arr, len) {
  if (len == null || len > arr.length) len = arr.length;

  for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];

  return arr2;
}

function _nonIterableRest() {
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

function createCommonjsModule(fn, basedir, module) {
	return module = {
	  path: basedir,
	  exports: {},
	  require: function (path, base) {
      return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
    }
	}, fn(module, module.exports), module.exports;
}

function commonjsRequire () {
	throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
}var utils = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.trim = exports.filterHtml = exports.htmlToArray = exports.wordsRgx = exports.isSpecialChar = exports.stringToLetterItems = exports.letterToLetterItem = exports.coinFlip = exports.getRandomFromRange = exports.animateWithClass = exports.isInRange = exports.arrayOfTheSame = exports.promiseWhile = exports.wait = exports.parseCharset = exports.filterDuplicates = exports.getRandom = exports.deleteRandom = exports.clamp = exports.random = void 0;
/* eslint-disable no-unused-vars */
function random(min, max, math) {
    const result = Math.random() * (max - min) + min;
    if (math) {
        // eslint-disable-next-line default-case
        switch (math) {
            case 'floor':
                return Math.floor(result);
            case 'round':
                return Math.round(result);
            case 'ceil':
                return Math.ceil(result);
        }
    }
    return result;
}
exports.random = random;
const clamp = (min, value, max) => Math.min(Math.max(value, min), max);
exports.clamp = clamp;
const deleteRandom = (array) => array.splice(random(0, array.length, 'floor'), 1).length > 0;
exports.deleteRandom = deleteRandom;
function getRandom(iterable) {
    return iterable[random(0, iterable.length, 'floor')];
}
exports.getRandom = getRandom;
function filterDuplicates(iterable) {
    const isString = typeof iterable === 'string', result = [];
    new Set(iterable).forEach(x => result.push(x));
    return isString ? result.join('') : result;
}
exports.filterDuplicates = filterDuplicates;
function parseCharset(input) {
    let result;
    // Charset is a string
    if (typeof input === 'string')
        result = input;
    // Charset is an array
    else if (input.length)
        result = input.join('');
    // Charset is a Set
    else
        result = Array.from(input).join('');
    return result;
}
exports.parseCharset = parseCharset;
const wait = (time) => new Promise(resolve => setTimeout(() => resolve(time), time));
exports.wait = wait;
function promiseWhile(conditionFunc, actionPromise) {
    const whilst = () => conditionFunc() ? actionPromise().then(whilst) : Promise.resolve();
    return whilst();
}
exports.promiseWhile = promiseWhile;
const arrayOfTheSame = (value, length) => new Array(length).fill(value);
exports.arrayOfTheSame = arrayOfTheSame;
const isInRange = (min, value, max) => value >= min && value < max;
exports.isInRange = isInRange;
const animateWithClass = (element, className) => {
    element.classList.remove(className);
    // eslint-disable-next-line no-void
    void element.offsetWidth;
    element.classList.add(className);
};
exports.animateWithClass = animateWithClass;
function getRandomFromRange(range, round = true) {
    return typeof range === 'number'
        ? range
        : random(...range, round ? 'round' : undefined);
}
exports.getRandomFromRange = getRandomFromRange;
const coinFlip = (p = 0.5) => Math.random() < p;
exports.coinFlip = coinFlip;
const letterToLetterItem = (string) => ({
    value: string,
});
exports.letterToLetterItem = letterToLetterItem;
const stringToLetterItems = (string) => [...string].map(exports.letterToLetterItem);
exports.stringToLetterItems = stringToLetterItems;
const isSpecialChar = (l) => ['\t', '\n', '\r', '\f', '\v', '', ' '].includes(l);
exports.isSpecialChar = isSpecialChar;
const findHTMLPattern = '(&(?:[a-z0-9]+|#[0-9]{1,6}|#x[0-9a-fA-F]{1,6});)|(<style.+?>.+?</style>|<script.+?>.+?</script>|<(?:!|/?[a-zA-Z]+).*?/?>)';
exports.wordsRgx = /(&(?:[a-z0-9]+|#[0-9]{1,6}|#x[0-9a-fA-F]{1,6});)|.\w+,*\.*"*\s?|\s+|\S+/gi;
function htmlToArray(string) {
    const reg = new RegExp(findHTMLPattern, 'gi'), resultArray = [];
    let find, lastIndex = 0;
    // eslint-disable-next-line no-cond-assign
    while ((find = reg.exec(string))) {
        const from = find.index, to = reg.lastIndex, stringBefore = string.slice(lastIndex, from);
        lastIndex = to;
        stringBefore && resultArray.push(...exports.stringToLetterItems(stringBefore));
        const result = {
            value: find[0],
            type: find[1] !== undefined ? 'html_entity' : 'tag',
        };
        resultArray.push(result);
    }
    string.length > lastIndex &&
        resultArray.push(...exports.stringToLetterItems(string.slice(lastIndex)));
    // return resultArray.map(l =>
    // 	l.type
    // 		? l
    // 		: {
    // 				value: l.value,
    // 				type: isSpecialChar(l.value) ? 'whitespace' : undefined,
    // 		  },
    // )
    return resultArray;
}
exports.htmlToArray = htmlToArray;
function filterHtml(string) {
    const reg = new RegExp(findHTMLPattern, 'g');
    return string.replace(reg, '');
}
exports.filterHtml = filterHtml;
function trim(str, l) {
    if (!l || l.length > 1 || !str)
        return str;
    if (l === ' ')
        return str.trim();
    const reg = new RegExp(`${l}+`, 'g');
    let find, result = str;
    // eslint-disable-next-line no-cond-assign
    while ((find = reg.exec(str))) {
        const from = find.index, to = reg.lastIndex, length = to - from;
        if (from === 0)
            result = result.substring(to);
        else if (to === str.length)
            result = result.substring(result.length - length);
    }
    return result;
}
exports.trim = trim;
});var presets = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.presets = exports.glyphs = void 0;
exports.glyphs = {
    nier: '一二三四五六七八九十百千上下左右中大小月日年早木林山川土空田天生花草虫犬人名女男子目耳口手足見音力気円入出立休先夕本文字学校村町森正水火玉王石竹糸貝車金雨赤青白数多少万半形太細広長点丸交光角計直線矢弱強高同親母父姉兄弟妹自友体毛頭顔首心時曜朝昼夜分週春夏秋冬今新古間方北南東西遠近前後内外場地国園谷野原里市京風雪雲池海岩星室戸家寺通門道話言答声聞語読書記紙画絵図工教晴思考知才理算作元食肉馬牛魚鳥羽鳴麦米茶色黄黒来行帰歩走止活店買売午汽弓回会組船明社切電毎合当台楽公引科歌刀番用何',
    full: 'ABCDĐEFGHIJKLMNOPQRSTUVWXYZabcdđefghijklmnopqrstuvwxyzĄąĆćŻżŹźŃńóŁłАБВГҐДЂЕЁЄЖЗЅИІЇЙЈКЛЉМНЊОПРСТЋУЎФХЦЧЏШЩЪЫЬЭЮЯабвгґдђеёєжзѕиіїйјклљмнњопрстћуўфхцчџшщъыьэюяΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωάΆέΈέΉίϊΐΊόΌύΰϋΎΫΏĂÂÊÔƠƯăâêôơư一二三四五六七八九十百千上下左右中大小月日年早木林山川土空田天生花草虫犬人名女男子目耳口手足見音力気円入出立休先夕本文字学校村町森正水火玉王石竹糸貝車金雨赤青白数多少万半形太細広長点丸交光角計直線矢弱強高同親母父姉兄弟妹自友体毛頭顔首心時曜朝昼夜分週春夏秋冬今新古間方北南東西遠近前後内外場地国園谷野原里市京風雪雲池海岩星室戸家寺通門道話言答声聞語読書記紙画絵図工教晴思考知才理算作元食肉馬牛魚鳥羽鳴麦米茶色黄黒来行帰歩走止活店買売午汽弓回会組船明社切電毎合当台楽公引科歌刀番用何ĂÂÊÔƠƯăâêôơư1234567890‘?’“!”(%)[#]{@}/\\&<-+÷×=>$€£¥¢:;,.*•°·…±†‡æ«»¦¯—–~˜¨_øÞ¿▬▭▮▯┐└╛░▒▓○‼⁇⁈⁉‽ℴℵℶℷℸℲ℮ℯ⅁⅂⅃⅄₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿          ',
    letterlike: 'ABCDĐEFGHIJKLMNOPQRSTUVWXYZabcdđefghijklmnopqrstuvwxyzĄąĆćŻżŹźŃńóŁłАБВГҐДЂЕЁЄЖЗЅИІЇЙЈКЛЉМНЊОПРСТЋУЎФХЦЧЏШЩЪЫЬЭЮЯабвгґдђеёєжзѕиіїйјклљмнњопрстћуўфхцчџшщъыьэюяΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωάΆέΈέΉίϊΐΊόΌύΰϋΎΫΏĂÂÊÔƠƯăâêôơưĂÂÊÔƠƯăâêôơư1234567890',
    numbers: '0123456789',
    zalgo: '̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ ͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡͏҉',
    neo: '!<>-_\\/[]{}—=+*^?#________',
    uppercase: '1234567890QWERTYUIOPASDFGHJKLZXCVBNM#$%',
};
exports.presets = {
    default: {
        steps: [1, 8],
        interval: [60, 170],
        delay: [0, 2000],
        changeChance: 0.6,
        ghostChance: 0.2,
        maxGhosts: 0.2,
        oneAtATime: 0,
        glyphs: exports.glyphs.full + exports.glyphs.zalgo,
        glyphsFromText: false,
        fillSpace: true,
        mode: 'matching',
        html: false,
        letterize: false,
        endless: false,
        fps: 60,
    },
    nier: {
        maxGhosts: 0,
        ghostChance: 0,
        changeChance: 0.8,
        steps: 3,
        interval: 10,
        delay: 0,
        oneAtATime: 1,
        glyphs: exports.glyphs.nier,
        fillSpace: false,
        glyphsFromText: true,
        mode: 'clear',
    },
    typewriter: {
        interval: [50, 150],
        delay: 0,
        steps: 0,
        changeChance: 0,
        maxGhosts: 0,
        oneAtATime: 1,
        glyphs: '',
        glyphsFromText: false,
        fillSpace: false,
        mode: 'erase_smart',
    },
    terminal: {
        interval: [25, 30],
        delay: [0, 0],
        steps: 0,
        changeChance: 0.5,
        maxGhosts: 0,
        oneAtATime: 1,
        glyphs: '',
        fillSpace: false,
        glyphsFromText: false,
        mode: 'clear',
    },
    zalgo: {
        delay: [0, 3000],
        interval: [10, 35],
        steps: [0, 30],
        maxGhosts: 4.6,
        changeChance: 0.5,
        ghostChance: 0.7,
        glyphs: exports.glyphs.zalgo,
        glyphsFromText: true,
        fillSpace: false,
    },
    neo: {
        interval: [30, 100],
        delay: [0, 1300],
        steps: [4, 7],
        maxGhosts: 0,
        ghostChance: 0,
        changeChance: 1,
        glyphs: exports.glyphs.neo,
        mode: 'normal',
    },
    encrypted: {
        interval: [50, 90],
        delay: [0, 1300],
        steps: [5, 8],
        maxGhosts: 0,
        ghostChance: 0,
        changeChance: 1,
        glyphs: exports.glyphs.uppercase,
        fillSpace: false,
        mode: 'normal',
    },
    bitbybit: {
        interval: [35, 65],
        delay: 180,
        steps: 1,
        maxGhosts: 1,
        ghostChance: 0.1,
        changeChance: 0.7,
        oneAtATime: 'word',
        glyphs: '',
        glyphsFromText: true,
        fillSpace: false,
        mode: 'erase',
    },
    cosmic: {
        steps: [0, 1],
        interval: 30,
        delay: [400, 2400],
        ghostChance: 0,
        changeChance: 0.3,
        maxGhosts: 0,
        glyphs: 'QWERTYUIOPASDFGHJKLZXCVBNM',
        glyphsFromText: false,
        fillSpace: true,
        mode: 'erase',
    },
};
});var options = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
// eslint-disable-next-line import/no-extraneous-dependencies


class Options {
    constructor(writer, options) {
        this.writer = writer;
        this.set(options);
    }
    set(options) {
        this.options = Object.assign(Object.assign({}, presets.presets.default), this.parseOptions(options));
        this.updateInternal();
    }
    extend(options) {
        this.options = Object.assign(Object.assign({}, this.options), this.parseOptions(options));
        this.updateInternal();
    }
    parseOptions(options) {
        var _a;
        if (!options)
            return {};
        if (typeof options === 'string')
            return (_a = presets.presets[options]) !== null && _a !== void 0 ? _a : {};
        return options;
    }
    updateInternal() {
        const { options } = this;
        this.glyphs = utils.parseCharset(options.glyphs);
        this.setCharset();
        this.space = options.fillSpace ? ' ' : '';
        if (Number.isInteger(options.oneAtATime))
            this.oneAtATime = options.oneAtATime;
        else if (options.oneAtATime === 'word')
            this.oneAtATime = 'word';
        else
            this.oneAtATime = options.oneAtATime ? 1 : 0;
    }
    setCharset() {
        const { writer } = this;
        let { glyphs } = this;
        if (this.glyphsFromText)
            glyphs += utils.filterDuplicates(writer.previousString +
                (this.html ? utils.filterHtml(writer.goalText) : writer.goalText));
        this.charset = [...glyphs].filter(l => !['\t', '\n', '\r', '\f', '\v'].includes(l));
        this.setMaxGhosts();
    }
    setMaxGhosts() {
        const { writer: { charTable }, options: { maxGhosts }, } = this;
        if (Number.isInteger(maxGhosts))
            this.maxGhosts = maxGhosts;
        const { length } = charTable.filter(char => char.specialType !== 'tag');
        this.maxGhosts = Math.round((length || 20) * maxGhosts);
    }
    getGlyph(char) {
        const { options } = this;
        return options.genGlyph
            ? options.genGlyph(char, this.baseGetGlyph)
            : this.baseGetGlyph();
    }
    baseGetGlyph() {
        var _a;
        return (_a = utils.getRandom(this.charset)) !== null && _a !== void 0 ? _a : '';
    }
    get steps() {
        return utils.getRandomFromRange(this.options.steps);
    }
    getInterval(char) {
        const { options, baseGetInterval } = this;
        return options.genInterval
            ? options.genInterval(char, baseGetInterval.bind(this, char))
            : baseGetInterval.call(this, char);
    }
    baseGetInterval(char) {
        let interval = utils.getRandomFromRange(this.options.interval);
        if (char.specialType === 'whitespace')
            interval /= 1.8;
        return interval;
    }
    getDelay(char) {
        const { options } = this;
        return options.genDelay
            ? options.genDelay(char, this.baseGetDelay)
            : this.baseGetDelay();
    }
    baseGetDelay() {
        return utils.getRandomFromRange(this.options.delay);
    }
    get mode() {
        return this.options.mode;
    }
    get html() {
        return this.options.html;
    }
    get endless() {
        return this.options.endless;
    }
    get fps() {
        return this.options.fps;
    }
    get letterize() {
        return this.options.letterize;
    }
    get ghostChance() {
        return this.options.ghostChance;
    }
    get changeChance() {
        return this.options.changeChance;
    }
    get glyphsFromText() {
        return this.options.glyphsFromText;
    }
}
exports.default = Options;
});var state = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });

class State {
    constructor(writer) {
        this.nGhosts = 0;
        /**
         * Numerical data about progress of writing
         */
        this.progress = {
            percent: 0,
            done: 0,
            todo: 0,
            increase() {
                this.done++;
                this.percent = this.done / this.todo;
            },
            reset(todo) {
                this.percent = 0;
                this.done = 0;
                this.todo = todo;
            },
            finish() {
                this.done = this.todo;
                this.percent = 1;
            },
        };
        this.isTyping = false;
        this.isPaused = false;
        this.finished = true;
        this.erasing = false;
        this.writer = writer;
        this.maxGhosts = this.writer.options.maxGhosts;
    }
    get ghostsInLimit() {
        return this.nGhosts < this.maxGhosts;
    }
    play() {
        this.isTyping = true;
        this.isPaused = false;
        this.finished = false;
        this.addClass();
        this.erasing && this.addClass('gw-erasing');
        this.maxGhosts = this.writer.options.maxGhosts;
        this.writer.animator.run();
        this.writer.emiter.callback('start', this.writer.goalText, this.writer.writerData);
    }
    pause() {
        this.isTyping = false;
        this.isPaused = true;
        this.removeClasses();
    }
    finish() {
        this.progress.finish();
        this.isTyping = false;
        this.finished = true;
        this.removeClasses();
    }
    addClass(className = 'gw-writing') {
        utils.animateWithClass(this.writer.htmlElement, className);
    }
    removeClasses() {
        this.writer.htmlElement.classList.remove('gw-writing', 'gw-erasing');
    }
}
exports.default = State;
});var emiter = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });

class Emiter {
    constructor(writer) {
        this.callbacks = {
            start: [],
            step: [],
            finish: [],
        };
        this.writer = writer;
    }
    addCallback(type, callback) {
        this.callbacks[type].push(callback);
    }
    removeCallback(type, callback) {
        const array = this.callbacks[type], i = array.indexOf(callback);
        if (i === -1)
            return false;
        array.splice(i, 1);
        return true;
    }
    callback(type, ...args) {
        this.callbacks[type].forEach(cb => cb(...args));
    }
    call(eventType) {
        const { writer } = this;
        writer.updateString();
        const { writerData, string } = writer;
        // for letterize: update data attribute every step
        if (writer.options.letterize)
            writer.htmlElement.setAttribute('data-gw-string', writer.options.html ? utils.filterHtml(string) : string);
        // ON STEP
        if (eventType === 'step')
            return this.callback('step', string, writerData);
        // ON FINISH
        writer.state.finish();
        // change state to finished but do not fire callbacks
        if (writer.state.erasing)
            return;
        this.callback('finish', string, writerData);
        this.emitEvent();
    }
    emitEvent() {
        const { htmlElement, writerData } = this.writer;
        if (typeof CustomEvent === 'undefined')
            return;
        htmlElement.dispatchEvent(new CustomEvent('gw-finished', { detail: writerData }));
    }
}
exports.default = Emiter;
});var char_1 = createCommonjsModule(function (module, exports) {
var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });

class Char {
    constructor(writer, l, gl, initialGhosts = '', specialType, index) {
        this.ghosts = [[], []];
        this.stop = false;
        this.afterGlitchChance = 0;
        this.writer = writer;
        const { options } = writer;
        this.index = index;
        this.l = l;
        this.gl = gl;
        this.specialType = specialType;
        this.ghosts[0] = [...initialGhosts];
        this.writer.state.nGhosts += initialGhosts.length;
        this.stepsLeft = options.steps;
        if (specialType === 'tag')
            this.stepsLeft = 0;
        else if (utils.isSpecialChar(gl))
            this.specialType = 'whitespace';
        this.afterGlitchChance =
            (options.ghostChance + options.changeChance) / 3.7;
        if (writer.options.letterize) {
            this.els = {
                ghostsBeforeEl: document.createElement('span'),
                letterEl: document.createElement('span'),
                ghostsAfterEl: document.createElement('span'),
            };
            this.els.ghostsBeforeEl.className = 'gw-ghosts';
            this.els.ghostsAfterEl.className = 'gw-ghosts';
            this.els.letterEl.className = 'gw-letter';
        }
    }
    get string() {
        const { l, ghosts } = this;
        return ghosts[0].join('') + l + ghosts[1].join('');
    }
    get finished() {
        const { l, gl, ghosts } = this;
        return ((l === gl && ghosts[0].length === 0 && ghosts[1].length === 0) ||
            this.specialType === 'tag');
    }
    writeToElement() {
        if (!this.els)
            return;
        const { ghostsBeforeEl, ghostsAfterEl, letterEl, charEl } = this.els;
        letterEl.innerHTML = this.l;
        ghostsBeforeEl.textContent = this.ghosts[0].join('');
        ghostsAfterEl.textContent = this.ghosts[1].join('');
        charEl && utils.animateWithClass(charEl, 'gw-changed');
    }
    set spanElement(el) {
        if (!this.els)
            return;
        this.els.charEl = el;
        this.appendChildren();
    }
    appendChildren() {
        var _a, _b;
        (_b = (_a = this.els) === null || _a === void 0 ? void 0 : _a.charEl) === null || _b === void 0 ? void 0 : _b.append(this.els.ghostsBeforeEl, this.els.letterEl, this.els.ghostsAfterEl);
        this.writeToElement();
    }
    type() {
        var _a, _b, _c;
        return __awaiter(this, void 0, void 0, function* () {
            const { options, state, emiter } = this.writer;
            if (this.specialType === 'tag') {
                this.l = this.gl;
                emiter.call('step');
                state.progress.increase();
                return true;
            }
            const loop = () => __awaiter(this, void 0, void 0, function* () {
                yield utils.wait(options.getInterval(this));
                const lastString = this.string;
                this.step();
                if (lastString !== this.string) {
                    emiter.call('step');
                    this.writeToElement();
                }
                !options.endless && this.stepsLeft--;
            });
            yield utils.wait(options.getDelay(this));
            yield utils.promiseWhile(() => (!this.finished || options.endless) &&
                !state.isPaused &&
                !this.stop, loop);
            if (this.finished) {
                state.progress.increase();
                (_b = (_a = this.els) === null || _a === void 0 ? void 0 : _a.charEl) === null || _b === void 0 ? void 0 : _b.classList.add('gw-finished');
                (_c = this.els) === null || _c === void 0 ? void 0 : _c.letterEl.classList.remove('gw-glitched');
            }
            return this.finished;
        });
    }
    step() {
        var _a, _b;
        const { writer } = this;
        if ((this.stepsLeft > 0 && this.l !== this.gl) ||
            (utils.coinFlip(this.afterGlitchChance) &&
                this.specialType !== 'whitespace') ||
            writer.options.endless) {
            /**
             * IS GROWING
             */
            const { ghostChance, changeChance } = writer.options;
            if (utils.coinFlip(ghostChance)) {
                if (writer.state.ghostsInLimit)
                    this.addGhost();
                else
                    this.removeGhost();
            }
            if (utils.coinFlip(changeChance)) {
                (_a = this.els) === null || _a === void 0 ? void 0 : _a.letterEl.classList.add('gw-glitched');
                this.l = writer.options.getGlyph(this);
            }
        }
        else if (!this.finished) {
            /**
             * IS SHRINKING
             */
            (_b = this.els) === null || _b === void 0 ? void 0 : _b.letterEl.classList.remove('gw-glitched');
            this.l = this.gl;
            this.removeGhost();
        }
    }
    addGhost() {
        const { writer, ghosts } = this, l = writer.options.getGlyph(this);
        writer.state.nGhosts++;
        utils.coinFlip() ? insertGhost(ghosts[0], l) : insertGhost(ghosts[1], l);
    }
    removeGhost() {
        const { writer, ghosts } = this, deleted = utils.coinFlip() && ghosts[0].length > 0
            ? utils.deleteRandom(ghosts[0])
            : utils.deleteRandom(ghosts[1]);
        if (deleted)
            writer.state.nGhosts--;
    }
}
exports.default = Char;
function insertGhost(ghostsArray, ghost) {
    const { length } = ghostsArray;
    ghostsArray.splice(utils.random(0, length, 'floor'), 0, ghost);
}
});var setupCharTable_1 = createCommonjsModule(function (module, exports) {
var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const char_1$1 = __importDefault(char_1);

function setupCharTable() {
    const { charTable, options } = this;
    // For "clear" mode char table will be prepared as starting from blank
    const from = options.mode === 'clear' && this.state.finished ? '' : this.previousString;
    // Clear Char Table -> stop all chars and remove them from char table
    charTable.forEach(char => (char.stop = true));
    this.charTable = [];
    options.mode === 'matching'
        ? createMatching.call(this, from)
        : createPrevious.call(this, from);
}
exports.default = setupCharTable;
function createMatching(from) {
    const maxDist = Math.min(Math.ceil(this.options.maxGhosts / 2), 5), goalTextArray = getGoalStringText.call(this, from);
    let pi = -1;
    goalTextArray.forEach((gl, gi) => {
        pi++;
        if (gl.type === 'tag') {
            pi--;
            setChar.call(this, gi, '', gl);
            return;
        }
        const fi = gl.value !== '' ? from.indexOf(gl.value, pi) : -1;
        if (fi !== -1 && fi - pi <= maxDist) {
            const ghosts = from.substring(pi, fi);
            setChar.call(this, gi, gl.value, gl, ghosts);
            pi = fi;
        }
        else
            setChar.call(this, gi, from[pi], gl);
    });
    removeExtraChars(this.charTable, goalTextArray.length);
}
function createPrevious(from) {
    const goalStringText = getGoalStringText.call(this, from);
    let pi = -1;
    goalStringText.forEach((gl, gi) => {
        pi++;
        if (gl.type === 'tag') {
            pi--;
            setChar.call(this, gi, '', gl);
            return;
        }
        setChar.call(this, gi, from[pi], gl);
    });
    removeExtraChars(this.charTable, goalStringText.length);
}
function getGoalStringText(from) {
    const { options, goalText } = this, goalArray = options.html
        ? utils.htmlToArray(goalText)
        : utils.stringToLetterItems(goalText), diff = Math.max(0, from.length - goalArray.length);
    if (options.oneAtATime)
        return goalArray.concat(utils.stringToLetterItems(utils.arrayOfTheSame('', diff)));
    const nBefore = Math.ceil(diff / 2), nAfter = Math.floor(diff / 2);
    return utils.stringToLetterItems(utils.arrayOfTheSame('', nBefore)).concat(goalArray, utils.stringToLetterItems(utils.arrayOfTheSame('', nAfter)));
}
function setChar(i, l, gl, ghosts) {
    const { charTable, options } = this;
    charTable.push(new char_1$1.default(this, l !== null && l !== void 0 ? l : '', gl.value || options.space, ghosts, gl.type, i));
}
function removeExtraChars(charTable, from) {
    charTable.splice(from, charTable.length - from);
}
});var letterize_1 = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
function letterize() {
    if (!this.options.letterize)
        return;
    const html = this.charTable
        .map(({ specialType, gl }) => specialType === 'tag' ? gl : '<span class="gw-char"></span>')
        .join('');
    this.htmlElement.innerHTML = html;
    const spans = this.htmlElement.querySelectorAll('span.gw-char');
    let i = 0;
    this.charTable.forEach(char => {
        if (char.specialType === 'tag')
            return;
        char.spanElement = spans[i];
        i++;
    });
}
exports.default = letterize;
});var words = createCommonjsModule(function (module, exports) {
var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });

/**
 * ONE WORD AT A TIME
 */
function prepWordsPlaylist(playList) {
    const { charTable, state } = this;
    // indexes of chars in chartable that are normal letters
    const indexes = [];
    let wordArray;
    /**
     * Scope for separating words
     */
    {
        const filteredTable = charTable.filter((char, i) => {
            if (char.specialType === 'tag' ||
                char.specialType === 'html_entity')
                return false;
            indexes.push(i);
            return true;
        }), 
        // I want to only find words in the normal text
        text = filteredTable.map(char => char.gl).join('');
        wordArray = text.match(utils.wordsRgx);
    }
    /**
     * Scope for greating groups
     */
    const charGroups = [];
    {
        // set of index variables
        // fi - index of (not) filtered chars
        // gi - index of all chars
        // sgi - steady gi - for detecting when gi grows more than 1
        let fi = -1, ai = -1, sgi = -1;
        const lastGroup = () => charGroups[charGroups.length - 1];
        wordArray === null || wordArray === void 0 ? void 0 : wordArray.forEach(word => {
            charGroups.push([]);
            [...word].forEach(() => {
                fi++;
                ai = indexes[fi];
                sgi++;
                // handling filtered chars before current char
                if (sgi !== ai) {
                    for (let i = sgi; i < ai; i++) {
                        lastGroup().push(charTable[i]);
                    }
                    sgi = ai;
                }
                lastGroup().push(charTable[ai]);
            });
        });
        // Adds the rest of the chartable chars
        if (!charGroups.length)
            charGroups.push([]);
        for (let i = ai + 1; i < charTable.length; i++) {
            lastGroup().push(charTable[i]);
        }
    }
    // so i can use .pop() and get the first group
    charGroups.reverse();
    let lastResult = true, ended = false;
    const loop = () => __awaiter(this, void 0, void 0, function* () {
        var _a;
        const group = charGroups.pop();
        if (!group)
            return (ended = true);
        const groupPromises = group.map(char => char.type());
        lastResult =
            (_a = (yield Promise.all(groupPromises)).every(result => result)) !== null && _a !== void 0 ? _a : false;
    });
    const executor = () => __awaiter(this, void 0, void 0, function* () {
        yield utils.promiseWhile(() => !ended && lastResult && !state.isPaused, loop);
        return ended && lastResult && !state.isPaused;
    });
    playList.push(executor());
}
exports.default = prepWordsPlaylist;
});var letters = createCommonjsModule(function (module, exports) {
var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });

function prepLettersPlaylist(playList, playOptions) {
    var _a;
    const { charTable, state, options } = this, reverse = (_a = playOptions === null || playOptions === void 0 ? void 0 : playOptions.reverse) !== null && _a !== void 0 ? _a : false, charTableCopy = reverse ? [...charTable] : [...charTable].reverse();
    // Char executor - runs a loop, typing one char at a time
    // It is possible to run multiple of them at the same time
    const executor = () => __awaiter(this, void 0, void 0, function* () {
        let lastResult = true, ended = false;
        const loop = () => __awaiter(this, void 0, void 0, function* () {
            var _b;
            const lastChar = charTableCopy.pop();
            if (!lastChar)
                ended = true;
            else
                lastResult = (_b = (yield lastChar.type())) !== null && _b !== void 0 ? _b : false;
        });
        yield utils.promiseWhile(() => !ended && lastResult && !state.isPaused, loop);
        return ended && lastResult && !state.isPaused;
    });
    // Add as many executors as needed to the playList
    for (let n = 0; n < options.oneAtATime; n++) {
        playList.push(executor());
    }
}
exports.default = prepLettersPlaylist;
});var animator = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });

class Animator {
    constructor(writer) {
        this.last = 0;
        this.rate = 16;
        this.running = false;
        this.writer = writer;
    }
    run() {
        // animator runs only for non-letterize mode
        if (this.running || this.writer.options.letterize)
            return;
        this.rate = Math.floor(1000 / this.writer.options.fps);
        this.running = true;
        requestAnimationFrame(this.frame.bind(this));
    }
    frame(t) {
        // exit the loop if the writer isn't writing anymore
        if (!this.writer.state.isTyping) {
            this.animate.call(this);
            return (this.running = false);
        }
        // keep it above specified refresh rate
        if (!this.last)
            this.last = t;
        if (t - this.last < this.rate)
            return requestAnimationFrame(this.frame.bind(this));
        // animate text
        this.animate.call(this);
        // request next frame
        this.last = t;
        return requestAnimationFrame(this.frame.bind(this));
    }
    animate() {
        const { htmlElement, string } = this.writer;
        if (this.writer.options.html)
            htmlElement.innerHTML = string;
        else
            htmlElement.textContent = string;
        htmlElement.setAttribute('data-gw-string', this.writer.options.html ? utils.filterHtml(string) : string);
    }
}
exports.default = Animator;
});var queue = createCommonjsModule(function (module, exports) {
var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });

class Queue {
    constructor(writer, texts, interval = 800, loop = false) {
        this.isStopped = false;
        this.isLooping = false;
        this.loopInterval = 0;
        this.index = -1;
        this.writer = writer;
        this.interval = interval;
        // Setup texts
        if (Array.isArray(texts))
            this.texts = texts;
        else {
            // If passed html element
            // get child paragraphs as sequent texts to write
            let el;
            if (typeof texts === 'object')
                el = texts;
            else
                el = document.querySelector(texts);
            this.texts = [];
            el === null || el === void 0 ? void 0 : el.childNodes.forEach(node => {
                const { tagName, innerHTML } = node;
                tagName === 'P' &&
                    innerHTML !== undefined &&
                    this.texts.push(innerHTML);
            });
        }
        // Setup looping settings
        if (typeof loop === 'boolean')
            this.isLooping = loop;
        else if (typeof loop === 'function')
            this.endCallback = loop;
        else {
            this.isLooping = true;
            this.loopInterval = loop;
        }
        this.loop();
    }
    stop() {
        this.isStopped = true;
    }
    resume() {
        this.index--;
        this.isStopped = false;
        this.writer.state.isPaused = false;
        this.loop();
    }
    loop() {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.texts.length)
                return;
            this.index++;
            if (this.index >= this.texts.length) {
                if (this.isLooping) {
                    yield utils.wait(this.loopInterval);
                    this.index = 0;
                }
                else
                    return (_a = this.endCallback) === null || _a === void 0 ? void 0 : _a.call(this, this.writer.string, this.writer.getWriterData('SUCCESS', "The queue has reached it's end."));
            }
            if (this.isStopped || this.writer.state.isPaused)
                return;
            const result = yield this.writer.manageWriting(this.texts[this.index]);
            if ((result === null || result === void 0 ? void 0 : result.status) !== 'SUCCESS' || this.writer.state.isPaused)
                return;
            yield utils.wait(this.interval);
            this.loop();
        });
    }
}
exports.default = Queue;
});var cjs = createCommonjsModule(function (module, exports) {
var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.wait = exports.glyphs = exports.presets = exports.create = exports.queueWrite = exports.write = void 0;
// eslint-disable-next-line import/no-extraneous-dependencies
const options_1 = __importDefault(options);
const state_1 = __importDefault(state);
const emiter_1 = __importDefault(emiter);

Object.defineProperty(exports, "wait", { enumerable: true, get: function () { return utils.wait; } });

Object.defineProperty(exports, "presets", { enumerable: true, get: function () { return presets.presets; } });
Object.defineProperty(exports, "glyphs", { enumerable: true, get: function () { return presets.glyphs; } });
const setupCharTable_1$1 = __importDefault(setupCharTable_1);
const letterize_1$1 = __importDefault(letterize_1);
const words_1 = __importDefault(words);
const letters_1 = __importDefault(letters);
const animator_1 = __importDefault(animator);
const queue_1 = __importDefault(queue);
class GlitchedWriter {
    /**
     * Create new instance of Glitched Writer, that manages writing text to one HTML Element. Few writers can possess the same HTML Element, but don't write with them at the same time.
     * Use .write(string) method to start writing.
     * @param htmlElement HTML Element OR a Selector string (eg. '.text')
     * @param options Options object (eg. { html: true, ... }) OR preset name (eg. 'zalgo').
     * @param onFinishCallback Callback, that will be triggered when each writing finishes. Params passed: string & writer data.
     */
    constructor(htmlElement, options, onFinishCallback) {
        var _a;
        this.charTable = [];
        this.goalText = '';
        this.lastText = '';
        this.string = '';
        if (!htmlElement)
            this.htmlElement = document.createElement('span');
        else if (typeof htmlElement === 'string') {
            this.htmlElement =
                (_a = document.querySelector(htmlElement)) !== null && _a !== void 0 ? _a : document.createElement('span');
        }
        else
            this.htmlElement = htmlElement;
        this.htmlElement.$writer = this;
        this.options = new options_1.default(this, options);
        this.state = new state_1.default(this);
        this.emiter = new emiter_1.default(this);
        if (onFinishCallback)
            this.emiter.addCallback('finish', onFinishCallback);
        this.animator = new animator_1.default(this);
        this.string = this.previousString;
    }
    updateString() {
        this.string = this.charTable.map(char => char.string).join('');
    }
    get previousString() {
        let prev = this.htmlElement.textContent;
        if (typeof prev !== 'string')
            prev = this.options.html ? utils.filterHtml(this.string) : this.string;
        prev = prev.trim();
        return prev;
    }
    /**
     * All the data, about current state of the writer instance.
     */
    get writerData() {
        const writer = this, { options, state, string } = this;
        return {
            string,
            writer,
            options,
            state,
        };
    }
    /**
     * Main function of Glitched Writer. It orders writer to start typing passed string. Can be called multiple times after each other, or even during writing.
     * @param text text, that will get written.
     * @returns Promise, with writer data result
     */
    write(text) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.queue) {
                this.queue.stop();
                delete this.queue;
            }
            return this.manageWriting(text);
        });
    }
    /**
     * Order Glitched writer to write sequence of texts.
     * @param texts - Array of sequent strings to write.
     *
     * You can also pass a `query selector` or a `HTMLElement`.
     * The content of children `<p>` tags will make the text queue
     * @param queueInterval - Time to wait between writing each texts [ms]
     * @param loop - boolean | Callback | number - What to do when the queue has ended.
     * - false -> stop;
     * - true -> continue looping;
     * - Callback -> fire the callback and stop.
     * - number -> wait that many ms and then continue
     */
    queueWrite(texts, queueInterval, loop) {
        if (this.queue) {
            this.queue.stop();
            delete this.queue;
        }
        this.queue = new queue_1.default(this, texts, queueInterval, loop);
    }
    /**
     * Add text to end method. Orders writer to write same string as previous, but with this added at the end.
     * @param string text that will get added
     * @returns Promise, with writer data result
     */
    add(string) {
        return __awaiter(this, void 0, void 0, function* () {
            const { previousString } = this;
            return this.write(previousString + string);
        });
    }
    /**
     * Remove last n-letters method. Orders writer to write same string as previous, but without n-letters at the end.
     * @param n number of letters to remove.
     * @returns Promise, with writer data result
     */
    remove(n) {
        return __awaiter(this, void 0, void 0, function* () {
            const { previousString } = this, array = Array.from(previousString);
            array.splice(-n);
            // return this.write(array.join(''), { erase: true })
            return this.write(array.join(''));
        });
    }
    /**
     * Resume last writing order.
     * @returns Promise, with writer data result
     */
    play() {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.state.isPaused)
                return this.getWriterData('ERROR', "The writer isn't paused.");
            if (this.queue) {
                this.queue.resume();
                return this.getWriterData('SUCCESS', 'The queue was resumed');
            }
            return this.manageWriting(null);
        });
    }
    /**
     * Pause current writer task.
     */
    pause() {
        this.state.pause();
    }
    /**
     * Shorthand for changing endless option value
     * @param bool goal state
     */
    endless(bool) {
        this.options.extend({ endless: bool });
    }
    /**
     * Use this to add callback to one of the writer events
     *
     * save callback in a variable first if you want to remove it later.
     * @param type "start" | "step" | "finish"
     * @param callback your callback function: (string, writerData) => {}
     * @returns GlitchedWriter instance (this)
     */
    addCallback(type, callback) {
        this.emiter.addCallback(type, callback);
        return this;
    }
    /**
     * Use this to remove added callback
     * @param type "start" | "step" | "finish"
     * @param callback variable pointing to your function
     * @returns GlitchedWriter instance (this)
     */
    removeCallback(type, callback) {
        this.emiter.removeCallback(type, callback);
        return this;
    }
    // private logCharTable() {
    // 	console.table(
    // 		this.charTable.map(
    // 			({ ghostsBefore, ghostsAfter, l, gl, isTag, isWhitespace }) => [
    // 				ghostsBefore.join(''),
    // 				ghostsAfter.join(''),
    // 				l,
    // 				gl,
    // 				(isTag && 'TAG') || (isWhitespace && 'Whitespace'),
    // 			],
    // 		),
    // 	)
    // }
    manageWriting(text) {
        return __awaiter(this, void 0, void 0, function* () {
            if (text !== null)
                this.lastText = text;
            // Erasing first
            if (['erase_smart', 'erase'].includes(this.options.mode) &&
                (this.state.finished || this.state.erasing)) {
                this.state.erasing = true;
                const eraseTo = this.genGoalStringToErase(this.lastText);
                this.preparePropertiesBeforeWrite(eraseTo);
                yield this.playChT({
                    reverse: this.options.oneAtATime !== 0,
                });
                // If erasing did not finish for some reason
                // Like it was paused
                if (!this.state.finished)
                    return this.getWriterData('ERROR', 'Erasing did not finish.');
                this.state.erasing = false;
            }
            this.preparePropertiesBeforeWrite(this.lastText);
            // this.logCharTable()
            this.pause();
            return this.playChT();
        });
    }
    preparePropertiesBeforeWrite(text) {
        /* PREPARE PROPERTIES */
        this.goalText = text;
        this.state.nGhosts = 0;
        this.options.setCharset();
        setupCharTable_1$1.default.call(this);
        this.state.progress.reset(this.charTable.length);
        letterize_1$1.default.call(this);
    }
    playChT(playOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            const playList = [], { charTable, state, options } = this;
            if (state.isTyping)
                return this.getWriterData('ERROR', `The writer is already typing.`);
            state.play();
            // N LETTERS AT A TIME
            if (options.oneAtATime > 0)
                letters_1.default.call(this, playList, playOptions);
            // BY WORDS
            else if (options.oneAtATime === 'word')
                words_1.default.call(this, playList);
            // NORMAL
            else
                charTable.forEach(char => playList.push(char.type()));
            /**
             * Play Playlist
             * and return the result
             */
            try {
                const finished = (yield Promise.all(playList)).every(result => result);
                return this.returnResult(finished);
            }
            catch (error) {
                return this.getWriterData('ERROR', 'Writer encountered an error.', error);
            }
        });
    }
    returnResult(finished) {
        finished ? this.emiter.call('finish') : this.emiter.call('step');
        return finished
            ? this.getWriterData('SUCCESS', `The writer finished typing.`)
            : this.getWriterData('ERROR', `Writer failed to finish typing.`);
    }
    getWriterData(status, message, error) {
        const { writerData } = this;
        return Object.assign(Object.assign({}, writerData), { status,
            message,
            error });
    }
    genGoalStringToErase(goal) {
        var _a;
        const { previousString: previous } = this;
        let result = '';
        if (this.options.mode === 'erase_smart') {
            // Do not erase matching with previous letters
            for (let i = 0; i < goal.length; i++) {
                const gl = goal[i], pl = (_a = previous[i]) !== null && _a !== void 0 ? _a : '';
                if (gl === pl)
                    result += pl;
                else
                    break;
            }
        }
        const diff = Math.max(goal.length - result.length, 0);
        if (diff > 0 && this.options.space === ' ')
            result = result.padEnd(diff + result.length, ' ');
        return result;
    }
}
exports.default = GlitchedWriter;
/**
 * One time use, standalone write function. Used to order a temporary Glitched Writer instance to animate content of html element to chosen text.
 * @param string text, that will get written.
 * @param htmlElement HTML Element OR a Selector string (eg. '.text')
 * @param options Options object (eg. { html: true, ... }) OR preset name (eg. 'zalgo').
 * @param onStepCallback Callback, that will be triggered on every step. Params passed: string & writer data.
 * @param onFinishCallback Callback, that will be triggered when each writing finishes. Params passed: string & writer data.
 * @returns Promise, with writer data result
 */
function write(string, htmlElement, options, onStepCallback, onFinishCallback) {
    return __awaiter(this, void 0, void 0, function* () {
        const writer = new GlitchedWriter(htmlElement, options, onFinishCallback);
        if (onStepCallback)
            writer.addCallback('step', onStepCallback);
        return writer.write(string);
    });
}
exports.write = write;
/**
 * Standalone queueWrite function. Used to
 * @param texts - Array of strings to write.
 *
 * You can also pass a `selector` or a `HTMLElement`.
 * The content of children `<p>` tags will make the text queue
 * @param htmlElement Writer HTML Element OR a Selector string (eg. '.text')
 * @param options Options object (eg. { html: true, ... }) OR preset name (eg. 'zalgo').
 * @param queueInterval - Time to wait between writing each texts [ms]
 * @param loop - boolean | Callback | number - What to do when the queue has ended.
 * - false -> stop;
 * - true -> continue looping;
 * - Callback -> stop and fire the callback.
 * - number -> wait number ms and then continue
 * @param onStepCallback Callback, that will be triggered on every step. Params passed: string & writer data.
 * @param onFinishCallback Callback, that will be triggered when each writing finishes. Params passed: string & writer data.
 * @returns created GlitchedWriter instance
 */
function queueWrite(texts, htmlElement, options, queueInterval, loop, onStepCallback, onFinishCallback) {
    const writer = new GlitchedWriter(htmlElement, options, onFinishCallback);
    if (onStepCallback)
        writer.addCallback('step', onStepCallback);
    writer.queueWrite(texts, queueInterval, loop);
    return writer;
}
exports.queueWrite = queueWrite;
/**
 * A way to create new Writer without having to rely on defult export.
 * @param htmlElement HTML Element OR a Selector string (eg. '.text')
 * @param options Options object (eg. { html: true, ... }) OR preset name (eg. 'zalgo').
 * @param onFinishCallback Callback, that will be triggered when each writing finishes. Params passed: string & writer data.
 * @returns GlitchedWriter Class Instance
 */
const create = (htmlElement, options, onFinishCallback) => new GlitchedWriter(htmlElement, options, onFinishCallback);
exports.create = create;
});

var cjs$1 = cjs;function escapeHtml(html) {
  var text = document.createTextNode(html);
  var p = document.createElement('p');
  p.appendChild(text);
  return p.innerHTML;
}var script = vue.defineComponent({
  name: 'VueGlitchedWriter',
  props: {
    text: {
      type: [String, Array],
      required: true
    },
    pause: {
      type: Boolean,
      default: false
    },
    preset: {
      type: String,
      default: 'default'
    },
    options: {
      type: Object,
      default: function _default() {}
    },
    queue: {
      type: Object,
      default: function _default() {}
    },
    silent: {
      type: Boolean,
      default: true
    },
    tag: {
      type: String,
      default: 'span'
    }
  },
  setup: function setup(props, _ref) {
    var emit = _ref.emit,
        attrs = _ref.attrs;
    var writer = vue.ref(null),
        element = vue.ref(null),
        preset = vue.computed( // @ts-ignore
    function () {
      var _presets$props$preset;

      return (_presets$props$preset = cjs$1.presets[props.preset]) !== null && _presets$props$preset !== void 0 ? _presets$props$preset : {};
    }),
        writeOnAppear = attrs.appear !== undefined || _typeof(props.text) === 'object' && props.text.length > 0,
        queueInterval = vue.computed(function () {
      var _props$queue;

      return typeof ((_props$queue = props.queue) === null || _props$queue === void 0 ? void 0 : _props$queue.interval) === 'number' ? props.queue.interval : undefined;
    }),
        queueLoop = vue.computed(function () {
      var _props$queue2;

      return ['number', 'boolean', 'function'].includes(_typeof((_props$queue2 = props.queue) === null || _props$queue2 === void 0 ? void 0 : _props$queue2.loop)) ? props.queue.loop : undefined;
    });
    /**
     * Options will react to preset and options props change
     * And force writer to update them internally
     */

    var computedOptions = vue.computed(function () {
      return _objectSpread2(_objectSpread2({}, preset.value), props.options);
    });
    vue.watch(computedOptions, function (options) {
      return writer.value.options.set(options);
    });
    /**
     * WRITE function
     */

    function write() {
      if (props.pause) return; // For string text prop: simply write

      if (typeof props.text === 'string') writer.value.write(props.text); // For Array text prop:
      // parse array items into strings
      // and queue write that
      else {
        var texts = props.text.map(function (item) {
          return String(item);
        });
        writer.value.queueWrite(texts, queueInterval.value, queueLoop.value);
      }
    }

    vue.watch(function () {
      return props.text;
    }, write); // Pause and Play

    vue.watch(function () {
      return props.pause;
    }, function (pause) {
      return pause ? writer.value.pause() : writer.value.play();
    });
    /**
     * Mounted hook
     */

    vue.onMounted(function () {
      // Set writer, after DOM is ready
      writer.value = cjs$1.create(element.value, computedOptions.value);
      writer.value.addCallback('step', function (string, data) {
        return emit('step', string, data);
      });
      writer.value.addCallback('start', function (string, data) {
        return emit('start', string, data);
      });
      writer.value.addCallback('finish', function (string, data) {
        return emit('finish', string, data);
      }); // Write initial text if props.appear is true

      writeOnAppear && write();
    });
    /**
     * Initial text will be displayed as html element's content
     * but only when appear attribute isn't present
     * then it will animate initial text instead
     */

    var initialText = '';

    if (attrs.appear === undefined) {
      var _props$text$;

      var text = typeof props.text === 'string' ? props.text : String((_props$text$ = props.text[0]) !== null && _props$text$ !== void 0 ? _props$text$ : '');
      initialText = computedOptions.value.html ? text : escapeHtml(text);
    }

    return {
      writer: writer,
      element: element,
      initialText: initialText
    };
  }
});function render(_ctx, _cache, $props, $setup, $data, $options) {
  return vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(_ctx.tag), {
    ref: "element",
    innerHTML: _ctx.initialText,
    class: ["glitched-writer", {
      'gw-paused': _ctx.pause
    }]
  }, null, 8, ["innerHTML", "class"]);
}script.render = render;// Import vue component

// Default export is installable instance of component.
// IIFE injects install function into component, allowing component
// to be registered via Vue.use() as well as Vue.component(),
var component = /*#__PURE__*/(function () {
  // Assign InstallableComponent type
  var installable = script; // Attach install function executed by Vue.use()

  installable.install = function (app) {
    app.component('VueGlitchedWriter', installable);
  };

  return installable;
})(); // It's possible to expose named exports when writing components that can
var namedExports=/*#__PURE__*/Object.freeze({__proto__:null,'default': component,presets: cjs$1.presets,wait: cjs$1.wait,glyphs: cjs$1.glyphs,CustomOptions: cjs$1.CustomOptions,Callback: cjs$1.Callback,GlitchedWriter: cjs$1,WriterDataResponse: cjs$1.WriterDataResponse});// only expose one global var, with named exports exposed as properties of
// that global var (eg. plugin.namedExport)

Object.entries(namedExports).forEach(function (_ref) {
  var _ref2 = _slicedToArray(_ref, 2),
      exportName = _ref2[0],
      exported = _ref2[1];

  if (exportName !== 'default') component[exportName] = exported;
});module.exports=component;