text: ( function () {
  const _changeEvent = {
    type: 'change'
  };
  const _startEvent = {
    type: 'start'
  };
  const _endEvent = {
    type: 'end'
  };
  
  class TrackballControls extends THREE.EventDispatcher {
    constructor(object, domElement) {
      super();
      if (domElement === undefined) console.warn('THREE.TrackballControls: The second parameter "domElement" is now mandatory.');
      if (domElement === document) console.error('THREE.TrackballControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
      const scope = this;
      const STATE = {
        NONE: -1,
        ROTATE: 0,
        ZOOM: 1,
        PAN: 2,
        TOUCH_ROTATE: 3,
        TOUCH_ZOOM_PAN: 4
      };
      this.object = object;
      this.domElement = domElement;
      this.domElement.style.touchAction = 'none'; // disable touch scroll
      // API
  
      this.enabled = true;
      this.screen = {
        left: 0,
        top: 0,
        width: 0,
        height: 0
      };
      this.rotateSpeed = 1.0;
      this.zoomSpeed = 1.2;
      this.panSpeed = 0.3;
      this.noRotate = false;
      this.noZoom = false;
      this.noPan = false;
      this.staticMoving = false;
      this.dynamicDampingFactor = 0.2;
      this.minDistance = 0;
      this.maxDistance = Infinity;
      this.keys = ['KeyA'
      /*A*/
      , 'KeyS'
      /*S*/
      , 'KeyD'
      /*D*/
      ];
      this.mouseButtons = {
        LEFT: THREE.MOUSE.ROTATE,
        MIDDLE: THREE.MOUSE.DOLLY,
        RIGHT: THREE.MOUSE.PAN
      }; // internals
  
      this.target = new THREE.Vector3();
      const EPS = 0.000001;
      const lastPosition = new THREE.Vector3();
      let lastZoom = 1;
      let _state = STATE.NONE,
          _keyState = STATE.NONE,
          _touchZoomDistanceStart = 0,
          _touchZoomDistanceEnd = 0,
          _lastAngle = 0;
  
      const _eye = new THREE.Vector3(),
            _movePrev = new THREE.Vector2(),
            _moveCurr = new THREE.Vector2(),
            _lastAxis = new THREE.Vector3(),
            _zoomStart = new THREE.Vector2(),
            _zoomEnd = new THREE.Vector2(),
            _panStart = new THREE.Vector2(),
            _panEnd = new THREE.Vector2(),
            _pointers = [],
            _pointerPositions = {}; // for reset
  
  
      this.target0 = this.target.clone();
      this.position0 = this.object.position.clone();
      this.up0 = this.object.up.clone();
      this.zoom0 = this.object.zoom; // methods
  
      this.handleResize = function () {
        const box = scope.domElement.getBoundingClientRect(); // adjustments come from similar code in the jquery offset() function
  
        const d = scope.domElement.ownerDocument.documentElement;
        scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
        scope.screen.top = box.top + window.pageYOffset - d.clientTop;
        scope.screen.width = box.width;
        scope.screen.height = box.height;
      };
  
      const getMouseOnScreen = function () {
        const vector = new THREE.Vector2();
        return function getMouseOnScreen(pageX, pageY) {
          vector.set((pageX - scope.screen.left) / scope.screen.width, (pageY - scope.screen.top) / scope.screen.height);
          return vector;
        };
      }();
  
      const getMouseOnCircle = function () {
        const vector = new THREE.Vector2();
        return function getMouseOnCircle(pageX, pageY) {
          vector.set((pageX - scope.screen.width * 0.5 - scope.screen.left) / (scope.screen.width * 0.5), (scope.screen.height + 2 * (scope.screen.top - pageY)) / scope.screen.width // screen.width intentional
          );
          return vector;
        };
      }();
  
      this.rotateCamera = function () {
        const axis = new THREE.Vector3(),
              quaternion = new THREE.Quaternion(),
              eyeDirection = new THREE.Vector3(),
              objectUpDirection = new THREE.Vector3(),
              objectSidewaysDirection = new THREE.Vector3(),
              moveDirection = new THREE.Vector3();
        return function rotateCamera() {
          moveDirection.set(_moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0);
          let angle = moveDirection.length();
  
          if (angle) {
            _eye.copy(scope.object.position).sub(scope.target);
  
            eyeDirection.copy(_eye).normalize();
            objectUpDirection.copy(scope.object.up).normalize();
            objectSidewaysDirection.crossVectors(objectUpDirection, eyeDirection).normalize();
            objectUpDirection.setLength(_moveCurr.y - _movePrev.y);
            objectSidewaysDirection.setLength(_moveCurr.x - _movePrev.x);
            moveDirection.copy(objectUpDirection.add(objectSidewaysDirection));
            axis.crossVectors(moveDirection, _eye).normalize();
            angle *= scope.rotateSpeed;
            quaternion.setFromAxisAngle(axis, angle);
  
            _eye.applyQuaternion(quaternion);
  
            scope.object.up.applyQuaternion(quaternion);
  
            _lastAxis.copy(axis);
  
            _lastAngle = angle;
          } else if (!scope.staticMoving && _lastAngle) {
            _lastAngle *= Math.sqrt(1.0 - scope.dynamicDampingFactor);
  
            _eye.copy(scope.object.position).sub(scope.target);
  
            quaternion.setFromAxisAngle(_lastAxis, _lastAngle);
  
            _eye.applyQuaternion(quaternion);
  
            scope.object.up.applyQuaternion(quaternion);
          }
  
          _movePrev.copy(_moveCurr);
        };
      }();
  
      this.zoomCamera = function () {
        let factor;
  
        if (_state === STATE.TOUCH_ZOOM_PAN) {
          factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
          _touchZoomDistanceStart = _touchZoomDistanceEnd;
  
          if (scope.object.isPerspectiveCamera) {
            _eye.multiplyScalar(factor);
          } else if (scope.object.isOrthographicCamera) {
            scope.object.zoom /= factor;
            scope.object.updateProjectionMatrix();
          } else {
            console.warn('THREE.TrackballControls: Unsupported camera type');
          }
        } else {
          factor = 1.0 + (_zoomEnd.y - _zoomStart.y) * scope.zoomSpeed;
  
          if (factor !== 1.0 && factor > 0.0) {
            if (scope.object.isPerspectiveCamera) {
              _eye.multiplyScalar(factor);
            } else if (scope.object.isOrthographicCamera) {
              scope.object.zoom /= factor;
              scope.object.updateProjectionMatrix();
            } else {
              console.warn('THREE.TrackballControls: Unsupported camera type');
            }
          }
  
          if (scope.staticMoving) {
            _zoomStart.copy(_zoomEnd);
          } else {
            _zoomStart.y += (_zoomEnd.y - _zoomStart.y) * this.dynamicDampingFactor;
          }
        }
      };
  
      this.panCamera = function () {
        const mouseChange = new THREE.Vector2(),
              objectUp = new THREE.Vector3(),
              pan = new THREE.Vector3();
        return function panCamera() {
          mouseChange.copy(_panEnd).sub(_panStart);
  
          if (mouseChange.lengthSq()) {
            if (scope.object.isOrthographicCamera) {
              const scale_x = (scope.object.right - scope.object.left) / scope.object.zoom / scope.domElement.clientWidth;
              const scale_y = (scope.object.top - scope.object.bottom) / scope.object.zoom / scope.domElement.clientWidth;
              mouseChange.x *= scale_x;
              mouseChange.y *= scale_y;
            }
  
            mouseChange.multiplyScalar(_eye.length() * scope.panSpeed);
            pan.copy(_eye).cross(scope.object.up).setLength(mouseChange.x);
            pan.add(objectUp.copy(scope.object.up).setLength(mouseChange.y));
            scope.object.position.add(pan);
            scope.target.add(pan);
  
            if (scope.staticMoving) {
              _panStart.copy(_panEnd);
            } else {
              _panStart.add(mouseChange.subVectors(_panEnd, _panStart).multiplyScalar(scope.dynamicDampingFactor));
            }
          }
        };
      }();
  
      this.checkDistances = function () {
        if (!scope.noZoom || !scope.noPan) {
          if (_eye.lengthSq() > scope.maxDistance * scope.maxDistance) {
            scope.object.position.addVectors(scope.target, _eye.setLength(scope.maxDistance));
  
            _zoomStart.copy(_zoomEnd);
          }
  
          if (_eye.lengthSq() < scope.minDistance * scope.minDistance) {
            scope.object.position.addVectors(scope.target, _eye.setLength(scope.minDistance));
  
            _zoomStart.copy(_zoomEnd);
          }
        }
      };
  
      this.update = function () {
        _eye.subVectors(scope.object.position, scope.target);
  
        if (!scope.noRotate) {
          scope.rotateCamera();
        }
  
        if (!scope.noZoom) {
          scope.zoomCamera();
        }
  
        if (!scope.noPan) {
          scope.panCamera();
        }
  
        scope.object.position.addVectors(scope.target, _eye);
  
        if (scope.object.isPerspectiveCamera) {
          scope.checkDistances();
          scope.object.lookAt(scope.target);
  
          if (lastPosition.distanceToSquared(scope.object.position) > EPS) {
            scope.dispatchEvent(_changeEvent);
            lastPosition.copy(scope.object.position);
          }
        } else if (scope.object.isOrthographicCamera) {
          scope.object.lookAt(scope.target);
  
          if (lastPosition.distanceToSquared(scope.object.position) > EPS || lastZoom !== scope.object.zoom) {
            scope.dispatchEvent(_changeEvent);
            lastPosition.copy(scope.object.position);
            lastZoom = scope.object.zoom;
          }
        } else {
          console.warn('THREE.TrackballControls: Unsupported camera type');
        }
      };
  
      this.reset = function () {
        _state = STATE.NONE;
        _keyState = STATE.NONE;
        scope.target.copy(scope.target0);
        scope.object.position.copy(scope.position0);
        scope.object.up.copy(scope.up0);
        scope.object.zoom = scope.zoom0;
        scope.object.updateProjectionMatrix();
  
        _eye.subVectors(scope.object.position, scope.target);
  
        scope.object.lookAt(scope.target);
        scope.dispatchEvent(_changeEvent);
        lastPosition.copy(scope.object.position);
        lastZoom = scope.object.zoom;
      }; // listeners
  
  
      function onPointerDown(event) {
        if (scope.enabled === false) return;
  
        if (_pointers.length === 0) {
          scope.domElement.setPointerCapture(event.pointerId);
          scope.domElement.addEventListener('pointermove', onPointerMove);
          scope.domElement.addEventListener('pointerup', onPointerUp);
        } //
  
  
        addPointer(event);
  
        if (event.pointerType === 'touch') {
          onTouchStart(event);
        } else {
          onMouseDown(event);
        }
      }
  
      function onPointerMove(event) {
        if (scope.enabled === false) return;
  
        if (event.pointerType === 'touch') {
          onTouchMove(event);
        } else {
          onMouseMove(event);
        }
      }
  
      function onPointerUp(event) {
        if (scope.enabled === false) return;
  
        if (event.pointerType === 'touch') {
          onTouchEnd(event);
        } else {
          onMouseUp();
        } //
  
  
        removePointer(event);
  
        if (_pointers.length === 0) {
          scope.domElement.releasePointerCapture(event.pointerId);
          scope.domElement.removeEventListener('pointermove', onPointerMove);
          scope.domElement.removeEventListener('pointerup', onPointerUp);
        }
      }
  
      function onPointerCancel(event) {
        removePointer(event);
      }
  
      function keydown(event) {
        if (scope.enabled === false) return;
        window.removeEventListener('keydown', keydown);
  
        if (_keyState !== STATE.NONE) {
          return;
        } else if (event.code === scope.keys[STATE.ROTATE] && !scope.noRotate) {
          _keyState = STATE.ROTATE;
        } else if (event.code === scope.keys[STATE.ZOOM] && !scope.noZoom) {
          _keyState = STATE.ZOOM;
        } else if (event.code === scope.keys[STATE.PAN] && !scope.noPan) {
          _keyState = STATE.PAN;
        }
      }
  
      function keyup() {
        if (scope.enabled === false) return;
        _keyState = STATE.NONE;
        window.addEventListener('keydown', keydown);
      }
  
      function onMouseDown(event) {
        if (_state === STATE.NONE) {
          switch (event.button) {
            case scope.mouseButtons.LEFT:
              _state = STATE.ROTATE;
              break;
  
            case scope.mouseButtons.MIDDLE:
              _state = STATE.ZOOM;
              break;
  
            case scope.mouseButtons.RIGHT:
              _state = STATE.PAN;
              break;
          }
        }
  
        const state = _keyState !== STATE.NONE ? _keyState : _state;
  
        if (state === STATE.ROTATE && !scope.noRotate) {
          _moveCurr.copy(getMouseOnCircle(event.pageX, event.pageY));
  
          _movePrev.copy(_moveCurr);
        } else if (state === STATE.ZOOM && !scope.noZoom) {
          _zoomStart.copy(getMouseOnScreen(event.pageX, event.pageY));
  
          _zoomEnd.copy(_zoomStart);
        } else if (state === STATE.PAN && !scope.noPan) {
          _panStart.copy(getMouseOnScreen(event.pageX, event.pageY));
  
          _panEnd.copy(_panStart);
        }
  
        scope.dispatchEvent(_startEvent);
      }
  
      function onMouseMove(event) {
        const state = _keyState !== STATE.NONE ? _keyState : _state;
  
        if (state === STATE.ROTATE && !scope.noRotate) {
          _movePrev.copy(_moveCurr);
  
          _moveCurr.copy(getMouseOnCircle(event.pageX, event.pageY));
        } else if (state === STATE.ZOOM && !scope.noZoom) {
          _zoomEnd.copy(getMouseOnScreen(event.pageX, event.pageY));
        } else if (state === STATE.PAN && !scope.noPan) {
          _panEnd.copy(getMouseOnScreen(event.pageX, event.pageY));
        }
      }
  
      function onMouseUp() {
        _state = STATE.NONE;
        scope.dispatchEvent(_endEvent);
      }
  
      function onMouseWheel(event) {
        if (scope.enabled === false) return;
        if (scope.noZoom === true) return;
        event.preventDefault();
  
        switch (event.deltaMode) {
          case 2:
            // Zoom in pages
            _zoomStart.y -= event.deltaY * 0.025;
            break;
  
          case 1:
            // Zoom in lines
            _zoomStart.y -= event.deltaY * 0.01;
            break;
  
          default:
            // undefined, 0, assume pixels
            _zoomStart.y -= event.deltaY * 0.00025;
            break;
        }
  
        scope.dispatchEvent(_startEvent);
        scope.dispatchEvent(_endEvent);
      }
  
      function onTouchStart(event) {
        trackPointer(event);
  
        switch (_pointers.length) {
          case 1:
            _state = STATE.TOUCH_ROTATE;
  
            _moveCurr.copy(getMouseOnCircle(_pointers[0].pageX, _pointers[0].pageY));
  
            _movePrev.copy(_moveCurr);
  
            break;
  
          default:
            // 2 or more
            _state = STATE.TOUCH_ZOOM_PAN;
            const dx = _pointers[0].pageX - _pointers[1].pageX;
            const dy = _pointers[0].pageY - _pointers[1].pageY;
            _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt(dx * dx + dy * dy);
            const x = (_pointers[0].pageX + _pointers[1].pageX) / 2;
            const y = (_pointers[0].pageY + _pointers[1].pageY) / 2;
  
            _panStart.copy(getMouseOnScreen(x, y));
  
            _panEnd.copy(_panStart);
  
            break;
        }
  
        scope.dispatchEvent(_startEvent);
      }
  
      function onTouchMove(event) {
        trackPointer(event);
  
        switch (_pointers.length) {
          case 1:
            _movePrev.copy(_moveCurr);
  
            _moveCurr.copy(getMouseOnCircle(event.pageX, event.pageY));
  
            break;
  
          default:
            // 2 or more
            const position = getSecondPointerPosition(event);
            const dx = event.pageX - position.x;
            const dy = event.pageY - position.y;
            _touchZoomDistanceEnd = Math.sqrt(dx * dx + dy * dy);
            const x = (event.pageX + position.x) / 2;
            const y = (event.pageY + position.y) / 2;
  
            _panEnd.copy(getMouseOnScreen(x, y));
  
            break;
        }
      }
  
      function onTouchEnd(event) {
        switch (_pointers.length) {
          case 0:
            _state = STATE.NONE;
            break;
  
          case 1:
            _state = STATE.TOUCH_ROTATE;
  
            _moveCurr.copy(getMouseOnCircle(event.pageX, event.pageY));
  
            _movePrev.copy(_moveCurr);
  
            break;
  
          case 2:
            _state = STATE.TOUCH_ZOOM_PAN;
  
            _moveCurr.copy(getMouseOnCircle(event.pageX - _movePrev.pageX, event.pageY - _movePrev.pageY));
  
            _movePrev.copy(_moveCurr);
  
            break;
        }
  
        scope.dispatchEvent(_endEvent);
      }
  
      function contextmenu(event) {
        if (scope.enabled === false) return;
        event.preventDefault();
      }
  
      function addPointer(event) {
        _pointers.push(event);
      }
  
      function removePointer(event) {
        delete _pointerPositions[event.pointerId];
  
        for (let i = 0; i < _pointers.length; i++) {
          if (_pointers[i].pointerId == event.pointerId) {
            _pointers.splice(i, 1);
  
            return;
          }
        }
      }
  
      function trackPointer(event) {
        let position = _pointerPositions[event.pointerId];
  
        if (position === undefined) {
          position = new THREE.Vector2();
          _pointerPositions[event.pointerId] = position;
        }
  
        position.set(event.pageX, event.pageY);
      }
  
      function getSecondPointerPosition(event) {
        const pointer = event.pointerId === _pointers[0].pointerId ? _pointers[1] : _pointers[0];
        return _pointerPositions[pointer.pointerId];
      }
  
      this.dispose = function () {
        scope.domElement.removeEventListener('contextmenu', contextmenu);
        scope.domElement.removeEventListener('pointerdown', onPointerDown);
        scope.domElement.removeEventListener('pointercancel', onPointerCancel);
        scope.domElement.removeEventListener('wheel', onMouseWheel);
        scope.domElement.removeEventListener('pointermove', onPointerMove);
        scope.domElement.removeEventListener('pointerup', onPointerUp);
        window.removeEventListener('keydown', keydown);
        window.removeEventListener('keyup', keyup);
      };
  
      this.domElement.addEventListener('contextmenu', contextmenu);
      this.domElement.addEventListener('pointerdown', onPointerDown);
      this.domElement.addEventListener('pointercancel', onPointerCancel);
      this.domElement.addEventListener('wheel', onMouseWheel, {
        passive: false
      });
      window.addEventListener('keydown', keydown);
      window.addEventListener('keyup', keyup);
      this.handleResize(); // force an update at start
  
      this.update();
    }
  
  }
  
  THREE.TrackballControls = TrackballControls;
  } )();
  