{"version":3,"sources":["webpack:///chat-banner-b36e10c3fbb944f9d5fa.js","webpack:///webpack/bootstrap bada3fc95fc820b6f189","webpack:///./components/chat-banner/chat-banner.js","webpack:///./src/js/lib/text_anim.js"],"names":["modules","__webpack_require__","moduleId","installedModules","exports","module","i","l","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","121","122","__webpack_exports__","_toConsumableArray","arr","Array","isArray","arr2","length","from","_classCallCheck","instance","Constructor","TypeError","_possibleConstructorReturn","self","ReferenceError","_inherits","subClass","superClass","create","constructor","value","writable","setPrototypeOf","__proto__","__WEBPACK_IMPORTED_MODULE_0__src_js_lib_text_anim__","_createClass","defineProperties","target","props","descriptor","key","protoProps","staticProps","_get","receiver","Function","desc","getOwnPropertyDescriptor","undefined","parent","getPrototypeOf","_window$Liftoff","window","Liftoff","Component","DOMUtils","Utils","register","_Component","ChatBanner","config","this","_this","assign","mergePropertiesWithDefaults","charAnimationInterval","passButton","likeButton","isInteractive","buttonAnimation","messageShouldType","photoShouldPulse","showMessageCount","photoStyles","captionStyles","buttonStyles","messageCountStyle","people","filter","_ref","image","map","person","personData","caption","personEl","captionEl","currentTextTypingIndex","currentlyTyping","startIfReady","loading","animating","buttonAnimationTimeout","peopleSwitchTimeout","currentPersonIndex","passButtonEl","likeButtonEl","switchPeople","numLikes","numPasses","imagesLoadedPromises","_ref2","concat","url","loadImage","imagesLoadedPromise","Promise","all","sliceResult","captionDom","tree","captionDomLen","parentNode","_this2","el","strToElement","TEMPLATE","peopleEl","querySelector","PERSON_TEMPLATE","imageEl","src","style","messageCountEl","classList","toggle","curIndex","getPersonCaptionLen","setPersonCaption","appendChild","forEach","_ref3","add","remove","then","imagesRenderedPromises","querySelectorAll","img","imageIsRendered","catch","finally","resizeFont","addEventListeners","start","_this3","_ref4","innerHTML","getFontSizeToFit","tempClass","fontSize","fontSizeThatFits","Math","min","apply","_ref5","clearTimeout","animateNextWhenReady","direction","arguments","setAnimationStartClasses","classes","className","forceReflow","currentPerson","nextPerson","resetTypingMessage","emit","_this4","messageTypeTime","timeToSwitch","max","setTimeout","animateNext","_this5","addNextLetter","_this6","stopButtonAnimation","startButtonAnimation","stopSwitchingPeople","startSwitchingPeople","_this7","addEventListener","e","animationDirection","contains","recordInteraction","totalChoicesMade","passes","likes","choices","stop","startTyping","startPhotoAnimation","stopTyping","stopPhotoAnimation","123","30","domCountCharacters","domSlice","moveChildrenFromNode","getTextDom","isVariationSelector","cstr","code","charCodeAt","mergeVariationSelectors","unicodechars","reduce","acc","push","unicodeLen","str","unicodeSlice","slice","join","nodeType","Node","TEXT_NODE","nodeValue","numChars","hasChildNodes","childNodes","child","charcount","text","textLen","numCharsToPreserve","newText","document","createTextNode","charCount","ret","cloneNode","children","slicedChild","textContent","dstnode","srcnode","empty","firstChild","removeChild","div","createElement"],"mappings":"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","file":"chat-banner-b36e10c3fbb944f9d5fa.js","sourcesContent":["/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 121);\n/******/ })\n/************************************************************************/\n/******/ ({\n\n/***/ 121:\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(122);\nmodule.exports = __webpack_require__(123);\n\n\n/***/ }),\n\n/***/ 122:\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__src_js_lib_text_anim__ = __webpack_require__(30);\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nfunction _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\n\n\nvar _window$Liftoff = window.Liftoff,\n    Component = _window$Liftoff.Component,\n    DOMUtils = _window$Liftoff.DOMUtils,\n    Utils = _window$Liftoff.Utils;\n\n// Duration (in ms) of gap between restarts of the typing effect.\n\nvar RESTART_TYPING_INTERVAL = 3000;\n// Minimum interval (in ms) between switching profiles/captions.\nvar PEOPLE_SWITCH_INTERVAL = 3200;\n// Minimum time to show a person _after_ the message has completely finished typing.\nvar MIN_MESSAGE_DISPLAY_TIME = 1000;\n// Interval (in ms) after which to restart the button animation if the user hasn't interacted.\nvar BUTTON_ANIMATION_INTERACTION_DELAY = 3000;\n// Interval (in ms) after which to restart the profile switching animation if the user hasn't interacted.\nvar PEOPLE_SWITCH_INTERACTION_DELAY = 8000;\n// Duration of the transition between people. Used to delay the message typing effect to only start typing\n// after the person has fully transitioned in.\nvar PERSON_TRANSITION_DURATION = 500;\n\nvar TEMPLATE = \"\\n  <div class=\\\"chat-banner loading\\\">\\n    <div class=\\\"people\\\"></div>\\n    <div class=\\\"buttons\\\">\\n      <img class=\\\"pass-button\\\" />\\n      <img class=\\\"like-button\\\" />\\n    </div>\\n  </div>\\n\";\nvar PERSON_TEMPLATE = \"\\n  <div class=\\\"person\\\">\\n    <div class=\\\"photo-container\\\">\\n      <img class=\\\"profile-photo\\\" />\\n      <div class=\\\"message-count\\\">1</div>\\n    </div>\\n    <div class=\\\"caption\\\"></div>\\n  </div>\\n\";\n\nComponent.register(\"chat-banner\", function (_Component) {\n  _inherits(ChatBanner, _Component);\n\n  function ChatBanner(config) {\n    _classCallCheck(this, ChatBanner);\n\n    var _this = _possibleConstructorReturn(this, (ChatBanner.__proto__ || Object.getPrototypeOf(ChatBanner)).call(this, config));\n\n    Object.assign(_this, Utils.mergePropertiesWithDefaults({\n      charAnimationInterval: 50,\n      passButton: null,\n      likeButton: null,\n      isInteractive: false,\n      buttonAnimation: \"alternate growing\",\n      messageShouldType: true,\n      photoShouldPulse: true,\n      showMessageCount: true\n    }, config));\n    _this.photoStyles = config.photoStyles || {};\n    _this.captionStyles = config.captionStyles || {};\n    _this.buttonStyles = config.buttonStyles || {};\n    _this.messageCountStyle = config.messageCountStyle || {};\n    _this.people = (config.people || []).filter(function (_ref) {\n      var image = _ref.image;\n      return image != null;\n    }).map(function (person) {\n      var personData = Object.assign({\n        image: null,\n        caption: \"\",\n        personEl: null,\n        captionEl: null,\n        currentTextTypingIndex: 0,\n        currentlyTyping: false\n      }, person);\n      personData.caption = personData.caption || \"\";\n      return personData;\n    });\n    _this.startIfReady = false;\n    _this.loading = true;\n    _this.animating = false;\n    // Timeout for restarting button animation after user interaction.\n    _this.buttonAnimationTimeout = null;\n    // Timeout for restarting automatic people switching after user interaction.\n    _this.peopleSwitchTimeout = null;\n    _this.currentPersonIndex = 0;\n    _this.passButtonEl = null;\n    _this.likeButtonEl = null;\n    _this.switchPeople = true;\n    _this.numLikes = 0;\n    _this.numPasses = 0;\n    return _this;\n  }\n\n  _createClass(ChatBanner, [{\n    key: \"load\",\n    value: function load() {\n      var imagesLoadedPromises = this.people.map(function (_ref2) {\n        var image = _ref2.image;\n        return image;\n      }).concat([this.passButton, this.likeButton]).filter(function (url) {\n        return url;\n      }).map(function (url) {\n        return Utils.loadImage(url);\n      });\n      this.imagesLoadedPromise = Promise.all(imagesLoadedPromises);\n      return this.imagesLoadedPromise;\n    }\n\n    // Proper way to set the contents of a person's caption element.\n\n  }, {\n    key: \"setPersonCaption\",\n    value: function setPersonCaption(person, currentTextTypingIndex) {\n      var captionEl = person.captionEl;\n\n      var sliceResult = __WEBPACK_IMPORTED_MODULE_0__src_js_lib_text_anim__[\"b\" /* domSlice */](person.captionDom, currentTextTypingIndex);\n\n      __WEBPACK_IMPORTED_MODULE_0__src_js_lib_text_anim__[\"d\" /* moveChildrenFromNode */](captionEl, sliceResult.tree);\n    }\n\n    // Proper way to read the number of \"typable\" characters in a person's caption element.\n\n  }, {\n    key: \"getPersonCaptionLen\",\n    value: function getPersonCaptionLen(person) {\n      return person.captionDomLen;\n    }\n  }, {\n    key: \"layout\",\n    value: function layout(parentNode) {\n      var _this2 = this;\n\n      _get(ChatBanner.prototype.__proto__ || Object.getPrototypeOf(ChatBanner.prototype), \"layout\", this).call(this, parentNode);\n      this.el = DOMUtils.strToElement(TEMPLATE);\n      this.peopleEl = this.el.querySelector(\".people\");\n      this.people = this.people.map(function (person) {\n        var caption = person.caption,\n            image = person.image;\n\n        var personEl = person.personEl = DOMUtils.strToElement(PERSON_TEMPLATE);\n        var imageEl = person.imageEl = personEl.querySelector(\".profile-photo\");\n        var captionEl = person.captionEl = personEl.querySelector(\".caption\");\n        imageEl.src = image;\n        Object.assign(imageEl.style, _this2.photoStyles);\n\n        var messageCountEl = personEl.querySelector(\".message-count\");\n        messageCountEl.classList.toggle(\"visible\", _this2.showMessageCount);\n        Object.assign(messageCountEl.style, _this2.messageCountStyle);\n\n        person.captionDom = __WEBPACK_IMPORTED_MODULE_0__src_js_lib_text_anim__[\"c\" /* getTextDom */](caption);\n        person.captionDomLen = __WEBPACK_IMPORTED_MODULE_0__src_js_lib_text_anim__[\"a\" /* domCountCharacters */](person.captionDom);\n\n        var curIndex = _this2.messageShouldType ? 0 : _this2.getPersonCaptionLen(person);\n        _this2.setPersonCaption(person, curIndex);\n        Object.assign(captionEl.style, _this2.captionStyles);\n        person.currentTextTypingIndex = curIndex;\n        _this2.peopleEl.appendChild(person.personEl);\n        return person;\n      });\n\n      if (this.people.length > 0) {\n        this.people.forEach(function (_ref3, i) {\n          var personEl = _ref3.personEl;\n\n          if (i === _this2.currentPersonIndex) return;\n          personEl.classList.add(\"staged\", \"down\");\n        });\n      }\n\n      this.passButtonEl = this.el.querySelector(\".pass-button\");\n      if (this.passButton != null) {\n        this.passButtonEl.src = this.passButton;\n        Object.assign(this.passButtonEl.style, this.buttonStyles);\n      } else {\n        this.passButtonEl.remove();\n      }\n\n      this.likeButtonEl = this.el.querySelector(\".like-button\");\n      if (this.likeButton != null) {\n        this.likeButtonEl.src = this.likeButton;\n        Object.assign(this.likeButtonEl.style, this.buttonStyles);\n      } else {\n        this.likeButtonEl.remove();\n      }\n\n      this.parentNode.appendChild(this.el);\n      // If the user doesn't explicitly set the caption font size, then size automatically so that the longest\n      // message fits completely. All images must have non-zero widths/heights (i.e. be fully rendered) in order\n      // to accurately determine font size. Wrap in the imagesLoaded promise so the imagesRenderedPromise time-\n      // out starts after all images are loaded.\n      var imagesRenderedPromise = this.imagesLoadedPromise.then(function () {\n        var imagesRenderedPromises = [].concat(_toConsumableArray(_this2.el.querySelectorAll(\"img\"))).map(function (img) {\n          return DOMUtils.imageIsRendered(img, 1000);\n        });\n\n        // TODO(brian): Investigate how this promise can reject.\n        return Promise.all(imagesRenderedPromises).catch(function () {});\n      });\n\n      imagesRenderedPromise.finally(function () {\n        _this2.resizeFont();\n        _this2.addEventListeners();\n        _this2.loading = false;\n        _this2.el.classList.remove(\"loading\");\n        if (_this2.startIfReady) {\n          _this2.start();\n        }\n      });\n    }\n  }, {\n    key: \"resizeFont\",\n    value: function resizeFont() {\n      var _this3 = this;\n\n      if (this.captionStyles[\"font-size\"] != null) return;\n      this.people.forEach(function (_ref4) {\n        var captionEl = _ref4.captionEl,\n            caption = _ref4.caption;\n        return captionEl.innerHTML = caption;\n      });\n\n      // Same as DOMUtils.getFontSizeToFit, but applies a class to the element temporarily.\n      //\n      // RATIONALE:\n      // We need to do this because, in chat-banner.less, we unset the \"display: flex\" style for the caption\n      // element and its children. And we set \"height: auto\" to bring the caption back into vertical centering.\n      // But this breaks the algorithm used by DOMUtils.getFontSizeToFit. So we temporarily set \"height: 100%\"\n      // while resizing.\n      var getFontSizeToFit = function getFontSizeToFit(el, tempClass) {\n        el.classList.add(tempClass);\n        var fontSize = DOMUtils.getFontSizeToFit(el);\n        el.classList.remove(tempClass);\n        return fontSize;\n      };\n\n      var fontSizeThatFits = Math.min.apply(Math, _toConsumableArray(this.people.map(function (_ref5) {\n        var captionEl = _ref5.captionEl;\n        return getFontSizeToFit(captionEl, \"during-resize\");\n      })));\n\n      this.people.forEach(function (person) {\n        var captionEl = person.captionEl,\n            currentTextTypingIndex = person.currentTextTypingIndex;\n\n\n        _this3.setPersonCaption(person, currentTextTypingIndex);\n\n        captionEl.style.fontSize = fontSizeThatFits + \"px\";\n      });\n    }\n  }, {\n    key: \"startButtonAnimation\",\n    value: function startButtonAnimation() {\n      if (this.buttonAnimation === \"alternate growing\") {\n        if (this.likeButton != null) {\n          this.likeButtonEl.classList.add(\"alternate-growing\");\n        }\n\n        if (this.passButton != null) {\n          this.passButtonEl.classList.add(\"alternate-growing\");\n        }\n      } else if (this.buttonAnimation === \"pulse like\" && this.likeButton != null) {\n        this.likeButtonEl.classList.add(\"pulse\");\n      }\n    }\n  }, {\n    key: \"stopButtonAnimation\",\n    value: function stopButtonAnimation() {\n      clearTimeout(this.buttonAnimationTimeout);\n      if (this.passButton != null) {\n        this.passButtonEl.classList.remove(\"alternate-growing\");\n      }\n\n      if (this.likeButton != null) {\n        this.likeButtonEl.classList.remove(\"alternate-growing\");\n        this.likeButtonEl.classList.remove(\"pulse\");\n      }\n    }\n  }, {\n    key: \"startSwitchingPeople\",\n    value: function startSwitchingPeople() {\n      this.switchPeople = true;\n      this.animateNextWhenReady();\n    }\n  }, {\n    key: \"stopSwitchingPeople\",\n    value: function stopSwitchingPeople() {\n      this.switchPeople = false;\n      clearTimeout(this.peopleSwitchTimeout);\n    }\n  }, {\n    key: \"startPhotoAnimation\",\n    value: function startPhotoAnimation() {\n      if (!this.photoShouldPulse) return;\n      [].concat(_toConsumableArray(this.el.querySelectorAll(\".profile-photo\"))).forEach(function (el) {\n        return el.classList.add(\"pulse\");\n      });\n    }\n  }, {\n    key: \"stopPhotoAnimation\",\n    value: function stopPhotoAnimation() {\n      if (!this.photoShouldPulse) return;\n      [].concat(_toConsumableArray(this.el.querySelectorAll(\".profile-photo\"))).forEach(function (el) {\n        return el.classList.remove(\"pulse\");\n      });\n    }\n\n    /** @param {(\"up\"|\"down\"|\"left\"|\"right\")} direction - Animates the current person out and the next\n        person in given a particular animation direction. */\n\n  }, {\n    key: \"animateNext\",\n    value: function animateNext() {\n      var direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : \"up\";\n\n      if (this.people.length <= 1) return;\n      var setAnimationStartClasses = function setAnimationStartClasses(el, classes) {\n        // Set classes that specify the element's starting position for the animation.\n        [\"staged\", \"exited\", \"up\", \"right\", \"down\", \"left\"].forEach(function (className) {\n          return el.classList.remove(className);\n        });\n        classes.forEach(function (className) {\n          return el.classList.add(className);\n        });\n        DOMUtils.forceReflow(el);\n      };\n\n      var currentPerson = this.people[this.currentPersonIndex];\n      this.currentPersonIndex = (this.currentPersonIndex + 1) % this.people.length;\n      var nextPerson = this.people[this.currentPersonIndex];\n      setAnimationStartClasses(currentPerson.personEl, [direction]);\n      setAnimationStartClasses(nextPerson.personEl, [direction, \"staged\"]);\n      // Start the animation by transitioning the current element into the \"exited\" position and the next\n      // element into the visible (non-\"staged\", non-\"exited\") position.\n      currentPerson.personEl.classList.add(\"exited\");\n      nextPerson.personEl.classList.remove(\"staged\");\n      DOMUtils.forceReflow(this.peopleEl);\n      if (this.messageShouldType) {\n        currentPerson.currentlyTyping = false;\n        nextPerson.currentlyTyping = true;\n        this.resetTypingMessage(nextPerson);\n      }\n\n      this.emit(\"personChanged\");\n      this.animateNextWhenReady(direction);\n    }\n  }, {\n    key: \"animateNextWhenReady\",\n    value: function animateNextWhenReady() {\n      var _this4 = this;\n\n      var direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : \"up\";\n\n      if (!this.switchPeople || this.people.length <= 1) return;\n      var currentPerson = this.people[this.currentPersonIndex];\n      var messageTypeTime = this.messageShouldType ? this.getPersonCaptionLen(currentPerson) * this.charAnimationInterval : 0;\n      var timeToSwitch = Math.max(PEOPLE_SWITCH_INTERVAL, PERSON_TRANSITION_DURATION + messageTypeTime + MIN_MESSAGE_DISPLAY_TIME);\n      this.peopleSwitchTimeout = setTimeout(function () {\n        return _this4.animateNext(direction);\n      }, timeToSwitch);\n    }\n  }, {\n    key: \"resetTypingMessage\",\n    value: function resetTypingMessage(person) {\n      var _this5 = this;\n\n      if (!person.currentlyTyping) return;\n      person.currentTextTypingIndex = 0;\n      person.captionEl.innerHTML = \"\";\n      var addNextLetter = function addNextLetter() {\n        if (!person.currentlyTyping) {\n          person.captionEl.innerHTML = person.caption;\n          return;\n        }\n\n        if (person.currentTextTypingIndex < _this5.getPersonCaptionLen(person)) {\n          _this5.setPersonCaption(person, person.currentTextTypingIndex + 1);\n\n          person.currentTextTypingIndex += 1;\n          setTimeout(addNextLetter, _this5.charAnimationInterval);\n        } else {\n          setTimeout(function () {\n            return _this5.resetTypingMessage(person);\n          }, RESTART_TYPING_INTERVAL);\n        }\n      };\n\n      setTimeout(addNextLetter, PERSON_TRANSITION_DURATION);\n    }\n  }, {\n    key: \"startTyping\",\n    value: function startTyping() {\n      if (!this.messageShouldType || this.people.length < 1) return;\n      var currentPerson = this.people[this.currentPersonIndex];\n      currentPerson.currentlyTyping = true;\n      this.resetTypingMessage(currentPerson);\n    }\n  }, {\n    key: \"stopTyping\",\n    value: function stopTyping() {\n      this.people.forEach(function (person) {\n        person.currentlyTyping = false;\n      });\n    }\n  }, {\n    key: \"recordInteraction\",\n    value: function recordInteraction() {\n      var _this6 = this;\n\n      _get(ChatBanner.prototype.__proto__ || Object.getPrototypeOf(ChatBanner.prototype), \"recordInteraction\", this).call(this);\n      this.stopButtonAnimation();\n      this.buttonAnimationTimeout = setTimeout(function () {\n        return _this6.startButtonAnimation();\n      }, BUTTON_ANIMATION_INTERACTION_DELAY);\n      this.stopSwitchingPeople();\n      this.peopleSwitchTimeout = setTimeout(function () {\n        return _this6.startSwitchingPeople();\n      }, PEOPLE_SWITCH_INTERACTION_DELAY);\n    }\n  }, {\n    key: \"addEventListeners\",\n    value: function addEventListeners() {\n      var _this7 = this;\n\n      if (!this.isInteractive || this.people.length === 0) return;\n      this.el.querySelector(\".buttons\").addEventListener(\"click\", function (e) {\n        var animationDirection = void 0;\n        if (e.target.classList.contains(\"pass-button\")) {\n          _this7.numPasses += 1;\n          animationDirection = \"left\";\n        } else if (e.target.classList.contains(\"like-button\")) {\n          _this7.numLikes += 1;\n          animationDirection = \"right\";\n        } else {\n          return;\n        }\n\n        _this7.recordInteraction();\n        _this7.animateNext(animationDirection);\n\n        var totalChoicesMade = _this7.numPasses + _this7.numLikes;\n        _this7.emit(\"passedOrLiked\", {\n          passes: _this7.numPasses,\n          likes: _this7.numLikes,\n          choices: totalChoicesMade\n        });\n        // The allChoicesMade event is emitted when the user has liked or passed as many people as there are.\n        // Since the animation runs in a loop automatically if the user doesn't interact, the choices aren't\n        // necessarily for distinct people.\n        if (_this7.people.length === totalChoicesMade) {\n          _this7.emit(\"allChoicesMade\");\n        }\n      });\n    }\n  }, {\n    key: \"resize\",\n    value: function resize() {\n      this.resizeFont();\n    }\n  }, {\n    key: \"restart\",\n    value: function restart() {\n      this.stop();\n      this.start();\n    }\n  }, {\n    key: \"start\",\n    value: function start() {\n      this.startIfReady = true;\n      if (this.loading || this.animating) return;\n      this.animating = true;\n      this.startButtonAnimation();\n      this.startTyping();\n      this.startPhotoAnimation();\n      this.startSwitchingPeople();\n    }\n  }, {\n    key: \"stop\",\n    value: function stop() {\n      this.startIfReady = false;\n      this.stopButtonAnimation();\n      this.stopTyping();\n      this.stopPhotoAnimation();\n      this.stopSwitchingPeople();\n      this.animating = false;\n    }\n  }]);\n\n  return ChatBanner;\n}(Component));\n\n/***/ }),\n\n/***/ 123:\n/***/ (function(module, exports) {\n\n// removed by extract-text-webpack-plugin\n\n/***/ }),\n\n/***/ 30:\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* unused harmony export unicodeLen */\n/* unused harmony export unicodeSlice */\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return domCountCharacters; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"b\", function() { return domSlice; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"d\", function() { return moveChildrenFromNode; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"c\", function() { return getTextDom; });\nfunction _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }\n\nvar DOMUtils = window.Liftoff.DOMUtils;\n\n\nvar isVariationSelector = function isVariationSelector(cstr) {\n  if (cstr.length !== 1) {\n    return false;\n  }\n\n  var code = cstr.charCodeAt(0);\n\n  return 0xfe00 <= code && code <= 0xfe0f;\n};\n\n// Expects an array generated by [...str].\n// Merges variation selectors into the previous character so that they do not occupy their own slot in the\n// array.\n// REFERENCE: See\n// - https://blog.jonnew.com/posts/poo-dot-length-equals-two\n// - https://codepoints.net/U+fe0f\n// - https://codepoints.net/variation_selectors\nvar mergeVariationSelectors = function mergeVariationSelectors(unicodechars) {\n  return unicodechars.reduce(function (acc, cstr) {\n    if (acc.length > 0 && isVariationSelector(cstr)) {\n      acc[acc.length - 1] += cstr;\n    } else {\n      acc.push(cstr);\n    }\n    return acc;\n  }, []);\n};\n\nvar unicodeLen = function unicodeLen(str) {\n  return mergeVariationSelectors([].concat(_toConsumableArray(str))).length;\n};\n\n// Returns a string consisting of the leftmost 'n' Unicode characters of 'str'.\nvar unicodeSlice = function unicodeSlice(str, n) {\n  return mergeVariationSelectors([].concat(_toConsumableArray(str))).slice(0, n).join(\"\");\n};\n\nvar domCountCharacters = function domCountCharacters(tree) {\n  if (tree == null) {\n    return 0;\n  }\n\n  if (tree.nodeType === Node.TEXT_NODE) {\n    return unicodeLen(tree.nodeValue);\n  }\n\n  var numChars = 0;\n\n  if (tree.hasChildNodes()) {\n    [].concat(_toConsumableArray(tree.childNodes)).forEach(function (child) {\n      numChars += domCountCharacters(child);\n    });\n  }\n\n  return numChars;\n};\n\n// Return a copy of the tree, such that only the leftmost n characters in the text elements are preserved.\n//\n// The return value includes the new tree and the number of characters in it.\n//\nvar domSlice = function domSlice(tree, n) {\n  if (tree == null) {\n    return { tree: null, charcount: 0 };\n  }\n\n  if (tree.nodeType === Node.TEXT_NODE) {\n    var text = tree.nodeValue;\n    var textLen = unicodeLen(text);\n    var numCharsToPreserve = Math.min(n, textLen);\n    var newText = unicodeSlice(text, numCharsToPreserve);\n\n    return {\n      tree: document.createTextNode(newText),\n      charCount: numCharsToPreserve\n    };\n  }\n\n  var ret = {\n    tree: tree.cloneNode(false),\n    charCount: 0\n  };\n\n  if (tree.hasChildNodes()) {\n    var children = [].concat(_toConsumableArray(tree.childNodes));\n    children.forEach(function (child) {\n      var slicedChild = domSlice(child, n - ret.charCount);\n\n      ret.charCount += slicedChild.charCount;\n\n      if (slicedChild.charCount > 0 || child.textContent.length === 0) {\n        ret.tree.appendChild(slicedChild.tree);\n      }\n    });\n  }\n\n  return ret;\n};\n\n// Removes any children of dstnode and moves the children of srcnode into dstnode.\nvar moveChildrenFromNode = function moveChildrenFromNode(dstnode, srcnode) {\n  dstnode = DOMUtils.empty(dstnode);\n\n  while (srcnode.firstChild) {\n    var child = srcnode.removeChild(srcnode.firstChild);\n\n    dstnode.appendChild(child);\n  }\n\n  return dstnode;\n};\n\nvar getTextDom = function getTextDom(caption) {\n  var div = document.createElement(\"div\");\n  div.innerHTML = caption;\n\n  return div;\n};\n\n/***/ })\n\n/******/ });\n\n\n// WEBPACK FOOTER //\n// chat-banner-b36e10c3fbb944f9d5fa.js"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 121);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap bada3fc95fc820b6f189","import * as TextAnim from \"../../src/js/lib/text_anim\";\n\nconst { Component, DOMUtils, Utils } = window.Liftoff;\n\n// Duration (in ms) of gap between restarts of the typing effect.\nconst RESTART_TYPING_INTERVAL = 3000;\n// Minimum interval (in ms) between switching profiles/captions.\nconst PEOPLE_SWITCH_INTERVAL = 3200;\n// Minimum time to show a person _after_ the message has completely finished typing.\nconst MIN_MESSAGE_DISPLAY_TIME = 1000;\n// Interval (in ms) after which to restart the button animation if the user hasn't interacted.\nconst BUTTON_ANIMATION_INTERACTION_DELAY = 3000;\n// Interval (in ms) after which to restart the profile switching animation if the user hasn't interacted.\nconst PEOPLE_SWITCH_INTERACTION_DELAY = 8000;\n// Duration of the transition between people. Used to delay the message typing effect to only start typing\n// after the person has fully transitioned in.\nconst PERSON_TRANSITION_DURATION = 500;\n\nconst TEMPLATE = `\n  <div class=\"chat-banner loading\">\n    <div class=\"people\"></div>\n    <div class=\"buttons\">\n      <img class=\"pass-button\" />\n      <img class=\"like-button\" />\n    </div>\n  </div>\n`;\nconst PERSON_TEMPLATE = `\n  <div class=\"person\">\n    <div class=\"photo-container\">\n      <img class=\"profile-photo\" />\n      <div class=\"message-count\">1</div>\n    </div>\n    <div class=\"caption\"></div>\n  </div>\n`;\n\nComponent.register(\n  \"chat-banner\",\n  class ChatBanner extends Component {\n    constructor(config) {\n      super(config);\n      Object.assign(\n        this,\n        Utils.mergePropertiesWithDefaults(\n          {\n            charAnimationInterval: 50,\n            passButton: null,\n            likeButton: null,\n            isInteractive: false,\n            buttonAnimation: \"alternate growing\",\n            messageShouldType: true,\n            photoShouldPulse: true,\n            showMessageCount: true,\n          },\n          config\n        )\n      );\n      this.photoStyles = config.photoStyles || {};\n      this.captionStyles = config.captionStyles || {};\n      this.buttonStyles = config.buttonStyles || {};\n      this.messageCountStyle = config.messageCountStyle || {};\n      this.people = (config.people || [])\n        .filter(({ image }) => image != null)\n        .map((person) => {\n          const personData = Object.assign(\n            {\n              image: null,\n              caption: \"\",\n              personEl: null,\n              captionEl: null,\n              currentTextTypingIndex: 0,\n              currentlyTyping: false,\n            },\n            person\n          );\n          personData.caption = personData.caption || \"\";\n          return personData;\n        });\n      this.startIfReady = false;\n      this.loading = true;\n      this.animating = false;\n      // Timeout for restarting button animation after user interaction.\n      this.buttonAnimationTimeout = null;\n      // Timeout for restarting automatic people switching after user interaction.\n      this.peopleSwitchTimeout = null;\n      this.currentPersonIndex = 0;\n      this.passButtonEl = null;\n      this.likeButtonEl = null;\n      this.switchPeople = true;\n      this.numLikes = 0;\n      this.numPasses = 0;\n    }\n\n    load() {\n      const imagesLoadedPromises = this.people\n        .map(({ image }) => image)\n        .concat([this.passButton, this.likeButton])\n        .filter((url) => url)\n        .map((url) => Utils.loadImage(url));\n      this.imagesLoadedPromise = Promise.all(imagesLoadedPromises);\n      return this.imagesLoadedPromise;\n    }\n\n    // Proper way to set the contents of a person's caption element.\n    setPersonCaption(person, currentTextTypingIndex) {\n      const captionEl = person.captionEl;\n\n      const sliceResult = TextAnim.domSlice(\n        person.captionDom,\n        currentTextTypingIndex\n      );\n\n      TextAnim.moveChildrenFromNode(captionEl, sliceResult.tree);\n    }\n\n    // Proper way to read the number of \"typable\" characters in a person's caption element.\n    getPersonCaptionLen(person) {\n      return person.captionDomLen;\n    }\n\n    layout(parentNode) {\n      super.layout(parentNode);\n      this.el = DOMUtils.strToElement(TEMPLATE);\n      this.peopleEl = this.el.querySelector(\".people\");\n      this.people = this.people.map((person) => {\n        const { caption, image } = person;\n        const personEl = (person.personEl =\n          DOMUtils.strToElement(PERSON_TEMPLATE));\n        const imageEl = (person.imageEl =\n          personEl.querySelector(\".profile-photo\"));\n        const captionEl = (person.captionEl =\n          personEl.querySelector(\".caption\"));\n        imageEl.src = image;\n        Object.assign(imageEl.style, this.photoStyles);\n\n        const messageCountEl = personEl.querySelector(\".message-count\");\n        messageCountEl.classList.toggle(\"visible\", this.showMessageCount);\n        Object.assign(messageCountEl.style, this.messageCountStyle);\n\n        person.captionDom = TextAnim.getTextDom(caption);\n        person.captionDomLen = TextAnim.domCountCharacters(person.captionDom);\n\n        const curIndex = this.messageShouldType\n          ? 0\n          : this.getPersonCaptionLen(person);\n        this.setPersonCaption(person, curIndex);\n        Object.assign(captionEl.style, this.captionStyles);\n        person.currentTextTypingIndex = curIndex;\n        this.peopleEl.appendChild(person.personEl);\n        return person;\n      });\n\n      if (this.people.length > 0) {\n        this.people.forEach(({ personEl }, i) => {\n          if (i === this.currentPersonIndex) return;\n          personEl.classList.add(\"staged\", \"down\");\n        });\n      }\n\n      this.passButtonEl = this.el.querySelector(\".pass-button\");\n      if (this.passButton != null) {\n        this.passButtonEl.src = this.passButton;\n        Object.assign(this.passButtonEl.style, this.buttonStyles);\n      } else {\n        this.passButtonEl.remove();\n      }\n\n      this.likeButtonEl = this.el.querySelector(\".like-button\");\n      if (this.likeButton != null) {\n        this.likeButtonEl.src = this.likeButton;\n        Object.assign(this.likeButtonEl.style, this.buttonStyles);\n      } else {\n        this.likeButtonEl.remove();\n      }\n\n      this.parentNode.appendChild(this.el);\n      // If the user doesn't explicitly set the caption font size, then size automatically so that the longest\n      // message fits completely. All images must have non-zero widths/heights (i.e. be fully rendered) in order\n      // to accurately determine font size. Wrap in the imagesLoaded promise so the imagesRenderedPromise time-\n      // out starts after all images are loaded.\n      const imagesRenderedPromise = this.imagesLoadedPromise.then(() => {\n        const imagesRenderedPromises = [...this.el.querySelectorAll(\"img\")].map(\n          (img) => {\n            return DOMUtils.imageIsRendered(img, 1000);\n          }\n        );\n\n        // TODO(brian): Investigate how this promise can reject.\n        return Promise.all(imagesRenderedPromises).catch(() => {});\n      });\n\n      imagesRenderedPromise.finally(() => {\n        this.resizeFont();\n        this.addEventListeners();\n        this.loading = false;\n        this.el.classList.remove(\"loading\");\n        if (this.startIfReady) {\n          this.start();\n        }\n      });\n    }\n\n    resizeFont() {\n      if (this.captionStyles[\"font-size\"] != null) return;\n      this.people.forEach(\n        ({ captionEl, caption }) => (captionEl.innerHTML = caption)\n      );\n\n      // Same as DOMUtils.getFontSizeToFit, but applies a class to the element temporarily.\n      //\n      // RATIONALE:\n      // We need to do this because, in chat-banner.less, we unset the \"display: flex\" style for the caption\n      // element and its children. And we set \"height: auto\" to bring the caption back into vertical centering.\n      // But this breaks the algorithm used by DOMUtils.getFontSizeToFit. So we temporarily set \"height: 100%\"\n      // while resizing.\n      const getFontSizeToFit = (el, tempClass) => {\n        el.classList.add(tempClass);\n        const fontSize = DOMUtils.getFontSizeToFit(el);\n        el.classList.remove(tempClass);\n        return fontSize;\n      };\n\n      const fontSizeThatFits = Math.min(\n        ...this.people.map(({ captionEl }) =>\n          getFontSizeToFit(captionEl, \"during-resize\")\n        )\n      );\n\n      this.people.forEach((person) => {\n        const { captionEl, currentTextTypingIndex } = person;\n\n        this.setPersonCaption(person, currentTextTypingIndex);\n\n        captionEl.style.fontSize = `${fontSizeThatFits}px`;\n      });\n    }\n\n    startButtonAnimation() {\n      if (this.buttonAnimation === \"alternate growing\") {\n        if (this.likeButton != null) {\n          this.likeButtonEl.classList.add(\"alternate-growing\");\n        }\n\n        if (this.passButton != null) {\n          this.passButtonEl.classList.add(\"alternate-growing\");\n        }\n      } else if (\n        this.buttonAnimation === \"pulse like\" &&\n        this.likeButton != null\n      ) {\n        this.likeButtonEl.classList.add(\"pulse\");\n      }\n    }\n\n    stopButtonAnimation() {\n      clearTimeout(this.buttonAnimationTimeout);\n      if (this.passButton != null) {\n        this.passButtonEl.classList.remove(\"alternate-growing\");\n      }\n\n      if (this.likeButton != null) {\n        this.likeButtonEl.classList.remove(\"alternate-growing\");\n        this.likeButtonEl.classList.remove(\"pulse\");\n      }\n    }\n\n    startSwitchingPeople() {\n      this.switchPeople = true;\n      this.animateNextWhenReady();\n    }\n\n    stopSwitchingPeople() {\n      this.switchPeople = false;\n      clearTimeout(this.peopleSwitchTimeout);\n    }\n\n    startPhotoAnimation() {\n      if (!this.photoShouldPulse) return;\n      [...this.el.querySelectorAll(\".profile-photo\")].forEach((el) =>\n        el.classList.add(\"pulse\")\n      );\n    }\n\n    stopPhotoAnimation() {\n      if (!this.photoShouldPulse) return;\n      [...this.el.querySelectorAll(\".profile-photo\")].forEach((el) =>\n        el.classList.remove(\"pulse\")\n      );\n    }\n\n    /** @param {(\"up\"|\"down\"|\"left\"|\"right\")} direction - Animates the current person out and the next\n        person in given a particular animation direction. */\n    animateNext(direction = \"up\") {\n      if (this.people.length <= 1) return;\n      const setAnimationStartClasses = (el, classes) => {\n        // Set classes that specify the element's starting position for the animation.\n        [\"staged\", \"exited\", \"up\", \"right\", \"down\", \"left\"].forEach(\n          (className) => el.classList.remove(className)\n        );\n        classes.forEach((className) => el.classList.add(className));\n        DOMUtils.forceReflow(el);\n      };\n\n      const currentPerson = this.people[this.currentPersonIndex];\n      this.currentPersonIndex =\n        (this.currentPersonIndex + 1) % this.people.length;\n      const nextPerson = this.people[this.currentPersonIndex];\n      setAnimationStartClasses(currentPerson.personEl, [direction]);\n      setAnimationStartClasses(nextPerson.personEl, [direction, \"staged\"]);\n      // Start the animation by transitioning the current element into the \"exited\" position and the next\n      // element into the visible (non-\"staged\", non-\"exited\") position.\n      currentPerson.personEl.classList.add(\"exited\");\n      nextPerson.personEl.classList.remove(\"staged\");\n      DOMUtils.forceReflow(this.peopleEl);\n      if (this.messageShouldType) {\n        currentPerson.currentlyTyping = false;\n        nextPerson.currentlyTyping = true;\n        this.resetTypingMessage(nextPerson);\n      }\n\n      this.emit(\"personChanged\");\n      this.animateNextWhenReady(direction);\n    }\n\n    animateNextWhenReady(direction = \"up\") {\n      if (!this.switchPeople || this.people.length <= 1) return;\n      const currentPerson = this.people[this.currentPersonIndex];\n      const messageTypeTime = this.messageShouldType\n        ? this.getPersonCaptionLen(currentPerson) * this.charAnimationInterval\n        : 0;\n      const timeToSwitch = Math.max(\n        PEOPLE_SWITCH_INTERVAL,\n        PERSON_TRANSITION_DURATION + messageTypeTime + MIN_MESSAGE_DISPLAY_TIME\n      );\n      this.peopleSwitchTimeout = setTimeout(\n        () => this.animateNext(direction),\n        timeToSwitch\n      );\n    }\n\n    resetTypingMessage(person) {\n      if (!person.currentlyTyping) return;\n      person.currentTextTypingIndex = 0;\n      person.captionEl.innerHTML = \"\";\n      const addNextLetter = () => {\n        if (!person.currentlyTyping) {\n          person.captionEl.innerHTML = person.caption;\n          return;\n        }\n\n        if (person.currentTextTypingIndex < this.getPersonCaptionLen(person)) {\n          this.setPersonCaption(person, person.currentTextTypingIndex + 1);\n\n          person.currentTextTypingIndex += 1;\n          setTimeout(addNextLetter, this.charAnimationInterval);\n        } else {\n          setTimeout(\n            () => this.resetTypingMessage(person),\n            RESTART_TYPING_INTERVAL\n          );\n        }\n      };\n\n      setTimeout(addNextLetter, PERSON_TRANSITION_DURATION);\n    }\n\n    startTyping() {\n      if (!this.messageShouldType || this.people.length < 1) return;\n      const currentPerson = this.people[this.currentPersonIndex];\n      currentPerson.currentlyTyping = true;\n      this.resetTypingMessage(currentPerson);\n    }\n\n    stopTyping() {\n      this.people.forEach((person) => {\n        person.currentlyTyping = false;\n      });\n    }\n\n    recordInteraction() {\n      super.recordInteraction();\n      this.stopButtonAnimation();\n      this.buttonAnimationTimeout = setTimeout(\n        () => this.startButtonAnimation(),\n        BUTTON_ANIMATION_INTERACTION_DELAY\n      );\n      this.stopSwitchingPeople();\n      this.peopleSwitchTimeout = setTimeout(\n        () => this.startSwitchingPeople(),\n        PEOPLE_SWITCH_INTERACTION_DELAY\n      );\n    }\n\n    addEventListeners() {\n      if (!this.isInteractive || this.people.length === 0) return;\n      this.el.querySelector(\".buttons\").addEventListener(\"click\", (e) => {\n        let animationDirection;\n        if (e.target.classList.contains(\"pass-button\")) {\n          this.numPasses += 1;\n          animationDirection = \"left\";\n        } else if (e.target.classList.contains(\"like-button\")) {\n          this.numLikes += 1;\n          animationDirection = \"right\";\n        } else {\n          return;\n        }\n\n        this.recordInteraction();\n        this.animateNext(animationDirection);\n\n        const totalChoicesMade = this.numPasses + this.numLikes;\n        this.emit(\"passedOrLiked\", {\n          passes: this.numPasses,\n          likes: this.numLikes,\n          choices: totalChoicesMade,\n        });\n        // The allChoicesMade event is emitted when the user has liked or passed as many people as there are.\n        // Since the animation runs in a loop automatically if the user doesn't interact, the choices aren't\n        // necessarily for distinct people.\n        if (this.people.length === totalChoicesMade) {\n          this.emit(\"allChoicesMade\");\n        }\n      });\n    }\n\n    resize() {\n      this.resizeFont();\n    }\n\n    restart() {\n      this.stop();\n      this.start();\n    }\n\n    start() {\n      this.startIfReady = true;\n      if (this.loading || this.animating) return;\n      this.animating = true;\n      this.startButtonAnimation();\n      this.startTyping();\n      this.startPhotoAnimation();\n      this.startSwitchingPeople();\n    }\n\n    stop() {\n      this.startIfReady = false;\n      this.stopButtonAnimation();\n      this.stopTyping();\n      this.stopPhotoAnimation();\n      this.stopSwitchingPeople();\n      this.animating = false;\n    }\n  }\n);\n\n\n\n// WEBPACK FOOTER //\n// ./components/chat-banner/chat-banner.js","const { DOMUtils } = window.Liftoff;\n\nconst isVariationSelector = (cstr) => {\n  if (cstr.length !== 1) {\n    return false;\n  }\n\n  const code = cstr.charCodeAt(0);\n\n  return 0xfe00 <= code && code <= 0xfe0f;\n};\n\n// Expects an array generated by [...str].\n// Merges variation selectors into the previous character so that they do not occupy their own slot in the\n// array.\n// REFERENCE: See\n// - https://blog.jonnew.com/posts/poo-dot-length-equals-two\n// - https://codepoints.net/U+fe0f\n// - https://codepoints.net/variation_selectors\nconst mergeVariationSelectors = (unicodechars) =>\n  unicodechars.reduce((acc, cstr) => {\n    if (acc.length > 0 && isVariationSelector(cstr)) {\n      acc[acc.length - 1] += cstr;\n    } else {\n      acc.push(cstr);\n    }\n    return acc;\n  }, []);\n\nexport const unicodeLen = (str) => mergeVariationSelectors([...str]).length;\n\n// Returns a string consisting of the leftmost 'n' Unicode characters of 'str'.\nexport const unicodeSlice = (str, n) =>\n  mergeVariationSelectors([...str])\n    .slice(0, n)\n    .join(\"\");\n\nexport const domCountCharacters = (tree) => {\n  if (tree == null) {\n    return 0;\n  }\n\n  if (tree.nodeType === Node.TEXT_NODE) {\n    return unicodeLen(tree.nodeValue);\n  }\n\n  let numChars = 0;\n\n  if (tree.hasChildNodes()) {\n    [...tree.childNodes].forEach((child) => {\n      numChars += domCountCharacters(child);\n    });\n  }\n\n  return numChars;\n};\n\n// Return a copy of the tree, such that only the leftmost n characters in the text elements are preserved.\n//\n// The return value includes the new tree and the number of characters in it.\n//\nexport const domSlice = (tree, n) => {\n  if (tree == null) {\n    return { tree: null, charcount: 0 };\n  }\n\n  if (tree.nodeType === Node.TEXT_NODE) {\n    const text = tree.nodeValue;\n    const textLen = unicodeLen(text);\n    const numCharsToPreserve = Math.min(n, textLen);\n    const newText = unicodeSlice(text, numCharsToPreserve);\n\n    return {\n      tree: document.createTextNode(newText),\n      charCount: numCharsToPreserve,\n    };\n  }\n\n  const ret = {\n    tree: tree.cloneNode(false),\n    charCount: 0,\n  };\n\n  if (tree.hasChildNodes()) {\n    const children = [...tree.childNodes];\n    children.forEach((child) => {\n      const slicedChild = domSlice(child, n - ret.charCount);\n\n      ret.charCount += slicedChild.charCount;\n\n      if (slicedChild.charCount > 0 || child.textContent.length === 0) {\n        ret.tree.appendChild(slicedChild.tree);\n      }\n    });\n  }\n\n  return ret;\n};\n\n// Removes any children of dstnode and moves the children of srcnode into dstnode.\nexport const moveChildrenFromNode = (dstnode, srcnode) => {\n  dstnode = DOMUtils.empty(dstnode);\n\n  while (srcnode.firstChild) {\n    const child = srcnode.removeChild(srcnode.firstChild);\n\n    dstnode.appendChild(child);\n  }\n\n  return dstnode;\n};\n\nexport const getTextDom = (caption) => {\n  const div = document.createElement(\"div\");\n  div.innerHTML = caption;\n\n  return div;\n};\n\n\n\n// WEBPACK FOOTER //\n// ./src/js/lib/text_anim.js"],"sourceRoot":""}