<!-- SOLIDIVISM · part 132 -->
group.add( meshB );
 *
 * scene.add( group );
 * ```
 *
 * @augments Object3D
 */
class Group extends Object3D {

	constructor() {

		super();

		/**
		 * This flag can be used for type testing.
		 *
		 * @type {boolean}
		 * @readonly
		 * @default true
		 */
		this.isGroup = true;

		this.type = 'Group';

	}

}

const _moveEvent = { type: 'move' };

/**
 * Class for representing a XR controller with its
 * different coordinate systems.
 *
 * @private
 */
class WebXRController {

	/**
	 * Constructs a new XR controller.
	 */
	constructor() {

		/**
		 * A group representing the target ray space
		 * of the XR controller.
		 *
		 * @private
		 * @type {?Group}
		 * @default null
		 */
		this._targetRay = null;

		/**
		 * A group representing the grip space
		 * of the XR controller.
		 *
		 * @private
		 * @type {?Group}
		 * @default null
		 */
		this._grip = null;

		/**
		 * A group representing the hand space
		 * of the XR controller.
		 *
		 * @private
		 * @type {?Group}
		 * @default null
		 */
		this._hand = null;

	}

	/**
	 * Returns a group representing the hand space of the XR controller.
	 *
	 * @return {Group} A group representing the hand space of the XR controller.
	 */
	getHandSpace() {

		if ( this._hand === null ) {

			this._hand = new Group();
			this._hand.matrixAutoUpdate = false;
			this._hand.visible = false;

			this._hand.joints = {};
			this._hand.inputState = { pinching: false };

		}

		return this._hand;

	}

	/**
	 * Returns a group representing the target ray space of the XR controller.
	 *
	 * @return {Group} A group representing the target ray space of the XR controller.
	 */
	getTargetRaySpace() {

		if ( this._targetRay === null ) {

			this._targetRay = new Group();
			this._targetRay.matrixAutoUpdate = false;
			this._targetRay.visible = false;
			this._targetRay.hasLinearVelocity = false;
			this._targetRay.linearVelocity = new Vector3();
			this._targetRay.hasAngularVelocity = false;
			this._targetRay.angularVelocity = new Vector3();

		}

		return this._targetRay;

	}

	/**
	 * Returns a group representing the grip space of the XR controller.
	 *
	 * @return {Group} A group representing the grip space of the XR controller.
	 */
	getGripSpace() {

		if ( this._grip === null ) {

			this._grip = new Group();
			this._grip.matrixAutoUpdate = false;
			this._grip.visible = false;
			this._grip.hasLinearVelocity = false;
			this._grip.linearVelocity = new Vector3();
			this._grip.hasAngularVelocity = false;
			this._grip.angularVelocity = new Vector3();
			this._grip.eventsEnabled = false;

		}

		return this._grip;

	}

	/**
	 * Dispatches the given event to the groups representing
	 * the different coordinate spaces of the XR controller.
	 *
	 * @param {Object} event - The event to dispatch.
	 * @return {WebXRController} A reference to this instance.
	 */
	dispatchEvent( event ) {

		if ( this._targetRay !== null ) {

			this._targetRay.dispatchEvent( event );

		}

		if ( this._grip !== null ) {

			this._grip.dispatchEvent( event );

		}

		if ( this._hand !== null ) {

			this._hand.dispatchEvent( event );

		}

		return this;

	}

	/**
	 * Connects the controller with the given XR input source.
	 *
	 * @param {XRInputSource} inputSource - The input source.
	 * @return {WebXRController} A reference to this instance.
	 */
	connect( inputSource ) {

		if ( inputSource && inputSource.hand ) {

			const hand = this._hand;

			if ( hand ) {

				for ( const inputjoint of inputSource.hand.values() ) {

					// Initialize hand with joints when connected
					this._getHandJoint( hand, inputjoint );

				}

			}

		}

		this.dispatchEvent( { type: 'connected', data: inputSource } );

		return this;

	}

	/**
	 * Disconnects the controller from the given XR input source.
	 *
	 * @param {XRInputSource} inputSource - The input source.
	 * @return {WebXRController} A reference to this instance.
	 */
	disconnect( inputSource ) {

		this.dispatchEvent( { type: 'disconnected', data: inputSource } );

		if ( this._targetRay !== null ) {

			this._targetRay.visible = false;

		}

		if ( this._grip !== null ) {

			this._grip.visible = false;

		}

		if ( this._hand !== null ) {

			this._hand.visible = false;

		}

		return this;

	}

	/**
	 * Updates the controller with the given input source, XR frame and reference space.
	 * This updates the transformations of the groups that represent the different
	 * coordinate systems of the controller.
	 *
	 * @param {XRInputSource} inputSource - The input source.
	 * @param {XRFrame} frame - The XR frame.
	 * @param {XRReferenceSpace} referenceSpace - The reference space.
	 * @return {WebXRController} A reference to this instance.
	 */
	update( inputSource, frame, referenceSpace ) {

		let inputPose = null;
		let gripPose = null;
		let handPose = null;

		const targetRay = this._targetRay;
		const grip = this._grip;
		const hand = this._hand;

		if ( inputSource && frame.session.visibilityState !== 'visibl