/* Typewriter text animation */

/* Typewriter class
 * The callback passed will be called with full strings, which should be used as text. This class
 * takes care of partial phases, just use the text passed to the callback when it is called.
 */

class Typewriter {
  constructor(phrases, delayRange, callback) {
    this.phrases = phrases;
    this.delayRange = delayRange;
    this.currentPhraseIndex = 0;
    this.callback = callback;
  }

  /* Find the words that are common to the beginning of two phrases. */
  static findCommonStart(phraseA, phraseB) {
    const aWords = phraseA.split(' ');
    const bWords = phraseB.split(' ');

    const commonWords = [];
    for (let i = 0; i < aWords.length; i += 1) {
      if (aWords[i] === bWords[i]) commonWords.push(aWords[i]);
      else break;
    }
    return commonWords.join(' ');
  }

  /* Get instructions for transforming between two strings in the form of
   * number of characters to delete and characters to add.
   */
  transitionDescription(phraseA, phraseB) {
    const commonStart = this.commonStart(phraseA, phraseB);
    const numCharactersToDelete = phraseA.length - commonStart.length;
    const charactersToAdd = phraseB.slice(commonStart.length);
    return {
      del: numCharactersToDelete,
      add: charactersToAdd,
    };
  }

  /* Pick a delay length from the delay range */
  getTypingDelay() {
    return Math.floor(
      (Math.random() * (this.delayRange[1] - this.delayRange[0])) + this.delayRange[0]
    );
  }
}


document.addEventListener('DOMContentLoaded', () => {
  window.typewriter = {
    element: document.getElementById('typewriter'),
    contents: [
      'I write code.\xa0',
      'I write software.\xa0',
      'I write websites.\xa0',
      'I write apps.\xa0',
      'I write libraries.\xa0',
      'I write simulations.\xa0',
      'I design experiences.\xa0',
      'I design interfaces.\xa0',
    ],
    contentIndex: 0,

    // A random delay in ms will be chosen from this range. The resulting delay
    // is multiplied for typing, so this isn't exact.
    typingDelay: [50, 100],

    /* Find common words that two strings start with. */
    commonStart(a, b) {
      const aWords = a.split(' ');
      const bWords = b.split(' ');

      const commonWords = [];
      for (let i = 0; i < aWords.length; i += 1) {
        if (aWords[i] === bWords[i]) {
          commonWords.push(aWords[i]);
        } else {
          break;
        }
      }
      return commonWords.join(' ');
    },

    /* Get instructions for transforming between two strings in the form of
     * number of characters to delete and characters to add.
     */
    transitionDescription(a, b) {
      const common = this.commonStart(a, b);
      const numCharactersToDelete = a.length - common.length;
      const charactersToAdd = b.slice(common.length);
      return {
        del: numCharactersToDelete,
        add: charactersToAdd,
      };
    },

    getTypingDelay() {
      return Math.floor(
        (Math.random() * (this.typingDelay[1] - this.typingDelay[0])) + this.typingDelay[0]
      );
    },

    /* Animate backspacing by a given number of characters */
    backspace(num, callback) {
      let content;
      let toGo = num;
      const callback2 = callback || (() => {});
      this.backspaceOne = () => {
        if (toGo === 0) {
          callback2();
        } else {
          content = this.element.textContent;
          this.element.textContent = content.slice(0, content.length - 1);
          toGo -= 1;
          setTimeout(() => window.typewriter.backspaceOne(), this.getTypingDelay());
        }
      };
      this.backspaceOne();
    },

    /* Animate inserting a given string at the end of the text */
    type(text, callback) {
      let i = 0;
      const callback2 = callback || (() => {});
      this.typeOne = () => {
        if (i === text.length) {
          callback2();
        } else {
          this.element.textContent += text[i];
          i += 1;
          setTimeout(() => window.typewriter.typeOne(), this.getTypingDelay() * 1.5);
        }
      };
      this.typeOne();
    },

    /* Animate advancing to the next phrase */
    next(callback) {
      this.contentIndex += 1;
      // Loop back
      if (this.contentIndex === this.contents.length) {
        this.contentIndex = 0;
      }
      // Make changes
      const changesNeeded = this.transitionDescription(
        this.element.textContent,
        this.contents[this.contentIndex]
      );
      this.backspace(changesNeeded.del, () => {
        setTimeout(() => window.typewriter.type(changesNeeded.add, callback), 200);
      });
    },

    /* Fill the element with the first phrase (non-animated) and set up other
     * miscellaneous details.
     */
    init() {
      function scrollFunc() {
        window.typewriter.startOnScroll();
      }
      this.element.textContent = this.contents[0];
      this.startOnScroll = () => {
        if (
          window.scrollY + window.innerHeight >
          this.element.offsetTop + (this.element.offsetHeight / 2)
        ) {
          setTimeout(() => window.typewriter.play(), 1000);
          window.removeEventListener('scroll', scrollFunc);
        }
      };
      window.addEventListener('scroll', scrollFunc);
    },

    /* Recursively continue advancing */
    play() {
      this.next(() => {
        setTimeout(() => window.typewriter.play(), 3000);
      });
    },

  };

  window.typewriter.init();
});

@@ -30,1479 +30,8 @@
 */%0A%0A
-/* Typewriter class%0A * The callback passed will be called with full strings, which should be used as text. This class%0A * takes care of partial phases, just use the text passed to the callback when it is called.%0A */%0A%0Aclass Typewriter %7B%0A  constructor(phrases, delayRange, callback) %7B%0A    this.phrases = phrases;%0A    this.delayRange = delayRange;%0A    this.currentPhraseIndex = 0;%0A    this.callback = callback;%0A  %7D%0A%0A  /* Find the words that are common to the beginning of two phrases. */%0A  static findCommonStart(phraseA, phraseB) %7B%0A    const aWords = phraseA.split(' ');%0A    const bWords = phraseB.split(' ');%0A%0A    const commonWords = %5B%5D;%0A    for (let i = 0; i %3C aWords.length; i += 1) %7B%0A      if (aWords%5Bi%5D === bWords%5Bi%5D) commonWords.push(aWords%5Bi%5D);%0A      else break;%0A    %7D%0A    return commonWords.join(' ');%0A  %7D%0A%0A  /* Get instructions for transforming between two strings in the form of%0A   * number of characters to delete and characters to add.%0A   */%0A  transitionDescription(phraseA, phraseB) %7B%0A    const commonStart = this.commonStart(phraseA, phraseB);%0A    const numCharactersToDelete = phraseA.length - commonStart.length;%0A    const charactersToAdd = phraseB.slice(commonStart.length);%0A    return %7B%0A      del: numCharactersToDelete,%0A      add: charactersToAdd,%0A    %7D;%0A  %7D%0A%0A  /* Pick a delay length from the delay range */%0A  getTypingDelay() %7B%0A    return Math.floor(%0A      (Math.random() * (this.delayRange%5B1%5D - this.delayRange%5B0%5D)) + this.delayRange%5B0%5D%0A    );%0A  %7D%0A%7D%0A%0A%0A
 docu

Remove typewriter class (because I don't want to do this yet)