elwm / yt_clipper

// modules are defined as an array
// [ module function, map of requires ]
//
// map of requires is short require name -> numeric require
//
// anything defined in a previous bundle is accessed via the
// orig method which is the require for previous bundles
parcelRequire = (function (modules, cache, entry, globalName) {
  // Save the require from previous bundle to this closure if any
  var previousRequire = typeof parcelRequire === 'function' && parcelRequire;
  var nodeRequire = typeof require === 'function' && require;

  function newRequire(name, jumped) {
    if (!cache[name]) {
      if (!modules[name]) {
        // if we cannot find the module within our internal map or
        // cache jump to the current global require ie. the last bundle
        // that was added to the page.
        var currentRequire = typeof parcelRequire === 'function' && parcelRequire;
        if (!jumped && currentRequire) {
          return currentRequire(name, true);
        }

        // If there are other bundles on this page the require from the
        // previous one is saved to 'previousRequire'. Repeat this as
        // many times as there are bundles until the module is found or
        // we exhaust the require chain.
        if (previousRequire) {
          return previousRequire(name, true);
        }

        // Try the node require function if it exists.
        if (nodeRequire && typeof name === 'string') {
          return nodeRequire(name);
        }

        var err = new Error('Cannot find module \'' + name + '\'');
        err.code = 'MODULE_NOT_FOUND';
        throw err;
      }

      localRequire.resolve = resolve;
      localRequire.cache = {};

      var module = cache[name] = new newRequire.Module(name);

      modules[name][0].call(module.exports, localRequire, module, module.exports, this);
    }

    return cache[name].exports;

    function localRequire(x){
      return newRequire(localRequire.resolve(x));
    }

    function resolve(x){
      return modules[name][1][x] || x;
    }
  }

  function Module(moduleName) {
    this.id = moduleName;
    this.bundle = newRequire;
    this.exports = {};
  }

  newRequire.isParcelRequire = true;
  newRequire.Module = Module;
  newRequire.modules = modules;
  newRequire.cache = cache;
  newRequire.parent = previousRequire;
  newRequire.register = function (id, exports) {
    modules[id] = [function (require, module) {
      module.exports = exports;
    }, {}];
  };

  var error;
  for (var i = 0; i < entry.length; i++) {
    try {
      newRequire(entry[i]);
    } catch (e) {
      // Save first error but execute all entries
      if (!error) {
        error = e;
      }
    }
  }

  if (entry.length) {
    // Expose entry point to Node, AMD or browser globals
    // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js
    var mainExports = newRequire(entry[entry.length - 1]);

    // CommonJS
    if (typeof exports === "object" && typeof module !== "undefined") {
      module.exports = mainExports;

    // RequireJS
    } else if (typeof define === "function" && define.amd) {
     define(function () {
       return mainExports;
     });

    // <script>
    } else if (globalName) {
      this[globalName] = mainExports;
    }
  }

  // Override the current require with this new one
  parcelRequire = newRequire;

  if (error) {
    // throw error from earlier, _after updating parcelRequire_
    throw error;
  }

  return newRequire;
})({"JsLj":[function(require,module,exports) {
if ("production" === 'production') {
  module.exports = Chart;
} else {
  module.exports = Chart;
}
},{}],"h4u2":[function(require,module,exports) {
module.exports = DOMPurify;
},{}],"Zcgp":[function(require,module,exports) {

},{}],"qrKm":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.VideoPlatforms = void 0;
exports.getPlatform = getPlatform;
exports.getVideoPlatformHooks = getVideoPlatformHooks;
exports.videoPlatformDataRecords = void 0;
var _util = require("../util/util");
var _fs = require("fs");
const youtubeCSS = '';
const vliveCSS = "#shortcutsTableToggleButton {\n  width: 30px;\n  top: -7px;\n  left: 10px;\n}\n\n#markers-upload-div input[type='file'],\n#settings-editor-div input[type='checkbox'],\n#settings-editor-div input[type='radio'] {\n  position: static;\n  width: auto;\n  height: auto;\n  opacity: 1;\n  appearance: auto;\n  -webkit-appearance: auto;\n  -moz-appearance: auto;\n}\n\n._click_zone[data-title-container] {\n  pointer-events: none !important;\n}\n\ndiv[class*='layout_main'] {\n  width: 100% !important;\n  margin: 5px auto 0px;\n}\ndiv[class*='layout_content'] {\n  width: 98% !important;\n  margin-left: 1%;\n}\ndiv[class*='lnb'] {\n  display: none;\n}\n\n@media (min-width: 1024px) {\n  div[class*='snb'] {\n    display: none;\n  }\n}\n";
const naver_tvCSS = "#shortcutsTableToggleButton {\n  width: 40px;\n  display: inline-block;\n  background-color: transparent;\n  border: transparent;\n  top: -3px;\n}\n\n#markers-div {\n  height: 11px;\n}\n\n#markers-svg,\n#selected-marker-pair-overlay,\n#start-marker-numberings,\n#end-marker-numberings {\n  font-size: 6.5pt;\n  width: 100%;\n  height: 300%;\n  top: 2px;\n  position: absolute;\n  z-index: 99;\n  paint-order: stroke;\n  stroke: rgba(0, 0, 0, 0.25);\n  stroke-width: 2px;\n}\n\n#start-marker-numberings {\n  top: -13px;\n  height: 13px;\n}\n\n#end-marker-numberings {\n  top: 13px;\n  height: 13px;\n}\n";
const weverseCSS = "#shortcutsTableToggleButton {\n  width: 40px;\n  display: inline-block;\n  background-color: transparent;\n  border: transparent;\n}\n\n#markers-svg,\n#selected-marker-pair-overlay,\n#start-marker-numberings,\n#end-marker-numberings {\n  font-size: 6.5pt;\n  width: 100%;\n  height: 100%;\n  top: 2px;\n  position: absolute;\n  z-index: 99;\n  paint-order: stroke;\n  stroke: rgba(0, 0, 0, 0.25);\n  stroke-width: 2px;\n}\n\n.slider {\n  padding-bottom: 15px !important;\n}\n\n\n#start-marker-numberings {\n  top: -13px;\n}\n\n#end-marker-numberings {\n  top: 13px;\n}\n\n.pzp-pc__bottom-buttons {\n  top: 8px;\n}\n";
const afreecatvCSS = "#shortcutsTableToggleButton {\n  width: 40px;\n  display: inline-block;\n  background-color: transparent;\n  border: transparent;\n  top: -3px;\n}\n\n#markers-div {\n  height: 14px;\n  position: relative;\n  top: -6px;\n}\n\n#markers-svg,\n#selected-marker-pair-overlay,\n#start-marker-numberings,\n#end-marker-numberings {\n  font-size: 6.5pt;\n  width: 100%;\n  height: 300%;\n  top: 2px;\n  z-index: 99;\n  position: absolute;\n  paint-order: stroke;\n  stroke: rgba(0, 0, 0, 0.25);\n  stroke-width: 2px;\n}\n\n\n\n.progress {\n  padding-bottom: 25px !important;\n}\n\n#start-marker-numberings {\n  top: -20px;\n  height: 13px;\n}\n\n#end-marker-numberings {\n  top: 8px;\n  height: 13px;\n}\n";
var VideoPlatforms;
(function (VideoPlatforms) {
  VideoPlatforms["youtube"] = "youtube";
  VideoPlatforms["vlive"] = "vlive";
  VideoPlatforms["weverse"] = "weverse";
  VideoPlatforms["naver_tv"] = "naver_tv";
  VideoPlatforms["afreecatv"] = "afreecatv";
})(VideoPlatforms || (exports.VideoPlatforms = VideoPlatforms = {}));
function getVideoPlatformHooks(selectors) {
  return (0, _util.querySelectors)(selectors);
}
function getPlatform() {
  const host = window.location.hostname;
  if (host.includes('youtube')) {
    return VideoPlatforms.youtube;
  } else if (host.includes('vlive')) {
    return VideoPlatforms.vlive;
  } else if (host.includes('weverse')) {
    return VideoPlatforms.weverse;
  } else if (host.includes('tv.naver')) {
    return VideoPlatforms.naver_tv;
  } else if (host.includes('afreecatv.com')) {
    return VideoPlatforms.afreecatv;
  } else {
    return VideoPlatforms.youtube;
  }
}
const youtubeSelectors = {
  playerContainer: '#ytd-player #container',
  player: '#movie_player',
  videoContainer: '#ytd-player #container',
  video: 'video',
  markersDiv: '.ytp-progress-bar',
  theaterModeIndicator: 'ytd-watch-flexy',
  progressBar: '.ytp-progress-bar',
  settingsEditor: '#below',
  settingsEditorTheater: '#full-bleed-container',
  shortcutsTable: '#below',
  frameCapturerProgressBar: '#below',
  flashMessage: '#below',
  cropOverlay: '.html5-video-container',
  cropMouseManipulation: '.html5-video-container',
  speedChartContainer: '.html5-video-container',
  cropChartContainer: '#columns',
  markerNumberingsDiv: '.ytp-chrome-bottom',
  controls: '.ytp-chrome-bottom',
  controlsGradient: '.ytp-gradient-bottom',
  shortcutsTableButton: '.ytp-right-controls',
  playerClickZone: '.html5-video-container'
};
const vliveSelectors = {
  playerContainer: 'div[class*=player_area]',
  player: 'div[id$="videoArea"]',
  videoContainer: 'div[id$="videoArea"]',
  video: 'video',
  progressBar: '.u_rmc_progress_bar',
  markersDiv: '.u_rmc_progress_bar',
  theaterModeIndicator: 'placeholder',
  settingsEditor: 'div[class*=player_area]',
  settingsEditorTheater: 'div[class*=player_area]',
  shortcutsTable: '[class*="video_title"]',
  frameCapturerProgressBar: '[class*="video_title"]',
  flashMessage: '[class*="video_title"]',
  cropOverlay: 'div[id$="videoArea"]',
  cropMouseManipulation: '._click_zone[data-video-overlay]',
  speedChartContainer: '._click_zone[data-video-overlay]',
  cropChartContainer: '[class*="video_title"]',
  markerNumberingsDiv: '.u_rmc_progress_bar_container',
  controls: '.u_rmcplayer_control',
  controlsGradient: '.u_rmcplayer_control_bg._click_zone',
  shortcutsTableButton: 'div[class*=video_content]',
  playerClickZone: '._click_zone[data-video-overlay]'
};
const naver_tvSelectors = {
  playerContainer: 'div[class=webplayer-internal-source-shadow]',
  player: 'div[class=webplayer-internal-source-wrapper]',
  playerClickZone: '.webplayer-internal-source-wrapper',
  videoContainer: 'div[class=webplayer-internal-source-wrapper]',
  video: 'video',
  progressBar: '.pzp-pc__progress-slider',
  markersDiv: '.pzp-ui-slider__wrap',
  markerNumberingsDiv: '.pzp-ui-slider__wrap',
  theaterModeIndicator: 'placeholder',
  settingsEditor: 'div[class*=ArticleSection_article_section]',
  settingsEditorTheater: 'div[class*=ArticleSection_article_section]',
  shortcutsTable: 'div[class*=ArticleSection_article_section]',
  frameCapturerProgressBar: 'div[class*=ArticleSection_article_section]',
  flashMessage: 'div[class*=ArticleSection_article_section]',
  cropOverlay: '.webplayer-internal-source-wrapper',
  cropMouseManipulation: '.webplayer-internal-source-wrapper',
  speedChartContainer: '.webplayer-internal-video',
  cropChartContainer: 'div[class*=ArticleSection_article_section]',
  controls: '.pzp-pc__bottom',
  controlsGradient: '.pzp-pc__bottom-shadow',
  shortcutsTableButton: '.pzp-pc__bottom-buttons-right'
};
const weverseSelectors = {
  playerContainer: 'div[class=webplayer-internal-source-shadow]',
  player: 'div[class=webplayer-internal-source-wrapper]',
  playerClickZone: '.webplayer-internal-source-wrapper',
  videoContainer: 'div[class=webplayer-internal-source-wrapper]',
  video: 'video',
  progressBar: '.pzp-pc__progress-slider',
  markersDiv: '.pzp-pc__progress-slider',
  markerNumberingsDiv: '.pzp-pc__progress-slider',
  theaterModeIndicator: 'placeholder',
  settingsEditor: 'div[class*=HeaderView_container]',
  settingsEditorTheater: 'div[class*=HeaderView_container]',
  shortcutsTable: 'div[class*="HeaderView_container"]',
  frameCapturerProgressBar: 'div[class*="HeaderView_container"]',
  flashMessage: 'div[class*="HeaderView_container"]',
  cropOverlay: '.webplayer-internal-source-wrapper',
  cropMouseManipulation: '.webplayer-internal-source-wrapper',
  speedChartContainer: '.webplayer-internal-video',
  cropChartContainer: 'div[class*="HeaderView_container"]',
  controls: '.pzp-pc__bottom-buttons',
  controlsGradient: '.pzp-pc__bottom-buttons',
  shortcutsTableButton: '.pzp-pc__bottom-buttons-right'
};
const afreecaPlayerItemListSelector = 'div[class~=player_item_list]';
const afreecatvSelectors = {
  playerContainer: 'div[class~=htmlplayer_wrap]',
  player: 'div[id=afreecatv_player]',
  playerClickZone: 'div[id=afreecatv_player]',
  videoContainer: 'div[id=videoLayer]',
  video: 'video[id=video]',
  progressBar: 'div[class~=progress_track]',
  markersDiv: 'div[class~=progress_track]',
  markerNumberingsDiv: 'div[class~=progress_track]',
  theaterModeIndicator: 'placeholder',
  settingsEditor: afreecaPlayerItemListSelector,
  settingsEditorTheater: afreecaPlayerItemListSelector,
  shortcutsTable: afreecaPlayerItemListSelector,
  frameCapturerProgressBar: afreecaPlayerItemListSelector,
  flashMessage: afreecaPlayerItemListSelector,
  cropOverlay: 'div[id=afreecatv_player]',
  cropMouseManipulation: 'div[id=afreecatv_player]',
  speedChartContainer: 'div[id=videoLayer]',
  cropChartContainer: afreecaPlayerItemListSelector,
  controls: 'div[class~=ctrl]',
  controlsGradient: 'div[class~=ctrl]',
  shortcutsTableButton: 'div[class~=right_ctrl]'
};
const youtubeData = {
  selectors: youtubeSelectors,
  css: youtubeCSS
};
const vliveData = {
  selectors: vliveSelectors,
  css: vliveCSS
};
const weverseData = {
  selectors: weverseSelectors,
  css: weverseCSS
};
const naver_tvData = {
  selectors: naver_tvSelectors,
  css: naver_tvCSS
};
const afreecaData = {
  selectors: afreecatvSelectors,
  css: afreecatvCSS
};
const videoPlatformDataRecords = exports.videoPlatformDataRecords = {
  [VideoPlatforms.youtube]: youtubeData,
  [VideoPlatforms.weverse]: weverseData,
  [VideoPlatforms.vlive]: vliveData,
  [VideoPlatforms.naver_tv]: naver_tvData,
  [VideoPlatforms.afreecatv]: afreecaData
};
},{"../util/util":"mCFQ","fs":"Zcgp"}],"mCFQ":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.arrayEquals = arrayEquals;
exports.blockEvent = blockEvent;
exports.bsearch = bsearch;
exports.clampNumber = clampNumber;
exports.copyToClipboard = copyToClipboard;
exports.deleteElement = deleteElement;
exports.flashMessage = flashMessage;
exports.getCropString = getCropString;
exports.getEasedValue = getEasedValue;
exports.getOutputDuration = getOutputDuration;
exports.getRounder = getRounder;
exports.getVideoDuration = getVideoDuration;
exports.htmlToElement = htmlToElement;
exports.htmlToSVGElement = htmlToSVGElement;
exports.injectCSS = injectCSS;
exports.mod = mod;
exports.observeVideoElementChange = observeVideoElementChange;
exports.onLoadVideoPage = onLoadVideoPage;
exports.once = once;
exports.parseTimeStringToSeconds = parseTimeStringToSeconds;
exports.querySelectors = querySelectors;
exports.retryUntilTruthyResult = retryUntilTruthyResult;
exports.roundValue = roundValue;
exports.safeSetInnerHtml = safeSetInnerHtml;
exports.sanitizeHtml = sanitizeHtml;
exports.seekBySafe = seekBySafe;
exports.seekToSafe = seekToSafe;
exports.setAttributes = setAttributes;
exports.setFlashMessageHook = setFlashMessageHook;
exports.sleep = sleep;
exports.speedRounder = void 0;
exports.ternaryToString = ternaryToString;
exports.timeRounder = void 0;
exports.toHHMMSS = toHHMMSS;
exports.toHHMMSSTrimmed = toHHMMSSTrimmed;
var _dompurify = _interopRequireDefault(require("dompurify"));
var _platforms = require("../platforms/platforms");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function sanitizeHtml(html) {
  let forceBody = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  const trustedHtml = _dompurify.default.sanitize(html, {
    USE_PROFILES: {
      html: true,
      svg: true
    },
    RETURN_TRUSTED_TYPE: Boolean(window.TrustedHTML),
    FORCE_BODY: forceBody
  });
  if (_dompurify.default.removed.length > 0) {
    console.warn('Sanitized html. removed elements = ', _dompurify.default.removed);
  }
  if (window.TrustedHTML) {
    return trustedHtml;
  } else {
    return trustedHtml.toString();
  }
}
function safeSetInnerHtml(e, html) {
  let forceBody = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  e.innerHTML = sanitizeHtml(html, forceBody);
}
let flashMessageHook;
function setFlashMessageHook(hook) {
  flashMessageHook = hook;
}
function flashMessage(msg, color) {
  let lifetime = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 3000;
  const flashDiv = document.createElement('div');
  flashDiv.setAttribute('class', 'msg-div flash-div');
  safeSetInnerHtml(flashDiv, `<span class="flash-msg" style="color:${color}">${msg}</span>`);
  flashMessageHook.insertAdjacentElement('beforebegin', flashDiv);
  setTimeout(() => deleteElement(flashDiv), lifetime);
}
async function retryUntilTruthyResult(fn) {
  let wait = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 200;
  let result = fn();
  while (!result) {
    console.debug(`Retrying function: ${fn.name || 'arrow'} with body ${fn.toString()} because result was ${result}`);
    result = fn();
    await sleep(wait);
  }
  return result;
}
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
function injectCSS(css, id) {
  const style = document.createElement('style');
  style.setAttribute('id', id);
  safeSetInnerHtml(style, css);
  document.body.appendChild(style);
  return style;
}
function htmlToElement(html) {
  const template = document.createElement('template');
  html = html.trim(); // Never return a text node of whitespace as the result
  safeSetInnerHtml(template, html);
  return template.content.firstChild;
}
function htmlToSVGElement(html) {
  const template = document.createElementNS('http://www.w3.org/2000/svg', 'template');
  html = html.trim(); // Never return a text node of whitespace as the result
  safeSetInnerHtml(template, html);
  return template.firstElementChild;
}
function deleteElement(elem) {
  if (elem && elem.parentElement) {
    elem.parentElement.removeChild(elem);
  }
}
function querySelectors(selectors) {
  let root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document;
  const elements = {};
  for (const key in selectors) {
    elements[key] = root.querySelector(selectors[key]);
  }
  return elements;
}
function once(fn, context) {
  var result;
  return function () {
    if (fn) {
      result = fn.apply(context || this, arguments);
      fn = null;
    }
    return result;
  };
}
function setAttributes(el, attrs) {
  Object.keys(attrs).forEach(key => el.setAttribute(key, attrs[key]));
}
function copyToClipboard(str) {
  const el = document.createElement('textarea');
  el.value = str;
  document.body.appendChild(el);
  el.select();
  document.execCommand('copy');
  document.body.removeChild(el);
}
function getRounder(multiple, precision) {
  return value => {
    const roundedValue = Math.round(value / multiple) * multiple;
    const roundedValueFixedPrecision = +roundedValue.toFixed(precision);
    return roundedValueFixedPrecision;
  };
}
function roundValue(value, multiple, precision) {
  return getRounder(multiple, precision)(value);
}
const speedRounder = exports.speedRounder = getRounder(5e-2, 2);
const timeRounder = exports.timeRounder = getRounder(1e-6, 6);
function clampNumber(number, min, max) {
  return Math.max(min, Math.min(number, max));
}
function toHHMMSS(seconds) {
  return new Date(seconds * 1000).toISOString().substr(11, 12);
}
function toHHMMSSTrimmed(seconds) {
  return toHHMMSS(seconds).replace(/(00:)+(.*)/, '$2');
}
function mod(dividend, divisor) {
  return (dividend % divisor + divisor) % divisor;
}
function bsearch(haystack, needle, comparator, low, high) {
  var mid, cmp;
  if (low === undefined) low = 0;else {
    low = low | 0;
    if (low < 0 || low >= haystack.length) throw new RangeError('invalid lower bound');
  }
  if (high === undefined) high = haystack.length - 1;else {
    high = high | 0;
    if (high < low || high >= haystack.length) throw new RangeError('invalid upper bound');
  }
  while (low <= high) {
    // The naive `low + high >>> 1` could fail for array lengths > 2**31
    // because `>>>` converts its operands to int32. `low + (high - low >>> 1)`
    // works for array lengths <= 2**32-1 which is also Javascript's max array
    // length.
    mid = low + (high - low >>> 1);
    cmp = +comparator(haystack[mid], needle, mid, haystack);
    // Too low.
    if (cmp < 0.0) low = mid + 1;
    // Too high.
    else if (cmp > 0.0) high = mid - 1;
    // Key found.
    else return [mid, mid];
  }
  // Key not found.
  return [high, low];
}
function getEasedValue(easingFunc, startValue, endValue, startTime, endTime, currentTime) {
  const elapsed = currentTime - startTime;
  const duration = endTime - startTime;
  const change = endValue - startValue;
  let easedTimePercentage;
  easedTimePercentage = easingFunc(elapsed / duration);
  const easedValue = startValue + change * easedTimePercentage;
  return easedValue;
}
function seekToSafe(video, newTime) {
  newTime = clampNumber(newTime, 0, video.duration);
  if (!isNaN(newTime) && video.getCurrentTime() != newTime && !video.seeking) {
    try {
      video.seekTo(newTime);
    } catch (e) {
      console.error(e);
    }
  }
}
function seekBySafe(video, timeDelta) {
  const newTime = video.getCurrentTime() + timeDelta;
  seekToSafe(video, newTime);
}
function blockEvent(e) {
  e.preventDefault();
  e.stopImmediatePropagation();
}
function getCropString(x, y, w, h) {
  return `${x}:${y}:${w}:${h}`;
}
function ternaryToString(ternary, def) {
  if (ternary == null) {
    return def != null ? def : '(Disabled)';
  } else if (ternary === true) {
    return '(Enabled)';
  } else if (ternary === false) {
    return '(Disabled)';
  } else {
    return null;
  }
}
function arrayEquals(a, b) {
  return a.length === b.length && a.every((v, i) => v === b[i]);
}
function getOutputDuration(speedMap) {
  let fps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 30;
  let outputDuration = 0;
  const frameDur = 1 / fps;
  const nSects = speedMap.length - 1;
  // Account for marker pair start time as trim filter sets start time to ~0
  const speedMapStartTime = speedMap[0].x;
  // Account for first input frame delay due to potentially imprecise trim
  const startt = Math.ceil(speedMapStartTime / frameDur) * frameDur - speedMapStartTime;
  for (let sect = 0; sect < nSects; ++sect) {
    const left = speedMap[sect];
    const right = speedMap[sect + 1];
    const startSpeed = left.y;
    const endSpeed = right.y;
    const speedChange = endSpeed - startSpeed;
    const sectStart = left.x - speedMapStartTime - startt;
    let sectEnd = right.x - speedMapStartTime - startt;
    // Account for last input frame delay due to potentially imprecise trim
    if (sect === nSects - 1) {
      sectEnd = Math.floor(right['x'] / frameDur) * frameDur;
      // When trim is frame-precise, the frame that begins at the marker pair end time is not included
      if (right.x - sectEnd < 1e-10) sectEnd = sectEnd - frameDur;
      sectEnd = sectEnd - speedMapStartTime - startt;
      sectEnd = Math.floor(sectEnd * 1000000) / 1000000;
    }
    const sectDuration = sectEnd - sectStart;
    if (sectDuration === 0) continue;
    const m = speedChange / sectDuration;
    const b = startSpeed - m * sectStart;
    if (speedChange === 0) {
      outputDuration += sectDuration / endSpeed;
    } else {
      // Integrate the reciprocal of the linear time vs speed function for the current section
      outputDuration += 1 / m * (Math.log(Math.abs(m * sectEnd + b)) - Math.log(Math.abs(m * sectStart + b)));
    }
  }
  // Each output frame time is rounded to the nearest multiple of a frame's duration at the given fps
  outputDuration = Math.round(outputDuration / frameDur) * frameDur;
  // The last included frame is held for a single frame's duration
  outputDuration += frameDur;
  outputDuration = Math.round(outputDuration * 1000) / 1000;
  return outputDuration;
}
async function onLoadVideoPage(callback) {
  const ytdapp = await retryUntilTruthyResult(() => document.getElementsByTagName('ytd-app')[0]);
  if (ytdapp.hasAttribute('is-watch-page')) {
    console.log('watch page loaded');
    callback();
    return;
  }
  const observer = new MutationObserver(mutationList => {
    mutationList.forEach(mutation => {
      if (mutation.type === 'attributes' && mutation.attributeName === 'is-watch-page' && ytdapp.hasAttribute('is-watch-page')) {
        console.log('watch page loaded');
        observer.disconnect();
        callback();
      }
    });
  });
  const config = {
    attributeFilter: ['is-watch-page']
  };
  console.log(`Waiting for video page load before calling ${callback.name}`);
  observer.observe(ytdapp, config);
}
function observeVideoElementChange(videoContainer, callback) {
  const observer = new MutationObserver(mutationList => {
    mutationList.forEach(mutation => {
      if (mutation.type === 'childList') {
        console.log('observed mutation in video container', mutation);
        callback(mutation.addedNodes);
      }
    });
  });
  console.log(`Watching for changes to video container nodes. callback=${callback.name}`);
  observer.observe(videoContainer, {
    childList: true
  });
}
function parseTimeStringToSeconds(timeString) {
  const [hours, minutes, seconds] = timeString.split(':').map(Number);
  return hours * 3600 + minutes * 60 + seconds;
}
let videoDuration = NaN;
function getVideoDuration(platform, video) {
  if (!Number.isNaN(videoDuration)) {
    return videoDuration;
  }
  if (platform === _platforms.VideoPlatforms.afreecatv) {
    let duration = 0;
    for (let videoPart of unsafeWindow.vodCore.playerController.fileItems) {
      duration += videoPart.duration;
    }
    videoDuration = duration;
  } else {
    videoDuration = video.duration;
  }
  return videoDuration;
}
},{"dompurify":"h4u2","../platforms/platforms":"qrKm"}],"zHQW":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _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;
  };
}();
var _templateObject = _taggedTemplateLiteral(['', ''], ['', '']);
function _taggedTemplateLiteral(strings, raw) {
  return Object.freeze(Object.defineProperties(strings, {
    raw: {
      value: Object.freeze(raw)
    }
  }));
}
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

/**
 * @class TemplateTag
 * @classdesc Consumes a pipeline of composable transformer plugins and produces a template tag.
 */
var TemplateTag = function () {
  /**
   * constructs a template tag
   * @constructs TemplateTag
   * @param  {...Object} [...transformers] - an array or arguments list of transformers
   * @return {Function}                    - a template tag
   */
  function TemplateTag() {
    var _this = this;
    for (var _len = arguments.length, transformers = Array(_len), _key = 0; _key < _len; _key++) {
      transformers[_key] = arguments[_key];
    }
    _classCallCheck(this, TemplateTag);
    this.tag = function (strings) {
      for (var _len2 = arguments.length, expressions = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
        expressions[_key2 - 1] = arguments[_key2];
      }
      if (typeof strings === 'function') {
        // if the first argument passed is a function, assume it is a template tag and return
        // an intermediary tag that processes the template using the aforementioned tag, passing the
        // result to our tag
        return _this.interimTag.bind(_this, strings);
      }
      if (typeof strings === 'string') {
        // if the first argument passed is a string, just transform it
        return _this.transformEndResult(strings);
      }

      // else, return a transformed end result of processing the template with our tag
      strings = strings.map(_this.transformString.bind(_this));
      return _this.transformEndResult(strings.reduce(_this.processSubstitutions.bind(_this, expressions)));
    };

    // if first argument is an array, extrude it as a list of transformers
    if (transformers.length > 0 && Array.isArray(transformers[0])) {
      transformers = transformers[0];
    }

    // if any transformers are functions, this means they are not initiated - automatically initiate them
    this.transformers = transformers.map(function (transformer) {
      return typeof transformer === 'function' ? transformer() : transformer;
    });

    // return an ES2015 template tag
    return this.tag;
  }

  /**
   * Applies all transformers to a template literal tagged with this method.
   * If a function is passed as the first argument, assumes the function is a template tag
   * and applies it to the template, returning a template tag.
   * @param  {(Function|String|Array<String>)} strings        - Either a template tag or an array containing template strings separated by identifier
   * @param  {...*}                            ...expressions - Optional list of substitution values.
   * @return {(String|Function)}                              - Either an intermediary tag function or the results of processing the template.
   */

  _createClass(TemplateTag, [{
    key: 'interimTag',
    /**
     * An intermediary template tag that receives a template tag and passes the result of calling the template with the received
     * template tag to our own template tag.
     * @param  {Function}        nextTag          - the received template tag
     * @param  {Array<String>}   template         - the template to process
     * @param  {...*}            ...substitutions - `substitutions` is an array of all substitutions in the template
     * @return {*}                                - the final processed value
     */
    value: function interimTag(previousTag, template) {
      for (var _len3 = arguments.length, substitutions = Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) {
        substitutions[_key3 - 2] = arguments[_key3];
      }
      return this.tag(_templateObject, previousTag.apply(undefined, [template].concat(substitutions)));
    }

    /**
     * Performs bulk processing on the tagged template, transforming each substitution and then
     * concatenating the resulting values into a string.
     * @param  {Array<*>} substitutions - an array of all remaining substitutions present in this template
     * @param  {String}   resultSoFar   - this iteration's result string so far
     * @param  {String}   remainingPart - the template chunk after the current substitution
     * @return {String}                 - the result of joining this iteration's processed substitution with the result
     */
  }, {
    key: 'processSubstitutions',
    value: function processSubstitutions(substitutions, resultSoFar, remainingPart) {
      var substitution = this.transformSubstitution(substitutions.shift(), resultSoFar);
      return ''.concat(resultSoFar, substitution, remainingPart);
    }

    /**
     * Iterate through each transformer, applying the transformer's `onString` method to the template
     * strings before all substitutions are processed.
     * @param {String}  str - The input string
     * @return {String}     - The final results of processing each transformer
     */
  }, {
    key: 'transformString',
    value: function transformString(str) {
      var cb = function cb(res, transform) {
        return transform.onString ? transform.onString(res) : res;
      };
      return this.transformers.reduce(cb, str);
    }

    /**
     * When a substitution is encountered, iterates through each transformer and applies the transformer's
     * `onSubstitution` method to the substitution.
     * @param  {*}      substitution - The current substitution
     * @param  {String} resultSoFar  - The result up to and excluding this substitution.
     * @return {*}                   - The final result of applying all substitution transformations.
     */
  }, {
    key: 'transformSubstitution',
    value: function transformSubstitution(substitution, resultSoFar) {
      var cb = function cb(res, transform) {
        return transform.onSubstitution ? transform.onSubstitution(res, resultSoFar) : res;
      };
      return this.transformers.reduce(cb, substitution);
    }

    /**
     * Iterates through each transformer, applying the transformer's `onEndResult` method to the
     * template literal after all substitutions have finished processing.
     * @param  {String} endResult - The processed template, just before it is returned from the tag
     * @return {String}           - The final results of processing each transformer
     */
  }, {
    key: 'transformEndResult',
    value: function transformEndResult(endResult) {
      var cb = function cb(res, transform) {
        return transform.onEndResult ? transform.onEndResult(res) : res;
      };
      return this.transformers.reduce(cb, endResult);
    }
  }]);
  return TemplateTag;
}();
var _default = exports.default = TemplateTag;
},{}],"QT9R":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _TemplateTag.default;
  }
});
var _TemplateTag = _interopRequireDefault(require("./TemplateTag"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./TemplateTag":"zHQW"}],"POTh":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
/**
 * TemplateTag transformer that trims whitespace on the end result of a tagged template
 * @param  {String} side = '' - The side of the string to trim. Can be 'start' or 'end' (alternatively 'left' or 'right')
 * @return {Object}           - a TemplateTag transformer
 */
var trimResultTransformer = function trimResultTransformer() {
  var side = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  return {
    onEndResult: function onEndResult(endResult) {
      if (side === '') {
        return endResult.trim();
      }
      side = side.toLowerCase();
      if (side === 'start' || side === 'left') {
        return endResult.replace(/^\s*/, '');
      }
      if (side === 'end' || side === 'right') {
        return endResult.replace(/\s*$/, '');
      }
      throw new Error('Side not supported: ' + side);
    }
  };
};
var _default = exports.default = trimResultTransformer;
},{}],"oMaF":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _trimResultTransformer.default;
  }
});
var _trimResultTransformer = _interopRequireDefault(require("./trimResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./trimResultTransformer":"POTh"}],"aIss":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
function _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);
  }
}

/**
 * strips indentation from a template literal
 * @param  {String} type = 'initial' - whether to remove all indentation or just leading indentation. can be 'all' or 'initial'
 * @return {Object}                  - a TemplateTag transformer
 */
var stripIndentTransformer = function stripIndentTransformer() {
  var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'initial';
  return {
    onEndResult: function onEndResult(endResult) {
      if (type === 'initial') {
        // remove the shortest leading indentation from each line
        var match = endResult.match(/^[^\S\n]*(?=\S)/gm);
        var indent = match && Math.min.apply(Math, _toConsumableArray(match.map(function (el) {
          return el.length;
        })));
        if (indent) {
          var regexp = new RegExp('^.{' + indent + '}', 'gm');
          return endResult.replace(regexp, '');
        }
        return endResult;
      }
      if (type === 'all') {
        // remove all indentation from each line
        return endResult.replace(/^[^\S\n]+/gm, '');
      }
      throw new Error('Unknown type: ' + type);
    }
  };
};
var _default = exports.default = stripIndentTransformer;
},{}],"wRV8":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _stripIndentTransformer.default;
  }
});
var _stripIndentTransformer = _interopRequireDefault(require("./stripIndentTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./stripIndentTransformer":"aIss"}],"wXPP":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
/**
 * Replaces tabs, newlines and spaces with the chosen value when they occur in sequences
 * @param  {(String|RegExp)} replaceWhat - the value or pattern that should be replaced
 * @param  {*}               replaceWith - the replacement value
 * @return {Object}                      - a TemplateTag transformer
 */
var replaceResultTransformer = function replaceResultTransformer(replaceWhat, replaceWith) {
  return {
    onEndResult: function onEndResult(endResult) {
      if (replaceWhat == null || replaceWith == null) {
        throw new Error('replaceResultTransformer requires at least 2 arguments.');
      }
      return endResult.replace(replaceWhat, replaceWith);
    }
  };
};
var _default = exports.default = replaceResultTransformer;
},{}],"CaXL":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _replaceResultTransformer.default;
  }
});
var _replaceResultTransformer = _interopRequireDefault(require("./replaceResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./replaceResultTransformer":"wXPP"}],"yRgv":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var replaceSubstitutionTransformer = function replaceSubstitutionTransformer(replaceWhat, replaceWith) {
  return {
    onSubstitution: function onSubstitution(substitution, resultSoFar) {
      if (replaceWhat == null || replaceWith == null) {
        throw new Error('replaceSubstitutionTransformer requires at least 2 arguments.');
      }

      // Do not touch if null or undefined
      if (substitution == null) {
        return substitution;
      } else {
        return substitution.toString().replace(replaceWhat, replaceWith);
      }
    }
  };
};
var _default = exports.default = replaceSubstitutionTransformer;
},{}],"a4QR":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _replaceSubstitutionTransformer.default;
  }
});
var _replaceSubstitutionTransformer = _interopRequireDefault(require("./replaceSubstitutionTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./replaceSubstitutionTransformer":"yRgv"}],"fL7M":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var replaceStringTransformer = function replaceStringTransformer(replaceWhat, replaceWith) {
  return {
    onString: function onString(str) {
      if (replaceWhat == null || replaceWith == null) {
        throw new Error('replaceStringTransformer requires at least 2 arguments.');
      }
      return str.replace(replaceWhat, replaceWith);
    }
  };
};
var _default = exports.default = replaceStringTransformer;
},{}],"S15p":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _replaceStringTransformer.default;
  }
});
var _replaceStringTransformer = _interopRequireDefault(require("./replaceStringTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./replaceStringTransformer":"fL7M"}],"T9vP":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var defaults = {
  separator: '',
  conjunction: '',
  serial: false
};

/**
 * Converts an array substitution to a string containing a list
 * @param  {String} [opts.separator = ''] - the character that separates each item
 * @param  {String} [opts.conjunction = '']  - replace the last separator with this
 * @param  {Boolean} [opts.serial = false] - include the separator before the conjunction? (Oxford comma use-case)
 *
 * @return {Object}                     - a TemplateTag transformer
 */
var inlineArrayTransformer = function inlineArrayTransformer() {
  var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaults;
  return {
    onSubstitution: function onSubstitution(substitution, resultSoFar) {
      // only operate on arrays
      if (Array.isArray(substitution)) {
        var arrayLength = substitution.length;
        var separator = opts.separator;
        var conjunction = opts.conjunction;
        var serial = opts.serial;
        // join each item in the array into a string where each item is separated by separator
        // be sure to maintain indentation
        var indent = resultSoFar.match(/(\n?[^\S\n]+)$/);
        if (indent) {
          substitution = substitution.join(separator + indent[1]);
        } else {
          substitution = substitution.join(separator + ' ');
        }
        // if conjunction is set, replace the last separator with conjunction, but only if there is more than one substitution
        if (conjunction && arrayLength > 1) {
          var separatorIndex = substitution.lastIndexOf(separator);
          substitution = substitution.slice(0, separatorIndex) + (serial ? separator : '') + ' ' + conjunction + substitution.slice(separatorIndex + 1);
        }
      }
      return substitution;
    }
  };
};
var _default = exports.default = inlineArrayTransformer;
},{}],"k2vK":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _inlineArrayTransformer.default;
  }
});
var _inlineArrayTransformer = _interopRequireDefault(require("./inlineArrayTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./inlineArrayTransformer":"T9vP"}],"OfCL":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var splitStringTransformer = function splitStringTransformer(splitBy) {
  return {
    onSubstitution: function onSubstitution(substitution, resultSoFar) {
      if (splitBy != null && typeof splitBy === 'string') {
        if (typeof substitution === 'string' && substitution.includes(splitBy)) {
          substitution = substitution.split(splitBy);
        }
      } else {
        throw new Error('You need to specify a string character to split by.');
      }
      return substitution;
    }
  };
};
var _default = exports.default = splitStringTransformer;
},{}],"tweT":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _splitStringTransformer.default;
  }
});
var _splitStringTransformer = _interopRequireDefault(require("./splitStringTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./splitStringTransformer":"OfCL"}],"sSuZ":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var isValidValue = function isValidValue(x) {
  return x != null && !Number.isNaN(x) && typeof x !== 'boolean';
};
var removeNonPrintingValuesTransformer = function removeNonPrintingValuesTransformer() {
  return {
    onSubstitution: function onSubstitution(substitution) {
      if (Array.isArray(substitution)) {
        return substitution.filter(isValidValue);
      }
      if (isValidValue(substitution)) {
        return substitution;
      }
      return '';
    }
  };
};
var _default = exports.default = removeNonPrintingValuesTransformer;
},{}],"MRAn":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _removeNonPrintingValuesTransformer.default;
  }
});
var _removeNonPrintingValuesTransformer = _interopRequireDefault(require("./removeNonPrintingValuesTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./removeNonPrintingValuesTransformer":"sSuZ"}],"ANjY":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _stripIndentTransformer = _interopRequireDefault(require("../stripIndentTransformer"));
var _inlineArrayTransformer = _interopRequireDefault(require("../inlineArrayTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var commaLists = new _TemplateTag.default((0, _inlineArrayTransformer.default)({
  separator: ','
}), _stripIndentTransformer.default, _trimResultTransformer.default);
var _default = exports.default = commaLists;
},{"../TemplateTag":"QT9R","../stripIndentTransformer":"wRV8","../inlineArrayTransformer":"k2vK","../trimResultTransformer":"oMaF"}],"zVoE":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _commaLists.default;
  }
});
var _commaLists = _interopRequireDefault(require("./commaLists"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./commaLists":"ANjY"}],"KTo5":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _stripIndentTransformer = _interopRequireDefault(require("../stripIndentTransformer"));
var _inlineArrayTransformer = _interopRequireDefault(require("../inlineArrayTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var commaListsAnd = new _TemplateTag.default((0, _inlineArrayTransformer.default)({
  separator: ',',
  conjunction: 'and'
}), _stripIndentTransformer.default, _trimResultTransformer.default);
var _default = exports.default = commaListsAnd;
},{"../TemplateTag":"QT9R","../stripIndentTransformer":"wRV8","../inlineArrayTransformer":"k2vK","../trimResultTransformer":"oMaF"}],"hUi7":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _commaListsAnd.default;
  }
});
var _commaListsAnd = _interopRequireDefault(require("./commaListsAnd"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./commaListsAnd":"KTo5"}],"p4N3":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _stripIndentTransformer = _interopRequireDefault(require("../stripIndentTransformer"));
var _inlineArrayTransformer = _interopRequireDefault(require("../inlineArrayTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var commaListsOr = new _TemplateTag.default((0, _inlineArrayTransformer.default)({
  separator: ',',
  conjunction: 'or'
}), _stripIndentTransformer.default, _trimResultTransformer.default);
var _default = exports.default = commaListsOr;
},{"../TemplateTag":"QT9R","../stripIndentTransformer":"wRV8","../inlineArrayTransformer":"k2vK","../trimResultTransformer":"oMaF"}],"lB9V":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _commaListsOr.default;
  }
});
var _commaListsOr = _interopRequireDefault(require("./commaListsOr"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./commaListsOr":"p4N3"}],"Xu5K":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _stripIndentTransformer = _interopRequireDefault(require("../stripIndentTransformer"));
var _inlineArrayTransformer = _interopRequireDefault(require("../inlineArrayTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
var _splitStringTransformer = _interopRequireDefault(require("../splitStringTransformer"));
var _removeNonPrintingValuesTransformer = _interopRequireDefault(require("../removeNonPrintingValuesTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var html = new _TemplateTag.default((0, _splitStringTransformer.default)('\n'), _removeNonPrintingValuesTransformer.default, _inlineArrayTransformer.default, _stripIndentTransformer.default, _trimResultTransformer.default);
var _default = exports.default = html;
},{"../TemplateTag":"QT9R","../stripIndentTransformer":"wRV8","../inlineArrayTransformer":"k2vK","../trimResultTransformer":"oMaF","../splitStringTransformer":"tweT","../removeNonPrintingValuesTransformer":"MRAn"}],"pGFf":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _html.default;
  }
});
var _html = _interopRequireDefault(require("./html"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./html":"Xu5K"}],"s4NW":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _html.default;
  }
});
var _html = _interopRequireDefault(require("../html"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"../html":"pGFf"}],"unBw":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _stripIndentTransformer = _interopRequireDefault(require("../stripIndentTransformer"));
var _inlineArrayTransformer = _interopRequireDefault(require("../inlineArrayTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
var _splitStringTransformer = _interopRequireDefault(require("../splitStringTransformer"));
var _replaceSubstitutionTransformer = _interopRequireDefault(require("../replaceSubstitutionTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var safeHtml = new _TemplateTag.default((0, _splitStringTransformer.default)('\n'), _inlineArrayTransformer.default, _stripIndentTransformer.default, _trimResultTransformer.default, (0, _replaceSubstitutionTransformer.default)(/&/g, '&amp;'), (0, _replaceSubstitutionTransformer.default)(/</g, '&lt;'), (0, _replaceSubstitutionTransformer.default)(/>/g, '&gt;'), (0, _replaceSubstitutionTransformer.default)(/"/g, '&quot;'), (0, _replaceSubstitutionTransformer.default)(/'/g, '&#x27;'), (0, _replaceSubstitutionTransformer.default)(/`/g, '&#x60;'));
var _default = exports.default = safeHtml;
},{"../TemplateTag":"QT9R","../stripIndentTransformer":"wRV8","../inlineArrayTransformer":"k2vK","../trimResultTransformer":"oMaF","../splitStringTransformer":"tweT","../replaceSubstitutionTransformer":"a4QR"}],"rCqp":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _safeHtml.default;
  }
});
var _safeHtml = _interopRequireDefault(require("./safeHtml"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./safeHtml":"unBw"}],"Po01":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
var _replaceResultTransformer = _interopRequireDefault(require("../replaceResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var oneLine = new _TemplateTag.default((0, _replaceResultTransformer.default)(/(?:\n(?:\s*))+/g, ' '), _trimResultTransformer.default);
var _default = exports.default = oneLine;
},{"../TemplateTag":"QT9R","../trimResultTransformer":"oMaF","../replaceResultTransformer":"CaXL"}],"okH6":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _oneLine.default;
  }
});
var _oneLine = _interopRequireDefault(require("./oneLine"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./oneLine":"Po01"}],"ShrF":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
var _replaceResultTransformer = _interopRequireDefault(require("../replaceResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var oneLineTrim = new _TemplateTag.default((0, _replaceResultTransformer.default)(/(?:\n\s*)/g, ''), _trimResultTransformer.default);
var _default = exports.default = oneLineTrim;
},{"../TemplateTag":"QT9R","../trimResultTransformer":"oMaF","../replaceResultTransformer":"CaXL"}],"bRvt":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _oneLineTrim.default;
  }
});
var _oneLineTrim = _interopRequireDefault(require("./oneLineTrim"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./oneLineTrim":"ShrF"}],"fF1q":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _inlineArrayTransformer = _interopRequireDefault(require("../inlineArrayTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
var _replaceResultTransformer = _interopRequireDefault(require("../replaceResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var oneLineCommaLists = new _TemplateTag.default((0, _inlineArrayTransformer.default)({
  separator: ','
}), (0, _replaceResultTransformer.default)(/(?:\s+)/g, ' '), _trimResultTransformer.default);
var _default = exports.default = oneLineCommaLists;
},{"../TemplateTag":"QT9R","../inlineArrayTransformer":"k2vK","../trimResultTransformer":"oMaF","../replaceResultTransformer":"CaXL"}],"NtMF":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _oneLineCommaLists.default;
  }
});
var _oneLineCommaLists = _interopRequireDefault(require("./oneLineCommaLists"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./oneLineCommaLists":"fF1q"}],"xuSy":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _inlineArrayTransformer = _interopRequireDefault(require("../inlineArrayTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
var _replaceResultTransformer = _interopRequireDefault(require("../replaceResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var oneLineCommaListsOr = new _TemplateTag.default((0, _inlineArrayTransformer.default)({
  separator: ',',
  conjunction: 'or'
}), (0, _replaceResultTransformer.default)(/(?:\s+)/g, ' '), _trimResultTransformer.default);
var _default = exports.default = oneLineCommaListsOr;
},{"../TemplateTag":"QT9R","../inlineArrayTransformer":"k2vK","../trimResultTransformer":"oMaF","../replaceResultTransformer":"CaXL"}],"wv27":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _oneLineCommaListsOr.default;
  }
});
var _oneLineCommaListsOr = _interopRequireDefault(require("./oneLineCommaListsOr"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./oneLineCommaListsOr":"xuSy"}],"cYKW":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _inlineArrayTransformer = _interopRequireDefault(require("../inlineArrayTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
var _replaceResultTransformer = _interopRequireDefault(require("../replaceResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var oneLineCommaListsAnd = new _TemplateTag.default((0, _inlineArrayTransformer.default)({
  separator: ',',
  conjunction: 'and'
}), (0, _replaceResultTransformer.default)(/(?:\s+)/g, ' '), _trimResultTransformer.default);
var _default = exports.default = oneLineCommaListsAnd;
},{"../TemplateTag":"QT9R","../inlineArrayTransformer":"k2vK","../trimResultTransformer":"oMaF","../replaceResultTransformer":"CaXL"}],"yzUR":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _oneLineCommaListsAnd.default;
  }
});
var _oneLineCommaListsAnd = _interopRequireDefault(require("./oneLineCommaListsAnd"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./oneLineCommaListsAnd":"cYKW"}],"Ey4o":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _stripIndentTransformer = _interopRequireDefault(require("../stripIndentTransformer"));
var _inlineArrayTransformer = _interopRequireDefault(require("../inlineArrayTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var inlineLists = new _TemplateTag.default(_inlineArrayTransformer.default, _stripIndentTransformer.default, _trimResultTransformer.default);
var _default = exports.default = inlineLists;
},{"../TemplateTag":"QT9R","../stripIndentTransformer":"wRV8","../inlineArrayTransformer":"k2vK","../trimResultTransformer":"oMaF"}],"RqFD":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _inlineLists.default;
  }
});
var _inlineLists = _interopRequireDefault(require("./inlineLists"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./inlineLists":"Ey4o"}],"CWPs":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _inlineArrayTransformer = _interopRequireDefault(require("../inlineArrayTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
var _replaceResultTransformer = _interopRequireDefault(require("../replaceResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var oneLineInlineLists = new _TemplateTag.default(_inlineArrayTransformer.default, (0, _replaceResultTransformer.default)(/(?:\s+)/g, ' '), _trimResultTransformer.default);
var _default = exports.default = oneLineInlineLists;
},{"../TemplateTag":"QT9R","../inlineArrayTransformer":"k2vK","../trimResultTransformer":"oMaF","../replaceResultTransformer":"CaXL"}],"D9qW":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _oneLineInlineLists.default;
  }
});
var _oneLineInlineLists = _interopRequireDefault(require("./oneLineInlineLists"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./oneLineInlineLists":"CWPs"}],"ciUv":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _stripIndentTransformer = _interopRequireDefault(require("../stripIndentTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var stripIndent = new _TemplateTag.default(_stripIndentTransformer.default, _trimResultTransformer.default);
var _default = exports.default = stripIndent;
},{"../TemplateTag":"QT9R","../stripIndentTransformer":"wRV8","../trimResultTransformer":"oMaF"}],"OZRL":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _stripIndent.default;
  }
});
var _stripIndent = _interopRequireDefault(require("./stripIndent"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./stripIndent":"ciUv"}],"l40o":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _TemplateTag = _interopRequireDefault(require("../TemplateTag"));
var _stripIndentTransformer = _interopRequireDefault(require("../stripIndentTransformer"));
var _trimResultTransformer = _interopRequireDefault(require("../trimResultTransformer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var stripIndents = new _TemplateTag.default((0, _stripIndentTransformer.default)('all'), _trimResultTransformer.default);
var _default = exports.default = stripIndents;
},{"../TemplateTag":"QT9R","../stripIndentTransformer":"wRV8","../trimResultTransformer":"oMaF"}],"Pgi2":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "default", {
  enumerable: true,
  get: function () {
    return _stripIndents.default;
  }
});
var _stripIndents = _interopRequireDefault(require("./stripIndents"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./stripIndents":"l40o"}],"Gk7m":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "TemplateTag", {
  enumerable: true,
  get: function () {
    return _TemplateTag2.default;
  }
});
Object.defineProperty(exports, "codeBlock", {
  enumerable: true,
  get: function () {
    return _codeBlock2.default;
  }
});
Object.defineProperty(exports, "commaLists", {
  enumerable: true,
  get: function () {
    return _commaLists2.default;
  }
});
Object.defineProperty(exports, "commaListsAnd", {
  enumerable: true,
  get: function () {
    return _commaListsAnd2.default;
  }
});
Object.defineProperty(exports, "commaListsOr", {
  enumerable: true,
  get: function () {
    return _commaListsOr2.default;
  }
});
Object.defineProperty(exports, "html", {
  enumerable: true,
  get: function () {
    return _html2.default;
  }
});
Object.defineProperty(exports, "inlineArrayTransformer", {
  enumerable: true,
  get: function () {
    return _inlineArrayTransformer2.default;
  }
});
Object.defineProperty(exports, "inlineLists", {
  enumerable: true,
  get: function () {
    return _inlineLists2.default;
  }
});
Object.defineProperty(exports, "oneLine", {
  enumerable: true,
  get: function () {
    return _oneLine2.default;
  }
});
Object.defineProperty(exports, "oneLineCommaLists", {
  enumerable: true,
  get: function () {
    return _oneLineCommaLists2.default;
  }
});
Object.defineProperty(exports, "oneLineCommaListsAnd", {
  enumerable: true,
  get: function () {
    return _oneLineCommaListsAnd2.default;
  }
});
Object.defineProperty(exports, "oneLineCommaListsOr", {
  enumerable: true,
  get: function () {
    return _oneLineCommaListsOr2.default;
  }
});
Object.defineProperty(exports, "oneLineInlineLists", {
  enumerable: true,
  get: function () {
    return _oneLineInlineLists2.default;
  }
});
Object.defineProperty(exports, "oneLineTrim", {
  enumerable: true,
  get: function () {
    return _oneLineTrim2.default;
  }
});
Object.defineProperty(exports, "removeNonPrintingValuesTransformer", {
  enumerable: true,
  get: function () {
    return _removeNonPrintingValuesTransformer2.default;
  }
});
Object.defineProperty(exports, "replaceResultTransformer", {
  enumerable: true,
  get: function () {
    return _replaceResultTransformer2.default;
  }
});
Object.defineProperty(exports, "replaceStringTransformer", {
  enumerable: true,
  get: function () {
    return _replaceStringTransformer2.default;
  }
});
Object.defineProperty(exports, "replaceSubstitutionTransformer", {
  enumerable: true,
  get: function () {
    return _replaceSubstitutionTransformer2.default;
  }
});
Object.defineProperty(exports, "safeHtml", {
  enumerable: true,
  get: function () {
    return _safeHtml2.default;
  }
});
Object.defineProperty(exports, "source", {
  enumerable: true,
  get: function () {
    return _source2.default;
  }
});
Object.defineProperty(exports, "splitStringTransformer", {
  enumerable: true,
  get: function () {
    return _splitStringTransformer2.default;
  }
});
Object.defineProperty(exports, "stripIndent", {
  enumerable: true,
  get: function () {
    return _stripIndent2.default;
  }
});
Object.defineProperty(exports, "stripIndentTransformer", {
  enumerable: true,
  get: function () {
    return _stripIndentTransformer2.default;
  }
});
Object.defineProperty(exports, "stripIndents", {
  enumerable: true,
  get: function () {
    return _stripIndents2.default;
  }
});
Object.defineProperty(exports, "trimResultTransformer", {
  enumerable: true,
  get: function () {
    return _trimResultTransformer2.default;
  }
});
var _TemplateTag2 = _interopRequireDefault(require("./TemplateTag"));
var _trimResultTransformer2 = _interopRequireDefault(require("./trimResultTransformer"));
var _stripIndentTransformer2 = _interopRequireDefault(require("./stripIndentTransformer"));
var _replaceResultTransformer2 = _interopRequireDefault(require("./replaceResultTransformer"));
var _replaceSubstitutionTransformer2 = _interopRequireDefault(require("./replaceSubstitutionTransformer"));
var _replaceStringTransformer2 = _interopRequireDefault(require("./replaceStringTransformer"));
var _inlineArrayTransformer2 = _interopRequireDefault(require("./inlineArrayTransformer"));
var _splitStringTransformer2 = _interopRequireDefault(require("./splitStringTransformer"));
var _removeNonPrintingValuesTransformer2 = _interopRequireDefault(require("./removeNonPrintingValuesTransformer"));
var _commaLists2 = _interopRequireDefault(require("./commaLists"));
var _commaListsAnd2 = _interopRequireDefault(require("./commaListsAnd"));
var _commaListsOr2 = _interopRequireDefault(require("./commaListsOr"));
var _html2 = _interopRequireDefault(require("./html"));
var _codeBlock2 = _interopRequireDefault(require("./codeBlock"));
var _source2 = _interopRequireDefault(require("./source"));
var _safeHtml2 = _interopRequireDefault(require("./safeHtml"));
var _oneLine2 = _interopRequireDefault(require("./oneLine"));
var _oneLineTrim2 = _interopRequireDefault(require("./oneLineTrim"));
var _oneLineCommaLists2 = _interopRequireDefault(require("./oneLineCommaLists"));
var _oneLineCommaListsOr2 = _interopRequireDefault(require("./oneLineCommaListsOr"));
var _oneLineCommaListsAnd2 = _interopRequireDefault(require("./oneLineCommaListsAnd"));
var _inlineLists2 = _interopRequireDefault(require("./inlineLists"));
var _oneLineInlineLists2 = _interopRequireDefault(require("./oneLineInlineLists"));
var _stripIndent2 = _interopRequireDefault(require("./stripIndent"));
var _stripIndents2 = _interopRequireDefault(require("./stripIndents"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./TemplateTag":"QT9R","./trimResultTransformer":"oMaF","./stripIndentTransformer":"wRV8","./replaceResultTransformer":"CaXL","./replaceSubstitutionTransformer":"a4QR","./replaceStringTransformer":"S15p","./inlineArrayTransformer":"k2vK","./splitStringTransformer":"tweT","./removeNonPrintingValuesTransformer":"MRAn","./commaLists":"zVoE","./commaListsAnd":"hUi7","./commaListsOr":"lB9V","./html":"pGFf","./codeBlock":"s4NW","./source":"s4NW","./safeHtml":"rCqp","./oneLine":"okH6","./oneLineTrim":"bRvt","./oneLineCommaLists":"NtMF","./oneLineCommaListsOr":"wv27","./oneLineCommaListsAnd":"yzUR","./inlineLists":"RqFD","./oneLineInlineLists":"D9qW","./stripIndent":"OZRL","./stripIndents":"Pgi2"}],"VNJ7":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.linear = linear;
function linear(t) {
  return +t;
}
},{}],"NlV3":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.quadIn = quadIn;
exports.quadInOut = quadInOut;
exports.quadOut = quadOut;
function quadIn(t) {
  return t * t;
}
function quadOut(t) {
  return t * (2 - t);
}
function quadInOut(t) {
  return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;
}
},{}],"ATE0":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.cubicIn = cubicIn;
exports.cubicInOut = cubicInOut;
exports.cubicOut = cubicOut;
function cubicIn(t) {
  return t * t * t;
}
function cubicOut(t) {
  return --t * t * t + 1;
}
function cubicInOut(t) {
  return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
}
},{}],"xJOL":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.polyOut = exports.polyInOut = exports.polyIn = void 0;
var exponent = 3;
var polyIn = exports.polyIn = function custom(e) {
  e = +e;
  function polyIn(t) {
    return Math.pow(t, e);
  }
  polyIn.exponent = custom;
  return polyIn;
}(exponent);
var polyOut = exports.polyOut = function custom(e) {
  e = +e;
  function polyOut(t) {
    return 1 - Math.pow(1 - t, e);
  }
  polyOut.exponent = custom;
  return polyOut;
}(exponent);
var polyInOut = exports.polyInOut = function custom(e) {
  e = +e;
  function polyInOut(t) {
    return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;
  }
  polyInOut.exponent = custom;
  return polyInOut;
}(exponent);
},{}],"aKKp":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.sinIn = sinIn;
exports.sinInOut = sinInOut;
exports.sinOut = sinOut;
var pi = Math.PI,
  halfPi = pi / 2;
function sinIn(t) {
  return +t === 1 ? 1 : 1 - Math.cos(t * halfPi);
}
function sinOut(t) {
  return Math.sin(t * halfPi);
}
function sinInOut(t) {
  return (1 - Math.cos(pi * t)) / 2;
}
},{}],"n70i":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.tpmt = tpmt;
// tpmt is two power minus ten times t scaled to [0,1]
function tpmt(x) {
  return (Math.pow(2, -10 * x) - 0.0009765625) * 1.0009775171065494;
}
},{}],"GEdw":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.expIn = expIn;
exports.expInOut = expInOut;
exports.expOut = expOut;
var _math = require("./math.js");
function expIn(t) {
  return (0, _math.tpmt)(1 - +t);
}
function expOut(t) {
  return 1 - (0, _math.tpmt)(t);
}
function expInOut(t) {
  return ((t *= 2) <= 1 ? (0, _math.tpmt)(1 - t) : 2 - (0, _math.tpmt)(t - 1)) / 2;
}
},{"./math.js":"n70i"}],"erIK":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.circleIn = circleIn;
exports.circleInOut = circleInOut;
exports.circleOut = circleOut;
function circleIn(t) {
  return 1 - Math.sqrt(1 - t * t);
}
function circleOut(t) {
  return Math.sqrt(1 - --t * t);
}
function circleInOut(t) {
  return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;
}
},{}],"WtpZ":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.bounceIn = bounceIn;
exports.bounceInOut = bounceInOut;
exports.bounceOut = bounceOut;
var b1 = 4 / 11,
  b2 = 6 / 11,
  b3 = 8 / 11,
  b4 = 3 / 4,
  b5 = 9 / 11,
  b6 = 10 / 11,
  b7 = 15 / 16,
  b8 = 21 / 22,
  b9 = 63 / 64,
  b0 = 1 / b1 / b1;
function bounceIn(t) {
  return 1 - bounceOut(1 - t);
}
function bounceOut(t) {
  return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;
}
function bounceInOut(t) {
  return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;
}
},{}],"NHFs":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.backOut = exports.backInOut = exports.backIn = void 0;
var overshoot = 1.70158;
var backIn = exports.backIn = function custom(s) {
  s = +s;
  function backIn(t) {
    return (t = +t) * t * (s * (t - 1) + t);
  }
  backIn.overshoot = custom;
  return backIn;
}(overshoot);
var backOut = exports.backOut = function custom(s) {
  s = +s;
  function backOut(t) {
    return --t * t * ((t + 1) * s + t) + 1;
  }
  backOut.overshoot = custom;
  return backOut;
}(overshoot);
var backInOut = exports.backInOut = function custom(s) {
  s = +s;
  function backInOut(t) {
    return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;
  }
  backInOut.overshoot = custom;
  return backInOut;
}(overshoot);
},{}],"ZzFe":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.elasticOut = exports.elasticInOut = exports.elasticIn = void 0;
var _math = require("./math.js");
var tau = 2 * Math.PI,
  amplitude = 1,
  period = 0.3;
var elasticIn = exports.elasticIn = function custom(a, p) {
  var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
  function elasticIn(t) {
    return a * (0, _math.tpmt)(- --t) * Math.sin((s - t) / p);
  }
  elasticIn.amplitude = function (a) {
    return custom(a, p * tau);
  };
  elasticIn.period = function (p) {
    return custom(a, p);
  };
  return elasticIn;
}(amplitude, period);
var elasticOut = exports.elasticOut = function custom(a, p) {
  var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
  function elasticOut(t) {
    return 1 - a * (0, _math.tpmt)(t = +t) * Math.sin((t + s) / p);
  }
  elasticOut.amplitude = function (a) {
    return custom(a, p * tau);
  };
  elasticOut.period = function (p) {
    return custom(a, p);
  };
  return elasticOut;
}(amplitude, period);
var elasticInOut = exports.elasticInOut = function custom(a, p) {
  var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
  function elasticInOut(t) {
    return ((t = t * 2 - 1) < 0 ? a * (0, _math.tpmt)(-t) * Math.sin((s - t) / p) : 2 - a * (0, _math.tpmt)(t) * Math.sin((s + t) / p)) / 2;
  }
  elasticInOut.amplitude = function (a) {
    return custom(a, p * tau);
  };
  elasticInOut.period = function (p) {
    return custom(a, p);
  };
  return elasticInOut;
}(amplitude, period);
},{"./math.js":"n70i"}],"NhEX":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "easeBack", {
  enumerable: true,
  get: function () {
    return _back.backInOut;
  }
});
Object.defineProperty(exports, "easeBackIn", {
  enumerable: true,
  get: function () {
    return _back.backIn;
  }
});
Object.defineProperty(exports, "easeBackInOut", {
  enumerable: true,
  get: function () {
    return _back.backInOut;
  }
});
Object.defineProperty(exports, "easeBackOut", {
  enumerable: true,
  get: function () {
    return _back.backOut;
  }
});
Object.defineProperty(exports, "easeBounce", {
  enumerable: true,
  get: function () {
    return _bounce.bounceOut;
  }
});
Object.defineProperty(exports, "easeBounceIn", {
  enumerable: true,
  get: function () {
    return _bounce.bounceIn;
  }
});
Object.defineProperty(exports, "easeBounceInOut", {
  enumerable: true,
  get: function () {
    return _bounce.bounceInOut;
  }
});
Object.defineProperty(exports, "easeBounceOut", {
  enumerable: true,
  get: function () {
    return _bounce.bounceOut;
  }
});
Object.defineProperty(exports, "easeCircle", {
  enumerable: true,
  get: function () {
    return _circle.circleInOut;
  }
});
Object.defineProperty(exports, "easeCircleIn", {
  enumerable: true,
  get: function () {
    return _circle.circleIn;
  }
});
Object.defineProperty(exports, "easeCircleInOut", {
  enumerable: true,
  get: function () {
    return _circle.circleInOut;
  }
});
Object.defineProperty(exports, "easeCircleOut", {
  enumerable: true,
  get: function () {
    return _circle.circleOut;
  }
});
Object.defineProperty(exports, "easeCubic", {
  enumerable: true,
  get: function () {
    return _cubic.cubicInOut;
  }
});
Object.defineProperty(exports, "easeCubicIn", {
  enumerable: true,
  get: function () {
    return _cubic.cubicIn;
  }
});
Object.defineProperty(exports, "easeCubicInOut", {
  enumerable: true,
  get: function () {
    return _cubic.cubicInOut;
  }
});
Object.defineProperty(exports, "easeCubicOut", {
  enumerable: true,
  get: function () {
    return _cubic.cubicOut;
  }
});
Object.defineProperty(exports, "easeElastic", {
  enumerable: true,
  get: function () {
    return _elastic.elasticOut;
  }
});
Object.defineProperty(exports, "easeElasticIn", {
  enumerable: true,
  get: function () {
    return _elastic.elasticIn;
  }
});
Object.defineProperty(exports, "easeElasticInOut", {
  enumerable: true,
  get: function () {
    return _elastic.elasticInOut;
  }
});
Object.defineProperty(exports, "easeElasticOut", {
  enumerable: true,
  get: function () {
    return _elastic.elasticOut;
  }
});
Object.defineProperty(exports, "easeExp", {
  enumerable: true,
  get: function () {
    return _exp.expInOut;
  }
});
Object.defineProperty(exports, "easeExpIn", {
  enumerable: true,
  get: function () {
    return _exp.expIn;
  }
});
Object.defineProperty(exports, "easeExpInOut", {
  enumerable: true,
  get: function () {
    return _exp.expInOut;
  }
});
Object.defineProperty(exports, "easeExpOut", {
  enumerable: true,
  get: function () {
    return _exp.expOut;
  }
});
Object.defineProperty(exports, "easeLinear", {
  enumerable: true,
  get: function () {
    return _linear.linear;
  }
});
Object.defineProperty(exports, "easePoly", {
  enumerable: true,
  get: function () {
    return _poly.polyInOut;
  }
});
Object.defineProperty(exports, "easePolyIn", {
  enumerable: true,
  get: function () {
    return _poly.polyIn;
  }
});
Object.defineProperty(exports, "easePolyInOut", {
  enumerable: true,
  get: function () {
    return _poly.polyInOut;
  }
});
Object.defineProperty(exports, "easePolyOut", {
  enumerable: true,
  get: function () {
    return _poly.polyOut;
  }
});
Object.defineProperty(exports, "easeQuad", {
  enumerable: true,
  get: function () {
    return _quad.quadInOut;
  }
});
Object.defineProperty(exports, "easeQuadIn", {
  enumerable: true,
  get: function () {
    return _quad.quadIn;
  }
});
Object.defineProperty(exports, "easeQuadInOut", {
  enumerable: true,
  get: function () {
    return _quad.quadInOut;
  }
});
Object.defineProperty(exports, "easeQuadOut", {
  enumerable: true,
  get: function () {
    return _quad.quadOut;
  }
});
Object.defineProperty(exports, "easeSin", {
  enumerable: true,
  get: function () {
    return _sin.sinInOut;
  }
});
Object.defineProperty(exports, "easeSinIn", {
  enumerable: true,
  get: function () {
    return _sin.sinIn;
  }
});
Object.defineProperty(exports, "easeSinInOut", {
  enumerable: true,
  get: function () {
    return _sin.sinInOut;
  }
});
Object.defineProperty(exports, "easeSinOut", {
  enumerable: true,
  get: function () {
    return _sin.sinOut;
  }
});
var _linear = require("./linear.js");
var _quad = require("./quad.js");
var _cubic = require("./cubic.js");
var _poly = require("./poly.js");
var _sin = require("./sin.js");
var _exp = require("./exp.js");
var _circle = require("./circle.js");
var _bounce = require("./bounce.js");
var _back = require("./back.js");
var _elastic = require("./elastic.js");
},{"./linear.js":"VNJ7","./quad.js":"NlV3","./cubic.js":"ATE0","./poly.js":"xJOL","./sin.js":"aKKp","./exp.js":"GEdw","./circle.js":"erIK","./bounce.js":"WtpZ","./back.js":"NHFs","./elastic.js":"ZzFe"}],"oSxG":[function(require,module,exports) {
var define;
var global = arguments[3];
(function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open("GET",a),d.responseType="blob",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error("could not download file")},d.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open("","_blank"),g&&(g.document.title=g.document.body.innerText="downloading..."),"string"==typeof b)return c(b,d,e);var h="application/octet-stream"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\/[\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&"undefined"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,"undefined"!=typeof module&&(module.exports=g)});


},{}],"wBnk":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.applyPatches = exports.Immer = void 0;
exports.castDraft = K;
exports.castImmutable = $;
exports.createDraft = void 0;
exports.current = R;
exports.default = void 0;
exports.enableAllPlugins = J;
exports.enableES5 = F;
exports.enableMapSet = C;
exports.enablePatches = T;
exports.finishDraft = void 0;
exports.freeze = d;
exports.immerable = void 0;
exports.isDraft = r;
exports.isDraftable = t;
exports.nothing = void 0;
exports.original = e;
exports.setUseProxies = exports.setAutoFreeze = exports.produceWithPatches = exports.produce = void 0;
function n(n) {
  for (var r = arguments.length, t = Array(r > 1 ? r - 1 : 0), e = 1; e < r; e++) t[e - 1] = arguments[e];
  if ("production" !== "production") {
    var i = Y[n],
      o = i ? "function" == typeof i ? i.apply(null, t) : i : "unknown error nr: " + n;
    throw Error("[Immer] " + o);
  }
  throw Error("[Immer] minified error nr: " + n + (t.length ? " " + t.map(function (n) {
    return "'" + n + "'";
  }).join(",") : "") + ". Find the full error at: https://bit.ly/3cXEKWf");
}
function r(n) {
  return !!n && !!n[Q];
}
function t(n) {
  var r;
  return !!n && (function (n) {
    if (!n || "object" != typeof n) return !1;
    var r = Object.getPrototypeOf(n);
    if (null === r) return !0;
    var t = Object.hasOwnProperty.call(r, "constructor") && r.constructor;
    return t === Object || "function" == typeof t && Function.toString.call(t) === Z;
  }(n) || Array.isArray(n) || !!n[L] || !!(null === (r = n.constructor) || void 0 === r ? void 0 : r[L]) || s(n) || v(n));
}
function e(t) {
  return r(t) || n(23, t), t[Q].t;
}
function i(n, r, t) {
  void 0 === t && (t = !1), 0 === o(n) ? (t ? Object.keys : nn)(n).forEach(function (e) {
    t && "symbol" == typeof e || r(e, n[e], n);
  }) : n.forEach(function (t, e) {
    return r(e, t, n);
  });
}
function o(n) {
  var r = n[Q];
  return r ? r.i > 3 ? r.i - 4 : r.i : Array.isArray(n) ? 1 : s(n) ? 2 : v(n) ? 3 : 0;
}
function u(n, r) {
  return 2 === o(n) ? n.has(r) : Object.prototype.hasOwnProperty.call(n, r);
}
function a(n, r) {
  return 2 === o(n) ? n.get(r) : n[r];
}
function f(n, r, t) {
  var e = o(n);
  2 === e ? n.set(r, t) : 3 === e ? n.add(t) : n[r] = t;
}
function c(n, r) {
  return n === r ? 0 !== n || 1 / n == 1 / r : n != n && r != r;
}
function s(n) {
  return X && n instanceof Map;
}
function v(n) {
  return q && n instanceof Set;
}
function p(n) {
  return n.o || n.t;
}
function l(n) {
  if (Array.isArray(n)) return Array.prototype.slice.call(n);
  var r = rn(n);
  delete r[Q];
  for (var t = nn(r), e = 0; e < t.length; e++) {
    var i = t[e],
      o = r[i];
    !1 === o.writable && (o.writable = !0, o.configurable = !0), (o.get || o.set) && (r[i] = {
      configurable: !0,
      writable: !0,
      enumerable: o.enumerable,
      value: n[i]
    });
  }
  return Object.create(Object.getPrototypeOf(n), r);
}
function d(n, e) {
  return void 0 === e && (e = !1), y(n) || r(n) || !t(n) || (o(n) > 1 && (n.set = n.add = n.clear = n.delete = h), Object.freeze(n), e && i(n, function (n, r) {
    return d(r, !0);
  }, !0)), n;
}
function h() {
  n(2);
}
function y(n) {
  return null == n || "object" != typeof n || Object.isFrozen(n);
}
function b(r) {
  var t = tn[r];
  return t || n(18, r), t;
}
function m(n, r) {
  tn[n] || (tn[n] = r);
}
function _() {
  return "production" === "production" || U || n(0), U;
}
function j(n, r) {
  r && (b("Patches"), n.u = [], n.s = [], n.v = r);
}
function g(n) {
  O(n), n.p.forEach(S), n.p = null;
}
function O(n) {
  n === U && (U = n.l);
}
function w(n) {
  return U = {
    p: [],
    l: U,
    h: n,
    m: !0,
    _: 0
  };
}
function S(n) {
  var r = n[Q];
  0 === r.i || 1 === r.i ? r.j() : r.g = !0;
}
function P(r, e) {
  e._ = e.p.length;
  var i = e.p[0],
    o = void 0 !== r && r !== i;
  return e.h.O || b("ES5").S(e, r, o), o ? (i[Q].P && (g(e), n(4)), t(r) && (r = M(e, r), e.l || x(e, r)), e.u && b("Patches").M(i[Q].t, r, e.u, e.s)) : r = M(e, i, []), g(e), e.u && e.v(e.u, e.s), r !== H ? r : void 0;
}
function M(n, r, t) {
  if (y(r)) return r;
  var e = r[Q];
  if (!e) return i(r, function (i, o) {
    return A(n, e, r, i, o, t);
  }, !0), r;
  if (e.A !== n) return r;
  if (!e.P) return x(n, e.t, !0), e.t;
  if (!e.I) {
    e.I = !0, e.A._--;
    var o = 4 === e.i || 5 === e.i ? e.o = l(e.k) : e.o,
      u = o,
      a = !1;
    3 === e.i && (u = new Set(o), o.clear(), a = !0), i(u, function (r, i) {
      return A(n, e, o, r, i, t, a);
    }), x(n, o, !1), t && n.u && b("Patches").N(e, t, n.u, n.s);
  }
  return e.o;
}
function A(e, i, o, a, c, s, v) {
  if ("production" !== "production" && c === o && n(5), r(c)) {
    var p = M(e, c, s && i && 3 !== i.i && !u(i.R, a) ? s.concat(a) : void 0);
    if (f(o, a, p), !r(p)) return;
    e.m = !1;
  } else v && o.add(c);
  if (t(c) && !y(c)) {
    if (!e.h.D && e._ < 1) return;
    M(e, c), i && i.A.l || x(e, c);
  }
}
function x(n, r, t) {
  void 0 === t && (t = !1), !n.l && n.h.D && n.m && d(r, t);
}
function z(n, r) {
  var t = n[Q];
  return (t ? p(t) : n)[r];
}
function I(n, r) {
  if (r in n) for (var t = Object.getPrototypeOf(n); t;) {
    var e = Object.getOwnPropertyDescriptor(t, r);
    if (e) return e;
    t = Object.getPrototypeOf(t);
  }
}
function k(n) {
  n.P || (n.P = !0, n.l && k(n.l));
}
function E(n) {
  n.o || (n.o = l(n.t));
}
function N(n, r, t) {
  var e = s(r) ? b("MapSet").F(r, t) : v(r) ? b("MapSet").T(r, t) : n.O ? function (n, r) {
    var t = Array.isArray(n),
      e = {
        i: t ? 1 : 0,
        A: r ? r.A : _(),
        P: !1,
        I: !1,
        R: {},
        l: r,
        t: n,
        k: null,
        o: null,
        j: null,
        C: !1
      },
      i = e,
      o = en;
    t && (i = [e], o = on);
    var u = Proxy.revocable(i, o),
      a = u.revoke,
      f = u.proxy;
    return e.k = f, e.j = a, f;
  }(r, t) : b("ES5").J(r, t);
  return (t ? t.A : _()).p.push(e), e;
}
function R(e) {
  return r(e) || n(22, e), function n(r) {
    if (!t(r)) return r;
    var e,
      u = r[Q],
      c = o(r);
    if (u) {
      if (!u.P && (u.i < 4 || !b("ES5").K(u))) return u.t;
      u.I = !0, e = D(r, c), u.I = !1;
    } else e = D(r, c);
    return i(e, function (r, t) {
      u && a(u.t, r) === t || f(e, r, n(t));
    }), 3 === c ? new Set(e) : e;
  }(e);
}
function D(n, r) {
  switch (r) {
    case 2:
      return new Map(n);
    case 3:
      return Array.from(n);
  }
  return l(n);
}
function F() {
  function t(n, r) {
    var t = s[n];
    return t ? t.enumerable = r : s[n] = t = {
      configurable: !0,
      enumerable: r,
      get: function () {
        var r = this[Q];
        return "production" !== "production" && f(r), en.get(r, n);
      },
      set: function (r) {
        var t = this[Q];
        "production" !== "production" && f(t), en.set(t, n, r);
      }
    }, t;
  }
  function e(n) {
    for (var r = n.length - 1; r >= 0; r--) {
      var t = n[r][Q];
      if (!t.P) switch (t.i) {
        case 5:
          a(t) && k(t);
          break;
        case 4:
          o(t) && k(t);
      }
    }
  }
  function o(n) {
    for (var r = n.t, t = n.k, e = nn(t), i = e.length - 1; i >= 0; i--) {
      var o = e[i];
      if (o !== Q) {
        var a = r[o];
        if (void 0 === a && !u(r, o)) return !0;
        var f = t[o],
          s = f && f[Q];
        if (s ? s.t !== a : !c(f, a)) return !0;
      }
    }
    var v = !!r[Q];
    return e.length !== nn(r).length + (v ? 0 : 1);
  }
  function a(n) {
    var r = n.k;
    if (r.length !== n.t.length) return !0;
    var t = Object.getOwnPropertyDescriptor(r, r.length - 1);
    if (t && !t.get) return !0;
    for (var e = 0; e < r.length; e++) if (!r.hasOwnProperty(e)) return !0;
    return !1;
  }
  function f(r) {
    r.g && n(3, JSON.stringify(p(r)));
  }
  var s = {};
  m("ES5", {
    J: function (n, r) {
      var e = Array.isArray(n),
        i = function (n, r) {
          if (n) {
            for (var e = Array(r.length), i = 0; i < r.length; i++) Object.defineProperty(e, "" + i, t(i, !0));
            return e;
          }
          var o = rn(r);
          delete o[Q];
          for (var u = nn(o), a = 0; a < u.length; a++) {
            var f = u[a];
            o[f] = t(f, n || !!o[f].enumerable);
          }
          return Object.create(Object.getPrototypeOf(r), o);
        }(e, n),
        o = {
          i: e ? 5 : 4,
          A: r ? r.A : _(),
          P: !1,
          I: !1,
          R: {},
          l: r,
          t: n,
          k: i,
          o: null,
          g: !1,
          C: !1
        };
      return Object.defineProperty(i, Q, {
        value: o,
        writable: !0
      }), i;
    },
    S: function (n, t, o) {
      o ? r(t) && t[Q].A === n && e(n.p) : (n.u && function n(r) {
        if (r && "object" == typeof r) {
          var t = r[Q];
          if (t) {
            var e = t.t,
              o = t.k,
              f = t.R,
              c = t.i;
            if (4 === c) i(o, function (r) {
              r !== Q && (void 0 !== e[r] || u(e, r) ? f[r] || n(o[r]) : (f[r] = !0, k(t)));
            }), i(e, function (n) {
              void 0 !== o[n] || u(o, n) || (f[n] = !1, k(t));
            });else if (5 === c) {
              if (a(t) && (k(t), f.length = !0), o.length < e.length) for (var s = o.length; s < e.length; s++) f[s] = !1;else for (var v = e.length; v < o.length; v++) f[v] = !0;
              for (var p = Math.min(o.length, e.length), l = 0; l < p; l++) o.hasOwnProperty(l) || (f[l] = !0), void 0 === f[l] && n(o[l]);
            }
          }
        }
      }(n.p[0]), e(n.p));
    },
    K: function (n) {
      return 4 === n.i ? o(n) : a(n);
    }
  });
}
function T() {
  function e(n) {
    if (!t(n)) return n;
    if (Array.isArray(n)) return n.map(e);
    if (s(n)) return new Map(Array.from(n.entries()).map(function (n) {
      return [n[0], e(n[1])];
    }));
    if (v(n)) return new Set(Array.from(n).map(e));
    var r = Object.create(Object.getPrototypeOf(n));
    for (var i in n) r[i] = e(n[i]);
    return u(n, L) && (r[L] = n[L]), r;
  }
  function f(n) {
    return r(n) ? e(n) : n;
  }
  var c = "add";
  m("Patches", {
    $: function (r, t) {
      return t.forEach(function (t) {
        for (var i = t.path, u = t.op, f = r, s = 0; s < i.length - 1; s++) {
          var v = o(f),
            p = i[s];
          "string" != typeof p && "number" != typeof p && (p = "" + p), 0 !== v && 1 !== v || "__proto__" !== p && "constructor" !== p || n(24), "function" == typeof f && "prototype" === p && n(24), "object" != typeof (f = a(f, p)) && n(15, i.join("/"));
        }
        var l = o(f),
          d = e(t.value),
          h = i[i.length - 1];
        switch (u) {
          case "replace":
            switch (l) {
              case 2:
                return f.set(h, d);
              case 3:
                n(16);
              default:
                return f[h] = d;
            }
          case c:
            switch (l) {
              case 1:
                return "-" === h ? f.push(d) : f.splice(h, 0, d);
              case 2:
                return f.set(h, d);
              case 3:
                return f.add(d);
              default:
                return f[h] = d;
            }
          case "remove":
            switch (l) {
              case 1:
                return f.splice(h, 1);
              case 2:
                return f.delete(h);
              case 3:
                return f.delete(t.value);
              default:
                return delete f[h];
            }
          default:
            n(17, u);
        }
      }), r;
    },
    N: function (n, r, t, e) {
      switch (n.i) {
        case 0:
        case 4:
        case 2:
          return function (n, r, t, e) {
            var o = n.t,
              s = n.o;
            i(n.R, function (n, i) {
              var v = a(o, n),
                p = a(s, n),
                l = i ? u(o, n) ? "replace" : c : "remove";
              if (v !== p || "replace" !== l) {
                var d = r.concat(n);
                t.push("remove" === l ? {
                  op: l,
                  path: d
                } : {
                  op: l,
                  path: d,
                  value: p
                }), e.push(l === c ? {
                  op: "remove",
                  path: d
                } : "remove" === l ? {
                  op: c,
                  path: d,
                  value: f(v)
                } : {
                  op: "replace",
                  path: d,
                  value: f(v)
                });
              }
            });
          }(n, r, t, e);
        case 5:
        case 1:
          return function (n, r, t, e) {
            var i = n.t,
              o = n.R,
              u = n.o;
            if (u.length < i.length) {
              var a = [u, i];
              i = a[0], u = a[1];
              var s = [e, t];
              t = s[0], e = s[1];
            }
            for (var v = 0; v < i.length; v++) if (o[v] && u[v] !== i[v]) {
              var p = r.concat([v]);
              t.push({
                op: "replace",
                path: p,
                value: f(u[v])
              }), e.push({
                op: "replace",
                path: p,
                value: f(i[v])
              });
            }
            for (var l = i.length; l < u.length; l++) {
              var d = r.concat([l]);
              t.push({
                op: c,
                path: d,
                value: f(u[l])
              });
            }
            i.length < u.length && e.push({
              op: "replace",
              path: r.concat(["length"]),
              value: i.length
            });
          }(n, r, t, e);
        case 3:
          return function (n, r, t, e) {
            var i = n.t,
              o = n.o,
              u = 0;
            i.forEach(function (n) {
              if (!o.has(n)) {
                var i = r.concat([u]);
                t.push({
                  op: "remove",
                  path: i,
                  value: n
                }), e.unshift({
                  op: c,
                  path: i,
                  value: n
                });
              }
              u++;
            }), u = 0, o.forEach(function (n) {
              if (!i.has(n)) {
                var o = r.concat([u]);
                t.push({
                  op: c,
                  path: o,
                  value: n
                }), e.unshift({
                  op: "remove",
                  path: o,
                  value: n
                });
              }
              u++;
            });
          }(n, r, t, e);
      }
    },
    M: function (n, r, t, e) {
      t.push({
        op: "replace",
        path: [],
        value: r === H ? void 0 : r
      }), e.push({
        op: "replace",
        path: [],
        value: n
      });
    }
  });
}
function C() {
  function r(n, r) {
    function t() {
      this.constructor = n;
    }
    a(n, r), n.prototype = (t.prototype = r.prototype, new t());
  }
  function e(n) {
    n.o || (n.R = new Map(), n.o = new Map(n.t));
  }
  function o(n) {
    n.o || (n.o = new Set(), n.t.forEach(function (r) {
      if (t(r)) {
        var e = N(n.A.h, r, n);
        n.p.set(r, e), n.o.add(e);
      } else n.o.add(r);
    }));
  }
  function u(r) {
    r.g && n(3, JSON.stringify(p(r)));
  }
  var a = function (n, r) {
      return (a = Object.setPrototypeOf || {
        __proto__: []
      } instanceof Array && function (n, r) {
        n.__proto__ = r;
      } || function (n, r) {
        for (var t in r) r.hasOwnProperty(t) && (n[t] = r[t]);
      })(n, r);
    },
    f = function () {
      function n(n, r) {
        return this[Q] = {
          i: 2,
          l: r,
          A: r ? r.A : _(),
          P: !1,
          I: !1,
          o: void 0,
          R: void 0,
          t: n,
          k: this,
          C: !1,
          g: !1
        }, this;
      }
      r(n, Map);
      var o = n.prototype;
      return Object.defineProperty(o, "size", {
        get: function () {
          return p(this[Q]).size;
        }
      }), o.has = function (n) {
        return p(this[Q]).has(n);
      }, o.set = function (n, r) {
        var t = this[Q];
        return u(t), p(t).has(n) && p(t).get(n) === r || (e(t), k(t), t.R.set(n, !0), t.o.set(n, r), t.R.set(n, !0)), this;
      }, o.delete = function (n) {
        if (!this.has(n)) return !1;
        var r = this[Q];
        return u(r), e(r), k(r), r.t.has(n) ? r.R.set(n, !1) : r.R.delete(n), r.o.delete(n), !0;
      }, o.clear = function () {
        var n = this[Q];
        u(n), p(n).size && (e(n), k(n), n.R = new Map(), i(n.t, function (r) {
          n.R.set(r, !1);
        }), n.o.clear());
      }, o.forEach = function (n, r) {
        var t = this;
        p(this[Q]).forEach(function (e, i) {
          n.call(r, t.get(i), i, t);
        });
      }, o.get = function (n) {
        var r = this[Q];
        u(r);
        var i = p(r).get(n);
        if (r.I || !t(i)) return i;
        if (i !== r.t.get(n)) return i;
        var o = N(r.A.h, i, r);
        return e(r), r.o.set(n, o), o;
      }, o.keys = function () {
        return p(this[Q]).keys();
      }, o.values = function () {
        var n,
          r = this,
          t = this.keys();
        return (n = {})[V] = function () {
          return r.values();
        }, n.next = function () {
          var n = t.next();
          return n.done ? n : {
            done: !1,
            value: r.get(n.value)
          };
        }, n;
      }, o.entries = function () {
        var n,
          r = this,
          t = this.keys();
        return (n = {})[V] = function () {
          return r.entries();
        }, n.next = function () {
          var n = t.next();
          if (n.done) return n;
          var e = r.get(n.value);
          return {
            done: !1,
            value: [n.value, e]
          };
        }, n;
      }, o[V] = function () {
        return this.entries();
      }, n;
    }(),
    c = function () {
      function n(n, r) {
        return this[Q] = {
          i: 3,
          l: r,
          A: r ? r.A : _(),
          P: !1,
          I: !1,
          o: void 0,
          t: n,
          k: this,
          p: new Map(),
          g: !1,
          C: !1
        }, this;
      }
      r(n, Set);
      var t = n.prototype;
      return Object.defineProperty(t, "size", {
        get: function () {
          return p(this[Q]).size;
        }
      }), t.has = function (n) {
        var r = this[Q];
        return u(r), r.o ? !!r.o.has(n) || !(!r.p.has(n) || !r.o.has(r.p.get(n))) : r.t.has(n);
      }, t.add = function (n) {
        var r = this[Q];
        return u(r), this.has(n) || (o(r), k(r), r.o.add(n)), this;
      }, t.delete = function (n) {
        if (!this.has(n)) return !1;
        var r = this[Q];
        return u(r), o(r), k(r), r.o.delete(n) || !!r.p.has(n) && r.o.delete(r.p.get(n));
      }, t.clear = function () {
        var n = this[Q];
        u(n), p(n).size && (o(n), k(n), n.o.clear());
      }, t.values = function () {
        var n = this[Q];
        return u(n), o(n), n.o.values();
      }, t.entries = function () {
        var n = this[Q];
        return u(n), o(n), n.o.entries();
      }, t.keys = function () {
        return this.values();
      }, t[V] = function () {
        return this.values();
      }, t.forEach = function (n, r) {
        for (var t = this.values(), e = t.next(); !e.done;) n.call(r, e.value, e.value, this), e = t.next();
      }, n;
    }();
  m("MapSet", {
    F: function (n, r) {
      return new f(n, r);
    },
    T: function (n, r) {
      return new c(n, r);
    }
  });
}
function J() {
  F(), C(), T();
}
function K(n) {
  return n;
}
function $(n) {
  return n;
}
var G,
  U,
  W = "undefined" != typeof Symbol && "symbol" == typeof Symbol("x"),
  X = "undefined" != typeof Map,
  q = "undefined" != typeof Set,
  B = "undefined" != typeof Proxy && void 0 !== Proxy.revocable && "undefined" != typeof Reflect,
  H = exports.nothing = W ? Symbol.for("immer-nothing") : ((G = {})["immer-nothing"] = !0, G),
  L = exports.immerable = W ? Symbol.for("immer-draftable") : "__$immer_draftable",
  Q = W ? Symbol.for("immer-state") : "__$immer_state",
  V = "undefined" != typeof Symbol && Symbol.iterator || "@@iterator",
  Y = {
    0: "Illegal state",
    1: "Immer drafts cannot have computed properties",
    2: "This object has been frozen and should not be mutated",
    3: function (n) {
      return "Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + n;
    },
    4: "An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.",
    5: "Immer forbids circular references",
    6: "The first or second argument to `produce` must be a function",
    7: "The third argument to `produce` must be a function or undefined",
    8: "First argument to `createDraft` must be a plain object, an array, or an immerable object",
    9: "First argument to `finishDraft` must be a draft returned by `createDraft`",
    10: "The given draft is already finalized",
    11: "Object.defineProperty() cannot be used on an Immer draft",
    12: "Object.setPrototypeOf() cannot be used on an Immer draft",
    13: "Immer only supports deleting array indices",
    14: "Immer only supports setting array indices and the 'length' property",
    15: function (n) {
      return "Cannot apply patch, path doesn't resolve: " + n;
    },
    16: 'Sets cannot have "replace" patches.',
    17: function (n) {
      return "Unsupported patch operation: " + n;
    },
    18: function (n) {
      return "The plugin for '" + n + "' has not been loaded into Immer. To enable the plugin, import and call `enable" + n + "()` when initializing your application.";
    },
    20: "Cannot use proxies if Proxy, Proxy.revocable or Reflect are not available",
    21: function (n) {
      return "produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '" + n + "'";
    },
    22: function (n) {
      return "'current' expects a draft, got: " + n;
    },
    23: function (n) {
      return "'original' expects a draft, got: " + n;
    },
    24: "Patching reserved attributes like __proto__, prototype and constructor is not allowed"
  },
  Z = "" + Object.prototype.constructor,
  nn = "undefined" != typeof Reflect && Reflect.ownKeys ? Reflect.ownKeys : void 0 !== Object.getOwnPropertySymbols ? function (n) {
    return Object.getOwnPropertyNames(n).concat(Object.getOwnPropertySymbols(n));
  } : Object.getOwnPropertyNames,
  rn = Object.getOwnPropertyDescriptors || function (n) {
    var r = {};
    return nn(n).forEach(function (t) {
      r[t] = Object.getOwnPropertyDescriptor(n, t);
    }), r;
  },
  tn = {},
  en = {
    get: function (n, r) {
      if (r === Q) return n;
      var e = p(n);
      if (!u(e, r)) return function (n, r, t) {
        var e,
          i = I(r, t);
        return i ? "value" in i ? i.value : null === (e = i.get) || void 0 === e ? void 0 : e.call(n.k) : void 0;
      }(n, e, r);
      var i = e[r];
      return n.I || !t(i) ? i : i === z(n.t, r) ? (E(n), n.o[r] = N(n.A.h, i, n)) : i;
    },
    has: function (n, r) {
      return r in p(n);
    },
    ownKeys: function (n) {
      return Reflect.ownKeys(p(n));
    },
    set: function (n, r, t) {
      var e = I(p(n), r);
      if (null == e ? void 0 : e.set) return e.set.call(n.k, t), !0;
      if (!n.P) {
        var i = z(p(n), r),
          o = null == i ? void 0 : i[Q];
        if (o && o.t === t) return n.o[r] = t, n.R[r] = !1, !0;
        if (c(t, i) && (void 0 !== t || u(n.t, r))) return !0;
        E(n), k(n);
      }
      return n.o[r] === t && (void 0 !== t || r in n.o) || Number.isNaN(t) && Number.isNaN(n.o[r]) || (n.o[r] = t, n.R[r] = !0), !0;
    },
    deleteProperty: function (n, r) {
      return void 0 !== z(n.t, r) || r in n.t ? (n.R[r] = !1, E(n), k(n)) : delete n.R[r], n.o && delete n.o[r], !0;
    },
    getOwnPropertyDescriptor: function (n, r) {
      var t = p(n),
        e = Reflect.getOwnPropertyDescriptor(t, r);
      return e ? {
        writable: !0,
        configurable: 1 !== n.i || "length" !== r,
        enumerable: e.enumerable,
        value: t[r]
      } : e;
    },
    defineProperty: function () {
      n(11);
    },
    getPrototypeOf: function (n) {
      return Object.getPrototypeOf(n.t);
    },
    setPrototypeOf: function () {
      n(12);
    }
  },
  on = {};
i(en, function (n, r) {
  on[n] = function () {
    return arguments[0] = arguments[0][0], r.apply(this, arguments);
  };
}), on.deleteProperty = function (r, t) {
  return "production" !== "production" && isNaN(parseInt(t)) && n(13), on.set.call(this, r, t, void 0);
}, on.set = function (r, t, e) {
  return "production" !== "production" && "length" !== t && isNaN(parseInt(t)) && n(14), en.set.call(this, r[0], t, e, r[0]);
};
var un = exports.Immer = function () {
    function e(r) {
      var e = this;
      this.O = B, this.D = !0, this.produce = function (r, i, o) {
        if ("function" == typeof r && "function" != typeof i) {
          var u = i;
          i = r;
          var a = e;
          return function (n) {
            var r = this;
            void 0 === n && (n = u);
            for (var t = arguments.length, e = Array(t > 1 ? t - 1 : 0), o = 1; o < t; o++) e[o - 1] = arguments[o];
            return a.produce(n, function (n) {
              var t;
              return (t = i).call.apply(t, [r, n].concat(e));
            });
          };
        }
        var f;
        if ("function" != typeof i && n(6), void 0 !== o && "function" != typeof o && n(7), t(r)) {
          var c = w(e),
            s = N(e, r, void 0),
            v = !0;
          try {
            f = i(s), v = !1;
          } finally {
            v ? g(c) : O(c);
          }
          return "undefined" != typeof Promise && f instanceof Promise ? f.then(function (n) {
            return j(c, o), P(n, c);
          }, function (n) {
            throw g(c), n;
          }) : (j(c, o), P(f, c));
        }
        if (!r || "object" != typeof r) {
          if (void 0 === (f = i(r)) && (f = r), f === H && (f = void 0), e.D && d(f, !0), o) {
            var p = [],
              l = [];
            b("Patches").M(r, f, p, l), o(p, l);
          }
          return f;
        }
        n(21, r);
      }, this.produceWithPatches = function (n, r) {
        if ("function" == typeof n) return function (r) {
          for (var t = arguments.length, i = Array(t > 1 ? t - 1 : 0), o = 1; o < t; o++) i[o - 1] = arguments[o];
          return e.produceWithPatches(r, function (r) {
            return n.apply(void 0, [r].concat(i));
          });
        };
        var t,
          i,
          o = e.produce(n, r, function (n, r) {
            t = n, i = r;
          });
        return "undefined" != typeof Promise && o instanceof Promise ? o.then(function (n) {
          return [n, t, i];
        }) : [o, t, i];
      }, "boolean" == typeof (null == r ? void 0 : r.useProxies) && this.setUseProxies(r.useProxies), "boolean" == typeof (null == r ? void 0 : r.autoFreeze) && this.setAutoFreeze(r.autoFreeze);
    }
    var i = e.prototype;
    return i.createDraft = function (e) {
      t(e) || n(8), r(e) && (e = R(e));
      var i = w(this),
        o = N(this, e, void 0);
      return o[Q].C = !0, O(i), o;
    }, i.finishDraft = function (r, t) {
      var e = r && r[Q];
      "production" !== "production" && (e && e.C || n(9), e.I && n(10));
      var i = e.A;
      return j(i, t), P(void 0, i);
    }, i.setAutoFreeze = function (n) {
      this.D = n;
    }, i.setUseProxies = function (r) {
      r && !B && n(20), this.O = r;
    }, i.applyPatches = function (n, t) {
      var e;
      for (e = t.length - 1; e >= 0; e--) {
        var i = t[e];
        if (0 === i.path.length && "replace" === i.op) {
          n = i.value;
          break;
        }
      }
      e > -1 && (t = t.slice(e + 1));
      var o = b("Patches").$;
      return r(n) ? o(n, t) : this.produce(n, function (n) {
        return o(n, t);
      });
    }, e;
  }(),
  an = new un(),
  fn = exports.produce = an.produce,
  cn = exports.produceWithPatches = an.produceWithPatches.bind(an),
  sn = exports.setAutoFreeze = an.setAutoFreeze.bind(an),
  vn = exports.setUseProxies = an.setUseProxies.bind(an),
  pn = exports.applyPatches = an.applyPatches.bind(an),
  ln = exports.createDraft = an.createDraft.bind(an),
  dn = exports.finishDraft = an.finishDraft.bind(an);
var _default = exports.default = fn;
},{}],"ZBUn":[function(require,module,exports) {
if ("production" === 'production') {
  module.exports = JSZip;
} else {
  module.exports = JSZip;
}
},{}],"AdD9":[function(require,module,exports) {
var global = arguments[3];

/**
 * lodash (Custom Build) <https://lodash.com/>
 * Build: `lodash modularize exports="npm" -o ./`
 * Copyright jQuery Foundation and other contributors <https://jquery.org/>
 * Released under MIT license <https://lodash.com/license>
 * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
 * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 */

/** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200;

/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;

/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
    arrayTag = '[object Array]',
    boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    funcTag = '[object Function]',
    genTag = '[object GeneratorFunction]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    objectTag = '[object Object]',
    promiseTag = '[object Promise]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    symbolTag = '[object Symbol]',
    weakMapTag = '[object WeakMap]';

var arrayBufferTag = '[object ArrayBuffer]',
    dataViewTag = '[object DataView]',
    float32Tag = '[object Float32Array]',
    float64Tag = '[object Float64Array]',
    int8Tag = '[object Int8Array]',
    int16Tag = '[object Int16Array]',
    int32Tag = '[object Int32Array]',
    uint8Tag = '[object Uint8Array]',
    uint8ClampedTag = '[object Uint8ClampedArray]',
    uint16Tag = '[object Uint16Array]',
    uint32Tag = '[object Uint32Array]';

/**
 * Used to match `RegExp`
 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
 */
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;

/** Used to match `RegExp` flags from their coerced string values. */
var reFlags = /\w*$/;

/** Used to detect host constructors (Safari). */
var reIsHostCtor = /^\[object .+?Constructor\]$/;

/** Used to detect unsigned integer values. */
var reIsUint = /^(?:0|[1-9]\d*)$/;

/** Used to identify `toStringTag` values supported by `_.clone`. */
var cloneableTags = {};
cloneableTags[argsTag] = cloneableTags[arrayTag] =
cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
cloneableTags[boolTag] = cloneableTags[dateTag] =
cloneableTags[float32Tag] = cloneableTags[float64Tag] =
cloneableTags[int8Tag] = cloneableTags[int16Tag] =
cloneableTags[int32Tag] = cloneableTags[mapTag] =
cloneableTags[numberTag] = cloneableTags[objectTag] =
cloneableTags[regexpTag] = cloneableTags[setTag] =
cloneableTags[stringTag] = cloneableTags[symbolTag] =
cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
cloneableTags[errorTag] = cloneableTags[funcTag] =
cloneableTags[weakMapTag] = false;

/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;

/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();

/** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;

/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;

/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;

/**
 * Adds the key-value `pair` to `map`.
 *
 * @private
 * @param {Object} map The map to modify.
 * @param {Array} pair The key-value pair to add.
 * @returns {Object} Returns `map`.
 */
function addMapEntry(map, pair) {
  // Don't return `map.set` because it's not chainable in IE 11.
  map.set(pair[0], pair[1]);
  return map;
}

/**
 * Adds `value` to `set`.
 *
 * @private
 * @param {Object} set The set to modify.
 * @param {*} value The value to add.
 * @returns {Object} Returns `set`.
 */
function addSetEntry(set, value) {
  // Don't return `set.add` because it's not chainable in IE 11.
  set.add(value);
  return set;
}

/**
 * A specialized version of `_.forEach` for arrays without support for
 * iteratee shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns `array`.
 */
function arrayEach(array, iteratee) {
  var index = -1,
      length = array ? array.length : 0;

  while (++index < length) {
    if (iteratee(array[index], index, array) === false) {
      break;
    }
  }
  return array;
}

/**
 * Appends the elements of `values` to `array`.
 *
 * @private
 * @param {Array} array The array to modify.
 * @param {Array} values The values to append.
 * @returns {Array} Returns `array`.
 */
function arrayPush(array, values) {
  var index = -1,
      length = values.length,
      offset = array.length;

  while (++index < length) {
    array[offset + index] = values[index];
  }
  return array;
}

/**
 * A specialized version of `_.reduce` for arrays without support for
 * iteratee shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @param {*} [accumulator] The initial value.
 * @param {boolean} [initAccum] Specify using the first element of `array` as
 *  the initial value.
 * @returns {*} Returns the accumulated value.
 */
function arrayReduce(array, iteratee, accumulator, initAccum) {
  var index = -1,
      length = array ? array.length : 0;

  if (initAccum && length) {
    accumulator = array[++index];
  }
  while (++index < length) {
    accumulator = iteratee(accumulator, array[index], index, array);
  }
  return accumulator;
}

/**
 * The base implementation of `_.times` without support for iteratee shorthands
 * or max array length checks.
 *
 * @private
 * @param {number} n The number of times to invoke `iteratee`.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the array of results.
 */
function baseTimes(n, iteratee) {
  var index = -1,
      result = Array(n);

  while (++index < n) {
    result[index] = iteratee(index);
  }
  return result;
}

/**
 * Gets the value at `key` of `object`.
 *
 * @private
 * @param {Object} [object] The object to query.
 * @param {string} key The key of the property to get.
 * @returns {*} Returns the property value.
 */
function getValue(object, key) {
  return object == null ? undefined : object[key];
}

/**
 * Checks if `value` is a host object in IE < 9.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
 */
function isHostObject(value) {
  // Many host objects are `Object` objects that can coerce to strings
  // despite having improperly defined `toString` methods.
  var result = false;
  if (value != null && typeof value.toString != 'function') {
    try {
      result = !!(value + '');
    } catch (e) {}
  }
  return result;
}

/**
 * Converts `map` to its key-value pairs.
 *
 * @private
 * @param {Object} map The map to convert.
 * @returns {Array} Returns the key-value pairs.
 */
function mapToArray(map) {
  var index = -1,
      result = Array(map.size);

  map.forEach(function(value, key) {
    result[++index] = [key, value];
  });
  return result;
}

/**
 * Creates a unary function that invokes `func` with its argument transformed.
 *
 * @private
 * @param {Function} func The function to wrap.
 * @param {Function} transform The argument transform.
 * @returns {Function} Returns the new function.
 */
function overArg(func, transform) {
  return function(arg) {
    return func(transform(arg));
  };
}

/**
 * Converts `set` to an array of its values.
 *
 * @private
 * @param {Object} set The set to convert.
 * @returns {Array} Returns the values.
 */
function setToArray(set) {
  var index = -1,
      result = Array(set.size);

  set.forEach(function(value) {
    result[++index] = value;
  });
  return result;
}

/** Used for built-in method references. */
var arrayProto = Array.prototype,
    funcProto = Function.prototype,
    objectProto = Object.prototype;

/** Used to detect overreaching core-js shims. */
var coreJsData = root['__core-js_shared__'];

/** Used to detect methods masquerading as native. */
var maskSrcKey = (function() {
  var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
  return uid ? ('Symbol(src)_1.' + uid) : '';
}());

/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var objectToString = objectProto.toString;

/** Used to detect if a method is native. */
var reIsNative = RegExp('^' +
  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);

/** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined,
    Symbol = root.Symbol,
    Uint8Array = root.Uint8Array,
    getPrototype = overArg(Object.getPrototypeOf, Object),
    objectCreate = Object.create,
    propertyIsEnumerable = objectProto.propertyIsEnumerable,
    splice = arrayProto.splice;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeGetSymbols = Object.getOwnPropertySymbols,
    nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,
    nativeKeys = overArg(Object.keys, Object);

/* Built-in method references that are verified to be native. */
var DataView = getNative(root, 'DataView'),
    Map = getNative(root, 'Map'),
    Promise = getNative(root, 'Promise'),
    Set = getNative(root, 'Set'),
    WeakMap = getNative(root, 'WeakMap'),
    nativeCreate = getNative(Object, 'create');

/** Used to detect maps, sets, and weakmaps. */
var dataViewCtorString = toSource(DataView),
    mapCtorString = toSource(Map),
    promiseCtorString = toSource(Promise),
    setCtorString = toSource(Set),
    weakMapCtorString = toSource(WeakMap);

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;

/**
 * Creates a hash object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Hash(entries) {
  var index = -1,
      length = entries ? entries.length : 0;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

/**
 * Removes all key-value entries from the hash.
 *
 * @private
 * @name clear
 * @memberOf Hash
 */
function hashClear() {
  this.__data__ = nativeCreate ? nativeCreate(null) : {};
}

/**
 * Removes `key` and its value from the hash.
 *
 * @private
 * @name delete
 * @memberOf Hash
 * @param {Object} hash The hash to modify.
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function hashDelete(key) {
  return this.has(key) && delete this.__data__[key];
}

/**
 * Gets the hash value for `key`.
 *
 * @private
 * @name get
 * @memberOf Hash
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function hashGet(key) {
  var data = this.__data__;
  if (nativeCreate) {
    var result = data[key];
    return result === HASH_UNDEFINED ? undefined : result;
  }
  return hasOwnProperty.call(data, key) ? data[key] : undefined;
}

/**
 * Checks if a hash value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf Hash
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function hashHas(key) {
  var data = this.__data__;
  return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
}

/**
 * Sets the hash `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf Hash
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the hash instance.
 */
function hashSet(key, value) {
  var data = this.__data__;
  data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
  return this;
}

// Add methods to `Hash`.
Hash.prototype.clear = hashClear;
Hash.prototype['delete'] = hashDelete;
Hash.prototype.get = hashGet;
Hash.prototype.has = hashHas;
Hash.prototype.set = hashSet;

/**
 * Creates an list cache object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function ListCache(entries) {
  var index = -1,
      length = entries ? entries.length : 0;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

/**
 * Removes all key-value entries from the list cache.
 *
 * @private
 * @name clear
 * @memberOf ListCache
 */
function listCacheClear() {
  this.__data__ = [];
}

/**
 * Removes `key` and its value from the list cache.
 *
 * @private
 * @name delete
 * @memberOf ListCache
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function listCacheDelete(key) {
  var data = this.__data__,
      index = assocIndexOf(data, key);

  if (index < 0) {
    return false;
  }
  var lastIndex = data.length - 1;
  if (index == lastIndex) {
    data.pop();
  } else {
    splice.call(data, index, 1);
  }
  return true;
}

/**
 * Gets the list cache value for `key`.
 *
 * @private
 * @name get
 * @memberOf ListCache
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function listCacheGet(key) {
  var data = this.__data__,
      index = assocIndexOf(data, key);

  return index < 0 ? undefined : data[index][1];
}

/**
 * Checks if a list cache value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf ListCache
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function listCacheHas(key) {
  return assocIndexOf(this.__data__, key) > -1;
}

/**
 * Sets the list cache `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf ListCache
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the list cache instance.
 */
function listCacheSet(key, value) {
  var data = this.__data__,
      index = assocIndexOf(data, key);

  if (index < 0) {
    data.push([key, value]);
  } else {
    data[index][1] = value;
  }
  return this;
}

// Add methods to `ListCache`.
ListCache.prototype.clear = listCacheClear;
ListCache.prototype['delete'] = listCacheDelete;
ListCache.prototype.get = listCacheGet;
ListCache.prototype.has = listCacheHas;
ListCache.prototype.set = listCacheSet;

/**
 * Creates a map cache object to store key-value pairs.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function MapCache(entries) {
  var index = -1,
      length = entries ? entries.length : 0;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

/**
 * Removes all key-value entries from the map.
 *
 * @private
 * @name clear
 * @memberOf MapCache
 */
function mapCacheClear() {
  this.__data__ = {
    'hash': new Hash,
    'map': new (Map || ListCache),
    'string': new Hash
  };
}

/**
 * Removes `key` and its value from the map.
 *
 * @private
 * @name delete
 * @memberOf MapCache
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function mapCacheDelete(key) {
  return getMapData(this, key)['delete'](key);
}

/**
 * Gets the map value for `key`.
 *
 * @private
 * @name get
 * @memberOf MapCache
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function mapCacheGet(key) {
  return getMapData(this, key).get(key);
}

/**
 * Checks if a map value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf MapCache
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function mapCacheHas(key) {
  return getMapData(this, key).has(key);
}

/**
 * Sets the map `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf MapCache
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the map cache instance.
 */
function mapCacheSet(key, value) {
  getMapData(this, key).set(key, value);
  return this;
}

// Add methods to `MapCache`.
MapCache.prototype.clear = mapCacheClear;
MapCache.prototype['delete'] = mapCacheDelete;
MapCache.prototype.get = mapCacheGet;
MapCache.prototype.has = mapCacheHas;
MapCache.prototype.set = mapCacheSet;

/**
 * Creates a stack cache object to store key-value pairs.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Stack(entries) {
  this.__data__ = new ListCache(entries);
}

/**
 * Removes all key-value entries from the stack.
 *
 * @private
 * @name clear
 * @memberOf Stack
 */
function stackClear() {
  this.__data__ = new ListCache;
}

/**
 * Removes `key` and its value from the stack.
 *
 * @private
 * @name delete
 * @memberOf Stack
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function stackDelete(key) {
  return this.__data__['delete'](key);
}

/**
 * Gets the stack value for `key`.
 *
 * @private
 * @name get
 * @memberOf Stack
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function stackGet(key) {
  return this.__data__.get(key);
}

/**
 * Checks if a stack value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf Stack
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function stackHas(key) {
  return this.__data__.has(key);
}

/**
 * Sets the stack `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf Stack
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the stack cache instance.
 */
function stackSet(key, value) {
  var cache = this.__data__;
  if (cache instanceof ListCache) {
    var pairs = cache.__data__;
    if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
      pairs.push([key, value]);
      return this;
    }
    cache = this.__data__ = new MapCache(pairs);
  }
  cache.set(key, value);
  return this;
}

// Add methods to `Stack`.
Stack.prototype.clear = stackClear;
Stack.prototype['delete'] = stackDelete;
Stack.prototype.get = stackGet;
Stack.prototype.has = stackHas;
Stack.prototype.set = stackSet;

/**
 * Creates an array of the enumerable property names of the array-like `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @param {boolean} inherited Specify returning inherited property names.
 * @returns {Array} Returns the array of property names.
 */
function arrayLikeKeys(value, inherited) {
  // Safari 8.1 makes `arguments.callee` enumerable in strict mode.
  // Safari 9 makes `arguments.length` enumerable in strict mode.
  var result = (isArray(value) || isArguments(value))
    ? baseTimes(value.length, String)
    : [];

  var length = result.length,
      skipIndexes = !!length;

  for (var key in value) {
    if ((inherited || hasOwnProperty.call(value, key)) &&
        !(skipIndexes && (key == 'length' || isIndex(key, length)))) {
      result.push(key);
    }
  }
  return result;
}

/**
 * Assigns `value` to `key` of `object` if the existing value is not equivalent
 * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * for equality comparisons.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function assignValue(object, key, value) {
  var objValue = object[key];
  if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
      (value === undefined && !(key in object))) {
    object[key] = value;
  }
}

/**
 * Gets the index at which the `key` is found in `array` of key-value pairs.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} key The key to search for.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function assocIndexOf(array, key) {
  var length = array.length;
  while (length--) {
    if (eq(array[length][0], key)) {
      return length;
    }
  }
  return -1;
}

/**
 * The base implementation of `_.assign` without support for multiple sources
 * or `customizer` functions.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @returns {Object} Returns `object`.
 */
function baseAssign(object, source) {
  return object && copyObject(source, keys(source), object);
}

/**
 * The base implementation of `_.clone` and `_.cloneDeep` which tracks
 * traversed objects.
 *
 * @private
 * @param {*} value The value to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @param {boolean} [isFull] Specify a clone including symbols.
 * @param {Function} [customizer] The function to customize cloning.
 * @param {string} [key] The key of `value`.
 * @param {Object} [object] The parent object of `value`.
 * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
 * @returns {*} Returns the cloned value.
 */
function baseClone(value, isDeep, isFull, customizer, key, object, stack) {
  var result;
  if (customizer) {
    result = object ? customizer(value, key, object, stack) : customizer(value);
  }
  if (result !== undefined) {
    return result;
  }
  if (!isObject(value)) {
    return value;
  }
  var isArr = isArray(value);
  if (isArr) {
    result = initCloneArray(value);
    if (!isDeep) {
      return copyArray(value, result);
    }
  } else {
    var tag = getTag(value),
        isFunc = tag == funcTag || tag == genTag;

    if (isBuffer(value)) {
      return cloneBuffer(value, isDeep);
    }
    if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
      if (isHostObject(value)) {
        return object ? value : {};
      }
      result = initCloneObject(isFunc ? {} : value);
      if (!isDeep) {
        return copySymbols(value, baseAssign(result, value));
      }
    } else {
      if (!cloneableTags[tag]) {
        return object ? value : {};
      }
      result = initCloneByTag(value, tag, baseClone, isDeep);
    }
  }
  // Check for circular references and return its corresponding clone.
  stack || (stack = new Stack);
  var stacked = stack.get(value);
  if (stacked) {
    return stacked;
  }
  stack.set(value, result);

  if (!isArr) {
    var props = isFull ? getAllKeys(value) : keys(value);
  }
  arrayEach(props || value, function(subValue, key) {
    if (props) {
      key = subValue;
      subValue = value[key];
    }
    // Recursively populate clone (susceptible to call stack limits).
    assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));
  });
  return result;
}

/**
 * The base implementation of `_.create` without support for assigning
 * properties to the created object.
 *
 * @private
 * @param {Object} prototype The object to inherit from.
 * @returns {Object} Returns the new object.
 */
function baseCreate(proto) {
  return isObject(proto) ? objectCreate(proto) : {};
}

/**
 * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
 * `keysFunc` and `symbolsFunc` to get the enumerable property names and
 * symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Function} keysFunc The function to get the keys of `object`.
 * @param {Function} symbolsFunc The function to get the symbols of `object`.
 * @returns {Array} Returns the array of property names and symbols.
 */
function baseGetAllKeys(object, keysFunc, symbolsFunc) {
  var result = keysFunc(object);
  return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
}

/**
 * The base implementation of `getTag`.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function baseGetTag(value) {
  return objectToString.call(value);
}

/**
 * The base implementation of `_.isNative` without bad shim checks.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a native function,
 *  else `false`.
 */
function baseIsNative(value) {
  if (!isObject(value) || isMasked(value)) {
    return false;
  }
  var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
  return pattern.test(toSource(value));
}

/**
 * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function baseKeys(object) {
  if (!isPrototype(object)) {
    return nativeKeys(object);
  }
  var result = [];
  for (var key in Object(object)) {
    if (hasOwnProperty.call(object, key) && key != 'constructor') {
      result.push(key);
    }
  }
  return result;
}

/**
 * Creates a clone of  `buffer`.
 *
 * @private
 * @param {Buffer} buffer The buffer to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Buffer} Returns the cloned buffer.
 */
function cloneBuffer(buffer, isDeep) {
  if (isDeep) {
    return buffer.slice();
  }
  var result = new buffer.constructor(buffer.length);
  buffer.copy(result);
  return result;
}

/**
 * Creates a clone of `arrayBuffer`.
 *
 * @private
 * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
 * @returns {ArrayBuffer} Returns the cloned array buffer.
 */
function cloneArrayBuffer(arrayBuffer) {
  var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
  new Uint8Array(result).set(new Uint8Array(arrayBuffer));
  return result;
}

/**
 * Creates a clone of `dataView`.
 *
 * @private
 * @param {Object} dataView The data view to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the cloned data view.
 */
function cloneDataView(dataView, isDeep) {
  var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
  return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
}

/**
 * Creates a clone of `map`.
 *
 * @private
 * @param {Object} map The map to clone.
 * @param {Function} cloneFunc The function to clone values.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the cloned map.
 */
function cloneMap(map, isDeep, cloneFunc) {
  var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);
  return arrayReduce(array, addMapEntry, new map.constructor);
}

/**
 * Creates a clone of `regexp`.
 *
 * @private
 * @param {Object} regexp The regexp to clone.
 * @returns {Object} Returns the cloned regexp.
 */
function cloneRegExp(regexp) {
  var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
  result.lastIndex = regexp.lastIndex;
  return result;
}

/**
 * Creates a clone of `set`.
 *
 * @private
 * @param {Object} set The set to clone.
 * @param {Function} cloneFunc The function to clone values.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the cloned set.
 */
function cloneSet(set, isDeep, cloneFunc) {
  var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);
  return arrayReduce(array, addSetEntry, new set.constructor);
}

/**
 * Creates a clone of the `symbol` object.
 *
 * @private
 * @param {Object} symbol The symbol object to clone.
 * @returns {Object} Returns the cloned symbol object.
 */
function cloneSymbol(symbol) {
  return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
}

/**
 * Creates a clone of `typedArray`.
 *
 * @private
 * @param {Object} typedArray The typed array to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the cloned typed array.
 */
function cloneTypedArray(typedArray, isDeep) {
  var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
  return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
}

/**
 * Copies the values of `source` to `array`.
 *
 * @private
 * @param {Array} source The array to copy values from.
 * @param {Array} [array=[]] The array to copy values to.
 * @returns {Array} Returns `array`.
 */
function copyArray(source, array) {
  var index = -1,
      length = source.length;

  array || (array = Array(length));
  while (++index < length) {
    array[index] = source[index];
  }
  return array;
}

/**
 * Copies properties of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy properties from.
 * @param {Array} props The property identifiers to copy.
 * @param {Object} [object={}] The object to copy properties to.
 * @param {Function} [customizer] The function to customize copied values.
 * @returns {Object} Returns `object`.
 */
function copyObject(source, props, object, customizer) {
  object || (object = {});

  var index = -1,
      length = props.length;

  while (++index < length) {
    var key = props[index];

    var newValue = customizer
      ? customizer(object[key], source[key], key, object, source)
      : undefined;

    assignValue(object, key, newValue === undefined ? source[key] : newValue);
  }
  return object;
}

/**
 * Copies own symbol properties of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy symbols from.
 * @param {Object} [object={}] The object to copy symbols to.
 * @returns {Object} Returns `object`.
 */
function copySymbols(source, object) {
  return copyObject(source, getSymbols(source), object);
}

/**
 * Creates an array of own enumerable property names and symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names and symbols.
 */
function getAllKeys(object) {
  return baseGetAllKeys(object, keys, getSymbols);
}

/**
 * Gets the data for `map`.
 *
 * @private
 * @param {Object} map The map to query.
 * @param {string} key The reference key.
 * @returns {*} Returns the map data.
 */
function getMapData(map, key) {
  var data = map.__data__;
  return isKeyable(key)
    ? data[typeof key == 'string' ? 'string' : 'hash']
    : data.map;
}

/**
 * Gets the native function at `key` of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {string} key The key of the method to get.
 * @returns {*} Returns the function if it's native, else `undefined`.
 */
function getNative(object, key) {
  var value = getValue(object, key);
  return baseIsNative(value) ? value : undefined;
}

/**
 * Creates an array of the own enumerable symbol properties of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of symbols.
 */
var getSymbols = nativeGetSymbols ? overArg(nativeGetSymbols, Object) : stubArray;

/**
 * Gets the `toStringTag` of `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
var getTag = baseGetTag;

// Fallback for data views, maps, sets, and weak maps in IE 11,
// for data views in Edge < 14, and promises in Node.js.
if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
    (Map && getTag(new Map) != mapTag) ||
    (Promise && getTag(Promise.resolve()) != promiseTag) ||
    (Set && getTag(new Set) != setTag) ||
    (WeakMap && getTag(new WeakMap) != weakMapTag)) {
  getTag = function(value) {
    var result = objectToString.call(value),
        Ctor = result == objectTag ? value.constructor : undefined,
        ctorString = Ctor ? toSource(Ctor) : undefined;

    if (ctorString) {
      switch (ctorString) {
        case dataViewCtorString: return dataViewTag;
        case mapCtorString: return mapTag;
        case promiseCtorString: return promiseTag;
        case setCtorString: return setTag;
        case weakMapCtorString: return weakMapTag;
      }
    }
    return result;
  };
}

/**
 * Initializes an array clone.
 *
 * @private
 * @param {Array} array The array to clone.
 * @returns {Array} Returns the initialized clone.
 */
function initCloneArray(array) {
  var length = array.length,
      result = array.constructor(length);

  // Add properties assigned by `RegExp#exec`.
  if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
    result.index = array.index;
    result.input = array.input;
  }
  return result;
}

/**
 * Initializes an object clone.
 *
 * @private
 * @param {Object} object The object to clone.
 * @returns {Object} Returns the initialized clone.
 */
function initCloneObject(object) {
  return (typeof object.constructor == 'function' && !isPrototype(object))
    ? baseCreate(getPrototype(object))
    : {};
}

/**
 * Initializes an object clone based on its `toStringTag`.
 *
 * **Note:** This function only supports cloning values with tags of
 * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
 *
 * @private
 * @param {Object} object The object to clone.
 * @param {string} tag The `toStringTag` of the object to clone.
 * @param {Function} cloneFunc The function to clone values.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the initialized clone.
 */
function initCloneByTag(object, tag, cloneFunc, isDeep) {
  var Ctor = object.constructor;
  switch (tag) {
    case arrayBufferTag:
      return cloneArrayBuffer(object);

    case boolTag:
    case dateTag:
      return new Ctor(+object);

    case dataViewTag:
      return cloneDataView(object, isDeep);

    case float32Tag: case float64Tag:
    case int8Tag: case int16Tag: case int32Tag:
    case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
      return cloneTypedArray(object, isDeep);

    case mapTag:
      return cloneMap(object, isDeep, cloneFunc);

    case numberTag:
    case stringTag:
      return new Ctor(object);

    case regexpTag:
      return cloneRegExp(object);

    case setTag:
      return cloneSet(object, isDeep, cloneFunc);

    case symbolTag:
      return cloneSymbol(object);
  }
}

/**
 * Checks if `value` is a valid array-like index.
 *
 * @private
 * @param {*} value The value to check.
 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
 */
function isIndex(value, length) {
  length = length == null ? MAX_SAFE_INTEGER : length;
  return !!length &&
    (typeof value == 'number' || reIsUint.test(value)) &&
    (value > -1 && value % 1 == 0 && value < length);
}

/**
 * Checks if `value` is suitable for use as unique object key.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
 */
function isKeyable(value) {
  var type = typeof value;
  return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
    ? (value !== '__proto__')
    : (value === null);
}

/**
 * Checks if `func` has its source masked.
 *
 * @private
 * @param {Function} func The function to check.
 * @returns {boolean} Returns `true` if `func` is masked, else `false`.
 */
function isMasked(func) {
  return !!maskSrcKey && (maskSrcKey in func);
}

/**
 * Checks if `value` is likely a prototype object.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
 */
function isPrototype(value) {
  var Ctor = value && value.constructor,
      proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;

  return value === proto;
}

/**
 * Converts `func` to its source code.
 *
 * @private
 * @param {Function} func The function to process.
 * @returns {string} Returns the source code.
 */
function toSource(func) {
  if (func != null) {
    try {
      return funcToString.call(func);
    } catch (e) {}
    try {
      return (func + '');
    } catch (e) {}
  }
  return '';
}

/**
 * This method is like `_.clone` except that it recursively clones `value`.
 *
 * @static
 * @memberOf _
 * @since 1.0.0
 * @category Lang
 * @param {*} value The value to recursively clone.
 * @returns {*} Returns the deep cloned value.
 * @see _.clone
 * @example
 *
 * var objects = [{ 'a': 1 }, { 'b': 2 }];
 *
 * var deep = _.cloneDeep(objects);
 * console.log(deep[0] === objects[0]);
 * // => false
 */
function cloneDeep(value) {
  return baseClone(value, true, true);
}

/**
 * Performs a
 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * comparison between two values to determine if they are equivalent.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 * @example
 *
 * var object = { 'a': 1 };
 * var other = { 'a': 1 };
 *
 * _.eq(object, object);
 * // => true
 *
 * _.eq(object, other);
 * // => false
 *
 * _.eq('a', 'a');
 * // => true
 *
 * _.eq('a', Object('a'));
 * // => false
 *
 * _.eq(NaN, NaN);
 * // => true
 */
function eq(value, other) {
  return value === other || (value !== value && other !== other);
}

/**
 * Checks if `value` is likely an `arguments` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
 *  else `false`.
 * @example
 *
 * _.isArguments(function() { return arguments; }());
 * // => true
 *
 * _.isArguments([1, 2, 3]);
 * // => false
 */
function isArguments(value) {
  // Safari 8.1 makes `arguments.callee` enumerable in strict mode.
  return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&
    (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);
}

/**
 * Checks if `value` is classified as an `Array` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
 * @example
 *
 * _.isArray([1, 2, 3]);
 * // => true
 *
 * _.isArray(document.body.children);
 * // => false
 *
 * _.isArray('abc');
 * // => false
 *
 * _.isArray(_.noop);
 * // => false
 */
var isArray = Array.isArray;

/**
 * Checks if `value` is array-like. A value is considered array-like if it's
 * not a function and has a `value.length` that's an integer greater than or
 * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
 * @example
 *
 * _.isArrayLike([1, 2, 3]);
 * // => true
 *
 * _.isArrayLike(document.body.children);
 * // => true
 *
 * _.isArrayLike('abc');
 * // => true
 *
 * _.isArrayLike(_.noop);
 * // => false
 */
function isArrayLike(value) {
  return value != null && isLength(value.length) && !isFunction(value);
}

/**
 * This method is like `_.isArrayLike` except that it also checks if `value`
 * is an object.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array-like object,
 *  else `false`.
 * @example
 *
 * _.isArrayLikeObject([1, 2, 3]);
 * // => true
 *
 * _.isArrayLikeObject(document.body.children);
 * // => true
 *
 * _.isArrayLikeObject('abc');
 * // => false
 *
 * _.isArrayLikeObject(_.noop);
 * // => false
 */
function isArrayLikeObject(value) {
  return isObjectLike(value) && isArrayLike(value);
}

/**
 * Checks if `value` is a buffer.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
 * @example
 *
 * _.isBuffer(new Buffer(2));
 * // => true
 *
 * _.isBuffer(new Uint8Array(2));
 * // => false
 */
var isBuffer = nativeIsBuffer || stubFalse;

/**
 * Checks if `value` is classified as a `Function` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
 * @example
 *
 * _.isFunction(_);
 * // => true
 *
 * _.isFunction(/abc/);
 * // => false
 */
function isFunction(value) {
  // The use of `Object#toString` avoids issues with the `typeof` operator
  // in Safari 8-9 which returns 'object' for typed array and other constructors.
  var tag = isObject(value) ? objectToString.call(value) : '';
  return tag == funcTag || tag == genTag;
}

/**
 * Checks if `value` is a valid array-like length.
 *
 * **Note:** This method is loosely based on
 * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
 * @example
 *
 * _.isLength(3);
 * // => true
 *
 * _.isLength(Number.MIN_VALUE);
 * // => false
 *
 * _.isLength(Infinity);
 * // => false
 *
 * _.isLength('3');
 * // => false
 */
function isLength(value) {
  return typeof value == 'number' &&
    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
}

/**
 * Checks if `value` is the
 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 * @example
 *
 * _.isObject({});
 * // => true
 *
 * _.isObject([1, 2, 3]);
 * // => true
 *
 * _.isObject(_.noop);
 * // => true
 *
 * _.isObject(null);
 * // => false
 */
function isObject(value) {
  var type = typeof value;
  return !!value && (type == 'object' || type == 'function');
}

/**
 * Checks if `value` is object-like. A value is object-like if it's not `null`
 * and has a `typeof` result of "object".
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
 * @example
 *
 * _.isObjectLike({});
 * // => true
 *
 * _.isObjectLike([1, 2, 3]);
 * // => true
 *
 * _.isObjectLike(_.noop);
 * // => false
 *
 * _.isObjectLike(null);
 * // => false
 */
function isObjectLike(value) {
  return !!value && typeof value == 'object';
}

/**
 * Creates an array of the own enumerable property names of `object`.
 *
 * **Note:** Non-object values are coerced to objects. See the
 * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
 * for more details.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Object
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.keys(new Foo);
 * // => ['a', 'b'] (iteration order is not guaranteed)
 *
 * _.keys('hi');
 * // => ['0', '1']
 */
function keys(object) {
  return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}

/**
 * This method returns a new empty array.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {Array} Returns the new empty array.
 * @example
 *
 * var arrays = _.times(2, _.stubArray);
 *
 * console.log(arrays);
 * // => [[], []]
 *
 * console.log(arrays[0] === arrays[1]);
 * // => false
 */
function stubArray() {
  return [];
}

/**
 * This method returns `false`.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {boolean} Returns `false`.
 * @example
 *
 * _.times(2, _.stubFalse);
 * // => [false, false]
 */
function stubFalse() {
  return false;
}

module.exports = cloneDeep;

},{}],"WKCK":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.flattenVRVideo = flattenVRVideo;
exports.openSubsEditor = openSubsEditor;
var _util = require("../util/util");
function flattenVRVideo(videoContainer, video) {
  let isVRVideo = true;
  const VRCanvas = videoContainer.getElementsByClassName('webgl')[0];
  VRCanvas != null ? (0, _util.deleteElement)(VRCanvas) : isVRVideo = false;
  const VRControl = document.getElementsByClassName('ytp-webgl-spherical-control')[0];
  VRControl != null ? (0, _util.deleteElement)(VRControl) : isVRVideo = false;
  if (isVRVideo) {
    videoContainer.style.cursor = 'auto';
    video.style.display = 'block';
    (0, _util.flashMessage)('Flattened VR video.', 'green');
  } else {
    (0, _util.flashMessage)('Not a VR video or already flattened.', 'red');
  }
}
function openSubsEditor(videoID) {
  const url = `https://www.youtube.com/timedtext_video?ref=player&v=${videoID}`;
  window.open(url, '_blank');
}
},{"../util/util":"mCFQ"}],"f1IS":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Crop = void 0;
exports.getCropSize = getCropSize;
exports.getMinMaxAvgCropPoint = getMinMaxAvgCropPoint;
exports.isVariableSize = isVariableSize;
var _util = require("../util/util");
class Crop {
  static get minW() {
    return Crop.shouldConstrainMinDimensions ? Crop._minW : 0;
  }
  static get minH() {
    return Crop.shouldConstrainMinDimensions ? Crop._minH : 0;
  }
  constructor(_x, _y, _w, _h, maxW, maxH // private _minW: number, // private _minH: number
  ) {
    this._x = _x;
    this._y = _y;
    this._w = _w;
    this._h = _h;
    this.maxW = maxW;
    this.maxH = maxH;
    this._history = [];
    this._defaultAspectRatio = 1;
    this._x = Math.max(Crop.minX, _x);
    this._y = Math.max(Crop.minY, _y);
    // this._minW = Crop.minW;
    // this._minW = Crop.minW;
    this.maxW = Math.max(Crop.minW, maxW);
    this.maxH = Math.max(Crop.minH, maxH);
    this._w = (0, _util.clampNumber)(_w, Crop.minW, this.maxW);
    this._h = (0, _util.clampNumber)(_h, Crop.minH, this.maxH);
  }
  static fromCropString(cropString, cropRes) {
    const [x, y, w, h] = Crop.getCropComponents(cropString);
    const [maxW, maxH] = Crop.getMaxDimensions(cropRes);
    return new this(x, y, w, h, maxW, maxH);
  }
  get cropString() {
    return this.cropComponents.join(':');
  }
  set cropString(cropString) {
    [this._x, this._y, this._w, this._h] = Crop.getCropComponents(cropString);
  }
  setCropStringSafe(cropString) {
    let shouldMaintainCropAspectRatio = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    const [nx, ny, nw, nh] = Crop.getCropComponents(cropString);
    const isDrag = nw === this._w && nh === this._h;
    const maxX = isDrag ? this.maxW - this._w : this.maxW - Crop.minW;
    const maxY = isDrag ? this.maxH - this._h : this.maxH - Crop.minH;
    let cx = (0, _util.clampNumber)(nx, Crop.minX, maxX);
    let cy = (0, _util.clampNumber)(ny, Crop.minY, maxY);
    const maxW = this.maxW - cx;
    const maxH = this.maxH - cy;
    let cw = isDrag ? this._w : (0, _util.clampNumber)(nw, Crop.minW, maxW);
    let ch = isDrag ? this._h : (0, _util.clampNumber)(nh, Crop.minH, maxH);
    if (shouldMaintainCropAspectRatio) {
      const ar = this.aspectRatio;
      const ph = Math.floor(cw / ar);
      const pw = Math.floor(ch * ar);
      const phWithinBounds = Crop.minH <= ph && ph <= this.maxH;
      const pwWithinBounds = Crop.minW <= pw && pw <= this.maxW;
      if (!phWithinBounds && !pwWithinBounds) {
        throw new Error('Could not determine a valid aspect-ratio-constrained crop.');
      }
      if (phWithinBounds) {
        ch = ph;
      } else {
        cw = pw;
      }
    }
    this.cropString = (0, _util.getCropString)(cx, cy, cw, ch);
  }
  static getCropComponents(cropString, cropRes) {
    let maxW, maxH;
    if (cropRes != null) [maxW, maxH] = Crop.getMaxDimensions(cropRes);
    const cropArr = cropString.split(':').map(cropComponent => {
      if (cropComponent === 'iw') return maxW;
      if (cropComponent === 'ih') return maxH;
      return parseInt(cropComponent, 10);
    });
    return cropArr;
  }
  static getMaxDimensions(cropRes) {
    const maxDimensions = cropRes.split('x').map(dim => parseInt(dim, 10));
    return maxDimensions;
  }
  static getMultipliedCropRes(cropRes, cropMultipleX, cropMultipleY) {
    let [maxW, maxH] = Crop.getMaxDimensions(cropRes);
    maxW = maxW * cropMultipleX;
    maxH = maxH * cropMultipleY;
    return `${maxW}x${maxH}`;
  }
  get cropComponents() {
    return [this._x, this._y, this._w, this._h];
  }
  get x() {
    return this._x;
  }
  get y() {
    return this._y;
  }
  get w() {
    return this._w;
  }
  get h() {
    return this._h;
  }
  get r() {
    return this._x + this._w;
  }
  get b() {
    return this._y + this._h;
  }
  panX(delta) {
    delta = (0, _util.clampNumber)(delta, -this._x, this.maxW - this.r);
    this._x += delta;
  }
  panY(delta) {
    delta = (0, _util.clampNumber)(delta, -this._y, this.maxH - this.b);
    this._y += delta;
  }
  set defaultAspectRatio(aspectRatio) {
    this._defaultAspectRatio = aspectRatio;
  }
  get aspectRatio() {
    return this._w == 0 || this._h == 0 ? this._defaultAspectRatio : this._w / this._h;
  }
  get minResizeS() {
    return -(this.b - (this._y + Crop.minH));
  }
  get maxResizeS() {
    return this.maxH - this.b;
  }
  get minResizeE() {
    return -(this.r - (this._x + Crop.minW));
  }
  get maxResizeE() {
    return this.maxW - this.r;
  }
  get minResizeN() {
    return -(this.b - Crop.minH - this._y);
  }
  get maxResizeN() {
    return this._y;
  }
  get minResizeW() {
    return -(this.r - Crop.minW - this._x);
  }
  get maxResizeW() {
    return this._x;
  }
  get cx() {
    return this._x + this._w / 2;
  }
  get cy() {
    return this._y + this._h / 2;
  }
  clampResizeN(delta) {
    delta = (0, _util.clampNumber)(delta, this.minResizeN, this.maxResizeN);
    return delta;
  }
  clampResizeE(delta) {
    delta = (0, _util.clampNumber)(delta, this.minResizeE, this.maxResizeE);
    return delta;
  }
  clampResizeS(delta) {
    delta = (0, _util.clampNumber)(delta, this.minResizeS, this.maxResizeS);
    return delta;
  }
  clampResizeW(delta) {
    delta = (0, _util.clampNumber)(delta, this.minResizeW, this.maxResizeW);
    return delta;
  }
  resizeN(delta) {
    let shouldClamp = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    delta = this.clampResizeN(delta);
    this._y -= delta;
    this._h += delta;
    return delta;
  }
  resizeW(delta) {
    let shouldClamp = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    if (shouldClamp) delta = (0, _util.clampNumber)(delta, this.minResizeW, this.maxResizeW);
    this._x -= delta;
    this._w += delta;
    return delta;
  }
  resizeS(delta) {
    let shouldClamp = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    if (shouldClamp) delta = (0, _util.clampNumber)(delta, this.minResizeS, this.maxResizeS);
    this._h += delta;
    return delta;
  }
  resizeE(delta) {
    let shouldClamp = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    if (shouldClamp) delta = (0, _util.clampNumber)(delta, this.minResizeE, this.maxResizeE);
    this._w += delta;
    return delta;
  }
  resizeNE(deltaY, deltaX) {
    this.resizeN(deltaY);
    this.resizeE(deltaX);
  }
  resizeSE(deltaY, deltaX) {
    this.resizeS(deltaY);
    this.resizeE(deltaX);
  }
  resizeSW(deltaY, deltaX) {
    this.resizeS(deltaY);
    this.resizeW(deltaX);
  }
  resizeNW(deltaY, deltaX) {
    this.resizeN(deltaY);
    this.resizeW(deltaX);
  }
  resizeNS(delta) {
    if (delta >= 0) {
      delta = this.clampResizeN(delta);
      delta = this.clampResizeS(delta);
    } else {
      delta = Math.max(delta, -(this.b - this.cy - Crop.minH / 2));
    }
    this.resizeN(delta, false);
    this.resizeS(delta, false);
  }
  resizeEW(delta) {
    if (delta >= 0) {
      delta = this.clampResizeE(delta);
      delta = this.clampResizeW(delta);
    } else {
      delta = Math.max(delta, -(this.r - this.cx - Crop.minW / 2));
    }
    this.resizeE(delta, false);
    this.resizeW(delta, false);
  }
  resizeNESW(deltaY, deltaX) {
    if (deltaY >= 0) {
      deltaY = this.clampResizeN(deltaY);
      deltaY = this.clampResizeS(deltaY);
    } else {
      deltaY = Math.max(deltaY, -(this.b - this.cy - Crop.minH / 2));
    }
    if (deltaX >= 0) {
      deltaX = this.clampResizeE(deltaX);
      deltaX = this.clampResizeW(deltaX);
    } else {
      deltaX = Math.max(deltaX, -(this.r - this.cx - Crop.minW / 2));
    }
    this.resizeN(deltaY, false);
    this.resizeS(deltaY, false);
    this.resizeE(deltaX, false);
    this.resizeW(deltaX, false);
  }
  resizeNAspectRatioLocked(delta) {
    const aspectRatio = this.aspectRatio;
    delta = this.clampResizeN(delta);
    delta *= aspectRatio;
    delta = Math.round(delta);
    delta = this.resizeE(delta);
    delta /= aspectRatio;
    delta = Math.round(delta);
    this.resizeN(delta);
  }
  resizeEAspectRatioLocked(delta) {
    const aspectRatio = this.aspectRatio;
    delta = this.clampResizeE(Math.round(delta));
    delta /= aspectRatio;
    delta = Math.round(delta);
    delta = this.resizeS(Math.round(delta));
    delta *= aspectRatio;
    delta = Math.round(delta);
    this.resizeE(delta);
  }
  resizeSAspectRatioLocked(delta) {
    const aspectRatio = this.aspectRatio;
    delta = this.clampResizeS(delta);
    delta *= aspectRatio;
    delta = Math.round(delta);
    delta = this.resizeE(delta);
    delta /= aspectRatio;
    delta = Math.round(delta);
    this.resizeS(delta);
  }
  resizeWAspectRatioLocked(delta) {
    const aspectRatio = this.aspectRatio;
    delta = this.clampResizeW(delta);
    delta /= aspectRatio;
    delta = Math.round(delta);
    delta = this.resizeS(delta);
    delta *= aspectRatio;
    delta = Math.round(delta);
    this.resizeW(delta);
  }
  get aspectRatioPair() {
    const a = this.aspectRatio / (this.aspectRatio + 1);
    const b = 1 - a;
    return [a, b];
  }
  resizeSEAspectRatioLocked(deltaY, deltaX) {
    const [a, b] = this.aspectRatioPair;
    deltaX *= a;
    deltaY *= b;
    deltaY += deltaX / this.aspectRatio;
    deltaY = this.clampResizeS(deltaY);
    deltaX = deltaY * this.aspectRatio;
    deltaX = Math.round(deltaX);
    deltaX = this.clampResizeE(deltaX);
    deltaY = deltaX / this.aspectRatio;
    deltaY = Math.round(deltaY);
    deltaY = this.clampResizeS(deltaY);
    this.resizeS(deltaY, false);
    this.resizeE(deltaX, false);
  }
  resizeSWAspectRatioLocked(deltaY, deltaX) {
    const [a, b] = this.aspectRatioPair;
    deltaX *= a;
    deltaY *= b;
    deltaY += deltaX / this.aspectRatio;
    deltaY = this.clampResizeS(deltaY);
    deltaX = deltaY * this.aspectRatio;
    deltaX = Math.round(deltaX);
    deltaX = this.clampResizeW(deltaX);
    deltaY = deltaX / this.aspectRatio;
    deltaY = Math.round(deltaY);
    deltaY = this.clampResizeS(deltaY);
    this.resizeS(deltaY, false);
    this.resizeW(deltaX, false);
  }
  resizeNEAspectRatioLocked(deltaY, deltaX) {
    const [a, b] = this.aspectRatioPair;
    deltaX *= a;
    deltaY *= b;
    deltaY += deltaX / this.aspectRatio;
    deltaY = this.clampResizeN(deltaY);
    deltaX = deltaY * this.aspectRatio;
    deltaX = Math.round(deltaX);
    deltaX = this.clampResizeE(deltaX);
    deltaY = deltaX / this.aspectRatio;
    deltaY = Math.round(deltaY);
    deltaY = this.clampResizeN(deltaY);
    this.resizeN(deltaY, false);
    this.resizeE(deltaX, false);
  }
  resizeNWAspectRatioLocked(deltaY, deltaX) {
    const [a, b] = this.aspectRatioPair;
    deltaX *= a;
    deltaY *= b;
    deltaY += deltaX / this.aspectRatio;
    deltaY = this.clampResizeN(deltaY);
    deltaX = deltaY * this.aspectRatio;
    deltaX = Math.round(deltaX);
    deltaX = this.clampResizeW(deltaX);
    deltaY = deltaX / this.aspectRatio;
    deltaY = Math.round(deltaY);
    deltaY = this.clampResizeN(deltaY);
    this.resizeN(deltaY, false);
    this.resizeW(deltaX, false);
  }
  resizeNESWAspectRatioLocked(deltaY, deltaX) {
    const [a, b] = this.aspectRatioPair;
    let isExpand = Math.abs(deltaX) > Math.abs(deltaY) ? deltaX >= 0 : deltaY >= 0;
    deltaX *= a;
    deltaY *= b;
    if (isExpand) {
      deltaY += deltaX / this.aspectRatio;
      deltaY = this.clampResizeN(deltaY);
      deltaY = this.clampResizeS(deltaY);
      deltaX = deltaY * this.aspectRatio;
      deltaX = Math.round(deltaX);
      deltaX = this.clampResizeE(deltaX);
      deltaX = this.clampResizeW(deltaX);
      deltaY = deltaX / this.aspectRatio;
      deltaY = Math.round(deltaY);
      deltaY = this.clampResizeN(deltaY);
      deltaY = this.clampResizeS(deltaY);
    } else {
      deltaY += deltaX / this.aspectRatio;
      deltaY = Math.max(deltaY, -(this.b - this.cy - Crop.minH / 2));
      deltaX = deltaY * this.aspectRatio;
      deltaX = Math.round(deltaX);
      deltaX = Math.max(deltaX, -(this.r - this.cx - Crop.minW / 2));
      deltaY = deltaX / this.aspectRatio;
      deltaY = Math.round(deltaY);
      deltaY = Math.max(deltaY, -(this.b - this.cy - Crop.minH / 2));
    }
    this.resizeN(deltaY, false);
    this.resizeE(deltaX, false);
    this.resizeS(deltaY, false);
    this.resizeW(deltaX, false);
  }
}
exports.Crop = Crop;
Crop.minX = 0;
Crop.minY = 0;
Crop._minW = 20;
Crop._minH = 20;
Crop.shouldConstrainMinDimensions = true;
function getCropSize(crop, cropRes) {
  const [,, w, h] = Crop.getCropComponents(crop, cropRes);
  const size = w * h;
  const aspectRatio = w / h;
  return {
    w,
    h,
    size,
    aspectRatio
  };
}
function getMinMaxAvgCropPoint(cropMap, cropRes) {
  const {
    aspectRatio
  } = getCropSize(cropMap[0].crop, cropRes);
  let [minSize, minSizeW, minSizeH] = [Infinity, Infinity, Infinity];
  let [maxSize, maxSizeW, maxSizeH] = [-Infinity, -Infinity, -Infinity];
  let [avgSize, avgSizeW, avgSizeH] = [0, 0, 0];
  cropMap.forEach((cropPoint, i) => {
    const {
      w,
      h,
      size
    } = getCropSize(cropPoint.crop, cropRes);
    if (size < minSize) {
      [minSizeW, minSizeH, minSize] = [w, h, size];
    }
    if (size > maxSize) {
      [maxSizeW, maxSizeH, maxSize] = [w, h, size];
    }
    avgSizeW += (w - avgSizeW) / (i + 1);
  });
  avgSizeH = Math.floor(avgSizeW / aspectRatio);
  avgSizeW = Math.floor(avgSizeW);
  avgSize = avgSizeW * avgSizeH;
  return {
    minSizeW,
    minSizeH,
    minSize,
    maxSizeW,
    maxSizeH,
    maxSize,
    avgSizeW,
    avgSizeH,
    avgSize
  };
}
function isVariableSize(cropMap, cropRes) {
  const {
    size
  } = getCropSize(cropMap[0].crop, cropRes);
  const isVariableSize = cropMap.some(cropPoint => {
    return size !== getCropSize(cropPoint.crop, cropRes).size;
  });
  return isVariableSize;
}
},{"../util/util":"mCFQ"}],"bOUC":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.disableCommonBlockers = disableCommonBlockers;
exports.enableCommonBlockers = enableCommonBlockers;
function enableCommonBlockers() {
  enablePreventMouseZoom();
  enablePreventSpaceScroll();
}
function disableCommonBlockers() {
  disablePreventMouseZoom();
  disablePreventSpaceScroll();
}
function enablePreventMouseZoom() {
  window.addEventListener('mousewheel', stopWheelZoom, {
    passive: false
  });
  window.addEventListener('DOMMouseScroll', stopWheelZoom, {
    passive: false
  });
}
function disablePreventMouseZoom() {
  window.removeEventListener('mousewheel', stopWheelZoom);
  window.removeEventListener('DOMMouseScroll', stopWheelZoom);
}
function stopWheelZoom(e) {
  if (e.ctrlKey || e.shiftKey) {
    e.preventDefault();
  }
}
function enablePreventSpaceScroll() {
  window.addEventListener('keydown', preventSpaceScrollHandler);
}
function disablePreventSpaceScroll() {
  window.removeEventListener('keydown', preventSpaceScrollHandler);
}
function preventSpaceScrollHandler(e) {
  if (e.code === 'Space' && e.target == document.body && !e.ctrlKey && !e.shiftKey && !e.altKey) {
    e.preventDefault();
  }
}
},{}],"jPPE":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.disableYTBlockers = disableYTBlockers;
exports.enableYTBlockers = enableYTBlockers;
function enableYTBlockers() {
  enablePreventSideBarPull();
  enablePreventAltDefault();
}
function disableYTBlockers() {
  disablePreventSideBarPull();
  disablePreventAltDefault();
}
function enablePreventAltDefault() {
  window.addEventListener('keyup', preventAltDefaultHandler, true);
}
function disablePreventAltDefault() {
  window.removeEventListener('keyup', preventAltDefaultHandler, true);
}
function enablePreventSideBarPull() {
  const sideBar = document.getElementById('contentContainer');
  const sideBarContent = document.getElementById('guide-content');
  sideBarContent.style.pointerEvents = 'auto';
  if (sideBar != null) sideBar.style.pointerEvents = 'none';
}
function disablePreventSideBarPull() {
  const sideBar = document.getElementById('contentContainer');
  if (sideBar != null) sideBar.style.removeProperty('pointer-events');
}
function preventAltDefaultHandler(e) {
  if (e.code === 'AltLeft' && !e.ctrlKey && !e.shiftKey) {
    e.preventDefault();
  }
}
},{}],"SYAW":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var noop = {
  value: function () {}
};
function dispatch() {
  for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
    if (!(t = arguments[i] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
    _[t] = [];
  }
  return new Dispatch(_);
}
function Dispatch(_) {
  this._ = _;
}
function parseTypenames(typenames, types) {
  return typenames.trim().split(/^|\s+/).map(function (t) {
    var name = "",
      i = t.indexOf(".");
    if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
    if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
    return {
      type: t,
      name: name
    };
  });
}
Dispatch.prototype = dispatch.prototype = {
  constructor: Dispatch,
  on: function (typename, callback) {
    var _ = this._,
      T = parseTypenames(typename + "", _),
      t,
      i = -1,
      n = T.length;

    // If no callback was specified, return the callback of the given type and name.
    if (arguments.length < 2) {
      while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;
      return;
    }

    // If a type was specified, set the callback for the given type and name.
    // Otherwise, if a null callback was specified, remove callbacks of the given name.
    if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
    while (++i < n) {
      if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);
    }
    return this;
  },
  copy: function () {
    var copy = {},
      _ = this._;
    for (var t in _) copy[t] = _[t].slice();
    return new Dispatch(copy);
  },
  call: function (type, that) {
    if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];
    if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
    for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
  },
  apply: function (type, that, args) {
    if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
    for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
  }
};
function get(type, name) {
  for (var i = 0, n = type.length, c; i < n; ++i) {
    if ((c = type[i]).name === name) {
      return c.value;
    }
  }
}
function set(type, name, callback) {
  for (var i = 0, n = type.length; i < n; ++i) {
    if (type[i].name === name) {
      type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));
      break;
    }
  }
  if (callback != null) type.push({
    name: name,
    value: callback
  });
  return type;
}
var _default = exports.default = dispatch;
},{}],"cNf8":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "dispatch", {
  enumerable: true,
  get: function () {
    return _dispatch.default;
  }
});
var _dispatch = _interopRequireDefault(require("./dispatch.js"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./dispatch.js":"SYAW"}],"TZ6S":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.xhtml = exports.default = void 0;
var xhtml = exports.xhtml = "http://www.w3.org/1999/xhtml";
var _default = exports.default = {
  svg: "http://www.w3.org/2000/svg",
  xhtml: xhtml,
  xlink: "http://www.w3.org/1999/xlink",
  xml: "http://www.w3.org/XML/1998/namespace",
  xmlns: "http://www.w3.org/2000/xmlns/"
};
},{}],"Sva1":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _namespaces = _interopRequireDefault(require("./namespaces"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default(name) {
  var prefix = name += "",
    i = prefix.indexOf(":");
  if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
  return _namespaces.default.hasOwnProperty(prefix) ? {
    space: _namespaces.default[prefix],
    local: name
  } : name;
}
},{"./namespaces":"TZ6S"}],"vTHN":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _namespace = _interopRequireDefault(require("./namespace"));
var _namespaces = require("./namespaces");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function creatorInherit(name) {
  return function () {
    var document = this.ownerDocument,
      uri = this.namespaceURI;
    return uri === _namespaces.xhtml && document.documentElement.namespaceURI === _namespaces.xhtml ? document.createElement(name) : document.createElementNS(uri, name);
  };
}
function creatorFixed(fullname) {
  return function () {
    return this.ownerDocument.createElementNS(fullname.space, fullname.local);
  };
}
function _default(name) {
  var fullname = (0, _namespace.default)(name);
  return (fullname.local ? creatorFixed : creatorInherit)(fullname);
}
},{"./namespace":"Sva1","./namespaces":"TZ6S"}],"hX8m":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function none() {}
function _default(selector) {
  return selector == null ? none : function () {
    return this.querySelector(selector);
  };
}
},{}],"um0S":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _index = require("./index");
var _selector = _interopRequireDefault(require("../selector"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default(select) {
  if (typeof select !== "function") select = (0, _selector.default)(select);
  for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
    for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
      if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
        if ("__data__" in node) subnode.__data__ = node.__data__;
        subgroup[i] = subnode;
      }
    }
  }
  return new _index.Selection(subgroups, this._parents);
}
},{"./index":"Pd8D","../selector":"hX8m"}],"sReu":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function empty() {
  return [];
}
function _default(selector) {
  return selector == null ? empty : function () {
    return this.querySelectorAll(selector);
  };
}
},{}],"EUVe":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _index = require("./index");
var _selectorAll = _interopRequireDefault(require("../selectorAll"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default(select) {
  if (typeof select !== "function") select = (0, _selectorAll.default)(select);
  for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
    for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
      if (node = group[i]) {
        subgroups.push(select.call(node, node.__data__, i, group));
        parents.push(node);
      }
    }
  }
  return new _index.Selection(subgroups, parents);
}
},{"./index":"Pd8D","../selectorAll":"sReu"}],"LbZ4":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default(selector) {
  return function () {
    return this.matches(selector);
  };
}
},{}],"hZf9":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _index = require("./index");
var _matcher = _interopRequireDefault(require("../matcher"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default(match) {
  if (typeof match !== "function") match = (0, _matcher.default)(match);
  for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
    for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
      if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
        subgroup.push(node);
      }
    }
  }
  return new _index.Selection(subgroups, this._parents);
}
},{"./index":"Pd8D","../matcher":"LbZ4"}],"NxKh":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default(update) {
  return new Array(update.length);
}
},{}],"BlDg":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.EnterNode = EnterNode;
exports.default = _default;
var _sparse = _interopRequireDefault(require("./sparse"));
var _index = require("./index");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default() {
  return new _index.Selection(this._enter || this._groups.map(_sparse.default), this._parents);
}
function EnterNode(parent, datum) {
  this.ownerDocument = parent.ownerDocument;
  this.namespaceURI = parent.namespaceURI;
  this._next = null;
  this._parent = parent;
  this.__data__ = datum;
}
EnterNode.prototype = {
  constructor: EnterNode,
  appendChild: function (child) {
    return this._parent.insertBefore(child, this._next);
  },
  insertBefore: function (child, next) {
    return this._parent.insertBefore(child, next);
  },
  querySelector: function (selector) {
    return this._parent.querySelector(selector);
  },
  querySelectorAll: function (selector) {
    return this._parent.querySelectorAll(selector);
  }
};
},{"./sparse":"NxKh","./index":"Pd8D"}],"QcpV":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default(x) {
  return function () {
    return x;
  };
}
},{}],"U337":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _index = require("./index");
var _enter = require("./enter");
var _constant = _interopRequireDefault(require("../constant"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var keyPrefix = "$"; // Protect against keys like “__proto__”.

function bindIndex(parent, group, enter, update, exit, data) {
  var i = 0,
    node,
    groupLength = group.length,
    dataLength = data.length;

  // Put any non-null nodes that fit into update.
  // Put any null nodes into enter.
  // Put any remaining data into enter.
  for (; i < dataLength; ++i) {
    if (node = group[i]) {
      node.__data__ = data[i];
      update[i] = node;
    } else {
      enter[i] = new _enter.EnterNode(parent, data[i]);
    }
  }

  // Put any non-null nodes that don’t fit into exit.
  for (; i < groupLength; ++i) {
    if (node = group[i]) {
      exit[i] = node;
    }
  }
}
function bindKey(parent, group, enter, update, exit, data, key) {
  var i,
    node,
    nodeByKeyValue = {},
    groupLength = group.length,
    dataLength = data.length,
    keyValues = new Array(groupLength),
    keyValue;

  // Compute the key for each node.
  // If multiple nodes have the same key, the duplicates are added to exit.
  for (i = 0; i < groupLength; ++i) {
    if (node = group[i]) {
      keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
      if (keyValue in nodeByKeyValue) {
        exit[i] = node;
      } else {
        nodeByKeyValue[keyValue] = node;
      }
    }
  }

  // Compute the key for each datum.
  // If there a node associated with this key, join and add it to update.
  // If there is not (or the key is a duplicate), add it to enter.
  for (i = 0; i < dataLength; ++i) {
    keyValue = keyPrefix + key.call(parent, data[i], i, data);
    if (node = nodeByKeyValue[keyValue]) {
      update[i] = node;
      node.__data__ = data[i];
      nodeByKeyValue[keyValue] = null;
    } else {
      enter[i] = new _enter.EnterNode(parent, data[i]);
    }
  }

  // Add any remaining nodes that were not bound to data to exit.
  for (i = 0; i < groupLength; ++i) {
    if ((node = group[i]) && nodeByKeyValue[keyValues[i]] === node) {
      exit[i] = node;
    }
  }
}
function _default(value, key) {
  if (!value) {
    data = new Array(this.size()), j = -1;
    this.each(function (d) {
      data[++j] = d;
    });
    return data;
  }
  var bind = key ? bindKey : bindIndex,
    parents = this._parents,
    groups = this._groups;
  if (typeof value !== "function") value = (0, _constant.default)(value);
  for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
    var parent = parents[j],
      group = groups[j],
      groupLength = group.length,
      data = value.call(parent, parent && parent.__data__, j, parents),
      dataLength = data.length,
      enterGroup = enter[j] = new Array(dataLength),
      updateGroup = update[j] = new Array(dataLength),
      exitGroup = exit[j] = new Array(groupLength);
    bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);

    // Now connect the enter nodes to their following update node, such that
    // appendChild can insert the materialized enter node before this node,
    // rather than at the end of the parent node.
    for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
      if (previous = enterGroup[i0]) {
        if (i0 >= i1) i1 = i0 + 1;
        while (!(next = updateGroup[i1]) && ++i1 < dataLength);
        previous._next = next || null;
      }
    }
  }
  update = new _index.Selection(update, parents);
  update._enter = enter;
  update._exit = exit;
  return update;
}
},{"./index":"Pd8D","./enter":"BlDg","../constant":"QcpV"}],"uwqx":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _sparse = _interopRequireDefault(require("./sparse"));
var _index = require("./index");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default() {
  return new _index.Selection(this._exit || this._groups.map(_sparse.default), this._parents);
}
},{"./sparse":"NxKh","./index":"Pd8D"}],"tBqC":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default(onenter, onupdate, onexit) {
  var enter = this.enter(),
    update = this,
    exit = this.exit();
  enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
  if (onupdate != null) update = onupdate(update);
  if (onexit == null) exit.remove();else onexit(exit);
  return enter && update ? enter.merge(update).order() : update;
}
},{}],"oMRz":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _index = require("./index");
function _default(selection) {
  for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
    for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
      if (node = group0[i] || group1[i]) {
        merge[i] = node;
      }
    }
  }
  for (; j < m0; ++j) {
    merges[j] = groups0[j];
  }
  return new _index.Selection(merges, this._parents);
}
},{"./index":"Pd8D"}],"DTXT":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default() {
  for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
    for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
      if (node = group[i]) {
        if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
        next = node;
      }
    }
  }
  return this;
}
},{}],"Eo98":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _index = require("./index");
function _default(compare) {
  if (!compare) compare = ascending;
  function compareNode(a, b) {
    return a && b ? compare(a.__data__, b.__data__) : !a - !b;
  }
  for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
    for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
      if (node = group[i]) {
        sortgroup[i] = node;
      }
    }
    sortgroup.sort(compareNode);
  }
  return new _index.Selection(sortgroups, this._parents).order();
}
function ascending(a, b) {
  return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
},{"./index":"Pd8D"}],"del0":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default() {
  var callback = arguments[0];
  arguments[0] = this;
  callback.apply(null, arguments);
  return this;
}
},{}],"RJvN":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default() {
  var nodes = new Array(this.size()),
    i = -1;
  this.each(function () {
    nodes[++i] = this;
  });
  return nodes;
}
},{}],"P06B":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default() {
  for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
    for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
      var node = group[i];
      if (node) return node;
    }
  }
  return null;
}
},{}],"cLfp":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default() {
  var size = 0;
  this.each(function () {
    ++size;
  });
  return size;
}
},{}],"fLE2":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default() {
  return !this.node();
}
},{}],"Ex0s":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default(callback) {
  for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
    for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
      if (node = group[i]) callback.call(node, node.__data__, i, group);
    }
  }
  return this;
}
},{}],"kqGm":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _namespace = _interopRequireDefault(require("../namespace"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function attrRemove(name) {
  return function () {
    this.removeAttribute(name);
  };
}
function attrRemoveNS(fullname) {
  return function () {
    this.removeAttributeNS(fullname.space, fullname.local);
  };
}
function attrConstant(name, value) {
  return function () {
    this.setAttribute(name, value);
  };
}
function attrConstantNS(fullname, value) {
  return function () {
    this.setAttributeNS(fullname.space, fullname.local, value);
  };
}
function attrFunction(name, value) {
  return function () {
    var v = value.apply(this, arguments);
    if (v == null) this.removeAttribute(name);else this.setAttribute(name, v);
  };
}
function attrFunctionNS(fullname, value) {
  return function () {
    var v = value.apply(this, arguments);
    if (v == null) this.removeAttributeNS(fullname.space, fullname.local);else this.setAttributeNS(fullname.space, fullname.local, v);
  };
}
function _default(name, value) {
  var fullname = (0, _namespace.default)(name);
  if (arguments.length < 2) {
    var node = this.node();
    return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname);
  }
  return this.each((value == null ? fullname.local ? attrRemoveNS : attrRemove : typeof value === "function" ? fullname.local ? attrFunctionNS : attrFunction : fullname.local ? attrConstantNS : attrConstant)(fullname, value));
}
},{"../namespace":"Sva1"}],"a7iO":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default(node) {
  return node.ownerDocument && node.ownerDocument.defaultView // node is a Node
  || node.document && node // node is a Window
  || node.defaultView; // node is a Document
}
},{}],"PNUm":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
exports.styleValue = styleValue;
var _window = _interopRequireDefault(require("../window"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function styleRemove(name) {
  return function () {
    this.style.removeProperty(name);
  };
}
function styleConstant(name, value, priority) {
  return function () {
    this.style.setProperty(name, value, priority);
  };
}
function styleFunction(name, value, priority) {
  return function () {
    var v = value.apply(this, arguments);
    if (v == null) this.style.removeProperty(name);else this.style.setProperty(name, v, priority);
  };
}
function _default(name, value, priority) {
  return arguments.length > 1 ? this.each((value == null ? styleRemove : typeof value === "function" ? styleFunction : styleConstant)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name);
}
function styleValue(node, name) {
  return node.style.getPropertyValue(name) || (0, _window.default)(node).getComputedStyle(node, null).getPropertyValue(name);
}
},{"../window":"a7iO"}],"Qc7K":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function propertyRemove(name) {
  return function () {
    delete this[name];
  };
}
function propertyConstant(name, value) {
  return function () {
    this[name] = value;
  };
}
function propertyFunction(name, value) {
  return function () {
    var v = value.apply(this, arguments);
    if (v == null) delete this[name];else this[name] = v;
  };
}
function _default(name, value) {
  return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name];
}
},{}],"u8Lo":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function classArray(string) {
  return string.trim().split(/^|\s+/);
}
function classList(node) {
  return node.classList || new ClassList(node);
}
function ClassList(node) {
  this._node = node;
  this._names = classArray(node.getAttribute("class") || "");
}
ClassList.prototype = {
  add: function (name) {
    var i = this._names.indexOf(name);
    if (i < 0) {
      this._names.push(name);
      this._node.setAttribute("class", this._names.join(" "));
    }
  },
  remove: function (name) {
    var i = this._names.indexOf(name);
    if (i >= 0) {
      this._names.splice(i, 1);
      this._node.setAttribute("class", this._names.join(" "));
    }
  },
  contains: function (name) {
    return this._names.indexOf(name) >= 0;
  }
};
function classedAdd(node, names) {
  var list = classList(node),
    i = -1,
    n = names.length;
  while (++i < n) list.add(names[i]);
}
function classedRemove(node, names) {
  var list = classList(node),
    i = -1,
    n = names.length;
  while (++i < n) list.remove(names[i]);
}
function classedTrue(names) {
  return function () {
    classedAdd(this, names);
  };
}
function classedFalse(names) {
  return function () {
    classedRemove(this, names);
  };
}
function classedFunction(names, value) {
  return function () {
    (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
  };
}
function _default(name, value) {
  var names = classArray(name + "");
  if (arguments.length < 2) {
    var list = classList(this.node()),
      i = -1,
      n = names.length;
    while (++i < n) if (!list.contains(names[i])) return false;
    return true;
  }
  return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value));
}
},{}],"sv0a":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function textRemove() {
  this.textContent = "";
}
function textConstant(value) {
  return function () {
    this.textContent = value;
  };
}
function textFunction(value) {
  return function () {
    var v = value.apply(this, arguments);
    this.textContent = v == null ? "" : v;
  };
}
function _default(value) {
  return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction : textConstant)(value)) : this.node().textContent;
}
},{}],"IOHV":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function htmlRemove() {
  this.innerHTML = "";
}
function htmlConstant(value) {
  return function () {
    this.innerHTML = value;
  };
}
function htmlFunction(value) {
  return function () {
    var v = value.apply(this, arguments);
    this.innerHTML = v == null ? "" : v;
  };
}
function _default(value) {
  return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML;
}
},{}],"Bs6a":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function raise() {
  if (this.nextSibling) this.parentNode.appendChild(this);
}
function _default() {
  return this.each(raise);
}
},{}],"s7fh":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function lower() {
  if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
}
function _default() {
  return this.each(lower);
}
},{}],"XBOq":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _creator = _interopRequireDefault(require("../creator"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default(name) {
  var create = typeof name === "function" ? name : (0, _creator.default)(name);
  return this.select(function () {
    return this.appendChild(create.apply(this, arguments));
  });
}
},{"../creator":"vTHN"}],"DmTR":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _creator = _interopRequireDefault(require("../creator"));
var _selector = _interopRequireDefault(require("../selector"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function constantNull() {
  return null;
}
function _default(name, before) {
  var create = typeof name === "function" ? name : (0, _creator.default)(name),
    select = before == null ? constantNull : typeof before === "function" ? before : (0, _selector.default)(before);
  return this.select(function () {
    return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
  });
}
},{"../creator":"vTHN","../selector":"hX8m"}],"lzg5":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function remove() {
  var parent = this.parentNode;
  if (parent) parent.removeChild(this);
}
function _default() {
  return this.each(remove);
}
},{}],"lcuA":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function selection_cloneShallow() {
  var clone = this.cloneNode(false),
    parent = this.parentNode;
  return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
}
function selection_cloneDeep() {
  var clone = this.cloneNode(true),
    parent = this.parentNode;
  return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
}
function _default(deep) {
  return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
}
},{}],"rHST":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default(value) {
  return arguments.length ? this.property("__data__", value) : this.node().__data__;
}
},{}],"tgqN":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.customEvent = customEvent;
exports.default = _default;
exports.event = void 0;
var filterEvents = {};
var event = exports.event = null;
if (typeof document !== "undefined") {
  var element = document.documentElement;
  if (!("onmouseenter" in element)) {
    filterEvents = {
      mouseenter: "mouseover",
      mouseleave: "mouseout"
    };
  }
}
function filterContextListener(listener, index, group) {
  listener = contextListener(listener, index, group);
  return function (event) {
    var related = event.relatedTarget;
    if (!related || related !== this && !(related.compareDocumentPosition(this) & 8)) {
      listener.call(this, event);
    }
  };
}
function contextListener(listener, index, group) {
  return function (event1) {
    var event0 = event; // Events can be reentrant (e.g., focus).
    exports.event = event = event1;
    try {
      listener.call(this, this.__data__, index, group);
    } finally {
      exports.event = event = event0;
    }
  };
}
function parseTypenames(typenames) {
  return typenames.trim().split(/^|\s+/).map(function (t) {
    var name = "",
      i = t.indexOf(".");
    if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
    return {
      type: t,
      name: name
    };
  });
}
function onRemove(typename) {
  return function () {
    var on = this.__on;
    if (!on) return;
    for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
      if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
        this.removeEventListener(o.type, o.listener, o.capture);
      } else {
        on[++i] = o;
      }
    }
    if (++i) on.length = i;else delete this.__on;
  };
}
function onAdd(typename, value, capture) {
  var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;
  return function (d, i, group) {
    var on = this.__on,
      o,
      listener = wrap(value, i, group);
    if (on) for (var j = 0, m = on.length; j < m; ++j) {
      if ((o = on[j]).type === typename.type && o.name === typename.name) {
        this.removeEventListener(o.type, o.listener, o.capture);
        this.addEventListener(o.type, o.listener = listener, o.capture = capture);
        o.value = value;
        return;
      }
    }
    this.addEventListener(typename.type, listener, capture);
    o = {
      type: typename.type,
      name: typename.name,
      value: value,
      listener: listener,
      capture: capture
    };
    if (!on) this.__on = [o];else on.push(o);
  };
}
function _default(typename, value, capture) {
  var typenames = parseTypenames(typename + ""),
    i,
    n = typenames.length,
    t;
  if (arguments.length < 2) {
    var on = this.node().__on;
    if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
      for (i = 0, o = on[j]; i < n; ++i) {
        if ((t = typenames[i]).type === o.type && t.name === o.name) {
          return o.value;
        }
      }
    }
    return;
  }
  on = value ? onAdd : onRemove;
  if (capture == null) capture = false;
  for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));
  return this;
}
function customEvent(event1, listener, that, args) {
  var event0 = event;
  event1.sourceEvent = event;
  exports.event = event = event1;
  try {
    return listener.apply(that, args);
  } finally {
    exports.event = event = event0;
  }
}
},{}],"Sfdu":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _window = _interopRequireDefault(require("../window"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function dispatchEvent(node, type, params) {
  var window = (0, _window.default)(node),
    event = window.CustomEvent;
  if (typeof event === "function") {
    event = new event(type, params);
  } else {
    event = window.document.createEvent("Event");
    if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;else event.initEvent(type, false, false);
  }
  node.dispatchEvent(event);
}
function dispatchConstant(type, params) {
  return function () {
    return dispatchEvent(this, type, params);
  };
}
function dispatchFunction(type, params) {
  return function () {
    return dispatchEvent(this, type, params.apply(this, arguments));
  };
}
function _default(type, params) {
  return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params));
}
},{"../window":"a7iO"}],"Pd8D":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Selection = Selection;
exports.root = exports.default = void 0;
var _select = _interopRequireDefault(require("./select"));
var _selectAll = _interopRequireDefault(require("./selectAll"));
var _filter = _interopRequireDefault(require("./filter"));
var _data = _interopRequireDefault(require("./data"));
var _enter = _interopRequireDefault(require("./enter"));
var _exit = _interopRequireDefault(require("./exit"));
var _join = _interopRequireDefault(require("./join"));
var _merge = _interopRequireDefault(require("./merge"));
var _order = _interopRequireDefault(require("./order"));
var _sort = _interopRequireDefault(require("./sort"));
var _call = _interopRequireDefault(require("./call"));
var _nodes = _interopRequireDefault(require("./nodes"));
var _node = _interopRequireDefault(require("./node"));
var _size = _interopRequireDefault(require("./size"));
var _empty = _interopRequireDefault(require("./empty"));
var _each = _interopRequireDefault(require("./each"));
var _attr = _interopRequireDefault(require("./attr"));
var _style = _interopRequireDefault(require("./style"));
var _property = _interopRequireDefault(require("./property"));
var _classed = _interopRequireDefault(require("./classed"));
var _text = _interopRequireDefault(require("./text"));
var _html = _interopRequireDefault(require("./html"));
var _raise = _interopRequireDefault(require("./raise"));
var _lower = _interopRequireDefault(require("./lower"));
var _append = _interopRequireDefault(require("./append"));
var _insert = _interopRequireDefault(require("./insert"));
var _remove = _interopRequireDefault(require("./remove"));
var _clone = _interopRequireDefault(require("./clone"));
var _datum = _interopRequireDefault(require("./datum"));
var _on = _interopRequireDefault(require("./on"));
var _dispatch = _interopRequireDefault(require("./dispatch"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var root = exports.root = [null];
function Selection(groups, parents) {
  this._groups = groups;
  this._parents = parents;
}
function selection() {
  return new Selection([[document.documentElement]], root);
}
Selection.prototype = selection.prototype = {
  constructor: Selection,
  select: _select.default,
  selectAll: _selectAll.default,
  filter: _filter.default,
  data: _data.default,
  enter: _enter.default,
  exit: _exit.default,
  join: _join.default,
  merge: _merge.default,
  order: _order.default,
  sort: _sort.default,
  call: _call.default,
  nodes: _nodes.default,
  node: _node.default,
  size: _size.default,
  empty: _empty.default,
  each: _each.default,
  attr: _attr.default,
  style: _style.default,
  property: _property.default,
  classed: _classed.default,
  text: _text.default,
  html: _html.default,
  raise: _raise.default,
  lower: _lower.default,
  append: _append.default,
  insert: _insert.default,
  remove: _remove.default,
  clone: _clone.default,
  datum: _datum.default,
  on: _on.default,
  dispatch: _dispatch.default
};
var _default = exports.default = selection;
},{"./select":"um0S","./selectAll":"EUVe","./filter":"hZf9","./data":"U337","./enter":"BlDg","./exit":"uwqx","./join":"tBqC","./merge":"oMRz","./order":"DTXT","./sort":"Eo98","./call":"del0","./nodes":"RJvN","./node":"P06B","./size":"cLfp","./empty":"fLE2","./each":"Ex0s","./attr":"kqGm","./style":"PNUm","./property":"Qc7K","./classed":"u8Lo","./text":"sv0a","./html":"IOHV","./raise":"Bs6a","./lower":"s7fh","./append":"XBOq","./insert":"DmTR","./remove":"lzg5","./clone":"lcuA","./datum":"rHST","./on":"tgqN","./dispatch":"Sfdu"}],"SawF":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _index = require("./selection/index");
function _default(selector) {
  return typeof selector === "string" ? new _index.Selection([[document.querySelector(selector)]], [document.documentElement]) : new _index.Selection([[selector]], _index.root);
}
},{"./selection/index":"Pd8D"}],"o4tJ":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _creator = _interopRequireDefault(require("./creator"));
var _select = _interopRequireDefault(require("./select"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default(name) {
  return (0, _select.default)((0, _creator.default)(name).call(document.documentElement));
}
},{"./creator":"vTHN","./select":"SawF"}],"F0np":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = local;
var nextId = 0;
function local() {
  return new Local();
}
function Local() {
  this._ = "@" + (++nextId).toString(36);
}
Local.prototype = local.prototype = {
  constructor: Local,
  get: function (node) {
    var id = this._;
    while (!(id in node)) if (!(node = node.parentNode)) return;
    return node[id];
  },
  set: function (node, value) {
    return node[this._] = value;
  },
  remove: function (node) {
    return this._ in node && delete node[this._];
  },
  toString: function () {
    return this._;
  }
};
},{}],"fcLX":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _on = require("./selection/on");
function _default() {
  var current = _on.event,
    source;
  while (source = current.sourceEvent) current = source;
  return current;
}
},{"./selection/on":"tgqN"}],"tjFE":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
function _default(node, event) {
  var svg = node.ownerSVGElement || node;
  if (svg.createSVGPoint) {
    var point = svg.createSVGPoint();
    point.x = event.clientX, point.y = event.clientY;
    point = point.matrixTransform(node.getScreenCTM().inverse());
    return [point.x, point.y];
  }
  var rect = node.getBoundingClientRect();
  return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
}
},{}],"dfC2":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _sourceEvent = _interopRequireDefault(require("./sourceEvent"));
var _point = _interopRequireDefault(require("./point"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default(node) {
  var event = (0, _sourceEvent.default)();
  if (event.changedTouches) event = event.changedTouches[0];
  return (0, _point.default)(node, event);
}
},{"./sourceEvent":"fcLX","./point":"tjFE"}],"n60l":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _index = require("./selection/index");
function _default(selector) {
  return typeof selector === "string" ? new _index.Selection([document.querySelectorAll(selector)], [document.documentElement]) : new _index.Selection([selector == null ? [] : selector], _index.root);
}
},{"./selection/index":"Pd8D"}],"XEgr":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _sourceEvent = _interopRequireDefault(require("./sourceEvent"));
var _point = _interopRequireDefault(require("./point"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default(node, touches, identifier) {
  if (arguments.length < 3) identifier = touches, touches = (0, _sourceEvent.default)().changedTouches;
  for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {
    if ((touch = touches[i]).identifier === identifier) {
      return (0, _point.default)(node, touch);
    }
  }
  return null;
}
},{"./sourceEvent":"fcLX","./point":"tjFE"}],"DKY7":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _sourceEvent = _interopRequireDefault(require("./sourceEvent"));
var _point = _interopRequireDefault(require("./point"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default(node, touches) {
  if (touches == null) touches = (0, _sourceEvent.default)().touches;
  for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {
    points[i] = (0, _point.default)(node, touches[i]);
  }
  return points;
}
},{"./sourceEvent":"fcLX","./point":"tjFE"}],"Gs4P":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "clientPoint", {
  enumerable: true,
  get: function () {
    return _point.default;
  }
});
Object.defineProperty(exports, "create", {
  enumerable: true,
  get: function () {
    return _create.default;
  }
});
Object.defineProperty(exports, "creator", {
  enumerable: true,
  get: function () {
    return _creator.default;
  }
});
Object.defineProperty(exports, "customEvent", {
  enumerable: true,
  get: function () {
    return _on.customEvent;
  }
});
Object.defineProperty(exports, "event", {
  enumerable: true,
  get: function () {
    return _on.event;
  }
});
Object.defineProperty(exports, "local", {
  enumerable: true,
  get: function () {
    return _local.default;
  }
});
Object.defineProperty(exports, "matcher", {
  enumerable: true,
  get: function () {
    return _matcher.default;
  }
});
Object.defineProperty(exports, "mouse", {
  enumerable: true,
  get: function () {
    return _mouse.default;
  }
});
Object.defineProperty(exports, "namespace", {
  enumerable: true,
  get: function () {
    return _namespace.default;
  }
});
Object.defineProperty(exports, "namespaces", {
  enumerable: true,
  get: function () {
    return _namespaces.default;
  }
});
Object.defineProperty(exports, "select", {
  enumerable: true,
  get: function () {
    return _select.default;
  }
});
Object.defineProperty(exports, "selectAll", {
  enumerable: true,
  get: function () {
    return _selectAll.default;
  }
});
Object.defineProperty(exports, "selection", {
  enumerable: true,
  get: function () {
    return _index.default;
  }
});
Object.defineProperty(exports, "selector", {
  enumerable: true,
  get: function () {
    return _selector.default;
  }
});
Object.defineProperty(exports, "selectorAll", {
  enumerable: true,
  get: function () {
    return _selectorAll.default;
  }
});
Object.defineProperty(exports, "style", {
  enumerable: true,
  get: function () {
    return _style.styleValue;
  }
});
Object.defineProperty(exports, "touch", {
  enumerable: true,
  get: function () {
    return _touch.default;
  }
});
Object.defineProperty(exports, "touches", {
  enumerable: true,
  get: function () {
    return _touches.default;
  }
});
Object.defineProperty(exports, "window", {
  enumerable: true,
  get: function () {
    return _window.default;
  }
});
var _create = _interopRequireDefault(require("./create"));
var _creator = _interopRequireDefault(require("./creator"));
var _local = _interopRequireDefault(require("./local"));
var _matcher = _interopRequireDefault(require("./matcher"));
var _mouse = _interopRequireDefault(require("./mouse"));
var _namespace = _interopRequireDefault(require("./namespace"));
var _namespaces = _interopRequireDefault(require("./namespaces"));
var _point = _interopRequireDefault(require("./point"));
var _select = _interopRequireDefault(require("./select"));
var _selectAll = _interopRequireDefault(require("./selectAll"));
var _index = _interopRequireDefault(require("./selection/index"));
var _selector = _interopRequireDefault(require("./selector"));
var _selectorAll = _interopRequireDefault(require("./selectorAll"));
var _style = require("./selection/style");
var _touch = _interopRequireDefault(require("./touch"));
var _touches = _interopRequireDefault(require("./touches"));
var _window = _interopRequireDefault(require("./window"));
var _on = require("./selection/on");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./create":"o4tJ","./creator":"vTHN","./local":"F0np","./matcher":"LbZ4","./mouse":"dfC2","./namespace":"Sva1","./namespaces":"TZ6S","./point":"tjFE","./select":"SawF","./selectAll":"n60l","./selection/index":"Pd8D","./selector":"hX8m","./selectorAll":"sReu","./selection/style":"PNUm","./touch":"XEgr","./touches":"DKY7","./window":"a7iO","./selection/on":"tgqN"}],"CQsD":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
exports.nopropagation = nopropagation;
var _d3Selection = require("d3-selection");
function nopropagation() {
  _d3Selection.event.stopImmediatePropagation();
}
function _default() {
  _d3Selection.event.preventDefault();
  _d3Selection.event.stopImmediatePropagation();
}
},{"d3-selection":"Gs4P"}],"JG2T":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
exports.yesdrag = yesdrag;
var _d3Selection = require("d3-selection");
var _noevent = _interopRequireDefault(require("./noevent.js"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _default(view) {
  var root = view.document.documentElement,
    selection = (0, _d3Selection.select)(view).on("dragstart.drag", _noevent.default, true);
  if ("onselectstart" in root) {
    selection.on("selectstart.drag", _noevent.default, true);
  } else {
    root.__noselect = root.style.MozUserSelect;
    root.style.MozUserSelect = "none";
  }
}
function yesdrag(view, noclick) {
  var root = view.document.documentElement,
    selection = (0, _d3Selection.select)(view).on("dragstart.drag", null);
  if (noclick) {
    selection.on("click.drag", _noevent.default, true);
    setTimeout(function () {
      selection.on("click.drag", null);
    }, 0);
  }
  if ("onselectstart" in root) {
    selection.on("selectstart.drag", null);
  } else {
    root.style.MozUserSelect = root.__noselect;
    delete root.__noselect;
  }
}
},{"d3-selection":"Gs4P","./noevent.js":"CQsD"}],"QusW":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = DragEvent;
function DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) {
  this.target = target;
  this.type = type;
  this.subject = subject;
  this.identifier = id;
  this.active = active;
  this.x = x;
  this.y = y;
  this.dx = dx;
  this.dy = dy;
  this._ = dispatch;
}
DragEvent.prototype.on = function () {
  var value = this._.on.apply(this._, arguments);
  return value === this._ ? this : value;
};
},{}],"U7NX":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
var _d3Dispatch = require("d3-dispatch");
var _d3Selection = require("d3-selection");
var _nodrag = _interopRequireWildcard(require("./nodrag.js"));
var _noevent = _interopRequireWildcard(require("./noevent.js"));
var _constant = _interopRequireDefault(require("./constant.js"));
var _event = _interopRequireDefault(require("./event.js"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
// Ignore right-click, since that should open the context menu.
function defaultFilter() {
  return !_d3Selection.event.ctrlKey && !_d3Selection.event.button;
}
function defaultContainer() {
  return this.parentNode;
}
function defaultSubject(d) {
  return d == null ? {
    x: _d3Selection.event.x,
    y: _d3Selection.event.y
  } : d;
}
function defaultTouchable() {
  return navigator.maxTouchPoints || "ontouchstart" in this;
}
function _default() {
  var filter = defaultFilter,
    container = defaultContainer,
    subject = defaultSubject,
    touchable = defaultTouchable,
    gestures = {},
    listeners = (0, _d3Dispatch.dispatch)("start", "drag", "end"),
    active = 0,
    mousedownx,
    mousedowny,
    mousemoving,
    touchending,
    clickDistance2 = 0;
  function drag(selection) {
    selection.on("mousedown.drag", mousedowned).filter(touchable).on("touchstart.drag", touchstarted).on("touchmove.drag", touchmoved).on("touchend.drag touchcancel.drag", touchended).style("touch-action", "none").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
  }
  function mousedowned() {
    if (touchending || !filter.apply(this, arguments)) return;
    var gesture = beforestart("mouse", container.apply(this, arguments), _d3Selection.mouse, this, arguments);
    if (!gesture) return;
    (0, _d3Selection.select)(_d3Selection.event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
    (0, _nodrag.default)(_d3Selection.event.view);
    (0, _noevent.nopropagation)();
    mousemoving = false;
    mousedownx = _d3Selection.event.clientX;
    mousedowny = _d3Selection.event.clientY;
    gesture("start");
  }
  function mousemoved() {
    (0, _noevent.default)();
    if (!mousemoving) {
      var dx = _d3Selection.event.clientX - mousedownx,
        dy = _d3Selection.event.clientY - mousedowny;
      mousemoving = dx * dx + dy * dy > clickDistance2;
    }
    gestures.mouse("drag");
  }
  function mouseupped() {
    (0, _d3Selection.select)(_d3Selection.event.view).on("mousemove.drag mouseup.drag", null);
    (0, _nodrag.yesdrag)(_d3Selection.event.view, mousemoving);
    (0, _noevent.default)();
    gestures.mouse("end");
  }
  function touchstarted() {
    if (!filter.apply(this, arguments)) return;
    var touches = _d3Selection.event.changedTouches,
      c = container.apply(this, arguments),
      n = touches.length,
      i,
      gesture;
    for (i = 0; i < n; ++i) {
      if (gesture = beforestart(touches[i].identifier, c, _d3Selection.touch, this, arguments)) {
        (0, _noevent.nopropagation)();
        gesture("start");
      }
    }
  }
  function touchmoved() {
    var touches = _d3Selection.event.changedTouches,
      n = touches.length,
      i,
      gesture;
    for (i = 0; i < n; ++i) {
      if (gesture = gestures[touches[i].identifier]) {
        (0, _noevent.default)();
        gesture("drag");
      }
    }
  }
  function touchended() {
    var touches = _d3Selection.event.changedTouches,
      n = touches.length,
      i,
      gesture;
    if (touchending) clearTimeout(touchending);
    touchending = setTimeout(function () {
      touchending = null;
    }, 500); // Ghost clicks are delayed!
    for (i = 0; i < n; ++i) {
      if (gesture = gestures[touches[i].identifier]) {
        (0, _noevent.nopropagation)();
        gesture("end");
      }
    }
  }
  function beforestart(id, container, point, that, args) {
    var p = point(container, id),
      s,
      dx,
      dy,
      sublisteners = listeners.copy();
    if (!(0, _d3Selection.customEvent)(new _event.default(drag, "beforestart", s, id, active, p[0], p[1], 0, 0, sublisteners), function () {
      if ((_d3Selection.event.subject = s = subject.apply(that, args)) == null) return false;
      dx = s.x - p[0] || 0;
      dy = s.y - p[1] || 0;
      return true;
    })) return;
    return function gesture(type) {
      var p0 = p,
        n;
      switch (type) {
        case "start":
          gestures[id] = gesture, n = active++;
          break;
        case "end":
          delete gestures[id], --active;
        // nobreak
        case "drag":
          p = point(container, id), n = active;
          break;
      }
      (0, _d3Selection.customEvent)(new _event.default(drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]);
    };
  }
  drag.filter = function (_) {
    return arguments.length ? (filter = typeof _ === "function" ? _ : (0, _constant.default)(!!_), drag) : filter;
  };
  drag.container = function (_) {
    return arguments.length ? (container = typeof _ === "function" ? _ : (0, _constant.default)(_), drag) : container;
  };
  drag.subject = function (_) {
    return arguments.length ? (subject = typeof _ === "function" ? _ : (0, _constant.default)(_), drag) : subject;
  };
  drag.touchable = function (_) {
    return arguments.length ? (touchable = typeof _ === "function" ? _ : (0, _constant.default)(!!_), drag) : touchable;
  };
  drag.on = function () {
    var value = listeners.on.apply(listeners, arguments);
    return value === listeners ? drag : value;
  };
  drag.clickDistance = function (_) {
    return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
  };
  return drag;
}
},{"d3-dispatch":"cNf8","d3-selection":"Gs4P","./nodrag.js":"JG2T","./noevent.js":"CQsD","./constant.js":"QcpV","./event.js":"QusW"}],"aj7N":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "drag", {
  enumerable: true,
  get: function () {
    return _drag.default;
  }
});
Object.defineProperty(exports, "dragDisable", {
  enumerable: true,
  get: function () {
    return _nodrag.default;
  }
});
Object.defineProperty(exports, "dragEnable", {
  enumerable: true,
  get: function () {
    return _nodrag.yesdrag;
  }
});
var _drag = _interopRequireDefault(require("./drag.js"));
var _nodrag = _interopRequireWildcard(require("./nodrag.js"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
},{"./drag.js":"U7NX","./nodrag.js":"JG2T"}],"bV5G":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createRounder = createRounder;
exports.default = void 0;
exports.roundValue = roundValue;
var _chart = _interopRequireDefault(require("chart.js"));
var _d3Drag = require("d3-drag");
var _d3Selection = require("d3-selection");
var _immer = require("immer");
var _yt_clipper = require("../../yt_clipper");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
let element, scale, scaleX, radar;
function getElement(chartInstance, callback) {
  return () => {
    if (_d3Selection.event) {
      const e = _d3Selection.event.sourceEvent;
      element = chartInstance.getElementAtEvent(e)[0];
      radar = chartInstance.config.type == 'radar';
      let scaleName = radar ? '_scale' : '_yScale';
      if (element) {
        if (chartInstance.data.datasets[element['_datasetIndex']].dragData === false || element[scaleName].options.dragData === false) {
          element = null;
          return;
        }
        scale = element[scaleName].id;
        if (element['_xScale']) {
          scaleX = element['_xScale'].id;
        }
        if (typeof callback === 'function' && element) {
          const datasetIndex = element['_datasetIndex'];
          const index = element['_index'];
          const value = chartInstance.data.datasets[datasetIndex].data[index];
          if (callback(e, chartInstance, element, value) === false) {
            element = null;
          }
        }
      }
    }
  };
}
function createRounder(multiple, precision) {
  return value => {
    const roundedValue = Math.round(value / multiple) * multiple;
    const roundedValueFixedPrecision = +roundedValue.toFixed(precision);
    return roundedValueFixedPrecision;
  };
}
function roundValue(value, multiple, precision) {
  return createRounder(multiple, precision)(value);
}
function updateData(chartInstance, callback) {
  return () => {
    if (element && _d3Selection.event) {
      const e = _d3Selection.event.sourceEvent;
      const datasetIndex = element['_datasetIndex'];
      const index = element['_index'];
      const roundMultipleX = chartInstance.options.dragDataRoundMultipleX;
      const roundPrecisionX = chartInstance.options.dragDataRoundPrecisionX;
      const roundMultipleY = chartInstance.options.dragDataRoundMultipleY;
      const roundPrecisionY = chartInstance.options.dragDataRoundPrecisionY;
      const roundX = createRounder(roundMultipleX, roundPrecisionX);
      const roundY = createRounder(roundMultipleY, roundPrecisionY);
      let x;
      let y;
      const initialState = chartInstance.data.datasets[datasetIndex].data;
      const dataRef = (0, _immer.createDraft)(initialState);
      let datumRef = dataRef[index];
      let proposedDatum = {
        x: datumRef.x,
        y: datumRef.y
      };
      if (radar) {
        let v;
        if (e.touches) {
          x = e.touches[0].clientX - chartInstance.canvas.getBoundingClientRect().left;
          y = e.touches[0].clientY - chartInstance.canvas.getBoundingClientRect().top;
        } else {
          x = e.clientX - chartInstance.canvas.getBoundingClientRect().left;
          y = e.clientY - chartInstance.canvas.getBoundingClientRect().top;
        }
        let rScale = chartInstance.scales[scale];
        let d = Math.sqrt(Math.pow(x - rScale.xCenter, 2) + Math.pow(y - rScale.yCenter, 2));
        let scalingFactor = rScale.drawingArea / (rScale.max - rScale.min);
        if (rScale.options.ticks.reverse) {
          v = rScale.max - d / scalingFactor;
        } else {
          v = rScale.min + d / scalingFactor;
        }
        v = roundValue(v, chartInstance.options.dragDataRound, 2);
        v = Math.min(v, chartInstance.scale.max);
        v = Math.max(v, chartInstance.scale.min);
        proposedDatum = v;
      } else {
        if (e.touches) {
          x = chartInstance.scales[scaleX].getValueForPixel(e.touches[0].clientX - chartInstance.canvas.getBoundingClientRect().left);
          y = chartInstance.scales[scale].getValueForPixel(e.touches[0].clientY - chartInstance.canvas.getBoundingClientRect().top);
        } else {
          x = chartInstance.scales[scaleX].getValueForPixel(e.clientX - chartInstance.canvas.getBoundingClientRect().left);
          y = chartInstance.scales[scale].getValueForPixel(e.clientY - chartInstance.canvas.getBoundingClientRect().top);
        }
        x = roundX(x);
        y = roundY(y);
        x = Math.min(x, chartInstance.scales[scaleX].max);
        x = Math.max(x, chartInstance.scales[scaleX].min);
        y = Math.min(y, chartInstance.scales[scale].max);
        y = Math.max(y, chartInstance.scales[scale].min);
        proposedDatum.x = x;
        if (datumRef.y !== undefined) {
          proposedDatum.y = y;
        } else {
          proposedDatum = y;
        }
      }
      let shouldChartUpdateX = chartInstance.options.dragX && datumRef.x !== undefined;
      let shouldChartUpdateY = chartInstance.options.dragY;
      let shouldChartUpdate;
      if (typeof callback === 'function') {
        shouldChartUpdate = callback(e, chartInstance, datasetIndex, index, datumRef, proposedDatum);
        shouldChartUpdateX = shouldChartUpdateX && shouldChartUpdate.dragX;
        shouldChartUpdateY = shouldChartUpdateY && shouldChartUpdate.dragY;
      }
      if (shouldChartUpdateX !== false) {
        datumRef.x = proposedDatum.x;
      }
      if (shouldChartUpdateY !== false) {
        if (datumRef.y !== undefined) {
          datumRef.y = proposedDatum.y;
        } else {
          datumRef = proposedDatum;
        }
      }
      const newState = (0, _immer.finishDraft)(dataRef);
      const markerPair = _yt_clipper.markerPairs[_yt_clipper.prevSelectedMarkerPairIndex];
      chartInstance.data.datasets[datasetIndex].data = newState;
      shouldChartUpdate.chartType === 'crop' ? markerPair.cropMap = newState : markerPair.speedMap = newState;
      if (shouldChartUpdateX !== false || shouldChartUpdateY !== false) {
        chartInstance.update(0);
      }
    }
  };
}
function dragEndCallback(chartInstance, callback) {
  return () => {
    if (typeof callback === 'function' && element) {
      const e = _d3Selection.event.sourceEvent;
      const datasetIndex = element['_datasetIndex'];
      const index = element['_index'];
      const value = chartInstance.data.datasets[datasetIndex].data[index];
      return callback(e, chartInstance, datasetIndex, index, value);
    }
  };
}
const ChartJSdragDataPlugin = {
  beforeDatasetsUpdate: function (chartInstance) {
    if (chartInstance.options.dragData) {
      (0, _d3Selection.select)(chartInstance.chart.canvas).call((0, _d3Drag.drag)().container(chartInstance.chart.canvas).on('start', getElement(chartInstance, chartInstance.options.onDragStart)).on('drag', updateData(chartInstance, chartInstance.options.onDrag)).on('end', dragEndCallback(chartInstance, chartInstance.options.onDragEnd)));
    }
  }
};
_chart.default.pluginService.register(ChartJSdragDataPlugin);
var _default = exports.default = ChartJSdragDataPlugin;
},{"chart.js":"JsLj","d3-drag":"aj7N","d3-selection":"Gs4P","immer":"wBnk","../../yt_clipper":"XbmT"}],"NG0t":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.cubicInOutTension = void 0;
exports.getInputUpdater = getInputUpdater;
exports.roundY = exports.roundX = exports.medgrey = exports.lightgrey = exports.inputId = exports.grey = void 0;
exports.setInputId = setInputId;
exports.sortX = void 0;
var _util = require("../../util/util");
const sortX = (a, b) => {
  if (a.x < b.x) return -1;
  if (a.x > b.x) return 1;
  return 0;
};
exports.sortX = sortX;
const lightgrey = opacity => `rgba(120, 120, 120, ${opacity})`;
exports.lightgrey = lightgrey;
const medgrey = opacity => `rgba(90, 90, 90, ${opacity})`;
exports.medgrey = medgrey;
const grey = opacity => `rgba(50, 50, 50, ${opacity})`;
exports.grey = grey;
const cubicInOutTension = exports.cubicInOutTension = 0.6;
const roundX = exports.roundX = (0, _util.getRounder)(0.01, 2);
const roundY = exports.roundY = (0, _util.getRounder)(0.05, 2);
let inputId = exports.inputId = null;
function setInputId(Id) {
  exports.inputId = inputId = Id;
}
function getInputUpdater(inputId) {
  return function (newValue) {
    const input = document.getElementById(inputId);
    if (input) {
      if (newValue != null) {
        input.value = newValue.toString();
      }
      // input.dispatchEvent(new Event('change'));
    } else {
      console.log(`Input with Id ${inputId} not found.`);
    }
  };
}
},{"../../util/util":"mCFQ"}],"clJU":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getMarkerPairHistory = getMarkerPairHistory;
exports.peekLastState = peekLastState;
exports.pushState = pushState;
exports.redo = redo;
exports.saveMarkerPairHistory = saveMarkerPairHistory;
exports.undo = undo;
var _immer = require("immer");
const historySize = 100;
function pushState(undoredo, state) {
  undoredo.history.splice(undoredo.index + 1);
  undoredo.history.push(state);
  if (undoredo.history.length > historySize) {
    undoredo.history.shift();
  } else {
    undoredo.index++;
  }
}
var RestoreDirection;
(function (RestoreDirection) {
  RestoreDirection[RestoreDirection["undo"] = 0] = "undo";
  RestoreDirection[RestoreDirection["redo"] = 1] = "redo";
})(RestoreDirection || (RestoreDirection = {}));
function undo(undoredo, restore) {
  if (undoredo.index <= 0) {
    return null;
  } else {
    undoredo.index--;
    const state = undoredo.history[undoredo.index];
    restore(state, RestoreDirection.undo);
    return state;
  }
}
function redo(undoredo, restore) {
  if (undoredo.index >= undoredo.history.length - 1) {
    return null;
  } else {
    undoredo.index++;
    const state = undoredo.history[undoredo.index];
    restore(state, RestoreDirection.redo);
    return state;
  }
}
function peekLastState(undoredo) {
  const state = undoredo.history[undoredo.index];
  return state;
}
function getMarkerPairHistory(markerPair) {
  const {
    start,
    end,
    speed,
    speedMap,
    crop,
    cropMap,
    enableZoomPan,
    cropRes
  } = markerPair;
  const history = {
    start,
    end,
    speed,
    speedMap,
    crop,
    cropMap,
    enableZoomPan,
    cropRes
  };
  return history;
}
function saveMarkerPairHistory(draft, markerPair) {
  let storeHistory = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
  const newState = (0, _immer.finishDraft)(draft);
  Object.assign(markerPair, newState);
  if (storeHistory) pushState(markerPair.undoredo, newState);
}
},{"immer":"wBnk"}],"ZNtu":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.addSpeedPoint = exports.addCropPoint = void 0;
exports.getScatterPointColor = getScatterPointColor;
exports.scatterChartDefaults = void 0;
exports.scatterChartSpec = scatterChartSpec;
var _immer = require("immer");
var _undoredo = require("../../util/undoredo");
var _util = require("../../util/util");
var _yt_clipper = require("../../yt_clipper");
var _chartutil = require("./chartutil");
var _cropChartSpec = require("./cropchart/cropChartSpec");
const scatterChartDefaults = exports.scatterChartDefaults = {
  defaultColor: 'rgba(255, 255, 255, 1)',
  defaultFontSize: 16,
  defaultFontStyle: 'bold',
  defaultFontColor: (0, _chartutil.lightgrey)(1),
  maintainAspectRatio: false,
  hover: {
    mode: 'nearest'
  },
  animation: {
    duration: 0
  }
};
function getScatterPointColor(context) {
  var index = context.dataIndex;
  var value = context.dataset.data[index];
  return value.y <= 1 ? `rgba(255, ${100 * value.y}, 100, 0.9)` : `rgba(${130 - 90 * (value.y - 1)}, 100, 245, 0.9)`;
}
function getScatterChartBounds(chartInstance) {
  const scatterChartBounds = {
    XMinBound: chartInstance.options.scales.xAxes[0].ticks.min,
    XMaxBound: chartInstance.options.scales.xAxes[0].ticks.max,
    YMinBound: 0.05,
    YMaxBound: 2
  };
  return scatterChartBounds;
}
function displayDataLabel(context) {
  return context.active ? true : 'auto';
}
function alignDataLabel(context) {
  const index = context.dataIndex;
  // const value = context.dataset.data[index];
  if (index === 0) {
    return 'right';
  } else if (index === context.dataset.data.length - 1) {
    return 'left';
  } else if (context.dataset.data[context.dataIndex].y > 1.85) {
    return 'start';
  } else {
    return 'end';
  }
}
const addSpeedPoint = function (time, speed) {
  // console.log(element, dataAtClick);
  if (time && speed) {
    const scatterChartBounds = getScatterChartBounds(this);
    if (time <= scatterChartBounds.XMinBound || time >= scatterChartBounds.XMaxBound || speed < scatterChartBounds.YMinBound || speed > scatterChartBounds.YMaxBound) {
      return;
    }
    time = (0, _chartutil.roundX)(time);
    speed = (0, _chartutil.roundY)(speed);
    const markerPair = _yt_clipper.markerPairs[_yt_clipper.prevSelectedMarkerPairIndex];
    const initialState = (0, _undoredo.getMarkerPairHistory)(markerPair);
    const draft = (0, _immer.createDraft)(initialState);
    draft.speedMap.push({
      x: time,
      y: speed
    });
    draft.speedMap.sort(_chartutil.sortX);
    (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
    this.renderSpeedAndCropUI(true);
  }
};
exports.addSpeedPoint = addSpeedPoint;
const addCropPoint = function (time) {
  // console.log(element, dataAtClick);
  if (time) {
    const scatterChartBounds = getScatterChartBounds(this);
    if (time <= scatterChartBounds.XMinBound || time >= scatterChartBounds.XMaxBound) {
      return;
    }
    time = (0, _chartutil.roundX)(time);
    const markerPair = _yt_clipper.markerPairs[_yt_clipper.prevSelectedMarkerPairIndex];
    const initialState = (0, _undoredo.getMarkerPairHistory)(markerPair);
    const draft = (0, _immer.createDraft)(initialState);
    draft.cropMap.push({
      x: time,
      y: 0,
      crop: '0:0:iw:ih'
    });
    draft.cropMap.sort(_chartutil.sortX);
    const cropPointIndex = draft.cropMap.map(cropPoint => cropPoint.x).indexOf(time);
    // console.log(currentCropPointIndex, cropPointIndex);
    if (_cropChartSpec.currentCropPointIndex >= cropPointIndex) {
      (0, _cropChartSpec.setCurrentCropPoint)(this, _cropChartSpec.currentCropPointIndex + 1);
    }
    if (cropPointIndex > 0) {
      const prevCropPointIndex = cropPointIndex - 1;
      draft.cropMap[cropPointIndex].crop = draft.cropMap[prevCropPointIndex].crop;
    }
    (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
    this.renderSpeedAndCropUI(true);
  }
};
exports.addCropPoint = addCropPoint;
function scatterChartSpec(chartType, inputId) {
  const updateInput = (0, _chartutil.getInputUpdater)(inputId);
  const onDragStart = function (e, chartInstance, element, value) {
    // console.log(arguments);
    if (!e.ctrlKey && !e.altKey && !e.shiftKey) {
      chartInstance.options.plugins.zoom.pan.enabled = false;
      e.target.style.cursor = 'grabbing';
      if (chartType === 'crop') {
        (0, _util.seekToSafe)(_yt_clipper.video, (0, _util.timeRounder)(value.x));
      }
      chartInstance.update();
    }
  };
  const onDrag = function (e, chartInstance, datasetIndex, index, fromValue, toValue) {
    // console.log(datasetIndex, index, fromValue, toValue);
    if (!e.ctrlKey && !e.altKey && !e.shiftKey) {
      const shouldDrag = {
        dragX: true,
        dragY: true,
        chartType
      };
      const scatterChartBounds = getScatterChartBounds(chartInstance);
      if (fromValue.x <= scatterChartBounds.XMinBound || fromValue.x >= scatterChartBounds.XMaxBound || toValue.x <= scatterChartBounds.XMinBound || toValue.x >= scatterChartBounds.XMaxBound) {
        shouldDrag.dragX = false;
      }
      if (chartType === 'crop' || toValue.y < scatterChartBounds.YMinBound || toValue.y > scatterChartBounds.YMaxBound) {
        shouldDrag.dragY = false;
      }
      if (chartType === 'crop' && shouldDrag.dragX && fromValue.x != toValue.x) {
        (0, _util.seekToSafe)(_yt_clipper.video, (0, _util.timeRounder)(toValue.x));
      }
      return shouldDrag;
    } else {
      return {
        dragX: false,
        dragY: false,
        chartType
      };
    }
  };
  const onDragEnd = function (e, chartInstance, datasetIndex, index, value) {
    // console.log(datasetIndex, index, value);
    if (!e.ctrlKey && !e.altKey && !e.shiftKey) {
      const markerPair = _yt_clipper.markerPairs[_yt_clipper.prevSelectedMarkerPairIndex];
      const draft = (0, _immer.createDraft)((0, _undoredo.getMarkerPairHistory)(markerPair));
      const draftMap = chartType === 'crop' ? draft.cropMap : draft.speedMap;
      let currentCropPointXPreSort = chartType === 'crop' ? draftMap[_cropChartSpec.currentCropPointIndex].x : null;
      draftMap.sort(_chartutil.sortX);
      if (index === 0 && chartType === 'speed') {
        draft.speed = value.y;
      }
      if (chartType === 'crop') {
        const newCurrentCropPointIndex = draftMap.map(cropPoint => cropPoint.x).indexOf(currentCropPointXPreSort);
        (0, _cropChartSpec.setCurrentCropPoint)(chartInstance, newCurrentCropPointIndex);
      }
      chartInstance.options.plugins.zoom.pan.enabled = true;
      e.target.style.cursor = 'default';
      (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
      chartInstance.renderSpeedAndCropUI(true);
    }
  };
  const onClick = function (event, dataAtClick) {
    event.stopImmediatePropagation();
    // add chart points on shift+left-click
    if (event.button === 0 && !event.ctrlKey && !event.altKey && event.shiftKey && dataAtClick.length === 0) {
      const time = this.scales['x-axis-1'].getValueForPixel(event.offsetX);
      if (chartType === 'speed') {
        const speed = this.scales['y-axis-1'].getValueForPixel(event.offsetY);
        addSpeedPoint.call(this, time, speed);
      } else if (chartType === 'crop') {
        addCropPoint.call(this, time);
      }
    }
    // delete chart points on alt+shift+left-click
    if (event.button === 0 && !event.ctrlKey && event.altKey && event.shiftKey && dataAtClick.length === 1) {
      const datum = dataAtClick[0];
      if (datum) {
        const datasetIndex = datum['_datasetIndex'];
        const index = datum['_index'];
        let scatterChartMinBound = this.options.scales.xAxes[0].ticks.min;
        let scatterChartMaxBound = this.options.scales.xAxes[0].ticks.max;
        const markerPair = _yt_clipper.markerPairs[_yt_clipper.prevSelectedMarkerPairIndex];
        const initialState = (0, _undoredo.getMarkerPairHistory)(markerPair);
        const draft = (0, _immer.createDraft)(initialState);
        let dataRef;
        if (chartType === 'crop') {
          dataRef = draft.cropMap;
        } else {
          dataRef = draft.speedMap;
        }
        if (dataRef[index].x !== scatterChartMinBound && dataRef[index].x !== scatterChartMaxBound) {
          dataRef.splice(index, 1);
          if (chartType === 'crop') {
            (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
            this.data.datasets[0].data = markerPair.cropMap;
            if (_cropChartSpec.currentCropPointIndex >= index) {
              (0, _cropChartSpec.setCurrentCropPoint)(this, _cropChartSpec.currentCropPointIndex - 1);
            }
            updateInput(markerPair.cropMap[_cropChartSpec.currentCropPointIndex].crop);
          } else {
            (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
            this.data.datasets[0].data = markerPair.speedMap;
            updateInput();
          }
          this.renderSpeedAndCropUI(true);
        }
      }
    }
    // change crop point ease in function
    if (event.button === 0 && event.ctrlKey && !event.altKey && event.shiftKey && dataAtClick.length === 1) {
      if (chartType === 'crop') {
        const datum = dataAtClick[0];
        if (datum) {
          const datasetIndex = datum['_datasetIndex'];
          const index = datum['_index'];
          const markerPair = _yt_clipper.markerPairs[_yt_clipper.prevSelectedMarkerPairIndex];
          const initialState = (0, _undoredo.getMarkerPairHistory)(markerPair);
          const draft = (0, _immer.createDraft)(initialState);
          if (draft.cropMap[index].easeIn == null) {
            draft.cropMap[index].easeIn = 'instant';
          } else {
            delete draft.cropMap[index].easeIn;
          }
          (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
          this.renderSpeedAndCropUI(true);
        }
      }
    }
    if (event.ctrlKey && !event.altKey && !event.shiftKey) {
      this.resetZoom();
    }
  };
  function onHover(e, chartElements) {
    e.target.style.cursor = chartElements[0] ? 'grab' : 'default';
    if (chartType === 'crop' && !e.shiftKey && chartElements.length === 1) {
      let mode;
      if (e.ctrlKey && !e.altKey) {
        mode = _cropChartSpec.cropChartMode.Start;
      } else if (!e.ctrlKey && e.altKey) {
        mode = _cropChartSpec.cropChartMode.End;
      } else {
        return;
      }
      const datum = chartElements[0];
      if (datum) {
        const index = datum['_index'];
        (0, _cropChartSpec.setCurrentCropPoint)(this, index, mode);
        (0, _yt_clipper.triggerCropChartLoop)();
      }
    }
  }
  return {
    type: 'scatter',
    options: {
      elements: {
        line: {
          fill: true,
          backgroundColor: 'rgba(160,0, 255, 0.05)',
          borderColor: (0, _chartutil.lightgrey)(0.8),
          borderWidth: 2,
          borderDash: [5, 2]
        }
      },
      legend: {
        display: false
      },
      layout: {
        padding: {
          left: 0,
          right: 0,
          top: 15,
          bottom: 0
        }
      },
      tooltips: {
        enabled: false
      },
      scales: {
        xAxes: [{
          scaleLabel: {
            display: true,
            labelString: 'Time (s)',
            fontSize: 12,
            padding: -4
          },
          position: 'bottom',
          gridLines: {
            color: (0, _chartutil.medgrey)(0.6),
            lineWidth: 1
          },
          ticks: {
            min: 0,
            max: 10,
            maxTicksLimit: 100,
            autoSkip: false,
            maxRotation: 60,
            minRotation: 0,
            major: {},
            minor: {}
          }
        }]
      },
      plugins: {
        datalabels: {
          clip: false,
          clamp: true,
          font: {
            size: 14,
            weight: 'bold'
          },
          textStrokeWidth: 2,
          textStrokeColor: (0, _chartutil.grey)(0.9),
          textAlign: 'center',
          display: displayDataLabel,
          align: alignDataLabel,
          color: getScatterPointColor
        },
        zoom: {
          pan: {
            enabled: true,
            mode: 'x',
            rangeMin: {
              x: 0,
              y: 0
            },
            rangeMax: {
              x: 10,
              y: 2
            }
          },
          zoom: {
            enabled: true,
            mode: 'x',
            drag: false,
            speed: 0.1,
            rangeMin: {
              x: 0,
              y: 0
            },
            rangeMax: {
              x: 10,
              y: 2
            }
          }
        }
      },
      annotation: {
        drawTime: 'afterDraw',
        annotations: [{
          label: 'time',
          type: 'line',
          mode: 'vertical',
          scaleID: 'x-axis-1',
          value: -1,
          borderColor: 'rgba(255, 0, 0, 0.9)',
          borderWidth: 1
        }, {
          label: 'start',
          type: 'line',
          display: true,
          mode: 'vertical',
          scaleID: 'x-axis-1',
          value: -1,
          borderColor: 'rgba(0, 255, 0, 0.9)',
          borderWidth: 1
        }, {
          label: 'end',
          type: 'line',
          display: true,
          mode: 'vertical',
          scaleID: 'x-axis-1',
          value: -1,
          borderColor: 'rgba(255, 215, 0, 0.9)',
          borderWidth: 1
        }]
      },
      onHover: onHover,
      dragData: true,
      dragY: true,
      dragX: true,
      dragDataRound: 0.5,
      dragDataRoundMultipleX: 0.01,
      dragDataRoundPrecisionX: 2,
      dragDataRoundMultipleY: 0.05,
      dragDataRoundPrecisionY: 2,
      dragDataSort: false,
      dragDataSortFunction: _chartutil.sortX,
      onDragStart: onDragStart,
      onDrag: onDrag,
      onDragEnd: onDragEnd,
      onClick: onClick
    }
  };
}
},{"immer":"wBnk","../../util/undoredo":"clJU","../../util/util":"mCFQ","../../yt_clipper":"XbmT","./chartutil":"NG0t","./cropchart/cropChartSpec":"Ldv2"}],"y0aG":[function(require,module,exports) {
var global = arguments[3];

/**
 * Lodash (Custom Build) <https://lodash.com/>
 * Build: `lodash modularize exports="npm" -o ./`
 * Copyright JS Foundation and other contributors <https://js.foundation/>
 * Released under MIT license <https://lodash.com/license>
 * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
 * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 */

/** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200;

/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
    COMPARE_UNORDERED_FLAG = 2;

/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;

/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
    arrayTag = '[object Array]',
    asyncTag = '[object AsyncFunction]',
    boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    funcTag = '[object Function]',
    genTag = '[object GeneratorFunction]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    nullTag = '[object Null]',
    objectTag = '[object Object]',
    promiseTag = '[object Promise]',
    proxyTag = '[object Proxy]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    symbolTag = '[object Symbol]',
    undefinedTag = '[object Undefined]',
    weakMapTag = '[object WeakMap]';

var arrayBufferTag = '[object ArrayBuffer]',
    dataViewTag = '[object DataView]',
    float32Tag = '[object Float32Array]',
    float64Tag = '[object Float64Array]',
    int8Tag = '[object Int8Array]',
    int16Tag = '[object Int16Array]',
    int32Tag = '[object Int32Array]',
    uint8Tag = '[object Uint8Array]',
    uint8ClampedTag = '[object Uint8ClampedArray]',
    uint16Tag = '[object Uint16Array]',
    uint32Tag = '[object Uint32Array]';

/**
 * Used to match `RegExp`
 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
 */
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;

/** Used to detect host constructors (Safari). */
var reIsHostCtor = /^\[object .+?Constructor\]$/;

/** Used to detect unsigned integer values. */
var reIsUint = /^(?:0|[1-9]\d*)$/;

/** Used to identify `toStringTag` values of typed arrays. */
var typedArrayTags = {};
typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
typedArrayTags[uint32Tag] = true;
typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
typedArrayTags[errorTag] = typedArrayTags[funcTag] =
typedArrayTags[mapTag] = typedArrayTags[numberTag] =
typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
typedArrayTags[setTag] = typedArrayTags[stringTag] =
typedArrayTags[weakMapTag] = false;

/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;

/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();

/** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;

/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;

/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;

/** Detect free variable `process` from Node.js. */
var freeProcess = moduleExports && freeGlobal.process;

/** Used to access faster Node.js helpers. */
var nodeUtil = (function() {
  try {
    return freeProcess && freeProcess.binding && freeProcess.binding('util');
  } catch (e) {}
}());

/* Node.js helper references. */
var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;

/**
 * A specialized version of `_.filter` for arrays without support for
 * iteratee shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} predicate The function invoked per iteration.
 * @returns {Array} Returns the new filtered array.
 */
function arrayFilter(array, predicate) {
  var index = -1,
      length = array == null ? 0 : array.length,
      resIndex = 0,
      result = [];

  while (++index < length) {
    var value = array[index];
    if (predicate(value, index, array)) {
      result[resIndex++] = value;
    }
  }
  return result;
}

/**
 * Appends the elements of `values` to `array`.
 *
 * @private
 * @param {Array} array The array to modify.
 * @param {Array} values The values to append.
 * @returns {Array} Returns `array`.
 */
function arrayPush(array, values) {
  var index = -1,
      length = values.length,
      offset = array.length;

  while (++index < length) {
    array[offset + index] = values[index];
  }
  return array;
}

/**
 * A specialized version of `_.some` for arrays without support for iteratee
 * shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} predicate The function invoked per iteration.
 * @returns {boolean} Returns `true` if any element passes the predicate check,
 *  else `false`.
 */
function arraySome(array, predicate) {
  var index = -1,
      length = array == null ? 0 : array.length;

  while (++index < length) {
    if (predicate(array[index], index, array)) {
      return true;
    }
  }
  return false;
}

/**
 * The base implementation of `_.times` without support for iteratee shorthands
 * or max array length checks.
 *
 * @private
 * @param {number} n The number of times to invoke `iteratee`.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the array of results.
 */
function baseTimes(n, iteratee) {
  var index = -1,
      result = Array(n);

  while (++index < n) {
    result[index] = iteratee(index);
  }
  return result;
}

/**
 * The base implementation of `_.unary` without support for storing metadata.
 *
 * @private
 * @param {Function} func The function to cap arguments for.
 * @returns {Function} Returns the new capped function.
 */
function baseUnary(func) {
  return function(value) {
    return func(value);
  };
}

/**
 * Checks if a `cache` value for `key` exists.
 *
 * @private
 * @param {Object} cache The cache to query.
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function cacheHas(cache, key) {
  return cache.has(key);
}

/**
 * Gets the value at `key` of `object`.
 *
 * @private
 * @param {Object} [object] The object to query.
 * @param {string} key The key of the property to get.
 * @returns {*} Returns the property value.
 */
function getValue(object, key) {
  return object == null ? undefined : object[key];
}

/**
 * Converts `map` to its key-value pairs.
 *
 * @private
 * @param {Object} map The map to convert.
 * @returns {Array} Returns the key-value pairs.
 */
function mapToArray(map) {
  var index = -1,
      result = Array(map.size);

  map.forEach(function(value, key) {
    result[++index] = [key, value];
  });
  return result;
}

/**
 * Creates a unary function that invokes `func` with its argument transformed.
 *
 * @private
 * @param {Function} func The function to wrap.
 * @param {Function} transform The argument transform.
 * @returns {Function} Returns the new function.
 */
function overArg(func, transform) {
  return function(arg) {
    return func(transform(arg));
  };
}

/**
 * Converts `set` to an array of its values.
 *
 * @private
 * @param {Object} set The set to convert.
 * @returns {Array} Returns the values.
 */
function setToArray(set) {
  var index = -1,
      result = Array(set.size);

  set.forEach(function(value) {
    result[++index] = value;
  });
  return result;
}

/** Used for built-in method references. */
var arrayProto = Array.prototype,
    funcProto = Function.prototype,
    objectProto = Object.prototype;

/** Used to detect overreaching core-js shims. */
var coreJsData = root['__core-js_shared__'];

/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/** Used to detect methods masquerading as native. */
var maskSrcKey = (function() {
  var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
  return uid ? ('Symbol(src)_1.' + uid) : '';
}());

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.toString;

/** Used to detect if a method is native. */
var reIsNative = RegExp('^' +
  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);

/** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined,
    Symbol = root.Symbol,
    Uint8Array = root.Uint8Array,
    propertyIsEnumerable = objectProto.propertyIsEnumerable,
    splice = arrayProto.splice,
    symToStringTag = Symbol ? Symbol.toStringTag : undefined;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeGetSymbols = Object.getOwnPropertySymbols,
    nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,
    nativeKeys = overArg(Object.keys, Object);

/* Built-in method references that are verified to be native. */
var DataView = getNative(root, 'DataView'),
    Map = getNative(root, 'Map'),
    Promise = getNative(root, 'Promise'),
    Set = getNative(root, 'Set'),
    WeakMap = getNative(root, 'WeakMap'),
    nativeCreate = getNative(Object, 'create');

/** Used to detect maps, sets, and weakmaps. */
var dataViewCtorString = toSource(DataView),
    mapCtorString = toSource(Map),
    promiseCtorString = toSource(Promise),
    setCtorString = toSource(Set),
    weakMapCtorString = toSource(WeakMap);

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;

/**
 * Creates a hash object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Hash(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

/**
 * Removes all key-value entries from the hash.
 *
 * @private
 * @name clear
 * @memberOf Hash
 */
function hashClear() {
  this.__data__ = nativeCreate ? nativeCreate(null) : {};
  this.size = 0;
}

/**
 * Removes `key` and its value from the hash.
 *
 * @private
 * @name delete
 * @memberOf Hash
 * @param {Object} hash The hash to modify.
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function hashDelete(key) {
  var result = this.has(key) && delete this.__data__[key];
  this.size -= result ? 1 : 0;
  return result;
}

/**
 * Gets the hash value for `key`.
 *
 * @private
 * @name get
 * @memberOf Hash
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function hashGet(key) {
  var data = this.__data__;
  if (nativeCreate) {
    var result = data[key];
    return result === HASH_UNDEFINED ? undefined : result;
  }
  return hasOwnProperty.call(data, key) ? data[key] : undefined;
}

/**
 * Checks if a hash value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf Hash
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function hashHas(key) {
  var data = this.__data__;
  return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key);
}

/**
 * Sets the hash `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf Hash
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the hash instance.
 */
function hashSet(key, value) {
  var data = this.__data__;
  this.size += this.has(key) ? 0 : 1;
  data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
  return this;
}

// Add methods to `Hash`.
Hash.prototype.clear = hashClear;
Hash.prototype['delete'] = hashDelete;
Hash.prototype.get = hashGet;
Hash.prototype.has = hashHas;
Hash.prototype.set = hashSet;

/**
 * Creates an list cache object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function ListCache(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

/**
 * Removes all key-value entries from the list cache.
 *
 * @private
 * @name clear
 * @memberOf ListCache
 */
function listCacheClear() {
  this.__data__ = [];
  this.size = 0;
}

/**
 * Removes `key` and its value from the list cache.
 *
 * @private
 * @name delete
 * @memberOf ListCache
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function listCacheDelete(key) {
  var data = this.__data__,
      index = assocIndexOf(data, key);

  if (index < 0) {
    return false;
  }
  var lastIndex = data.length - 1;
  if (index == lastIndex) {
    data.pop();
  } else {
    splice.call(data, index, 1);
  }
  --this.size;
  return true;
}

/**
 * Gets the list cache value for `key`.
 *
 * @private
 * @name get
 * @memberOf ListCache
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function listCacheGet(key) {
  var data = this.__data__,
      index = assocIndexOf(data, key);

  return index < 0 ? undefined : data[index][1];
}

/**
 * Checks if a list cache value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf ListCache
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function listCacheHas(key) {
  return assocIndexOf(this.__data__, key) > -1;
}

/**
 * Sets the list cache `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf ListCache
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the list cache instance.
 */
function listCacheSet(key, value) {
  var data = this.__data__,
      index = assocIndexOf(data, key);

  if (index < 0) {
    ++this.size;
    data.push([key, value]);
  } else {
    data[index][1] = value;
  }
  return this;
}

// Add methods to `ListCache`.
ListCache.prototype.clear = listCacheClear;
ListCache.prototype['delete'] = listCacheDelete;
ListCache.prototype.get = listCacheGet;
ListCache.prototype.has = listCacheHas;
ListCache.prototype.set = listCacheSet;

/**
 * Creates a map cache object to store key-value pairs.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function MapCache(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

/**
 * Removes all key-value entries from the map.
 *
 * @private
 * @name clear
 * @memberOf MapCache
 */
function mapCacheClear() {
  this.size = 0;
  this.__data__ = {
    'hash': new Hash,
    'map': new (Map || ListCache),
    'string': new Hash
  };
}

/**
 * Removes `key` and its value from the map.
 *
 * @private
 * @name delete
 * @memberOf MapCache
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function mapCacheDelete(key) {
  var result = getMapData(this, key)['delete'](key);
  this.size -= result ? 1 : 0;
  return result;
}

/**
 * Gets the map value for `key`.
 *
 * @private
 * @name get
 * @memberOf MapCache
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function mapCacheGet(key) {
  return getMapData(this, key).get(key);
}

/**
 * Checks if a map value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf MapCache
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function mapCacheHas(key) {
  return getMapData(this, key).has(key);
}

/**
 * Sets the map `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf MapCache
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the map cache instance.
 */
function mapCacheSet(key, value) {
  var data = getMapData(this, key),
      size = data.size;

  data.set(key, value);
  this.size += data.size == size ? 0 : 1;
  return this;
}

// Add methods to `MapCache`.
MapCache.prototype.clear = mapCacheClear;
MapCache.prototype['delete'] = mapCacheDelete;
MapCache.prototype.get = mapCacheGet;
MapCache.prototype.has = mapCacheHas;
MapCache.prototype.set = mapCacheSet;

/**
 *
 * Creates an array cache object to store unique values.
 *
 * @private
 * @constructor
 * @param {Array} [values] The values to cache.
 */
function SetCache(values) {
  var index = -1,
      length = values == null ? 0 : values.length;

  this.__data__ = new MapCache;
  while (++index < length) {
    this.add(values[index]);
  }
}

/**
 * Adds `value` to the array cache.
 *
 * @private
 * @name add
 * @memberOf SetCache
 * @alias push
 * @param {*} value The value to cache.
 * @returns {Object} Returns the cache instance.
 */
function setCacheAdd(value) {
  this.__data__.set(value, HASH_UNDEFINED);
  return this;
}

/**
 * Checks if `value` is in the array cache.
 *
 * @private
 * @name has
 * @memberOf SetCache
 * @param {*} value The value to search for.
 * @returns {number} Returns `true` if `value` is found, else `false`.
 */
function setCacheHas(value) {
  return this.__data__.has(value);
}

// Add methods to `SetCache`.
SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
SetCache.prototype.has = setCacheHas;

/**
 * Creates a stack cache object to store key-value pairs.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Stack(entries) {
  var data = this.__data__ = new ListCache(entries);
  this.size = data.size;
}

/**
 * Removes all key-value entries from the stack.
 *
 * @private
 * @name clear
 * @memberOf Stack
 */
function stackClear() {
  this.__data__ = new ListCache;
  this.size = 0;
}

/**
 * Removes `key` and its value from the stack.
 *
 * @private
 * @name delete
 * @memberOf Stack
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function stackDelete(key) {
  var data = this.__data__,
      result = data['delete'](key);

  this.size = data.size;
  return result;
}

/**
 * Gets the stack value for `key`.
 *
 * @private
 * @name get
 * @memberOf Stack
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function stackGet(key) {
  return this.__data__.get(key);
}

/**
 * Checks if a stack value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf Stack
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function stackHas(key) {
  return this.__data__.has(key);
}

/**
 * Sets the stack `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf Stack
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the stack cache instance.
 */
function stackSet(key, value) {
  var data = this.__data__;
  if (data instanceof ListCache) {
    var pairs = data.__data__;
    if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
      pairs.push([key, value]);
      this.size = ++data.size;
      return this;
    }
    data = this.__data__ = new MapCache(pairs);
  }
  data.set(key, value);
  this.size = data.size;
  return this;
}

// Add methods to `Stack`.
Stack.prototype.clear = stackClear;
Stack.prototype['delete'] = stackDelete;
Stack.prototype.get = stackGet;
Stack.prototype.has = stackHas;
Stack.prototype.set = stackSet;

/**
 * Creates an array of the enumerable property names of the array-like `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @param {boolean} inherited Specify returning inherited property names.
 * @returns {Array} Returns the array of property names.
 */
function arrayLikeKeys(value, inherited) {
  var isArr = isArray(value),
      isArg = !isArr && isArguments(value),
      isBuff = !isArr && !isArg && isBuffer(value),
      isType = !isArr && !isArg && !isBuff && isTypedArray(value),
      skipIndexes = isArr || isArg || isBuff || isType,
      result = skipIndexes ? baseTimes(value.length, String) : [],
      length = result.length;

  for (var key in value) {
    if ((inherited || hasOwnProperty.call(value, key)) &&
        !(skipIndexes && (
           // Safari 9 has enumerable `arguments.length` in strict mode.
           key == 'length' ||
           // Node.js 0.10 has enumerable non-index properties on buffers.
           (isBuff && (key == 'offset' || key == 'parent')) ||
           // PhantomJS 2 has enumerable non-index properties on typed arrays.
           (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
           // Skip index properties.
           isIndex(key, length)
        ))) {
      result.push(key);
    }
  }
  return result;
}

/**
 * Gets the index at which the `key` is found in `array` of key-value pairs.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} key The key to search for.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function assocIndexOf(array, key) {
  var length = array.length;
  while (length--) {
    if (eq(array[length][0], key)) {
      return length;
    }
  }
  return -1;
}

/**
 * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
 * `keysFunc` and `symbolsFunc` to get the enumerable property names and
 * symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Function} keysFunc The function to get the keys of `object`.
 * @param {Function} symbolsFunc The function to get the symbols of `object`.
 * @returns {Array} Returns the array of property names and symbols.
 */
function baseGetAllKeys(object, keysFunc, symbolsFunc) {
  var result = keysFunc(object);
  return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
}

/**
 * The base implementation of `getTag` without fallbacks for buggy environments.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function baseGetTag(value) {
  if (value == null) {
    return value === undefined ? undefinedTag : nullTag;
  }
  return (symToStringTag && symToStringTag in Object(value))
    ? getRawTag(value)
    : objectToString(value);
}

/**
 * The base implementation of `_.isArguments`.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
 */
function baseIsArguments(value) {
  return isObjectLike(value) && baseGetTag(value) == argsTag;
}

/**
 * The base implementation of `_.isEqual` which supports partial comparisons
 * and tracks traversed objects.
 *
 * @private
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @param {boolean} bitmask The bitmask flags.
 *  1 - Unordered comparison
 *  2 - Partial comparison
 * @param {Function} [customizer] The function to customize comparisons.
 * @param {Object} [stack] Tracks traversed `value` and `other` objects.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 */
function baseIsEqual(value, other, bitmask, customizer, stack) {
  if (value === other) {
    return true;
  }
  if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {
    return value !== value && other !== other;
  }
  return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
}

/**
 * A specialized version of `baseIsEqual` for arrays and objects which performs
 * deep comparisons and tracks traversed objects enabling objects with circular
 * references to be compared.
 *
 * @private
 * @param {Object} object The object to compare.
 * @param {Object} other The other object to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} [stack] Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
  var objIsArr = isArray(object),
      othIsArr = isArray(other),
      objTag = objIsArr ? arrayTag : getTag(object),
      othTag = othIsArr ? arrayTag : getTag(other);

  objTag = objTag == argsTag ? objectTag : objTag;
  othTag = othTag == argsTag ? objectTag : othTag;

  var objIsObj = objTag == objectTag,
      othIsObj = othTag == objectTag,
      isSameTag = objTag == othTag;

  if (isSameTag && isBuffer(object)) {
    if (!isBuffer(other)) {
      return false;
    }
    objIsArr = true;
    objIsObj = false;
  }
  if (isSameTag && !objIsObj) {
    stack || (stack = new Stack);
    return (objIsArr || isTypedArray(object))
      ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)
      : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
  }
  if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
    var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
        othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');

    if (objIsWrapped || othIsWrapped) {
      var objUnwrapped = objIsWrapped ? object.value() : object,
          othUnwrapped = othIsWrapped ? other.value() : other;

      stack || (stack = new Stack);
      return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
    }
  }
  if (!isSameTag) {
    return false;
  }
  stack || (stack = new Stack);
  return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
}

/**
 * The base implementation of `_.isNative` without bad shim checks.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a native function,
 *  else `false`.
 */
function baseIsNative(value) {
  if (!isObject(value) || isMasked(value)) {
    return false;
  }
  var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
  return pattern.test(toSource(value));
}

/**
 * The base implementation of `_.isTypedArray` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
 */
function baseIsTypedArray(value) {
  return isObjectLike(value) &&
    isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
}

/**
 * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function baseKeys(object) {
  if (!isPrototype(object)) {
    return nativeKeys(object);
  }
  var result = [];
  for (var key in Object(object)) {
    if (hasOwnProperty.call(object, key) && key != 'constructor') {
      result.push(key);
    }
  }
  return result;
}

/**
 * A specialized version of `baseIsEqualDeep` for arrays with support for
 * partial deep comparisons.
 *
 * @private
 * @param {Array} array The array to compare.
 * @param {Array} other The other array to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `array` and `other` objects.
 * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
 */
function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
      arrLength = array.length,
      othLength = other.length;

  if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
    return false;
  }
  // Assume cyclic values are equal.
  var stacked = stack.get(array);
  if (stacked && stack.get(other)) {
    return stacked == other;
  }
  var index = -1,
      result = true,
      seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;

  stack.set(array, other);
  stack.set(other, array);

  // Ignore non-index properties.
  while (++index < arrLength) {
    var arrValue = array[index],
        othValue = other[index];

    if (customizer) {
      var compared = isPartial
        ? customizer(othValue, arrValue, index, other, array, stack)
        : customizer(arrValue, othValue, index, array, other, stack);
    }
    if (compared !== undefined) {
      if (compared) {
        continue;
      }
      result = false;
      break;
    }
    // Recursively compare arrays (susceptible to call stack limits).
    if (seen) {
      if (!arraySome(other, function(othValue, othIndex) {
            if (!cacheHas(seen, othIndex) &&
                (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
              return seen.push(othIndex);
            }
          })) {
        result = false;
        break;
      }
    } else if (!(
          arrValue === othValue ||
            equalFunc(arrValue, othValue, bitmask, customizer, stack)
        )) {
      result = false;
      break;
    }
  }
  stack['delete'](array);
  stack['delete'](other);
  return result;
}

/**
 * A specialized version of `baseIsEqualDeep` for comparing objects of
 * the same `toStringTag`.
 *
 * **Note:** This function only supports comparing values with tags of
 * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
 *
 * @private
 * @param {Object} object The object to compare.
 * @param {Object} other The other object to compare.
 * @param {string} tag The `toStringTag` of the objects to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
  switch (tag) {
    case dataViewTag:
      if ((object.byteLength != other.byteLength) ||
          (object.byteOffset != other.byteOffset)) {
        return false;
      }
      object = object.buffer;
      other = other.buffer;

    case arrayBufferTag:
      if ((object.byteLength != other.byteLength) ||
          !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
        return false;
      }
      return true;

    case boolTag:
    case dateTag:
    case numberTag:
      // Coerce booleans to `1` or `0` and dates to milliseconds.
      // Invalid dates are coerced to `NaN`.
      return eq(+object, +other);

    case errorTag:
      return object.name == other.name && object.message == other.message;

    case regexpTag:
    case stringTag:
      // Coerce regexes to strings and treat strings, primitives and objects,
      // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
      // for more details.
      return object == (other + '');

    case mapTag:
      var convert = mapToArray;

    case setTag:
      var isPartial = bitmask & COMPARE_PARTIAL_FLAG;
      convert || (convert = setToArray);

      if (object.size != other.size && !isPartial) {
        return false;
      }
      // Assume cyclic values are equal.
      var stacked = stack.get(object);
      if (stacked) {
        return stacked == other;
      }
      bitmask |= COMPARE_UNORDERED_FLAG;

      // Recursively compare objects (susceptible to call stack limits).
      stack.set(object, other);
      var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
      stack['delete'](object);
      return result;

    case symbolTag:
      if (symbolValueOf) {
        return symbolValueOf.call(object) == symbolValueOf.call(other);
      }
  }
  return false;
}

/**
 * A specialized version of `baseIsEqualDeep` for objects with support for
 * partial deep comparisons.
 *
 * @private
 * @param {Object} object The object to compare.
 * @param {Object} other The other object to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
      objProps = getAllKeys(object),
      objLength = objProps.length,
      othProps = getAllKeys(other),
      othLength = othProps.length;

  if (objLength != othLength && !isPartial) {
    return false;
  }
  var index = objLength;
  while (index--) {
    var key = objProps[index];
    if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {
      return false;
    }
  }
  // Assume cyclic values are equal.
  var stacked = stack.get(object);
  if (stacked && stack.get(other)) {
    return stacked == other;
  }
  var result = true;
  stack.set(object, other);
  stack.set(other, object);

  var skipCtor = isPartial;
  while (++index < objLength) {
    key = objProps[index];
    var objValue = object[key],
        othValue = other[key];

    if (customizer) {
      var compared = isPartial
        ? customizer(othValue, objValue, key, other, object, stack)
        : customizer(objValue, othValue, key, object, other, stack);
    }
    // Recursively compare objects (susceptible to call stack limits).
    if (!(compared === undefined
          ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))
          : compared
        )) {
      result = false;
      break;
    }
    skipCtor || (skipCtor = key == 'constructor');
  }
  if (result && !skipCtor) {
    var objCtor = object.constructor,
        othCtor = other.constructor;

    // Non `Object` object instances with different constructors are not equal.
    if (objCtor != othCtor &&
        ('constructor' in object && 'constructor' in other) &&
        !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
          typeof othCtor == 'function' && othCtor instanceof othCtor)) {
      result = false;
    }
  }
  stack['delete'](object);
  stack['delete'](other);
  return result;
}

/**
 * Creates an array of own enumerable property names and symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names and symbols.
 */
function getAllKeys(object) {
  return baseGetAllKeys(object, keys, getSymbols);
}

/**
 * Gets the data for `map`.
 *
 * @private
 * @param {Object} map The map to query.
 * @param {string} key The reference key.
 * @returns {*} Returns the map data.
 */
function getMapData(map, key) {
  var data = map.__data__;
  return isKeyable(key)
    ? data[typeof key == 'string' ? 'string' : 'hash']
    : data.map;
}

/**
 * Gets the native function at `key` of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {string} key The key of the method to get.
 * @returns {*} Returns the function if it's native, else `undefined`.
 */
function getNative(object, key) {
  var value = getValue(object, key);
  return baseIsNative(value) ? value : undefined;
}

/**
 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the raw `toStringTag`.
 */
function getRawTag(value) {
  var isOwn = hasOwnProperty.call(value, symToStringTag),
      tag = value[symToStringTag];

  try {
    value[symToStringTag] = undefined;
    var unmasked = true;
  } catch (e) {}

  var result = nativeObjectToString.call(value);
  if (unmasked) {
    if (isOwn) {
      value[symToStringTag] = tag;
    } else {
      delete value[symToStringTag];
    }
  }
  return result;
}

/**
 * Creates an array of the own enumerable symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of symbols.
 */
var getSymbols = !nativeGetSymbols ? stubArray : function(object) {
  if (object == null) {
    return [];
  }
  object = Object(object);
  return arrayFilter(nativeGetSymbols(object), function(symbol) {
    return propertyIsEnumerable.call(object, symbol);
  });
};

/**
 * Gets the `toStringTag` of `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
var getTag = baseGetTag;

// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
    (Map && getTag(new Map) != mapTag) ||
    (Promise && getTag(Promise.resolve()) != promiseTag) ||
    (Set && getTag(new Set) != setTag) ||
    (WeakMap && getTag(new WeakMap) != weakMapTag)) {
  getTag = function(value) {
    var result = baseGetTag(value),
        Ctor = result == objectTag ? value.constructor : undefined,
        ctorString = Ctor ? toSource(Ctor) : '';

    if (ctorString) {
      switch (ctorString) {
        case dataViewCtorString: return dataViewTag;
        case mapCtorString: return mapTag;
        case promiseCtorString: return promiseTag;
        case setCtorString: return setTag;
        case weakMapCtorString: return weakMapTag;
      }
    }
    return result;
  };
}

/**
 * Checks if `value` is a valid array-like index.
 *
 * @private
 * @param {*} value The value to check.
 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
 */
function isIndex(value, length) {
  length = length == null ? MAX_SAFE_INTEGER : length;
  return !!length &&
    (typeof value == 'number' || reIsUint.test(value)) &&
    (value > -1 && value % 1 == 0 && value < length);
}

/**
 * Checks if `value` is suitable for use as unique object key.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
 */
function isKeyable(value) {
  var type = typeof value;
  return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
    ? (value !== '__proto__')
    : (value === null);
}

/**
 * Checks if `func` has its source masked.
 *
 * @private
 * @param {Function} func The function to check.
 * @returns {boolean} Returns `true` if `func` is masked, else `false`.
 */
function isMasked(func) {
  return !!maskSrcKey && (maskSrcKey in func);
}

/**
 * Checks if `value` is likely a prototype object.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
 */
function isPrototype(value) {
  var Ctor = value && value.constructor,
      proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;

  return value === proto;
}

/**
 * Converts `value` to a string using `Object.prototype.toString`.
 *
 * @private
 * @param {*} value The value to convert.
 * @returns {string} Returns the converted string.
 */
function objectToString(value) {
  return nativeObjectToString.call(value);
}

/**
 * Converts `func` to its source code.
 *
 * @private
 * @param {Function} func The function to convert.
 * @returns {string} Returns the source code.
 */
function toSource(func) {
  if (func != null) {
    try {
      return funcToString.call(func);
    } catch (e) {}
    try {
      return (func + '');
    } catch (e) {}
  }
  return '';
}

/**
 * Performs a
 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * comparison between two values to determine if they are equivalent.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 * @example
 *
 * var object = { 'a': 1 };
 * var other = { 'a': 1 };
 *
 * _.eq(object, object);
 * // => true
 *
 * _.eq(object, other);
 * // => false
 *
 * _.eq('a', 'a');
 * // => true
 *
 * _.eq('a', Object('a'));
 * // => false
 *
 * _.eq(NaN, NaN);
 * // => true
 */
function eq(value, other) {
  return value === other || (value !== value && other !== other);
}

/**
 * Checks if `value` is likely an `arguments` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
 *  else `false`.
 * @example
 *
 * _.isArguments(function() { return arguments; }());
 * // => true
 *
 * _.isArguments([1, 2, 3]);
 * // => false
 */
var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
  return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&
    !propertyIsEnumerable.call(value, 'callee');
};

/**
 * Checks if `value` is classified as an `Array` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
 * @example
 *
 * _.isArray([1, 2, 3]);
 * // => true
 *
 * _.isArray(document.body.children);
 * // => false
 *
 * _.isArray('abc');
 * // => false
 *
 * _.isArray(_.noop);
 * // => false
 */
var isArray = Array.isArray;

/**
 * Checks if `value` is array-like. A value is considered array-like if it's
 * not a function and has a `value.length` that's an integer greater than or
 * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
 * @example
 *
 * _.isArrayLike([1, 2, 3]);
 * // => true
 *
 * _.isArrayLike(document.body.children);
 * // => true
 *
 * _.isArrayLike('abc');
 * // => true
 *
 * _.isArrayLike(_.noop);
 * // => false
 */
function isArrayLike(value) {
  return value != null && isLength(value.length) && !isFunction(value);
}

/**
 * Checks if `value` is a buffer.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
 * @example
 *
 * _.isBuffer(new Buffer(2));
 * // => true
 *
 * _.isBuffer(new Uint8Array(2));
 * // => false
 */
var isBuffer = nativeIsBuffer || stubFalse;

/**
 * Performs a deep comparison between two values to determine if they are
 * equivalent.
 *
 * **Note:** This method supports comparing arrays, array buffers, booleans,
 * date objects, error objects, maps, numbers, `Object` objects, regexes,
 * sets, strings, symbols, and typed arrays. `Object` objects are compared
 * by their own, not inherited, enumerable properties. Functions and DOM
 * nodes are compared by strict equality, i.e. `===`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 * @example
 *
 * var object = { 'a': 1 };
 * var other = { 'a': 1 };
 *
 * _.isEqual(object, other);
 * // => true
 *
 * object === other;
 * // => false
 */
function isEqual(value, other) {
  return baseIsEqual(value, other);
}

/**
 * Checks if `value` is classified as a `Function` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
 * @example
 *
 * _.isFunction(_);
 * // => true
 *
 * _.isFunction(/abc/);
 * // => false
 */
function isFunction(value) {
  if (!isObject(value)) {
    return false;
  }
  // The use of `Object#toString` avoids issues with the `typeof` operator
  // in Safari 9 which returns 'object' for typed arrays and other constructors.
  var tag = baseGetTag(value);
  return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
}

/**
 * Checks if `value` is a valid array-like length.
 *
 * **Note:** This method is loosely based on
 * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
 * @example
 *
 * _.isLength(3);
 * // => true
 *
 * _.isLength(Number.MIN_VALUE);
 * // => false
 *
 * _.isLength(Infinity);
 * // => false
 *
 * _.isLength('3');
 * // => false
 */
function isLength(value) {
  return typeof value == 'number' &&
    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
}

/**
 * Checks if `value` is the
 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 * @example
 *
 * _.isObject({});
 * // => true
 *
 * _.isObject([1, 2, 3]);
 * // => true
 *
 * _.isObject(_.noop);
 * // => true
 *
 * _.isObject(null);
 * // => false
 */
function isObject(value) {
  var type = typeof value;
  return value != null && (type == 'object' || type == 'function');
}

/**
 * Checks if `value` is object-like. A value is object-like if it's not `null`
 * and has a `typeof` result of "object".
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
 * @example
 *
 * _.isObjectLike({});
 * // => true
 *
 * _.isObjectLike([1, 2, 3]);
 * // => true
 *
 * _.isObjectLike(_.noop);
 * // => false
 *
 * _.isObjectLike(null);
 * // => false
 */
function isObjectLike(value) {
  return value != null && typeof value == 'object';
}

/**
 * Checks if `value` is classified as a typed array.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
 * @example
 *
 * _.isTypedArray(new Uint8Array);
 * // => true
 *
 * _.isTypedArray([]);
 * // => false
 */
var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;

/**
 * Creates an array of the own enumerable property names of `object`.
 *
 * **Note:** Non-object values are coerced to objects. See the
 * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
 * for more details.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Object
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.keys(new Foo);
 * // => ['a', 'b'] (iteration order is not guaranteed)
 *
 * _.keys('hi');
 * // => ['0', '1']
 */
function keys(object) {
  return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}

/**
 * This method returns a new empty array.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {Array} Returns the new empty array.
 * @example
 *
 * var arrays = _.times(2, _.stubArray);
 *
 * console.log(arrays);
 * // => [[], []]
 *
 * console.log(arrays[0] === arrays[1]);
 * // => false
 */
function stubArray() {
  return [];
}

/**
 * This method returns `false`.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {boolean} Returns `false`.
 * @example
 *
 * _.times(2, _.stubFalse);
 * // => [false, false]
 */
function stubFalse() {
  return false;
}

module.exports = isEqual;

},{}],"Ldv2":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.currentCropPointIndex = exports.currentCropChartSection = exports.currentCropChartMode = exports.cropPointXYFormatter = exports.cropPointFormatter = exports.cropChartMode = void 0;
exports.getCropChartConfig = getCropChartConfig;
exports.setCropChartMode = setCropChartMode;
exports.setCurrentCropChartSection = setCurrentCropChartSection;
exports.setCurrentCropPoint = setCurrentCropPoint;
exports.updateCurrentCropPoint = void 0;
var _chart = _interopRequireDefault(require("chart.js"));
var _util = require("../../../util/util");
var _chartutil = require("../chartutil");
var _scatterChartSpec = require("../scatterChartSpec");
var _lodash = _interopRequireDefault(require("lodash.isequal"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const inputId = 'crop-input';
let currentCropPointIndex = exports.currentCropPointIndex = 0;
var cropChartMode;
(function (cropChartMode) {
  cropChartMode[cropChartMode["Start"] = 0] = "Start";
  cropChartMode[cropChartMode["End"] = 1] = "End";
})(cropChartMode || (exports.cropChartMode = cropChartMode = {}));
let currentCropChartMode = exports.currentCropChartMode = cropChartMode.Start;
function setCropChartMode(mode) {
  exports.currentCropChartMode = currentCropChartMode = mode;
}
function setCurrentCropPoint(cropChart, cropPointIndex, mode) {
  const maxIndex = cropChart ? cropChart.data.datasets[0].data.length - 1 : 1;
  const newCropPointIndex = (0, _util.clampNumber)(cropPointIndex, 0, maxIndex);
  const cropPointIndexChanged = currentCropPointIndex !== newCropPointIndex;
  exports.currentCropPointIndex = currentCropPointIndex = newCropPointIndex;
  const oldCropChartSection = currentCropChartSection;
  if (currentCropPointIndex <= 0) {
    setCropChartMode(cropChartMode.Start);
    setCurrentCropChartSection(cropChart, [0, 1]);
  } else if (currentCropPointIndex >= maxIndex) {
    setCropChartMode(cropChartMode.End);
    setCurrentCropChartSection(cropChart, [maxIndex - 1, maxIndex]);
  } else {
    if (mode != null) exports.currentCropChartMode = currentCropChartMode = mode;
    currentCropChartMode === cropChartMode.Start ? setCurrentCropChartSection(cropChart, [currentCropPointIndex, currentCropPointIndex + 1]) : setCurrentCropChartSection(cropChart, [currentCropPointIndex - 1, currentCropPointIndex]);
  }
  const cropChartSectionChanged = !(0, _lodash.default)(currentCropChartSection, oldCropChartSection);
  if ((cropPointIndexChanged || cropChartSectionChanged) && cropChart) {
    cropChart.renderSpeedAndCropUI(true, false);
  }
}
let currentCropChartSection = exports.currentCropChartSection = [0, 1];
function setCurrentCropChartSection(cropChart, _ref) {
  let [left, right] = _ref;
  const maxIndex = cropChart ? cropChart.data.datasets[0].data.length - 1 : 1;
  if (left <= 0) {
    exports.currentCropChartSection = currentCropChartSection = [0, 1];
  } else if (left >= maxIndex) {
    exports.currentCropChartSection = currentCropChartSection = [maxIndex - 1, maxIndex];
  } else if (left === right) {
    exports.currentCropChartSection = currentCropChartSection = [left, left + 1];
  } else {
    exports.currentCropChartSection = currentCropChartSection = [left, right];
  }
}
const updateCurrentCropPoint = function (cropChart, cropString) {
  const cropChartData = cropChart.data.datasets[0].data;
  const cropPoint = cropChartData[currentCropPointIndex];
  cropPoint.crop = cropString;
  cropChart.update();
};
exports.updateCurrentCropPoint = updateCurrentCropPoint;
const cropPointFormatter = point => {
  return `T:${point.x.toFixed(2)}\nC:${point.crop}`;
};
exports.cropPointFormatter = cropPointFormatter;
const cropPointXYFormatter = (point, ctx) => {
  const [x, y, w, h] = point.crop.split(':');
  const index = ctx.dataIndex;
  const label = index === 0 ? `T:${point.x.toFixed(2)}\nC:${x}:${y}:${w}:${h}` : `T:${point.x.toFixed(2)}\nC:${x}:${y}`;
  return label;
};
exports.cropPointXYFormatter = cropPointXYFormatter;
function getCropPointStyle(ctx) {
  const index = ctx.dataIndex;
  return index === currentCropPointIndex ? 'rectRounded' : 'circle';
}
function getCropPointColor(ctx) {
  const index = ctx.dataIndex;
  if (index === currentCropChartSection[0]) {
    return 'green';
  } else if (index === currentCropChartSection[1]) {
    return 'yellow';
  } else {
    return 'red';
  }
}
function getCropPointBackgroundOverlayColor(ctx) {
  const cropPoint = ctx.dataset.data[ctx.dataIndex];
  return cropPoint.easeIn === 'instant' ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0)';
}
function getCropPointBorderColor(ctx) {
  const index = ctx.dataIndex;
  return index === currentCropPointIndex ? 'black' : (0, _chartutil.medgrey)(0.9);
}
function getCropPointBorderWidth(ctx) {
  const index = ctx.dataIndex;
  return index === currentCropPointIndex ? 2 : 1;
}
function getCropPointRadius(ctx) {
  const index = ctx.dataIndex;
  return index === currentCropPointIndex ? 6 : 4;
}
const cropChartConfig = {
  data: {
    datasets: [{
      label: 'Crop',
      lineTension: 0,
      data: [],
      showLine: true,
      pointBackgroundColor: getCropPointColor,
      pointBorderColor: getCropPointBorderColor,
      pointBorderWidth: getCropPointBorderWidth,
      pointStyle: getCropPointStyle,
      pointRadius: getCropPointRadius,
      backgroundOverlayColor: getCropPointBackgroundOverlayColor,
      backgroundOverlayMode: 'multiply',
      pointHitRadius: 3
    }]
  },
  options: {
    scales: {
      yAxes: [{
        display: false
      }]
    },
    plugins: {
      datalabels: {
        formatter: cropPointFormatter,
        font: {
          size: 10,
          weight: 'normal'
        }
      }
    },
    dragY: false,
    dragX: true
  }
};
function getCropChartConfig(isCropChartPanOnly) {
  let cropChartConfigOverrides = {};
  if (isCropChartPanOnly) {
    cropChartConfigOverrides = {
      options: {
        plugins: {
          datalabels: {
            formatter: cropPointXYFormatter
          }
        }
      }
    };
  } else {
    cropChartConfigOverrides = {
      options: {
        plugins: {
          datalabels: {
            formatter: cropPointFormatter
          }
        }
      }
    };
  }
  const cropChartConfigOverridden = _chart.default.helpers.merge(cropChartConfig, cropChartConfigOverrides);
  return _chart.default.helpers.merge((0, _scatterChartSpec.scatterChartSpec)('crop', inputId), cropChartConfigOverridden);
}
},{"chart.js":"JsLj","../../../util/util":"mCFQ","../chartutil":"NG0t","../scatterChartSpec":"ZNtu","lodash.isequal":"y0aG"}],"C3pH":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.speedChartSpec = void 0;
var _chart = _interopRequireDefault(require("chart.js"));
var _chartutil = require("../chartutil");
var _scatterChartSpec = require("../scatterChartSpec");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const inputId = 'speed-input';
const speedPointFormatter = point => {
  return `T:${point.x.toFixed(2)}\nS:${+point.y.toFixed(2)}`;
};
const speedChartConfig = {
  data: {
    datasets: [{
      label: 'Speed',
      lineTension: 0,
      data: [],
      showLine: true,
      pointBackgroundColor: _scatterChartSpec.getScatterPointColor,
      pointBorderColor: (0, _chartutil.medgrey)(0.9),
      pointRadius: 5,
      pointHoverRadius: 4,
      pointHoverBorderWidth: 1.5,
      pointHoverBorderColor: (0, _chartutil.lightgrey)(0.8),
      pointHitRadius: 4
    }]
  },
  options: {
    scales: {
      yAxes: [{
        scaleLabel: {
          display: true,
          labelString: 'Speed',
          fontSize: 12,
          padding: 0
        },
        gridLines: {
          color: (0, _chartutil.medgrey)(0.6),
          lineWidth: 1
        },
        ticks: {
          stepSize: 0.1,
          min: 0,
          max: 2
        }
      }]
    },
    plugins: {
      datalabels: {
        formatter: speedPointFormatter,
        font: {
          size: 10,
          weight: 'normal'
        }
      }
    }
  }
};
const speedChartSpec = exports.speedChartSpec = _chart.default.helpers.merge((0, _scatterChartSpec.scatterChartSpec)('speed', inputId), speedChartConfig);
},{"chart.js":"JsLj","../chartutil":"NG0t","../scatterChartSpec":"ZNtu"}],"ayli":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.autoHideUnselectedMarkerPairsCSS = exports.adjustRotatedVideoPositionCSS = void 0;
exports.getRotatedVideoCSS = getRotatedVideoCSS;
const autoHideUnselectedMarkerPairsCSS = exports.autoHideUnselectedMarkerPairsCSS = `
    rect.marker {
      opacity: 0.25;
    }
    text.markerNumbering {
      opacity: 0.25;
      pointer-events: none;
    }

    rect.selected-marker {
      opacity: 1;
    }
    text.selectedMarkerNumbering {
      opacity: 1;
      pointer-events: visibleFill;
    }

    rect.marker.end-marker {
      pointer-events: none;
    }
    rect.selected-marker.end-marker {
      pointer-events: visibleFill;
    }
    `;
const adjustRotatedVideoPositionCSS = exports.adjustRotatedVideoPositionCSS = `\

    `;
function getRotatedVideoCSS(rotation) {
  return `
        .yt-clipper-video {
          transform: rotate(${rotation}deg) !important;
        }
        #full-bleed-container {
          height: 100vh !important;
          max-height: none !important;
        }
        #page-manager {
          margin-top: 0px !important;
        }
        #masthead #container {
          display: none !important;
        }
      `;
}
},{}],"vbnC":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Tooltips = void 0;
var _commonTags = require("common-tags");
var Tooltips;
(function (Tooltips) {
  Tooltips.markerPairNumberTooltip = (0, _commonTags.stripIndent)`
    Enter a new marker pair number here to reorder marker pairs.
    Does not automatically update merge list.
    `;
  Tooltips.speedTooltip = (0, _commonTags.stripIndent)`
    Toggle speed previewing with C.
    When audio is enabled, speeds below 0.5 are not yet supported.
    YouTube can only preview speeds that are multiples of 0.05 (e.g., 0.75 or 0.45).
    YouTube does not support previewing speeds below 0.25.
    `;
  Tooltips.timeDurationTooltip = (0, _commonTags.stripIndent)`
      When speed is constant (static) the duration is formatted as <in-duration>/<speed> = <out-duration>.
      When speed is variable (dynamic) the duration is formatted as <in-duration> (<out-duration>).
      Durations are formatted as HH:MM:SS.MS omitting insignificant 0s on the left.
    `;
  Tooltips.cropTooltip = (0, _commonTags.stripIndent)`
    Crop values are given as x-offset:y-offset:width:height. Each value is a positive integer in pixels.
    Width and height can also be 'iw' and 'ih' respectively for input width and input height.
    Use Ctrl+Click+Drag on the crop preview to adjust the crop with the mouse instead.
    Increment/decrement values in the crop input with the up/down keys by ±10.
    The cursor position within the crop string determines the crop component to change.
    Use modifier keys to alter the change amount: Alt: ±1, Shift: ±50, Alt+Shift: ±100.
    `;
  Tooltips.titlePrefixTooltip = (0, _commonTags.stripIndent)`
    Specify a title prefix to be prepended to the tile suffix of the file name generated by this marker pair.
    `;
  Tooltips.titleSuffixTooltip = (0, _commonTags.stripIndent)`
    Specify a title suffix to be appended to the title prefixes specified for each marker pair.
    The title suffix is followed by the marker pair number in the final file name for each marker pair.
    The title suffix is also the the file name stem of the markers data json file saved with S.
    The markers data file name is used as the title suffix when running the clipper script.
    Thus it can be renamed to change the title suffix without editing the json data.
    `;
  Tooltips.cropResolutionTooltip = (0, _commonTags.stripIndent)`
    The crop resolution specifies the scaling of crop strings, which should match the input video's resolution.
    However, lower crop resolutions can be easier to work with.
    The clipper script will automatically scale the crop resolution if a mismatch is detected.
    `;
  Tooltips.rotateTooltip = (0, _commonTags.stripIndent)`
    Correct video rotation by rotating the input video clockwise or counterclockwise by 90 degrees.
    Note that the YouTube video rotate preview using the R/Alt+R shortcuts does NOT affect the output video.
    `;
  Tooltips.mergeListTooltip = (0, _commonTags.stripIndent)`
    Specify which marker pairs if any you would like to merge/concatenate.
    Each merge is a comma separated list of marker pair numbers or ranges (e.g., '1-3,5,9' = '1,2,3,5,9').
    Multiple merges are separated with semicolons (e.g., '1-3,5,9;6-2,8' will create two merged webms).
    Merge occurs successfully only after successful generation of each required generated webm.
    Merge does not require reencoding and simply orders each webm into one container.
    `;
  Tooltips.audioTooltip = (0, _commonTags.stripIndent)`
    Enable audio.
    Not yet compatible with special loop behaviors or time-variable speed.
    `;
  Tooltips.encodeSpeedTooltip = (0, _commonTags.stripIndent)`
    Higher values will speed up encoding at the cost of some quality.
    Very high values will also reduce bitrate control effectiveness, which may increase file sizes.
    `;
  Tooltips.CRFTooltip = (0, _commonTags.stripIndent)`
    Constant Rate Factor or CRF allows the video bitrate to vary while maintaining roughly constant quality.
    Lower CRF values result in higher quality but larger file sizes.
    A CRF around 25 (~30 for 4k) usually results in file size compression that does not visibly reduce quality.
    When the target bitrate is set to 0 (unlimited), the bitrate is unconstrained and operates in constant quality mode .
    When the target bitrate is set to auto or a positive value in kbps, the script operates in constrained quality mode.
    Constrained quality mode keeps file sizes reasonable even when low CRF values are specified.
    `;
  Tooltips.targetBitrateTooltip = (0, _commonTags.stripIndent)`
    Specify the target bitrate in kbps of the output video.
    The bitrate determines how much data is used to encode each second of video and thus the final file size.
    If the bitrate is too low then the compression of the video will visibly reduce quality.
    When the target bitrate is set to 0 for unlimited, the script operates in constant quality mode.
    When the target bitrate is set to auto or a positive value in kbps, the script operates in constrained quality mode.
    Constrained quality mode keeps file sizes reasonable even when low CRF values are specified.
    `;
  Tooltips.twoPassTooltip = (0, _commonTags.stripIndent)`
    Encode in two passes for improved bitrate control which can reduce filesizes.
    Results in better quality, with diminishing returns for high bitrate video.
    Significantly reduces encode speed.
    `;
  Tooltips.hdrTooltip = (0, _commonTags.stripIndent)`
    Enabling HDR (high dynamic range) may improve the output video's image vibrancy and colors at the expense of file size and playback compatibility.
    `;
  Tooltips.gammaTooltip = (0, _commonTags.stripIndent)`
    A gamma function is used to map input luminance values to output luminance values or vice versa.
    Note that the gamma preview is not accurate. Use the offline previewer for more accurate gamma preview.
    The gamma value is an exponent applied to the input luminance values.
    A gamma value of 1 is neutral and does not modify the video.
    A gamma value greater than 1 can be used to darken the video and enhance highlight detail.
    A gamma value less than 1 can be used to lighten the video and enhance shadow detail.
    Even small changes in gamma can have large effects (smallest possible change is 0.01).
    Use the gamma preview toggle (Alt+C) to set the gamma to taste.
    `;
  Tooltips.denoiseTooltip = (0, _commonTags.stripIndent)`
    Reduce noise, static, and blockiness at the cost of some encoding speed.
    Improves compression efficiency and thus reduces file sizes.
    Higher strength presets may result in oversmoothing of details.
    `;
  Tooltips.minterpModeTooltip = (0, _commonTags.stripIndent)`
    Motion interpolation (minterp for short) is sometimes called optical flow, super slowmo, or just interpolation.
    Motion interpolation is typically used to increase fps and achieve smoother slow motion.
    Motion interpolation is in Numeric mode by default. This requires a valid fps (10-120) input to work.
    A higher fps value requires more resources and time to encode.
    Motion interpolation can and will introduce artifacting (visual glitches).
    Artifacting increases with the speed and complexity of the video.
    In MaxSpeed mode, motion interpolation targets the fps of the highest speed seen in the dynamic speed chart.
    This helps smooth out motion when using dynamic speed with reasonable resource efficiency.
    In VideoFPS mode, motion interpolation targets the fps of the input video.
    MaxSpeedx2 and VideoFPSx2 modes double the target fps from the previous two modes.
    `;
  Tooltips.minterpFPSTooltip = (0, _commonTags.stripIndent)`
    Input an fps value from 10-120 to add interpolated frames and achieve smooth slow motion.
    Motion interpolation mode must be set to Numeric.
    Motion interpolation is resource intensive and will take longer to process the higher the target fps.
    Motion interpolation can and will introduce artifacting (visual glitches).
    Artifacting increases with the speed and complexity of the video.
    `;
  Tooltips.vidstabTooltip = (0, _commonTags.stripIndent)`
    Video stabilization tries to smooth out the motion in the video and reduce shaking.
    Usually requires cropping and zooming the video.
    Higher strength presets result in more cropping and zooming.
    Low contrast video or video with flashing lights may give poor results.
    Video stabilization may cause static elements within the cropped region to shake.
    `;
  Tooltips.dynamicZoomTooltip = (0, _commonTags.stripIndent)`
    Allow cropping and zooming of video to vary with the need for stabilization over time.
    `;
  Tooltips.speedMapTooltip = (0, _commonTags.stripIndent)`
    Time-variable speed maps are enabled by default, but can be force enabled/disabled with this setting.
    A speed map may be specified using the speed chart (toggled with D).
    `;
  Tooltips.enableZoomPanTooltip = (0, _commonTags.stripIndent)`
    Enable or disable dynamic crop zoompan mode.
    When disabled, dynamic crop is in pan-only mode.
    In pan-only mode, crop points all have the same size.
    In zoompan mode, crop points can have different sizes but have the same aspect ratio.
    Switching from zoompan to pan-only mode requires removing zooms and a prompt may appear.
  `;
  function zoomPanToPanOnlyTooltip(sw, sh, lw, lh, aw, ah) {
    return (0, _commonTags.stripIndent)`
    Switching from zoompan to pan-only mode requires removing zooms.
    That is, all crop points must be made to have the same size.
    This can be the (s)mallest, (l)argest or (a)verage size in the crop map.

    Enter 's' for ${sw}x${sh}, 'l' for ${lw}x${lh}, or 'a' for ${aw}x${ah}.

    *Note that choosing (l)argest or (a)verage will shift crops as necessary.
  `;
  }
  Tooltips.zoomPanToPanOnlyTooltip = zoomPanToPanOnlyTooltip;
  Tooltips.loopTooltip = (0, _commonTags.stripIndent)`
    Enable one of the special loop behaviors.
    fwrev loops will play the video normally once, then immediately play it in reverse.
    fade loops will crossfade the end of the video into the start of the video.
    fade loops can make short clips easier on the eyes and reduce the perceived jerkiness when the video repeats.
    `;
  Tooltips.fadeDurationTooltip = (0, _commonTags.stripIndent)`
    The duration to cut from the beginning and end of the output video to produce the crossfade for fade loops.
    Will be clamped to a minimum of 0.1 seconds and a maximum of 40% of the output clip duration.
    Only applicable when loop is set to fade.
    `;
})(Tooltips || (exports.Tooltips = Tooltips = {}));
},{"common-tags":"Gk7m"}],"XbmT":[function(require,module,exports) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.prevSelectedMarkerPairIndex = exports.player = exports.markerPairs = exports.isCropChartLoopingOn = void 0;
exports.triggerCropChartLoop = triggerCropChartLoop;
exports.video = void 0;
var _chart = require("chart.js");
var _util = require("./util/util");
var _commonTags = require("common-tags");
var _d3Ease = require("d3-ease");
var _fileSaver = require("file-saver");
var _fs = require("fs");
var _immer = require("immer");
var _jszip = _interopRequireDefault(require("jszip"));
var _lodash = _interopRequireDefault(require("lodash.clonedeep"));
var _misc = require("./actions/misc");
var _crop = require("./crop/crop");
var _common = require("./platforms/blockers/common");
var _youtube = require("./platforms/blockers/youtube");
var _platforms = require("./platforms/platforms");
require("./ui/chart/chart.js-drag-data-plugin");
var _chartutil = require("./ui/chart/chartutil");
var _cropChartSpec = require("./ui/chart/cropchart/cropChartSpec");
var _scatterChartSpec = require("./ui/chart/scatterChartSpec");
var _speedChartSpec = require("./ui/chart/speedchart/speedChartSpec");
var _css = require("./ui/css/css");
var _tooltips = require("./ui/tooltips");
var _undoredo = require("./util/undoredo");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
// BANNER GUARD
// ==UserScript==
// BANNER GUARD
// @locale       english
// @name         yt_clipper
// @version      5.24.0
// @version      5.24.0
// @description  Mark up YouTube videos and quickly generate clipped webms.
// @author       elwm
// @namespace    https://github.com/exwm
// @homepage     https://github.com/exwm/yt_clipper
// @supportURL   https://github.com/exwm/yt_clipper/issues
// @downloadURL  https://openuserjs.org/src/scripts/elwm/yt_clipper.user.js
// @updateURL    https://openuserjs.org/meta/elwm/yt_clipper.meta.js
// @icon         https://raw.githubusercontent.com/exwm/yt_clipper/master/assets/image/pepe-clipper.gif
// @license      MIT
// @require      https://cdn.jsdelivr.net/npm/jszip@3.4.0/dist/jszip.min.js
// @require      https://rawcdn.githack.com/exwm/Chart.js/141fe542034bc127b0a932de25d0c4f351f3bce1/dist/Chart.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js
// @require      https://rawcdn.githack.com/exwm/chartjs-plugin-zoom/b1adf6115d5816cabf0d82fba87950a32f7f965e/dist/chartjs-plugin-zoom.min.js
// @require      https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@0.7.0/dist/chartjs-plugin-datalabels.min.js
// @require      https://cdn.jsdelivr.net/npm/chartjs-plugin-style@latest/dist/chartjs-plugin-style.min.js
// @require      https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation@0.5.7/chartjs-plugin-annotation.min.js
// @require      https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js
// @run-at       document-end
// @match        http*://*.youtube.com/*
// @match        http*://*.vlive.tv/video/*
// @match        http*://*.vlive.tv/post/*
// @match        http*://weverse.io/*
// @match        https*://tv.naver.com/*
// @match        https*://*.afreecatv.com/*
// @noframes
// dummy grant to enable sandboxing
// @grant         GM_getValue
// BANNER GUARD
// ==/UserScript==
// BANNER GUARD
const __version__ = '5.24.0';
const ytClipperCSS = ":root {\n  --lighter-grey: rgb(235, 235, 235);\n  --light-grey: rgb(210, 210, 210);\n  --med-light-grey: rgb(160, 160, 160);\n  --med-grey: rgb(110, 110, 110);\n  --med-dark-grey: rgb(90, 90, 90);\n  --dark-grey: rgb(40, 40, 40);\n  --darker-grey: rgb(10, 10, 10);\n  --bright-red: rgb(255, 0, 0);\n  --dark-red: rgb(50, 0, 0);\n  --marker-pair-editor-accent: rgb(245, 118, 0);\n  --global-editor-accent: rgb(245, 0, 0);\n  --inherited-accent: dimgrey;\n}\n\n@keyframes valid-input {\n  0% {\n    background-color: tomato;\n  }\n  100% {\n    background-color: lightgreen;\n  }\n}\n\n@keyframes invalid-input {\n  0% {\n    background-color: lightgreen;\n  }\n  100% {\n    background-color: tomato;\n  }\n}\n\n@keyframes flash {\n  0% {\n    opacity: 1;\n  }\n  100% {\n    opacity: 0;\n  }\n}\n\n.msg-div,\n.long-msg-div,\n.long-msg-div input:not([type='file']) {\n  display: inline-block;\n  margin: 4px;\n  padding: 4px;\n  color: var(--light-grey);\n  background: var(--dark-grey);\n  box-shadow: 2px 2px 3px 0px var(--darker-grey);\n  border-radius: 2px;\n  border-color: var(--med-grey);\n  border-width: 1px 0px 0px 1px;\n  font-size: 10pt;\n  font-weight: 500;\n}\n\n.long-msg-div {\n  display: block;\n}\n\n.flash-div {\n  animation-name: flash;\n  animation-duration: 5s;\n  animation-fill-mode: forwards;\n}\n\n.marker {\n  width: 1px;\n  height: 14px;\n}\n\n.start-marker {\n  fill: lime;\n  pointer-events: none;\n}\n\n.end-marker {\n  fill: gold;\n  pointer-events: visibleFill;\n}\n\n.end-marker:hover {\n  fill: var(--bright-red);\n}\n\n.selected-marker-overlay {\n  fill: black;\n  width: 1px;\n  height: 8px;\n  pointer-events: none;\n}\n.selected-marker-overlay-hidden {\n  fill-opacity: 0.3;\n}\n\n#settings-editor-div {\n  display: flex;\n}\n\n.settings-editor-panel {\n  display: inline;\n  flex-grow: 1;\n  color: var(--med-grey);\n  font-size: 12pt;\n  margin: 3px;\n  padding: 2px 6px 0px 4px;\n  border: 2px solid var(--med-grey);\n  border-radius: 5px;\n  background: var(--dark-red);\n}\n\n.settings-editor-panel > legend {\n  display: block;\n  width: fit-content;\n  font-size: 12pt;\n  text-shadow: -1px -1px 0 black, 1px -1px black, -1px 1px 0 black, 1px 1px 0 black,\n    -1px 0px 0px black, 0px -1px 0px black, 1px 0px 0px black, 0px 1px 0px black;\n  padding: 0px 4px 0px 4px;\n  margin-left: 12px;\n}\n\n.settings-editor-input-div {\n  display: inline-block;\n  color: grey;\n  font-size: 11.5pt;\n  margin: 0px 0px 6px 2px;\n  padding: 4px 10px 4px 4px;\n  background: var(--dark-grey);\n  box-shadow: 2px 2px 3px 0px var(--darker-grey);\n  border-radius: 2px;\n  border-color: var(--med-grey);\n  border-style: solid;\n  border-width: 1px 0px 0px 1px;\n  vertical-align: top;\n}\n\n.settings-editor-input-div span {\n  display: block;\n  color: var(--light-grey);\n  margin-bottom: 2px;\n}\n\n.settings-editor-input-div select option {\n  color: black;\n}\n\n.settings-editor-input-div select option:first-child {\n  color: dimgrey;\n}\n\n.multi-input-div {\n  display: inline-flex;\n  width: fit-content;\n}\n\n.settings-editor-input-div > div {\n  display: inline-block;\n  margin-right: 6px;\n}\n.settings-editor-input-div > div:last-of-type {\n  margin-right: -4px;\n}\n\n.settings-editor-input-div select,\n.settings-editor-input-div input:not([type='radio']),\n#marker-pair-number-input {\n  display: block;\n  font-weight: bold;\n  background: var(--lighter-grey);\n  width: 100%;\n  border: none;\n  box-shadow: #151515 2px 2px 2px 0px;\n}\n\n.settings-editor-input-div input:not([type='radio']),\n#marker-pair-number-input {\n  border-radius: 5px;\n  padding-right: 4px;\n  padding-left: 2px;\n}\n\n.settings-info-display span,\n#global-settings-rotate label,\n#merge-list-div input,\n#global-settings-rotate input,\n#marker-pair-number-input {\n  display: inline;\n  width: auto;\n}\n\n#marker-pair-number-input {\n  vertical-align: top;\n  border: 1px solid black;\n}\n\n.settings-editor-input-div input:valid,\n#marker-pair-number-input:valid {\n  animation-name: valid-input;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n}\n\n.settings-editor-input-div input:invalid,\n#marker-pair-number-input:invalid {\n  animation-name: invalid-input;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n}\n\n.marker-pair-settings-editor-highlighted-div {\n  border: 2px solid var(--marker-pair-editor-accent) !important;\n}\n\n.global-settings-editor-highlighted-div {\n  border: 2px solid var(--global-editor-accent) !important;\n}\n\n.marker-pair-settings-editor-highlighted-label {\n  color: var(--marker-pair-editor-accent) !important;\n}\n\n.global-settings-editor-highlighted-label {\n  color: var(--global-editor-accent) !important;\n}\n\n.inherited-settings-highlighted-label {\n  color: var(--inherited-accent);\n}\n\n#markers-svg,\n#selected-marker-pair-overlay,\n#start-marker-numberings,\n#end-marker-numberings {\n  font-size: 6.5pt;\n  width: 100%;\n  height: 300%;\n  top: -5px;\n  position: absolute;\n  z-index: 99;\n  paint-order: stroke;\n  stroke: rgba(0, 0, 0, 0.25);\n  stroke-width: 2px;\n}\n\n#selected-marker-pair-overlay {\n  pointer-events: none;\n}\n\n#start-marker-numberings {\n  top: -19px;\n}\n\n#end-marker-numberings {\n  top: 5px;\n}\n\n.markerNumbering {\n  pointer-events: visibleFill;\n  user-select: none;\n}\n.markerNumbering:hover {\n  fill: var(--bright-red);\n  cursor: pointer;\n}\n\n.startMarkerNumbering {\n  fill: lime;\n}\n\n.endMarkerNumbering {\n  fill: gold;\n}\n\n#crop-div {\n  pointer-events: none;\n  z-index: 10;\n}\n\n#begin-crop-preview-div {\n  pointer-events: none;\n  z-index: 11;\n}\n\n#crop-svg,\n#begin-crop-preview-svg {\n  width: 100%;\n  height: 100%;\n  top: 0px;\n  position: absolute;\n}\n\n#cropChartCanvas {\n  background-color: rgb(24, 24, 24);\n}\n\n#shortcutsTableToggleButton {\n  cursor: help;\n  position: relative;\n  float: left;\n}\n";
const shortcutsTableHTML = "<h2>Basic Features</h2>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">Marker Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class=\"essential-row\">\n      <td>Toggle shortcuts on/off</td>\n      <td><kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Add marker at current time</td>\n      <td><kbd>A</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Toggle targeted end marker's editor</td>\n      <td><kbd>Shift</kbd> + <kbd>Mouseover</kbd></td>\n    </tr>\n    <tr>\n      <td>Jump to marker numbering and open marker pair's editor</td>\n      <td><kbd>Click</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle marker pair overrides editor</td>\n      <td><kbd>Shift</kbd> + <kbd>W</kbd></td>\n    </tr>\n    <tr>\n      <td>Duplicate selected or previously selected marker pair</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Undo/redo last marker</td>\n      <td><kbd>Z</kbd> / <kbd>Shift</kbd> + <kbd>Z</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Delete selected marker pair</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Z</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Move start/end marker to current time</td>\n      <td><kbd>Shift</kbd> + <kbd>Q</kbd>/<kbd>A</kbd></td>\n    </tr>\n    <tr>\n      <td>Drag start/end marker numbering to new time</td>\n      <td><kbd>Alt</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr>\n      <td>Move start/end marker a frame when on left/right half of window</td>\n      <td><kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Mousewheel</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Undo/redo time, speed, and crop changes of selected pair</td>\n      <td><kbd>Alt</kbd> + <kbd>Z</kbd> / <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Z</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle marker pair selection</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Up</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle auto-hiding unselected marker pairs</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Down</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Jump to nearest next/previous marker</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Left</kbd>/<kbd>Right</kbd></td>\n    </tr>\n    <tr>\n      <td>Select next/previous marker pair</td>\n      <td><kbd>Alt</kbd> + <kbd>Left</kbd>/<kbd>Right</kbd></td>\n    </tr>\n    <tr>\n      <td>Select next/previous marker pair and jump to start marker</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Left</kbd>/<kbd>Right</kbd></td>\n    </tr>\n  </tbody>\n</table>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">Global Settings Editor Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class=\"essential-row\">\n      <td>Toggle global settings editor</td>\n      <td><kbd>W</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle encoding settings editor</td>\n      <td><kbd>Shift</kbd> + <kbd>W</kbd></td>\n    </tr>\n    <tr>\n      <td>Update all markers to default new marker speed/crop</td>\n      <td><kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Q</kbd>/<kbd>X</kbd></td>\n    </tr>\n  </tbody>\n</table>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">Cropping Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class=\"essential-row\">\n      <td>Begin drawing crop</td>\n      <td><kbd>X</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Draw crop</td>\n      <td><kbd>Click</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Move or resize crop</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Click</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Cycle crop dim opacity up by 0.25</td>\n      <td><kbd>Ctrl</kbd> + <kbd>X</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle crop crosshair</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>X</kbd></td>\n    </tr>\n    <tr>\n      <td>Crop-aspect-ratio-locked resize/draw of crop</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr>\n      <td>Center-out resize/draw of crop</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr>\n      <td>Horizontally-fixed (Y-only) drag of crop</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr>\n      <td>Vertically-fixed (X-only) drag of crop</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle crop adjustment with arrow keys</td>\n      <td><kbd>Alt</kbd> + <kbd>X</kbd></td>\n    </tr>\n    <tr>\n      <td>Adjust crop input string with arrow keys</td>\n      <td><pre>Place cursor on target value</pre></td>\n    </tr>\n    <tr>\n      <td>Change crop change amount from 10 to 1/50/100</td>\n      <td><kbd>Alt</kbd> / <kbd>Shift</kbd> / <kbd>Alt</kbd> + <kbd>Shift</kbd></td>\n    </tr>\n    <tr>\n      <td>Modify crop width/height instead of x/y offset</td>\n      <td><kbd>Ctrl</kbd> + <kbd>ArrowKey</kbd></td>\n    </tr>\n  </tbody>\n</table>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">Preview Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class=\"essential-row\">\n      <td>Seek video frame by frame</td>\n      <td><kbd>&lt;</kbd> / <kbd>&gt;</kbd> or <kbd>Shift</kbd> + <kbd>Mousewheel</kbd></td>\n    </tr>\n    <tr>\n      <td>Scrub video time left or right</td>\n      <td><kbd>Alt</kbd> + <kbd>Click</kbd> + <kbd> Drag</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Toggle previewing speed</td>\n      <td><kbd>C</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Toggle auto marker pair looping</td>\n      <td><kbd>Shift</kbd> + <kbd>C</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle auto crop chart section looping</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle previewing gamma</td>\n      <td><kbd>Alt</kbd> + <kbd>C</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle previewing fade loop</td>\n      <td><kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Toggle all previews</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle force setting video speed</td>\n      <td><kbd>Q</kbd></td>\n    </tr>\n    <tr>\n      <td>Cycle force set video speed value down by 0.25</td>\n      <td><kbd>Alt</kbd> + <kbd>Q</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle previewing rotation 90&deg; clockwise/anti-clockwise</td>\n      <td><kbd>R</kbd> / <kbd>Alt</kbd> + <kbd>R</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle big video preview thumbnails</td>\n      <td><kbd>Shift</kbd> + <kbd>R</kbd></td>\n    </tr>\n  </tbody>\n</table>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">Frame Capturer Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>Capture frame</td>\n      <td><kbd>E</kbd></td>\n    </tr>\n    <tr>\n      <td>Zip and download captured frames</td>\n      <td><kbd>Alt</kbd> + <kbd>E</kbd></td>\n    </tr>\n  </tbody>\n</table>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">Saving and Loading Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class=\"essential-row\">\n      <td>Save markers data as json</td>\n      <td><kbd>S</kbd></td>\n    </tr>\n    <tr>\n      <td>Copy markers data to clipboard</td>\n      <td><kbd>Alt</kbd> + <kbd>S</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle markers data commands (loading, restoring, and clearing).</td>\n      <td><kbd>G</kbd></td>\n    </tr>\n  </tbody>\n</table>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">Miscellaneous Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>Flatten VR Video</td>\n      <td><kbd>Shift</kbd> + <kbd>F</kbd></td>\n    </tr>\n  </tbody>\n  <tbody>\n    <tr>\n      <td>Open YouTube subtitles editor (supports creating, downloading and uploading).</td>\n      <td><kbd>Alt</kbd> + <kbd>F</kbd></td>\n    </tr>\n  </tbody>\n</table>\n\n<h2>Advanced Features</h2>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">General Chart Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class=\"essential-row\">\n      <td>Add point</td>\n      <td><kbd>Shift</kbd> + <kbd>Click</kbd></td>\n    </tr>\n    <tr>\n      <td>Add point at current time.</td>\n      <td><kbd>Alt</kbd> + <kbd>A</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Delete point</td>\n      <td><kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Click</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Move point or pan chart</td>\n      <td><kbd>Click</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr>\n      <td>Zoom in and out</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Mousewheel</kbd></td>\n    </tr>\n    <tr>\n      <td>Reset zoom</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Click</kbd></td>\n    </tr>\n    <tr>\n      <td>Seek to time on time-axis</td>\n      <td><kbd>Right-Click</kbd></td>\n    </tr>\n    <tr>\n      <td>Set chart loop start/end marker</td>\n      <td><kbd>Shift</kbd>/<kbd>Alt</kbd> + <kbd>Right-click</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle chart marker looping</td>\n      <td><kbd>Shift</kbd> + <kbd>D</kbd></td>\n    </tr>\n  </tbody>\n</table>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">Speed Chart Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class=\"essential-row\">\n      <td>Toggle speed chart</td>\n      <td><kbd>D</kbd></td>\n    </tr>\n  </tbody>\n</table>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">Crop Chart Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class=\"essential-row\">\n      <td>Toggle crop chart</td>\n      <td><kbd>Alt</kbd> + <kbd>D</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Toggle auto crop chart section looping</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd></td>\n    </tr>\n    <tr class=\"essential-row\">\n      <td>Select point as start/end of crop section</td>\n      <td><kbd>Ctrl/Alt</kbd> + <kbd>Mouseover</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle start/end mode. If in end mode also select prev point</td>\n      <td><kbd>Alt</kbd> + <kbd>Mousewheel Down</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle start/end mode. If in start mode also select next point</td>\n      <td><kbd>Alt</kbd> + <kbd>Mousewheel Up</kbd></td>\n    </tr>\n    <tr>\n      <td>Set current point's crop to next/prev point's crop</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Mousewheel Up/Down</kbd></td>\n    </tr>\n    <tr>\n      <td>Toggle crop point ease in between auto and instant</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Click</kbd></td>\n    </tr>\n    <tr>\n      <td>\n        Set target crop component of all points following/preceding selected point. Select crop\n        component with cursor in crop input field.\n      </td>\n      <td>\n        <pre><kbd>a</kbd> / <kbd>Shift</kbd> + <kbd>A</kbd></pre>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"2\">ZoomPan Mode Crop Chart Shortcuts</th>\n    </tr>\n    <tr>\n      <th>Action</th>\n      <th>Shortcut</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>Crop-aspect-ratio-locked resize of crop</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr>\n      <td>Freely resize crop</td>\n      <td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr>\n      <td>Crop-aspect-ratio-locked draw crop</td>\n      <td><kbd>X</kbd>, <kbd>Click</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n    <tr>\n      <td>Freely draw crop</td>\n      <td><kbd>X</kbd>, <kbd>Alt</kbd> + <kbd>Click</kbd> + <kbd>Drag</kbd></td>\n    </tr>\n  </tbody>\n</table>\n";
const shortcutsTableStyle = ":root {\n  --lighter-grey: rgb(235, 235, 235);\n  --light-grey: rgb(210, 210, 210);\n  --med-grey: rgb(110, 110, 110);\n  --dark-grey: rgb(40, 40, 40);\n  --darker-grey: rgb(10, 10, 10);\n  --bright-red: rgb(237, 28, 63);\n  --marker-pair-editor-accent: rgb(245, 118, 0);\n  --essential-red: rgb(200, 40, 40);\n}\n\n#shortcutsTableContainer {\n  text-align: center;\n  position: relative;\n}\n\n#shortcutsTableContainer table {\n  text-align: left;\n  line-height: 32px;\n  border-collapse: separate;\n  border-spacing: 0;\n  border: 2px solid var(--bright-red);\n  width: 640px;\n  margin: 8px auto;\n  border-radius: 0.25rem;\n  font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n  font-size: 13px;\n  font-weight: 500;\n  background-color: #222;\n  color: #ddd;\n}\n\n#shortcutsTableContainer thead tr:first-child {\n  background: var(--bright-red);\n  color: var(--lighter-grey);\n  border: none;\n}\n\n#shortcutsTableContainer thead tr:nth-child(2) {\n  text-align: center;\n}\n\n#shortcutsTableContainer tr:nth-child(even) {\n  background-color: #333;\n}\n\n#shortcutsTableContainer th:first-child,\ntd:first-child {\n  padding: 0 5px;\n}\n\n#shortcutsTableContainer th {\n  font-size: 14px;\n  font-weight: 600;\n}\n\n#shortcutsTableContainer thead tr:last-child th {\n  border-bottom: 3px solid #777;\n}\n\n#shortcutsTableContainer tbody tr:hover {\n  background-color: #444;\n}\n\n#shortcutsTableContainer tbody tr:first-child {\n  border-bottom: 1px solid #666;\n  border-right: 2px solid #666;\n}\n\n#shortcutsTableContainer td {\n  border-bottom: 1px solid #666;\n}\n\n#shortcutsTableContainer td:first-child {\n  border-right: 2px solid #666;\n}\n\n#shortcutsTableContainer td:last-child {\n  text-align: right;\n  padding-right: 5px;\n}\n\n#shortcutsTableContainer kbd {\n  border: 1px solid #999;\n  border-radius: 2px;\n  font-weight: bold;\n  padding: 2px 4px;\n  margin: 1px;\n  background-color: #e0e0e0;\n  color: #333;\n}\n\n#shortcutsTableContainer tr.essential-row {\n  color: var(--bright-red);\n}\n\n#shortcutsTableContainer h2 {\n  color: var(--lighter-grey);\n  margin: 10px;\n}\n";
const shortcutsTableToggleButtonHTML = "<button\n  id=\"shortcutsTableToggleButton\"\n  class=\"ytp-button\"\n  title=\"Toggle yt_clipper Shortcuts Table\"\n>\n  <svg version=\"1.1\" viewbox=\"0 0 40 40\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path\n      d=\"M19.66 21.528l5.425 3.972c0.447 0.335 1.26 0.599 1.818 0.599h3.149l-13.319-9.773c0.073-0.27 0.116-0.579 0.116-0.899 0-1.964-1.592-3.556-3.556-3.556s-3.556 1.592-3.556 3.556c0 1.964 1.592 3.556 3.556 3.556 0.759 0 1.462-0.237 2.039-0.643l-0.011 0.008 2.265 1.656-2.265 1.656c-0.568-0.403-1.276-0.644-2.040-0.644-1.964 0-3.556 1.592-3.556 3.556s1.592 3.556 3.556 3.556c1.964 0 3.556-1.592 3.556-3.556 0-0.316-0.041-0.623-0.119-0.915l0.005 0.025 2.946-2.154zM13.29 16.957c-0.841 0-1.524-0.682-1.524-1.524s0.682-1.524 1.524-1.524v0c0.841 0 1.524 0.682 1.524 1.524s-0.682 1.524-1.524 1.524v0zM13.29 26.1c-0.841 0-1.524-0.682-1.524-1.524s0.682-1.524 1.524-1.524v0c0.841 0 1.524 0.682 1.524 1.524s-0.682 1.524-1.524 1.524v0zM25.075 14.508c0.515-0.348 1.143-0.567 1.82-0.599l0.008-0h3.149l-7.619 5.587-2.083-1.524 4.724-3.464z\"\n      fill=\"#fff\"\n    ></path>\n  </svg>\n</button>\n";
let player = exports.player = void 0;
let video = exports.video = void 0;
let markerPairs = exports.markerPairs = [];
let prevSelectedMarkerPairIndex = exports.prevSelectedMarkerPairIndex = null;
let isCropChartLoopingOn = exports.isCropChartLoopingOn = false;
let shouldTriggerCropChartLoop = false;
function triggerCropChartLoop() {
  shouldTriggerCropChartLoop = true;
}
const platform = (0, _platforms.getPlatform)();
loadytClipper();
async function loadytClipper() {
  console.log('Loading yt_clipper markup script...');
  function hotkeys(e) {
    if (isHotkeysEnabled) {
      switch (e.code) {
        case 'KeyA':
          if (!e.ctrlKey && !e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            addMarker();
          } else if (!e.ctrlKey && e.shiftKey && !e.altKey && markerHotkeysEnabled) {
            (0, _util.blockEvent)(e);
            moveMarker(enableMarkerHotkeys.endMarker);
          } else if (!e.ctrlKey && !e.shiftKey && e.altKey && markerHotkeysEnabled) {
            (0, _util.blockEvent)(e);
            addChartPoint();
          } else if (e.ctrlKey && e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            duplicateSelectedMarkerPair();
          }
          break;
        case 'KeyS':
          if (!e.ctrlKey && !e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            saveMarkersAndSettings();
          } else if (!e.ctrlKey && e.altKey && !e.shiftKey) {
            (0, _util.blockEvent)(e);
            (0, _util.copyToClipboard)(getClipperInputJSON());
          }
          break;
        case 'KeyQ':
          if (!e.ctrlKey && !e.altKey && e.shiftKey && markerHotkeysEnabled) {
            (0, _util.blockEvent)(e);
            moveMarker(enableMarkerHotkeys.startMarker);
          } else if (!e.ctrlKey && !e.altKey && !e.shiftKey) {
            (0, _util.blockEvent)(e);
            toggleForceSetSpeed();
          } else if (!e.ctrlKey && e.altKey && !e.shiftKey) {
            (0, _util.blockEvent)(e);
            cycleForceSetSpeedValueDown();
          } else if (!e.ctrlKey && e.altKey && e.shiftKey) {
            (0, _util.blockEvent)(e);
            updateAllMarkerPairSpeeds(settings.newMarkerSpeed);
          }
          break;
        case 'KeyE':
          if (!e.ctrlKey && !e.altKey && !e.shiftKey) {
            (0, _util.blockEvent)(e);
            captureFrame();
          } else if (!e.ctrlKey && e.altKey && !e.shiftKey) {
            (0, _util.blockEvent)(e);
            saveCapturedFrames();
          }
          break;
        case 'KeyW':
          if (!e.ctrlKey && !e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            toggleGlobalSettingsEditor();
          } else if (!e.ctrlKey && e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            toggleMarkerPairOverridesEditor();
          }
          break;
        case 'KeyC':
          if (!e.ctrlKey && !e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            toggleMarkerPairSpeedPreview();
          } else if (!e.ctrlKey && e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            toggleMarkerPairLoop();
          } else if (!e.ctrlKey && !e.shiftKey && e.altKey) {
            (0, _util.blockEvent)(e);
            toggleGammaPreview();
          } else if (!e.ctrlKey && e.shiftKey && e.altKey) {
            (0, _util.blockEvent)(e);
            toggleFadeLoopPreview();
          } else if (e.ctrlKey && e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            toggleCropChartLooping();
          } else if (e.ctrlKey && e.shiftKey && e.altKey) {
            (0, _util.blockEvent)(e);
            toggleAllPreviews();
          }
          break;
        case 'KeyG':
          if (!e.ctrlKey && !e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            toggleMarkersDataCommands();
          }
          break;
        case 'KeyD':
          // alt+shift+D does not work in chrome 75.0.3770.100
          if (!e.ctrlKey && !e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            toggleChart(speedChartInput);
          } else if (!e.ctrlKey && e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            toggleChartLoop();
          } else if (!e.ctrlKey && !e.shiftKey && e.altKey) {
            (0, _util.blockEvent)(e);
            toggleChart(cropChartInput);
          }
          break;
        case 'KeyZ':
          if (!e.ctrlKey && !e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            undoMarker();
          } else if (!e.ctrlKey && e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            redoMarker();
          } else if (!e.ctrlKey && !e.shiftKey && e.altKey) {
            (0, _util.blockEvent)(e);
            undoRedoMarkerPairChange('undo');
          } else if (!e.ctrlKey && e.shiftKey && e.altKey) {
            (0, _util.blockEvent)(e);
            undoRedoMarkerPairChange('redo');
          } else if (e.ctrlKey && e.shiftKey && e.altKey && markerHotkeysEnabled) {
            (0, _util.blockEvent)(e);
            deleteMarkerPair();
          }
          break;
        case 'KeyX':
          if (!e.ctrlKey && !e.altKey && !e.shiftKey) {
            (0, _util.blockEvent)(e);
            drawCrop();
          } else if (!e.ctrlKey && e.altKey && !e.shiftKey) {
            (0, _util.blockEvent)(e);
            toggleArrowKeyCropAdjustment();
          } else if (!e.ctrlKey && e.altKey && e.shiftKey) {
            (0, _util.blockEvent)(e);
            updateAllMarkerPairCrops(settings.newMarkerCrop);
          } else if (e.ctrlKey && !e.altKey && !e.shiftKey) {
            (0, _util.blockEvent)(e);
            cycleCropDimOpacity();
          } else if (e.ctrlKey && !e.altKey && e.shiftKey) {
            (0, _util.blockEvent)(e);
            toggleCropCrossHair();
          }
          break;
        case 'KeyR':
          if (!e.ctrlKey && !e.shiftKey && !e.altKey && isTheatreMode()) {
            (0, _util.blockEvent)(e);
            rotateVideo('clock');
          } else if (!e.ctrlKey && !e.shiftKey && e.altKey && isTheatreMode()) {
            (0, _util.blockEvent)(e);
            rotateVideo('cclock');
          } else if (!e.ctrlKey && e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            toggleBigVideoPreviews();
          } else if (!e.ctrlKey && !e.shiftKey && !isTheatreMode()) {
            (0, _util.blockEvent)(e);
            (0, _util.flashMessage)('Please switch to theater mode to rotate video.', 'red');
          }
          break;
        case 'KeyF':
          if (!e.ctrlKey && e.shiftKey && !e.altKey) {
            (0, _util.blockEvent)(e);
            (0, _misc.flattenVRVideo)(hooks.videoContainer, video);
          } else if (!e.ctrlKey && !e.shiftKey && e.altKey) {
            (0, _util.blockEvent)(e);
            (0, _misc.openSubsEditor)(settings.videoID);
          }
          break;
        case 'ArrowLeft':
        case 'ArrowRight':
          jumpToNearestMarkerOrPair(e, e.code);
          break;
        case 'ArrowUp':
          if (e.ctrlKey && !arrowKeyCropAdjustmentEnabled) {
            (0, _util.blockEvent)(e);
            togglePrevSelectedMarkerPair();
          }
          break;
        case 'ArrowDown':
          toggleAutoHideUnselectedMarkerPairs(e);
          break;
      }
    }
    if (!e.ctrlKey && e.shiftKey && e.altKey && e.code === 'KeyA') {
      isHotkeysEnabled = !isHotkeysEnabled;
      initOnce();
      if (isHotkeysEnabled) {
        showShortcutsTableToggleButton();
        (0, _common.enableCommonBlockers)();
        if (platform === _platforms.VideoPlatforms.youtube) {
          (0, _youtube.enableYTBlockers)();
        }
        (0, _util.flashMessage)('Enabled Hotkeys', 'green');
      } else {
        hideShortcutsTableToggleButton();
        (0, _common.disableCommonBlockers)();
        if (platform === _platforms.VideoPlatforms.youtube) {
          (0, _youtube.disableYTBlockers)();
        }
        (0, _util.flashMessage)('Disabled Hotkeys', 'red');
      }
    }
  }
  let start = true;
  let markerHotkeysEnabled = false;
  let isSettingsEditorOpen = false;
  let wasGlobalSettingsEditorOpen = false;
  let isCropOverlayVisible = false;
  let isCurrentChartVisible = false;
  let markerPairsHistory = [];
  let startTime = 0.0;
  let isHotkeysEnabled = false;
  let prevSelectedEndMarker = null;
  const initOnce = (0, _util.once)(init, this);
  function init() {
    //immer
    (0, _immer.enableAllPlugins)();
    //yt-clipper
    (0, _util.injectCSS)(ytClipperCSS, 'yt-clipper-css');
    (0, _util.injectCSS)(_platforms.videoPlatformDataRecords[platform].css, 'platform-css');
    initHooks();
    initVideoInfo();
    initObservers();
    initMarkersContainer();
    initChartHooks();
    addForeignEventListeners();
    injectToggleShortcutsTableButton();
    addCropMouseManipulationListener();
    addScrubVideoHandler();
    loopMarkerPair();
  }
  let autoSaveIntervalId;
  function initAutoSave() {
    if (autoSaveIntervalId == null) {
      (0, _util.flashMessage)('Initializing auto saving of markers data to local storage...', 'olive');
      autoSaveIntervalId = setInterval(() => {
        saveClipperInputDataToLocalStorage();
      }, 5000);
    }
  }
  const localStorageKeyPrefix = 'yt_clipper';
  function saveClipperInputDataToLocalStorage() {
    const date = Date.now(); /*  */
    const key = `${localStorageKeyPrefix}_${settings.videoTag}`;
    const data = getClipperInputData(date);
    try {
      localStorage.setItem(key, JSON.stringify(data, null, 2));
    } catch (e) {
      if (e instanceof DOMException && e.code == DOMException.QUOTA_EXCEEDED_ERR) {
        const markersDataFiles = getMarkersDataEntriesFromLocalStorage();
        (0, _util.flashMessage)(`Failed to save markers data.
          Browser local storage quota exceeded with ${markersDataFiles === null || markersDataFiles === void 0 ? void 0 : markersDataFiles.length} markers data files.
          Try clearing auto-saved markers data after backing it up (see marker data commands menu (shortcut: G).`, 'red', 4500);
      } else {
        (0, _util.flashMessage)(`Failed to save markers data. Error: ${e}`, 'red');
      }
    }
  }
  function loadClipperInputDataFromLocalStorage() {
    if (markerPairs.length === 0) {
      const key = `${localStorageKeyPrefix}_${settings.videoTag}`;
      const clipperInputJSON = localStorage.getItem(key);
      if (clipperInputJSON != null) {
        const clipperInputData = JSON.parse(clipperInputJSON);
        const date = new Date(clipperInputData.date);
        const confirmLoad = confirm((0, _commonTags.stripIndent)`
        The last auto-saved markers data for video ${settings.videoTag} will be restored.
        This data was saved on ${date}.
        It contains ${clipperInputData.markerPairs.length} marker pair(s).\n
        Proceed to restore markers data?
      `);
        if (confirmLoad) {
          loadClipperInputJSON(clipperInputJSON);
          deleteMarkersDataCommands();
        }
      } else {
        (0, _util.flashMessage)(`No markers data found in local storage for video ${settings.videoTag}.`, 'red');
      }
    } else {
      (0, _util.flashMessage)('Please delete all marker pairs before restoring markers data.', 'red');
    }
  }
  function getMarkersDataEntriesFromLocalStorage() {
    const entries = Object.entries(localStorage).map(x => x[0]).filter(x => x.startsWith(localStorageKeyPrefix));
    return entries;
  }
  function clearYTClipperLocalStorage() {
    const entries = getMarkersDataEntriesFromLocalStorage();
    const nEntries = entries.length;
    const clearAll = confirm((0, _commonTags.stripIndent)`
      The following markers data files will be cleared from local storage:
      ${entries.map(entry => entry.replace(localStorageKeyPrefix + '_', '')).join(', ')}\n
      Proceed to clear all (${nEntries}) markers data files from local storage?
    `);
    if (clearAll) {
      entries.map(x => localStorage.removeItem(x));
      (0, _util.flashMessage)(`Cleared ${nEntries} markers data files.`, 'olive');
    }
  }
  function downloadAutoSavedMarkersData() {
    const entries = Object.entries(localStorage).map(x => x[0]).filter(x => x.startsWith(localStorageKeyPrefix));
    const nEntries = entries.length;
    if (nEntries === 0) {
      (0, _util.flashMessage)('No markers data in local storage to zip.', 'olive');
      return;
    }
    (0, _util.flashMessage)(`Zipping ${nEntries} markers data files.`, 'olive');
    const now = new Date();
    const zip = new _jszip.default();
    const markersZipFolderName = 'yt_clipper_markers_data_' + now.toISOString();
    const markersZip = zip.folder(markersZipFolderName);
    entries.forEach(entry => {
      markersZip.file(entry.replace(localStorageKeyPrefix, '') + '.json', localStorage.getItem(entry), {
        binary: false
      });
    });
    const progressDiv = injectProgressBar('green', 'Markers Data');
    const progressSpan = progressDiv.firstElementChild;
    zip.generateAsync({
      type: 'blob'
    }, metadata => {
      const percent = metadata.percent.toFixed(2) + '%';
      progressSpan.textContent = `Markers Data Zipping Progress: ${percent}`;
    }).then(blob => {
      (0, _fileSaver.saveAs)(blob, markersZipFolderName + '.zip');
      progressDiv.dispatchEvent(new Event('done'));
    });
  }
  function addEventListeners() {
    document.addEventListener('keydown', hotkeys, true);
    document.addEventListener('keydown', addCropHoverListener, true);
    document.addEventListener('keyup', removeCropHoverListener, true);
    document.body.addEventListener('wheel', mouseWheelFrameSkipHandler);
    document.body.addEventListener('wheel', moveMarkerByFrameHandler);
    document.body.addEventListener('wheel', selectCropPoint, {
      passive: false
    });
    document.body.addEventListener('wheel', inheritCropPointCrop, {
      passive: false
    });
  }
  const selectors = _platforms.videoPlatformDataRecords[platform].selectors;
  exports.player = player = await (0, _util.retryUntilTruthyResult)(() => document.querySelector(selectors.player));
  exports.video = video = await (0, _util.retryUntilTruthyResult)(() => player.querySelector(selectors.video));
  await (0, _util.retryUntilTruthyResult)(() => video.readyState != 0);
  await (0, _util.retryUntilTruthyResult)(() => video.videoWidth * video.videoHeight * (0, _util.getVideoDuration)(platform, video));
  if (platform === 'vlive') {
    await (0, _util.retryUntilTruthyResult)(() => !video.src.startsWith('data:video'));
    await (0, _util.retryUntilTruthyResult)(() => video.videoWidth * video.videoHeight * (0, _util.getVideoDuration)(platform, video));
  }
  video.classList.add('yt-clipper-video');
  let settingsEditorHook;
  let hooks = {};
  function initHooks() {
    hooks = (0, _platforms.getVideoPlatformHooks)(selectors);
    (0, _util.setFlashMessageHook)(hooks.flashMessage);
    updateSettingsEditorHook();
    hooks.progressBar.removeAttribute('draggable');
  }
  function isTheatreMode() {
    if (platform === _platforms.VideoPlatforms.youtube) {
      return hooks.theaterModeIndicator.theater;
    }
  }
  const videoInfo = {};
  function initVideoInfo() {
    var _a, _b, _c, _d, _e, _f;
    videoInfo.aspectRatio = video.videoWidth / video.videoHeight;
    videoInfo.isVerticalVideo = videoInfo.aspectRatio <= 1;
    const url = window.location.origin + window.location.pathname;
    videoInfo.videoUrl = url;
    videoInfo.fps = getFPS();
    video.seekTo = time => video.currentTime = time;
    video.getCurrentTime = () => {
      return video.currentTime;
    };
    if (platform === _platforms.VideoPlatforms.youtube) {
      const playerData = player.getVideoData();
      videoInfo.id = playerData.video_id;
      videoInfo.videoUrl += '?v=' + videoInfo.id;
      videoInfo.title = playerData.title;
      video.seekTo = time => player.seekTo(time);
    } else if (platform === _platforms.VideoPlatforms.vlive) {
      const location = window.location;
      const preloadedState = unsafeWindow.__PRELOADED_STATE__;
      const videoParams = (_b = (_a = preloadedState === null || preloadedState === void 0 ? void 0 : preloadedState.postDetail) === null || _a === void 0 ? void 0 : _a.post) === null || _b === void 0 ? void 0 : _b.officialVideo;
      videoInfo.id = videoParams === null || videoParams === void 0 ? void 0 : videoParams.videoSeq;
      videoInfo.title = videoParams === null || videoParams === void 0 ? void 0 : videoParams.title;
      if (location.pathname.includes('video')) {
        if (videoInfo.id == null) videoInfo.id = location.pathname.split('/')[2];
        if (videoInfo.title == null) videoInfo.title = (_c = document.querySelector('[class*="video_title"]')) === null || _c === void 0 ? void 0 : _c.textContent;
      }
    } else if (platform === _platforms.VideoPlatforms.naver_tv) {
      videoInfo.id = location.pathname.split('/')[2];
      videoInfo.title = (_d = document.querySelector('h2[class*=ArticleSection_article_title]')) === null || _d === void 0 ? void 0 : _d.textContent;
    } else if (platform === _platforms.VideoPlatforms.weverse) {
      videoInfo.title = (_e = document.querySelector('h2[class*=TitleView_title]')) === null || _e === void 0 ? void 0 : _e.textContent;
      if (location.pathname.includes('media') || location.pathname.includes('live')) {
        if (videoInfo.id == null) videoInfo.id = location.pathname.split('/')[3];
      }
    } else if (platform === _platforms.VideoPlatforms.afreecatv) {
      videoInfo.id = location.pathname.split('/')[2];
      videoInfo.title = (_f = document.querySelector('div[class~=broadcast_title]')) === null || _f === void 0 ? void 0 : _f.textContent;
      video.getCurrentTime = () => {
        return unsafeWindow.vodCore.playerController._playingTime;
      };
      video.seekTo = time => {
        unsafeWindow.vodCore.seek(time);
      };
    }
    if (videoInfo.id == null) {
      (0, _util.flashMessage)('Could not get video ID.', 'red');
      throw new Error('Could not get video ID.');
    }
  }
  function initObservers() {
    new ResizeObserver(resizeCropOverlay).observe(hooks.videoContainer);
    if (platform === _platforms.VideoPlatforms.afreecatv) {
      (0, _util.observeVideoElementChange)(hooks.videoContainer, addedNodes => {
        exports.video = video = addedNodes[0];
        video.classList.add('yt-clipper-video');
        initVideoInfo();
      });
    }
  }
  function updateSettingsEditorHook() {
    if (isTheatreMode()) {
      settingsEditorHook = hooks.settingsEditorTheater;
    } else {
      settingsEditorHook = hooks.settingsEditor;
    }
  }
  addEventListeners();
  function addCropHoverListener(e) {
    const isCropBlockingChartVisible = isCurrentChartVisible && currentChartInput && currentChartInput.type !== 'crop';
    if ((e.key === 'Control' || e.key === 'Meta') && isHotkeysEnabled && !e.repeat && isCropOverlayVisible && !isDrawingCrop && !isCropBlockingChartVisible) {
      document.addEventListener('pointermove', cropHoverHandler, true);
    }
  }
  function removeCropHoverListener(e) {
    if (e.key === 'Control' || e.key === 'Meta') {
      document.removeEventListener('pointermove', cropHoverHandler, true);
      showPlayerControls();
      hooks.cropMouseManipulation.style.removeProperty('cursor');
    }
  }
  function cropHoverHandler(e) {
    if (isSettingsEditorOpen && isCropOverlayVisible && !isDrawingCrop) {
      updateCropHoverCursor(e);
    }
  }
  function updateCropHoverCursor(e) {
    const cursor = getMouseCropHoverRegion(e);
    if (cursor) {
      hidePlayerControls();
      hooks.cropMouseManipulation.style.cursor = cursor;
    } else {
      showPlayerControls();
      hooks.cropMouseManipulation.style.removeProperty('cursor');
    }
  }
  function togglePrevSelectedMarkerPair() {
    if (enableMarkerHotkeys.endMarker) {
      toggleMarkerPairEditor(enableMarkerHotkeys.endMarker);
    } else if (prevSelectedEndMarker) {
      toggleMarkerPairEditor(prevSelectedEndMarker);
    } else {
      const firstEndMarker = markersSvg.firstElementChild ? markersSvg.firstElementChild.nextElementSibling : null;
      if (firstEndMarker) toggleMarkerPairEditor(firstEndMarker);
    }
  }
  function mouseWheelFrameSkipHandler(event) {
    if (isHotkeysEnabled && !event.ctrlKey && !event.altKey && event.shiftKey && Math.abs(event.deltaY) > 0) {
      let fps = getFPS();
      if (event.deltaY < 0) {
        (0, _util.seekBySafe)(video, 1 / fps);
      } else if (event.deltaY > 0) {
        (0, _util.seekBySafe)(video, -1 / fps);
      }
    }
  }
  function moveMarkerByFrameHandler(event) {
    if (isHotkeysEnabled && !event.ctrlKey && event.altKey && event.shiftKey && Math.abs(event.deltaY) > 0 && isSettingsEditorOpen && !wasGlobalSettingsEditorOpen && prevSelectedEndMarker) {
      const fps = getFPS();
      let targetMarker = prevSelectedEndMarker;
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      let targetMarkerTime = markerPair.end;
      if (event.pageX < window.innerWidth / 2) {
        targetMarker = prevSelectedEndMarker.previousElementSibling;
        targetMarkerTime = markerPair.start;
      }
      let newMarkerTime;
      if (event.deltaY > 0) {
        newMarkerTime = targetMarkerTime - 1 / fps;
        moveMarker(targetMarker, Math.max(0, newMarkerTime));
      } else if (event.deltaY < 0) {
        newMarkerTime = targetMarkerTime + 1 / fps;
        moveMarker(targetMarker, Math.min((0, _util.getVideoDuration)(platform, video), newMarkerTime));
      }
      video.pause();
      (0, _util.seekToSafe)(video, newMarkerTime);
    }
  }
  function selectCropPoint(e) {
    var _a;
    if (isHotkeysEnabled && !e.ctrlKey && e.altKey && !e.shiftKey) {
      (0, _util.blockEvent)(e);
    } else {
      return;
    }
    const cropChart = cropChartInput.chart;
    const cropChartData = cropChart.data.datasets[0].data;
    if (Math.abs(e.deltaY) > 0 && isSettingsEditorOpen && !wasGlobalSettingsEditorOpen && prevSelectedEndMarker && cropChartInput.chart) {
      if (e.deltaY < 0) {
        if (_cropChartSpec.currentCropChartMode === _cropChartSpec.cropChartMode.Start) {
          (0, _cropChartSpec.setCurrentCropPoint)(cropChart, _cropChartSpec.currentCropPointIndex + 1, _cropChartSpec.cropChartMode.End);
        } else {
          (0, _cropChartSpec.setCurrentCropPoint)(cropChart, _cropChartSpec.currentCropPointIndex, _cropChartSpec.cropChartMode.Start);
        }
      } else if (e.deltaY > 0) {
        if (_cropChartSpec.currentCropChartMode === _cropChartSpec.cropChartMode.End) {
          (0, _cropChartSpec.setCurrentCropPoint)(cropChart, _cropChartSpec.currentCropPointIndex - 1, _cropChartSpec.cropChartMode.Start);
        } else {
          (0, _cropChartSpec.setCurrentCropPoint)(cropChart, _cropChartSpec.currentCropPointIndex, _cropChartSpec.cropChartMode.End);
        }
      }
    }
    if (!isCropChartLoopingOn) {
      triggerCropChartLoop();
    }
    const cropPoint = cropChartData[_cropChartSpec.currentCropPointIndex];
    cropInput.value = cropPoint.crop;
    highlightSpeedAndCropInputs();
    if (isCurrentChartVisible && currentChartInput.type === 'crop') {
      (_a = currentChartInput === null || currentChartInput === void 0 ? void 0 : currentChartInput.chart) === null || _a === void 0 ? void 0 : _a.update();
    }
  }
  function inheritCropPointCrop(e) {
    if (isHotkeysEnabled && e.ctrlKey && e.altKey && e.shiftKey && Math.abs(e.deltaY) > 0 && isSettingsEditorOpen && !wasGlobalSettingsEditorOpen && prevSelectedEndMarker && cropChartInput.chart) {
      (0, _util.blockEvent)(e);
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      const cropMap = markerPair.cropMap;
      const cropPoint = cropMap[_cropChartSpec.currentCropPointIndex];
      const oldCrop = cropPoint.crop;
      let newCrop;
      if (e.deltaY < 0) {
        const nextCropPoint = cropMap[Math.min(_cropChartSpec.currentCropPointIndex + 1, cropMap.length - 1)];
        newCrop = nextCropPoint.crop;
      } else if (e.deltaY > 0) {
        const prevCropPoint = cropMap[Math.max(_cropChartSpec.currentCropPointIndex - 1, 0)];
        newCrop = prevCropPoint.crop;
      }
      const draftCropMap = (0, _immer.createDraft)(cropMap);
      const initCropMap = (0, _immer.finishDraft)(draftCropMap);
      const shouldUpdateCropChart = oldCrop !== newCrop;
      updateCropString(newCrop, shouldUpdateCropChart, false, initCropMap);
    }
  }
  let settings;
  let markersSvg;
  let markersDiv;
  let markerNumberingsDiv;
  let selectedMarkerPairOverlay;
  let startMarkerNumberings;
  let endMarkerNumberings;
  function initMarkersContainer() {
    settings = {
      platform: platform,
      videoID: videoInfo.id,
      videoTitle: videoInfo.title,
      videoUrl: videoInfo.videoUrl,
      newMarkerSpeed: 1.0,
      newMarkerCrop: '0:0:iw:ih',
      videoTag: `[${platform}@${videoInfo.id}]`,
      titleSuffix: `[${platform}@${videoInfo.id}]`,
      isVerticalVideo: videoInfo.isVerticalVideo,
      markerPairMergeList: '',
      ...getDefaultCropRes()
    };
    markersDiv = document.createElement('div');
    markersDiv.setAttribute('id', 'markers-div');
    (0, _util.safeSetInnerHtml)(markersDiv, `
        <svg id="markers-svg"></svg>
        <svg id="selected-marker-pair-overlay" style="display:none">
          <rect id="selected-start-marker-overlay"  class="selected-marker-overlay" width="1px" height="8px" y="3.5px" shape-rendering="crispEdges"></rect>
          <rect id="selected-end-marker-overlay"  class="selected-marker-overlay" width="1px" height="8px" y="3.5px" shape-rendering="crispEdges"></rect>
        </svg>
      `);
    markersSvg = markersDiv.children[0];
    selectedMarkerPairOverlay = markersDiv.children[1];
    markerNumberingsDiv = document.createElement('div');
    markerNumberingsDiv.setAttribute('id', 'marker-numberings-div');
    (0, _util.safeSetInnerHtml)(markerNumberingsDiv, `
        <svg id="start-marker-numberings"></svg>
        <svg id="end-marker-numberings"></svg>
      `);
    startMarkerNumberings = markerNumberingsDiv.children[0];
    endMarkerNumberings = markerNumberingsDiv.children[1];
    if ([_platforms.VideoPlatforms.weverse, _platforms.VideoPlatforms.naver_tv].includes(platform)) {
      hooks.markerNumberingsDiv.prepend(markerNumberingsDiv);
      hooks.markersDiv.prepend(markersDiv);
    } else {
      hooks.markersDiv.appendChild(markersDiv);
      hooks.markerNumberingsDiv.appendChild(markerNumberingsDiv);
    }
    videoInfo.fps = getFPS();
  }
  function getDefaultCropRes() {
    const cropResWidth = videoInfo.isVerticalVideo ? Math.round(1920 * videoInfo.aspectRatio) : 1920;
    const cropResHeight = videoInfo.isVerticalVideo ? 1920 : Math.round(1920 / videoInfo.aspectRatio);
    const cropRes = `${cropResWidth}x${cropResHeight}`;
    return {
      cropResWidth,
      cropResHeight,
      cropRes
    };
  }
  let rotatedVideoCSS;
  let fullscreenRotatedVideoCSS;
  let rotatedVideoPreviewsCSS;
  let rotatedVideoStyle;
  let adjustRotatedVideoPositionStyle;
  let fullscreenRotatedVideoStyle;
  let rotatedVideoPreviewsStyle;
  let rotation = 0;
  function rotateVideo(direction) {
    if (direction === 'clock') {
      rotation = rotation === 0 ? 90 : 0;
    } else if (direction === 'cclock') {
      rotation = rotation === 0 ? -90 : 0;
    }
    if (rotation === 90 || rotation === -90) {
      let scale = 1;
      scale = 1 / videoInfo.aspectRatio;
      rotatedVideoCSS = (0, _css.getRotatedVideoCSS)(rotation);
      rotatedVideoPreviewsCSS = `\
        .ytp-tooltip {
          transform: translateY(-15%) rotate(${rotation}deg) !important;
        }
        .ytp-tooltip-text-wrapper {
          transform: rotate(${-rotation}deg) !important;
          opacity: 0.6;
        }
      `;
      fullscreenRotatedVideoCSS = `
      .yt-clipper-video {
        transform: rotate(${rotation}deg) scale(${scale}) !important;
        margin-left: auto;
      }
      `;
      if (!document.fullscreen) {
        adjustRotatedVideoPositionStyle = (0, _util.injectCSS)(_css.adjustRotatedVideoPositionCSS, 'adjust-rotated-video-position-css');
        rotatedVideoStyle = (0, _util.injectCSS)(rotatedVideoCSS, 'yt-clipper-rotate-video-css');
        window.dispatchEvent(new Event('resize'));
      } else {
        fullscreenRotatedVideoStyle = (0, _util.injectCSS)(fullscreenRotatedVideoCSS, 'fullscreen-rotated-video-css');
      }
      rotatedVideoPreviewsStyle = (0, _util.injectCSS)(rotatedVideoPreviewsCSS, 'yt-clipper-rotated-video-previews-css');
      (0, _util.deleteElement)(bigVideoPreviewsStyle);
      bigVideoPreviewsStyle = null;
      window.dispatchEvent(new Event('resize'));
      document.addEventListener('fullscreenchange', fullscreenRotateVideoHandler);
    } else {
      (0, _util.deleteElement)(rotatedVideoStyle);
      (0, _util.deleteElement)(adjustRotatedVideoPositionStyle);
      (0, _util.deleteElement)(fullscreenRotatedVideoStyle);
      (0, _util.deleteElement)(rotatedVideoPreviewsStyle);
      (0, _util.deleteElement)(bigVideoPreviewsStyle);
      bigVideoPreviewsStyle = null;
      window.dispatchEvent(new Event('resize'));
      document.removeEventListener('fullscreenchange', fullscreenRotateVideoHandler);
    }
  }
  function fullscreenRotateVideoHandler() {
    if (document.fullscreen) {
      (0, _util.deleteElement)(rotatedVideoStyle);
      (0, _util.deleteElement)(adjustRotatedVideoPositionStyle);
      fullscreenRotatedVideoStyle = (0, _util.injectCSS)(fullscreenRotatedVideoCSS, 'fullscreen-rotated-video-css');
    } else {
      (0, _util.deleteElement)(fullscreenRotatedVideoStyle);
      adjustRotatedVideoPositionStyle = (0, _util.injectCSS)(_css.adjustRotatedVideoPositionCSS, 'adjust-rotated-video-position-css');
      rotatedVideoStyle = (0, _util.injectCSS)(rotatedVideoCSS, 'yt-clipper-rotate-video-css');
      document.removeEventListener('fullscreenchange', fullscreenRotateVideoHandler);
      window.dispatchEvent(new Event('resize'));
    }
  }
  let bigVideoPreviewsStyle;
  function toggleBigVideoPreviews() {
    const bigVideoPreviewsCSS = `\
    .ytp-tooltip {
      left: 45% !important;
      transform: ${rotation ? `translateY(-285%) rotate(${rotation}deg)` : 'translateY(-160%) '} scale(4) !important;
      padding: 1px !important;
      border-radius: 1px !important;
    }
    .ytp-tooltip-text-wrapper {
      transform: scale(0.5) ${rotation ? `rotate(${-rotation}deg)` : ''}!important;
      opacity: 0.6;
    }
    `;
    if (bigVideoPreviewsStyle) {
      (0, _util.deleteElement)(bigVideoPreviewsStyle);
      bigVideoPreviewsStyle = null;
    } else {
      bigVideoPreviewsStyle = (0, _util.injectCSS)(bigVideoPreviewsCSS, 'yt-clipper-big-video-previews-css');
    }
  }
  function addForeignEventListeners() {
    const selectors = ['input[type="text"', 'textarea'];
    selectors.forEach(selector => {
      const inputs = document.querySelectorAll(selector);
      for (const input of Array.from(inputs)) {
        if (isHotkeysEnabled) {
          input.addEventListener('focus', () => isHotkeysEnabled = false, {
            capture: true
          });
          input.addEventListener('blur', () => isHotkeysEnabled = true, {
            capture: true
          });
        }
      }
    });
  }
  function getShortestActiveMarkerPair(currentTime) {
    if (currentTime == null) currentTime = video.getCurrentTime();
    if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen && prevSelectedMarkerPairIndex != null) {
      const selectedMarkerPair = markerPairs[prevSelectedMarkerPairIndex];
      if (currentTime >= Math.floor(selectedMarkerPair.start * 1e6) / 1e6 && currentTime <= Math.ceil(selectedMarkerPair.end * 1e6) / 1e6) {
        return selectedMarkerPair;
      }
    }
    const activeMarkerPairs = markerPairs.filter(markerPair => {
      if (currentTime >= Math.floor(markerPair.start * 1e6) / 1e6 && currentTime <= Math.ceil(markerPair.end * 1e6) / 1e6) {
        return true;
      }
      return false;
    });
    if (activeMarkerPairs.length === 0) {
      return null;
    }
    const shortestActiveMarkerPair = activeMarkerPairs.reduce((prev, cur) => {
      if (cur.end - cur.start < prev.end - prev.start) {
        return cur;
      }
      return prev;
    });
    return shortestActiveMarkerPair;
  }
  let isSpeedPreviewOn = false;
  const toggleMarkerPairSpeedPreview = () => {
    if (isSpeedPreviewOn) {
      isSpeedPreviewOn = false;
      (0, _util.flashMessage)('Marker pair speed preview disabled', 'red');
    } else {
      isSpeedPreviewOn = true;
      if (!isForceSetSpeedOn) requestAnimationFrame(updateSpeed);
      (0, _util.flashMessage)('Marker pair speed preview enabled', 'green');
    }
  };
  let prevSpeed = 1;
  const defaultRoundSpeedMapEasing = 0.05;
  function updateSpeed() {
    if (!isSpeedPreviewOn && !isForceSetSpeedOn) {
      video.playbackRate = 1;
      prevSpeed = 1;
      updateSpeedInputLabel('Speed');
      return;
    }
    if (isForceSetSpeedOn) {
      if (prevSpeed !== forceSetSpeedValue) {
        video.playbackRate = forceSetSpeedValue;
        prevSpeed = forceSetSpeedValue;
        updateSpeedInputLabel(`Speed (${forceSetSpeedValue.toFixed(2)})`);
      }
      requestAnimationFrame(updateSpeed);
      return;
    }
    const shortestActiveMarkerPair = getShortestActiveMarkerPair();
    let newSpeed = prevSpeed;
    if (shortestActiveMarkerPair) {
      let markerPairSpeed;
      if (isVariableSpeed(shortestActiveMarkerPair.speedMap)) {
        markerPairSpeed = getSpeedMapping(shortestActiveMarkerPair.speedMap, video.getCurrentTime());
      } else {
        markerPairSpeed = shortestActiveMarkerPair.speed;
      }
      // console.log(markerPairSpeed);
      if (prevSpeed !== markerPairSpeed) {
        newSpeed = markerPairSpeed;
      }
    } else {
      newSpeed = 1;
    }
    if (prevSpeed !== newSpeed) {
      video.playbackRate = newSpeed;
      prevSpeed = newSpeed;
      updateSpeedInputLabel('Speed');
    }
    requestAnimationFrame(updateSpeed);
  }
  function updateSpeedInputLabel(text) {
    if (isSettingsEditorOpen && speedInputLabel != null) {
      speedInputLabel.textContent = text;
    }
  }
  const defaultSpeedRoundPrecision = 2;
  function getSpeedMapping(speedMap, time) {
    let roundMultiple = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultRoundSpeedMapEasing;
    let roundPrecision = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : defaultSpeedRoundPrecision;
    let len = speedMap.length;
    if (len === 2 && speedMap[0].y === speedMap[1].y) {
      return speedMap[0].y;
    }
    len--;
    let left;
    let right;
    for (let i = 0; i < len; ++i) {
      if (speedMap[i].x <= time && time <= speedMap[i + 1].x) {
        left = speedMap[i];
        right = speedMap[i + 1];
        break;
      }
    }
    if (left && right) {
      if (left.y === right.y) {
        return left.y;
      }
      const speed = getInterpolatedSpeed(left, right, video.getCurrentTime(), roundMultiple, roundPrecision);
      return speed;
    } else {
      return 1;
    }
  }
  function getInterpolatedSpeed(left, right, time) {
    let roundMultiple = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : defaultRoundSpeedMapEasing;
    let roundPrecision = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : defaultSpeedRoundPrecision;
    const elapsed = time - left.x;
    const duration = right.x - left.x;
    let easedTimePercentage;
    if (easingMode === 'cubicInOut') {
      easedTimePercentage = (0, _d3Ease.easeCubicInOut)(elapsed / duration);
    } else if (easingMode === 'linear') {
      easedTimePercentage = elapsed / duration;
    }
    const change = right.y - left.y;
    const rawSpeed = left.y + change * easedTimePercentage || right.y;
    const roundedSpeed = roundMultiple > 0 ? (0, _util.roundValue)(rawSpeed, roundMultiple, roundPrecision) : rawSpeed;
    return roundedSpeed;
  }
  let isMarkerLoopPreviewOn = false;
  function toggleMarkerPairLoop() {
    if (isMarkerLoopPreviewOn) {
      isMarkerLoopPreviewOn = false;
      (0, _util.flashMessage)('Auto marker looping disabled', 'red');
    } else {
      isMarkerLoopPreviewOn = true;
      (0, _util.flashMessage)('Auto marker looping enabled', 'green');
    }
  }
  function loopMarkerPair() {
    if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen) {
      if (prevSelectedMarkerPairIndex != null) {
        const markerPair = markerPairs[prevSelectedMarkerPairIndex];
        const chartLoop = currentChartInput ? markerPair[currentChartInput.chartLoopKey] : null;
        if (chartLoop && chartLoop.enabled && chartLoop.start > markerPair.start && chartLoop.end < markerPair.end && chartLoop.start < chartLoop.end) {
          const isTimeBetweenChartLoop = chartLoop.start <= video.getCurrentTime() && video.getCurrentTime() <= chartLoop.end;
          if (!isTimeBetweenChartLoop) {
            (0, _util.seekToSafe)(video, chartLoop.start);
          }
        } else if (isCropChartLoopingOn && isCurrentChartVisible && currentChartInput.type === 'crop' || cropChartInput.chart && (isMouseManipulatingCrop || isDrawingCrop)) {
          shouldTriggerCropChartLoop = false;
          cropChartSectionLoop();
        } else if (isMarkerLoopPreviewOn) {
          const isTimeBetweenMarkerPair = markerPair.start <= video.getCurrentTime() && video.getCurrentTime() <= markerPair.end;
          if (!isTimeBetweenMarkerPair) {
            (0, _util.seekToSafe)(video, markerPair.start);
          }
        }
      }
    }
    setTimeout(loopMarkerPair, 4);
  }
  let gammaFilterDiv;
  let isGammaPreviewOn = false;
  let gammaR;
  let gammaG;
  let gammaB;
  let gammaFilterSvg;
  function toggleGammaPreview() {
    if (!gammaFilterDiv) {
      gammaFilterDiv = document.createElement('div');
      gammaFilterDiv.setAttribute('id', 'gamma-filter-div');
      (0, _util.safeSetInnerHtml)(gammaFilterDiv, `
      <svg id="gamma-filter-svg" xmlns="http://www.w3.org/2000/svg" width="0" height="0">
        <defs>
          <filter id="gamma-filter">
            <feComponentTransfer id="gamma-filter-comp-transfer">
              <feFuncR id="gamma-r" type="gamma" offset="0" amplitude="1"></feFuncR>
              <feFuncG id="gamma-g" type="gamma" offset="0" amplitude="1"></feFuncG>
              <feFuncB id="gamma-b" type="gamma" offset="0" amplitude="1"></feFuncB>
            </feComponentTransfer>
          </filter>
        </defs>
      </svg>
      `);
      document.body.appendChild(gammaFilterDiv);
      gammaFilterSvg = gammaFilterDiv.firstElementChild;
      gammaR = document.getElementById('gamma-r');
      gammaG = document.getElementById('gamma-g');
      gammaB = document.getElementById('gamma-b');
    }
    if (!isGammaPreviewOn) {
      video.style.filter = 'url(#gamma-filter)';
      isGammaPreviewOn = true;
      requestAnimationFrame(gammaPreviewHandler);
      (0, _util.flashMessage)('Gamma preview enabled', 'green');
    } else {
      video.style.filter = null;
      isGammaPreviewOn = false;
      (0, _util.flashMessage)('Gamma preview disabled', 'red');
    }
  }
  let prevGammaVal = 1;
  function gammaPreviewHandler() {
    const shortestActiveMarkerPair = getShortestActiveMarkerPair();
    const markerPairGamma = shortestActiveMarkerPair && shortestActiveMarkerPair.overrides.gamma || settings.gamma || 1;
    if (markerPairGamma == 1) {
      if (video.style.filter) video.style.filter = null;
      prevGammaVal = 1;
    } else if (prevGammaVal !== markerPairGamma) {
      // console.log(`Updating gamma from ${prevGammaVal} to ${markerPairGamma}`);
      gammaR.exponent.baseVal = markerPairGamma;
      gammaG.exponent.baseVal = markerPairGamma;
      gammaB.exponent.baseVal = markerPairGamma;
      // force re-render of filter (possible bug with chrome and other browsers?)
      if (!video.style.filter) video.style.filter = 'url(#gamma-filter)';
      gammaFilterSvg.setAttribute('width', '0');
      prevGammaVal = markerPairGamma;
    }
    if (isGammaPreviewOn) {
      requestAnimationFrame(gammaPreviewHandler);
    }
  }
  let isFadeLoopPreviewOn = false;
  function toggleFadeLoopPreview() {
    if (!isFadeLoopPreviewOn) {
      isFadeLoopPreviewOn = true;
      requestAnimationFrame(fadeLoopPreviewHandler);
      (0, _util.flashMessage)('Fade loop preview enabled', 'green');
    } else {
      isFadeLoopPreviewOn = false;
      video.style.opacity = '1';
      (0, _util.flashMessage)('Fade loop preview disabled', 'red');
    }
  }
  function fadeLoopPreviewHandler() {
    const currentTime = video.getCurrentTime();
    const shortestActiveMarkerPair = getShortestActiveMarkerPair();
    if (shortestActiveMarkerPair && (shortestActiveMarkerPair.overrides.loop === 'fade' || shortestActiveMarkerPair.overrides.loop == null && settings.loop === 'fade')) {
      const currentTimeP = getFadeBounds(shortestActiveMarkerPair, currentTime);
      if (currentTimeP == null) {
        video.style.opacity = '1';
      } else {
        let currentTimeEased = Math.max(0.1, (0, _d3Ease.easeCubicInOut)(currentTimeP));
        video.style.opacity = currentTimeEased.toString();
        // console.log(video.style.opacity);
      }
    } else {
      video.style.opacity = '1';
    }
    isFadeLoopPreviewOn ? requestAnimationFrame(fadeLoopPreviewHandler) : video.style.opacity = '1';
  }
  function getFadeBounds(markerPair, currentTime) {
    const start = Math.floor(markerPair.start * 1e6) / 1e6;
    const end = Math.ceil(markerPair.end * 1e6) / 1e6;
    const inputDuration = end - start;
    const outputDuration = markerPair.outputDuration;
    let fadeDuration = markerPair.overrides.fadeDuration || settings.fadeDuration || 0.5;
    fadeDuration = Math.min(fadeDuration, 0.4 * outputDuration);
    const fadeInStartP = 0;
    const fadeInEndP = fadeDuration / outputDuration;
    const fadeOutStartP = (outputDuration - fadeDuration) / outputDuration;
    const fadeOutEndP = outputDuration / outputDuration;
    let currentTimeP = (currentTime - start) / inputDuration;
    if (currentTimeP >= fadeInStartP && currentTimeP <= fadeInEndP) {
      currentTimeP = (currentTime - start) / fadeDuration;
      return currentTimeP;
    } else if (currentTimeP >= fadeOutStartP && currentTimeP <= fadeOutEndP) {
      currentTimeP = 1 - (currentTime - start - (inputDuration - fadeDuration)) / fadeDuration;
      return currentTimeP;
    } else {
      return null;
    }
  }
  let isAllPreviewsOn = false;
  function toggleAllPreviews() {
    isAllPreviewsOn = isSpeedPreviewOn && isMarkerLoopPreviewOn && isGammaPreviewOn && isFadeLoopPreviewOn && isCropChartLoopingOn;
    if (!isAllPreviewsOn) {
      !isSpeedPreviewOn && toggleMarkerPairSpeedPreview();
      !isMarkerLoopPreviewOn && toggleMarkerPairLoop();
      !isGammaPreviewOn && toggleGammaPreview();
      !isFadeLoopPreviewOn && toggleFadeLoopPreview();
      !isCropChartLoopingOn && toggleCropChartLooping();
      isAllPreviewsOn = true;
    } else {
      isSpeedPreviewOn && toggleMarkerPairSpeedPreview();
      isMarkerLoopPreviewOn && toggleMarkerPairLoop();
      isGammaPreviewOn && toggleGammaPreview();
      isFadeLoopPreviewOn && toggleFadeLoopPreview();
      isCropChartLoopingOn && toggleCropChartLooping();
      isAllPreviewsOn = false;
    }
  }
  function jumpToNearestMarkerOrPair(e, keyCode) {
    if (!arrowKeyCropAdjustmentEnabled) {
      if (e.ctrlKey && !e.altKey && !e.shiftKey) {
        jumpToNearestMarker(e, video.getCurrentTime(), keyCode);
      } else if (e.altKey && !e.shiftKey) {
        if (!e.ctrlKey && !(isSettingsEditorOpen && !wasGlobalSettingsEditorOpen)) {
          (0, _util.blockEvent)(e);
          togglePrevSelectedMarkerPair();
        }
        if (enableMarkerHotkeys.endMarker) {
          jumpToNearestMarkerPair(e, enableMarkerHotkeys.endMarker, keyCode);
        }
      }
    }
  }
  function jumpToNearestMarkerPair(e, targetEndMarker, keyCode) {
    (0, _util.blockEvent)(e);
    let index = parseInt(targetEndMarker.getAttribute('data-idx')) - 1;
    if (keyCode === 'ArrowLeft' && index > 0) {
      targetEndMarker = enableMarkerHotkeys.endMarker.previousElementSibling.previousElementSibling;
      targetEndMarker && toggleMarkerPairEditor(targetEndMarker);
      if (e.ctrlKey) {
        index--;
        (0, _util.seekToSafe)(video, markerPairs[index].start);
      }
    } else if (keyCode === 'ArrowRight' && index < markerPairs.length - 1) {
      targetEndMarker = enableMarkerHotkeys.endMarker.nextElementSibling.nextElementSibling;
      targetEndMarker && toggleMarkerPairEditor(targetEndMarker);
      if (e.ctrlKey) {
        index++;
        (0, _util.seekToSafe)(video, markerPairs[index].start);
      }
    }
  }
  let dblJump = 0;
  let prevJumpKeyCode;
  let prevTime;
  function jumpToNearestMarker(e, currentTime, keyCode) {
    (0, _util.blockEvent)(e);
    let minTime;
    currentTime = prevTime != null ? prevTime : currentTime;
    let markerTimes = [];
    markerPairs.forEach(markerPair => {
      markerTimes.push(markerPair.start);
      markerTimes.push(markerPair.end);
    });
    if (start === false) {
      markerTimes.push(startTime);
    }
    markerTimes = markerTimes.map(markerTime => parseFloat(markerTime.toFixed(6)));
    if (keyCode === 'ArrowLeft') {
      markerTimes = markerTimes.filter(markerTime => markerTime < currentTime);
      minTime = Math.max(...markerTimes);
      if (dblJump != 0 && markerTimes.length > 0 && prevJumpKeyCode === 'ArrowLeft') {
        markerTimes = markerTimes.filter(markerTime => markerTime < minTime);
        minTime = Math.max(...markerTimes);
      }
      prevJumpKeyCode = 'ArrowLeft';
    } else if (keyCode === 'ArrowRight') {
      markerTimes = markerTimes.filter(markerTime => markerTime > currentTime);
      minTime = Math.min(...markerTimes);
      if (dblJump != 0 && markerTimes.length > 0 && prevJumpKeyCode === 'ArrowRight') {
        markerTimes = markerTimes.filter(markerTime => markerTime > minTime);
        minTime = Math.min(...markerTimes);
      }
      prevJumpKeyCode = 'ArrowRight';
    }
    if (dblJump !== 0) {
      clearTimeout(dblJump);
      dblJump = 0;
      prevTime = null;
      if (minTime !== currentTime && minTime != Infinity && minTime != -Infinity) (0, _util.seekToSafe)(video, minTime);
    } else {
      prevTime = currentTime;
      if (minTime !== currentTime && minTime != Infinity && minTime != -Infinity) (0, _util.seekToSafe)(video, minTime);
      dblJump = setTimeout(() => {
        dblJump = 0;
        prevTime = null;
      }, 150);
    }
  }
  function saveMarkersAndSettings() {
    const settingsJSON = getClipperInputJSON();
    const blob = new Blob([settingsJSON], {
      type: 'application/json;charset=utf-8'
    });
    (0, _fileSaver.saveAs)(blob, `${settings.titleSuffix || `[${settings.videoID}]`}.json`);
  }
  function getClipperInputData(date) {
    markerPairs.forEach((markerPair, index) => {
      const speed = markerPair.speed;
      if (typeof speed === 'string') {
        markerPair.speed = Number(speed);
        console.log(`Converted marker pair ${index}'s speed from String to Number`);
      }
    });
    const markerPairsNumbered = markerPairs.map((markerPair, idx) => {
      const markerPairNumbered = {
        number: idx + 1,
        ...markerPair,
        speedMapLoop: undefined,
        speedMap: isVariableSpeed(markerPair.speedMap) ? markerPair.speedMap : undefined,
        speedChartLoop: undefined,
        cropMap: !isStaticCrop(markerPair.cropMap) ? markerPair.cropMap : undefined,
        cropChartLoop: undefined,
        undoredo: undefined,
        startNumbering: undefined,
        endNumbering: undefined,
        moveHistory: undefined,
        outputDuration: undefined
      };
      return markerPairNumbered;
    });
    const clipperInputData = {
      ...settings,
      version: __version__,
      markerPairs: markerPairsNumbered,
      date: date !== null && date !== void 0 ? date : undefined
    };
    return clipperInputData;
  }
  function getClipperInputJSON() {
    const settingsJSON = JSON.stringify(getClipperInputData(), undefined, 2);
    return settingsJSON;
  }
  function isVariableSpeed(speedMap) {
    if (speedMap.length < 2) return false;
    let isVariableSpeed = speedMap.some((speedPoint, i) => {
      if (i === speedMap.length - 1) return false;
      return speedPoint.y !== speedMap[i + 1].y;
    });
    return isVariableSpeed;
  }
  function deleteMarkersDataCommands() {
    const markersDataCommandsDiv = document.getElementById('markers-data-commands-div');
    if (markersDataCommandsDiv) {
      (0, _util.deleteElement)(markersDataCommandsDiv);
      return true;
    }
    return false;
  }
  function toggleMarkersDataCommands() {
    if (!deleteMarkersDataCommands()) {
      const markersDataCommandsDiv = document.createElement('div');
      markersDataCommandsDiv.setAttribute('id', 'markers-data-commands-div');
      const markersUploadDiv = document.createElement('div');
      markersUploadDiv.setAttribute('class', 'long-msg-div');
      (0, _util.safeSetInnerHtml)(markersUploadDiv, `
        <fieldset>
          <legend>Load markers data from an uploaded markers .json file.</legend>
          <input type="file" id="markers-json-input" />
          <input type="button" id="upload-markers-json" value="Load" />
        </fieldset>
        <fieldset hidden>
          <legend>Upload a markers array file.</legend>
          <input type="file" id="markers-array-input" />
          <input type="button" id="upload-markers-array" value="Load" />
        </fieldset>
      `);
      const restoreMarkersDataDiv = document.createElement('div');
      restoreMarkersDataDiv.setAttribute('class', 'long-msg-div');
      const markersDataFiles = getMarkersDataEntriesFromLocalStorage();
      (0, _util.safeSetInnerHtml)(restoreMarkersDataDiv, `
        <fieldset>
          <legend>Restore auto-saved markers data from browser local storage.</legend>
          <input type="button" id="restore-markers-data" value="Restore" />
        </fieldset>
        <fieldset>
          <legend>
            Zip and download ${markersDataFiles === null || markersDataFiles === void 0 ? void 0 : markersDataFiles.length} auto-saved markers data files from browser
            local storage.
          </legend>
          <input type="button" id="download-markers-data" value="Download" />
        </fieldset>
      `);
      const clearMarkersDataDiv = document.createElement('div');
      clearMarkersDataDiv.setAttribute('class', 'long-msg-div');
      (0, _util.safeSetInnerHtml)(clearMarkersDataDiv, `
        <fieldset>
          <legend>Clear all markers data files from browser local storage.</legend>
          <input type="button" id="clear-markers-data" value="Clear" style="color:red" />
        </fieldset>
      `);
      markersDataCommandsDiv.appendChild(markersUploadDiv);
      markersDataCommandsDiv.appendChild(restoreMarkersDataDiv);
      markersDataCommandsDiv.appendChild(clearMarkersDataDiv);
      injectYtcWidget(markersDataCommandsDiv);
      const fileUploadButton = document.getElementById('upload-markers-json');
      fileUploadButton.onclick = loadMarkersJson;
      const markersArrayUploadButton = document.getElementById('upload-markers-array');
      markersArrayUploadButton.onclick = loadMarkersArray;
      const restoreMarkersDataButton = document.getElementById('restore-markers-data');
      restoreMarkersDataButton.onclick = loadClipperInputDataFromLocalStorage;
      const downloadMarkersDataButton = document.getElementById('download-markers-data');
      downloadMarkersDataButton.onclick = downloadAutoSavedMarkersData;
      const clearMarkersDataButton = document.getElementById('clear-markers-data');
      clearMarkersDataButton.onclick = clearYTClipperLocalStorage;
    }
  }
  function injectYtcWidget(widget) {
    updateSettingsEditorHook();
    if (isTheatreMode()) {
      settingsEditorHook.insertAdjacentElement('afterend', widget);
    } else {
      widget.style.position = 'relative';
      settingsEditorHook.insertAdjacentElement('beforebegin', widget);
    }
  }
  function loadMarkersJson() {
    const input = document.getElementById('markers-json-input');
    if (input.files.length === 0) return;
    console.log(input.files);
    const file = input.files[0];
    const fr = new FileReader();
    fr.onload = e => loadClipperInputJSON(e.target.result);
    fr.readAsText(file);
    deleteMarkersDataCommands();
  }
  function loadMarkersArray() {
    const input = document.getElementById('markers-array-input');
    if (input.files.length === 0) return;
    console.log(input.files);
    const file = input.files[0];
    const fr = new FileReader();
    fr.onload = receivedMarkersArray;
    fr.readAsText(file);
    deleteMarkersDataCommands();
  }
  function loadClipperInputJSON(json) {
    const markersData = JSON.parse(json);
    console.log(markersData);
    (0, _util.flashMessage)('Loading markers data...', 'green');
    if (markersData) {
      // move markers field to marker Pairs for backwards compat)
      if (markersData.markers && !markersData.markerPairs) {
        markersData.markerPairs = markersData.markers;
        delete markersData.markers;
      }
      if (!markersData.markerPairs) {
        (0, _util.flashMessage)('Could not find markers or markerPairs field. Could not load marker data.', 'red');
      }
      // copy markersJson to settings object less markerPairs field
      const {
        markerPairs: _markerPairs,
        ...loadedSettings
      } = markersData;
      delete loadedSettings.videoID;
      delete loadedSettings.videoTitle;
      delete loadedSettings.isVerticalVideo;
      delete loadedSettings.version;
      settings = {
        ...settings,
        ...loadedSettings
      };
      addMarkerPairs(markersData.markerPairs);
    }
  }
  function addMarkerPairs(markerPairs) {
    markerPairs.forEach(markerPair => {
      const startMarkerConfig = {
        time: markerPair.start,
        type: 'start'
      };
      const endMarkerConfig = {
        time: markerPair.end,
        type: 'end',
        speed: markerPair.speed,
        speedMap: markerPair.speedMap,
        speedChartLoop: markerPair.speedChartLoop,
        crop: markerPair.crop,
        cropMap: markerPair.cropMap,
        cropChartLoop: markerPair.cropChartLoop,
        enableZoomPan: markerPair.enableZoomPan,
        overrides: markerPair.overrides,
        undoredo: markerPair.undoredo
      };
      addMarker(startMarkerConfig);
      addMarker(endMarkerConfig);
    });
  }
  function receivedMarkersArray(e) {
    const lines = e.target.result;
    const markersJson = JSON.parse(lines);
    console.log(markersJson);
    (0, _util.flashMessage)('Loading markers...', 'green');
    markersJson.markerPairs = markersJson.markerPairs.flat(1);
    for (let i = 0; i < markersJson.markerPairs.length; i = i + 4) {
      console.log(markerPairs);
      const start = (0, _util.timeRounder)(markersJson.markerPairs[i]);
      const end = (0, _util.timeRounder)(markersJson.markerPairs[i + 1]);
      const speed = (0, _util.speedRounder)(1 / markersJson.markerPairs[i + 2]);
      const cropString = markersJson.markerPairs[i + 3];
      // const crop = Crop.fromCropString(cropString, settings.cropRes);
      const startMarkerConfig = {
        time: start,
        type: 'start'
      };
      const endMarkerConfig = {
        time: end,
        type: 'end',
        crop: cropString,
        speed: speed
      };
      addMarker(startMarkerConfig);
      addMarker(endMarkerConfig);
    }
  }
  // set width and height attributes for browsers not supporting svg 2
  const marker_attrs = {
    class: 'marker',
    width: '1px',
    height: '14px',
    'shape-rendering': 'crispEdges'
  };
  function addMarker() {
    let markerConfig = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    var _a;
    const preciseCurrentTime = (_a = markerConfig.time) !== null && _a !== void 0 ? _a : video.getCurrentTime();
    // TODO: Calculate video fps precisely so current frame time
    // is accurately determined.
    // const currentFrameTime = getCurrentFrameTime(roughCurrentTime);
    const currentFrameTime = preciseCurrentTime;
    const progressPos = currentFrameTime / (0, _util.getVideoDuration)(platform, video) * 100;
    if (!start && currentFrameTime <= startTime) {
      (0, _util.flashMessage)('End marker must be after start marker.', 'red');
      return;
    }
    const marker = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
    markersSvg.appendChild(marker);
    (0, _util.setAttributes)(marker, marker_attrs);
    marker.setAttribute('x', `${progressPos}%`);
    const rectIdx = markerPairs.length + 1;
    marker.setAttribute('data-idx', rectIdx.toString());
    if (start === true) {
      marker.classList.add('start-marker');
      marker.setAttribute('type', 'start');
      marker.setAttribute('z-index', '1');
      startTime = currentFrameTime;
    } else {
      marker.addEventListener('pointerover', toggleMarkerPairEditorHandler, false);
      marker.classList.add('end-marker');
      marker.setAttribute('type', 'end');
      marker.setAttribute('z-index', '2');
      const startProgressPos = startTime / (0, _util.getVideoDuration)(platform, video) * 100;
      const [startNumbering, endNumbering] = addMarkerPairNumberings(rectIdx, startProgressPos, progressPos, marker);
      pushMarkerPairsArray(currentFrameTime, {
        ...markerConfig,
        ...{
          startNumbering,
          endNumbering
        }
      });
      updateMarkerPairEditor();
    }
    start = !start;
    console.log(markerPairs);
  }
  let prevVideoWidth;
  function getFPS() {
    let defaultFPS = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 60;
    let fps;
    try {
      if (videoInfo.fps != null && video.videoWidth != null && prevVideoWidth === video.videoWidth) {
        fps = videoInfo.fps;
      } else if (platform === _platforms.VideoPlatforms.youtube) {
        videoInfo.fps = parseFloat(player.getStatsForNerds().resolution.match(/@(\d+)/)[1]);
        fps = videoInfo.fps;
      } else {
        fps = defaultFPS;
      }
    } catch (e) {
      console.log('Could not detect fps', e);
      fps = defaultFPS; // by default parameter value assume high fps to avoid skipping frames
    }
    prevVideoWidth = video.videoWidth;
    return fps;
  }
  function getCurrentFrameTime(roughCurrentTime) {
    let currentFrameTime;
    let fps = getFPS(null);
    // If fps cannot be detected use precise time reported by video player
    // instead of estimating nearest frame time
    fps ? currentFrameTime = Math.floor(roughCurrentTime * fps) / fps : currentFrameTime = roughCurrentTime;
    return currentFrameTime;
  }
  function pushMarkerPairsArray(currentTime, markerPairConfig) {
    var _a;
    const speed = markerPairConfig.speed || settings.newMarkerSpeed;
    const crop = markerPairConfig.crop || settings.newMarkerCrop;
    const newMarkerPair = {
      start: startTime,
      end: currentTime,
      speed,
      speedMap: markerPairConfig.speedMap || [{
        x: startTime,
        y: speed
      }, {
        x: currentTime,
        y: speed
      }],
      speedChartLoop: markerPairConfig.speedChartLoop || {
        enabled: true
      },
      crop,
      cropMap: markerPairConfig.cropMap || [{
        x: startTime,
        y: 0,
        crop: crop
      }, {
        x: currentTime,
        y: 0,
        crop: crop
      }],
      cropChartLoop: markerPairConfig.cropChartLoop || {
        enabled: true
      },
      enableZoomPan: (_a = markerPairConfig.enableZoomPan) !== null && _a !== void 0 ? _a : false,
      cropRes: settings.cropRes,
      outputDuration: markerPairConfig.outputDuration || currentTime - startTime,
      startNumbering: markerPairConfig.startNumbering,
      endNumbering: markerPairConfig.endNumbering,
      overrides: markerPairConfig.overrides || {},
      undoredo: markerPairConfig.undoredo || {
        history: [],
        index: -1
      }
    };
    if (newMarkerPair.undoredo.history.length === 0) {
      const draft = (0, _immer.createDraft)((0, _undoredo.getMarkerPairHistory)(newMarkerPair));
      (0, _undoredo.saveMarkerPairHistory)(draft, newMarkerPair);
    }
    markerPairs.push(newMarkerPair);
    initAutoSave();
  }
  function updateMarkerPairEditor() {
    if (isSettingsEditorOpen) {
      const markerPairCountLabel = document.getElementById('marker-pair-count-label');
      if (markerPairCountLabel) {
        markerPairCountLabel.textContent = markerPairs.length.toString();
        markerPairNumberInput.setAttribute('max', markerPairs.length.toString());
      }
    }
  }
  function addMarkerPairNumberings(idx, startProgressPos, endProgressPos, endMarker) {
    let startNumbering = (0, _util.htmlToSVGElement)(`\
        <svg>\
        <text class="markerNumbering startMarkerNumbering" data-idx="${idx}"\
          x="${startProgressPos}%" y="11.5px"
          text-anchor="middle"
        >\
        ${idx}\
        </text>\
        </svg>\
        `);
    startNumbering = startNumbering.children[0];
    let endNumbering = (0, _util.htmlToSVGElement)(`\
        <svg>\
        <text class="markerNumbering endMarkerNumbering" data-idx="${idx}"\
          x="${endProgressPos}%" y="11.5px"
          text-anchor="middle"
        >\
        ${idx}\
        </text>\
        </svg>\
        `);
    endNumbering = endNumbering.children[0];
    const startNumberingText = startMarkerNumberings.appendChild(startNumbering);
    const endNumberingText = endMarkerNumberings.appendChild(endNumbering);
    endNumberingText.marker = endMarker;
    startNumberingText.marker = endMarker;
    endNumberingText.addEventListener('pointerover', markerNumberingMouseOverHandler, false);
    startNumberingText.addEventListener('pointerdown', markerNumberingMouseDownHandler, true);
    endNumberingText.addEventListener('pointerdown', markerNumberingMouseDownHandler, true);
    return [startNumberingText, endNumberingText];
  }
  function undoMarker() {
    const targetMarker = markersSvg.lastElementChild;
    if (!targetMarker) return;
    const targetMarkerType = targetMarker.getAttribute('type');
    // toggle off marker pair editor before undoing a selected marker pair
    if (targetMarkerType === 'end' && prevSelectedMarkerPairIndex >= markerPairs.length - 1) {
      if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen) {
        toggleOffMarkerPairEditor(true);
      } else {
        hideSelectedMarkerPairOverlay(true);
      }
      clearPrevSelectedMarkerPairReferences();
    }
    (0, _util.deleteElement)(targetMarker);
    if (targetMarkerType === 'end') {
      const markerPair = markerPairs[markerPairs.length - 1];
      (0, _util.deleteElement)(markerPair.startNumbering);
      (0, _util.deleteElement)(markerPair.endNumbering);
      startTime = markerPair.start;
      markerPairsHistory.push(markerPairs.pop());
      console.log(markerPairs);
      updateMarkerPairEditor();
    }
    start = !start;
  }
  function redoMarker() {
    if (markerPairsHistory.length > 0) {
      const markerPairToRestore = markerPairsHistory[markerPairsHistory.length - 1];
      if (start) {
        addMarker({
          time: markerPairToRestore.start
        });
      } else {
        markerPairsHistory.pop();
        addMarker({
          ...markerPairToRestore,
          time: markerPairToRestore.end
        });
      }
    }
  }
  function duplicateSelectedMarkerPair() {
    const markerPairIndex = prevSelectedMarkerPairIndex;
    if (markerPairIndex != null) {
      const markerPair = (0, _lodash.default)(markerPairs[markerPairIndex]);
      addMarkerPairs([markerPair]);
      (0, _util.flashMessage)(`Duplicated marker pair ${markerPairIndex + 1}.`, 'green');
    } else {
      (0, _util.flashMessage)(`No selected or previously selected marker pair to duplicate.`, 'red');
    }
  }
  function addChartPoint() {
    if (isChartEnabled && isCurrentChartVisible) {
      if (currentChartInput.type == 'speed') {
        _scatterChartSpec.addSpeedPoint.call(currentChartInput.chart, video.getCurrentTime(), 1);
      } else if (currentChartInput.type == 'crop') {
        _scatterChartSpec.addCropPoint.call(currentChartInput.chart, video.getCurrentTime());
      }
    }
  }
  let forceSetSpeedValue = 1;
  function cycleForceSetSpeedValueDown() {
    forceSetSpeedValue = forceSetSpeedValue - 0.25;
    if (forceSetSpeedValue <= 0) forceSetSpeedValue = 1;
    (0, _util.flashMessage)(`Force set video speed value set to ${forceSetSpeedValue}`, 'green');
  }
  let isForceSetSpeedOn = false;
  function toggleForceSetSpeed() {
    if (isForceSetSpeedOn) {
      isForceSetSpeedOn = false;
      updateSpeedInputLabel(`Speed`);
      (0, _util.flashMessage)('Force set speed disabled', 'red');
    } else {
      isForceSetSpeedOn = true;
      updateSpeedInputLabel(`Speed (${forceSetSpeedValue.toFixed(2)})`);
      if (!isSpeedPreviewOn) requestAnimationFrame(updateSpeed);
      (0, _util.flashMessage)('Force set speed enabled', 'green');
    }
  }
  function toggleGlobalSettingsEditor() {
    if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen) {
      toggleOffMarkerPairEditor();
    }
    if (wasGlobalSettingsEditorOpen) {
      toggleOffGlobalSettingsEditor();
    } else {
      createGlobalSettingsEditor();
    }
  }
  function toggleOffGlobalSettingsEditor() {
    deleteSettingsEditor();
    hideCropOverlay();
    hideChart();
  }
  function createGlobalSettingsEditor() {
    createCropOverlay(settings.newMarkerCrop);
    const globalSettingsEditorDiv = document.createElement('div');
    const cropInputValidation = `\\d+:\\d+:(\\d+|iw):(\\d+|ih)`;
    const [x, y, w, h] = getCropComponents(settings.newMarkerCrop);
    const cropAspectRatio = (w / h).toFixed(13);
    const numOrRange = `(\\d{1,2})|(\\d{1,2}-\\d{1,2})`;
    const csvRange = `(${numOrRange})*(,(${numOrRange}))*`;
    const csvRangeReq = `(${numOrRange}){1}(,(${numOrRange}))*`;
    const mergeListInputValidation = `^(${csvRange})(;${csvRangeReq})*$`;
    const gte100 = `([1-9]\\d{3}|[1-9]\\d{2})`;
    const cropResInputValidation = `${gte100}x${gte100}`;
    const {
      cropRes,
      cropResWidth,
      cropResHeight
    } = getDefaultCropRes();
    const cropResX2 = `${cropResWidth * 2}x${cropResHeight * 2}`;
    const resList = `<option value="${cropRes}"><option value="${cropResX2}">`;
    const minterpMode = settings.minterpMode;
    const minterpFPS = settings.minterpFPS;
    const denoise = settings.denoise;
    const denoiseDesc = denoise ? denoise.desc : null;
    const vidstab = settings.videoStabilization;
    const vidstabDesc = vidstab ? vidstab.desc : null;
    const vidstabDynamicZoomEnabled = settings.videoStabilizationDynamicZoom;
    const markerPairMergelistDurations = getMarkerPairMergeListDurations();
    const globalEncodeSettingsEditorDisplay = isExtraSettingsEditorEnabled ? 'block' : 'none';
    globalSettingsEditorDiv.setAttribute('id', 'settings-editor-div');
    (0, _util.safeSetInnerHtml)(globalSettingsEditorDiv, `
    <fieldset id="new-marker-defaults-inputs"
      class="settings-editor-panel global-settings-editor global-settings-editor-highlighted-div">
      <legend class="global-settings-editor-highlighted-label">New Marker Settings</legend>
      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.speedTooltip}">
        <span>Speed</span>
        <input id="speed-input" type="number" placeholder="speed" value="${settings.newMarkerSpeed}" step="0.05" min="0.05" max="2" style="width:7ch">
      </div>
      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.cropTooltip}">
        <span>Crop</span>
        <input id="crop-input" value="${settings.newMarkerCrop}" pattern="${cropInputValidation}" style="width:21ch" required>
      </div>
      <div class="settings-editor-input-div  settings-info-display">
        <span>Crop Aspect Ratio</span>
        <span id="crop-aspect-ratio">${cropAspectRatio}</span>
      </div>
    </fieldset>
    <fieldset id="global-marker-settings"
    class="settings-editor-panel global-settings-editor global-settings-editor-highlighted-div">
      <legend class="global-settings-editor-highlighted-label settings-editor-panel-label">Global Settings</legend>
      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.titleSuffixTooltip}">
        <span>Title Suffix</span>
        <input id="title-suffix-input" value="${settings.titleSuffix}" style="background-color:lightgreen;min-width:20em;text-align:right" required>
      </div>
      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.cropResolutionTooltip}">
        <span>Crop Resolution</span>
        <input id="crop-res-input" list="resolutions" pattern="${cropResInputValidation}" value="${settings.cropRes}" style="width:14ch" required>
        <datalist id="resolutions" autocomplete="off">${resList}</datalist>
      </div>
      <div id="global-settings-rotate" class="settings-editor-input-div" title="${_tooltips.Tooltips.rotateTooltip}">
        <span style="display:inline">Rotate: </span>
        <input id="rotate-0" type="radio" name="rotate" value="0" ${settings.rotate == null || settings.rotate === '0' ? 'checked' : ''}></input>
        <label for="rotate-0">0&#x00B0; </label>
        <input id="rotate-90-clock" type="radio" value="clock" name="rotate" ${settings.rotate === 'clock' ? 'checked' : ''}></input>
        <label for="rotate-90-clock">90&#x00B0; &#x27F3;</label>
        <input id="rotate-90-counterclock" type="radio" value="cclock" name="rotate" ${settings.rotate === 'cclock' ? 'checked' : ''}></input>
        <label for="rotate-90-counterclock">90&#x00B0; &#x27F2;</label>
      </div>
      <div id="merge-list-div" class="settings-editor-input-div" title="${_tooltips.Tooltips.mergeListTooltip}">
          <span style="display:inline">Merge List: </span>
          <input id="merge-list-input" pattern="${mergeListInputValidation}" value="${settings.markerPairMergeList != null ? settings.markerPairMergeList : ''}" placeholder="None" style="min-width:15em">
      </div>
      <div class="settings-editor-input-div">
        <span style="display:inline">Merge Durations: </span>
        <span id="merge-list-durations" style="display:inline">${markerPairMergelistDurations}</span>
      </div>
    </fieldset>
    <fieldset id="global-encode-settings"
      class="settings-editor-panel global-settings-editor global-settings-editor-highlighted-div" style="display:${globalEncodeSettingsEditorDisplay}">
      <legend class="global-settings-editor-highlighted-label">Encode Settings</legend>
      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.audioTooltip}">
        <span>Audio</span>
        <select id="audio-input">
          <option value="Default" ${settings.audio == null ? 'selected' : ''}>(Disabled)</option>
          <option ${settings.audio === false ? 'selected' : ''}>Disabled</option>
          <option ${settings.audio ? 'selected' : ''}>Enabled</option>
        </select>
      </div>
      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.encodeSpeedTooltip}">
        <span>Encode Speed (0-5)</span>
        <input id="encode-speed-input" type="number" min="0" max="5" step="1" value="${settings.encodeSpeed != null ? settings.encodeSpeed : ''}" placeholder="Auto" style="min-width:4em"></input>
      </div>
      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.CRFTooltip}">
        <span>CRF (0-63)</span>
        <input id="crf-input" type="number" min="0" max="63" step="1" value="${settings.crf != null ? settings.crf : ''}" placeholder="Auto" style="min-width:4em"></input>
      </div>
      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.targetBitrateTooltip}">
        <span>Target Bitrate (kb/s)</span>
        <input id="target-max-bitrate-input" type="number" min="0" max="1e5"step="100" value="${settings.targetMaxBitrate != null ? settings.targetMaxBitrate : ''}" placeholder="Auto" style="min-width:4em"></input>
      </div>

      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.twoPassTooltip}">
        <span>Two-Pass</span>
        <select id="two-pass-input">
          <option value="Default" ${settings.twoPass == null ? 'selected' : ''}>(Disabled)</option>
          <option ${settings.twoPass === false ? 'selected' : ''}>Disabled</option>
          <option ${settings.twoPass ? 'selected' : ''}>Enabled</option>
        </select>
      </div>

      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.gammaTooltip}">
        <span>Gamma (0-4)</span>
        <input id="gamma-input" type="number" min="0.01" max="4.00" step="0.01" value="${settings.gamma != null ? settings.gamma : ''}" placeholder="1" style="min-width:4em"></input>
      </div>

      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.hdrTooltip}">
        <span>Enable HDR</span>
        <select id="enable-hdr-input">
          <option value="Default" ${settings.enableHDR == null ? 'selected' : ''}>(Disabled)</option>
          <option ${settings.enableHDR === false ? 'selected' : ''}>Disabled</option>
          <option ${settings.enableHDR ? 'selected' : ''}>Enabled</option>
        </select>
      </div>

      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.denoiseTooltip}">
        <span>Denoise</span>
        <select id="denoise-input">
          <option value="Inherit" ${denoiseDesc == null ? 'selected' : ''}>(Disabled)</option>
          <option ${denoiseDesc === 'Very Weak' ? 'selected' : ''}>Very Weak</option>
          <option ${denoiseDesc === 'Weak' ? 'selected' : ''}>Weak</option>
          <option ${denoiseDesc === 'Medium' ? 'selected' : ''}>Medium</option>
          <option ${denoiseDesc === 'Strong' ? 'selected' : ''}>Strong</option>
          <option ${denoiseDesc === 'Very Strong' ? 'selected' : ''}>Very Strong</option>
        </select>
      </div>
      <div class="settings-editor-input-div">
        <div  title="${_tooltips.Tooltips.minterpModeTooltip}">
          <span>Minterpolation</span>
          <select id="minterp-mode-input">
            <option value="Default" ${minterpMode == null ? 'selected' : ''}>(Numeric)</option>
            <option ${minterpMode === 'None' ? 'selected' : ''}>None</option>
            <option value="MaxSpeed" ${minterpMode == 'MaxSpeed' ? 'selected' : ''}>MaxSpeed</option>
            <option value="VideoFPS" ${minterpMode == 'VideoFPS' ? 'selected' : ''}>VideoFPS</option>
            <option value="MaxSpeedx2" ${minterpMode == 'MaxSpeedx2' ? 'selected' : ''}>MaxSpeedx2</option>
            <option value="VideoFPSx2" ${minterpMode == 'VideoFPSx2' ? 'selected' : ''}>VideoFPSx2</option>
          </select>
        </div>
        <div  title="${_tooltips.Tooltips.minterpFPSTooltip}">
          <span>FPS</span>
          <input id="minterp-fps-input" type="number" min="10" max="120" step="1" value="${minterpFPS !== null && minterpFPS !== void 0 ? minterpFPS : ''}" placeholder="" style="min-width:2em"></input>
        </div>
      </div>
      <div class="settings-editor-input-div multi-input-div" title="${_tooltips.Tooltips.vidstabTooltip}">
        <div>
          <span>Stabilization</span>
          <select id="video-stabilization-input">
            <option value="Inherit" ${vidstabDesc == null ? 'selected' : ''}>(Disabled)</option>
            <option ${vidstabDesc === 'Very Weak' ? 'selected' : ''}>Very Weak</option>
            <option ${vidstabDesc === 'Weak' ? 'selected' : ''}>Weak</option>
            <option ${vidstabDesc === 'Medium' ? 'selected' : ''}>Medium</option>
            <option ${vidstabDesc === 'Strong' ? 'selected' : ''}>Strong</option>
            <option ${vidstabDesc === 'Very Strong' ? 'selected' : ''}>Very Strong</option>
            <option ${vidstabDesc === 'Strongest' ? 'selected' : ''}>Strongest</option>
          </select>
        </div>
        <div title="${_tooltips.Tooltips.dynamicZoomTooltip}">
          <span>Dynamic Zoom</span>
          <select id="video-stabilization-dynamic-zoom-input">
            <option value="Default" ${vidstabDynamicZoomEnabled == null ? 'selected' : ''}>(Disabled)</option>
            <option ${vidstabDynamicZoomEnabled === false ? 'selected' : ''}>Disabled</option>
            <option ${vidstabDynamicZoomEnabled ? 'selected' : ''}>Enabled</option>
          </select>
        </div>
      </div>
      <div class="settings-editor-input-div multi-input-div" title="${_tooltips.Tooltips.loopTooltip}">
        <div>
          <span>Loop</span>
          <select id="loop-input">
          <option value="Default" ${settings.loop == null ? 'selected' : ''}>(none)</option>
          <option ${settings.loop === 'none' ? 'selected' : ''}>none</option>
            <option ${settings.loop === 'fwrev' ? 'selected' : ''}>fwrev</option>
            <option ${settings.loop === 'fade' ? 'selected' : ''}>fade</option>
          </select>
        </div>
        <div title="${_tooltips.Tooltips.fadeDurationTooltip}">
          <span>Fade Duration</span>
          <input id="fade-duration-input" type="number" min="0.1" step="0.1" value="${settings.fadeDuration != null ? settings.fadeDuration : ''}" placeholder="0.7" style="width:7em"></input>
        </div>
      </div>
    </fieldset>
    `);
    injectYtcWidget(globalSettingsEditorDiv);
    const settingsInputsConfigs = [['crop-res-input', 'cropRes', 'string']];
    const settingsInputsConfigsHighlightable = [['crop-input', 'newMarkerCrop', 'string'], ['speed-input', 'newMarkerSpeed', 'number'], ['title-suffix-input', 'titleSuffix', 'string'], ['merge-list-input', 'markerPairMergeList', 'string'], ['enable-hdr-input', 'enableHDR', 'ternary'], ['gamma-input', 'gamma', 'number'], ['encode-speed-input', 'encodeSpeed', 'number'], ['crf-input', 'crf', 'number'], ['target-max-bitrate-input', 'targetMaxBitrate', 'number'], ['rotate-0', 'rotate', 'string'], ['rotate-90-clock', 'rotate', 'string'], ['rotate-90-counterclock', 'rotate', 'string'], ['two-pass-input', 'twoPass', 'ternary'], ['audio-input', 'audio', 'ternary'], ['denoise-input', 'denoise', 'preset'], ['minterp-mode-input', 'minterpMode', 'inheritableString'], ['minterp-fps-input', 'minterpFPS', 'number'], ['video-stabilization-input', 'videoStabilization', 'preset'], ['video-stabilization-dynamic-zoom-input', 'videoStabilizationDynamicZoom', 'ternary'], ['loop-input', 'loop', 'inheritableString'], ['fade-duration-input', 'fadeDuration', 'number']];
    addSettingsInputListeners(settingsInputsConfigs, settings, false);
    addSettingsInputListeners(settingsInputsConfigsHighlightable, settings, true);
    cropInput = document.getElementById('crop-input');
    cropAspectRatioSpan = document.getElementById('crop-aspect-ratio');
    wasGlobalSettingsEditorOpen = true;
    isSettingsEditorOpen = true;
    addMarkerPairMergeListDurationsListener();
    addCropInputHotkeys();
    highlightModifiedSettings(settingsInputsConfigsHighlightable, settings);
  }
  function addSettingsInputListeners(inputs, target) {
    let highlightable = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    inputs.forEach(input => {
      const id = input[0];
      const targetProperty = input[1];
      const valueType = input[2] || 'string';
      const inputElem = document.getElementById(id);
      inputElem.addEventListener('focus', () => isHotkeysEnabled = false, false);
      inputElem.addEventListener('blur', () => isHotkeysEnabled = true, false);
      inputElem.addEventListener('change', e => updateSettingsValue(e, id, target, targetProperty, valueType, highlightable), false);
    });
  }
  function deleteSettingsEditor() {
    const settingsEditorDiv = document.getElementById('settings-editor-div');
    hideCropOverlay();
    (0, _util.deleteElement)(settingsEditorDiv);
    isSettingsEditorOpen = false;
    wasGlobalSettingsEditorOpen = false;
    markerHotkeysEnabled = false;
  }
  let isExtraSettingsEditorEnabled = false;
  function toggleMarkerPairOverridesEditor() {
    if (isSettingsEditorOpen) {
      const markerPairOverridesEditor = document.getElementById('marker-pair-overrides');
      if (markerPairOverridesEditor) {
        if (markerPairOverridesEditor.style.display === 'none') {
          markerPairOverridesEditor.style.display = 'block';
          isExtraSettingsEditorEnabled = true;
        } else {
          markerPairOverridesEditor.style.display = 'none';
          isExtraSettingsEditorEnabled = false;
        }
      }
      const globalEncodeSettingsEditor = document.getElementById('global-encode-settings');
      if (globalEncodeSettingsEditor) {
        if (globalEncodeSettingsEditor.style.display === 'none') {
          globalEncodeSettingsEditor.style.display = 'block';
          isExtraSettingsEditorEnabled = true;
        } else if (globalEncodeSettingsEditor.style.display === 'block') {
          globalEncodeSettingsEditor.style.display = 'none';
          isExtraSettingsEditorEnabled = false;
        }
      }
    }
  }
  function markerNumberingMouseOverHandler(e) {
    const targetMarker = e.target.marker;
    toggleMarkerPairEditorHandler(e, targetMarker);
  }
  function markerNumberingMouseDownHandler(e) {
    if (!(e.button === 0)) return;
    (0, _util.blockEvent)(e);
    const numbering = e.target;
    const numberingType = numbering.classList.contains('startMarkerNumbering') ? 'start' : 'end';
    const targetEndMarker = numbering.marker;
    const targetStartMarker = targetEndMarker.previousSibling;
    const targetMarker = numberingType === 'start' ? targetStartMarker : targetEndMarker;
    const markerPairIndex = parseInt(numbering.getAttribute('data-idx')) - 1;
    const markerPair = markerPairs[markerPairIndex];
    const markerTime = numberingType === 'start' ? markerPair.start : markerPair.end;
    // open editor of target marker corresponding to clicked numbering
    if (!isSettingsEditorOpen) {
      toggleOnMarkerPairEditor(targetEndMarker);
    } else {
      if (wasGlobalSettingsEditorOpen) {
        toggleOffGlobalSettingsEditor();
        toggleOnMarkerPairEditor(targetEndMarker);
      } else if (prevSelectedEndMarker != targetEndMarker) {
        toggleOffMarkerPairEditor();
        toggleOnMarkerPairEditor(targetEndMarker);
      }
    }
    (0, _util.seekToSafe)(video, markerTime);
    if (!e.altKey) return;
    const pointerId = e.pointerId;
    numbering.setPointerCapture(pointerId);
    const numberingRect = numbering.getBoundingClientRect();
    const progressBarRect = hooks.progressBar.getBoundingClientRect();
    const offsetX = e.pageX - numberingRect.left - numberingRect.width / 2;
    const offsetY = e.pageY - numberingRect.top;
    let prevPageX = e.pageX;
    let prevZoom = 1;
    function getDragTime(e) {
      let newTime = (0, _util.getVideoDuration)(platform, video) * (e.pageX - offsetX - progressBarRect.left) / progressBarRect.width;
      let prevTime = (0, _util.getVideoDuration)(platform, video) * (prevPageX - offsetX - progressBarRect.left) / progressBarRect.width;
      const zoom = (0, _util.clampNumber)((e.pageY - offsetY) / video.clientHeight, 0, 1);
      const zoomDelta = Math.abs(zoom - prevZoom);
      prevZoom = zoom;
      prevPageX = e.pageX;
      if (zoomDelta >= 0.0001) return video.getCurrentTime();
      let timeDelta = (0, _util.roundValue)(zoom * (newTime - prevTime), 0.01, 2);
      if (Math.abs(timeDelta) < 0.01) return video.getCurrentTime();
      let time = video.getCurrentTime() + timeDelta;
      time = numberingType === 'start' ? (0, _util.clampNumber)(time, 0, markerPair.end - 1e-3) : (0, _util.clampNumber)(time, markerPair.start + 1e-3, (0, _util.getVideoDuration)(platform, video));
      return time;
    }
    function dragNumbering(e) {
      const time = getDragTime(e);
      if (Math.abs(time - video.getCurrentTime()) < 0.01) return;
      moveMarker(targetMarker, time, false, false);
      (0, _util.seekToSafe)(video, time);
    }
    document.addEventListener('pointermove', dragNumbering);
    document.addEventListener('pointerup', e => {
      document.removeEventListener('pointermove', dragNumbering);
      numbering.releasePointerCapture(pointerId);
      const time = getDragTime(e);
      if (Math.abs(time - markerTime) < 0.001) return;
      moveMarker(targetMarker, time, true, true);
    }, {
      once: true,
      capture: true
    });
  }
  function toggleMarkerPairEditorHandler(e, targetMarker) {
    targetMarker = targetMarker !== null && targetMarker !== void 0 ? targetMarker : e.target;
    if (targetMarker && e.shiftKey) {
      toggleMarkerPairEditor(targetMarker);
    }
  }
  let isChartEnabled = false;
  function toggleMarkerPairEditor(targetMarker) {
    // if target marker is previously selected marker: toggle target on/off
    if (prevSelectedEndMarker === targetMarker && !wasGlobalSettingsEditorOpen) {
      isSettingsEditorOpen ? toggleOffMarkerPairEditor() : toggleOnMarkerPairEditor(targetMarker);
      // otherwise switching from a different marker pair or from global settings editor
    } else {
      // delete current settings editor appropriately
      if (isSettingsEditorOpen) {
        wasGlobalSettingsEditorOpen ? toggleOffGlobalSettingsEditor() : toggleOffMarkerPairEditor();
      }
      // create new marker pair settings editor
      toggleOnMarkerPairEditor(targetMarker);
    }
  }
  function toggleOnMarkerPairEditor(targetMarker) {
    prevSelectedEndMarker = targetMarker;
    const selectedMarkerPairIndex = parseInt(prevSelectedEndMarker.getAttribute('data-idx')) - 1;
    if (selectedMarkerPairIndex !== prevSelectedMarkerPairIndex) {
      (0, _cropChartSpec.setCurrentCropPoint)(null, 0);
    }
    exports.prevSelectedMarkerPairIndex = prevSelectedMarkerPairIndex = selectedMarkerPairIndex;
    highlightSelectedMarkerPair(targetMarker);
    enableMarkerHotkeys(targetMarker);
    // creating editor sets isSettingsEditorOpen to true
    createMarkerPairEditor(targetMarker);
    addCropInputHotkeys();
    loadChartData(speedChartInput);
    loadChartData(cropChartInput);
    showCropOverlay();
    if (isChartEnabled) {
      showChart();
    }
    targetMarker.classList.add('selected-marker');
    targetMarker.previousElementSibling.classList.add('selected-marker');
    const markerPair = markerPairs[prevSelectedMarkerPairIndex];
    markerPair.startNumbering.classList.add('selectedMarkerNumbering');
    markerPair.endNumbering.classList.add('selectedMarkerNumbering');
    if (isAutoHideUnselectedMarkerPairsOn) {
      autoHideUnselectedMarkerPairsStyle = (0, _util.injectCSS)(_css.autoHideUnselectedMarkerPairsCSS, 'auto-hide-unselected-marker-pairs-css');
    }
  }
  function toggleOffMarkerPairEditor() {
    let hardHide = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
    deleteSettingsEditor();
    hideSelectedMarkerPairOverlay(hardHide);
    hideCropOverlay();
    hideChart();
    prevSelectedEndMarker.classList.remove('selected-marker');
    prevSelectedEndMarker.previousElementSibling.classList.remove('selected-marker');
    const markerPair = markerPairs[prevSelectedMarkerPairIndex];
    markerPair.startNumbering.classList.remove('selectedMarkerNumbering');
    markerPair.endNumbering.classList.remove('selectedMarkerNumbering');
    if (isAutoHideUnselectedMarkerPairsOn) {
      (0, _util.deleteElement)(autoHideUnselectedMarkerPairsStyle);
    }
  }
  let autoHideUnselectedMarkerPairsStyle;
  let isAutoHideUnselectedMarkerPairsOn = false;
  function toggleAutoHideUnselectedMarkerPairs(e) {
    if (e.ctrlKey && !arrowKeyCropAdjustmentEnabled) {
      (0, _util.blockEvent)(e);
      if (!isAutoHideUnselectedMarkerPairsOn) {
        autoHideUnselectedMarkerPairsStyle = (0, _util.injectCSS)(_css.autoHideUnselectedMarkerPairsCSS, 'auto-hide-unselected-marker-pairs-css');
        isAutoHideUnselectedMarkerPairsOn = true;
        (0, _util.flashMessage)('Auto-hiding of unselected marker pairs enabled', 'green');
      } else {
        (0, _util.deleteElement)(autoHideUnselectedMarkerPairsStyle);
        isAutoHideUnselectedMarkerPairsOn = false;
        (0, _util.flashMessage)('Auto-hiding of unselected marker pairs disabled', 'red');
      }
    }
  }
  let speedInputLabel;
  let cropInputLabel;
  let cropInput;
  let speedInput;
  let enableZoomPanInput;
  let cropAspectRatioSpan;
  let markerPairNumberInput;
  function createMarkerPairEditor(targetMarker) {
    const markerPairIndex = parseInt(targetMarker.getAttribute('data-idx'), 10) - 1;
    const markerPair = markerPairs[markerPairIndex];
    const startTime = (0, _util.toHHMMSSTrimmed)(markerPair.start);
    const endTime = (0, _util.toHHMMSSTrimmed)(markerPair.end);
    const speed = markerPair.speed;
    const duration = (0, _util.toHHMMSSTrimmed)(markerPair.end - markerPair.start);
    const speedAdjustedDuration = (0, _util.toHHMMSSTrimmed)((markerPair.end - markerPair.start) / speed);
    const crop = markerPair.crop;
    const cropInputValidation = `\\d+:\\d+:(\\d+|iw):(\\d+|ih)`;
    const [x, y, w, h] = getCropComponents(crop);
    const cropAspectRatio = (w / h).toFixed(13);
    const settingsEditorDiv = document.createElement('div');
    const overrides = markerPair.overrides;
    const vidstab = overrides.videoStabilization;
    const vidstabDesc = vidstab ? vidstab.desc : null;
    const vidstabDescGlobal = settings.videoStabilization ? `(${settings.videoStabilization.desc})` : '(Disabled)';
    const vidstabDynamicZoomEnabled = overrides.videoStabilizationDynamicZoom;
    const minterpMode = overrides.minterpMode;
    const minterpFPS = overrides.minterpFPS;
    const denoise = overrides.denoise;
    const denoiseDesc = denoise ? denoise.desc : null;
    const denoiseDescGlobal = settings.denoise ? `(${settings.denoise.desc})` : '(Disabled)';
    const overridesEditorDisplay = isExtraSettingsEditorEnabled ? 'block' : 'none';
    createCropOverlay(crop);
    settingsEditorDiv.setAttribute('id', 'settings-editor-div');
    (0, _util.safeSetInnerHtml)(settingsEditorDiv, `
      <fieldset class="settings-editor-panel marker-pair-settings-editor-highlighted-div">
        <legend class="marker-pair-settings-editor-highlighted-label">Marker Pair
          <input id="marker-pair-number-input"
            title="${_tooltips.Tooltips.markerPairNumberTooltip}"
            type="number" value="${markerPairIndex + 1}"
            step="1" min="1" max="${markerPairs.length}" style="width:3em" required>
          </input>
          /
          <span id="marker-pair-count-label">${markerPairs.length}</span>
          Settings\
        </legend>
        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.speedTooltip}">
          <span id="speed-input-label">Speed</span>
          <input id="speed-input"type="number" placeholder="speed" value="${speed}"
            step="0.05" min="0.05" max="2" style="width:7ch" required></input>
        </div>
        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.cropTooltip}">
          <span id="crop-input-label">Crop</span>
          <input id="crop-input" value="${crop}" pattern="${cropInputValidation}"
          style="width:20ch" required></input>
        </div>
        <div class="settings-editor-input-div settings-info-display">
          <span>Crop Aspect Ratio</span>
          <br>
          <span id="crop-aspect-ratio">${cropAspectRatio}</span>
        </div>
        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.titlePrefixTooltip}">
          <span>Title Prefix</span>
          <input id="title-prefix-input" value="${overrides.titlePrefix != null ? overrides.titlePrefix : ''}" placeholder="None" style="width:20ch;text-align:right"></input>
        </div>
        <div class="settings-editor-input-div settings-info-display" title="${_tooltips.Tooltips.timeDurationTooltip}">
          <span>Time:</span>
          <span id="start-time">${startTime}</span>
          <span> - </span>
          <span id="end-time">${endTime}</span>
          <br>
          <span>Duration: </span>
          <span id="duration">${duration}/${markerPair.speed} = ${speedAdjustedDuration}</span>
        </div>
      </fieldset>
      <fieldset id="marker-pair-overrides" class="settings-editor-panel marker-pair-settings-editor-highlighted-div" style="display:${overridesEditorDisplay}">
        <legend class="marker-pair-settings-editor-highlighted-label">Overrides</legend>
        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.audioTooltip}">
          <span>Audio</span>
          <select id="audio-input">
            <option value="Default" ${overrides.audio == null ? 'selected' : ''}>${(0, _util.ternaryToString)(settings.audio)}</option>
            <option ${overrides.audio === false ? 'selected' : ''}>Disabled</option>
            <option ${overrides.audio ? 'selected' : ''}>Enabled</option>
          </select>
        </div>
        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.encodeSpeedTooltip}">
          <span>Encode Speed (0-5)</span>
          <input id="encode-speed-input" type="number" min="0" max="5" step="1" value="${overrides.encodeSpeed != null ? overrides.encodeSpeed : ''}" placeholder="${settings.encodeSpeed || 'Auto'}"  style="min-width:4em"></input>
        </div>
        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.CRFTooltip}">
          <span>CRF (0-63)</span>
          <input id="crf-input" type="number" min="0" max="63" step="1" value="${overrides.crf != null ? overrides.crf : ''}" placeholder="${settings.crf != null ? settings.crf : 'Auto'}" style="min-width:4em"></input>
        </div>
        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.targetBitrateTooltip}">
          <span>Bitrate (kb/s)</span>
          <input id="target-max-bitrate-input" type="number" min="0" max="10e5" step="100" value="${overrides.targetMaxBitrate != null ? overrides.targetMaxBitrate : ''}" placeholder="${settings.targetMaxBitrate || 'Auto'}" style="min-width:4em"></input>
        </div>
        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.twoPassTooltip}">
          <span>Two-Pass</span>
          <select id="two-pass-input">
            <option value="Default" ${overrides.twoPass == null ? 'selected' : ''}>
              ${(0, _util.ternaryToString)(settings.twoPass)}
            </option>
            <option ${overrides.twoPass === false ? 'selected' : ''}>Disabled</option>
            <option ${overrides.twoPass ? 'selected' : ''}>Enabled</option>
          </select>
        </div>

      <div class="settings-editor-input-div" title="${_tooltips.Tooltips.hdrTooltip}">
        <span>Enable HDR</span>
        <select id="enable-hdr-input">
          <option value="Default" ${overrides.enableHDR == null ? 'selected' : ''}>
            ${(0, _util.ternaryToString)(settings.enableHDR)}
          </option>
          <option ${overrides.enableHDR === false ? 'selected' : ''}>Disabled</option>
          <option ${overrides.enableHDR ? 'selected' : ''}>Enabled</option>
        </select>
      </div>

      </div>
        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.gammaTooltip}">
          <span>Gamma (0-4)</span>
          <input id="gamma-input" type="number" min="0.01" max="4.00" step="0.01" value="${overrides.gamma != null ? overrides.gamma : ''}" placeholder="${settings.gamma != null ? settings.gamma : '1'}" style="min-width:4em"></input>
        </div>

        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.denoiseTooltip}">
          <span>Denoise</span>
          <select id="denoise-input">
            <option value="Inherit" ${denoiseDesc == null ? 'selected' : ''}>${denoiseDescGlobal}</option>
            <option value="Disabled" ${denoiseDesc == 'Disabled' ? 'selected' : ''}>Disabled</option>
            <option ${denoiseDesc === 'Very Weak' ? 'selected' : ''}>Very Weak</option>
            <option ${denoiseDesc === 'Weak' ? 'selected' : ''}>Weak</option>
            <option ${denoiseDesc === 'Medium' ? 'selected' : ''}>Medium</option>
            <option ${denoiseDesc === 'Strong' ? 'selected' : ''}>Strong</option>
            <option ${denoiseDesc === 'Very Strong' ? 'selected' : ''}>Very Strong</option>
          </select>
        </div>
        <div class="settings-editor-input-div">
          <div title="${_tooltips.Tooltips.minterpModeTooltip}">
            <span>Minterpolation</span>
            <select id="minterp-mode-input">
              <option value="Default" ${minterpMode == null ? 'selected' : ''}>${settings.minterpMode != null ? `(${settings.minterpMode})` : '(Numeric)'}</option>
              <option ${minterpMode === 'None' ? 'selected' : ''}>None</option>
              <option ${minterpMode === 'Numeric' ? 'selected' : ''}>Numeric</option>
              <option value="MaxSpeed" ${minterpMode == 'MaxSpeed' ? 'selected' : ''}>MaxSpeed</option>
              <option value="VideoFPS" ${minterpMode == 'VideoFPS' ? 'selected' : ''}>VideoFPS</option>
              <option value="MaxSpeedx2" ${minterpMode == 'MaxSpeedx2' ? 'selected' : ''}>MaxSpeedx2</option>
              <option value="VideoFPSx2" ${minterpMode == 'VideoFPSx2' ? 'selected' : ''}>VideoFPSx2</option>
            </select>
          </div>
          <div title="${_tooltips.Tooltips.minterpFPSTooltip}">
            <span>FPS</span>
            <input id="minterp-fps-input" type="number" min="10" max="120" step="1" value="${minterpFPS !== null && minterpFPS !== void 0 ? minterpFPS : ''}" placeholder="" style="min-width:2em"></input>
          </div>
        </div>
        <div class="settings-editor-input-div multi-input-div" title="${_tooltips.Tooltips.vidstabTooltip}">
        <div>
          <span>Stabilization</span>
          <select id="video-stabilization-input">
              <option value="Inherit" ${vidstabDesc == null ? 'selected' : ''}>${vidstabDescGlobal}</option>
              <option value="Disabled" ${vidstabDesc == 'Disabled' ? 'selected' : ''}>Disabled</option>
              <option ${vidstabDesc === 'Very Weak' ? 'selected' : ''}>Very Weak</option>
              <option ${vidstabDesc === 'Weak' ? 'selected' : ''}>Weak</option>
              <option ${vidstabDesc === 'Medium' ? 'selected' : ''}>Medium</option>
              <option ${vidstabDesc === 'Strong' ? 'selected' : ''}>Strong</option>
              <option ${vidstabDesc === 'Very Strong' ? 'selected' : ''}>Very Strong</option>
              <option ${vidstabDesc === 'Strongest' ? 'selected' : ''}>Strongest</option>
            </select>
          </div>
          <div title="${_tooltips.Tooltips.dynamicZoomTooltip}">
            <span>Dynamic Zoom</span>
            <select id="video-stabilization-dynamic-zoom-input">
              <option value="Default" ${vidstabDynamicZoomEnabled == null ? 'selected' : ''}>${(0, _util.ternaryToString)(settings.videoStabilizationDynamicZoom)}</option>
              <option ${vidstabDynamicZoomEnabled === false ? 'selected' : ''}>Disabled</option>
              <option ${vidstabDynamicZoomEnabled ? 'selected' : ''}>Enabled</option>
            </select>
          </div>
        </div>
        <div class="settings-editor-input-div multi-input-div" title="${_tooltips.Tooltips.loopTooltip}">
          <div>
            <span>Loop</span>
            <select id="loop-input">
              <option value="Default" ${overrides.loop == null ? 'selected' : ''}>${settings.loop != null ? `(${settings.loop})` : '(none)'}</option>
              <option ${overrides.loop === 'none' ? 'selected' : ''}>none</option>
              <option ${overrides.loop === 'fwrev' ? 'selected' : ''}>fwrev</option>
              <option ${overrides.loop === 'fade' ? 'selected' : ''}>fade</option>
            </select>
          </div>
          <div title="${_tooltips.Tooltips.fadeDurationTooltip}">
            <span>Fade Duration</span>
            <input id="fade-duration-input" type="number" min="0.1" step="0.1" value="${overrides.fadeDuration != null ? overrides.fadeDuration : ''}" placeholder="${settings.fadeDuration != null ? settings.fadeDuration : '0.7'}" style="width:7em"></input>
          </div>
        </div>
        <div class="settings-editor-input-div" title="${_tooltips.Tooltips.enableZoomPanTooltip}">
          <span>ZoomPan</span>
            <select id="enable-zoom-pan-input">
              <option ${!markerPair.enableZoomPan ? 'selected' : ''}>Disabled</option>
              <option ${markerPair.enableZoomPan ? 'selected' : ''}>Enabled</option>
            </select>
        </div>
      </fieldset>
      `);
    injectYtcWidget(settingsEditorDiv);
    const inputConfigs = [['speed-input', 'speed', 'number'], ['crop-input', 'crop', 'string'], ['enable-zoom-pan-input', 'enableZoomPan', 'bool']];
    addSettingsInputListeners(inputConfigs, markerPair, true);
    const overrideInputConfigs = [['title-prefix-input', 'titlePrefix', 'string'], ['enable-hdr-input', 'enableHDR', 'ternary'], ['gamma-input', 'gamma', 'number'], ['encode-speed-input', 'encodeSpeed', 'number'], ['crf-input', 'crf', 'number'], ['target-max-bitrate-input', 'targetMaxBitrate', 'number'], ['two-pass-input', 'twoPass', 'ternary'], ['audio-input', 'audio', 'ternary'], ['minterp-mode-input', 'minterpMode', 'inheritableString'], ['minterp-fps-input', 'minterpFPS', 'number'], ['denoise-input', 'denoise', 'preset'], ['video-stabilization-input', 'videoStabilization', 'preset'], ['video-stabilization-dynamic-zoom-input', 'videoStabilizationDynamicZoom', 'ternary'], ['loop-input', 'loop', 'inheritableString'], ['fade-duration-input', 'fadeDuration', 'number']];
    addSettingsInputListeners(overrideInputConfigs, markerPair.overrides, true);
    markerPairNumberInput = document.getElementById('marker-pair-number-input');
    markerPairNumberInput.addEventListener('change', markerPairNumberInputHandler);
    speedInputLabel = document.getElementById('speed-input-label');
    speedInput = document.getElementById('speed-input');
    cropInputLabel = document.getElementById('crop-input-label');
    cropInput = document.getElementById('crop-input');
    cropAspectRatioSpan = document.getElementById('crop-aspect-ratio');
    enableZoomPanInput = document.getElementById('enable-zoom-pan-input');
    isSettingsEditorOpen = true;
    wasGlobalSettingsEditorOpen = false;
    if (isForceSetSpeedOn) {
      updateSpeedInputLabel(`Speed (${forceSetSpeedValue.toFixed(2)})`);
    }
    highlightModifiedSettings(inputConfigs, markerPair);
    highlightModifiedSettings(overrideInputConfigs, markerPair.overrides);
  }
  function markerPairNumberInputHandler(e) {
    const markerPair = markerPairs[prevSelectedMarkerPairIndex];
    const startNumbering = markerPair.startNumbering;
    const endNumbering = markerPair.endNumbering;
    const newIdx = e.target.value - 1;
    markerPairs.splice(newIdx, 0, ...markerPairs.splice(prevSelectedMarkerPairIndex, 1));
    let targetMarkerRect = markersSvg.children[newIdx * 2];
    let targetStartNumbering = startMarkerNumberings.children[newIdx];
    let targetEndNumbering = endMarkerNumberings.children[newIdx];
    // if target succeedes current marker pair, move pair after target
    if (newIdx > prevSelectedMarkerPairIndex) {
      targetMarkerRect = targetMarkerRect.nextElementSibling.nextElementSibling;
      targetStartNumbering = targetStartNumbering.nextElementSibling;
      targetEndNumbering = targetEndNumbering.nextElementSibling;
    }
    const prevSelectedStartMarker = prevSelectedEndMarker.previousElementSibling;
    // if target precedes current marker pair, move pair before target
    markersSvg.insertBefore(prevSelectedStartMarker, targetMarkerRect);
    markersSvg.insertBefore(prevSelectedEndMarker, targetMarkerRect);
    startMarkerNumberings.insertBefore(startNumbering, targetStartNumbering);
    endMarkerNumberings.insertBefore(endNumbering, targetEndNumbering);
    renumberMarkerPairs();
    exports.prevSelectedMarkerPairIndex = prevSelectedMarkerPairIndex = newIdx;
  }
  function highlightModifiedSettings(inputs, target) {
    if (isSettingsEditorOpen) {
      const markerPairSettingsLabelHighlight = 'marker-pair-settings-editor-highlighted-label';
      const globalSettingsLabelHighlight = 'global-settings-editor-highlighted-label';
      const inheritedSettingsLabelHighlight = 'inherited-settings-highlighted-label';
      let markerPair;
      if (!wasGlobalSettingsEditorOpen && prevSelectedMarkerPairIndex != null) {
        markerPair = markerPairs[prevSelectedMarkerPairIndex];
      }
      inputs.forEach(input => {
        const [id, targetProperty, valueType] = input;
        const inputElem = document.getElementById(id);
        const storedTargetValue = target[targetProperty];
        let label = inputElem.previousElementSibling;
        if (id === 'rotate-90-clock' || id === 'rotate-90-counterclock') label = inputElem.parentElement.getElementsByTagName('span')[0];
        if (storedTargetValue == null) {
          inputElem.classList.add(inheritedSettingsLabelHighlight);
        } else {
          inputElem.classList.remove(inheritedSettingsLabelHighlight);
        }
        let shouldRemoveHighlight = storedTargetValue == null || storedTargetValue === '' || valueType === 'bool' && storedTargetValue === false;
        if (target === settings) {
          shouldRemoveHighlight || (shouldRemoveHighlight = id === 'title-suffix-input' && storedTargetValue == `[${settings.videoID}]` || id === 'speed-input' && storedTargetValue === 1 || id === 'crop-input' && (storedTargetValue === '0:0:iw:ih' || storedTargetValue === `0:0:${settings.cropResWidth}:${settings.cropResHeight}`) || id === 'rotate-0');
        }
        if (shouldRemoveHighlight) {
          label.classList.remove(globalSettingsLabelHighlight);
          label.classList.remove(markerPairSettingsLabelHighlight);
          return;
        }
        if (target === settings) {
          label.classList.add(globalSettingsLabelHighlight);
        } else {
          let settingsProperty = targetProperty;
          if (targetProperty === 'speed') settingsProperty = 'newMarkerSpeed';
          if (targetProperty === 'crop') settingsProperty = 'newMarkerCrop';
          let globalValue = settings[settingsProperty];
          let shouldApplyGlobalHighlight = storedTargetValue === globalValue;
          if (targetProperty === 'crop') {
            shouldApplyGlobalHighlight = cropStringsEqual(storedTargetValue, globalValue);
            shouldApplyGlobalHighlight = shouldApplyGlobalHighlight && isStaticCrop(markerPair.cropMap);
          }
          if (shouldApplyGlobalHighlight) {
            label.classList.add(globalSettingsLabelHighlight);
            label.classList.remove(markerPairSettingsLabelHighlight);
          } else {
            label.classList.add(markerPairSettingsLabelHighlight);
            label.classList.remove(globalSettingsLabelHighlight);
          }
        }
      });
    }
  }
  function enableMarkerHotkeys(endMarker) {
    markerHotkeysEnabled = true;
    enableMarkerHotkeys.endMarker = endMarker;
    enableMarkerHotkeys.startMarker = endMarker.previousSibling;
  }
  function moveMarker(marker, newTime) {
    let storeHistory = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
    let adjustCharts = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
    const type = marker.getAttribute('type');
    const idx = parseInt(marker.getAttribute('data-idx')) - 1;
    const markerPair = markerPairs[idx];
    const toTime = newTime != null ? newTime : video.getCurrentTime();
    if (type === 'start' && toTime >= markerPair.end) {
      (0, _util.flashMessage)('Start marker cannot be placed after or at end marker', 'red');
      return;
    }
    if (type === 'end' && toTime <= markerPair.start) {
      (0, _util.flashMessage)('End marker cannot be placed before or at start marker', 'red');
      return;
    }
    const initialState = (0, _undoredo.getMarkerPairHistory)(markerPair);
    const draft = (0, _immer.createDraft)(initialState);
    const lastState = (0, _undoredo.peekLastState)(markerPair.undoredo);
    const isStretch = type === 'start' ? toTime <= lastState.start : toTime >= lastState.end;
    draft[type] = toTime;
    if (adjustCharts) {
      if (isStretch) {
        draft.speedMap = stretchPointMap(draft, draft.speedMap, 'speed', toTime, type);
        draft.cropMap = stretchPointMap(draft, draft.cropMap, 'crop', toTime, type);
      } else {
        draft.speedMap = shrinkPointMap(draft, draft.speedMap, 'speed', toTime, type);
        draft.cropMap = shrinkPointMap(draft, draft.cropMap, 'crop', toTime, type);
      }
    }
    (0, _undoredo.saveMarkerPairHistory)(draft, markerPair, storeHistory);
    renderSpeedAndCropUI(adjustCharts, adjustCharts);
  }
  function stretchPointMap(draft, pointMap, pointType, toTime, type) {
    const maxIndex = pointMap.length - 1;
    const [sectStart, sectEnd] = type === 'start' ? [0, 1] : [maxIndex - 1, maxIndex];
    const leftPoint = pointMap[sectStart];
    const rightPoint = pointMap[sectEnd];
    const targetPoint = type === 'start' ? leftPoint : rightPoint;
    const isSectionStatic = pointType === 'crop' ? cropStringsEqual(leftPoint.crop, rightPoint.crop) : leftPoint.y === rightPoint.y;
    if (isSectionStatic) {
      targetPoint.x = toTime;
    } else {
      const targetPointCopy = (0, _lodash.default)(targetPoint);
      targetPointCopy.x = toTime;
      type === 'start' ? pointMap.unshift(targetPointCopy) : pointMap.push(targetPointCopy);
    }
    return pointMap;
  }
  function shrinkPointMap(draft, pointMap, pointType, toTime, type) {
    const maxIndex = pointMap.length - 1;
    const searchPoint = {
      x: toTime,
      y: 0,
      crop: ''
    };
    let [sectStart, sectEnd] = (0, _util.bsearch)(pointMap, searchPoint, _chartutil.sortX);
    if (sectStart <= 0) {
      [sectStart, sectEnd] = [0, 1];
    } else if (sectStart >= maxIndex) {
      [sectStart, sectEnd] = [maxIndex - 1, maxIndex];
    } else {
      [sectStart, sectEnd] = [sectStart, sectStart + 1];
    }
    const leftPoint = pointMap[sectStart];
    const rightPoint = pointMap[sectEnd];
    const targetPointIndex = type === 'start' ? sectStart : sectEnd;
    const targetPoint = pointMap[targetPointIndex];
    if (pointType === 'crop') {
      let toCropString = getInterpolatedCrop(leftPoint, rightPoint, toTime);
      let [x, y, w, h] = getCropComponents(targetPoint.crop);
      const toCrop = new _crop.Crop(x, y, w, h, settings.cropResWidth, settings.cropResHeight);
      toCrop.setCropStringSafe(toCropString, draft.enableZoomPan);
      targetPoint.crop = toCrop.cropString;
      setAspectRatioForAllPoints(toCrop.aspectRatio, pointMap, pointMap, targetPointIndex);
      if (type === 'start') draft.crop = toCrop.cropString;
    } else {
      const speed = getInterpolatedSpeed(leftPoint, rightPoint, toTime);
      targetPoint.y = speed;
      if (type === 'start') draft.speed = speed;
    }
    targetPoint.x = toTime;
    pointMap = pointMap.filter(point => {
      const keepPoint = point === targetPoint || (type === 'start' ? point.x > toTime : point.x < toTime);
      return keepPoint;
    });
    return pointMap;
  }
  function renderMarkerPair(markerPair, markerPairIndex) {
    const startMarker = markersSvg.querySelector(`.start-marker[data-idx="${markerPairIndex + 1}"]`);
    const endMarker = markersSvg.querySelector(`.end-marker[data-idx="${markerPairIndex + 1}"]`);
    const startMarkerNumbering = startMarkerNumberings.children[markerPairIndex];
    const endMarkerNumbering = endMarkerNumberings.children[markerPairIndex];
    const startProgressPos = markerPair.start / (0, _util.getVideoDuration)(platform, video) * 100;
    const endProgressPos = markerPair.end / (0, _util.getVideoDuration)(platform, video) * 100;
    startMarker.setAttribute('x', `${startProgressPos}%`);
    startMarkerNumbering.setAttribute('x', `${startProgressPos}%`);
    selectedStartMarkerOverlay.setAttribute('x', `${startProgressPos}%`);
    endMarker.setAttribute('x', `${endProgressPos}%`);
    endMarkerNumbering.setAttribute('x', `${endProgressPos}%`);
    selectedEndMarkerOverlay.setAttribute('x', `${endProgressPos}%`);
    const startMarkerTimeSpan = document.getElementById(`start-time`);
    const endMarkerTimeSpan = document.getElementById(`end-time`);
    startMarkerTimeSpan.textContent = `${(0, _util.toHHMMSSTrimmed)(markerPair.start)}`;
    endMarkerTimeSpan.textContent = `${(0, _util.toHHMMSSTrimmed)(markerPair.end)}`;
    updateMarkerPairDuration(markerPair);
  }
  function updateCharts(markerPair) {
    let rerender = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    const speedChart = speedChartInput.chart;
    if (speedChart) {
      speedChart.config.data.datasets[0].data = markerPair.speedMap;
      updateChartBounds(speedChart.config, markerPair.start, markerPair.end);
    }
    const cropChart = cropChartInput.chart;
    if (cropChart) {
      cropChart.config.data.datasets[0].data = markerPair.cropMap;
      updateChartBounds(cropChart.config, markerPair.start, markerPair.end);
    }
    if (rerender) rerenderCurrentChart();
  }
  function rerenderCurrentChart() {
    if (isCurrentChartVisible && currentChartInput && currentChartInput.chart) {
      currentChartInput.chart.update();
    }
  }
  const presetsMap = {
    videoStabilization: {
      Disabled: {
        desc: 'Disabled',
        enabled: false
      },
      'Very Weak': {
        desc: 'Very Weak',
        enabled: true,
        shakiness: 2,
        smoothing: 2,
        zoomspeed: 0.05
      },
      Weak: {
        desc: 'Weak',
        enabled: true,
        shakiness: 4,
        smoothing: 4,
        zoomspeed: 0.1
      },
      Medium: {
        desc: 'Medium',
        enabled: true,
        shakiness: 6,
        smoothing: 6,
        zoomspeed: 0.2
      },
      Strong: {
        desc: 'Strong',
        enabled: true,
        shakiness: 8,
        smoothing: 10,
        zoomspeed: 0.3
      },
      'Very Strong': {
        desc: 'Very Strong',
        enabled: true,
        shakiness: 10,
        smoothing: 16,
        zoomspeed: 0.4
      },
      Strongest: {
        desc: 'Strongest',
        enabled: true,
        shakiness: 10,
        smoothing: 22,
        zoomspeed: 0.5
      }
    },
    denoise: {
      Disabled: {
        enabled: false,
        desc: 'Disabled'
      },
      'Very Weak': {
        enabled: true,
        lumaSpatial: 1,
        desc: 'Very Weak'
      },
      Weak: {
        enabled: true,
        lumaSpatial: 2,
        desc: 'Weak'
      },
      Medium: {
        enabled: true,
        lumaSpatial: 4,
        desc: 'Medium'
      },
      Strong: {
        enabled: true,
        lumaSpatial: 6,
        desc: 'Strong'
      },
      'Very Strong': {
        enabled: true,
        lumaSpatial: 8,
        desc: 'Very Strong'
      }
    }
  };
  function updateSettingsValue(e, id, target, targetProperty, valueType, highlightable) {
    if (e.target.reportValidity()) {
      const prevValue = e.target.value;
      let newValue = e.target.value;
      if (newValue != null) {
        if (targetProperty !== 'titleSuffix' && targetProperty !== 'markerPairMergeList' && newValue === '') {
          delete target[targetProperty];
          newValue = undefined;
        } else if (valueType === 'number') {
          newValue = parseFloat(newValue);
        } else if (valueType === 'bool') {
          if (newValue === 'Enabled') {
            newValue = true;
          } else if (newValue === 'Disabled') {
            newValue = false;
          }
        } else if (valueType === 'ternary' || valueType === 'inheritableString') {
          if (newValue === 'Default' || newValue === 'Inherit') {
            delete target[targetProperty];
            newValue = undefined;
          } else if (newValue === 'Enabled') {
            newValue = true;
          } else if (newValue === 'Disabled') {
            newValue = false;
          }
        } else if (valueType === 'preset') {
          if (newValue === 'Inherit') {
            delete target[targetProperty];
            newValue = undefined;
          } else {
            newValue = presetsMap[targetProperty][newValue];
          }
        }
      }
      if (!['crop', 'enableZoomPan', 'cropRes'].includes(targetProperty)) {
        target[targetProperty] = newValue;
      }
      if (targetProperty === 'newMarkerCrop') {
        const newCrop = transformCropWithPushBack(prevValue, newValue);
        updateCropString(newCrop, true);
      }
      if (targetProperty === 'cropRes') {
        const {
          cropMultipleX,
          cropMultipleY,
          newWidth,
          newHeight
        } = getCropMultiples(settings.cropRes, newValue);
        settings.cropRes = newValue;
        settings.cropResWidth = newWidth;
        settings.cropResHeight = newHeight;
        _crop.Crop._minW = Math.round(_crop.Crop.minW * cropMultipleX);
        _crop.Crop._minH = Math.round(_crop.Crop.minH * cropMultipleY);
        multiplyAllCrops(cropMultipleX, cropMultipleY);
      }
      if (targetProperty === 'crop') {
        const markerPair = target;
        setCropString(markerPair, newValue);
      }
      if (targetProperty === 'speed') {
        const markerPair = markerPairs[prevSelectedMarkerPairIndex];
        updateMarkerPairSpeed(markerPair, newValue);
        renderSpeedAndCropUI();
      }
      if (targetProperty === 'enableZoomPan') {
        const markerPair = markerPairs[prevSelectedMarkerPairIndex];
        const cropMap = markerPair.cropMap;
        const draft = (0, _immer.createDraft)((0, _undoredo.getMarkerPairHistory)(markerPair));
        const cropString = cropMap[_cropChartSpec.currentCropPointIndex].crop;
        const enableZoomPan = newValue;
        const cropRes = settings.cropRes;
        if (!enableZoomPan && (0, _crop.isVariableSize)(cropMap, cropRes)) {
          video.pause();
          const {
            minSizeW,
            minSizeH,
            maxSizeW,
            maxSizeH,
            avgSizeW,
            avgSizeH
          } = (0, _crop.getMinMaxAvgCropPoint)(cropMap, cropRes);
          const crop = _crop.Crop.fromCropString(cropString, settings.cropRes);
          const tooltip = _tooltips.Tooltips.zoomPanToPanOnlyTooltip(minSizeW, minSizeH, maxSizeW, maxSizeH, avgSizeW, avgSizeH);
          const desiredSize = prompt(tooltip, 's');
          let w;
          let h;
          switch (desiredSize) {
            case 's':
              [w, h] = [minSizeW, minSizeH];
              break;
            case 'l':
              [w, h] = [maxSizeW, maxSizeH];
              break;
            case 'a':
              [w, h] = [avgSizeW, avgSizeH];
              break;
            case null:
              (0, _util.flashMessage)('Zoompan not disabled (canceled).', 'olive');
              e.target.value = 'Enabled';
              return;
            default:
              (0, _util.flashMessage)("Zoompan not disabled. Please enter 's' for smallest, 'l' for largest, or 'a' for average.", 'red');
              e.target.value = 'Enabled';
              return;
          }
          draft.enableZoomPan = false;
          (0, _undoredo.saveMarkerPairHistory)(draft, markerPair, false);
          crop.setCropStringSafe((0, _util.getCropString)(crop.x, crop.y, w, h));
          setCropString(markerPair, crop.cropString, true);
          (0, _util.flashMessage)(`Zoompan disabled. All crop points set to size ${w}x${h}.`, 'green');
        } else {
          draft.enableZoomPan = enableZoomPan;
          (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
          renderSpeedAndCropUI();
        }
      }
    }
    if (highlightable) highlightModifiedSettings([[id, targetProperty, valueType]], target);
  }
  function setCropString(markerPair, newCrop) {
    let forceCropConstraints = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    const prevCrop = markerPair.cropMap[_cropChartSpec.currentCropPointIndex].crop;
    const {
      isDynamicCrop,
      enableZoomPan,
      initCropMap
    } = getCropMapProperties();
    const shouldMaintainCropAspectRatio = enableZoomPan && isDynamicCrop;
    const crop = transformCropWithPushBack(prevCrop, newCrop, shouldMaintainCropAspectRatio);
    updateCropString(crop, true, forceCropConstraints, initCropMap);
  }
  function getCropMultiples(oldCropRes, newCropRes) {
    const [oldWidth, oldHeight] = oldCropRes.split('x').map(str => parseInt(str), 10);
    const [newWidth, newHeight] = newCropRes.split('x').map(str => parseInt(str), 10);
    const cropMultipleX = newWidth / oldWidth;
    const cropMultipleY = newHeight / oldHeight;
    return {
      cropMultipleX,
      cropMultipleY,
      newWidth,
      newHeight
    };
  }
  function multiplyAllCrops(cropMultipleX, cropMultipleY) {
    const cropString = settings.newMarkerCrop;
    const multipliedCropString = multiplyCropString(cropMultipleX, cropMultipleY, cropString);
    settings.newMarkerCrop = multipliedCropString;
    cropInput.value = multipliedCropString;
    markerPairs.forEach(markerPair => {
      multiplyMarkerPairCrops(markerPair, cropMultipleX, cropMultipleY);
    });
  }
  function multiplyMarkerPairCrops(markerPair, cropMultipleX, cropMultipleY) {
    markerPair.cropRes = settings.cropRes;
    const draft = (0, _immer.createDraft)((0, _undoredo.getMarkerPairHistory)(markerPair));
    draft.cropMap.forEach((cropPoint, idx) => {
      const multipliedCropString = multiplyCropString(cropMultipleX, cropMultipleY, cropPoint.crop);
      cropPoint.crop = multipliedCropString;
      if (idx === 0) draft.crop = multipliedCropString;
    });
    (0, _undoredo.saveMarkerPairHistory)(draft, markerPair, false);
  }
  function multiplyCropString(cropMultipleX, cropMultipleY, cropString) {
    let [x, y, w, h] = cropString.split(':');
    x = Math.round(x * cropMultipleX);
    y = Math.round(y * cropMultipleY);
    w = w !== 'iw' ? Math.round(w * cropMultipleX) : w;
    h = h !== 'ih' ? Math.round(h * cropMultipleY) : h;
    const multipliedCropString = [x, y, w, h].join(':');
    return multipliedCropString;
  }
  function getMarkerPairMergeListDurations() {
    let markerPairMergeList = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : settings.markerPairMergeList;
    const durations = [];
    for (let merge of markerPairMergeList.split(';')) {
      let duration = 0;
      for (let mergeRange of merge.split(',')) {
        if (mergeRange.includes('-')) {
          let [mergeRangeStart, mergeRangeEnd] = mergeRange.split('-').map(str => parseInt(str, 10) - 1);
          if (mergeRangeStart > mergeRangeEnd) {
            [mergeRangeStart, mergeRangeEnd] = [mergeRangeEnd, mergeRangeStart];
          }
          for (let idx = mergeRangeStart; idx <= mergeRangeEnd; idx++) {
            if (!isNaN(idx) && idx >= 0 && idx < markerPairs.length) {
              const marker = markerPairs[idx];
              duration += (marker.end - marker.start) / marker.speed;
            }
          }
        } else {
          const idx = parseInt(mergeRange, 10) - 1;
          if (!isNaN(idx) && idx >= 0 && idx < markerPairs.length) {
            const marker = markerPairs[idx];
            duration += (marker.end - marker.start) / marker.speed;
          }
        }
      }
      durations.push(duration);
    }
    const markerPairMergelistDurations = durations.map(_util.toHHMMSSTrimmed).join(' ; ');
    return markerPairMergelistDurations;
  }
  function addCropInputHotkeys() {
    cropInput.addEventListener('keydown', ke => {
      if (ke.code === 'Space' || !ke.ctrlKey && !ke.altKey && 66 <= ke.which && ke.which <= 90 && !(ke.code === 'KeyI' || ke.code === 'KeyW' || ke.code === 'KeyH') || ke.which === 65 && (ke.ctrlKey || ke.altKey) // blur on KeyA with ctrl or alt modifiers
      ) {
        (0, _util.blockEvent)(ke);
        cropInput.blur();
        (0, _util.flashMessage)('Auto blurred crop input focus', 'olive');
        return;
      }
      if (ke.code === 'ArrowUp' || ke.code === 'ArrowDown' || ke.code === 'KeyA' && !ke.ctrlKey && !ke.altKey) {
        (0, _util.blockEvent)(ke);
        let cropString = cropInput.value;
        let cropStringArray = cropString.split(':');
        const initialCropArray = getCropComponents(cropString);
        let cropArray = [...initialCropArray];
        const cropStringCursorPos = ke.target.selectionStart;
        let cropComponentCursorPos = cropStringCursorPos;
        let cropTarget = 0;
        while (cropComponentCursorPos - (cropStringArray[cropTarget].length + 1) >= 0) {
          cropComponentCursorPos -= cropStringArray[cropTarget].length + 1;
          cropTarget++;
        }
        const isValidCropTarget = cropTarget >= 0 && cropTarget <= cropArray.length - 1 && typeof cropArray[cropTarget] === 'number';
        if (!isValidCropTarget) return;
        if (ke.code === 'KeyA' && !wasGlobalSettingsEditorOpen) {
          const markerPair = markerPairs[prevSelectedMarkerPairIndex];
          const initState = (0, _undoredo.getMarkerPairHistory)(markerPair);
          const draft = (0, _immer.createDraft)(initState);
          const draftCropMap = draft.cropMap;
          const {
            enableZoomPan
          } = getCropMapProperties();
          const [ix, iy, iw, ih] = initialCropArray;
          if (cropTarget === 0 || cropTarget === 1 || enableZoomPan && (cropTarget === 2 || cropTarget === 3)) {
            draftCropMap.forEach((cropPoint, idx) => {
              if (!ke.shiftKey && idx <= _cropChartSpec.currentCropPointIndex || ke.shiftKey && idx >= _cropChartSpec.currentCropPointIndex) {
                return;
              }
              let [x, y, w, h] = getCropComponents(cropPoint.crop);
              if (cropTarget === 0) x = ix;
              if (cropTarget === 1) y = iy;
              if (cropTarget === 2 || cropTarget === 3) {
                w = iw;
                h = ih;
              }
              cropPoint.crop = [x, y, w, h].join(':');
              if (idx === 0) draft.crop = cropPoint.crop;
            });
            (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
            renderSpeedAndCropUI();
          }
          const targetPointsMsg = `${ke.shiftKey ? 'preceding' : 'following'} point ${_cropChartSpec.currentCropPointIndex + 1}`;
          if (cropTarget === 0) (0, _util.flashMessage)(`Updated X values of crop points ${targetPointsMsg} to ${ix}`, 'green');
          if (cropTarget === 1) (0, _util.flashMessage)(`Updated Y values crop points ${targetPointsMsg} Y values to ${iy}`, 'green');
          if (enableZoomPan && (cropTarget === 2 || cropTarget === 3)) (0, _util.flashMessage)(`Updated size of all crop points ${targetPointsMsg} to ${iw}x${ih}`, 'green');
          if (!enableZoomPan && (cropTarget === 2 || cropTarget === 3)) {
            (0, _util.flashMessage)(`All crop points have the same size in pan-only mode`, 'olive');
          }
        } else if (ke.code === 'ArrowUp' || ke.code === 'ArrowDown') {
          let changeAmount;
          let [ix, iy, iw, ih] = getCropComponents(cropInput.value);
          if (!ke.altKey && !ke.shiftKey) {
            changeAmount = 10;
          } else if (ke.altKey && !ke.shiftKey) {
            changeAmount = 1;
          } else if (!ke.altKey && ke.shiftKey) {
            changeAmount = 50;
          } else if (ke.altKey && ke.shiftKey) {
            changeAmount = 100;
          }
          const {
            isDynamicCrop,
            enableZoomPan
          } = getCropMapProperties();
          const shouldMaintainCropAspectRatio = enableZoomPan && isDynamicCrop;
          const cropResWidth = settings.cropResWidth;
          const cropResHeight = settings.cropResHeight;
          const crop = new _crop.Crop(ix, iy, iw, ih, cropResWidth, cropResHeight);
          // without modifiers move crop x/y offset
          // with ctrl key modifier expand/shrink crop width/height
          if (cropTarget === 0) {
            ke.code === 'ArrowUp' ? crop.panX(changeAmount) : crop.panX(-changeAmount);
          } else if (cropTarget === 1) {
            ke.code === 'ArrowUp' ? crop.panY(changeAmount) : crop.panY(-changeAmount);
          } else {
            let cursor;
            if (cropTarget === 2) cursor = 'e-resize';
            if (cropTarget === 3) cursor = 's-resize';
            if (ke.code === 'ArrowDown') changeAmount = -changeAmount;
            resizeCrop(crop, cursor, changeAmount, changeAmount, shouldMaintainCropAspectRatio);
          }
          const {
            initCropMap
          } = getCropMapProperties();
          updateCropString(crop.cropString, true, false, initCropMap);
          const updatedCropString = cropInput.value;
          let newCursorPos = cropStringCursorPos - cropComponentCursorPos;
          if (cropTarget === 3 && cropStringArray[3] === 'ih') {
            const cropStringLengthDelta = updatedCropString.length - cropString.length;
            const cursorPosAdjustment = cropStringLengthDelta - cropComponentCursorPos;
            newCursorPos += cursorPosAdjustment;
          }
          cropInput.selectionStart = newCursorPos;
          cropInput.selectionEnd = newCursorPos;
        }
      }
    });
  }
  function addMarkerPairMergeListDurationsListener() {
    const markerPairMergeListInput = document.getElementById('merge-list-input');
    const markerPairMergeListDurationsSpan = document.getElementById('merge-list-durations');
    markerPairMergeListInput.addEventListener('change', () => {
      const markerPairMergelistDurations = getMarkerPairMergeListDurations();
      markerPairMergeListDurationsSpan.textContent = markerPairMergelistDurations;
    });
  }
  let shortcutsTableToggleButton;
  function injectToggleShortcutsTableButton() {
    shortcutsTableToggleButton = (0, _util.htmlToElement)(shortcutsTableToggleButtonHTML);
    shortcutsTableToggleButton.onclick = toggleShortcutsTable;
    if ([_platforms.VideoPlatforms.weverse, _platforms.VideoPlatforms.naver_tv].includes(platform)) {
      shortcutsTableToggleButton.classList.add('pzp-button', 'pzp-subtitle-button', 'pzp-pc-subtitle-button', 'pzp-pc__subtitle-button');
    }
    if ([_platforms.VideoPlatforms.afreecatv].includes(platform)) {
      shortcutsTableToggleButton.classList.add('btn_statistics');
    }
    hooks.shortcutsTableButton.insertAdjacentElement('afterbegin', shortcutsTableToggleButton);
  }
  function showShortcutsTableToggleButton() {
    if (shortcutsTableToggleButton) {
      shortcutsTableToggleButton.style.display = 'inline-block';
    }
  }
  function hideShortcutsTableToggleButton() {
    if (shortcutsTableToggleButton) {
      shortcutsTableToggleButton.style.display = 'none';
    }
  }
  let shortcutsTableContainer;
  function toggleShortcutsTable() {
    if (!shortcutsTableContainer) {
      (0, _util.injectCSS)(shortcutsTableStyle, 'shortcutsTableStyle');
      shortcutsTableContainer = document.createElement('div');
      shortcutsTableContainer.setAttribute('id', 'shortcutsTableContainer');
      (0, _util.safeSetInnerHtml)(shortcutsTableContainer, shortcutsTableHTML);
      hooks.shortcutsTable.insertAdjacentElement('beforebegin', shortcutsTableContainer);
    } else if (shortcutsTableContainer.style.display !== 'none') {
      shortcutsTableContainer.style.display = 'none';
    } else {
      shortcutsTableContainer.style.display = 'block';
    }
  }
  const frameCaptureViewerHeadHTML = `
      <title>yt_clipper Frame Capture Viewer</title>
      <style>
        body {
          margin: 0px;
          text-align: center;
        }
        #frames-div {
          font-family: Helvetica;
          background-color: rgb(160,50,20);
          margin: 0 auto;
          padding: 2px;
          width: 99%;
          text-align: center;
        }
        .frame-div {
          margin: 2px;
          padding: 2px;
          border: 2px black solid;
          font-weight: bold;
          color: black;
          text-align: center;
        }
        figcaption {
          display: inline-block;
          margin: 2px;
        }
        button {
          display: inline-block;
          font-weight: bold;
          margin-bottom: 2px;
          cursor: pointer;
          border: 2px solid black;
          border-radius: 4px;
        }
        button.download {
          background-color: rgb(66, 134, 244);
        }
        button.delete {
          background-color: red;
        }
        button:hover {
          box-shadow: 2px 4px 4px 0 rgba(0,0,0,0.2);
        }
        canvas {
          display: block;
          margin: 0 auto;
          ${videoInfo.aspectRatio > 1 ? 'width: 98%;' : 'height: 96vh;'}
        }
        @keyframes flash {
          0% {
            opacity: 1;
          }
          100% {
            opacity: 0.5;
          }
        }
        .flash-div {
          animation-name: flash;
          animation-duration: 0.5s;
          animation-fill-mode: forwards;
        }
        </style>
      `;
  const frameCaptureViewerBodyHTML = `\
        <div id="frames-div"><strong></strong></div>
        `;
  let frameCaptureViewerWindow;
  let frameCaptureViewerDoc;
  async function captureFrame() {
    const currentTime = video.getCurrentTime();
    for (let i = 0; i < video.buffered.length; i++) {
      console.log(video.buffered.start(i), video.buffered.end(i));
      if (video.buffered.start(i) <= currentTime && currentTime <= video.buffered.end(i)) {
        break;
      }
      if (i === video.buffered.length - 1) {
        (0, _util.flashMessage)('Frame not captured. Video has not yet buffered the frame.', 'red');
        return;
      }
    }
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    let resString;
    if (isSettingsEditorOpen) {
      const cropMultipleX = video.videoWidth / settings.cropResWidth;
      const cropMultipleY = video.videoHeight / settings.cropResHeight;
      if (!wasGlobalSettingsEditorOpen) {
        const idx = parseInt(prevSelectedEndMarker.getAttribute('data-idx'), 10) - 1;
        const markerPair = markerPairs[idx];
        resString = multiplyCropString(cropMultipleX, cropMultipleY, markerPair.crop);
      } else {
        resString = multiplyCropString(cropMultipleX, cropMultipleY, settings.newMarkerCrop);
      }
      const cropRes = _crop.Crop.getMultipliedCropRes(settings.cropRes, cropMultipleX, cropMultipleY);
      const [x, y, w, h] = _crop.Crop.getCropComponents(resString, cropRes);
      canvas.width = w;
      canvas.height = h;
      if (h > w) {
        canvas.style.height = '96vh';
        canvas.style.width = 'auto';
      }
      context.drawImage(video, x, y, w, h, 0, 0, w, h);
      resString = `x${x}y${y}w${w}h${h}`;
    } else {
      resString = `x0y0w${video.videoWidth}h${video.videoHeight}`;
      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;
      context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
    }
    if (!frameCaptureViewerWindow || !frameCaptureViewerDoc || frameCaptureViewerWindow.closed) {
      frameCaptureViewerWindow = window.open('', 'window', `height=${window.innerHeight}, width=${window.innerWidth}`);
      frameCaptureViewerDoc = frameCaptureViewerWindow.document;
      (0, _util.safeSetInnerHtml)(frameCaptureViewerDoc.head, frameCaptureViewerHeadHTML, true);
      (0, _util.safeSetInnerHtml)(frameCaptureViewerDoc.body, frameCaptureViewerBodyHTML, true);
    }
    const frameDiv = document.createElement('div');
    frameDiv.setAttribute('class', 'frame-div');
    const frameCount = getFrameCount(currentTime);
    const frameFileName = `${settings.titleSuffix}-${resString}-@${currentTime}s(${(0, _util.toHHMMSSTrimmed)(currentTime).replace(':', ';')})-f${frameCount.frameNumber}(${frameCount.totalFrames})`;
    (0, _util.safeSetInnerHtml)(frameDiv, `
      <figcaption>Resolution: ${canvas.width}x${canvas.height} Name: ${frameFileName}</figcaption>
      <button class="download">Download Frame</button>
      <button class="delete">Delete Frame</button>
      `);
    canvas.fileName = `${frameFileName}.png`;
    frameDiv.appendChild(canvas);
    frameDiv.getElementsByClassName('download')[0].onclick = () => {
      canvas.toBlob(blob => (0, _fileSaver.saveAs)(blob, canvas.fileName));
    };
    frameDiv.getElementsByClassName('delete')[0].onclick = () => {
      frameDiv.setAttribute('class', 'frame-div flash-div');
      setTimeout(() => (0, _util.deleteElement)(frameDiv), 300);
    };
    const framesDiv = frameCaptureViewerDoc.getElementById('frames-div');
    framesDiv.appendChild(frameDiv);
    (0, _util.flashMessage)(`Captured frame: ${frameFileName}`, 'green');
  }
  function getFrameCount(seconds) {
    let fps = getFPS(null);
    let frameNumber;
    let totalFrames;
    if (fps) {
      frameNumber = Math.floor(seconds * fps);
      totalFrames = Math.floor((0, _util.getVideoDuration)(platform, video) * fps);
    } else {
      frameNumber = 'Unknown';
      totalFrames = 'Unknown';
    }
    return {
      frameNumber,
      totalFrames
    };
  }
  function canvasBlobToPromise(canvas) {
    return new Promise(resolve => {
      canvas.toBlob(blob => resolve(blob));
    });
  }
  let isFrameCapturerZippingInProgress = false;
  function saveCapturedFrames() {
    if (isFrameCapturerZippingInProgress) {
      (0, _util.flashMessage)('Frame Capturer zipping already in progress. Please wait before trying to zip again.', 'red');
      return;
    }
    if (!frameCaptureViewerWindow || frameCaptureViewerWindow.closed || !frameCaptureViewerDoc) {
      (0, _util.flashMessage)('Frame capturer not open. Please capture a frame before zipping.', 'olive');
      return;
    }
    const zip = new _jszip.default();
    const framesZip = zip.folder(settings.titleSuffix).folder('frames');
    const frames = frameCaptureViewerDoc.getElementsByTagName('canvas');
    if (frames.length === 0) {
      (0, _util.flashMessage)('No frames to zip.', 'olive');
      return;
    }
    isFrameCapturerZippingInProgress = true;
    Array.from(frames).forEach(frame => {
      framesZip.file(frame.fileName, canvasBlobToPromise(frame), {
        binary: true
      });
    });
    const progressDiv = injectProgressBar('green', 'Frame Capturer');
    const progressSpan = progressDiv.firstElementChild;
    zip.generateAsync({
      type: 'blob'
    }, metadata => {
      const percent = metadata.percent.toFixed(2) + '%';
      progressSpan.textContent = `Frame Capturer Zipping Progress: ${percent}`;
    }).then(blob => {
      (0, _fileSaver.saveAs)(blob, `${settings.titleSuffix}-frames.zip`);
      progressDiv.dispatchEvent(new Event('done'));
      isFrameCapturerZippingInProgress = false;
    });
  }
  function injectProgressBar(color, tag) {
    const progressDiv = document.createElement('div');
    progressDiv.setAttribute('class', 'msg-div');
    progressDiv.addEventListener('done', () => {
      progressDiv.setAttribute('class', 'msg-div flash-div');
      setTimeout(() => (0, _util.deleteElement)(progressDiv), 2500);
    });
    (0, _util.safeSetInnerHtml)(progressDiv, `<span class="flash-msg" style="color:${color}"> ${tag} Zipping Progress: 0%</span>`);
    hooks.frameCapturerProgressBar.insertAdjacentElement('beforebegin', progressDiv);
    return progressDiv;
  }
  let cropDiv;
  let cropSvg;
  let cropDim;
  let cropRect;
  let cropRectBorder;
  let cropRectBorderBlack;
  let cropRectBorderWhite;
  let cropChartSectionStart;
  let cropChartSectionStartBorderGreen;
  let cropChartSectionStartBorderWhite;
  let cropChartSectionEnd;
  let cropChartSectionEndBorderYellow;
  let cropChartSectionEndBorderWhite;
  let cropCrossHair;
  let cropCrossHairXBlack;
  let cropCrossHairXWhite;
  let cropCrossHairYBlack;
  let cropCrossHairYWhite;
  let cropCrossHairs;
  function createCropOverlay(cropString) {
    deleteCropOverlay();
    cropDiv = document.createElement('div');
    cropDiv.setAttribute('id', 'crop-div');
    (0, _util.safeSetInnerHtml)(cropDiv, `
        <svg id="crop-svg">
          <defs>
            <mask id="cropMask">
              <rect x="0" y="0" width="100%" height="100%" fill="white" />
              <rect id="cropRect" x="0" y="0" width="100%" height="100%" fill="black" />
            </mask>
          </defs>
          <rect id="cropDim" mask="url(#cropMask)" x="0" y="0" width="100%" height="100%"
            fill="black" fill-opacity="${cropDims[cropDimIndex]}"
          />

          <g id="cropChartSectionStart" opacity="0.7" shape-rendering="geometricPrecision">
            <rect id="cropChartSectionStartBorderGreen" x="0" y="0" width="0%" height="0%" fill="none"
              stroke="lime" stroke-width="1px"
            />
            <rect id="cropChartSectionStartBorderWhite" x="0" y="0" width="0%" height="0%" fill="none"
              stroke="black" stroke-width="1px" stroke-dasharray="5 10"
            />
          </g>
          <g id="cropChartSectionEnd" opacity="0.7" shape-rendering="geometricPrecision">
            <rect id="cropChartSectionEndBorderYellow" x="0" y="0" width="0%" height="0%" fill="none"
              stroke="yellow" stroke-width="1px"
            />
            <rect id="cropChartSectionEndBorderWhite" x="0" y="0" width="0%" height="0%" fill="none"
              stroke="black" stroke-width="1px" stroke-dasharray="5 10"
            />
          </g>

          <g id="cropRectBorder" opacity="1" shape-rendering="geometricPrecision">
            <rect id="cropRectBorderBlack" x="0" y="0" width="100%" height="100%" fill="none"
              stroke="black" stroke-width="1px" stroke-opacity="0.8"
            />
            <rect id="cropRectBorderWhite" x="0" y="0" width="100%" height="100%" fill="none"
            stroke="white" stroke-width="1px" stroke-dasharray="5 5" stroke-opacity="0.8"
            >
            </rect>
            <g id="cropCrossHair" opacity="0.9" stroke="white" display="${cropCrossHairEnabled ? 'block' : 'none'}">
              <line id="cropCrossHairXBlack" x1="0" y1="50%" x2="100%" y2="50%" stroke="black" stroke-width="1px" type="x"/>
              <line id="cropCrossHairXWhite" x1="0" y1="50%" x2="100%" y2="50%" stroke-width="1px" stroke-dasharray="5 5" type="x"/>

              <line id="cropCrossHairYBlack" x1="50%" y1="0" x2="50%" y2="100%" stroke="black" stroke-width="1px" type="y"/>
              <line id="cropCrossHairYWhite" x1="50%" y1="0" x2="50%" y2="100%" stroke-width="1px" stroke-dasharray="5 5" type="y"/>
            </g>
          </g>
        </svg>
      `);
    resizeCropOverlay();
    hooks.cropOverlay.insertAdjacentElement('afterend', cropDiv);
    cropSvg = cropDiv.firstElementChild;
    cropDim = document.getElementById('cropDim');
    cropRect = document.getElementById('cropRect');
    cropRectBorder = document.getElementById('cropRectBorder');
    cropRectBorderBlack = document.getElementById('cropRectBorderBlack');
    cropRectBorderWhite = document.getElementById('cropRectBorderWhite');
    cropChartSectionStart = document.getElementById('cropChartSectionStart');
    cropChartSectionStartBorderGreen = document.getElementById('cropChartSectionStartBorderGreen');
    cropChartSectionStartBorderWhite = document.getElementById('cropChartSectionStartBorderWhite');
    cropChartSectionEnd = document.getElementById('cropChartSectionEnd');
    cropChartSectionEndBorderYellow = document.getElementById('cropChartSectionEndBorderYellow');
    cropChartSectionEndBorderWhite = document.getElementById('cropChartSectionEndBorderWhite');
    cropCrossHair = document.getElementById('cropCrossHair');
    cropCrossHairXBlack = document.getElementById('cropCrossHairXBlack');
    cropCrossHairXWhite = document.getElementById('cropCrossHairXWhite');
    cropCrossHairYBlack = document.getElementById('cropCrossHairYBlack');
    cropCrossHairYWhite = document.getElementById('cropCrossHairYWhite');
    cropCrossHairs = [cropCrossHairXBlack, cropCrossHairXWhite, cropCrossHairYBlack, cropCrossHairYWhite];
    [cropRect, cropRectBorderBlack, cropRectBorderWhite].map(cropRect => setCropOverlay(cropRect, cropString));
    cropCrossHairs.map(cropCrossHair => setCropCrossHair(cropCrossHair, cropString));
    isCropOverlayVisible = true;
  }
  function resizeCropOverlay() {
    requestAnimationFrame(forceRerenderCrop);
  }
  function forceRerenderCrop() {
    centerVideo();
    if (cropDiv) {
      const videoRect = video.getBoundingClientRect();
      const videoContainerRect = hooks.videoContainer.getBoundingClientRect();
      let {
        width,
        height,
        top,
        left
      } = videoRect;
      top = top - videoContainerRect.top;
      left = left - videoContainerRect.left;
      [width, height, top, left] = [width, height, top, left].map(e => `${Math.floor(e)}px`);
      Object.assign(cropDiv.style, {
        width,
        height,
        top,
        left,
        position: 'absolute'
      });
      if (cropSvg) {
        cropSvg.setAttribute('width', '0');
      }
    }
  }
  function centerVideo() {
    const videoContainerRect = hooks.videoContainer.getBoundingClientRect();
    let width, height;
    if (rotation === 0) {
      height = videoContainerRect.height;
      width = height * videoInfo.aspectRatio;
      width = Math.floor(Math.min(width, videoContainerRect.width));
      height = Math.floor(width / videoInfo.aspectRatio);
    } else {
      width = videoContainerRect.height;
      height = width / videoInfo.aspectRatio;
      height = Math.floor(Math.min(height, videoContainerRect.width));
      width = Math.floor(height * videoInfo.aspectRatio);
    }
    let left = videoContainerRect.width / 2 - width / 2;
    let top = videoContainerRect.height / 2 - height / 2;
    [width, height, top, left] = [width, height, top, left].map(e => `${Math.round(e)}px`);
    Object.assign(video.style, {
      width,
      height,
      top,
      left,
      position: 'absolute'
    });
  }
  function setCropOverlay(cropRect, cropString) {
    const [x, y, w, h] = getCropComponents(cropString);
    setCropOverlayDimensions(cropRect, x, y, w, h);
  }
  function setCropOverlayDimensions(cropRect, x, y, w, h) {
    if (cropRect) {
      const cropRectAttrs = {
        x: `${x / settings.cropResWidth * 100}%`,
        y: `${y / settings.cropResHeight * 100}%`,
        width: `${w / settings.cropResWidth * 100}%`,
        height: `${h / settings.cropResHeight * 100}%`
      };
      (0, _util.setAttributes)(cropRect, cropRectAttrs);
    }
  }
  function setCropCrossHair(cropCrossHair, cropString) {
    const [x, y, w, h] = getCropComponents(cropString);
    if (cropCrossHair) {
      const [x1M, x2M, y1M, y2M] = cropCrossHair.getAttribute('type') === 'x' ? [0, 1, 0.5, 0.5] : [0.5, 0.5, 0, 1];
      const cropCrossHairAttrs = {
        x1: `${(x + x1M * w) / settings.cropResWidth * 100}%`,
        x2: `${(x + x2M * w) / settings.cropResWidth * 100}%`,
        y1: `${(y + y1M * h) / settings.cropResHeight * 100}%`,
        y2: `${(y + y2M * h) / settings.cropResHeight * 100}%`
      };
      (0, _util.setAttributes)(cropCrossHair, cropCrossHairAttrs);
    }
  }
  const cropDims = [0, 0.25, 0.5, 0.75, 0.9, 1];
  let cropDimIndex = 2;
  function cycleCropDimOpacity() {
    cropDimIndex = (cropDimIndex + 1) % cropDims.length;
    cropDim.setAttribute('fill-opacity', cropDims[cropDimIndex].toString());
  }
  function showCropOverlay() {
    if (cropSvg) {
      cropSvg.style.display = 'block';
      isCropOverlayVisible = true;
    }
  }
  function hideCropOverlay() {
    if (isDrawingCrop) {
      finishDrawingCrop(true);
    }
    if (isMouseManipulatingCrop) {
      endCropMouseManipulation(null, true);
    }
    if (cropSvg) {
      cropSvg.style.display = 'none';
      isCropOverlayVisible = false;
    }
  }
  function deleteCropOverlay() {
    const cropDiv = document.getElementById('crop-div');
    (0, _util.deleteElement)(cropDiv);
    isCropOverlayVisible = false;
  }
  function hidePlayerControls() {
    var _a, _b;
    hooks.controls.originalDisplay = (_a = hooks.controls.originalDisplay) !== null && _a !== void 0 ? _a : hooks.controls.style.display;
    hooks.controlsGradient.originalDisplay = (_b = hooks.controlsGradient.originalDisplay) !== null && _b !== void 0 ? _b : hooks.controlsGradient.style.display;
    hooks.controls.style.display = 'none';
    hooks.controlsGradient.style.display = 'none';
  }
  function showPlayerControls() {
    hooks.controls.style.display = hooks.controls.originalDisplay;
    hooks.controlsGradient.style.display = hooks.controlsGradient.originalDisplay;
  }
  function getRelevantCropString() {
    if (!isSettingsEditorOpen) return null;
    if (!wasGlobalSettingsEditorOpen) {
      return markerPairs[prevSelectedMarkerPairIndex].cropMap[_cropChartSpec.currentCropPointIndex].crop;
    } else {
      return settings.newMarkerCrop;
    }
  }
  function addScrubVideoHandler() {
    hooks.cropMouseManipulation.addEventListener('pointerdown', scrubVideoHandler, {
      capture: true
    });
  }
  function scrubVideoHandler(e) {
    const isCropBlockingChartVisible = isCurrentChartVisible && currentChartInput && currentChartInput.type !== 'crop';
    if (!e.ctrlKey && e.altKey && !e.shiftKey && !isMouseManipulatingCrop && !isDrawingCrop && !isCropBlockingChartVisible) {
      (0, _util.blockEvent)(e);
      document.addEventListener('click', blockVideoPause, {
        once: true,
        capture: true
      });
      const videoRect = video.getBoundingClientRect();
      let prevClickPosX = e.clientX - videoRect.left;
      let prevClickPosY = e.clientY - videoRect.top;
      const pointerId = e.pointerId;
      video.setPointerCapture(pointerId);
      const baseWidth = 1920;
      function dragHandler(e) {
        (0, _util.blockEvent)(e);
        const pixelRatio = window.devicePixelRatio;
        const widthMultiple = baseWidth / screen.width;
        const dragPosX = e.clientX - videoRect.left;
        const dragPosY = e.clientY - videoRect.top;
        const changeX = (dragPosX - prevClickPosX) * pixelRatio * widthMultiple;
        const seekBy = changeX * (1 / videoInfo.fps);
        (0, _util.seekBySafe)(video, seekBy);
        prevClickPosX = e.clientX - videoRect.left;
      }
      function endDragHandler(e) {
        (0, _util.blockEvent)(e);
        document.removeEventListener('pointermove', dragHandler);
        video.releasePointerCapture(pointerId);
      }
      document.addEventListener('pointermove', dragHandler);
      document.addEventListener('pointerup', endDragHandler, {
        once: true,
        capture: true
      });
    }
  }
  let isMouseManipulatingCrop = false;
  let endCropMouseManipulation;
  function ctrlOrCommand(e) {
    return e.ctrlKey || e.metaKey;
  }
  function addCropMouseManipulationListener() {
    hooks.cropMouseManipulation.addEventListener('pointerdown', cropMouseManipulationHandler, {
      capture: true
    });
    function cropMouseManipulationHandler(e) {
      const isCropBlockingChartVisible = isCurrentChartVisible && currentChartInput && currentChartInput.type !== 'crop';
      if (ctrlOrCommand(e) && isSettingsEditorOpen && isCropOverlayVisible && !isDrawingCrop && !isCropBlockingChartVisible) {
        const cropString = getRelevantCropString();
        const [ix, iy, iw, ih] = getCropComponents(cropString);
        const cropResWidth = settings.cropResWidth;
        const cropResHeight = settings.cropResHeight;
        const videoRect = video.getBoundingClientRect();
        const clickPosX = e.clientX - videoRect.left;
        const clickPosY = e.clientY - videoRect.top;
        const cursor = getMouseCropHoverRegion(e, cropString);
        const pointerId = e.pointerId;
        const {
          isDynamicCrop,
          enableZoomPan,
          initCropMap
        } = getCropMapProperties();
        endCropMouseManipulation = function (e) {
          let forceEnd = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
          if (forceEnd) {
            document.removeEventListener('pointerup', endCropMouseManipulation, {
              capture: true
            });
          }
          isMouseManipulatingCrop = false;
          hooks.cropMouseManipulation.releasePointerCapture(pointerId);
          if (!wasGlobalSettingsEditorOpen) {
            const markerPair = markerPairs[prevSelectedMarkerPairIndex];
            const draft = (0, _immer.createDraft)((0, _undoredo.getMarkerPairHistory)(markerPair));
            (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
          }
          renderSpeedAndCropUI();
          document.removeEventListener('pointermove', dragCropHandler);
          document.removeEventListener('pointermove', cropResizeHandler);
          showPlayerControls();
          if (!forceEnd && ctrlOrCommand(e)) {
            if (cursor) hooks.cropMouseManipulation.style.cursor = cursor;
            updateCropHoverCursor(e);
            document.addEventListener('pointermove', cropHoverHandler, true);
          } else {
            hooks.cropMouseManipulation.style.removeProperty('cursor');
          }
          document.addEventListener('keyup', removeCropHoverListener, true);
          document.addEventListener('keydown', addCropHoverListener, true);
        };
        let cropResizeHandler;
        if (!cursor) {
          return;
        } else {
          document.addEventListener('click', blockVideoPause, {
            once: true,
            capture: true
          });
          document.removeEventListener('pointermove', cropHoverHandler, true);
          document.removeEventListener('keydown', addCropHoverListener, true);
          document.removeEventListener('keyup', removeCropHoverListener, true);
          e.preventDefault();
          hooks.cropMouseManipulation.setPointerCapture(pointerId);
          if (cursor === 'grab') {
            hooks.cropMouseManipulation.style.cursor = 'grabbing';
            document.addEventListener('pointermove', dragCropHandler);
          } else {
            cropResizeHandler = e => getCropResizeHandler(e, cursor);
            document.addEventListener('pointermove', cropResizeHandler);
          }
          document.addEventListener('pointerup', endCropMouseManipulation, {
            once: true,
            capture: true
          });
          hidePlayerControls();
          isMouseManipulatingCrop = true;
        }
        function dragCropHandler(e) {
          const dragPosX = e.clientX - videoRect.left;
          const dragPosY = e.clientY - videoRect.top;
          const changeX = dragPosX - clickPosX;
          const changeY = dragPosY - clickPosY;
          let changeXScaled = Math.round(changeX / videoRect.width * settings.cropResWidth);
          let changeYScaled = Math.round(changeY / videoRect.height * settings.cropResHeight);
          const crop = new _crop.Crop(ix, iy, iw, ih, cropResWidth, cropResHeight);
          const shouldMaintainCropX = e.shiftKey;
          const shouldMaintainCropY = e.altKey;
          if (shouldMaintainCropX) changeXScaled = 0;
          if (shouldMaintainCropY) changeYScaled = 0;
          crop.panX(changeXScaled);
          crop.panY(changeYScaled);
          updateCropString(crop.cropString, false, false, initCropMap);
        }
        function getCropResizeHandler(e, cursor) {
          const dragPosX = e.clientX - videoRect.left;
          const changeX = dragPosX - clickPosX;
          let deltaX = changeX / videoRect.width * settings.cropResWidth;
          const dragPosY = e.clientY - videoRect.top;
          const changeY = dragPosY - clickPosY;
          let deltaY = changeY / videoRect.height * settings.cropResHeight;
          const shouldMaintainCropAspectRatio = (!enableZoomPan || !isDynamicCrop) && e.altKey || enableZoomPan && isDynamicCrop && !e.altKey;
          const shouldResizeCenterOut = e.shiftKey;
          const crop = new _crop.Crop(ix, iy, iw, ih, cropResWidth, cropResHeight);
          resizeCrop(crop, cursor, deltaX, deltaY, shouldMaintainCropAspectRatio, shouldResizeCenterOut);
          updateCropString(crop.cropString, false, false, initCropMap);
        }
      }
    }
  }
  function resizeCrop(crop, cursor, deltaX, deltaY) {
    let shouldMaintainCropAspectRatio = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
    let shouldResizeCenterOut = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
    const isWResize = ['w-resize', 'nw-resize', 'sw-resize'].includes(cursor);
    const isNResize = ['n-resize', 'nw-resize', 'ne-resize'].includes(cursor);
    if (isWResize) deltaX = -deltaX;
    if (isNResize) deltaY = -deltaY;
    const isDiagonalResize = ['ne-resize', 'se-resize', 'sw-resize', 'nw-resize'].includes(cursor);
    if (shouldMaintainCropAspectRatio && shouldResizeCenterOut) {
      crop.resizeNESWAspectRatioLocked(deltaY, deltaX);
    } else if (shouldResizeCenterOut && isDiagonalResize) {
      crop.resizeNESW(deltaY, deltaX);
    } else {
      switch (cursor) {
        case 'n-resize':
          shouldMaintainCropAspectRatio ? crop.resizeNAspectRatioLocked(deltaY) : shouldResizeCenterOut ? crop.resizeNS(deltaY) : crop.resizeN(deltaY);
          break;
        case 'ne-resize':
          shouldMaintainCropAspectRatio ? crop.resizeNEAspectRatioLocked(deltaY, deltaX) : crop.resizeNE(deltaY, deltaX);
          break;
        case 'e-resize':
          shouldMaintainCropAspectRatio ? crop.resizeEAspectRatioLocked(deltaX) : shouldResizeCenterOut ? crop.resizeEW(deltaX) : crop.resizeE(deltaX);
          break;
        case 'se-resize':
          shouldMaintainCropAspectRatio ? crop.resizeSEAspectRatioLocked(deltaY, deltaX) : crop.resizeSE(deltaY, deltaX);
          break;
        case 's-resize':
          shouldMaintainCropAspectRatio ? crop.resizeSAspectRatioLocked(deltaY) : shouldResizeCenterOut ? crop.resizeNS(deltaY) : crop.resizeS(deltaY);
          break;
        case 'sw-resize':
          shouldMaintainCropAspectRatio ? crop.resizeSWAspectRatioLocked(deltaY, deltaX) : crop.resizeSW(deltaY, deltaX);
          break;
        case 'w-resize':
          shouldMaintainCropAspectRatio ? crop.resizeWAspectRatioLocked(deltaX) : shouldResizeCenterOut ? crop.resizeEW(deltaX) : crop.resizeW(deltaX);
          break;
        case 'nw-resize':
          shouldMaintainCropAspectRatio ? crop.resizeNWAspectRatioLocked(deltaY, deltaX) : crop.resizeNW(deltaY, deltaX);
          break;
      }
    }
  }
  function getMouseCropHoverRegion(e, cropString) {
    cropString = cropString !== null && cropString !== void 0 ? cropString : getRelevantCropString();
    const [x, y, w, h] = getCropComponents(cropString);
    const videoRect = video.getBoundingClientRect();
    const clickPosX = e.clientX - videoRect.left;
    const clickPosY = e.clientY - videoRect.top;
    const clickPosXScaled = clickPosX / videoRect.width * settings.cropResWidth;
    const clickPosYScaled = clickPosY / videoRect.height * settings.cropResHeight;
    const slMultiplier = Math.min(settings.cropResWidth, settings.cropResHeight) / 1080;
    const sl = Math.ceil(Math.min(w, h) * slMultiplier * 0.1);
    const edgeOffset = 30 * slMultiplier;
    let cursor;
    let mouseCropColumn;
    if (x - edgeOffset < clickPosXScaled && clickPosXScaled < x + sl) {
      mouseCropColumn = 1;
    } else if (x + sl < clickPosXScaled && clickPosXScaled < x + w - sl) {
      mouseCropColumn = 2;
    } else if (x + w - sl < clickPosXScaled && clickPosXScaled < x + w + edgeOffset) {
      mouseCropColumn = 3;
    }
    let mouseCropRow;
    if (y - edgeOffset < clickPosYScaled && clickPosYScaled < y + sl) {
      mouseCropRow = 1;
    } else if (y + sl < clickPosYScaled && clickPosYScaled < y + h - sl) {
      mouseCropRow = 2;
    } else if (y + h - sl < clickPosYScaled && clickPosYScaled < y + h + edgeOffset) {
      mouseCropRow = 3;
    }
    const isMouseInCropCenter = mouseCropColumn === 2 && mouseCropRow === 2;
    const isMouseInCropN = mouseCropColumn === 2 && mouseCropRow === 1;
    const isMouseInCropNE = mouseCropColumn === 3 && mouseCropRow === 1;
    const isMouseInCropE = mouseCropColumn === 3 && mouseCropRow === 2;
    const isMouseInCropSE = mouseCropColumn === 3 && mouseCropRow === 3;
    const isMouseInCropS = mouseCropColumn === 2 && mouseCropRow === 3;
    const isMouseInCropSW = mouseCropColumn === 1 && mouseCropRow === 3;
    const isMouseInCropW = mouseCropColumn === 1 && mouseCropRow === 2;
    const isMouseInCropNW = mouseCropColumn === 1 && mouseCropRow === 1;
    if (isMouseInCropCenter) cursor = 'grab';
    if (isMouseInCropN) cursor = 'n-resize';
    if (isMouseInCropNE) cursor = 'ne-resize';
    if (isMouseInCropE) cursor = 'e-resize';
    if (isMouseInCropSE) cursor = 'se-resize';
    if (isMouseInCropS) cursor = 's-resize';
    if (isMouseInCropSW) cursor = 'sw-resize';
    if (isMouseInCropW) cursor = 'w-resize';
    if (isMouseInCropNW) cursor = 'nw-resize';
    return cursor;
  }
  let isDrawingCrop = false;
  let prevNewMarkerCrop = '0:0:iw:ih';
  let initDrawCropMap;
  let beginDrawHandler;
  function drawCrop() {
    if (isDrawingCrop) {
      finishDrawingCrop(true);
    } else if (isCurrentChartVisible && currentChartInput && currentChartInput.type !== 'crop') {
      (0, _util.flashMessage)('Please toggle off the speed chart before drawing crop', 'olive');
    } else if (isMouseManipulatingCrop) {
      (0, _util.flashMessage)('Please finish dragging or resizing before drawing crop', 'olive');
    } else if (isSettingsEditorOpen && isCropOverlayVisible) {
      isDrawingCrop = true;
      ({
        initCropMap: initDrawCropMap
      } = getCropMapProperties());
      prevNewMarkerCrop = settings.newMarkerCrop;
      _crop.Crop.shouldConstrainMinDimensions = false;
      document.removeEventListener('keydown', addCropHoverListener, true);
      document.removeEventListener('pointermove', cropHoverHandler, true);
      hidePlayerControls();
      hooks.cropMouseManipulation.style.removeProperty('cursor');
      hooks.cropMouseManipulation.style.cursor = 'crosshair';
      beginDrawHandler = e => beginDraw(e);
      hooks.cropMouseManipulation.addEventListener('pointerdown', beginDrawHandler, {
        once: true,
        capture: true
      });
      (0, _util.flashMessage)('Begin drawing crop', 'green');
    } else {
      (0, _util.flashMessage)('Please open the global settings or a marker pair editor before drawing crop', 'olive');
    }
  }
  let drawCropHandler;
  let shouldFinishDrawMaintainAspectRatio = false;
  function beginDraw(e) {
    if (e.button == 0 && !drawCropHandler) {
      e.preventDefault();
      hooks.cropMouseManipulation.setPointerCapture(e.pointerId);
      const cropResWidth = settings.cropResWidth;
      const cropResHeight = settings.cropResHeight;
      const videoRect = video.getBoundingClientRect();
      const clickPosX = e.clientX - videoRect.left;
      const clickPosY = e.clientY - videoRect.top;
      const ix = clickPosX / videoRect.width * cropResWidth;
      const iy = clickPosY / videoRect.height * cropResHeight;
      const {
        isDynamicCrop,
        enableZoomPan
      } = getCropMapProperties();
      const initCrop = !wasGlobalSettingsEditorOpen ? initDrawCropMap[_cropChartSpec.currentCropPointIndex].crop : prevNewMarkerCrop;
      const shouldMaintainCropAspectRatio = (!enableZoomPan || !isDynamicCrop) && e.altKey || enableZoomPan && isDynamicCrop && !e.altKey;
      shouldFinishDrawMaintainAspectRatio = shouldMaintainCropAspectRatio;
      const [px, py, pw, ph] = getCropComponents(initCrop);
      const par = pw <= 0 || ph <= 0 ? 1 : pw / ph;
      const crop = new _crop.Crop(ix, iy, _crop.Crop.minW, _crop.Crop.minH, cropResWidth, cropResHeight);
      updateCropString(crop.cropString, false, false, initDrawCropMap);
      const {
        initCropMap: zeroCropMap
      } = getCropMapProperties();
      drawCropHandler = function (e) {
        const dragPosX = e.clientX - videoRect.left;
        const changeX = dragPosX - clickPosX;
        let deltaX = changeX / videoRect.width * cropResWidth;
        const dragPosY = e.clientY - videoRect.top;
        const changeY = dragPosY - clickPosY;
        let deltaY = changeY / videoRect.height * cropResHeight;
        const shouldMaintainCropAspectRatio = (!enableZoomPan || !isDynamicCrop) && e.altKey || enableZoomPan && isDynamicCrop && !e.altKey;
        shouldFinishDrawMaintainAspectRatio = shouldMaintainCropAspectRatio;
        const shouldResizeCenterOut = e.shiftKey;
        const crop = new _crop.Crop(ix, iy, _crop.Crop.minW, _crop.Crop.minH, cropResWidth, cropResHeight);
        crop.defaultAspectRatio = par;
        let cursor;
        if (deltaX >= 0 && deltaY < 0) cursor = 'ne-resize';
        if (deltaX >= 0 && deltaY >= 0) cursor = 'se-resize';
        if (deltaX < 0 && deltaY >= 0) cursor = 'sw-resize';
        if (deltaX < 0 && deltaY < 0) cursor = 'nw-resize';
        resizeCrop(crop, cursor, deltaX, deltaY, shouldMaintainCropAspectRatio, shouldResizeCenterOut);
        updateCropString(crop.cropString, false, false, zeroCropMap);
      };
      document.addEventListener('pointermove', drawCropHandler);
      document.addEventListener('pointerup', endDraw, {
        once: true,
        capture: true
      });
      // exact event listener reference only added once so remove not required
      document.addEventListener('click', blockVideoPause, {
        once: true,
        capture: true
      });
    } else {
      finishDrawingCrop(true);
    }
  }
  function blockVideoPause(e) {
    e.stopImmediatePropagation();
  }
  function endDraw(e) {
    if (e.button === 0) {
      finishDrawingCrop(false, e.pointerId);
    } else {
      finishDrawingCrop(true, e.pointerId);
    }
    if (ctrlOrCommand(e)) {
      document.addEventListener('pointermove', cropHoverHandler, true);
    }
  }
  function finishDrawingCrop(shouldRevertCrop, pointerId) {
    _crop.Crop.shouldConstrainMinDimensions = true;
    if (pointerId != null) hooks.cropMouseManipulation.releasePointerCapture(pointerId);
    hooks.cropMouseManipulation.style.cursor = 'auto';
    hooks.cropMouseManipulation.removeEventListener('pointerdown', beginDrawHandler, true);
    document.removeEventListener('pointermove', drawCropHandler);
    document.removeEventListener('pointerup', endDraw, true);
    drawCropHandler = null;
    isDrawingCrop = false;
    showPlayerControls();
    document.addEventListener('keydown', addCropHoverListener, true);
    if (wasGlobalSettingsEditorOpen) {
      if (shouldRevertCrop) {
        settings.newMarkerCrop = prevNewMarkerCrop;
      } else {
        const newCrop = transformCropWithPushBack(prevNewMarkerCrop, settings.newMarkerCrop, shouldFinishDrawMaintainAspectRatio);
        settings.newMarkerCrop = newCrop;
      }
      updateCropString(settings.newMarkerCrop, true);
    }
    if (!wasGlobalSettingsEditorOpen) {
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      const cropMap = markerPair.cropMap;
      if (shouldRevertCrop) {
        const draft = (0, _immer.createDraft)((0, _undoredo.getMarkerPairHistory)(markerPair));
        draft.cropMap = initDrawCropMap;
        (0, _undoredo.saveMarkerPairHistory)(draft, markerPair, false);
        renderSpeedAndCropUI();
      } else {
        const newCrop = transformCropWithPushBack(initDrawCropMap[_cropChartSpec.currentCropPointIndex].crop, cropMap[_cropChartSpec.currentCropPointIndex].crop, shouldFinishDrawMaintainAspectRatio);
        updateCropString(newCrop, true, false, initDrawCropMap);
      }
    }
    shouldRevertCrop ? (0, _util.flashMessage)('Drawing crop canceled', 'red') : (0, _util.flashMessage)('Finished drawing crop', 'green');
  }
  function transformCropWithPushBack(oldCrop, newCrop) {
    let shouldMaintainCropAspectRatio = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    const [,, iw, ih] = getCropComponents(oldCrop);
    const [nx, ny, nw, nh] = getCropComponents(newCrop);
    const dw = nw - iw;
    const dh = nh - ih;
    const crop = _crop.Crop.fromCropString((0, _util.getCropString)(0, 0, iw, ih), settings.cropRes);
    shouldMaintainCropAspectRatio ? crop.resizeSEAspectRatioLocked(dh, dw) : crop.resizeSE(dh, dw);
    crop.panX(nx);
    crop.panY(ny);
    return crop.cropString;
  }
  let cropCrossHairEnabled = false;
  function toggleCropCrossHair() {
    if (cropCrossHairEnabled) {
      (0, _util.flashMessage)('Disabled crop crosshair', 'red');
      cropCrossHairEnabled = false;
      cropCrossHair && (cropCrossHair.style.display = 'none');
    } else {
      (0, _util.flashMessage)('Enabled crop crosshair', 'green');
      cropCrossHairEnabled = true;
      cropCrossHair && (cropCrossHair.style.display = 'block');
      renderSpeedAndCropUI(false, false);
    }
  }
  let arrowKeyCropAdjustmentEnabled = false;
  function toggleArrowKeyCropAdjustment() {
    if (arrowKeyCropAdjustmentEnabled) {
      document.removeEventListener('keydown', arrowKeyCropAdjustmentHandler, true);
      (0, _util.flashMessage)('Disabled crop adjustment with arrow keys', 'red');
      arrowKeyCropAdjustmentEnabled = false;
    } else {
      document.addEventListener('keydown', arrowKeyCropAdjustmentHandler, true);
      (0, _util.flashMessage)('Enabled crop adjustment with arrow keys', 'green');
      arrowKeyCropAdjustmentEnabled = true;
    }
  }
  function arrowKeyCropAdjustmentHandler(ke) {
    if (isSettingsEditorOpen) {
      if (cropInput !== document.activeElement && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].indexOf(ke.code) > -1) {
        (0, _util.blockEvent)(ke);
        let [ix, iy, iw, ih] = getCropComponents(cropInput.value);
        let changeAmount;
        if (!ke.altKey && !ke.shiftKey) {
          changeAmount = 10;
        } else if (ke.altKey && !ke.shiftKey) {
          changeAmount = 1;
        } else if (!ke.altKey && ke.shiftKey) {
          changeAmount = 50;
        } else if (ke.altKey && ke.shiftKey) {
          changeAmount = 100;
        }
        const {
          isDynamicCrop,
          enableZoomPan,
          initCropMap
        } = getCropMapProperties();
        const shouldMaintainCropAspectRatio = enableZoomPan && isDynamicCrop;
        const cropResWidth = settings.cropResWidth;
        const cropResHeight = settings.cropResHeight;
        const crop = new _crop.Crop(ix, iy, iw, ih, cropResWidth, cropResHeight);
        // without modifiers move crop x/y offset
        // with ctrl key modifier expand/shrink crop width/height
        if (!ke.ctrlKey) {
          switch (ke.code) {
            case 'ArrowUp':
              crop.panY(-changeAmount);
              break;
            case 'ArrowDown':
              crop.panY(changeAmount);
              break;
            case 'ArrowLeft':
              crop.panX(-changeAmount);
              break;
            case 'ArrowRight':
              crop.panX(changeAmount);
              break;
          }
        } else {
          let cursor;
          switch (ke.code) {
            case 'ArrowUp':
              cursor = 's-resize';
              changeAmount = -changeAmount;
              break;
            case 'ArrowDown':
              cursor = 's-resize';
              break;
            case 'ArrowLeft':
              cursor = 'e-resize';
              changeAmount = -changeAmount;
              break;
            case 'ArrowRight':
              cursor = 'e-resize';
              break;
          }
          resizeCrop(crop, cursor, changeAmount, changeAmount, shouldMaintainCropAspectRatio);
        }
        updateCropString(crop.cropString, true, false, initCropMap);
      }
    }
  }
  function getCropMapProperties() {
    let isDynamicCrop = false;
    let enableZoomPan = false;
    let initCropMap = null;
    if (!wasGlobalSettingsEditorOpen) {
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      const cropMap = markerPair.cropMap;
      const draftCropMap = (0, _immer.createDraft)(cropMap);
      initCropMap = (0, _immer.finishDraft)(draftCropMap);
      isDynamicCrop = !isStaticCrop(cropMap) || cropMap.length === 2 && _cropChartSpec.currentCropPointIndex === 1;
      enableZoomPan = markerPair.enableZoomPan;
    }
    return {
      isDynamicCrop,
      enableZoomPan,
      initCropMap
    };
  }
  function getCropComponents(cropString) {
    if (!cropString && isSettingsEditorOpen) {
      if (!wasGlobalSettingsEditorOpen && prevSelectedMarkerPairIndex != null) {
        cropString = markerPairs[prevSelectedMarkerPairIndex].crop;
      } else {
        cropString = settings.newMarkerCrop;
      }
    }
    if (!cropString) {
      console.error('No valid crop string to extract components from.');
      cropString = '0:0:iw:ih';
    }
    const cropArray = cropString.split(':').map((cropStringComponent, i) => {
      let cropComponent;
      if (cropStringComponent === 'iw') {
        cropComponent = settings.cropResWidth;
      } else if (cropStringComponent === 'ih') {
        cropComponent = settings.cropResHeight;
      } else if (i % 2 == 0) {
        cropComponent = parseFloat(cropStringComponent);
        cropComponent = Math.min(Math.round(cropComponent), settings.cropResWidth);
      } else {
        cropComponent = parseFloat(cropStringComponent);
        cropComponent = Math.min(Math.round(cropComponent), settings.cropResHeight);
      }
      return cropComponent;
    });
    return cropArray;
  }
  function getNumericCropString(cropString) {
    const [x, y, w, h] = getCropComponents(cropString);
    return (0, _util.getCropString)(x, y, w, h);
  }
  function updateCropString(cropString) {
    let shouldRerenderCharts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    let forceCropConstraints = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    let initCropMap = arguments.length > 3 ? arguments[3] : undefined;
    if (!isSettingsEditorOpen) throw new Error('No editor was open when trying to update crop.');
    let draft;
    const [nx, ny, nw, nh] = getCropComponents(cropString);
    cropString = (0, _util.getCropString)(nx, ny, nw, nh);
    let wasDynamicCrop = false;
    let enableZoomPan = false;
    if (!wasGlobalSettingsEditorOpen) {
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      enableZoomPan = markerPair.enableZoomPan;
      const initState = (0, _undoredo.getMarkerPairHistory)(markerPair);
      draft = (0, _immer.createDraft)(initState);
      if (initCropMap == null) throw new Error('No initial crop map given when modifying marker pair crop.');
      const draftCropMap = draft.cropMap;
      wasDynamicCrop = !isStaticCrop(initCropMap) || initCropMap.length === 2 && _cropChartSpec.currentCropPointIndex === 1;
      const draftCropPoint = draftCropMap[_cropChartSpec.currentCropPointIndex];
      const initCrop = initCropMap[_cropChartSpec.currentCropPointIndex].crop;
      if (initCrop == null) throw new Error('Init crop undefined.');
      draftCropPoint.crop = cropString;
      if (wasDynamicCrop) {
        if (!enableZoomPan || forceCropConstraints) {
          setCropComponentForAllPoints({
            w: nw,
            h: nh
          }, draftCropMap, initCropMap);
        } else if (enableZoomPan || forceCropConstraints) {
          const aspectRatio = nw / nh;
          setAspectRatioForAllPoints(aspectRatio, draftCropMap, initCropMap);
        }
      }
      const maxIndex = draftCropMap.length - 1;
      const isSecondLastPoint = _cropChartSpec.currentCropPointIndex === maxIndex - 1;
      const isLastSectionStatic = cropStringsEqual(initCrop, initCropMap[maxIndex].crop);
      if (isSecondLastPoint && isLastSectionStatic) {
        draftCropMap[maxIndex].crop = cropString;
      }
      draft.crop = draftCropMap[0].crop;
    } else {
      settings.newMarkerCrop = cropString;
    }
    if (!wasGlobalSettingsEditorOpen) {
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      (0, _undoredo.saveMarkerPairHistory)(draft, markerPair, shouldRerenderCharts);
    }
    renderSpeedAndCropUI(shouldRerenderCharts);
  }
  function renderSpeedAndCropUI() {
    let rerenderCharts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
    let updateCurrentCropPoint = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    if (isSettingsEditorOpen) {
      if (!wasGlobalSettingsEditorOpen) {
        const markerPair = markerPairs[prevSelectedMarkerPairIndex];
        updateCharts(markerPair, rerenderCharts);
        // avoid updating current crop point unless crop map times have changed
        if (updateCurrentCropPoint) setCurrentCropPointWithCurrentTime();
        renderMarkerPair(markerPair, prevSelectedMarkerPairIndex);
        speedInput.value = markerPair.speed.toString();
        const cropMap = markerPair.cropMap;
        const crop = cropMap[_cropChartSpec.currentCropPointIndex].crop;
        const isDynamicCrop = !isStaticCrop(cropMap);
        renderCropForm(crop);
        if (!isDynamicCrop) {
          renderStaticCropOverlay(crop);
        } else {
          updateDynamicCropOverlays(cropMap, video.getCurrentTime(), isDynamicCrop);
        }
        const enableZoomPan = markerPair.enableZoomPan;
        enableZoomPanInput.value = enableZoomPan ? 'Enabled' : 'Disabled';
        const formatter = enableZoomPan ? _cropChartSpec.cropPointFormatter : _cropChartSpec.cropPointXYFormatter;
        if (cropChartInput.chart) {
          cropChartInput.chart.options.plugins.datalabels.formatter = formatter;
        } else {
          cropChartInput.chartSpec = (0, _cropChartSpec.getCropChartConfig)(enableZoomPan);
        }
      } else {
        const crop = settings.newMarkerCrop;
        renderCropForm(crop);
        renderStaticCropOverlay(crop);
      }
      highlightSpeedAndCropInputs();
    }
  }
  function renderStaticCropOverlay(crop) {
    const [x, y, w, h] = getCropComponents(crop);
    [cropRect, cropRectBorderBlack, cropRectBorderWhite].map(cropRect => setCropOverlayDimensions(cropRect, x, y, w, h));
    if (cropCrossHairEnabled && cropCrossHair) {
      cropCrossHairs.map(cropCrossHair => setCropCrossHair(cropCrossHair, (0, _util.getCropString)(x, y, w, h)));
      cropCrossHair.style.stroke = 'white';
    }
  }
  function renderCropForm(crop) {
    const [x, y, w, h] = getCropComponents(crop);
    cropInput.value = crop;
    const cropAspectRatio = (w / h).toFixed(13);
    cropAspectRatioSpan && (cropAspectRatioSpan.textContent = cropAspectRatio);
  }
  function highlightSpeedAndCropInputs() {
    if (wasGlobalSettingsEditorOpen) {
      highlightModifiedSettings([['crop-input', 'newMarkerCrop', 'string'], ['speed-input', 'newMarkerSpeed', 'number']], settings);
    } else {
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      highlightModifiedSettings([['crop-input', 'crop', 'string'], ['speed-input', 'speed', 'number'], ['enable-zoom-pan-input', 'enableZoomPan', 'bool']], markerPair);
    }
  }
  function setCropComponentForAllPoints(newCrop, draftCropMap, initialCropMap) {
    draftCropMap.forEach((cropPoint, i) => {
      var _a, _b, _c, _d;
      if (i === _cropChartSpec.currentCropPointIndex) return;
      const initCrop = initialCropMap[i].crop;
      const [ix, iy, iw, ih] = getCropComponents(initCrop !== null && initCrop !== void 0 ? initCrop : cropPoint.crop);
      const nw = (_a = newCrop.w) !== null && _a !== void 0 ? _a : iw;
      const nh = (_b = newCrop.h) !== null && _b !== void 0 ? _b : ih;
      const nx = (_c = newCrop.x) !== null && _c !== void 0 ? _c : (0, _util.clampNumber)(ix, 0, settings.cropResWidth - nw);
      const ny = (_d = newCrop.y) !== null && _d !== void 0 ? _d : (0, _util.clampNumber)(iy, 0, settings.cropResHeight - nh);
      cropPoint.crop = `${nx}:${ny}:${nw}:${nh}`;
    });
  }
  function setAspectRatioForAllPoints(aspectRatio, draftCropMap, initialCropMap) {
    let referencePointIndex = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _cropChartSpec.currentCropPointIndex;
    _crop.Crop.shouldConstrainMinDimensions = false;
    const cropResWidth = settings.cropResWidth;
    const cropResHeight = settings.cropResHeight;
    draftCropMap.forEach((cropPoint, i) => {
      if (i === referencePointIndex) return;
      const initCrop = initialCropMap[i].crop;
      const [ix, iy, iw, ih] = getCropComponents(initCrop !== null && initCrop !== void 0 ? initCrop : cropPoint.crop);
      const crop = new _crop.Crop(0, 0, 0, 0, cropResWidth, cropResHeight);
      crop.defaultAspectRatio = aspectRatio;
      if (ih >= iw) {
        crop.resizeSAspectRatioLocked(ih);
      } else {
        crop.resizeEAspectRatioLocked(iw);
      }
      crop.panX(ix);
      crop.panY(iy);
      cropPoint.crop = crop.cropString;
    });
    _crop.Crop.shouldConstrainMinDimensions = true;
  }
  function isStaticCrop(cropMap) {
    return cropMap.length === 2 && cropStringsEqual(cropMap[0].crop, cropMap[1].crop);
  }
  function cropStringsEqual(a, b) {
    const [ax, ay, aw, ah] = getCropComponents(a);
    const [bx, by, bw, bh] = getCropComponents(b);
    return ax === bx && ay === by && aw === bw && ah === bh;
  }
  function updateChart() {
    let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'crop';
    if (isCurrentChartVisible && currentChartInput && currentChartInput.chart && currentChartInput.type === type) {
      currentChartInput.chart.update();
    }
  }
  let speedChartInput = {
    chart: null,
    type: 'speed',
    chartContainer: null,
    chartContainerId: 'speedChartContainer',
    chartContainerHook: null,
    chartContainerHookPosition: 'afterend',
    chartContainerStyle: 'width: 100%; height: calc(100% - 20px); position: relative; z-index: 55; opacity:0.8;',
    chartCanvasHTML: `<canvas id="speedChartCanvas" width="1600px" height="900px"></canvas>`,
    chartSpec: _speedChartSpec.speedChartSpec,
    chartCanvasId: 'speedChartCanvas',
    minBound: 0,
    maxBound: 0,
    chartLoopKey: 'speedChartLoop',
    dataMapKey: 'speedMap'
  };
  let cropChartInput = {
    chart: null,
    type: 'crop',
    chartContainer: null,
    chartContainerId: 'cropChartContainer',
    chartContainerHook: null,
    chartContainerHookPosition: 'beforebegin',
    chartContainerStyle: 'display:flex',
    chartCanvasHTML: `<canvas id="cropChartCanvas" width="1600px" height="87px"></canvas>`,
    chartCanvasId: 'cropChartCanvas',
    chartSpec: (0, _cropChartSpec.getCropChartConfig)(false),
    minBound: 0,
    maxBound: 0,
    chartLoopKey: 'cropChartLoop',
    dataMapKey: 'cropMap'
  };
  let currentChartInput;
  function initChartHooks() {
    speedChartInput.chartContainerHook = hooks.speedChartContainer;
    cropChartInput.chartContainerHook = hooks.cropChartContainer;
  }
  _chart.Chart.helpers.merge(_chart.Chart.defaults.global, _scatterChartSpec.scatterChartDefaults);
  function toggleChart(chartInput) {
    if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen && prevSelectedMarkerPairIndex != null) {
      if (!chartInput.chart) {
        if (currentChartInput && isCurrentChartVisible) {
          hideChart();
        }
        currentChartInput = chartInput;
        initializeChartData(chartInput.chartSpec, chartInput.dataMapKey);
        chartInput.chartContainer = (0, _util.htmlToElement)(`
            <div
              id="${chartInput.chartContainerId}"
              style="${chartInput.chartContainerStyle}"
            ></div>
          `);
        (0, _util.safeSetInnerHtml)(chartInput.chartContainer, chartInput.chartCanvasHTML);
        chartInput.chartContainerHook.insertAdjacentElement(chartInput.chartContainerHookPosition, chartInput.chartContainer);
        chartInput.chart = new _chart.Chart(chartInput.chartCanvasId, chartInput.chartSpec);
        chartInput.chart.renderSpeedAndCropUI = renderSpeedAndCropUI;
        chartInput.chart.canvas.removeEventListener('wheel', chartInput.chart.$zoom._wheelHandler);
        const wheelHandler = chartInput.chart.$zoom._wheelHandler;
        chartInput.chart.$zoom._wheelHandler = e => {
          if (e.ctrlKey && !e.altKey && !e.shiftKey) {
            wheelHandler(e);
          }
        };
        chartInput.chart.ctx.canvas.addEventListener('wheel', chartInput.chart.$zoom._wheelHandler);
        chartInput.chart.ctx.canvas.addEventListener('contextmenu', e => {
          (0, _util.blockEvent)(e);
        }, true);
        chartInput.chart.ctx.canvas.addEventListener('pointerdown', getMouseChartTimeAnnotationSetter(chartInput), true);
        isCurrentChartVisible = true;
        isChartEnabled = true;
        updateChartTimeAnnotation();
        cropChartPreviewHandler();
        // console.log(chartInput.chart);
      } else {
        if (currentChartInput.type !== chartInput.type) {
          hideChart();
          currentChartInput = chartInput;
        }
        toggleCurrentChartVisibility();
        isChartEnabled = isCurrentChartVisible;
      }
    } else {
      (0, _util.flashMessage)('Please open a marker pair editor before toggling a chart input.', 'olive');
    }
  }
  function getMouseChartTimeAnnotationSetter(chartInput) {
    return function mouseChartTimeAnnotationSetter(e) {
      if (e.buttons !== 2) return;
      (0, _util.blockEvent)(e);
      const chart = chartInput.chart;
      const chartLoop = markerPairs[prevSelectedMarkerPairIndex][chartInput.chartLoopKey];
      // shift+right-click context menu opens screenshot tool in firefox 67.0.2
      function chartTimeAnnotationDragHandler(e) {
        const time = (0, _util.timeRounder)(chart.scales['x-axis-1'].getValueForPixel(e.offsetX));
        chart.config.options.annotation.annotations[0].value = time;
        if (Math.abs(video.getCurrentTime() - time) >= 0.01) {
          (0, _util.seekToSafe)(video, time);
        }
        if (!e.ctrlKey && !e.altKey && e.shiftKey) {
          chart.config.options.annotation.annotations[1].value = time;
          chartLoop.start = time;
          chart.update();
        } else if (!e.ctrlKey && e.altKey && !e.shiftKey) {
          chart.config.options.annotation.annotations[2].value = time;
          chartLoop.end = time;
          chart.update();
        }
      }
      chartTimeAnnotationDragHandler(e);
      function chartTimeAnnotationDragEnd(e) {
        (0, _util.blockEvent)(e);
        chart.ctx.canvas.releasePointerCapture(e.pointerId);
        document.removeEventListener('pointermove', chartTimeAnnotationDragHandler);
      }
      chart.ctx.canvas.setPointerCapture(e.pointerId);
      document.addEventListener('pointermove', chartTimeAnnotationDragHandler);
      document.addEventListener('pointerup', chartTimeAnnotationDragEnd, {
        once: true
      });
      document.addEventListener('contextmenu', _util.blockEvent, {
        once: true,
        capture: true
      });
    };
  }
  let easingMode = 'linear';
  function toggleSpeedChartEasing(chartInput) {
    const chart = chartInput.chart;
    if (chart) {
      if (easingMode === 'linear') {
        chart.data.datasets[0].lineTension = _chartutil.cubicInOutTension;
        easingMode = 'cubicInOut';
      } else {
        chart.data.datasets[0].lineTension = 0;
        easingMode = 'linear';
      }
      chart.update();
    }
  }
  function toggleChartLoop() {
    if (currentChartInput && isCurrentChartVisible && prevSelectedMarkerPairIndex != null) {
      const chart = currentChartInput.chart;
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      const chartLoop = markerPair[currentChartInput.chartLoopKey];
      if (chartLoop.enabled) {
        chartLoop.enabled = false;
        chart.config.options.annotation.annotations[1].borderColor = 'rgba(0, 255, 0, 0.4)';
        chart.config.options.annotation.annotations[2].borderColor = 'rgba(255, 215, 0, 0.4)';
        (0, _util.flashMessage)('Speed chart looping disabled', 'red');
      } else {
        chartLoop.enabled = true;
        chart.config.options.annotation.annotations[1].borderColor = 'rgba(0, 255, 0, 0.9)';
        chart.config.options.annotation.annotations[2].borderColor = 'rgba(255, 215, 0, 0.9)';
        (0, _util.flashMessage)('Speed chart looping enabled', 'green');
      }
      chart.update();
    }
  }
  function initializeChartData(chartConfig, dataMapKey) {
    if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen && prevSelectedMarkerPairIndex != null) {
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      const dataMap = markerPair[dataMapKey];
      chartConfig.data.datasets[0].data = dataMap;
      updateChartBounds(chartConfig, markerPair.start, markerPair.end);
    }
  }
  function loadChartData(chartInput) {
    if (chartInput && chartInput.chart) {
      if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen && prevSelectedMarkerPairIndex != null) {
        const markerPair = markerPairs[prevSelectedMarkerPairIndex];
        const dataMapKey = chartInput.dataMapKey;
        const dataMap = markerPair[dataMapKey];
        const chart = chartInput.chart;
        chart.data.datasets[0].data = dataMap;
        updateChartBounds(chart.config, markerPair.start, markerPair.end);
        if (isCurrentChartVisible && currentChartInput === chartInput) chart.update();
      }
    }
  }
  function updateChartBounds(chartConfig, start, end) {
    if (cropChartInput) {
      cropChartInput.minBound = start;
      cropChartInput.maxBound = end;
    }
    if (speedChartInput) {
      speedChartInput.minBound = start;
      speedChartInput.maxBound = end;
    }
    chartConfig.options.scales.xAxes[0].ticks.min = start;
    chartConfig.options.scales.xAxes[0].ticks.max = end;
    chartConfig.options.plugins.zoom.pan.rangeMin.x = start;
    chartConfig.options.plugins.zoom.pan.rangeMax.x = end;
    chartConfig.options.plugins.zoom.zoom.rangeMin.x = start;
    chartConfig.options.plugins.zoom.zoom.rangeMax.x = end;
  }
  let prevChartTime;
  function updateChartTimeAnnotation() {
    if (isCurrentChartVisible) {
      if (prevChartTime !== video.getCurrentTime()) {
        const time = video.getCurrentTime();
        prevChartTime = time;
        const chart = currentChartInput.chart;
        chart.config.options.annotation.annotations[0].value = (0, _util.clampNumber)(time, currentChartInput.minBound, currentChartInput.maxBound);
        const timeAnnotation = Object.values(chart.annotation.elements)[0];
        timeAnnotation.options.value = (0, _util.clampNumber)(time, currentChartInput.minBound, currentChartInput.maxBound);
        timeAnnotation.configure();
        chart.render();
      }
    }
    requestAnimationFrame(updateChartTimeAnnotation);
  }
  function toggleCropChartLooping() {
    if (!isCropChartLoopingOn) {
      exports.isCropChartLoopingOn = isCropChartLoopingOn = true;
      (0, _util.flashMessage)('Dynamic crop looping enabled', 'green');
    } else {
      exports.isCropChartLoopingOn = isCropChartLoopingOn = false;
      (0, _util.flashMessage)('Dynamic crop looping  disabled', 'red');
    }
  }
  function cropChartPreviewHandler() {
    const chart = cropChartInput.chart;
    if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen && chart) {
      const chartData = chart === null || chart === void 0 ? void 0 : chart.data.datasets[0].data;
      const time = video.getCurrentTime();
      const isDynamicCrop = !isStaticCrop(chartData);
      const isCropChartVisible = currentChartInput && currentChartInput.type == 'crop' && isCurrentChartVisible;
      if (shouldTriggerCropChartLoop ||
      // assume auto time-based update not required for crop chart section if looping section
      isCropChartLoopingOn && isCropChartVisible || cropChartInput.chart && (isMouseManipulatingCrop || isDrawingCrop)) {
        shouldTriggerCropChartLoop = false;
        cropChartSectionLoop();
      } else if (isDynamicCrop) {
        setCurrentCropPointWithCurrentTime();
      }
      if (isDynamicCrop || _cropChartSpec.currentCropPointIndex > 0) {
        cropInputLabel.textContent = `Crop Point ${_cropChartSpec.currentCropPointIndex + 1}`;
      } else {
        cropInputLabel.textContent = `Crop`;
      }
      updateDynamicCropOverlays(chartData, time, isDynamicCrop);
    }
    requestAnimationFrame(cropChartPreviewHandler);
  }
  function setCurrentCropPointWithCurrentTime() {
    const cropChart = cropChartInput.chart;
    if (cropChart) {
      const chartData = cropChart.data.datasets[0].data;
      const time = video.getCurrentTime();
      const searchCropPoint = {
        x: time,
        y: 0,
        crop: ''
      };
      let [istart, iend] = _cropChartSpec.currentCropChartSection;
      let [start, end] = (0, _util.bsearch)(chartData, searchCropPoint, _chartutil.sortX);
      if (_cropChartSpec.currentCropChartMode === _cropChartSpec.cropChartMode.Start) {
        if (start === end && end === iend) start--;
        (0, _cropChartSpec.setCurrentCropPoint)(cropChart, Math.min(start, chartData.length - 2));
      } else if (_cropChartSpec.currentCropChartMode === _cropChartSpec.cropChartMode.End) {
        if (start === end && start === istart) end++;
        (0, _cropChartSpec.setCurrentCropPoint)(cropChart, Math.max(end, 1));
      }
    }
  }
  const easeInInstant = nt => nt === 0 ? 0 : 1;
  function updateDynamicCropOverlays(chartData, currentTime, isDynamicCrop) {
    if (isDynamicCrop || _cropChartSpec.currentCropPointIndex > 0) {
      cropChartSectionStart.style.display = 'block';
      cropChartSectionEnd.style.display = 'block';
      cropRectBorder.style.opacity = '0.6';
    } else {
      cropChartSectionStart.style.display = 'none';
      cropChartSectionEnd.style.display = 'none';
      cropRectBorder.style.opacity = '1';
      return;
    }
    const sectStart = chartData[_cropChartSpec.currentCropChartSection[0]];
    const sectEnd = chartData[_cropChartSpec.currentCropChartSection[1]];
    [cropChartSectionStartBorderGreen, cropChartSectionStartBorderWhite].map(cropRect => setCropOverlay(cropRect, sectStart.crop));
    [cropChartSectionEndBorderYellow, cropChartSectionEndBorderWhite].map(cropRect => setCropOverlay(cropRect, sectEnd.crop));
    const currentCropPoint = chartData[_cropChartSpec.currentCropPointIndex];
    if (cropCrossHairEnabled && cropCrossHair) {
      cropCrossHairs.map(cropCrossHair => setCropCrossHair(cropCrossHair, currentCropPoint.crop));
      cropCrossHair.style.stroke = _cropChartSpec.currentCropChartMode === _cropChartSpec.cropChartMode.Start ? 'lime' : 'yellow';
    }
    if (_cropChartSpec.currentCropChartMode === _cropChartSpec.cropChartMode.Start) {
      cropChartSectionStart.setAttribute('opacity', '0.8');
      cropChartSectionEnd.setAttribute('opacity', '0.3');
    } else if (_cropChartSpec.currentCropChartMode === _cropChartSpec.cropChartMode.End) {
      cropChartSectionStart.setAttribute('opacity', '0.3');
      cropChartSectionEnd.setAttribute('opacity', '0.8');
    }
    const [startX, startY, startW, startH] = getCropComponents(sectStart.crop);
    const [endX, endY, endW, endH] = getCropComponents(sectEnd.crop);
    const clampedTime = (0, _util.clampNumber)(currentTime, sectStart.x, sectEnd.x);
    const easingFunc = sectEnd.easeIn == 'instant' ? easeInInstant : _d3Ease.easeSinInOut;
    const [easedX, easedY, easedW, easedH] = [[startX, endX], [startY, endY], [startW, endW], [startH, endH]].map(pair => (0, _util.getEasedValue)(easingFunc, pair[0], pair[1], sectStart.x, sectEnd.x, clampedTime));
    [cropRect, cropRectBorderBlack, cropRectBorderWhite].map(cropRect => setCropOverlayDimensions(cropRect, easedX, easedY, easedW, easedH));
  }
  function getInterpolatedCrop(sectStart, sectEnd, time) {
    const [startX, startY, startW, startH] = getCropComponents(sectStart.crop);
    const [endX, endY, endW, endH] = getCropComponents(sectEnd.crop);
    const clampedTime = (0, _util.clampNumber)(time, sectStart.x, sectEnd.x);
    const easingFunc = sectEnd.easeIn == 'instant' ? easeInInstant : _d3Ease.easeSinInOut;
    const [x, y, w, h] = [[startX, endX], [startY, endY], [startW, endW], [startH, endH]].map(_ref => {
      let [startValue, endValue] = _ref;
      const eased = (0, _util.getEasedValue)(easingFunc, startValue, endValue, sectStart.x, sectEnd.x, clampedTime);
      return eased;
    });
    // return [x, y, w, h];
    return (0, _util.getCropString)(x, y, w, h);
  }
  function cropChartSectionLoop() {
    if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen) {
      if (prevSelectedMarkerPairIndex != null) {
        const chart = cropChartInput.chart;
        if (chart == null) return;
        const chartData = chart.data.datasets[0].data;
        const [start, end] = _cropChartSpec.currentCropChartSection;
        const sectStart = chartData[start].x;
        const sectEnd = chartData[end].x;
        const isTimeBetweenCropChartSection = sectStart <= video.getCurrentTime() && video.getCurrentTime() <= sectEnd;
        if (!isTimeBetweenCropChartSection) {
          (0, _util.seekToSafe)(video, sectStart);
        }
      }
    }
  }
  function updateAllMarkerPairSpeeds(newSpeed) {
    markerPairs.forEach(markerPair => {
      updateMarkerPairSpeed(markerPair, newSpeed);
    });
    if (isSettingsEditorOpen) {
      if (wasGlobalSettingsEditorOpen) {
        const markerPairMergeListInput = document.getElementById('merge-list-input');
        markerPairMergeListInput.dispatchEvent(new Event('change'));
      } else {
        speedInput.value = newSpeed.toString();
        renderSpeedAndCropUI();
      }
    }
    (0, _util.flashMessage)(`All marker speeds updated to ${newSpeed}`, 'olive');
  }
  function updateMarkerPairSpeed(markerPair, newSpeed) {
    const draft = (0, _immer.createDraft)((0, _undoredo.getMarkerPairHistory)(markerPair));
    draft.speed = newSpeed;
    const speedMap = draft.speedMap;
    if (speedMap.length === 2 && speedMap[0].y === speedMap[1].y) {
      speedMap[1].y = newSpeed;
    }
    speedMap[0].y = newSpeed;
    (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
  }
  function updateAllMarkerPairCrops(newCrop) {
    markerPairs.forEach(markerPair => {
      const draft = (0, _immer.createDraft)((0, _undoredo.getMarkerPairHistory)(markerPair));
      const cropMap = draft.cropMap;
      if (isStaticCrop(cropMap)) {
        draft.crop = newCrop;
        cropMap[0].crop = newCrop;
        cropMap[1].crop = newCrop;
      }
      (0, _undoredo.saveMarkerPairHistory)(draft, markerPair);
    });
    if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen) {
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      const cropMap = markerPair.cropMap;
      if (isStaticCrop(cropMap)) {
        cropInput.value = newCrop;
        renderSpeedAndCropUI();
      }
    }
    (0, _util.flashMessage)(`All static marker crops updated to ${newCrop}`, 'olive');
  }
  function undoRedoMarkerPairChange(dir) {
    if (isSettingsEditorOpen && !wasGlobalSettingsEditorOpen && prevSelectedMarkerPairIndex != null) {
      const markerPair = markerPairs[prevSelectedMarkerPairIndex];
      const newState = dir === 'undo' ? (0, _undoredo.undo)(markerPair.undoredo, () => null) : (0, _undoredo.redo)(markerPair.undoredo, () => null);
      if (newState == null) {
        (0, _util.flashMessage)(`Nothing left to ${dir}.`, 'red');
      } else {
        Object.assign(markerPair, newState);
        if (markerPair.cropRes !== settings.cropRes) {
          const {
            cropMultipleX,
            cropMultipleY
          } = getCropMultiples(markerPair.cropRes, settings.cropRes);
          multiplyMarkerPairCrops(markerPair, cropMultipleX, cropMultipleY);
        }
        renderSpeedAndCropUI(true, true);
        (0, _util.flashMessage)(`Applied ${dir}.`, 'green');
      }
    } else {
      (0, _util.flashMessage)('Please select a marker pair editor for undo/redo.', 'olive');
    }
  }
  function deleteMarkerPair(idx) {
    if (idx == null) idx = prevSelectedMarkerPairIndex;
    const markerPair = markerPairs[idx];
    const me = new PointerEvent('pointerover', {
      shiftKey: true
    });
    enableMarkerHotkeys.endMarker.dispatchEvent(me);
    (0, _util.deleteElement)(enableMarkerHotkeys.endMarker);
    (0, _util.deleteElement)(enableMarkerHotkeys.startMarker);
    (0, _util.deleteElement)(markerPair.startNumbering);
    (0, _util.deleteElement)(markerPair.endNumbering);
    hideSelectedMarkerPairOverlay(true);
    renumberMarkerPairs();
    markerPairs.splice(idx, 1);
    clearPrevSelectedMarkerPairReferences();
  }
  function clearPrevSelectedMarkerPairReferences() {
    exports.prevSelectedMarkerPairIndex = prevSelectedMarkerPairIndex = null;
    prevSelectedEndMarker = null;
    enableMarkerHotkeys.startMarker = null;
    enableMarkerHotkeys.endMarker = null;
    markerHotkeysEnabled = false;
  }
  let selectedStartMarkerOverlay;
  let selectedEndMarkerOverlay;
  function highlightSelectedMarkerPair(currentMarker) {
    if (!selectedStartMarkerOverlay) {
      selectedStartMarkerOverlay = document.getElementById('selected-start-marker-overlay');
    }
    if (!selectedEndMarkerOverlay) {
      selectedEndMarkerOverlay = document.getElementById('selected-end-marker-overlay');
    }
    const startMarker = currentMarker.previousSibling;
    selectedStartMarkerOverlay.setAttribute('x', startMarker.getAttribute('x'));
    selectedEndMarkerOverlay.setAttribute('x', currentMarker.getAttribute('x'));
    selectedStartMarkerOverlay.classList.remove('selected-marker-overlay-hidden');
    selectedEndMarkerOverlay.classList.remove('selected-marker-overlay-hidden');
    selectedMarkerPairOverlay.style.display = 'block';
  }
  function updateMarkerPairDuration(markerPair) {
    const speedAdjustedDurationSpan = document.getElementById('duration');
    const duration = markerPair.end - markerPair.start;
    const durationHHMMSS = (0, _util.toHHMMSSTrimmed)(duration);
    const speed = markerPair.speed;
    const speedMap = markerPair.speedMap;
    if (isVariableSpeed(speedMap)) {
      const outputDuration = (0, _util.getOutputDuration)(markerPair.speedMap, getFPS());
      const outputDurationHHMMSS = (0, _util.toHHMMSSTrimmed)(outputDuration);
      if (speedAdjustedDurationSpan) speedAdjustedDurationSpan.textContent = `${durationHHMMSS} (${outputDurationHHMMSS})`;
      markerPair.outputDuration = outputDuration;
    } else {
      const outputDuration = duration / speed;
      const outputDurationHHMMSS = (0, _util.toHHMMSSTrimmed)(outputDuration);
      if (speedAdjustedDurationSpan) speedAdjustedDurationSpan.textContent = `${durationHHMMSS}/${speed} = ${outputDurationHHMMSS}`;
      markerPair.outputDuration = outputDuration;
    }
  }
  function renumberMarkerPairs() {
    const markersSvg = document.getElementById('markers-svg');
    markersSvg.childNodes.forEach((markerRect, idx) => {
      // renumber markers by pair starting with index 1
      const newIdx = Math.floor((idx + 2) / 2);
      markerRect.setAttribute('data-idx', newIdx);
    });
    startMarkerNumberings.childNodes.forEach((startNumbering, idx) => {
      const newIdx = idx + 1;
      startNumbering.setAttribute('data-idx', newIdx);
      startNumbering.textContent = newIdx.toString();
    });
    endMarkerNumberings.childNodes.forEach((endNumbering, idx) => {
      const newIdx = idx + 1;
      endNumbering.setAttribute('data-idx', newIdx);
      endNumbering.textContent = newIdx.toString();
    });
  }
  function hideSelectedMarkerPairOverlay() {
    let hardHide = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
    if (hardHide) {
      selectedMarkerPairOverlay.style.display = 'none';
    } else {
      selectedStartMarkerOverlay.classList.add('selected-marker-overlay-hidden');
      selectedEndMarkerOverlay.classList.add('selected-marker-overlay-hidden');
    }
  }
  function showChart() {
    if (currentChartInput && currentChartInput.chartContainer) {
      if (isDrawingCrop) {
        finishDrawingCrop(true);
      }
      currentChartInput.chartContainer.style.display = 'block';
      isCurrentChartVisible = true;
      currentChartInput.chart.update();
      // force chart time annotation to update
      prevChartTime = -1;
    }
  }
  function hideChart() {
    if (currentChartInput && currentChartInput.chartContainer) {
      currentChartInput.chartContainer.style.display = 'none';
      isCurrentChartVisible = false;
    }
  }
  function toggleCurrentChartVisibility() {
    if (!isCurrentChartVisible) {
      showChart();
    } else {
      hideChart();
    }
  }
}
},{"chart.js":"JsLj","./util/util":"mCFQ","common-tags":"Gk7m","d3-ease":"NhEX","file-saver":"oSxG","fs":"Zcgp","immer":"wBnk","jszip":"ZBUn","lodash.clonedeep":"AdD9","./actions/misc":"WKCK","./crop/crop":"f1IS","./platforms/blockers/common":"bOUC","./platforms/blockers/youtube":"jPPE","./platforms/platforms":"qrKm","./ui/chart/chart.js-drag-data-plugin":"bV5G","./ui/chart/chartutil":"NG0t","./ui/chart/cropchart/cropChartSpec":"Ldv2","./ui/chart/scatterChartSpec":"ZNtu","./ui/chart/speedchart/speedChartSpec":"C3pH","./ui/css/css":"ayli","./ui/tooltips":"vbnC","./util/undoredo":"clJU"}]},{},["XbmT"], null)
//# sourceMappingURL=/yt_clipper.js.map