diff --git a/FBXLoader.js b/FBXLoader.js new file mode 100644 index 0000000..6a5a133 --- /dev/null +++ b/FBXLoader.js @@ -0,0 +1,4115 @@ +/** + * @author Kyle-Larson https://github.com/Kyle-Larson + * @author Takahiro https://github.com/takahirox + * @author Lewy Blue https://github.com/looeee + * + * Loader loads FBX file and generates Group representing FBX scene. + * Requires FBX file to be >= 7.0 and in ASCII or >= 6400 in Binary format + * Versions lower than this may load but will probably have errors + * + * Needs Support: + * Morph normals / blend shape normals + * + * FBX format references: + * https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure + * http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_index_html (C++ SDK reference) + * + * Binary format specification: + * https://code.blender.org/2013/08/fbx-binary-file-format-specification/ + */ + + +THREE.FBXLoader = ( function () { + + var fbxTree; + var connections; + var sceneGraph; + + function FBXLoader( manager ) { + + THREE.Loader.call( this, manager ); + + } + + FBXLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), { + + constructor: FBXLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var self = this; + + var path = ( self.path === '' ) ? THREE.LoaderUtils.extractUrlBase( url ) : self.path; + + var loader = new THREE.FileLoader( this.manager ); + loader.setPath( self.path ); + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( buffer ) { + + try { + + onLoad( self.parse( buffer, path ) ); + + } catch ( error ) { + + setTimeout( function () { + + if ( onError ) onError( error ); + + self.manager.itemError( url ); + + }, 0 ); + + } + + }, onProgress, onError ); + + }, + + parse: function ( FBXBuffer, path ) { + + if ( isFbxFormatBinary( FBXBuffer ) ) { + + fbxTree = new BinaryParser().parse( FBXBuffer ); + + } else { + + var FBXText = convertArrayBufferToString( FBXBuffer ); + + if ( ! isFbxFormatASCII( FBXText ) ) { + + throw new Error( 'THREE.FBXLoader: Unknown format.' ); + + } + + if ( getFbxVersion( FBXText ) < 7000 ) { + + throw new Error( 'THREE.FBXLoader: FBX version not supported, FileVersion: ' + getFbxVersion( FBXText ) ); + + } + + fbxTree = new TextParser().parse( FBXText ); + + } + + // console.log( fbxTree ); + + var textureLoader = new THREE.TextureLoader( this.manager ).setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); + + return new FBXTreeParser( textureLoader, this.manager ).parse( fbxTree ); + + } + + } ); + + // Parse the FBXTree object returned by the BinaryParser or TextParser and return a THREE.Group + function FBXTreeParser( textureLoader, manager ) { + + this.textureLoader = textureLoader; + this.manager = manager; + + } + + FBXTreeParser.prototype = { + + constructor: FBXTreeParser, + + parse: function () { + + connections = this.parseConnections(); + + var images = this.parseImages(); + var textures = this.parseTextures( images ); + var materials = this.parseMaterials( textures ); + var deformers = this.parseDeformers(); + var geometryMap = new GeometryParser().parse( deformers ); + + this.parseScene( deformers, geometryMap, materials ); + + return sceneGraph; + + }, + + // Parses FBXTree.Connections which holds parent-child connections between objects (e.g. material -> texture, model->geometry ) + // and details the connection type + parseConnections: function () { + + var connectionMap = new Map(); + + if ( 'Connections' in fbxTree ) { + + var rawConnections = fbxTree.Connections.connections; + + rawConnections.forEach( function ( rawConnection ) { + + var fromID = rawConnection[ 0 ]; + var toID = rawConnection[ 1 ]; + var relationship = rawConnection[ 2 ]; + + if ( ! connectionMap.has( fromID ) ) { + + connectionMap.set( fromID, { + parents: [], + children: [] + } ); + + } + + var parentRelationship = { ID: toID, relationship: relationship }; + connectionMap.get( fromID ).parents.push( parentRelationship ); + + if ( ! connectionMap.has( toID ) ) { + + connectionMap.set( toID, { + parents: [], + children: [] + } ); + + } + + var childRelationship = { ID: fromID, relationship: relationship }; + connectionMap.get( toID ).children.push( childRelationship ); + + } ); + + } + + return connectionMap; + + }, + + // Parse FBXTree.Objects.Video for embedded image data + // These images are connected to textures in FBXTree.Objects.Textures + // via FBXTree.Connections. + parseImages: function () { + + var images = {}; + var blobs = {}; + + if ( 'Video' in fbxTree.Objects ) { + + var videoNodes = fbxTree.Objects.Video; + + for ( var nodeID in videoNodes ) { + + var videoNode = videoNodes[ nodeID ]; + + var id = parseInt( nodeID ); + + images[ id ] = videoNode.RelativeFilename || videoNode.Filename; + + // raw image data is in videoNode.Content + if ( 'Content' in videoNode ) { + + var arrayBufferContent = ( videoNode.Content instanceof ArrayBuffer ) && ( videoNode.Content.byteLength > 0 ); + var base64Content = ( typeof videoNode.Content === 'string' ) && ( videoNode.Content !== '' ); + + if ( arrayBufferContent || base64Content ) { + + var image = this.parseImage( videoNodes[ nodeID ] ); + + blobs[ videoNode.RelativeFilename || videoNode.Filename ] = image; + + } + + } + + } + + } + + for ( var id in images ) { + + var filename = images[ id ]; + + if ( blobs[ filename ] !== undefined ) images[ id ] = blobs[ filename ]; + else images[ id ] = images[ id ].split( '\\' ).pop(); + + } + + return images; + + }, + + // Parse embedded image data in FBXTree.Video.Content + parseImage: function ( videoNode ) { + + var content = videoNode.Content; + var fileName = videoNode.RelativeFilename || videoNode.Filename; + var extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase(); + + var type; + + switch ( extension ) { + + case 'bmp': + + type = 'image/bmp'; + break; + + case 'jpg': + case 'jpeg': + + type = 'image/jpeg'; + break; + + case 'png': + + type = 'image/png'; + break; + + case 'tif': + + type = 'image/tiff'; + break; + + case 'tga': + + if ( this.manager.getHandler( '.tga' ) === null ) { + + console.warn( 'FBXLoader: TGA loader not found, skipping ', fileName ); + + } + + type = 'image/tga'; + break; + + default: + + console.warn( 'FBXLoader: Image type "' + extension + '" is not supported.' ); + return; + + } + + if ( typeof content === 'string' ) { // ASCII format + + return 'data:' + type + ';base64,' + content; + + } else { // Binary Format + + var array = new Uint8Array( content ); + return window.URL.createObjectURL( new Blob( [ array ], { type: type } ) ); + + } + + }, + + // Parse nodes in FBXTree.Objects.Texture + // These contain details such as UV scaling, cropping, rotation etc and are connected + // to images in FBXTree.Objects.Video + parseTextures: function ( images ) { + + var textureMap = new Map(); + + if ( 'Texture' in fbxTree.Objects ) { + + var textureNodes = fbxTree.Objects.Texture; + for ( var nodeID in textureNodes ) { + + var texture = this.parseTexture( textureNodes[ nodeID ], images ); + textureMap.set( parseInt( nodeID ), texture ); + + } + + } + + return textureMap; + + }, + + // Parse individual node in FBXTree.Objects.Texture + parseTexture: function ( textureNode, images ) { + + var texture = this.loadTexture( textureNode, images ); + + texture.ID = textureNode.id; + + texture.name = textureNode.attrName; + + var wrapModeU = textureNode.WrapModeU; + var wrapModeV = textureNode.WrapModeV; + + var valueU = wrapModeU !== undefined ? wrapModeU.value : 0; + var valueV = wrapModeV !== undefined ? wrapModeV.value : 0; + + // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a + // 0: repeat(default), 1: clamp + + texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + + if ( 'Scaling' in textureNode ) { + + var values = textureNode.Scaling.value; + + texture.repeat.x = values[ 0 ]; + texture.repeat.y = values[ 1 ]; + + } + + return texture; + + }, + + // load a texture specified as a blob or data URI, or via an external URL using THREE.TextureLoader + loadTexture: function ( textureNode, images ) { + + var fileName; + + var currentPath = this.textureLoader.path; + + var children = connections.get( textureNode.id ).children; + + if ( children !== undefined && children.length > 0 && images[ children[ 0 ].ID ] !== undefined ) { + + fileName = images[ children[ 0 ].ID ]; + + if ( fileName.indexOf( 'blob:' ) === 0 || fileName.indexOf( 'data:' ) === 0 ) { + + this.textureLoader.setPath( undefined ); + + } + + } + + var texture; + + var extension = textureNode.FileName.slice( - 3 ).toLowerCase(); + + if ( extension === 'tga' ) { + + var loader = this.manager.getHandler( '.tga' ); + + if ( loader === null ) { + + console.warn( 'FBXLoader: TGA loader not found, creating placeholder texture for', textureNode.RelativeFilename ); + texture = new THREE.Texture(); + + } else { + + texture = loader.load( fileName ); + + } + + } else if ( extension === 'psd' ) { + + console.warn( 'FBXLoader: PSD textures are not supported, creating placeholder texture for', textureNode.RelativeFilename ); + texture = new THREE.Texture(); + + } else { + + texture = this.textureLoader.load( fileName ); + + } + + this.textureLoader.setPath( currentPath ); + + return texture; + + }, + + // Parse nodes in FBXTree.Objects.Material + parseMaterials: function ( textureMap ) { + + var materialMap = new Map(); + + if ( 'Material' in fbxTree.Objects ) { + + var materialNodes = fbxTree.Objects.Material; + + for ( var nodeID in materialNodes ) { + + var material = this.parseMaterial( materialNodes[ nodeID ], textureMap ); + + if ( material !== null ) materialMap.set( parseInt( nodeID ), material ); + + } + + } + + return materialMap; + + }, + + // Parse single node in FBXTree.Objects.Material + // Materials are connected to texture maps in FBXTree.Objects.Textures + // FBX format currently only supports Lambert and Phong shading models + parseMaterial: function ( materialNode, textureMap ) { + + var ID = materialNode.id; + var name = materialNode.attrName; + var type = materialNode.ShadingModel; + + // Case where FBX wraps shading model in property object. + if ( typeof type === 'object' ) { + + type = type.value; + + } + + // Ignore unused materials which don't have any connections. + if ( ! connections.has( ID ) ) return null; + + var parameters = this.parseParameters( materialNode, textureMap, ID ); + + var material; + + switch ( type.toLowerCase() ) { + + case 'phong': + material = new THREE.MeshPhongMaterial(); + break; + case 'lambert': + material = new THREE.MeshLambertMaterial(); + break; + default: + console.warn( 'THREE.FBXLoader: unknown material type "%s". Defaulting to MeshPhongMaterial.', type ); + material = new THREE.MeshPhongMaterial(); + break; + + } + + material.setValues( parameters ); + material.name = name; + + return material; + + }, + + // Parse FBX material and return parameters suitable for a three.js material + // Also parse the texture map and return any textures associated with the material + parseParameters: function ( materialNode, textureMap, ID ) { + + var parameters = {}; + + if ( materialNode.BumpFactor ) { + + parameters.bumpScale = materialNode.BumpFactor.value; + + } + if ( materialNode.Diffuse ) { + + parameters.color = new THREE.Color().fromArray( materialNode.Diffuse.value ); + + } else if ( materialNode.DiffuseColor && materialNode.DiffuseColor.type === 'Color' ) { + + // The blender exporter exports diffuse here instead of in materialNode.Diffuse + parameters.color = new THREE.Color().fromArray( materialNode.DiffuseColor.value ); + + } + + if ( materialNode.DisplacementFactor ) { + + parameters.displacementScale = materialNode.DisplacementFactor.value; + + } + + if ( materialNode.Emissive ) { + + parameters.emissive = new THREE.Color().fromArray( materialNode.Emissive.value ); + + } else if ( materialNode.EmissiveColor && materialNode.EmissiveColor.type === 'Color' ) { + + // The blender exporter exports emissive color here instead of in materialNode.Emissive + parameters.emissive = new THREE.Color().fromArray( materialNode.EmissiveColor.value ); + + } + + if ( materialNode.EmissiveFactor ) { + + parameters.emissiveIntensity = parseFloat( materialNode.EmissiveFactor.value ); + + } + + if ( materialNode.Opacity ) { + + parameters.opacity = parseFloat( materialNode.Opacity.value ); + + } + + if ( parameters.opacity < 1.0 ) { + + parameters.transparent = true; + + } + + if ( materialNode.ReflectionFactor ) { + + parameters.reflectivity = materialNode.ReflectionFactor.value; + + } + + if ( materialNode.Shininess ) { + + parameters.shininess = materialNode.Shininess.value; + + } + + if ( materialNode.Specular ) { + + parameters.specular = new THREE.Color().fromArray( materialNode.Specular.value ); + + } else if ( materialNode.SpecularColor && materialNode.SpecularColor.type === 'Color' ) { + + // The blender exporter exports specular color here instead of in materialNode.Specular + parameters.specular = new THREE.Color().fromArray( materialNode.SpecularColor.value ); + + } + + var self = this; + connections.get( ID ).children.forEach( function ( child ) { + + var type = child.relationship; + + switch ( type ) { + + case 'Bump': + parameters.bumpMap = self.getTexture( textureMap, child.ID ); + break; + + case 'Maya|TEX_ao_map': + parameters.aoMap = self.getTexture( textureMap, child.ID ); + break; + + case 'DiffuseColor': + case 'Maya|TEX_color_map': + parameters.map = self.getTexture( textureMap, child.ID ); + parameters.map.encoding = THREE.sRGBEncoding; + break; + + case 'DisplacementColor': + parameters.displacementMap = self.getTexture( textureMap, child.ID ); + break; + + case 'EmissiveColor': + parameters.emissiveMap = self.getTexture( textureMap, child.ID ); + parameters.emissiveMap.encoding = THREE.sRGBEncoding; + break; + + case 'NormalMap': + case 'Maya|TEX_normal_map': + parameters.normalMap = self.getTexture( textureMap, child.ID ); + break; + + case 'ReflectionColor': + parameters.envMap = self.getTexture( textureMap, child.ID ); + parameters.envMap.mapping = THREE.EquirectangularReflectionMapping; + parameters.envMap.encoding = THREE.sRGBEncoding; + break; + + case 'SpecularColor': + parameters.specularMap = self.getTexture( textureMap, child.ID ); + parameters.specularMap.encoding = THREE.sRGBEncoding; + break; + + case 'TransparentColor': + parameters.alphaMap = self.getTexture( textureMap, child.ID ); + parameters.transparent = true; + break; + + case 'AmbientColor': + case 'ShininessExponent': // AKA glossiness map + case 'SpecularFactor': // AKA specularLevel + case 'VectorDisplacementColor': // NOTE: Seems to be a copy of DisplacementColor + default: + console.warn( 'THREE.FBXLoader: %s map is not supported in three.js, skipping texture.', type ); + break; + + } + + } ); + + return parameters; + + }, + + // get a texture from the textureMap for use by a material. + getTexture: function ( textureMap, id ) { + + // if the texture is a layered texture, just use the first layer and issue a warning + if ( 'LayeredTexture' in fbxTree.Objects && id in fbxTree.Objects.LayeredTexture ) { + + console.warn( 'THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer.' ); + id = connections.get( id ).children[ 0 ].ID; + + } + + return textureMap.get( id ); + + }, + + // Parse nodes in FBXTree.Objects.Deformer + // Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here + // Generates map of Skeleton-like objects for use later when generating and binding skeletons. + parseDeformers: function () { + + var skeletons = {}; + var morphTargets = {}; + + if ( 'Deformer' in fbxTree.Objects ) { + + var DeformerNodes = fbxTree.Objects.Deformer; + + for ( var nodeID in DeformerNodes ) { + + var deformerNode = DeformerNodes[ nodeID ]; + + var relationships = connections.get( parseInt( nodeID ) ); + + if ( deformerNode.attrType === 'Skin' ) { + + var skeleton = this.parseSkeleton( relationships, DeformerNodes ); + skeleton.ID = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: skeleton attached to more than one geometry is not supported.' ); + skeleton.geometryID = relationships.parents[ 0 ].ID; + + skeletons[ nodeID ] = skeleton; + + } else if ( deformerNode.attrType === 'BlendShape' ) { + + var morphTarget = { + id: nodeID, + }; + + morphTarget.rawTargets = this.parseMorphTargets( relationships, DeformerNodes ); + morphTarget.id = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: morph target attached to more than one geometry is not supported.' ); + + morphTargets[ nodeID ] = morphTarget; + + } + + } + + } + + return { + + skeletons: skeletons, + morphTargets: morphTargets, + + }; + + }, + + // Parse single nodes in FBXTree.Objects.Deformer + // The top level skeleton node has type 'Skin' and sub nodes have type 'Cluster' + // Each skin node represents a skeleton and each cluster node represents a bone + parseSkeleton: function ( relationships, deformerNodes ) { + + var rawBones = []; + + relationships.children.forEach( function ( child ) { + + var boneNode = deformerNodes[ child.ID ]; + + if ( boneNode.attrType !== 'Cluster' ) return; + + var rawBone = { + + ID: child.ID, + indices: [], + weights: [], + transformLink: new THREE.Matrix4().fromArray( boneNode.TransformLink.a ), + // transform: new THREE.Matrix4().fromArray( boneNode.Transform.a ), + // linkMode: boneNode.Mode, + + }; + + if ( 'Indexes' in boneNode ) { + + rawBone.indices = boneNode.Indexes.a; + rawBone.weights = boneNode.Weights.a; + + } + + rawBones.push( rawBone ); + + } ); + + return { + + rawBones: rawBones, + bones: [] + + }; + + }, + + // The top level morph deformer node has type "BlendShape" and sub nodes have type "BlendShapeChannel" + parseMorphTargets: function ( relationships, deformerNodes ) { + + var rawMorphTargets = []; + + for ( var i = 0; i < relationships.children.length; i ++ ) { + + var child = relationships.children[ i ]; + + var morphTargetNode = deformerNodes[ child.ID ]; + + var rawMorphTarget = { + + name: morphTargetNode.attrName, + initialWeight: morphTargetNode.DeformPercent, + id: morphTargetNode.id, + fullWeights: morphTargetNode.FullWeights.a + + }; + + if ( morphTargetNode.attrType !== 'BlendShapeChannel' ) return; + + rawMorphTarget.geoID = connections.get( parseInt( child.ID ) ).children.filter( function ( child ) { + + return child.relationship === undefined; + + } )[ 0 ].ID; + + rawMorphTargets.push( rawMorphTarget ); + + } + + return rawMorphTargets; + + }, + + // create the main THREE.Group() to be returned by the loader + parseScene: function ( deformers, geometryMap, materialMap ) { + + sceneGraph = new THREE.Group(); + + var modelMap = this.parseModels( deformers.skeletons, geometryMap, materialMap ); + + var modelNodes = fbxTree.Objects.Model; + + var self = this; + modelMap.forEach( function ( model ) { + + var modelNode = modelNodes[ model.ID ]; + self.setLookAtProperties( model, modelNode ); + + var parentConnections = connections.get( model.ID ).parents; + + parentConnections.forEach( function ( connection ) { + + var parent = modelMap.get( connection.ID ); + if ( parent !== undefined ) parent.add( model ); + + } ); + + if ( model.parent === null ) { + + sceneGraph.add( model ); + + } + + + } ); + + this.bindSkeleton( deformers.skeletons, geometryMap, modelMap ); + + this.createAmbientLight(); + + this.setupMorphMaterials(); + + sceneGraph.traverse( function ( node ) { + + if ( node.userData.transformData ) { + + if ( node.parent ) node.userData.transformData.parentMatrixWorld = node.parent.matrix; + + var transform = generateTransform( node.userData.transformData ); + + node.applyMatrix( transform ); + + } + + } ); + + var animations = new AnimationParser().parse(); + + // if all the models where already combined in a single group, just return that + if ( sceneGraph.children.length === 1 && sceneGraph.children[ 0 ].isGroup ) { + + sceneGraph.children[ 0 ].animations = animations; + sceneGraph = sceneGraph.children[ 0 ]; + + } + + sceneGraph.animations = animations; + + }, + + // parse nodes in FBXTree.Objects.Model + parseModels: function ( skeletons, geometryMap, materialMap ) { + + var modelMap = new Map(); + var modelNodes = fbxTree.Objects.Model; + + for ( var nodeID in modelNodes ) { + + var id = parseInt( nodeID ); + var node = modelNodes[ nodeID ]; + var relationships = connections.get( id ); + + var model = this.buildSkeleton( relationships, skeletons, id, node.attrName ); + + if ( ! model ) { + + switch ( node.attrType ) { + + case 'Camera': + model = this.createCamera( relationships ); + break; + case 'Light': + model = this.createLight( relationships ); + break; + case 'Mesh': + model = this.createMesh( relationships, geometryMap, materialMap ); + break; + case 'NurbsCurve': + model = this.createCurve( relationships, geometryMap ); + break; + case 'LimbNode': + case 'Root': + model = new THREE.Bone(); + break; + case 'Null': + default: + model = new THREE.Group(); + break; + + } + + model.name = node.attrName ? THREE.PropertyBinding.sanitizeNodeName( node.attrName ) : ''; + + model.ID = id; + + } + + this.getTransformData( model, node ); + modelMap.set( id, model ); + + } + + return modelMap; + + }, + + buildSkeleton: function ( relationships, skeletons, id, name ) { + + var bone = null; + + relationships.parents.forEach( function ( parent ) { + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + if ( rawBone.ID === parent.ID ) { + + var subBone = bone; + bone = new THREE.Bone(); + + bone.matrixWorld.copy( rawBone.transformLink ); + + // set name and id here - otherwise in cases where "subBone" is created it will not have a name / id + + bone.name = name ? THREE.PropertyBinding.sanitizeNodeName( name ) : ''; + bone.ID = id; + + skeleton.bones[ i ] = bone; + + // In cases where a bone is shared between multiple meshes + // duplicate the bone here and and it as a child of the first bone + if ( subBone !== null ) { + + // bone.add( subBone ); + + } + + } + + } ); + + } + + } ); + + return bone; + + }, + + // create a THREE.PerspectiveCamera or THREE.OrthographicCamera + createCamera: function ( relationships ) { + + var model; + var cameraAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = fbxTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + cameraAttribute = attr; + + } + + } ); + + if ( cameraAttribute === undefined ) { + + model = new THREE.Object3D(); + + } else { + + var type = 0; + if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) { + + type = 1; + + } + + var nearClippingPlane = 1; + if ( cameraAttribute.NearPlane !== undefined ) { + + nearClippingPlane = cameraAttribute.NearPlane.value / 1000; + + } + + var farClippingPlane = 1000; + if ( cameraAttribute.FarPlane !== undefined ) { + + farClippingPlane = cameraAttribute.FarPlane.value / 1000; + + } + + + var width = window.innerWidth; + var height = window.innerHeight; + + if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) { + + width = cameraAttribute.AspectWidth.value; + height = cameraAttribute.AspectHeight.value; + + } + + var aspect = width / height; + + var fov = 45; + if ( cameraAttribute.FieldOfView !== undefined ) { + + fov = cameraAttribute.FieldOfView.value; + + } + + var focalLength = cameraAttribute.FocalLength ? cameraAttribute.FocalLength.value : null; + + switch ( type ) { + + case 0: // Perspective + model = new THREE.PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane ); + if ( focalLength !== null ) model.setFocalLength( focalLength ); + break; + + case 1: // Orthographic + model = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' ); + model = new THREE.Object3D(); + break; + + } + + } + + return model; + + }, + + // Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight + createLight: function ( relationships ) { + + var model; + var lightAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = fbxTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + lightAttribute = attr; + + } + + } ); + + if ( lightAttribute === undefined ) { + + model = new THREE.Object3D(); + + } else { + + var type; + + // LightType can be undefined for Point lights + if ( lightAttribute.LightType === undefined ) { + + type = 0; + + } else { + + type = lightAttribute.LightType.value; + + } + + var color = 0xffffff; + + if ( lightAttribute.Color !== undefined ) { + + color = new THREE.Color().fromArray( lightAttribute.Color.value ); + + } + + var intensity = ( lightAttribute.Intensity === undefined ) ? 1 : lightAttribute.Intensity.value / 100; + + // light disabled + if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) { + + intensity = 0; + + } + + var distance = 0; + if ( lightAttribute.FarAttenuationEnd !== undefined ) { + + if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) { + + distance = 0; + + } else { + + distance = lightAttribute.FarAttenuationEnd.value; + + } + + } + + // TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd? + var decay = 1; + + switch ( type ) { + + case 0: // Point + model = new THREE.PointLight( color, intensity, distance, decay ); + break; + + case 1: // Directional + model = new THREE.DirectionalLight( color, intensity ); + break; + + case 2: // Spot + var angle = Math.PI / 3; + + if ( lightAttribute.InnerAngle !== undefined ) { + + angle = THREE.Math.degToRad( lightAttribute.InnerAngle.value ); + + } + + var penumbra = 0; + if ( lightAttribute.OuterAngle !== undefined ) { + + // TODO: this is not correct - FBX calculates outer and inner angle in degrees + // with OuterAngle > InnerAngle && OuterAngle <= Math.PI + // while three.js uses a penumbra between (0, 1) to attenuate the inner angle + penumbra = THREE.Math.degToRad( lightAttribute.OuterAngle.value ); + penumbra = Math.max( penumbra, 1 ); + + } + + model = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.' ); + model = new THREE.PointLight( color, intensity ); + break; + + } + + if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) { + + model.castShadow = true; + + } + + } + + return model; + + }, + + createMesh: function ( relationships, geometryMap, materialMap ) { + + var model; + var geometry = null; + var material = null; + var materials = []; + + // get geometry and materials(s) from connections + relationships.children.forEach( function ( child ) { + + if ( geometryMap.has( child.ID ) ) { + + geometry = geometryMap.get( child.ID ); + + } + + if ( materialMap.has( child.ID ) ) { + + materials.push( materialMap.get( child.ID ) ); + + } + + } ); + + if ( materials.length > 1 ) { + + material = materials; + + } else if ( materials.length > 0 ) { + + material = materials[ 0 ]; + + } else { + + material = new THREE.MeshPhongMaterial( { color: 0xcccccc } ); + materials.push( material ); + + } + + if ( 'color' in geometry.attributes ) { + + materials.forEach( function ( material ) { + + material.vertexColors = THREE.VertexColors; + + } ); + + } + + if ( geometry.FBX_Deformer ) { + + materials.forEach( function ( material ) { + + material.skinning = true; + + } ); + + model = new THREE.SkinnedMesh( geometry, material ); + model.normalizeSkinWeights(); + + } else { + + model = new THREE.Mesh( geometry, material ); + + } + + return model; + + }, + + createCurve: function ( relationships, geometryMap ) { + + var geometry = relationships.children.reduce( function ( geo, child ) { + + if ( geometryMap.has( child.ID ) ) geo = geometryMap.get( child.ID ); + + return geo; + + }, null ); + + // FBX does not list materials for Nurbs lines, so we'll just put our own in here. + var material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 1 } ); + return new THREE.Line( geometry, material ); + + }, + + // parse the model node for transform data + getTransformData: function ( model, modelNode ) { + + var transformData = {}; + + if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.value ); + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = getEulerOrder( modelNode.RotationOrder.value ); + else transformData.eulerOrder = 'ZYX'; + + if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value; + + if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value; + if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value; + if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value; + + if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value; + + if ( 'ScalingOffset' in modelNode ) transformData.scalingOffset = modelNode.ScalingOffset.value; + if ( 'ScalingPivot' in modelNode ) transformData.scalingPivot = modelNode.ScalingPivot.value; + + if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value; + if ( 'RotationPivot' in modelNode ) transformData.rotationPivot = modelNode.RotationPivot.value; + + model.userData.transformData = transformData; + + }, + + setLookAtProperties: function ( model, modelNode ) { + + if ( 'LookAtProperty' in modelNode ) { + + var children = connections.get( model.ID ).children; + + children.forEach( function ( child ) { + + if ( child.relationship === 'LookAtProperty' ) { + + var lookAtTarget = fbxTree.Objects.Model[ child.ID ]; + + if ( 'Lcl_Translation' in lookAtTarget ) { + + var pos = lookAtTarget.Lcl_Translation.value; + + // DirectionalLight, SpotLight + if ( model.target !== undefined ) { + + model.target.position.fromArray( pos ); + sceneGraph.add( model.target ); + + } else { // Cameras and other Object3Ds + + model.lookAt( new THREE.Vector3().fromArray( pos ) ); + + } + + } + + } + + } ); + + } + + }, + + bindSkeleton: function ( skeletons, geometryMap, modelMap ) { + + var bindMatrices = this.parsePoseNodes(); + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + var parents = connections.get( parseInt( skeleton.ID ) ).parents; + + parents.forEach( function ( parent ) { + + if ( geometryMap.has( parent.ID ) ) { + + var geoID = parent.ID; + var geoRelationships = connections.get( geoID ); + + geoRelationships.parents.forEach( function ( geoConnParent ) { + + if ( modelMap.has( geoConnParent.ID ) ) { + + var model = modelMap.get( geoConnParent.ID ); + + model.bind( new THREE.Skeleton( skeleton.bones ), bindMatrices[ geoConnParent.ID ] ); + + } + + } ); + + } + + } ); + + } + + }, + + parsePoseNodes: function () { + + var bindMatrices = {}; + + if ( 'Pose' in fbxTree.Objects ) { + + var BindPoseNode = fbxTree.Objects.Pose; + + for ( var nodeID in BindPoseNode ) { + + if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) { + + var poseNodes = BindPoseNode[ nodeID ].PoseNode; + + if ( Array.isArray( poseNodes ) ) { + + poseNodes.forEach( function ( poseNode ) { + + bindMatrices[ poseNode.Node ] = new THREE.Matrix4().fromArray( poseNode.Matrix.a ); + + } ); + + } else { + + bindMatrices[ poseNodes.Node ] = new THREE.Matrix4().fromArray( poseNodes.Matrix.a ); + + } + + } + + } + + } + + return bindMatrices; + + }, + + // Parse ambient color in FBXTree.GlobalSettings - if it's not set to black (default), create an ambient light + createAmbientLight: function () { + + if ( 'GlobalSettings' in fbxTree && 'AmbientColor' in fbxTree.GlobalSettings ) { + + var ambientColor = fbxTree.GlobalSettings.AmbientColor.value; + var r = ambientColor[ 0 ]; + var g = ambientColor[ 1 ]; + var b = ambientColor[ 2 ]; + + if ( r !== 0 || g !== 0 || b !== 0 ) { + + var color = new THREE.Color( r, g, b ); + sceneGraph.add( new THREE.AmbientLight( color, 1 ) ); + + } + + } + + }, + + setupMorphMaterials: function () { + + var self = this; + sceneGraph.traverse( function ( child ) { + + if ( child.isMesh ) { + + if ( child.geometry.morphAttributes.position && child.geometry.morphAttributes.position.length ) { + + if ( Array.isArray( child.material ) ) { + + child.material.forEach( function ( material, i ) { + + self.setupMorphMaterial( child, material, i ); + + } ); + + } else { + + self.setupMorphMaterial( child, child.material ); + + } + + } + + } + + } ); + + }, + + setupMorphMaterial: function ( child, material, index ) { + + var uuid = child.uuid; + var matUuid = material.uuid; + + // if a geometry has morph targets, it cannot share the material with other geometries + var sharedMat = false; + + sceneGraph.traverse( function ( node ) { + + if ( node.isMesh ) { + + if ( Array.isArray( node.material ) ) { + + node.material.forEach( function ( mat ) { + + if ( mat.uuid === matUuid && node.uuid !== uuid ) sharedMat = true; + + } ); + + } else if ( node.material.uuid === matUuid && node.uuid !== uuid ) sharedMat = true; + + } + + } ); + + if ( sharedMat === true ) { + + var clonedMat = material.clone(); + clonedMat.morphTargets = true; + + if ( index === undefined ) child.material = clonedMat; + else child.material[ index ] = clonedMat; + + } else material.morphTargets = true; + + } + + }; + + // parse Geometry data from FBXTree and return map of BufferGeometries + function GeometryParser() {} + + GeometryParser.prototype = { + + constructor: GeometryParser, + + // Parse nodes in FBXTree.Objects.Geometry + parse: function ( deformers ) { + + var geometryMap = new Map(); + + if ( 'Geometry' in fbxTree.Objects ) { + + var geoNodes = fbxTree.Objects.Geometry; + + for ( var nodeID in geoNodes ) { + + var relationships = connections.get( parseInt( nodeID ) ); + var geo = this.parseGeometry( relationships, geoNodes[ nodeID ], deformers ); + + geometryMap.set( parseInt( nodeID ), geo ); + + } + + } + + return geometryMap; + + }, + + // Parse single node in FBXTree.Objects.Geometry + parseGeometry: function ( relationships, geoNode, deformers ) { + + switch ( geoNode.attrType ) { + + case 'Mesh': + return this.parseMeshGeometry( relationships, geoNode, deformers ); + break; + + case 'NurbsCurve': + return this.parseNurbsGeometry( geoNode ); + break; + + } + + }, + + + // Parse single node mesh geometry in FBXTree.Objects.Geometry + parseMeshGeometry: function ( relationships, geoNode, deformers ) { + + var skeletons = deformers.skeletons; + var morphTargets = []; + + var modelNodes = relationships.parents.map( function ( parent ) { + + return fbxTree.Objects.Model[ parent.ID ]; + + } ); + + // don't create geometry if it is not associated with any models + if ( modelNodes.length === 0 ) return; + + var skeleton = relationships.children.reduce( function ( skeleton, child ) { + + if ( skeletons[ child.ID ] !== undefined ) skeleton = skeletons[ child.ID ]; + + return skeleton; + + }, null ); + + relationships.children.forEach( function ( child ) { + + if ( deformers.morphTargets[ child.ID ] !== undefined ) { + + morphTargets.push( deformers.morphTargets[ child.ID ] ); + + } + + } ); + + // Assume one model and get the preRotation from that + // if there is more than one model associated with the geometry this may cause problems + var modelNode = modelNodes[ 0 ]; + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = getEulerOrder( modelNode.RotationOrder.value ); + if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.value ); + + if ( 'GeometricTranslation' in modelNode ) transformData.translation = modelNode.GeometricTranslation.value; + if ( 'GeometricRotation' in modelNode ) transformData.rotation = modelNode.GeometricRotation.value; + if ( 'GeometricScaling' in modelNode ) transformData.scale = modelNode.GeometricScaling.value; + + var transform = generateTransform( transformData ); + + return this.genGeometry( geoNode, skeleton, morphTargets, transform ); + + }, + + // Generate a THREE.BufferGeometry from a node in FBXTree.Objects.Geometry + genGeometry: function ( geoNode, skeleton, morphTargets, preTransform ) { + + var geo = new THREE.BufferGeometry(); + if ( geoNode.attrName ) geo.name = geoNode.attrName; + + var geoInfo = this.parseGeoNode( geoNode, skeleton ); + var buffers = this.genBuffers( geoInfo ); + + var positionAttribute = new THREE.Float32BufferAttribute( buffers.vertex, 3 ); + + preTransform.applyToBufferAttribute( positionAttribute ); + + geo.setAttribute( 'position', positionAttribute ); + + if ( buffers.colors.length > 0 ) { + + geo.setAttribute( 'color', new THREE.Float32BufferAttribute( buffers.colors, 3 ) ); + + } + + if ( skeleton ) { + + geo.setAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( buffers.weightsIndices, 4 ) ); + + geo.setAttribute( 'skinWeight', new THREE.Float32BufferAttribute( buffers.vertexWeights, 4 ) ); + + // used later to bind the skeleton to the model + geo.FBX_Deformer = skeleton; + + } + + if ( buffers.normal.length > 0 ) { + + var normalAttribute = new THREE.Float32BufferAttribute( buffers.normal, 3 ); + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( preTransform ); + normalMatrix.applyToBufferAttribute( normalAttribute ); + + geo.setAttribute( 'normal', normalAttribute ); + + } + + buffers.uvs.forEach( function ( uvBuffer, i ) { + + // subsequent uv buffers are called 'uv1', 'uv2', ... + var name = 'uv' + ( i + 1 ).toString(); + + // the first uv buffer is just called 'uv' + if ( i === 0 ) { + + name = 'uv'; + + } + + geo.setAttribute( name, new THREE.Float32BufferAttribute( buffers.uvs[ i ], 2 ) ); + + } ); + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + // Convert the material indices of each vertex into rendering groups on the geometry. + var prevMaterialIndex = buffers.materialIndex[ 0 ]; + var startIndex = 0; + + buffers.materialIndex.forEach( function ( currentIndex, i ) { + + if ( currentIndex !== prevMaterialIndex ) { + + geo.addGroup( startIndex, i - startIndex, prevMaterialIndex ); + + prevMaterialIndex = currentIndex; + startIndex = i; + + } + + } ); + + // the loop above doesn't add the last group, do that here. + if ( geo.groups.length > 0 ) { + + var lastGroup = geo.groups[ geo.groups.length - 1 ]; + var lastIndex = lastGroup.start + lastGroup.count; + + if ( lastIndex !== buffers.materialIndex.length ) { + + geo.addGroup( lastIndex, buffers.materialIndex.length - lastIndex, prevMaterialIndex ); + + } + + } + + // case where there are multiple materials but the whole geometry is only + // using one of them + if ( geo.groups.length === 0 ) { + + geo.addGroup( 0, buffers.materialIndex.length, buffers.materialIndex[ 0 ] ); + + } + + } + + this.addMorphTargets( geo, geoNode, morphTargets, preTransform ); + + return geo; + + }, + + parseGeoNode: function ( geoNode, skeleton ) { + + var geoInfo = {}; + + geoInfo.vertexPositions = ( geoNode.Vertices !== undefined ) ? geoNode.Vertices.a : []; + geoInfo.vertexIndices = ( geoNode.PolygonVertexIndex !== undefined ) ? geoNode.PolygonVertexIndex.a : []; + + if ( geoNode.LayerElementColor ) { + + geoInfo.color = this.parseVertexColors( geoNode.LayerElementColor[ 0 ] ); + + } + + if ( geoNode.LayerElementMaterial ) { + + geoInfo.material = this.parseMaterialIndices( geoNode.LayerElementMaterial[ 0 ] ); + + } + + if ( geoNode.LayerElementNormal ) { + + geoInfo.normal = this.parseNormals( geoNode.LayerElementNormal[ 0 ] ); + + } + + if ( geoNode.LayerElementUV ) { + + geoInfo.uv = []; + + var i = 0; + while ( geoNode.LayerElementUV[ i ] ) { + + geoInfo.uv.push( this.parseUVs( geoNode.LayerElementUV[ i ] ) ); + i ++; + + } + + } + + geoInfo.weightTable = {}; + + if ( skeleton !== null ) { + + geoInfo.skeleton = skeleton; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + // loop over the bone's vertex indices and weights + rawBone.indices.forEach( function ( index, j ) { + + if ( geoInfo.weightTable[ index ] === undefined ) geoInfo.weightTable[ index ] = []; + + geoInfo.weightTable[ index ].push( { + + id: i, + weight: rawBone.weights[ j ], + + } ); + + } ); + + } ); + + } + + return geoInfo; + + }, + + genBuffers: function ( geoInfo ) { + + var buffers = { + vertex: [], + normal: [], + colors: [], + uvs: [], + materialIndex: [], + vertexWeights: [], + weightsIndices: [], + }; + + var polygonIndex = 0; + var faceLength = 0; + var displayedWeightsWarning = false; + + // these will hold data for a single face + var facePositionIndexes = []; + var faceNormals = []; + var faceColors = []; + var faceUVs = []; + var faceWeights = []; + var faceWeightIndices = []; + + var self = this; + geoInfo.vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) { + + var endOfFace = false; + + // Face index and vertex index arrays are combined in a single array + // A cube with quad faces looks like this: + // PolygonVertexIndex: *24 { + // a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5 + // } + // Negative numbers mark the end of a face - first face here is 0, 1, 3, -3 + // to find index of last vertex bit shift the index: ^ - 1 + if ( vertexIndex < 0 ) { + + vertexIndex = vertexIndex ^ - 1; // equivalent to ( x * -1 ) - 1 + endOfFace = true; + + } + + var weightIndices = []; + var weights = []; + + facePositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 ); + + if ( geoInfo.color ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.color ); + + faceColors.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.skeleton ) { + + if ( geoInfo.weightTable[ vertexIndex ] !== undefined ) { + + geoInfo.weightTable[ vertexIndex ].forEach( function ( wt ) { + + weights.push( wt.weight ); + weightIndices.push( wt.id ); + + } ); + + + } + + if ( weights.length > 4 ) { + + if ( ! displayedWeightsWarning ) { + + console.warn( 'THREE.FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' ); + displayedWeightsWarning = true; + + } + + var wIndex = [ 0, 0, 0, 0 ]; + var Weight = [ 0, 0, 0, 0 ]; + + weights.forEach( function ( weight, weightIndex ) { + + var currentWeight = weight; + var currentIndex = weightIndices[ weightIndex ]; + + Weight.forEach( function ( comparedWeight, comparedWeightIndex, comparedWeightArray ) { + + if ( currentWeight > comparedWeight ) { + + comparedWeightArray[ comparedWeightIndex ] = currentWeight; + currentWeight = comparedWeight; + + var tmp = wIndex[ comparedWeightIndex ]; + wIndex[ comparedWeightIndex ] = currentIndex; + currentIndex = tmp; + + } + + } ); + + } ); + + weightIndices = wIndex; + weights = Weight; + + } + + // if the weight array is shorter than 4 pad with 0s + while ( weights.length < 4 ) { + + weights.push( 0 ); + weightIndices.push( 0 ); + + } + + for ( var i = 0; i < 4; ++ i ) { + + faceWeights.push( weights[ i ] ); + faceWeightIndices.push( weightIndices[ i ] ); + + } + + } + + if ( geoInfo.normal ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.normal ); + + faceNormals.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.material )[ 0 ]; + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, i ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, uv ); + + if ( faceUVs[ i ] === undefined ) { + + faceUVs[ i ] = []; + + } + + faceUVs[ i ].push( data[ 0 ] ); + faceUVs[ i ].push( data[ 1 ] ); + + } ); + + } + + faceLength ++; + + if ( endOfFace ) { + + self.genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ); + + polygonIndex ++; + faceLength = 0; + + // reset arrays for the next face + facePositionIndexes = []; + faceNormals = []; + faceColors = []; + faceUVs = []; + faceWeights = []; + faceWeightIndices = []; + + } + + } ); + + return buffers; + + }, + + // Generate data for a single face in a geometry. If the face is a quad then split it into 2 tris + genFace: function ( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ) { + + for ( var i = 2; i < faceLength; i ++ ) { + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 0 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 2 ] ] ); + + if ( geoInfo.skeleton ) { + + buffers.vertexWeights.push( faceWeights[ 0 ] ); + buffers.vertexWeights.push( faceWeights[ 1 ] ); + buffers.vertexWeights.push( faceWeights[ 2 ] ); + buffers.vertexWeights.push( faceWeights[ 3 ] ); + + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 3 ] ); + + buffers.vertexWeights.push( faceWeights[ i * 4 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ 0 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ i * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 3 ] ); + + } + + if ( geoInfo.color ) { + + buffers.colors.push( faceColors[ 0 ] ); + buffers.colors.push( faceColors[ 1 ] ); + buffers.colors.push( faceColors[ 2 ] ); + + buffers.colors.push( faceColors[ ( i - 1 ) * 3 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 1 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 2 ] ); + + buffers.colors.push( faceColors[ i * 3 ] ); + buffers.colors.push( faceColors[ i * 3 + 1 ] ); + buffers.colors.push( faceColors[ i * 3 + 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + + } + + if ( geoInfo.normal ) { + + buffers.normal.push( faceNormals[ 0 ] ); + buffers.normal.push( faceNormals[ 1 ] ); + buffers.normal.push( faceNormals[ 2 ] ); + + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 1 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 2 ] ); + + buffers.normal.push( faceNormals[ i * 3 ] ); + buffers.normal.push( faceNormals[ i * 3 + 1 ] ); + buffers.normal.push( faceNormals[ i * 3 + 2 ] ); + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, j ) { + + if ( buffers.uvs[ j ] === undefined ) buffers.uvs[ j ] = []; + + buffers.uvs[ j ].push( faceUVs[ j ][ 0 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 + 1 ] ); + + } ); + + } + + } + + }, + + addMorphTargets: function ( parentGeo, parentGeoNode, morphTargets, preTransform ) { + + if ( morphTargets.length === 0 ) return; + + parentGeo.morphAttributes.position = []; + // parentGeo.morphAttributes.normal = []; // not implemented + + var self = this; + morphTargets.forEach( function ( morphTarget ) { + + morphTarget.rawTargets.forEach( function ( rawTarget ) { + + var morphGeoNode = fbxTree.Objects.Geometry[ rawTarget.geoID ]; + + if ( morphGeoNode !== undefined ) { + + self.genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform, rawTarget.name ); + + } + + } ); + + } ); + + }, + + // a morph geometry node is similar to a standard node, and the node is also contained + // in FBXTree.Objects.Geometry, however it can only have attributes for position, normal + // and a special attribute Index defining which vertices of the original geometry are affected + // Normal and position attributes only have data for the vertices that are affected by the morph + genMorphGeometry: function ( parentGeo, parentGeoNode, morphGeoNode, preTransform, name ) { + + var morphGeo = new THREE.BufferGeometry(); + if ( morphGeoNode.attrName ) morphGeo.name = morphGeoNode.attrName; + + var vertexIndices = ( parentGeoNode.PolygonVertexIndex !== undefined ) ? parentGeoNode.PolygonVertexIndex.a : []; + + // make a copy of the parent's vertex positions + var vertexPositions = ( parentGeoNode.Vertices !== undefined ) ? parentGeoNode.Vertices.a.slice() : []; + + var morphPositions = ( morphGeoNode.Vertices !== undefined ) ? morphGeoNode.Vertices.a : []; + var indices = ( morphGeoNode.Indexes !== undefined ) ? morphGeoNode.Indexes.a : []; + + for ( var i = 0; i < indices.length; i ++ ) { + + var morphIndex = indices[ i ] * 3; + + // FBX format uses blend shapes rather than morph targets. This can be converted + // by additively combining the blend shape positions with the original geometry's positions + vertexPositions[ morphIndex ] += morphPositions[ i * 3 ]; + vertexPositions[ morphIndex + 1 ] += morphPositions[ i * 3 + 1 ]; + vertexPositions[ morphIndex + 2 ] += morphPositions[ i * 3 + 2 ]; + + } + + // TODO: add morph normal support + var morphGeoInfo = { + vertexIndices: vertexIndices, + vertexPositions: vertexPositions, + }; + + var morphBuffers = this.genBuffers( morphGeoInfo ); + + var positionAttribute = new THREE.Float32BufferAttribute( morphBuffers.vertex, 3 ); + positionAttribute.name = name || morphGeoNode.attrName; + + preTransform.applyToBufferAttribute( positionAttribute ); + + parentGeo.morphAttributes.position.push( positionAttribute ); + + }, + + // Parse normal from FBXTree.Objects.Geometry.LayerElementNormal if it exists + parseNormals: function ( NormalNode ) { + + var mappingType = NormalNode.MappingInformationType; + var referenceType = NormalNode.ReferenceInformationType; + var buffer = NormalNode.Normals.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + if ( 'NormalIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalIndex.a; + + } else if ( 'NormalsIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalsIndex.a; + + } + + } + + return { + dataSize: 3, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse UVs from FBXTree.Objects.Geometry.LayerElementUV if it exists + parseUVs: function ( UVNode ) { + + var mappingType = UVNode.MappingInformationType; + var referenceType = UVNode.ReferenceInformationType; + var buffer = UVNode.UV.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = UVNode.UVIndex.a; + + } + + return { + dataSize: 2, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse Vertex Colors from FBXTree.Objects.Geometry.LayerElementColor if it exists + parseVertexColors: function ( ColorNode ) { + + var mappingType = ColorNode.MappingInformationType; + var referenceType = ColorNode.ReferenceInformationType; + var buffer = ColorNode.Colors.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = ColorNode.ColorIndex.a; + + } + + return { + dataSize: 4, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse mapping and material data in FBXTree.Objects.Geometry.LayerElementMaterial if it exists + parseMaterialIndices: function ( MaterialNode ) { + + var mappingType = MaterialNode.MappingInformationType; + var referenceType = MaterialNode.ReferenceInformationType; + + if ( mappingType === 'NoMappingInformation' ) { + + return { + dataSize: 1, + buffer: [ 0 ], + indices: [ 0 ], + mappingType: 'AllSame', + referenceType: referenceType + }; + + } + + var materialIndexBuffer = MaterialNode.Materials.a; + + // Since materials are stored as indices, there's a bit of a mismatch between FBX and what + // we expect.So we create an intermediate buffer that points to the index in the buffer, + // for conforming with the other functions we've written for other data. + var materialIndices = []; + + for ( var i = 0; i < materialIndexBuffer.length; ++ i ) { + + materialIndices.push( i ); + + } + + return { + dataSize: 1, + buffer: materialIndexBuffer, + indices: materialIndices, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Generate a NurbGeometry from a node in FBXTree.Objects.Geometry + parseNurbsGeometry: function ( geoNode ) { + + if ( THREE.NURBSCurve === undefined ) { + + console.error( 'THREE.FBXLoader: The loader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry.' ); + return new THREE.BufferGeometry(); + + } + + var order = parseInt( geoNode.Order ); + + if ( isNaN( order ) ) { + + console.error( 'THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geoNode.Order, geoNode.id ); + return new THREE.BufferGeometry(); + + } + + var degree = order - 1; + + var knots = geoNode.KnotVector.a; + var controlPoints = []; + var pointsValues = geoNode.Points.a; + + for ( var i = 0, l = pointsValues.length; i < l; i += 4 ) { + + controlPoints.push( new THREE.Vector4().fromArray( pointsValues, i ) ); + + } + + var startKnot, endKnot; + + if ( geoNode.Form === 'Closed' ) { + + controlPoints.push( controlPoints[ 0 ] ); + + } else if ( geoNode.Form === 'Periodic' ) { + + startKnot = degree; + endKnot = knots.length - 1 - startKnot; + + for ( var i = 0; i < degree; ++ i ) { + + controlPoints.push( controlPoints[ i ] ); + + } + + } + + var curve = new THREE.NURBSCurve( degree, knots, controlPoints, startKnot, endKnot ); + var vertices = curve.getPoints( controlPoints.length * 7 ); + + var positions = new Float32Array( vertices.length * 3 ); + + vertices.forEach( function ( vertex, i ) { + + vertex.toArray( positions, i * 3 ); + + } ); + + var geometry = new THREE.BufferGeometry(); + geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + return geometry; + + }, + + }; + + // parse animation data from FBXTree + function AnimationParser() {} + + AnimationParser.prototype = { + + constructor: AnimationParser, + + // take raw animation clips and turn them into three.js animation clips + parse: function () { + + var animationClips = []; + + var rawClips = this.parseClips(); + + if ( rawClips !== undefined ) { + + for ( var key in rawClips ) { + + var rawClip = rawClips[ key ]; + + var clip = this.addClip( rawClip ); + + animationClips.push( clip ); + + } + + } + + return animationClips; + + }, + + parseClips: function () { + + // since the actual transformation data is stored in FBXTree.Objects.AnimationCurve, + // if this is undefined we can safely assume there are no animations + if ( fbxTree.Objects.AnimationCurve === undefined ) return undefined; + + var curveNodesMap = this.parseAnimationCurveNodes(); + + this.parseAnimationCurves( curveNodesMap ); + + var layersMap = this.parseAnimationLayers( curveNodesMap ); + var rawClips = this.parseAnimStacks( layersMap ); + + return rawClips; + + }, + + // parse nodes in FBXTree.Objects.AnimationCurveNode + // each AnimationCurveNode holds data for an animation transform for a model (e.g. left arm rotation ) + // and is referenced by an AnimationLayer + parseAnimationCurveNodes: function () { + + var rawCurveNodes = fbxTree.Objects.AnimationCurveNode; + + var curveNodesMap = new Map(); + + for ( var nodeID in rawCurveNodes ) { + + var rawCurveNode = rawCurveNodes[ nodeID ]; + + if ( rawCurveNode.attrName.match( /S|R|T|DeformPercent/ ) !== null ) { + + var curveNode = { + + id: rawCurveNode.id, + attr: rawCurveNode.attrName, + curves: {}, + + }; + + curveNodesMap.set( curveNode.id, curveNode ); + + } + + } + + return curveNodesMap; + + }, + + // parse nodes in FBXTree.Objects.AnimationCurve and connect them up to + // previously parsed AnimationCurveNodes. Each AnimationCurve holds data for a single animated + // axis ( e.g. times and values of x rotation) + parseAnimationCurves: function ( curveNodesMap ) { + + var rawCurves = fbxTree.Objects.AnimationCurve; + + // TODO: Many values are identical up to roundoff error, but won't be optimised + // e.g. position times: [0, 0.4, 0. 8] + // position values: [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.235384487103147e-7, 93.67520904541016, -0.9982695579528809] + // clearly, this should be optimised to + // times: [0], positions [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809] + // this shows up in nearly every FBX file, and generally time array is length > 100 + + for ( var nodeID in rawCurves ) { + + var animationCurve = { + + id: rawCurves[ nodeID ].id, + times: rawCurves[ nodeID ].KeyTime.a.map( convertFBXTimeToSeconds ), + values: rawCurves[ nodeID ].KeyValueFloat.a, + + }; + + var relationships = connections.get( animationCurve.id ); + + if ( relationships !== undefined ) { + + var animationCurveID = relationships.parents[ 0 ].ID; + var animationCurveRelationship = relationships.parents[ 0 ].relationship; + + if ( animationCurveRelationship.match( /X/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'x' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Y/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'y' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Z/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'z' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /d|DeformPercent/ ) && curveNodesMap.has( animationCurveID ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'morph' ] = animationCurve; + + } + + } + + } + + }, + + // parse nodes in FBXTree.Objects.AnimationLayer. Each layers holds references + // to various AnimationCurveNodes and is referenced by an AnimationStack node + // note: theoretically a stack can have multiple layers, however in practice there always seems to be one per stack + parseAnimationLayers: function ( curveNodesMap ) { + + var rawLayers = fbxTree.Objects.AnimationLayer; + + var layersMap = new Map(); + + for ( var nodeID in rawLayers ) { + + var layerCurveNodes = []; + + var connection = connections.get( parseInt( nodeID ) ); + + if ( connection !== undefined ) { + + // all the animationCurveNodes used in the layer + var children = connection.children; + + children.forEach( function ( child, i ) { + + if ( curveNodesMap.has( child.ID ) ) { + + var curveNode = curveNodesMap.get( child.ID ); + + // check that the curves are defined for at least one axis, otherwise ignore the curveNode + if ( curveNode.curves.x !== undefined || curveNode.curves.y !== undefined || curveNode.curves.z !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var modelID = connections.get( child.ID ).parents.filter( function ( parent ) { + + return parent.relationship !== undefined; + + } )[ 0 ].ID; + + if ( modelID !== undefined ) { + + var rawModel = fbxTree.Objects.Model[ modelID.toString() ]; + + var node = { + + modelName: rawModel.attrName ? THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ) : '', + ID: rawModel.id, + initialPosition: [ 0, 0, 0 ], + initialRotation: [ 0, 0, 0 ], + initialScale: [ 1, 1, 1 ], + + }; + + sceneGraph.traverse( function ( child ) { + + if ( child.ID === rawModel.id ) { + + node.transform = child.matrix; + + if ( child.userData.transformData ) node.eulerOrder = child.userData.transformData.eulerOrder; + + } + + } ); + + if ( ! node.transform ) node.transform = new THREE.Matrix4(); + + // if the animated model is pre rotated, we'll have to apply the pre rotations to every + // animation value as well + if ( 'PreRotation' in rawModel ) node.preRotation = rawModel.PreRotation.value; + if ( 'PostRotation' in rawModel ) node.postRotation = rawModel.PostRotation.value; + + layerCurveNodes[ i ] = node; + + } + + } + + if ( layerCurveNodes[ i ] ) layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } else if ( curveNode.curves.morph !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var deformerID = connections.get( child.ID ).parents.filter( function ( parent ) { + + return parent.relationship !== undefined; + + } )[ 0 ].ID; + + var morpherID = connections.get( deformerID ).parents[ 0 ].ID; + var geoID = connections.get( morpherID ).parents[ 0 ].ID; + + // assuming geometry is not used in more than one model + var modelID = connections.get( geoID ).parents[ 0 ].ID; + + var rawModel = fbxTree.Objects.Model[ modelID ]; + + var node = { + + modelName: rawModel.attrName ? THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ) : '', + morphName: fbxTree.Objects.Deformer[ deformerID ].attrName, + + }; + + layerCurveNodes[ i ] = node; + + } + + layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } + + } + + } ); + + layersMap.set( parseInt( nodeID ), layerCurveNodes ); + + } + + } + + return layersMap; + + }, + + // parse nodes in FBXTree.Objects.AnimationStack. These are the top level node in the animation + // hierarchy. Each Stack node will be used to create a THREE.AnimationClip + parseAnimStacks: function ( layersMap ) { + + var rawStacks = fbxTree.Objects.AnimationStack; + + // connect the stacks (clips) up to the layers + var rawClips = {}; + + for ( var nodeID in rawStacks ) { + + var children = connections.get( parseInt( nodeID ) ).children; + + if ( children.length > 1 ) { + + // it seems like stacks will always be associated with a single layer. But just in case there are files + // where there are multiple layers per stack, we'll display a warning + console.warn( 'THREE.FBXLoader: Encountered an animation stack with multiple layers, this is currently not supported. Ignoring subsequent layers.' ); + + } + + var layer = layersMap.get( children[ 0 ].ID ); + + rawClips[ nodeID ] = { + + name: rawStacks[ nodeID ].attrName, + layer: layer, + + }; + + } + + return rawClips; + + }, + + addClip: function ( rawClip ) { + + var tracks = []; + + var self = this; + rawClip.layer.forEach( function ( rawTracks ) { + + tracks = tracks.concat( self.generateTracks( rawTracks ) ); + + } ); + + return new THREE.AnimationClip( rawClip.name, - 1, tracks ); + + }, + + generateTracks: function ( rawTracks ) { + + var tracks = []; + + var initialPosition = new THREE.Vector3(); + var initialRotation = new THREE.Quaternion(); + var initialScale = new THREE.Vector3(); + + if ( rawTracks.transform ) rawTracks.transform.decompose( initialPosition, initialRotation, initialScale ); + + initialPosition = initialPosition.toArray(); + initialRotation = new THREE.Euler().setFromQuaternion( initialRotation, rawTracks.eulerOrder ).toArray(); + initialScale = initialScale.toArray(); + + if ( rawTracks.T !== undefined && Object.keys( rawTracks.T.curves ).length > 0 ) { + + var positionTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.T.curves, initialPosition, 'position' ); + if ( positionTrack !== undefined ) tracks.push( positionTrack ); + + } + + if ( rawTracks.R !== undefined && Object.keys( rawTracks.R.curves ).length > 0 ) { + + var rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, initialRotation, rawTracks.preRotation, rawTracks.postRotation, rawTracks.eulerOrder ); + if ( rotationTrack !== undefined ) tracks.push( rotationTrack ); + + } + + if ( rawTracks.S !== undefined && Object.keys( rawTracks.S.curves ).length > 0 ) { + + var scaleTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.S.curves, initialScale, 'scale' ); + if ( scaleTrack !== undefined ) tracks.push( scaleTrack ); + + } + + if ( rawTracks.DeformPercent !== undefined ) { + + var morphTrack = this.generateMorphTrack( rawTracks ); + if ( morphTrack !== undefined ) tracks.push( morphTrack ); + + } + + return tracks; + + }, + + generateVectorTrack: function ( modelName, curves, initialValue, type ) { + + var times = this.getTimesForAllAxes( curves ); + var values = this.getKeyframeTrackValues( times, curves, initialValue ); + + return new THREE.VectorKeyframeTrack( modelName + '.' + type, times, values ); + + }, + + generateRotationTrack: function ( modelName, curves, initialValue, preRotation, postRotation, eulerOrder ) { + + if ( curves.x !== undefined ) { + + this.interpolateRotations( curves.x ); + curves.x.values = curves.x.values.map( THREE.Math.degToRad ); + + } + if ( curves.y !== undefined ) { + + this.interpolateRotations( curves.y ); + curves.y.values = curves.y.values.map( THREE.Math.degToRad ); + + } + if ( curves.z !== undefined ) { + + this.interpolateRotations( curves.z ); + curves.z.values = curves.z.values.map( THREE.Math.degToRad ); + + } + + var times = this.getTimesForAllAxes( curves ); + var values = this.getKeyframeTrackValues( times, curves, initialValue ); + + if ( preRotation !== undefined ) { + + preRotation = preRotation.map( THREE.Math.degToRad ); + preRotation.push( eulerOrder ); + + preRotation = new THREE.Euler().fromArray( preRotation ); + preRotation = new THREE.Quaternion().setFromEuler( preRotation ); + + } + + if ( postRotation !== undefined ) { + + postRotation = postRotation.map( THREE.Math.degToRad ); + postRotation.push( eulerOrder ); + + postRotation = new THREE.Euler().fromArray( postRotation ); + postRotation = new THREE.Quaternion().setFromEuler( postRotation ).inverse(); + + } + + var quaternion = new THREE.Quaternion(); + var euler = new THREE.Euler(); + + var quaternionValues = []; + + for ( var i = 0; i < values.length; i += 3 ) { + + euler.set( values[ i ], values[ i + 1 ], values[ i + 2 ], eulerOrder ); + + quaternion.setFromEuler( euler ); + + if ( preRotation !== undefined ) quaternion.premultiply( preRotation ); + if ( postRotation !== undefined ) quaternion.multiply( postRotation ); + + quaternion.toArray( quaternionValues, ( i / 3 ) * 4 ); + + } + + return new THREE.QuaternionKeyframeTrack( modelName + '.quaternion', times, quaternionValues ); + + }, + + generateMorphTrack: function ( rawTracks ) { + + var curves = rawTracks.DeformPercent.curves.morph; + var values = curves.values.map( function ( val ) { + + return val / 100; + + } ); + + var morphNum = sceneGraph.getObjectByName( rawTracks.modelName ).morphTargetDictionary[ rawTracks.morphName ]; + + return new THREE.NumberKeyframeTrack( rawTracks.modelName + '.morphTargetInfluences[' + morphNum + ']', curves.times, values ); + + }, + + // For all animated objects, times are defined separately for each axis + // Here we'll combine the times into one sorted array without duplicates + getTimesForAllAxes: function ( curves ) { + + var times = []; + + // first join together the times for each axis, if defined + if ( curves.x !== undefined ) times = times.concat( curves.x.times ); + if ( curves.y !== undefined ) times = times.concat( curves.y.times ); + if ( curves.z !== undefined ) times = times.concat( curves.z.times ); + + // then sort them and remove duplicates + times = times.sort( function ( a, b ) { + + return a - b; + + } ).filter( function ( elem, index, array ) { + + return array.indexOf( elem ) == index; + + } ); + + return times; + + }, + + getKeyframeTrackValues: function ( times, curves, initialValue ) { + + var prevValue = initialValue; + + var values = []; + + var xIndex = - 1; + var yIndex = - 1; + var zIndex = - 1; + + times.forEach( function ( time ) { + + if ( curves.x ) xIndex = curves.x.times.indexOf( time ); + if ( curves.y ) yIndex = curves.y.times.indexOf( time ); + if ( curves.z ) zIndex = curves.z.times.indexOf( time ); + + // if there is an x value defined for this frame, use that + if ( xIndex !== - 1 ) { + + var xValue = curves.x.values[ xIndex ]; + values.push( xValue ); + prevValue[ 0 ] = xValue; + + } else { + + // otherwise use the x value from the previous frame + values.push( prevValue[ 0 ] ); + + } + + if ( yIndex !== - 1 ) { + + var yValue = curves.y.values[ yIndex ]; + values.push( yValue ); + prevValue[ 1 ] = yValue; + + } else { + + values.push( prevValue[ 1 ] ); + + } + + if ( zIndex !== - 1 ) { + + var zValue = curves.z.values[ zIndex ]; + values.push( zValue ); + prevValue[ 2 ] = zValue; + + } else { + + values.push( prevValue[ 2 ] ); + + } + + } ); + + return values; + + }, + + // Rotations are defined as Euler angles which can have values of any size + // These will be converted to quaternions which don't support values greater than + // PI, so we'll interpolate large rotations + interpolateRotations: function ( curve ) { + + for ( var i = 1; i < curve.values.length; i ++ ) { + + var initialValue = curve.values[ i - 1 ]; + var valuesSpan = curve.values[ i ] - initialValue; + + var absoluteSpan = Math.abs( valuesSpan ); + + if ( absoluteSpan >= 180 ) { + + var numSubIntervals = absoluteSpan / 180; + + var step = valuesSpan / numSubIntervals; + var nextValue = initialValue + step; + + var initialTime = curve.times[ i - 1 ]; + var timeSpan = curve.times[ i ] - initialTime; + var interval = timeSpan / numSubIntervals; + var nextTime = initialTime + interval; + + var interpolatedTimes = []; + var interpolatedValues = []; + + while ( nextTime < curve.times[ i ] ) { + + interpolatedTimes.push( nextTime ); + nextTime += interval; + + interpolatedValues.push( nextValue ); + nextValue += step; + + } + + curve.times = inject( curve.times, i, interpolatedTimes ); + curve.values = inject( curve.values, i, interpolatedValues ); + + } + + } + + }, + + }; + + // parse an FBX file in ASCII format + function TextParser() {} + + TextParser.prototype = { + + constructor: TextParser, + + getPrevNode: function () { + + return this.nodeStack[ this.currentIndent - 2 ]; + + }, + + getCurrentNode: function () { + + return this.nodeStack[ this.currentIndent - 1 ]; + + }, + + getCurrentProp: function () { + + return this.currentProp; + + }, + + pushStack: function ( node ) { + + this.nodeStack.push( node ); + this.currentIndent += 1; + + }, + + popStack: function () { + + this.nodeStack.pop(); + this.currentIndent -= 1; + + }, + + setCurrentProp: function ( val, name ) { + + this.currentProp = val; + this.currentPropName = name; + + }, + + parse: function ( text ) { + + this.currentIndent = 0; + + this.allNodes = new FBXTree(); + this.nodeStack = []; + this.currentProp = []; + this.currentPropName = ''; + + var self = this; + + var split = text.split( /[\r\n]+/ ); + + split.forEach( function ( line, i ) { + + var matchComment = line.match( /^[\s\t]*;/ ); + var matchEmpty = line.match( /^[\s\t]*$/ ); + + if ( matchComment || matchEmpty ) return; + + var matchBeginning = line.match( '^\\t{' + self.currentIndent + '}(\\w+):(.*){', '' ); + var matchProperty = line.match( '^\\t{' + ( self.currentIndent ) + '}(\\w+):[\\s\\t\\r\\n](.*)' ); + var matchEnd = line.match( '^\\t{' + ( self.currentIndent - 1 ) + '}}' ); + + if ( matchBeginning ) { + + self.parseNodeBegin( line, matchBeginning ); + + } else if ( matchProperty ) { + + self.parseNodeProperty( line, matchProperty, split[ ++ i ] ); + + } else if ( matchEnd ) { + + self.popStack(); + + } else if ( line.match( /^[^\s\t}]/ ) ) { + + // large arrays are split over multiple lines terminated with a ',' character + // if this is encountered the line needs to be joined to the previous line + self.parseNodePropertyContinued( line ); + + } + + } ); + + return this.allNodes; + + }, + + parseNodeBegin: function ( line, property ) { + + var nodeName = property[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, '' ); + + var nodeAttrs = property[ 2 ].split( ',' ).map( function ( attr ) { + + return attr.trim().replace( /^"/, '' ).replace( /"$/, '' ); + + } ); + + var node = { name: nodeName }; + var attrs = this.parseNodeAttr( nodeAttrs ); + + var currentNode = this.getCurrentNode(); + + // a top node + if ( this.currentIndent === 0 ) { + + this.allNodes.add( nodeName, node ); + + } else { // a subnode + + // if the subnode already exists, append it + if ( nodeName in currentNode ) { + + // special case Pose needs PoseNodes as an array + if ( nodeName === 'PoseNode' ) { + + currentNode.PoseNode.push( node ); + + } else if ( currentNode[ nodeName ].id !== undefined ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ currentNode[ nodeName ].id ] = currentNode[ nodeName ]; + + } + + if ( attrs.id !== '' ) currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( typeof attrs.id === 'number' ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( nodeName !== 'Properties70' ) { + + if ( nodeName === 'PoseNode' ) currentNode[ nodeName ] = [ node ]; + else currentNode[ nodeName ] = node; + + } + + } + + if ( typeof attrs.id === 'number' ) node.id = attrs.id; + if ( attrs.name !== '' ) node.attrName = attrs.name; + if ( attrs.type !== '' ) node.attrType = attrs.type; + + this.pushStack( node ); + + }, + + parseNodeAttr: function ( attrs ) { + + var id = attrs[ 0 ]; + + if ( attrs[ 0 ] !== '' ) { + + id = parseInt( attrs[ 0 ] ); + + if ( isNaN( id ) ) { + + id = attrs[ 0 ]; + + } + + } + + var name = '', type = ''; + + if ( attrs.length > 1 ) { + + name = attrs[ 1 ].replace( /^(\w+)::/, '' ); + type = attrs[ 2 ]; + + } + + return { id: id, name: name, type: type }; + + }, + + parseNodeProperty: function ( line, property, contentLine ) { + + var propName = property[ 1 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + var propValue = property[ 2 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + + // for special case: base64 image data follows "Content: ," line + // Content: , + // "/9j/4RDaRXhpZgAATU0A..." + if ( propName === 'Content' && propValue === ',' ) { + + propValue = contentLine.replace( /"/g, '' ).replace( /,$/, '' ).trim(); + + } + + var currentNode = this.getCurrentNode(); + var parentName = currentNode.name; + + if ( parentName === 'Properties70' ) { + + this.parseNodeSpecialProperty( line, propName, propValue ); + return; + + } + + // Connections + if ( propName === 'C' ) { + + var connProps = propValue.split( ',' ).slice( 1 ); + var from = parseInt( connProps[ 0 ] ); + var to = parseInt( connProps[ 1 ] ); + + var rest = propValue.split( ',' ).slice( 3 ); + + rest = rest.map( function ( elem ) { + + return elem.trim().replace( /^"/, '' ); + + } ); + + propName = 'connections'; + propValue = [ from, to ]; + append( propValue, rest ); + + if ( currentNode[ propName ] === undefined ) { + + currentNode[ propName ] = []; + + } + + } + + // Node + if ( propName === 'Node' ) currentNode.id = propValue; + + // connections + if ( propName in currentNode && Array.isArray( currentNode[ propName ] ) ) { + + currentNode[ propName ].push( propValue ); + + } else { + + if ( propName !== 'a' ) currentNode[ propName ] = propValue; + else currentNode.a = propValue; + + } + + this.setCurrentProp( currentNode, propName ); + + // convert string to array, unless it ends in ',' in which case more will be added to it + if ( propName === 'a' && propValue.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( propValue ); + + } + + }, + + parseNodePropertyContinued: function ( line ) { + + var currentNode = this.getCurrentNode(); + + currentNode.a += line; + + // if the line doesn't end in ',' we have reached the end of the property value + // so convert the string to an array + if ( line.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( currentNode.a ); + + } + + }, + + // parse "Property70" + parseNodeSpecialProperty: function ( line, propName, propValue ) { + + // split this + // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1 + // into array like below + // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ] + var props = propValue.split( '",' ).map( function ( prop ) { + + return prop.trim().replace( /^\"/, '' ).replace( /\s/, '_' ); + + } ); + + var innerPropName = props[ 0 ]; + var innerPropType1 = props[ 1 ]; + var innerPropType2 = props[ 2 ]; + var innerPropFlag = props[ 3 ]; + var innerPropValue = props[ 4 ]; + + // cast values where needed, otherwise leave as strings + switch ( innerPropType1 ) { + + case 'int': + case 'enum': + case 'bool': + case 'ULongLong': + case 'double': + case 'Number': + case 'FieldOfView': + innerPropValue = parseFloat( innerPropValue ); + break; + + case 'Color': + case 'ColorRGB': + case 'Vector3D': + case 'Lcl_Translation': + case 'Lcl_Rotation': + case 'Lcl_Scaling': + innerPropValue = parseNumberArray( innerPropValue ); + break; + + } + + // CAUTION: these props must append to parent's parent + this.getPrevNode()[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + this.setCurrentProp( this.getPrevNode(), innerPropName ); + + }, + + }; + + // Parse an FBX file in Binary format + function BinaryParser() {} + + BinaryParser.prototype = { + + constructor: BinaryParser, + + parse: function ( buffer ) { + + var reader = new BinaryReader( buffer ); + reader.skip( 23 ); // skip magic 23 bytes + + var version = reader.getUint32(); + + console.log( 'THREE.FBXLoader: FBX binary version: ' + version ); + + var allNodes = new FBXTree(); + + while ( ! this.endOfContent( reader ) ) { + + var node = this.parseNode( reader, version ); + if ( node !== null ) allNodes.add( node.name, node ); + + } + + return allNodes; + + }, + + // Check if reader has reached the end of content. + endOfContent: function ( reader ) { + + // footer size: 160bytes + 16-byte alignment padding + // - 16bytes: magic + // - padding til 16-byte alignment (at least 1byte?) + // (seems like some exporters embed fixed 15 or 16bytes?) + // - 4bytes: magic + // - 4bytes: version + // - 120bytes: zero + // - 16bytes: magic + if ( reader.size() % 16 === 0 ) { + + return ( ( reader.getOffset() + 160 + 16 ) & ~ 0xf ) >= reader.size(); + + } else { + + return reader.getOffset() + 160 + 16 >= reader.size(); + + } + + }, + + // recursively parse nodes until the end of the file is reached + parseNode: function ( reader, version ) { + + var node = {}; + + // The first three data sizes depends on version. + var endOffset = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + var numProperties = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + // note: do not remove this even if you get a linter warning as it moves the buffer forward + var propertyListLen = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + var nameLen = reader.getUint8(); + var name = reader.getString( nameLen ); + + // Regards this node as NULL-record if endOffset is zero + if ( endOffset === 0 ) return null; + + var propertyList = []; + + for ( var i = 0; i < numProperties; i ++ ) { + + propertyList.push( this.parseProperty( reader ) ); + + } + + // Regards the first three elements in propertyList as id, attrName, and attrType + var id = propertyList.length > 0 ? propertyList[ 0 ] : ''; + var attrName = propertyList.length > 1 ? propertyList[ 1 ] : ''; + var attrType = propertyList.length > 2 ? propertyList[ 2 ] : ''; + + // check if this node represents just a single property + // like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]} + node.singleProperty = ( numProperties === 1 && reader.getOffset() === endOffset ) ? true : false; + + while ( endOffset > reader.getOffset() ) { + + var subNode = this.parseNode( reader, version ); + + if ( subNode !== null ) this.parseSubNode( name, node, subNode ); + + } + + node.propertyList = propertyList; // raw property list used by parent + + if ( typeof id === 'number' ) node.id = id; + if ( attrName !== '' ) node.attrName = attrName; + if ( attrType !== '' ) node.attrType = attrType; + if ( name !== '' ) node.name = name; + + return node; + + }, + + parseSubNode: function ( name, node, subNode ) { + + // special case: child node is single property + if ( subNode.singleProperty === true ) { + + var value = subNode.propertyList[ 0 ]; + + if ( Array.isArray( value ) ) { + + node[ subNode.name ] = subNode; + + subNode.a = value; + + } else { + + node[ subNode.name ] = value; + + } + + } else if ( name === 'Connections' && subNode.name === 'C' ) { + + var array = []; + + subNode.propertyList.forEach( function ( property, i ) { + + // first Connection is FBX type (OO, OP, etc.). We'll discard these + if ( i !== 0 ) array.push( property ); + + } ); + + if ( node.connections === undefined ) { + + node.connections = []; + + } + + node.connections.push( array ); + + } else if ( subNode.name === 'Properties70' ) { + + var keys = Object.keys( subNode ); + + keys.forEach( function ( key ) { + + node[ key ] = subNode[ key ]; + + } ); + + } else if ( name === 'Properties70' && subNode.name === 'P' ) { + + var innerPropName = subNode.propertyList[ 0 ]; + var innerPropType1 = subNode.propertyList[ 1 ]; + var innerPropType2 = subNode.propertyList[ 2 ]; + var innerPropFlag = subNode.propertyList[ 3 ]; + var innerPropValue; + + if ( innerPropName.indexOf( 'Lcl ' ) === 0 ) innerPropName = innerPropName.replace( 'Lcl ', 'Lcl_' ); + if ( innerPropType1.indexOf( 'Lcl ' ) === 0 ) innerPropType1 = innerPropType1.replace( 'Lcl ', 'Lcl_' ); + + if ( innerPropType1 === 'Color' || innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' || innerPropType1 === 'Vector3D' || innerPropType1.indexOf( 'Lcl_' ) === 0 ) { + + innerPropValue = [ + subNode.propertyList[ 4 ], + subNode.propertyList[ 5 ], + subNode.propertyList[ 6 ] + ]; + + } else { + + innerPropValue = subNode.propertyList[ 4 ]; + + } + + // this will be copied to parent, see above + node[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + } else if ( node[ subNode.name ] === undefined ) { + + if ( typeof subNode.id === 'number' ) { + + node[ subNode.name ] = {}; + node[ subNode.name ][ subNode.id ] = subNode; + + } else { + + node[ subNode.name ] = subNode; + + } + + } else { + + if ( subNode.name === 'PoseNode' ) { + + if ( ! Array.isArray( node[ subNode.name ] ) ) { + + node[ subNode.name ] = [ node[ subNode.name ] ]; + + } + + node[ subNode.name ].push( subNode ); + + } else if ( node[ subNode.name ][ subNode.id ] === undefined ) { + + node[ subNode.name ][ subNode.id ] = subNode; + + } + + } + + }, + + parseProperty: function ( reader ) { + + var type = reader.getString( 1 ); + + switch ( type ) { + + case 'C': + return reader.getBoolean(); + + case 'D': + return reader.getFloat64(); + + case 'F': + return reader.getFloat32(); + + case 'I': + return reader.getInt32(); + + case 'L': + return reader.getInt64(); + + case 'R': + var length = reader.getUint32(); + return reader.getArrayBuffer( length ); + + case 'S': + var length = reader.getUint32(); + return reader.getString( length ); + + case 'Y': + return reader.getInt16(); + + case 'b': + case 'c': + case 'd': + case 'f': + case 'i': + case 'l': + + var arrayLength = reader.getUint32(); + var encoding = reader.getUint32(); // 0: non-compressed, 1: compressed + var compressedLength = reader.getUint32(); + + if ( encoding === 0 ) { + + switch ( type ) { + + case 'b': + case 'c': + return reader.getBooleanArray( arrayLength ); + + case 'd': + return reader.getFloat64Array( arrayLength ); + + case 'f': + return reader.getFloat32Array( arrayLength ); + + case 'i': + return reader.getInt32Array( arrayLength ); + + case 'l': + return reader.getInt64Array( arrayLength ); + + } + + } + + if ( typeof Zlib === 'undefined' ) { + + console.error( 'THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js' ); + + } + + var inflate = new Zlib.Inflate( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) ); // eslint-disable-line no-undef + var reader2 = new BinaryReader( inflate.decompress().buffer ); + + switch ( type ) { + + case 'b': + case 'c': + return reader2.getBooleanArray( arrayLength ); + + case 'd': + return reader2.getFloat64Array( arrayLength ); + + case 'f': + return reader2.getFloat32Array( arrayLength ); + + case 'i': + return reader2.getInt32Array( arrayLength ); + + case 'l': + return reader2.getInt64Array( arrayLength ); + + } + + default: + throw new Error( 'THREE.FBXLoader: Unknown property type ' + type ); + + } + + } + + }; + + function BinaryReader( buffer, littleEndian ) { + + this.dv = new DataView( buffer ); + this.offset = 0; + this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true; + + } + + BinaryReader.prototype = { + + constructor: BinaryReader, + + getOffset: function () { + + return this.offset; + + }, + + size: function () { + + return this.dv.buffer.byteLength; + + }, + + skip: function ( length ) { + + this.offset += length; + + }, + + // seems like true/false representation depends on exporter. + // true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54) + // then sees LSB. + getBoolean: function () { + + return ( this.getUint8() & 1 ) === 1; + + }, + + getBooleanArray: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getBoolean() ); + + } + + return a; + + }, + + getUint8: function () { + + var value = this.dv.getUint8( this.offset ); + this.offset += 1; + return value; + + }, + + getInt16: function () { + + var value = this.dv.getInt16( this.offset, this.littleEndian ); + this.offset += 2; + return value; + + }, + + getInt32: function () { + + var value = this.dv.getInt32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getInt32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt32() ); + + } + + return a; + + }, + + getUint32: function () { + + var value = this.dv.getUint32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + // JavaScript doesn't support 64-bit integer so calculate this here + // 1 << 32 will return 1 so using multiply operation instead here. + // There's a possibility that this method returns wrong value if the value + // is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER. + // TODO: safely handle 64-bit integer + getInt64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + // calculate negative value + if ( high & 0x80000000 ) { + + high = ~ high & 0xFFFFFFFF; + low = ~ low & 0xFFFFFFFF; + + if ( low === 0xFFFFFFFF ) high = ( high + 1 ) & 0xFFFFFFFF; + + low = ( low + 1 ) & 0xFFFFFFFF; + + return - ( high * 0x100000000 + low ); + + } + + return high * 0x100000000 + low; + + }, + + getInt64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt64() ); + + } + + return a; + + }, + + // Note: see getInt64() comment + getUint64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + return high * 0x100000000 + low; + + }, + + getFloat32: function () { + + var value = this.dv.getFloat32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getFloat32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat32() ); + + } + + return a; + + }, + + getFloat64: function () { + + var value = this.dv.getFloat64( this.offset, this.littleEndian ); + this.offset += 8; + return value; + + }, + + getFloat64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat64() ); + + } + + return a; + + }, + + getArrayBuffer: function ( size ) { + + var value = this.dv.buffer.slice( this.offset, this.offset + size ); + this.offset += size; + return value; + + }, + + getString: function ( size ) { + + // note: safari 9 doesn't support Uint8Array.indexOf; create intermediate array instead + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a[ i ] = this.getUint8(); + + } + + var nullByte = a.indexOf( 0 ); + if ( nullByte >= 0 ) a = a.slice( 0, nullByte ); + + return THREE.LoaderUtils.decodeText( new Uint8Array( a ) ); + + } + + }; + + // FBXTree holds a representation of the FBX data, returned by the TextParser ( FBX ASCII format) + // and BinaryParser( FBX Binary format) + function FBXTree() {} + + FBXTree.prototype = { + + constructor: FBXTree, + + add: function ( key, val ) { + + this[ key ] = val; + + }, + + }; + + // ************** UTILITY FUNCTIONS ************** + + function isFbxFormatBinary( buffer ) { + + var CORRECT = 'Kaydara FBX Binary \0'; + + return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString( buffer, 0, CORRECT.length ); + + } + + function isFbxFormatASCII( text ) { + + var CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ]; + + var cursor = 0; + + function read( offset ) { + + var result = text[ offset - 1 ]; + text = text.slice( cursor + offset ); + cursor ++; + return result; + + } + + for ( var i = 0; i < CORRECT.length; ++ i ) { + + var num = read( 1 ); + if ( num === CORRECT[ i ] ) { + + return false; + + } + + } + + return true; + + } + + function getFbxVersion( text ) { + + var versionRegExp = /FBXVersion: (\d+)/; + var match = text.match( versionRegExp ); + if ( match ) { + + var version = parseInt( match[ 1 ] ); + return version; + + } + throw new Error( 'THREE.FBXLoader: Cannot find the version number for the file given.' ); + + } + + // Converts FBX ticks into real time seconds. + function convertFBXTimeToSeconds( time ) { + + return time / 46186158000; + + } + + var dataArray = []; + + // extracts the data from the correct position in the FBX array based on indexing type + function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + var index; + + switch ( infoObject.mappingType ) { + + case 'ByPolygonVertex' : + index = polygonVertexIndex; + break; + case 'ByPolygon' : + index = polygonIndex; + break; + case 'ByVertice' : + index = vertexIndex; + break; + case 'AllSame' : + index = infoObject.indices[ 0 ]; + break; + default : + console.warn( 'THREE.FBXLoader: unknown attribute mapping type ' + infoObject.mappingType ); + + } + + if ( infoObject.referenceType === 'IndexToDirect' ) index = infoObject.indices[ index ]; + + var from = index * infoObject.dataSize; + var to = from + infoObject.dataSize; + + return slice( dataArray, infoObject.buffer, from, to ); + + } + + var tempEuler = new THREE.Euler(); + var tempVec = new THREE.Vector3(); + + // generate transformation from FBX transform data + // ref: https://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_10CDD63C_79C1_4F2D_BB28_AD2BE65A02ED_htm + // ref: http://docs.autodesk.com/FBX/2014/ENU/FBX-SDK-Documentation/index.html?url=cpp_ref/_transformations_2main_8cxx-example.html,topicNumber=cpp_ref__transformations_2main_8cxx_example_htmlfc10a1e1-b18d-4e72-9dc0-70d0f1959f5e + function generateTransform( transformData ) { + + var lTranslationM = new THREE.Matrix4(); + var lPreRotationM = new THREE.Matrix4(); + var lRotationM = new THREE.Matrix4(); + var lPostRotationM = new THREE.Matrix4(); + + var lScalingM = new THREE.Matrix4(); + var lScalingPivotM = new THREE.Matrix4(); + var lScalingOffsetM = new THREE.Matrix4(); + var lRotationOffsetM = new THREE.Matrix4(); + var lRotationPivotM = new THREE.Matrix4(); + + var lParentGX = new THREE.Matrix4(); + var lGlobalT = new THREE.Matrix4(); + + var inheritType = ( transformData.inheritType ) ? transformData.inheritType : 0; + + if ( transformData.translation ) lTranslationM.setPosition( tempVec.fromArray( transformData.translation ) ); + + if ( transformData.preRotation ) { + + var array = transformData.preRotation.map( THREE.Math.degToRad ); + array.push( transformData.eulerOrder ); + lPreRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.rotation ) { + + var array = transformData.rotation.map( THREE.Math.degToRad ); + array.push( transformData.eulerOrder ); + lRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.postRotation ) { + + var array = transformData.postRotation.map( THREE.Math.degToRad ); + array.push( transformData.eulerOrder ); + lPostRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.scale ) lScalingM.scale( tempVec.fromArray( transformData.scale ) ); + + // Pivots and offsets + if ( transformData.scalingOffset ) lScalingOffsetM.setPosition( tempVec.fromArray( transformData.scalingOffset ) ); + if ( transformData.scalingPivot ) lScalingPivotM.setPosition( tempVec.fromArray( transformData.scalingPivot ) ); + if ( transformData.rotationOffset ) lRotationOffsetM.setPosition( tempVec.fromArray( transformData.rotationOffset ) ); + if ( transformData.rotationPivot ) lRotationPivotM.setPosition( tempVec.fromArray( transformData.rotationPivot ) ); + + // parent transform + if ( transformData.parentMatrixWorld ) lParentGX = transformData.parentMatrixWorld; + + // Global Rotation + var lLRM = lPreRotationM.multiply( lRotationM ).multiply( lPostRotationM ); + var lParentGRM = new THREE.Matrix4(); + lParentGX.extractRotation( lParentGRM ); + + // Global Shear*Scaling + var lParentTM = new THREE.Matrix4(); + var lLSM; + var lParentGSM; + var lParentGRSM; + + lParentTM.copyPosition( lParentGX ); + lParentGRSM = lParentTM.getInverse( lParentTM ).multiply( lParentGX ); + lParentGSM = lParentGRM.getInverse( lParentGRM ).multiply( lParentGRSM ); + lLSM = lScalingM; + + var lGlobalRS; + if ( inheritType === 0 ) { + + lGlobalRS = lParentGRM.multiply( lLRM ).multiply( lParentGSM ).multiply( lLSM ); + + } else if ( inheritType === 1 ) { + + lGlobalRS = lParentGRM.multiply( lParentGSM ).multiply( lLRM ).multiply( lLSM ); + + } else { + + var lParentLSM = new THREE.Matrix4().copy( lScalingM ); + + var lParentGSM_noLocal = lParentGSM.multiply( lParentLSM.getInverse( lParentLSM ) ); + + lGlobalRS = lParentGRM.multiply( lLRM ).multiply( lParentGSM_noLocal ).multiply( lLSM ); + + } + + // Calculate the local transform matrix + var lTransform = lTranslationM.multiply( lRotationOffsetM ).multiply( lRotationPivotM ).multiply( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM ).multiply( lRotationPivotM.getInverse( lRotationPivotM ) ).multiply( lScalingOffsetM ).multiply( lScalingPivotM ).multiply( lScalingM ).multiply( lScalingPivotM.getInverse( lScalingPivotM ) ); + + var lLocalTWithAllPivotAndOffsetInfo = new THREE.Matrix4().copyPosition( lTransform ); + + var lGlobalTranslation = lParentGX.multiply( lLocalTWithAllPivotAndOffsetInfo ); + lGlobalT.copyPosition( lGlobalTranslation ); + + lTransform = lGlobalT.multiply( lGlobalRS ); + + return lTransform; + + } + + // Returns the three.js intrinsic Euler order corresponding to FBX extrinsic Euler order + // ref: http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html + function getEulerOrder( order ) { + + order = order || 0; + + var enums = [ + 'ZYX', // -> XYZ extrinsic + 'YZX', // -> XZY extrinsic + 'XZY', // -> YZX extrinsic + 'ZXY', // -> YXZ extrinsic + 'YXZ', // -> ZXY extrinsic + 'XYZ', // -> ZYX extrinsic + //'SphericXYZ', // not possible to support + ]; + + if ( order === 6 ) { + + console.warn( 'THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.' ); + return enums[ 0 ]; + + } + + return enums[ order ]; + + } + + // Parses comma separated list of numbers and returns them an array. + // Used internally by the TextParser + function parseNumberArray( value ) { + + var array = value.split( ',' ).map( function ( val ) { + + return parseFloat( val ); + + } ); + + return array; + + } + + function convertArrayBufferToString( buffer, from, to ) { + + if ( from === undefined ) from = 0; + if ( to === undefined ) to = buffer.byteLength; + + return THREE.LoaderUtils.decodeText( new Uint8Array( buffer, from, to ) ); + + } + + function append( a, b ) { + + for ( var i = 0, j = a.length, l = b.length; i < l; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + } + + function slice( a, b, from, to ) { + + for ( var i = from, j = 0; i < to; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + return a; + + } + + // inject array a2 into array a1 at index + function inject( a1, index, a2 ) { + + return a1.slice( 0, index ).concat( a2 ).concat( a1.slice( index ) ); + + } + + return FBXLoader; + +} )(); diff --git a/HelioWebXRPolyfill.js b/HelioWebXRPolyfill.js new file mode 100644 index 0000000..ffa81eb --- /dev/null +++ b/HelioWebXRPolyfill.js @@ -0,0 +1,192 @@ +/** + * @author mvilledieu / http://github.com/mvilledieu + */ + +if ( /(Helio)/g.test( navigator.userAgent ) && "xr" in navigator ) { + + console.log( "Helio WebXR Polyfill (Lumin 0.97.0)" ); + + const isHelio96 = navigator.userAgent.includes("Chrome/73"); + + // WebXRManager - XR.supportSession() Polyfill - WebVR.js line 147 + + if ( + "supportsSession" in navigator.xr === false && + "supportsSessionMode" in navigator.xr + ) { + + navigator.xr.supportsSession = function ( sessionType ) { + + // Force using immersive-ar + return navigator.xr.supportsSessionMode( 'immersive-ar' ); + + }; + + } + + if ( "requestSession" in navigator.xr ) { + + const tempRequestSession = navigator.xr.requestSession.bind( navigator.xr ); + + navigator.xr.requestSession = function ( sessionType ) { + + return new Promise( function ( resolve, reject ) { + + const sessionType = (isHelio96 ? { + mode: 'immersive-ar' // Force using immersive-ar + } : 'immersive-ar'); + + tempRequestSession( sessionType ) + .then( function ( session ) { + + // WebXRManager - xrFrame.getPose() Polyfill - line 279 + + const tempRequestAnimationFrame = session.requestAnimationFrame.bind( + session + ); + + session.requestAnimationFrame = function ( callback ) { + + return tempRequestAnimationFrame( function ( time, frame ) { + + // WebXRManager - xrFrame.getViewerPose() Polyfill - line 279 + // Transforms view.viewMatrix to view.transform.inverse.matrix + + const tempGetViewerPose = frame.getViewerPose.bind( frame ); + + frame.getViewerPose = function ( referenceSpace ) { + + const pose = tempGetViewerPose( referenceSpace ); + + pose.views.forEach( function ( view ) { + + view.transform = { + inverse: { + matrix: view.viewMatrix + } + }; + + } ); + + return pose; + + }; + + // WebXRManager - xrFrame.getPose() Polyfill - line 259 + + const tempGetPose = (isHelio96 ? null : frame.getPose.bind( frame )); + + frame.getPose = function ( targetRaySpace, referenceSpace ) { + + if (isHelio96) { + + const inputPose = frame.getInputPose( + targetRaySpace, + referenceSpace + ); + + inputPose.transform = { + matrix: inputPose.targetRay.transformMatrix + }; + + return inputPose; + + } else { + + return tempGetPose(targetRaySpace.gripSpace, referenceSpace); + + } + + }; + + callback( time, frame ); + + } ); + + }; + + // WebXRManager - xrFrame.getPose( inputSource.targetRaySpace, referenceSpace) Polyfill - line 279 + + const tempGetInputSources = session.getInputSources.bind( session ); + + session.getInputSources = function () { + + const res = tempGetInputSources(); + + res.forEach( function (xrInputSource ) { + + if (!('targetRaySpace' in xrInputSource)) { + Object.defineProperty( xrInputSource, "targetRaySpace", { + get: function () { + + return xrInputSource; + + } + } ); + } + + } ); + + return res; + + }; + + // WebXRManager - xrSession.getInputSources() Polyfill Line 132 - 136 + + /* session.inputSources = */Object.defineProperty( + session, + "inputSources", + { + get: session.getInputSources + } + ); + + // WebXRManager - xrSession.updateRenderState() Polyfill Line 129 + + if (isHelio96) { + + session.updateRenderState = function ( { baseLayer } ) { + + session.baseLayer = baseLayer; + + // WebXRManager - xrSession.renderState.baseLayer Polyfill Line 219 + + session.renderState = { + baseLayer: baseLayer + }; + + }; + + } + + // WebXRManager - xrSession.requestReferenceSpace() Polyfill Line 130 + + const tempRequestReferenceSpace = session.requestReferenceSpace.bind( + session + ); + + session.requestReferenceSpace = function () { + + return tempRequestReferenceSpace( { + type: "stationary", + subtype: "floor-level" + } ); + + }; + + resolve( session ); + + } ) + .catch( function ( error ) { + + return reject( error ); + + } ); + + } ); + + }; + + } + +} diff --git a/Reflector.js b/Reflector.js new file mode 100644 index 0000000..6c7533a --- /dev/null +++ b/Reflector.js @@ -0,0 +1,256 @@ +/** + * @author Slayvin / http://slayvin.net + */ + +THREE.Reflector = function ( geometry, options ) { + + THREE.Mesh.call( this, geometry ); + + this.type = 'Reflector'; + + var scope = this; + + options = options || {}; + + var color = ( options.color !== undefined ) ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F ); + var textureWidth = options.textureWidth || 512; + var textureHeight = options.textureHeight || 512; + var clipBias = options.clipBias || 0; + var shader = options.shader || THREE.Reflector.ReflectorShader; + var recursion = options.recursion !== undefined ? options.recursion : 0; + + // + + var reflectorPlane = new THREE.Plane(); + var normal = new THREE.Vector3(); + var reflectorWorldPosition = new THREE.Vector3(); + var cameraWorldPosition = new THREE.Vector3(); + var rotationMatrix = new THREE.Matrix4(); + var lookAtPosition = new THREE.Vector3( 0, 0, - 1 ); + var clipPlane = new THREE.Vector4(); + + var view = new THREE.Vector3(); + var target = new THREE.Vector3(); + var q = new THREE.Vector4(); + + var textureMatrix = new THREE.Matrix4(); + var virtualCamera = new THREE.PerspectiveCamera(); + + var parameters = { + minFilter: THREE.LinearFilter, + magFilter: THREE.LinearFilter, + format: THREE.RGBFormat, + stencilBuffer: false + }; + + var renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight, parameters ); + + if ( ! THREE.Math.isPowerOfTwo( textureWidth ) || ! THREE.Math.isPowerOfTwo( textureHeight ) ) { + + renderTarget.texture.generateMipmaps = false; + + } + + var material = new THREE.ShaderMaterial( { + uniforms: THREE.UniformsUtils.clone( shader.uniforms ), + fragmentShader: shader.fragmentShader, + vertexShader: shader.vertexShader, + transparent: options.transparent, + } ); + + material.uniforms[ "tDiffuse" ].value = renderTarget.texture; + material.uniforms[ "color" ].value = color; + material.uniforms[ "textureMatrix" ].value = textureMatrix; + + this.material = material; + + this.onBeforeRender = function ( renderer, scene, camera ) { + this.onBeforeRender2 && this.onBeforeRender2(renderer, scene, camera); + + if ( 'recursion' in camera.userData ) { + + if ( camera.userData.recursion === recursion ) return; + + camera.userData.recursion ++; + + } + + reflectorWorldPosition.setFromMatrixPosition( scope.matrixWorld ); + cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld ); + + rotationMatrix.extractRotation( scope.matrixWorld ); + + normal.set( 0, 0, 1 ); + normal.applyMatrix4( rotationMatrix ); + + view.subVectors( reflectorWorldPosition, cameraWorldPosition ); + + // Avoid rendering when reflector is facing away + + if ( view.dot( normal ) > 0 ) return; + + view.reflect( normal ).negate(); + view.add( reflectorWorldPosition ); + + rotationMatrix.extractRotation( camera.matrixWorld ); + + lookAtPosition.set( 0, 0, - 1 ); + lookAtPosition.applyMatrix4( rotationMatrix ); + lookAtPosition.add( cameraWorldPosition ); + + target.subVectors( reflectorWorldPosition, lookAtPosition ); + target.reflect( normal ).negate(); + target.add( reflectorWorldPosition ); + + virtualCamera.position.copy( view ); + virtualCamera.up.set( 0, 1, 0 ); + virtualCamera.up.applyMatrix4( rotationMatrix ); + virtualCamera.up.reflect( normal ); + virtualCamera.lookAt( target ); + + virtualCamera.far = camera.far; // Used in WebGLBackground + + virtualCamera.updateMatrixWorld(); + virtualCamera.projectionMatrix.copy( camera.projectionMatrix ); + + virtualCamera.userData.recursion = 0; + + // Update the texture matrix + textureMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); + textureMatrix.multiply( virtualCamera.projectionMatrix ); + textureMatrix.multiply( virtualCamera.matrixWorldInverse ); + textureMatrix.multiply( scope.matrixWorld ); + + // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html + // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf + reflectorPlane.setFromNormalAndCoplanarPoint( normal, reflectorWorldPosition ); + reflectorPlane.applyMatrix4( virtualCamera.matrixWorldInverse ); + + clipPlane.set( reflectorPlane.normal.x, reflectorPlane.normal.y, reflectorPlane.normal.z, reflectorPlane.constant ); + + var projectionMatrix = virtualCamera.projectionMatrix; + + q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ]; + q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ]; + q.z = - 1.0; + q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ]; + + // Calculate the scaled plane vector + clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) ); + + // Replacing the third row of the projection matrix + projectionMatrix.elements[ 2 ] = clipPlane.x; + projectionMatrix.elements[ 6 ] = clipPlane.y; + projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias; + projectionMatrix.elements[ 14 ] = clipPlane.w; + + // Render + + scope.visible = false; + + var currentRenderTarget = renderer.getRenderTarget(); + + var currentVrEnabled = renderer.vr.enabled; + var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate; + + renderer.vr.enabled = false; // Avoid camera modification and recursion + renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows + + renderer.setRenderTarget( renderTarget ); + renderer.clear(); + renderer.render( scene, virtualCamera ); + + renderer.vr.enabled = currentVrEnabled; + renderer.shadowMap.autoUpdate = currentShadowAutoUpdate; + + renderer.setRenderTarget( currentRenderTarget ); + + // Restore viewport + + var viewport = camera.viewport; + + if ( viewport !== undefined ) { + + renderer.state.viewport( viewport ); + + } + + scope.visible = true; + + }; + this.onAfterRender = (renderer, scene, camera) => { + this.onAfterRender2 && this.onAfterRender2(renderer, scene, camera); + }; + + this.getRenderTarget = function () { + + return renderTarget; + + }; + +}; + +THREE.Reflector.prototype = Object.create( THREE.Mesh.prototype ); +THREE.Reflector.prototype.constructor = THREE.Reflector; + +THREE.Reflector.ReflectorShader = { + + uniforms: { + + 'color': { + value: null + }, + + 'tDiffuse': { + value: null + }, + + 'textureMatrix': { + value: null + } + + }, + + vertexShader: [ + 'uniform mat4 textureMatrix;', + 'varying vec4 vUv;', + + 'void main() {', + + ' vUv = textureMatrix * vec4( position, 1.0 );', + + ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + + '}' + ].join( '\n' ), + + fragmentShader: [ + 'uniform vec3 color;', + 'uniform sampler2D tDiffuse;', + 'varying vec4 vUv;', + + 'float blendOverlay( float base, float blend ) {', + + ' return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );', + + '}', + + 'vec3 blendOverlay( vec3 base, vec3 blend ) {', + + ' return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) );', + + '}', + + 'void main() {', + + ' vec4 base = texture2DProj( tDiffuse, vUv );', + ' gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );', + + '}' + ].join( '\n' ) +}; diff --git a/app.html b/app.html index 4241cb1..8c48aed 100644 --- a/app.html +++ b/app.html @@ -9,6 +9,7 @@ + @@ -16,7 +17,7 @@ - + @@ -25,6 +26,8 @@ + + + + + +
+
+ + + + + +
+ +
+

Examples

+

+ model.glb
+ model2.vrm
+ model3.unitypackage
+ model4.zip
+ model5.fbx
+ model6.zip
+ model7.vrm
+ model8.vrm
+ model9.vrm
+ model10.vrm
+ model11.vrm
+ model12.fbx
+ model13.zip
+ model14.zip
+ model15.zip
+ model16.zip
+ model17.zip
+ model18.zip
+ model19.zip
+ model20.zip
+ model21.zip
+ model22.zip
+ model23.zip
+ model24.zip
+ model25.zip
+ model26.zip
+ model27.png
+ model28.png
+

+
+ +
+
+ +
+
+ + +
+

Multiplayer

+ +
+ +
+
+ +
+ +
+ + + Firstperson + Thirdperson + + +
+
+ + + diff --git a/vrarmik/ArmTransforms.js b/vrarmik/ArmTransforms.js new file mode 100644 index 0000000..e0f9b5e --- /dev/null +++ b/vrarmik/ArmTransforms.js @@ -0,0 +1,15 @@ +class ArmTransforms + { + constructor() { + this.transform = new THREE.Object3D(); + this.upperArm = new THREE.Object3D(); + this.lowerArm = new THREE.Object3D(); + this.hand = new THREE.Object3D(); + + this.transform.add(this.upperArm); + this.upperArm.add(this.lowerArm); + this.lowerArm.add(this.hand); + } + } + +export default ArmTransforms; diff --git a/vrarmik/LegsManager.js b/vrarmik/LegsManager.js new file mode 100644 index 0000000..ce3ec53 --- /dev/null +++ b/vrarmik/LegsManager.js @@ -0,0 +1,458 @@ +import {Helpers} from './Unity.js'; + +const stepRate = 0.2; +const stepHeight = 0.2; +const stepMinDistance = 0; +const stepMaxDistance = 0.25; +const stepRestitutionDistance = 0.8; +// const minStepDistanceTimeFactor = 0.2; +const minHmdVelocityTimeFactor = 0.015; +// const velocityLearningFactor = 1; +const maxVelocity = 0.015; +const velocityRestitutionFactor = 25; +const crossStepFactor = 0.9; + +const zeroVector = new THREE.Vector3(); +const oneVector = new THREE.Vector3(1, 1, 1); +const identityRotation = new THREE.Quaternion(); +const downHalfRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), -Math.PI/2); +const upHalfRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI/2); +const downJumpRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), -Math.PI/4); +// const downQuarterRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), -Math.PI/4); + +const localVector = new THREE.Vector3(); +const localVector2 = new THREE.Vector3(); +const localVector3 = new THREE.Vector3(); +const localVector4 = new THREE.Vector3(); +const localVector5 = new THREE.Vector3(); +const localVector6 = new THREE.Vector3(); +const localVector7 = new THREE.Vector3(); +const localQuaternion = new THREE.Quaternion(); +const localQuaternion2 = new THREE.Quaternion(); +const localQuaternion3 = new THREE.Quaternion(); +const localEuler = new THREE.Euler(); +const localMatrix = new THREE.Matrix4(); +const localMatrix2 = new THREE.Matrix4(); +const localMatrix3 = new THREE.Matrix4(); + +const _mod = (a, n) => (a % n + n) % n; +const _angleDiff = (targetA, sourceA) => { + let a = targetA - sourceA; + a = _mod((a + Math.PI), Math.PI*2) - Math.PI; + return a; +}; + +class Leg { + constructor(legsManager, left) { + this.transform = new THREE.Object3D(); + this.upperLeg = new THREE.Object3D(); + this.lowerLeg = new THREE.Object3D(); + this.foot = new THREE.Object3D(); + this.foot.stickTransform = new THREE.Object3D(); + this.foot.startTransform = new THREE.Object3D(); + this.foot.endTransform = new THREE.Object3D(); + this.foot.startHmdFloorTransform = new THREE.Object3D(); + + this.transform.add(this.upperLeg); + this.upperLeg.add(this.lowerLeg); + this.lowerLeg.add(this.foot); + + this.upperLegLength = 0; + this.lowerLegLength = 0; + this.legLength = 0; + this.eyesToUpperLegOffset = new THREE.Vector3(); + + this.legsManager = legsManager; + this.left = left; + + this.standing = true; + this.standFactor = 1; + const now = Date.now(); + this.lastStandTimestamp = now; + this.lastJumpTimestamp = now; + + this.stepping = false; + this.lastStepTimestamp = now; + + this.balance = 1; + } + + Start() { + this.upperLegLength = this.lowerLeg.position.length(); + this.lowerLegLength = this.foot.position.length(); + this.legLength = this.upperLegLength + this.lowerLegLength; + + Helpers.getWorldPosition(this.upperLeg, this.eyesToUpperLegOffset) + .sub(Helpers.getWorldPosition(this.legsManager.rig.shoulderTransforms.eyes, localVector)); + } + + Update() { + const footPosition = localVector.copy(this.foot.stickTransform.position); + // footPosition.y = 0; + const upperLegPosition = Helpers.getWorldPosition(this.upperLeg, localVector2); + + const footRotation = this.foot.stickTransform.quaternion; + /* localEuler.setFromQuaternion(footRotation, 'YXZ'); + localEuler.x = 0; + localEuler.z = 0; + const flatFootRotation = localQuaternion.setFromEuler(localEuler); */ + + const hypotenuseDistance = this.upperLegLength; + const verticalDistance = ((this.legsManager.rig.shoulderTransforms.prone || !this.standing) ? + upperLegPosition.distanceTo(this.foot.stickTransform.position) + : + Math.abs(upperLegPosition.y - this.foot.stickTransform.position.y) + ) * this.upperLegLength / this.legLength; + const offsetDistance = hypotenuseDistance > verticalDistance ? Math.sqrt(hypotenuseDistance*hypotenuseDistance - verticalDistance*verticalDistance) : 0; + + const lowerLegPosition = localVector4.copy(upperLegPosition).add(footPosition).divideScalar(2) + .add( + localVector5.copy(footPosition).sub(upperLegPosition) + .cross(localVector6.set(1, 0, 0).applyQuaternion(footRotation)) + .normalize() + .multiplyScalar(offsetDistance) + ); + + this.upperLeg.quaternion.setFromRotationMatrix( + localMatrix.lookAt( + zeroVector, + localVector5.copy(upperLegPosition).sub(lowerLegPosition), + localVector6.set(0, 1, 0).applyQuaternion(footRotation) + ) + ) + .multiply(downHalfRotation) + .premultiply(Helpers.getWorldQuaternion(this.transform, localQuaternion2).inverse()); + Helpers.updateMatrixMatrixWorld(this.upperLeg); + + this.lowerLeg.quaternion.setFromRotationMatrix( + localMatrix.lookAt( + zeroVector, + localVector5.copy(lowerLegPosition).sub(footPosition), + localVector6.set(0, 0, 1).applyQuaternion(footRotation) + ) + ) + .multiply(downHalfRotation) + .premultiply(Helpers.getWorldQuaternion(this.upperLeg, localQuaternion2).inverse()); + Helpers.updateMatrixMatrixWorld(this.lowerLeg); + + // this.lowerLeg.position = lowerLegPosition; + + // if (this.standing || this.stepping) { + // this.foot.position = footPosition; + this.foot.quaternion.copy(footRotation) + .multiply(downHalfRotation) + .premultiply(Helpers.getWorldQuaternion(this.lowerLeg, localQuaternion2).inverse()); + Helpers.updateMatrixMatrixWorld(this.foot); + /* } else { + this.foot.quaternion.slerp(downQuarterRotation, 0.1); + } */ + } + + getStandFactor() { + return 1 - Math.pow(Math.min(Math.max( + (Helpers.getWorldPosition(this.legsManager.rig.shoulderTransforms.eyes, localVector).add(this.eyesToUpperLegOffset).y - this.legLength) / (this.legsManager.rig.height*0.2), + 0), 1), 0.7); + } +} + +class LegsManager { + constructor(rig) { + this.hips = rig.shoulderTransforms.hips; + this.leftLeg = new Leg(this, true); + this.hips.add(this.leftLeg.transform); + this.rightLeg = new Leg(this, false); + this.hips.add(this.rightLeg.transform); + + this.rig = rig; + this.poseManager = rig.poseManager; + + this.legSeparation = 0; + this.lastHmdPosition = new THREE.Vector3(); + + this.hmdVelocity = new THREE.Vector3(); + } + + Start() { + this.legSeparation = Helpers.getWorldPosition(this.leftLeg.upperLeg, localVector) + .distanceTo(Helpers.getWorldPosition(this.rightLeg.upperLeg, localVector2)); + this.lastHmdPosition.copy(this.poseManager.vrTransforms.head.position); + this.leftLeg.Start(); + this.rightLeg.Start(); + } + + Update() { + Helpers.updateMatrixWorld(this.leftLeg.transform); + Helpers.updateMatrixWorld(this.leftLeg.upperLeg); + Helpers.updateMatrixWorld(this.leftLeg.lowerLeg); + Helpers.updateMatrixWorld(this.leftLeg.foot); + + Helpers.updateMatrixWorld(this.rightLeg.transform); + Helpers.updateMatrixWorld(this.rightLeg.upperLeg); + Helpers.updateMatrixWorld(this.rightLeg.lowerLeg); + Helpers.updateMatrixWorld(this.rightLeg.foot); + + const now = Date.now(); + + /* this.hmdVelocity.multiplyScalar(1-velocityLearningFactor) + .add(localVector.copy(this.poseManager.vrTransforms.head.position).sub(this.lastHmdPosition).multiplyScalar(velocityLearningFactor)); */ + this.hmdVelocity.copy(this.poseManager.vrTransforms.head.position).sub(this.lastHmdPosition); + this.lastHmdPosition.copy(this.poseManager.vrTransforms.head.position); + // console.log('v', this.hmdVelocity.toArray().join(',')); + + this.leftLeg.standFactor = this.leftLeg.getStandFactor(); + this.leftLeg.standing = this.leftLeg.standFactor >= 1; + if (this.leftLeg.standing) { + this.leftLeg.lastStandTimestamp = now; + } else { + this.leftLeg.lastJumpTimestamp = now; + } + if (this.leftLeg.stepping && !this.leftLeg.standing) { + this.leftLeg.stepping = false; + } + this.rightLeg.standFactor = this.rightLeg.getStandFactor(); + this.rightLeg.standing = this.rightLeg.standFactor >= 1; + if (this.rightLeg.standing) { + this.rightLeg.lastStandTimestamp = now; + } else { + this.rightLeg.lastJumpTimestamp = now; + } + if (this.rightLeg.stepping && !this.rightLeg.standing) { + this.rightLeg.stepping = false; + } + + const hipsFloorPosition = localVector.copy(this.hips.position); + hipsFloorPosition.y = 0; + const hipsFloorEuler = localEuler.setFromQuaternion(this.hips.quaternion, 'YXZ'); + hipsFloorEuler.x = 0; + hipsFloorEuler.z = 0; + const planeMatrix = localMatrix.compose(hipsFloorPosition, localQuaternion.setFromEuler(hipsFloorEuler), oneVector); + const planeMatrixInverse = localMatrix2.getInverse(planeMatrix); + + const fakePosition = localVector2; + const fakeScale = localVector3; + + const leftFootPosition = localVector4; + const leftFootRotation = localQuaternion; + localMatrix3.compose(this.leftLeg.foot.stickTransform.position, this.leftLeg.foot.stickTransform.quaternion, oneVector) + .premultiply(planeMatrixInverse) + .decompose(leftFootPosition, leftFootRotation, fakeScale); + + const rightFootPosition = localVector5; + const rightFootRotation = localQuaternion2; + localMatrix3.compose(this.rightLeg.foot.stickTransform.position, this.rightLeg.foot.stickTransform.quaternion, oneVector) + .premultiply(planeMatrixInverse) + .decompose(rightFootPosition, rightFootRotation, fakeScale); + + // rotation + + const maxTiltAngleFactor = 0.1; + if (this.leftLeg.standing && !this.rig.shoulderTransforms.prone) { + const leftFootEuler = localEuler.setFromQuaternion(leftFootRotation, 'YXZ'); + leftFootEuler.x = 0; + leftFootEuler.z = 0; + if (leftFootEuler.y < -Math.PI*maxTiltAngleFactor) { + leftFootEuler.y = -Math.PI*maxTiltAngleFactor; + } + if (leftFootEuler.y > Math.PI*maxTiltAngleFactor) { + leftFootEuler.y = Math.PI*maxTiltAngleFactor; + } + localMatrix3.compose(zeroVector, localQuaternion3.setFromEuler(leftFootEuler), oneVector) + .premultiply(planeMatrix) + .decompose(fakePosition, this.leftLeg.foot.stickTransform.quaternion, fakeScale); + } else if (!this.leftLeg.standing) { + this.leftLeg.foot.stickTransform.quaternion.copy(this.hips.quaternion) + .multiply(downJumpRotation); + } else { + Helpers.getWorldQuaternion(this.leftLeg.foot, this.leftLeg.foot.stickTransform.quaternion) + .multiply(upHalfRotation); + } + if (this.rightLeg.standing && !this.rig.shoulderTransforms.prone) { + const rightFootEuler = localEuler.setFromQuaternion(rightFootRotation, 'YXZ'); + rightFootEuler.x = 0; + rightFootEuler.z = 0; + if (rightFootEuler.y < -Math.PI*maxTiltAngleFactor) { + rightFootEuler.y = -Math.PI*maxTiltAngleFactor; + } + if (rightFootEuler.y > Math.PI*maxTiltAngleFactor) { + rightFootEuler.y = Math.PI*maxTiltAngleFactor; + } + localMatrix3.compose(zeroVector, localQuaternion3.setFromEuler(rightFootEuler), oneVector) + .premultiply(planeMatrix) + .decompose(fakePosition, this.rightLeg.foot.stickTransform.quaternion, fakeScale); + } else if (!this.rightLeg.standing) { + this.rightLeg.foot.stickTransform.quaternion.copy(this.hips.quaternion) + .multiply(downJumpRotation); + } else { + Helpers.getWorldQuaternion(this.rightLeg.foot, this.rightLeg.foot.stickTransform.quaternion) + .multiply(upHalfRotation); + } + + // position + + const _getLegStepFactor = leg => { + if (leg.stepping) { + const timeDiff = now - leg.lastStepTimestamp; + leg.lastStepTimestamp = now; + + const scaledStepRate = stepRate + /* / Math.max( + localVector.set(this.poseManager.vrTransforms.head.position.x, 0, this.poseManager.vrTransforms.head.position.z) + .distanceTo(leg.foot.startHmdFloorTransform.position), + minStepDistanceTimeFactor + ) */ + * Math.max(localVector2.set(this.hmdVelocity.x, 0, this.hmdVelocity.z).length() / this.rig.height, minHmdVelocityTimeFactor); + return Math.min(Math.max(leg.stepFactor + scaledStepRate * timeDiff, 0), 1); + } else { + return 0; + } + }; + this.leftLeg.stepFactor = _getLegStepFactor(this.leftLeg); + this.rightLeg.stepFactor = _getLegStepFactor(this.rightLeg); + + const leftCanStep = this.leftLeg.standing && !this.leftLeg.stepping && (!this.rightLeg.stepping || this.rightLeg.stepFactor >= crossStepFactor); + const rightCanStep = this.rightLeg.standing && !this.rightLeg.stepping && (!this.leftLeg.stepping || this.leftLeg.stepFactor >= crossStepFactor); + const maxStepAngleFactor = 0; + if (leftCanStep || rightCanStep) { + let leftStepDistance = 0; + let leftStepAngleDiff = 0; + if (leftCanStep) { + const leftDistance = Math.sqrt(leftFootPosition.x*leftFootPosition.x + leftFootPosition.z*leftFootPosition.z); + const leftAngleDiff = Math.atan2(leftFootPosition.x, leftFootPosition.z); + if (leftDistance < this.rig.height*stepMinDistance) { + leftStepDistance = leftDistance; + } else if (leftDistance > this.rig.height*stepMaxDistance) { + leftStepDistance = leftDistance; + } + if (leftAngleDiff > -Math.PI*maxStepAngleFactor) { + leftStepAngleDiff = leftAngleDiff; + } else if (leftAngleDiff < -Math.PI+Math.PI*maxStepAngleFactor) { + leftStepAngleDiff = leftAngleDiff; + } + } + let rightStepDistance = 0; + let rightStepAngleDiff = 0; + if (rightCanStep) { + const rightDistance = Math.sqrt(rightFootPosition.x*rightFootPosition.x + rightFootPosition.z*rightFootPosition.z); + const rightAngleDiff = Math.atan2(rightFootPosition.x, rightFootPosition.z); + if (rightDistance < this.rig.height*stepMinDistance) { + rightStepDistance = rightDistance; + } else if (rightDistance > this.rig.height*stepMaxDistance) { + rightStepDistance = rightDistance; + } + if (rightAngleDiff < Math.PI*maxStepAngleFactor) { + rightStepAngleDiff = rightAngleDiff; + } else if (rightAngleDiff > Math.PI-Math.PI*maxStepAngleFactor) { + rightStepAngleDiff = rightAngleDiff; + } + } + + const _stepLeg = leg => { + const footDistance = this.legSeparation*stepRestitutionDistance;//Math.min(Math.max(leftStepDistance, this.legSeparation*0.7), this.legSeparation*1.4); + + leg.foot.startTransform.position.copy(leg.foot.stickTransform.position); + // leg.foot.startTransform.quaternion.copy(leg.foot.stickTransform.quaternion); + + leg.foot.endTransform.position.copy(hipsFloorPosition) + .add(localVector6.set((leg.left ? -1 : 1) * footDistance, 0, 0).applyQuaternion(leg.foot.stickTransform.quaternion)); + const velocityVector = localVector6.set(this.hmdVelocity.x, 0, this.hmdVelocity.z); + const velocityVectorLength = velocityVector.length(); + if (velocityVectorLength > maxVelocity*this.rig.height) { + velocityVector.multiplyScalar(maxVelocity*this.rig.height / velocityVectorLength); + } + velocityVector.multiplyScalar(velocityRestitutionFactor); + leg.foot.endTransform.position.add(velocityVector); + // leg.foot.endTransform.quaternion.copy(this.rightLeg.foot.stickTransform.quaternion); + + leg.foot.startHmdFloorTransform.position.set(this.poseManager.vrTransforms.head.position.x, 0, this.poseManager.vrTransforms.head.position.z); + + leg.lastStepTimestamp = now; + leg.stepping = true; + }; + + if ( + (leftStepDistance !== 0 || leftStepAngleDiff !== 0) && + (rightStepDistance === 0 || Math.abs(leftStepDistance*this.leftLeg.balance) >= Math.abs(rightStepDistance*this.rightLeg.balance)) && + (rightStepAngleDiff === 0 || Math.abs(leftStepAngleDiff*this.leftLeg.balance) >= Math.abs(rightStepAngleDiff*this.rightLeg.balance)) + ) { + _stepLeg(this.leftLeg); + this.leftLeg.balance = 0; + this.rightLeg.balance = 1; + } else if (rightStepDistance !== 0 || rightStepAngleDiff !== 0) { + _stepLeg(this.rightLeg); + this.rightLeg.balance = 0; + this.leftLeg.balance = 1; + } + } + + if (this.rig.shoulderTransforms.prone) { + const targetPosition = Helpers.getWorldPosition(this.leftLeg.upperLeg, localVector6) + .add( + localVector7.set(0, -this.leftLeg.legLength*0.95, 0) + .applyQuaternion(this.hips.quaternion) + ); + targetPosition.y = 0; + this.leftLeg.foot.stickTransform.position.lerp(targetPosition, 0.1); + + this.leftLeg.stepping = false; + } else if (this.leftLeg.stepping) { + this.leftLeg.foot.stickTransform.position.copy(this.leftLeg.foot.startTransform.position) + .lerp(this.leftLeg.foot.endTransform.position, this.leftLeg.stepFactor) + .add(localVector6.set(0, Math.sin(this.leftLeg.stepFactor*Math.PI) * stepHeight * this.rig.height, 0)); + + if (this.leftLeg.stepFactor >= 1) { + this.leftLeg.stepping = false; + } + } else if (!this.leftLeg.standing) { + const targetPosition = Helpers.getWorldPosition(this.leftLeg.upperLeg, localVector6) + .add( + localVector7.set(0, 0, 1) + .normalize() + .applyQuaternion(this.hips.quaternion) + .multiplyScalar(this.leftLeg.legLength*0.5) + ); + this.leftLeg.foot.stickTransform.position.lerp(targetPosition, 0.1); + } else { + const targetPosition = localVector6.copy(this.leftLeg.foot.stickTransform.position); + targetPosition.y = 0; + this.leftLeg.foot.stickTransform.position.lerp(targetPosition, 0.2); + } + if (this.rig.shoulderTransforms.prone) { + const targetPosition = Helpers.getWorldPosition(this.rightLeg.upperLeg, localVector6) + .add( + localVector7.set(0, -this.rightLeg.legLength*0.95, 0) + .applyQuaternion(this.hips.quaternion) + ); + targetPosition.y = 0; + this.rightLeg.foot.stickTransform.position.lerp(targetPosition, 0.1); + + this.rightLeg.stepping = false; + } else if (this.rightLeg.stepping) { + this.rightLeg.foot.stickTransform.position.copy(this.rightLeg.foot.startTransform.position) + .lerp(this.rightLeg.foot.endTransform.position, this.rightLeg.stepFactor) + .add(localVector6.set(0, Math.sin(this.rightLeg.stepFactor*Math.PI) * stepHeight * this.rig.height, 0)); + // this.rightLeg.foot.stickTransform.quaternion.copy(this.rightLeg.foot.startTransform.quaternion).slerp(this.rightLeg.foot.endTransform.quaternion, stepFactor); + + if (this.rightLeg.stepFactor >= 1) { + this.rightLeg.stepping = false; + } + } else if (!this.rightLeg.standing) { + const targetPosition = Helpers.getWorldPosition(this.rightLeg.upperLeg, localVector6) + .add( + localVector7.set(0, 0, 1) + .normalize() + .applyQuaternion(this.hips.quaternion) + .multiplyScalar(this.rightLeg.legLength*0.6) + ); + this.rightLeg.foot.stickTransform.position.lerp(targetPosition, 0.1); + } else { + const targetPosition = localVector6.copy(this.rightLeg.foot.stickTransform.position); + targetPosition.y = 0; + this.rightLeg.foot.stickTransform.position.lerp(targetPosition, 0.2); + } + + this.leftLeg.Update(); + this.rightLeg.Update(); + } +} + +export default LegsManager; diff --git a/vrarmik/PoseManager.js b/vrarmik/PoseManager.js new file mode 100644 index 0000000..0c43d53 --- /dev/null +++ b/vrarmik/PoseManager.js @@ -0,0 +1,84 @@ +import VRTrackingReferences from './VRTrackingReferences.js'; + +class PoseManager + { + constructor(rig) { + this.vrTransforms = new VRTrackingReferences(); + // this.OnCalibrateListener = null; + + // Oculus uses a different reference position -> 0 is the reference head position if the user is standing in the middle of the room. + // In OpenVR, the 0 position is the ground position and the user is then at (0, playerHeightHmd, 0) if he is in the middle of the room, so I need to correct this for shoulder calculation + // this.vrSystemOffsetHeight = 0.0; + + this.referencePlayerHeightHmd = 1.7; + this.referencePlayerWidthWrist = 1.39; + this.playerHeightHmd = 1.70; + this.playerWidthWrist = 1.39; + // this.playerWidthShoulders = 0.31; + // this.loadPlayerSizeOnAwake = false; + + // PoseManager.Instance = this; + } + + /* OnEnable() + { + if (PoseManager.Instance === null) + { + PoseManager.Instance = this; + } + else if (PoseManager.Instance !== null) + { + Debug.LogError("Multiple Instances of PoseManager in Scene"); + } + } */ + + /* Start() + { + if (this.loadPlayerSizeOnAwake) + { + this.loadPlayerSize(); + } + // this.vrSystemOffsetHeight = 0; + + onCalibrate += OnCalibrate; + } + + OnCalibrate() + { + this.playerHeightHmd = Camera.main.transform.position.y; + } + + loadPlayerWidthShoulders() + { + this.playerWidthShoulders = PlayerPrefs.GetFloat("VRArmIK_PlayerWidthShoulders", 0.31); + } + + savePlayerWidthShoulders(width) + { + PlayerPrefs.SetFloat("VRArmIK_PlayerWidthShoulders", width); + } + + calibrateIK() + { + this.playerWidthWrist = this.vrTransforms.leftHand.position.clone().sub(this.vrTransforms.rightHand.position).magnitude; + this.playerHeightHmd = this.vrTransforms.hmd.position.y; + this.savePlayerSize(this.playerHeightHmd, this.playerWidthWrist); + } + + savePlayerSize(heightHmd, widthWrist) + { + PlayerPrefs.SetFloat("VRArmIK_PlayerHeightHmd", this.heightHmd); + PlayerPrefs.SetFloat("VRArmIK_PlayerWidthWrist", this.widthWrist); + this.loadPlayerSize(); + this.onCalibrate && this.onCalibrate.Invoke(); + } + + loadPlayerSize() + { + this.playerHeightHmd = PlayerPrefs.GetFloat("VRArmIK_PlayerHeightHmd", this.referencePlayerHeightHmd); + this.playerWidthWrist = PlayerPrefs.GetFloat("VRArmIK_PlayerWidthWrist", this.referencePlayerWidthWrist); + } */ + } + // PoseManager.Instance = null; + +export default PoseManager; diff --git a/vrarmik/ShoulderPoser.js b/vrarmik/ShoulderPoser.js new file mode 100644 index 0000000..27ef53c --- /dev/null +++ b/vrarmik/ShoulderPoser.js @@ -0,0 +1,438 @@ +import {Helpers} from './Unity.js'; + +const rightVector = new THREE.Vector3(1, 0, 0); +const z180Quaternion = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI); + +const localVector = new THREE.Vector3(); +const localVector2 = new THREE.Vector3(); +const localVector3 = new THREE.Vector3(); +const localVector4 = new THREE.Vector3(); +const localQuaternion = new THREE.Quaternion(); +const localQuaternion2 = new THREE.Quaternion(); +const localQuaternion3 = new THREE.Quaternion(); +const localEuler = new THREE.Euler(); +const localEuler2 = new THREE.Euler(); + +class ShoulderPoser + { + constructor(rig, shoulder) { + this.rig = rig; + this.shoulder = shoulder; + this.poseManager = rig.poseManager; + this.vrTransforms = this.poseManager.vrTransforms; + + // this.headNeckDirectionVector = new Vector3(1.0894440904962721e-10, -0.06860782711996793, -0.0006757629250115499).normalize(); + // this.headNeckDistance = 0.06861115505261682; + // this.neckShoulderDistance = new Vector3(3.122724301363178e-10, -0.1953215129534993, 0.02834002902116923); + + // this.maxDeltaHeadRotation = 80; + + // this.distinctShoulderRotationLimitForward = 33; + + // this.distinctShoulderRotationLimitBackward = 0; + + // this.distinctShoulderRotationLimitUpward = 33; + // this.distinctShoulderRotationMultiplier = 30; + + // this.rightRotationStartHeight = 0; + // this.rightRotationHeightFactor = 142; + // this.rightRotationHeadRotationFactor = 0.3; + // this.rightRotationHeadRotationOffset = -20; + + // this.startShoulderDislocationBefore = 0.005; + + // this.ignoreYPos = true; + // this.autoDetectHandsBehindHead = true; + // this.clampRotationToHead = true; + // this.enableDistinctShoulderRotation = true; + // this.enableShoulderDislocation = true; + + + // this.handsBehindHead = false; + + // this.clampingHeadRotation = false; + // this.shoulderDislocated = false; + // this.shoulderRightRotation; + + // this.lastAngle = Vector3.zero; + + // this.leftShoulderAnkerStartLocalPosition = new Vector3(); + // this.rightShoulderAnkerStartLocalPosition = new Vector3(); + } + + /* Start() { + this.leftShoulderAnkerStartLocalPosition = this.shoulder.leftShoulderAnchor.localPosition.clone(); + this.rightShoulderAnkerStartLocalPosition = this.shoulder.rightShoulderAnchor.position.clone(); + } */ + + /* onCalibrate() + { + this.shoulder.leftArm.setArmLength((avatarTrackingReferences.leftHand.position - this.shoulder.leftShoulderAnchor.position) + .magnitude); + this.shoulder.rightArm.setArmLength((avatarTrackingReferences.rightHand.position - this.shoulder.rightShoulderAnchor.position) + .magnitude); + } */ + + Update() + { + this.shoulder.proneFactor = this.getProneFactor(); + this.shoulder.prone = this.shoulder.proneFactor > 0; + if (this.shoulder.prone) { + this.shoulder.lastProneTimestamp = Date.now(); + } else { + this.shoulder.lastStandTimestamp = Date.now(); + } + + this.updateHips(); + + // this.shoulder.transform.rotation = Quaternion.identity; + // this.positionShoulder(); + this.rotateShoulderBase(); + + /* if (this.enableDistinctShoulderRotation) + { + this.rotateLeftShoulder(rotation); + this.rotateRightShoulder(rotation); + } */ + + /* if (this.enableShoulderDislocation) + { + this.clampShoulderHandDistance(); + } + else + { + this.shoulder.leftArm.transform.localPosition = Vector3.zero; + this.shoulder.rightArm.transform.localPosition = Vector3.zero; + } */ + + this.updateNeck(); + + // Debug.DrawRay(this.shoulder.transform.position, this.shoulder.transform.forward); + } + + /* updateHips() { + const hmdRotation = localQuaternion.copy(this.vrTransforms.head.quaternion) + .multiply(z180Quaternion); + const hmdEuler = localEuler.setFromQuaternion(hmdRotation, 'YXZ'); + hmdEuler.x = 0; + hmdEuler.z = 0; + const hmdFlatRotation = localQuaternion2.setFromEuler(hmdEuler); + + const headPosition = localVector.copy(this.vrTransforms.head.position) + .add(localVector2.copy(this.shoulder.eyes.position).multiplyScalar(-1).applyQuaternion(hmdRotation)); + const neckPosition = headPosition.add(localVector2.copy(this.shoulder.head.position).multiplyScalar(-1).applyQuaternion(hmdRotation)); + const chestPosition = neckPosition.add(localVector2.copy(this.shoulder.neck.position).multiplyScalar(-1).applyQuaternion(hmdFlatRotation)); + const spinePosition = chestPosition.add(localVector2.copy(this.shoulder.transform.position).multiplyScalar(-1).applyQuaternion(hmdFlatRotation)); + const hipsPosition = spinePosition.add(localVector2.copy(this.shoulder.spine.position).multiplyScalar(-1).applyQuaternion(hmdFlatRotation)); + + this.shoulder.hips.position.copy(hipsPosition); + this.shoulder.hips.quaternion.copy(hmdFlatRotation); + Helpers.updateMatrix(this.shoulder.hips); + this.shoulder.hips.matrixWorld.copy(this.shoulder.hips.matrix); + Helpers.updateMatrixWorld(this.shoulder.spine); + Helpers.updateMatrixWorld(this.shoulder.transform); + } */ + + updateHips() { + const hmdRotation = localQuaternion.copy(this.vrTransforms.head.quaternion) + .multiply(z180Quaternion); + /* const hmdXYRotation = localQuaternion2.setFromRotationMatrix(localMatrix.lookAt( + new THREE.Vector3(), + new THREE.Vector3(0, 0, -1).applyQuaternion(hmdRotation), + new THREE.Vector3(0, 1, 0).applyQuaternion(hmdRotation) + )); */ + const hmdEuler = localEuler.setFromQuaternion(hmdRotation, 'YXZ'); + hmdEuler.x = 0; + hmdEuler.z = 0; + const hmdXYRotation = localQuaternion2.setFromEuler(hmdEuler); + hmdXYRotation.multiply(localQuaternion3.setFromAxisAngle(rightVector, this.shoulder.proneFactor * Math.PI/2)); + if (!this.rig.legsManager.leftLeg.standing && !this.rig.legsManager.rightLeg.standing) { + const jumpFactor = 1-Math.min(this.rig.legsManager.leftLeg.standFactor, this.rig.legsManager.rightLeg.standFactor); + hmdXYRotation.multiply(localQuaternion3.setFromAxisAngle(rightVector, jumpFactor * Math.PI/4)); + } else { + const standFactor = Math.min(this.rig.legsManager.leftLeg.standFactor, this.rig.legsManager.rightLeg.standFactor); + hmdXYRotation.multiply(localQuaternion3.setFromAxisAngle(rightVector, (1-standFactor) * Math.PI/4)); + } + + const headPosition = localVector.copy(this.vrTransforms.head.position) + .sub(localVector2.copy(this.shoulder.eyes.position).applyQuaternion(hmdRotation)); + const neckPosition = headPosition.sub(localVector2.copy(this.shoulder.head.position).applyQuaternion(hmdRotation)); + const chestPosition = neckPosition.sub(localVector2.copy(this.shoulder.neck.position).applyQuaternion(hmdXYRotation)); + const spinePosition = chestPosition.sub(localVector2.copy(this.shoulder.transform.position).applyQuaternion(hmdXYRotation)); + const hipsPosition = spinePosition.sub(localVector2.copy(this.shoulder.spine.position).applyQuaternion(hmdXYRotation)); + + this.shoulder.hips.position.copy(hipsPosition); + this.shoulder.hips.quaternion.copy(hmdXYRotation); + Helpers.updateMatrix(this.shoulder.hips); + this.shoulder.hips.matrixWorld.copy(this.shoulder.hips.matrix); + Helpers.updateMatrixWorld(this.shoulder.spine); + Helpers.updateMatrixWorld(this.shoulder.transform); + } + + /* updateNeck() { + const hmdRotation = localQuaternion.copy(this.vrTransforms.head.quaternion) + .multiply(z180Quaternion); + const hmdFlatEuler = localEuler.setFromQuaternion(hmdRotation, 'YXZ'); + hmdFlatEuler.x = 0; + hmdFlatEuler.z = 0; + const hmdUpEuler = localEuler2.setFromQuaternion(hmdRotation, 'YXZ'); + hmdUpEuler.y = 0; + + this.shoulder.neck.quaternion.setFromEuler(hmdFlatEuler) + .premultiply(Helpers.getWorldQuaternion(this.shoulder.neck.parent, localQuaternion).inverse()); + Helpers.updateMatrixMatrixWorld(this.shoulder.neck); + + this.shoulder.head.quaternion.setFromEuler(hmdUpEuler); + Helpers.updateMatrixMatrixWorld(this.shoulder.head); + + Helpers.updateMatrixWorld(this.shoulder.eyes); + } */ + + updateNeck() { + const hmdRotation = localQuaternion.copy(this.vrTransforms.head.quaternion) + .multiply(z180Quaternion); + /* const hmdXYRotation = localQuaternion2.setFromRotationMatrix(localMatrix.lookAt( + new THREE.Vector3(), + new THREE.Vector3(0, 0, -1).applyQuaternion(hmdRotation), + new THREE.Vector3(0, 1, 0).applyQuaternion(hmdRotation) + )); */ + const hmdEuler = localEuler.setFromQuaternion(hmdRotation, 'YXZ'); + hmdEuler.x = 0; + hmdEuler.z = 0; + const hmdXYRotation = localQuaternion2.setFromEuler(hmdEuler); + + this.shoulder.neck.quaternion.copy(hmdXYRotation) + .premultiply(Helpers.getWorldQuaternion(this.shoulder.neck.parent, localQuaternion3).inverse()); + Helpers.updateMatrixMatrixWorld(this.shoulder.neck); + + this.shoulder.head.quaternion.copy(hmdRotation) + .premultiply(Helpers.getWorldQuaternion(this.shoulder.head.parent, localQuaternion3).inverse()); + Helpers.updateMatrixMatrixWorld(this.shoulder.head); + + Helpers.updateMatrixWorld(this.shoulder.eyes); + } + + /* rotateLeftShoulder(shoulderRotation) + { + this.rotateShoulderUp(this.shoulder.leftShoulder, this.shoulder.leftArm, this.avatarTrackingReferences.leftHand, this.leftShoulderAnkerStartLocalPosition, 1, shoulderRotation); + } + + rotateRightShoulder(shoulderRotation) + { + this.rotateShoulderUp(this.shoulder.rightShoulder, this.shoulder.rightArm, this.avatarTrackingReferences.rightHand, this.rightShoulderAnkerStartLocalPosition, -1, shoulderRotation); + } + + rotateShoulderUp(shoulderSide, arm, targetHand, initialShoulderLocalPos, angleSign, shoulderRotation) + { + const initialShoulderPos = initialShoulderLocalPos.clone().applyMatrix4(this.shoulder.transform.matrixWorld); + const handShoulderOffset = new Vector3().subVectors(targetHand.position, initialShoulderPos); + const armLength = arm.armLength; + + const targetAngle = Vector3.zero; + + const forwardDistanceRatio = Vector3.Dot(handShoulderOffset, Vector3.forward.applyQuaternion(shoulderRotation)) / armLength; + const upwardDistanceRatio = Vector3.Dot(handShoulderOffset, Vector3.up.applyQuaternion(shoulderRotation)) / armLength; + if (forwardDistanceRatio > 0) + { + targetAngle.y = Mathf.Clamp((forwardDistanceRatio - 0.5) * this.distinctShoulderRotationMultiplier, 0, this.distinctShoulderRotationLimitForward); + } + else + { + targetAngle.y = Mathf.Clamp(-(forwardDistanceRatio + 0.08) * this.distinctShoulderRotationMultiplier * 10, -this.distinctShoulderRotationLimitBackward, 0); + } + + targetAngle.z = Mathf.Clamp(-(upwardDistanceRatio - 0.5) * this.distinctShoulderRotationMultiplier, -this.distinctShoulderRotationLimitUpward, 0); + + targetAngle.multiplyScalar(angleSign); + + shoulderSide.localRotation = new THREE.Quaternion().setFromEuler(new THREE.Euler(targetAngle.x * Mathf.Deg2Rad, targetAngle.y * Mathf.Deg2Rad, targetAngle.z * Mathf.Deg2Rad, Mathf.Order)); + } + + positionShoulder() + { + const headNeckOffset = this.headNeckDirectionVector.clone().applyQuaternion(this.avatarTrackingReferences.head.rotation); + const targetPosition = new Vector3().addVectors(this.avatarTrackingReferences.head.position, headNeckOffset.clone().multiplyScalar(this.headNeckDistance)); + this.shoulder.transform.localPosition = + new Vector3().addVectors(targetPosition, this.neckShoulderDistance); + } */ + + rotateShoulderBase() + { + const angleY = this.getCombinedDirectionAngleUp(); + + // const targetRotation = new Vector3(0, angle, 0); + + /* if (this.autoDetectHandsBehindHead) + { + this.detectHandsBehindHead(targetRotation); + } */ + + /* if (this.clampRotationToHead) + { */ + // angleY = this.clampHeadRotationDeltaUp(angleY); + // } + + this.shoulder.transform.quaternion.setFromEuler(localEuler.set(0, angleY, 0, 'YXZ')) + .premultiply( + localQuaternion.copy(this.shoulder.hips.quaternion) + .multiply(z180Quaternion) + ); + /* this.shoulder.transform.quaternion.multiply(localQuaternion3.setFromAxisAngle(rightVector, this.shoulder.proneFactor * Math.PI/2)); + if (!this.rig.legsManager.leftLeg.standing && !this.rig.legsManager.rightLeg.standing) { + const jumpFactor = 1-Math.min(this.rig.legsManager.leftLeg.standFactor, this.rig.legsManager.rightLeg.standFactor); + this.shoulder.transform.quaternion.multiply(localQuaternion3.setFromAxisAngle(rightVector, jumpFactor * Math.PI/4)); + } else { + const standFactor = Math.min(this.rig.legsManager.leftLeg.standFactor, this.rig.legsManager.rightLeg.standFactor); + this.shoulder.transform.quaternion.multiply(localQuaternion3.setFromAxisAngle(rightVector, (1-standFactor) * Math.PI/4)); + } */ + this.shoulder.transform.quaternion + .premultiply(Helpers.getWorldQuaternion(this.shoulder.transform.parent, localQuaternion).inverse()); + Helpers.updateMatrixMatrixWorld(this.shoulder.transform); + Helpers.updateMatrixWorld(this.shoulder.leftShoulderAnchor); + Helpers.updateMatrixWorld(this.shoulder.rightShoulderAnchor); + } + + /* rotateShoulderRightBase(rotation) + { + + const heightDiff = this.vrTransforms.head.position.y - this.poseManager.vrSystemOffsetHeight; + const relativeHeightDiff = -heightDiff / this.poseManager.playerHeightHmd; + + const hmdRotation = this.vrTransforms.head.rotation; + hmdRotation.multiply(z180Quaternion); + const headRightRotation = VectorHelpers.getAngleBetween(this.shoulder.transform.forward, + new Vector3(0, 0, 1).applyQuaternion(hmdRotation), + Vector3.up, this.shoulder.transform.right) + this.rightRotationHeadRotationOffset; + const heightFactor = Mathf.Clamp(relativeHeightDiff - this.rightRotationStartHeight, 0, 1); + this.shoulderRightRotation = heightFactor * this.rightRotationHeightFactor; + this.shoulderRightRotation += Mathf.Clamp(headRightRotation * this.rightRotationHeadRotationFactor * heightFactor, 0, 50); + + this.shoulderRightRotation = Mathf.Clamp(this.shoulderRightRotation, 0, 50); + + const deltaRot = Quaternion.AngleAxis(this.shoulderRightRotation, this.shoulder.transform.right); + + + // this.shoulder.transform.rotation = new Quaternion().multiplyQuaternions(deltaRot, this.shoulder.transform.rotation); + return new Quaternion().multiplyQuaternions(deltaRot, rotation); + // this.positionShoulderRelative(); + } + + positionShoulderRelative() + { + const deltaRot = Quaternion.AngleAxis(this.shoulderRightRotation, this.shoulder.transform.right); + const shoulderHeadDiff = new Vector3().subVectors(this.shoulder.transform.position, this.avatarTrackingReferences.head.position); + // this.shoulder.transform.position = new Vector3().addVectors(shoulderHeadDiff.clone().applyQuaternion(deltaRot), this.avatarTrackingReferences.head.position); + } */ + + getCombinedDirectionAngleUp() + { + const hipsRotation = localQuaternion.copy(this.shoulder.hips.quaternion) + .multiply(z180Quaternion); + const hipsRotationInverse = localQuaternion2.copy(hipsRotation) + .inverse(); + + const distanceLeftHand = localVector.copy(this.vrTransforms.leftHand.position) + .sub(this.vrTransforms.head.position) + .applyQuaternion(hipsRotationInverse); + const distanceRightHand = localVector2.copy(this.vrTransforms.rightHand.position) + .sub(this.vrTransforms.head.position) + .applyQuaternion(hipsRotationInverse); + + distanceLeftHand.y = 0; + distanceRightHand.y = 0; + + const leftBehind = distanceLeftHand.z > 0; + const rightBehind = distanceRightHand.z > 0; + if (leftBehind) { + distanceLeftHand.z *= rightBehind ? -2 : -1; + } + if (rightBehind) { + distanceRightHand.z *= leftBehind ? -2 : -1; + } + + const combinedDirection = localVector.addVectors(distanceLeftHand.normalize(), distanceRightHand.normalize()); + return Math.atan2(combinedDirection.x, combinedDirection.z); + } + + getProneFactor() { + return 1 - Math.min(Math.max((this.vrTransforms.head.position.y - this.rig.height*0.3)/(this.rig.height*0.3), 0), 1); + } + + /* detectHandsBehindHead(targetRotation) + { + const delta = Mathf.Abs(targetRotation.y - this.lastAngle.y + 360) % 360; + if (delta > 150 && delta < 210 && this.lastAngle.magnitude > 0.000001 && !this.clampingHeadRotation) + { + this.handsBehindHead = !this.handsBehindHead; + } + + this.lastAngle = targetRotation; + + if (this.handsBehindHead) + { + targetRotation.y += 180; + } + } + + clampHeadRotationDeltaUp(angleY) + { + const hmdRotation = localQuaternion.copy(this.vrTransforms.head.quaternion) + .multiply(z180Quaternion); + + const headUpRotation = (localEuler.setFromQuaternion(hmdRotation, 'YXZ').y + Math.PI*2) % (Math.PI*2); + const targetUpRotation = (angleY + Math.PI*2) % (Math.PI*2); + + const delta = headUpRotation - targetUpRotation; + + if (delta > this.maxDeltaHeadRotation && delta < Math.PI || delta < -Math.PI && delta >= -Math.PI*2 + this.maxDeltaHeadRotation) + { + angleY = headUpRotation - this.maxDeltaHeadRotation; + // this.clampingHeadRotation = true; + } + else if (delta < -this.maxDeltaHeadRotation && delta > -Math.PI || delta > Math.PI && delta < Math.PI*2 - this.maxDeltaHeadRotation) + { + angleY = headUpRotation + this.maxDeltaHeadRotation; + // this.clampingHeadRotation = true; + } + // else + // { + // this.clampingHeadRotation = false; + // } + return angleY; + } + + clampShoulderHandDistance() + { + const leftHandVector = new Vector3().subVectors(this.avatarTrackingReferences.leftHand.position, this.shoulder.leftShoulderAnchor.position); + const rightHandVector = new Vector3().subVectors(this.avatarTrackingReferences.rightHand.position, this.shoulder.rightShoulderAnchor.position); + const leftShoulderHandDistance = leftHandVector.magnitude; + const rightShoulderHandDistance = rightHandVector.magnitude; + this.shoulderDislocated = false; + + const startBeforeFactor = (1 - this.startShoulderDislocationBefore); + + if (leftShoulderHandDistance > this.shoulder.leftArm.armLength * startBeforeFactor) + { + this.shoulderDislocated = true; + this.shoulder.leftArm.transform.position = new Vector3().addVectors(this.shoulder.leftShoulderAnchor.position, + leftHandVector.normalized.multiplyScalar(leftShoulderHandDistance - this.shoulder.leftArm.armLength * startBeforeFactor)); + } + else + { + this.shoulder.leftArm.transform.localPosition = Vector3.zero; + } + + if (rightShoulderHandDistance > this.shoulder.rightArm.armLength * startBeforeFactor) + { + this.shoulderDislocated = true; + this.shoulder.rightArm.transform.position = new Vector3().addVectors(this.shoulder.rightShoulderAnchor.position, + rightHandVector.normalized.multiplyScalar(rightShoulderHandDistance - this.shoulder.rightArm.armLength * startBeforeFactor)); + } + else + { + this.shoulder.rightArm.transform.localPosition = Vector3.zero; + } + } */ + } + +export default ShoulderPoser; diff --git a/vrarmik/ShoulderTransforms.js b/vrarmik/ShoulderTransforms.js new file mode 100644 index 0000000..30dcfe4 --- /dev/null +++ b/vrarmik/ShoulderTransforms.js @@ -0,0 +1,63 @@ +import ArmTransforms from './ArmTransforms.js'; +import ShoulderPoser from './ShoulderPoser.js'; +import VRArmIK from './VRArmIK.js'; + + +class ShoulderTransforms + { + constructor(rig) { + this.transform = new THREE.Object3D(); + this.hips = new THREE.Object3D(); + this.spine = new THREE.Object3D(); + this.neck = new THREE.Object3D(); + this.head = new THREE.Object3D(); + this.eyes = new THREE.Object3D(); + + this.hips.add(this.spine); + this.spine.add(this.transform); + this.transform.add(this.neck); + this.neck.add(this.head); + this.head.add(this.eyes); + + // this.leftShoulder = new THREE.Object3D(); + // this.transform.add(this.leftShoulder); + // this.rightShoulder = new THREE.Object3D(); + // this.transform.add(this.rightShoulder); + + this.leftShoulderAnchor = new THREE.Object3D(); + this.transform.add(this.leftShoulderAnchor); + this.rightShoulderAnchor = new THREE.Object3D(); + this.transform.add(this.rightShoulderAnchor); + + this.leftArm = new ArmTransforms(); + this.rightArm = new ArmTransforms(); + + this.leftShoulderAnchor.add(this.leftArm.transform); + this.rightShoulderAnchor.add(this.rightArm.transform); + + this.prone = false; + this.proneFactor = 0; + const now = Date.now(); + this.lastStandTimestamp = now; + this.lastProneTimestamp = now; + + this.shoulderPoser = new ShoulderPoser(rig, this); + + this.leftArmIk = new VRArmIK(this.leftArm, this, this.shoulderPoser, this.shoulderPoser.vrTransforms.leftHand, true); + this.rightArmIk = new VRArmIK(this.rightArm, this, this.shoulderPoser, this.shoulderPoser.vrTransforms.rightHand, false); + } + + Start() + { + this.leftArmIk.Start(); + this.rightArmIk.Start(); + } + + Update() { + this.shoulderPoser.Update(); + this.leftArmIk.Update(); + this.rightArmIk.Update(); + } + } + +export default ShoulderTransforms; diff --git a/vrarmik/SkeletonUtils.js b/vrarmik/SkeletonUtils.js new file mode 100644 index 0000000..fd82960 --- /dev/null +++ b/vrarmik/SkeletonUtils.js @@ -0,0 +1,169 @@ +/** +* Takes in a rootBone and recursively traverses the bone heirarchy, +* setting each bone's +Z axis to face it's child bones. The IK system follows this +* convention, so this step is necessary to update the bindings of a skinned mesh. +* +* Must rebind the model to it's skeleton after this function. +* +* @param {THREE.Bone} rootBone +* @param {Object} context - options and buffer for stateful bone calculations +* context.exclude: [ boneNames to exclude ] +* context.preRotations: { boneName: THREE.Quaternion, ... } +*/ + +function fixSkeletonZForward(rootBone, context) { + context = context || {}; + precalculateZForwards(rootBone, context); + if (context.exclude) { + var bones = [rootBone]; + rootBone.traverse((b) => bones.push(b)); + bones.forEach((b) => { + if (~context.exclude.indexOf(b.id) || ~context.exclude.indexOf(b.name)) { + delete context.averagedDirs[b.id]; + } + }); + } + return setZForward(rootBone, context); +} + +const RESETQUAT = new THREE.Quaternion(); +const Y_AXIS = new THREE.Vector3(0,1,0); + +/** +* Takes in a rootBone and recursively traverses the bone heirarchy, +* setting each bone's +Z axis to face it's child bones. The IK system follows this +* convention, so this step is necessary to update the bindings of a skinned mesh. +* +* Must rebind the model to it's skeleton after this function. +* +* @param {THREE.BONE} rootBone +*/ + +function precalculateZForwards(rootBone, context) { + context = context || rootBone; + context.worldPos = context.worldPos || {}; + context.averagedDirs = context.averagedDirs || {}; + context.preRotations = context.preRotations || {}; + getOriginalWorldPositions(rootBone, context.worldPos) + calculateAverages(rootBone, context.worldPos, context.averagedDirs); + return context; +} + +function setZForward(rootBone, context) { + if (!context || !context.worldPos) { + context = context || {}; + precalculateZForwards(rootBone, context); + } + updateTransformations(rootBone, context.worldPos, context.averagedDirs, context.preRotations); + return context; +} + +function calculateAverages(parentBone, worldPos, averagedDirs) { + var averagedDir = new THREE.Vector3(); + parentBone.children.forEach((childBone) => { + //average the child bone world pos + var childBonePosWorld = worldPos[childBone.id][0]; + averagedDir.add(childBonePosWorld); + }); + + averagedDir.multiplyScalar(1/(parentBone.children.length)); + averagedDirs[parentBone.id] = averagedDir; + + parentBone.children.forEach((childBone) => { + calculateAverages(childBone, worldPos, averagedDirs); + }); +} + +function updateTransformations(parentBone, worldPos, averagedDirs, preRotations) { + + var averagedDir = averagedDirs[parentBone.id]; + if (averagedDir) { + + //set quaternion + parentBone.quaternion.copy(RESETQUAT); + // parentBone.quaternion.premultiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI*2)); + parentBone.updateMatrixWorld(); + + //get the child bone position in local coordinates + // var childBoneDir = parentBone.worldToLocal(averagedDir.clone()).normalize(); + + //set direction to face child + // setQuaternionFromDirection(childBoneDir, Y_AXIS, parentBone.quaternion) + // console.log('new quaternion', parentBone.quaternion.toArray().join(',')); + } + var preRot = preRotations[parentBone.id] || preRotations[parentBone.name]; + if (preRot) parentBone.quaternion.multiply(preRot); + // parentBone.quaternion.multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI)); + parentBone.updateMatrixWorld(); + + //set child bone position relative to the new parent matrix. + parentBone.children.forEach((childBone) => { + var childBonePosWorld = worldPos[childBone.id][0].clone(); + parentBone.worldToLocal(childBonePosWorld); + childBone.position.copy(childBonePosWorld); + }); + + parentBone.children.forEach((childBone) => { + updateTransformations(childBone, worldPos, averagedDirs, preRotations); + }); +} + +//borrowing this from utils.js , not sure how to import it +const t1 = new THREE.Vector3(); +const t2 = new THREE.Vector3(); +const t3 = new THREE.Vector3(); +const m1 = new THREE.Matrix4(); +function setQuaternionFromDirection(direction, up, target) { + const x = t1; + const y = t2; + const z = t3; + const m = m1; + const el = m1.elements; + + z.copy(direction); + x.crossVectors(up, z); + + if (x.lengthSq() === 0) { + // parallel + if (Math.abs(up.z) === 1) { + z.x += 0.0001; + } else { + z.z += 0.0001; + } + z.normalize(); + x.crossVectors(up, z); + } + + x.normalize(); + y.crossVectors(z, x); + + el[ 0 ] = x.x; el[ 4 ] = y.x; el[ 8 ] = z.x; + el[ 1 ] = x.y; el[ 5 ] = y.y; el[ 9 ] = z.y; + el[ 2 ] = x.z; el[ 6 ] = y.z; el[ 10 ] = z.z; + + return target.setFromRotationMatrix(m); +} + +function getOriginalWorldPositions(rootBone, worldPos) { + var rootBoneWorldPos = rootBone.getWorldPosition(new THREE.Vector3()) + worldPos[rootBone.id] = [rootBoneWorldPos]; + rootBone.children.forEach((child) => { + getOriginalWorldPositions(child, worldPos) + }) +} + +function _worldToLocalDirection(direction, parent) { + const inverseParent = new THREE.Matrix4().getInverse(parent.matrixWorld); + direction.transformDirection(inverseParent); + return direction; +} + +function _localToWorldDirection(direction, parent) { + const parentMat = parent.matrixWorld; + direction.transformDirection(parentMat); + return direction; +} + +export { + fixSkeletonZForward, setQuaternionFromDirection +}; \ No newline at end of file diff --git a/vrarmik/Unity.js b/vrarmik/Unity.js new file mode 100644 index 0000000..9039b53 --- /dev/null +++ b/vrarmik/Unity.js @@ -0,0 +1,28 @@ +const localVector = new THREE.Vector3(); +const localVector2 = new THREE.Vector3(); +const Helpers = { + getWorldPosition(o, v) { + return v.setFromMatrixPosition(o.matrixWorld); + }, + getWorldQuaternion(o, q) { + o.matrixWorld.decompose(localVector, q, localVector2); + return q; + }, + getWorldScale(o, v) { + return v.setFromMatrixScale(o.matrixWorld); + }, + updateMatrix(o) { + o.matrix.compose(o.position, o.quaternion, o.scale); + }, + updateMatrixWorld(o) { + o.matrixWorld.multiplyMatrices(o.parent.matrixWorld, o.matrix); + }, + updateMatrixMatrixWorld(o) { + o.matrix.compose(o.position, o.quaternion, o.scale); + o.matrixWorld.multiplyMatrices(o.parent.matrixWorld, o.matrix); + }, +}; + +export { + Helpers, +}; diff --git a/vrarmik/VRArmIK.js b/vrarmik/VRArmIK.js new file mode 100644 index 0000000..452fe53 --- /dev/null +++ b/vrarmik/VRArmIK.js @@ -0,0 +1,130 @@ +import {Helpers} from './Unity.js'; + +const zeroVector = new THREE.Vector3(); +const forwardVector = new THREE.Vector3(0, 0, 1); +const leftRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI/2); +const rightRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), -Math.PI/2); +const bankLeftRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI/2); +const bankRightRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), -Math.PI/2); +const z180Quaternion = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI); + +const localVector = new THREE.Vector3(); +const localVector2 = new THREE.Vector3(); +const localVector3 = new THREE.Vector3(); +const localVector4 = new THREE.Vector3(); +const localVector5 = new THREE.Vector3(); +const localVector6 = new THREE.Vector3(); +const localQuaternion = new THREE.Quaternion(); +const localQuaternion2 = new THREE.Quaternion(); +const localQuaternion3 = new THREE.Quaternion(); +const localEuler = new THREE.Euler(); +const localMatrix = new THREE.Matrix4(); + + class VRArmIK + { + constructor(arm, shoulder, shoulderPoser, target, left) { + this.arm = arm; + this.shoulder = shoulder; + this.shoulderPoser = shoulderPoser; + this.target = target; + this.left = left; + + this.upperArmLength = 0; + this.lowerArmLength = 0; + this.armLength = 0; + } + + Start() + { + this.upperArmLength = Helpers.getWorldPosition(this.arm.lowerArm, localVector).distanceTo(Helpers.getWorldPosition(this.arm.upperArm, localVector2)); + this.lowerArmLength = Helpers.getWorldPosition(this.arm.hand, localVector).distanceTo(Helpers.getWorldPosition(this.arm.lowerArm, localVector2)); + this.armLength = this.upperArmLength + this.lowerArmLength; + } + + Update() + { + Helpers.updateMatrixWorld(this.arm.transform); + Helpers.updateMatrixWorld(this.arm.upperArm); + + const upperArmPosition = Helpers.getWorldPosition(this.arm.upperArm, localVector); + const handRotation = this.target.quaternion; + const handPosition = localVector2.copy(this.target.position); + + const shoulderRotation = Helpers.getWorldQuaternion(this.shoulder.transform, localQuaternion); + const shoulderRotationInverse = localQuaternion2.copy(shoulderRotation).inverse(); + + const hypotenuseDistance = this.upperArmLength; + const directDistance = upperArmPosition.distanceTo(handPosition) / 2; + const offsetDistance = hypotenuseDistance > directDistance ? Math.sqrt(hypotenuseDistance*hypotenuseDistance - directDistance*directDistance) : 0; + const offsetDirection = localVector3.copy(handPosition).sub(upperArmPosition) + .normalize() + .cross(localVector4.set(-1, 0, 0).applyQuaternion(shoulderRotation)); + + const targetEuler = localEuler.setFromQuaternion( + localQuaternion3 + .multiplyQuaternions(handRotation, shoulderRotationInverse) + .premultiply(z180Quaternion), + 'XYZ' + ); + // const targetDirection = new Vector3(0, 0, 1).applyQuaternion(targetLocalRotation); + if (this.left) { + const yFactor = Math.min(Math.max((targetEuler.y+Math.PI*0.1)/(Math.PI/2), 0), 1); + // const zFactor = Math.min(Math.max((-targetDirection.x + 0.5)/0.25, 0), 1) + // const xFactor = Math.min(Math.max((targetDirection.y-0.8)/0.2, 0), 1); + // yFactor *= 1-xFactor; + // const factor = Math.min(yFactor, 1-xFactor);//Math.min(yFactor, 1-xFactor); + targetEuler.z = Math.min(Math.max(targetEuler.z, -Math.PI/2), 0); + targetEuler.z = (targetEuler.z * (1 - yFactor)) + (-Math.PI/2 * yFactor); + // targetEuler.z *= 1 - xFactor; + // targetEuler.z *= 1 - zFactor; + } else { + const yFactor = Math.min(Math.max((-targetEuler.y-Math.PI*0.1)/(Math.PI/2), 0), 1); + // const zFactor = Math.min(Math.max((-targetDirection.x + 0.5)/0.25, 0), 1) + // const xFactor = Math.min(Math.max((targetDirection.y-0.8)/0.2, 0), 1); + // yFactor *= 1-xFactor; + // const factor = Math.min(yFactor, 1-xFactor);//Math.min(yFactor, 1-xFactor); + targetEuler.z = Math.min(Math.max(targetEuler.z, 0), Math.PI/2); + targetEuler.z = (targetEuler.z * (1 - yFactor)) + (Math.PI/2 * yFactor); + // targetEuler.z *= 1 - xFactor; + // targetEuler.z *= 1 - zFactor; + } + offsetDirection + .applyQuaternion(shoulderRotationInverse) + .applyAxisAngle(forwardVector, targetEuler.z) + .applyQuaternion(shoulderRotation); + + const elbowPosition = localVector4.copy(upperArmPosition).add(handPosition).divideScalar(2) + .add(localVector5.copy(offsetDirection).multiplyScalar(offsetDistance)); + const upVector = localVector5.set(this.left ? -1 : 1, 0, 0).applyQuaternion(shoulderRotation); + this.arm.upperArm.quaternion.setFromRotationMatrix( + localMatrix.lookAt( + zeroVector, + localVector6.copy(elbowPosition).sub(upperArmPosition), + upVector + ) + ) + .multiply(this.left ? rightRotation : leftRotation) + .premultiply(Helpers.getWorldQuaternion(this.arm.upperArm.parent, localQuaternion3).inverse()); + Helpers.updateMatrixMatrixWorld(this.arm.upperArm); + + // this.arm.lowerArm.position = elbowPosition; + this.arm.lowerArm.quaternion.setFromRotationMatrix( + localMatrix.lookAt( + zeroVector, + localVector6.copy(handPosition).sub(elbowPosition), + upVector + ) + ) + .multiply(this.left ? rightRotation : leftRotation) + .premultiply(Helpers.getWorldQuaternion(this.arm.lowerArm.parent, localQuaternion3).inverse()); + Helpers.updateMatrixMatrixWorld(this.arm.lowerArm); + + // this.arm.hand.position = handPosition; + this.arm.hand.quaternion.copy(this.target.quaternion) + .multiply(this.left ? bankRightRotation : bankLeftRotation) + .premultiply(Helpers.getWorldQuaternion(this.arm.hand.parent, localQuaternion3).inverse()); + Helpers.updateMatrixMatrixWorld(this.arm.hand); + } + } + +export default VRArmIK; diff --git a/vrarmik/VRTrackingReferences.js b/vrarmik/VRTrackingReferences.js new file mode 100644 index 0000000..c58358e --- /dev/null +++ b/vrarmik/VRTrackingReferences.js @@ -0,0 +1,22 @@ +class VRTrackingReferences { + constructor() { + /* this.leftController = new Transform(); + this.rightController = new Transform(); + this.hmd = new Transform(); */ + /* this.hmd.onchange = () => { + console.log('change 1', new Error().stack); + }; */ + this.head = new THREE.Object3D(); + this.leftHand = new THREE.Object3D(); + this.leftHand.pointer = 0; + this.leftHand.grip = 0; + this.rightHand = new THREE.Object3D(); + this.rightHand.pointer = 0; + this.rightHand.grip = 0; + /* this.head.onchange = () => { + console.log('change 2', new Error().stack); + }; */ + } +} + +export default VRTrackingReferences; diff --git a/vrarmik/audio-volume-worklet.js b/vrarmik/audio-volume-worklet.js new file mode 100644 index 0000000..3e1f4ca --- /dev/null +++ b/vrarmik/audio-volume-worklet.js @@ -0,0 +1,56 @@ +const numTicks = 1; + +let tick = 0; +let sampleSum = 0; +let numSamples = 0; +let muted = true; + +class VolumeProcessor extends AudioWorkletProcessor { + constructor() { + super(); + + this.port.addEventListener('message', e => { + const data = JSON.parse(e.data); + const {method} = data; + if (method === 'muted') { + muted = data.muted; + } + }); + this.port.start(); + } + process(inputs, outputs) { + const channels = inputs[0]; + // const output = outputs[0]; + + // for (let i = 0; i < channels.length; i++) { + const i = 0; + const samples = channels[i]; + for (let j = 0; j < samples.length; j++) { + sampleSum += Math.abs(samples[j]); + } + numSamples += samples.length; + // } + + if (++tick >= numTicks) { + this.port.postMessage(sampleSum / numSamples); + + tick = 0; + sampleSum = 0; + numSamples = 0; + } + + if (!muted) { + for (let i = 0; i < outputs.length; i++) { + const input = inputs[i]; + const output = outputs[i]; + + for (let channel = 0; channel < output.length; channel++) { + output[channel].set(input[channel]); + } + } + } + + return true; + } +} +registerProcessor('volume-processor', VolumeProcessor); \ No newline at end of file diff --git a/vrarmik/avatars.js b/vrarmik/avatars.js new file mode 100644 index 0000000..b727b3c --- /dev/null +++ b/vrarmik/avatars.js @@ -0,0 +1,967 @@ +import './three-vrm.js'; +import {fixSkeletonZForward} from './SkeletonUtils.js'; +import PoseManager from './PoseManager.js'; +import ShoulderTransforms from './ShoulderTransforms.js'; +import LegsManager from './LegsManager.js'; +import MicrophoneWorker from './microphone-worker.js'; + +const zeroVector = new THREE.Vector3(); +const upRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI/2); +const leftRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI*0.8); +const rightRotation = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), -Math.PI*0.8); +const z180Quaternion = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI); + +const localVector = new THREE.Vector3(); +const localVector2 = new THREE.Vector3(); +const localVector3 = new THREE.Vector3(); +const localVector4 = new THREE.Vector3(); +const localVector5 = new THREE.Vector3(); +const localVector6 = new THREE.Vector3(); +const localQuaternion = new THREE.Quaternion(); +const localQuaternion2 = new THREE.Quaternion(); +const localMatrix = new THREE.Matrix4(); + +const _localizeMatrixWorld = bone => { + bone.matrix.copy(bone.matrixWorld); + if (bone.parent) { + bone.matrix.premultiply(new THREE.Matrix4().getInverse(bone.parent.matrixWorld)); + } + bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); + + for (let i = 0; i < bone.children.length; i++) { + _localizeMatrixWorld(bone.children[i]); + } +}; +const _findBoneDeep = (bones, boneName) => { + for (let i = 0; i < bones.length; i++) { + const bone = bones[i]; + if (bone.name === boneName) { + return bone; + } else { + const deepBone = _findBoneDeep(bone.children, boneName); + if (deepBone) { + return deepBone; + } + } + } + return null; +}; +const _copySkeleton = (src, dst) => { + for (let i = 0; i < src.bones.length; i++) { + const srcBone = src.bones[i]; + const dstBone = _findBoneDeep(dst.bones, srcBone.name); + dstBone.matrixWorld.copy(srcBone.matrixWorld); + } + + const armature = dst.bones[0].parent; + _localizeMatrixWorld(armature); + + dst.calculateInverses(); +}; + +class Avatar { + constructor(object, options = {}) { + const model = object.isMesh ? object : object.scene; + this.model = model; + this.options = options; + + model.updateMatrixWorld(true); + const skinnedMeshes = []; + model.traverse(o => { + if (o.isSkinnedMesh) { + skinnedMeshes.push(o); + } + }); + skinnedMeshes.sort((a, b) => b.skeleton.bones.length - a.skeleton.bones.length); + this.skinnedMeshes = skinnedMeshes; + + const skeletonSkinnedMesh = skinnedMeshes.find(o => o.skeleton.bones[0].parent) || null; + const skeleton = skeletonSkinnedMesh && skeletonSkinnedMesh.skeleton; + /* if (skeleton) { + skeletonSkinnedMesh.bind(skeleton); + } */ + const poseSkeletonSkinnedMesh = skeleton ? skinnedMeshes.find(o => o.skeleton !== skeleton && o.skeleton.bones.length >= skeleton.bones.length) : null; + const poseSkeleton = poseSkeletonSkinnedMesh && poseSkeletonSkinnedMesh.skeleton; + if (poseSkeleton) { + _copySkeleton(poseSkeleton, skeleton); + poseSkeletonSkinnedMesh.bind(skeleton); + } + + const _getTailBones = skeleton => { + const result = []; + const _recurse = bones => { + for (let i = 0; i < bones.length; i++) { + const bone = bones[i]; + if (bone.children.length === 0) { + if (!result.includes(bone)) { + result.push(bone); + } + } else { + _recurse(bone.children); + } + } + }; + _recurse(skeleton.bones); + return result; + }; + const tailBones = _getTailBones(skeleton); + // const tailBones = skeleton.bones.filter(bone => bone.children.length === 0); + const _findClosestParentBone = (bone, pred) => { + for (; bone; bone = bone.parent) { + if (pred(bone)) { + return bone; + } + } + return null; + }; + const _findFurthestParentBone = (bone, pred) => { + let result = null; + for (; bone; bone = bone.parent) { + if (pred(bone)) { + result = bone; + } + } + return result; + }; + const _distanceToParentBone = (bone, parentBone) => { + for (let i = 0; bone; bone = bone.parent, i++) { + if (bone === parentBone) { + return i; + } + } + return Infinity; + }; + const _findClosestChildBone = (bone, pred) => { + const _recurse = bone => { + if (pred(bone)) { + return bone; + } else { + for (let i = 0; i < bone.children.length; i++) { + const result = _recurse(bone.children[i]); + if (result) { + return result; + } + } + return null; + } + } + return _recurse(bone); + }; + const _traverseChild = (bone, distance) => { + if (distance <= 0) { + return bone; + } else { + for (let i = 0; i < bone.children.length; i++) { + const child = bone.children[i]; + const subchild = _traverseChild(child, distance - 1); + if (subchild !== null) { + return subchild; + } + } + return null; + } + }; + const _countCharacters = (name, regex) => { + let result = 0; + for (let i = 0; i < name.length; i++) { + if (regex.test(name[i])) { + result++; + } + } + return result; + }; + const _findHead = () => { + const headBones = tailBones.map(tailBone => { + const headBone = _findFurthestParentBone(tailBone, bone => /head/i.test(bone.name)); + if (headBone) { + return headBone; + } else { + return null; + } + }).filter(bone => bone); + const headBone = headBones.length > 0 ? headBones[0] : null; + if (headBone) { + return headBone; + } else { + return null; + } + }; + const _findEye = left => { + const regexp = left ? /l/i : /r/i; + const eyeBones = tailBones.map(tailBone => { + const eyeBone = _findFurthestParentBone(tailBone, bone => /eye/i.test(bone.name) && regexp.test(bone.name.replace(/eye/gi, ''))); + if (eyeBone) { + return eyeBone; + } else { + return null; + } + }).filter(spec => spec).sort((a, b) => { + const aName = a.name.replace(/shoulder/gi, ''); + const aLeftBalance = _countCharacters(aName, /l/i) - _countCharacters(aName, /r/i); + const bName = b.name.replace(/shoulder/gi, ''); + const bLeftBalance = _countCharacters(bName, /l/i) - _countCharacters(bName, /r/i); + if (!left) { + return aLeftBalance - bLeftBalance; + } else { + return bLeftBalance - aLeftBalance; + } + }); + const eyeBone = eyeBones.length > 0 ? eyeBones[0] : null; + if (eyeBone) { + return eyeBone; + } else { + return null; + } + }; + const _findHips = () => { + return skeleton.bones.find(bone => /hip/i.test(bone.name)); + }; + const _findSpine = (chest, hips) => { + for (let bone = chest; bone; bone = bone.parent) { + if (bone.parent === hips) { + return bone; + } + } + return null; + }; + const _findShoulder = left => { + const regexp = left ? /l/i : /r/i; + const shoulderBones = tailBones.map(tailBone => { + const shoulderBone = _findClosestParentBone(tailBone, bone => /shoulder/i.test(bone.name) && regexp.test(bone.name.replace(/shoulder/gi, ''))); + if (shoulderBone) { + const distance = _distanceToParentBone(tailBone, shoulderBone); + if (distance >= 3) { + return { + bone: shoulderBone, + distance, + }; + } else { + return null; + } + } else { + return null; + } + }).filter(spec => spec).sort((a, b) => { + const diff = b.distance - a.distance; + if (diff !== 0) { + return diff; + } else { + const aName = a.bone.name.replace(/shoulder/gi, ''); + const aLeftBalance = _countCharacters(aName, /l/i) - _countCharacters(aName, /r/i); + const bName = b.bone.name.replace(/shoulder/gi, ''); + const bLeftBalance = _countCharacters(bName, /l/i) - _countCharacters(bName, /r/i); + if (!left) { + return aLeftBalance - bLeftBalance; + } else { + return bLeftBalance - aLeftBalance; + } + } + }); + const shoulderBone = shoulderBones.length > 0 ? shoulderBones[0].bone : null; + if (shoulderBone) { + return shoulderBone; + } else { + return null; + } + }; + const _findHand = shoulderBone => _findClosestChildBone(shoulderBone, bone => /hand|wrist/i.test(bone.name)); + const _findFoot = left => { + const regexp = left ? /l/i : /r/i; + const legBones = tailBones.map(tailBone => { + const footBone = _findFurthestParentBone(tailBone, bone => /foot|ankle/i.test(bone.name) && regexp.test(bone.name.replace(/foot|ankle/gi, ''))); + if (footBone) { + const legBone = _findFurthestParentBone(footBone, bone => /leg|thigh/i.test(bone.name) && regexp.test(bone.name.replace(/leg|thigh/gi, ''))); + if (legBone) { + const distance = _distanceToParentBone(footBone, legBone); + if (distance >= 2) { + return { + footBone, + distance, + }; + } else { + return null; + } + } else { + return null; + } + } else { + return null; + } + }).filter(spec => spec).sort((a, b) => { + const diff = b.distance - a.distance; + if (diff !== 0) { + return diff; + } else { + const aName = a.footBone.name.replace(/foot|ankle/gi, ''); + const aLeftBalance = _countCharacters(aName, /l/i) - _countCharacters(aName, /r/i); + const bName = b.footBone.name.replace(/foot|ankle/gi, ''); + const bLeftBalance = _countCharacters(bName, /l/i) - _countCharacters(bName, /r/i); + if (!left) { + return aLeftBalance - bLeftBalance; + } else { + return bLeftBalance - aLeftBalance; + } + } + }); + const footBone = legBones.length > 0 ? legBones[0].footBone : null; + if (footBone) { + return footBone; + } else { + return null; + } + }; + const Eye_L = _findEye(true); + const Eye_R = _findEye(false); + const Head = _findHead(); + const Neck = Head.parent; + const Chest = Neck.parent; + const Hips = _findHips(); + const Spine = _findSpine(Chest, Hips); + const Left_shoulder = _findShoulder(true); + const Left_wrist = _findHand(Left_shoulder); + const Left_elbow = Left_wrist.parent; + const Left_arm = Left_elbow.parent; + const Right_shoulder = _findShoulder(false); + const Right_wrist = _findHand(Right_shoulder); + const Right_elbow = Right_wrist.parent; + const Right_arm = Right_elbow.parent; + const Left_ankle = _findFoot(true); + const Left_knee = Left_ankle.parent; + const Left_leg = Left_knee.parent; + const Right_ankle = _findFoot(false); + const Right_knee = Right_ankle.parent; + const Right_leg = Right_knee.parent; + const modelBones = { + Hips, + Spine, + Chest, + Neck, + Head, + /* Eye_L, + Eye_R, */ + + Left_shoulder, + Left_arm, + Left_elbow, + Left_wrist, + Left_leg, + Left_knee, + Left_ankle, + + Right_shoulder, + Right_arm, + Right_elbow, + Right_wrist, + Right_leg, + Right_knee, + Right_ankle, + }; + this.modelBones = modelBones; + /* for (const k in modelBones) { + if (!modelBones[k]) { + console.warn('missing bone', k); + } + } */ + + const _findArmature = bone => { + for (; bone; bone = bone.parent) { + if (!bone.isBone) { + return bone; + } + } + return null; + }; + const armature = _findArmature(Hips); + + const _getEyePosition = () => { + if (Eye_L && Eye_R) { + return Eye_L.getWorldPosition(new THREE.Vector3()) + .add(Eye_R.getWorldPosition(new THREE.Vector3())) + .divideScalar(2); + } else { + const neckToHeadDiff = Head.getWorldPosition(new THREE.Vector3()).sub(Neck.getWorldPosition(new THREE.Vector3())); + if (neckToHeadDiff.z < 0) { + neckToHeadDiff.z *= -1; + } + return Head.getWorldPosition(new THREE.Vector3()).add(neckToHeadDiff); + } + }; + // const eyeDirection = _getEyePosition().sub(Head.getWorldPosition(new Vector3())); + const leftArmDirection = Left_wrist.getWorldPosition(new THREE.Vector3()).sub(Head.getWorldPosition(new THREE.Vector3())); + const flipZ = leftArmDirection.x < 0;//eyeDirection.z < 0; + const armatureDirection = new THREE.Vector3(0, 1, 0).applyQuaternion(armature.quaternion); + const flipY = armatureDirection.z < -0.5; + const legDirection = new THREE.Vector3(0, 0, -1).applyQuaternion(Left_leg.getWorldQuaternion(new THREE.Quaternion()).premultiply(armature.quaternion.clone().inverse())); + const flipLeg = legDirection.y < 0.5; + console.log('flip', flipZ, flipY, flipLeg); + this.flipZ = flipZ; + this.flipY = flipY; + this.flipLeg = flipLeg; + + const armatureQuaternion = armature.quaternion.clone(); + const armatureMatrixInverse = new THREE.Matrix4().getInverse(armature.matrixWorld); + armature.position.set(0, 0, 0); + armature.quaternion.set(0, 0, 0, 1); + armature.scale.set(1, 1, 1); + armature.updateMatrix(); + + Head.traverse(o => { + o.savedPosition = o.position.clone(); + o.savedMatrixWorld = o.matrixWorld.clone(); + }); + + const allHairBones = []; + const _recurseAllHairBones = bones => { + for (let i = 0; i < bones.length; i++) { + const bone = bones[i]; + if (/hair/i.test(bone.name)) { + allHairBones.push(bone); + } + _recurseAllHairBones(bone.children); + } + }; + _recurseAllHairBones(skeleton.bones); + const hairBones = tailBones.filter(bone => /hair/i.test(bone.name)).map(bone => { + for (; bone; bone = bone.parent) { + if (bone.parent === Head) { + return bone; + } + } + return null; + }).filter(bone => bone); + this.allHairBones = allHairBones; + this.hairBones = hairBones; + + this.springBoneManager = null; + if (options.hair) { + new Promise((accept, reject) => { + if (!object.parser) { + object.parser = { + json: { + extensions: {}, + }, + }; + } + if (!object.parser.json.extensions) { + object.parser.json.extensions = {}; + } + if (!object.parser.json.extensions.VRM) { + object.parser.json.extensions.VRM = { + secondaryAnimation: { + boneGroups: this.hairBones.map(hairBone => { + const boneIndices = []; + const _recurse = bone => { + boneIndices.push(this.allHairBones.indexOf(bone)); + if (bone.children.length > 0) { + _recurse(bone.children[0]); + } + }; + _recurse(hairBone); + return { + comment: hairBone.name, + stiffiness: 0.5, + gravityPower: 0.2, + gravityDir: { + x: 0, + y: -1, + z: 0 + }, + dragForce: 0.3, + center: -1, + hitRadius: 0.02, + bones: boneIndices, + colliderGroups: [], + }; + }), + }, + }; + object.parser.getDependency = async (type, nodeIndex) => { + if (type === 'node') { + return this.allHairBones[nodeIndex]; + } else { + throw new Error('unsupported type'); + } + }; + } + + new THREE.VRMSpringBoneImporter().import(object) + .then(springBoneManager => { + this.springBoneManager = springBoneManager; + }); + }); + } + + const _findFinger = (r, left) => { + const fingerTipBone = tailBones + .filter(bone => r.test(bone.name) && _findClosestParentBone(bone, bone => bone === modelBones.Left_wrist || bone === modelBones.Right_wrist)) + .sort((a, b) => { + const aName = a.name.replace(r, ''); + const aLeftBalance = _countCharacters(aName, /l/i) - _countCharacters(aName, /r/i); + const bName = b.name.replace(r, ''); + const bLeftBalance = _countCharacters(bName, /l/i) - _countCharacters(bName, /r/i); + if (!left) { + return aLeftBalance - bLeftBalance; + } else { + return bLeftBalance - aLeftBalance; + } + }); + const fingerRootBone = fingerTipBone.length > 0 ? _findFurthestParentBone(fingerTipBone[0], bone => r.test(bone.name)) : null; + return fingerRootBone; + }; + const fingerBones = { + left: { + thumb: _findFinger(/thumb/gi, true), + index: _findFinger(/index/gi, true), + middle: _findFinger(/middle/gi, true), + ring: _findFinger(/ring/gi, true), + little: _findFinger(/little/gi, true) || _findFinger(/pinky/gi, true), + }, + right: { + thumb: _findFinger(/thumb/gi, false), + index: _findFinger(/index/gi, false), + middle: _findFinger(/middle/gi, false), + ring: _findFinger(/ring/gi, false), + little: _findFinger(/little/gi, false) || _findFinger(/pinky/gi, false), + }, + }; + this.fingerBones = fingerBones; + + const preRotations = {}; + const _ensurePrerotation = k => { + const boneName = modelBones[k].name; + if (!preRotations[boneName]) { + preRotations[boneName] = new THREE.Quaternion(); + } + return preRotations[boneName]; + }; + if (flipY) { + ['Hips'].forEach(k => { + _ensurePrerotation(k).premultiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), -Math.PI/2)); + }); + } + if (flipZ) { + ['Hips'].forEach(k => { + _ensurePrerotation(k).premultiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI)); + }); + } + if (flipLeg) { + ['Left_leg', 'Right_leg'].forEach(k => { + _ensurePrerotation(k).premultiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI/2)); + }); + } + + const qrArm = flipZ ? Left_arm : Right_arm; + const qrElbow = flipZ ? Left_elbow : Right_elbow; + const qrWrist = flipZ ? Left_wrist : Right_wrist; + const qr = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), -Math.PI/2) + .premultiply( + new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt( + new THREE.Vector3(0, 0, 0), + qrElbow.getWorldPosition(new THREE.Vector3()).applyMatrix4(armatureMatrixInverse) + .sub(qrArm.getWorldPosition(new THREE.Vector3()).applyMatrix4(armatureMatrixInverse)) + .applyQuaternion(armatureQuaternion), + new THREE.Vector3(0, 1, 0), + )) + ); + const qr2 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), -Math.PI/2) + .premultiply( + new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt( + new THREE.Vector3(0, 0, 0), + qrWrist.getWorldPosition(new THREE.Vector3()).applyMatrix4(armatureMatrixInverse) + .sub(qrElbow.getWorldPosition(new THREE.Vector3()).applyMatrix4(armatureMatrixInverse)) + .applyQuaternion(armatureQuaternion), + new THREE.Vector3(0, 1, 0), + )) + ); + const qlArm = flipZ ? Right_arm : Left_arm; + const qlElbow = flipZ ? Right_elbow : Left_elbow; + const qlWrist = flipZ ? Right_wrist : Left_wrist; + const ql = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI/2) + .premultiply( + new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt( + new THREE.Vector3(0, 0, 0), + qlElbow.getWorldPosition(new THREE.Vector3()).applyMatrix4(armatureMatrixInverse) + .sub(qlArm.getWorldPosition(new THREE.Vector3()).applyMatrix4(armatureMatrixInverse)) + .applyQuaternion(armatureQuaternion), + new THREE.Vector3(0, 1, 0), + )) + ); + const ql2 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI/2) + .premultiply( + new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt( + new THREE.Vector3(0, 0, 0), + qlWrist.getWorldPosition(new THREE.Vector3()).applyMatrix4(armatureMatrixInverse) + .sub(qlElbow.getWorldPosition(new THREE.Vector3()).applyMatrix4(armatureMatrixInverse)) + .applyQuaternion(armatureQuaternion), + new THREE.Vector3(0, 1, 0), + )) + ); + + _ensurePrerotation('Right_arm') + .multiply(qr.clone().inverse()); + _ensurePrerotation('Right_elbow') + .multiply(qr.clone()) + .premultiply(qr2.clone().inverse()); + _ensurePrerotation('Left_arm') + .multiply(ql.clone().inverse()); + _ensurePrerotation('Left_elbow') + .multiply(ql.clone()) + .premultiply(ql2.clone().inverse()); + + _ensurePrerotation('Left_leg').premultiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), -Math.PI/2)); + _ensurePrerotation('Right_leg').premultiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), -Math.PI/2)); + + for (const k in preRotations) { + preRotations[k].inverse(); + } + fixSkeletonZForward(armature.children[0], { + preRotations, + }); + model.traverse(o => { + if (o.isSkinnedMesh) { + o.bind((o.skeleton.bones.length === skeleton.bones.length && o.skeleton.bones.every((bone, i) => bone === skeleton.bones[i])) ? skeleton : o.skeleton); + } + }); + if (flipY) { + ['Hips'].forEach(name => { + modelBones[name].quaternion.premultiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), -Math.PI/2)); + }); + } + if (!flipZ) { + /* ['Left_arm', 'Right_arm'].forEach((name, i) => { + const bone = modelBones[name]; + if (bone) { + bone.quaternion.premultiply(new Quaternion().setFromAxisAngle(new Vector3(0, 0, 1), (i === 0 ? 1 : -1) * Math.PI*0.25)); + } + }); */ + } else { + ['Hips'].forEach(name => { + modelBones[name].quaternion.premultiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI)); + }); + } + modelBones.Right_arm.quaternion.premultiply(qr.clone().inverse()); + modelBones.Right_elbow.quaternion + .premultiply(qr) + .premultiply(qr2.clone().inverse()); + modelBones.Left_arm.quaternion.premultiply(ql.clone().inverse()); + modelBones.Left_elbow.quaternion + .premultiply(ql) + .premultiply(ql2.clone().inverse()); + model.updateMatrixWorld(true); + + for (let i = 0; i < skeleton.bones.length; i++) { + const bone = skeleton.bones[i]; + if (!bone.initialQuaternion) { + bone.initialQuaternion = bone.quaternion.clone(); + } + } + + const _averagePoint = points => { + const result = new THREE.Vector3(); + for (let i = 0; i < points.length; i++) { + result.add(points[i]); + } + result.divideScalar(points.length); + return result; + }; + const eyePosition = _getEyePosition(); + + this.poseManager = new PoseManager(this); + this.shoulderTransforms = new ShoulderTransforms(this); + this.legsManager = new LegsManager(this); + + const _getOffset = (bone, parent = bone.parent) => bone.getWorldPosition(new THREE.Vector3()).sub(parent.getWorldPosition(new THREE.Vector3())); + this.initializeBonePositions({ + spine: _getOffset(modelBones.Spine), + chest: _getOffset(modelBones.Chest, modelBones.Spine), + neck: _getOffset(modelBones.Neck), + head: _getOffset(modelBones.Head), + eyes: eyePosition.clone().sub(Head.getWorldPosition(new THREE.Vector3())), + + leftShoulder: _getOffset(modelBones.Right_shoulder), + leftUpperArm: _getOffset(modelBones.Right_arm), + leftLowerArm: _getOffset(modelBones.Right_elbow), + leftHand: _getOffset(modelBones.Right_wrist), + + rightShoulder: _getOffset(modelBones.Left_shoulder), + rightUpperArm: _getOffset(modelBones.Left_arm), + rightLowerArm: _getOffset(modelBones.Left_elbow), + rightHand: _getOffset(modelBones.Left_wrist), + + leftUpperLeg: _getOffset(modelBones.Right_leg), + leftLowerLeg: _getOffset(modelBones.Right_knee), + leftFoot: _getOffset(modelBones.Right_ankle), + + rightUpperLeg: _getOffset(modelBones.Left_leg), + rightLowerLeg: _getOffset(modelBones.Left_knee), + rightFoot: _getOffset(modelBones.Left_ankle), + }); + + this.height = eyePosition.sub(_averagePoint([modelBones.Left_ankle.getWorldPosition(new THREE.Vector3()), modelBones.Right_ankle.getWorldPosition(new THREE.Vector3())])).y; + this.shoulderWidth = modelBones.Left_arm.getWorldPosition(new THREE.Vector3()).distanceTo(modelBones.Right_arm.getWorldPosition(new THREE.Vector3())); + this.leftArmLength = this.shoulderTransforms.leftArm.armLength; + this.rightArmLength = this.shoulderTransforms.rightArm.armLength; + + this.inputs = { + hmd: this.poseManager.vrTransforms.head, + leftGamepad: this.poseManager.vrTransforms.leftHand, + rightGamepad: this.poseManager.vrTransforms.rightHand, + }; + this.inputs.hmd.scaleFactor = 1; + this.lastModelScaleFactor = 1; + this.outputs = { + eyes: this.shoulderTransforms.eyes, + head: this.shoulderTransforms.head, + hips: this.legsManager.hips, + spine: this.shoulderTransforms.spine, + chest: this.shoulderTransforms.transform, + neck: this.shoulderTransforms.neck, + leftShoulder: this.shoulderTransforms.leftShoulderAnchor, + leftUpperArm: this.shoulderTransforms.leftArm.upperArm, + leftLowerArm: this.shoulderTransforms.leftArm.lowerArm, + leftHand: this.shoulderTransforms.leftArm.hand, + rightShoulder: this.shoulderTransforms.rightShoulderAnchor, + rightUpperArm: this.shoulderTransforms.rightArm.upperArm, + rightLowerArm: this.shoulderTransforms.rightArm.lowerArm, + rightHand: this.shoulderTransforms.rightArm.hand, + leftUpperLeg: this.legsManager.leftLeg.upperLeg, + leftLowerLeg: this.legsManager.leftLeg.lowerLeg, + leftFoot: this.legsManager.leftLeg.foot, + rightUpperLeg: this.legsManager.rightLeg.upperLeg, + rightLowerLeg: this.legsManager.rightLeg.lowerLeg, + rightFoot: this.legsManager.rightLeg.foot, + }; + this.modelBoneOutputs = { + Hips: this.outputs.hips, + Spine: this.outputs.spine, + Chest: this.outputs.chest, + Neck: this.outputs.neck, + Head: this.outputs.head, + + Left_shoulder: this.outputs.rightShoulder, + Left_arm: this.outputs.rightUpperArm, + Left_elbow: this.outputs.rightLowerArm, + Left_wrist: this.outputs.rightHand, + Left_leg: this.outputs.rightUpperLeg, + Left_knee: this.outputs.rightLowerLeg, + Left_ankle: this.outputs.rightFoot, + + Right_shoulder: this.outputs.leftShoulder, + Right_arm: this.outputs.leftUpperArm, + Right_elbow: this.outputs.leftLowerArm, + Right_wrist: this.outputs.leftHand, + Right_leg: this.outputs.leftUpperLeg, + Right_knee: this.outputs.leftLowerLeg, + Right_ankle: this.outputs.leftFoot, + }; + + this.microphoneWorker = null; + this.volume = 0; + this.setMicrophoneMediaStream(options.microphoneMediaStream, { + muted: options.muted, + }); + + this.lastTimestamp = Date.now(); + + this.shoulderTransforms.Start(); + this.legsManager.Start(); + + this.decapitated = false; + if (options.decapitate) { + this.decapitate(); + } + } + initializeBonePositions(setups) { + this.shoulderTransforms.spine.position.copy(setups.spine); + this.shoulderTransforms.transform.position.copy(setups.chest); + this.shoulderTransforms.neck.position.copy(setups.neck); + this.shoulderTransforms.head.position.copy(setups.head); + this.shoulderTransforms.eyes.position.copy(setups.eyes); + + this.shoulderTransforms.leftShoulderAnchor.position.copy(setups.leftShoulder); + this.shoulderTransforms.leftArm.upperArm.position.copy(setups.leftUpperArm); + this.shoulderTransforms.leftArm.lowerArm.position.copy(setups.leftLowerArm); + this.shoulderTransforms.leftArm.hand.position.copy(setups.leftHand); + + this.shoulderTransforms.rightShoulderAnchor.position.copy(setups.rightShoulder); + this.shoulderTransforms.rightArm.upperArm.position.copy(setups.rightUpperArm); + this.shoulderTransforms.rightArm.lowerArm.position.copy(setups.rightLowerArm); + this.shoulderTransforms.rightArm.hand.position.copy(setups.rightHand); + + this.legsManager.leftLeg.upperLeg.position.copy(setups.leftUpperLeg); + this.legsManager.leftLeg.lowerLeg.position.copy(setups.leftLowerLeg); + this.legsManager.leftLeg.foot.position.copy(setups.leftFoot); + + this.legsManager.rightLeg.upperLeg.position.copy(setups.rightUpperLeg); + this.legsManager.rightLeg.lowerLeg.position.copy(setups.rightLowerLeg); + this.legsManager.rightLeg.foot.position.copy(setups.rightFoot); + + this.shoulderTransforms.hips.updateMatrixWorld(); + } + update() { +// return; + + const wasDecapitated = this.decapitated; + if (this.springBoneManager && wasDecapitated) { + this.undecapitate(); + } + + const modelScaleFactor = this.inputs.hmd.scaleFactor; + if (modelScaleFactor !== this.lastModelScaleFactor) { + this.model.scale.set(modelScaleFactor, modelScaleFactor, modelScaleFactor); + this.lastModelScaleFactor = modelScaleFactor; + + this.springBoneManager && this.springBoneManager.springBoneGroupList.forEach(springBoneGroup => { + springBoneGroup.forEach(springBone => { + springBone._worldBoneLength = springBone.bone + .localToWorld(localVector.copy(springBone._initialLocalChildPosition)) + .sub(springBone._worldPosition) + .length(); + }); + }); + } + + this.shoulderTransforms.Update(); + this.legsManager.Update(); + + for (const k in this.modelBones) { + const modelBone = this.modelBones[k]; + const modelBoneOutput = this.modelBoneOutputs[k]; + + if (k === 'Hips') { + modelBone.position.copy(modelBoneOutput.position); + } + modelBone.quaternion.multiplyQuaternions(modelBoneOutput.quaternion, modelBone.initialQuaternion) + + if (k === 'Left_ankle' || k === 'Right_ankle') { + modelBone.quaternion.multiply(upRotation); + } else if (k === 'Left_wrist') { + modelBone.quaternion.multiply(leftRotation); // center + } else if (k === 'Right_wrist') { + modelBone.quaternion.multiply(rightRotation); // center + } + modelBone.updateMatrixWorld(); + } + + const now = Date.now(); + const timeDiff = Math.min(now - this.lastTimestamp, 1000); + this.lastTimestamp = now; + + if (this.options.fingers) { + const _processFingerBones = left => { + const fingerBones = left ? this.fingerBones.left : this.fingerBones.right; + const gamepadInput = left ? this.inputs.rightGamepad : this.inputs.leftGamepad; + for (const k in fingerBones) { + const fingerBone = fingerBones[k]; + if (fingerBone) { + let setter; + if (k === 'thumb') { + setter = (q, i) => q.setFromAxisAngle(localVector.set(0, left ? 1 : -1, 0), gamepadInput.grip * Math.PI*(i === 0 ? 0.125 : 0.25)); + } else if (k === 'index') { + setter = (q, i) => q.setFromAxisAngle(localVector.set(0, 0, left ? -1 : 1), gamepadInput.pointer * Math.PI*0.5); + } else { + setter = (q, i) => q.setFromAxisAngle(localVector.set(0, 0, left ? -1 : 1), gamepadInput.grip * Math.PI*0.5); + } + let index = 0; + fingerBone.traverse(subFingerBone => { + setter(subFingerBone.quaternion, index++); + }); + } + } + }; + _processFingerBones(true); + _processFingerBones(false); + } + + if (this.springBoneManager) { + this.springBoneManager.lateUpdate(timeDiff / 1000); + } + if (this.springBoneManager && wasDecapitated) { + this.decapitate(); + } + + if (this.options.visemes) { + const aaValue = Math.min(this.volume * 10, 1); + const blinkValue = (() => { + const nowWindow = now % 2000; + if (nowWindow >= 0 && nowWindow < 100) { + return nowWindow/100; + } else if (nowWindow >= 100 && nowWindow < 200) { + return 1 - (nowWindow-100)/100; + } else { + return 0; + } + })(); + this.skinnedMeshes.forEach(o => { + const {morphTargetDictionary, morphTargetInfluences} = o; + if (morphTargetDictionary && morphTargetInfluences) { + let aaMorphTargetIndex = morphTargetDictionary['vrc.v_aa']; + if (aaMorphTargetIndex === undefined) { + aaMorphTargetIndex = morphTargetDictionary['morphTarget26']; + } + if (aaMorphTargetIndex !== undefined) { + morphTargetInfluences[aaMorphTargetIndex] = aaValue; + } + + let blinkLeftMorphTargetIndex = morphTargetDictionary['vrc.blink_left']; + if (blinkLeftMorphTargetIndex === undefined) { + blinkLeftMorphTargetIndex = morphTargetDictionary['morphTarget16']; + } + if (blinkLeftMorphTargetIndex !== undefined) { + morphTargetInfluences[blinkLeftMorphTargetIndex] = blinkValue; + } + + let blinkRightMorphTargetIndex = morphTargetDictionary['vrc.blink_right']; + if (blinkRightMorphTargetIndex === undefined) { + blinkRightMorphTargetIndex = morphTargetDictionary['morphTarget17']; + } + if (blinkRightMorphTargetIndex !== undefined) { + morphTargetInfluences[blinkRightMorphTargetIndex] = blinkValue; + } + } + }); + } + } + + async setMicrophoneMediaStream(microphoneMediaStream, options = {}) { + if (this.microphoneWorker) { + this.microphoneWorker.close(); + this.microphoneWorker = null; + setTimeout(() => { + this.volume = 0; + }); + } + if (microphoneMediaStream) { + this.microphoneWorker = new MicrophoneWorker(microphoneMediaStream, options); + this.microphoneWorker.addEventListener('volume', e => { + this.volume = this.volume*0.8 + e.data*0.2; + }); + } + } + + decapitate() { + if (!this.decapitated) { + this.modelBones.Head.traverse(o => { + o.savedPosition.copy(o.position); + o.savedMatrixWorld.copy(o.matrixWorld); + o.position.set(NaN, NaN, NaN); + o.matrixWorld.set(NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN); + }); + this.decapitated = true; + } + } + undecapitate() { + if (this.decapitated) { + this.modelBones.Head.traverse(o => { + o.position.copy(o.savedPosition); + o.matrixWorld.copy(o.savedMatrixWorld); + }); + this.decapitated = false; + } + } + + destroy() { + this.setMicrophoneMediaStream(null); + } +} +export default Avatar; \ No newline at end of file diff --git a/vrarmik/microphone-worker.js b/vrarmik/microphone-worker.js new file mode 100644 index 0000000..6d9aaca --- /dev/null +++ b/vrarmik/microphone-worker.js @@ -0,0 +1,32 @@ + class MicrophoneWorker extends EventTarget { + constructor(mediaStream, options = {}) { + super(); + + const audio = document.createElement('audio'); + audio.srcObject = mediaStream; + audio.muted = true; + this.audioContext = new AudioContext(); + const mediaStreamSource = this.audioContext.createMediaStreamSource(mediaStream); + + this.audioContext.audioWorklet.addModule('vrarmik/audio-volume-worklet.js') + .then(() => { + const audioWorkletNode = new AudioWorkletNode(this.audioContext, 'volume-processor'); + if (options.muted === false) { + audioWorkletNode.port.postMessage(JSON.stringify({ + method: 'muted', + muted: false, + })); + } + audioWorkletNode.port.onmessage = e => { + this.dispatchEvent(new MessageEvent('volume', { + data: e.data, + })); + }; + mediaStreamSource.connect(audioWorkletNode).connect(this.audioContext.destination); + }); + } + close() { + this.audioContext.close(); + } +} +export default MicrophoneWorker; \ No newline at end of file diff --git a/vrarmik/three-vrm.js b/vrarmik/three-vrm.js new file mode 100644 index 0000000..65e3338 --- /dev/null +++ b/vrarmik/three-vrm.js @@ -0,0 +1,4219 @@ +var __three_vrm__ = +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./src/assign.ts"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./src/assign.ts": +/*!***********************!*\ + !*** ./src/assign.ts ***! + \***********************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var __three_vrm__ = __webpack_require__(/*! . */ "./src/index.ts"); +Object.assign(THREE, __three_vrm__); + + +/***/ }), + +/***/ "./src/index.ts": +/*!**********************!*\ + !*** ./src/index.ts ***! + \**********************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(__webpack_require__(/*! ./vrm/ */ "./src/vrm/index.ts")); + + +/***/ }), + +/***/ "./src/vrm/VRM.ts": +/*!************************!*\ + !*** ./src/vrm/VRM.ts ***! + \************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var disposer_1 = __webpack_require__(/*! ./utils/disposer */ "./src/vrm/utils/disposer.ts"); +var VRMImporter_1 = __webpack_require__(/*! ./VRMImporter */ "./src/vrm/VRMImporter.ts"); +var VRM = (function () { + function VRM(params) { + this.scene = params.scene; + this.humanoid = params.humanoid; + this.blendShapeProxy = params.blendShapeProxy; + this.firstPerson = params.firstPerson; + this.lookAt = params.lookAt; + this.materials = params.materials; + this.springBoneManager = params.springBoneManager; + this.meta = params.meta; + } + VRM.from = function (gltf, options) { + if (options === void 0) { options = {}; } + return __awaiter(this, void 0, Promise, function () { + var importer; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + importer = new VRMImporter_1.VRMImporter(options); + return [4, importer.import(gltf)]; + case 1: return [2, _a.sent()]; + } + }); + }); + }; + VRM.prototype.update = function (delta) { + if (this.lookAt) { + this.lookAt.update(delta); + } + if (this.blendShapeProxy) { + this.blendShapeProxy.update(); + } + if (this.springBoneManager) { + this.springBoneManager.lateUpdate(delta); + } + if (this.materials) { + this.materials.forEach(function (material) { + if (material.updateVRMMaterials) { + material.updateVRMMaterials(delta); + } + }); + } + }; + VRM.prototype.dispose = function () { + var scene = this.scene; + if (scene) { + while (scene.children.length > 0) { + var object = scene.children[scene.children.length - 1]; + disposer_1.deepDispose(object); + scene.remove(object); + } + } + }; + return VRM; +}()); +exports.VRM = VRM; + + +/***/ }), + +/***/ "./src/vrm/VRMImporter.ts": +/*!********************************!*\ + !*** ./src/vrm/VRMImporter.ts ***! + \********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var blendshape_1 = __webpack_require__(/*! ./blendshape */ "./src/vrm/blendshape/index.ts"); +var firstperson_1 = __webpack_require__(/*! ./firstperson */ "./src/vrm/firstperson/index.ts"); +var VRMHumanoidImporter_1 = __webpack_require__(/*! ./humanoid/VRMHumanoidImporter */ "./src/vrm/humanoid/VRMHumanoidImporter.ts"); +var VRMLookAtImporter_1 = __webpack_require__(/*! ./lookat/VRMLookAtImporter */ "./src/vrm/lookat/VRMLookAtImporter.ts"); +var material_1 = __webpack_require__(/*! ./material */ "./src/vrm/material/index.ts"); +var reduceBones_1 = __webpack_require__(/*! ./reduceBones */ "./src/vrm/reduceBones.ts"); +var VRMSpringBoneImporter_1 = __webpack_require__(/*! ./springbone/VRMSpringBoneImporter */ "./src/vrm/springbone/VRMSpringBoneImporter.ts"); +var VRM_1 = __webpack_require__(/*! ./VRM */ "./src/vrm/VRM.ts"); +var VRMImporter = (function () { + function VRMImporter(options) { + if (options === void 0) { options = {}; } + this._blendShapeImporter = options.blendShapeImporter || new blendshape_1.VRMBlendShapeImporter(); + this._lookAtImporter = options.lookAtImporter || new VRMLookAtImporter_1.VRMLookAtImporter(); + this._humanoidImporter = options.humanoidImporter || new VRMHumanoidImporter_1.VRMHumanoidImporter(); + this._firstPersonImporter = options.firstPersonImporter || new firstperson_1.VRMFirstPersonImporter(); + this._materialImporter = options.materialImporter || new material_1.VRMMaterialImporter(); + this._springBoneImporter = options.springBoneImporter || new VRMSpringBoneImporter_1.VRMSpringBoneImporter(); + } + VRMImporter.prototype.import = function (gltf) { + return __awaiter(this, void 0, Promise, function () { + var vrmExt, scene, materials, humanoid, firstPerson, _a, blendShapeProxy, lookAt, _b, springBoneManager; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (gltf.parser.json.extensions === undefined || gltf.parser.json.extensions.VRM === undefined) { + throw new Error('Could not find VRM extension on the GLTF'); + } + vrmExt = gltf.parser.json.extensions.VRM; + scene = gltf.scene; + scene.updateMatrixWorld(false); + scene.traverse(function (object3d) { + if (object3d.isMesh) { + object3d.frustumCulled = false; + } + }); + reduceBones_1.reduceBones(scene); + return [4, this._materialImporter.convertGLTFMaterials(gltf)]; + case 1: + materials = (_c.sent()) || undefined; + return [4, this._humanoidImporter.import(gltf)]; + case 2: + humanoid = (_c.sent()) || undefined; + if (!humanoid) return [3, 4]; + return [4, this._firstPersonImporter.import(gltf, humanoid)]; + case 3: + _a = (_c.sent()) || undefined; + return [3, 5]; + case 4: + _a = undefined; + _c.label = 5; + case 5: + firstPerson = _a; + return [4, this._blendShapeImporter.import(gltf)]; + case 6: + blendShapeProxy = (_c.sent()) || undefined; + if (!(firstPerson && blendShapeProxy && humanoid)) return [3, 8]; + return [4, this._lookAtImporter.import(gltf, firstPerson, blendShapeProxy, humanoid)]; + case 7: + _b = (_c.sent()) || undefined; + return [3, 9]; + case 8: + _b = undefined; + _c.label = 9; + case 9: + lookAt = _b; + return [4, this._springBoneImporter.import(gltf)]; + case 10: + springBoneManager = (_c.sent()) || undefined; + return [2, new VRM_1.VRM({ + scene: gltf.scene, + meta: vrmExt.meta, + materials: materials, + humanoid: humanoid, + firstPerson: firstPerson, + blendShapeProxy: blendShapeProxy, + lookAt: lookAt, + springBoneManager: springBoneManager, + })]; + } + }); + }); + }; + return VRMImporter; +}()); +exports.VRMImporter = VRMImporter; + + +/***/ }), + +/***/ "./src/vrm/blendshape/VRMBlendShapeGroup.ts": +/*!**************************************************!*\ + !*** ./src/vrm/blendshape/VRMBlendShapeGroup.ts ***! + \**************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var VRMBlendShapeMaterialValueType; +(function (VRMBlendShapeMaterialValueType) { + VRMBlendShapeMaterialValueType[VRMBlendShapeMaterialValueType["NUMBER"] = 0] = "NUMBER"; + VRMBlendShapeMaterialValueType[VRMBlendShapeMaterialValueType["VECTOR2"] = 1] = "VECTOR2"; + VRMBlendShapeMaterialValueType[VRMBlendShapeMaterialValueType["VECTOR3"] = 2] = "VECTOR3"; + VRMBlendShapeMaterialValueType[VRMBlendShapeMaterialValueType["VECTOR4"] = 3] = "VECTOR4"; + VRMBlendShapeMaterialValueType[VRMBlendShapeMaterialValueType["COLOR"] = 4] = "COLOR"; +})(VRMBlendShapeMaterialValueType || (VRMBlendShapeMaterialValueType = {})); +var _v2 = new THREE.Vector2(); +var _v3 = new THREE.Vector3(); +var _v4 = new THREE.Vector4(); +var _color = new THREE.Color(); +var VRMBlendShapeGroup = (function (_super) { + __extends(VRMBlendShapeGroup, _super); + function VRMBlendShapeGroup(expressionName) { + var _this = _super.call(this) || this; + _this.weight = 0.0; + _this.isBinary = false; + _this._binds = []; + _this._materialValues = []; + _this.name = "BlendShapeController_" + expressionName; + _this.type = 'BlendShapeController'; + _this.visible = false; + return _this; + } + VRMBlendShapeGroup.prototype.addBind = function (args) { + var weight = args.weight / 100; + this._binds.push({ + meshes: args.meshes, + morphTargetIndex: args.morphTargetIndex, + weight: weight, + }); + }; + VRMBlendShapeGroup.prototype.addMaterialValue = function (args) { + var material = args.material; + var propertyName = args.propertyName; + var value = material[propertyName]; + if (!value) { + return; + } + value = args.defaultValue || value; + var type; + var defaultValue; + var targetValue; + var deltaValue; + if (value.isVector2) { + type = VRMBlendShapeMaterialValueType.VECTOR2; + defaultValue = value.clone(); + targetValue = new THREE.Vector2().fromArray(args.targetValue); + deltaValue = targetValue.clone().sub(defaultValue); + } + else if (value.isVector3) { + type = VRMBlendShapeMaterialValueType.VECTOR3; + defaultValue = value.clone(); + targetValue = new THREE.Vector3().fromArray(args.targetValue); + deltaValue = targetValue.clone().sub(defaultValue); + } + else if (value.isVector4) { + type = VRMBlendShapeMaterialValueType.VECTOR4; + defaultValue = value.clone(); + targetValue = new THREE.Vector4().fromArray([ + args.targetValue[2], + args.targetValue[3], + args.targetValue[0], + args.targetValue[1], + ]); + deltaValue = targetValue.clone().sub(defaultValue); + } + else if (value.isColor) { + type = VRMBlendShapeMaterialValueType.COLOR; + defaultValue = value.clone(); + targetValue = new THREE.Color().fromArray(args.targetValue); + deltaValue = targetValue.clone().sub(defaultValue); + } + else { + type = VRMBlendShapeMaterialValueType.NUMBER; + defaultValue = value; + targetValue = args.targetValue[0]; + deltaValue = targetValue - defaultValue; + } + this._materialValues.push({ + material: material, + propertyName: propertyName, + defaultValue: defaultValue, + targetValue: targetValue, + deltaValue: deltaValue, + type: type, + }); + }; + VRMBlendShapeGroup.prototype.applyWeight = function () { + var w = this.isBinary ? (this.weight < 0.5 ? 0.0 : 1.0) : this.weight; + this._binds.forEach(function (bind) { + bind.meshes.forEach(function (mesh) { + if (!mesh.morphTargetInfluences) { + return; + } + mesh.morphTargetInfluences[bind.morphTargetIndex] += w * bind.weight; + }); + }); + this._materialValues.forEach(function (materialValue) { + var prop = materialValue.material[materialValue.propertyName]; + if (prop === undefined) { + return; + } + if (materialValue.type === VRMBlendShapeMaterialValueType.NUMBER) { + var deltaValue = materialValue.deltaValue; + materialValue.material[materialValue.propertyName] += deltaValue * w; + } + else if (materialValue.type === VRMBlendShapeMaterialValueType.VECTOR2) { + var deltaValue = materialValue.deltaValue; + materialValue.material[materialValue.propertyName].add(_v2.copy(deltaValue).multiplyScalar(w)); + } + else if (materialValue.type === VRMBlendShapeMaterialValueType.VECTOR3) { + var deltaValue = materialValue.deltaValue; + materialValue.material[materialValue.propertyName].add(_v3.copy(deltaValue).multiplyScalar(w)); + } + else if (materialValue.type === VRMBlendShapeMaterialValueType.VECTOR4) { + var deltaValue = materialValue.deltaValue; + materialValue.material[materialValue.propertyName].add(_v4.copy(deltaValue).multiplyScalar(w)); + } + else if (materialValue.type === VRMBlendShapeMaterialValueType.COLOR) { + var deltaValue = materialValue.deltaValue; + materialValue.material[materialValue.propertyName].add(_color.copy(deltaValue).multiplyScalar(w)); + } + if (typeof materialValue.material.shouldApplyUniforms === 'boolean') { + materialValue.material.shouldApplyUniforms = true; + } + }); + }; + VRMBlendShapeGroup.prototype.clearAppliedWeight = function () { + this._binds.forEach(function (bind) { + bind.meshes.forEach(function (mesh) { + if (!mesh.morphTargetInfluences) { + return; + } + mesh.morphTargetInfluences[bind.morphTargetIndex] = 0.0; + }); + }); + this._materialValues.forEach(function (materialValue) { + var prop = materialValue.material[materialValue.propertyName]; + if (prop === undefined) { + return; + } + if (materialValue.type === VRMBlendShapeMaterialValueType.NUMBER) { + var defaultValue = materialValue.defaultValue; + materialValue.material[materialValue.propertyName] = defaultValue; + } + else if (materialValue.type === VRMBlendShapeMaterialValueType.VECTOR2) { + var defaultValue = materialValue.defaultValue; + materialValue.material[materialValue.propertyName].copy(defaultValue); + } + else if (materialValue.type === VRMBlendShapeMaterialValueType.VECTOR3) { + var defaultValue = materialValue.defaultValue; + materialValue.material[materialValue.propertyName].copy(defaultValue); + } + else if (materialValue.type === VRMBlendShapeMaterialValueType.VECTOR4) { + var defaultValue = materialValue.defaultValue; + materialValue.material[materialValue.propertyName].copy(defaultValue); + } + else if (materialValue.type === VRMBlendShapeMaterialValueType.COLOR) { + var defaultValue = materialValue.defaultValue; + materialValue.material[materialValue.propertyName].copy(defaultValue); + } + if (typeof materialValue.material.shouldApplyUniforms === 'boolean') { + materialValue.material.shouldApplyUniforms = true; + } + }); + }; + return VRMBlendShapeGroup; +}(THREE.Object3D)); +exports.VRMBlendShapeGroup = VRMBlendShapeGroup; + + +/***/ }), + +/***/ "./src/vrm/blendshape/VRMBlendShapeImporter.ts": +/*!*****************************************************!*\ + !*** ./src/vrm/blendshape/VRMBlendShapeImporter.ts ***! + \*****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var types_1 = __webpack_require__(/*! ../types */ "./src/vrm/types/index.ts"); +var renameMaterialProperty_1 = __webpack_require__(/*! ../utils/renameMaterialProperty */ "./src/vrm/utils/renameMaterialProperty.ts"); +var VRMBlendShapeGroup_1 = __webpack_require__(/*! ./VRMBlendShapeGroup */ "./src/vrm/blendshape/VRMBlendShapeGroup.ts"); +var VRMBlendShapeProxy_1 = __webpack_require__(/*! ./VRMBlendShapeProxy */ "./src/vrm/blendshape/VRMBlendShapeProxy.ts"); +var VRMBlendShapeImporter = (function () { + function VRMBlendShapeImporter() { + } + VRMBlendShapeImporter.prototype.import = function (gltf) { + return __awaiter(this, void 0, Promise, function () { + var vrmExt, schemaBlendShape, blendShape, blendShapeGroups, blendShapePresetMap; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + vrmExt = gltf.parser.json.extensions && gltf.parser.json.extensions.VRM; + if (!vrmExt) { + return [2, null]; + } + schemaBlendShape = vrmExt.blendShapeMaster; + if (!schemaBlendShape) { + return [2, null]; + } + blendShape = new VRMBlendShapeProxy_1.VRMBlendShapeProxy(); + blendShapeGroups = schemaBlendShape.blendShapeGroups; + if (!blendShapeGroups) { + return [2, blendShape]; + } + blendShapePresetMap = {}; + return [4, Promise.all(blendShapeGroups.map(function (schemaGroup) { return __awaiter(_this, void 0, void 0, function () { + var name, presetName, group, materialValues; + var _this = this; + return __generator(this, function (_a) { + name = schemaGroup.name; + if (name === undefined) { + console.warn('VRMBlendShapeImporter: One of blendShapeGroups has no name'); + return [2]; + } + if (schemaGroup.presetName && + schemaGroup.presetName !== types_1.VRMSchema.BlendShapePresetName.Unknown && + !blendShapePresetMap[schemaGroup.presetName]) { + presetName = schemaGroup.presetName; + blendShapePresetMap[schemaGroup.presetName] = name; + } + group = new VRMBlendShapeGroup_1.VRMBlendShapeGroup(name); + gltf.scene.add(group); + group.isBinary = schemaGroup.isBinary || false; + if (schemaGroup.binds) { + schemaGroup.binds.forEach(function (bind) { return __awaiter(_this, void 0, void 0, function () { + var morphMeshes, primitives, morphTargetIndex; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (bind.mesh === undefined || bind.index === undefined) { + return [2]; + } + return [4, gltf.parser.getDependency('mesh', bind.mesh)]; + case 1: + morphMeshes = _a.sent(); + primitives = morphMeshes.type === 'Group' + ? morphMeshes.children + : [morphMeshes]; + morphTargetIndex = bind.index; + if (!primitives.every(function (primitive) { + return Array.isArray(primitive.morphTargetInfluences) && + morphTargetIndex < primitive.morphTargetInfluences.length; + })) { + console.warn("VRMBlendShapeImporter: " + schemaGroup.name + " attempts to index " + morphTargetIndex + "th morph but not found."); + return [2]; + } + group.addBind({ + meshes: primitives, + morphTargetIndex: morphTargetIndex, + weight: bind.weight || 100, + }); + return [2]; + } + }); + }); }); + } + materialValues = schemaGroup.materialValues; + if (materialValues) { + materialValues.forEach(function (materialValue) { + if (materialValue.materialName === undefined || + materialValue.propertyName === undefined || + materialValue.targetValue === undefined) { + return; + } + var materials = []; + gltf.scene.traverse(function (object) { + if (object.material) { + var material = object.material; + if (Array.isArray(material)) { + materials.push.apply(materials, material.filter(function (mtl) { return mtl.name === materialValue.materialName && materials.indexOf(mtl) === -1; })); + } + else if (material.name === materialValue.materialName && materials.indexOf(material) === -1) { + materials.push(material); + } + } + }); + materials.forEach(function (material) { + group.addMaterialValue({ + material: material, + propertyName: renameMaterialProperty_1.renameMaterialProperty(materialValue.propertyName), + targetValue: materialValue.targetValue, + }); + }); + }); + } + blendShape.registerBlendShapeGroup(name, presetName, group); + return [2]; + }); + }); }))]; + case 1: + _a.sent(); + return [2, blendShape]; + } + }); + }); + }; + return VRMBlendShapeImporter; +}()); +exports.VRMBlendShapeImporter = VRMBlendShapeImporter; + + +/***/ }), + +/***/ "./src/vrm/blendshape/VRMBlendShapeProxy.ts": +/*!**************************************************!*\ + !*** ./src/vrm/blendshape/VRMBlendShapeProxy.ts ***! + \**************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = __webpack_require__(/*! ../utils/math */ "./src/vrm/utils/math.ts"); +var VRMBlendShapeProxy = (function () { + function VRMBlendShapeProxy() { + this._blendShapeGroups = {}; + this._blendShapePresetMap = {}; + } + Object.defineProperty(VRMBlendShapeProxy.prototype, "expressions", { + get: function () { + return Object.keys(this._blendShapeGroups); + }, + enumerable: true, + configurable: true + }); + VRMBlendShapeProxy.prototype.getBlendShapeGroup = function (name) { + var presetName = this._blendShapePresetMap[name]; + var controller = presetName ? this._blendShapeGroups[presetName] : this._blendShapeGroups[name]; + if (!controller) { + console.warn("no blend shape found by " + name); + return undefined; + } + return controller; + }; + VRMBlendShapeProxy.prototype.registerBlendShapeGroup = function (name, presetName, controller) { + this._blendShapeGroups[name] = controller; + if (presetName) { + this._blendShapePresetMap[presetName] = name; + } + }; + VRMBlendShapeProxy.prototype.getValue = function (name) { + var controller = this.getBlendShapeGroup(name); + return (controller && controller.weight) || null; + }; + VRMBlendShapeProxy.prototype.setValue = function (name, weight) { + var controller = this.getBlendShapeGroup(name); + if (controller) { + controller.weight = math_1.saturate(weight); + } + }; + VRMBlendShapeProxy.prototype.getBlendShapeTrackName = function (name) { + var controller = this.getBlendShapeGroup(name); + return controller ? controller.name + ".weight" : null; + }; + VRMBlendShapeProxy.prototype.update = function () { + var _this = this; + Object.keys(this._blendShapeGroups).forEach(function (name) { + var controller = _this._blendShapeGroups[name]; + controller.clearAppliedWeight(); + }); + Object.keys(this._blendShapeGroups).forEach(function (name) { + var controller = _this._blendShapeGroups[name]; + controller.applyWeight(); + }); + }; + return VRMBlendShapeProxy; +}()); +exports.VRMBlendShapeProxy = VRMBlendShapeProxy; + + +/***/ }), + +/***/ "./src/vrm/blendshape/index.ts": +/*!*************************************!*\ + !*** ./src/vrm/blendshape/index.ts ***! + \*************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(__webpack_require__(/*! ./VRMBlendShapeGroup */ "./src/vrm/blendshape/VRMBlendShapeGroup.ts")); +__export(__webpack_require__(/*! ./VRMBlendShapeImporter */ "./src/vrm/blendshape/VRMBlendShapeImporter.ts")); +__export(__webpack_require__(/*! ./VRMBlendShapeProxy */ "./src/vrm/blendshape/VRMBlendShapeProxy.ts")); + + +/***/ }), + +/***/ "./src/vrm/debug/VRMDebug.ts": +/*!***********************************!*\ + !*** ./src/vrm/debug/VRMDebug.ts ***! + \***********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var VRM_1 = __webpack_require__(/*! ../VRM */ "./src/vrm/VRM.ts"); +var VRMImporterDebug_1 = __webpack_require__(/*! ./VRMImporterDebug */ "./src/vrm/debug/VRMImporterDebug.ts"); +var VRMDebug = (function (_super) { + __extends(VRMDebug, _super); + function VRMDebug(params, debugOption) { + if (debugOption === void 0) { debugOption = {}; } + var _this = _super.call(this, params) || this; + if (!debugOption.disableBoxHelper) { + _this.scene.add(new THREE.BoxHelper(_this.scene)); + } + if (!debugOption.disableSkeletonHelper) { + _this.scene.add(new THREE.SkeletonHelper(_this.scene)); + } + return _this; + } + VRMDebug.from = function (gltf, options, debugOption) { + if (options === void 0) { options = {}; } + if (debugOption === void 0) { debugOption = {}; } + return __awaiter(this, void 0, Promise, function () { + var importer; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + importer = new VRMImporterDebug_1.VRMImporterDebug(options); + return [4, importer.import(gltf, debugOption)]; + case 1: return [2, _a.sent()]; + } + }); + }); + }; + VRMDebug.prototype.update = function (delta) { + _super.prototype.update.call(this, delta); + }; + return VRMDebug; +}(VRM_1.VRM)); +exports.VRMDebug = VRMDebug; + + +/***/ }), + +/***/ "./src/vrm/debug/VRMDebugOptions.ts": +/*!******************************************!*\ + !*** ./src/vrm/debug/VRMDebugOptions.ts ***! + \******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); + + +/***/ }), + +/***/ "./src/vrm/debug/VRMImporterDebug.ts": +/*!*******************************************!*\ + !*** ./src/vrm/debug/VRMImporterDebug.ts ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var reduceBones_1 = __webpack_require__(/*! ../reduceBones */ "./src/vrm/reduceBones.ts"); +var VRMImporter_1 = __webpack_require__(/*! ../VRMImporter */ "./src/vrm/VRMImporter.ts"); +var VRMDebug_1 = __webpack_require__(/*! ./VRMDebug */ "./src/vrm/debug/VRMDebug.ts"); +var VRMLookAtImporterDebug_1 = __webpack_require__(/*! ./VRMLookAtImporterDebug */ "./src/vrm/debug/VRMLookAtImporterDebug.ts"); +var VRMSpringBoneImporterDebug_1 = __webpack_require__(/*! ./VRMSpringBoneImporterDebug */ "./src/vrm/debug/VRMSpringBoneImporterDebug.ts"); +var VRMImporterDebug = (function (_super) { + __extends(VRMImporterDebug, _super); + function VRMImporterDebug(options) { + if (options === void 0) { options = {}; } + var _this = this; + options.lookAtImporter = options.lookAtImporter || new VRMLookAtImporterDebug_1.VRMLookAtImporterDebug(); + options.springBoneImporter = options.springBoneImporter || new VRMSpringBoneImporterDebug_1.VRMSpringBoneImporterDebug(); + _this = _super.call(this, options) || this; + return _this; + } + VRMImporterDebug.prototype.import = function (gltf, debugOptions) { + if (debugOptions === void 0) { debugOptions = {}; } + return __awaiter(this, void 0, Promise, function () { + var vrmExt, scene, materials, humanoid, firstPerson, _a, blendShapeProxy, lookAt, _b, springBoneManager; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (gltf.parser.json.extensions === undefined || gltf.parser.json.extensions.VRM === undefined) { + throw new Error('Could not find VRM extension on the GLTF'); + } + vrmExt = gltf.parser.json.extensions.VRM; + scene = gltf.scene; + scene.updateMatrixWorld(false); + scene.traverse(function (object3d) { + if (object3d.isMesh) { + object3d.frustumCulled = false; + } + }); + reduceBones_1.reduceBones(scene); + return [4, this._materialImporter.convertGLTFMaterials(gltf)]; + case 1: + materials = (_c.sent()) || undefined; + return [4, this._humanoidImporter.import(gltf)]; + case 2: + humanoid = (_c.sent()) || undefined; + if (!humanoid) return [3, 4]; + return [4, this._firstPersonImporter.import(gltf, humanoid)]; + case 3: + _a = (_c.sent()) || undefined; + return [3, 5]; + case 4: + _a = undefined; + _c.label = 5; + case 5: + firstPerson = _a; + return [4, this._blendShapeImporter.import(gltf)]; + case 6: + blendShapeProxy = (_c.sent()) || undefined; + if (!(firstPerson && blendShapeProxy && humanoid)) return [3, 8]; + return [4, this._lookAtImporter.import(gltf, firstPerson, blendShapeProxy, humanoid)]; + case 7: + _b = (_c.sent()) || undefined; + return [3, 9]; + case 8: + _b = undefined; + _c.label = 9; + case 9: + lookAt = _b; + if (lookAt.setupHelper) { + lookAt.setupHelper(scene, debugOptions); + } + return [4, this._springBoneImporter.import(gltf)]; + case 10: + springBoneManager = (_c.sent()) || undefined; + return [2, new VRMDebug_1.VRMDebug({ + scene: gltf.scene, + meta: vrmExt.meta, + materials: materials, + humanoid: humanoid, + firstPerson: firstPerson, + blendShapeProxy: blendShapeProxy, + lookAt: lookAt, + springBoneManager: springBoneManager, + }, debugOptions)]; + } + }); + }); + }; + return VRMImporterDebug; +}(VRMImporter_1.VRMImporter)); +exports.VRMImporterDebug = VRMImporterDebug; + + +/***/ }), + +/***/ "./src/vrm/debug/VRMLookAtHeadDebug.ts": +/*!*********************************************!*\ + !*** ./src/vrm/debug/VRMLookAtHeadDebug.ts ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var VRMLookAtHead_1 = __webpack_require__(/*! ../lookat/VRMLookAtHead */ "./src/vrm/lookat/VRMLookAtHead.ts"); +var _v3 = new THREE.Vector3(); +var VRMLookAtHeadDebug = (function (_super) { + __extends(VRMLookAtHeadDebug, _super); + function VRMLookAtHeadDebug() { + return _super !== null && _super.apply(this, arguments) || this; + } + VRMLookAtHeadDebug.prototype.setupHelper = function (scene, debugOption) { + if (!debugOption.disableFaceDirectionHelper) { + this._faceDirectionHelper = new THREE.ArrowHelper(new THREE.Vector3(0, 0, -1), new THREE.Vector3(0, 0, 0), 0.5, 0xff00ff); + scene.add(this._faceDirectionHelper); + } + }; + VRMLookAtHeadDebug.prototype.update = function (delta) { + _super.prototype.update.call(this, delta); + if (this._faceDirectionHelper) { + this.firstPerson.getFirstPersonWorldPosition(this._faceDirectionHelper.position); + this._faceDirectionHelper.setDirection(this.getLookAtWorldDirection(_v3)); + } + }; + return VRMLookAtHeadDebug; +}(VRMLookAtHead_1.VRMLookAtHead)); +exports.VRMLookAtHeadDebug = VRMLookAtHeadDebug; + + +/***/ }), + +/***/ "./src/vrm/debug/VRMLookAtImporterDebug.ts": +/*!*************************************************!*\ + !*** ./src/vrm/debug/VRMLookAtImporterDebug.ts ***! + \*************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var VRMLookAtImporter_1 = __webpack_require__(/*! ../lookat/VRMLookAtImporter */ "./src/vrm/lookat/VRMLookAtImporter.ts"); +var VRMLookAtHeadDebug_1 = __webpack_require__(/*! ./VRMLookAtHeadDebug */ "./src/vrm/debug/VRMLookAtHeadDebug.ts"); +var VRMLookAtImporterDebug = (function (_super) { + __extends(VRMLookAtImporterDebug, _super); + function VRMLookAtImporterDebug() { + return _super !== null && _super.apply(this, arguments) || this; + } + VRMLookAtImporterDebug.prototype.import = function (gltf, firstPerson, blendShapeProxy, humanoid) { + var vrmExt = gltf.parser.json.extensions && gltf.parser.json.extensions.VRM; + if (!vrmExt) { + return null; + } + var schemaFirstPerson = vrmExt.firstPerson; + if (!schemaFirstPerson) { + return null; + } + var applyer = this._importApplyer(schemaFirstPerson, blendShapeProxy, humanoid); + return new VRMLookAtHeadDebug_1.VRMLookAtHeadDebug(firstPerson, applyer || undefined); + }; + return VRMLookAtImporterDebug; +}(VRMLookAtImporter_1.VRMLookAtImporter)); +exports.VRMLookAtImporterDebug = VRMLookAtImporterDebug; + + +/***/ }), + +/***/ "./src/vrm/debug/VRMSpringBoneDebug.ts": +/*!*********************************************!*\ + !*** ./src/vrm/debug/VRMSpringBoneDebug.ts ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var springbone_1 = __webpack_require__(/*! ../springbone */ "./src/vrm/springbone/index.ts"); +var _v3A = new THREE.Vector3(); +var VRMSpringBoneDebug = (function (_super) { + __extends(VRMSpringBoneDebug, _super); + function VRMSpringBoneDebug(bone, radius, stiffiness, gravityDir, gravityPower, dragForce, colliders) { + if (colliders === void 0) { colliders = []; } + return _super.call(this, bone, radius, stiffiness, gravityDir, gravityPower, dragForce, colliders) || this; + } + VRMSpringBoneDebug.prototype.getGizmo = function () { + if (this._gizmo) { + return this._gizmo; + } + var nextTailRelative = _v3A.copy(this._nextTail).sub(this._worldPosition); + var nextTailRelativeLength = nextTailRelative.length(); + this._gizmo = new THREE.ArrowHelper(nextTailRelative.normalize(), this._worldPosition, nextTailRelativeLength, 0xffff00, this.radius, this.radius); + this._gizmo.line.renderOrder = springbone_1.GIZMO_RENDER_ORDER; + this._gizmo.cone.renderOrder = springbone_1.GIZMO_RENDER_ORDER; + this._gizmo.line.material.depthTest = false; + this._gizmo.line.material.transparent = true; + this._gizmo.cone.material.depthTest = false; + this._gizmo.cone.material.transparent = true; + return this._gizmo; + }; + VRMSpringBoneDebug.prototype.update = function (delta) { + _super.prototype.update.call(this, delta); + this._updateGizmo(); + }; + VRMSpringBoneDebug.prototype._updateGizmo = function () { + if (!this._gizmo) { + return; + } + var nextTailRelative = _v3A.copy(this._currentTail).sub(this._worldPosition); + var nextTailRelativeLength = nextTailRelative.length(); + this._gizmo.setDirection(nextTailRelative.normalize()); + this._gizmo.setLength(nextTailRelativeLength, this.radius, this.radius); + this._gizmo.position.copy(this._worldPosition); + }; + return VRMSpringBoneDebug; +}(springbone_1.VRMSpringBone)); +exports.VRMSpringBoneDebug = VRMSpringBoneDebug; + + +/***/ }), + +/***/ "./src/vrm/debug/VRMSpringBoneImporterDebug.ts": +/*!*****************************************************!*\ + !*** ./src/vrm/debug/VRMSpringBoneImporterDebug.ts ***! + \*****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var VRMSpringBoneImporter_1 = __webpack_require__(/*! ../springbone/VRMSpringBoneImporter */ "./src/vrm/springbone/VRMSpringBoneImporter.ts"); +var VRMSpringBoneDebug_1 = __webpack_require__(/*! ./VRMSpringBoneDebug */ "./src/vrm/debug/VRMSpringBoneDebug.ts"); +var VRMSpringBoneImporterDebug = (function (_super) { + __extends(VRMSpringBoneImporterDebug, _super); + function VRMSpringBoneImporterDebug() { + return _super !== null && _super.apply(this, arguments) || this; + } + Object.defineProperty(VRMSpringBoneImporterDebug.prototype, "_isColiderMeshVisible", { + get: function () { + return true; + }, + enumerable: true, + configurable: true + }); + VRMSpringBoneImporterDebug.prototype._createSpringBone = function (gltf, bone, hitRadius, stiffiness, gravityDir, gravityPower, dragForce, colliders) { + if (colliders === void 0) { colliders = []; } + var springBone = new VRMSpringBoneDebug_1.VRMSpringBoneDebug(bone, hitRadius, stiffiness, gravityDir, gravityPower, dragForce, colliders); + gltf.scene.add(springBone.getGizmo()); + return springBone; + }; + return VRMSpringBoneImporterDebug; +}(VRMSpringBoneImporter_1.VRMSpringBoneImporter)); +exports.VRMSpringBoneImporterDebug = VRMSpringBoneImporterDebug; + + +/***/ }), + +/***/ "./src/vrm/debug/index.ts": +/*!********************************!*\ + !*** ./src/vrm/debug/index.ts ***! + \********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(__webpack_require__(/*! ./VRMDebugOptions */ "./src/vrm/debug/VRMDebugOptions.ts")); +__export(__webpack_require__(/*! ./VRMDebug */ "./src/vrm/debug/VRMDebug.ts")); +__export(__webpack_require__(/*! ./VRMSpringBoneDebug */ "./src/vrm/debug/VRMSpringBoneDebug.ts")); +__export(__webpack_require__(/*! ./VRMSpringBoneImporterDebug */ "./src/vrm/debug/VRMSpringBoneImporterDebug.ts")); + + +/***/ }), + +/***/ "./src/vrm/firstperson/VRMFirstPerson.ts": +/*!***********************************************!*\ + !*** ./src/vrm/firstperson/VRMFirstPerson.ts ***! + \***********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var math_1 = __webpack_require__(/*! ../utils/math */ "./src/vrm/utils/math.ts"); +var VECTOR3_FRONT = Object.freeze(new THREE.Vector3(0.0, 0.0, -1.0)); +var _quat = new THREE.Quaternion(); +var FirstPersonFlag; +(function (FirstPersonFlag) { + FirstPersonFlag[FirstPersonFlag["Auto"] = 0] = "Auto"; + FirstPersonFlag[FirstPersonFlag["Both"] = 1] = "Both"; + FirstPersonFlag[FirstPersonFlag["ThirdPersonOnly"] = 2] = "ThirdPersonOnly"; + FirstPersonFlag[FirstPersonFlag["FirstPersonOnly"] = 3] = "FirstPersonOnly"; +})(FirstPersonFlag || (FirstPersonFlag = {})); +var VRMRendererFirstPersonFlags = (function () { + function VRMRendererFirstPersonFlags(firstPersonFlag, mesh) { + this.firstPersonFlag = VRMRendererFirstPersonFlags._parseFirstPersonFlag(firstPersonFlag); + this.mesh = mesh; + } + VRMRendererFirstPersonFlags._parseFirstPersonFlag = function (firstPersonFlag) { + switch (firstPersonFlag) { + case 'Both': + return FirstPersonFlag.Both; + case 'ThirdPersonOnly': + return FirstPersonFlag.ThirdPersonOnly; + case 'FirstPersonOnly': + return FirstPersonFlag.FirstPersonOnly; + default: + return FirstPersonFlag.Auto; + } + }; + return VRMRendererFirstPersonFlags; +}()); +exports.VRMRendererFirstPersonFlags = VRMRendererFirstPersonFlags; +var VRMFirstPerson = (function () { + function VRMFirstPerson(firstPersonBone, firstPersonBoneOffset, meshAnnotations) { + this._meshAnnotations = []; + this._firstPersonOnlyLayer = VRMFirstPerson._DEFAULT_FIRSTPERSON_ONLY_LAYER; + this._thirdPersonOnlyLayer = VRMFirstPerson._DEFAULT_THIRDPERSON_ONLY_LAYER; + this._initialized = false; + this._firstPersonBone = firstPersonBone; + this._firstPersonBoneOffset = firstPersonBoneOffset; + this._meshAnnotations = meshAnnotations; + } + Object.defineProperty(VRMFirstPerson.prototype, "firstPersonBone", { + get: function () { + return this._firstPersonBone; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(VRMFirstPerson.prototype, "meshAnnotations", { + get: function () { + return this._meshAnnotations; + }, + enumerable: true, + configurable: true + }); + VRMFirstPerson.prototype.getFirstPersonWorldDirection = function (target) { + return target.copy(VECTOR3_FRONT).applyQuaternion(math_1.getWorldQuaternionLite(this._firstPersonBone, _quat)); + }; + Object.defineProperty(VRMFirstPerson.prototype, "firstPersonOnlyLayer", { + get: function () { + return this._firstPersonOnlyLayer; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(VRMFirstPerson.prototype, "thirdPersonOnlyLayer", { + get: function () { + return this._thirdPersonOnlyLayer; + }, + enumerable: true, + configurable: true + }); + VRMFirstPerson.prototype.getFirstPersonBoneOffset = function (target) { + return target.copy(this._firstPersonBoneOffset); + }; + VRMFirstPerson.prototype.getFirstPersonWorldPosition = function (v3) { + var offset = this._firstPersonBoneOffset; + var v4 = new THREE.Vector4(offset.x, offset.y, offset.z, 1.0); + v4.applyMatrix4(this._firstPersonBone.matrixWorld); + return v3.set(v4.x, v4.y, v4.z); + }; + VRMFirstPerson.prototype.setup = function (_a) { + var _this = this; + var _b = _a === void 0 ? {} : _a, _c = _b.firstPersonOnlyLayer, firstPersonOnlyLayer = _c === void 0 ? VRMFirstPerson._DEFAULT_FIRSTPERSON_ONLY_LAYER : _c, _d = _b.thirdPersonOnlyLayer, thirdPersonOnlyLayer = _d === void 0 ? VRMFirstPerson._DEFAULT_THIRDPERSON_ONLY_LAYER : _d; + if (this._initialized) { + return; + } + this._initialized = true; + this._firstPersonOnlyLayer = firstPersonOnlyLayer; + this._thirdPersonOnlyLayer = thirdPersonOnlyLayer; + this._meshAnnotations.forEach(function (item) { + if (item.firstPersonFlag === FirstPersonFlag.FirstPersonOnly) { + item.mesh.layers.set(_this._firstPersonOnlyLayer); + item.mesh.traverse(function (child) { return child.layers.set(_this._firstPersonOnlyLayer); }); + } + else if (item.firstPersonFlag === FirstPersonFlag.ThirdPersonOnly) { + item.mesh.layers.set(_this._thirdPersonOnlyLayer); + item.mesh.traverse(function (child) { return child.layers.set(_this._thirdPersonOnlyLayer); }); + } + else if (item.firstPersonFlag === FirstPersonFlag.Auto) { + _this._createHeadlessModel(item.mesh); + } + }); + }; + VRMFirstPerson.prototype._excludeTriangles = function (triangles, bws, skinIndex, exclude) { + var count = 0; + if (bws != null && bws.length > 0) { + for (var i = 0; i < triangles.length; i += 3) { + var a = triangles[i]; + var b = triangles[i + 1]; + var c = triangles[i + 2]; + var bw0 = bws[a]; + var skin0 = skinIndex[a]; + if (bw0[0] > 0 && exclude.includes(skin0[0])) + continue; + if (bw0[1] > 0 && exclude.includes(skin0[1])) + continue; + if (bw0[2] > 0 && exclude.includes(skin0[2])) + continue; + if (bw0[3] > 0 && exclude.includes(skin0[3])) + continue; + var bw1 = bws[b]; + var skin1 = skinIndex[b]; + if (bw1[0] > 0 && exclude.includes(skin1[0])) + continue; + if (bw1[1] > 0 && exclude.includes(skin1[1])) + continue; + if (bw1[2] > 0 && exclude.includes(skin1[2])) + continue; + if (bw1[3] > 0 && exclude.includes(skin1[3])) + continue; + var bw2 = bws[c]; + var skin2 = skinIndex[c]; + if (bw2[0] > 0 && exclude.includes(skin2[0])) + continue; + if (bw2[1] > 0 && exclude.includes(skin2[1])) + continue; + if (bw2[2] > 0 && exclude.includes(skin2[2])) + continue; + if (bw2[3] > 0 && exclude.includes(skin2[3])) + continue; + triangles[count++] = a; + triangles[count++] = b; + triangles[count++] = c; + } + } + return count; + }; + VRMFirstPerson.prototype._createErasedMesh = function (src, erasingBonesIndex) { + var dst = new THREE.SkinnedMesh(src.geometry.clone(), src.material); + dst.name = src.name + "(erase)"; + dst.frustumCulled = src.frustumCulled; + dst.layers.set(this._firstPersonOnlyLayer); + var geometry = dst.geometry; + var skinIndexAttr = geometry.getAttribute('skinIndex').array; + var skinIndex = []; + for (var i = 0; i < skinIndexAttr.length; i += 4) { + skinIndex.push([skinIndexAttr[i], skinIndexAttr[i + 1], skinIndexAttr[i + 2], skinIndexAttr[i + 3]]); + } + var skinWeightAttr = geometry.getAttribute('skinWeight').array; + var skinWeight = []; + for (var i = 0; i < skinWeightAttr.length; i += 4) { + skinWeight.push([skinWeightAttr[i], skinWeightAttr[i + 1], skinWeightAttr[i + 2], skinWeightAttr[i + 3]]); + } + var oldTriangles = Array.from(geometry.getIndex().array); + var count = this._excludeTriangles(oldTriangles, skinWeight, skinIndex, erasingBonesIndex); + var newTriangle = []; + for (var i = 0; i < count; i++) { + newTriangle[i] = oldTriangles[i]; + } + geometry.setIndex(newTriangle); + if (src.onBeforeRender) { + dst.onBeforeRender = src.onBeforeRender; + } + dst.bind(new THREE.Skeleton(src.skeleton.bones, src.skeleton.boneInverses), new THREE.Matrix4()); + return dst; + }; + VRMFirstPerson.prototype._createHeadlessModelForSkinnedMesh = function (parent, mesh) { + var _this = this; + var eraseBoneIndexes = []; + mesh.skeleton.bones.forEach(function (bone, index) { + if (_this._isEraseTarget(bone)) + eraseBoneIndexes.push(index); + }); + if (!eraseBoneIndexes.length) { + mesh.layers.enable(this._thirdPersonOnlyLayer); + mesh.layers.enable(this._firstPersonOnlyLayer); + return; + } + mesh.layers.set(this._thirdPersonOnlyLayer); + var newMesh = this._createErasedMesh(mesh, eraseBoneIndexes); + parent.add(newMesh); + }; + VRMFirstPerson.prototype._createHeadlessModel = function (node) { + var _this = this; + if (node.type === 'Group') { + node.layers.set(this._thirdPersonOnlyLayer); + if (this._isEraseTarget(node)) { + node.traverse(function (child) { return child.layers.set(_this._thirdPersonOnlyLayer); }); + } + else { + var parent_1 = new THREE.Group(); + parent_1.name = "_headless_" + node.name; + parent_1.layers.set(this._firstPersonOnlyLayer); + node.parent.add(parent_1); + node.children + .filter(function (child) { return child.type === 'SkinnedMesh'; }) + .forEach(function (child) { + _this._createHeadlessModelForSkinnedMesh(parent_1, child); + }); + } + } + else if (node.type === 'SkinnedMesh') { + this._createHeadlessModelForSkinnedMesh(node.parent, node); + } + else { + if (this._isEraseTarget(node)) { + node.layers.set(this._thirdPersonOnlyLayer); + node.traverse(function (child) { return child.layers.set(_this._thirdPersonOnlyLayer); }); + } + } + }; + VRMFirstPerson.prototype._isEraseTarget = function (bone) { + if (bone.name === this._firstPersonBone.name) { + return true; + } + else if (!bone.parent) { + return false; + } + else { + return this._isEraseTarget(bone.parent); + } + }; + VRMFirstPerson._DEFAULT_FIRSTPERSON_ONLY_LAYER = 9; + VRMFirstPerson._DEFAULT_THIRDPERSON_ONLY_LAYER = 10; + return VRMFirstPerson; +}()); +exports.VRMFirstPerson = VRMFirstPerson; + + +/***/ }), + +/***/ "./src/vrm/firstperson/VRMFirstPersonImporter.ts": +/*!*******************************************************!*\ + !*** ./src/vrm/firstperson/VRMFirstPersonImporter.ts ***! + \*******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var types_1 = __webpack_require__(/*! ../types */ "./src/vrm/types/index.ts"); +var VRMFirstPerson_1 = __webpack_require__(/*! ./VRMFirstPerson */ "./src/vrm/firstperson/VRMFirstPerson.ts"); +var VRMFirstPersonImporter = (function () { + function VRMFirstPersonImporter() { + } + VRMFirstPersonImporter.prototype.import = function (gltf, humanoid) { + return __awaiter(this, void 0, Promise, function () { + var vrmExt, schemaFirstPerson, firstPersonBoneIndex, firstPersonBone, firstPersonBoneOffset, meshAnnotations, meshes; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + vrmExt = gltf.parser.json.extensions && gltf.parser.json.extensions.VRM; + if (!vrmExt) { + return [2, null]; + } + schemaFirstPerson = vrmExt.firstPerson; + if (!schemaFirstPerson) { + return [2, null]; + } + firstPersonBoneIndex = schemaFirstPerson.firstPersonBone; + if (!(firstPersonBoneIndex === undefined || firstPersonBoneIndex === -1)) return [3, 1]; + firstPersonBone = humanoid.getBoneNode(types_1.VRMSchema.HumanoidBoneName.Head); + return [3, 3]; + case 1: return [4, gltf.parser.getDependency('node', firstPersonBoneIndex)]; + case 2: + firstPersonBone = _a.sent(); + _a.label = 3; + case 3: + if (!firstPersonBone) { + console.warn('VRMFirstPersonImporter: Could not find firstPersonBone of the VRM'); + return [2, null]; + } + firstPersonBoneOffset = schemaFirstPerson.firstPersonBoneOffset + ? new THREE.Vector3(schemaFirstPerson.firstPersonBoneOffset.x, schemaFirstPerson.firstPersonBoneOffset.y, schemaFirstPerson.firstPersonBoneOffset.z) + : new THREE.Vector3(0.0, 0.06, 0.0); + meshAnnotations = []; + return [4, gltf.parser.getDependencies('mesh')]; + case 4: + meshes = _a.sent(); + meshes.forEach(function (mesh, meshIndex) { + var flag = schemaFirstPerson.meshAnnotations + ? schemaFirstPerson.meshAnnotations.find(function (a) { return a.mesh === meshIndex; }) + : undefined; + meshAnnotations.push(new VRMFirstPerson_1.VRMRendererFirstPersonFlags(flag && flag.firstPersonFlag, mesh)); + }); + return [2, new VRMFirstPerson_1.VRMFirstPerson(firstPersonBone, firstPersonBoneOffset, meshAnnotations)]; + } + }); + }); + }; + return VRMFirstPersonImporter; +}()); +exports.VRMFirstPersonImporter = VRMFirstPersonImporter; + + +/***/ }), + +/***/ "./src/vrm/firstperson/index.ts": +/*!**************************************!*\ + !*** ./src/vrm/firstperson/index.ts ***! + \**************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(__webpack_require__(/*! ./VRMFirstPerson */ "./src/vrm/firstperson/VRMFirstPerson.ts")); +__export(__webpack_require__(/*! ./VRMFirstPersonImporter */ "./src/vrm/firstperson/VRMFirstPersonImporter.ts")); + + +/***/ }), + +/***/ "./src/vrm/humanoid/VRMHumanBone.ts": +/*!******************************************!*\ + !*** ./src/vrm/humanoid/VRMHumanBone.ts ***! + \******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var VRMHumanBone = (function () { + function VRMHumanBone(node, humanLimit) { + this.node = node; + this.humanLimit = humanLimit; + } + return VRMHumanBone; +}()); +exports.VRMHumanBone = VRMHumanBone; + + +/***/ }), + +/***/ "./src/vrm/humanoid/VRMHumanBones.ts": +/*!*******************************************!*\ + !*** ./src/vrm/humanoid/VRMHumanBones.ts ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); + + +/***/ }), + +/***/ "./src/vrm/humanoid/VRMHumanDescription.ts": +/*!*************************************************!*\ + !*** ./src/vrm/humanoid/VRMHumanDescription.ts ***! + \*************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); + + +/***/ }), + +/***/ "./src/vrm/humanoid/VRMHumanLimit.ts": +/*!*******************************************!*\ + !*** ./src/vrm/humanoid/VRMHumanLimit.ts ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); + + +/***/ }), + +/***/ "./src/vrm/humanoid/VRMHumanoid.ts": +/*!*****************************************!*\ + !*** ./src/vrm/humanoid/VRMHumanoid.ts ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var types_1 = __webpack_require__(/*! ../types */ "./src/vrm/types/index.ts"); +var VRMHumanoid = (function () { + function VRMHumanoid(boneArray, humanDescription) { + this.humanBones = this._createHumanBones(boneArray); + this.humanDescription = humanDescription; + this.restPose = this.getPose(); + } + VRMHumanoid.prototype.getPose = function () { + var _this = this; + var pose = {}; + Object.keys(this.humanBones).forEach(function (vrmBoneName) { + var node = _this.getBoneNode(vrmBoneName); + if (!node) { + return; + } + if (pose[vrmBoneName]) { + return; + } + pose[vrmBoneName] = { + position: node.position.toArray(), + rotation: node.quaternion.toArray(), + }; + }, {}); + return pose; + }; + VRMHumanoid.prototype.setPose = function (poseObject) { + var _this = this; + Object.keys(poseObject).forEach(function (boneName) { + var state = poseObject[boneName]; + var node = _this.getBoneNode(boneName); + if (!node) { + return; + } + var restState = _this.restPose[boneName]; + if (!restState) { + return; + } + if (state.position) { + node.position.set(restState.position[0] + state.position[0], restState.position[1] + state.position[1], restState.position[2] + state.position[2]); + } + if (state.rotation) { + node.quaternion.fromArray(state.rotation); + } + }); + }; + VRMHumanoid.prototype.getBone = function (name) { + return this.humanBones[name][0] || undefined; + }; + VRMHumanoid.prototype.getBones = function (name) { + return this.humanBones[name]; + }; + VRMHumanoid.prototype.getBoneNode = function (name) { + return (this.humanBones[name][0] && this.humanBones[name][0].node) || null; + }; + VRMHumanoid.prototype.getBoneNodes = function (name) { + return this.humanBones[name].map(function (bone) { return bone.node; }); + }; + VRMHumanoid.prototype._createHumanBones = function (boneArray) { + var bones = Object.values(types_1.VRMSchema.HumanoidBoneName).reduce(function (accum, name) { + accum[name] = []; + return accum; + }, {}); + boneArray.forEach(function (bone) { + bones[bone.name].push(bone.bone); + }); + return bones; + }; + return VRMHumanoid; +}()); +exports.VRMHumanoid = VRMHumanoid; + + +/***/ }), + +/***/ "./src/vrm/humanoid/VRMHumanoidImporter.ts": +/*!*************************************************!*\ + !*** ./src/vrm/humanoid/VRMHumanoidImporter.ts ***! + \*************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var VRMHumanBone_1 = __webpack_require__(/*! ./VRMHumanBone */ "./src/vrm/humanoid/VRMHumanBone.ts"); +var VRMHumanoid_1 = __webpack_require__(/*! ./VRMHumanoid */ "./src/vrm/humanoid/VRMHumanoid.ts"); +var VRMHumanoidImporter = (function () { + function VRMHumanoidImporter() { + } + VRMHumanoidImporter.prototype.import = function (gltf) { + return __awaiter(this, void 0, Promise, function () { + var vrmExt, schemaHumanoid, humanBoneArray, humanDescription; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + vrmExt = gltf.parser.json.extensions && gltf.parser.json.extensions.VRM; + if (!vrmExt) { + return [2, null]; + } + schemaHumanoid = vrmExt.humanoid; + if (!schemaHumanoid) { + return [2, null]; + } + humanBoneArray = []; + if (!schemaHumanoid.humanBones) return [3, 2]; + return [4, Promise.all(schemaHumanoid.humanBones.map(function (bone) { return __awaiter(_this, void 0, void 0, function () { + var node; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!bone.bone || !bone.node) { + return [2]; + } + return [4, gltf.parser.getDependency('node', bone.node)]; + case 1: + node = _a.sent(); + humanBoneArray.push({ + name: bone.bone, + bone: new VRMHumanBone_1.VRMHumanBone(node, { + axisLength: bone.axisLength, + center: bone.center && new THREE.Vector3(bone.center.x, bone.center.y, bone.center.z), + max: bone.max && new THREE.Vector3(bone.max.x, bone.max.y, bone.max.z), + min: bone.min && new THREE.Vector3(bone.min.x, bone.min.y, bone.min.z), + useDefaultValues: bone.useDefaultValues, + }), + }); + return [2]; + } + }); + }); }))]; + case 1: + _a.sent(); + _a.label = 2; + case 2: + humanDescription = { + armStretch: schemaHumanoid.armStretch, + legStretch: schemaHumanoid.legStretch, + upperArmTwist: schemaHumanoid.upperArmTwist, + lowerArmTwist: schemaHumanoid.lowerArmTwist, + upperLegTwist: schemaHumanoid.upperLegTwist, + lowerLegTwist: schemaHumanoid.lowerLegTwist, + feetSpacing: schemaHumanoid.feetSpacing, + hasTranslationDoF: schemaHumanoid.hasTranslationDoF, + }; + return [2, new VRMHumanoid_1.VRMHumanoid(humanBoneArray, humanDescription)]; + } + }); + }); + }; + return VRMHumanoidImporter; +}()); +exports.VRMHumanoidImporter = VRMHumanoidImporter; + + +/***/ }), + +/***/ "./src/vrm/humanoid/index.ts": +/*!***********************************!*\ + !*** ./src/vrm/humanoid/index.ts ***! + \***********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(__webpack_require__(/*! ./VRMHumanBone */ "./src/vrm/humanoid/VRMHumanBone.ts")); +__export(__webpack_require__(/*! ./VRMHumanBones */ "./src/vrm/humanoid/VRMHumanBones.ts")); +__export(__webpack_require__(/*! ./VRMHumanDescription */ "./src/vrm/humanoid/VRMHumanDescription.ts")); +__export(__webpack_require__(/*! ./VRMHumanLimit */ "./src/vrm/humanoid/VRMHumanLimit.ts")); +__export(__webpack_require__(/*! ./VRMHumanoid */ "./src/vrm/humanoid/VRMHumanoid.ts")); +__export(__webpack_require__(/*! ./VRMHumanoidImporter */ "./src/vrm/humanoid/VRMHumanoidImporter.ts")); + + +/***/ }), + +/***/ "./src/vrm/index.ts": +/*!**************************!*\ + !*** ./src/vrm/index.ts ***! + \**************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(__webpack_require__(/*! ./VRM */ "./src/vrm/VRM.ts")); +__export(__webpack_require__(/*! ./VRMImporter */ "./src/vrm/VRMImporter.ts")); +__export(__webpack_require__(/*! ./reduceBones */ "./src/vrm/reduceBones.ts")); +__export(__webpack_require__(/*! ./blendshape */ "./src/vrm/blendshape/index.ts")); +__export(__webpack_require__(/*! ./debug */ "./src/vrm/debug/index.ts")); +__export(__webpack_require__(/*! ./firstperson */ "./src/vrm/firstperson/index.ts")); +__export(__webpack_require__(/*! ./humanoid */ "./src/vrm/humanoid/index.ts")); +__export(__webpack_require__(/*! ./lookat */ "./src/vrm/lookat/index.ts")); +__export(__webpack_require__(/*! ./springbone */ "./src/vrm/springbone/index.ts")); +__export(__webpack_require__(/*! ./types */ "./src/vrm/types/index.ts")); +__export(__webpack_require__(/*! ./material */ "./src/vrm/material/index.ts")); + + +/***/ }), + +/***/ "./src/vrm/lookat/CurveMapper.ts": +/*!***************************************!*\ + !*** ./src/vrm/lookat/CurveMapper.ts ***! + \***************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var hermiteSpline = function (y0, y1, t0, t1, x) { + var xc = x * x * x; + var xs = x * x; + var dy = y1 - y0; + var h01 = -2.0 * xc + 3.0 * xs; + var h10 = xc - 2.0 * xs + x; + var h11 = xc - xs; + return y0 + dy * h01 + t0 * h10 + t1 * h11; +}; +var evaluateCurve = function (arr, x) { + if (arr.length < 8) { + throw new Error('evaluateCurve: Invalid curve detected! (Array length must be 8 at least)'); + } + if (arr.length % 4 !== 0) { + throw new Error('evaluateCurve: Invalid curve detected! (Array length must be multiples of 4'); + } + var outNode; + for (outNode = 0;; outNode++) { + if (arr.length <= 4 * outNode) { + return arr[4 * outNode - 3]; + } + else if (x <= arr[4 * outNode]) { + break; + } + } + var inNode = outNode - 1; + if (inNode < 0) { + return arr[4 * inNode + 5]; + } + var x0 = arr[4 * inNode]; + var x1 = arr[4 * outNode]; + var xHermite = (x - x0) / (x1 - x0); + var y0 = arr[4 * inNode + 1]; + var y1 = arr[4 * outNode + 1]; + var t0 = arr[4 * inNode + 3]; + var t1 = arr[4 * outNode + 2]; + return hermiteSpline(y0, y1, t0, t1, xHermite); +}; +var CurveMapper = (function () { + function CurveMapper(xRange, yRange, curve) { + this.curve = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0]; + this.curveXRangeDegree = 90.0; + this.curveYRangeDegree = 10.0; + if (xRange !== undefined) { + this.curveXRangeDegree = xRange; + } + if (yRange !== undefined) { + this.curveYRangeDegree = yRange; + } + if (curve !== undefined) { + this.curve = curve; + } + } + CurveMapper.prototype.map = function (src) { + var clampedSrc = Math.min(Math.max(src, 0.0), this.curveXRangeDegree); + var x = clampedSrc / this.curveXRangeDegree; + return this.curveYRangeDegree * evaluateCurve(this.curve, x); + }; + return CurveMapper; +}()); +exports.CurveMapper = CurveMapper; + + +/***/ }), + +/***/ "./src/vrm/lookat/VRMLookAtApplyer.ts": +/*!********************************************!*\ + !*** ./src/vrm/lookat/VRMLookAtApplyer.ts ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var VRMLookAtApplyer = (function () { + function VRMLookAtApplyer() { + } + return VRMLookAtApplyer; +}()); +exports.VRMLookAtApplyer = VRMLookAtApplyer; + + +/***/ }), + +/***/ "./src/vrm/lookat/VRMLookAtBlendShapeApplyer.ts": +/*!******************************************************!*\ + !*** ./src/vrm/lookat/VRMLookAtBlendShapeApplyer.ts ***! + \******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var types_1 = __webpack_require__(/*! ../types */ "./src/vrm/types/index.ts"); +var VRMLookAtApplyer_1 = __webpack_require__(/*! ./VRMLookAtApplyer */ "./src/vrm/lookat/VRMLookAtApplyer.ts"); +var VRMLookAtBlendShapeApplyer = (function (_super) { + __extends(VRMLookAtBlendShapeApplyer, _super); + function VRMLookAtBlendShapeApplyer(blendShapeProxy, curveHorizontal, curveVerticalDown, curveVerticalUp) { + var _this = _super.call(this) || this; + _this.type = types_1.VRMSchema.FirstPersonLookAtTypeName.BlendShape; + _this._curveHorizontal = curveHorizontal; + _this._curveVerticalDown = curveVerticalDown; + _this._curveVerticalUp = curveVerticalUp; + _this._blendShapeProxy = blendShapeProxy; + return _this; + } + VRMLookAtBlendShapeApplyer.prototype.name = function () { + return types_1.VRMSchema.FirstPersonLookAtTypeName.BlendShape; + }; + VRMLookAtBlendShapeApplyer.prototype.lookAt = function (euler) { + var srcX = euler.x; + var srcY = euler.y; + if (srcX < 0.0) { + this._blendShapeProxy.setValue(types_1.VRMSchema.BlendShapePresetName.Lookup, 0.0); + this._blendShapeProxy.setValue(types_1.VRMSchema.BlendShapePresetName.Lookdown, this._curveVerticalDown.map(-srcX)); + } + else { + this._blendShapeProxy.setValue(types_1.VRMSchema.BlendShapePresetName.Lookdown, 0.0); + this._blendShapeProxy.setValue(types_1.VRMSchema.BlendShapePresetName.Lookup, this._curveVerticalUp.map(srcX)); + } + if (srcY < 0.0) { + this._blendShapeProxy.setValue(types_1.VRMSchema.BlendShapePresetName.Lookleft, 0.0); + this._blendShapeProxy.setValue(types_1.VRMSchema.BlendShapePresetName.Lookright, this._curveHorizontal.map(-srcY)); + } + else { + this._blendShapeProxy.setValue(types_1.VRMSchema.BlendShapePresetName.Lookright, 0.0); + this._blendShapeProxy.setValue(types_1.VRMSchema.BlendShapePresetName.Lookleft, this._curveHorizontal.map(srcY)); + } + }; + return VRMLookAtBlendShapeApplyer; +}(VRMLookAtApplyer_1.VRMLookAtApplyer)); +exports.VRMLookAtBlendShapeApplyer = VRMLookAtBlendShapeApplyer; + + +/***/ }), + +/***/ "./src/vrm/lookat/VRMLookAtBoneApplyer.ts": +/*!************************************************!*\ + !*** ./src/vrm/lookat/VRMLookAtBoneApplyer.ts ***! + \************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var types_1 = __webpack_require__(/*! ../types */ "./src/vrm/types/index.ts"); +var VRMLookAtApplyer_1 = __webpack_require__(/*! ./VRMLookAtApplyer */ "./src/vrm/lookat/VRMLookAtApplyer.ts"); +var VRMLookAtHead_1 = __webpack_require__(/*! ./VRMLookAtHead */ "./src/vrm/lookat/VRMLookAtHead.ts"); +var _euler = new THREE.Euler(0.0, 0.0, 0.0, VRMLookAtHead_1.VRMLookAtHead.EULER_ORDER); +var VRMLookAtBoneApplyer = (function (_super) { + __extends(VRMLookAtBoneApplyer, _super); + function VRMLookAtBoneApplyer(humanoid, curveHorizontalInner, curveHorizontalOuter, curveVerticalDown, curveVerticalUp) { + var _this = _super.call(this) || this; + _this.type = types_1.VRMSchema.FirstPersonLookAtTypeName.Bone; + _this._curveHorizontalInner = curveHorizontalInner; + _this._curveHorizontalOuter = curveHorizontalOuter; + _this._curveVerticalDown = curveVerticalDown; + _this._curveVerticalUp = curveVerticalUp; + _this._leftEye = humanoid.getBoneNode(types_1.VRMSchema.HumanoidBoneName.LeftEye); + _this._rightEye = humanoid.getBoneNode(types_1.VRMSchema.HumanoidBoneName.RightEye); + return _this; + } + VRMLookAtBoneApplyer.prototype.lookAt = function (euler) { + var srcX = euler.x; + var srcY = euler.y; + if (this._leftEye) { + if (srcX < 0.0) { + _euler.x = -this._curveVerticalDown.map(-srcX); + } + else { + _euler.x = this._curveVerticalUp.map(srcX); + } + if (srcY < 0.0) { + _euler.y = -this._curveHorizontalInner.map(-srcY); + } + else { + _euler.y = this._curveHorizontalOuter.map(srcY); + } + this._leftEye.quaternion.setFromEuler(_euler); + } + if (this._rightEye) { + if (srcX < 0.0) { + _euler.x = -this._curveVerticalDown.map(-srcX); + } + else { + _euler.x = this._curveVerticalUp.map(srcX); + } + if (srcY < 0.0) { + _euler.y = -this._curveHorizontalOuter.map(-srcY); + } + else { + _euler.y = this._curveHorizontalInner.map(srcY); + } + this._rightEye.quaternion.setFromEuler(_euler); + } + }; + return VRMLookAtBoneApplyer; +}(VRMLookAtApplyer_1.VRMLookAtApplyer)); +exports.VRMLookAtBoneApplyer = VRMLookAtBoneApplyer; + + +/***/ }), + +/***/ "./src/vrm/lookat/VRMLookAtHead.ts": +/*!*****************************************!*\ + !*** ./src/vrm/lookat/VRMLookAtHead.ts ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var math_1 = __webpack_require__(/*! ../utils/math */ "./src/vrm/utils/math.ts"); +var VECTOR3_FRONT = Object.freeze(new THREE.Vector3(0.0, 0.0, -1.0)); +var _v3A = new THREE.Vector3(); +var _v3B = new THREE.Vector3(); +var _v3C = new THREE.Vector3(); +var _quat = new THREE.Quaternion(); +var VRMLookAtHead = (function () { + function VRMLookAtHead(firstPerson, applyer) { + this.autoUpdate = true; + this._euler = new THREE.Euler(0.0, 0.0, 0.0, VRMLookAtHead.EULER_ORDER); + this.firstPerson = firstPerson; + this.applyer = applyer; + } + VRMLookAtHead.prototype.getLookAtWorldDirection = function (target) { + var rot = math_1.getWorldQuaternionLite(this.firstPerson.firstPersonBone, _quat); + return target + .copy(VECTOR3_FRONT) + .applyEuler(this._euler) + .applyQuaternion(rot); + }; + VRMLookAtHead.prototype.lookAt = function (position) { + this._calcEuler(this._euler, position); + if (this.applyer) { + this.applyer.lookAt(this._euler); + } + }; + VRMLookAtHead.prototype.update = function (delta) { + if (this.target && this.autoUpdate) { + this.lookAt(this.target.getWorldPosition(_v3A)); + if (this.applyer) { + this.applyer.lookAt(this._euler); + } + } + }; + VRMLookAtHead.prototype._calcEuler = function (target, position) { + var headPosition = this.firstPerson.getFirstPersonWorldPosition(_v3B); + var lookAtDir = _v3C + .copy(position) + .sub(headPosition) + .normalize(); + lookAtDir.applyQuaternion(math_1.getWorldQuaternionLite(this.firstPerson.firstPersonBone, _quat).inverse()); + target.x = Math.atan2(lookAtDir.y, Math.sqrt(lookAtDir.x * lookAtDir.x + lookAtDir.z * lookAtDir.z)); + target.y = Math.atan2(-lookAtDir.x, -lookAtDir.z); + return target; + }; + VRMLookAtHead.EULER_ORDER = 'YXZ'; + return VRMLookAtHead; +}()); +exports.VRMLookAtHead = VRMLookAtHead; + + +/***/ }), + +/***/ "./src/vrm/lookat/VRMLookAtImporter.ts": +/*!*********************************************!*\ + !*** ./src/vrm/lookat/VRMLookAtImporter.ts ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var types_1 = __webpack_require__(/*! ../types */ "./src/vrm/types/index.ts"); +var CurveMapper_1 = __webpack_require__(/*! ./CurveMapper */ "./src/vrm/lookat/CurveMapper.ts"); +var VRMLookAtBlendShapeApplyer_1 = __webpack_require__(/*! ./VRMLookAtBlendShapeApplyer */ "./src/vrm/lookat/VRMLookAtBlendShapeApplyer.ts"); +var VRMLookAtBoneApplyer_1 = __webpack_require__(/*! ./VRMLookAtBoneApplyer */ "./src/vrm/lookat/VRMLookAtBoneApplyer.ts"); +var VRMLookAtHead_1 = __webpack_require__(/*! ./VRMLookAtHead */ "./src/vrm/lookat/VRMLookAtHead.ts"); +var VRMLookAtImporter = (function () { + function VRMLookAtImporter() { + } + VRMLookAtImporter.prototype.import = function (gltf, firstPerson, blendShapeProxy, humanoid) { + var vrmExt = gltf.parser.json.extensions && gltf.parser.json.extensions.VRM; + if (!vrmExt) { + return null; + } + var schemaFirstPerson = vrmExt.firstPerson; + if (!schemaFirstPerson) { + return null; + } + var applyer = this._importApplyer(schemaFirstPerson, blendShapeProxy, humanoid); + return new VRMLookAtHead_1.VRMLookAtHead(firstPerson, applyer || undefined); + }; + VRMLookAtImporter.prototype._importApplyer = function (schemaFirstPerson, blendShapeProxy, humanoid) { + var lookAtHorizontalInner = schemaFirstPerson.lookAtHorizontalInner; + var lookAtHorizontalOuter = schemaFirstPerson.lookAtHorizontalOuter; + var lookAtVerticalDown = schemaFirstPerson.lookAtVerticalDown; + var lookAtVerticalUp = schemaFirstPerson.lookAtVerticalUp; + switch (schemaFirstPerson.lookAtTypeName) { + case types_1.VRMSchema.FirstPersonLookAtTypeName.Bone: { + if (lookAtHorizontalInner === undefined || + lookAtHorizontalOuter === undefined || + lookAtVerticalDown === undefined || + lookAtVerticalUp === undefined) { + return null; + } + else { + return new VRMLookAtBoneApplyer_1.VRMLookAtBoneApplyer(humanoid, this._importCurveMapperBone(lookAtHorizontalInner), this._importCurveMapperBone(lookAtHorizontalOuter), this._importCurveMapperBone(lookAtVerticalDown), this._importCurveMapperBone(lookAtVerticalUp)); + } + } + case types_1.VRMSchema.FirstPersonLookAtTypeName.BlendShape: { + if (lookAtHorizontalOuter === undefined || lookAtVerticalDown === undefined || lookAtVerticalUp === undefined) { + return null; + } + else { + return new VRMLookAtBlendShapeApplyer_1.VRMLookAtBlendShapeApplyer(blendShapeProxy, this._importCurveMapperBlendShape(lookAtHorizontalOuter), this._importCurveMapperBlendShape(lookAtVerticalDown), this._importCurveMapperBlendShape(lookAtVerticalUp)); + } + } + default: { + return null; + } + } + }; + VRMLookAtImporter.prototype._importCurveMapperBone = function (map) { + return new CurveMapper_1.CurveMapper(typeof map.xRange === 'number' ? THREE.Math.DEG2RAD * map.xRange : undefined, typeof map.yRange === 'number' ? THREE.Math.DEG2RAD * map.yRange : undefined, map.curve); + }; + VRMLookAtImporter.prototype._importCurveMapperBlendShape = function (map) { + return new CurveMapper_1.CurveMapper(typeof map.xRange === 'number' ? THREE.Math.DEG2RAD * map.xRange : undefined, map.yRange, map.curve); + }; + return VRMLookAtImporter; +}()); +exports.VRMLookAtImporter = VRMLookAtImporter; + + +/***/ }), + +/***/ "./src/vrm/lookat/index.ts": +/*!*********************************!*\ + !*** ./src/vrm/lookat/index.ts ***! + \*********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(__webpack_require__(/*! ./CurveMapper */ "./src/vrm/lookat/CurveMapper.ts")); +__export(__webpack_require__(/*! ./VRMLookAtApplyer */ "./src/vrm/lookat/VRMLookAtApplyer.ts")); +__export(__webpack_require__(/*! ./VRMLookAtBlendShapeApplyer */ "./src/vrm/lookat/VRMLookAtBlendShapeApplyer.ts")); +__export(__webpack_require__(/*! ./VRMLookAtBoneApplyer */ "./src/vrm/lookat/VRMLookAtBoneApplyer.ts")); +__export(__webpack_require__(/*! ./VRMLookAtHead */ "./src/vrm/lookat/VRMLookAtHead.ts")); +__export(__webpack_require__(/*! ./VRMLookAtImporter */ "./src/vrm/lookat/VRMLookAtImporter.ts")); + + +/***/ }), + +/***/ "./src/vrm/material/MToonMaterial.ts": +/*!*******************************************!*\ + !*** ./src/vrm/material/MToonMaterial.ts ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var getTexelDecodingFunction_1 = __webpack_require__(/*! ./getTexelDecodingFunction */ "./src/vrm/material/getTexelDecodingFunction.ts"); +var mtoon_vert_1 = __webpack_require__(/*! ./shaders/mtoon.vert */ "./src/vrm/material/shaders/mtoon.vert"); +var mtoon_frag_1 = __webpack_require__(/*! ./shaders/mtoon.frag */ "./src/vrm/material/shaders/mtoon.frag"); +var TAU = 2.0 * Math.PI; +var MToonMaterialCullMode; +(function (MToonMaterialCullMode) { + MToonMaterialCullMode[MToonMaterialCullMode["Off"] = 0] = "Off"; + MToonMaterialCullMode[MToonMaterialCullMode["Front"] = 1] = "Front"; + MToonMaterialCullMode[MToonMaterialCullMode["Back"] = 2] = "Back"; +})(MToonMaterialCullMode = exports.MToonMaterialCullMode || (exports.MToonMaterialCullMode = {})); +var MToonMaterialDebugMode; +(function (MToonMaterialDebugMode) { + MToonMaterialDebugMode[MToonMaterialDebugMode["None"] = 0] = "None"; + MToonMaterialDebugMode[MToonMaterialDebugMode["Normal"] = 1] = "Normal"; + MToonMaterialDebugMode[MToonMaterialDebugMode["LitShadeRate"] = 2] = "LitShadeRate"; + MToonMaterialDebugMode[MToonMaterialDebugMode["UV"] = 3] = "UV"; +})(MToonMaterialDebugMode = exports.MToonMaterialDebugMode || (exports.MToonMaterialDebugMode = {})); +var MToonMaterialOutlineColorMode; +(function (MToonMaterialOutlineColorMode) { + MToonMaterialOutlineColorMode[MToonMaterialOutlineColorMode["FixedColor"] = 0] = "FixedColor"; + MToonMaterialOutlineColorMode[MToonMaterialOutlineColorMode["MixedLighting"] = 1] = "MixedLighting"; +})(MToonMaterialOutlineColorMode = exports.MToonMaterialOutlineColorMode || (exports.MToonMaterialOutlineColorMode = {})); +var MToonMaterialOutlineWidthMode; +(function (MToonMaterialOutlineWidthMode) { + MToonMaterialOutlineWidthMode[MToonMaterialOutlineWidthMode["None"] = 0] = "None"; + MToonMaterialOutlineWidthMode[MToonMaterialOutlineWidthMode["WorldCoordinates"] = 1] = "WorldCoordinates"; + MToonMaterialOutlineWidthMode[MToonMaterialOutlineWidthMode["ScreenCoordinates"] = 2] = "ScreenCoordinates"; +})(MToonMaterialOutlineWidthMode = exports.MToonMaterialOutlineWidthMode || (exports.MToonMaterialOutlineWidthMode = {})); +var MToonMaterialRenderMode; +(function (MToonMaterialRenderMode) { + MToonMaterialRenderMode[MToonMaterialRenderMode["Opaque"] = 0] = "Opaque"; + MToonMaterialRenderMode[MToonMaterialRenderMode["Cutout"] = 1] = "Cutout"; + MToonMaterialRenderMode[MToonMaterialRenderMode["Transparent"] = 2] = "Transparent"; + MToonMaterialRenderMode[MToonMaterialRenderMode["TransparentWithZWrite"] = 3] = "TransparentWithZWrite"; +})(MToonMaterialRenderMode = exports.MToonMaterialRenderMode || (exports.MToonMaterialRenderMode = {})); +var MToonMaterial = (function (_super) { + __extends(MToonMaterial, _super); + function MToonMaterial(colorSpaceGamma, parameters) { + var _this = _super.call(this) || this; + _this.isMToonMaterial = true; + _this.cutoff = 0.5; + _this.color = new THREE.Vector4(1.0, 1.0, 1.0, 1.0); + _this.shadeColor = new THREE.Vector4(0.97, 0.81, 0.86, 1.0); + _this.map = null; + _this.mainTex_ST = new THREE.Vector4(0.0, 0.0, 1.0, 1.0); + _this.shadeTexture = null; + _this.bumpScale = 1.0; + _this.normalMap = null; + _this.receiveShadowRate = 1.0; + _this.receiveShadowTexture = null; + _this.shadingGradeRate = 1.0; + _this.shadingGradeTexture = null; + _this.shadeShift = 0.0; + _this.shadeToony = 0.9; + _this.lightColorAttenuation = 0.0; + _this.indirectLightIntensity = 0.1; + _this.rimTexture = null; + _this.rimColor = new THREE.Vector4(0.0, 0.0, 0.0, 1.0); + _this.rimLightingMix = 0.0; + _this.rimFresnelPower = 1.0; + _this.rimLift = 0.0; + _this.sphereAdd = null; + _this.emissionColor = new THREE.Vector4(0.0, 0.0, 0.0, 1.0); + _this.emissiveMap = null; + _this.outlineWidthTexture = null; + _this.outlineWidth = 0.5; + _this.outlineScaledMaxDistance = 1.0; + _this.outlineColor = new THREE.Vector4(0.0, 0.0, 0.0, 1.0); + _this.outlineLightingMix = 1.0; + _this.uvAnimMaskTexture = null; + _this.uvAnimScrollX = 0.0; + _this.uvAnimScrollY = 0.0; + _this.uvAnimRotation = 0.0; + _this.shouldApplyUniforms = true; + _this._debugMode = MToonMaterialDebugMode.None; + _this._blendMode = MToonMaterialRenderMode.Opaque; + _this._outlineWidthMode = MToonMaterialOutlineWidthMode.None; + _this._outlineColorMode = MToonMaterialOutlineColorMode.FixedColor; + _this._cullMode = MToonMaterialCullMode.Back; + _this._outlineCullMode = MToonMaterialCullMode.Front; + _this._isOutline = false; + _this._uvAnimOffsetX = 0.0; + _this._uvAnimOffsetY = 0.0; + _this._uvAnimPhase = 0.0; + _this._colorSpaceGamma = colorSpaceGamma; + if (parameters === undefined) { + parameters = {}; + } + [ + 'mToonVersion', + 'shadeTexture_ST', + 'bumpMap_ST', + 'receiveShadowTexture_ST', + 'shadingGradeTexture_ST', + 'sphereAdd_ST', + 'emissionMap_ST', + 'outlineWidthTexture_ST', + 'srcBlend', + 'dstBlend', + ].forEach(function (key) { + if (parameters[key] !== undefined) { + delete parameters[key]; + } + }); + parameters.fog = true; + parameters.lights = true; + parameters.clipping = true; + parameters.skinning = parameters.skinning || false; + parameters.morphTargets = parameters.morphTargets || false; + parameters.morphNormals = parameters.morphNormals || false; + parameters.uniforms = THREE.UniformsUtils.merge([ + THREE.UniformsLib.common, + THREE.UniformsLib.normalmap, + THREE.UniformsLib.emissivemap, + THREE.UniformsLib.fog, + THREE.UniformsLib.lights, + { + cutoff: { value: 0.5 }, + color: { value: new THREE.Color(1.0, 1.0, 1.0) }, + colorAlpha: { value: 1.0 }, + shadeColor: { value: new THREE.Color(0.97, 0.81, 0.86) }, + mainTex_ST: { value: new THREE.Vector4(0.0, 0.0, 1.0, 1.0) }, + shadeTexture: { value: null }, + bumpScale: { value: 1.0 }, + receiveShadowRate: { value: 1.0 }, + receiveShadowTexture: { value: null }, + shadingGradeRate: { value: 1.0 }, + shadingGradeTexture: { value: null }, + shadeShift: { value: 0.0 }, + shadeToony: { value: 0.9 }, + lightColorAttenuation: { value: 0.0 }, + indirectLightIntensity: { value: 0.1 }, + rimTexture: { value: null }, + rimColor: { value: new THREE.Color(0.0, 0.0, 0.0) }, + rimLightingMix: { value: 0.0 }, + rimFresnelPower: { value: 1.0 }, + rimLift: { value: 0.0 }, + sphereAdd: { value: null }, + emissionColor: { value: new THREE.Color(0.0, 0.0, 0.0) }, + outlineWidthTexture: { value: null }, + outlineWidth: { value: 0.5 }, + outlineScaledMaxDistance: { value: 1.0 }, + outlineColor: { value: new THREE.Color(0.0, 0.0, 0.0) }, + outlineLightingMix: { value: 1.0 }, + uvAnimMaskTexture: { value: null }, + uvAnimOffsetX: { value: 0.0 }, + uvAnimOffsetY: { value: 0.0 }, + uvAnimTheta: { value: 0.0 }, + }, + ]); + _this.setValues(parameters); + _this._updateShaderCode(); + _this._applyUniforms(); + return _this; + } + Object.defineProperty(MToonMaterial.prototype, "mainTex", { + get: function () { + return this.map; + }, + set: function (t) { + this.map = t; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MToonMaterial.prototype, "bumpMap", { + get: function () { + return this.normalMap; + }, + set: function (t) { + this.normalMap = t; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MToonMaterial.prototype, "emissionMap", { + get: function () { + return this.emissiveMap; + }, + set: function (t) { + this.emissiveMap = t; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MToonMaterial.prototype, "blendMode", { + get: function () { + return this._blendMode; + }, + set: function (m) { + this._blendMode = m; + this.depthWrite = this._blendMode !== MToonMaterialRenderMode.Transparent; + this.transparent = + this._blendMode === MToonMaterialRenderMode.Transparent || + this._blendMode === MToonMaterialRenderMode.TransparentWithZWrite; + this._updateShaderCode(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MToonMaterial.prototype, "debugMode", { + get: function () { + return this._debugMode; + }, + set: function (m) { + this._debugMode = m; + this._updateShaderCode(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MToonMaterial.prototype, "outlineWidthMode", { + get: function () { + return this._outlineWidthMode; + }, + set: function (m) { + this._outlineWidthMode = m; + this._updateShaderCode(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MToonMaterial.prototype, "outlineColorMode", { + get: function () { + return this._outlineColorMode; + }, + set: function (m) { + this._outlineColorMode = m; + this._updateShaderCode(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MToonMaterial.prototype, "cullMode", { + get: function () { + return this._cullMode; + }, + set: function (m) { + this._cullMode = m; + this._updateCullFace(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MToonMaterial.prototype, "outlineCullMode", { + get: function () { + return this._outlineCullMode; + }, + set: function (m) { + this._outlineCullMode = m; + this._updateCullFace(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MToonMaterial.prototype, "zWrite", { + get: function () { + return this.depthWrite ? 1 : 0; + }, + set: function (i) { + this.depthWrite = 0.5 <= i; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(MToonMaterial.prototype, "isOutline", { + get: function () { + return this._isOutline; + }, + set: function (b) { + this._isOutline = b; + this._updateShaderCode(); + this._updateCullFace(); + }, + enumerable: true, + configurable: true + }); + MToonMaterial.prototype.updateVRMMaterials = function (delta) { + this._uvAnimOffsetX = this._uvAnimOffsetX + delta * this.uvAnimScrollX; + this._uvAnimOffsetY = this._uvAnimOffsetY + delta * this.uvAnimScrollY; + this._uvAnimPhase = this._uvAnimPhase + delta * this.uvAnimRotation; + this._applyUniforms(); + }; + MToonMaterial.prototype.copy = function (source) { + _super.prototype.copy.call(this, source); + this.cutoff = source.cutoff; + this.color.copy(source.color); + this.shadeColor.copy(source.shadeColor); + this.map = source.map; + this.mainTex_ST.copy(source.mainTex_ST); + this.shadeTexture = source.shadeTexture; + this.bumpScale = source.bumpScale; + this.normalMap = source.normalMap; + this.receiveShadowRate = source.receiveShadowRate; + this.receiveShadowTexture = source.receiveShadowTexture; + this.shadingGradeRate = source.shadingGradeRate; + this.shadingGradeTexture = source.shadingGradeTexture; + this.shadeShift = source.shadeShift; + this.shadeToony = source.shadeToony; + this.lightColorAttenuation = source.lightColorAttenuation; + this.indirectLightIntensity = source.indirectLightIntensity; + this.rimTexture = source.rimTexture; + this.rimColor.copy(source.rimColor); + this.rimLightingMix = source.rimLightingMix; + this.rimFresnelPower = source.rimFresnelPower; + this.rimLift = source.rimLift; + this.sphereAdd = source.sphereAdd; + this.emissionColor.copy(source.emissionColor); + this.emissiveMap = source.emissiveMap; + this.outlineWidthTexture = source.outlineWidthTexture; + this.outlineWidth = source.outlineWidth; + this.outlineScaledMaxDistance = source.outlineScaledMaxDistance; + this.outlineColor.copy(source.outlineColor); + this.outlineLightingMix = source.outlineLightingMix; + this.uvAnimMaskTexture = source.uvAnimMaskTexture; + this.uvAnimScrollX = source.uvAnimScrollX; + this.uvAnimScrollY = source.uvAnimScrollY; + this.uvAnimRotation = source.uvAnimRotation; + this.debugMode = source.debugMode; + this.blendMode = source.blendMode; + this.outlineWidthMode = source.outlineWidthMode; + this.outlineColorMode = source.outlineColorMode; + this.cullMode = source.cullMode; + this.outlineCullMode = source.outlineCullMode; + this.isOutline = source.isOutline; + return this; + }; + MToonMaterial.prototype._applyUniforms = function () { + this.uniforms.uvAnimOffsetX.value = this._uvAnimOffsetX; + this.uniforms.uvAnimOffsetY.value = this._uvAnimOffsetY; + this.uniforms.uvAnimTheta.value = TAU * this._uvAnimPhase; + if (!this.shouldApplyUniforms) { + return; + } + this.shouldApplyUniforms = false; + this.uniforms.cutoff.value = this.cutoff; + this.uniforms.color.value.setRGB(this.color.x, this.color.y, this.color.z); + if (!this._colorSpaceGamma) { + this.uniforms.color.value.convertSRGBToLinear(); + } + this.uniforms.colorAlpha.value = this.color.w; + this.uniforms.shadeColor.value.setRGB(this.shadeColor.x, this.shadeColor.y, this.shadeColor.z); + if (!this._colorSpaceGamma) { + this.uniforms.shadeColor.value.convertSRGBToLinear(); + } + this.uniforms.map.value = this.map; + this.uniforms.mainTex_ST.value.copy(this.mainTex_ST); + this.uniforms.shadeTexture.value = this.shadeTexture; + this.uniforms.bumpScale.value = this.bumpScale; + this.uniforms.normalMap.value = this.normalMap; + this.uniforms.receiveShadowRate.value = this.receiveShadowRate; + this.uniforms.receiveShadowTexture.value = this.receiveShadowTexture; + this.uniforms.shadingGradeRate.value = this.shadingGradeRate; + this.uniforms.shadingGradeTexture.value = this.shadingGradeTexture; + this.uniforms.shadeShift.value = this.shadeShift; + this.uniforms.shadeToony.value = this.shadeToony; + this.uniforms.lightColorAttenuation.value = this.lightColorAttenuation; + this.uniforms.indirectLightIntensity.value = this.indirectLightIntensity; + this.uniforms.rimTexture.value = this.rimTexture; + this.uniforms.rimColor.value.setRGB(this.rimColor.x, this.rimColor.y, this.rimColor.z); + if (!this._colorSpaceGamma) { + this.uniforms.rimColor.value.convertSRGBToLinear(); + } + this.uniforms.rimLightingMix.value = this.rimLightingMix; + this.uniforms.rimFresnelPower.value = this.rimFresnelPower; + this.uniforms.rimLift.value = this.rimLift; + this.uniforms.sphereAdd.value = this.sphereAdd; + this.uniforms.emissionColor.value.setRGB(this.emissionColor.x, this.emissionColor.y, this.emissionColor.z); + if (!this._colorSpaceGamma) { + this.uniforms.emissionColor.value.convertSRGBToLinear(); + } + this.uniforms.emissiveMap.value = this.emissiveMap; + this.uniforms.outlineWidthTexture.value = this.outlineWidthTexture; + this.uniforms.outlineWidth.value = this.outlineWidth; + this.uniforms.outlineScaledMaxDistance.value = this.outlineScaledMaxDistance; + this.uniforms.outlineColor.value.setRGB(this.outlineColor.x, this.outlineColor.y, this.outlineColor.z); + if (!this._colorSpaceGamma) { + this.uniforms.outlineColor.value.convertSRGBToLinear(); + } + this.uniforms.outlineLightingMix.value = this.outlineLightingMix; + this.uniforms.uvAnimMaskTexture.value = this.uvAnimMaskTexture; + this._updateCullFace(); + }; + MToonMaterial.prototype._updateShaderCode = function () { + this.defines = { + OUTLINE: this._isOutline, + BLENDMODE_OPAQUE: this._blendMode === MToonMaterialRenderMode.Opaque, + BLENDMODE_CUTOUT: this._blendMode === MToonMaterialRenderMode.Cutout, + BLENDMODE_TRANSPARENT: this._blendMode === MToonMaterialRenderMode.Transparent || + this._blendMode === MToonMaterialRenderMode.TransparentWithZWrite, + USE_SHADETEXTURE: this.shadeTexture !== null, + USE_RECEIVESHADOWTEXTURE: this.receiveShadowTexture !== null, + USE_SHADINGGRADETEXTURE: this.shadingGradeTexture !== null, + USE_RIMTEXTURE: this.rimTexture !== null, + USE_SPHEREADD: this.sphereAdd !== null, + USE_OUTLINEWIDTHTEXTURE: this.outlineWidthTexture !== null, + USE_UVANIMMASKTEXTURE: this.uvAnimMaskTexture !== null, + DEBUG_NORMAL: this._debugMode === MToonMaterialDebugMode.Normal, + DEBUG_LITSHADERATE: this._debugMode === MToonMaterialDebugMode.LitShadeRate, + DEBUG_UV: this._debugMode === MToonMaterialDebugMode.UV, + OUTLINE_WIDTH_WORLD: this._outlineWidthMode === MToonMaterialOutlineWidthMode.WorldCoordinates, + OUTLINE_WIDTH_SCREEN: this._outlineWidthMode === MToonMaterialOutlineWidthMode.ScreenCoordinates, + OUTLINE_COLOR_FIXED: this._outlineColorMode === MToonMaterialOutlineColorMode.FixedColor, + OUTLINE_COLOR_MIXED: this._outlineColorMode === MToonMaterialOutlineColorMode.MixedLighting, + }; + var encodings = (this.shadeTexture !== null + ? getTexelDecodingFunction_1.getTexelDecodingFunction('shadeTextureTexelToLinear', this.shadeTexture.encoding) + '\n' + : '') + + (this.sphereAdd !== null + ? getTexelDecodingFunction_1.getTexelDecodingFunction('sphereAddTexelToLinear', this.sphereAdd.encoding) + '\n' + : ''); + this.vertexShader = mtoon_vert_1.default; + this.fragmentShader = encodings + mtoon_frag_1.default; + this.needsUpdate = true; + }; + MToonMaterial.prototype._updateCullFace = function () { + if (!this.isOutline) { + if (this.cullMode === MToonMaterialCullMode.Off) { + this.side = THREE.DoubleSide; + } + else if (this.cullMode === MToonMaterialCullMode.Front) { + this.side = THREE.BackSide; + } + else if (this.cullMode === MToonMaterialCullMode.Back) { + this.side = THREE.FrontSide; + } + } + else { + if (this.outlineCullMode === MToonMaterialCullMode.Off) { + this.side = THREE.DoubleSide; + } + else if (this.outlineCullMode === MToonMaterialCullMode.Front) { + this.side = THREE.BackSide; + } + else if (this.outlineCullMode === MToonMaterialCullMode.Back) { + this.side = THREE.FrontSide; + } + } + }; + return MToonMaterial; +}(THREE.ShaderMaterial)); +exports.MToonMaterial = MToonMaterial; + + +/***/ }), + +/***/ "./src/vrm/material/VRMMaterialImporter.ts": +/*!*************************************************!*\ + !*** ./src/vrm/material/VRMMaterialImporter.ts ***! + \*************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var MToonMaterial_1 = __webpack_require__(/*! ./MToonMaterial */ "./src/vrm/material/MToonMaterial.ts"); +var VRMUnlitMaterial_1 = __webpack_require__(/*! ./VRMUnlitMaterial */ "./src/vrm/material/VRMUnlitMaterial.ts"); +var VRMMaterialImporter = (function () { + function VRMMaterialImporter(options) { + if (options === void 0) { options = {}; } + this._colorSpaceGamma = options.colorSpaceGamma || true; + this._requestEnvMap = options.requestEnvMap; + } + VRMMaterialImporter.prototype.convertGLTFMaterials = function (gltf) { + return __awaiter(this, void 0, Promise, function () { + var vrmExt, materialProperties, meshesMap, materialList, materials; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + vrmExt = gltf.parser.json.extensions && gltf.parser.json.extensions.VRM; + if (!vrmExt) { + return [2, null]; + } + materialProperties = vrmExt.materialProperties; + if (!materialProperties) { + return [2, null]; + } + return [4, gltf.parser.getDependencies('mesh')]; + case 1: + meshesMap = _a.sent(); + materialList = {}; + materials = []; + return [4, Promise.all(meshesMap.map(function (mesh, meshIndex) { return __awaiter(_this, void 0, void 0, function () { + var primitives; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + primitives = mesh.type === 'Group' ? mesh.children : [mesh]; + return [4, Promise.all(primitives.map(function (primitive, primitiveIndex) { return __awaiter(_this, void 0, void 0, function () { + var vrmMaterialIndex, props, vrmMaterials; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!Array.isArray(primitive.material)) { + primitive.material = [primitive.material]; + primitive.geometry.addGroup(0, primitive.geometry.index.count, 0); + } + vrmMaterialIndex = gltf.parser.json.meshes[meshIndex].primitives[primitiveIndex].material; + props = materialProperties[vrmMaterialIndex]; + if (!props) { + console.warn("VRMMaterialImporter: There are no material definition for material #" + vrmMaterialIndex + " on VRM extension."); + props = { shader: 'VRM_USE_GLTFSHADER' }; + } + if (!materialList[vrmMaterialIndex]) return [3, 1]; + vrmMaterials = materialList[vrmMaterialIndex]; + return [3, 3]; + case 1: return [4, this.createVRMMaterials(primitive.material[0], props, gltf)]; + case 2: + vrmMaterials = _a.sent(); + materialList[vrmMaterialIndex] = vrmMaterials; + materials.push(vrmMaterials.surface); + if (vrmMaterials.outline) { + materials.push(vrmMaterials.outline); + } + _a.label = 3; + case 3: + primitive.material[0] = vrmMaterials.surface; + if (this._requestEnvMap) { + this._requestEnvMap().then(function (envMap) { + vrmMaterials.surface.envMap = envMap; + vrmMaterials.surface.needsUpdate = true; + }); + } + primitive.renderOrder = props.renderQueue || 2000; + if (vrmMaterials.outline) { + primitive.material[1] = vrmMaterials.outline; + primitive.geometry.addGroup(0, primitive.geometry.index.count, 1); + } + return [2]; + } + }); + }); }))]; + case 1: + _a.sent(); + return [2]; + } + }); + }); }))]; + case 2: + _a.sent(); + return [2, materials]; + } + }); + }); + }; + VRMMaterialImporter.prototype.createVRMMaterials = function (originalMaterial, vrmProps, gltf) { + return __awaiter(this, void 0, Promise, function () { + var newSurface, newOutline, params_1, params, params, params, params; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!(vrmProps.shader === 'VRM/MToon')) return [3, 2]; + return [4, this._extractMaterialProperties(originalMaterial, vrmProps, gltf)]; + case 1: + params_1 = _a.sent(); + ['srcBlend', 'dstBlend', 'isFirstSetup'].forEach(function (name) { + if (params_1[name] !== undefined) { + delete params_1[name]; + } + }); + ['mainTex', 'shadeTexture', 'emission', 'sphereAdd'].forEach(function (name) { + if (params_1[name] !== undefined) { + params_1[name].encoding = _this._colorSpaceGamma ? THREE.LinearEncoding : THREE.sRGBEncoding; + } + }); + newSurface = new MToonMaterial_1.MToonMaterial(this._colorSpaceGamma, params_1); + if (params_1.outlineWidthMode !== MToonMaterial_1.MToonMaterialOutlineWidthMode.None) { + params_1.isOutline = true; + newOutline = new MToonMaterial_1.MToonMaterial(this._colorSpaceGamma, params_1); + } + return [3, 11]; + case 2: + if (!(vrmProps.shader === 'VRM/UnlitTexture')) return [3, 4]; + return [4, this._extractMaterialProperties(originalMaterial, vrmProps, gltf)]; + case 3: + params = _a.sent(); + params.renderType = VRMUnlitMaterial_1.VRMUnlitMaterialRenderType.Opaque; + newSurface = new VRMUnlitMaterial_1.VRMUnlitMaterial(params); + return [3, 11]; + case 4: + if (!(vrmProps.shader === 'VRM/UnlitCutout')) return [3, 6]; + return [4, this._extractMaterialProperties(originalMaterial, vrmProps, gltf)]; + case 5: + params = _a.sent(); + params.renderType = VRMUnlitMaterial_1.VRMUnlitMaterialRenderType.Cutout; + newSurface = new VRMUnlitMaterial_1.VRMUnlitMaterial(params); + return [3, 11]; + case 6: + if (!(vrmProps.shader === 'VRM/UnlitTransparent')) return [3, 8]; + return [4, this._extractMaterialProperties(originalMaterial, vrmProps, gltf)]; + case 7: + params = _a.sent(); + params.renderType = VRMUnlitMaterial_1.VRMUnlitMaterialRenderType.Transparent; + newSurface = new VRMUnlitMaterial_1.VRMUnlitMaterial(params); + return [3, 11]; + case 8: + if (!(vrmProps.shader === 'VRM/UnlitTransparentZWrite')) return [3, 10]; + return [4, this._extractMaterialProperties(originalMaterial, vrmProps, gltf)]; + case 9: + params = _a.sent(); + params.renderType = VRMUnlitMaterial_1.VRMUnlitMaterialRenderType.TransparentWithZWrite; + newSurface = new VRMUnlitMaterial_1.VRMUnlitMaterial(params); + return [3, 11]; + case 10: + if (vrmProps.shader !== 'VRM_USE_GLTFSHADER') { + console.warn("Unknown shader detected: \"" + vrmProps.shader + "\""); + } + newSurface = this._convertGLTFMaterial(originalMaterial.clone()); + _a.label = 11; + case 11: + newSurface.name = originalMaterial.name; + newSurface.userData = JSON.parse(JSON.stringify(originalMaterial.userData)); + newSurface.userData.vrmMaterialProperties = vrmProps; + if (newOutline) { + newOutline.name = originalMaterial.name + ' (Outline)'; + newOutline.userData = JSON.parse(JSON.stringify(originalMaterial.userData)); + newOutline.userData.vrmMaterialProperties = vrmProps; + } + return [2, { + surface: newSurface, + outline: newOutline, + }]; + } + }); + }); + }; + VRMMaterialImporter.prototype._renameMaterialProperty = function (name) { + if (name[0] !== '_') { + console.warn("VRMMaterials: Given property name \"" + name + "\" might be invalid"); + return name; + } + name = name.substring(1); + if (!/[A-Z]/.test(name[0])) { + console.warn("VRMMaterials: Given property name \"" + name + "\" might be invalid"); + return name; + } + return name[0].toLowerCase() + name.substring(1); + }; + VRMMaterialImporter.prototype._convertGLTFMaterial = function (material) { + if (material.isMeshStandardMaterial) { + var mtl = material; + if (this._colorSpaceGamma) { + if (mtl.map) { + mtl.map.encoding = THREE.LinearEncoding; + } + if (mtl.emissiveMap) { + mtl.emissiveMap.encoding = THREE.LinearEncoding; + } + } + else { + mtl.color.convertSRGBToLinear(); + mtl.emissive.convertSRGBToLinear(); + } + } + if (material.isMeshBasicMaterial) { + var mtl = material; + if (this._colorSpaceGamma) { + if (mtl.map) { + mtl.map.encoding = THREE.LinearEncoding; + } + } + else { + mtl.color.convertSRGBToLinear(); + } + } + return material; + }; + VRMMaterialImporter.prototype._extractMaterialProperties = function (originalMaterial, vrmProps, gltf) { + var taskList = []; + var params = {}; + if (vrmProps.textureProperties) { + var _loop_1 = function (name) { + var newName = this_1._renameMaterialProperty(name); + var textureIndex = vrmProps.textureProperties[name]; + taskList.push(gltf.parser.getDependency('texture', textureIndex).then(function (texture) { + params[newName] = texture; + })); + }; + var this_1 = this; + for (var _i = 0, _a = Object.keys(vrmProps.textureProperties); _i < _a.length; _i++) { + var name = _a[_i]; + _loop_1(name); + } + } + if (vrmProps.floatProperties) { + for (var _b = 0, _c = Object.keys(vrmProps.floatProperties); _b < _c.length; _b++) { + var name = _c[_b]; + var newName = this._renameMaterialProperty(name); + params[newName] = vrmProps.floatProperties[name]; + } + } + if (vrmProps.vectorProperties) { + var _loop_2 = function (name) { + var _a; + var newName = this_2._renameMaterialProperty(name); + var isTextureST = [ + '_MainTex', + '_ShadeTexture', + '_BumpMap', + '_ReceiveShadowTexture', + '_ShadingGradeTexture', + '_SphereAdd', + '_EmissionMap', + '_OutlineWidthTexture', + ].some(function (textureName) { return name === textureName; }); + if (isTextureST) { + newName += '_ST'; + } + params[newName] = new ((_a = THREE.Vector4).bind.apply(_a, [void 0].concat(vrmProps.vectorProperties[name])))(); + }; + var this_2 = this; + for (var _d = 0, _e = Object.keys(vrmProps.vectorProperties); _d < _e.length; _d++) { + var name = _e[_d]; + _loop_2(name); + } + } + if (vrmProps.keywordMap._ALPHATEST_ON && params.blendMode === MToonMaterial_1.MToonMaterialRenderMode.Opaque) { + params.blendMode = MToonMaterial_1.MToonMaterialRenderMode.Cutout; + } + params.skinning = originalMaterial.skinning || false; + params.morphTargets = originalMaterial.morphTargets || false; + params.morphNormals = originalMaterial.morphNormals || false; + return Promise.all(taskList).then(function () { return params; }); + }; + return VRMMaterialImporter; +}()); +exports.VRMMaterialImporter = VRMMaterialImporter; + + +/***/ }), + +/***/ "./src/vrm/material/VRMUnlitMaterial.ts": +/*!**********************************************!*\ + !*** ./src/vrm/material/VRMUnlitMaterial.ts ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var unlit_vert_1 = __webpack_require__(/*! ./shaders/unlit.vert */ "./src/vrm/material/shaders/unlit.vert"); +var unlit_frag_1 = __webpack_require__(/*! ./shaders/unlit.frag */ "./src/vrm/material/shaders/unlit.frag"); +var VRMUnlitMaterialRenderType; +(function (VRMUnlitMaterialRenderType) { + VRMUnlitMaterialRenderType[VRMUnlitMaterialRenderType["Opaque"] = 0] = "Opaque"; + VRMUnlitMaterialRenderType[VRMUnlitMaterialRenderType["Cutout"] = 1] = "Cutout"; + VRMUnlitMaterialRenderType[VRMUnlitMaterialRenderType["Transparent"] = 2] = "Transparent"; + VRMUnlitMaterialRenderType[VRMUnlitMaterialRenderType["TransparentWithZWrite"] = 3] = "TransparentWithZWrite"; +})(VRMUnlitMaterialRenderType = exports.VRMUnlitMaterialRenderType || (exports.VRMUnlitMaterialRenderType = {})); +var VRMUnlitMaterial = (function (_super) { + __extends(VRMUnlitMaterial, _super); + function VRMUnlitMaterial(parameters) { + var _this = _super.call(this) || this; + _this.isVRMUnlitMaterial = true; + _this.cutoff = 0.5; + _this.map = null; + _this.mainTex_ST = new THREE.Vector4(0.0, 0.0, 1.0, 1.0); + _this._renderType = VRMUnlitMaterialRenderType.Opaque; + _this.shouldApplyUniforms = true; + if (parameters === undefined) { + parameters = {}; + } + parameters.fog = true; + parameters.clipping = true; + parameters.skinning = parameters.skinning || false; + parameters.morphTargets = parameters.morphTargets || false; + parameters.morphNormals = parameters.morphNormals || false; + parameters.uniforms = THREE.UniformsUtils.merge([ + THREE.UniformsLib.common, + THREE.UniformsLib.fog, + { + cutoff: { value: 0.5 }, + mainTex_ST: { value: new THREE.Vector4(0.0, 0.0, 1.0, 1.0) }, + }, + ]); + _this.setValues(parameters); + _this._updateShaderCode(); + _this._applyUniforms(); + return _this; + } + Object.defineProperty(VRMUnlitMaterial.prototype, "mainTex", { + get: function () { + return this.map; + }, + set: function (t) { + this.map = t; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(VRMUnlitMaterial.prototype, "renderType", { + get: function () { + return this._renderType; + }, + set: function (t) { + this._renderType = t; + this.depthWrite = this._renderType !== VRMUnlitMaterialRenderType.Transparent; + this.transparent = + this._renderType === VRMUnlitMaterialRenderType.Transparent || + this._renderType === VRMUnlitMaterialRenderType.TransparentWithZWrite; + this._updateShaderCode(); + }, + enumerable: true, + configurable: true + }); + VRMUnlitMaterial.prototype.updateVRMMaterials = function (delta) { + this._applyUniforms(); + }; + VRMUnlitMaterial.prototype.copy = function (source) { + _super.prototype.copy.call(this, source); + this.cutoff = source.cutoff; + this.map = source.map; + this.mainTex_ST.copy(source.mainTex_ST); + this.renderType = source.renderType; + return this; + }; + VRMUnlitMaterial.prototype._applyUniforms = function () { + if (!this.shouldApplyUniforms) { + return; + } + this.shouldApplyUniforms = false; + this.uniforms.cutoff.value = this.cutoff; + this.uniforms.map.value = this.map; + this.uniforms.mainTex_ST.value.copy(this.mainTex_ST); + }; + VRMUnlitMaterial.prototype._updateShaderCode = function () { + this.defines = { + RENDERTYPE_OPAQUE: this._renderType === VRMUnlitMaterialRenderType.Opaque, + RENDERTYPE_CUTOUT: this._renderType === VRMUnlitMaterialRenderType.Cutout, + RENDERTYPE_TRANSPARENT: this._renderType === VRMUnlitMaterialRenderType.Transparent || + this._renderType === VRMUnlitMaterialRenderType.TransparentWithZWrite, + }; + this.vertexShader = unlit_vert_1.default; + this.fragmentShader = unlit_frag_1.default; + this.needsUpdate = true; + }; + return VRMUnlitMaterial; +}(THREE.ShaderMaterial)); +exports.VRMUnlitMaterial = VRMUnlitMaterial; + + +/***/ }), + +/***/ "./src/vrm/material/getTexelDecodingFunction.ts": +/*!******************************************************!*\ + !*** ./src/vrm/material/getTexelDecodingFunction.ts ***! + \******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +exports.getEncodingComponents = function (encoding) { + switch (encoding) { + case THREE.LinearEncoding: + return ['Linear', '( value )']; + case THREE.sRGBEncoding: + return ['sRGB', '( value )']; + case THREE.RGBEEncoding: + return ['RGBE', '( value )']; + case THREE.RGBM7Encoding: + return ['RGBM', '( value, 7.0 )']; + case THREE.RGBM16Encoding: + return ['RGBM', '( value, 16.0 )']; + case THREE.RGBDEncoding: + return ['RGBD', '( value, 256.0 )']; + case THREE.GammaEncoding: + return ['Gamma', '( value, float( GAMMA_FACTOR ) )']; + default: + throw new Error('unsupported encoding: ' + encoding); + } +}; +exports.getTexelDecodingFunction = function (functionName, encoding) { + var components = exports.getEncodingComponents(encoding); + return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[0] + 'ToLinear' + components[1] + '; }'; +}; + + +/***/ }), + +/***/ "./src/vrm/material/index.ts": +/*!***********************************!*\ + !*** ./src/vrm/material/index.ts ***! + \***********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(__webpack_require__(/*! ./MToonMaterial */ "./src/vrm/material/MToonMaterial.ts")); +__export(__webpack_require__(/*! ./VRMMaterialImporter */ "./src/vrm/material/VRMMaterialImporter.ts")); +__export(__webpack_require__(/*! ./VRMUnlitMaterial */ "./src/vrm/material/VRMUnlitMaterial.ts")); + + +/***/ }), + +/***/ "./src/vrm/material/shaders/mtoon.frag": +/*!*********************************************!*\ + !*** ./src/vrm/material/shaders/mtoon.frag ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony default export */ __webpack_exports__["default"] = ("// #define PHONG\n\n#ifdef BLENDMODE_CUTOUT\n uniform float cutoff;\n#endif\n\nuniform vec3 color;\nuniform float colorAlpha;\nuniform vec3 shadeColor;\n#ifdef USE_SHADETEXTURE\n uniform sampler2D shadeTexture;\n#endif\n\nuniform float receiveShadowRate;\n#ifdef USE_RECEIVESHADOWTEXTURE\n uniform sampler2D receiveShadowTexture;\n#endif\n\nuniform float shadingGradeRate;\n#ifdef USE_SHADINGGRADETEXTURE\n uniform sampler2D shadingGradeTexture;\n#endif\n\nuniform float shadeShift;\nuniform float shadeToony;\nuniform float lightColorAttenuation;\nuniform float indirectLightIntensity;\n\n#ifdef USE_RIMTEXTURE\n uniform sampler2D rimTexture;\n#endif\nuniform vec3 rimColor;\nuniform float rimLightingMix;\nuniform float rimFresnelPower;\nuniform float rimLift;\n\n#ifdef USE_SPHEREADD\n uniform sampler2D sphereAdd;\n#endif\n\nuniform vec3 emissionColor;\n\nuniform vec3 outlineColor;\nuniform float outlineLightingMix;\n\n#ifdef USE_UVANIMMASKTEXTURE\n uniform sampler2D uvAnimMaskTexture;\n#endif\n\nuniform float uvAnimOffsetX;\nuniform float uvAnimOffsetY;\nuniform float uvAnimTheta;\n\n#include \n#include \n#include \n#include \n\n// #include \n#if defined( USE_MAP ) || defined( USE_SHADETEXTURE ) || defined( USE_NORMALMAP ) || defined( USE_RECEIVESHADOWTEXTURE ) || defined( USE_SHADINGGRADETEXTURE ) || defined( USE_RIMTEXTURE ) || defined( USE_EMISSIVEMAP ) || defined( USE_OUTLINEWIDTHTEXTURE ) || defined( USE_UVANIMMASKTEXTURE )\n varying vec2 vUv;\n#endif\n\n#include \n#include \n// #include \n#include \n// #include \n#include \n// #include \n// #include \n#include \n#include \n#include \n\n// #include \nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n varying vec3 vNormal;\n#endif\n\n#define Material_LightProbeLOD( material ) (0)\n\n#include \n// #include \n\n// #include \n#ifdef USE_NORMALMAP\n uniform sampler2D normalMap;\n uniform float bumpScale;\n\n // this number is very random, this is still a 対処療法\n #define UV_DERIVATIVE_EPSILON 1E-6\n\n // Per-Pixel Tangent Space Normal Mapping\n // http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n vec3 perturbNormal2Arb( vec2 uv, vec3 eye_pos, vec3 surf_norm ) {\n // Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988\n vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n vec2 st0 = dFdx( uv.st );\n vec2 st1 = dFdy( uv.st );\n\n float scale = sign( st1.t * st0.s - st0.t * st1.s ); // we do not care about the magnitude\n vec3 S = ( q0 * st1.t - q1 * st0.t ) * scale;\n vec3 T = ( - q0 * st1.s + q1 * st0.s ) * scale;\n\n // Workaround for the issue that happens when delta of uv = 0.0\n if ( length( S ) == 0.0 || length( T ) == 0.0 ) {\n return surf_norm;\n }\n\n S = normalize( S );\n T = normalize( T );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, uv ).xyz * 2.0 - 1.0;\n\n mapN.xy *= bumpScale;\n\n #ifdef DOUBLE_SIDED\n // Workaround for Adreno GPUs gl_FrontFacing bug. See #15850 and #10331\n // http://hacksoflife.blogspot.com/2009/11/per-pixel-tangent-space-normal-mapping.html?showComment=1522254677437#c5087545147696715943\n vec3 NfromST = cross( S, T );\n if( dot( NfromST, N ) > 0.0 ) {\n S *= -1.0;\n T *= -1.0;\n }\n #else\n mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #endif\n\n mat3 tsn = mat3( S, T, N );\n\n return normalize( tsn * mapN );\n }\n#endif\n\n// #include \n#include \n#include \n\n// == lighting stuff ===========================================================\nfloat getLightIntensity(\n const in IncidentLight directLight,\n const in GeometricContext geometry,\n const in float shadow,\n const in float shadingGrade\n) {\n float lightIntensity = dot( geometry.normal, directLight.direction );\n lightIntensity = 0.5 + 0.5 * lightIntensity;\n lightIntensity = lightIntensity * shadow;\n lightIntensity = lightIntensity * shadingGrade;\n lightIntensity = lightIntensity * 2.0 - 1.0;\n return smoothstep( shadeShift, shadeShift + ( 1.0 - shadeToony ), lightIntensity );\n}\n\nvec3 getLighting( const in vec3 lightColor ) {\n vec3 lighting = lightColor;\n lighting = mix(\n lighting,\n vec3( max( 0.001, max( lighting.x, max( lighting.y, lighting.z ) ) ) ),\n lightColorAttenuation\n );\n\n #ifndef PHYSICALLY_CORRECT_LIGHTS\n lighting *= PI;\n #endif\n\n return lighting;\n}\n\nvec3 getDiffuse(\n const in vec3 lit,\n const in vec3 shade,\n const in float lightIntensity,\n const in vec3 lighting\n) {\n #ifdef DEBUG_LITSHADERATE\n return vec3( BRDF_Diffuse_Lambert( lightIntensity * lighting ) );\n #endif\n\n return lighting * BRDF_Diffuse_Lambert( mix( shade, lit, lightIntensity ) );\n}\n\nvec3 calcDirectDiffuse(\n const in vec2 uv,\n const in vec3 lit,\n const in vec3 shade,\n in GeometricContext geometry,\n inout ReflectedLight reflectedLight\n) {\n IncidentLight directLight;\n vec3 lightingSum = vec3( 0.0 );\n\n float shadingGrade = 1.0;\n #ifdef USE_SHADINGGRADETEXTURE\n shadingGrade = 1.0 - shadingGradeRate * ( 1.0 - texture2D( shadingGradeTexture, uv ).r );\n #endif\n\n float receiveShadow = receiveShadowRate;\n #ifdef USE_RECEIVESHADOWTEXTURE\n receiveShadow *= texture2D( receiveShadowTexture, uv ).a;\n #endif\n\n #if ( NUM_POINT_LIGHTS > 0 )\n PointLight pointLight;\n\n #pragma unroll_loop\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n pointLight = pointLights[ i ];\n getPointDirectLightIrradiance( pointLight, geometry, directLight );\n\n float atten = 1.0;\n #ifdef USE_SHADOWMAP\n atten = all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n #endif\n\n float shadow = 1.0 - receiveShadow * ( 1.0 - ( 0.5 + 0.5 * atten ) );\n float lightIntensity = getLightIntensity( directLight, geometry, shadow, shadingGrade );\n vec3 lighting = getLighting( directLight.color );\n reflectedLight.directDiffuse += getDiffuse( lit, shade, lightIntensity, lighting );\n lightingSum += lighting;\n }\n #endif\n\n #if ( NUM_SPOT_LIGHTS > 0 )\n SpotLight spotLight;\n\n #pragma unroll_loop\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n spotLight = spotLights[ i ];\n getSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\n float atten = 1.0;\n #ifdef USE_SHADOWMAP\n atten = all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n #endif\n\n float shadow = 1.0 - receiveShadow * ( 1.0 - ( 0.5 + 0.5 * atten ) );\n float lightIntensity = getLightIntensity( directLight, geometry, shadow, shadingGrade );\n vec3 lighting = getLighting( directLight.color );\n reflectedLight.directDiffuse += getDiffuse( lit, shade, lightIntensity, lighting );\n lightingSum += lighting;\n }\n #endif\n\n #if ( NUM_DIR_LIGHTS > 0 )\n DirectionalLight directionalLight;\n\n #pragma unroll_loop\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n directionalLight = directionalLights[ i ];\n getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\n float atten = 1.0;\n #ifdef USE_SHADOWMAP\n atten = all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n #endif\n\n float shadow = 1.0 - receiveShadow * ( 1.0 - ( 0.5 + 0.5 * atten ) );\n float lightIntensity = getLightIntensity( directLight, geometry, shadow, shadingGrade );\n vec3 lighting = getLighting( directLight.color );\n reflectedLight.directDiffuse += getDiffuse( lit, shade, lightIntensity, lighting );\n lightingSum += lighting;\n }\n #endif\n\n return lightingSum;\n}\n\n// == post correction ==========================================================\nvoid postCorrection() {\n #include \n #include \n #include \n #include \n #include \n}\n\n// == main procedure ===========================================================\nvoid main() {\n #include \n\n vec2 uv = vec2(0.5, 0.5);\n\n #if defined( USE_MAP ) || defined( USE_SHADETEXTURE ) || defined( USE_NORMALMAP ) || defined( USE_RECEIVESHADOWTEXTURE ) || defined( USE_SHADINGGRADETEXTURE ) || defined( USE_RIMTEXTURE ) || defined( USE_EMISSIVEMAP ) || defined( USE_OUTLINEWIDTHTEXTURE ) || defined( USE_UVANIMMASKTEXTURE )\n uv = vUv;\n\n float uvAnimMask = 1.0;\n #ifdef USE_UVANIMMASKTEXTURE\n uvAnimMask = texture2D( uvAnimMaskTexture, uv ).x;\n #endif\n\n uv = uv + vec2( uvAnimOffsetX, uvAnimOffsetY ) * uvAnimMask;\n float uvRotCos = cos( uvAnimTheta * uvAnimMask );\n float uvRotSin = sin( uvAnimTheta * uvAnimMask );\n uv = mat2( uvRotCos, uvRotSin, -uvRotSin, uvRotCos ) * ( uv - 0.5 ) + 0.5;\n #endif\n\n #ifdef DEBUG_UV\n gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n #if defined( USE_MAP ) || defined( USE_SHADETEXTURE ) || defined( USE_NORMALMAP ) || defined( USE_RECEIVESHADOWTEXTURE ) || defined( USE_SHADINGGRADETEXTURE ) || defined( USE_RIMTEXTURE ) || defined( USE_EMISSIVEMAP ) || defined( USE_OUTLINEWIDTHTEXTURE ) || defined( USE_UVANIMMASKTEXTURE )\n gl_FragColor = vec4( uv, 0.0, 1.0 );\n #endif\n return;\n #endif\n\n vec4 diffuseColor = vec4( color, colorAlpha );\n ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n vec3 totalEmissiveRadiance = emissionColor;\n\n #include \n\n // #include \n #ifdef USE_MAP\n diffuseColor *= mapTexelToLinear( texture2D( map, uv ) );\n #endif\n\n #include \n // #include \n\n // -- MToon: alpha -----------------------------------------------------------\n // #include \n #ifdef BLENDMODE_CUTOUT\n if ( diffuseColor.a <= cutoff ) { discard; }\n diffuseColor.a = 1.0;\n #endif\n\n #ifdef BLENDMODE_OPAQUE\n diffuseColor.a = 1.0;\n #endif\n\n #if defined( OUTLINE ) && defined( OUTLINE_COLOR_FIXED ) // omitting DebugMode\n gl_FragColor = vec4( outlineColor, diffuseColor.a );\n postCorrection();\n return;\n #endif\n\n // #include \n #include \n\n #ifdef OUTLINE\n normal *= -1.0;\n #endif\n\n // #include \n #ifdef USE_NORMALMAP\n normal = perturbNormal2Arb( uv, -vViewPosition, normal );\n #endif\n\n // #include \n #ifdef USE_EMISSIVEMAP\n totalEmissiveRadiance *= emissiveMapTexelToLinear( texture2D( emissiveMap, uv ) ).rgb;\n #endif\n\n #ifdef DEBUG_NORMAL\n gl_FragColor = vec4( 0.5 + 0.5 * normal, 1.0 );\n return;\n #endif\n\n // -- MToon: lighting --------------------------------------------------------\n // accumulation\n // #include \n // #include \n vec3 lit = diffuseColor.rgb;\n vec3 shade = shadeColor;\n #ifdef USE_SHADETEXTURE\n shade *= shadeTextureTexelToLinear( texture2D( shadeTexture, uv ) ).rgb;\n #endif\n\n GeometricContext geometry;\n\n geometry.position = - vViewPosition;\n geometry.normal = normal;\n geometry.viewDir = normalize( vViewPosition );\n\n vec3 lighting = calcDirectDiffuse( uv, diffuseColor.rgb, shade, geometry, reflectedLight );\n\n vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n #if ( NUM_HEMI_LIGHTS > 0 )\n #pragma unroll_loop\n for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n }\n #endif\n\n // #include \n #ifdef USE_LIGHTMAP\n vec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).rgb * lightMapIntensity;\n #ifndef PHYSICALLY_CORRECT_LIGHTS\n lightMapIrradiance *= PI; // factor of PI should not be present; included here to prevent breakage\n #endif\n irradiance += lightMapIrradiance;\n #endif\n\n // #include \n reflectedLight.indirectDiffuse += indirectLightIntensity * irradiance * BRDF_Diffuse_Lambert( lit );\n\n // modulation\n #include \n\n vec3 col = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\n\n #if defined( OUTLINE ) && defined( OUTLINE_COLOR_MIXED ) // omitting DebugMode\n gl_FragColor = vec4(\n outlineColor.rgb * mix( vec3( 1.0 ), col, outlineLightingMix ),\n diffuseColor.a\n );\n postCorrection();\n return;\n #endif\n\n // -- MToon: parametric rim lighting -----------------------------------------\n vec3 viewDir = normalize( vViewPosition );\n vec3 rimMix = mix(vec3(1.0), lighting + indirectLightIntensity * irradiance, rimLightingMix);\n vec3 rim = rimColor * pow( saturate( 1.0 - dot( viewDir, normal ) + rimLift ), rimFresnelPower );\n #ifdef USE_RIMTEXTURE\n rim *= texture2D( rimTexture, uv ).rgb;\n #endif\n col += rim;\n\n // -- MToon: additive matcap -------------------------------------------------\n #ifdef USE_SPHEREADD\n {\n vec3 x = normalize( vec3( viewDir.z, 0.0, -viewDir.x ) );\n vec3 y = cross( viewDir, x ); // guaranteed to be normalized\n vec2 sphereUv = 0.5 + 0.5 * vec2( dot( x, normal ), -dot( y, normal ) );\n vec3 matcap = sphereAddTexelToLinear( texture2D( sphereAdd, sphereUv ) ).xyz;\n col += matcap;\n }\n #endif\n\n // -- MToon: Emission --------------------------------------------------------\n col += totalEmissiveRadiance;\n\n // #include \n\n // -- Almost done! -----------------------------------------------------------\n gl_FragColor = vec4( col, diffuseColor.a );\n postCorrection();\n}"); + +/***/ }), + +/***/ "./src/vrm/material/shaders/mtoon.vert": +/*!*********************************************!*\ + !*** ./src/vrm/material/shaders/mtoon.vert ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony default export */ __webpack_exports__["default"] = ("// #define PHONG\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n varying vec3 vNormal;\n#endif\n\n#include \n\n// #include \n#if defined( USE_MAP ) || defined( USE_SHADETEXTURE ) || defined( USE_NORMALMAP ) || defined( USE_RECEIVESHADOWTEXTURE ) || defined( USE_SHADINGGRADETEXTURE ) || defined( USE_RIMTEXTURE ) || defined( USE_EMISSIVEMAP ) || defined( USE_OUTLINEWIDTHTEXTURE ) || defined( USE_UVANIMMASKTEXTURE )\n varying vec2 vUv;\n uniform vec4 mainTex_ST;\n#endif\n\n#include \n// #include \n// #include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\n#ifdef USE_OUTLINEWIDTHTEXTURE\n uniform sampler2D outlineWidthTexture;\n#endif\n\nuniform float outlineWidth;\nuniform float outlineScaledMaxDistance;\n\nvoid main() {\n\n // #include \n #if defined( USE_MAP ) || defined( USE_SHADETEXTURE ) || defined( USE_NORMALMAP ) || defined( USE_RECEIVESHADOWTEXTURE ) || defined( USE_SHADINGGRADETEXTURE ) || defined( USE_RIMTEXTURE ) || defined( USE_EMISSIVEMAP ) || defined( USE_OUTLINEWIDTHTEXTURE ) || defined( USE_UVANIMMASKTEXTURE )\n vUv = vec2( mainTex_ST.p * uv.x + mainTex_ST.s, mainTex_ST.q * uv.y + mainTex_ST.t );\n #endif\n\n #include \n #include \n\n #include \n #include \n #include \n #include \n #include \n\n #ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED\n vNormal = normalize( transformedNormal );\n #endif\n\n #include \n\n #include \n #include \n // #include \n #include \n #include \n #include \n\n vViewPosition = - mvPosition.xyz;\n\n float outlineTex = 1.0;\n\n #ifdef OUTLINE\n #ifdef USE_OUTLINEWIDTHTEXTURE\n outlineTex = texture2D( outlineWidthTexture, vUv ).r;\n #endif\n\n #ifdef OUTLINE_WIDTH_WORLD\n vec3 outlineOffset = 0.01 * outlineWidth * outlineTex * normalize( objectNormal );\n gl_Position += projectionMatrix * modelViewMatrix * vec4( outlineOffset, 0.0 );\n #endif\n\n #ifdef OUTLINE_WIDTH_SCREEN\n vec3 clipNormal = ( projectionMatrix * modelViewMatrix * vec4( normalize( objectNormal ), 0.0 ) ).xyz;\n vec2 projectedNormal = normalize( clipNormal.xy );\n projectedNormal *= min( gl_Position.w, outlineScaledMaxDistance );\n projectedNormal.x *= projectionMatrix[ 0 ].x / projectionMatrix[ 1 ].y;\n gl_Position.xy += 0.01 * outlineWidth * outlineTex * projectedNormal.xy;\n #endif\n\n gl_Position.z += 1E-6 * gl_Position.w; // anti-artifact magic\n #endif\n\n #include \n // #include \n #include \n #include \n\n}"); + +/***/ }), + +/***/ "./src/vrm/material/shaders/unlit.frag": +/*!*********************************************!*\ + !*** ./src/vrm/material/shaders/unlit.frag ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony default export */ __webpack_exports__["default"] = ("#ifdef RENDERTYPE_CUTOUT\n uniform float cutoff;\n#endif\n\n#include \n#include \n#include \n#include \n#include \n// #include \n// #include \n// #include \n// #include \n#include \n// #include \n#include \n#include \n\n// == main procedure ===========================================================\nvoid main() {\n #include \n\n vec4 diffuseColor = vec4( 1.0 );\n\n #include \n\n // #include \n #ifdef USE_MAP\n diffuseColor *= mapTexelToLinear( texture2D( map, vUv ) );\n #endif\n\n #include \n // #include \n\n // MToon: alpha\n // #include \n #ifdef RENDERTYPE_CUTOUT\n if ( diffuseColor.a <= cutoff ) { discard; }\n diffuseColor.a = 1.0;\n #endif\n\n #ifdef RENDERTYPE_OPAQUE\n diffuseColor.a = 1.0;\n #endif\n\n // #include \n\n ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\n // accumulation (baked indirect lighting only)\n #ifdef USE_LIGHTMAP\n reflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n #else\n reflectedLight.indirectDiffuse += vec3( 1.0 );\n #endif\n\n // modulation\n // #include \n\n reflectedLight.indirectDiffuse *= diffuseColor.rgb;\n vec3 outgoingLight = reflectedLight.indirectDiffuse;\n\n // #include \n\n gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\n #include \n #include \n #include \n #include \n}"); + +/***/ }), + +/***/ "./src/vrm/material/shaders/unlit.vert": +/*!*********************************************!*\ + !*** ./src/vrm/material/shaders/unlit.vert ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony default export */ __webpack_exports__["default"] = ("#include \n\n// #include \n#ifdef USE_MAP\n varying vec2 vUv;\n uniform vec4 mainTex_ST;\n#endif\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n // #include \n #ifdef USE_MAP\n vUv = vec2( mainTex_ST.p * uv.x + mainTex_ST.s, mainTex_ST.q * uv.y + mainTex_ST.t );\n #endif\n\n #include \n #include \n #include \n\n #ifdef USE_ENVMAP\n\n #include \n #include \n #include \n #include \n\n #endif\n\n #include \n #include \n #include \n #include \n #include \n\n #include \n #include \n #include \n #include \n\n}"); + +/***/ }), + +/***/ "./src/vrm/reduceBones.ts": +/*!********************************!*\ + !*** ./src/vrm/reduceBones.ts ***! + \********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +function reduceBones(root) { + root.traverse(function (obj) { + if (obj.type !== 'SkinnedMesh') { + return; + } + var mesh = obj; + var geometry = mesh.geometry.clone(); + mesh.geometry = geometry; + var attribute = geometry.getAttribute('skinIndex'); + var bones = []; + var boneInverses = []; + var boneIndexMap = {}; + var array = attribute.array.map(function (index) { + if (boneIndexMap[index] === undefined) { + boneIndexMap[index] = bones.length; + bones.push(mesh.skeleton.bones[index]); + boneInverses.push(mesh.skeleton.boneInverses[index]); + } + return boneIndexMap[index]; + }); + geometry.removeAttribute('skinIndex'); + geometry.addAttribute('skinIndex', new THREE.BufferAttribute(array, 4, false)); + mesh.bind(new THREE.Skeleton(bones, boneInverses), new THREE.Matrix4()); + }); +} +exports.reduceBones = reduceBones; + + +/***/ }), + +/***/ "./src/vrm/springbone/VRMSpringBone.ts": +/*!*********************************************!*\ + !*** ./src/vrm/springbone/VRMSpringBone.ts ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var math_1 = __webpack_require__(/*! ../utils/math */ "./src/vrm/utils/math.ts"); +exports.GIZMO_RENDER_ORDER = 10000; +var IDENTITY_MATRIX4 = Object.freeze(new THREE.Matrix4()); +var IDENTITY_QUATERNION = Object.freeze(new THREE.Quaternion()); +var _v3A = new THREE.Vector3(); +var _v3B = new THREE.Vector3(); +var _v3C = new THREE.Vector3(); +var _quatA = new THREE.Quaternion(); +var _matA = new THREE.Matrix4(); +var _matB = new THREE.Matrix4(); +var VRMSpringBone = (function () { + function VRMSpringBone(bone, radius, stiffiness, gravityDir, gravityPower, dragForce, colliders) { + var _this = this; + if (colliders === void 0) { colliders = []; } + this.bone = bone; + this.bone.matrixAutoUpdate = false; + this.radius = radius; + this.stiffnessForce = stiffiness; + this.gravityDir = gravityDir; + this.gravityPower = gravityPower; + this.dragForce = dragForce; + this.colliders = colliders; + this._worldPosition = new THREE.Vector3().setFromMatrixPosition(this.bone.matrixWorld); + this._parentWorldRotation = new THREE.Quaternion(); + this._initialLocalMatrix = this.bone.matrix.clone(); + this._initialLocalRotation = this.bone.quaternion.clone(); + this._initialLocalChildPosition = (function () { + if (_this.bone.children.length === 0) { + return _this.bone.position + .clone() + .normalize() + .multiplyScalar(0.07); + } + else { + var firstChild = _this.bone.children[0]; + return firstChild.position.clone(); + } + })(); + this._currentTail = this.bone.localToWorld(this._initialLocalChildPosition.clone()); + this._prevTail = this._currentTail.clone(); + this._nextTail = this._currentTail.clone(); + this._boneAxis = this._initialLocalChildPosition.clone().normalize(); + this._worldBoneLength = this.bone + .localToWorld(_v3A.copy(this._initialLocalChildPosition)) + .sub(this._worldPosition) + .length(); + } + VRMSpringBone.prototype.reset = function () { + this.bone.matrix.copy(this._initialLocalMatrix); + this.bone.localToWorld(this._currentTail.copy(this._initialLocalChildPosition)); + this._prevTail.copy(this._currentTail); + this._nextTail.copy(this._currentTail); + this.bone.updateMatrix(); + this.bone.matrixWorld.multiplyMatrices(this._getParentMatrixWorld(), this.bone.matrix); + this._worldPosition.setFromMatrixPosition(this.bone.matrixWorld); + }; + VRMSpringBone.prototype.update = function (delta) { + if (delta <= 0) + return; + this.bone.matrixWorld.multiplyMatrices(this._getParentMatrixWorld(), this.bone.matrix); + if (this.bone.parent) { + math_1.getWorldQuaternionLite(this.bone.parent, this._parentWorldRotation); + } + else { + this._parentWorldRotation.copy(IDENTITY_QUATERNION); + } + this._worldPosition.setFromMatrixPosition(this.bone.matrixWorld); + var stiffness = this.stiffnessForce * delta; + var external = _v3B.copy(this.gravityDir).multiplyScalar(this.gravityPower * delta); + this._nextTail + .copy(this._currentTail) + .add(_v3A + .copy(this._currentTail) + .sub(this._prevTail) + .multiplyScalar(1 - this.dragForce)) + .add(_v3A + .copy(this._boneAxis) + .applyMatrix4(this._initialLocalMatrix) + .applyMatrix4(this._getParentMatrixWorld()) + .sub(this._worldPosition) + .normalize() + .multiplyScalar(stiffness)) + .add(external); + this._nextTail + .sub(this._worldPosition) + .normalize() + .multiplyScalar(this._worldBoneLength) + .add(this._worldPosition); + this._collision(this._nextTail); + this._prevTail.copy(this._currentTail); + this._currentTail.copy(this._nextTail); + var initialWorldMatrixInv = _matA.getInverse(_matB.copy(this._getParentMatrixWorld()).multiply(this._initialLocalMatrix)); + var applyRotation = _quatA.setFromUnitVectors(this._boneAxis, _v3A + .copy(this._nextTail) + .applyMatrix4(initialWorldMatrixInv) + .normalize()); + this.bone.quaternion.copy(this._initialLocalRotation).multiply(applyRotation); + this.bone.updateMatrix(); + this.bone.matrixWorld.multiplyMatrices(this._getParentMatrixWorld(), this.bone.matrix); + }; + VRMSpringBone.prototype._collision = function (tail) { + var _this = this; + this.colliders.forEach(function (collider) { + var colliderWorldPosition = _v3A.setFromMatrixPosition(collider.matrixWorld); + var colliderRadius = collider.geometry.boundingSphere.radius; + var r = _this.radius + colliderRadius; + if (tail.distanceToSquared(colliderWorldPosition) <= r * r) { + var normal = _v3B.subVectors(tail, colliderWorldPosition).normalize(); + var posFromCollider = _v3C.addVectors(colliderWorldPosition, normal.multiplyScalar(r)); + tail.copy(posFromCollider + .sub(_this._worldPosition) + .normalize() + .multiplyScalar(_this._worldBoneLength) + .add(_this._worldPosition)); + } + }); + }; + VRMSpringBone.prototype._getParentMatrixWorld = function () { + return this.bone.parent ? this.bone.parent.matrixWorld : IDENTITY_MATRIX4; + }; + return VRMSpringBone; +}()); +exports.VRMSpringBone = VRMSpringBone; + + +/***/ }), + +/***/ "./src/vrm/springbone/VRMSpringBoneColliderGroup.ts": +/*!**********************************************************!*\ + !*** ./src/vrm/springbone/VRMSpringBoneColliderGroup.ts ***! + \**********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); + + +/***/ }), + +/***/ "./src/vrm/springbone/VRMSpringBoneImporter.ts": +/*!*****************************************************!*\ + !*** ./src/vrm/springbone/VRMSpringBoneImporter.ts ***! + \*****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +var VRMSpringBone_1 = __webpack_require__(/*! ./VRMSpringBone */ "./src/vrm/springbone/VRMSpringBone.ts"); +var VRMSpringBoneManager_1 = __webpack_require__(/*! ./VRMSpringBoneManager */ "./src/vrm/springbone/VRMSpringBoneManager.ts"); +var VRMSpringBoneImporter = (function () { + function VRMSpringBoneImporter() { + } + VRMSpringBoneImporter.prototype.import = function (gltf) { + return __awaiter(this, void 0, Promise, function () { + var colliderGroups, springBoneGroupList; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!gltf.parser.json.extensions || + !gltf.parser.json.extensions.VRM || + !gltf.parser.json.extensions.VRM.secondaryAnimation) { + return [2, null]; + } + return [4, this._getColliderMeshGroups(gltf)]; + case 1: + colliderGroups = _a.sent(); + colliderGroups.forEach(function (group) { + var _a; + return (_a = gltf.scene).add.apply(_a, group.colliders); + }); + return [4, this._getSpringBoneGroupList(gltf, colliderGroups)]; + case 2: + springBoneGroupList = _a.sent(); + return [2, new VRMSpringBoneManager_1.VRMSpringBoneManager(springBoneGroupList)]; + } + }); + }); + }; + Object.defineProperty(VRMSpringBoneImporter.prototype, "_isColiderMeshVisible", { + get: function () { + return false; + }, + enumerable: true, + configurable: true + }); + VRMSpringBoneImporter.prototype._createSpringBone = function (gltf, bone, hitRadius, stiffiness, gravityDir, gravityPower, dragForce, colliders) { + if (colliders === void 0) { colliders = []; } + return new VRMSpringBone_1.VRMSpringBone(bone, hitRadius, stiffiness, gravityDir, gravityPower, dragForce, colliders); + }; + VRMSpringBoneImporter.prototype._getSpringBoneGroupList = function (gltf, colliderGroups) { + return __awaiter(this, void 0, Promise, function () { + var springBoneGroups, springBoneGroupList; + var _this = this; + return __generator(this, function (_a) { + springBoneGroups = gltf.parser.json.extensions.VRM.secondaryAnimation + .boneGroups; + springBoneGroupList = []; + springBoneGroups.forEach(function (vrmBoneGroup) { + if (vrmBoneGroup.stiffiness === undefined || + vrmBoneGroup.gravityDir === undefined || + vrmBoneGroup.gravityDir.x === undefined || + vrmBoneGroup.gravityDir.y === undefined || + vrmBoneGroup.gravityDir.z === undefined || + vrmBoneGroup.gravityPower === undefined || + vrmBoneGroup.dragForce === undefined || + vrmBoneGroup.hitRadius === undefined || + vrmBoneGroup.colliderGroups === undefined || + vrmBoneGroup.bones === undefined) { + return; + } + var stiffiness = vrmBoneGroup.stiffiness; + var gravityDir = new THREE.Vector3(vrmBoneGroup.gravityDir.x, vrmBoneGroup.gravityDir.y, vrmBoneGroup.gravityDir.z); + var gravityPower = vrmBoneGroup.gravityPower; + var dragForce = vrmBoneGroup.dragForce; + var hitRadius = vrmBoneGroup.hitRadius; + var colliders = []; + vrmBoneGroup.colliderGroups.forEach(function (colliderIndex) { + colliders.push.apply(colliders, colliderGroups[colliderIndex].colliders); + }); + var springBoneGroup = []; + vrmBoneGroup.bones.forEach(function (nodeIndex) { return __awaiter(_this, void 0, void 0, function () { + var springRootBone; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4, gltf.parser.getDependency('node', nodeIndex)]; + case 1: + springRootBone = _a.sent(); + if (!springRootBone) { + return [2]; + } + springRootBone.traverse(function (bone) { + var springBone = _this._createSpringBone(gltf, bone, hitRadius, stiffiness, gravityDir, gravityPower, dragForce, colliders); + springBoneGroup.push(springBone); + }); + return [2]; + } + }); + }); }); + springBoneGroupList.push(springBoneGroup); + }); + return [2, springBoneGroupList]; + }); + }); + }; + VRMSpringBoneImporter.prototype._getColliderMeshGroups = function (gltf) { + return __awaiter(this, void 0, Promise, function () { + var vrmExt, secondaryAnimation, vrmColliderGroups, colliderGroups; + var _this = this; + return __generator(this, function (_a) { + vrmExt = gltf.parser.json.extensions && gltf.parser.json.extensions.VRM; + if (vrmExt === undefined) { + return [2, []]; + } + secondaryAnimation = vrmExt.secondaryAnimation; + if (secondaryAnimation === undefined) { + return [2, []]; + } + vrmColliderGroups = secondaryAnimation.colliderGroups; + if (vrmColliderGroups === undefined) { + return [2, []]; + } + colliderGroups = []; + vrmColliderGroups.forEach(function (colliderGroup) { return __awaiter(_this, void 0, void 0, function () { + var bone, colliders, colliderMeshGroup; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (colliderGroup.node === undefined || colliderGroup.colliders === undefined) { + return [2]; + } + return [4, gltf.parser.getDependency('node', colliderGroup.node)]; + case 1: + bone = _a.sent(); + colliders = []; + colliderGroup.colliders.forEach(function (collider) { + if (collider.offset === undefined || + collider.offset.x === undefined || + collider.offset.y === undefined || + collider.offset.z === undefined || + collider.radius === undefined) { + return; + } + var offsetMatrix = new THREE.Matrix4().makeTranslation(collider.offset.x, collider.offset.y, -collider.offset.z); + var visible = _this._isColiderMeshVisible; + var colliderMesh = new THREE.Mesh(new THREE.SphereBufferGeometry(collider.radius, 8, 4), new THREE.MeshBasicMaterial({ + color: 0xff00ff, + visible: visible, + wireframe: true, + transparent: true, + depthTest: false, + })); + colliderMesh.material.renderOrder = VRMSpringBone_1.GIZMO_RENDER_ORDER; + colliderMesh.name = 'vrmColliderSphere'; + colliderMesh.geometry.computeBoundingSphere(); + colliderMesh.updateMatrixWorld = function () { + colliderMesh.matrixWorld.copy(bone.matrixWorld).multiply(offsetMatrix); + }; + colliders.push(colliderMesh); + }); + colliderMeshGroup = { + node: colliderGroup.node, + colliders: colliders, + }; + colliderGroups.push(colliderMeshGroup); + return [2]; + } + }); + }); }); + return [2, colliderGroups]; + }); + }); + }; + return VRMSpringBoneImporter; +}()); +exports.VRMSpringBoneImporter = VRMSpringBoneImporter; + + +/***/ }), + +/***/ "./src/vrm/springbone/VRMSpringBoneManager.ts": +/*!****************************************************!*\ + !*** ./src/vrm/springbone/VRMSpringBoneManager.ts ***! + \****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var VRMSpringBoneManager = (function () { + function VRMSpringBoneManager(springBoneGroupList) { + this.springBoneGroupList = []; + this.springBoneGroupList = springBoneGroupList; + } + VRMSpringBoneManager.prototype.lateUpdate = function (delta) { + this.springBoneGroupList.forEach(function (springBoneGroup) { + springBoneGroup.forEach(function (springBone) { + springBone.update(delta); + }); + }); + }; + VRMSpringBoneManager.prototype.reset = function () { + this.springBoneGroupList.forEach(function (springBoneGroup) { + springBoneGroup.forEach(function (springBone) { + springBone.reset(); + }); + }); + }; + return VRMSpringBoneManager; +}()); +exports.VRMSpringBoneManager = VRMSpringBoneManager; + + +/***/ }), + +/***/ "./src/vrm/springbone/index.ts": +/*!*************************************!*\ + !*** ./src/vrm/springbone/index.ts ***! + \*************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(__webpack_require__(/*! ./VRMSpringBone */ "./src/vrm/springbone/VRMSpringBone.ts")); +__export(__webpack_require__(/*! ./VRMSpringBoneColliderGroup */ "./src/vrm/springbone/VRMSpringBoneColliderGroup.ts")); +__export(__webpack_require__(/*! ./VRMSpringBoneImporter */ "./src/vrm/springbone/VRMSpringBoneImporter.ts")); +__export(__webpack_require__(/*! ./VRMSpringBoneManager */ "./src/vrm/springbone/VRMSpringBoneManager.ts")); + + +/***/ }), + +/***/ "./src/vrm/types/GLTFSchema.ts": +/*!*************************************!*\ + !*** ./src/vrm/types/GLTFSchema.ts ***! + \*************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); + + +/***/ }), + +/***/ "./src/vrm/types/VRMSchema.ts": +/*!************************************!*\ + !*** ./src/vrm/types/VRMSchema.ts ***! + \************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var VRMSchema; +(function (VRMSchema) { + var BlendShapePresetName; + (function (BlendShapePresetName) { + BlendShapePresetName["A"] = "a"; + BlendShapePresetName["Angry"] = "angry"; + BlendShapePresetName["Blink"] = "blink"; + BlendShapePresetName["BlinkL"] = "blink_l"; + BlendShapePresetName["BlinkR"] = "blink_r"; + BlendShapePresetName["E"] = "e"; + BlendShapePresetName["Fun"] = "fun"; + BlendShapePresetName["I"] = "i"; + BlendShapePresetName["Joy"] = "joy"; + BlendShapePresetName["Lookdown"] = "lookdown"; + BlendShapePresetName["Lookleft"] = "lookleft"; + BlendShapePresetName["Lookright"] = "lookright"; + BlendShapePresetName["Lookup"] = "lookup"; + BlendShapePresetName["Neutral"] = "neutral"; + BlendShapePresetName["O"] = "o"; + BlendShapePresetName["Sorrow"] = "sorrow"; + BlendShapePresetName["U"] = "u"; + BlendShapePresetName["Unknown"] = "unknown"; + })(BlendShapePresetName = VRMSchema.BlendShapePresetName || (VRMSchema.BlendShapePresetName = {})); + var FirstPersonLookAtTypeName; + (function (FirstPersonLookAtTypeName) { + FirstPersonLookAtTypeName["BlendShape"] = "BlendShape"; + FirstPersonLookAtTypeName["Bone"] = "Bone"; + })(FirstPersonLookAtTypeName = VRMSchema.FirstPersonLookAtTypeName || (VRMSchema.FirstPersonLookAtTypeName = {})); + var HumanoidBoneName; + (function (HumanoidBoneName) { + HumanoidBoneName["Chest"] = "chest"; + HumanoidBoneName["Head"] = "head"; + HumanoidBoneName["Hips"] = "hips"; + HumanoidBoneName["Jaw"] = "jaw"; + HumanoidBoneName["LeftEye"] = "leftEye"; + HumanoidBoneName["LeftFoot"] = "leftFoot"; + HumanoidBoneName["LeftHand"] = "leftHand"; + HumanoidBoneName["LeftIndexDistal"] = "leftIndexDistal"; + HumanoidBoneName["LeftIndexIntermediate"] = "leftIndexIntermediate"; + HumanoidBoneName["LeftIndexProximal"] = "leftIndexProximal"; + HumanoidBoneName["LeftLittleDistal"] = "leftLittleDistal"; + HumanoidBoneName["LeftLittleIntermediate"] = "leftLittleIntermediate"; + HumanoidBoneName["LeftLittleProximal"] = "leftLittleProximal"; + HumanoidBoneName["LeftLowerArm"] = "leftLowerArm"; + HumanoidBoneName["LeftLowerLeg"] = "leftLowerLeg"; + HumanoidBoneName["LeftMiddleDistal"] = "leftMiddleDistal"; + HumanoidBoneName["LeftMiddleIntermediate"] = "leftMiddleIntermediate"; + HumanoidBoneName["LeftMiddleProximal"] = "leftMiddleProximal"; + HumanoidBoneName["LeftRingDistal"] = "leftRingDistal"; + HumanoidBoneName["LeftRingIntermediate"] = "leftRingIntermediate"; + HumanoidBoneName["LeftRingProximal"] = "leftRingProximal"; + HumanoidBoneName["LeftShoulder"] = "leftShoulder"; + HumanoidBoneName["LeftThumbDistal"] = "leftThumbDistal"; + HumanoidBoneName["LeftThumbIntermediate"] = "leftThumbIntermediate"; + HumanoidBoneName["LeftThumbProximal"] = "leftThumbProximal"; + HumanoidBoneName["LeftToes"] = "leftToes"; + HumanoidBoneName["LeftUpperArm"] = "leftUpperArm"; + HumanoidBoneName["LeftUpperLeg"] = "leftUpperLeg"; + HumanoidBoneName["Neck"] = "neck"; + HumanoidBoneName["RightEye"] = "rightEye"; + HumanoidBoneName["RightFoot"] = "rightFoot"; + HumanoidBoneName["RightHand"] = "rightHand"; + HumanoidBoneName["RightIndexDistal"] = "rightIndexDistal"; + HumanoidBoneName["RightIndexIntermediate"] = "rightIndexIntermediate"; + HumanoidBoneName["RightIndexProximal"] = "rightIndexProximal"; + HumanoidBoneName["RightLittleDistal"] = "rightLittleDistal"; + HumanoidBoneName["RightLittleIntermediate"] = "rightLittleIntermediate"; + HumanoidBoneName["RightLittleProximal"] = "rightLittleProximal"; + HumanoidBoneName["RightLowerArm"] = "rightLowerArm"; + HumanoidBoneName["RightLowerLeg"] = "rightLowerLeg"; + HumanoidBoneName["RightMiddleDistal"] = "rightMiddleDistal"; + HumanoidBoneName["RightMiddleIntermediate"] = "rightMiddleIntermediate"; + HumanoidBoneName["RightMiddleProximal"] = "rightMiddleProximal"; + HumanoidBoneName["RightRingDistal"] = "rightRingDistal"; + HumanoidBoneName["RightRingIntermediate"] = "rightRingIntermediate"; + HumanoidBoneName["RightRingProximal"] = "rightRingProximal"; + HumanoidBoneName["RightShoulder"] = "rightShoulder"; + HumanoidBoneName["RightThumbDistal"] = "rightThumbDistal"; + HumanoidBoneName["RightThumbIntermediate"] = "rightThumbIntermediate"; + HumanoidBoneName["RightThumbProximal"] = "rightThumbProximal"; + HumanoidBoneName["RightToes"] = "rightToes"; + HumanoidBoneName["RightUpperArm"] = "rightUpperArm"; + HumanoidBoneName["RightUpperLeg"] = "rightUpperLeg"; + HumanoidBoneName["Spine"] = "spine"; + HumanoidBoneName["UpperChest"] = "upperChest"; + })(HumanoidBoneName = VRMSchema.HumanoidBoneName || (VRMSchema.HumanoidBoneName = {})); + var MetaAllowedUserName; + (function (MetaAllowedUserName) { + MetaAllowedUserName["Everyone"] = "Everyone"; + MetaAllowedUserName["ExplicitlyLicensedPerson"] = "ExplicitlyLicensedPerson"; + MetaAllowedUserName["OnlyAuthor"] = "OnlyAuthor"; + })(MetaAllowedUserName = VRMSchema.MetaAllowedUserName || (VRMSchema.MetaAllowedUserName = {})); + var MetaUssageName; + (function (MetaUssageName) { + MetaUssageName["Allow"] = "Allow"; + MetaUssageName["Disallow"] = "Disallow"; + })(MetaUssageName = VRMSchema.MetaUssageName || (VRMSchema.MetaUssageName = {})); + var MetaLicenseName; + (function (MetaLicenseName) { + MetaLicenseName["Cc0"] = "CC0"; + MetaLicenseName["CcBy"] = "CC_BY"; + MetaLicenseName["CcByNc"] = "CC_BY_NC"; + MetaLicenseName["CcByNcNd"] = "CC_BY_NC_ND"; + MetaLicenseName["CcByNcSa"] = "CC_BY_NC_SA"; + MetaLicenseName["CcByNd"] = "CC_BY_ND"; + MetaLicenseName["CcBySa"] = "CC_BY_SA"; + MetaLicenseName["Other"] = "Other"; + MetaLicenseName["RedistributionProhibited"] = "Redistribution_Prohibited"; + })(MetaLicenseName = VRMSchema.MetaLicenseName || (VRMSchema.MetaLicenseName = {})); +})(VRMSchema = exports.VRMSchema || (exports.VRMSchema = {})); + + +/***/ }), + +/***/ "./src/vrm/types/index.ts": +/*!********************************!*\ + !*** ./src/vrm/types/index.ts ***! + \********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(__webpack_require__(/*! ./GLTFSchema */ "./src/vrm/types/GLTFSchema.ts")); +__export(__webpack_require__(/*! ./VRMSchema */ "./src/vrm/types/VRMSchema.ts")); +__export(__webpack_require__(/*! ./types */ "./src/vrm/types/types.ts")); + + +/***/ }), + +/***/ "./src/vrm/types/types.ts": +/*!********************************!*\ + !*** ./src/vrm/types/types.ts ***! + \********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); + + +/***/ }), + +/***/ "./src/vrm/utils/disposer.ts": +/*!***********************************!*\ + !*** ./src/vrm/utils/disposer.ts ***! + \***********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +function disposeMaterial(material) { + Object.keys(material).forEach(function (propertyName) { + if (!!material[propertyName] && typeof material[propertyName].dispose === 'function') { + material[propertyName].dispose(); + } + }); + material.dispose(); + material = undefined; +} +function dispose(object3D) { + if (object3D.geometry) { + object3D.geometry.dispose(); + object3D.geometry = undefined; + } + if (!!object3D.material && Array.isArray(object3D.material)) { + object3D.material.forEach(function (material) { return disposeMaterial(material); }); + } + else if (object3D.material) { + disposeMaterial(object3D.material); + } +} +function deepDispose(object3D) { + object3D.traverse(dispose); +} +exports.deepDispose = deepDispose; + + +/***/ }), + +/***/ "./src/vrm/utils/math.ts": +/*!*******************************!*\ + !*** ./src/vrm/utils/math.ts ***! + \*******************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var THREE = __webpack_require__(/*! three */ "three"); +function saturate(value) { + return Math.max(Math.min(value, 1.0), 0.0); +} +exports.saturate = saturate; +function linstep(x, min, max) { + if (x <= min) + return 0; + if (x >= max) + return 1; + return (x - min) / (max - min); +} +exports.linstep = linstep; +var _position = new THREE.Vector3(); +var _scale = new THREE.Vector3(); +var _rotation = new THREE.Quaternion(); +function getWorldPositionLite(object, out) { + object.matrixWorld.decompose(out, _rotation, _scale); + return out; +} +exports.getWorldPositionLite = getWorldPositionLite; +function getWorldScaleLite(object, out) { + object.matrixWorld.decompose(_position, _rotation, out); + return out; +} +exports.getWorldScaleLite = getWorldScaleLite; +function getWorldQuaternionLite(object, out) { + object.matrixWorld.decompose(_position, out, _scale); + return out; +} +exports.getWorldQuaternionLite = getWorldQuaternionLite; + + +/***/ }), + +/***/ "./src/vrm/utils/renameMaterialProperty.ts": +/*!*************************************************!*\ + !*** ./src/vrm/utils/renameMaterialProperty.ts ***! + \*************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +function renameMaterialProperty(name) { + if (name[0] !== '_') { + console.warn("renameMaterialProperty: Given property name \"" + name + "\" might be invalid"); + return name; + } + name = name.substring(1); + if (!/[A-Z]/.test(name[0])) { + console.warn("renameMaterialProperty: Given property name \"" + name + "\" might be invalid"); + return name; + } + return name[0].toLowerCase() + name.substring(1); +} +exports.renameMaterialProperty = renameMaterialProperty; + + +/***/ }), + +/***/ "three": +/*!************************!*\ + !*** external "THREE" ***! + \************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = THREE; + +/***/ }) + +/******/ }); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fL3dlYnBhY2svYm9vdHN0cmFwIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvYXNzaWduLnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvaW5kZXgudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vVlJNLnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL1ZSTUltcG9ydGVyLnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL2JsZW5kc2hhcGUvVlJNQmxlbmRTaGFwZUdyb3VwLnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL2JsZW5kc2hhcGUvVlJNQmxlbmRTaGFwZUltcG9ydGVyLnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL2JsZW5kc2hhcGUvVlJNQmxlbmRTaGFwZVByb3h5LnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL2JsZW5kc2hhcGUvaW5kZXgudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vZGVidWcvVlJNRGVidWcudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vZGVidWcvVlJNSW1wb3J0ZXJEZWJ1Zy50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9kZWJ1Zy9WUk1Mb29rQXRIZWFkRGVidWcudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vZGVidWcvVlJNTG9va0F0SW1wb3J0ZXJEZWJ1Zy50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9kZWJ1Zy9WUk1TcHJpbmdCb25lRGVidWcudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vZGVidWcvVlJNU3ByaW5nQm9uZUltcG9ydGVyRGVidWcudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vZGVidWcvaW5kZXgudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vZmlyc3RwZXJzb24vVlJNRmlyc3RQZXJzb24udHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vZmlyc3RwZXJzb24vVlJNRmlyc3RQZXJzb25JbXBvcnRlci50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9maXJzdHBlcnNvbi9pbmRleC50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9odW1hbm9pZC9WUk1IdW1hbkJvbmUudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vaHVtYW5vaWQvVlJNSHVtYW5vaWQudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vaHVtYW5vaWQvVlJNSHVtYW5vaWRJbXBvcnRlci50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9odW1hbm9pZC9pbmRleC50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9pbmRleC50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9sb29rYXQvQ3VydmVNYXBwZXIudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vbG9va2F0L1ZSTUxvb2tBdEFwcGx5ZXIudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vbG9va2F0L1ZSTUxvb2tBdEJsZW5kU2hhcGVBcHBseWVyLnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL2xvb2thdC9WUk1Mb29rQXRCb25lQXBwbHllci50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9sb29rYXQvVlJNTG9va0F0SGVhZC50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9sb29rYXQvVlJNTG9va0F0SW1wb3J0ZXIudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vbG9va2F0L2luZGV4LnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL21hdGVyaWFsL01Ub29uTWF0ZXJpYWwudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vbWF0ZXJpYWwvVlJNTWF0ZXJpYWxJbXBvcnRlci50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9tYXRlcmlhbC9WUk1VbmxpdE1hdGVyaWFsLnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL21hdGVyaWFsL2dldFRleGVsRGVjb2RpbmdGdW5jdGlvbi50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9tYXRlcmlhbC9pbmRleC50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9tYXRlcmlhbC9zaGFkZXJzL210b29uLmZyYWciLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vbWF0ZXJpYWwvc2hhZGVycy9tdG9vbi52ZXJ0Iiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL21hdGVyaWFsL3NoYWRlcnMvdW5saXQuZnJhZyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS9tYXRlcmlhbC9zaGFkZXJzL3VubGl0LnZlcnQiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vcmVkdWNlQm9uZXMudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vc3ByaW5nYm9uZS9WUk1TcHJpbmdCb25lLnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL3NwcmluZ2JvbmUvVlJNU3ByaW5nQm9uZUltcG9ydGVyLnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vLi9zcmMvdnJtL3NwcmluZ2JvbmUvVlJNU3ByaW5nQm9uZU1hbmFnZXIudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vc3ByaW5nYm9uZS9pbmRleC50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS90eXBlcy9WUk1TY2hlbWEudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vdHlwZXMvaW5kZXgudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vdXRpbHMvZGlzcG9zZXIudHMiLCJ3ZWJwYWNrOi8vX190aHJlZV92cm1fXy8uL3NyYy92cm0vdXRpbHMvbWF0aC50cyIsIndlYnBhY2s6Ly9fX3RocmVlX3ZybV9fLy4vc3JjL3ZybS91dGlscy9yZW5hbWVNYXRlcmlhbFByb3BlcnR5LnRzIiwid2VicGFjazovL19fdGhyZWVfdnJtX18vZXh0ZXJuYWwgXCJUSFJFRVwiIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxrREFBMEMsZ0NBQWdDO0FBQzFFO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsZ0VBQXdELGtCQUFrQjtBQUMxRTtBQUNBLHlEQUFpRCxjQUFjO0FBQy9EOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpREFBeUMsaUNBQWlDO0FBQzFFLHdIQUFnSCxtQkFBbUIsRUFBRTtBQUNySTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLG1DQUEyQiwwQkFBMEIsRUFBRTtBQUN2RCx5Q0FBaUMsZUFBZTtBQUNoRDtBQUNBO0FBQ0E7O0FBRUE7QUFDQSw4REFBc0QsK0RBQStEOztBQUVySDtBQUNBOzs7QUFHQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7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WUFDMUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLG1CQUFtQixFQUFFLENBQUM7U0FDakQ7UUFDRCxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDOUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQy9GLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUU7WUFDMUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLG1CQUFtQixFQUFFLENBQUM7U0FDdEQ7UUFDRCxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQztRQUNuQyxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNyRCxJQUFJLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQztRQUNyRCxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUMvQyxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUMvQyxJQUFJLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUM7UUFDL0QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixDQUFDO1FBQ3JFLElBQUksQ0FB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QUMsQ0FBQztJQUV6QixJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRTtRQUMxQixPQUFPLENBQUMsSUFBSSxDQUFDLG1EQUFnRCxJQUFJLHdCQUFvQixDQUFDLENBQUM7UUFDdkYsT0FBTyxJQUFJLENBQUM7S0FDYjtJQUNELE9BQU8sSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDbkQsQ0FBQztBQVpELHdEQVlDOzs7Ozs7Ozs7Ozs7QUNaRCx1QiIsImZpbGUiOiJ0aHJlZS12cm0uanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3NyYy9hc3NpZ24udHNcIik7XG4iLCIvKiBlc2xpbnQtZGlzYWJsZSAqL1xuaW1wb3J0ICogYXMgX190aHJlZV92cm1fXyBmcm9tICcuJztcbi8vIEB0cy1pZ25vcmVcbk9iamVjdC5hc3NpZ24oVEhSRUUsIF9fdGhyZWVfdnJtX18pO1xuIiwiZXhwb3J0ICogZnJvbSAnLi92cm0vJztcbiIsImltcG9ydCAqIGFzIFRIUkVFIGZyb20gJ3RocmVlJztcbmltcG9ydCB7IFZSTUJsZW5kU2hhcGVQcm94eSB9IGZyb20gJy4vYmxlbmRzaGFwZSc7XG5pbXBvcnQgeyBWUk1GaXJzdFBlcnNvbiB9IGZyb20gJy4vZmlyc3RwZXJzb24nO1xuaW1wb3J0IHsgVlJNSHVtYW5vaWQgfSBmcm9tICcuL2h1bWFub2lkJztcbmltcG9ydCB7IFZSTUxvb2tBdEhlYWQgfSBmcm9tICcuL2xvb2thdCc7XG5pbXBvcnQgeyBWUk1TcHJpbmdCb25lTWFuYWdlciB9IGZyb20gJy4vc3ByaW5nYm9uZSc7XG5pbXBvcnQgeyBWUk1TY2hlbWEgfSBmcm9tICcuL3R5cGVzJztcbmltcG9ydCB7IGRlZXBEaXNwb3NlIH0gZnJvbSAnLi91dGlscy9kaXNwb3Nlcic7XG5pbXBvcnQgeyBWUk1JbXBvcnRlciwgVlJNSW1wb3J0ZXJPcHRpb25zIH0gZnJvbSAnLi9WUk1JbXBvcnRlcic7XG5cbi8qKlxuICogUGFyYW1ldGVycyBmb3IgYSBbW1ZSTV1dIGNsYXNzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFZSTVBhcmFtZXRlcnMge1xuICBzY2VuZTogVEhSRUUuU2NlbmU7XG4gIGh1bWFub2lkPzogVlJNSHVtYW5vaWQ7XG4gIGJsZW5kU2hhcGVQcm94eT86IFZSTUJsZW5kU2hhcGVQcm94eTtcbiAgZmlyc3RQZXJzb24/OiBWUk1GaXJzdFBlcnNvbjtcbiAgbG9va0F0PzogVlJNTG9va0F0SGVhZDtcbiAgbWF0ZXJpYWxzPzogVEhSRUUuTWF0ZXJpYWxbXTtcbiAgc3ByaW5nQm9uZU1hbmFnZXI/OiBWUk1TcHJpbmdCb25lTWFuYWdlcjtcbiAgbWV0YT86IFZSTVNjaGVtYS5NZXRhO1xufVxuXG4vKipcbiAqIEEgY2xhc3MgdGhhdCByZXByZXNlbnRzIGEgc2luZ2xlIFZSTSBtb2RlbC5cbiAqIFNlZSB0aGUgZG9jdW1lbnRhdGlvbiBvZiBbW1ZSTS5mcm9tXV0gZm9yIHRoZSBtb3N0IGJhc2ljIHVzZSBvZiBWUk0uXG4gKi9cbmV4cG9ydCBjbGFzcyBWUk0ge1xuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IFZSTSBmcm9tIGEgcGFyc2VkIHJlc3VsdCBvZiBHTFRGIHRha2VuIGZyb20gR0xURkxvYWRlci5cbiAgICogSXQncyBwcm9iYWJseSBhIHRoaW5nIHdoYXQgeW91IHdhbnQgdG8gZ2V0IHN0YXJ0ZWQgd2l0aCBWUk1zLlxuICAgKlxuICAgKiBAZXhhbXBsZSBNb3N0IGJhc2ljIHVzZSBvZiBWUk1cbiAgICogYGBgXG4gICAqIGNvbnN0IHNjZW5lID0gbmV3IFRIUkVFLlNjZW5lKCk7XG4gICAqXG4gICAqIG5ldyBUSFJFRS5HTFRGTG9hZGVyKCkubG9hZCggJ21vZGVscy90aHJlZS12cm0tZ2lybC52cm0nLCAoIGdsdGYgKSA9PiB7XG4gICAqXG4gICAqICAgVEhSRUUuVlJNLmZyb20oIGdsdGYgKS50aGVuKCAoIHZybSApID0+IHtcbiAgICpcbiAgICogICAgIHNjZW5lLmFkZCggdnJtLnNjZW5lICk7XG4gICAqXG4gICAqICAgfSApO1xuICAgKlxuICAgKiB9ICk7XG4gICAqIGBgYFxuICAgKlxuICAgKiBAcGFyYW0gZ2x0ZiBBIHBhcnNlZCBHTFRGIG9iamVjdCB0YWtlbiBmcm9tIEdMVEZMb2FkZXJcbiAgICogQHBhcmFtIG9wdGlvbnMgT3B0aW9ucyB0aGF0IHdpbGwgYmUgdXNlZCBpbiBpbXBvcnRlclxuICAgKi9cbiAgcHVibGljIHN0YXRpYyBhc3luYyBmcm9tKGdsdGY6IFRIUkVFLkdMVEYsIG9wdGlvbnM6IFZSTUltcG9ydGVyT3B0aW9ucyA9IHt9KTogUHJvbWlzZTxWUk0+IHtcbiAgICBjb25zdCBpbXBvcnRlciA9IG5ldyBWUk1JbXBvcnRlcihvcHRpb25zKTtcbiAgICByZXR1cm4gYXdhaXQgaW1wb3J0ZXIuaW1wb3J0KGdsdGYpO1xuICB9XG4gIC8qKlxuICAgKiBgVEhSRUUuU2NlbmVgIHRoYXQgY29udGFpbnMgdGhlIGVudGlyZSBWUk0uXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgc2NlbmU6IFRIUkVFLlNjZW5lO1xuXG4gIC8qKlxuICAgKiBDb250YWlucyBbW1ZSTUh1bWFub2lkXV0gb2YgdGhlIFZSTS5cbiAgICogWW91IGNhbiBjb250cm9sIGVhY2ggYm9uZXMgdXNpbmcgW1tWUk1IdW1hbm9pZC5nZXRCb25lTm9kZV1dLlxuICAgKlxuICAgKiBAVE9ETyBBZGQgYSBsaW5rIHRvIFZSTSBzcGVjXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgaHVtYW5vaWQ/OiBWUk1IdW1hbm9pZDtcblxuICAvKipcbiAgICogQ29udGFpbnMgW1tWUk1CbGVuZFNoYXBlUHJveHldXSBvZiB0aGUgVlJNLlxuICAgKiBZb3UgbWlnaHQgd2FudCB0byBjb250cm9sIHRoZXNlIGZhY2lhbCBleHByZXNzaW9ucyB2aWEgW1tWUk1CbGVuZFNoYXBlUHJveHkuc2V0VmFsdWVdXS5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBibGVuZFNoYXBlUHJveHk/OiBWUk1CbGVuZFNoYXBlUHJveHk7XG5cbiAgLyoqXG4gICAqIENvbnRhaW5zIFtbVlJNRmlyc3RQZXJzb25dXSBvZiB0aGUgVlJNLlxuICAgKiBZb3UgY2FuIHVzZSB2YXJpb3VzIGZlYXR1cmUgb2YgdGhlIGZpcnN0UGVyc29uIGZpZWxkLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGZpcnN0UGVyc29uPzogVlJNRmlyc3RQZXJzb247XG5cbiAgLyoqXG4gICAqIENvbnRhaW5zIFtbVlJNTG9va0F0SGVhZF1dIG9mIHRoZSBWUk0uXG4gICAqIFlvdSBtaWdodCB3YW50IHRvIHVzZSBbW1ZSTUxvb2tBdEhlYWQudGFyZ2V0XV0gdG8gY29udHJvbCB0aGUgZXllIGRpcmVjdGlvbiBvZiB5b3VyIFZSTXMuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgbG9va0F0PzogVlJNTG9va0F0SGVhZDtcblxuICAvKipcbiAgICogQ29udGFpbnMgbWF0ZXJpYWxzIG9mIHRoZSBWUk0uXG4gICAqIGB1cGRhdGVWUk1NYXRlcmlhbHNgIG1ldGhvZCBvZiB0aGVzZSBtYXRlcmlhbHMgd2lsbCBiZSBjYWxsZWQgdmlhIGl0cyBbW1ZSTS51cGRhdGVdXSBtZXRob2QuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgbWF0ZXJpYWxzPzogVEhSRUUuTWF0ZXJpYWxbXTtcblxuICAvKipcbiAgICogQ29udGFpbnMgbWV0YSBmaWVsZHMgb2YgdGhlIFZSTS5cbiAgICogWW91IG1pZ2h0IHdhbnQgdG8gcmVmZXIgdGhlc2UgbGljZW5zZSBmaWVsZHMgYmVmb3JlIHVzZSB5b3VyIFZSTXMuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgbWV0YT86IFZSTVNjaGVtYS5NZXRhO1xuXG4gIC8qKlxuICAgKiBBIFtbVlJNU3ByaW5nQm9uZU1hbmFnZXJdXSBtYW5pcHVsYXRlcyBhbGwgc3ByaW5nIGJvbmVzIGF0dGFjaGVkIG9uIHRoZSBWUk0uXG4gICAqIFVzdWFsbHkgeW91IGRvbid0IGhhdmUgdG8gY2FyZSBhYm91dCB0aGlzIHByb3BlcnR5LlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHNwcmluZ0JvbmVNYW5hZ2VyPzogVlJNU3ByaW5nQm9uZU1hbmFnZXI7XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBWUk0gaW5zdGFuY2UuXG4gICAqXG4gICAqIEBwYXJhbSBwYXJhbXMgW1tWUk1QYXJhbWV0ZXJzXV0gdGhhdCByZXByZXNlbnRzIGNvbXBvbmVudHMgb2YgdGhlIFZSTVxuICAgKi9cbiAgcHVibGljIGNvbnN0cnVjdG9yKHBhcmFtczogVlJNUGFyYW1ldGVycykge1xuICAgIHRoaXMuc2NlbmUgPSBwYXJhbXMuc2NlbmU7XG4gICAgdGhpcy5odW1hbm9pZCA9IHBhcmFtcy5odW1hbm9pZDtcbiAgICB0aGlzLmJsZW5kU2hhcGVQcm94eSA9IHBhcmFtcy5ibGVuZFNoYXBlUHJveHk7XG4gICAgdGhpcy5maXJzdFBlcnNvbiA9IHBhcmFtcy5maXJzdFBlcnNvbjtcbiAgICB0aGlzLmxvb2tBdCA9IHBhcmFtcy5sb29rQXQ7XG4gICAgdGhpcy5tYXRlcmlhbHMgPSBwYXJhbXMubWF0ZXJpYWxzO1xuICAgIHRoaXMuc3ByaW5nQm9uZU1hbmFnZXIgPSBwYXJhbXMuc3ByaW5nQm9uZU1hbmFnZXI7XG4gICAgdGhpcy5tZXRhID0gcGFyYW1zLm1ldGE7XG4gIH1cblxuICAvKipcbiAgICogKipZb3UgbmVlZCB0byBjYWxsIHRoaXMgb24geW91ciB1cGRhdGUgbG9vcC4qKlxuICAgKlxuICAgKiBUaGlzIGZ1bmN0aW9uIHVwZGF0ZXMgZXZlcnkgVlJNIGNvbXBvbmVudHMuXG4gICAqXG4gICAqIEBwYXJhbSBkZWx0YSBkZWx0YVRpbWVcbiAgICovXG4gIHB1YmxpYyB1cGRhdGUoZGVsdGE6IG51bWJlcik6IHZvaWQge1xuICAgIGlmICh0aGlzLmxvb2tBdCkge1xuICAgICAgdGhpcy5sb29rQXQudXBkYXRlKGRlbHRhKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5ibGVuZFNoYXBlUHJveHkpIHtcbiAgICAgIHRoaXMuYmxlbmRTaGFwZVByb3h5LnVwZGF0ZSgpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLnNwcmluZ0JvbmVNYW5hZ2VyKSB7XG4gICAgICB0aGlzLnNwcmluZ0JvbmVNYW5hZ2VyLmxhdGVVcGRhdGUoZGVsdGEpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLm1hdGVyaWFscykge1xuICAgICAgdGhpcy5tYXRlcmlhbHMuZm9yRWFjaCgobWF0ZXJpYWw6IGFueSkgPT4ge1xuICAgICAgICBpZiAobWF0ZXJpYWwudXBkYXRlVlJNTWF0ZXJpYWxzKSB7XG4gICAgICAgICAgbWF0ZXJpYWwudXBkYXRlVlJNTWF0ZXJpYWxzKGRlbHRhKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIERpc3Bvc2UgZXZlcnl0aGluZyBhYm91dCB0aGUgVlJNIGluc3RhbmNlLlxuICAgKi9cbiAgcHVibGljIGRpc3Bvc2UoKTogdm9pZCB7XG4gICAgY29uc3Qgc2NlbmUgPSB0aGlzLnNjZW5lO1xuICAgIGlmIChzY2VuZSkge1xuICAgICAgd2hpbGUgKHNjZW5lLmNoaWxkcmVuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3Qgb2JqZWN0ID0gc2NlbmUuY2hpbGRyZW5bc2NlbmUuY2hpbGRyZW4ubGVuZ3RoIC0gMV07XG4gICAgICAgIGRlZXBEaXNwb3NlKG9iamVjdCk7XG4gICAgICAgIHNjZW5lLnJlbW92ZShvYmplY3QpO1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuIiwiaW1wb3J0ICogYXMgVEhSRUUgZnJvbSAndGhyZWUnO1xuaW1wb3J0IHsgVlJNQmxlbmRTaGFwZUltcG9ydGVyIH0gZnJvbSAnLi9ibGVuZHNoYXBlJztcbmltcG9ydCB7IFZSTUZpcnN0UGVyc29uSW1wb3J0ZXIgfSBmcm9tICcuL2ZpcnN0cGVyc29uJztcbmltcG9ydCB7IFZSTUh1bWFub2lkSW1wb3J0ZXIgfSBmcm9tICcuL2h1bWFub2lkL1ZSTUh1bWFub2lkSW1wb3J0ZXInO1xuaW1wb3J0IHsgVlJNTG9va0F0SW1wb3J0ZXIgfSBmcm9tICcuL2xvb2thdC9WUk1Mb29rQXRJbXBvcnRlcic7XG5pbXBvcnQgeyBWUk1NYXRlcmlhbEltcG9ydGVyIH0gZnJvbSAnLi9tYXRlcmlhbCc7XG5pbXBvcnQgeyByZWR1Y2VCb25lcyB9IGZyb20gJy4vcmVkdWNlQm9uZXMnO1xuaW1wb3J0IHsgVlJNU3ByaW5nQm9uZUltcG9ydGVyIH0gZnJvbSAnLi9zcHJpbmdib25lL1ZSTVNwcmluZ0JvbmVJbXBvcnRlcic7XG5pbXBvcnQgeyBWUk1TY2hlbWEgfSBmcm9tICcuL3R5cGVzJztcbmltcG9ydCB7IFZSTSB9IGZyb20gJy4vVlJNJztcblxuZXhwb3J0IGludGVyZmFjZSBWUk1JbXBvcnRlck9wdGlvbnMge1xuICBsb29rQXRJbXBvcnRlcj86IFZSTUxvb2tBdEltcG9ydGVyO1xuICBodW1hbm9pZEltcG9ydGVyPzogVlJNSHVtYW5vaWRJbXBvcnRlcjtcbiAgYmxlbmRTaGFwZUltcG9ydGVyPzogVlJNQmxlbmRTaGFwZUltcG9ydGVyO1xuICBmaXJzdFBlcnNvbkltcG9ydGVyPzogVlJNRmlyc3RQZXJzb25JbXBvcnRlcjtcbiAgbWF0ZXJpYWxJbXBvcnRlcj86IFZSTU1hdGVyaWFsSW1wb3J0ZXI7XG4gIHNwcmluZ0JvbmVJbXBvcnRlcj86IFZSTVNwcmluZ0JvbmVJbXBvcnRlcjtcbn1cblxuLyoqXG4gKiBBbiBpbXBvcnRlciB0aGF0IGltcG9ydHMgYSBbW1ZSTV1dIGZyb20gYSBWUk0gZXh0ZW5zaW9uIG9mIGEgR0xURi5cbiAqL1xuZXhwb3J0IGNsYXNzIFZSTUltcG9ydGVyIHtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IF9ibGVuZFNoYXBlSW1wb3J0ZXI6IFZSTUJsZW5kU2hhcGVJbXBvcnRlcjtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IF9sb29rQXRJbXBvcnRlcjogVlJNTG9va0F0SW1wb3J0ZXI7XG4gIHByb3RlY3RlZCByZWFkb25seSBfaHVtYW5vaWRJbXBvcnRlcjogVlJNSHVtYW5vaWRJbXBvcnRlcjtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IF9maXJzdFBlcnNvbkltcG9ydGVyOiBWUk1GaXJzdFBlcnNvbkltcG9ydGVyO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgX21hdGVyaWFsSW1wb3J0ZXI6IFZSTU1hdGVyaWFsSW1wb3J0ZXI7XG4gIHByb3RlY3RlZCByZWFkb25seSBfc3ByaW5nQm9uZUltcG9ydGVyOiBWUk1TcHJpbmdCb25lSW1wb3J0ZXI7XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBWUk1JbXBvcnRlci5cbiAgICpcbiAgICogQHBhcmFtIG9wdGlvbnMgW1tWUk1JbXBvcnRlck9wdGlvbnNdXSwgb3B0aW9uYWxseSBjb250YWlucyBpbXBvcnRlcnMgZm9yIGVhY2ggY29tcG9uZW50XG4gICAqL1xuICBwdWJsaWMgY29uc3RydWN0b3Iob3B0aW9uczogVlJNSW1wb3J0ZXJPcHRpb25zID0ge30pIHtcbiAgICB0aGlzLl9ibGVuZFNoYXBlSW1wb3J0ZXIgPSBvcHRpb25zLmJsZW5kU2hhcGVJbXBvcnRlciB8fCBuZXcgVlJNQmxlbmRTaGFwZUltcG9ydGVyKCk7XG4gICAgdGhpcy5fbG9va0F0SW1wb3J0ZXIgPSBvcHRpb25zLmxvb2tBdEltcG9ydGVyIHx8IG5ldyBWUk1Mb29rQXRJbXBvcnRlcigpO1xuICAgIHRoaXMuX2h1bWFub2lkSW1wb3J0ZXIgPSBvcHRpb25zLmh1bWFub2lkSW1wb3J0ZXIgfHwgbmV3IFZSTUh1bWFub2lkSW1wb3J0ZXIoKTtcbiAgICB0aGlzLl9maXJzdFBlcnNvbkltcG9ydGVyID0gb3B0aW9ucy5maXJzdFBlcnNvbkltcG9ydGVyIHx8IG5ldyBWUk1GaXJzdFBlcnNvbkltcG9ydGVyKCk7XG4gICAgdGhpcy5fbWF0ZXJpYWxJbXBvcnRlciA9IG9wdGlvbnMubWF0ZXJpYWxJbXBvcnRlciB8fCBuZXcgVlJNTWF0ZXJpYWxJbXBvcnRlcigpO1xuICAgIHRoaXMuX3NwcmluZ0JvbmVJbXBvcnRlciA9IG9wdGlvbnMuc3ByaW5nQm9uZUltcG9ydGVyIHx8IG5ldyBWUk1TcHJpbmdCb25lSW1wb3J0ZXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNlaXZlIGEgR0xURiBvYmplY3QgcmV0cmlldmVkIGZyb20gYFRIUkVFLkdMVEZMb2FkZXJgIGFuZCBjcmVhdGUgYSBuZXcgW1tWUk1dXSBpbnN0YW5jZS5cbiAgICpcbiAgICogQHBhcmFtIGdsdGYgQSBwYXJzZWQgcmVzdWx0IG9mIEdMVEYgdGFrZW4gZnJvbSBHTFRGTG9hZGVyXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgaW1wb3J0KGdsdGY6IFRIUkVFLkdMVEYpOiBQcm9taXNlPFZSTT4ge1xuICAgIGlmIChnbHRmLnBhcnNlci5qc29uLmV4dGVuc2lvbnMgPT09IHVuZGVmaW5lZCB8fCBnbHRmLnBhcnNlci5qc29uLmV4dGVuc2lvbnMuVlJNID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGZpbmQgVlJNIGV4dGVuc2lvbiBvbiB0aGUgR0xURicpO1xuICAgIH1cbiAgICBjb25zdCB2cm1FeHQ6IFZSTVNjaGVtYS5WUk0gPSBnbHRmLnBhcnNlci5qc29uLmV4dGVuc2lvbnMuVlJNO1xuXG4gICAgY29uc3Qgc2NlbmUgPSBnbHRmLnNjZW5lO1xuXG4gICAgc2NlbmUudXBkYXRlTWF0cml4V29ybGQoZmFsc2UpO1xuXG4gICAgLy8gU2tpbm5lZCBvYmplY3Qgc2hvdWxkIG5vdCBiZSBmcnVzdHVtQ3VsbGVkXG4gICAgLy8gU2luY2UgcHJlLXNraW5uZWQgcG9zaXRpb24gbWlnaHQgYmUgb3V0c2lkZSBvZiB2aWV3XG4gICAgc2NlbmUudHJhdmVyc2UoKG9iamVjdDNkKSA9PiB7XG4gICAgICBpZiAoKG9iamVjdDNkIGFzIGFueSkuaXNNZXNoKSB7XG4gICAgICAgIG9iamVjdDNkLmZydXN0dW1DdWxsZWQgPSBmYWxzZTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHJlZHVjZUJvbmVzKHNjZW5lKTtcblxuICAgIGNvbnN0IG1hdGVyaWFscyA9IChhd2FpdCB0aGlzLl9tYXRlcmlhbEltcG9ydGVyLmNvbnZlcnRHTFRGTWF0ZXJpYWxzKGdsdGYpKSB8fCB1bmRlZmluZWQ7XG5cbiAgICBjb25zdCBodW1hbm9pZCA9IChhd2FpdCB0aGlzLl9odW1hbm9pZEltcG9ydGVyLmltcG9ydChnbHRmKSkgfHwgdW5kZWZpbmVkO1xuXG4gICAgY29uc3QgZmlyc3RQZXJzb24gPSBodW1hbm9pZCA/IChhd2FpdCB0aGlzLl9maXJzdFBlcnNvbkltcG9ydGVyLmltcG9ydChnbHRmLCBodW1hbm9pZCkpIHx8IHVuZGVmaW5lZCA6IHVuZGVmaW5lZDtcblxuICAgIGNvbnN0IGJsZW5kU2hhcGVQcm94eSA9IChhd2FpdCB0aGlzLl9ibGVuZFNoYXBlSW1wb3J0ZXIuaW1wb3J0KGdsdGYpKSB8fCB1bmRlZmluZWQ7XG5cbiAgICBjb25zdCBsb29rQXQgPVxuICAgICAgZmlyc3RQZXJzb24gJiYgYmxlbmRTaGFwZVByb3h5ICYmIGh1bWFub2lkXG4gICAgICAgID8gKGF3YWl0IHRoaXMuX2xvb2tBdEltcG9ydGVyLmltcG9ydChnbHRmLCBmaXJzdFBlcnNvbiwgYmxlbmRTaGFwZVByb3h5LCBodW1hbm9pZCkpIHx8IHVuZGVmaW5lZFxuICAgICAgICA6IHVuZGVmaW5lZDtcblxuICAgIGNvbnN0IHNwcmluZ0JvbmVNYW5hZ2VyID0gKGF3YWl0IHRoaXMuX3NwcmluZ0JvbmVJbXBvcnRlci5pbXBvcnQoZ2x0ZikpIHx8IHVuZGVmaW5lZDtcblxuICAgIHJldHVybiBuZXcgVlJNKHtcbiAgICAgIHNjZW5lOiBnbHRmLnNjZW5lLFxuICAgICAgbWV0YTogdnJtRXh0Lm1ldGEsXG4gICAgICBtYXRlcmlhbHMsXG4gICAgICBodW1hbm9pZCxcbiAgICAgIGZpcnN0UGVyc29uLFxuICAgICAgYmxlbmRTaGFwZVByb3h5LFxuICAgICAgbG9va0F0LFxuICAgICAgc3ByaW5nQm9uZU1hbmFnZXIsXG4gICAgfSk7XG4gIH1cbn1cbiIsImltcG9ydCAqIGFzIFRIUkVFIGZyb20gJ3RocmVlJztcbmltcG9ydCB7IEdMVEZQcmltaXRpdmUgfSBmcm9tICcuLi90eXBlcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgVlJNQmxlbmRTaGFwZUJpbmQge1xuICBtZXNoZXM6IEdMVEZQcmltaXRpdmVbXTtcbiAgbW9ycGhUYXJnZXRJbmRleDogbnVtYmVyO1xuICB3ZWlnaHQ6IG51bWJlcjtcbn1cblxuZW51bSBWUk1CbGVuZFNoYXBlTWF0ZXJpYWxWYWx1ZVR5cGUge1xuICBOVU1CRVIsXG4gIFZFQ1RPUjIsXG4gIFZFQ1RPUjMsXG4gIFZFQ1RPUjQsXG4gIENPTE9SLFxufVxuXG5leHBvcnQgaW50ZXJmYWNlIFZSTUJsZW5kU2hhcGVNYXRlcmlhbFZhbHVlIHtcbiAgbWF0ZXJpYWw6IFRIUkVFLk1hdGVyaWFsO1xuICBwcm9wZXJ0eU5hbWU6IHN0cmluZztcbiAgZGVmYXVsdFZhbHVlOiBudW1iZXIgfCBUSFJFRS5WZWN0b3IyIHwgVEhSRUUuVmVjdG9yMyB8IFRIUkVFLlZlY3RvcjQgfCBUSFJFRS5Db2xvcjtcbiAgdGFyZ2V0VmFsdWU6IG51bWJlciB8IFRIUkVFLlZlY3RvcjIgfCBUSFJFRS5WZWN0b3IzIHwgVEhSRUUuVmVjdG9yNCB8IFRIUkVFLkNvbG9yO1xuICBkZWx0YVZhbHVlOiBudW1iZXIgfCBUSFJFRS5WZWN0b3IyIHwgVEhSRUUuVmVjdG9yMyB8IFRIUkVFLlZlY3RvcjQgfCBUSFJFRS5Db2xvcjsgLy8gdGFyZ2V0VmFsdWUgLSBkZWZhdWx0VmFsdWVcbiAgdHlwZTogVlJNQmxlbmRTaGFwZU1hdGVyaWFsVmFsdWVUeXBlO1xufVxuXG5jb25zdCBfdjIgPSBuZXcgVEhSRUUuVmVjdG9yMigpO1xuY29uc3QgX3YzID0gbmV3IFRIUkVFLlZlY3RvcjMoKTtcbmNvbnN0IF92NCA9IG5ldyBUSFJFRS5WZWN0b3I0KCk7XG5jb25zdCBfY29sb3IgPSBuZXcgVEhSRUUuQ29sb3IoKTtcblxuLy8gYW5pbWF0aW9uTWl4ZXIg44Gu55uj6KaW5a++6LGh44Gv44CBU2NlbmUg44Gu5Lit44Gr5YWl44Gj44Gm44GE44KL5b+F6KaB44GM44GC44KL44CCXG4vLyDjgZ3jga7jgZ/jgoHjgIHooajnpLrjgqrjg5bjgrjjgqfjgq/jg4jjgafjga/jgarjgYTjgZHjgozjganjgIFPYmplY3QzRCDjgpLntpnmib/jgZfjgaYgU2NlbmUg44Gr5oqV5YWl44Gn44GN44KL44KI44GG44Gr44GZ44KL44CCXG5leHBvcnQgY2xhc3MgVlJNQmxlbmRTaGFwZUdyb3VwIGV4dGVuZHMgVEhSRUUuT2JqZWN0M0Qge1xuICBwdWJsaWMgd2VpZ2h0ID0gMC4wO1xuICBwdWJsaWMgaXNCaW5hcnkgPSBmYWxzZTtcblxuICBwcml2YXRlIF9iaW5kczogVlJNQmxlbmRTaGFwZUJpbmRbXSA9IFtdO1xuICBwcml2YXRlIF9tYXRlcmlhbFZhbHVlczogVlJNQmxlbmRTaGFwZU1hdGVyaWFsVmFsdWVbXSA9IFtdO1xuXG4gIGNvbnN0cnVjdG9yKGV4cHJlc3Npb25OYW1lOiBzdHJpbmcpIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMubmFtZSA9IGBCbGVuZFNoYXBlQ29udHJvbGxlcl8ke2V4cHJlc3Npb25OYW1lfWA7XG5cbiAgICAvLyB0cmF2ZXJzZSDmmYLjga7mlZHmuIjmiYvmrrXjgajjgZfjgaYgT2JqZWN0M0Qg44Gn44Gv44Gq44GE44GT44Go44KS5piO56S644GX44Gm44GK44GPXG4gICAgdGhpcy50eXBlID0gJ0JsZW5kU2hhcGVDb250cm9sbGVyJztcbiAgICAvLyDooajnpLrnm67nmoTjga7jgqrjg5bjgrjjgqfjgq/jg4jjgafjga/jgarjgYTjga7jgafjgIHosqDojbfou73muJvjga7jgZ/jgoHjgasgdmlzaWJsZSDjgpIgZmFsc2Ug44Gr44GX44Gm44GK44GP44CCXG4gICAgLy8g44GT44KM44Gr44KI44KK44CB44GT44Gu44Kk44Oz44K544K/44Oz44K544Gr5a++44GZ44KL5q+O44OV44Os44O844Og44GuIG1hdHJpeCDoh6rli5XoqIjnrpfjgpLnnIHnlaXjgafjgY3jgovjgIJcbiAgICB0aGlzLnZpc2libGUgPSBmYWxzZTtcbiAgfVxuXG4gIHB1YmxpYyBhZGRCaW5kKGFyZ3M6IHsgbWVzaGVzOiBHTFRGUHJpbWl0aXZlW107IG1vcnBoVGFyZ2V0SW5kZXg6IG51bWJlcjsgd2VpZ2h0OiBudW1iZXIgfSk6IHZvaWQge1xuICAgIC8vIG9yaWdpbmFsIHdlaWdodCBpcyAwLTEwMCBidXQgd2Ugd2FudCB0byBkZWFsIHdpdGggdGhpcyB2YWx1ZSB3aXRoaW4gMC0xXG4gICAgY29uc3Qgd2VpZ2h0ID0gYXJncy53ZWlnaHQgLyAxMDA7XG5cbiAgICB0aGlzLl9iaW5kcy5wdXNoKHtcbiAgICAgIG1lc2hlczogYXJncy5tZXNoZXMsXG4gICAgICBtb3JwaFRhcmdldEluZGV4OiBhcmdzLm1vcnBoVGFyZ2V0SW5kZXgsXG4gICAgICB3ZWlnaHQsXG4gICAgfSk7XG4gIH1cblxuICBwdWJsaWMgYWRkTWF0ZXJpYWxWYWx1ZShhcmdzOiB7XG4gICAgbWF0ZXJpYWw6IFRIUkVFLk1hdGVyaWFsO1xuICAgIHByb3BlcnR5TmFtZTogc3RyaW5nO1xuICAgIHRhcmdldFZhbHVlOiBudW1iZXJbXTtcbiAgICBkZWZhdWx0VmFsdWU/OiBudW1iZXIgfCBUSFJFRS5WZWN0b3IyIHwgVEhSRUUuVmVjdG9yMyB8IFRIUkVFLlZlY3RvcjQgfCBUSFJFRS5Db2xvcjtcbiAgfSk6IHZvaWQge1xuICAgIGNvbnN0IG1hdGVyaWFsID0gYXJncy5tYXRlcmlhbDtcbiAgICBjb25zdCBwcm9wZXJ0eU5hbWUgPSBhcmdzLnByb3BlcnR5TmFtZTtcblxuICAgIGxldCB2YWx1ZSA9IChtYXRlcmlhbCBhcyBhbnkpW3Byb3BlcnR5TmFtZV07XG4gICAgaWYgKCF2YWx1ZSkge1xuICAgICAgLy8gcHJvcGVydHkgaGFzIG5vdCBiZWVuIGZvdW5kXG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHZhbHVlID0gYXJncy5kZWZhdWx0VmFsdWUgfHwgdmFsdWU7XG5cbiAgICBsZXQgdHlwZTogVlJNQmxlbmRTaGFwZU1hdGVyaWFsVmFsdWVUeXBlO1xuICAgIGxldCBkZWZhdWx0VmFsdWU6IG51bWJlciB8IFRIUkVFLlZlY3RvcjIgfCBUSFJFRS5WZWN0b3IzIHwgVEhSRUUuVmVjdG9yNCB8IFRIUkVFLkNvbG9yO1xuICAgIGxldCB0YXJnZXRWYWx1ZTogbnVtYmVyIHwgVEhSRUUuVmVjdG9yMiB8IFRIUkVFLlZlY3RvcjMgfCBUSFJFRS5WZWN0b3I0IHwgVEhSRUUuQ29sb3I7XG4gICAgbGV0IGRlbHRhVmFsdWU6IG51bWJlciB8IFRIUkVFLlZlY3RvcjIgfCBUSFJFRS5WZWN0b3IzIHwgVEhSRUUuVmVjdG9yNCB8IFRIUkVFLkNvbG9yO1xuXG4gICAgaWYgKCh2YWx1ZSBhcyBhbnkpLmlzVmVjdG9yMikge1xuICAgICAgdHlwZSA9IFZSTUJsZW5kU2hhcGVNYXRlcmlhbFZhbHVlVHlwZS5WRUNUT1IyO1xuICAgICAgZGVmYXVsdFZhbHVlID0gKHZhbHVlIGFzIFRIUkVFLlZlY3RvcjIpLmNsb25lKCk7XG4gICAgICB0YXJnZXRWYWx1ZSA9IG5ldyBUSFJFRS5WZWN0b3IyKCkuZnJvbUFycmF5KGFyZ3MudGFyZ2V0VmFsdWUpO1xuICAgICAgZGVsdGFWYWx1ZSA9IHRhcmdldFZhbHVlLmNsb25lKCkuc3ViKGRlZmF1bHRWYWx1ZSk7XG4gICAgfSBlbHNlIGlmICgodmFsdWUgYXMgYW55KS5pc1ZlY3RvcjMpIHtcbiAgICAgIHR5cGUgPSBWUk1CbGVuZFNoYXBlTWF0ZXJpYWxWYWx1ZVR5cGUuVkVDVE9SMztcbiAgICAgIGRlZmF1bHRWYWx1ZSA9ICh2YWx1ZSBhcyBUSFJFRS5WZWN0b3IzKS5jbG9uZSgpO1xuICAgICAgdGFyZ2V0VmFsdWUgPSBuZXcgVEhSRUUuVmVjdG9yMygpLmZyb21BcnJheShhcmdzLnRhcmdldFZhbHVlKTtcbiAgICAgIGRlbHRhVmFsdWUgPSB0YXJnZXRWYWx1ZS5jbG9uZSgpLnN1YihkZWZhdWx0VmFsdWUpO1xuICAgIH0gZWxzZSBpZiAoKHZhbHVlIGFzIGFueSkuaXNWZWN0b3I0KSB7XG4gICAgICB0eXBlID0gVlJNQmxlbmRTaGFwZU1hdGVyaWFsVmFsdWVUeXBlLlZFQ1RPUjQ7XG4gICAgICBkZWZhdWx0VmFsdWUgPSAodmFsdWUgYXMgVEhSRUUuVmVjdG9yNCkuY2xvbmUoKTtcblxuICAgICAgLy8gdmVjdG9yUHJvcGVydHkgYW5kIHRhcmdldFZhbHVlIGluZGV4IGlzIGRpZmZlcmVudCBmcm9tIGVhY2ggb3RoZXJcbiAgICAgIC8vIGV4cG9ydGVkIHZybSBieSBVbmlWUk0gZmlsZSBpc1xuICAgICAgLy9cbiAgICAgIC8vIHZlY3RvclByb3BlcnR5XG4gICAgICAvLyBvZmZzZXQgPSB0YXJnZXRWYWx1ZVswXSwgdGFyZ2V0VmFsdWVbMV1cbiAgICAgIC8vIHRpbGluZyA9IHRhcmdldFZhbHVlWzJdLCB0YXJnZXRWYWx1ZVszXVxuICAgICAgLy9cbiAgICAgIC8vIHRhcmdldFZhbHVlXG4gICAgICAvLyBvZmZzZXQgPSB0YXJnZXRWYWx1ZVsyXSwgdGFyZ2V0VmFsdWVbM11cbiAgICAgIC8vIHRpbGluZyA9IHRhcmdldFZhbHVlWzBdLCB0YXJnZXRWYWx1ZVsxXVxuICAgICAgdGFyZ2V0VmFsdWUgPSBuZXcgVEhSRUUuVmVjdG9yNCgpLmZyb21BcnJheShbXG4gICAgICAgIGFyZ3MudGFyZ2V0VmFsdWVbMl0sXG4gICAgICAgIGFyZ3MudGFyZ2V0VmFsdWVbM10sXG4gICAgICAgIGFyZ3MudGFyZ2V0VmFsdWVbMF0sXG4gICAgICAgIGFyZ3MudGFyZ2V0VmFsdWVbMV0sXG4gICAgICBdKTtcbiAgICAgIGRlbHRhVmFsdWUgPSB0YXJnZXRWYWx1ZS5jbG9uZSgpLnN1YihkZWZhdWx0VmFsdWUpO1xuICAgIH0gZWxzZSBpZiAoKHZhbHVlIGFzIGFueSkuaXNDb2xvcikge1xuICAgICAgdHlwZSA9IFZSTUJsZW5kU2hhcGVNYXRlcmlhbFZhbHVlVHlwZS5DT0xPUjtcbiAgICAgIGRlZmF1bHRWYWx1ZSA9ICh2YWx1ZSBhcyBUSFJFRS5Db2xvcikuY2xvbmUoKTtcbiAgICAgIHRhcmdldFZhbHVlID0gbmV3IFRIUkVFLkNvbG9yKCkuZnJvbUFycmF5KGFyZ3MudGFyZ2V0VmFsdWUpO1xuICAgICAgZGVsdGFWYWx1ZSA9IHRhcmdldFZhbHVlLmNsb25lKCkuc3ViKGRlZmF1bHRWYWx1ZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHR5cGUgPSBWUk1CbGVuZFNoYXBlTWF0ZXJpYWxWYWx1ZVR5cGUuTlVNQkVSO1xuICAgICAgZGVmYXVsdFZhbHVlID0gdmFsdWUgYXMgbnVtYmVyO1xuICAgICAgdGFyZ2V0VmFsdWUgPSBhcmdzLnRhcmdldFZhbHVlWzBdO1xuICAgICAgZGVsdGFWYWx1ZSA9IHRhcmdldFZhbHVlIC0gZGVmYXVsdFZhbHVlO1xuICAgIH1cblxuICAgIHRoaXMuX21hdGVyaWFsVmFsdWVzLnB1c2goe1xuICAgICAgbWF0ZXJpYWwsXG4gICAgICBwcm9wZXJ0eU5hbWUsXG4gICAgICBkZWZhdWx0VmFsdWUsXG4gICAgICB0YXJnZXRWYWx1ZSxcbiAgICAgIGRlbHRhVmFsdWUsXG4gICAgICB0eXBlLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFwcGx5IHdlaWdodCB0byBldmVyeSBhc3NpZ25lZCBibGVuZCBzaGFwZXMuXG4gICAqIFNob3VsZCBiZSBjYWxsZWQgdmlhIHtAbGluayBCbGVuZFNoYXBlTWFzdGVyI3VwZGF0ZX0uXG4gICAqL1xuICBwdWJsaWMgYXBwbHlXZWlnaHQoKTogdm9pZCB7XG4gICAgY29uc3QgdyA9IHRoaXMuaXNCaW5hcnkgPyAodGhpcy53ZWlnaHQgPCAwLjUgPyAwLjAgOiAxLjApIDogdGhpcy53ZWlnaHQ7XG5cbiAgICB0aGlzLl9iaW5kcy5mb3JFYWNoKChiaW5kKSA9PiB7XG4gICAgICBiaW5kLm1lc2hlcy5mb3JFYWNoKChtZXNoKSA9PiB7XG4gICAgICAgIGlmICghbWVzaC5tb3JwaFRhcmdldEluZmx1ZW5jZXMpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH0gLy8gVE9ETzogd2Ugc2hvdWxkIGtpY2sgdGhpcyBhdCBgYWRkQmluZGBcbiAgICAgICAgbWVzaC5tb3JwaFRhcmdldEluZmx1ZW5jZXNbYmluZC5tb3JwaFRhcmdldEluZGV4XSArPSB3ICogYmluZC53ZWlnaHQ7XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIHRoaXMuX21hdGVyaWFsVmFsdWVzLmZvckVhY2goKG1hdGVyaWFsVmFsdWUpID0+IHtcbiAgICAgIGNvbnN0IHByb3AgPSAobWF0ZXJpYWxWYWx1ZS5tYXRlcmlhbCBhcyBhbnkpW21hdGVyaWFsVmFsdWUucHJvcGVydHlOYW1lXTtcbiAgICAgIGlmIChwcm9wID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfSAvLyBUT0RPOiB3ZSBzaG91bGQga2ljayB0aGlzIGF0IGBhZGRNYXRlcmlhbFZhbHVlYFxuXG4gICAgICBpZiAobWF0ZXJpYWxWYWx1ZS50eXBlID09PSBWUk1CbGVuZFNoYXBlTWF0ZXJpYWxWYWx1ZVR5cGUuTlVNQkVSKSB7XG4gICAgICAgIGNvbnN0IGRlbHRhVmFsdWUgPSBtYXRlcmlhbFZhbHVlLmRlbHRhVmFsdWUgYXMgbnVtYmVyO1xuICAgICAgICAobWF0ZXJpYWxWYWx1ZS5tYXRlcmlhbCBhcyBhbnkpW21hdGVyaWFsVmFsdWUucHJvcGVydHlOYW1lXSArPSBkZWx0YVZhbHVlICogdztcbiAgICAgIH0gZWxzZSBpZiAobWF0ZXJpYWxWYWx1ZS50eXBlID09PSBWUk1CbGVuZFNoYXBlTWF0ZXJpYWxWYWx1ZVR5cGUuVkVDVE9SMikge1xuICAgICAgICBjb25zdCBkZWx0YVZhbHVlID0gbWF0ZXJpYWxWYWx1ZS5kZWx0YVZhbHVlIGFzIFRIUkVFLlZlY3RvcjI7XG4gICAgICAgIChtYXRlcmlhbFZhbHVlLm1hdGVyaWFsIGFzIGFueSlbbWF0ZXJpYWxWYWx1ZS5wcm9wZXJ0eU5hbWVdLmFkZChfdjIuY29weShkZWx0YVZhbHVlKS5tdWx0aXBseVNjYWxhcih3KSk7XG4gICAgICB9IGVsc2UgaWYgKG1hdGVyaWFsVmFsdWUudHlwZSA9PT0gVlJNQmxlbmRTaGFwZU1hdGVyaWFsVmFsdWVUeXBlLlZFQ1RPUjMpIHtcbiAgICAgICAgY29uc3QgZGVsdGFWYWx1ZSA9IG1hdGVyaWFsVmFsdWUuZGVsdGFWYWx1ZSBhcyBUSFJFRS5WZWN0b3IzO1xuICAgICAgICAobWF0ZXJpYWxWYWx1ZS5tYXRlcmlhbCBhcyBhbnkpW21hdGVyaWFsVmFsdWUucHJvcGVydHlOYW1lXS5hZGQoX3YzLmNvcHkoZGVsdGFWYWx1ZSkubXVsdGlwbHlTY2FsYXIodykpO1xuICAgICAgfSBlbHNlIGlmIChtYXRlcmlhbFZhbHVlLnR5cGUgPT09IFZSTUJsZW5kU2hhcGVNYXRlcmlhbFZhbHVlVHlwZS5WRUNUT1I0KSB7XG4gICAgICAgIGNvbnN0IGRlbHRhVmFsdWUgPSBtYXRlcmlhbFZhbHVlLmRlbHRhVmFsdWUgYXMgVEhSRUUuVmVjdG9yNDtcbiAgICAgICAgKG1hdGVyaWFsVmFsdWUubWF0ZXJpYWwgYXMgYW55KVttYXRlcmlhbFZhbHVlLnByb3BlcnR5TmFtZV0uYWRkKF92NC5jb3B5KGRlbHRhVmFsdWUpLm11bHRpcGx5U2NhbGFyKHcpKTtcbiAgICAgIH0gZWxzZSBpZiAobWF0ZXJpYWxWYWx1ZS50eXBlID09PSBWUk1CbGVuZFNoYXBlTWF0ZXJpYWxWYWx1ZVR5cGUuQ09MT1IpIHtcbiAgICAgICAgY29uc3QgZGVsdGFWYWx1ZSA9IG1hdGVyaWFsVmFsdWUuZGVsdGFWYWx1ZSBhcyBUSFJFRS5Db2xvcjtcbiAgICAgICAgKG1hdGVyaWFsVmFsdWUubWF0ZXJpYWwgYXMgYW55KVttYXRlcmlhbFZhbHVlLnByb3BlcnR5TmFtZV0uYWRkKF9jb2xvci5jb3B5KGRlbHRhVmFsdWUpLm11bHRpcGx5U2NhbGFyKHcpKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHR5cGVvZiAobWF0ZXJpYWxWYWx1ZS5tYXRlcmlhbCBhcyBhbnkpLnNob3VsZEFwcGx5VW5pZm9ybXMgPT09ICdib29sZWFuJykge1xuICAgICAgICAobWF0ZXJpYWxWYWx1ZS5tYXRlcmlhbCBhcyBhbnkpLnNob3VsZEFwcGx5VW5pZm9ybXMgPSB0cnVlO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFyIHByZXZpb3VzbHkgYXNzaWduZWQgYmxlbmQgc2hhcGVzLlxuICAgKi9cbiAgcHVibGljIGNsZWFyQXBwbGllZFdlaWdodCgpOiB2b2lkIHtcbiAgICB0aGlzLl9iaW5kcy5mb3JFYWNoKChiaW5kKSA9PiB7XG4gICAgICBiaW5kLm1lc2hlcy5mb3JFYWNoKChtZXNoKSA9PiB7XG4gICAgICAgIGlmICghbWVzaC5tb3JwaFRhcmdldEluZmx1ZW5jZXMpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH0gLy8gVE9ETzogd2Ugc2hvdWxkIGtpY2sgdGhpcyBhdCBgYWRkQmluZGBcbiAgICAgICAgbWVzaC5tb3JwaFRhcmdldEluZmx1ZW5jZXNbYmluZC5tb3JwaFRhcmdldEluZGV4XSA9IDAuMDtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGhpcy5fbWF0ZXJpYWxWYWx1ZXMuZm9yRWFjaCgobWF0ZXJpYWxWYWx1ZSkgPT4ge1xuICAgICAgY29uc3QgcHJvcCA9IChtYXRlcmlhbFZhbHVlLm1hdGVyaWFsIGFzIGFueSlbbWF0ZXJpYWxWYWx1ZS5wcm9wZXJ0eU5hbWVdO1xuICAgICAgaWYgKHByb3AgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9IC8vIFRPRE86IHdlIHNob3VsZCBraWNrIHRoaXMgYXQgYGFkZE1hdGVyaWFsVmFsdWVgXG5cbiAgICAgIGlmIChtYXRlcmlhbFZhbHVlLnR5cGUgPT09IFZSTUJsZW5kU2hhcGVNYXRlcmlhbFZhbHVlVHlwZS5OVU1CRVIpIHtcbiAgICAgICAgY29uc3QgZGVmYXVsdFZhbHVlID0gbWF0ZXJpYWxWYWx1ZS5kZWZhdWx0VmFsdWUgYXMgbnVtYmVyO1xuICAgICAgICAobWF0ZXJpYWxWYWx1ZS5tYXRlcmlhbCBhcyBhbnkpW21hdGVyaWFsVmFsdWUucHJvcGVydHlOYW1lXSA9IGRlZmF1bHRWYWx1ZTtcbiAgICAgIH0gZWxzZSBpZiAobWF0ZXJpYWxWYWx1ZS50eXBlID09PSBWUk1CbGVuZFNoYXBlTWF0ZXJpYWxWYWx1ZVR5cGUuVkVDVE9SMikge1xuICAgICAgICBjb25zdCBkZWZhdWx0VmFsdWUgPSBtYXRlcmlhbFZhbHVlLmRlZmF1bHRWYWx1ZSBhcyBUSFJFRS5WZWN0b3IyO1xuICAgICAgICAobWF0ZXJpYWxWYWx1ZS5tYXRlcmlhbCBhcyBhbnkpW21hdGVyaWFsVmFsdWUucHJvcGVydHlOYW1lXS5jb3B5KGRlZmF1bHRWYWx1ZSk7XG4gICAgICB9IGVsc2UgaWYgKG1hdGVyaWFsVmFsdWUudHlwZSA9PT0gVlJNQmxlbmRTaGFwZU1hdGVyaWFsVmFsdWVUeXBlLlZFQ1RPUjMpIHtcbiAgICAgICAgY29uc3QgZGVmYXVsdFZhbHVlID0gbWF0ZXJpYWxWYWx1ZS5kZWZhdWx0VmFsdWUgYXMgVEhSRUUuVmVjdG9yMztcbiAgICAgICAgKG1hdGVyaWFsVmFsdWUubWF0ZXJpYWwgYXMgYW55KVttYXRlcmlhbFZhbHVlLnByb3BlcnR5TmFtZV0uY29weShkZWZhdWx0VmFsdWUpO1xuICAgICAgfSBlbHNlIGlmIChtYXRlcmlhbFZhbHVlLnR5cGUgPT09IFZSTUJsZW5kU2hhcGVNYXRlcmlhbFZhbHVlVHlwZS5WRUNUT1I0KSB7XG4gICAgICAgIGNvbnN0IGRlZmF1bHRWYWx1ZSA9IG1hdGVyaWFsVmFsdWUuZGVmYXVsdFZhbHVlIGFzIFRIUkVFLlZlY3RvcjQ7XG4gICAgICAgIChtYXRlcmlhbFZhbHVlLm1hdGVyaWFsIGFzIGFueSlbbWF0ZXJpYWxWYWx1ZS5wcm9wZXJ0eU5hbWVdLmNvcHkoZGVmYXVsdFZhbHVlKTtcbiAgICAgIH0gZWxzZSBpZiAobWF0ZXJpYWxWYWx1ZS50eXBlID09PSBWUk1CbGVuZFNoYXBlTWF0ZXJpYWxWYWx1ZVR5cGUuQ09MT1IpIHtcbiAgICAgICAgY29uc3QgZGVmYXVsdFZhbHVlID0gbWF0ZXJpYWxWYWx1ZS5kZWZhdWx0VmFsdWUgYXMgVEhSRUUuQ29sb3I7XG4gICAgICAgIChtYXRlcmlhbFZhbHVlLm1hdGVyaWFsIGFzIGFueSlbbWF0ZXJpYWxWYWx1ZS5wcm9wZXJ0eU5hbWVdLmNvcHkoZGVmYXVsdFZhbHVlKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHR5cGVvZiAobWF0ZXJpYWxWYWx1ZS5tYXRlcmlhbCBhcyBhbnkpLnNob3VsZEFwcGx5VW5pZm9ybXMgPT09ICdib29sZWFuJykge1xuICAgICAgICAobWF0ZXJpYWxWYWx1ZS5tYXRlcmlhbCBhcyBhbnkpLnNob3VsZEFwcGx5VW5pZm9ybXMgPSB0cnVlO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG59XG4iLCJpbXBvcnQgKiBhcyBUSFJFRSBmcm9tICd0aHJlZSc7XG5pbXBvcnQgeyBHTFRGTWVzaCwgR0xURlByaW1pdGl2ZSwgVlJNU2NoZW1hIH0gZnJvbSAnLi4vdHlwZXMnO1xuaW1wb3J0IHsgcmVuYW1lTWF0ZXJpYWxQcm9wZXJ0eSB9IGZyb20gJy4uL3V0aWxzL3JlbmFtZU1hdGVyaWFsUHJvcGVydHknO1xuaW1wb3J0IHsgVlJNQmxlbmRTaGFwZUdyb3VwIH0gZnJvbSAnLi9WUk1CbGVuZFNoYXBlR3JvdXAnO1xuaW1wb3J0IHsgVlJNQmxlbmRTaGFwZVByb3h5IH0gZnJvbSAnLi9WUk1CbGVuZFNoYXBlUHJveHknO1xuXG4vKipcbiAqIEFuIGltcG9ydGVyIHRoYXQgaW1wb3J0cyBhIFtbVlJNQmxlbmRTaGFwZV1dIGZyb20gYSBWUk0gZXh0ZW5zaW9uIG9mIGEgR0xURi5cbiAqL1xuZXhwb3J0IGNsYXNzIFZSTUJsZW5kU2hhcGVJbXBvcnRlciB7XG4gIC8qKlxuICAgKiBJbXBvcnQgYSBbW1ZSTUJsZW5kU2hhcGVdXSBmcm9tIGEgVlJNLlxuICAgKlxuICAgKiBAcGFyYW0gZ2x0ZiBBIHBhcnNlZCByZXN1bHQgb2YgR0xURiB0YWtlbiBmcm9tIEdMVEZMb2FkZXJcbiAgICovXG4gIHB1YmxpYyBhc3luYyBpbXBvcnQoZ2x0ZjogVEhSRUUuR0xURik6IFByb21pc2U8VlJNQmxlbmRTaGFwZVByb3h5IHwgbnVsbD4ge1xuICAgIGNvbnN0IHZybUV4dDogVlJNU2NoZW1hLlZSTSB8IHVuZGVmaW5lZCA9IGdsdGYucGFyc2VyLmpzb24uZXh0ZW5zaW9ucyAmJiBnbHRmLnBhcnNlci5qc29uLmV4dGVuc2lvbnMuVlJNO1xuICAgIGlmICghdnJtRXh0KSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBjb25zdCBzY2hlbWFCbGVuZFNoYXBlOiBWUk1TY2hlbWEuQmxlbmRTaGFwZSB8IHVuZGVmaW5lZCA9IHZybUV4dC5ibGVuZFNoYXBlTWFzdGVyO1xuICAgIGlmICghc2NoZW1hQmxlbmRTaGFwZSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3QgYmxlbmRTaGFwZSA9IG5ldyBWUk1CbGVuZFNoYXBlUHJveHkoKTtcblxuICAgIGNvbnN0IGJsZW5kU2hhcGVHcm91cHM6IFZSTVNjaGVtYS5CbGVuZFNoYXBlR3JvdXBbXSB8IHVuZGVmaW5lZCA9IHNjaGVtYUJsZW5kU2hhcGUuYmxlbmRTaGFwZUdyb3VwcztcbiAgICBpZiAoIWJsZW5kU2hhcGVHcm91cHMpIHtcbiAgICAgIHJldHVybiBibGVuZFNoYXBlO1xuICAgIH1cblxuICAgIGNvbnN0IGJsZW5kU2hhcGVQcmVzZXRNYXA6IHsgW3ByZXNldE5hbWUgaW4gVlJNU2NoZW1hLkJsZW5kU2hhcGVQcmVzZXROYW1lXT86IHN0cmluZyB9ID0ge307XG5cbiAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgIGJsZW5kU2hhcGVHcm91cHMubWFwKGFzeW5jIChzY2hlbWFHcm91cCkgPT4ge1xuICAgICAgICBjb25zdCBuYW1lID0gc2NoZW1hR3JvdXAubmFtZTtcbiAgICAgICAgaWYgKG5hbWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIGNvbnNvbGUud2FybignVlJNQmxlbmRTaGFwZUltcG9ydGVyOiBPbmUgb2YgYmxlbmRTaGFwZUdyb3VwcyBoYXMgbm8gbmFtZScpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBwcmVzZXROYW1lOiBWUk1TY2hlbWEuQmxlbmRTaGFwZVByZXNldE5hbWUgfCB1bmRlZmluZWQ7XG4gICAgICAgIGlmIChcbiAgICAgICAgICBzY2hlbWFHcm91cC5wcmVzZXROYW1lICYmXG4gICAgICAgICAgc2NoZW1hR3JvdXAucHJlc2V0TmFtZSAhPT0gVlJNU2NoZW1hLkJsZW5kU2hhcGVQcmVzZXROYW1lLlVua25vd24gJiZcbiAgICAgICAgICAhYmxlbmRTaGFwZVByZXNldE1hcFtzY2hlbWFHcm91cC5wcmVzZXROYW1lXVxuICAgICAgICApIHtcbiAgICAgICAgICBwcmVzZXROYW1lID0gc2NoZW1hR3JvdXAucHJlc2V0TmFtZTtcbiAgICAgICAgICBibGVuZFNoYXBlUHJlc2V0TWFwW3NjaGVtYUdyb3VwLnByZXNldE5hbWVdID0gbmFtZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGdyb3VwID0gbmV3IFZSTUJsZW5kU2hhcGVHcm91cChuYW1lKTtcbiAgICAgICAgZ2x0Zi5zY2VuZS5hZGQoZ3JvdXApO1xuXG4gICAgICAgIGdyb3VwLmlzQmluYXJ5ID0gc2NoZW1hR3JvdXAuaXNCaW5hcnkgfHwgZmFsc2U7XG5cbiAgICAgICAgaWYgKHNjaGVtYUdyb3VwLmJpbmRzKSB7XG4gICAgICAgICAgc2NoZW1hR3JvdXAuYmluZHMuZm9yRWFjaChhc3luYyAoYmluZCkgPT4ge1xuICAgICAgICAgICAgaWYgKGJpbmQubWVzaCA9PT0gdW5kZWZpbmVkIHx8IGJpbmQuaW5kZXggPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IG1vcnBoTWVzaGVzOiBHTFRGTWVzaCA9IGF3YWl0IGdsdGYucGFyc2VyLmdldERlcGVuZGVuY3koJ21lc2gnLCBiaW5kLm1lc2gpO1xuICAgICAgICAgICAgY29uc3QgcHJpbWl0aXZlczogR0xURlByaW1pdGl2ZVtdID1cbiAgICAgICAgICAgICAgbW9ycGhNZXNoZXMudHlwZSA9PT0gJ0dyb3VwJ1xuICAgICAgICAgICAgICAgID8gKG1vcnBoTWVzaGVzLmNoaWxkcmVuIGFzIEFycmF5PEdMVEZQcmltaXRpdmU+KVxuICAgICAgICAgICAgICAgIDogW21vcnBoTWVzaGVzIGFzIEdMVEZQcmltaXRpdmVdO1xuICAgICAgICAgICAgY29uc3QgbW9ycGhUYXJnZXRJbmRleCA9IGJpbmQuaW5kZXg7XG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICFwcmltaXRpdmVzLmV2ZXJ5KFxuICAgICAgICAgICAgICAgIChwcmltaXRpdmUpID0+XG4gICAgICAgICAgICAgICAgICBBcnJheS5pc0FycmF5KHByaW1pdGl2ZS5tb3JwaFRhcmdldEluZmx1ZW5jZXMpICYmXG4gICAgICAgICAgICAgICAgICBtb3JwaFRhcmdldEluZGV4IDwgcHJpbWl0aXZlLm1vcnBoVGFyZ2V0SW5mbHVlbmNlcy5sZW5ndGgsXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgICAgICAgYFZSTUJsZW5kU2hhcGVJbXBvcnRlcjogJHtzY2hlbWFHcm91cC5uYW1lfSBhdHRlbXB0cyB0byBpbmRleCAke21vcnBoVGFyZ2V0SW5kZXh9dGggbW9ycGggYnV0IG5vdCBmb3VuZC5gLFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGdyb3VwLmFkZEJpbmQoe1xuICAgICAgICAgICAgICBtZXNoZXM6IHByaW1pdGl2ZXMsXG4gICAgICAgICAgICAgIG1vcnBoVGFyZ2V0SW5kZXgsXG4gICAgICAgICAgICAgIHdlaWdodDogYmluZC53ZWlnaHQgfHwgMTAwLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBtYXRlcmlhbFZhbHVlcyA9IHNjaGVtYUdyb3VwLm1hdGVyaWFsVmFsdWVzO1xuICAgICAgICBpZiAobWF0ZXJpYWxWYWx1ZXMpIHtcbiAgICAgICAgICBtYXRlcmlhbFZhbHVlcy5mb3JFYWNoKChtYXRlcmlhbFZhbHVlKSA9PiB7XG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgIG1hdGVyaWFsVmFsdWUubWF0ZXJpYWxOYW1lID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgICAgICAgbWF0ZXJpYWxWYWx1ZS5wcm9wZXJ0eU5hbWUgPT09IHVuZGVmaW5lZCB8fFxuICAgICAgICAgICAgICBtYXRlcmlhbFZhbHVlLnRhcmdldFZhbHVlID09PSB1bmRlZmluZWRcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IG1hdGVyaWFsczogVEhSRUUuTWF0ZXJpYWxbXSA9IFtdO1xuICAgICAgICAgICAgZ2x0Zi5zY2VuZS50cmF2ZXJzZSgob2JqZWN0KSA9PiB7XG4gICAgICAgICAgICAgIGlmICgob2JqZWN0IGFzIGFueSkubWF0ZXJpYWwpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBtYXRlcmlhbDogVEhSRUUuTWF0ZXJpYWxbXSB8IFRIUkVFLk1hdGVyaWFsID0gKG9iamVjdCBhcyBhbnkpLm1hdGVyaWFsO1xuICAgICAgICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KG1hdGVyaWFsKSkge1xuICAgICAgICAgICAgICAgICAgbWF0ZXJpYWxzLnB1c2goXG4gICAgICAgICAgICAgICAgICAgIC4uLm1hdGVyaWFsLmZpbHRlcihcbiAgICAgICAgICAgICAgICAgICAgICAobXRsKSA9PiBtdGwubmFtZSA9PT0gbWF0ZXJpYWxWYWx1ZS5tYXRlcmlhbE5hbWUhICYmIG1hdGVyaWFscy5pbmRleE9mKG10bCkgPT09IC0xLFxuICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKG1hdGVyaWFsLm5hbWUgPT09IG1hdGVyaWFsVmFsdWUubWF0ZXJpYWxOYW1lICYmIG1hdGVyaWFscy5pbmRleE9mKG1hdGVyaWFsKSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgIG1hdGVyaWFscy5wdXNoKG1hdGVyaWFsKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICBtYXRlcmlhbHMuZm9yRWFjaCgobWF0ZXJpYWwpID0+IHtcbiAgICAgICAgICAgICAgZ3JvdXAuYWRkTWF0ZXJpYWxWYWx1ZSh7XG4gICAgICAgICAgICAgICAgbWF0ZXJpYWwsXG4gICAgICAgICAgICAgICAgcHJvcGVydHlOYW1lOiByZW5hbWVNYXRlcmlhbFByb3BlcnR5KG1hdGVyaWFsVmFsdWUucHJvcGVydHlOYW1lISksXG4gICAgICAgICAgICAgICAgdGFyZ2V0VmFsdWU6IG1hdGVyaWFsVmFsdWUudGFyZ2V0VmFsdWUhLFxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgYmxlbmRTaGFwZS5yZWdpc3RlckJsZW5kU2hhcGVHcm91cChuYW1lLCBwcmVzZXROYW1lLCBncm91cCk7XG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgcmV0dXJuIGJsZW5kU2hhcGU7XG4gIH1cbn1cbiIsImltcG9ydCB7IFZSTVNjaGVtYSB9IGZyb20gJy4uL3R5cGVzJztcbmltcG9ydCB7IHNhdHVyYXRlIH0gZnJvbSAnLi4vdXRpbHMvbWF0aCc7XG5pbXBvcnQgeyBWUk1CbGVuZFNoYXBlR3JvdXAgfSBmcm9tICcuL1ZSTUJsZW5kU2hhcGVHcm91cCc7XG5cbmV4cG9ydCBjbGFzcyBWUk1CbGVuZFNoYXBlUHJveHkge1xuICAvKipcbiAgICogTGlzdCBvZiByZWdpc3RlcmVkIGJsZW5kIHNoYXBlLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IF9ibGVuZFNoYXBlR3JvdXBzOiB7IFtuYW1lOiBzdHJpbmddOiBWUk1CbGVuZFNoYXBlR3JvdXAgfSA9IHt9O1xuXG4gIC8qKlxuICAgKiBBIG1hcCBmcm9tIFtbVlJNU2NoZW1hLkJsZW5kU2hhcGVQcmVzZXROYW1lXV0gdG8gaXRzIGFjdHVhbCBibGVuZCBzaGFwZSBuYW1lLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBfYmxlbmRTaGFwZVByZXNldE1hcDogeyBbcHJlc2V0TmFtZSBpbiBWUk1TY2hlbWEuQmxlbmRTaGFwZVByZXNldE5hbWVdPzogc3RyaW5nIH0gPSB7fTtcblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IFZSTUJsZW5kU2hhcGUuXG4gICAqL1xuICBwdWJsaWMgY29uc3RydWN0b3IoKSB7XG4gICAgLy8gZG8gbm90aGluZ1xuICB9XG5cbiAgLyoqXG4gICAqIExpc3Qgb2YgbmFtZSBvZiByZWdpc3RlcmVkIGJsZW5kIHNoYXBlIGdyb3VwLlxuICAgKi9cbiAgcHVibGljIGdldCBleHByZXNzaW9ucygpOiBzdHJpbmdbXSB7XG4gICAgcmV0dXJuIE9iamVjdC5rZXlzKHRoaXMuX2JsZW5kU2hhcGVHcm91cHMpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiByZWdpc3RlcmVkIGJsZW5kIHNoYXBlIGdyb3VwLlxuICAgKlxuICAgKiBAcGFyYW0gbmFtZSBOYW1lIG9mIHRoZSBibGVuZCBzaGFwZSBncm91cFxuICAgKi9cbiAgcHVibGljIGdldEJsZW5kU2hhcGVHcm91cChuYW1lOiBzdHJpbmcgfCBWUk1TY2hlbWEuQmxlbmRTaGFwZVByZXNldE5hbWUpOiBWUk1CbGVuZFNoYXBlR3JvdXAgfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IHByZXNldE5hbWUgPSB0aGlzLl9ibGVuZFNoYXBlUHJlc2V0TWFwW25hbWUgYXMgVlJNU2NoZW1hLkJsZW5kU2hhcGVQcmVzZXROYW1lXTtcbiAgICBjb25zdCBjb250cm9sbGVyID0gcHJlc2V0TmFtZSA/IHRoaXMuX2JsZW5kU2hhcGVHcm91cHNbcHJlc2V0TmFtZV0gOiB0aGlzLl9ibGVuZFNoYXBlR3JvdXBzW25hbWVdO1xuICAgIGlmICghY29udHJvbGxlcikge1xuICAgICAgY29uc29sZS53YXJuKGBubyBibGVuZCBzaGFwZSBmb3VuZCBieSAke25hbWV9YCk7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICByZXR1cm4gY29udHJvbGxlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpc3RlciBhIGJsZW5kIHNoYXBlIGdyb3VwLlxuICAgKlxuICAgKiBAcGFyYW0gbmFtZSBOYW1lIG9mIHRoZSBibGVuZCBzaGFwZSBnb3J1cFxuICAgKiBAcGFyYW0gY29udHJvbGxlciBWUk1CbGVuZFNoYXBlQ29udHJvbGxlciB0aGF0IGRlc2NyaWJlcyB0aGUgYmxlbmQgc2hhcGUgZ3JvdXBcbiAgICovXG4gIHB1YmxpYyByZWdpc3RlckJsZW5kU2hhcGVHcm91cChcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgcHJlc2V0TmFtZTogVlJNU2NoZW1hLkJsZW5kU2hhcGVQcmVzZXROYW1lIHwgdW5kZWZpbmVkLFxuICAgIGNvbnRyb2xsZXI6IFZSTUJsZW5kU2hhcGVHcm91cCxcbiAgKTogdm9pZCB7XG4gICAgdGhpcy5fYmxlbmRTaGFwZUdyb3Vwc1tuYW1lXSA9IGNvbnRyb2xsZXI7XG4gICAgaWYgKHByZXNldE5hbWUpIHtcbiAgICAgIHRoaXMuX2JsZW5kU2hhcGVQcmVzZXRNYXBbcHJlc2V0TmFtZV0gPSBuYW1lO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgY3VycmVudCB3ZWlnaHQgb2Ygc3BlY2lmaWVkIGJsZW5kIHNoYXBlIGdyb3VwLlxuICAgKlxuICAgKiBAcGFyYW0gbmFtZSBOYW1lIG9mIHRoZSBibGVuZCBzaGFwZSBncm91cFxuICAgKi9cbiAgcHVibGljIGdldFZhbHVlKG5hbWU6IFZSTVNjaGVtYS5CbGVuZFNoYXBlUHJlc2V0TmFtZSB8IHN0cmluZyk6IG51bWJlciB8IG51bGwge1xuICAgIGNvbnN0IGNvbnRyb2xsZXIgPSB0aGlzLmdldEJsZW5kU2hhcGVHcm91cChuYW1lKTtcbiAgICByZXR1cm4gKGNvbnRyb2xsZXIgJiYgY29udHJvbGxlci53ZWlnaHQpIHx8IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogU2V0IGEgd2VpZ2h0IHRvIHNwZWNpZmllZCBibGVuZCBzaGFwZSBncm91cC5cbiAgICpcbiAgICogQHBhcmFtIG5hbWUgTmFtZSBvZiB0aGUgYmxlbmQgc2hhcGUgZ3JvdXBcbiAgICogQHBhcmFtIHdlaWdodCBXZWlnaHRcbiAgICovXG4gIHB1YmxpYyBzZXRWYWx1ZShuYW1lOiBWUk1TY2hlbWEuQmxlbmRTaGFwZVByZXNldE5hbWUgfCBzdHJpbmcsIHdlaWdodDogbnVtYmVyKTogdm9pZCB7XG4gICAgY29uc3QgY29udHJvbGxlciA9IHRoaXMuZ2V0QmxlbmRTaGFwZUdyb3VwKG5hbWUpO1xuICAgIGlmIChjb250cm9sbGVyKSB7XG4gICAgICBjb250cm9sbGVyLndlaWdodCA9IHNhdHVyYXRlKHdlaWdodCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhIHRyYWNrIG5hbWUgb2Ygc3BlY2lmaWVkIGJsZW5kIHNoYXBlIGdyb3VwLlxuICAgKiBUaGlzIHRyYWNrIG5hbWUgaXMgbmVlZGVkIHRvIG1hbmlwdWxhdGUgaXRzIGJsZW5kIHNoYXBlIGdyb3VwIHZpYSBrZXlmcmFtZSBhbmltYXRpb25zLlxuICAgKlxuICAgKiBAZXhhbXBsZSBNYW5pcHVsYXRlIGEgYmxlbmQgc2hhcGUgZ3JvdXAgdXNpbmcga2V5ZnJhbWUgYW5pbWF0aW9uXG4gICAqIGBgYGpzXG4gICAqIGNvbnN0IHRyYWNrTmFtZSA9IHZybS5ibGVuZFNoYXBlUHJveHkuZ2V0QmxlbmRTaGFwZVRyYWNrTmFtZSggVEhSRUUuVlJNU2NoZW1hLkJsZW5kU2hhcGVQcmVzZXROYW1lLkJsaW5rICk7XG4gICAqIGNvbnN0IHRyYWNrID0gbmV3IFRIUkVFLk51bWJlcktleWZyYW1lVHJhY2soXG4gICAqICAgbmFtZSxcbiAgICogICBbIDAuMCwgMC41LCAxLjAgXSwgLy8gdGltZXNcbiAgICogICBbIDAuMCwgMS4wLCAwLjAgXSAvLyB2YWx1ZXNcbiAgICogKTtcbiAgICpcbiAgICogY29uc3QgY2xpcCA9IG5ldyBUSFJFRS5BbmltYXRpb25DbGlwKFxuICAgKiAgICdibGluaycsIC8vIG5hbWVcbiAgICogICAxLjAsIC8vIGR1cmF0aW9uXG4gICAqICAgWyB0cmFjayBdIC8vIHRyYWNrc1xuICAgKiApO1xuICAgKlxuICAgKiBjb25zdCBtaXhlciA9IG5ldyBUSFJFRS5BbmltYXRpb25NaXhlciggdnJtLnNjZW5lICk7XG4gICAqIGNvbnN0IGFjdGlvbiA9IG1peGVyLmNsaXBBY3Rpb24oIGNsaXAgKTtcbiAgICogYWN0aW9uLnBsYXkoKTtcbiAgICogYGBgXG4gICAqXG4gICAqIEBwYXJhbSBuYW1lIE5hbWUgb2YgdGhlIGJsZW5kIHNoYXBlIGdyb3VwXG4gICAqL1xuICBwdWJsaWMgZ2V0QmxlbmRTaGFwZVRyYWNrTmFtZShuYW1lOiBWUk1TY2hlbWEuQmxlbmRTaGFwZVByZXNldE5hbWUgfCBzdHJpbmcpOiBzdHJpbmcgfCBudWxsIHtcbiAgICBjb25zdCBjb250cm9sbGVyID0gdGhpcy5nZXRCbGVuZFNoYXBlR3JvdXAobmFtZSk7XG4gICAgcmV0dXJuIGNvbnRyb2xsZXIgPyBgJHtjb250cm9sbGVyLm5hbWV9LndlaWdodGAgOiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSBldmVyeSBibGVuZCBzaGFwZSBncm91cHMuXG4gICAqL1xuICBwdWJsaWMgdXBkYXRlKCk6IHZvaWQge1xuICAgIE9iamVjdC5rZXlzKHRoaXMuX2JsZW5kU2hhcGVHcm91cHMpLmZvckVhY2goKG5hbWUpID0+IHtcbiAgICAgIGNvbnN0IGNvbnRyb2xsZXIgPSB0aGlzLl9ibGVuZFNoYXBlR3JvdXBzW25hbWVdO1xuICAgICAgY29udHJvbGxlci5jbGVhckFwcGxpZWRXZWlnaHQoKTtcbiAgICB9KTtcblxuICAgIE9iamVjdC5rZXlzKHRoaXMuX2JsZW5kU2hhcGVHcm91cHMpLmZvckVhY2goKG5hbWUpID0+IHtcbiAgICAgIGNvbnN0IGNvbnRyb2xsZXIgPSB0aGlzLl9ibGVuZFNoYXBlR3JvdXBzW25hbWVdO1xuICAgICAgY29udHJvbGxlci5hcHBseVdlaWdodCgpO1xuICAgIH0pO1xuICB9XG59XG4iLCJleHBvcnQgKiBmcm9tICcuL1ZSTUJsZW5kU2hhcGVHcm91cCc7XG5leHBvcnQgKiBmcm9tICcuL1ZSTUJsZW5kU2hhcGVJbXBvcnRlcic7XG5leHBvcnQgKiBmcm9tICcuL1ZSTUJsZW5kU2hhcGVQcm94eSc7XG4iLCJpbXBvcnQgKiBhcyBUSFJFRSBmcm9tICd0aHJlZSc7XG5pbXBvcnQgeyBWUk0sIFZSTVBhcmFtZXRlcnMgfSBmcm9tICcuLi9WUk0nO1xuaW1wb3J0IHsgVlJNSW1wb3J0ZXJPcHRpb25zIH0gZnJvbSAnLi4vVlJNSW1wb3J0ZXInO1xuaW1wb3J0IHsgVlJNRGVidWdPcHRpb25zIH0gZnJvbSAnLi9WUk1EZWJ1Z09wdGlvbnMnO1xuaW1wb3J0IHsgVlJNSW1wb3J0ZXJEZWJ1ZyB9IGZyb20gJy4vVlJNSW1wb3J0ZXJEZWJ1Zyc7XG5cbi8qKlxuICogW1tWUk1dXSBidXQgaXQgaGFzIHNvbWUgdXNlZnVsIGdpem1vcy5cbiAqL1xuZXhwb3J0IGNsYXNzIFZSTURlYnVnIGV4dGVuZHMgVlJNIHtcbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBWUk1EZWJ1ZyBmcm9tIGEgcGFyc2VkIHJlc3VsdCBvZiBHTFRGIHRha2VuIGZyb20gR0xURkxvYWRlci5cbiAgICpcbiAgICogU2VlIFtbVlJNLmZyb21dXSBmb3IgYSBkZXRhaWxlZCBleGFtcGxlLlxuICAgKlxuICAgKiBAcGFyYW0gZ2x0ZiBBIHBhcnNlZCBHTFRGIG9iamVjdCB0YWtlbiBmcm9tIEdMVEZMb2FkZXJcbiAgICogQHBhcmFtIG9wdGlvbnMgT3B0aW9ucyB0aGF0IHdpbGwgYmUgdXNlZCBpbiBpbXBvcnRlclxuICAgKiBAcGFyYW0gZGVidWdPcHRpb24gT3B0aW9ucyBmb3IgVlJNRGVidWcgZmVhdHVyZXNcbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgYXN5bmMgZnJvbShcbiAgICBnbHRmOiBUSFJFRS5HTFRGLFxuICAgIG9wdGlvbnM6IFZSTUltcG9ydGVyT3B0aW9ucyA9IHt9LFxuICAgIGRlYnVnT3B0aW9uOiBWUk1EZWJ1Z09wdGlvbnMgPSB7fSxcbiAgKTogUHJvbWlzZTxWUk0+IHtcbiAgICBjb25zdCBpbXBvcnRlciA9IG5ldyBWUk1JbXBvcnRlckRlYnVnKG9wdGlvbnMpO1xuICAgIHJldHVybiBhd2FpdCBpbXBvcnRlci5pbXBvcnQoZ2x0ZiwgZGVidWdPcHRpb24pO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBWUk1EZWJ1ZyBpbnN0YW5jZS5cbiAgICpcbiAgICogQHBhcmFtIHBhcmFtcyBbW1ZSTVBhcmFtZXRlcnNdXSB0aGF0IHJlcHJlc2VudHMgY29tcG9uZW50cyBvZiB0aGUgVlJNXG4gICAqIEBwYXJhbSBkZWJ1Z09wdGlvbiBPcHRpb25zIGZvciBWUk1EZWJ1ZyBmZWF0dXJlc1xuICAgKi9cbiAgY29uc3RydWN0b3IocGFyYW1zOiBWUk1QYXJhbWV0ZXJzLCBkZWJ1Z09wdGlvbjogVlJNRGVidWdPcHRpb25zID0ge30pIHtcbiAgICBzdXBlcihwYXJhbXMpO1xuXG4gICAgLy8gR2l6bW/jgpLlsZXplotcbiAgICBpZiAoIWRlYnVnT3B0aW9uLmRpc2FibGVCb3hIZWxwZXIpIHtcbiAgICAgIHRoaXMuc2NlbmUuYWRkKG5ldyBUSFJFRS5Cb3hIZWxwZXIodGhpcy5zY2VuZSkpO1xuICAgIH1cblxuICAgIGlmICghZGVidWdPcHRpb24uZGlzYWJsZVNrZWxldG9uSGVscGVyKSB7XG4gICAgICB0aGlzLnNjZW5lLmFkZChuZXcgVEhSRUUuU2tlbGV0b25IZWxwZXIodGhpcy5zY2VuZSkpO1xuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyB1cGRhdGUoZGVsdGE6IG51bWJlcik6IHZvaWQge1xuICAgIHN1cGVyLnVwZGF0ZShkZWx0YSk7XG4gIH1cbn1cbiIsImltcG9ydCAqIGFzIFRIUkVFIGZyb20gJ3RocmVlJztcbmltcG9ydCB7IHJlZHVjZUJvbmVzIH0gZnJvbSAnLi4vcmVkdWNlQm9uZXMnO1xuaW1wb3J0IHsgVlJNSW1wb3J0ZXIsIFZSTUltcG9ydGVyT3B0aW9ucyB9IGZyb20gJy4uL1ZSTUltcG9ydGVyJztcbmltcG9ydCB7IFZSTURlYnVnIH0gZnJvbSAnLi9WUk1EZWJ1Zyc7XG5pbXBvcnQgeyBWUk1EZWJ1Z09wdGlvbnMgfSBmcm9tICcuL1ZSTURlYnVnT3B0aW9ucyc7XG5pbXBvcnQgeyBWUk1Mb29rQXRIZWFkRGVidWcgfSBmcm9tICcuL1ZSTUxvb2tBdEhlYWREZWJ1Zyc7XG5pbXBvcnQgeyBWUk1Mb29rQXRJbXBvcnRlckRlYnVnIH0gZnJvbSAnLi9WUk1Mb29rQXRJbXBvcnRlckRlYnVnJztcbmltcG9ydCB7IFZSTVNwcmluZ0JvbmVJbXBvcnRlckRlYnVnIH0gZnJvbSAnLi9WUk1TcHJpbmdCb25lSW1wb3J0ZXJEZWJ1Zyc7XG5cbi8qKlxuICogQW4gaW1wb3J0ZXIgdGhhdCBpbXBvcnRzIGEgW1tWUk1EZWJ1Z11dIGZyb20gYSBWUk0gZXh0ZW5zaW9uIG9mIGEgR0xURi5cbiAqL1xuZXhwb3J0IGNsYXNzIFZSTUltcG9ydGVyRGVidWcgZXh0ZW5kcyBWUk1JbXBvcnRlciB7XG4gIHB1YmxpYyBjb25zdHJ1Y3RvcihvcHRpb25zOiBWUk1JbXBvcnRlck9wdGlvbnMgPSB7fSkge1xuICAgIG9wdGlvbnMubG9va0F0SW1wb3J0ZXIgPSBvcHRpb25zLmxvb2tBdEltcG9ydGVyIHx8IG5ldyBWUk1Mb29rQXRJbXBvcnRlckRlYnVnKCk7XG4gICAgb3B0aW9ucy5zcHJpbmdCb25lSW1wb3J0ZXIgPSBvcHRpb25zLnNwcmluZ0JvbmVJbXBvcnRlciB8fCBuZXcgVlJNU3ByaW5nQm9uZUltcG9ydGVyRGVidWcoKTtcbiAgICBzdXBlcihvcHRpb25zKTtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBpbXBvcnQoZ2x0ZjogVEhSRUUuR0xURiwgZGVidWdPcHRpb25zOiBWUk1EZWJ1Z09wdGlvbnMgPSB7fSk6IFByb21pc2U8VlJNRGVidWc+IHtcbiAgICBpZiAoZ2x0Zi5wYXJzZXIuanNvbi5leHRlbnNpb25zID09PSB1bmRlZmluZWQgfHwgZ2x0Zi5wYXJzZXIuanNvbi5leHRlbnNpb25zLlZSTSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBmaW5kIFZSTSBleHRlbnNpb24gb24gdGhlIEdMVEYnKTtcbiAgICB9XG4gICAgY29uc3QgdnJtRXh0ID0gZ2x0Zi5wYXJzZXIuanNvbi5leHRlbnNpb25zLlZSTTtcblxuICAgIGNvbnN0IHNjZW5lID0gZ2x0Zi5zY2VuZTtcblxuICAgIHNjZW5lLnVwZGF0ZU1hdHJpeFdvcmxkKGZhbHNlKTtcblxuICAgIC8vIFNraW5uZWQgb2JqZWN0IHNob3VsZCBub3QgYmUgZnJ1c3R1bUN1bGxlZFxuICAgIC8vIFNpbmNlIHByZS1za2lubmVkIHBvc2l0aW9uIG1pZ2h0IGJlIG91dHNpZGUgb2Ygdmlld1xuICAgIHNjZW5lLnRyYXZlcnNlKChvYmplY3QzZCkgPT4ge1xuICAgICAgaWYgKChvYmplY3QzZCBhcyBhbnkpLmlzTWVzaCkge1xuICAgICAgICBvYmplY3QzZC5mcnVzdHVtQ3VsbGVkID0gZmFsc2U7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICByZWR1Y2VCb25lcyhzY2VuZSk7XG5cbiAgICBjb25zdCBtYXRlcmlhbHMgPSAoYXdhaXQgdGhpcy5fbWF0ZXJpYWxJbXBvcnRlci5jb252ZXJ0R0xURk1hdGVyaWFscyhnbHRmKSkgfHwgdW5kZWZpbmVkO1xuXG4gICAgY29uc3QgaHVtYW5vaWQgPSAoYXdhaXQgdGhpcy5faHVtYW5vaWRJbXBvcnRlci5pbXBvcnQoZ2x0ZikpIHx8IHVuZGVmaW5lZDtcblxuICAgIGNvbnN0IGZpcnN0UGVyc29uID0gaHVtYW5vaWQgPyAoYXdhaXQgdGhpcy5fZmlyc3RQZXJzb25JbXBvcnRlci5pbXBvcnQoZ2x0ZiwgaHVtYW5vaWQpKSB8fCB1bmRlZmluZWQgOiB1bmRlZmluZWQ7XG5cbiAgICBjb25zdCBibGVuZFNoYXBlUHJveHkgPSAoYXdhaXQgdGhpcy5fYmxlbmRTaGFwZUltcG9ydGVyLmltcG9ydChnbHRmKSkgfHwgdW5kZWZpbmVkO1xuXG4gICAgY29uc3QgbG9va0F0ID1cbiAgICAgIGZpcnN0UGVyc29uICYmIGJsZW5kU2hhcGVQcm94eSAmJiBodW1hbm9pZFxuICAgICAgICA/IChhd2FpdCB0aGlzLl9sb29rQXRJbXBvcnRlci5pbXBvcnQoZ2x0ZiwgZmlyc3RQZXJzb24sIGJsZW5kU2hhcGVQcm94eSwgaHVtYW5vaWQpKSB8fCB1bmRlZmluZWRcbiAgICAgICAgOiB1bmRlZmluZWQ7XG4gICAgaWYgKChsb29rQXQgYXMgYW55KS5zZXR1cEhlbHBlcikge1xuICAgICAgKGxvb2tBdCBhcyBWUk1Mb29rQXRIZWFkRGVidWcpLnNldHVwSGVscGVyKHNjZW5lLCBkZWJ1Z09wdGlvbnMpO1xuICAgIH1cblxuICAgIGNvbnN0IHNwcmluZ0JvbmVNYW5hZ2VyID0gKGF3YWl0IHRoaXMuX3NwcmluZ0JvbmVJbXBvcnRlci5pbXBvcnQoZ2x0ZikpIHx8IHVuZGVmaW5lZDtcblxuICAgIHJldHVybiBuZXcgVlJNRGVidWcoXG4gICAgICB7XG4gICAgICAgIHNjZW5lOiBnbHRmLnNjZW5lLFxuICAgICAgICBtZXRhOiB2cm1FeHQubWV0YSxcbiAgICAgICAgbWF0ZXJpYWxzLFxuICAgICAgICBodW1hbm9pZCxcbiAgICAgICAgZmlyc3RQZXJzb24sXG4gICAgICAgIGJsZW5kU2hhcGVQcm94eSxcbiAgICAgICAgbG9va0F0LFxuICAgICAgICBzcHJpbmdCb25lTWFuYWdlcixcbiAgICAgIH0sXG4gICAgICBkZWJ1Z09wdGlvbnMsXG4gICAgKTtcbiAgfVxufVxuIiwiaW1wb3J0ICogYXMgVEhSRUUgZnJvbSAndGhyZWUnO1xuaW1wb3J0IHsgVlJNTG9va0F0SGVhZCB9IGZyb20gJy4uL2xvb2thdC9WUk1Mb29rQXRIZWFkJztcbmltcG9ydCB7IFZSTURlYnVnT3B0aW9ucyB9IGZyb20gJy4vVlJNRGVidWdPcHRpb25zJztcblxuY29uc3QgX3YzID0gbmV3IFRIUkVFLlZlY3RvcjMoKTtcblxuZXhwb3J0IGNsYXNzIFZSTUxvb2tBdEhlYWREZWJ1ZyBleHRlbmRzIFZSTUxvb2tBdEhlYWQge1xuICBwcml2YXRlIF9mYWNlRGlyZWN0aW9uSGVscGVyPzogVEhSRUUuQXJyb3dIZWxwZXI7XG5cbiAgcHVibGljIHNldHVwSGVscGVyKHNjZW5lOiBUSFJFRS5TY2VuZSwgZGVidWdPcHRpb246IFZSTURlYnVnT3B0aW9ucyk6IHZvaWQge1xuICAgIGlmICghZGVidWdPcHRpb24uZGlzYWJsZUZhY2VEaXJlY3Rpb25IZWxwZXIpIHtcbiAgICAgIHRoaXMuX2ZhY2VEaXJlY3Rpb25IZWxwZXIgPSBuZXcgVEhSRUUuQXJyb3dIZWxwZXIoXG4gICAgICAgIG5ldyBUSFJFRS5WZWN0b3IzKDAsIDAsIC0xKSxcbiAgICAgICAgbmV3IFRIUkVFLlZlY3RvcjMoMCwgMCwgMCksXG4gICAgICAgIDAuNSxcbiAgICAgICAgMHhmZjAwZmYsXG4gICAgICApO1xuICAgICAgc2NlbmUuYWRkKHRoaXMuX2ZhY2VEaXJlY3Rpb25IZWxwZXIpO1xuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyB1cGRhdGUoZGVsdGE6IG51bWJlcik6IHZvaWQge1xuICAgIHN1cGVyLnVwZGF0ZShkZWx0YSk7XG5cbiAgICBpZiAodGhpcy5fZmFjZURpcmVjdGlvbkhlbHBlcikge1xuICAgICAgdGhpcy5maXJzdFBlcnNvbi5nZXRGaXJzdFBlcnNvbldvcmxkUG9zaXRpb24odGhpcy5fZmFjZURpcmVjdGlvbkhlbHBlci5wb3NpdGlvbik7XG4gICAgICB0aGlzLl9mYWNlRGlyZWN0aW9uSGVscGVyLnNldERpcmVjdGlvbih0aGlzLmdldExvb2tBdFdvcmxkRGlyZWN0aW9uKF92MykpO1xuICAgIH1cbiAgfVxufVxuIiwiaW1wb3J0ICogYXMgVEhSRUUgZnJvbSAndGhyZWUnO1xuaW1wb3J0IHsgVlJNQmxlbmRTaGFwZVByb3h5IH0gZnJvbSAnLi4vYmxlbmRzaGFwZSc7XG5pbXBvcnQgeyBWUk1GaXJzdFBlcnNvbiB9IGZyb20gJy4uL2ZpcnN0cGVyc29uJztcbmltcG9ydCB7IFZSTUh1bWFub2lkIH0gZnJvbSAnLi4vaHVtYW5vaWQnO1xuaW1wb3J0IHsgVlJNTG9va0F0SGVhZCB9IGZyb20gJy4uL2xvb2thdC9WUk1Mb29rQXRIZWFkJztcbmltcG9ydCB7IFZSTUxvb2tBdEltcG9ydGVyIH0gZnJvbSAnLi4vbG9va2F0L1ZSTUxvb2tBdEltcG9ydGVyJztcbmltcG9ydCB7IFZSTVNjaGVtYSB9IGZyb20gJy4uL3R5cGVzJztcbmltcG9ydCB7IFZSTUxvb2tBdEhlYWREZWJ1ZyB9IGZyb20gJy4vVlJNTG9va0F0SGVhZERlYnVnJztcblxuZXhwb3J0IGNsYXNzIFZSTUxvb2tBdEltcG9ydGVyRGVidWcgZXh0ZW5kcyBWUk1Mb29rQXRJbXBvcnRlciB7XG4gIHB1YmxpYyBpbXBvcnQoXG4gICAgZ2x0ZjogVEhSRUUuR0xURixcbiAgICBmaXJzdFBlcnNvbjogVlJNRmlyc3RQZXJzb24sXG4gICAgYmxlbmRTaGFwZVByb3h5OiBWUk1CbGVuZFNoYXBlUHJveHksXG4gICAgaHVtYW5vaWQ6IFZSTUh1bWFub2lkLFxuICApOiBWUk1Mb29rQXRIZWFkIHwgbnVsbCB7XG4gICAgY29uc3QgdnJtRXh0OiBWUk1TY2hlbWEuVlJNIHwgdW5kZWZpbmVkID0gZ2x0Zi5wYXJzZXIuanNvbi5leHRlbnNpb25zICYmIGdsdGYucGFyc2VyLmpzb24uZXh0ZW5zaW9ucy5WUk07XG4gICAgaWYgKCF2cm1FeHQpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IHNjaGVtYUZpcnN0UGVyc29uOiBWUk1TY2hlbWEuRmlyc3RQZXJzb24gfCB1bmRlZmluZWQgPSB2cm1FeHQuZmlyc3RQZXJzb247XG4gICAgaWYgKCFzY2hlbWFGaXJzdFBlcnNvbikge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3QgYXBwbHllciA9IHRoaXMuX2ltcG9ydEFwcGx5ZXIoc2NoZW1hRmlyc3RQZXJzb24sIGJsZW5kU2hhcGVQcm94eSwgaHVtYW5vaWQpO1xuICAgIHJldHVybiBuZXcgVlJNTG9va0F0SGVhZERlYnVnKGZpcnN0UGVyc29uLCBhcHBseWVyIHx8IHVuZGVmaW5lZCk7XG4gIH1cbn1cbiIsImltcG9ydCAqIGFzIFRIUkVFIGZyb20gJ3RocmVlJztcbmltcG9ydCB7IEdJWk1PX1JFTkRFUl9PUkRFUiwgVlJNU3ByaW5nQm9uZSB9IGZyb20gJy4uL3NwcmluZ2JvbmUnO1xuXG5jb25zdCBfdjNBID0gbmV3IFRIUkVFLlZlY3RvcjMoKTtcblxuZXhwb3J0IGNsYXNzIFZSTVNwcmluZ0JvbmVEZWJ1ZyBleHRlbmRzIFZSTVNwcmluZ0JvbmUge1xuICBwcml2YXRlIF9naXptbz86IFRIUkVFLkFycm93SGVscGVyO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIGJvbmU6IFRIUkVFLk9iamVjdDNELFxuICAgIHJhZGl1czogbnVtYmVyLFxuICAgIHN0aWZmaW5lc3M6IG51bWJlcixcbiAgICBncmF2aXR5RGlyOiBUSFJFRS5WZWN0b3IzLFxuICAgIGdyYXZpdHlQb3dlcjogbnVtYmVyLFxuICAgIGRyYWdGb3JjZTogbnVtYmVyLFxuICAgIGNvbGxpZGVyczogVEhSRUUuTWVzaFtdID0gW10sXG4gICkge1xuICAgIHN1cGVyKGJvbmUsIHJhZGl1cywgc3RpZmZpbmVzcywgZ3Jhdml0eURpciwgZ3Jhdml0eVBvd2VyLCBkcmFnRm9yY2UsIGNvbGxpZGVycyk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIHNwcmluZyBib25lIGdpem1vLCBhcyBgVEhSRUUuQXJyb3dIZWxwZXJgLlxuICAgKiBVc2VmdWwgaW4gZGVidWdnaW5nIHNwcmluZyBib25lcy5cbiAgICovXG4gIHB1YmxpYyBnZXRHaXptbygpOiBUSFJFRS5BcnJvd0hlbHBlciB7XG4gICAgLy8gcmV0dXJuIGlmIGdpem1vIGlzIGFscmVhZHkgZXhpc3RlZFxuICAgIGlmICh0aGlzLl9naXptbykge1xuICAgICAgcmV0dXJuIHRoaXMuX2dpem1vO1xuICAgIH1cblxuICAgIGNvbnN0IG5leHRUYWlsUmVsYXRpdmUgPSBfdjNBLmNvcHkodGhpcy5fbmV4dFRhaWwpLnN1Yih0aGlzLl93b3JsZFBvc2l0aW9uKTtcbiAgICBjb25zdCBuZXh0VGFpbFJlbGF0aXZlTGVuZ3RoID0gbmV4dFRhaWxSZWxhdGl2ZS5sZW5ndGgoKTtcblxuICAgIHRoaXMuX2dpem1vID0gbmV3IFRIUkVFLkFycm93SGVscGVyKFxuICAgICAgbmV4dFRhaWxSZWxhdGl2ZS5ub3JtYWxpemUoKSxcbiAgICAgIHRoaXMuX3dvcmxkUG9zaXRpb24sXG4gICAgICBuZXh0VGFpbFJlbGF0aXZlTGVuZ3RoLFxuICAgICAgMHhmZmZmMDAsXG4gICAgICB0aGlzLnJhZGl1cyxcbiAgICAgIHRoaXMucmFkaXVzLFxuICAgICk7XG5cbiAgICAvLyBpdCBzaG91bGQgYmUgYWx3YXlzIHZpc2libGVcbiAgICB0aGlzLl9naXptby5saW5lLnJlbmRlck9yZGVyID0gR0laTU9fUkVOREVSX09SREVSO1xuICAgIHRoaXMuX2dpem1vLmNvbmUucmVuZGVyT3JkZXIgPSBHSVpNT19SRU5ERVJfT1JERVI7XG4gICAgKHRoaXMuX2dpem1vLmxpbmUubWF0ZXJpYWwgYXMgVEhSRUUuTWF0ZXJpYWwpLmRlcHRoVGVzdCA9IGZhbHNlO1xuICAgICh0aGlzLl9naXptby5saW5lLm1hdGVyaWFsIGFzIFRIUkVFLk1hdGVyaWFsKS50cmFuc3BhcmVudCA9IHRydWU7XG4gICAgKHRoaXMuX2dpem1vLmNvbmUubWF0ZXJpYWwgYXMgVEhSRUUuTWF0ZXJpYWwpLmRlcHRoVGVzdCA9IGZhbHNlO1xuICAgICh0aGlzLl9naXptby5jb25lLm1hdGVyaWFsIGFzIFRIUkVFLk1hdGVyaWFsKS50cmFuc3BhcmVudCA9IHRydWU7XG5cbiAgICByZXR1cm4gdGhpcy5fZ2l6bW87XG4gIH1cblxuICBwdWJsaWMgdXBkYXRlKGRlbHRhOiBudW1iZXIpOiB2b2lkIHtcbiAgICBzdXBlci51cGRhdGUoZGVsdGEpO1xuICAgIC8vIGxhc3RseSB3ZSdyZSBnb25uYSB1cGRhdGUgZ2l6bW9cbiAgICB0aGlzLl91cGRhdGVHaXptbygpO1xuICB9XG5cbiAgcHJpdmF0ZSBfdXBkYXRlR2l6bW8oKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLl9naXptbykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IG5leHRUYWlsUmVsYXRpdmUgPSBfdjNBLmNvcHkodGhpcy5fY3VycmVudFRhaWwpLnN1Yih0aGlzLl93b3JsZFBvc2l0aW9uKTtcbiAgICBjb25zdCBuZXh0VGFpbFJlbGF0aXZlTGVuZ3RoID0gbmV4dFRhaWxSZWxhdGl2ZS5sZW5ndGgoKTtcblxuICAgIHRoaXMuX2dpem1vLnNldERpcmVjdGlvbihuZXh0VGFpbFJlbGF0aXZlLm5vcm1hbGl6ZSgpKTtcbiAgICB0aGlzLl9naXptby5zZXRMZW5ndGgobmV4dFRhaWxSZWxhdGl2ZUxlbmd0aCwgdGhpcy5yYWRpdXMsIHRoaXMucmFkaXVzKTtcbiAgICB0aGlzLl9naXptby5wb3NpdGlvbi5jb3B5KHRoaXMuX3dvcmxkUG9zaXRpb24pO1xuICB9XG59XG4iLCJpbXBvcnQgKiBhcyBUSFJFRSBmcm9tICd0aHJlZSc7XG5pbXBvcnQgeyBWUk1TcHJpbmdCb25lIH0gZnJvbSAnLi4vc3ByaW5nYm9uZS9WUk1TcHJpbmdCb25lJztcbmltcG9ydCB7IFZSTVNwcmluZ0JvbmVJbXBvcnRlciB9IGZyb20gJy4uL3NwcmluZ2JvbmUvVlJNU3ByaW5nQm9uZUltcG9ydGVyJztcbmltcG9ydCB7IFZSTVNwcmluZ0JvbmVEZWJ1ZyB9IGZyb20gJy4vVlJNU3ByaW5nQm9uZURlYnVnJztcblxuZXhwb3J0IGNsYXNzIFZSTVNwcmluZ0JvbmVJbXBvcnRlckRlYnVnIGV4dGVuZHMgVlJNU3ByaW5nQm9uZUltcG9ydGVyIHtcbiAgcHJvdGVjdGVkIGdldCBfaXNDb2xpZGVyTWVzaFZpc2libGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICBwcm90ZWN0ZWQgX2NyZWF0ZVNwcmluZ0JvbmUoXG4gICAgZ2x0ZjogVEhSRUUuR0xURixcbiAgICBib25lOiBUSFJFRS5PYmplY3QzRCxcbiAgICBoaXRSYWRpdXM6IG51bWJlcixcbiAgICBzdGlmZmluZXNzOiBudW1iZXIsXG4gICAgZ3Jhdml0eURpcjogVEhSRUUuVmVjdG9yMyxcbiAgICBncmF2aXR5UG93ZXI6IG51bWJlcixcbiAgICBkcmFnRm9yY2U6IG51bWJlcixcbiAgICBjb2xsaWRlcnM6IFRIUkVFLk1lc2hbXSA9IFtdLFxuICApOiBWUk1TcHJpbmdCb25lIHtcbiAgICBjb25zdCBzcHJpbmdCb25lID0gbmV3IFZSTVNwcmluZ0JvbmVEZWJ1ZyhcbiAgICAgIGJvbmUsXG4gICAgICBoaXRSYWRpdXMsXG4gICAgICBzdGlmZmluZXNzLFxuICAgICAgZ3Jhdml0eURpcixcbiAgICAgIGdyYXZpdHlQb3dlcixcbiAgICAgIGRyYWdGb3JjZSxcbiAgICAgIGNvbGxpZGVycyxcbiAgICApO1xuICAgIGdsdGYuc2NlbmUuYWRkKHNwcmluZ0JvbmUuZ2V0R2l6bW8oKSk7XG4gICAgcmV0dXJuIHNwcmluZ0JvbmU7XG4gIH1cbn1cbiIsImV4cG9ydCAqIGZyb20gJy4vVlJNRGVidWdPcHRpb25zJztcbmV4cG9ydCAqIGZyb20gJy4vVlJNRGVidWcnO1xuZXhwb3J0ICogZnJvbSAnLi9WUk1TcHJpbmdCb25lRGVidWcnO1xuZXhwb3J0ICogZnJvbSAnLi9WUk1TcHJpbmdCb25lSW1wb3J0ZXJEZWJ1Zyc7XG4iLCJpbXBvcnQgKiBhcyBUSFJFRSBmcm9tICd0aHJlZSc7XG5pbXBvcnQgeyBHTFRGTWVzaCwgR0xURk5vZGUgfSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgeyBnZXRXb3JsZFF1YXRlcm5pb25MaXRlIH0gZnJvbSAnLi4vdXRpbHMvbWF0aCc7XG5cbmNvbnN0IFZFQ1RPUjNfRlJPTlQgPSBPYmplY3QuZnJlZXplKG5ldyBUSFJFRS5WZWN0b3IzKDAuMCwgMC4wLCAtMS4wKSk7XG5cbmNvbnN0IF9xdWF0ID0gbmV3IFRIUkVFLlF1YXRlcm5pb24oKTtcblxuZW51bSBGaXJzdFBlcnNvbkZsYWcge1xuICBBdXRvLFxuICBCb3RoLFxuICBUaGlyZFBlcnNvbk9ubHksXG4gIEZpcnN0UGVyc29uT25seSxcbn1cblxuLyoqXG4gKiBUaGlzIGNsYXNzIHJlcHJlc2VudHMgYSBzaW5nbGUgW2BtZXNoQW5ub3RhdGlvbmBdKGh0dHBzOi8vZ2l0aHViLmNvbS92cm0tYy9VbmlWUk0vYmxvYi9tYXN0ZXIvc3BlY2lmaWNhdGlvbi8wLjAvc2NoZW1hL3ZybS5maXJzdHBlcnNvbi5tZXNoYW5ub3RhdGlvbi5zY2hlbWEuanNvbikgZW50cnkuXG4gKiBFYWNoIG1lc2ggd2lsbCBiZSBhc3NpZ25lZCB0byBzcGVjaWZpZWQgbGF5ZXIgd2hlbiB5b3UgY2FsbCBbW1ZSTUZpcnN0UGVyc29uLnNldHVwXV0uXG4gKi9cbmV4cG9ydCBjbGFzcyBWUk1SZW5kZXJlckZpcnN0UGVyc29uRmxhZ3Mge1xuICBwcml2YXRlIHN0YXRpYyBfcGFyc2VGaXJzdFBlcnNvbkZsYWcoZmlyc3RQZXJzb25GbGFnOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBGaXJzdFBlcnNvbkZsYWcge1xuICAgIHN3aXRjaCAoZmlyc3RQZXJzb25GbGFnKSB7XG4gICAgICBjYXNlICdCb3RoJzpcbiAgICAgICAgcmV0dXJuIEZpcnN0UGVyc29uRmxhZy5Cb3RoO1xuICAgICAgY2FzZSAnVGhpcmRQZXJzb25Pbmx5JzpcbiAgICAgICAgcmV0dXJuIEZpcnN0UGVyc29uRmxhZy5UaGlyZFBlcnNvbk9ubHk7XG4gICAgICBjYXNlICdGaXJzdFBlcnNvbk9ubHknOlxuICAgICAgICByZXR1cm4gRmlyc3RQZXJzb25GbGFnLkZpcnN0UGVyc29uT25seTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBGaXJzdFBlcnNvbkZsYWcuQXV0bztcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQSBbW0ZpcnN0UGVyc29uRmxhZ11dIG9mIHRoZSBhbm5vdGF0aW9uIGVudHJ5LlxuICAgKi9cbiAgcHVibGljIGZpcnN0UGVyc29uRmxhZzogRmlyc3RQZXJzb25GbGFnO1xuXG4gIC8qKlxuICAgKiBBIG1lc2ggb2YgdGhlIGFubm90YXRpb24gZW50cnkuXG4gICAqL1xuICBwdWJsaWMgbWVzaDogR0xURk1lc2g7XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBtZXNoIGFubm90YXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSBmaXJzdFBlcnNvbkZsYWcgQSBbW0ZpcnN0UGVyc29uRmxhZ11dIG9mIHRoZSBhbm5vdGF0aW9uIGVudHJ5XG4gICAqIEBwYXJhbSBub2RlIEEgbm9kZSBvZiB0aGUgYW5ub3RhdGlvbiBlbnRyeS5cbiAgICovXG4gIGNvbnN0cnVjdG9yKGZpcnN0UGVyc29uRmxhZzogc3RyaW5nIHwgdW5kZWZpbmVkLCBtZXNoOiBHTFRGTWVzaCkge1xuICAgIHRoaXMuZmlyc3RQZXJzb25GbGFnID0gVlJNUmVuZGVyZXJGaXJzdFBlcnNvbkZsYWdzLl9wYXJzZUZpcnN0UGVyc29uRmxhZyhmaXJzdFBlcnNvbkZsYWcpO1xuICAgIHRoaXMubWVzaCA9IG1lc2g7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFZSTUZpcnN0UGVyc29uIHtcbiAgLyoqXG4gICAqIEEgZGVmYXVsdCBjYW1lcmEgbGF5ZXIgZm9yIGBGaXJzdFBlcnNvbk9ubHlgIGxheWVyLlxuICAgKlxuICAgKiBAc2VlIFtbZ2V0Rmlyc3RQZXJzb25Pbmx5TGF5ZXJdXVxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgX0RFRkFVTFRfRklSU1RQRVJTT05fT05MWV9MQVlFUiA9IDk7XG5cbiAgLyoqXG4gICAqIEEgZGVmYXVsdCBjYW1lcmEgbGF5ZXIgZm9yIGBUaGlyZFBlcnNvbk9ubHlgIGxheWVyLlxuICAgKlxuICAgKiBAc2VlIFtbZ2V0VGhpcmRQZXJzb25Pbmx5TGF5ZXJdXVxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgX0RFRkFVTFRfVEhJUkRQRVJTT05fT05MWV9MQVlFUiA9IDEwO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgX2ZpcnN0UGVyc29uQm9uZTogR0xURk5vZGU7XG4gIHByaXZhdGUgcmVhZG9ubHkgX21lc2hBbm5vdGF0aW9uczogVlJNUmVuZGVyZXJGaXJzdFBlcnNvbkZsYWdzW10gPSBbXTtcbiAgcHJpdmF0ZSByZWFkb25seSBfZmlyc3RQZXJzb25Cb25lT2Zmc2V0OiBUSFJFRS5WZWN0b3IzO1xuXG4gIHByaXZhdGUgX2ZpcnN0UGVyc29uT25seUxheWVyID0gVlJNRmlyc3RQZXJzb24uX0RFRkFVTFRfRklSU1RQRVJTT05fT05MWV9MQVlFUjtcbiAgcHJpdmF0ZSBfdGhpcmRQZXJzb25Pbmx5TGF5ZXIgPSBWUk1GaXJzdFBlcnNvbi5fREVGQVVMVF9USElSRFBFUlNPTl9PTkxZX0xBWUVSO1xuXG4gIHByaXZhdGUgX2luaXRpYWxpemVkID0gZmFsc2U7XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBWUk1GaXJzdFBlcnNvbiBvYmplY3QuXG4gICAqXG4gICAqIEBwYXJhbSBmaXJzdFBlcnNvbkJvbmUgQSBmaXJzdCBwZXJzb24gYm9uZVxuICAgKiBAcGFyYW0gZmlyc3RQZXJzb25Cb25lT2Zmc2V0IEFuIG9mZnNldCBmcm9tIHRoZSBzcGVjaWZpZWQgZmlyc3QgcGVyc29uIGJvbmVcbiAgICogQHBhcmFtIG1lc2hBbm5vdGF0aW9ucyBBIHJlbmRlcmVyIHNldHRpbmdzLiBTZWUgdGhlIGRlc2NyaXB0aW9uIG9mIFtbUmVuZGVyZXJGaXJzdFBlcnNvbkZsYWdzXV0gZm9yIG1vcmUgaW5mb1xuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgZmlyc3RQZXJzb25Cb25lOiBHTFRGTm9kZSxcbiAgICBmaXJzdFBlcnNvbkJvbmVPZmZzZXQ6IFRIUkVFLlZlY3RvcjMsXG4gICAgbWVzaEFubm90YXRpb25zOiBWUk1SZW5kZXJlckZpcnN0UGVyc29uRmxhZ3NbXSxcbiAgKSB7XG4gICAgdGhpcy5fZmlyc3RQZXJzb25Cb25lID0gZmlyc3RQZXJzb25Cb25lO1xuICAgIHRoaXMuX2ZpcnN0UGVyc29uQm9uZU9mZnNldCA9IGZpcnN0UGVyc29uQm9uZU9mZnNldDtcbiAgICB0aGlzLl9tZXNoQW5ub3RhdGlvbnMgPSBtZXNoQW5ub3RhdGlvbnM7XG4gIH1cblxuICBwdWJsaWMgZ2V0IGZpcnN0UGVyc29uQm9uZSgpOiBHTFRGTm9kZSB7XG4gICAgcmV0dXJuIHRoaXMuX2ZpcnN0UGVyc29uQm9uZTtcbiAgfVxuXG4gIHB1YmxpYyBnZXQgbWVzaEFubm90YXRpb25zKCk6IFZSTVJlbmRlcmVyRmlyc3RQZXJzb25GbGFnc1tdIHtcbiAgICByZXR1cm4gdGhpcy5fbWVzaEFubm90YXRpb25zO1xuICB9XG5cbiAgcHVibGljIGdldEZpcnN0UGVyc29uV29ybGREaXJlY3Rpb24odGFyZ2V0OiBUSFJFRS5WZWN0b3IzKTogVEhSRUUuVmVjdG9yMyB7XG4gICAgcmV0dXJuIHRhcmdldC5jb3B5KFZFQ1RPUjNfRlJPTlQpLmFwcGx5UXVhdGVybmlvbihnZXRXb3JsZFF1YXRlcm5pb25MaXRlKHRoaXMuX2ZpcnN0UGVyc29uQm9uZSwgX3F1YXQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBIGNhbWVyYSBsYXllciByZXByZXNlbnRzIGBGaXJzdFBlcnNvbk9ubHlgIGxheWVyLlxuICAgKiBOb3RlIHRoYXQgKip5b3UgbXVzdCBjYWxsIFtbc2V0dXBdXSBmaXJzdCBiZWZvcmUgeW91IHVzZSB0aGUgbGF5ZXIgZmVhdHVyZSoqIG9yIGl0IGRvZXMgbm90IHdvcmsgcHJvcGVybHkuXG4gICAqXG4gICAqIFRoZSB2YWx1ZSBpcyBbW0RFRkFVTFRfRklSU1RQRVJTT05fT05MWV9MQVlFUl1dIGJ5IGRlZmF1bHQgYnV0IHlvdSBjYW4gY2hhbmdlIHRoZSBsYXllciBieSBzcGVjaWZ5aW5nIHZpYSBbW3NldHVwXV0gaWYgeW91IHByZWZlci5cbiAgICpcbiAgICogQHNlZSBodHRwczovL3ZybS5kZXYvZW4vdW5pdnJtL2FwaS91bml2cm1fdXNlX2ZpcnN0cGVyc29uL1xuICAgKiBAc2VlIGh0dHBzOi8vdGhyZWVqcy5vcmcvZG9jcy8jYXBpL2VuL2NvcmUvTGF5ZXJzXG4gICAqL1xuICBwdWJsaWMgZ2V0IGZpcnN0UGVyc29uT25seUxheWVyKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2ZpcnN0UGVyc29uT25seUxheWVyO1xuICB9XG5cbiAgLyoqXG4gICAqIEEgY2FtZXJhIGxheWVyIHJlcHJlc2VudHMgYFRoaXJkUGVyc29uT25seWAgbGF5ZXIuXG4gICAqIE5vdGUgdGhhdCAqKnlvdSBtdXN0IGNhbGwgW1tzZXR1cF1dIGZpcnN0IGJlZm9yZSB5b3UgdXNlIHRoZSBsYXllciBmZWF0dXJlKiogb3IgaXQgZG9lcyBub3Qgd29yayBwcm9wZXJseS5cbiAgICpcbiAgICogVGhlIHZhbHVlIGlzIFtbREVGQVVMVF9USElSRFBFUlNPTl9PTkxZX0xBWUVSXV0gYnkgZGVmYXVsdCBidXQgeW91IGNhbiBjaGFuZ2UgdGhlIGxheWVyIGJ5IHNwZWNpZnlpbmcgdmlhIFtbc2V0dXBdXSBpZiB5b3UgcHJlZmVyLlxuICAgKlxuICAgKiBAc2VlIGh0dHBzOi8vdnJtLmRldi9lbi91bml2cm0vYXBpL3VuaXZybV91c2VfZmlyc3RwZXJzb24vXG4gICAqIEBzZWUgaHR0cHM6Ly90aHJlZWpzLm9yZy9kb2NzLyNhcGkvZW4vY29yZS9MYXllcnNcbiAgICovXG4gIHB1YmxpYyBnZXQgdGhpcmRQZXJzb25Pbmx5TGF5ZXIoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5fdGhpcmRQZXJzb25Pbmx5TGF5ZXI7XG4gIH1cblxuICBwdWJsaWMgZ2V0Rmlyc3RQZXJzb25Cb25lT2Zmc2V0KHRhcmdldDogVEhSRUUuVmVjdG9yMyk6IFRIUkVFLlZlY3RvcjMge1xuICAgIHJldHVybiB0YXJnZXQuY29weSh0aGlzLl9maXJzdFBlcnNvbkJvbmVPZmZzZXQpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBjdXJyZW50IHdvcmxkIHBvc2l0aW9uIG9mIHRoZSBmaXJzdCBwZXJzb24uXG4gICAqIFRoZSBwb3NpdGlvbiB0YWtlcyBbW0ZpcnN0UGVyc29uQm9uZV1dIGFuZCBbW0ZpcnN0UGVyc29uT2Zmc2V0XV0gaW50byBhY2NvdW50LlxuICAgKlxuICAgKiBAcGFyYW0gdjMgdGFyZ2V0XG4gICAqIEByZXR1cm5zIEN1cnJlbnQgd29ybGQgcG9zaXRpb24gb2YgdGhlIGZpcnN0IHBlcnNvblxuICAgKi9cbiAgcHVibGljIGdldEZpcnN0UGVyc29uV29ybGRQb3NpdGlvbih2MzogVEhSRUUuVmVjdG9yMyk6IFRIUkVFLlZlY3RvcjMge1xuICAgIC8vIFVuaVZSTSNWUk1GaXJzdFBlcnNvbkVkaXRvclxuICAgIC8vIHZhciB3b3JsZE9mZnNldCA9IGhlYWQubG9jYWxUb1dvcmxkTWF0cml4Lk11bHRpcGx5UG9pbnQoY29tcG9uZW50LkZpcnN0UGVyc29uT2Zmc2V0KTtcbiAgICBjb25zdCBvZmZzZXQgPSB0aGlzLl9maXJzdFBlcnNvbkJvbmVPZmZzZXQ7XG4gICAgY29uc3QgdjQgPSBuZXcgVEhSRUUuVmVjdG9yNChvZmZzZXQueCwgb2Zmc2V0LnksIG9mZnNldC56LCAxLjApO1xuICAgIHY0LmFwcGx5TWF0cml4NCh0aGlzLl9maXJzdFBlcnNvbkJvbmUubWF0cml4V29ybGQpO1xuICAgIHJldHVybiB2My5zZXQodjQueCwgdjQueSwgdjQueik7XG4gIH1cblxuICAvKipcbiAgICogSW4gdGhpcyBtZXRob2QsIGl0IGFzc2lnbnMgbGF5ZXJzIGZvciBldmVyeSBtZXNoZXMgYmFzZWQgb24gbWVzaCBhbm5vdGF0aW9ucy5cbiAgICogWW91IG11c3QgY2FsbCB0aGlzIG1ldGhvZCBmaXJzdCBiZWZvcmUgeW91IHVzZSB0aGUgbGF5ZXIgZmVhdHVyZS5cbiAgICpcbiAgICogVGhpcyBpcyBhbiBlcXVpdmFsZW50IG9mIFtWUk1GaXJzdFBlcnNvbi5TZXR1cF0oaHR0cHM6Ly9naXRodWIuY29tL3ZybS1jL1VuaVZSTS9ibG9iL21hc3Rlci9Bc3NldHMvVlJNL1VuaVZSTS9TY3JpcHRzL0ZpcnN0UGVyc29uL1ZSTUZpcnN0UGVyc29uLmNzKSBvZiB0aGUgVW5pVlJNLlxuICAgKlxuICAgKiBUaGUgYGNhbWVyYUxheWVyYCBwYXJhbWV0ZXIgc3BlY2lmaWVzIHdoaWNoIGxheWVyIHdpbGwgYmUgYXNzaWduZWQgZm9yIGBGaXJzdFBlcnNvbk9ubHlgIC8gYFRoaXJkUGVyc29uT25seWAuXG4gICAqIEluIFVuaVZSTSwgd2Ugc3BlY2lmaWVkIHRob3NlIGJ5IG5hbWluZyBlYWNoIGRlc2lyZWQgbGF5ZXIgYXMgYEZJUlNUUEVSU09OX09OTFlfTEFZRVJgIC8gYFRISVJEUEVSU09OX09OTFlfTEFZRVJgXG4gICAqIGJ1dCB3ZSBhcmUgZ29pbmcgdG8gc3BlY2lmeSB0aGVzZSBsYXllcnMgYXQgaGVyZSBzaW5jZSB3ZSBhcmUgdW5hYmxlIHRvIG5hbWUgbGF5ZXJzIGluIFRocmVlLmpzLlxuICAgKlxuICAgKiBAcGFyYW0gY2FtZXJhTGF5ZXIgU3BlY2lmeSB3aGljaCBsYXllciB3aWxsIGJlIGZvciBgRmlyc3RQZXJzb25Pbmx5YCAvIGBUaGlyZFBlcnNvbk9ubHlgLlxuICAgKi9cbiAgcHVibGljIHNldHVwKHtcbiAgICBmaXJzdFBlcnNvbk9ubHlMYXllciA9IFZSTUZpcnN0UGVyc29uLl9ERUZBVUxUX0ZJUlNUUEVSU09OX09OTFlfTEFZRVIsXG4gICAgdGhpcmRQZXJzb25Pbmx5TGF5ZXIgPSBWUk1GaXJzdFBlcnNvbi5fREVGQVVMVF9USElSRFBFUlNPTl9PTkxZX0xBWUVSLFxuICB9ID0ge30pOiB2b2lkIHtcbiAgICBpZiAodGhpcy5faW5pdGlhbGl6ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5faW5pdGlhbGl6ZWQgPSB0cnVlO1xuICAgIHRoaXMuX2ZpcnN0UGVyc29uT25seUxheWVyID0gZmlyc3RQZXJzb25Pbmx5TGF5ZXI7XG4gICAgdGhpcy5fdGhpcmRQZXJzb25Pbmx5TGF5ZXIgPSB0aGlyZFBlcnNvbk9ubHlMYXllcjtcblxuICAgIHRoaXMuX21lc2hBbm5vdGF0aW9ucy5mb3JFYWNoKChpdGVtKSA9PiB7XG4gICAgICBpZiAoaXRlbS5maXJzdFBlcnNvbkZsYWcgPT09IEZpcnN0UGVyc29uRmxhZy5GaXJzdFBlcnNvbk9ubHkpIHtcbiAgICAgICAgaXRlbS5tZXNoLmxheWVycy5zZXQodGhpcy5fZmlyc3RQZXJzb25Pbmx5TGF5ZXIpO1xuICAgICAgICBpdGVtLm1lc2gudHJhdmVyc2UoKGNoaWxkKSA9PiBjaGlsZC5sYXllcnMuc2V0KHRoaXMuX2ZpcnN0UGVyc29uT25seUxheWVyKSk7XG4gICAgICB9IGVsc2UgaWYgKGl0ZW0uZmlyc3RQZXJzb25GbGFnID09PSBGaXJzdFBlcnNvbkZsYWcuVGhpcmRQZXJzb25Pbmx5KSB7XG4gICAgICAgIGl0ZW0ubWVzaC5sYXllcnMuc2V0KHRoaXMuX3RoaXJkUGVyc29uT25seUxheWVyKTtcbiAgICAgICAgaXRlbS5tZXNoLnRyYXZlcnNlKChjaGlsZCkgPT4gY2hpbGQubGF5ZXJzLnNldCh0aGlzLl90aGlyZFBlcnNvbk9ubHlMYXllcikpO1xuICAgICAgfSBlbHNlIGlmIChpdGVtLmZpcnN0UGVyc29uRmxhZyA9PT0gRmlyc3RQZXJzb25GbGFnLkF1dG8pIHtcbiAgICAgICAgdGhpcy5fY3JlYXRlSGVhZGxlc3NNb2RlbChpdGVtLm1lc2gpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBfZXhjbHVkZVRyaWFuZ2xlcyh0cmlhbmdsZXM6IG51bWJlcltdLCBid3M6IG51bWJlcltdW10sIHNraW5JbmRleDogbnVtYmVyW11bXSwgZXhjbHVkZTogbnVtYmVyW10pOiBudW1iZXIge1xuICAgIGxldCBjb3VudCA9IDA7XG4gICAgaWYgKGJ3cyAhPSBudWxsICYmIGJ3cy5sZW5ndGggPiAwKSB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRyaWFuZ2xlcy5sZW5ndGg7IGkgKz0gMykge1xuICAgICAgICBjb25zdCBhID0gdHJpYW5nbGVzW2ldO1xuICAgICAgICBjb25zdCBiID0gdHJpYW5nbGVzW2kgKyAxXTtcbiAgICAgICAgY29uc3QgYyA9IHRyaWFuZ2xlc1tpICsgMl07XG4gICAgICAgIGNvbnN0IGJ3MCA9IGJ3c1thXTtcbiAgICAgICAgY29uc3Qgc2tpbjAgPSBza2luSW5kZXhbYV07XG5cbiAgICAgICAgaWYgKGJ3MFswXSA+IDAgJiYgZXhjbHVkZS5pbmNsdWRlcyhza2luMFswXSkpIGNvbnRpbnVlO1xuICAgICAgICBpZiAoYncwWzFdID4gMCAmJiBleGNsdWRlLmluY2x1ZGVzKHNraW4wWzFdKSkgY29udGludWU7XG4gICAgICAgIGlmIChidzBbMl0gPiAwICYmIGV4Y2x1ZGUuaW5jbHVkZXMoc2tpbjBbMl0pKSBjb250aW51ZTtcbiAgICAgICAgaWYgKGJ3MFszXSA+IDAgJiYgZXhjbHVkZS5pbmNsdWRlcyhza2luMFszXSkpIGNvbnRpbnVlO1xuXG4gICAgICAgIGNvbnN0IGJ3MSA9IGJ3c1tiXTtcbiAgICAgICAgY29uc3Qgc2tpbjEgPSBza2luSW5kZXhbYl07XG4gICAgICAgIGlmIChidzFbMF0gPiAwICYmIGV4Y2x1ZGUuaW5jbHVkZXMoc2tpbjFbMF0pKSBjb250aW51ZTtcbiAgICAgICAgaWYgKGJ3MVsxXSA+IDAgJiYgZXhjbHVkZS5pbmNsdWRlcyhza2luMVsxXSkpIGNvbnRpbnVlO1xuICAgICAgICBpZiAoYncxWzJdID4gMCAmJiBleGNsdWRlLmluY2x1ZGVzKHNraW4xWzJdKSkgY29udGludWU7XG4gICAgICAgIGlmIChidzFbM10gPiAwICYmIGV4Y2x1ZGUuaW5jbHVkZXMoc2tpbjFbM10pKSBjb250aW51ZTtcblxuICAgICAgICBjb25zdCBidzIgPSBid3NbY107XG4gICAgICAgIGNvbnN0IHNraW4yID0gc2tpbkluZGV4W2NdO1xuICAgICAgICBpZiAoYncyWzBdID4gMCAmJiBleGNsdWRlLmluY2x1ZGVzKHNraW4yWzBdKSkgY29udGludWU7XG4gICAgICAgIGlmIChidzJbMV0gPiAwICYmIGV4Y2x1ZGUuaW5jbHVkZXMoc2tpbjJbMV0pKSBjb250aW51ZTtcbiAgICAgICAgaWYgKGJ3MlsyXSA+IDAgJiYgZXhjbHVkZS5pbmNsdWRlcyhza2luMlsyXSkpIGNvbnRpbnVlO1xuICAgICAgICBpZiAoYncyWzNdID4gMCAmJiBleGNsdWRlLmluY2x1ZGVzKHNraW4yWzNdKSkgY29udGludWU7XG5cbiAgICAgICAgdHJpYW5nbGVzW2NvdW50KytdID0gYTtcbiAgICAgICAgdHJpYW5nbGVzW2NvdW50KytdID0gYjtcbiAgICAgICAgdHJpYW5nbGVzW2NvdW50KytdID0gYztcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGNvdW50O1xuICB9XG5cbiAgcHJpdmF0ZSBfY3JlYXRlRXJhc2VkTWVzaChzcmM6IFRIUkVFLlNraW5uZWRNZXNoLCBlcmFzaW5nQm9uZXNJbmRleDogbnVtYmVyW10pOiBUSFJFRS5Ta2lubmVkTWVzaCB7XG4gICAgY29uc3QgZHN0ID0gbmV3IFRIUkVFLlNraW5uZWRNZXNoKHNyYy5nZW9tZXRyeS5jbG9uZSgpLCBzcmMubWF0ZXJpYWwpO1xuICAgIGRzdC5uYW1lID0gYCR7c3JjLm5hbWV9KGVyYXNlKWA7XG4gICAgZHN0LmZydXN0dW1DdWxsZWQgPSBzcmMuZnJ1c3R1bUN1bGxlZDtcbiAgICBkc3QubGF5ZXJzLnNldCh0aGlzLl9maXJzdFBlcnNvbk9ubHlMYXllcik7XG5cbiAgICBjb25zdCBnZW9tZXRyeSA9IGRzdC5nZW9tZXRyeSBhcyBUSFJFRS5CdWZmZXJHZW9tZXRyeTtcbiAgICBjb25zdCBza2luSW5kZXhBdHRyID0gZ2VvbWV0cnkuZ2V0QXR0cmlidXRlKCdza2luSW5kZXgnKS5hcnJheTtcbiAgICBjb25zdCBza2luSW5kZXggPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNraW5JbmRleEF0dHIubGVuZ3RoOyBpICs9IDQpIHtcbiAgICAgIHNraW5JbmRleC5wdXNoKFtza2luSW5kZXhBdHRyW2ldLCBza2luSW5kZXhBdHRyW2kgKyAxXSwgc2tpbkluZGV4QXR0cltpICsgMl0sIHNraW5JbmRleEF0dHJbaSArIDNdXSk7XG4gICAgfVxuICAgIGNvbnN0IHNraW5XZWlnaHRBdHRyID0gZ2VvbWV0cnkuZ2V0QXR0cmlidXRlKCdza2luV2VpZ2h0JykuYXJyYXk7XG4gICAgY29uc3Qgc2tpbldlaWdodCA9IFtdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc2tpbldlaWdodEF0dHIubGVuZ3RoOyBpICs9IDQpIHtcbiAgICAgIHNraW5XZWlnaHQucHVzaChbc2tpbldlaWdodEF0dHJbaV0sIHNraW5XZWlnaHRBdHRyW2kgKyAxXSwgc2tpbldlaWdodEF0dHJbaSArIDJdLCBza2luV2VpZ2h0QXR0cltpICsgM11dKTtcbiAgICB9XG4gICAgY29uc3Qgb2xkVHJpYW5nbGVzID0gQXJyYXkuZnJvbShnZW9tZXRyeS5nZXRJbmRleCgpLmFycmF5KTtcbiAgICBjb25zdCBjb3VudCA9IHRoaXMuX2V4Y2x1ZGVUcmlhbmdsZXMob2xkVHJpYW5nbGVzLCBza2luV2VpZ2h0LCBza2luSW5kZXgsIGVyYXNpbmdCb25lc0luZGV4KTtcbiAgICBjb25zdCBuZXdUcmlhbmdsZTogbnVtYmVyW10gPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGNvdW50OyBpKyspIHtcbiAgICAgIG5ld1RyaWFuZ2xlW2ldID0gb2xkVHJpYW5nbGVzW2ldO1xuICAgIH1cbiAgICBnZW9tZXRyeS5zZXRJbmRleChuZXdUcmlhbmdsZSk7XG5cbiAgICAvLyBtdG9vbiBtYXRlcmlhbCBpbmNsdWRlcyBvbkJlZm9yZVJlbmRlci4gdGhpcyBpcyB1bnN1cHBvcnRlZCBhdCBTa2lubmVkTWVzaCNjbG9uZVxuICAgIGlmIChzcmMub25CZWZvcmVSZW5kZXIpIHtcbiAgICAgIGRzdC5vbkJlZm9yZVJlbmRlciA9IHNyYy5vbkJlZm9yZVJlbmRlcjtcbiAgICB9XG4gICAgZHN0LmJpbmQobmV3IFRIUkVFLlNrZWxldG9uKHNyYy5za2VsZXRvbi5ib25lcywgc3JjLnNrZWxldG9uLmJvbmVJbnZlcnNlcyksIG5ldyBUSFJFRS5NYXRyaXg0KCkpO1xuICAgIHJldHVybiBkc3Q7XG4gIH1cblxuICBwcml2YXRlIF9jcmVhdGVIZWFkbGVzc01vZGVsRm9yU2tpbm5lZE1lc2gocGFyZW50OiBUSFJFRS5PYmplY3QzRCwgbWVzaDogVEhSRUUuU2tpbm5lZE1lc2gpOiB2b2lkIHtcbiAgICBjb25zdCBlcmFzZUJvbmVJbmRleGVzOiBudW1iZXJbXSA9IFtdO1xuICAgIG1lc2guc2tlbGV0b24uYm9uZXMuZm9yRWFjaCgoYm9uZSwgaW5kZXgpID0+IHtcbiAgICAgIGlmICh0aGlzLl9pc0VyYXNlVGFyZ2V0KGJvbmUpKSBlcmFzZUJvbmVJbmRleGVzLnB1c2goaW5kZXgpO1xuICAgIH0pO1xuXG4gICAgLy8gVW5saWtlIFVuaVZSTSB3ZSBkb24ndCBjb3B5IG1lc2ggaWYgbm8gaW52aXNpYmxlIGJvbmUgd2FzIGZvdW5kXG4gICAgaWYgKCFlcmFzZUJvbmVJbmRleGVzLmxlbmd0aCkge1xuICAgICAgbWVzaC5sYXllcnMuZW5hYmxlKHRoaXMuX3RoaXJkUGVyc29uT25seUxheWVyKTtcbiAgICAgIG1lc2gubGF5ZXJzLmVuYWJsZSh0aGlzLl9maXJzdFBlcnNvbk9ubHlMYXllcik7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIG1lc2gubGF5ZXJzLnNldCh0aGlzLl90aGlyZFBlcnNvbk9ubHlMYXllcik7XG4gICAgY29uc3QgbmV3TWVzaCA9IHRoaXMuX2NyZWF0ZUVyYXNlZE1lc2gobWVzaCwgZXJhc2VCb25lSW5kZXhlcyk7XG4gICAgcGFyZW50LmFkZChuZXdNZXNoKTtcbiAgfVxuXG4gIHByaXZhdGUgX2NyZWF0ZUhlYWRsZXNzTW9kZWwobm9kZTogR0xURk5vZGUpOiB2b2lkIHtcbiAgICBpZiAobm9kZS50eXBlID09PSAnR3JvdXAnKSB7XG4gICAgICBub2RlLmxheWVycy5zZXQodGhpcy5fdGhpcmRQZXJzb25Pbmx5TGF5ZXIpO1xuICAgICAgaWYgKHRoaXMuX2lzRXJhc2VUYXJnZXQobm9kZSkpIHtcbiAgICAgICAgbm9kZS50cmF2ZXJzZSgoY2hpbGQpID0+IGNoaWxkLmxheWVycy5zZXQodGhpcy5fdGhpcmRQZXJzb25Pbmx5TGF5ZXIpKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IHBhcmVudCA9IG5ldyBUSFJFRS5Hcm91cCgpO1xuICAgICAgICBwYXJlbnQubmFtZSA9IGBfaGVhZGxlc3NfJHtub2RlLm5hbWV9YDtcbiAgICAgICAgcGFyZW50LmxheWVycy5zZXQodGhpcy5fZmlyc3RQZXJzb25Pbmx5TGF5ZXIpO1xuICAgICAgICBub2RlLnBhcmVudCEuYWRkKHBhcmVudCk7XG4gICAgICAgIG5vZGUuY2hpbGRyZW5cbiAgICAgICAgICAuZmlsdGVyKChjaGlsZCkgPT4gY2hpbGQudHlwZSA9PT0gJ1NraW5uZWRNZXNoJylcbiAgICAgICAgICAuZm9yRWFjaCgoY2hpbGQpID0+IHtcbiAgICAgICAgICAgIHRoaXMuX2NyZWF0ZUhlYWRsZXNzTW9kZWxGb3JTa2lubmVkTWVzaChwYXJlbnQsIGNoaWxkIGFzIFRIUkVFLlNraW5uZWRNZXNoKTtcbiAgICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKG5vZGUudHlwZSA9PT0gJ1NraW5uZWRNZXNoJykge1xuICAgICAgdGhpcy5fY3JlYXRlSGVhZGxlc3NNb2RlbEZvclNraW5uZWRNZXNoKG5vZGUucGFyZW50ISwgbm9kZSBhcyBUSFJFRS5Ta2lubmVkTWVzaCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmICh0aGlzLl9pc0VyYXNlVGFyZ2V0KG5vZGUpKSB7XG4gICAgICAgIG5vZGUubGF5ZXJzLnNldCh0aGlzLl90aGlyZFBlcnNvbk9ubHlMYXllcik7XG4gICAgICAgIG5vZGUudHJhdmVyc2UoKGNoaWxkKSA9PiBjaGlsZC5sYXllcnMuc2V0KHRoaXMuX3RoaXJkUGVyc29uT25seUxheWVyKSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBfaXNFcmFzZVRhcmdldChib25lOiBHTFRGTm9kZSk6IGJvb2xlYW4ge1xuICAgIGlmIChib25lLm5hbWUgPT09IHRoaXMuX2ZpcnN0UGVyc29uQm9uZS5uYW1lKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9IGVsc2UgaWYgKCFib25lLnBhcmVudCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gdGhpcy5faXNFcmFzZVRhcmdldChib25lLnBhcmVudCEpO1xuICAgIH1cbiAgfVxufVxuIiwiaW1wb3J0ICogYXMgVEhSRUUgZnJvbSAndGhyZWUnO1xuaW1wb3J0IHsgVlJNSHVtYW5vaWQgfSBmcm9tICcuLi9odW1hbm9pZCc7XG5pbXBvcnQgeyBHTFRGTWVzaCwgR0xURk5vZGUsIFZSTVNjaGVtYSB9IGZyb20gJy4uL3R5cGVzJztcbmltcG9ydCB7IFZSTUZpcnN0UGVyc29uLCBWUk1SZW5kZXJlckZpcnN0UGVyc29uRmxhZ3MgfSBmcm9tICcuL1ZSTUZpcnN0UGVyc29uJztcblxuLyoqXG4gKiBBbiBpbXBvcnRlciB0aGF0IGltcG9ydHMgYSBbW1ZSTUZpcnN0UGVyc29uXV0gZnJvbSBhIFZSTSBleHRlbnNpb24gb2YgYSBHTFRGLlxuICovXG5leHBvcnQgY2xhc3MgVlJNRmlyc3RQZXJzb25JbXBvcnRlciB7XG4gIC8qKlxuICAgKiBJbXBvcnQgYSBbW1ZSTUZpcnN0UGVyc29uXV0gZnJvbSBhIFZSTS5cbiAgICpcbiAgICogQHBhcmFtIGdsdGYgQSBwYXJzZWQgcmVzdWx0IG9mIEdMVEYgdGFrZW4gZnJvbSBHTFRGTG9hZGVyXG4gICAqIEBwYXJhbSBodW1hbm9pZCBBIFtbVlJNSHVtYW5vaWRdXSBpbnN0YW5jZSB0aGF0IHJlcHJlc2VudHMgdGhlIFZSTVxuICAgKi9cbiAgcHVibGljIGFzeW5jIGltcG9ydChnbHRmOiBUSFJFRS5HTFRGLCBodW1hbm9pZDogVlJNSHVtYW5vaWQpOiBQcm9taXNlPFZSTUZpcnN0UGVyc29uIHwgbnVsbD4ge1xuICAgIGNvbnN0IHZybUV4dDogVlJNU2NoZW1hLlZSTSB8IHVuZGVmaW5lZCA9IGdsdGYucGFyc2VyLmpzb24uZXh0ZW5zaW9ucyAmJiBnbHRmLnBhcnNlci5qc29uLmV4dGVuc2lvbnMuVlJNO1xuICAgIGlmICghdnJtRXh0KSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBjb25zdCBzY2hlbWFGaXJzdFBlcnNvbjogVlJNU2NoZW1hLkZpcnN0UGVyc29uIHwgdW5kZWZpbmVkID0gdnJtRXh0LmZpcnN0UGVyc29uO1xuICAgIGlmICghc2NoZW1hRmlyc3RQZXJzb24pIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IGZpcnN0UGVyc29uQm9uZUluZGV4ID0gc2NoZW1hRmlyc3RQZXJzb24uZmlyc3RQZXJzb25Cb25lO1xuXG4gICAgbGV0IGZpcnN0UGVyc29uQm9uZTogR0xURk5vZGUgfCBudWxsO1xuICAgIGlmIChmaXJzdFBlcnNvbkJvbmVJbmRleCA9PT0gdW5kZWZpbmVkIHx8IGZpcnN0UGVyc29uQm9uZUluZGV4ID09PSAtMSkge1xuICAgICAgZmlyc3RQZXJzb25Cb25lID0gaHVtYW5vaWQuZ2V0Qm9uZU5vZGUoVlJNU2NoZW1hLkh1bWFub2lkQm9uZU5hbWUuSGVhZCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGZpcnN0UGVyc29uQm9uZSA9IGF3YWl0IGdsdGYucGFyc2VyLmdldERlcGVuZGVuY3koJ25vZGUnLCBmaXJzdFBlcnNvbkJvbmVJbmRleCk7XG4gICAgfVxuXG4gICAgaWYgKCFmaXJzdFBlcnNvbkJvbmUpIHtcbiAgICAgIGNvbnNvbGUud2FybignVlJNRmlyc3RQZXJzb25JbXBvcnRlcjogQ291bGQgbm90IGZpbmQgZmlyc3RQZXJzb25Cb25lIG9mIHRoZSBWUk0nKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IGZpcnN0UGVyc29uQm9uZU9mZnNldCA9IHNjaGVtYUZpcnN0UGVyc29uLmZpcnN0UGVyc29uQm9uZU9mZnNldFxuICAgICAgPyBuZXcgVEhSRUUuVmVjdG9yMyhcbiAgICAgICAgICBzY2hlbWFGaXJzdFBlcnNvbi5maXJzdFBlcnNvbkJvbmVPZmZzZXQueCxcbiAgICAgICAgICBzY2hlbWFGaXJzdFBlcnNvbi5maXJzdFBlcnNvbkJvbmVPZmZzZXQueSxcbiAgICAgICAgICBzY2hlbWFGaXJzdFBlcnNvbi5maXJzdFBlcnNvbkJvbmVPZmZzZXQueixcbiAgICAgICAgKVxuICAgICAgOiBuZXcgVEhSRUUuVmVjdG9yMygwLjAsIDAuMDYsIDAuMCk7IC8vIGZhbGxiYWNrLCB0YWtlbiBmcm9tIFVuaVZSTSBpbXBsZW1lbnRhdGlvblxuXG4gICAgY29uc3QgbWVzaEFubm90YXRpb25zOiBWUk1SZW5kZXJlckZpcnN0UGVyc29uRmxhZ3NbXSA9IFtdO1xuICAgIGNvbnN0IG1lc2hlczogR0xURk1lc2hbXSA9IGF3YWl0IGdsdGYucGFyc2VyLmdldERlcGVuZGVuY2llcygnbWVzaCcpO1xuICAgIG1lc2hlcy5mb3JFYWNoKChtZXNoLCBtZXNoSW5kZXgpID0+IHtcbiAgICAgIGNvbnN0IGZsYWcgPSBzY2hlbWFGaXJzdFBlcnNvbi5tZXNoQW5ub3RhdGlvbnNcbiAgICAgICAgPyBzY2hlbWFGaXJzdFBlcnNvbi5tZXNoQW5ub3RhdGlvbnMuZmluZCgoYSkgPT4gYS5tZXNoID09PSBtZXNoSW5kZXgpXG4gICAgICAgIDogdW5kZWZpbmVkO1xuICAgICAgbWVzaEFubm90YXRpb25zLnB1c2gobmV3IFZSTVJlbmRlcmVyRmlyc3RQZXJzb25GbGFncyhmbGFnICYmIGZsYWcuZmlyc3RQZXJzb25GbGFnLCBtZXNoKSk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gbmV3IFZSTUZpcnN0UGVyc29uKGZpcnN0UGVyc29uQm9uZSwgZmlyc3RQZXJzb25Cb25lT2Zmc2V0LCBtZXNoQW5ub3RhdGlvbnMpO1xuICB9XG59XG4iLCJleHBvcnQgKiBmcm9tICcuL1ZSTUZpcnN0UGVyc29uJztcbmV4cG9ydCAqIGZyb20gJy4vVlJNRmlyc3RQZXJzb25JbXBvcnRlcic7XG4iLCJpbXBvcnQgeyBHTFRGTm9kZSB9IGZyb20gJy4uL3R5cGVzJztcbmltcG9ydCB7IFZSTUh1bWFuTGltaXQgfSBmcm9tICcuL1ZSTUh1bWFuTGltaXQnO1xuXG4vKipcbiAqIEEgY2xhc3MgcmVwcmVzZW50cyBhIHNpbmdsZSBgaHVtYW5Cb25lYCBvZiBhIFZSTS5cbiAqL1xuZXhwb3J0IGNsYXNzIFZSTUh1bWFuQm9uZSB7XG4gIC8qKlxuICAgKiBBIFtbR0xURk5vZGVdXSAodGhhdCBhY3R1YWxseSBpcyBhIGBUSFJFRS5PYmplY3QzRGApIHRoYXQgcmVwcmVzZW50cyB0aGUgYm9uZS5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBub2RlOiBHTFRGTm9kZTtcblxuICAvKipcbiAgICogQSBbW1ZSTUh1bWFuTGltaXRdXSBvYmplY3QgdGhhdCByZXByZXNlbnRzIHByb3BlcnRpZXMgb2YgdGhlIGJvbmUuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgaHVtYW5MaW1pdDogVlJNSHVtYW5MaW1pdDtcblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IFZSTUh1bWFuQm9uZS5cbiAgICpcbiAgICogQHBhcmFtIG5vZGUgQSBbW0dMVEZOb2RlXV0gdGhhdCByZXByZXNlbnRzIHRoZSBuZXcgYm9uZVxuICAgKiBAcGFyYW0gaHVtYW5MaW1pdCBBIFtbVlJNSHVtYW5MaW1pdF1dIG9iamVjdCB0aGF0IHJlcHJlc2VudHMgcHJvcGVydGllcyBvZiB0aGUgbmV3IGJvbmVcbiAgICovXG4gIHB1YmxpYyBjb25zdHJ1Y3Rvcihub2RlOiBHTFRGTm9kZSwgaHVtYW5MaW1pdDogVlJNSHVtYW5MaW1pdCkge1xuICAgIHRoaXMubm9kZSA9IG5vZGU7XG4gICAgdGhpcy5odW1hbkxpbWl0ID0gaHVtYW5MaW1pdDtcbiAgfVxufVxuIiwiaW1wb3J0IHsgR0xURk5vZGUsIFJhd1ZlY3RvcjMsIFJhd1ZlY3RvcjQsIFZSTVBvc2UsIFZSTVNjaGVtYSB9IGZyb20gJy4uL3R5cGVzJztcbmltcG9ydCB7IFZSTUh1bWFuQm9uZSB9IGZyb20gJy4vVlJNSHVtYW5Cb25lJztcbmltcG9ydCB7IFZSTUh1bWFuQm9uZUFycmF5IH0gZnJvbSAnLi9WUk1IdW1hbkJvbmVBcnJheSc7XG5pbXBvcnQgeyBWUk1IdW1hbkJvbmVzIH0gZnJvbSAnLi9WUk1IdW1hbkJvbmVzJztcbmltcG9ydCB7IFZSTUh1bWFuRGVzY3JpcHRpb24gfSBmcm9tICcuL1ZSTUh1bWFuRGVzY3JpcHRpb24nO1xuXG4vKipcbiAqIEEgY2xhc3MgcmVwcmVzZW50cyBodW1hbm9pZCBvZiBhIFZSTS5cbiAqL1xuZXhwb3J0IGNsYXNzIFZSTUh1bWFub2lkIHtcbiAgLyoqXG4gICAqIEEgW1tWUk1IdW1hbkJvbmVzXV0gdGhhdCBjb250YWlucyBhbGwgdGhlIGh1bWFuIGJvbmVzIG9mIHRoZSBWUk0uXG4gICAqIFlvdSBtaWdodCB3YW50IHRvIGdldCB0aGVzZSBib25lcyB1c2luZyBbW1ZSTUh1bWFub2lkLmdldEJvbmVdXS5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBodW1hbkJvbmVzOiBWUk1IdW1hbkJvbmVzO1xuXG4gIC8qKlxuICAgKiBBIFtbVlJNSHVtYW5EZXNjcmlwdGlvbl1dIHRoYXQgcmVwcmVzZW50cyBwcm9wZXJ0aWVzIG9mIHRoZSBodW1hbm9pZC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBodW1hbkRlc2NyaXB0aW9uOiBWUk1IdW1hbkRlc2NyaXB0aW9uO1xuXG4gIC8qKlxuICAgKiBBIFtbVlJNUG9zZV1dIHRoYXQgaXMgaXRzIGRlZmF1bHQgc3RhdGUuXG4gICAqIFlvdSBtaWdodCB1c2UgW1tWUk1IdW1hbm9pZC5zZXRQb3NlXV0gd2l0aCB0aGlzIHBvc2UgdG8gcmVzZXQgaXRzIHN0YXRlLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHJlc3RQb3NlOiBWUk1Qb3NlO1xuXG4gIC8qKlxuICAgKiBDcmVhdGUgYSBuZXcgW1tWUk1IdW1hbm9pZF1dLlxuICAgKiBAcGFyYW0gYm9uZUFycmF5IEEgW1tWUk1IdW1hbkJvbmVBcnJheV1dIGNvbnRhaW5zIGFsbCB0aGUgYm9uZXMgb2YgdGhlIG5ldyBodW1hbm9pZFxuICAgKiBAcGFyYW0gaHVtYW5EZXNjcmlwdGlvbiBBIFtbVlJNSHVtYW5EZXNjcmlwdGlvbl1dIHRoYXQgcmVwcmVzZW50cyBwcm9wZXJ0aWVzIG9mIHRoZSBuZXcgaHVtYW5vaWRcbiAgICovXG4gIHB1YmxpYyBjb25zdHJ1Y3Rvcihib25lQXJyYXk6IFZSTUh1bWFuQm9uZUFycmF5LCBodW1hbkRlc2NyaXB0aW9uOiBWUk1IdW1hbkRlc2NyaXB0aW9uKSB7XG4gICAgdGhpcy5odW1hbkJvbmVzID0gdGhpcy5fY3JlYXRlSHVtYW5Cb25lcyhib25lQXJyYXkpO1xuICAgIHRoaXMuaHVtYW5EZXNjcmlwdGlvbiA9IGh1bWFuRGVzY3JpcHRpb247XG5cbiAgICB0aGlzLnJlc3RQb3NlID0gdGhpcy5nZXRQb3NlKCk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIHRoZSBjdXJyZW50IHBvc2Ugb2YgdGhpcyBodW1hbm9pZCBhcyBhIFtbVlJNUG9zZV1dLlxuICAgKi9cbiAgcHVibGljIGdldFBvc2UoKTogVlJNUG9zZSB7XG4gICAgY29uc3QgcG9zZTogVlJNUG9zZSA9IHt9O1xuICAgIE9iamVjdC5rZXlzKHRoaXMuaHVtYW5Cb25lcykuZm9yRWFjaChcbiAgICAgICh2cm1Cb25lTmFtZSkgPT4ge1xuICAgICAgICBjb25zdCBub2RlID0gdGhpcy5nZXRCb25lTm9kZSh2cm1Cb25lTmFtZSBhcyBWUk1TY2hlbWEuSHVtYW5vaWRCb25lTmFtZSkhO1xuXG4gICAgICAgIC8vIElnbm9yZSB3aGVuIHRoZXJlIGFyZSBubyBib25lIG9uIHRoZSBWUk1IdW1hbm9pZFxuICAgICAgICBpZiAoIW5vZGUpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBXaGVuIHRoZXJlIGFyZSB0d28gb3IgbW9yZSBib25lcyBpbiBhIHNhbWUgbmFtZSwgd2UgYXJlIG5vdCBnb2luZyB0byBvdmVyd3JpdGUgZXhpc3Rpbmcgb25lXG4gICAgICAgIGlmIChwb3NlW3ZybUJvbmVOYW1lXSkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHBvc2VbdnJtQm9uZU5hbWVdID0ge1xuICAgICAgICAgIHBvc2l0aW9uOiBub2RlLnBvc2l0aW9uLnRvQXJyYXkoKSBhcyBSYXdWZWN0b3IzLFxuICAgICAgICAgIHJvdGF0aW9uOiBub2RlLnF1YXRlcm5pb24udG9BcnJheSgpIGFzIFJhd1ZlY3RvcjQsXG4gICAgICAgIH07XG4gICAgICB9LFxuICAgICAge30gYXMgVlJNUG9zZSxcbiAgICApO1xuICAgIHJldHVybiBwb3NlO1xuICB9XG5cbiAgLyoqXG4gICAqIExldCB0aGUgaHVtYW5vaWQgZG8gYSBzcGVjaWZpZWQgcG9zZS5cbiAgICpcbiAgICogQHBhcmFtIHBvc2VPYmplY3QgQSBbW1ZSTVBvc2VdXSB0aGF0IHJlcHJlc2VudHMgYSBzaW5nbGUgcG9zZVxuICAgKi9cbiAgcHVibGljIHNldFBvc2UocG9zZU9iamVjdDogVlJNUG9zZSk6IHZvaWQge1xuICAgIE9iamVjdC5rZXlzKHBvc2VPYmplY3QpLmZvckVhY2goKGJvbmVOYW1lKSA9PiB7XG4gICAgICBjb25zdCBzdGF0ZSA9IHBvc2VPYmplY3RbYm9uZU5hbWVdITtcbiAgICAgIGNvbnN0IG5vZGUgPSB0aGlzLmdldEJvbmVOb2RlKGJvbmVOYW1lIGFzIFZSTVNjaGVtYS5IdW1hbm9pZEJvbmVOYW1lKTtcblxuICAgICAgLy8gSWdub3JlIHdoZW4gdGhlcmUgYXJlIG5vIGJvbmUgdGhhdCBpcyBkZWZpbmVkIGluIHRoZSBwb3NlIG9uIHRoZSBWUk1IdW1hbm9pZFxuICAgICAgaWYgKCFub2RlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgY29uc3QgcmVzdFN0YXRlID0gdGhpcy5yZXN0UG9zZVtib25lTmFtZV07XG4gICAgICBpZiAoIXJlc3RTdGF0ZSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGlmIChzdGF0ZS5wb3NpdGlvbikge1xuICAgICAgICAvLyDlhYPjga7nirbmhYvjgavmiLvjgZfjgabjgYvjgonjgIHnp7vli5XliIbjgpLov73liqBcbiAgICAgICAgbm9kZS5wb3NpdGlvbi5zZXQoXG4gICAgICAgICAgcmVzdFN0YXRlLnBvc2l0aW9uIVswXSArIHN0YXRlLnBvc2l0aW9uWzBdLFxuICAgICAgICAgIHJlc3RTdGF0ZS5wb3NpdGlvbiFbMV0gKyBzdGF0ZS5wb3NpdGlvblsxXSxcbiAgICAgICAgICByZXN0U3RhdGUucG9zaXRpb24hWzJdICsgc3RhdGUucG9zaXRpb25bMl0sXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICBpZiAoc3RhdGUucm90YXRpb24pIHtcbiAgICAgICAgbm9kZS5xdWF0ZXJuaW9uLmZyb21BcnJheShzdGF0ZS5yb3RhdGlvbik7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIGEgYm9uZSBib3VuZCB0byBhIHNwZWNpZmllZCBbW0h1bWFuQm9uZV1dLCBhcyBhIFtbVlJNSHVtYW5Cb25lXV0uXG4gICAqXG4gICAqIFNlZSBhbHNvOiBbW1ZSTUh1bWFub2lkLmdldEJvbmVzXV1cbiAgICpcbiAgICogQHBhcmFtIG5hbWUgTmFtZSBvZiB0aGUgYm9uZSB5b3Ugd2FudFxuICAgKi9cbiAgcHVibGljIGdldEJvbmUobmFtZTogVlJNU2NoZW1hLkh1bWFub2lkQm9uZU5hbWUpOiBWUk1IdW1hbkJvbmUgfCB1bmRlZmluZWQge1xuICAgIHJldHVybiB0aGlzLmh1bWFuQm9uZXNbbmFtZV1bMF0gfHwgdW5kZWZpbmVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiBib25lcyBib3VuZCB0byBhIHNwZWNpZmllZCBbW0h1bWFuQm9uZV1dLCBhcyBhbiBhcnJheSBvZiBbW1ZSTUh1bWFuQm9uZV1dLlxuICAgKlxuICAgKiBTZWUgYWxzbzogW1tWUk1IdW1hbm9pZC5nZXRCb25lXV1cbiAgICpcbiAgICogQHBhcmFtIG5hbWUgTmFtZSBvZiB0aGUgYm9uZSB5b3Ugd2FudFxuICAgKi9cbiAgcHVibGljIGdldEJvbmVzKG5hbWU6IFZSTVNjaGVtYS5IdW1hbm9pZEJvbmVOYW1lKTogVlJNSHVtYW5Cb25lW10ge1xuICAgIHJldHVybiB0aGlzLmh1bWFuQm9uZXNbbmFtZV07XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIGEgYm9uZSBib3VuZCB0byBhIHNwZWNpZmllZCBbW0h1bWFuQm9uZV1dLCBhcyBhIFRIUkVFLk9iamVjdDNELlxuICAgKlxuICAgKiBTZWUgYWxzbzogW1tWUk1IdW1hbm9pZC5nZXRCb25lTm9kZXNdXVxuICAgKlxuICAgKiBAcGFyYW0gbmFtZSBOYW1lIG9mIHRoZSBib25lIHlvdSB3YW50XG4gICAqL1xuICBwdWJsaWMgZ2V0Qm9uZU5vZGUobmFtZTogVlJNU2NoZW1hLkh1bWFub2lkQm9uZU5hbWUpOiBHTFRGTm9kZSB8IG51bGwge1xuICAgIHJldHVybiAodGhpcy5odW1hbkJvbmVzW25hbWVdWzBdICYmIHRoaXMuaHVtYW5Cb25lc1tuYW1lXVswXS5ub2RlKSB8fCBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiBib25lcyBib3VuZCB0byBhIHNwZWNpZmllZCBbW0h1bWFuQm9uZV1dLCBhcyBhbiBhcnJheSBvZiBUSFJFRS5PYmplY3QzRC5cbiAgICpcbiAgICogU2VlIGFsc286IFtbVlJNSHVtYW5vaWQuZ2V0Qm9uZU5vZGVdXVxuICAgKlxuICAgKiBAcGFyYW0gbmFtZSBOYW1lIG9mIHRoZSBib25lIHlvdSB3YW50XG4gICAqL1xuICBwdWJsaWMgZ2V0Qm9uZU5vZGVzKG5hbWU6IFZSTVNjaGVtYS5IdW1hbm9pZEJvbmVOYW1lKTogR0xURk5vZGVbXSB7XG4gICAgcmV0dXJuIHRoaXMuaHVtYW5Cb25lc1tuYW1lXS5tYXAoKGJvbmUpID0+IGJvbmUubm9kZSk7XG4gIH1cblxuICAvKipcbiAgICogUHJlcGFyZSBhIFtbVlJNSHVtYW5Cb25lc11dIGZyb20gYSBbW1ZSTUh1bWFuQm9uZUFycmF5XV0uXG4gICAqL1xuICBwcml2YXRlIF9jcmVhdGVIdW1hbkJvbmVzKGJvbmVBcnJheTogVlJNSHVtYW5Cb25lQXJyYXkpOiBWUk1IdW1hbkJvbmVzIHtcbiAgICBjb25zdCBib25lczogVlJNSHVtYW5Cb25lcyA9IE9iamVjdC52YWx1ZXMoVlJNU2NoZW1hLkh1bWFub2lkQm9uZU5hbWUpLnJlZHVjZSgoYWNjdW0sIG5hbWUpID0+IHtcbiAgICAgIGFjY3VtW25hbWVdID0gW107XG4gICAgICByZXR1cm4gYWNjdW07XG4gICAgfSwge30pO1xuXG4gICAgYm9uZUFycmF5LmZvckVhY2goKGJvbmUpID0+IHtcbiAgICAgIGJvbmVzW2JvbmUubmFtZV0ucHVzaChib25lLmJvbmUpO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIGJvbmVzO1xuICB9XG59XG4iLCJpbXBvcnQgKiBhcyBUSFJFRSBmcm9tICd0aHJlZSc7XG5pbXBvcnQgeyBWUk1TY2hlbWEgfSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgeyBWUk1IdW1hbkJvbmUgfSBmcm9tICcuL1ZSTUh1bWFuQm9uZSc7XG5pbXBvcnQgeyBWUk1IdW1hbkJvbmVBcnJheSB9IGZyb20gJy4vVlJNSHVtYW5Cb25lQXJyYXknO1xuaW1wb3J0IHsgVlJNSHVtYW5EZXNjcmlwdGlvbiB9IGZyb20gJy4vVlJNSHVtYW5EZXNjcmlwdGlvbic7XG5pbXBvcnQgeyBWUk1IdW1hbm9pZCB9IGZyb20gJy4vVlJNSHVtYW5vaWQnO1xuXG4vKipcbiAqIEFuIGltcG9ydGVyIHRoYXQgaW1wb3J0cyBhIFtbVlJNSHVtYW5vaWRdXSBmcm9tIGEgVlJNIGV4dGVuc2lvbiBvZiBhIEdMVEYuXG4gKi9cbmV4cG9ydCBjbGFzcyBWUk1IdW1hbm9pZEltcG9ydGVyIHtcbiAgLyoqXG4gICAqIEltcG9ydCBhIFtbVlJNSHVtYW5vaWRdXSBmcm9tIGEgVlJNLlxuICAgKlxuICAgKiBAcGFyYW0gZ2x0ZiBBIHBhcnNlZCByZXN1bHQgb2YgR0xURiB0YWtlbiBmcm9tIEdMVEZMb2FkZXJcbiAgICovXG4gIHB1YmxpYyBhc3luYyBpbXBvcnQoZ2x0ZjogVEhSRUUuR0xURik6IFByb21pc2U8VlJNSHVtYW5vaWQgfCBudWxsPiB7XG4gICAgY29uc3QgdnJtRXh0OiBWUk1TY2hlbWEuVlJNIHwgdW5kZWZpbmVkID0gZ2x0Zi5wYXJzZXIuanNvbi5leHRlbnNpb25zICYmIGdsdGYucGFyc2VyLmpzb24uZXh0ZW5zaW9ucy5WUk07XG4gICAgaWYgKCF2cm1FeHQpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IHNjaGVtYUh1bWFub2lkOiBWUk1TY2hlbWEuSHVtYW5vaWQgfCB1bmRlZmluZWQgPSB2cm1FeHQuaHVtYW5vaWQ7XG4gICAgaWYgKCFzY2hlbWFIdW1hbm9pZCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3QgaHVtYW5Cb25lQXJyYXk6IFZSTUh1bWFuQm9uZUFycmF5ID0gW107XG4gICAgaWYgKHNjaGVtYUh1bWFub2lkLmh1bWFuQm9uZXMpIHtcbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICBzY2hlbWFIdW1hbm9pZC5odW1hbkJvbmVzLm1hcChhc3luYyAoYm9uZSkgPT4ge1xuICAgICAgICAgIGlmICghYm9uZS5ib25lIHx8ICFib25lLm5vZGUpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCBub2RlID0gYXdhaXQgZ2x0Zi5wYXJzZXIuZ2V0RGVwZW5kZW5jeSgnbm9kZScsIGJvbmUubm9kZSk7XG4gICAgICAgICAgaHVtYW5Cb25lQXJyYXkucHVzaCh7XG4gICAgICAgICAgICBuYW1lOiBib25lLmJvbmUsXG4gICAgICAgICAgICBib25lOiBuZXcgVlJNSHVtYW5Cb25lKG5vZGUsIHtcbiAgICAgICAgICAgICAgYXhpc0xlbmd0aDogYm9uZS5heGlzTGVuZ3RoLFxuICAgICAgICAgICAgICBjZW50ZXI6IGJvbmUuY2VudGVyICYmIG5ldyBUSFJFRS5WZWN0b3IzKGJvbmUuY2VudGVyLngsIGJvbmUuY2VudGVyLnksIGJvbmUuY2VudGVyLnopLFxuICAgICAgICAgICAgICBtYXg6IGJvbmUubWF4ICYmIG5ldyBUSFJFRS5WZWN0b3IzKGJvbmUubWF4LngsIGJvbmUubWF4LnksIGJvbmUubWF4LnopLFxuICAgICAgICAgICAgICBtaW46IGJvbmUubWluICYmIG5ldyBUSFJFRS5WZWN0b3IzKGJvbmUubWluLngsIGJvbmUubWluLnksIGJvbmUubWluLnopLFxuICAgICAgICAgICAgICB1c2VEZWZhdWx0VmFsdWVzOiBib25lLnVzZURlZmF1bHRWYWx1ZXMsXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IGh1bWFuRGVzY3JpcHRpb246IFZSTUh1bWFuRGVzY3JpcHRpb24gPSB7XG4gICAgICBhcm1TdHJldGNoOiBzY2hlbWFIdW1hbm9pZC5hcm1TdHJldGNoLFxuICAgICAgbGVnU3RyZXRjaDogc2NoZW1hSHVtYW5vaWQubGVnU3RyZXRjaCxcbiAgICAgIHVwcGVyQXJtVHdpc3Q6IHNjaGVtYUh1bWFub2lkLnVwcGVyQXJtVHdpc3QsXG4gICAgICBsb3dlckFybVR3aXN0OiBzY2hlbWFIdW1hbm9pZC5sb3dlckFybVR3aXN0LFxuICAgICAgdXBwZXJMZWdUd2lzdDogc2NoZW1hSHVtYW5vaWQudXBwZXJMZWdUd2lzdCxcbiAgICAgIGxvd2VyTGVnVHdpc3Q6IHNjaGVtYUh1bWFub2lkLmxvd2VyTGVnVHdpc3QsXG4gICAgICBmZWV0U3BhY2luZzogc2NoZW1hSHVtYW5vaWQuZmVldFNwYWNpbmcsXG4gICAgICBoYXNUcmFuc2xhdGlvbkRvRjogc2NoZW1hSHVtYW5vaWQuaGFzVHJhbnNsYXRpb25Eb0YsXG4gICAgfTtcblxuICAgIHJldHVybiBuZXcgVlJNSHVtYW5vaWQoaHVtYW5Cb25lQXJyYXksIGh1bWFuRGVzY3JpcHRpb24pO1xuICB9XG59XG4iLCJleHBvcnQgKiBmcm9tICcuL1ZSTUh1bWFuQm9uZSc7XG5leHBvcnQgKiBmcm9tICcuL1ZSTUh1bWFuQm9uZXMnO1xuZXhwb3J0ICogZnJvbSAnLi9WUk1IdW1hbkRlc2NyaXB0aW9uJztcbmV4cG9ydCAqIGZyb20gJy4vVlJNSHVtYW5MaW1pdCc7XG5leHBvcnQgKiBmcm9tICcuL1ZSTUh1bWFub2lkJztcbmV4cG9ydCAqIGZyb20gJy4vVlJNSHVtYW5vaWRJbXBvcnRlcic7XG4iLCJleHBvcnQgKiBmcm9tICcuL1ZSTSc7XG5leHBvcnQgKiBmcm9tICcuL1ZSTUltcG9ydGVyJztcbmV4cG9ydCAqIGZyb20gJy4vcmVkdWNlQm9uZXMnO1xuZXhwb3J0ICogZnJvbSAnLi9ibGVuZHNoYXBlJztcbmV4cG9ydCAqIGZyb20gJy4vZGVidWcnO1xuZXhwb3J0ICogZnJvbSAnLi9maXJzdHBlcnNvbic7XG5leHBvcnQgKiBmcm9tICcuL2h1bWFub2lkJztcbmV4cG9ydCAqIGZyb20gJy4vbG9va2F0JztcbmV4cG9ydCAqIGZyb20gJy4vc3ByaW5nYm9uZSc7XG5leHBvcnQgKiBmcm9tICcuL3R5cGVzJztcbmV4cG9ydCAqIGZyb20gJy4vbWF0ZXJpYWwnO1xuIiwiLyoqXG4gKiBFdmFsdWF0ZSBhIGhlcm1pdGUgc3BsaW5lLlxuICpcbiAqIEBwYXJhbSB5MCB5IG9uIHN0YXJ0XG4gKiBAcGFyYW0geTEgeSBvbiBlbmRcbiAqIEBwYXJhbSB0MCBkZWx0YSB5IG9uIHN0YXJ0XG4gKiBAcGFyYW0gdDEgZGVsdGEgeSBvbiBlbmRcbiAqIEBwYXJhbSB4IGlucHV0IHZhbHVlXG4gKi9cbmNvbnN0IGhlcm1pdGVTcGxpbmUgPSAoeTA6IG51bWJlciwgeTE6IG51bWJlciwgdDA6IG51bWJlciwgdDE6IG51bWJlciwgeDogbnVtYmVyKTogbnVtYmVyID0+IHtcbiAgY29uc3QgeGMgPSB4ICogeCAqIHg7XG4gIGNvbnN0IHhzID0geCAqIHg7XG4gIGNvbnN0IGR5ID0geTEgLSB5MDtcbiAgY29uc3QgaDAxID0gLTIuMCAqIHhjICsgMy4wICogeHM7XG4gIGNvbnN0IGgxMCA9IHhjIC0gMi4wICogeHMgKyB4O1xuICBjb25zdCBoMTEgPSB4YyAtIHhzO1xuICByZXR1cm4geTAgKyBkeSAqIGgwMSArIHQwICogaDEwICsgdDEgKiBoMTE7XG59O1xuXG4vKipcbiAqIEV2YWx1YXRlIGFuIEFuaW1hdGlvbkN1cnZlIGFycmF5LiBTZWUgQW5pbWF0aW9uQ3VydmUgY2xhc3Mgb2YgVW5pdHkgZm9yIGl0cyBkZXRhaWxzLlxuICpcbiAqIFNlZTogaHR0cHM6Ly9kb2NzLnVuaXR5M2QuY29tL2phL2N1cnJlbnQvU2NyaXB0UmVmZXJlbmNlL0FuaW1hdGlvbkN1cnZlLmh0bWxcbiAqXG4gKiBAcGFyYW0gYXJyIEFuIGFycmF5IHJlcHJlc2VudHMgYSBjdXJ2ZVxuICogQHBhcmFtIHggQW4gaW5wdXQgdmFsdWVcbiAqL1xuY29uc3QgZXZhbHVhdGVDdXJ2ZSA9IChhcnI6IG51bWJlcltdLCB4OiBudW1iZXIpOiBudW1iZXIgPT4ge1xuICAvLyAtLSBzYW5pdHkgY2hlY2sgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgaWYgKGFyci5sZW5ndGggPCA4KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdldmFsdWF0ZUN1cnZlOiBJbnZhbGlkIGN1cnZlIGRldGVjdGVkISAoQXJyYXkgbGVuZ3RoIG11c3QgYmUgOCBhdCBsZWFzdCknKTtcbiAgfVxuICBpZiAoYXJyLmxlbmd0aCAlIDQgIT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2V2YWx1YXRlQ3VydmU6IEludmFsaWQgY3VydmUgZGV0ZWN0ZWQhIChBcnJheSBsZW5ndGggbXVzdCBiZSBtdWx0aXBsZXMgb2YgNCcpO1xuICB9XG5cbiAgLy8gLS0gY2hlY2sgcmFuZ2UgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIGxldCBvdXROb2RlO1xuICBmb3IgKG91dE5vZGUgPSAwOyA7IG91dE5vZGUrKykge1xuICAgIGlmIChhcnIubGVuZ3RoIDw9IDQgKiBvdXROb2RlKSB7XG4gICAgICByZXR1cm4gYXJyWzQgKiBvdXROb2RlIC0gM107IC8vIHRvbyBmdXJ0aGVyISEgYXNzdW1lIGFzIFwiQ2xhbXBcIlxuICAgIH0gZWxzZSBpZiAoeCA8PSBhcnJbNCAqIG91dE5vZGVdKSB7XG4gICAgICBicmVhaztcbiAgICB9XG4gIH1cblxuICBjb25zdCBpbk5vZGUgPSBvdXROb2RlIC0gMTtcbiAgaWYgKGluTm9kZSA8IDApIHtcbiAgICByZXR1cm4gYXJyWzQgKiBpbk5vZGUgKyA1XTsgLy8gdG9vIGJlaGluZCEhIGFzc3VtZSBhcyBcIkNsYW1wXCJcbiAgfVxuXG4gIC8vIC0tIGNhbGN1bGF0ZSBsb2NhbCB4IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICBjb25zdCB4MCA9IGFycls0ICogaW5Ob2RlXTtcbiAgY29uc3QgeDEgPSBhcnJbNCAqIG91dE5vZGVdO1xuICBjb25zdCB4SGVybWl0ZSA9ICh4IC0geDApIC8gKHgxIC0geDApO1xuXG4gIC8vIC0tIGZpbmFsbHkgZG8gdGhlIGhlcm1pdGUgc3BsaW5lIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICBjb25zdCB5MCA9IGFycls0ICogaW5Ob2RlICsgMV07XG4gIGNvbnN0IHkxID0gYXJyWzQgKiBvdXROb2RlICsgMV07XG4gIGNvbnN0IHQwID0gYXJyWzQgKiBpbk5vZGUgKyAzXTtcbiAgY29uc3QgdDEgPSBhcnJbNCAqIG91dE5vZGUgKyAyXTtcbiAgcmV0dXJuIGhlcm1pdGVTcGxpbmUoeTAsIHkxLCB0MCwgdDEsIHhIZXJtaXRlKTtcbn07XG5cbi8qKlxuICogVGhpcyBpcyBhbiBlcXVpdmFsZW50IG9mIEN1cnZlTWFwcGVyIGNsYXNzIGRlZmluZWQgaW4gVW5pVlJNLlxuICogV2lsbCBiZSB1c2VkIGZvciBbW1ZSTUxvb2tBdEFwcGx5ZXJdXXMsIHRvIGRlZmluZSBiZWhhdmlvciBvZiBMb29rQXQuXG4gKlxuICogU2VlOiBodHRwczovL2dpdGh1Yi5jb20vdnJtLWMvVW5pVlJNL2Jsb2IvbWFzdGVyL0Fzc2V0cy9WUk0vVW5pVlJNL1NjcmlwdHMvTG9va0F0L0N1cnZlTWFwcGVyLmNzXG4gKi9cbmV4cG9ydCBjbGFzcyBDdXJ2ZU1hcHBlciB7XG4gIC8qKlxuICAgKiBBbiBhcnJheSByZXByZXNlbnRzIHRoZSBjdXJ2ZS4gU2VlIEFuaW1hdGlvbkN1cnZlIGNsYXNzIG9mIFVuaXR5IGZvciBpdHMgZGV0YWlscy5cbiAgICpcbiAgICogU2VlOiBodHRwczovL2RvY3MudW5pdHkzZC5jb20vamEvY3VycmVudC9TY3JpcHRSZWZlcmVuY2UvQW5pbWF0aW9uQ3VydmUuaHRtbFxuICAgKi9cbiAgcHVibGljIGN1cnZlOiBudW1iZXJbXSA9IFswLjAsIDAuMCwgMC4wLCAxLjAsIDEuMCwgMS4wLCAxLjAsIDAuMF07XG5cbiAgLyoqXG4gICAqIFRoZSBtYXhpbXVtIGlucHV0IHJhbmdlIG9mIHRoZSBbW0N1cnZlTWFwcGVyXV0uXG4gICAqL1xuICBwdWJsaWMgY3VydmVYUmFuZ2VEZWdyZWUgPSA5MC4wO1xuXG4gIC8qKlxuICAgKiBUaGUgbWF4aW11bSBvdXRwdXQgdmFsdWUgb2YgdGhlIFtbQ3VydmVNYXBwZXJdXS5cbiAgICovXG4gIHB1YmxpYyBjdXJ2ZVlSYW5nZURlZ3JlZSA9IDEwLjA7XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBbW0N1cnZlTWFwcGVyXV0uXG4gICAqXG4gICAqIEBwYXJhbSB4UmFuZ2UgVGhlIG1heGltdW0gaW5wdXQgcmFuZ2VcbiAgICogQHBhcmFtIHlSYW5nZSBUaGUgbWF4aW11bSBvdXRwdXQgdmFsdWVcbiAgICogQHBhcmFtIGN1cnZlIEFuIGFycmF5IHJlcHJlc2VudHMgdGhlIGN1cnZlXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih4UmFuZ2U/OiBudW1iZXIsIHlSYW5nZT86IG51bWJlciwgY3VydmU/OiBudW1iZXJbXSkge1xuICAgIGlmICh4UmFuZ2UgIT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhpcy5jdXJ2ZVhSYW5nZURlZ3JlZSA9IHhSYW5nZTtcbiAgICB9XG5cbiAgICBpZiAoeVJhbmdlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHRoaXMuY3VydmVZUmFuZ2VEZWdyZWUgPSB5UmFuZ2U7XG4gICAgfVxuXG4gICAgaWYgKGN1cnZlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHRoaXMuY3VydmUgPSBjdXJ2ZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRXZhbHVhdGUgYW4gaW5wdXQgdmFsdWUgYW5kIG91dHB1dCBhIG1hcHBlZCB2YWx1ZS5cbiAgICpcbiAgICogQHBhcmFtIHNyYyBUaGUgaW5wdXQgdmFsdWVcbiAgICovXG4gIHB1YmxpYyBtYXAoc3JjOiBudW1iZXIpOiBudW1iZXIge1xuICAgIGNvbnN0IGNsYW1wZWRTcmMgPSBNYXRoLm1pbihNYXRoLm1heChzcmMsIDAuMCksIHRoaXMuY3VydmVYUmFuZ2VEZWdyZWUpO1xuICAgIGNvbnN0IHggPSBjbGFtcGVkU3JjIC8gdGhpcy5jdXJ2ZVhSYW5nZURlZ3JlZTtcbiAgICByZXR1cm4gdGhpcy5jdXJ2ZVlSYW5nZURlZ3JlZSAqIGV2YWx1YXRlQ3VydmUodGhpcy5jdXJ2ZSwgeCk7XG4gIH1cbn1cbiIsImltcG9ydCAqIGFzIFRIUkVFIGZyb20gJ3RocmVlJztcbmltcG9ydCB7IFZSTVNjaGVtYSB9IGZyb20gJy4uL3R5cGVzJztcblxuLyoqXG4gKiBUaGlzIGNsYXNzIGlzIHVzZWQgYnkgW1tWUk1Mb29rQXRIZWFkXV0sIGFwcGxpZXMgbG9vayBhdCBkaXJlY3Rpb24uXG4gKiBUaGVyZSBhcmUgY3VycmVudGx5IHR3byB2YXJpYW50IG9mIGFwcGxpZXI6IFtbVlJNTG9va0F0Qm9uZUFwcGx5ZXJdXSBhbmQgW1tWUk1Mb29rQXRCbGVuZFNoYXBlQXBwbHllcl1dLlxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgVlJNTG9va0F0QXBwbHllciB7XG4gIC8qKlxuICAgKiBJdCByZXByZXNlbnRzIGl0cyB0eXBlIG9mIGFwcGxpZXIuXG4gICAqL1xuICBwdWJsaWMgYWJzdHJhY3QgcmVhZG9ubHkgdHlwZTogVlJNU2NoZW1hLkZpcnN0UGVyc29uTG9va0F0VHlwZU5hbWU7XG5cbiAgLyoqXG4gICAqIEFwcGx5IGxvb2sgYXQgZGlyZWN0aW9uIHRvIGl0cyBhc3NvY2lhdGVkIFZSTSBtb2RlbC5cbiAgICpcbiAgICogQHBhcmFtIGV1bGVyIGBUSFJFRS5FdWxlcmAgb2JqZWN0IHRoYXQgcmVwcmVzZW50cyB0aGUgbG9vayBhdCBkaXJlY3Rpb25cbiAgICovXG4gIHB1YmxpYyBhYnN0cmFjdCBsb29rQXQoZXVsZXI6IFRIUkVFLkV1bGVyKTogdm9pZDtcbn1cbiIsImltcG9ydCAqIGFzIFRIUkVFIGZyb20gJ3RocmVlJztcbmltcG9ydCB7IFZSTUJsZW5kU2hhcGVQcm94eSB9IGZyb20gJy4uL2JsZW5kc2hhcGUnO1xuaW1wb3J0IHsgVlJNU2NoZW1hIH0gZnJvbSAnLi4vdHlwZXMnO1xuaW1wb3J0IHsgQ3VydmVNYXBwZXIgfSBmcm9tICcuL0N1cnZlTWFwcGVyJztcbmltcG9ydCB7IFZSTUxvb2tBdEFwcGx5ZXIgfSBmcm9tICcuL1ZSTUxvb2tBdEFwcGx5ZXInO1xuXG4vKipcbiAqIFRoaXMgY2xhc3MgaXMgdXNlZCBieSBbW1ZSTUxvb2tBdEhlYWRdXSwgYXBwbGllcyBsb29rIGF0IGRpcmVjdGlvbiB0byBleWUgYmxlbmQgc2hhcGVzIG9mIGEgVlJNLlxuICovXG5leHBvcnQgY2xhc3MgVlJNTG9va0F0QmxlbmRTaGFwZUFwcGx5ZXIgZXh0ZW5kcyBWUk1Mb29rQXRBcHBseWVyIHtcbiAgcHVibGljIHJlYWRvbmx5IHR5cGUgPSBWUk1TY2hlbWEuRmlyc3RQZXJzb25Mb29rQXRUeXBlTmFtZS5CbGVuZFNoYXBlO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgX2N1cnZlSG9yaXpvbnRhbDogQ3VydmVNYXBwZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgX2N1cnZlVmVydGljYWxEb3duOiBDdXJ2ZU1hcHBlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBfY3VydmVWZXJ0aWNhbFVwOiBDdXJ2ZU1hcHBlcjtcblxuICBwcml2YXRlIHJlYWRvbmx5IF9ibGVuZFNoYXBlUHJveHk6IFZSTUJsZW5kU2hhcGVQcm94eTtcblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IFZSTUxvb2tBdEJsZW5kU2hhcGVBcHBseWVyLlxuICAgKlxuICAgKiBAcGFyYW0gYmxlbmRTaGFwZVByb3h5IEEgW1tWUk1CbGVuZFNoYXBlUHJveHldXSB1c2VkIGJ5IHRoaXMgYXBwbGllclxuICAgKiBAcGFyYW0gY3VydmVIb3Jpem9udGFsIEEgW1tDdXJ2ZU1hcHBlcl1dIHVzZWQgZm9yIHRyYW5zdmVyc2UgZGlyZWN0aW9uXG4gICAqIEBwYXJhbSBjdXJ2ZVZlcnRpY2FsRG93biBBIFtbQ3VydmVNYXBwZXJdXSB1c2VkIGZvciBkb3duIGRpcmVjdGlvblxuICAgKiBAcGFyYW0gY3VydmVWZXJ0aWNhbFVwIEEgW1tDdXJ2ZU1hcHBlcl1dIHVzZWQgZm9yIHVwIGRpcmVjdGlvblxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgYmxlbmRTaGFwZVByb3h5OiBWUk1CbGVuZFNoYXBlUHJveHksXG4gICAgY3VydmVIb3Jpem9udGFsOiBDdXJ2ZU1hcHBlcixcbiAgICBjdXJ2ZVZlcnRpY2FsRG93bjogQ3VydmVNYXBwZXIsXG4gICAgY3VydmVWZXJ0aWNhbFVwOiBDdXJ2ZU1hcHBlcixcbiAgKSB7XG4gICAgc3VwZXIoKTtcblxuICAgIHRoaXMuX2N1cnZlSG9yaXpvbnRhbCA9IGN1cnZlSG9yaXpvbnRhbDtcbiAgICB0aGlzLl9jdXJ2ZVZlcnRpY2FsRG93biA9IGN1cnZlVmVydGljYWxEb3duO1xuICAgIHRoaXMuX2N1cnZlVmVydGljYWxVcCA9IGN1cnZlVmVydGljYWxVcDtcblxuICAgIHRoaXMuX2JsZW5kU2hhcGVQcm94eSA9IGJsZW5kU2hhcGVQcm94eTtcbiAgfVxuXG4gIHB1YmxpYyBuYW1lKCk6IFZSTVNjaGVtYS5GaXJzdFBlcnNvbkxvb2tBdFR5cGVOYW1lIHtcbiAgICByZXR1cm4gVlJNU2NoZW1hLkZpcnN0UGVyc29uTG9va0F0VHlwZU5hbWUuQmxlbmRTaGFwZTtcbiAgfVxuXG4gIHB1YmxpYyBsb29rQXQoZXVsZXI6IFRIUkVFLkV1bGVyKTogdm9pZCB7XG4gICAgY29uc3Qgc3JjWCA9IGV1bGVyLng7XG4gICAgY29uc3Qgc3JjWSA9IGV1bGVyLnk7XG5cbiAgICBpZiAoc3JjWCA8IDAuMCkge1xuICAgICAgdGhpcy5fYmxlbmRTaGFwZVByb3h5LnNldFZhbHVlKFZSTVNjaGVtYS5CbGVuZFNoYXBlUHJlc2V0TmFtZS5Mb29rdXAsIDAuMCk7XG4gICAgICB0aGlzLl9ibGVuZFNoYXBlUHJveHkuc2V0VmFsdWUoVlJNU2NoZW1hLkJsZW5kU2hhcGVQcmVzZXROYW1lLkxvb2tkb3duLCB0aGlzLl9jdXJ2ZVZlcnRpY2FsRG93bi5tYXAoLXNyY1gpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5fYmxlbmRTaGFwZVByb3h5LnNldFZhbHVlKFZSTVNjaGVtYS5CbGVuZFNoYXBlUHJlc2V0TmFtZS5Mb29rZG93biwgMC4wKTtcbiAgICAgIHRoaXMuX2JsZW5kU2hhcGVQcm94eS5zZXRWYWx1ZShWUk1TY2hlbWEuQmxlbmRTaGFwZVByZXNldE5hbWUuTG9va3VwLCB0aGlzLl9jdXJ2ZVZlcnRpY2FsVXAubWFwKHNyY1gpKTtcbiAgICB9XG5cbiAgICBpZiAoc3JjWSA8IDAuMCkge1xuICAgICAgdGhpcy5fYmxlbmRTaGFwZVByb3h5LnNldFZhbHVlKFZSTVNjaGVtYS5CbGVuZFNoYXBlUHJlc2V0TmFtZS5Mb29rbGVmdCwgMC4wKTtcbiAgICAgIHRoaXMuX2JsZW5kU2hhcGVQcm94eS5zZXRWYWx1ZShWUk1TY2hlbWEuQmxlbmRTaGFwZVByZXNldE5hbWUuTG9va3JpZ2h0LCB0aGlzLl9jdXJ2ZUhvcml6b250YWwubWFwKC1zcmNZKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuX2JsZW5kU2hhcGVQcm94eS5zZXRWYWx1ZShWUk1TY2hlbWEuQmxlbmRTaGFwZVByZXNldE5hbWUuTG9va3JpZ2h0LCAwLjApO1xuICAgICAgdGhpcy5fYmxlbmRTaGFwZVByb3h5LnNldFZhbHVlKFZSTVNjaGVtYS5CbGVuZFNoYXBlUHJlc2V0TmFtZS5Mb29rbGVmdCwgdGhpcy5fY3VydmVIb3Jpem9udGFsLm1hcChzcmNZKSk7XG4gICAgfVxuICB9XG59XG4iLCJpbXBvcnQgKiBhcyBUSFJFRSBmcm9tICd0aHJlZSc7XG5pbXBvcnQgeyBWUk1IdW1hbm9pZCB9IGZyb20gJy4uL2h1bWFub2lkJztcbmltcG9ydCB7IEdMVEZOb2RlLCBWUk1TY2hlbWEgfSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgeyBDdXJ2ZU1hcHBlciB9IGZyb20gJy4vQ3VydmVNYXBwZXInO1xuaW1wb3J0IHsgVlJNTG9va0F0QXBwbHllciB9IGZyb20gJy4vVlJNTG9va0F0QXBwbHllcic7XG5pbXBvcnQgeyBWUk1Mb29rQXRIZWFkIH0gZnJvbSAnLi9WUk1Mb29rQXRIZWFkJztcblxuY29uc3QgX2V1bGVyID0gbmV3IFRIUkVFLkV1bGVyKDAuMCwgMC4wLCAwLjAsIFZSTUxvb2tBdEhlYWQuRVVMRVJfT1JERVIpO1xuXG4vKipcbiAqIFRoaXMgY2xhc3MgaXMgdXNlZCBieSBbW1ZSTUxvb2tBdEhlYWRdXSwgYXBwbGllcyBsb29rIGF0IGRpcmVjdGlvbiB0byBleWUgYm9uZXMgb2YgYSBWUk0uXG4gKi9cbmV4cG9ydCBjbGFzcyBWUk1Mb29rQXRCb25lQXBwbHllciBleHRlbmRzIFZSTUxvb2tBdEFwcGx5ZXIge1xuICBwdWJsaWMgcmVhZG9ubHkgdHlwZSA9IFZSTVNjaGVtYS5GaXJzdFBlcnNvbkxvb2tBdFR5cGVOYW1lLkJvbmU7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBfY3VydmVIb3Jpem9udGFsSW5uZXI6IEN1cnZlTWFwcGVyO1xuICBwcml2YXRlIHJlYWRvbmx5IF9jdXJ2ZUhvcml6b250YWxPdXRlcjogQ3VydmVNYXBwZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgX2N1cnZlVmVydGljYWxEb3duOiBDdXJ2ZU1hcHBlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBfY3VydmVWZXJ0aWNhbFVwOiBDdXJ2ZU1hcHBlcjtcblxuICBwcml2YXRlIHJlYWRvbmx5IF9sZWZ0RXllOiBHTFRGTm9kZSB8IG51bGw7XG4gIHByaXZhdGUgcmVhZG9ubHkgX3JpZ2h0RXllOiBHTFRGTm9kZSB8IG51bGw7XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBWUk1Mb29rQXRCb25lQXBwbHllci5cbiAgICpcbiAgICogQHBhcmFtIGh1bWFub2lkIEEgW1tWUk1IdW1hbm9pZF1dIHVzZWQgYnkgdGhpcyBhcHBsaWVyXG4gICAqIEBwYXJhbSBjdXJ2ZUhvcml6b250YWxJbm5lciBBIFtbQ3VydmVNYXBwZXJdXSB1c2VkIGZvciBpbm5lciB0cmFuc3ZlcnNlIGRpcmVjdGlvblxuICAgKiBAcGFyYW0gY3VydmVIb3Jpem9udGFsT3V0ZXIgQSBbW0N1cnZlTWFwcGVyXV0gdXNlZCBmb3Igb3V0ZXIgdHJhbnN2ZXJzZSBkaXJlY3Rpb25cbiAgICogQHBhcmFtIGN1cnZlVmVydGljYWxEb3duIEEgW1tDdXJ2ZU1hcHBlcl1dIHVzZWQgZm9yIGRvd24gZGlyZWN0aW9uXG4gICAqIEBwYXJhbSBjdXJ2ZVZlcnRpY2FsVXAgQSBbW0N1cnZlTWFwcGVyXV0gdXNlZCBmb3IgdXAgZGlyZWN0aW9uXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICBodW1hbm9pZDogVlJNSHVtYW5vaWQsXG4gICAgY3VydmVIb3Jpem9udGFsSW5uZXI6IEN1cnZlTWFwcGVyLFxuICAgIGN1cnZlSG9yaXpvbnRhbE91dGVyOiBDdXJ2ZU1hcHBlcixcbiAgICBjdXJ2ZVZlcnRpY2FsRG93bjogQ3VydmVNYXBwZXIsXG4gICAgY3VydmVWZXJ0aWNhbFVwOiBDdXJ2ZU1hcHBlcixcbiAgKSB7XG4gICAgc3VwZXIoKTtcblxuICAgIHRoaXMuX2N1cnZlSG9yaXpvbnRhbElubmVyID0gY3VydmVIb3Jpem9udGFsSW5uZXI7XG4gICAgdGhpcy5fY3VydmVIb3Jpem9udGFsT3V0ZXIgPSBjdXJ2ZUhvcml6b250YWxPdXRlcjtcbiAgICB0aGlzLl9jdXJ2ZVZlcnRpY2FsRG93biA9IGN1cnZlVmVydGljYWxEb3duO1xuICAgIHRoaXMuX2N1cnZlVmVydGljYWxVcCA9IGN1cnZlVmVydGljYWxVcDtcblxuICAgIHRoaXMuX2xlZnRFeWUgPSBodW1hbm9pZC5nZXRCb25lTm9kZShWUk1TY2hlbWEuSHVtYW5vaWRCb25lTmFtZS5MZWZ0RXllKTtcbiAgICB0aGlzLl9yaWdodEV5ZSA9IGh1bWFub2lkLmdldEJvbmVOb2RlKFZSTVNjaGVtYS5IdW1hbm9pZEJvbmVOYW1lLlJpZ2h0RXllKTtcbiAgfVxuXG4gIHB1YmxpYyBsb29rQXQoZXVsZXI6IFRIUkVFLkV1bGVyKTogdm9pZCB7XG4gICAgY29uc3Qgc3JjWCA9IGV1bGVyLng7XG4gICAgY29uc3Qgc3JjWSA9IGV1bGVyLnk7XG5cbiAgICAvLyBsZWZ0XG4gICAgaWYgKHRoaXMuX2xlZnRFeWUpIHtcbiAgICAgIGlmIChzcmNYIDwgMC4wKSB7XG4gICAgICAgIF9ldWxlci54ID0gLXRoaXMuX2N1cnZlVmVydGljYWxEb3duLm1hcCgtc3JjWCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBfZXVsZXIueCA9IHRoaXMuX2N1cnZlVmVydGljYWxVcC5tYXAoc3JjWCk7XG4gICAgICB9XG5cbiAgICAgIGlmIChzcmNZIDwgMC4wKSB7XG4gICAgICAgIF9ldWxlci55ID0gLXRoaXMuX2N1cnZlSG9yaXpvbnRhbElubmVyLm1hcCgtc3JjWSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBfZXVsZXIueSA9IHRoaXMuX2N1cnZlSG9yaXpvbnRhbE91dGVyLm1hcChzcmNZKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5fbGVmdEV5ZS5xdWF0ZXJuaW9uLnNldEZyb21FdWxlcihfZXVsZXIpO1xuICAgIH1cblxuICAgIC8vIHJpZ2h0XG4gICAgaWYgKHRoaXMuX3JpZ2h0RXllKSB7XG4gICAgICBpZiAoc3JjWCA8IDAuMCkge1xuICAgICAgICBfZXVsZXIueCA9IC10aGlzLl9jdXJ2ZVZlcnRpY2FsRG93bi5tYXAoLXNyY1gpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgX2V1bGVyLnggPSB0aGlzLl9jdXJ2ZVZlcnRpY2FsVXAubWFwKHNyY1gpO1xuICAgICAgfVxuXG4gICAgICBpZiAoc3JjWSA8IDAuMCkge1xuICAgICAgICBfZXVsZXIueSA9IC10aGlzLl9jdXJ2ZUhvcml6b250YWxPdXRlci5tYXAoLXNyY1kpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgX2V1bGVyLnkgPSB0aGlzLl9jdXJ2ZUhvcml6b250YWxJbm5lci5tYXAoc3JjWSk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuX3JpZ2h0RXllLnF1YXRlcm5pb24uc2V0RnJvbUV1bGVyKF9ldWxlcik7XG4gICAgfVxuICB9XG59XG4iLCJpbXBvcnQgKiBhcyBUSFJFRSBmcm9tICd0aHJlZSc7XG5pbXBvcnQgeyBWUk1GaXJzdFBlcnNvbiB9IGZyb20gJy4uL2ZpcnN0cGVyc29uL1ZSTUZpcnN0UGVyc29uJztcbmltcG9ydCB7IGdldFdvcmxkUXVhdGVybmlvbkxpdGUgfSBmcm9tICcuLi91dGlscy9tYXRoJztcbmltcG9ydCB7IFZSTUxvb2tBdEFwcGx5ZXIgfSBmcm9tICcuL1ZSTUxvb2tBdEFwcGx5ZXInO1xuXG5jb25zdCBWRUNUT1IzX0ZST05UID0gT2JqZWN0LmZyZWV6ZShuZXcgVEhSRUUuVmVjdG9yMygwLjAsIDAuMCwgLTEuMCkpO1xuXG5jb25zdCBfdjNBID0gbmV3IFRIUkVFLlZlY3RvcjMoKTtcbmNvbnN0IF92M0IgPSBuZXcgVEhSRUUuVmVjdG9yMygpO1xuY29uc3QgX3YzQyA9IG5ldyBUSFJFRS5WZWN0b3IzKCk7XG5jb25zdCBfcXVhdCA9IG5ldyBUSFJFRS5RdWF0ZXJuaW9uKCk7XG5cbi8qKlxuICogQSBjbGFzcyByZXByZXNlbnRzIGxvb2sgYXQgb2YgYSBWUk0uXG4gKi9cbmV4cG9ydCBjbGFzcyBWUk1Mb29rQXRIZWFkIHtcbiAgcHVibGljIHN0YXRpYyByZWFkb25seSBFVUxFUl9PUkRFUiA9ICdZWFonOyAvLyB5YXctcGl0Y2gtcm9sbFxuXG4gIC8qKlxuICAgKiBBc3NvY2lhdGVkIFtbVlJNRmlyc3RQZXJzb25dXSwgd2lsbCBiZSB1c2VkIGZvciBkaXJlY3Rpb24gY2FsY3VsYXRpb24uXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZmlyc3RQZXJzb246IFZSTUZpcnN0UGVyc29uO1xuXG4gIC8qKlxuICAgKiBBc3NvY2lhdGVkIFtbVlJNTG9va0F0QXBwbHllcl1dLCBpdHMgbG9vayBhdCBkaXJlY3Rpb24gd2lsbCBiZSBhcHBsaWVkIHRvIHRoZSBtb2RlbCB1c2luZyB0aGlzIGFwcGxpZXIuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgYXBwbHllcj86IFZSTUxvb2tBdEFwcGx5ZXI7XG5cbiAgLyoqXG4gICAqIElmIHRoaXMgaXMgdHJ1ZSwgaXRzIGxvb2sgYXQgZGlyZWN0aW9uIHdpbGwgYmUgdXBkYXRlZCBhdXRvbWF0aWNhbGx5IGJ5IGNhbGxpbmcgW1tWUk1Mb29rQXRIZWFkLnVwZGF0ZV1dICh3aGljaCBpcyBjYWxsZWQgZnJvbSBbW1ZSTS51cGRhdGVdXSkuXG4gICAqXG4gICAqIFNlZSBhbHNvOiBbW1ZSTUxvb2tBdEhlYWQudGFyZ2V0XV1cbiAgICovXG4gIHB1YmxpYyBhdXRvVXBkYXRlID0gdHJ1ZTtcblxuICAvKipcbiAgICogVGhlIHRhcmdldCBvYmplY3Qgb2YgdGhlIGxvb2sgYXQuXG4gICAqIE5vdGUgdGhhdCBpdCBkb2VzIG5vdCBtYWtlIGFueSBzZW5zZSBpZiBbW1ZSTUxvb2tBdEhlYWQuYXV0b1VwZGF0ZV1dIGlzIGRpc2FibGVkLlxuICAgKi9cbiAgcHVibGljIHRhcmdldD86IFRIUkVFLk9iamVjdDNEO1xuXG4gIHByb3RlY3RlZCBfZXVsZXI6IFRIUkVFLkV1bGVyID0gbmV3IFRIUkVFLkV1bGVyKDAuMCwgMC4wLCAwLjAsIFZSTUxvb2tBdEhlYWQuRVVMRVJfT1JERVIpO1xuXG4gIC8qKlxuICAgKiBDcmVhdGUgYSBuZXcgVlJNTG9va0F0SGVhZC5cbiAgICpcbiAgICogQHBhcmFtIGZpcnN0UGVyc29uIEEgW1tWUk1GaXJzdFBlcnNvbl1dIHRoYXQgd2lsbCBiZSBhc3NvY2lhdGVkIHdpdGggdGhpcyBuZXcgVlJNTG9va0F0SGVhZFxuICAgKiBAcGFyYW0gYXBwbHllciBBIFtbVlJNTG9va0F0QXBwbHllcl1dIHRoYXQgd2lsbCBiZSBhc3NvY2lhdGVkIHdpdGggdGhpcyBuZXcgVlJNTG9va0F0SGVhZFxuICAgKi9cbiAgY29uc3RydWN0b3IoZmlyc3RQZXJzb246IFZSTUZpcnN0UGVyc29uLCBhcHBseWVyPzogVlJNTG9va0F0QXBwbHllcikge1xuICAgIHRoaXMuZmlyc3RQZXJzb24gPSBmaXJzdFBlcnNvbjtcbiAgICB0aGlzLmFwcGx5ZXIgPSBhcHBseWVyO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBpdHMgbG9vayBhdCBkaXJlY3Rpb24gaW4gd29ybGQgY29vcmRpbmF0ZS5cbiAgICpcbiAgICogQHBhcmFtIHRhcmdldCBBIHRhcmdldCBgVEhSRUUuVmVjdG9yM2BcbiAgICovXG4gIHB1YmxpYyBnZXRMb29rQXRXb3JsZERpcmVjdGlvbih0YXJnZXQ6IFRIUkVFLlZlY3RvcjMpOiBUSFJFRS5WZWN0b3IzIHtcbiAgICBjb25zdCByb3QgPSBnZXRXb3JsZFF1YXRlcm5pb25MaXRlKHRoaXMuZmlyc3RQZXJzb24uZmlyc3RQZXJzb25Cb25lLCBfcXVhdCk7XG4gICAgcmV0dXJuIHRhcmdldFxuICAgICAgLmNvcHkoVkVDVE9SM19GUk9OVClcbiAgICAgIC5hcHBseUV1bGVyKHRoaXMuX2V1bGVyKVxuICAgICAgLmFwcGx5UXVhdGVybmlvbihyb3QpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCBpdHMgbG9vayBhdCBwb3NpdGlvbi5cbiAgICogTm90ZSB0aGF0IGl0cyByZXN1bHQgd2lsbCBiZSBpbnN0YW50bHkgb3ZlcndyaXR0ZW4gaWYgW1tWUk1Mb29rQXRIZWFkLmF1dG9VcGRhdGVdXSBpcyBlbmFibGVkLlxuICAgKlxuICAgKiBAcGFyYW0gcG9zaXRpb24gQSB0YXJnZXQgcG9zaXRpb25cbiAgICovXG4gIHB1YmxpYyBsb29rQXQocG9zaXRpb246IFRIUkVFLlZlY3RvcjMpOiB2b2lkIHtcbiAgICB0aGlzLl9jYWxjRXVsZXIodGhpcy5fZXVsZXIsIHBvc2l0aW9uKTtcblxuICAgIGlmICh0aGlzLmFwcGx5ZXIpIHtcbiAgICAgIHRoaXMuYXBwbHllci5sb29rQXQodGhpcy5fZXVsZXIpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgdGhlIFZSTUxvb2tBdEhlYWQuXG4gICAqIElmIFtbVlJNTG9va0F0SGVhZC5hdXRvVXBkYXRlXV0gaXMgZGlzYWJsZWQsIGl0IHdpbGwgZG8gbm90aGluZy5cbiAgICpcbiAgICogQHBhcmFtIGRlbHRhIGRlbHRhVGltZVxuICAgKi9cbiAgcHVibGljIHVwZGF0ZShkZWx0YTogbnVtYmVyKTogdm9pZCB7XG4gICAgaWYgKHRoaXMudGFyZ2V0ICYmIHRoaXMuYXV0b1VwZGF0ZSkge1xuICAgICAgdGhpcy5sb29rQXQodGhpcy50YXJnZXQuZ2V0V29ybGRQb3NpdGlvbihfdjNBKSk7XG5cbiAgICAgIGlmICh0aGlzLmFwcGx5ZXIpIHtcbiAgICAgICAgdGhpcy5hcHBseWVyLmxvb2tBdCh0aGlzLl9ldWxlcik7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIF9jYWxjRXVsZXIodGFyZ2V0OiBUSFJFRS5FdWxlciwgcG9zaXRpb246IFRIUkVFLlZlY3RvcjMpOiBUSFJFRS5FdWxlciB7XG4gICAgY29uc3QgaGVhZFBvc2l0aW9uID0gdGhpcy5maXJzdFBlcnNvbi5nZXRGaXJzdFBlcnNvbldvcmxkUG9zaXRpb24oX3YzQik7XG5cbiAgICAvLyBMb29rIGF0IGRpcmVjdGlvbiBpbiB3b3JsZCBjb29yZGluYXRlXG4gICAgY29uc3QgbG9va0F0RGlyID0gX3YzQ1xuICAgICAgLmNvcHkocG9zaXRpb24pXG4gICAgICAuc3ViKGhlYWRQb3NpdGlvbilcbiAgICAgIC5ub3JtYWxpemUoKTtcblxuICAgIC8vIFRyYW5zZm9ybSB0aGUgZGlyZWN0aW9uIGludG8gbG9jYWwgY29vcmRpbmF0ZSBmcm9tIHRoZSBmaXJzdCBwZXJzb24gYm9uZVxuICAgIGxvb2tBdERpci5hcHBseVF1YXRlcm5pb24oZ2V0V29ybGRRdWF0ZXJuaW9uTGl0ZSh0aGlzLmZpcnN0UGVyc29uLmZpcnN0UGVyc29uQm9uZSwgX3F1YXQpLmludmVyc2UoKSk7XG5cbiAgICAvLyBjb252ZXJ0IHRoZSBkaXJlY3Rpb24gaW50byBldWxlclxuICAgIHRhcmdldC54ID0gTWF0aC5hdGFuMihsb29rQXREaXIueSwgTWF0aC5zcXJ0KGxvb2tBdERpci54ICogbG9va0F0RGlyLnggKyBsb29rQXREaXIueiAqIGxvb2tBdERpci56KSk7XG4gICAgdGFyZ2V0LnkgPSBNYXRoLmF0YW4yKC1sb29rQXREaXIueCwgLWxvb2tBdERpci56KTtcblxuICAgIHJldHVybiB0YXJnZXQ7XG4gIH1cbn1cbiIsImltcG9ydCAqIGFzIFRIUkVFIGZyb20gJ3RocmVlJztcbmltcG9ydCB7IFZSTUJsZW5kU2hhcGVQcm94eSB9IGZyb20gJy4uL2JsZW5kc2hhcGUnO1xuaW1wb3J0IHsgVlJNRmlyc3RQZXJzb24gfSBmcm9tICcuLi9maXJzdHBlcnNvbic7XG5pbXBvcnQgeyBWUk1IdW1hbm9pZCB9IGZyb20gJy4uL2h1bWFub2lkJztcbmltcG9ydCB7IFZSTVNjaGVtYSB9IGZyb20gJy4uL3R5cGVzJztcbmltcG9ydCB7IEN1cnZlTWFwcGVyIH0gZnJvbSAnLi9DdXJ2ZU1hcHBlcic7XG5pbXBvcnQgeyBWUk1Mb29rQXRBcHBseWVyIH0gZnJvbSAnLi9WUk1Mb29rQXRBcHBseWVyJztcbmltcG9ydCB7IFZSTUxvb2tBdEJsZW5kU2hhcGVBcHBseWVyIH0gZnJvbSAnLi9WUk1Mb29rQXRCbGVuZFNoYXBlQXBwbHllcic7XG5pbXBvcnQgeyBWUk1Mb29rQXRCb25lQXBwbHllciB9IGZyb20gJy4vVlJNTG9va0F0Qm9uZUFwcGx5ZXInO1xuaW1wb3J0IHsgVlJNTG9va0F0SGVhZCB9IGZyb20gJy4vVlJNTG9va0F0SGVhZCc7XG5cbi8qKlxuICogQW4gaW1wb3J0ZXIgdGhhdCBpbXBvcnRzIGEgW1tWUk1Mb29rQXRIZWFkXV0gZnJvbSBhIFZSTSBleHRlbnNpb24gb2YgYSBHTFRGLlxuICovXG5leHBvcnQgY2xhc3MgVlJNTG9va0F0SW1wb3J0ZXIge1xuICAvKipcbiAgICogSW1wb3J0IGEgW1tWUk1Mb29rQXRIZWFkXV0gZnJvbSBhIFZSTS5cbiAgICpcbiAgICogQHBhcmFtIGdsdGYgQSBwYXJzZWQgcmVzdWx0IG9mIEdMVEYgdGFrZW4gZnJvbSBHTFRGTG9hZGVyXG4gICAqIEBwYXJhbSBibGVuZFNoYXBlUHJveHkgQSBbW1ZSTUJsZW5kU2hhcGVQcm94eV1dIGluc3RhbmNlIHRoYXQgcmVwcmVzZW50cyB0aGUgVlJNXG4gICAqIEBwYXJhbSBodW1hbm9pZCBBIFtbVlJNSHVtYW5vaWRdXSBpbnN0YW5jZSB0aGF0IHJlcHJlc2VudHMgdGhlIFZSTVxuICAgKi9cbiAgcHVibGljIGltcG9ydChcbiAgICBnbHRmOiBUSFJFRS5HTFRGLFxuICAgIGZpcnN0UGVyc29uOiBWUk1GaXJzdFBlcnNvbixcbiAgICBibGVuZFNoYXBlUHJveHk6IFZSTUJsZW5kU2hhcGVQcm94eSxcbiAgICBodW1hbm9pZDogVlJNSHVtYW5vaWQsXG4gICk6IFZSTUxvb2tBdEhlYWQgfCBudWxsIHtcbiAgICBjb25zdCB2cm1FeHQ6IFZSTVNjaGVtYS5WUk0gfCB1bmRlZmluZWQgPSBnbHRmLnBhcnNlci5qc29uLmV4dGVuc2lvbnMgJiYgZ2x0Zi5wYXJzZXIuanNvbi5leHRlbnNpb25zLlZSTTtcbiAgICBpZiAoIXZybUV4dCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3Qgc2NoZW1hRmlyc3RQZXJzb246IFZSTVNjaGVtYS5GaXJzdFBlcnNvbiB8IHVuZGVmaW5lZCA9IHZybUV4dC5maXJzdFBlcnNvbjtcbiAgICBpZiAoIXNjaGVtYUZpcnN0UGVyc29uKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBjb25zdCBhcHBseWVyID0gdGhpcy5faW1wb3J0QXBwbHllcihzY2hlbWFGaXJzdFBlcnNvbiwgYmxlbmRTaGFwZVByb3h5LCBodW1hbm9pZCk7XG4gICAgcmV0dXJuIG5ldyBWUk1Mb29rQXRIZWFkKGZpcnN0UGVyc29uLCBhcHBseWVyIHx8IHVuZGVmaW5lZCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgX2ltcG9ydEFwcGx5ZXIoXG4gICAgc2NoZW1hRmlyc3RQZXJzb246IFZSTVNjaGVtYS5GaXJzdFBlcnNvbixcbiAgICBibGVuZFNoYXBlUHJveHk6IFZSTUJsZW5kU2hhcGVQcm94eSxcbiAgICBodW1hbm9pZDogVlJNSHVtYW5vaWQsXG4gICk6IFZSTUxvb2tBdEFwcGx5ZXIgfCBudWxsIHtcbiAgICBjb25zdCBsb29rQXRIb3Jpem9udGFsSW5uZXIgPSBzY2hlbWFGaXJzdFBlcnNvbi5sb29rQXRIb3Jpem9udGFsSW5uZXI7XG4gICAgY29uc3QgbG9va0F0SG9yaXpvbnRhbE91dGVyID0gc2NoZW1hRmlyc3RQZXJzb24ubG9va0F0SG9yaXpvbnRhbE91dGVyO1xuICAgIGNvbnN0IGxvb2tBdFZlcnRpY2FsRG93biA9IHNjaGVtYUZpcnN0UGVyc29uLmxvb2tBdFZlcnRpY2FsRG93bjtcbiAgICBjb25zdCBsb29rQXRWZXJ0aWNhbFVwID0gc2NoZW1hRmlyc3RQZXJzb24ubG9va0F0VmVydGljYWxVcDtcblxuICAgIHN3aXRjaCAoc2NoZW1hRmlyc3RQZXJzb24ubG9va0F0VHlwZU5hbWUpIHtcbiAgICAgIGNhc2UgVlJNU2NoZW1hLkZpcnN0UGVyc29uTG9va0F0VHlwZU5hbWUuQm9uZToge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgbG9va0F0SG9yaXpvbnRhbElubmVyID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgICBsb29rQXRIb3Jpem9udGFsT3V0ZXIgPT09IHVuZGVmaW5lZCB8fFxuICAgICAgICAgIGxvb2tBdFZlcnRpY2FsRG93biA9PT0gdW5kZWZpbmVkIHx8XG4gICAgICAgICAgbG9va0F0VmVydGljYWxVcCA9PT0gdW5kZWZpbmVkXG4gICAgICAgICkge1xuICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBuZXcgVlJNTG9va0F0Qm9uZUFwcGx5ZXIoXG4gICAgICAgICAgICBodW1hbm9pZCxcbiAgICAgICAgICAgIHRoaXMuX2ltcG9ydEN1cnZlTWFwcGVyQm9uZShsb29rQXRIb3Jpem9udGFsSW5uZXIpLFxuICAgICAgICAgICAgdGhpcy5faW1wb3J0Q3VydmVNYXBwZXJCb25lKGxvb2tBdEhvcml6b250YWxPdXRlciksXG4gICAgICAgICAgICB0aGlzLl9pbXBvcnRDdXJ2ZU1hcHBlckJvbmUobG9va0F0VmVydGljYWxEb3duKSxcbiAgICAgICAgICAgIHRoaXMuX2ltcG9ydEN1cnZlTWFwcGVyQm9uZShsb29rQXRWZXJ0aWNhbFVwKSxcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjYXNlIFZSTVNjaGVtYS5GaXJzdFBlcnNvbkxvb2tBdFR5cGVOYW1lLkJsZW5kU2hhcGU6IHtcbiAgICAgICAgaWYgKGxvb2tBdEhvcml6b250YWxPdXRlciA9PT0gdW5kZWZpbmVkIHx8IGxvb2tBdFZlcnRpY2FsRG93biA9PT0gdW5kZWZpbmVkIHx8IGxvb2tBdFZlcnRpY2FsVXAgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBuZXcgVlJNTG9va0F0QmxlbmRTaGFwZUFwcGx5ZXIoXG4gICAgICAgICAgICBibGVuZFNoYXBlUHJveHksXG4gICAgICAgICAgICB0aGlzLl9pbXBvcnRDdXJ2ZU1hcHBlckJsZW5kU2hhcGUobG9va0F0SG9yaXpvbnRhbE91dGVyKSxcbiAgICAgICAgICAgIHRoaXMuX2ltcG9ydEN1cnZlTWFwcGVyQmxlbmRTaGFwZShsb29rQXRWZXJ0aWNhbERvd24pLFxuICAgICAgICAgICAgdGhpcy5faW1wb3J0Q3VydmVNYXBwZXJCbGVuZFNoYXBlKGxvb2tBdFZlcnRpY2FsVXApLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGRlZmF1bHQ6IHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBfaW1wb3J0Q3VydmVNYXBwZXJCb25lKG1hcDogVlJNU2NoZW1hLkZpcnN0UGVyc29uRGVncmVlTWFwKTogQ3VydmVNYXBwZXIge1xuICAgIHJldHVybiBuZXcgQ3VydmVNYXBwZXIoXG4gICAgICB0eXBlb2YgbWFwLnhSYW5nZSA9PT0gJ251bWJlcicgPyBUSFJFRS5NYXRoLkRFRzJSQUQgKiBtYXAueFJhbmdlIDogdW5kZWZpbmVkLFxuICAgICAgdHlwZW9mIG1hcC55UmFuZ2UgPT09ICdudW1iZXInID8gVEhSRUUuTWF0aC5ERUcyUkFEICogbWFwLnlSYW5nZSA6IHVuZGVmaW5lZCxcbiAgICAgIG1hcC5jdXJ2ZSxcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBfaW1wb3J0Q3VydmVNYXBwZXJCbGVuZFNoYXBlKG1hcDogVlJNU2NoZW1hLkZpcnN0UGVyc29uRGVncmVlTWFwKTogQ3VydmVNYXBwZXIge1xuICAgIHJldHVybiBuZXcgQ3VydmVNYXBwZXIoXG4gICAgICB0eXBlb2YgbWFwLnhSYW5nZSA9PT0gJ251bWJlcicgPyBUSFJFRS5NYXRoLkRFRzJSQUQgKiBtYXAueFJhbmdlIDogdW5kZWZpbmVkLFxuICAgICAgbWFwLnlSYW5nZSxcbiAgICAgIG1hcC5jdXJ2ZSxcbiAgICApO1xuICB9XG59XG4iLCJleHBvcnQgKiBmcm9tICcuL0N1cnZlTWFwcGVyJztcbmV4cG9ydCAqIGZyb20gJy4vVlJNTG9va0F0QXBwbHllcic7XG5leHBvcnQgKiBmcm9tICcuL1ZSTUxvb2tBdEJsZW5kU2hhcGVBcHBseWVyJztcbmV4cG9ydCAqIGZyb20gJy4vVlJNTG9va0F0Qm9uZUFwcGx5ZXInO1xuZXhwb3J0ICogZnJvbSAnLi9WUk1Mb29rQXRIZWFkJztcbmV4cG9ydCAqIGZyb20gJy4vVlJNTG9va0F0SW1wb3J0ZXInO1xuIiwiLyogdHNsaW50OmRpc2FibGU6bWVtYmVyLW9yZGVyaW5nICovXG5cbmltcG9ydCAqIGFzIFRIUkVFIGZyb20gJ3RocmVlJztcbmltcG9ydCB7IGdldFRleGVsRGVjb2RpbmdGdW5jdGlvbiB9IGZyb20gJy4vZ2V0VGV4ZWxEZWNvZGluZ0Z1bmN0aW9uJztcbmltcG9ydCB2ZXJ0ZXhTaGFkZXIgZnJvbSAnLi9zaGFkZXJzL210b29uLnZlcnQnO1xuaW1wb3J0IGZyYWdtZW50U2hhZGVyIGZyb20gJy4vc2hhZGVycy9tdG9vbi5mcmFnJztcblxuY29uc3QgVEFVID0gMi4wICogTWF0aC5QSTtcblxuZXhwb3J0IGludGVyZmFjZSBNVG9vblBhcmFtZXRlcnMgZXh0ZW5kcyBUSFJFRS5TaGFkZXJNYXRlcmlhbFBhcmFtZXRlcnMge1xuICBtVG9vblZlcnNpb24/OiBudW1iZXI7IC8vIF9NVG9vblZlcnNpb25cblxuICBjdXRvZmY/OiBudW1iZXI7IC8vIF9DdXRvZmZcbiAgY29sb3I/OiBUSFJFRS5WZWN0b3I0OyAvLyByZ2Igb2YgX0NvbG9yXG4gIHNoYWRlQ29sb3I/OiBUSFJFRS5WZWN0b3I0OyAvLyBfU2hhZGVDb2xvclxuICBtYXA/OiBUSFJFRS5UZXh0dXJlOyAvLyBfTWFpblRleFxuICBtYWluVGV4PzogVEhSRUUuVGV4dHVyZTsgLy8gX01haW5UZXggKHdpbGwgYmUgcmVuYW1lZCB0byBtYXApXG4gIG1haW5UZXhfU1Q/OiBUSFJFRS5WZWN0b3I0OyAvLyBfTWFpblRleF9TVFxuICBzaGFkZVRleHR1cmU/OiBUSFJFRS5UZXh0dXJlOyAvLyBfU2hhZGVUZXh0dXJlXG4gIGJ1bXBTY2FsZT86IG51bWJlcjsgLy8gX0J1bXBTY2FsZVxuICBub3JtYWxNYXA/OiBUSFJFRS5UZXh0dXJlOyAvLyBfQnVtcE1hcFxuICBidW1wTWFwPzogVEhSRUUuVGV4dHVyZTsgLy8gX0J1bXBNYXAgKHdpbGwgYmUgcmVuYW1lZCB0byBub3JtYWxNYXApXG4gIHJlY2VpdmVTaGFkb3dSYXRlPzogbnVtYmVyOyAvLyBfUmVjZWl2ZVNoYWRvd1JhdGVcbiAgcmVjZWl2ZVNoYWRvd1RleHR1cmU/OiBUSFJFRS5UZXh0dXJlOyAvLyBfUmVjZWl2ZVNoYWRvd1RleHR1cmVcbiAgc2hhZGluZ0dyYWRlUmF0ZT86IG51bWJlcjsgLy8gX1NoYWRpbmdHcmFkZVJhdGVcbiAgc2hhZGluZ0dyYWRlVGV4dHVyZT86IFRIUkVFLlRleHR1cmU7IC8vIF9TaGFkaW5nR3JhZGVUZXh0dXJlXG4gIHNoYWRlU2hpZnQ/OiBudW1iZXI7IC8vIF9TaGFkZVNoaWZ0XG4gIHNoYWRlVG9vbnk/OiBudW1iZXI7IC8vIF9TaGFkZVRvb255XG4gIGxpZ2h0Q29sb3JBdHRlbnVhdGlvbj86IG51bWJlcjsgLy8gX0xpZ2h0Q29sb3JBdHRlbnVhdGlvblxuICBpbmRpcmVjdExpZ2h0SW50ZW5zaXR5PzogbnVtYmVyOyAvLyBfSW5kaXJlY3RMaWdodEludGVuc2l0eVxuICByaW1UZXh0dXJlPzogVEhSRUUuVGV4dHVyZTsgLy8gX1JpbVRleHR1cmVcbiAgcmltQ29sb3I/OiBUSFJFRS5WZWN0b3I0OyAvLyBfUmltQ29sb3JcbiAgcmltTGlnaHRpbmdNaXg/OiBudW1iZXI7IC8vIF9SaW1MaWdodGluZ01peFxuICByaW1GcmVzbmVsUG93ZXI/OiBudW1iZXI7IC8vIF9SaW1GcmVzbmVsUG93ZXJcbiAgcmltTGlmdD86IG51bWJlcjsgLy8gX1JpbUxpZnRcbiAgc3BoZXJlQWRkPzogVEhSRUUuVGV4dHVyZTsgLy8gX1NwaGVyZUFkZFxuICBlbWlzc2lvbkNvbG9yPzogVEhSRUUuVmVjdG9yNDsgLy8gX0VtaXNzaW9uQ29sb3JcbiAgZW1pc3NpdmVNYXA/OiBUSFJFRS5UZXh0dXJlOyAvLyBfRW1pc3Npb25NYXBcbiAgZW1pc3Npb25NYXA/OiBUSFJFRS5UZXh0dXJlOyAvLyBfRW1pc3Npb25NYXAgKHdpbGwgYmUgcmVuYW1lZCB0byBlbWlzc2l2ZU1hcClcbiAgb3V0bGluZVdpZHRoVGV4dHVyZT86IFRIUkVFLlRleHR1cmU7IC8vIF9PdXRsaW5lV2lkdGhUZXh0dXJlXG4gIG91dGxpbmVXaWR0aD86IG51bWJlcjsgLy8gX091dGxpbmVXaWR0aFxuICBvdXRsaW5lU2NhbGVkTWF4RGlzdGFuY2U/OiBudW1iZXI7IC8vIF9PdXRsaW5lU2NhbGVkTWF4RGlzdGFuY2VcbiAgb3V0bGluZUNvbG9yPzogVEhSRUUuVmVjdG9yNDsgLy8gX091dGxpbmVDb2xvclxuICBvdXRsaW5lTGlnaHRpbmdNaXg/OiBudW1iZXI7IC8vIF9PdXRsaW5lTGlnaHRpbmdNaXhcbiAgdXZBbmltTWFza1RleHR1cmU/OiBUSFJFRS5UZXh0dXJlOyAvLyBfVXZBbmltTWFza1RleHR1cmVcbiAgdXZBbmltU2Nyb2xsWD86IG51bWJlcjsgLy8gX1V2QW5pbVNjcm9sbFhcbiAgdXZBbmltU2Nyb2xsWT86IG51bWJlcjsgLy8gX1V2QW5pbVNjcm9sbFlcbiAgdXZBbmltUm90YXRpb24/OiBudW1iZXI7IC8vIF91dkFuaW1Sb3RhdGlvblxuXG4gIGRlYnVnTW9kZT86IE1Ub29uTWF0ZXJpYWxEZWJ1Z01vZGUgfCBudW1iZXI7IC8vIF9EZWJ1Z01vZGVcbiAgYmxlbmRNb2RlPzogTVRvb25NYXRlcmlhbFJlbmRlck1vZGUgfCBudW1iZXI7IC8vIF9CbGVuZE1vZGVcbiAgb3V0bGluZVdpZHRoTW9kZT86IE1Ub29uTWF0ZXJpYWxPdXRsaW5lV2lkdGhNb2RlIHwgbnVtYmVyOyAvLyBPdXRsaW5lV2lkdGhNb2RlXG4gIG91dGxpbmVDb2xvck1vZGU/OiBNVG9vbk1hdGVyaWFsT3V0bGluZUNvbG9yTW9kZSB8IG51bWJlcjsgLy8gT3V0bGluZUNvbG9yTW9kZVxuICBjdWxsTW9kZT86IE1Ub29uTWF0ZXJpYWxDdWxsTW9kZSB8IG51bWJlcjsgLy8gX0N1bGxNb2RlXG4gIG91dGxpbmVDdWxsTW9kZT86IE1Ub29uTWF0ZXJpYWxDdWxsTW9kZSB8IG51bWJlcjsgLy8gX091dGxpbmVDdWxsTW9kZVxuICBzcmNCbGVuZD86IG51bWJlcjsgLy8gX1NyY0JsZW5kXG4gIGRzdEJsZW5kPzogbnVtYmVyOyAvLyBfRHN0QmxlbmRcbiAgeldyaXRlPzogbnVtYmVyOyAvLyBfWldyaXRlICh3aWxsIGJlIHJlbmFtZWQgdG8gZGVwdGhXcml0ZSlcblxuICBpc091dGxpbmU/OiBib29sZWFuO1xufVxuXG5leHBvcnQgZW51bSBNVG9vbk1hdGVyaWFsQ3VsbE1vZGUge1xuICBPZmYsXG4gIEZyb250LFxuICBCYWNrLFxufVxuXG5leHBvcnQgZW51bSBNVG9vbk1hdGVyaWFsRGVidWdNb2RlIHtcbiAgTm9uZSxcbiAgTm9ybWFsLFxuICBMaXRTaGFkZVJhdGUsXG4gIFVWLFxufVxuXG5leHBvcnQgZW51bSBNVG9vbk1hdGVyaWFsT3V0bGluZUNvbG9yTW9kZSB7XG4gIEZpeGVkQ29sb3IsXG4gIE1peGVkTGlnaHRpbmcsXG59XG5cbmV4cG9ydCBlbnVtIE1Ub29uTWF0ZXJpYWxPdXRsaW5lV2lkdGhNb2RlIHtcbiAgTm9uZSxcbiAgV29ybGRDb29yZGluYXRlcyxcbiAgU2NyZWVuQ29vcmRpbmF0ZXMsXG59XG5cbmV4cG9ydCBlbnVtIE1Ub29uTWF0ZXJpYWxSZW5kZXJNb2RlIHtcbiAgT3BhcXVlLFxuICBDdXRvdXQsXG4gIFRyYW5zcGFyZW50LFxuICBUcmFuc3BhcmVudFdpdGhaV3JpdGUsXG59XG5cbi8qKlxuICogTVRvb24gaXMgYSBtYXRlcmlhbCBzcGVjaWZpY2F0aW9uIHRoYXQgaGFzIHZhcmlvdXMgZmVhdHVyZXMuXG4gKiBUaGUgc3BlYyBhbmQgaW1wbGVtZW50YXRpb24gYXJlIG9yaWdpbmFsbHkgZm91bmRlZCBmb3IgVW5pdHkgZW5naW5lIGFuZCB0aGlzIGlzIGEgcG9ydCBvZiB0aGUgbWF0ZXJpYWwuXG4gKlxuICogU2VlOiBodHRwczovL2dpdGh1Yi5jb20vU2FudGFyaC9NVG9vblxuICovXG5leHBvcnQgY2xhc3MgTVRvb25NYXRlcmlhbCBleHRlbmRzIFRIUkVFLlNoYWRlck1hdGVyaWFsIHtcbiAgLyoqXG4gICAqIFJlYWRvbmx5IGJvb2xlYW4gdGhhdCBpbmRpY2F0ZXMgdGhpcyBpcyBhIFtbTVRvb25NYXRlcmlhbF1dLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGlzTVRvb25NYXRlcmlhbDogYm9vbGVhbiA9IHRydWU7XG5cbiAgcHVibGljIGN1dG9mZiA9IDAuNTsgLy8gX0N1dG9mZlxuICBwdWJsaWMgY29sb3I6IFRIUkVFLlZlY3RvcjQgPSBuZXcgVEhSRUUuVmVjdG9yNCgxLjAsIDEuMCwgMS4wLCAxLjApOyAvLyBfQ29sb3JcbiAgcHVibGljIHNoYWRlQ29sb3I6IFRIUkVFLlZlY3RvcjQgPSBuZXcgVEhSRUUuVmVjdG9yNCgwLjk3LCAwLjgxLCAwLjg2LCAxLjApOyAvLyBfU2hhZGVDb2xvclxuICBwdWJsaWMgbWFwOiBUSFJFRS5UZXh0dXJlIHwgbnVsbCA9IG51bGw7IC8vIF9NYWluVGV4XG4gIHB1YmxpYyBtYWluVGV4X1NUOiBUSFJFRS5WZWN0b3I0ID0gbmV3IFRIUkVFLlZlY3RvcjQoMC4wLCAwLjAsIDEuMCwgMS4wKTsgLy8gX01haW5UZXhfU1RcbiAgcHVibGljIHNoYWRlVGV4dHVyZTogVEhSRUUuVGV4dHVyZSB8IG51bGwgPSBudWxsOyAvLyBfU2hhZGVUZXh0dXJlXG4gIC8vIHB1YmxpYyBzaGFkZVRleHR1cmVfU1Q6IFRIUkVFLlZlY3RvcjQgPSBuZXcgVEhSRUUuVmVjdG9yNCgwLjAsIDAuMCwgMS4wLCAxLjApOyAvLyBfU2hhZGVUZXh0dXJlX1NUICh1bnVzZWQpXG4gIHB1YmxpYyBidW1wU2NhbGUgPSAxLjA7IC8vIF9CdW1wU2NhbGVcbiAgcHVibGljIG5vcm1hbE1hcDogVEhSRUUuVGV4dHVyZSB8IG51bGwgPSBudWxsOyAvLyBfQnVtcE1hcC4gYWdhaW4sIFRISVMgSVMgX0J1bXBNYXBcbiAgLy8gcHVibGljIGJ1bXBNYXBfU1Q6IFRIUkVFLlZlY3RvcjQgPSBuZXcgVEhSRUUuVmVjdG9yNCgwLjAsIDAuMCwgMS4wLCAxLjApOyAvLyBfQnVtcE1hcF9TVCAodW51c2VkKVxuICBwdWJsaWMgcmVjZWl2ZVNoYWRvd1JhdGUgPSAxLjA7IC8vIF9SZWNlaXZlU2hhZG93UmF0ZVxuICBwdWJsaWMgcmVjZWl2ZVNoYWRvd1RleHR1cmU6IFRIUkVFLlRleHR1cmUgfCBudWxsID0gbnVsbDsgLy8gX1JlY2VpdmVTaGFkb3dUZXh0dXJlXG4gIC8vIHB1YmxpYyByZWNlaXZlU2hhZG93VGV4dHVyZV9TVDogVEhSRUUuVmVjdG9yNCA9IG5ldyBUSFJFRS5WZWN0b3I0KDAuMCwgMC4wLCAxLjAsIDEuMCk7IC8vIF9SZWNlaXZlU2hhZG93VGV4dHVyZV9TVCAodW51c2VkKVxuICBwdWJsaWMgc2hhZGluZ0dyYWRlUmF0ZSA9IDEuMDsgLy8gX1NoYWRpbmdHcmFkZVJhdGVcbiAgcHVibGljIHNoYWRpbmdHcmFkZVRleHR1cmU6IFRIUkVFLlRleHR1cmUgfCBudWxsID0gbnVsbDsgLy8gX1NoYWRpbmdHcmFkZVRleHR1cmVcbiAgLy8gcHVibGljIHNoYWRpbmdHcmFkZVRleHR1cmVfU1Q6IFRIUkVFLlZlY3RvcjQgPSBuZXcgVEhSRUUuVmVjdG9yNCgwLjAsIDAuMCwgMS4wLCAxLjApOyAvLyBfU2hhZGluZ0dyYWRlVGV4dHVyZV9TVCAodW51c2VkKVxuICBwdWJsaWMgc2hhZGVTaGlmdCA9IDAuMDsgLy8gX1NoYWRlU2hpZnRcbiAgcHVibGljIHNoYWRlVG9vbnkgPSAwLjk7IC8vIF9TaGFkZVRvb255XG4gIHB1YmxpYyBsaWdodENvbG9yQXR0ZW51YXRpb24gPSAwLjA7IC8vIF9MaWdodENvbG9yQXR0ZW51YXRpb25cbiAgcHVibGljIGluZGlyZWN0TGlnaHRJbnRlbnNpdHkgPSAwLjE7IC8vIF9JbmRpcmVjdExpZ2h0SW50ZW5zaXR5XG4gIHB1YmxpYyByaW1UZXh0dXJlOiBUSFJFRS5UZXh0dXJlIHwgbnVsbCA9IG51bGw7IC8vIF9SaW1UZXh0dXJlXG4gIHB1YmxpYyByaW1Db2xvcjogVEhSRUUuVmVjdG9yNCA9IG5ldyBUSFJFRS5WZWN0b3I0KDAuMCwgMC4wLCAwLjAsIDEuMCk7IC8vIF9SaW1Db2xvclxuICBwdWJsaWMgcmltTGlnaHRpbmdNaXggPSAwLjA7IC8vIF9SaW1MaWdodGluZ01peFxuICBwdWJsaWMgcmltRnJlc25lbFBvd2VyID0gMS4wOyAvLyBfUmltRnJlc25lbFBvd2VyXG4gIHB1YmxpYyByaW1MaWZ0ID0gMC4wOyAvLyBfUmltTGlmdFxuICBwdWJsaWMgc3BoZXJlQWRkOiBUSFJFRS5UZXh0dXJlIHwgbnVsbCA9IG51bGw7IC8vIF9TcGhlcmVBZGRcbiAgLy8gcHVibGljIHNwaGVyZUFkZF9TVDogVEhSRUUuVmVjdG9yNCA9IG5ldyBUSFJFRS5WZWN0b3I0KDAuMCwgMC4wLCAxLjAsIDEuMCk7IC8vIF9TcGhlcmVBZGRfU1QgKHVudXNlZClcbiAgcHVibGljIGVtaXNzaW9uQ29sb3I6IFRIUkVFLlZlY3RvcjQgPSBuZXcgVEhSRUUuVmVjdG9yNCgwLjAsIDAuMCwgMC4wLCAxLjApOyAvLyBfRW1pc3Npb25Db2xvclxuICBwdWJsaWMgZW1pc3NpdmVNYXA6IFRIUkVFLlRleHR1cmUgfCBudWxsID0gbnVsbDsgLy8gX0VtaXNzaW9uTWFwXG4gIC8vIHB1YmxpYyBlbWlzc2lvbk1hcF9TVDogVEhSRUUuVmVjdG9yNCA9IG5ldyBUSFJFRS5WZWN0b3I0KDAuMCwgMC4wLCAxLjAsIDEuMCk7IC8vIF9FbWlzc2lvbk1hcF9TVCAodW51c2VkKVxuICBwdWJsaWMgb3V0bGluZVdpZHRoVGV4dHVyZTogVEhSRUUuVGV4dHVyZSB8IG51bGwgPSBudWxsOyAvLyBfT3V0bGluZVdpZHRoVGV4dHVyZVxuICAvLyBwdWJsaWMgb3V0bGluZVdpZHRoVGV4dHVyZV9TVDogVEhSRUUuVmVjdG9yNCA9IG5ldyBUSFJFRS5WZWN0b3I0KDAuMCwgMC4wLCAxLjAsIDEuMCk7IC8vIF9PdXRsaW5lV2lkdGhUZXh0dXJlX1NUICh1bnVzZWQpXG4gIHB1YmxpYyBvdXRsaW5lV2lkdGggPSAwLjU7IC8vIF9PdXRsaW5lV2lkdGhcbiAgcHVibGljIG91dGxpbmVTY2FsZWRNYXhEaXN0YW5jZSA9IDEuMDsgLy8gX091dGxpbmVTY2FsZWRNYXhEaXN0YW5jZVxuICBwdWJsaWMgb3V0bGluZUNvbG9yOiBUSFJFRS5WZWN0b3I0ID0gbmV3IFRIUkVFLlZlY3RvcjQoMC4wLCAwLjAsIDAuMCwgMS4wKTsgLy8gX091dGxpbmVDb2xvclxuICBwdWJsaWMgb3V0bGluZUxpZ2h0aW5nTWl4ID0gMS4wOyAvLyBfT3V0bGluZUxpZ2h0aW5nTWl4XG4gIHB1YmxpYyB1dkFuaW1NYXNrVGV4dHVyZTogVEhSRUUuVGV4dHVyZSB8IG51bGwgPSBudWxsOyAvLyBfVXZBbmltTWFza1RleHR1cmVcbiAgcHVibGljIHV2QW5pbVNjcm9sbFggPSAwLjA7IC8vIF9VdkFuaW1TY3JvbGxYXG4gIHB1YmxpYyB1dkFuaW1TY3JvbGxZID0gMC4wOyAvLyBfVXZBbmltU2Nyb2xsWVxuICBwdWJsaWMgdXZBbmltUm90YXRpb24gPSAwLjA7IC8vIF91dkFuaW1Sb3RhdGlvblxuXG4gIHB1YmxpYyBzaG91bGRBcHBseVVuaWZvcm1zID0gdHJ1ZTsgLy8gd2hlbiB0aGlzIGlzIHRydWUsIGFwcGx5VW5pZm9ybXMgZWZmZWN0c1xuXG4gIHByaXZhdGUgX2RlYnVnTW9kZTogTVRvb25NYXRlcmlhbERlYnVnTW9kZSA9IE1Ub29uTWF0ZXJpYWxEZWJ1Z01vZGUuTm9uZTsgLy8gX0RlYnVnTW9kZVxuICBwcml2YXRlIF9ibGVuZE1vZGU6IE1Ub29uTWF0ZXJpYWxSZW5kZXJNb2RlID0gTVRvb25NYXRlcmlhbFJlbmRlck1vZGUuT3BhcXVlOyAvLyBfQmxlbmRNb2RlXG4gIHByaXZhdGUgX291dGxpbmVXaWR0aE1vZGU6IE1Ub29uTWF0ZXJpYWxPdXRsaW5lV2lkdGhNb2RlID0gTVRvb25NYXRlcmlhbE91dGxpbmVXaWR0aE1vZGUuTm9uZTsgLy8gX091dGxpbmVXaWR0aE1vZGVcbiAgcHJpdmF0ZSBfb3V0bGluZUNvbG9yTW9kZTogTVRvb25NYXRlcmlhbE91dGxpbmVDb2xvck1vZGUgPSBNVG9vbk1hdGVyaWFsT3V0bGluZUNvbG9yTW9kZS5GaXhlZENvbG9yOyAvLyBfT3V0bGluZUNvbG9yTW9kZVxuICBwcml2YXRlIF9jdWxsTW9kZTogTVRvb25NYXRlcmlhbEN1bGxNb2RlID0gTVRvb25NYXRlcmlhbEN1bGxNb2RlLkJhY2s7IC8vIF9DdWxsTW9kZVxuICBwcml2YXRlIF9vdXRsaW5lQ3VsbE1vZGU6IE1Ub29uTWF0ZXJpYWxDdWxsTW9kZSA9IE1Ub29uTWF0ZXJpYWxDdWxsTW9kZS5Gcm9udDsgLy8gX091dGxpbmVDdWxsTW9kZVxuICAvLyBwdWJsaWMgc3JjQmxlbmQ6IG51bWJlciA9IDEuMDsgLy8gX1NyY0JsZW5kIChpcyBub3Qgc3VwcG9ydGVkKVxuICAvLyBwdWJsaWMgZHN0QmxlbmQ6IG51bWJlciA9IDAuMDsgLy8gX0RzdEJsZW5kIChpcyBub3Qgc3VwcG9ydGVkKVxuICAvLyBwdWJsaWMgeldyaXRlOiBudW1iZXIgPSAxLjA7IC8vIF9aV3JpdGUgKHdpbGwgYmUgY29udmVydGVkIHRvIGRlcHRoV3JpdGUpXG5cbiAgcHJpdmF0ZSBfaXNPdXRsaW5lID0gZmFsc2U7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBfY29sb3JTcGFjZUdhbW1hOiBib29sZWFuO1xuXG4gIHByaXZhdGUgX3V2QW5pbU9mZnNldFggPSAwLjA7XG4gIHByaXZhdGUgX3V2QW5pbU9mZnNldFkgPSAwLjA7XG4gIHByaXZhdGUgX3V2QW5pbVBoYXNlID0gMC4wO1xuXG4gIC8vIFRPRE86IOOBk+OBk+OBq2NvbG9yU3BhY2VHYW1tYeOBguOCi+OBruODgOOCteOBhFxuICBjb25zdHJ1Y3Rvcihjb2xvclNwYWNlR2FtbWE6IGJvb2xlYW4sIHBhcmFtZXRlcnM/OiBNVG9vblBhcmFtZXRlcnMpIHtcbiAgICBzdXBlcigpO1xuXG4gICAgdGhpcy5fY29sb3JTcGFjZUdhbW1hID0gY29sb3JTcGFjZUdhbW1hO1xuXG4gICAgaWYgKHBhcmFtZXRlcnMgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcGFyYW1ldGVycyA9IHt9O1xuICAgIH1cblxuICAgIC8vID09IHRoZXNlIHBhcmFtZXRlciBoYXMgbm8gY29tcGF0aWJpbGl0eSB3aXRoIHRoaXMgaW1wbGVtZW50YXRpb24gPT09PT09PT1cbiAgICBbXG4gICAgICAnbVRvb25WZXJzaW9uJyxcbiAgICAgICdzaGFkZVRleHR1cmVfU1QnLFxuICAgICAgJ2J1bXBNYXBfU1QnLFxuICAgICAgJ3JlY2VpdmVTaGFkb3dUZXh0dXJlX1NUJyxcbiAgICAgICdzaGFkaW5nR3JhZGVUZXh0dXJlX1NUJyxcbiAgICAgICdzcGhlcmVBZGRfU1QnLFxuICAgICAgJ2VtaXNzaW9uTWFwX1NUJyxcbiAgICAgICdvdXRsaW5lV2lkdGhUZXh0dXJlX1NUJyxcbiAgICAgICdzcmNCbGVuZCcsXG4gICAgICAnZHN0QmxlbmQnLFxuICAgIF0uZm9yRWFjaCgoa2V5KSA9PiB7XG4gICAgICBpZiAoKHBhcmFtZXRlcnMgYXMgYW55KVtrZXldICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgLy8gY29uc29sZS53YXJuKGBUSFJFRS4ke3RoaXMudHlwZX06IFRoZSBwYXJhbWV0ZXIgXCIke2tleX1cIiBpcyBub3Qgc3VwcG9ydGVkLmApO1xuICAgICAgICBkZWxldGUgKHBhcmFtZXRlcnMgYXMgYW55KVtrZXldO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy8gPT0gZW5hYmxpbmcgYnVuY2ggb2Ygc3R1ZmYgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIHBhcmFtZXRlcnMuZm9nID0gdHJ1ZTtcbiAgICBwYXJhbWV0ZXJzLmxpZ2h0cyA9IHRydWU7XG4gICAgcGFyYW1ldGVycy5jbGlwcGluZyA9IHRydWU7XG5cbiAgICBwYXJhbWV0ZXJzLnNraW5uaW5nID0gcGFyYW1ldGVycy5za2lubmluZyB8fCBmYWxzZTtcbiAgICBwYXJhbWV0ZXJzLm1vcnBoVGFyZ2V0cyA9IHBhcmFtZXRlcnMubW9ycGhUYXJnZXRzIHx8IGZhbHNlO1xuICAgIHBhcmFtZXRlcnMubW9ycGhOb3JtYWxzID0gcGFyYW1ldGVycy5tb3JwaE5vcm1hbHMgfHwgZmFsc2U7XG5cbiAgICAvLyA9PSB1bmlmb3JtcyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgcGFyYW1ldGVycy51bmlmb3JtcyA9IFRIUkVFLlVuaWZvcm1zVXRpbHMubWVyZ2UoW1xuICAgICAgVEhSRUUuVW5pZm9ybXNMaWIuY29tbW9uLCAvLyBtYXBcbiAgICAgIFRIUkVFLlVuaWZvcm1zTGliLm5vcm1hbG1hcCwgLy8gbm9ybWFsTWFwXG4gICAgICBUSFJFRS5Vbmlmb3Jtc0xpYi5lbWlzc2l2ZW1hcCwgLy8gZW1pc3NpdmVNYXBcbiAgICAgIFRIUkVFLlVuaWZvcm1zTGliLmZvZyxcbiAgICAgIFRIUkVFLlVuaWZvcm1zTGliLmxpZ2h0cyxcbiAgICAgIHtcbiAgICAgICAgY3V0b2ZmOiB7IHZhbHVlOiAwLjUgfSxcbiAgICAgICAgY29sb3I6IHsgdmFsdWU6IG5ldyBUSFJFRS5Db2xvcigxLjAsIDEuMCwgMS4wKSB9LFxuICAgICAgICBjb2xvckFscGhhOiB7IHZhbHVlOiAxLjAgfSxcbiAgICAgICAgc2hhZGVDb2xvcjogeyB2YWx1ZTogbmV3IFRIUkVFLkNvbG9yKDAuOTcsIDAuODEsIDAuODYpIH0sXG4gICAgICAgIG1haW5UZXhfU1Q6IHsgdmFsdWU6IG5ldyBUSFJFRS5WZWN0b3I0KDAuMCwgMC4wLCAxLjAsIDEuMCkgfSxcbiAgICAgICAgc2hhZGVUZXh0dXJlOiB7IHZhbHVlOiBudWxsIH0sXG4gICAgICAgIGJ1bXBTY2FsZTogeyB2YWx1ZTogMS4wIH0sXG4gICAgICAgIHJlY2VpdmVTaGFkb3dSYXRlOiB7IHZhbHVlOiAxLjAgfSxcbiAgICAgICAgcmVjZWl2ZVNoYWRvd1RleHR1cmU6IHsgdmFsdWU6IG51bGwgfSxcbiAgICAgICAgc2hhZGluZ0dyYWRlUmF0ZTogeyB2YWx1ZTogMS4wIH0sXG4gICAgICAgIHNoYWRpbmdHcmFkZVRleHR1cmU6IHsgdmFsdWU6IG51bGwgfSxcbiAgICAgICAgc2hhZGVTaGlmdDogeyB2YWx1ZTogMC4wIH0sXG4gICAgICAgIHNoYWRlVG9vbnk6IHsgdmFsdWU6IDAuOSB9LFxuICAgICAgICBsaWdodENvbG9yQXR0ZW51YXRpb246IHsgdmFsdWU6IDAuMCB9LFxuICAgICAgICBpbmRpcmVjdExpZ2h0SW50ZW5zaXR5OiB7IHZhbHVlOiAwLjEgfSxcbiAgICAgICAgcmltVGV4dHVyZTogeyB2YWx1ZTogbnVsbCB9LFxuICAgICAgICByaW1Db2xvcjogeyB2YWx1ZTogbmV3IFRIUkVFLkNvbG9yKDAuMCwgMC4wLCAwLjApIH0sXG4gICAgICAgIHJpbUxpZ2h0aW5nTWl4OiB7IHZhbHVlOiAwLjAgfSxcbiAgICAgICAgcmltRnJlc25lbFBvd2VyOiB7IHZhbHVlOiAxLjAgfSxcbiAgICAgICAgcmltTGlmdDogeyB2YWx1ZTogMC4wIH0sXG4gICAgICAgIHNwaGVyZUFkZDogeyB2YWx1ZTogbnVsbCB9LFxuICAgICAgICBlbWlzc2lvbkNvbG9yOiB7IHZhbHVlOiBuZXcgVEhSRUUuQ29sb3IoMC4wLCAwLjAsIDAuMCkgfSxcbiAgICAgICAgb3V0bGluZVdpZHRoVGV4dHVyZTogeyB2YWx1ZTogbnVsbCB9LFxuICAgICAgICBvdXRsaW5lV2lkdGg6IHsgdmFsdWU6IDAuNSB9LFxuICAgICAgICBvdXRsaW5lU2NhbGVkTWF4RGlzdGFuY2U6IHsgdmFsdWU6IDEuMCB9LFxuICAgICAgICBvdXRsaW5lQ29sb3I6IHsgdmFsdWU6IG5ldyBUSFJFRS5Db2xvcigwLjAsIDAuMCwgMC4wKSB9LFxuICAgICAgICBvdXRsaW5lTGlnaHRpbmdNaXg6IHsgdmFsdWU6IDEuMCB9LFxuICAgICAgICB1dkFuaW1NYXNrVGV4dHVyZTogeyB2YWx1ZTogbnVsbCB9LFxuICAgICAgICB1dkFuaW1PZmZzZXRYOiB7IHZhbHVlOiAwLjAgfSxcbiAgICAgICAgdXZBbmltT2Zmc2V0WTogeyB2YWx1ZTogMC4wIH0sXG4gICAgICAgIHV2QW5pbVRoZXRhOiB7IHZhbHVlOiAwLjAgfSxcbiAgICAgIH0sXG4gICAgXSk7XG5cbiAgICAvLyA9PSBmaW5hbGx5IGNvbXBpbGUgdGhlIHNoYWRlciBwcm9ncmFtID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgdGhpcy5zZXRWYWx1ZXMocGFyYW1ldGVycyk7XG5cbiAgICAvLyA9PSB1cGRhdGUgc2hhZGVyIHN0dWZmID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgdGhpcy5fdXBkYXRlU2hhZGVyQ29kZSgpO1xuICAgIHRoaXMuX2FwcGx5VW5pZm9ybXMoKTtcbiAgfVxuXG4gIGdldCBtYWluVGV4KCk6IFRIUkVFLlRleHR1cmUgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5tYXA7XG4gIH1cblxuICBzZXQgbWFpblRleCh0OiBUSFJFRS5UZXh0dXJlIHwgbnVsbCkge1xuICAgIHRoaXMubWFwID0gdDtcbiAgfVxuXG4gIGdldCBidW1wTWFwKCk6IFRIUkVFLlRleHR1cmUgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5ub3JtYWxNYXA7XG4gIH1cblxuICBzZXQgYnVtcE1hcCh0OiBUSFJFRS5UZXh0dXJlIHwgbnVsbCkge1xuICAgIHRoaXMubm9ybWFsTWFwID0gdDtcbiAgfVxuXG4gIGdldCBlbWlzc2lvbk1hcCgpOiBUSFJFRS5UZXh0dXJlIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuZW1pc3NpdmVNYXA7XG4gIH1cblxuICBzZXQgZW1pc3Npb25NYXAodDogVEhSRUUuVGV4dHVyZSB8IG51bGwpIHtcbiAgICB0aGlzLmVtaXNzaXZlTWFwID0gdDtcbiAgfVxuXG4gIGdldCBibGVuZE1vZGUoKTogTVRvb25NYXRlcmlhbFJlbmRlck1vZGUge1xuICAgIHJldHVybiB0aGlzLl9ibGVuZE1vZGU7XG4gIH1cblxuICBzZXQgYmxlbmRNb2RlKG06IE1Ub29uTWF0ZXJpYWxSZW5kZXJNb2RlKSB7XG4gICAgdGhpcy5fYmxlbmRNb2RlID0gbTtcblxuICAgIHRoaXMuZGVwdGhXcml0ZSA9IHRoaXMuX2JsZW5kTW9kZSAhPT0gTVRvb25NYXRlcmlhbFJlbmRlck1vZGUuVHJhbnNwYXJlbnQ7XG4gICAgdGhpcy50cmFuc3BhcmVudCA9XG4gICAgICB0aGlzLl9ibGVuZE1vZGUgPT09IE1Ub29uTWF0ZXJpYWxSZW5kZXJNb2RlLlRyYW5zcGFyZW50IHx8XG4gICAgICB0aGlzLl9ibGVuZE1vZGUgPT09IE1Ub29uTWF0ZXJpYWxSZW5kZXJNb2RlLlRyYW5zcGFyZW50V2l0aFpXcml0ZTtcbiAgICB0aGlzLl91cGRhdGVTaGFkZXJDb2RlKCk7XG4gIH1cblxuICBnZXQgZGVidWdNb2RlKCk6IE1Ub29uTWF0ZXJpYWxEZWJ1Z01vZGUge1xuICAgIHJldHVybiB0aGlzLl9kZWJ1Z01vZGU7XG4gIH1cblxuICBzZXQgZGVidWdNb2RlKG06IE1Ub29uTWF0ZXJpYWxEZWJ1Z01vZGUpIHtcbiAgICB0aGlzLl9kZWJ1Z01vZGUgPSBtO1xuXG4gICAgdGhpcy5fdXBkYXRlU2hhZGVyQ29kZSgpO1xuICB9XG5cbiAgZ2V0IG91dGxpbmVXaWR0aE1vZGUoKTogTVRvb25NYXRlcmlhbE91dGxpbmVXaWR0aE1vZGUge1xuICAgIHJldHVybiB0aGlzLl9vdXRsaW5lV2lkdGhNb2RlO1xuICB9XG5cbiAgc2V0IG91dGxpbmVXaWR0aE1vZGUobTogTVRvb25NYXRlcmlhbE91dGxpbmVXaWR0aE1vZGUpIHtcbiAgICB0aGlzLl9vdXRsaW5lV2lkdGhNb2RlID0gbTtcblxuICAgIHRoaXMuX3VwZGF0ZVNoYWRlckNvZGUoKTtcbiAgfVxuXG4gIGdldCBvdXRsaW5lQ29sb3JNb2RlKCk6IE1Ub29uTWF0ZXJpYWxPdXRsaW5lQ29sb3JNb2RlIHtcbiAgICByZXR1cm4gdGhpcy5fb3V0bGluZUNvbG9yTW9kZTtcbiAgfVxuXG4gIHNldCBvdXRsaW5lQ29sb3JNb2RlKG06IE1Ub29uTWF0ZXJpYWxPdXRsaW5lQ29sb3JNb2RlKSB7XG4gICAgdGhpcy5fb3V0bGluZUNvbG9yTW9kZSA9IG07XG5cbiAgICB0aGlzLl91cGRhdGVTaGFkZXJDb2RlKCk7XG4gIH1cblxuICBnZXQgY3VsbE1vZGUoKTogTVRvb25NYXRlcmlhbEN1bGxNb2RlIHtcbiAgICByZXR1cm4gdGhpcy5fY3VsbE1vZGU7XG4gIH1cblxuICBzZXQgY3VsbE1vZGUobTogTVRvb25NYXRlcmlhbEN1bGxNb2RlKSB7XG4gICAgdGhpcy5fY3VsbE1vZGUgPSBtO1xuXG4gICAgdGhpcy5fdXBkYXRlQ3VsbEZhY2UoKTtcbiAgfVxuXG4gIGdldCBvdXRsaW5lQ3VsbE1vZGUoKTogTVRvb25NYXRlcmlhbEN1bGxNb2RlIHtcbiAgICByZXR1cm4gdGhpcy5fb3V0bGluZUN1bGxNb2RlO1xuICB9XG5cbiAgc2V0IG91dGxpbmVDdWxsTW9kZShtOiBNVG9vbk1hdGVyaWFsQ3VsbE1vZGUpIHtcbiAgICB0aGlzLl9vdXRsaW5lQ3VsbE1vZGUgPSBtO1xuXG4gICAgdGhpcy5fdXBkYXRlQ3VsbEZhY2UoKTtcbiAgfVxuXG4gIGdldCB6V3JpdGUoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5kZXB0aFdyaXRlID8gMSA6IDA7XG4gIH1cblxuICBzZXQgeldyaXRlKGk6IG51bWJlcikge1xuICAgIHRoaXMuZGVwdGhXcml0ZSA9IDAuNSA8PSBpO1xuICB9XG5cbiAgZ2V0IGlzT3V0bGluZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5faXNPdXRsaW5lO1xuICB9XG5cbiAgc2V0IGlzT3V0bGluZShiOiBib29sZWFuKSB7XG4gICAgdGhpcy5faXNPdXRsaW5lID0gYjtcblxuICAgIHRoaXMuX3VwZGF0ZVNoYWRlckNvZGUoKTtcbiAgICB0aGlzLl91cGRhdGVDdWxsRmFjZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSB0aGlzIG1hdGVyaWFsLlxuICAgKiBVc3VhbGx5IHRoaXMgd2lsbCBiZSBjYWxsZWQgdmlhIFtbVlJNLnVwZGF0ZV1dIHNvIHlvdSBkb24ndCBoYXZlIHRvIGNhbGwgdGhpcyBtYW51YWxseS5cbiAgICpcbiAgICogQHBhcmFtIGRlbHRhIGRlbHRhVGltZSBzaW5jZSBsYXN0IHVwZGF0ZVxuICAgKi9cbiAgcHVibGljIHVwZGF0ZVZSTU1hdGVyaWFscyhkZWx0YTogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy5fdXZBbmltT2Zmc2V0WCA9IHRoaXMuX3V2QW5pbU9mZnNldFggKyBkZWx0YSAqIHRoaXMudXZBbmltU2Nyb2xsWDtcbiAgICB0aGlzLl91dkFuaW1PZmZzZXRZID0gdGhpcy5fdXZBbmltT2Zmc2V0WSArIGRlbHRhICogdGhpcy51dkFuaW1TY3JvbGxZO1xuICAgIHRoaXMuX3V2QW5pbVBoYXNlID0gdGhpcy5fdXZBbmltUGhhc2UgKyBkZWx0YSAqIHRoaXMudXZBbmltUm90YXRpb247XG5cbiAgICB0aGlzLl9hcHBseVVuaWZvcm1zKCk7XG4gIH1cblxuICBwdWJsaWMgY29weShzb3VyY2U6IHRoaXMpOiB0aGlzIHtcbiAgICBzdXBlci5jb3B5KHNvdXJjZSk7XG5cbiAgICAvLyA9PSBjb3B5IG1lbWJlcnMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgdGhpcy5jdXRvZmYgPSBzb3VyY2UuY3V0b2ZmO1xuICAgIHRoaXMuY29sb3IuY29weShzb3VyY2UuY29sb3IpO1xuICAgIHRoaXMuc2hhZGVDb2xvci5jb3B5KHNvdXJjZS5zaGFkZUNvbG9yKTtcbiAgICB0aGlzLm1hcCA9IHNvdXJjZS5tYXA7XG4gICAgdGhpcy5tYWluVGV4X1NULmNvcHkoc291cmNlLm1haW5UZXhfU1QpO1xuICAgIHRoaXMuc2hhZGVUZXh0dXJlID0gc291cmNlLnNoYWRlVGV4dHVyZTtcbiAgICB0aGlzLmJ1bXBTY2FsZSA9IHNvdXJjZS5idW1wU2NhbGU7XG4gICAgdGhpcy5ub3JtYWxNYXAgPSBzb3VyY2Uubm9ybWFsTWFwO1xuICAgIHRoaXMucmVjZWl2ZVNoYWRvd1JhdGUgPSBzb3VyY2UucmVjZWl2ZVNoYWRvd1JhdGU7XG4gICAgdGhpcy5yZWNlaXZlU2hhZG93VGV4dHVyZSA9IHNvdXJjZS5yZWNlaXZlU2hhZG93VGV4dHVyZTtcbiAgICB0aGlzLnNoYWRpbmdHcmFkZVJhdGUgPSBzb3VyY2Uuc2hhZGluZ0dyYWRlUmF0ZTtcbiAgICB0aGlzLnNoYWRpbmdHcmFkZVRleHR1cmUgPSBzb3VyY2Uuc2hhZGluZ0dyYWRlVGV4dHVyZTtcbiAgICB0aGlzLnNoYWRlU2hpZnQgPSBzb3VyY2Uuc2hhZGVTaGlmdDtcbiAgICB0aGlzLnNoYWRlVG9vbnkgPSBzb3VyY2Uuc2hhZGVUb29ueTtcbiAgICB0aGlzLmxpZ2h0Q29sb3JBdHRlbnVhdGlvbiA9IHNvdXJjZS5saWdodENvbG9yQXR0ZW51YXRpb247XG4gICAgdGhpcy5pbmRpcmVjdExpZ2h0SW50ZW5zaXR5ID0gc291cmNlLmluZGlyZWN0TGlnaHRJbnRlbnNpdHk7XG4gICAgdGhpcy5yaW1UZXh0dXJlID0gc291cmNlLnJpbVRleHR1cmU7XG4gICAgdGhpcy5yaW1Db2xvci5jb3B5KHNvdXJjZS5yaW1Db2xvcik7XG4gICAgdGhpcy5yaW1MaWdodGluZ01peCA9IHNvdXJjZS5yaW1MaWdodGluZ01peDtcbiAgICB0aGlzLnJpbUZyZXNuZWxQb3dlciA9IHNvdXJjZS5yaW1GcmVzbmVsUG93ZXI7XG4gICAgdGhpcy5yaW1MaWZ0ID0gc291cmNlLnJpbUxpZnQ7XG4gICAgdGhpcy5zcGhlcmVBZGQgPSBzb3VyY2Uuc3BoZXJlQWRkO1xuICAgIHRoaXMuZW1pc3Npb25Db2xvci5jb3B5KHNvdXJjZS5lbWlzc2lvbkNvbG9yKTtcbiAgICB0aGlzLmVtaXNzaXZlTWFwID0gc291cmNlLmVtaXNzaXZlTWFwO1xuICAgIHRoaXMub3V0bGluZVdpZHRoVGV4dHVyZSA9IHNvdXJjZS5vdXRsaW5lV2lkdGhUZXh0dXJlO1xuICAgIHRoaXMub3V0bGluZVdpZHRoID0gc291cmNlLm91dGxpbmVXaWR0aDtcbiAgICB0aGlzLm91dGxpbmVTY2FsZWRNYXhEaXN0YW5jZSA9IHNvdXJjZS5vdXRsaW5lU2NhbGVkTWF4RGlzdGFuY2U7XG4gICAgdGhpcy5vdXRsaW5lQ29sb3IuY29weShzb3VyY2Uub3V0bGluZUNvbG9yKTtcbiAgICB0aGlzLm91dGxpbmVMaWdodGluZ01peCA9IHNvdXJjZS5vdXRsaW5lTGlnaHRpbmdNaXg7XG4gICAgdGhpcy51dkFuaW1NYXNrVGV4dHVyZSA9IHNvdXJjZS51dkFuaW1NYXNrVGV4dHVyZTtcbiAgICB0aGlzLnV2QW5pbVNjcm9sbFggPSBzb3VyY2UudXZBbmltU2Nyb2xsWDtcbiAgICB0aGlzLnV2QW5pbVNjcm9sbFkgPSBzb3VyY2UudXZBbmltU2Nyb2xsWTtcbiAgICB0aGlzLnV2QW5pbVJvdGF0aW9uID0gc291cmNlLnV2QW5pbVJvdGF0aW9uO1xuXG4gICAgdGhpcy5kZWJ1Z01vZGUgPSBzb3VyY2UuZGVidWdNb2RlO1xuICAgIHRoaXMuYmxlbmRNb2RlID0gc291cmNlLmJsZW5kTW9kZTtcbiAgICB0aGlzLm91dGxpbmVXaWR0aE1vZGUgPSBzb3VyY2Uub3V0bGluZVdpZHRoTW9kZTtcbiAgICB0aGlzLm91dGxpbmVDb2xvck1vZGUgPSBzb3VyY2Uub3V0bGluZUNvbG9yTW9kZTtcbiAgICB0aGlzLmN1bGxNb2RlID0gc291cmNlLmN1bGxNb2RlO1xuICAgIHRoaXMub3V0bGluZUN1bGxNb2RlID0gc291cmNlLm91dGxpbmVDdWxsTW9kZTtcblxuICAgIHRoaXMuaXNPdXRsaW5lID0gc291cmNlLmlzT3V0bGluZTtcblxuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLyoqXG4gICAqIEFwcGx5IHVwZGF0ZWQgdW5pZm9ybSB2YXJpYWJsZXMuXG4gICAqL1xuICBwcml2YXRlIF9hcHBseVVuaWZvcm1zKCk6IHZvaWQge1xuICAgIHRoaXMudW5pZm9ybXMudXZBbmltT2Zmc2V0WC52YWx1ZSA9IHRoaXMuX3V2QW5pbU9mZnNldFg7XG4gICAgdGhpcy51bmlmb3Jtcy51dkFuaW1PZmZzZXRZLnZhbHVlID0gdGhpcy5fdXZBbmltT2Zmc2V0WTtcbiAgICB0aGlzLnVuaWZvcm1zLnV2QW5pbVRoZXRhLnZhbHVlID0gVEFVICogdGhpcy5fdXZBbmltUGhhc2U7XG5cbiAgICBpZiAoIXRoaXMuc2hvdWxkQXBwbHlVbmlmb3Jtcykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLnNob3VsZEFwcGx5VW5pZm9ybXMgPSBmYWxzZTtcblxuICAgIHRoaXMudW5pZm9ybXMuY3V0b2ZmLnZhbHVlID0gdGhpcy5jdXRvZmY7XG4gICAgdGhpcy51bmlmb3Jtcy5jb2xvci52YWx1ZS5zZXRSR0IodGhpcy5jb2xvci54LCB0aGlzLmNvbG9yLnksIHRoaXMuY29sb3Iueik7XG4gICAgaWYgKCF0aGlzLl9jb2xvclNwYWNlR2FtbWEpIHtcbiAgICAgIHRoaXMudW5pZm9ybXMuY29sb3IudmFsdWUuY29udmVydFNSR0JUb0xpbmVhcigpO1xuICAgIH1cbiAgICB0aGlzLnVuaWZvcm1zLmNvbG9yQWxwaGEudmFsdWUgPSB0aGlzLmNvbG9yLnc7XG4gICAgdGhpcy51bmlmb3Jtcy5zaGFkZUNvbG9yLnZhbHVlLnNldFJHQih0aGlzLnNoYWRlQ29sb3IueCwgdGhpcy5zaGFkZUNvbG9yLnksIHRoaXMuc2hhZGVDb2xvci56KTtcbiAgICBpZiAoIXRoaXMuX2NvbG9yU3BhY2VHYW1tYSkge1xuICAgICAgdGhpcy51bmlmb3Jtcy5zaGFkZUNvbG9yLnZhbHVlLmNvbnZlcnRTUkdCVG9MaW5lYXIoKTtcbiAgICB9XG4gICAgdGhpcy51bmlmb3Jtcy5tYXAudmFsdWUgPSB0aGlzLm1hcDtcbiAgICB0aGlzLnVuaWZvcm1zLm1haW5UZXhfU1QudmFsdWUuY29weSh0aGlzLm1haW5UZXhfU1QpO1xuICAgIHRoaXMudW5pZm9ybXMuc2hhZGVUZXh0dXJlLnZhbHVlID0gdGhpcy5zaGFkZVRleHR1cmU7XG4gICAgdGhpcy51bmlmb3Jtcy5idW1wU2NhbGUudmFsdWUgPSB0aGlzLmJ1bXBTY2FsZTtcbiAgICB0aGlzLnVuaWZvcm1zLm5vcm1hbE1hcC52YWx1ZSA9IHRoaXMubm9ybWFsTWFwO1xuICAgIHRoaXMudW5pZm9ybXMucmVjZWl2ZVNoYWRvd1JhdGUudmFsdWUgPSB0aGlzLnJlY2VpdmVTaGFkb3dSYXRlO1xuICAgIHRoaXMudW5pZm9ybXMucmVjZWl2ZVNoYWRvd1RleHR1cmUudmFsdWUgPSB0aGlzLnJlY2VpdmVTaGFkb3dUZXh0dXJlO1xuICAgIHRoaXMudW5pZm9ybXMuc2hhZGluZ0dyYWRlUmF0ZS52YWx1ZSA9IHRoaXMuc2hhZGluZ0dyYWRlUmF0ZTtcbiAgICB0aGlzLnVuaWZvcm1zLnNoYWRpbmdHcmFkZVRleHR1cmUudmFsdWUgPSB0aGlzLnNoYWRpbmdHcmFkZVRleHR1cmU7XG4gICAgdGhpcy51bmlmb3Jtcy5zaGFkZVNoaWZ0LnZhbHVlID0gdGhpcy5zaGFkZVNoaWZ0O1xuICAgIHRoaXMudW5pZm9ybXMuc2hhZGVUb29ueS52YWx1ZSA9IHRoaXMuc2hhZGVUb29ueTtcbiAgICB0aGlzLnVuaWZvcm1zLmxpZ2h0Q29sb3JBdHRlbnVhdGlvbi52YWx1ZSA9IHRoaXMubGlnaHRDb2xvckF0dGVudWF0aW9uO1xuICAgIHRoaXMudW5pZm9ybXMuaW5kaXJlY3RMaWdodEludGVuc2l0eS52YWx1ZSA9IHRoaXMuaW5kaXJlY3RMaWdodEludGVuc2l0eTtcbiAgICB0aGlzLnVuaWZvcm1zLnJpbVRleHR1cmUudmFsdWUgPSB0aGlzLnJpbVRleHR1cmU7XG4gICAgdGhpcy51bmlmb3Jtcy5yaW1Db2xvci52YWx1ZS5zZXRSR0IodGhpcy5yaW1Db2xvci54LCB0aGlzLnJpbUNvbG9yLnksIHRoaXMucmltQ29sb3Iueik7XG4gICAgaWYgKCF0aGlzLl9jb2xvclNwYWNlR2FtbWEpIHtcbiAgICAgIHRoaXMudW5pZm9ybXMucmltQ29sb3IudmFsdWUuY29udmVydFNSR0JUb0xpbmVhcigpO1xuICAgIH1cbiAgICB0aGlzLnVuaWZvcm1zLnJpbUxpZ2h0aW5nTWl4LnZhbHVlID0gdGhpcy5yaW1MaWdodGluZ01peDtcbiAgICB0aGlzLnVuaWZvcm1zLnJpbUZyZXNuZWxQb3dlci52YWx1ZSA9IHRoaXMucmltRnJlc25lbFBvd2VyO1xuICAgIHRoaXMudW5pZm9ybXMucmltTGlmdC52YWx1ZSA9IHRoaXMucmltTGlmdDtcbiAgICB0aGlzLnVuaWZvcm1zLnNwaGVyZUFkZC52YWx1ZSA9IHRoaXMuc3BoZXJlQWRkO1xuICAgIHRoaXMudW5pZm9ybXMuZW1pc3Npb25Db2xvci52YWx1ZS5zZXRSR0IodGhpcy5lbWlzc2lvbkNvbG9yLngsIHRoaXMuZW1pc3Npb25Db2xvci55LCB0aGlzLmVtaXNzaW9uQ29sb3Iueik7XG4gICAgaWYgKCF0aGlzLl9jb2xvclNwYWNlR2FtbWEpIHtcbiAgICAgIHRoaXMudW5pZm9ybXMuZW1pc3Npb25Db2xvci52YWx1ZS5jb252ZXJ0U1JHQlRvTGluZWFyKCk7XG4gICAgfVxuICAgIHRoaXMudW5pZm9ybXMuZW1pc3NpdmVNYXAudmFsdWUgPSB0aGlzLmVtaXNzaXZlTWFwO1xuICAgIHRoaXMudW5pZm9ybXMub3V0bGluZVdpZHRoVGV4dHVyZS52YWx1ZSA9IHRoaXMub3V0bGluZVdpZHRoVGV4dHVyZTtcbiAgICB0aGlzLnVuaWZvcm1zLm91dGxpbmVXaWR0aC52YWx1ZSA9IHRoaXMub3V0bGluZVdpZHRoO1xuICAgIHRoaXMudW5pZm9ybXMub3V0bGluZVNjYWxlZE1heERpc3RhbmNlLnZhbHVlID0gdGhpcy5vdXRsaW5lU2NhbGVkTWF4RGlzdGFuY2U7XG4gICAgdGhpcy51bmlmb3Jtcy5vdXRsaW5lQ29sb3IudmFsdWUuc2V0UkdCKHRoaXMub3V0bGluZUNvbG9yLngsIHRoaXMub3V0bGluZUNvbG9yLnksIHRoaXMub3V0bGluZUNvbG9yLnopO1xuICAgIGlmICghdGhpcy5fY29sb3JTcGFjZUdhbW1hKSB7XG4gICAgICB0aGlzLnVuaWZvcm1zLm91dGxpbmVDb2xvci52YWx1ZS5jb252ZXJ0U1JHQlRvTGluZWFyKCk7XG4gICAgfVxuICAgIHRoaXMudW5pZm9ybXMub3V0bGluZUxpZ2h0aW5nTWl4LnZhbHVlID0gdGhpcy5vdXRsaW5lTGlnaHRpbmdNaXg7XG4gICAgdGhpcy51bmlmb3Jtcy51dkFuaW1NYXNrVGV4dHVyZS52YWx1ZSA9IHRoaXMudXZBbmltTWFza1RleHR1cmU7XG5cbiAgICB0aGlzLl91cGRhdGVDdWxsRmFjZSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBfdXBkYXRlU2hhZGVyQ29kZSgpOiB2b2lkIHtcbiAgICB0aGlzLmRlZmluZXMgPSB7XG4gICAgICBPVVRMSU5FOiB0aGlzLl9pc091dGxpbmUsXG4gICAgICBCTEVORE1PREVfT1BBUVVFOiB0aGlzLl9ibGVuZE1vZGUgPT09IE1Ub29uTWF0ZXJpYWxSZW5kZXJNb2RlLk9wYXF1ZSxcbiAgICAgIEJMRU5ETU9ERV9DVVRPVVQ6IHRoaXMuX2JsZW5kTW9kZSA9PT0gTVRvb25NYXRlcmlhbFJlbmRlck1vZGUuQ3V0b3V0LFxuICAgICAgQkxFTkRNT0RFX1RSQU5TUEFSRU5UOlxuICAgICAgICB0aGlzLl9ibGVuZE1vZGUgPT09IE1Ub29uTWF0ZXJpYWxSZW5kZXJNb2RlLlRyYW5zcGFyZW50IHx8XG4gICAgICAgIHRoaXMuX2JsZW5kTW9kZSA9PT0gTVRvb25NYXRlcmlhbFJlbmRlck1vZGUuVHJhbnNwYXJlbnRXaXRoWldyaXRlLFxuICAgICAgVVNFX1NIQURFVEVYVFVSRTogdGhpcy5zaGFkZVRleHR1cmUgIT09IG51bGwsXG4gICAgICBVU0VfUkVDRUlWRVNIQURPV1RFWFRVUkU6IHRoaXMucmVjZWl2ZVNoYWRvd1RleHR1cmUgIT09IG51bGwsXG4gICAgICBVU0VfU0hBRElOR0dSQURFVEVYVFVSRTogdGhpcy5zaGFkaW5nR3JhZGVUZXh0dXJlICE9PSBudWxsLFxuICAgICAgVVNFX1JJTVRFWFRVUkU6IHRoaXMucmltVGV4dHVyZSAhPT0gbnVsbCxcbiAgICAgIFVTRV9TUEhFUkVBREQ6IHRoaXMuc3BoZXJlQWRkICE9PSBudWxsLFxuICAgICAgVVNFX09VVExJTkVXSURUSFRFWFRVUkU6IHRoaXMub3V0bGluZVdpZHRoVGV4dHVyZSAhPT0gbnVsbCxcbiAgICAgIFVTRV9VVkFOSU1NQVNLVEVYVFVSRTogdGhpcy51dkFuaW1NYXNrVGV4dHVyZSAhPT0gbnVsbCxcbiAgICAgIERFQlVHX05PUk1BTDogdGhpcy5fZGVidWdNb2RlID09PSBNVG9vbk1hdGVyaWFsRGVidWdNb2RlLk5vcm1hbCxcbiAgICAgIERFQlVHX0xJVFNIQURFUkFURTogdGhpcy5fZGVidWdNb2RlID09PSBNVG9vbk1hdGVyaWFsRGVidWdNb2RlLkxpdFNoYWRlUmF0ZSxcbiAgICAgIERFQlVHX1VWOiB0aGlzLl9kZWJ1Z01vZGUgPT09IE1Ub29uTWF0ZXJpYWxEZWJ1Z01vZGUuVVYsXG4gICAgICBPVVRMSU5FX1dJRFRIX1dPUkxEOiB0aGlzLl9vdXRsaW5lV2lkdGhNb2RlID09PSBNVG9vbk1hdGVyaWFsT3V0bGluZVdpZHRoTW9kZS5Xb3JsZENvb3JkaW5hdGVzLFxuICAgICAgT1VUTElORV9XSURUSF9TQ1JFRU46IHRoaXMuX291dGxpbmVXaWR0aE1vZGUgPT09IE1Ub29uTWF0ZXJpYWxPdXRsaW5lV2lkdGhNb2RlLlNjcmVlbkNvb3JkaW5hdGVzLFxuICAgICAgT1VUTElORV9DT0xPUl9GSVhFRDogdGhpcy5fb3V0bGluZUNvbG9yTW9kZSA9PT0gTVRvb25NYXRlcmlhbE91dGxpbmVDb2xvck1vZGUuRml4ZWRDb2xvcixcbiAgICAgIE9VVExJTkVfQ09MT1JfTUlYRUQ6IHRoaXMuX291dGxpbmVDb2xvck1vZGUgPT09IE1Ub29uTWF0ZXJpYWxPdXRsaW5lQ29sb3JNb2RlLk1peGVkTGlnaHRpbmcsXG4gICAgfTtcblxuICAgIC8vID09IHRleHR1cmUgZW5jb2RpbmdzID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICBjb25zdCBlbmNvZGluZ3MgPVxuICAgICAgKHRoaXMuc2hhZGVUZXh0dXJlICE9PSBudWxsXG4gICAgICAgID8gZ2V0VGV4ZWxEZWNvZGluZ0Z1bmN0aW9uKCdzaGFkZVRleHR1cmVUZXhlbFRvTGluZWFyJywgdGhpcy5zaGFkZVRleHR1cmUuZW5jb2RpbmcpICsgJ1xcbidcbiAgICAgICAgOiAnJykgK1xuICAgICAgKHRoaXMuc3BoZXJlQWRkICE9PSBudWxsXG4gICAgICAgID8gZ2V0VGV4ZWxEZWNvZGluZ0Z1bmN0aW9uKCdzcGhlcmVBZGRUZXhlbFRvTGluZWFyJywgdGhpcy5zcGhlcmVBZGQuZW5jb2RpbmcpICsgJ1xcbidcbiAgICAgICAgOiAnJyk7XG5cbiAgICAvLyA9PSBnZW5lcmF0ZSBzaGFkZXIgY29kZSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgdGhpcy52ZXJ0ZXhTaGFkZXIgPSB2ZXJ0ZXhTaGFkZXI7XG4gICAgdGhpcy5mcmFnbWVudFNoYWRlciA9IGVuY29kaW5ncyArIGZyYWdtZW50U2hhZGVyO1xuXG4gICAgLy8gPT0gc2V0IG5lZWRzVXBkYXRlIGZsYWcgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIHRoaXMubmVlZHNVcGRhdGUgPSB0cnVlO1xuICB9XG5cbiAgcHJpdmF0ZSBfdXBkYXRlQ3VsbEZhY2UoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmlzT3V0bGluZSkge1xuICAgICAgaWYgKHRoaXMuY3VsbE1vZGUgPT09IE1Ub29uTWF0ZXJpYWxDdWxsTW9kZS5PZmYpIHtcbiAgICAgICAgdGhpcy5zaWRlID0gVEhSRUUuRG91YmxlU2lkZTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5jdWxsTW9kZSA9PT0gTVRvb25NYXRlcmlhbEN1bGxNb2RlLkZyb250KSB7XG4gICAgICAgIHRoaXMuc2lkZSA9IFRIUkVFLkJhY2tTaWRlO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLmN1bGxNb2RlID09PSBNVG9vbk1hdGVyaWFsQ3VsbE1vZGUuQmFjaykge1xuICAgICAgICB0aGlzLnNpZGUgPSBUSFJFRS5Gcm9udFNpZGU7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmICh0aGlzLm91dGxpbmVDdWxsTW9kZSA9PT0gTVRvb25NYXRlcmlhbEN1bGxNb2RlLk9mZikge1xuICAgICAgICB0aGlzLnNpZGUgPSBUSFJFRS5Eb3VibGVTaWRlO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLm91dGxpbmVDdWxsTW9kZSA9PT0gTVRvb25NYXRlcmlhbEN1bGxNb2RlLkZyb250KSB7XG4gICAgICAgIHRoaXMuc2lkZSA9IFRIUkVFLkJhY2tTaWRlO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLm91dGxpbmVDdWxsTW9kZSA9PT0gTVRvb25NYXRlcmlhbEN1bGxNb2RlLkJhY2spIHtcbiAgICAgICAgdGhpcy5zaWRlID0gVEhSRUUuRnJvbnRTaWRlO1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuIiwiaW1wb3J0ICogYXMgVEhSRUUgZnJvbSAndGhyZWUnO1xuaW1wb3J0IHsgR0xURk1lc2gsIEdMVEZQcmltaXRpdmUsIFZSTVNjaGVtYSB9IGZyb20gJy4uL3R5cGVzJztcbmltcG9ydCB7IE1Ub29uTWF0ZXJpYWwsIE1Ub29uTWF0ZXJpYWxPdXRsaW5lV2lkdGhNb2RlLCBNVG9vbk1hdGVyaWFsUmVuZGVyTW9kZSB9IGZyb20gJy4vTVRvb25NYXRlcmlhbCc7XG5pbXBvcnQgeyBWUk1VbmxpdE1hdGVyaWFsLCBWUk1VbmxpdE1hdGVyaWFsUmVuZGVyVHlwZSB9IGZyb20gJy4vVlJNVW5saXRNYXRlcmlhbCc7XG5cbi8qKlxuICogT3B0aW9ucyBmb3IgYSBbW1ZSTU1hdGVyaWFsSW1wb3J0ZXJdXSBpbnN0YW5jZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBWUk1NYXRlcmlhbEltcG9ydGVyT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBXaGV0aGVyIHRoZSB3b3JrZmxvdyBzaG91bGQgYmUgbGluZWFyIG9yIGdhbW1hLlxuICAgKlxuICAgKiBTZWUgYWxzbzogaHR0cHM6Ly90aHJlZWpzLm9yZy9kb2NzLyNhcGkvZW4vcmVuZGVyZXJzL1dlYkdMUmVuZGVyZXIuZ2FtbWFPdXRwdXRcbiAgICovXG4gIGNvbG9yU3BhY2VHYW1tYT86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIEEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEgYFByb21pc2VgIG9mIGVudmlyb25tZW50IG1hcCB0ZXh0dXJlLlxuICAgKiBUaGUgaW1wb3J0ZXIgd2lsbCBhdHRlbXB0IHRvIGNhbGwgdGhpcyBmdW5jdGlvbiB3aGVuIGl0IGhhdmUgdG8gdXNlIGFuIGVudm1hcC5cbiAgICovXG4gIHJlcXVlc3RFbnZNYXA/OiAoKSA9PiBQcm9taXNlPFRIUkVFLlRleHR1cmUgfCBudWxsPjtcbn1cblxuLyoqXG4gKiBBbiBpbXBvcnRlciB0aGF0IGltcG9ydHMgVlJNIG1hdGVyaWFscyBmcm9tIGEgVlJNIGV4dGVuc2lvbiBvZiBhIEdMVEYuXG4gKi9cbmV4cG9ydCBjbGFzcyBWUk1NYXRlcmlhbEltcG9ydGVyIHtcbiAgcHJpdmF0ZSByZWFkb25seSBfY29sb3JTcGFjZUdhbW1hOiBib29sZWFuO1xuICBwcml2YXRlIHJlYWRvbmx5IF9yZXF1ZXN0RW52TWFwPzogKCkgPT4gUHJvbWlzZTxUSFJFRS5UZXh0dXJlIHwgbnVsbD47XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBWUk1NYXRlcmlhbEltcG9ydGVyLlxuICAgKlxuICAgKiBAcGFyYW0gb3B0aW9ucyBPcHRpb25zIG9mIHRoZSBWUk1NYXRlcmlhbEltcG9ydGVyXG4gICAqL1xuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBWUk1NYXRlcmlhbEltcG9ydGVyT3B0aW9ucyA9IHt9KSB7XG4gICAgdGhpcy5fY29sb3JTcGFjZUdhbW1hID0gb3B0aW9ucy5jb2xvclNwYWNlR2FtbWEgfHwgdHJ1ZTtcbiAgICB0aGlzLl9yZXF1ZXN0RW52TWFwID0gb3B0aW9ucy5yZXF1ZXN0RW52TWFwO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnZlcnQgYWxsIHRoZSBtYXRlcmlhbHMgb2YgZ2l2ZW4gR0xURiBiYXNlZCBvbiBWUk0gZXh0ZW5zaW9uIGZpZWxkIGBtYXRlcmlhbFByb3BlcnRpZXNgLlxuICAgKlxuICAgKiBAcGFyYW0gZ2x0ZiBBIHBhcnNlZCByZXN1bHQgb2YgR0xURiB0YWtlbiBmcm9tIEdMVEZMb2FkZXJcbiAgICovXG4gIHB1YmxpYyBhc3luYyBjb252ZXJ0R0xURk1hdGVyaWFscyhnbHRmOiBUSFJFRS5HTFRGKTogUHJvbWlzZTxUSFJFRS5NYXRlcmlhbFtdIHwgbnVsbD4ge1xuICAgIGNvbnN0IHZybUV4dDogVlJNU2NoZW1hLlZSTSB8IHVuZGVmaW5lZCA9IGdsdGYucGFyc2VyLmpzb24uZXh0ZW5zaW9ucyAmJiBnbHRmLnBhcnNlci5qc29uLmV4dGVuc2lvbnMuVlJNO1xuICAgIGlmICghdnJtRXh0KSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBjb25zdCBtYXRlcmlhbFByb3BlcnRpZXM6IFZSTVNjaGVtYS5NYXRlcmlhbFtdIHwgdW5kZWZpbmVkID0gdnJtRXh0Lm1hdGVyaWFsUHJvcGVydGllcztcbiAgICBpZiAoIW1hdGVyaWFsUHJvcGVydGllcykge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3QgbWVzaGVzTWFwOiBHTFRGTWVzaFtdID0gYXdhaXQgZ2x0Zi5wYXJzZXIuZ2V0RGVwZW5kZW5jaWVzKCdtZXNoJyk7XG4gICAgY29uc3QgbWF0ZXJpYWxMaXN0OiB7IFt2cm1NYXRlcmlhbEluZGV4OiBudW1iZXJdOiB7IHN1cmZhY2U6IFRIUkVFLk1hdGVyaWFsOyBvdXRsaW5lPzogVEhSRUUuTWF0ZXJpYWwgfSB9ID0ge307XG4gICAgY29uc3QgbWF0ZXJpYWxzOiBUSFJFRS5NYXRlcmlhbFtdID0gW107IC8vIHJlc3VsdFxuXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICBtZXNoZXNNYXAubWFwKGFzeW5jIChtZXNoLCBtZXNoSW5kZXgpID0+IHtcbiAgICAgICAgY29uc3QgcHJpbWl0aXZlczogR0xURlByaW1pdGl2ZVtdID1cbiAgICAgICAgICBtZXNoLnR5cGUgPT09ICdHcm91cCcgPyAobWVzaC5jaGlsZHJlbiBhcyBHTFRGUHJpbWl0aXZlW10pIDogW21lc2ggYXMgR0xURlByaW1pdGl2ZV07XG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICAgIHByaW1pdGl2ZXMubWFwKGFzeW5jIChwcmltaXRpdmUsIHByaW1pdGl2ZUluZGV4KSA9PiB7XG4gICAgICAgICAgICAvLyBpZiBwcmltaXRpdmVzIG1hdGVyaWFsIGlzIG5vdCBhbiBhcnJheSwgbWFrZSBpdCBhbiBhcnJheVxuICAgICAgICAgICAgaWYgKCFBcnJheS5pc0FycmF5KHByaW1pdGl2ZS5tYXRlcmlhbCkpIHtcbiAgICAgICAgICAgICAgcHJpbWl0aXZlLm1hdGVyaWFsID0gW3ByaW1pdGl2ZS5tYXRlcmlhbF07XG4gICAgICAgICAgICAgIChwcmltaXRpdmUuZ2VvbWV0cnkgYXMgVEhSRUUuQnVmZmVyR2VvbWV0cnkpLmFkZEdyb3VwKFxuICAgICAgICAgICAgICAgIDAsXG4gICAgICAgICAgICAgICAgKHByaW1pdGl2ZS5nZW9tZXRyeSBhcyBUSFJFRS5CdWZmZXJHZW9tZXRyeSkuaW5kZXguY291bnQsXG4gICAgICAgICAgICAgICAgMCxcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gY3JlYXRlIC8gcHVzaCB0byBjYWNoZSAob3IgcG9wIGZyb20gY2FjaGUpIHZybSBtYXRlcmlhbHNcbiAgICAgICAgICAgIGNvbnN0IHZybU1hdGVyaWFsSW5kZXggPSBnbHRmLnBhcnNlci5qc29uLm1lc2hlcyFbbWVzaEluZGV4XS5wcmltaXRpdmVzW3ByaW1pdGl2ZUluZGV4XS5tYXRlcmlhbCE7XG5cbiAgICAgICAgICAgIGxldCBwcm9wcyA9IG1hdGVyaWFsUHJvcGVydGllc1t2cm1NYXRlcmlhbEluZGV4XTtcbiAgICAgICAgICAgIGlmICghcHJvcHMpIHtcbiAgICAgICAgICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgICAgICAgIGBWUk1NYXRlcmlhbEltcG9ydGVyOiBUaGVyZSBhcmUgbm8gbWF0ZXJpYWwgZGVmaW5pdGlvbiBmb3IgbWF0ZXJpYWwgIyR7dnJtTWF0ZXJpYWxJbmRleH0gb24gVlJNIGV4dGVuc2lvbi5gLFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICBwcm9wcyA9IHsgc2hhZGVyOiAnVlJNX1VTRV9HTFRGU0hBREVSJyB9OyAvLyBmYWxsYmFja1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBsZXQgdnJtTWF0ZXJpYWxzOiB7IHN1cmZhY2U6IFRIUkVFLk1hdGVyaWFsOyBvdXRsaW5lPzogVEhSRUUuTWF0ZXJpYWwgfTtcbiAgICAgICAgICAgIGlmIChtYXRlcmlhbExpc3RbdnJtTWF0ZXJpYWxJbmRleF0pIHtcbiAgICAgICAgICAgICAgdnJtTWF0ZXJpYWxzID0gbWF0ZXJpYWxMaXN0W3ZybU1hdGVyaWFsSW5kZXhdO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdnJtTWF0ZXJpYWxzID0gYXdhaXQgdGhpcy5jcmVhdGVWUk1NYXRlcmlhbHMocHJpbWl0aXZlLm1hdGVyaWFsWzBdLCBwcm9wcywgZ2x0Zik7XG4gICAgICAgICAgICAgIG1hdGVyaWFsTGlzdFt2cm1NYXRlcmlhbEluZGV4XSA9IHZybU1hdGVyaWFscztcblxuICAgICAgICAgICAgICBtYXRlcmlhbHMucHVzaCh2cm1NYXRlcmlhbHMuc3VyZmFjZSk7XG4gICAgICAgICAgICAgIGlmICh2cm1NYXRlcmlhbHMub3V0bGluZSkge1xuICAgICAgICAgICAgICAgIG1hdGVyaWFscy5wdXNoKHZybU1hdGVyaWFscy5vdXRsaW5lKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBzdXJmYWNlXG4gICAgICAgICAgICBwcmltaXRpdmUubWF0ZXJpYWxbMF0gPSB2cm1NYXRlcmlhbHMuc3VyZmFjZTtcblxuICAgICAgICAgICAgLy8gZW52bWFwXG4gICAgICAgICAgICBpZiAodGhpcy5fcmVxdWVzdEVudk1hcCkge1xuICAgICAgICAgICAgICB0aGlzLl9yZXF1ZXN0RW52TWFwKCkudGhlbigoZW52TWFwKSA9PiB7XG4gICAgICAgICAgICAgICAgKHZybU1hdGVyaWFscy5zdXJmYWNlIGFzIGFueSkuZW52TWFwID0gZW52TWFwO1xuICAgICAgICAgICAgICAgIHZybU1hdGVyaWFscy5zdXJmYWNlLm5lZWRzVXBkYXRlID0gdHJ1ZTtcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIHJlbmRlciBvcmRlclxuICAgICAgICAgICAgcHJpbWl0aXZlLnJlbmRlck9yZGVyID0gcHJvcHMucmVuZGVyUXVldWUgfHwgMjAwMDtcblxuICAgICAgICAgICAgLy8gb3V0bGluZSAoXCIyIHBhc3Mgc2hhZGluZyB1c2luZyBncm91cHNcIiB0cmljayBoZXJlKVxuICAgICAgICAgICAgaWYgKHZybU1hdGVyaWFscy5vdXRsaW5lKSB7XG4gICAgICAgICAgICAgIHByaW1pdGl2ZS5tYXRlcmlhbFsxXSA9IHZybU1hdGVyaWFscy5vdXRsaW5lO1xuICAgICAgICAgICAgICAocHJpbWl0aXZlLmdlb21ldHJ5IGFzIFRIUkVFLkJ1ZmZlckdlb21ldHJ5KS5hZGRHcm91cChcbiAgICAgICAgICAgICAgICAwLFxuICAgICAgICAgICAgICAgIChwcmltaXRpdmUuZ2VvbWV0cnkgYXMgVEhSRUUuQnVmZmVyR2VvbWV0cnkpLmluZGV4LmNvdW50LFxuICAgICAgICAgICAgICAgIDEsXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSksXG4gICAgICAgICk7XG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgcmV0dXJuIG1hdGVyaWFscztcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBjcmVhdGVWUk1NYXRlcmlhbHMoXG4gICAgb3JpZ2luYWxNYXRlcmlhbDogVEhSRUUuTWF0ZXJpYWwsXG4gICAgdnJtUHJvcHM6IFZSTVNjaGVtYS5NYXRlcmlhbCxcbiAgICBnbHRmOiBUSFJFRS5HTFRGLFxuICApOiBQcm9taXNlPHtcbiAgICBzdXJmYWNlOiBUSFJFRS5NYXRlcmlhbDtcbiAgICBvdXRsaW5lPzogVEhSRUUuTWF0ZXJpYWw7XG4gIH0+IHtcbiAgICBsZXQgbmV3U3VyZmFjZTogVEhSRUUuTWF0ZXJpYWwgfCB1bmRlZmluZWQ7XG4gICAgbGV0IG5ld091dGxpbmU6IFRIUkVFLk1hdGVyaWFsIHwgdW5kZWZpbmVkO1xuXG4gICAgaWYgKHZybVByb3BzLnNoYWRlciA9PT0gJ1ZSTS9NVG9vbicpIHtcbiAgICAgIGNvbnN0IHBhcmFtcyA9IGF3YWl0IHRoaXMuX2V4dHJhY3RNYXRlcmlhbFByb3BlcnRpZXMob3JpZ2luYWxNYXRlcmlhbCwgdnJtUHJvcHMsIGdsdGYpO1xuXG4gICAgICAvLyB3ZSBuZWVkIHRvIGdldCByaWQgb2YgdGhlc2UgcHJvcGVydGllc1xuICAgICAgWydzcmNCbGVuZCcsICdkc3RCbGVuZCcsICdpc0ZpcnN0U2V0dXAnXS5mb3JFYWNoKChuYW1lKSA9PiB7XG4gICAgICAgIGlmIChwYXJhbXNbbmFtZV0gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIGRlbGV0ZSBwYXJhbXNbbmFtZV07XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICAvLyB0aGVzZSB0ZXh0dXJlcyBtdXN0IGJlIHNSR0IgRW5jb2RpbmcsIGRlcGVuZHMgb24gY3VycmVudCBjb2xvcnNwYWNlXG4gICAgICBbJ21haW5UZXgnLCAnc2hhZGVUZXh0dXJlJywgJ2VtaXNzaW9uJywgJ3NwaGVyZUFkZCddLmZvckVhY2goKG5hbWUpID0+IHtcbiAgICAgICAgaWYgKHBhcmFtc1tuYW1lXSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgcGFyYW1zW25hbWVdLmVuY29kaW5nID0gdGhpcy5fY29sb3JTcGFjZUdhbW1hID8gVEhSRUUuTGluZWFyRW5jb2RpbmcgOiBUSFJFRS5zUkdCRW5jb2Rpbmc7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICAvLyBkb25lXG4gICAgICBuZXdTdXJmYWNlID0gbmV3IE1Ub29uTWF0ZXJpYWwodGhpcy5fY29sb3JTcGFjZUdhbW1hLCBwYXJhbXMpO1xuXG4gICAgICAvLyBvdXRsaW5lXG4gICAgICBpZiAocGFyYW1zLm91dGxpbmVXaWR0aE1vZGUgIT09IE1Ub29uTWF0ZXJpYWxPdXRsaW5lV2lkdGhNb2RlLk5vbmUpIHtcbiAgICAgICAgcGFyYW1zLmlzT3V0bGluZSA9IHRydWU7XG4gICAgICAgIG5ld091dGxpbmUgPSBuZXcgTVRvb25NYXRlcmlhbCh0aGlzLl9jb2xvclNwYWNlR2FtbWEsIHBhcmFtcyk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh2cm1Qcm9wcy5zaGFkZXIgPT09ICdWUk0vVW5saXRUZXh0dXJlJykge1xuICAgICAgLy8gdGhpcyBpcyB2ZXJ5IGxlZ2FjeVxuICAgICAgY29uc3QgcGFyYW1zID0gYXdhaXQgdGhpcy5fZXh0cmFjdE1hdGVyaWFsUHJvcGVydGllcyhvcmlnaW5hbE1hdGVyaWFsLCB2cm1Qcm9wcywgZ2x0Zik7XG4gICAgICBwYXJhbXMucmVuZGVyVHlwZSA9IFZSTVVubGl0TWF0ZXJpYWxSZW5kZXJUeXBlLk9wYXF1ZTtcbiAgICAgIG5ld1N1cmZhY2UgPSBuZXcgVlJNVW5saXRNYXRlcmlhbChwYXJhbXMpO1xuICAgIH0gZWxzZSBpZiAodnJtUHJvcHMuc2hhZGVyID09PSAnVlJNL1VubGl0Q3V0b3V0Jykge1xuICAgICAgLy8gdGhpcyBpcyB2ZXJ5IGxlZ2FjeVxuICAgICAgY29uc3QgcGFyYW1zID0gYXdhaXQgdGhpcy5fZXh0cmFjdE1hdGVyaWFsUHJvcGVydGllcyhvcmlnaW5hbE1hdGVyaWFsLCB2cm1Qcm9wcywgZ2x0Zik7XG4gICAgICBwYXJhbXMucmVuZGVyVHlwZSA9IFZSTVVubGl0TWF0ZXJpYWxSZW5kZXJUeXBlLkN1dG91dDtcbiAgICAgIG5ld1N1cmZhY2UgPSBuZXcgVlJNVW5saXRNYXRlcmlhbChwYXJhbXMpO1xuICAgIH0gZWxzZSBpZiAodnJtUHJvcHMuc2hhZGVyID09PSAnVlJNL1VubGl0VHJhbnNwYXJlbnQnKSB7XG4gICAgICAvLyB0aGlzIGlzIHZlcnkgbGVnYWN5XG4gICAgICBjb25zdCBwYXJhbXMgPSBhd2FpdCB0aGlzLl9leHRyYWN0TWF0ZXJpYWxQcm9wZXJ0aWVzKG9yaWdpbmFsTWF0ZXJpYWwsIHZybVByb3BzLCBnbHRmKTtcbiAgICAgIHBhcmFtcy5yZW5kZXJUeXBlID0gVlJNVW5saXRNYXRlcmlhbFJlbmRlclR5cGUuVHJhbnNwYXJlbnQ7XG4gICAgICBuZXdTdXJmYWNlID0gbmV3IFZSTVVubGl0TWF0ZXJpYWwocGFyYW1zKTtcbiAgICB9IGVsc2UgaWYgKHZybVByb3BzLnNoYWRlciA9PT0gJ1ZSTS9VbmxpdFRyYW5zcGFyZW50WldyaXRlJykge1xuICAgICAgLy8gdGhpcyBpcyB2ZXJ5IGxlZ2FjeVxuICAgICAgY29uc3QgcGFyYW1zID0gYXdhaXQgdGhpcy5fZXh0cmFjdE1hdGVyaWFsUHJvcGVydGllcyhvcmlnaW5hbE1hdGVyaWFsLCB2cm1Qcm9wcywgZ2x0Zik7XG4gICAgICBwYXJhbXMucmVuZGVyVHlwZSA9IFZSTVVubGl0TWF0ZXJpYWxSZW5kZXJUeXBlLlRyYW5zcGFyZW50V2l0aFpXcml0ZTtcbiAgICAgIG5ld1N1cmZhY2UgPSBuZXcgVlJNVW5saXRNYXRlcmlhbChwYXJhbXMpO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAodnJtUHJvcHMuc2hhZGVyICE9PSAnVlJNX1VTRV9HTFRGU0hBREVSJykge1xuICAgICAgICBjb25zb2xlLndhcm4oYFVua25vd24gc2hhZGVyIGRldGVjdGVkOiBcIiR7dnJtUHJvcHMuc2hhZGVyfVwiYCk7XG4gICAgICAgIC8vIHRoZW4gcHJlc3VtZSBhcyBWUk1fVVNFX0dMVEZTSEFERVJcbiAgICAgIH1cblxuICAgICAgbmV3U3VyZmFjZSA9IHRoaXMuX2NvbnZlcnRHTFRGTWF0ZXJpYWwob3JpZ2luYWxNYXRlcmlhbC5jbG9uZSgpKTtcbiAgICB9XG5cbiAgICBuZXdTdXJmYWNlLm5hbWUgPSBvcmlnaW5hbE1hdGVyaWFsLm5hbWU7XG4gICAgbmV3U3VyZmFjZS51c2VyRGF0YSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkob3JpZ2luYWxNYXRlcmlhbC51c2VyRGF0YSkpO1xuICAgIG5ld1N1cmZhY2UudXNlckRhdGEudnJtTWF0ZXJpYWxQcm9wZXJ0aWVzID0gdnJtUHJvcHM7XG5cbiAgICBpZiAobmV3T3V0bGluZSkge1xuICAgICAgbmV3T3V0bGluZS5uYW1lID0gb3JpZ2luYWxNYXRlcmlhbC5uYW1lICsgJyAoT3V0bGluZSknO1xuICAgICAgbmV3T3V0bGluZS51c2VyRGF0YSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkob3JpZ2luYWxNYXRlcmlhbC51c2VyRGF0YSkpO1xuICAgICAgbmV3T3V0bGluZS51c2VyRGF0YS52cm1NYXRlcmlhbFByb3BlcnRpZXMgPSB2cm1Qcm9wcztcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgc3VyZmFjZTogbmV3U3VyZmFjZSxcbiAgICAgIG91dGxpbmU6IG5ld091dGxpbmUsXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgX3JlbmFtZU1hdGVyaWFsUHJvcGVydHkobmFtZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBpZiAobmFtZVswXSAhPT0gJ18nKSB7XG4gICAgICBjb25zb2xlLndhcm4oYFZSTU1hdGVyaWFsczogR2l2ZW4gcHJvcGVydHkgbmFtZSBcIiR7bmFtZX1cIiBtaWdodCBiZSBpbnZhbGlkYCk7XG4gICAgICByZXR1cm4gbmFtZTtcbiAgICB9XG4gICAgbmFtZSA9IG5hbWUuc3Vic3RyaW5nKDEpO1xuXG4gICAgaWYgKCEvW0EtWl0vLnRlc3QobmFtZVswXSkpIHtcbiAgICAgIGNvbnNvbGUud2FybihgVlJNTWF0ZXJpYWxzOiBHaXZlbiBwcm9wZXJ0eSBuYW1lIFwiJHtuYW1lfVwiIG1pZ2h0IGJlIGludmFsaWRgKTtcbiAgICAgIHJldHVybiBuYW1lO1xuICAgIH1cbiAgICByZXR1cm4gbmFtZVswXS50b0xvd2VyQ2FzZSgpICsgbmFtZS5zdWJzdHJpbmcoMSk7XG4gIH1cblxuICBwcml2YXRlIF9jb252ZXJ0R0xURk1hdGVyaWFsKG1hdGVyaWFsOiBUSFJFRS5NYXRlcmlhbCk6IFRIUkVFLk1hdGVyaWFsIHtcbiAgICBpZiAoKG1hdGVyaWFsIGFzIGFueSkuaXNNZXNoU3RhbmRhcmRNYXRlcmlhbCkge1xuICAgICAgY29uc3QgbXRsID0gbWF0ZXJpYWwgYXMgVEhSRUUuTWVzaFN0YW5kYXJkTWF0ZXJpYWw7XG5cbiAgICAgIGlmICh0aGlzLl9jb2xvclNwYWNlR2FtbWEpIHtcbiAgICAgICAgaWYgKG10bC5tYXApIHtcbiAgICAgICAgICBtdGwubWFwLmVuY29kaW5nID0gVEhSRUUuTGluZWFyRW5jb2Rpbmc7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG10bC5lbWlzc2l2ZU1hcCkge1xuICAgICAgICAgIG10bC5lbWlzc2l2ZU1hcC5lbmNvZGluZyA9IFRIUkVFLkxpbmVhckVuY29kaW5nO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAobXRsIGFzIGFueSkuY29sb3IuY29udmVydFNSR0JUb0xpbmVhcigpOyAvLyBUT0RPOiBgYXMgYW55YCBpcyB0ZW1wb3JhbCwgc2luY2UgdGhlcmUgYXJlIG5vIGRlY2xhcmF0aW9uIGluIEB0eXBlcy90aHJlZVxuICAgICAgICAobXRsIGFzIGFueSkuZW1pc3NpdmUuY29udmVydFNSR0JUb0xpbmVhcigpOyAvLyBUT0RPOiBgYXMgYW55YCBpcyB0ZW1wb3JhbCwgc2luY2UgdGhlcmUgYXJlIG5vIGRlY2xhcmF0aW9uIGluIEB0eXBlcy90aHJlZVxuICAgICAgfVxuICAgIH1cblxuICAgIGlmICgobWF0ZXJpYWwgYXMgYW55KS5pc01lc2hCYXNpY01hdGVyaWFsKSB7XG4gICAgICBjb25zdCBtdGwgPSBtYXRlcmlhbCBhcyBUSFJFRS5NZXNoQmFzaWNNYXRlcmlhbDtcblxuICAgICAgaWYgKHRoaXMuX2NvbG9yU3BhY2VHYW1tYSkge1xuICAgICAgICBpZiAobXRsLm1hcCkge1xuICAgICAgICAgIG10bC5tYXAuZW5jb2RpbmcgPSBUSFJFRS5MaW5lYXJFbmNvZGluZztcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgKG10bCBhcyBhbnkpLmNvbG9yLmNvbnZlcnRTUkdCVG9MaW5lYXIoKTsgLy8gVE9ETzogYGFzIGFueWAgaXMgdGVtcG9yYWwsIHNpbmNlIHRoZXJlIGFyZSBubyBkZWNsYXJhdGlvbiBpbiBAdHlwZXMvdGhyZWVcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gbWF0ZXJpYWw7XG4gIH1cblxuICBwcml2YXRlIF9leHRyYWN0TWF0ZXJpYWxQcm9wZXJ0aWVzKFxuICAgIG9yaWdpbmFsTWF0ZXJpYWw6IFRIUkVFLk1hdGVyaWFsLFxuICAgIHZybVByb3BzOiBWUk1TY2hlbWEuTWF0ZXJpYWwsXG4gICAgZ2x0ZjogVEhSRUUuR0xURixcbiAgKTogUHJvbWlzZTxhbnk+IHtcbiAgICBjb25zdCB0YXNrTGlzdDogQXJyYXk8UHJvbWlzZTxhbnk+PiA9IFtdO1xuICAgIGNvbnN0IHBhcmFtczogYW55ID0ge307XG5cbiAgICAvLyBleHRyYWN0IHRleHR1cmUgcHJvcGVydGllc1xuICAgIGlmICh2cm1Qcm9wcy50ZXh0dXJlUHJvcGVydGllcykge1xuICAgICAgZm9yIChjb25zdCBuYW1lIG9mIE9iamVjdC5rZXlzKHZybVByb3BzLnRleHR1cmVQcm9wZXJ0aWVzKSkge1xuICAgICAgICBjb25zdCBuZXdOYW1lID0gdGhpcy5fcmVuYW1lTWF0ZXJpYWxQcm9wZXJ0eShuYW1lKTtcbiAgICAgICAgY29uc3QgdGV4dHVyZUluZGV4ID0gdnJtUHJvcHMudGV4dHVyZVByb3BlcnRpZXNbbmFtZV07XG5cbiAgICAgICAgdGFza0xpc3QucHVzaChcbiAgICAgICAgICBnbHRmLnBhcnNlci5nZXREZXBlbmRlbmN5KCd0ZXh0dXJlJywgdGV4dHVyZUluZGV4KS50aGVuKCh0ZXh0dXJlOiBUSFJFRS5UZXh0dXJlKSA9PiB7XG4gICAgICAgICAgICBwYXJhbXNbbmV3TmFtZV0gPSB0ZXh0dXJlO1xuICAgICAgICAgIH0pLFxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIGV4dHJhY3QgZmxvYXQgcHJvcGVydGllc1xuICAgIGlmICh2cm1Qcm9wcy5mbG9hdFByb3BlcnRpZXMpIHtcbiAgICAgIGZvciAoY29uc3QgbmFtZSBvZiBPYmplY3Qua2V5cyh2cm1Qcm9wcy5mbG9hdFByb3BlcnRpZXMpKSB7XG4gICAgICAgIGNvbnN0IG5ld05hbWUgPSB0aGlzLl9yZW5hbWVNYXRlcmlhbFByb3BlcnR5KG5hbWUpO1xuICAgICAgICBwYXJhbXNbbmV3TmFtZV0gPSB2cm1Qcm9wcy5mbG9hdFByb3BlcnRpZXNbbmFtZV07XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gZXh0cmFjdCB2ZWN0b3IgKGNvbG9yIHRiaCkgcHJvcGVydGllc1xuICAgIGlmICh2cm1Qcm9wcy52ZWN0b3JQcm9wZXJ0aWVzKSB7XG4gICAgICBmb3IgKGNvbnN0IG5hbWUgb2YgT2JqZWN0LmtleXModnJtUHJvcHMudmVjdG9yUHJvcGVydGllcykpIHtcbiAgICAgICAgbGV0IG5ld05hbWUgPSB0aGlzLl9yZW5hbWVNYXRlcmlhbFByb3BlcnR5KG5hbWUpO1xuXG4gICAgICAgIC8vIGlmIHRoaXMgaXMgdGV4dHVyZVNUIChzYW1lIG5hbWUgYXMgdGV4dHVyZSBuYW1lIGl0c2VsZiksIGFkZCAnX1NUJ1xuICAgICAgICAvLyB0aGlzIGlzIG15IG1vc3QgZmF2b3JpdGUgTVRvb24gZmVhdHVyZSB0YmhcbiAgICAgICAgY29uc3QgaXNUZXh0dXJlU1QgPSBbXG4gICAgICAgICAgJ19NYWluVGV4JyxcbiAgICAgICAgICAnX1NoYWRlVGV4dHVyZScsXG4gICAgICAgICAgJ19CdW1wTWFwJyxcbiAgICAgICAgICAnX1JlY2VpdmVTaGFkb3dUZXh0dXJlJyxcbiAgICAgICAgICAnX1NoYWRpbmdHcmFkZVRleHR1cmUnLFxuICAgICAgICAgICdfU3BoZXJlQWRkJyxcbiAgICAgICAgICAnX0VtaXNzaW9uTWFwJyxcbiAgICAgICAgICAnX091dGxpbmVXaWR0aFRleHR1cmUnLFxuICAgICAgICBdLnNvbWUoKHRleHR1cmVOYW1lKSA9PiBuYW1lID09PSB0ZXh0dXJlTmFtZSk7XG4gICAgICAgIGlmIChpc1RleHR1cmVTVCkge1xuICAgICAgICAgIG5ld05hbWUgKz0gJ19TVCc7XG4gICAgICAgIH1cblxuICAgICAgICBwYXJhbXNbbmV3TmFtZV0gPSBuZXcgVEhSRUUuVmVjdG9yNCguLi52cm1Qcm9wcy52ZWN0b3JQcm9wZXJ0aWVzW25hbWVdKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUT0RPOiBmIChodHRwczovL2dpdGh1Yi5jb20vZHdhbmdvL1VuaVZSTS9pc3N1ZXMvMTcyKVxuICAgIGlmICh2cm1Qcm9wcy5rZXl3b3JkTWFwIS5fQUxQSEFURVNUX09OICYmIHBhcmFtcy5ibGVuZE1vZGUgPT09IE1Ub29uTWF0ZXJpYWxSZW5kZXJNb2RlLk9wYXF1ZSkge1xuICAgICAgcGFyYW1zLmJsZW5kTW9kZSA9IE1Ub29uTWF0ZXJpYWxSZW5kZXJNb2RlLkN1dG91dDtcbiAgICB9XG5cbiAgICAvLyBzZXQgd2hldGhlciBpdCBuZWVkcyBza2lubmluZyBhbmQgbW9ycGhpbmcgb3Igbm90XG4gICAgcGFyYW1zLnNraW5uaW5nID0gKG9yaWdpbmFsTWF0ZXJpYWwgYXMgYW55KS5za2lubmluZyB8fCBmYWxzZTtcbiAgICBwYXJhbXMubW9ycGhUYXJnZXRzID0gKG9yaWdpbmFsTWF0ZXJpYWwgYXMgYW55KS5tb3JwaFRhcmdldHMgfHwgZmFsc2U7XG4gICAgcGFyYW1zLm1vcnBoTm9ybWFscyA9IChvcmlnaW5hbE1hdGVyaWFsIGFzIGFueSkubW9ycGhOb3JtYWxzIHx8IGZhbHNlO1xuXG4gICAgcmV0dXJuIFByb21pc2UuYWxsKHRhc2tMaXN0KS50aGVuKCgpID0+IHBhcmFtcyk7XG4gIH1cbn1cbiIsIi8qIHRzbGludDpkaXNhYmxlOm1lbWJlci1vcmRlcmluZyAqL1xuXG5pbXBvcnQgKiBhcyBUSFJFRSBmcm9tICd0aHJlZSc7XG5pbXBvcnQgdmVydGV4U2hhZGVyIGZyb20gJy4vc2hhZGVycy91bmxpdC52ZXJ0JztcbmltcG9ydCBmcmFnbWVudFNoYWRlciBmcm9tICcuL3NoYWRlcnMvdW5saXQuZnJhZyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgVlJNVW5saXRNYXRlcmlhbFBhcmFtZXRlcnMgZXh0ZW5kcyBUSFJFRS5TaGFkZXJNYXRlcmlhbFBhcmFtZXRlcnMge1xuICBjdXRvZmY/OiBudW1iZXI7IC8vIF9DdXRvZmZcbiAgbWFwPzogVEhSRUUuVGV4dHVyZTsgLy8gX01haW5UZXhcbiAgbWFpblRleD86IFRIUkVFLlRleHR1cmU7IC8vIF9NYWluVGV4ICh3aWxsIGJlIHJlbmFtZWQgdG8gbWFwKVxuICBtYWluVGV4X1NUPzogVEhSRUUuVmVjdG9yNDsgLy8gX01haW5UZXhfU1RcblxuICByZW5kZXJUeXBlPzogVlJNVW5saXRNYXRlcmlhbFJlbmRlclR5cGUgfCBudW1iZXI7XG59XG5cbmV4cG9ydCBlbnVtIFZSTVVubGl0TWF0ZXJpYWxSZW5kZXJUeXBlIHtcbiAgT3BhcXVlLFxuICBDdXRvdXQsXG4gIFRyYW5zcGFyZW50LFxuICBUcmFuc3BhcmVudFdpdGhaV3JpdGUsXG59XG5cbi8qKlxuICogVGhpcyBpcyBhIG1hdGVyaWFsIHRoYXQgaXMgYW4gZXF1aXZhbGVudCBvZiBcIlZSTS9VbmxpdCoqKlwiIG9uIFZSTSBzcGVjLCB0aG9zZSBtYXRlcmlhbHMgYXJlIGFscmVhZHkga2luZGEgZGVwcmVjYXRlZCB0aG91Z2guLi5cbiAqL1xuZXhwb3J0IGNsYXNzIFZSTVVubGl0TWF0ZXJpYWwgZXh0ZW5kcyBUSFJFRS5TaGFkZXJNYXRlcmlhbCB7XG4gIC8qKlxuICAgKiBSZWFkb25seSBib29sZWFuIHRoYXQgaW5kaWNhdGVzIHRoaXMgaXMgYSBbW1ZSTVVubGl0TWF0ZXJpYWxdXS5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBpc1ZSTVVubGl0TWF0ZXJpYWw6IGJvb2xlYW4gPSB0cnVlO1xuXG4gIHB1YmxpYyBjdXRvZmYgPSAwLjU7XG4gIHB1YmxpYyBtYXA6IFRIUkVFLlRleHR1cmUgfCBudWxsID0gbnVsbDsgLy8gX01haW5UZXhcbiAgcHVibGljIG1haW5UZXhfU1Q6IFRIUkVFLlZlY3RvcjQgPSBuZXcgVEhSRUUuVmVjdG9yNCgwLjAsIDAuMCwgMS4wLCAxLjApOyAvLyBfTWFpblRleF9TVFxuICBwcml2YXRlIF9yZW5kZXJUeXBlOiBWUk1VbmxpdE1hdGVyaWFsUmVuZGVyVHlwZSA9IFZSTVVubGl0TWF0ZXJpYWxSZW5kZXJUeXBlLk9wYXF1ZTtcblxuICBwdWJsaWMgc2hvdWxkQXBwbHlVbmlmb3JtcyA9IHRydWU7IC8vIHdoZW4gdGhpcyBpcyB0cnVlLCBhcHBseVVuaWZvcm1zIGVmZmVjdHNcblxuICBjb25zdHJ1Y3RvcihwYXJhbWV0ZXJzPzogVlJNVW5saXRNYXRlcmlhbFBhcmFtZXRlcnMpIHtcbiAgICBzdXBlcigpO1xuXG4gICAgaWYgKHBhcmFtZXRlcnMgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcGFyYW1ldGVycyA9IHt9O1xuICAgIH1cblxuICAgIC8vID09IGVuYWJsaW5nIGJ1bmNoIG9mIHN0dWZmID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICBwYXJhbWV0ZXJzLmZvZyA9IHRydWU7XG4gICAgcGFyYW1ldGVycy5jbGlwcGluZyA9IHRydWU7XG5cbiAgICBwYXJhbWV0ZXJzLnNraW5uaW5nID0gcGFyYW1ldGVycy5za2lubmluZyB8fCBmYWxzZTtcbiAgICBwYXJhbWV0ZXJzLm1vcnBoVGFyZ2V0cyA9IHBhcmFtZXRlcnMubW9ycGhUYXJnZXRzIHx8IGZhbHNlO1xuICAgIHBhcmFtZXRlcnMubW9ycGhOb3JtYWxzID0gcGFyYW1ldGVycy5tb3JwaE5vcm1hbHMgfHwgZmFsc2U7XG5cbiAgICAvLyA9PSB1bmlmb3JtcyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgcGFyYW1ldGVycy51bmlmb3JtcyA9IFRIUkVFLlVuaWZvcm1zVXRpbHMubWVyZ2UoW1xuICAgICAgVEhSRUUuVW5pZm9ybXNMaWIuY29tbW9uLCAvLyBtYXBcbiAgICAgIFRIUkVFLlVuaWZvcm1zTGliLmZvZyxcbiAgICAgIHtcbiAgICAgICAgY3V0b2ZmOiB7IHZhbHVlOiAwLjUgfSxcbiAgICAgICAgbWFpblRleF9TVDogeyB2YWx1ZTogbmV3IFRIUkVFLlZlY3RvcjQoMC4wLCAwLjAsIDEuMCwgMS4wKSB9LFxuICAgICAgfSxcbiAgICBdKTtcblxuICAgIC8vID09IGZpbmFsbHkgY29tcGlsZSB0aGUgc2hhZGVyIHByb2dyYW0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICB0aGlzLnNldFZhbHVlcyhwYXJhbWV0ZXJzKTtcblxuICAgIC8vID09IHVwZGF0ZSBzaGFkZXIgc3R1ZmYgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICB0aGlzLl91cGRhdGVTaGFkZXJDb2RlKCk7XG4gICAgdGhpcy5fYXBwbHlVbmlmb3JtcygpO1xuICB9XG5cbiAgZ2V0IG1haW5UZXgoKTogVEhSRUUuVGV4dHVyZSB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLm1hcDtcbiAgfVxuXG4gIHNldCBtYWluVGV4KHQ6IFRIUkVFLlRleHR1cmUgfCBudWxsKSB7XG4gICAgdGhpcy5tYXAgPSB0O1xuICB9XG5cbiAgZ2V0IHJlbmRlclR5cGUoKTogVlJNVW5saXRNYXRlcmlhbFJlbmRlclR5cGUge1xuICAgIHJldHVybiB0aGlzLl9yZW5kZXJUeXBlO1xuICB9XG5cbiAgc2V0IHJlbmRlclR5cGUodDogVlJNVW5saXRNYXRlcmlhbFJlbmRlclR5cGUpIHtcbiAgICB0aGlzLl9yZW5kZXJUeXBlID0gdDtcblxuICAgIHRoaXMuZGVwdGhXcml0ZSA9IHRoaXMuX3JlbmRlclR5cGUgIT09IFZSTVVubGl0TWF0ZXJpYWxSZW5kZXJUeXBlLlRyYW5zcGFyZW50O1xuICAgIHRoaXMudHJhbnNwYXJlbnQgPVxuICAgICAgdGhpcy5fcmVuZGVyVHlwZSA9PT0gVlJNVW5saXRNYXRlcmlhbFJlbmRlclR5cGUuVHJhbnNwYXJlbnQgfHxcbiAgICAgIHRoaXMuX3JlbmRlclR5cGUgPT09IFZSTVVubGl0TWF0ZXJpYWxSZW5kZXJUeXBlLlRyYW5zcGFyZW50V2l0aFpXcml0ZTtcbiAgICB0aGlzLl91cGRhdGVTaGFkZXJDb2RlKCk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIHRoaXMgbWF0ZXJpYWwuXG4gICAqIFVzdWFsbHkgdGhpcyB3aWxsIGJlIGNhbGxlZCB2aWEgW1tWUk0udXBkYXRlXV0gc28geW91IGRvbid0IGhhdmUgdG8gY2FsbCB0aGlzIG1hbnVhbGx5LlxuICAgKlxuICAgKiBAcGFyYW0gZGVsdGEgZGVsdGFUaW1lIHNpbmNlIGxhc3QgdXBkYXRlXG4gICAqL1xuICBwdWJsaWMgdXBkYXRlVlJNTWF0ZXJpYWxzKGRlbHRhOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLl9hcHBseVVuaWZvcm1zKCk7XG4gIH1cblxuICBwdWJsaWMgY29weShzb3VyY2U6IHRoaXMpOiB0aGlzIHtcbiAgICBzdXBlci5jb3B5KHNvdXJjZSk7XG5cbiAgICAvLyA9PSBjb3B5IG1lbWJlcnMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgdGhpcy5jdXRvZmYgPSBzb3VyY2UuY3V0b2ZmO1xuICAgIHRoaXMubWFwID0gc291cmNlLm1hcDtcbiAgICB0aGlzLm1haW5UZXhfU1QuY29weShzb3VyY2UubWFpblRleF9TVCk7XG4gICAgdGhpcy5yZW5kZXJUeXBlID0gc291cmNlLnJlbmRlclR5cGU7XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIC8qKlxuICAgKiBBcHBseSB1cGRhdGVkIHVuaWZvcm0gdmFyaWFibGVzLlxuICAgKi9cbiAgcHJpdmF0ZSBfYXBwbHlVbmlmb3JtcygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuc2hvdWxkQXBwbHlVbmlmb3Jtcykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLnNob3VsZEFwcGx5VW5pZm9ybXMgPSBmYWxzZTtcblxuICAgIHRoaXMudW5pZm9ybXMuY3V0b2ZmLnZhbHVlID0gdGhpcy5jdXRvZmY7XG4gICAgdGhpcy51bmlmb3Jtcy5tYXAudmFsdWUgPSB0aGlzLm1hcDtcbiAgICB0aGlzLnVuaWZvcm1zLm1haW5UZXhfU1QudmFsdWUuY29weSh0aGlzLm1haW5UZXhfU1QpO1xuICB9XG5cbiAgcHJpdmF0ZSBfdXBkYXRlU2hhZGVyQ29kZSgpOiB2b2lkIHtcbiAgICB0aGlzLmRlZmluZXMgPSB7XG4gICAgICBSRU5ERVJUWVBFX09QQVFVRTogdGhpcy5fcmVuZGVyVHlwZSA9PT0gVlJNVW5saXRNYXRlcmlhbFJlbmRlclR5cGUuT3BhcXVlLFxuICAgICAgUkVOREVSVFlQRV9DVVRPVVQ6IHRoaXMuX3JlbmRlclR5cGUgPT09IFZSTVVubGl0TWF0ZXJpYWxSZW5kZXJUeXBlLkN1dG91dCxcbiAgICAgIFJFTkRFUlRZUEVfVFJBTlNQQVJFTlQ6XG4gICAgICAgIHRoaXMuX3JlbmRlclR5cGUgPT09IFZSTVVubGl0TWF0ZXJpYWxSZW5kZXJUeXBlLlRyYW5zcGFyZW50IHx8XG4gICAgICAgIHRoaXMuX3JlbmRlclR5cGUgPT09IFZSTVVubGl0TWF0ZXJpYWxSZW5kZXJUeXBlLlRyYW5zcGFyZW50V2l0aFpXcml0ZSxcbiAgICB9O1xuXG4gICAgdGhpcy52ZXJ0ZXhTaGFkZXIgPSB2ZXJ0ZXhTaGFkZXI7XG4gICAgdGhpcy5mcmFnbWVudFNoYWRlciA9IGZyYWdtZW50U2hhZGVyO1xuXG4gICAgLy8gPT0gc2V0IG5lZWRzVXBkYXRlIGZsYWcgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIHRoaXMubmVlZHNVcGRhdGUgPSB0cnVlO1xuICB9XG59XG4iLCJpbXBvcnQgKiBhcyBUSFJFRSBmcm9tICd0aHJlZSc7XG5cbmV4cG9ydCBjb25zdCBnZXRFbmNvZGluZ0NvbXBvbmVudHMgPSAoZW5jb2Rpbmc6IFRIUkVFLlRleHR1cmVFbmNvZGluZyk6IFtzdHJpbmcsIHN0cmluZ10gPT4ge1xuICBzd2l0Y2ggKGVuY29kaW5nKSB7XG4gICAgY2FzZSBUSFJFRS5MaW5lYXJFbmNvZGluZzpcbiAgICAgIHJldHVybiBbJ0xpbmVhcicsICcoIHZhbHVlICknXTtcbiAgICBjYXNlIFRIUkVFLnNSR0JFbmNvZGluZzpcbiAgICAgIHJldHVybiBbJ3NSR0InLCAnKCB2YWx1ZSApJ107XG4gICAgY2FzZSBUSFJFRS5SR0JFRW5jb2Rpbmc6XG4gICAgICByZXR1cm4gWydSR0JFJywgJyggdmFsdWUgKSddO1xuICAgIGNhc2UgVEhSRUUuUkdCTTdFbmNvZGluZzpcbiAgICAgIHJldHVybiBbJ1JHQk0nLCAnKCB2YWx1ZSwgNy4wICknXTtcbiAgICBjYXNlIFRIUkVFLlJHQk0xNkVuY29kaW5nOlxuICAgICAgcmV0dXJuIFsnUkdCTScsICcoIHZhbHVlLCAxNi4wICknXTtcbiAgICBjYXNlIFRIUkVFLlJHQkRFbmNvZGluZzpcbiAgICAgIHJldHVybiBbJ1JHQkQnLCAnKCB2YWx1ZSwgMjU2LjAgKSddO1xuICAgIGNhc2UgVEhSRUUuR2FtbWFFbmNvZGluZzpcbiAgICAgIHJldHVybiBbJ0dhbW1hJywgJyggdmFsdWUsIGZsb2F0KCBHQU1NQV9GQUNUT1IgKSApJ107XG4gICAgZGVmYXVsdDpcbiAgICAgIHRocm93IG5ldyBFcnJvcigndW5zdXBwb3J0ZWQgZW5jb2Rpbmc6ICcgKyBlbmNvZGluZyk7XG4gIH1cbn07XG5cbmV4cG9ydCBjb25zdCBnZXRUZXhlbERlY29kaW5nRnVuY3Rpb24gPSAoZnVuY3Rpb25OYW1lOiBzdHJpbmcsIGVuY29kaW5nOiBUSFJFRS5UZXh0dXJlRW5jb2RpbmcpOiBzdHJpbmcgPT4ge1xuICBjb25zdCBjb21wb25lbnRzID0gZ2V0RW5jb2RpbmdDb21wb25lbnRzKGVuY29kaW5nKTtcbiAgcmV0dXJuICd2ZWM0ICcgKyBmdW5jdGlvbk5hbWUgKyAnKCB2ZWM0IHZhbHVlICkgeyByZXR1cm4gJyArIGNvbXBvbmVudHNbMF0gKyAnVG9MaW5lYXInICsgY29tcG9uZW50c1sxXSArICc7IH0nO1xufTtcbiIsImV4cG9ydCAqIGZyb20gJy4vTVRvb25NYXRlcmlhbCc7XG5leHBvcnQgKiBmcm9tICcuL1ZSTU1hdGVyaWFsSW1wb3J0ZXInO1xuZXhwb3J0ICogZnJvbSAnLi9WUk1VbmxpdE1hdGVyaWFsJztcbiIsImV4cG9ydCBkZWZhdWx0IFwiLy8gI2RlZmluZSBQSE9OR1xcblxcbiNpZmRlZiBCTEVORE1PREVfQ1VUT1VUXFxuICB1bmlmb3JtIGZsb2F0IGN1dG9mZjtcXG4jZW5kaWZcXG5cXG51bmlmb3JtIHZlYzMgY29sb3I7XFxudW5pZm9ybSBmbG9hdCBjb2xvckFscGhhO1xcbnVuaWZvcm0gdmVjMyBzaGFkZUNvbG9yO1xcbiNpZmRlZiBVU0VfU0hBREVURVhUVVJFXFxuICB1bmlmb3JtIHNhbXBsZXIyRCBzaGFkZVRleHR1cmU7XFxuI2VuZGlmXFxuXFxudW5pZm9ybSBmbG9hdCByZWNlaXZlU2hhZG93UmF0ZTtcXG4jaWZkZWYgVVNFX1JFQ0VJVkVTSEFET1dURVhUVVJFXFxuICB1bmlmb3JtIHNhbXBsZXIyRCByZWNlaXZlU2hhZG93VGV4dHVyZTtcXG4jZW5kaWZcXG5cXG51bmlmb3JtIGZsb2F0IHNoYWRpbmdHcmFkZVJhdGU7XFxuI2lmZGVmIFVTRV9TSEFESU5HR1JBREVURVhUVVJFXFxuICB1bmlmb3JtIHNhbXBsZXIyRCBzaGFkaW5nR3JhZGVUZXh0dXJlO1xcbiNlbmRpZlxcblxcbnVuaWZvcm0gZmxvYXQgc2hhZGVTaGlmdDtcXG51bmlmb3JtIGZsb2F0IHNoYWRlVG9vbnk7XFxudW5pZm9ybSBmbG9hdCBsaWdodENvbG9yQXR0ZW51YXRpb247XFxudW5pZm9ybSBmbG9hdCBpbmRpcmVjdExpZ2h0SW50ZW5zaXR5O1xcblxcbiNpZmRlZiBVU0VfUklNVEVYVFVSRVxcbiAgdW5pZm9ybSBzYW1wbGVyMkQgcmltVGV4dHVyZTtcXG4jZW5kaWZcXG51bmlmb3JtIHZlYzMgcmltQ29sb3I7XFxudW5pZm9ybSBmbG9hdCByaW1MaWdodGluZ01peDtcXG51bmlmb3JtIGZsb2F0IHJpbUZyZXNuZWxQb3dlcjtcXG51bmlmb3JtIGZsb2F0IHJpbUxpZnQ7XFxuXFxuI2lmZGVmIFVTRV9TUEhFUkVBRERcXG4gIHVuaWZvcm0gc2FtcGxlcjJEIHNwaGVyZUFkZDtcXG4jZW5kaWZcXG5cXG51bmlmb3JtIHZlYzMgZW1pc3Npb25Db2xvcjtcXG5cXG51bmlmb3JtIHZlYzMgb3V0bGluZUNvbG9yO1xcbnVuaWZvcm0gZmxvYXQgb3V0bGluZUxpZ2h0aW5nTWl4O1xcblxcbiNpZmRlZiBVU0VfVVZBTklNTUFTS1RFWFRVUkVcXG4gIHVuaWZvcm0gc2FtcGxlcjJEIHV2QW5pbU1hc2tUZXh0dXJlO1xcbiNlbmRpZlxcblxcbnVuaWZvcm0gZmxvYXQgdXZBbmltT2Zmc2V0WDtcXG51bmlmb3JtIGZsb2F0IHV2QW5pbU9mZnNldFk7XFxudW5pZm9ybSBmbG9hdCB1dkFuaW1UaGV0YTtcXG5cXG4jaW5jbHVkZSA8Y29tbW9uPlxcbiNpbmNsdWRlIDxwYWNraW5nPlxcbiNpbmNsdWRlIDxkaXRoZXJpbmdfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8Y29sb3JfcGFyc19mcmFnbWVudD5cXG5cXG4vLyAjaW5jbHVkZSA8dXZfcGFyc19mcmFnbWVudD5cXG4jaWYgZGVmaW5lZCggVVNFX01BUCApIHx8IGRlZmluZWQoIFVTRV9TSEFERVRFWFRVUkUgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQICkgfHwgZGVmaW5lZCggVVNFX1JFQ0VJVkVTSEFET1dURVhUVVJFICkgfHwgZGVmaW5lZCggVVNFX1NIQURJTkdHUkFERVRFWFRVUkUgKSB8fCBkZWZpbmVkKCBVU0VfUklNVEVYVFVSRSApIHx8IGRlZmluZWQoIFVTRV9FTUlTU0lWRU1BUCApIHx8IGRlZmluZWQoIFVTRV9PVVRMSU5FV0lEVEhURVhUVVJFICkgfHwgZGVmaW5lZCggVVNFX1VWQU5JTU1BU0tURVhUVVJFIClcXG4gIHZhcnlpbmcgdmVjMiB2VXY7XFxuI2VuZGlmXFxuXFxuI2luY2x1ZGUgPHV2Ml9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxtYXBfcGFyc19mcmFnbWVudD5cXG4vLyAjaW5jbHVkZSA8YWxwaGFtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8YW9tYXBfcGFyc19mcmFnbWVudD5cXG4vLyAjaW5jbHVkZSA8bGlnaHRtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8ZW1pc3NpdmVtYXBfcGFyc19mcmFnbWVudD5cXG4vLyAjaW5jbHVkZSA8ZW52bWFwX3BhcnNfZnJhZ21lbnQ+XFxuLy8gI2luY2x1ZGUgPGdyYWRpZW50bWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGZvZ19wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxic2Rmcz5cXG4jaW5jbHVkZSA8bGlnaHRzX3BhcnNfYmVnaW4+XFxuXFxuLy8gI2luY2x1ZGUgPGxpZ2h0c19waG9uZ19wYXJzX2ZyYWdtZW50PlxcbnZhcnlpbmcgdmVjMyB2Vmlld1Bvc2l0aW9uO1xcblxcbiNpZm5kZWYgRkxBVF9TSEFERURcXG4gIHZhcnlpbmcgdmVjMyB2Tm9ybWFsO1xcbiNlbmRpZlxcblxcbiNkZWZpbmUgTWF0ZXJpYWxfTGlnaHRQcm9iZUxPRCggbWF0ZXJpYWwgKSAoMClcXG5cXG4jaW5jbHVkZSA8c2hhZG93bWFwX3BhcnNfZnJhZ21lbnQ+XFxuLy8gI2luY2x1ZGUgPGJ1bXBtYXBfcGFyc19mcmFnbWVudD5cXG5cXG4vLyAjaW5jbHVkZSA8bm9ybWFsbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2lmZGVmIFVTRV9OT1JNQUxNQVBcXG4gIHVuaWZvcm0gc2FtcGxlcjJEIG5vcm1hbE1hcDtcXG4gIHVuaWZvcm0gZmxvYXQgYnVtcFNjYWxlO1xcblxcbiAgLy8gdGhpcyBudW1iZXIgaXMgdmVyeSByYW5kb20sIHRoaXMgaXMgc3RpbGwgYSDlr77lh6bnmYLms5VcXG4gICNkZWZpbmUgVVZfREVSSVZBVElWRV9FUFNJTE9OIDFFLTZcXG5cXG4gIC8vIFBlci1QaXhlbCBUYW5nZW50IFNwYWNlIE5vcm1hbCBNYXBwaW5nXFxuICAvLyBodHRwOi8vaGFja3NvZmxpZmUuYmxvZ3Nwb3QuY2gvMjAwOS8xMS9wZXItcGl4ZWwtdGFuZ2VudC1zcGFjZS1ub3JtYWwtbWFwcGluZy5odG1sXFxuICB2ZWMzIHBlcnR1cmJOb3JtYWwyQXJiKCB2ZWMyIHV2LCB2ZWMzIGV5ZV9wb3MsIHZlYzMgc3VyZl9ub3JtICkge1xcbiAgICAvLyBXb3JrYXJvdW5kIGZvciBBZHJlbm8gM1hYIGRGZCooIHZlYzMgKSBidWcuIFNlZSAjOTk4OFxcbiAgICB2ZWMzIHEwID0gdmVjMyggZEZkeCggZXllX3Bvcy54ICksIGRGZHgoIGV5ZV9wb3MueSApLCBkRmR4KCBleWVfcG9zLnogKSApO1xcbiAgICB2ZWMzIHExID0gdmVjMyggZEZkeSggZXllX3Bvcy54ICksIGRGZHkoIGV5ZV9wb3MueSApLCBkRmR5KCBleWVfcG9zLnogKSApO1xcbiAgICB2ZWMyIHN0MCA9IGRGZHgoIHV2LnN0ICk7XFxuICAgIHZlYzIgc3QxID0gZEZkeSggdXYuc3QgKTtcXG5cXG4gICAgZmxvYXQgc2NhbGUgPSBzaWduKCBzdDEudCAqIHN0MC5zIC0gc3QwLnQgKiBzdDEucyApOyAvLyB3ZSBkbyBub3QgY2FyZSBhYm91dCB0aGUgbWFnbml0dWRlXFxuICAgIHZlYzMgUyA9ICggcTAgKiBzdDEudCAtIHExICogc3QwLnQgKSAqIHNjYWxlO1xcbiAgICB2ZWMzIFQgPSAoIC0gcTAgKiBzdDEucyArIHExICogc3QwLnMgKSAqIHNjYWxlO1xcblxcbiAgICAvLyBXb3JrYXJvdW5kIGZvciB0aGUgaXNzdWUgdGhhdCBoYXBwZW5zIHdoZW4gZGVsdGEgb2YgdXYgPSAwLjBcXG4gICAgaWYgKCBsZW5ndGgoIFMgKSA9PSAwLjAgfHwgbGVuZ3RoKCBUICkgPT0gMC4wICkge1xcbiAgICAgIHJldHVybiBzdXJmX25vcm07XFxuICAgIH1cXG5cXG4gICAgUyA9IG5vcm1hbGl6ZSggUyApO1xcbiAgICBUID0gbm9ybWFsaXplKCBUICk7XFxuICAgIHZlYzMgTiA9IG5vcm1hbGl6ZSggc3VyZl9ub3JtICk7XFxuXFxuICAgIHZlYzMgbWFwTiA9IHRleHR1cmUyRCggbm9ybWFsTWFwLCB1diApLnh5eiAqIDIuMCAtIDEuMDtcXG5cXG4gICAgbWFwTi54eSAqPSBidW1wU2NhbGU7XFxuXFxuICAgICNpZmRlZiBET1VCTEVfU0lERURcXG4gICAgICAvLyBXb3JrYXJvdW5kIGZvciBBZHJlbm8gR1BVcyBnbF9Gcm9udEZhY2luZyBidWcuIFNlZSAjMTU4NTAgYW5kICMxMDMzMVxcbiAgICAgIC8vIGh0dHA6Ly9oYWNrc29mbGlmZS5ibG9nc3BvdC5jb20vMjAwOS8xMS9wZXItcGl4ZWwtdGFuZ2VudC1zcGFjZS1ub3JtYWwtbWFwcGluZy5odG1sP3Nob3dDb21tZW50PTE1MjIyNTQ2Nzc0MzcjYzUwODc1NDUxNDc2OTY3MTU5NDNcXG4gICAgICB2ZWMzIE5mcm9tU1QgPSBjcm9zcyggUywgVCApO1xcbiAgICAgIGlmKCBkb3QoIE5mcm9tU1QsIE4gKSA+IDAuMCApIHtcXG4gICAgICAgIFMgKj0gLTEuMDtcXG4gICAgICAgIFQgKj0gLTEuMDtcXG4gICAgICB9XFxuICAgICNlbHNlXFxuICAgICAgbWFwTi54eSAqPSAoIGZsb2F0KCBnbF9Gcm9udEZhY2luZyApICogMi4wIC0gMS4wICk7XFxuICAgICNlbmRpZlxcblxcbiAgICBtYXQzIHRzbiA9IG1hdDMoIFMsIFQsIE4gKTtcXG5cXG4gICAgcmV0dXJuIG5vcm1hbGl6ZSggdHNuICogbWFwTiApO1xcbiAgfVxcbiNlbmRpZlxcblxcbi8vICNpbmNsdWRlIDxzcGVjdWxhcm1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfcGFyc19mcmFnbWVudD5cXG5cXG4vLyA9PSBsaWdodGluZyBzdHVmZiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxcbmZsb2F0IGdldExpZ2h0SW50ZW5zaXR5KFxcbiAgY29uc3QgaW4gSW5jaWRlbnRMaWdodCBkaXJlY3RMaWdodCxcXG4gIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksXFxuICBjb25zdCBpbiBmbG9hdCBzaGFkb3csXFxuICBjb25zdCBpbiBmbG9hdCBzaGFkaW5nR3JhZGVcXG4pIHtcXG4gIGZsb2F0IGxpZ2h0SW50ZW5zaXR5ID0gZG90KCBnZW9tZXRyeS5ub3JtYWwsIGRpcmVjdExpZ2h0LmRpcmVjdGlvbiApO1xcbiAgbGlnaHRJbnRlbnNpdHkgPSAwLjUgKyAwLjUgKiBsaWdodEludGVuc2l0eTtcXG4gIGxpZ2h0SW50ZW5zaXR5ID0gbGlnaHRJbnRlbnNpdHkgKiBzaGFkb3c7XFxuICBsaWdodEludGVuc2l0eSA9IGxpZ2h0SW50ZW5zaXR5ICogc2hhZGluZ0dyYWRlO1xcbiAgbGlnaHRJbnRlbnNpdHkgPSBsaWdodEludGVuc2l0eSAqIDIuMCAtIDEuMDtcXG4gIHJldHVybiBzbW9vdGhzdGVwKCBzaGFkZVNoaWZ0LCBzaGFkZVNoaWZ0ICsgKCAxLjAgLSBzaGFkZVRvb255ICksIGxpZ2h0SW50ZW5zaXR5ICk7XFxufVxcblxcbnZlYzMgZ2V0TGlnaHRpbmcoIGNvbnN0IGluIHZlYzMgbGlnaHRDb2xvciApIHtcXG4gIHZlYzMgbGlnaHRpbmcgPSBsaWdodENvbG9yO1xcbiAgbGlnaHRpbmcgPSBtaXgoXFxuICAgIGxpZ2h0aW5nLFxcbiAgICB2ZWMzKCBtYXgoIDAuMDAxLCBtYXgoIGxpZ2h0aW5nLngsIG1heCggbGlnaHRpbmcueSwgbGlnaHRpbmcueiApICkgKSApLFxcbiAgICBsaWdodENvbG9yQXR0ZW51YXRpb25cXG4gICk7XFxuXFxuICAjaWZuZGVmIFBIWVNJQ0FMTFlfQ09SUkVDVF9MSUdIVFNcXG4gICAgbGlnaHRpbmcgKj0gUEk7XFxuICAjZW5kaWZcXG5cXG4gIHJldHVybiBsaWdodGluZztcXG59XFxuXFxudmVjMyBnZXREaWZmdXNlKFxcbiAgY29uc3QgaW4gdmVjMyBsaXQsXFxuICBjb25zdCBpbiB2ZWMzIHNoYWRlLFxcbiAgY29uc3QgaW4gZmxvYXQgbGlnaHRJbnRlbnNpdHksXFxuICBjb25zdCBpbiB2ZWMzIGxpZ2h0aW5nXFxuKSB7XFxuICAjaWZkZWYgREVCVUdfTElUU0hBREVSQVRFXFxuICAgIHJldHVybiB2ZWMzKCBCUkRGX0RpZmZ1c2VfTGFtYmVydCggbGlnaHRJbnRlbnNpdHkgKiBsaWdodGluZyApICk7XFxuICAjZW5kaWZcXG5cXG4gIHJldHVybiBsaWdodGluZyAqIEJSREZfRGlmZnVzZV9MYW1iZXJ0KCBtaXgoIHNoYWRlLCBsaXQsIGxpZ2h0SW50ZW5zaXR5ICkgKTtcXG59XFxuXFxudmVjMyBjYWxjRGlyZWN0RGlmZnVzZShcXG4gIGNvbnN0IGluIHZlYzIgdXYsXFxuICBjb25zdCBpbiB2ZWMzIGxpdCxcXG4gIGNvbnN0IGluIHZlYzMgc2hhZGUsXFxuICBpbiBHZW9tZXRyaWNDb250ZXh0IGdlb21ldHJ5LFxcbiAgaW5vdXQgUmVmbGVjdGVkTGlnaHQgcmVmbGVjdGVkTGlnaHRcXG4pIHtcXG4gIEluY2lkZW50TGlnaHQgZGlyZWN0TGlnaHQ7XFxuICB2ZWMzIGxpZ2h0aW5nU3VtID0gdmVjMyggMC4wICk7XFxuXFxuICBmbG9hdCBzaGFkaW5nR3JhZGUgPSAxLjA7XFxuICAjaWZkZWYgVVNFX1NIQURJTkdHUkFERVRFWFRVUkVcXG4gICAgc2hhZGluZ0dyYWRlID0gMS4wIC0gc2hhZGluZ0dyYWRlUmF0ZSAqICggMS4wIC0gdGV4dHVyZTJEKCBzaGFkaW5nR3JhZGVUZXh0dXJlLCB1diApLnIgKTtcXG4gICNlbmRpZlxcblxcbiAgZmxvYXQgcmVjZWl2ZVNoYWRvdyA9IHJlY2VpdmVTaGFkb3dSYXRlO1xcbiAgI2lmZGVmIFVTRV9SRUNFSVZFU0hBRE9XVEVYVFVSRVxcbiAgICByZWNlaXZlU2hhZG93ICo9IHRleHR1cmUyRCggcmVjZWl2ZVNoYWRvd1RleHR1cmUsIHV2ICkuYTtcXG4gICNlbmRpZlxcblxcbiAgI2lmICggTlVNX1BPSU5UX0xJR0hUUyA+IDAgKVxcbiAgICBQb2ludExpZ2h0IHBvaW50TGlnaHQ7XFxuXFxuICAgICNwcmFnbWEgdW5yb2xsX2xvb3BcXG4gICAgZm9yICggaW50IGkgPSAwOyBpIDwgTlVNX1BPSU5UX0xJR0hUUzsgaSArKyApIHtcXG4gICAgICBwb2ludExpZ2h0ID0gcG9pbnRMaWdodHNbIGkgXTtcXG4gICAgICBnZXRQb2ludERpcmVjdExpZ2h0SXJyYWRpYW5jZSggcG9pbnRMaWdodCwgZ2VvbWV0cnksIGRpcmVjdExpZ2h0ICk7XFxuXFxuICAgICAgZmxvYXQgYXR0ZW4gPSAxLjA7XFxuICAgICAgI2lmZGVmIFVTRV9TSEFET1dNQVBcXG4gICAgICAgIGF0dGVuID0gYWxsKCBidmVjMiggcG9pbnRMaWdodC5zaGFkb3csIGRpcmVjdExpZ2h0LnZpc2libGUgKSApID8gZ2V0UG9pbnRTaGFkb3coIHBvaW50U2hhZG93TWFwWyBpIF0sIHBvaW50TGlnaHQuc2hhZG93TWFwU2l6ZSwgcG9pbnRMaWdodC5zaGFkb3dCaWFzLCBwb2ludExpZ2h0LnNoYWRvd1JhZGl1cywgdlBvaW50U2hhZG93Q29vcmRbIGkgXSwgcG9pbnRMaWdodC5zaGFkb3dDYW1lcmFOZWFyLCBwb2ludExpZ2h0LnNoYWRvd0NhbWVyYUZhciApIDogMS4wO1xcbiAgICAgICNlbmRpZlxcblxcbiAgICAgIGZsb2F0IHNoYWRvdyA9IDEuMCAtIHJlY2VpdmVTaGFkb3cgKiAoIDEuMCAtICggMC41ICsgMC41ICogYXR0ZW4gKSApO1xcbiAgICAgIGZsb2F0IGxpZ2h0SW50ZW5zaXR5ID0gZ2V0TGlnaHRJbnRlbnNpdHkoIGRpcmVjdExpZ2h0LCBnZW9tZXRyeSwgc2hhZG93LCBzaGFkaW5nR3JhZGUgKTtcXG4gICAgICB2ZWMzIGxpZ2h0aW5nID0gZ2V0TGlnaHRpbmcoIGRpcmVjdExpZ2h0LmNvbG9yICk7XFxuICAgICAgcmVmbGVjdGVkTGlnaHQuZGlyZWN0RGlmZnVzZSArPSBnZXREaWZmdXNlKCBsaXQsIHNoYWRlLCBsaWdodEludGVuc2l0eSwgbGlnaHRpbmcgKTtcXG4gICAgICBsaWdodGluZ1N1bSArPSBsaWdodGluZztcXG4gICAgfVxcbiAgI2VuZGlmXFxuXFxuICAjaWYgKCBOVU1fU1BPVF9MSUdIVFMgPiAwIClcXG4gICAgU3BvdExpZ2h0IHNwb3RMaWdodDtcXG5cXG4gICAgI3ByYWdtYSB1bnJvbGxfbG9vcFxcbiAgICBmb3IgKCBpbnQgaSA9IDA7IGkgPCBOVU1fU1BPVF9MSUdIVFM7IGkgKysgKSB7XFxuICAgICAgc3BvdExpZ2h0ID0gc3BvdExpZ2h0c1sgaSBdO1xcbiAgICAgIGdldFNwb3REaXJlY3RMaWdodElycmFkaWFuY2UoIHNwb3RMaWdodCwgZ2VvbWV0cnksIGRpcmVjdExpZ2h0ICk7XFxuXFxuICAgICAgZmxvYXQgYXR0ZW4gPSAxLjA7XFxuICAgICAgI2lmZGVmIFVTRV9TSEFET1dNQVBcXG4gICAgICAgIGF0dGVuID0gYWxsKCBidmVjMiggc3BvdExpZ2h0LnNoYWRvdywgZGlyZWN0TGlnaHQudmlzaWJsZSApICkgPyBnZXRTaGFkb3coIHNwb3RTaGFkb3dNYXBbIGkgXSwgc3BvdExpZ2h0LnNoYWRvd01hcFNpemUsIHNwb3RMaWdodC5zaGFkb3dCaWFzLCBzcG90TGlnaHQuc2hhZG93UmFkaXVzLCB2U3BvdFNoYWRvd0Nvb3JkWyBpIF0gKSA6IDEuMDtcXG4gICAgICAjZW5kaWZcXG5cXG4gICAgICBmbG9hdCBzaGFkb3cgPSAxLjAgLSByZWNlaXZlU2hhZG93ICogKCAxLjAgLSAoIDAuNSArIDAuNSAqIGF0dGVuICkgKTtcXG4gICAgICBmbG9hdCBsaWdodEludGVuc2l0eSA9IGdldExpZ2h0SW50ZW5zaXR5KCBkaXJlY3RMaWdodCwgZ2VvbWV0cnksIHNoYWRvdywgc2hhZGluZ0dyYWRlICk7XFxuICAgICAgdmVjMyBsaWdodGluZyA9IGdldExpZ2h0aW5nKCBkaXJlY3RMaWdodC5jb2xvciApO1xcbiAgICAgIHJlZmxlY3RlZExpZ2h0LmRpcmVjdERpZmZ1c2UgKz0gZ2V0RGlmZnVzZSggbGl0LCBzaGFkZSwgbGlnaHRJbnRlbnNpdHksIGxpZ2h0aW5nICk7XFxuICAgICAgbGlnaHRpbmdTdW0gKz0gbGlnaHRpbmc7XFxuICAgIH1cXG4gICNlbmRpZlxcblxcbiAgI2lmICggTlVNX0RJUl9MSUdIVFMgPiAwIClcXG4gICAgRGlyZWN0aW9uYWxMaWdodCBkaXJlY3Rpb25hbExpZ2h0O1xcblxcbiAgICAjcHJhZ21hIHVucm9sbF9sb29wXFxuICAgIGZvciAoIGludCBpID0gMDsgaSA8IE5VTV9ESVJfTElHSFRTOyBpICsrICkge1xcbiAgICAgIGRpcmVjdGlvbmFsTGlnaHQgPSBkaXJlY3Rpb25hbExpZ2h0c1sgaSBdO1xcbiAgICAgIGdldERpcmVjdGlvbmFsRGlyZWN0TGlnaHRJcnJhZGlhbmNlKCBkaXJlY3Rpb25hbExpZ2h0LCBnZW9tZXRyeSwgZGlyZWN0TGlnaHQgKTtcXG5cXG4gICAgICBmbG9hdCBhdHRlbiA9IDEuMDtcXG4gICAgICAjaWZkZWYgVVNFX1NIQURPV01BUFxcbiAgICAgICAgYXR0ZW4gPSBhbGwoIGJ2ZWMyKCBkaXJlY3Rpb25hbExpZ2h0LnNoYWRvdywgZGlyZWN0TGlnaHQudmlzaWJsZSApICkgPyBnZXRTaGFkb3coIGRpcmVjdGlvbmFsU2hhZG93TWFwWyBpIF0sIGRpcmVjdGlvbmFsTGlnaHQuc2hhZG93TWFwU2l6ZSwgZGlyZWN0aW9uYWxMaWdodC5zaGFkb3dCaWFzLCBkaXJlY3Rpb25hbExpZ2h0LnNoYWRvd1JhZGl1cywgdkRpcmVjdGlvbmFsU2hhZG93Q29vcmRbIGkgXSApIDogMS4wO1xcbiAgICAgICNlbmRpZlxcblxcbiAgICAgIGZsb2F0IHNoYWRvdyA9IDEuMCAtIHJlY2VpdmVTaGFkb3cgKiAoIDEuMCAtICggMC41ICsgMC41ICogYXR0ZW4gKSApO1xcbiAgICAgIGZsb2F0IGxpZ2h0SW50ZW5zaXR5ID0gZ2V0TGlnaHRJbnRlbnNpdHkoIGRpcmVjdExpZ2h0LCBnZW9tZXRyeSwgc2hhZG93LCBzaGFkaW5nR3JhZGUgKTtcXG4gICAgICB2ZWMzIGxpZ2h0aW5nID0gZ2V0TGlnaHRpbmcoIGRpcmVjdExpZ2h0LmNvbG9yICk7XFxuICAgICAgcmVmbGVjdGVkTGlnaHQuZGlyZWN0RGlmZnVzZSArPSBnZXREaWZmdXNlKCBsaXQsIHNoYWRlLCBsaWdodEludGVuc2l0eSwgbGlnaHRpbmcgKTtcXG4gICAgICBsaWdodGluZ1N1bSArPSBsaWdodGluZztcXG4gICAgfVxcbiAgI2VuZGlmXFxuXFxuICByZXR1cm4gbGlnaHRpbmdTdW07XFxufVxcblxcbi8vID09IHBvc3QgY29ycmVjdGlvbiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XFxudm9pZCBwb3N0Q29ycmVjdGlvbigpIHtcXG4gICNpbmNsdWRlIDx0b25lbWFwcGluZ19mcmFnbWVudD5cXG4gICNpbmNsdWRlIDxlbmNvZGluZ3NfZnJhZ21lbnQ+XFxuICAjaW5jbHVkZSA8Zm9nX2ZyYWdtZW50PlxcbiAgI2luY2x1ZGUgPHByZW11bHRpcGxpZWRfYWxwaGFfZnJhZ21lbnQ+XFxuICAjaW5jbHVkZSA8ZGl0aGVyaW5nX2ZyYWdtZW50Plxcbn1cXG5cXG4vLyA9PSBtYWluIHByb2NlZHVyZSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxcbnZvaWQgbWFpbigpIHtcXG4gICNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfZnJhZ21lbnQ+XFxuXFxuICB2ZWMyIHV2ID0gdmVjMigwLjUsIDAuNSk7XFxuXFxuICAjaWYgZGVmaW5lZCggVVNFX01BUCApIHx8IGRlZmluZWQoIFVTRV9TSEFERVRFWFRVUkUgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQICkgfHwgZGVmaW5lZCggVVNFX1JFQ0VJVkVTSEFET1dURVhUVVJFICkgfHwgZGVmaW5lZCggVVNFX1NIQURJTkdHUkFERVRFWFRVUkUgKSB8fCBkZWZpbmVkKCBVU0VfUklNVEVYVFVSRSApIHx8IGRlZmluZWQoIFVTRV9FTUlTU0lWRU1BUCApIHx8IGRlZmluZWQoIFVTRV9PVVRMSU5FV0lEVEhURVhUVVJFICkgfHwgZGVmaW5lZCggVVNFX1VWQU5JTU1BU0tURVhUVVJFIClcXG4gICAgdXYgPSB2VXY7XFxuXFxuICAgIGZsb2F0IHV2QW5pbU1hc2sgPSAxLjA7XFxuICAgICNpZmRlZiBVU0VfVVZBTklNTUFTS1RFWFRVUkVcXG4gICAgICB1dkFuaW1NYXNrID0gdGV4dHVyZTJEKCB1dkFuaW1NYXNrVGV4dHVyZSwgdXYgKS54O1xcbiAgICAjZW5kaWZcXG5cXG4gICAgdXYgPSB1diArIHZlYzIoIHV2QW5pbU9mZnNldFgsIHV2QW5pbU9mZnNldFkgKSAqIHV2QW5pbU1hc2s7XFxuICAgIGZsb2F0IHV2Um90Q29zID0gY29zKCB1dkFuaW1UaGV0YSAqIHV2QW5pbU1hc2sgKTtcXG4gICAgZmxvYXQgdXZSb3RTaW4gPSBzaW4oIHV2QW5pbVRoZXRhICogdXZBbmltTWFzayApO1xcbiAgICB1diA9IG1hdDIoIHV2Um90Q29zLCB1dlJvdFNpbiwgLXV2Um90U2luLCB1dlJvdENvcyApICogKCB1diAtIDAuNSApICsgMC41O1xcbiAgI2VuZGlmXFxuXFxuICAjaWZkZWYgREVCVUdfVVZcXG4gICAgZ2xfRnJhZ0NvbG9yID0gdmVjNCggMC4wLCAwLjAsIDAuMCwgMS4wICk7XFxuICAgICNpZiBkZWZpbmVkKCBVU0VfTUFQICkgfHwgZGVmaW5lZCggVVNFX1NIQURFVEVYVFVSRSApIHx8IGRlZmluZWQoIFVTRV9OT1JNQUxNQVAgKSB8fCBkZWZpbmVkKCBVU0VfUkVDRUlWRVNIQURPV1RFWFRVUkUgKSB8fCBkZWZpbmVkKCBVU0VfU0hBRElOR0dSQURFVEVYVFVSRSApIHx8IGRlZmluZWQoIFVTRV9SSU1URVhUVVJFICkgfHwgZGVmaW5lZCggVVNFX0VNSVNTSVZFTUFQICkgfHwgZGVmaW5lZCggVVNFX09VVExJTkVXSURUSFRFWFRVUkUgKSB8fCBkZWZpbmVkKCBVU0VfVVZBTklNTUFTS1RFWFRVUkUgKVxcbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoIHV2LCAwLjAsIDEuMCApO1xcbiAgICAjZW5kaWZcXG4gICAgcmV0dXJuO1xcbiAgI2VuZGlmXFxuXFxuICB2ZWM0IGRpZmZ1c2VDb2xvciA9IHZlYzQoIGNvbG9yLCBjb2xvckFscGhhICk7XFxuICBSZWZsZWN0ZWRMaWdodCByZWZsZWN0ZWRMaWdodCA9IFJlZmxlY3RlZExpZ2h0KCB2ZWMzKCAwLjAgKSwgdmVjMyggMC4wICksIHZlYzMoIDAuMCApLCB2ZWMzKCAwLjAgKSApO1xcbiAgdmVjMyB0b3RhbEVtaXNzaXZlUmFkaWFuY2UgPSBlbWlzc2lvbkNvbG9yO1xcblxcbiAgI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX2ZyYWdtZW50PlxcblxcbiAgLy8gI2luY2x1ZGUgPG1hcF9mcmFnbWVudD5cXG4gICNpZmRlZiBVU0VfTUFQXFxuICAgIGRpZmZ1c2VDb2xvciAqPSBtYXBUZXhlbFRvTGluZWFyKCB0ZXh0dXJlMkQoIG1hcCwgdXYgKSApO1xcbiAgI2VuZGlmXFxuXFxuICAjaW5jbHVkZSA8Y29sb3JfZnJhZ21lbnQ+XFxuICAvLyAjaW5jbHVkZSA8YWxwaGFtYXBfZnJhZ21lbnQ+XFxuXFxuICAvLyAtLSBNVG9vbjogYWxwaGEgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIC8vICNpbmNsdWRlIDxhbHBoYXRlc3RfZnJhZ21lbnQ+XFxuICAjaWZkZWYgQkxFTkRNT0RFX0NVVE9VVFxcbiAgICBpZiAoIGRpZmZ1c2VDb2xvci5hIDw9IGN1dG9mZiApIHsgZGlzY2FyZDsgfVxcbiAgICBkaWZmdXNlQ29sb3IuYSA9IDEuMDtcXG4gICNlbmRpZlxcblxcbiAgI2lmZGVmIEJMRU5ETU9ERV9PUEFRVUVcXG4gICAgZGlmZnVzZUNvbG9yLmEgPSAxLjA7XFxuICAjZW5kaWZcXG5cXG4gICNpZiBkZWZpbmVkKCBPVVRMSU5FICkgJiYgZGVmaW5lZCggT1VUTElORV9DT0xPUl9GSVhFRCApIC8vIG9taXR0aW5nIERlYnVnTW9kZVxcbiAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KCBvdXRsaW5lQ29sb3IsIGRpZmZ1c2VDb2xvci5hICk7XFxuICAgIHBvc3RDb3JyZWN0aW9uKCk7XFxuICAgIHJldHVybjtcXG4gICNlbmRpZlxcblxcbiAgLy8gI2luY2x1ZGUgPHNwZWN1bGFybWFwX2ZyYWdtZW50PlxcbiAgI2luY2x1ZGUgPG5vcm1hbF9mcmFnbWVudF9iZWdpbj5cXG5cXG4gICNpZmRlZiBPVVRMSU5FXFxuICAgIG5vcm1hbCAqPSAtMS4wO1xcbiAgI2VuZGlmXFxuXFxuICAvLyAjaW5jbHVkZSA8bm9ybWFsX2ZyYWdtZW50X21hcHM+XFxuICAjaWZkZWYgVVNFX05PUk1BTE1BUFxcbiAgICBub3JtYWwgPSBwZXJ0dXJiTm9ybWFsMkFyYiggdXYsIC12Vmlld1Bvc2l0aW9uLCBub3JtYWwgKTtcXG4gICNlbmRpZlxcblxcbiAgLy8gI2luY2x1ZGUgPGVtaXNzaXZlbWFwX2ZyYWdtZW50PlxcbiAgI2lmZGVmIFVTRV9FTUlTU0lWRU1BUFxcbiAgICB0b3RhbEVtaXNzaXZlUmFkaWFuY2UgKj0gZW1pc3NpdmVNYXBUZXhlbFRvTGluZWFyKCB0ZXh0dXJlMkQoIGVtaXNzaXZlTWFwLCB1diApICkucmdiO1xcbiAgI2VuZGlmXFxuXFxuICAjaWZkZWYgREVCVUdfTk9STUFMXFxuICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoIDAuNSArIDAuNSAqIG5vcm1hbCwgMS4wICk7XFxuICAgIHJldHVybjtcXG4gICNlbmRpZlxcblxcbiAgLy8gLS0gTVRvb246IGxpZ2h0aW5nIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAvLyBhY2N1bXVsYXRpb25cXG4gIC8vICNpbmNsdWRlIDxsaWdodHNfcGhvbmdfZnJhZ21lbnQ+XFxuICAvLyAjaW5jbHVkZSA8bGlnaHRzX2ZyYWdtZW50X2JlZ2luPlxcbiAgdmVjMyBsaXQgPSBkaWZmdXNlQ29sb3IucmdiO1xcbiAgdmVjMyBzaGFkZSA9IHNoYWRlQ29sb3I7XFxuICAjaWZkZWYgVVNFX1NIQURFVEVYVFVSRVxcbiAgICBzaGFkZSAqPSBzaGFkZVRleHR1cmVUZXhlbFRvTGluZWFyKCB0ZXh0dXJlMkQoIHNoYWRlVGV4dHVyZSwgdXYgKSApLnJnYjtcXG4gICNlbmRpZlxcblxcbiAgR2VvbWV0cmljQ29udGV4dCBnZW9tZXRyeTtcXG5cXG4gIGdlb21ldHJ5LnBvc2l0aW9uID0gLSB2Vmlld1Bvc2l0aW9uO1xcbiAgZ2VvbWV0cnkubm9ybWFsID0gbm9ybWFsO1xcbiAgZ2VvbWV0cnkudmlld0RpciA9IG5vcm1hbGl6ZSggdlZpZXdQb3NpdGlvbiApO1xcblxcbiAgdmVjMyBsaWdodGluZyA9IGNhbGNEaXJlY3REaWZmdXNlKCB1diwgZGlmZnVzZUNvbG9yLnJnYiwgc2hhZGUsIGdlb21ldHJ5LCByZWZsZWN0ZWRMaWdodCApO1xcblxcbiAgdmVjMyBpcnJhZGlhbmNlID0gZ2V0QW1iaWVudExpZ2h0SXJyYWRpYW5jZSggYW1iaWVudExpZ2h0Q29sb3IgKTtcXG4gICNpZiAoIE5VTV9IRU1JX0xJR0hUUyA+IDAgKVxcbiAgICAjcHJhZ21hIHVucm9sbF9sb29wXFxuICAgIGZvciAoIGludCBpID0gMDsgaSA8IE5VTV9IRU1JX0xJR0hUUzsgaSArKyApIHtcXG4gICAgICBpcnJhZGlhbmNlICs9IGdldEhlbWlzcGhlcmVMaWdodElycmFkaWFuY2UoIGhlbWlzcGhlcmVMaWdodHNbIGkgXSwgZ2VvbWV0cnkgKTtcXG4gICAgfVxcbiAgI2VuZGlmXFxuXFxuICAvLyAjaW5jbHVkZSA8bGlnaHRzX2ZyYWdtZW50X21hcHM+XFxuICAjaWZkZWYgVVNFX0xJR0hUTUFQXFxuICAgIHZlYzMgbGlnaHRNYXBJcnJhZGlhbmNlID0gdGV4dHVyZTJEKCBsaWdodE1hcCwgdlV2MiApLnJnYiAqIGxpZ2h0TWFwSW50ZW5zaXR5O1xcbiAgICAjaWZuZGVmIFBIWVNJQ0FMTFlfQ09SUkVDVF9MSUdIVFNcXG4gICAgICBsaWdodE1hcElycmFkaWFuY2UgKj0gUEk7IC8vIGZhY3RvciBvZiBQSSBzaG91bGQgbm90IGJlIHByZXNlbnQ7IGluY2x1ZGVkIGhlcmUgdG8gcHJldmVudCBicmVha2FnZVxcbiAgICAjZW5kaWZcXG4gICAgaXJyYWRpYW5jZSArPSBsaWdodE1hcElycmFkaWFuY2U7XFxuICAjZW5kaWZcXG5cXG4gIC8vICNpbmNsdWRlIDxsaWdodHNfZnJhZ21lbnRfZW5kPlxcbiAgcmVmbGVjdGVkTGlnaHQuaW5kaXJlY3REaWZmdXNlICs9IGluZGlyZWN0TGlnaHRJbnRlbnNpdHkgKiBpcnJhZGlhbmNlICogQlJERl9EaWZmdXNlX0xhbWJlcnQoIGxpdCApO1xcblxcbiAgLy8gbW9kdWxhdGlvblxcbiAgI2luY2x1ZGUgPGFvbWFwX2ZyYWdtZW50PlxcblxcbiAgdmVjMyBjb2wgPSByZWZsZWN0ZWRMaWdodC5kaXJlY3REaWZmdXNlICsgcmVmbGVjdGVkTGlnaHQuaW5kaXJlY3REaWZmdXNlO1xcblxcbiAgI2lmIGRlZmluZWQoIE9VVExJTkUgKSAmJiBkZWZpbmVkKCBPVVRMSU5FX0NPTE9SX01JWEVEICkgLy8gb21pdHRpbmcgRGVidWdNb2RlXFxuICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoXFxuICAgICAgb3V0bGluZUNvbG9yLnJnYiAqIG1peCggdmVjMyggMS4wICksIGNvbCwgb3V0bGluZUxpZ2h0aW5nTWl4ICksXFxuICAgICAgZGlmZnVzZUNvbG9yLmFcXG4gICAgKTtcXG4gICAgcG9zdENvcnJlY3Rpb24oKTtcXG4gICAgcmV0dXJuO1xcbiAgI2VuZGlmXFxuXFxuICAvLyAtLSBNVG9vbjogcGFyYW1ldHJpYyByaW0gbGlnaHRpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIHZlYzMgdmlld0RpciA9IG5vcm1hbGl6ZSggdlZpZXdQb3NpdGlvbiApO1xcbiAgdmVjMyByaW1NaXggPSBtaXgodmVjMygxLjApLCBsaWdodGluZyArIGluZGlyZWN0TGlnaHRJbnRlbnNpdHkgKiBpcnJhZGlhbmNlLCByaW1MaWdodGluZ01peCk7XFxuICB2ZWMzIHJpbSA9IHJpbUNvbG9yICogcG93KCBzYXR1cmF0ZSggMS4wIC0gZG90KCB2aWV3RGlyLCBub3JtYWwgKSArIHJpbUxpZnQgKSwgcmltRnJlc25lbFBvd2VyICk7XFxuICAjaWZkZWYgVVNFX1JJTVRFWFRVUkVcXG4gICAgcmltICo9IHRleHR1cmUyRCggcmltVGV4dHVyZSwgdXYgKS5yZ2I7XFxuICAjZW5kaWZcXG4gIGNvbCArPSByaW07XFxuXFxuICAvLyAtLSBNVG9vbjogYWRkaXRpdmUgbWF0Y2FwIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gICNpZmRlZiBVU0VfU1BIRVJFQUREXFxuICAgIHtcXG4gICAgICB2ZWMzIHggPSBub3JtYWxpemUoIHZlYzMoIHZpZXdEaXIueiwgMC4wLCAtdmlld0Rpci54ICkgKTtcXG4gICAgICB2ZWMzIHkgPSBjcm9zcyggdmlld0RpciwgeCApOyAvLyBndWFyYW50ZWVkIHRvIGJlIG5vcm1hbGl6ZWRcXG4gICAgICB2ZWMyIHNwaGVyZVV2ID0gMC41ICsgMC41ICogdmVjMiggZG90KCB4LCBub3JtYWwgKSwgLWRvdCggeSwgbm9ybWFsICkgKTtcXG4gICAgICB2ZWMzIG1hdGNhcCA9IHNwaGVyZUFkZFRleGVsVG9MaW5lYXIoIHRleHR1cmUyRCggc3BoZXJlQWRkLCBzcGhlcmVVdiApICkueHl6O1xcbiAgICAgIGNvbCArPSBtYXRjYXA7XFxuICAgIH1cXG4gICNlbmRpZlxcblxcbiAgLy8gLS0gTVRvb246IEVtaXNzaW9uIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICBjb2wgKz0gdG90YWxFbWlzc2l2ZVJhZGlhbmNlO1xcblxcbiAgLy8gI2luY2x1ZGUgPGVudm1hcF9mcmFnbWVudD5cXG5cXG4gIC8vIC0tIEFsbW9zdCBkb25lISAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgZ2xfRnJhZ0NvbG9yID0gdmVjNCggY29sLCBkaWZmdXNlQ29sb3IuYSApO1xcbiAgcG9zdENvcnJlY3Rpb24oKTtcXG59XCIiLCJleHBvcnQgZGVmYXVsdCBcIi8vICNkZWZpbmUgUEhPTkdcXG5cXG52YXJ5aW5nIHZlYzMgdlZpZXdQb3NpdGlvbjtcXG5cXG4jaWZuZGVmIEZMQVRfU0hBREVEXFxuICB2YXJ5aW5nIHZlYzMgdk5vcm1hbDtcXG4jZW5kaWZcXG5cXG4jaW5jbHVkZSA8Y29tbW9uPlxcblxcbi8vICNpbmNsdWRlIDx1dl9wYXJzX3ZlcnRleD5cXG4jaWYgZGVmaW5lZCggVVNFX01BUCApIHx8IGRlZmluZWQoIFVTRV9TSEFERVRFWFRVUkUgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQICkgfHwgZGVmaW5lZCggVVNFX1JFQ0VJVkVTSEFET1dURVhUVVJFICkgfHwgZGVmaW5lZCggVVNFX1NIQURJTkdHUkFERVRFWFRVUkUgKSB8fCBkZWZpbmVkKCBVU0VfUklNVEVYVFVSRSApIHx8IGRlZmluZWQoIFVTRV9FTUlTU0lWRU1BUCApIHx8IGRlZmluZWQoIFVTRV9PVVRMSU5FV0lEVEhURVhUVVJFICkgfHwgZGVmaW5lZCggVVNFX1VWQU5JTU1BU0tURVhUVVJFIClcXG4gIHZhcnlpbmcgdmVjMiB2VXY7XFxuICB1bmlmb3JtIHZlYzQgbWFpblRleF9TVDtcXG4jZW5kaWZcXG5cXG4jaW5jbHVkZSA8dXYyX3BhcnNfdmVydGV4Plxcbi8vICNpbmNsdWRlIDxkaXNwbGFjZW1lbnRtYXBfcGFyc192ZXJ0ZXg+XFxuLy8gI2luY2x1ZGUgPGVudm1hcF9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8Y29sb3JfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPGZvZ19wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8bW9ycGh0YXJnZXRfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPHNraW5uaW5nX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxzaGFkb3dtYXBfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfcGFyc192ZXJ0ZXg+XFxuXFxuI2lmZGVmIFVTRV9PVVRMSU5FV0lEVEhURVhUVVJFXFxuICB1bmlmb3JtIHNhbXBsZXIyRCBvdXRsaW5lV2lkdGhUZXh0dXJlO1xcbiNlbmRpZlxcblxcbnVuaWZvcm0gZmxvYXQgb3V0bGluZVdpZHRoO1xcbnVuaWZvcm0gZmxvYXQgb3V0bGluZVNjYWxlZE1heERpc3RhbmNlO1xcblxcbnZvaWQgbWFpbigpIHtcXG5cXG4gIC8vICNpbmNsdWRlIDx1dl92ZXJ0ZXg+XFxuICAjaWYgZGVmaW5lZCggVVNFX01BUCApIHx8IGRlZmluZWQoIFVTRV9TSEFERVRFWFRVUkUgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQICkgfHwgZGVmaW5lZCggVVNFX1JFQ0VJVkVTSEFET1dURVhUVVJFICkgfHwgZGVmaW5lZCggVVNFX1NIQURJTkdHUkFERVRFWFRVUkUgKSB8fCBkZWZpbmVkKCBVU0VfUklNVEVYVFVSRSApIHx8IGRlZmluZWQoIFVTRV9FTUlTU0lWRU1BUCApIHx8IGRlZmluZWQoIFVTRV9PVVRMSU5FV0lEVEhURVhUVVJFICkgfHwgZGVmaW5lZCggVVNFX1VWQU5JTU1BU0tURVhUVVJFIClcXG4gICAgdlV2ID0gdmVjMiggbWFpblRleF9TVC5wICogdXYueCArIG1haW5UZXhfU1QucywgbWFpblRleF9TVC5xICogdXYueSArIG1haW5UZXhfU1QudCApO1xcbiAgI2VuZGlmXFxuXFxuICAjaW5jbHVkZSA8dXYyX3ZlcnRleD5cXG4gICNpbmNsdWRlIDxjb2xvcl92ZXJ0ZXg+XFxuXFxuICAjaW5jbHVkZSA8YmVnaW5ub3JtYWxfdmVydGV4PlxcbiAgI2luY2x1ZGUgPG1vcnBobm9ybWFsX3ZlcnRleD5cXG4gICNpbmNsdWRlIDxza2luYmFzZV92ZXJ0ZXg+XFxuICAjaW5jbHVkZSA8c2tpbm5vcm1hbF92ZXJ0ZXg+XFxuICAjaW5jbHVkZSA8ZGVmYXVsdG5vcm1hbF92ZXJ0ZXg+XFxuXFxuICAjaWZuZGVmIEZMQVRfU0hBREVEIC8vIE5vcm1hbCBjb21wdXRlZCB3aXRoIGRlcml2YXRpdmVzIHdoZW4gRkxBVF9TSEFERURcXG4gICAgdk5vcm1hbCA9IG5vcm1hbGl6ZSggdHJhbnNmb3JtZWROb3JtYWwgKTtcXG4gICNlbmRpZlxcblxcbiAgI2luY2x1ZGUgPGJlZ2luX3ZlcnRleD5cXG5cXG4gICNpbmNsdWRlIDxtb3JwaHRhcmdldF92ZXJ0ZXg+XFxuICAjaW5jbHVkZSA8c2tpbm5pbmdfdmVydGV4PlxcbiAgLy8gI2luY2x1ZGUgPGRpc3BsYWNlbWVudG1hcF92ZXJ0ZXg+XFxuICAjaW5jbHVkZSA8cHJvamVjdF92ZXJ0ZXg+XFxuICAjaW5jbHVkZSA8bG9nZGVwdGhidWZfdmVydGV4PlxcbiAgI2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc192ZXJ0ZXg+XFxuXFxuICB2Vmlld1Bvc2l0aW9uID0gLSBtdlBvc2l0aW9uLnh5ejtcXG5cXG4gIGZsb2F0IG91dGxpbmVUZXggPSAxLjA7XFxuXFxuICAjaWZkZWYgT1VUTElORVxcbiAgICAjaWZkZWYgVVNFX09VVExJTkVXSURUSFRFWFRVUkVcXG4gICAgICBvdXRsaW5lVGV4ID0gdGV4dHVyZTJEKCBvdXRsaW5lV2lkdGhUZXh0dXJlLCB2VXYgKS5yO1xcbiAgICAjZW5kaWZcXG5cXG4gICAgI2lmZGVmIE9VVExJTkVfV0lEVEhfV09STERcXG4gICAgICB2ZWMzIG91dGxpbmVPZmZzZXQgPSAwLjAxICogb3V0bGluZVdpZHRoICogb3V0bGluZVRleCAqIG5vcm1hbGl6ZSggb2JqZWN0Tm9ybWFsICk7XFxuICAgICAgZ2xfUG9zaXRpb24gKz0gcHJvamVjdGlvbk1hdHJpeCAqIG1vZGVsVmlld01hdHJpeCAqIHZlYzQoIG91dGxpbmVPZmZzZXQsIDAuMCApO1xcbiAgICAjZW5kaWZcXG5cXG4gICAgI2lmZGVmIE9VVExJTkVfV0lEVEhfU0NSRUVOXFxuICAgICAgdmVjMyBjbGlwTm9ybWFsID0gKCBwcm9qZWN0aW9uTWF0cml4ICogbW9kZWxWaWV3TWF0cml4ICogdmVjNCggbm9ybWFsaXplKCBvYmplY3ROb3JtYWwgKSwgMC4wICkgKS54eXo7XFxuICAgICAgdmVjMiBwcm9qZWN0ZWROb3JtYWwgPSBub3JtYWxpemUoIGNsaXBOb3JtYWwueHkgKTtcXG4gICAgICBwcm9qZWN0ZWROb3JtYWwgKj0gbWluKCBnbF9Qb3NpdGlvbi53LCBvdXRsaW5lU2NhbGVkTWF4RGlzdGFuY2UgKTtcXG4gICAgICBwcm9qZWN0ZWROb3JtYWwueCAqPSBwcm9qZWN0aW9uTWF0cml4WyAwIF0ueCAvIHByb2plY3Rpb25NYXRyaXhbIDEgXS55O1xcbiAgICAgIGdsX1Bvc2l0aW9uLnh5ICs9IDAuMDEgKiBvdXRsaW5lV2lkdGggKiBvdXRsaW5lVGV4ICogcHJvamVjdGVkTm9ybWFsLnh5O1xcbiAgICAjZW5kaWZcXG5cXG4gICAgZ2xfUG9zaXRpb24ueiArPSAxRS02ICogZ2xfUG9zaXRpb24udzsgLy8gYW50aS1hcnRpZmFjdCBtYWdpY1xcbiAgI2VuZGlmXFxuXFxuICAjaW5jbHVkZSA8d29ybGRwb3NfdmVydGV4PlxcbiAgLy8gI2luY2x1ZGUgPGVudm1hcF92ZXJ0ZXg+XFxuICAjaW5jbHVkZSA8c2hhZG93bWFwX3ZlcnRleD5cXG4gICNpbmNsdWRlIDxmb2dfdmVydGV4Plxcblxcbn1cIiIsImV4cG9ydCBkZWZhdWx0IFwiI2lmZGVmIFJFTkRFUlRZUEVfQ1VUT1VUXFxuICB1bmlmb3JtIGZsb2F0IGN1dG9mZjtcXG4jZW5kaWZcXG5cXG4jaW5jbHVkZSA8Y29tbW9uPlxcbiNpbmNsdWRlIDxjb2xvcl9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDx1dl9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDx1djJfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8bWFwX3BhcnNfZnJhZ21lbnQ+XFxuLy8gI2luY2x1ZGUgPGFscGhhbWFwX3BhcnNfZnJhZ21lbnQ+XFxuLy8gI2luY2x1ZGUgPGFvbWFwX3BhcnNfZnJhZ21lbnQ+XFxuLy8gI2luY2x1ZGUgPGxpZ2h0bWFwX3BhcnNfZnJhZ21lbnQ+XFxuLy8gI2luY2x1ZGUgPGVudm1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxmb2dfcGFyc19mcmFnbWVudD5cXG4vLyAjaW5jbHVkZSA8c3BlY3VsYXJtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8bG9nZGVwdGhidWZfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX3BhcnNfZnJhZ21lbnQ+XFxuXFxuLy8gPT0gbWFpbiBwcm9jZWR1cmUgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cXG52b2lkIG1haW4oKSB7XFxuICAjaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX2ZyYWdtZW50PlxcblxcbiAgdmVjNCBkaWZmdXNlQ29sb3IgPSB2ZWM0KCAxLjAgKTtcXG5cXG4gICNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl9mcmFnbWVudD5cXG5cXG4gIC8vICNpbmNsdWRlIDxtYXBfZnJhZ21lbnQ+XFxuICAjaWZkZWYgVVNFX01BUFxcbiAgICBkaWZmdXNlQ29sb3IgKj0gbWFwVGV4ZWxUb0xpbmVhciggdGV4dHVyZTJEKCBtYXAsIHZVdiApICk7XFxuICAjZW5kaWZcXG5cXG4gICNpbmNsdWRlIDxjb2xvcl9mcmFnbWVudD5cXG4gIC8vICNpbmNsdWRlIDxhbHBoYW1hcF9mcmFnbWVudD5cXG5cXG4gIC8vIE1Ub29uOiBhbHBoYVxcbiAgLy8gI2luY2x1ZGUgPGFscGhhdGVzdF9mcmFnbWVudD5cXG4gICNpZmRlZiBSRU5ERVJUWVBFX0NVVE9VVFxcbiAgICBpZiAoIGRpZmZ1c2VDb2xvci5hIDw9IGN1dG9mZiApIHsgZGlzY2FyZDsgfVxcbiAgICBkaWZmdXNlQ29sb3IuYSA9IDEuMDtcXG4gICNlbmRpZlxcblxcbiAgI2lmZGVmIFJFTkRFUlRZUEVfT1BBUVVFXFxuICAgIGRpZmZ1c2VDb2xvci5hID0gMS4wO1xcbiAgI2VuZGlmXFxuXFxuICAvLyAjaW5jbHVkZSA8c3BlY3VsYXJtYXBfZnJhZ21lbnQ+XFxuXFxuICBSZWZsZWN0ZWRMaWdodCByZWZsZWN0ZWRMaWdodCA9IFJlZmxlY3RlZExpZ2h0KCB2ZWMzKCAwLjAgKSwgdmVjMyggMC4wICksIHZlYzMoIDAuMCApLCB2ZWMzKCAwLjAgKSApO1xcblxcbiAgLy8gYWNjdW11bGF0aW9uIChiYWtlZCBpbmRpcmVjdCBsaWdodGluZyBvbmx5KVxcbiAgI2lmZGVmIFVTRV9MSUdIVE1BUFxcbiAgICByZWZsZWN0ZWRMaWdodC5pbmRpcmVjdERpZmZ1c2UgKz0gdGV4dHVyZTJEKCBsaWdodE1hcCwgdlV2MiApLnh5eiAqIGxpZ2h0TWFwSW50ZW5zaXR5O1xcbiAgI2Vsc2VcXG4gICAgcmVmbGVjdGVkTGlnaHQuaW5kaXJlY3REaWZmdXNlICs9IHZlYzMoIDEuMCApO1xcbiAgI2VuZGlmXFxuXFxuICAvLyBtb2R1bGF0aW9uXFxuICAvLyAjaW5jbHVkZSA8YW9tYXBfZnJhZ21lbnQ+XFxuXFxuICByZWZsZWN0ZWRMaWdodC5pbmRpcmVjdERpZmZ1c2UgKj0gZGlmZnVzZUNvbG9yLnJnYjtcXG4gIHZlYzMgb3V0Z29pbmdMaWdodCA9IHJlZmxlY3RlZExpZ2h0LmluZGlyZWN0RGlmZnVzZTtcXG5cXG4gIC8vICNpbmNsdWRlIDxlbnZtYXBfZnJhZ21lbnQ+XFxuXFxuICBnbF9GcmFnQ29sb3IgPSB2ZWM0KCBvdXRnb2luZ0xpZ2h0LCBkaWZmdXNlQ29sb3IuYSApO1xcblxcbiAgI2luY2x1ZGUgPHByZW11bHRpcGxpZWRfYWxwaGFfZnJhZ21lbnQ+XFxuICAjaW5jbHVkZSA8dG9uZW1hcHBpbmdfZnJhZ21lbnQ+XFxuICAjaW5jbHVkZSA8ZW5jb2RpbmdzX2ZyYWdtZW50PlxcbiAgI2luY2x1ZGUgPGZvZ19mcmFnbWVudD5cXG59XCIiLCJleHBvcnQgZGVmYXVsdCBcIiNpbmNsdWRlIDxjb21tb24+XFxuXFxuLy8gI2luY2x1ZGUgPHV2X3BhcnNfdmVydGV4PlxcbiNpZmRlZiBVU0VfTUFQXFxuICB2YXJ5aW5nIHZlYzIgdlV2O1xcbiAgdW5pZm9ybSB2ZWM0IG1haW5UZXhfU1Q7XFxuI2VuZGlmXFxuXFxuI2luY2x1ZGUgPHV2Ml9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8ZW52bWFwX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxjb2xvcl9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8Zm9nX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxtb3JwaHRhcmdldF9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8c2tpbm5pbmdfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfcGFyc192ZXJ0ZXg+XFxuXFxudm9pZCBtYWluKCkge1xcblxcbiAgLy8gI2luY2x1ZGUgPHV2X3ZlcnRleD5cXG4gICNpZmRlZiBVU0VfTUFQXFxuICAgIHZVdiA9IHZlYzIoIG1haW5UZXhfU1QucCAqIHV2LnggKyBtYWluVGV4X1NULnMsIG1haW5UZXhfU1QucSAqIHV2LnkgKyBtYWluVGV4X1NULnQgKTtcXG4gICNlbmRpZlxcblxcbiAgI2luY2x1ZGUgPHV2Ml92ZXJ0ZXg+XFxuICAjaW5jbHVkZSA8Y29sb3JfdmVydGV4PlxcbiAgI2luY2x1ZGUgPHNraW5iYXNlX3ZlcnRleD5cXG5cXG4gICNpZmRlZiBVU0VfRU5WTUFQXFxuXFxuICAjaW5jbHVkZSA8YmVnaW5ub3JtYWxfdmVydGV4PlxcbiAgI2luY2x1ZGUgPG1vcnBobm9ybWFsX3ZlcnRleD5cXG4gICNpbmNsdWRlIDxza2lubm9ybWFsX3ZlcnRleD5cXG4gICNpbmNsdWRlIDxkZWZhdWx0bm9ybWFsX3ZlcnRleD5cXG5cXG4gICNlbmRpZlxcblxcbiAgI2luY2x1ZGUgPGJlZ2luX3ZlcnRleD5cXG4gICNpbmNsdWRlIDxtb3JwaHRhcmdldF92ZXJ0ZXg+XFxuICAjaW5jbHVkZSA8c2tpbm5pbmdfdmVydGV4PlxcbiAgI2luY2x1ZGUgPHByb2plY3RfdmVydGV4PlxcbiAgI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3ZlcnRleD5cXG5cXG4gICNpbmNsdWRlIDx3b3JsZHBvc192ZXJ0ZXg+XFxuICAjaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX3ZlcnRleD5cXG4gICNpbmNsdWRlIDxlbnZtYXBfdmVydGV4PlxcbiAgI2luY2x1ZGUgPGZvZ192ZXJ0ZXg+XFxuXFxufVwiIiwiaW1wb3J0ICogYXMgVEhSRUUgZnJvbSAndGhyZWUnO1xuXG5leHBvcnQgZnVuY3Rpb24gcmVkdWNlQm9uZXMocm9vdDogVEhSRUUuT2JqZWN0M0QpOiB2b2lkIHtcbiAgLy8gVHJhdmVyc2UgYW4gZW50aXJlIHRyZWVcbiAgcm9vdC50cmF2ZXJzZSgob2JqKSA9PiB7XG4gICAgaWYgKG9iai50eXBlICE9PSAnU2tpbm5lZE1lc2gnKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgbWVzaCA9IG9iaiBhcyBUSFJFRS5Ta2lubmVkTWVzaDtcbiAgICBjb25zdCBnZW9tZXRyeSA9IChtZXNoLmdlb21ldHJ5IGFzIFRIUkVFLkJ1ZmZlckdlb21ldHJ5KS5jbG9uZSgpO1xuICAgIG1lc2guZ2VvbWV0cnkgPSBnZW9tZXRyeTtcbiAgICBjb25zdCBhdHRyaWJ1dGUgPSBnZW9tZXRyeS5nZXRBdHRyaWJ1dGUoJ3NraW5JbmRleCcpO1xuXG4gICAgLy8gZ2VuZXJhdGUgcmVkdWNlZCBib25lIGxpc3RcbiAgICBjb25zdCBib25lczogVEhSRUUuQm9uZVtdID0gW107IC8vIG5ldyBsaXN0IG9mIGJvbmVcbiAgICBjb25zdCBib25lSW52ZXJzZXM6IFRIUkVFLk1hdHJpeDRbXSA9IFtdOyAvLyBuZXcgbGlzdCBvZiBib25lSW52ZXJzZVxuICAgIGNvbnN0IGJvbmVJbmRleE1hcDogeyBbaW5kZXg6IG51bWJlcl06IG51bWJlciB9ID0ge307IC8vIG1hcCBvZiBvbGQgYm9uZSBpbmRleCB2cy4gbmV3IGJvbmUgaW5kZXhcbiAgICBjb25zdCBhcnJheSA9IChhdHRyaWJ1dGUuYXJyYXkgYXMgYW55KS5tYXAoKGluZGV4OiBudW1iZXIpID0+IHtcbiAgICAgIC8vIG5ldyBza2luSW5kZXggYnVmZmVyXG4gICAgICBpZiAoYm9uZUluZGV4TWFwW2luZGV4XSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGJvbmVJbmRleE1hcFtpbmRleF0gPSBib25lcy5sZW5ndGg7XG4gICAgICAgIGJvbmVzLnB1c2gobWVzaC5za2VsZXRvbi5ib25lc1tpbmRleF0pO1xuICAgICAgICBib25lSW52ZXJzZXMucHVzaChtZXNoLnNrZWxldG9uLmJvbmVJbnZlcnNlc1tpbmRleF0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGJvbmVJbmRleE1hcFtpbmRleF07XG4gICAgfSk7XG5cbiAgICAvLyBhdHRhY2ggbmV3IHNraW5JbmRleCBidWZmZXJcbiAgICBnZW9tZXRyeS5yZW1vdmVBdHRyaWJ1dGUoJ3NraW5JbmRleCcpO1xuICAgIGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSgnc2tpbkluZGV4JywgbmV3IFRIUkVFLkJ1ZmZlckF0dHJpYnV0ZShhcnJheSwgNCwgZmFsc2UpKTtcbiAgICBtZXNoLmJpbmQobmV3IFRIUkVFLlNrZWxldG9uKGJvbmVzLCBib25lSW52ZXJzZXMpLCBuZXcgVEhSRUUuTWF0cml4NCgpKTtcbiAgICAvLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBeXl5eXl5eXl5eXl5eXl5eXl5eIHRyYW5zZm9ybSBvZiBtZXNoZXMgc2hvdWxkIGJlIGlnbm9yZWRcbiAgICAvLyBTZWU6IGh0dHBzOi8vZ2l0aHViLmNvbS9LaHJvbm9zR3JvdXAvZ2xURi90cmVlL21hc3Rlci9zcGVjaWZpY2F0aW9uLzIuMCNza2luc1xuICB9KTtcbn1cbiIsImltcG9ydCAqIGFzIFRIUkVFIGZyb20gJ3RocmVlJztcbmltcG9ydCB7IGdldFdvcmxkUXVhdGVybmlvbkxpdGUgfSBmcm9tICcuLi91dGlscy9tYXRoJztcbi8vIGJhc2VkIG9uXG4vLyBodHRwOi8vcm9ja2V0anVtcC5za3IuanAvdW5pdHkzZC8xMDkvXG4vLyBodHRwczovL2dpdGh1Yi5jb20vZHdhbmdvL1VuaVZSTS9ibG9iL21hc3Rlci9TY3JpcHRzL1NwcmluZ0JvbmUvVlJNU3ByaW5nQm9uZS5jc1xuXG5leHBvcnQgY29uc3QgR0laTU9fUkVOREVSX09SREVSID0gMTAwMDA7XG5jb25zdCBJREVOVElUWV9NQVRSSVg0ID0gT2JqZWN0LmZyZWV6ZShuZXcgVEhSRUUuTWF0cml4NCgpKTtcbmNvbnN0IElERU5USVRZX1FVQVRFUk5JT04gPSBPYmplY3QuZnJlZXplKG5ldyBUSFJFRS5RdWF0ZXJuaW9uKCkpO1xuXG4vLyDoqIjnrpfkuK3jga7kuIDmmYLkv53lrZjnlKjlpInmlbDvvIjkuIDluqbjgqTjg7Pjgrnjgr/jg7PjgrnjgpLkvZzjgaPjgZ/jgonjgYLjgajjga/kvb/jgYTlm57jgZnvvIlcbmNvbnN0IF92M0EgPSBuZXcgVEhSRUUuVmVjdG9yMygpO1xuY29uc3QgX3YzQiA9IG5ldyBUSFJFRS5WZWN0b3IzKCk7XG5jb25zdCBfdjNDID0gbmV3IFRIUkVFLlZlY3RvcjMoKTtcbmNvbnN0IF9xdWF0QSA9IG5ldyBUSFJFRS5RdWF0ZXJuaW9uKCk7XG5jb25zdCBfbWF0QSA9IG5ldyBUSFJFRS5NYXRyaXg0KCk7XG5jb25zdCBfbWF0QiA9IG5ldyBUSFJFRS5NYXRyaXg0KCk7XG5cbi8qKlxuICogQSBjbGFzcyByZXByZXNlbnRzIGEgc2luZ2xlIHNwcmluZyBib25lIG9mIGEgVlJNLlxuICogSXQgc2hvdWxkIGJlIG1hbmFnZWQgYnkgYSBbW1ZSTVNwcmluZ0JvbmVNYW5hZ2VyXV0uXG4gKi9cbmV4cG9ydCBjbGFzcyBWUk1TcHJpbmdCb25lIHtcbiAgLyoqXG4gICAqIFJhZGl1cyBvZiB0aGUgYm9uZSwgd2lsbCBiZSB1c2VkIGZvciBjb2xsaXNpb24uXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgcmFkaXVzOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIFN0aWZmbmVzcyBmb3JjZSBvZiB0aGUgYm9uZS4gSW5jcmVhc2luZyB0aGUgdmFsdWUgPSBmYXN0ZXIgY29udmVyZ2VuY2UgKGZlZWxzIFwiaGFyZGVyXCIpLlxuICAgKiBPbiBVbmlWUk0sIGl0cyByYW5nZSBvbiBHVUkgaXMgYmV0d2VlbiBgMC4wYCBhbmQgYDQuMGAgLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHN0aWZmbmVzc0ZvcmNlOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIFBvd2VyIG9mIHRoZSBncmF2aXR5IGFnYWluc3QgdGhpcyBib25lLlxuICAgKiBUaGUgXCJwb3dlclwiIHVzZWQgaW4gaGVyZSBpcyB2ZXJ5IGZhciBmcm9tIHNjaWVudGlmaWMgcGh5c2ljcyB0ZXJtLi4uXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZ3Jhdml0eVBvd2VyOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIERpcmVjdGlvbiBvZiB0aGUgZ3Jhdml0eSBhZ2FpbnN0IHRoaXMgYm9uZS5cbiAgICogVXN1YWxseSBpdCBzaG91bGQgYmUgbm9ybWFsaXplZC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBncmF2aXR5RGlyOiBUSFJFRS5WZWN0b3IzO1xuXG4gIC8qKlxuICAgKiBEcmFnIGZvcmNlIG9mIHRoZSBib25lLiBJbmNyZWFzaW5nIHRoZSB2YWx1ZSA9IGxlc3Mgb3NjaWxsYXRpb24gKGZlZWxzIFwiaGVhdmllclwiKS5cbiAgICogT24gVW5pVlJNLCBpdHMgcmFuZ2Ugb24gR1VJIGlzIGJldHdlZW4gYDAuMGAgYW5kIGAxLjBgIC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBkcmFnRm9yY2U6IG51bWJlcjtcblxuICAvKipcbiAgICogQW4gT2JqZWN0M0QgYXR0YWNoZWQgdG8gdGhpcyBib25lLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGJvbmU6IFRIUkVFLk9iamVjdDNEO1xuXG4gIC8qKlxuICAgKiBDb2xsaWRlcnMgKGFzIGBUSFJFRS5NZXNoYCApIGF0dGFjaGVkIHRvIHRoaXMgYm9uZS5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBjb2xsaWRlcnM6IFRIUkVFLk1lc2hbXTtcblxuICAvKipcbiAgICogQ3VycmVudCBwb3NpdGlvbiBvZiBjaGlsZCB0YWlsLCBpbiB3b3JsZCB1bml0LiBXaWxsIGJlIHVzZWQgZm9yIHZlcmxldCBpbnRlZ3JhdGlvbi5cbiAgICovXG4gIHByb3RlY3RlZCBfY3VycmVudFRhaWw6IFRIUkVFLlZlY3RvcjM7XG5cbiAgLyoqXG4gICAqIFByZXZpb3VzIHBvc2l0aW9uIG9mIGNoaWxkIHRhaWwsIGluIHdvcmxkIHVuaXQuIFdpbGwgYmUgdXNlZCBmb3IgdmVybGV0IGludGVncmF0aW9uLlxuICAgKi9cbiAgcHJvdGVjdGVkIF9wcmV2VGFpbDogVEhSRUUuVmVjdG9yMztcblxuICAvKipcbiAgICogTmV4dCBwb3NpdGlvbiBvZiBjaGlsZCB0YWlsLCBpbiB3b3JsZCB1bml0LiBXaWxsIGJlIHVzZWQgZm9yIHZlcmxldCBpbnRlZ3JhdGlvbi5cbiAgICogQWN0dWFsbHkgdXNlZCBvbmx5IGluIFtbdXBkYXRlXV0gYW5kIGl0J3Mga2luZCBvZiB0ZW1wb3JhcnkgdmFyaWFibGUuXG4gICAqL1xuICBwcm90ZWN0ZWQgX25leHRUYWlsOiBUSFJFRS5WZWN0b3IzO1xuXG4gIC8qKlxuICAgKiBJbml0aWFsIGF4aXMgb2YgdGhlIGJvbmUsIGluIGxvY2FsIHVuaXQuXG4gICAqL1xuICBwcm90ZWN0ZWQgX2JvbmVBeGlzOiBUSFJFRS5WZWN0b3IzO1xuXG4gIC8qKlxuICAgKiBMZW5ndGggb2YgdGhlIGJvbmUgaW4gKip3b3JsZCB1bml0KiouIFdpbGwgYmUgdXNlZCBmb3Igbm9ybWFsaXphdGlvbiBpbiB1cGRhdGUgbG9vcC5cbiAgICogSXQncyBzYW1lIGFzIGxvY2FsIHVuaXQgbGVuZ3RoIHVubGVzcyB0aGVyZSBhcmUgc2NhbGUgdHJhbnNmb3JtYXRpb24gaW4gd29ybGQgbWF0cml4LlxuICAgKi9cbiAgcHJvdGVjdGVkIF93b3JsZEJvbmVMZW5ndGg6IG51bWJlcjtcblxuICAvKipcbiAgICogV29ybGQgcG9zaXRpb24gb2YgdGhpcyBib25lLCBraW5kIG9mIHRlbXBvcmFyeSB2YXJpYWJsZS5cbiAgICovXG4gIHByb3RlY3RlZCBfd29ybGRQb3NpdGlvbjogVEhSRUUuVmVjdG9yMztcblxuICAvKipcbiAgICogUm90YXRpb24gb2YgcGFyZW50IGJvbmUsIGluIHdvcmxkIHVuaXQuXG4gICAqIFdlIHNob3VsZCB1cGRhdGUgdGhpcyBjb25zdGFudGx5IGluIFtbdXBkYXRlXV0uXG4gICAqL1xuICBwcml2YXRlIF9wYXJlbnRXb3JsZFJvdGF0aW9uOiBUSFJFRS5RdWF0ZXJuaW9uO1xuXG4gIC8qKlxuICAgKiBJbml0aWFsIHN0YXRlIG9mIHRoZSBsb2NhbCBtYXRyaXggb2YgdGhlIGJvbmUuXG4gICAqL1xuICBwcml2YXRlIF9pbml0aWFsTG9jYWxNYXRyaXg6IFRIUkVFLk1hdHJpeDQ7XG5cbiAgLyoqXG4gICAqIEluaXRpYWwgc3RhdGUgb2YgdGhlIHJvdGF0aW9uIG9mIHRoZSBib25lLlxuICAgKi9cbiAgcHJpdmF0ZSBfaW5pdGlhbExvY2FsUm90YXRpb246IFRIUkVFLlF1YXRlcm5pb247XG5cbiAgLyoqXG4gICAqIEluaXRpYWwgc3RhdGUgb2YgdGhlIHBvc2l0aW9uIG9mIGl0cyBjaGlsZC5cbiAgICovXG4gIHByaXZhdGUgX2luaXRpYWxMb2NhbENoaWxkUG9zaXRpb246IFRIUkVFLlZlY3RvcjM7XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBWUk1TcHJpbmdCb25lLlxuICAgKlxuICAgKiBAcGFyYW0gYm9uZSBBbiBPYmplY3QzRCB0aGF0IHdpbGwgYmUgYXR0YWNoZWQgdG8gdGhpcyBib25lXG4gICAqIEBwYXJhbSByYWRpdXMgUmFkaXVzIG9mIHRoZSBib25lXG4gICAqIEBwYXJhbSBzdGlmZm5lc3MgU3RpZmZuZXNzIGZvcmNlIG9mIHRoZSBib25lXG4gICAqIEBwYXJhbSBncmF2aXR5RGlyIERpcmVjdGlvbiBvZiB0aGUgZ3Jhdml0eSBhZ2FpbnN0IHRoaXMgYm9uZVxuICAgKiBAcGFyYW0gZ3Jhdml0eVBvd2VyIFBvd2VyIG9mIHRoZSBncmF2aXR5IGFnYWluc3QgdGhpcyBib25lXG4gICAqIEBwYXJhbSBkcmFnRm9yY2UgRHJhZyBmb3JjZSBvZiB0aGUgYm9uZVxuICAgKiBAcGFyYW0gY29sbGlkZXJzIENvbGxpZGVycyB0aGF0IHdpbGwgYmUgYXR0YWNoZWQgdG8gdGhpcyBib25lXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICBib25lOiBUSFJFRS5PYmplY3QzRCxcbiAgICByYWRpdXM6IG51bWJlcixcbiAgICBzdGlmZmluZXNzOiBudW1iZXIsXG4gICAgZ3Jhdml0eURpcjogVEhSRUUuVmVjdG9yMyxcbiAgICBncmF2aXR5UG93ZXI6IG51bWJlcixcbiAgICBkcmFnRm9yY2U6IG51bWJlcixcbiAgICBjb2xsaWRlcnM6IFRIUkVFLk1lc2hbXSA9IFtdLFxuICApIHtcbiAgICB0aGlzLmJvbmUgPSBib25lOyAvLyB1bmlWUk3jgafjga4gcGFyZW50XG4gICAgdGhpcy5ib25lLm1hdHJpeEF1dG9VcGRhdGUgPSBmYWxzZTsgLy8gdXBkYXRl44Gr44KI44KK6KiI566X44GV44KM44KL44Gu44GndGhyZWUuanPlhoXjgafjga7oh6rli5Xlh6bnkIbjga/kuI3opoFcblxuICAgIHRoaXMucmFkaXVzID0gcmFkaXVzO1xuICAgIHRoaXMuc3RpZmZuZXNzRm9yY2UgPSBzdGlmZmluZXNzO1xuICAgIHRoaXMuZ3Jhdml0eURpciA9IGdyYXZpdHlEaXI7XG4gICAgdGhpcy5ncmF2aXR5UG93ZXIgPSBncmF2aXR5UG93ZXI7XG4gICAgdGhpcy5kcmFnRm9yY2UgPSBkcmFnRm9yY2U7XG4gICAgdGhpcy5jb2xsaWRlcnMgPSBjb2xsaWRlcnM7XG5cbiAgICB0aGlzLl93b3JsZFBvc2l0aW9uID0gbmV3IFRIUkVFLlZlY3RvcjMoKS5zZXRGcm9tTWF0cml4UG9zaXRpb24odGhpcy5ib25lLm1hdHJpeFdvcmxkKTtcblxuICAgIHRoaXMuX3BhcmVudFdvcmxkUm90YXRpb24gPSBuZXcgVEhSRUUuUXVhdGVybmlvbigpO1xuXG4gICAgdGhpcy5faW5pdGlhbExvY2FsTWF0cml4ID0gdGhpcy5ib25lLm1hdHJpeC5jbG9uZSgpO1xuICAgIHRoaXMuX2luaXRpYWxMb2NhbFJvdGF0aW9uID0gdGhpcy5ib25lLnF1YXRlcm5pb24uY2xvbmUoKTtcbiAgICB0aGlzLl9pbml0aWFsTG9jYWxDaGlsZFBvc2l0aW9uID0gKCgpOiBUSFJFRS5WZWN0b3IzID0+IHtcbiAgICAgIGlmICh0aGlzLmJvbmUuY2hpbGRyZW4ubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIC8vIOacq+err+OBruODnOODvOODs+OAguWtkOODnOODvOODs+OBjOOBhOOBquOBhOOBn+OCgeOAjOiHquWIhuOBruWwkeOBl+WFiOOAjeOBjOWtkOODnOODvOODs+OBqOOBhOOBhuOBk+OBqOOBq+OBmeOCi1xuICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZHdhbmdvL1VuaVZSTS9ibG9iL21hc3Rlci9Bc3NldHMvVlJNL1VuaVZSTS9TY3JpcHRzL1NwcmluZ0JvbmUvVlJNU3ByaW5nQm9uZS5jcyNMMjQ2XG4gICAgICAgIHJldHVybiB0aGlzLmJvbmUucG9zaXRpb25cbiAgICAgICAgICAuY2xvbmUoKVxuICAgICAgICAgIC5ub3JtYWxpemUoKVxuICAgICAgICAgIC5tdWx0aXBseVNjYWxhcigwLjA3KTsgLy8gbWFnaWMgbnVtYmVyISBkZXJpdmVzIGZyb20gb3JpZ2luYWwgc291cmNlXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBmaXJzdENoaWxkID0gdGhpcy5ib25lLmNoaWxkcmVuWzBdO1xuICAgICAgICByZXR1cm4gZmlyc3RDaGlsZC5wb3NpdGlvbi5jbG9uZSgpO1xuICAgICAgfVxuICAgIH0pKCk7XG5cbiAgICB0aGlzLl9jdXJyZW50VGFpbCA9IHRoaXMuYm9uZS5sb2NhbFRvV29ybGQodGhpcy5faW5pdGlhbExvY2FsQ2hpbGRQb3NpdGlvbi5jbG9uZSgpKTtcbiAgICB0aGlzLl9wcmV2VGFpbCA9IHRoaXMuX2N1cnJlbnRUYWlsLmNsb25lKCk7XG4gICAgdGhpcy5fbmV4dFRhaWwgPSB0aGlzLl9jdXJyZW50VGFpbC5jbG9uZSgpO1xuXG4gICAgdGhpcy5fYm9uZUF4aXMgPSB0aGlzLl9pbml0aWFsTG9jYWxDaGlsZFBvc2l0aW9uLmNsb25lKCkubm9ybWFsaXplKCk7XG4gICAgdGhpcy5fd29ybGRCb25lTGVuZ3RoID0gdGhpcy5ib25lXG4gICAgICAubG9jYWxUb1dvcmxkKF92M0EuY29weSh0aGlzLl9pbml0aWFsTG9jYWxDaGlsZFBvc2l0aW9uKSlcbiAgICAgIC5zdWIodGhpcy5fd29ybGRQb3NpdGlvbilcbiAgICAgIC5sZW5ndGgoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNldCB0aGUgc3RhdGUgb2YgdGhpcyBib25lLlxuICAgKiBZb3UgbWlnaHQgd2FudCB0byBjYWxsIFtbVlJNU3ByaW5nQm9uZU1hbmFnZXIucmVzZXRdXSBpbnN0ZWFkLlxuICAgKi9cbiAgcHVibGljIHJlc2V0KCk6IHZvaWQge1xuICAgIHRoaXMuYm9uZS5tYXRyaXguY29weSh0aGlzLl9pbml0aWFsTG9jYWxNYXRyaXgpO1xuXG4gICAgdGhpcy5ib25lLmxvY2FsVG9Xb3JsZCh0aGlzLl9jdXJyZW50VGFpbC5jb3B5KHRoaXMuX2luaXRpYWxMb2NhbENoaWxkUG9zaXRpb24pKTtcbiAgICB0aGlzLl9wcmV2VGFpbC5jb3B5KHRoaXMuX2N1cnJlbnRUYWlsKTtcbiAgICB0aGlzLl9uZXh0VGFpbC5jb3B5KHRoaXMuX2N1cnJlbnRUYWlsKTtcblxuICAgIC8vIOODnOODvOODs+OBruWnv+WLouOCkuaJi+WLleOBp+aTjeS9nOOBl+OBn+OBruOBp+OAgW1hdHJpeFdvcmxk44KC5pu05paw44GX44Gm44GK44GPXG4gICAgdGhpcy5ib25lLnVwZGF0ZU1hdHJpeCgpO1xuICAgIHRoaXMuYm9uZS5tYXRyaXhXb3JsZC5tdWx0aXBseU1hdHJpY2VzKHRoaXMuX2dldFBhcmVudE1hdHJpeFdvcmxkKCksIHRoaXMuYm9uZS5tYXRyaXgpO1xuICAgIHRoaXMuX3dvcmxkUG9zaXRpb24uc2V0RnJvbU1hdHJpeFBvc2l0aW9uKHRoaXMuYm9uZS5tYXRyaXhXb3JsZCk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIHRoZSBzdGF0ZSBvZiB0aGlzIGJvbmUuXG4gICAqIFlvdSBtaWdodCB3YW50IHRvIGNhbGwgW1tWUk1TcHJpbmdCb25lTWFuYWdlci51cGRhdGVdXSBpbnN0ZWFkLlxuICAgKlxuICAgKiBAcGFyYW0gZGVsdGEgZGVsdGFUaW1lXG4gICAqL1xuICBwdWJsaWMgdXBkYXRlKGRlbHRhOiBudW1iZXIpOiB2b2lkIHtcbiAgICBpZiAoZGVsdGEgPD0gMCkgcmV0dXJuO1xuXG4gICAgLy8g6Kaq44K544OX44Oq44Oz44Kw44Oc44O844Oz44Gu5ae/5Yui44Gv5bi444Gr5aSJ5YyW44GX44Gm44GE44KL44CCXG4gICAgLy8g44Gd44KM44Gr5Z+644Gl44GE44Gm5Yem55CG55u05YmN44Gr6Ieq5YiG44Gud29ybGRNYXRyaXjjgpLmm7TmlrDjgZfjgabjgYrjgY9cbiAgICB0aGlzLmJvbmUubWF0cml4V29ybGQubXVsdGlwbHlNYXRyaWNlcyh0aGlzLl9nZXRQYXJlbnRNYXRyaXhXb3JsZCgpLCB0aGlzLmJvbmUubWF0cml4KTtcblxuICAgIGlmICh0aGlzLmJvbmUucGFyZW50KSB7XG4gICAgICAvLyBTcHJpbmdCb25l44Gv6Kaq44GL44KJ6aCG44Gr5Yem55CG44GV44KM44Gm44GE44GP44Gf44KB44CBXG4gICAgICAvLyDopqrjga5tYXRyaXhXb3JsZOOBr+acgOaWsOeKtuaFi+OBruWJjeaPkOOBp3dvcmxkTWF0cml444GL44KJcXVhdGVybmlvbuOCkuWPluOCiuWHuuOBmeOAglxuICAgICAgLy8g5Yi26ZmQ44Gv44GC44KL44GR44KM44Gp44CB6KiI566X44Gv5bCR44Gq44GE44Gu44GnZ2V0V29ybGRRdWF0ZXJuaW9u44Gn44Gv44Gq44GP44GT44Gu5pa55rOV44KS5Y+W44KL44CCXG4gICAgICBnZXRXb3JsZFF1YXRlcm5pb25MaXRlKHRoaXMuYm9uZS5wYXJlbnQsIHRoaXMuX3BhcmVudFdvcmxkUm90YXRpb24pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLl9wYXJlbnRXb3JsZFJvdGF0aW9uLmNvcHkoSURFTlRJVFlfUVVBVEVSTklPTik7XG4gICAgfVxuXG4gICAgLy8g5pu05paw5riI44G/44Gud29ybGRNYXRyaXjjgYvjgol3b3JsZFBvc2l0aW9u44KS5Y+W44KK5Ye644GZ44CCXG4gICAgLy8gYGdldFdvcmxkUG9zaXRpb24oKWAg44Gv6LKg6I2344GM6auY44GE44Gu44Gn5Yip55So44GX44Gq44GE44CCXG4gICAgdGhpcy5fd29ybGRQb3NpdGlvbi5zZXRGcm9tTWF0cml4UG9zaXRpb24odGhpcy5ib25lLm1hdHJpeFdvcmxkKTtcbiAgICBjb25zdCBzdGlmZm5lc3MgPSB0aGlzLnN0aWZmbmVzc0ZvcmNlICogZGVsdGE7XG4gICAgY29uc3QgZXh0ZXJuYWwgPSBfdjNCLmNvcHkodGhpcy5ncmF2aXR5RGlyKS5tdWx0aXBseVNjYWxhcih0aGlzLmdyYXZpdHlQb3dlciAqIGRlbHRhKTtcblxuICAgIC8vIHZlcmxldOepjeWIhuOBp+asoeOBruS9jee9ruOCkuioiOeul1xuICAgIHRoaXMuX25leHRUYWlsXG4gICAgICAuY29weSh0aGlzLl9jdXJyZW50VGFpbClcbiAgICAgIC5hZGQoXG4gICAgICAgIF92M0FcbiAgICAgICAgICAuY29weSh0aGlzLl9jdXJyZW50VGFpbClcbiAgICAgICAgICAuc3ViKHRoaXMuX3ByZXZUYWlsKVxuICAgICAgICAgIC5tdWx0aXBseVNjYWxhcigxIC0gdGhpcy5kcmFnRm9yY2UpLFxuICAgICAgKSAvLyDliY3jg5Xjg6zjg7zjg6Djga7np7vli5XjgpLntpnntprjgZnjgoso5rib6KGw44KC44GC44KL44KIKVxuICAgICAgLmFkZChcbiAgICAgICAgX3YzQVxuICAgICAgICAgIC5jb3B5KHRoaXMuX2JvbmVBeGlzKVxuICAgICAgICAgIC5hcHBseU1hdHJpeDQodGhpcy5faW5pdGlhbExvY2FsTWF0cml4KVxuICAgICAgICAgIC5hcHBseU1hdHJpeDQodGhpcy5fZ2V0UGFyZW50TWF0cml4V29ybGQoKSlcbiAgICAgICAgICAuc3ViKHRoaXMuX3dvcmxkUG9zaXRpb24pXG4gICAgICAgICAgLm5vcm1hbGl6ZSgpXG4gICAgICAgICAgLm11bHRpcGx5U2NhbGFyKHN0aWZmbmVzcyksXG4gICAgICApIC8vIOimquOBruWbnui7ouOBq+OCiOOCi+WtkOODnOODvOODs+OBruenu+WLleebruaomVxuICAgICAgLmFkZChleHRlcm5hbCk7IC8vIOWkluWKm+OBq+OCiOOCi+enu+WLlemHj1xuXG4gICAgLy8gbm9ybWFsaXplIGJvbmUgbGVuZ3RoXG4gICAgdGhpcy5fbmV4dFRhaWxcbiAgICAgIC5zdWIodGhpcy5fd29ybGRQb3NpdGlvbilcbiAgICAgIC5ub3JtYWxpemUoKVxuICAgICAgLm11bHRpcGx5U2NhbGFyKHRoaXMuX3dvcmxkQm9uZUxlbmd0aClcbiAgICAgIC5hZGQodGhpcy5fd29ybGRQb3NpdGlvbik7XG5cbiAgICAvLyBDb2xsaXNpb27jgafnp7vli5VcbiAgICB0aGlzLl9jb2xsaXNpb24odGhpcy5fbmV4dFRhaWwpO1xuXG4gICAgdGhpcy5fcHJldlRhaWwuY29weSh0aGlzLl9jdXJyZW50VGFpbCk7XG4gICAgdGhpcy5fY3VycmVudFRhaWwuY29weSh0aGlzLl9uZXh0VGFpbCk7XG5cbiAgICAvLyBBcHBseSByb3RhdGlvbiwgY29udmVydCB2ZWN0b3IzIHRoaW5nIGludG8gYWN0dWFsIHF1YXRlcm5pb25cbiAgICAvLyBPcmlnaW5hbCBVbmlWUk0gaXMgZG9pbmcgd29ybGQgdW5pdCBjYWxjdWx1cyBhdCBoZXJlIGJ1dCB3ZSdyZSBnb25uYSBkbyB0aGlzIG9uIGxvY2FsIHVuaXRcbiAgICAvLyBzaW5jZSBUaHJlZS5qcyBpcyBub3QgZ29vZCBhdCB3b3JsZCBjb29yZGluYXRpb24gc3R1ZmZcbiAgICBjb25zdCBpbml0aWFsV29ybGRNYXRyaXhJbnYgPSBfbWF0QS5nZXRJbnZlcnNlKFxuICAgICAgX21hdEIuY29weSh0aGlzLl9nZXRQYXJlbnRNYXRyaXhXb3JsZCgpKS5tdWx0aXBseSh0aGlzLl9pbml0aWFsTG9jYWxNYXRyaXgpLFxuICAgICk7XG4gICAgY29uc3QgYXBwbHlSb3RhdGlvbiA9IF9xdWF0QS5zZXRGcm9tVW5pdFZlY3RvcnMoXG4gICAgICB0aGlzLl9ib25lQXhpcyxcbiAgICAgIF92M0FcbiAgICAgICAgLmNvcHkodGhpcy5fbmV4dFRhaWwpXG4gICAgICAgIC5hcHBseU1hdHJpeDQoaW5pdGlhbFdvcmxkTWF0cml4SW52KVxuICAgICAgICAubm9ybWFsaXplKCksXG4gICAgKTtcblxuICAgIHRoaXMuYm9uZS5xdWF0ZXJuaW9uLmNvcHkodGhpcy5faW5pdGlhbExvY2FsUm90YXRpb24pLm11bHRpcGx5KGFwcGx5Um90YXRpb24pO1xuXG4gICAgLy8gV2UgbmVlZCB0byB1cGRhdGUgaXRzIG1hdHJpeFdvcmxkIG1hbnVhbGx5LCBzaW5jZSB3ZSB0d2Vha2VkIHRoZSBib25lIGJ5IG91ciBoYW5kXG4gICAgdGhpcy5ib25lLnVwZGF0ZU1hdHJpeCgpO1xuICAgIHRoaXMuYm9uZS5tYXRyaXhXb3JsZC5tdWx0aXBseU1hdHJpY2VzKHRoaXMuX2dldFBhcmVudE1hdHJpeFdvcmxkKCksIHRoaXMuYm9uZS5tYXRyaXgpO1xuICB9XG5cbiAgLyoqXG4gICAqIERvIGNvbGxpc2lvbiBtYXRoIGFnYWluc3QgZXZlcnkgY29sbGlkZXJzIGF0dGFjaGVkIHRvIHRoaXMgYm9uZS5cbiAgICpcbiAgICogQHBhcmFtIHRhaWwgVGhlIHRhaWwgeW91IHdhbnQgdG8gcHJvY2Vzc1xuICAgKi9cbiAgcHJpdmF0ZSBfY29sbGlzaW9uKHRhaWw6IFRIUkVFLlZlY3RvcjMpOiB2b2lkIHtcbiAgICB0aGlzLmNvbGxpZGVycy5mb3JFYWNoKChjb2xsaWRlcikgPT4ge1xuICAgICAgY29uc3QgY29sbGlkZXJXb3JsZFBvc2l0aW9uID0gX3YzQS5zZXRGcm9tTWF0cml4UG9zaXRpb24oY29sbGlkZXIubWF0cml4V29ybGQpO1xuICAgICAgY29uc3QgY29sbGlkZXJSYWRpdXMgPSBjb2xsaWRlci5nZW9tZXRyeS5ib3VuZGluZ1NwaGVyZS5yYWRpdXM7XG4gICAgICBjb25zdCByID0gdGhpcy5yYWRpdXMgKyBjb2xsaWRlclJhZGl1cztcblxuICAgICAgaWYgKHRhaWwuZGlzdGFuY2VUb1NxdWFyZWQoY29sbGlkZXJXb3JsZFBvc2l0aW9uKSA8PSByICogcikge1xuICAgICAgICAvLyDjg5Ljg4Pjg4jjgIJDb2xsaWRlcuOBruWNiuW+hOaWueWQkeOBq+aKvOOBl+WHuuOBmVxuICAgICAgICBjb25zdCBub3JtYWwgPSBfdjNCLnN1YlZlY3RvcnModGFpbCwgY29sbGlkZXJXb3JsZFBvc2l0aW9uKS5ub3JtYWxpemUoKTtcbiAgICAgICAgY29uc3QgcG9zRnJvbUNvbGxpZGVyID0gX3YzQy5hZGRWZWN0b3JzKGNvbGxpZGVyV29ybGRQb3NpdGlvbiwgbm9ybWFsLm11bHRpcGx5U2NhbGFyKHIpKTtcblxuICAgICAgICAvLyBub3JtYWxpemUgYm9uZSBsZW5ndGhcbiAgICAgICAgdGFpbC5jb3B5KFxuICAgICAgICAgIHBvc0Zyb21Db2xsaWRlclxuICAgICAgICAgICAgLnN1Yih0aGlzLl93b3JsZFBvc2l0aW9uKVxuICAgICAgICAgICAgLm5vcm1hbGl6ZSgpXG4gICAgICAgICAgICAubXVsdGlwbHlTY2FsYXIodGhpcy5fd29ybGRCb25lTGVuZ3RoKVxuICAgICAgICAgICAgLmFkZCh0aGlzLl93b3JsZFBvc2l0aW9uKSxcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSB3b3JsZCBtYXRyaXggb2YgaXRzIHBhcmVudCBvYmplY3QuXG4gICAqL1xuICBwcml2YXRlIF9nZXRQYXJlbnRNYXRyaXhXb3JsZCgpOiBUSFJFRS5NYXRyaXg0IHtcbiAgICByZXR1cm4gdGhpcy5ib25lLnBhcmVudCA/IHRoaXMuYm9uZS5wYXJlbnQubWF0cml4V29ybGQgOiBJREVOVElUWV9NQVRSSVg0O1xuICB9XG59XG4iLCJpbXBvcnQgKiBhcyBUSFJFRSBmcm9tICd0aHJlZSc7XG5pbXBvcnQgeyBHTFRGTm9kZSwgVlJNU2NoZW1hIH0gZnJvbSAnLi4vdHlwZXMnO1xuaW1wb3J0IHsgR0laTU9fUkVOREVSX09SREVSLCBWUk1TcHJpbmdCb25lIH0gZnJvbSAnLi9WUk1TcHJpbmdCb25lJztcbmltcG9ydCB7IFZSTVNwcmluZ0JvbmVDb2xsaWRlckdyb3VwLCBWUk1TcHJpbmdCb25lQ29sbGlkZXJNZXNoIH0gZnJvbSAnLi9WUk1TcHJpbmdCb25lQ29sbGlkZXJHcm91cCc7XG5pbXBvcnQgeyBWUk1TcHJpbmdCb25lR3JvdXAsIFZSTVNwcmluZ0JvbmVNYW5hZ2VyIH0gZnJvbSAnLi9WUk1TcHJpbmdCb25lTWFuYWdlcic7XG5cbi8qKlxuICogQW4gaW1wb3J0ZXIgdGhhdCBpbXBvcnRzIGEgW1tWUk1TcHJpbmdCb25lTWFuYWdlcl1dIGZyb20gYSBWUk0gZXh0ZW5zaW9uIG9mIGEgR0xURi5cbiAqL1xuZXhwb3J0IGNsYXNzIFZSTVNwcmluZ0JvbmVJbXBvcnRlciB7XG4gIC8qKlxuICAgKiBJbXBvcnQgYSBbW1ZSTUxvb2tBdEhlYWRdXSBmcm9tIGEgVlJNLlxuICAgKlxuICAgKiBAcGFyYW0gZ2x0ZiBBIHBhcnNlZCByZXN1bHQgb2YgR0xURiB0YWtlbiBmcm9tIEdMVEZMb2FkZXJcbiAgICovXG4gIHB1YmxpYyBhc3luYyBpbXBvcnQoZ2x0ZjogVEhSRUUuR0xURik6IFByb21pc2U8VlJNU3ByaW5nQm9uZU1hbmFnZXIgfCBudWxsPiB7XG4gICAgaWYgKFxuICAgICAgIWdsdGYucGFyc2VyLmpzb24uZXh0ZW5zaW9ucyB8fFxuICAgICAgIWdsdGYucGFyc2VyLmpzb24uZXh0ZW5zaW9ucy5WUk0gfHxcbiAgICAgICFnbHRmLnBhcnNlci5qc29uLmV4dGVuc2lvbnMuVlJNLnNlY29uZGFyeUFuaW1hdGlvblxuICAgICkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgLy8g6KGd56qB5Yik5a6a55CD5L2T44Oh44OD44K344Ol44CCXG4gICAgY29uc3QgY29sbGlkZXJHcm91cHMgPSBhd2FpdCB0aGlzLl9nZXRDb2xsaWRlck1lc2hHcm91cHMoZ2x0Zik7XG4gICAgY29sbGlkZXJHcm91cHMuZm9yRWFjaCgoZ3JvdXApID0+IGdsdGYuc2NlbmUuYWRkKC4uLmdyb3VwLmNvbGxpZGVycykpO1xuXG4gICAgLy8g5ZCM44GY5bGe5oCn77yIc3RpZmZpbmVzc+OChGRyYWdGb3JjZeOBjOWQjOOBmO+8ieOBruODnOODvOODs+OBr2JvbmVHcm91cOOBq+OBvuOBqOOCgeOCieOCjOOBpuOBhOOCi+OAglxuICAgIC8vIOS4gOWIl+OBoOOBkeOBp+OBr+OBquOBhOOBk+OBqOOBq+azqOaEj+OAglxuICAgIGNvbnN0IHNwcmluZ0JvbmVHcm91cExpc3QgPSBhd2FpdCB0aGlzLl9nZXRTcHJpbmdCb25lR3JvdXBMaXN0KGdsdGYsIGNvbGxpZGVyR3JvdXBzKTtcblxuICAgIHJldHVybiBuZXcgVlJNU3ByaW5nQm9uZU1hbmFnZXIoc3ByaW5nQm9uZUdyb3VwTGlzdCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0IF9pc0NvbGlkZXJNZXNoVmlzaWJsZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBwcm90ZWN0ZWQgX2NyZWF0ZVNwcmluZ0JvbmUoXG4gICAgZ2x0ZjogVEhSRUUuR0xURixcbiAgICBib25lOiBUSFJFRS5PYmplY3QzRCxcbiAgICBoaXRSYWRpdXM6IG51bWJlcixcbiAgICBzdGlmZmluZXNzOiBudW1iZXIsXG4gICAgZ3Jhdml0eURpcjogVEhSRUUuVmVjdG9yMyxcbiAgICBncmF2aXR5UG93ZXI6IG51bWJlcixcbiAgICBkcmFnRm9yY2U6IG51bWJlcixcbiAgICBjb2xsaWRlcnM6IFRIUkVFLk1lc2hbXSA9IFtdLFxuICApOiBWUk1TcHJpbmdCb25lIHtcbiAgICByZXR1cm4gbmV3IFZSTVNwcmluZ0JvbmUoYm9uZSwgaGl0UmFkaXVzLCBzdGlmZmluZXNzLCBncmF2aXR5RGlyLCBncmF2aXR5UG93ZXIsIGRyYWdGb3JjZSwgY29sbGlkZXJzKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgX2dldFNwcmluZ0JvbmVHcm91cExpc3QoXG4gICAgZ2x0ZjogVEhSRUUuR0xURixcbiAgICBjb2xsaWRlckdyb3VwczogVlJNU3ByaW5nQm9uZUNvbGxpZGVyR3JvdXBbXSxcbiAgKTogUHJvbWlzZTxWUk1TcHJpbmdCb25lR3JvdXBbXT4ge1xuICAgIGNvbnN0IHNwcmluZ0JvbmVHcm91cHM6IFZSTVNjaGVtYS5TZWNvbmRhcnlBbmltYXRpb25TcHJpbmdbXSA9IGdsdGYucGFyc2VyLmpzb24uZXh0ZW5zaW9ucyEuVlJNIS5zZWNvbmRhcnlBbmltYXRpb24hXG4gICAgICAuYm9uZUdyb3VwcztcblxuICAgIGNvbnN0IHNwcmluZ0JvbmVHcm91cExpc3Q6IFZSTVNwcmluZ0JvbmVHcm91cFtdID0gW107XG5cbiAgICBzcHJpbmdCb25lR3JvdXBzLmZvckVhY2goKHZybUJvbmVHcm91cCkgPT4ge1xuICAgICAgaWYgKFxuICAgICAgICB2cm1Cb25lR3JvdXAuc3RpZmZpbmVzcyA9PT0gdW5kZWZpbmVkIHx8XG4gICAgICAgIHZybUJvbmVHcm91cC5ncmF2aXR5RGlyID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgdnJtQm9uZUdyb3VwLmdyYXZpdHlEaXIueCA9PT0gdW5kZWZpbmVkIHx8XG4gICAgICAgIHZybUJvbmVHcm91cC5ncmF2aXR5RGlyLnkgPT09IHVuZGVmaW5lZCB8fFxuICAgICAgICB2cm1Cb25lR3JvdXAuZ3Jhdml0eURpci56ID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgdnJtQm9uZUdyb3VwLmdyYXZpdHlQb3dlciA9PT0gdW5kZWZpbmVkIHx8XG4gICAgICAgIHZybUJvbmVHcm91cC5kcmFnRm9yY2UgPT09IHVuZGVmaW5lZCB8fFxuICAgICAgICB2cm1Cb25lR3JvdXAuaGl0UmFkaXVzID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgdnJtQm9uZUdyb3VwLmNvbGxpZGVyR3JvdXBzID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgdnJtQm9uZUdyb3VwLmJvbmVzID09PSB1bmRlZmluZWRcbiAgICAgICkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHN0aWZmaW5lc3MgPSB2cm1Cb25lR3JvdXAuc3RpZmZpbmVzcztcbiAgICAgIGNvbnN0IGdyYXZpdHlEaXIgPSBuZXcgVEhSRUUuVmVjdG9yMyhcbiAgICAgICAgdnJtQm9uZUdyb3VwLmdyYXZpdHlEaXIueCxcbiAgICAgICAgdnJtQm9uZUdyb3VwLmdyYXZpdHlEaXIueSxcbiAgICAgICAgdnJtQm9uZUdyb3VwLmdyYXZpdHlEaXIueixcbiAgICAgICk7XG4gICAgICBjb25zdCBncmF2aXR5UG93ZXIgPSB2cm1Cb25lR3JvdXAuZ3Jhdml0eVBvd2VyO1xuICAgICAgY29uc3QgZHJhZ0ZvcmNlID0gdnJtQm9uZUdyb3VwLmRyYWdGb3JjZTtcbiAgICAgIGNvbnN0IGhpdFJhZGl1cyA9IHZybUJvbmVHcm91cC5oaXRSYWRpdXM7XG5cbiAgICAgIGNvbnN0IGNvbGxpZGVyczogVlJNU3ByaW5nQm9uZUNvbGxpZGVyTWVzaFtdID0gW107XG4gICAgICB2cm1Cb25lR3JvdXAuY29sbGlkZXJHcm91cHMuZm9yRWFjaCgoY29sbGlkZXJJbmRleCkgPT4ge1xuICAgICAgICBjb2xsaWRlcnMucHVzaCguLi5jb2xsaWRlckdyb3Vwc1tjb2xsaWRlckluZGV4XS5jb2xsaWRlcnMpO1xuICAgICAgfSk7XG5cbiAgICAgIGNvbnN0IHNwcmluZ0JvbmVHcm91cDogVlJNU3ByaW5nQm9uZUdyb3VwID0gW107XG4gICAgICB2cm1Cb25lR3JvdXAuYm9uZXMuZm9yRWFjaChhc3luYyAobm9kZUluZGV4KSA9PiB7XG4gICAgICAgIC8vIFZSTeOBruaDheWgseOBi+OCieOAjOaPuuOCjOODouODjuOAjeODnOODvOODs+OBruODq+ODvOODiOOBjOWPluOCjOOCi1xuICAgICAgICBjb25zdCBzcHJpbmdSb290Qm9uZTogR0xURk5vZGUgPSBhd2FpdCBnbHRmLnBhcnNlci5nZXREZXBlbmRlbmN5KCdub2RlJywgbm9kZUluZGV4KTtcblxuICAgICAgICAvLyBpdCdzIHdlaXJkIGJ1dCB0aGVyZSBtaWdodCBiZSBjYXNlcyB3ZSBjYW4ndCBmaW5kIHRoZSByb290IGJvbmVcbiAgICAgICAgaWYgKCFzcHJpbmdSb290Qm9uZSkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHNwcmluZ1Jvb3RCb25lLnRyYXZlcnNlKChib25lKSA9PiB7XG4gICAgICAgICAgY29uc3Qgc3ByaW5nQm9uZSA9IHRoaXMuX2NyZWF0ZVNwcmluZ0JvbmUoXG4gICAgICAgICAgICBnbHRmLFxuICAgICAgICAgICAgYm9uZSxcbiAgICAgICAgICAgIGhpdFJhZGl1cyxcbiAgICAgICAgICAgIHN0aWZmaW5lc3MsXG4gICAgICAgICAgICBncmF2aXR5RGlyLFxuICAgICAgICAgICAgZ3Jhdml0eVBvd2VyLFxuICAgICAgICAgICAgZHJhZ0ZvcmNlLFxuICAgICAgICAgICAgY29sbGlkZXJzLFxuICAgICAgICAgICk7XG4gICAgICAgICAgc3ByaW5nQm9uZUdyb3VwLnB1c2goc3ByaW5nQm9uZSk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG5cbiAgICAgIHNwcmluZ0JvbmVHcm91cExpc3QucHVzaChzcHJpbmdCb25lR3JvdXApO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHNwcmluZ0JvbmVHcm91cExpc3Q7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGFuIGFycmF5IG9mIFtbVlJNU3ByaW5nQm9uZUNvbGxpZGVyR3JvdXBdXS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgX2dldENvbGxpZGVyTWVzaEdyb3VwcyhnbHRmOiBUSFJFRS5HTFRGKTogUHJvbWlzZTxWUk1TcHJpbmdCb25lQ29sbGlkZXJHcm91cFtdPiB7XG4gICAgY29uc3QgdnJtRXh0OiBWUk1TY2hlbWEuVlJNIHwgdW5kZWZpbmVkID0gZ2x0Zi5wYXJzZXIuanNvbi5leHRlbnNpb25zICYmIGdsdGYucGFyc2VyLmpzb24uZXh0ZW5zaW9ucy5WUk07XG4gICAgaWYgKHZybUV4dCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICAgIGNvbnN0IHNlY29uZGFyeUFuaW1hdGlvbiA9IHZybUV4dC5zZWNvbmRhcnlBbmltYXRpb247XG4gICAgaWYgKHNlY29uZGFyeUFuaW1hdGlvbiA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICAgIGNvbnN0IHZybUNvbGxpZGVyR3JvdXBzID0gc2Vjb25kYXJ5QW5pbWF0aW9uLmNvbGxpZGVyR3JvdXBzO1xuICAgIGlmICh2cm1Db2xsaWRlckdyb3VwcyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgY29uc3QgY29sbGlkZXJHcm91cHM6IFZSTVNwcmluZ0JvbmVDb2xsaWRlckdyb3VwW10gPSBbXTtcbiAgICB2cm1Db2xsaWRlckdyb3Vwcy5mb3JFYWNoKGFzeW5jIChjb2xsaWRlckdyb3VwKSA9PiB7XG4gICAgICBpZiAoY29sbGlkZXJHcm91cC5ub2RlID09PSB1bmRlZmluZWQgfHwgY29sbGlkZXJHcm91cC5jb2xsaWRlcnMgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGJvbmUgPSBhd2FpdCBnbHRmLnBhcnNlci5nZXREZXBlbmRlbmN5KCdub2RlJywgY29sbGlkZXJHcm91cC5ub2RlKTtcbiAgICAgIGNvbnN0IGNvbGxpZGVyczogVlJNU3ByaW5nQm9uZUNvbGxpZGVyTWVzaFtdID0gW107XG4gICAgICBjb2xsaWRlckdyb3VwLmNvbGxpZGVycy5mb3JFYWNoKChjb2xsaWRlcikgPT4ge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgY29sbGlkZXIub2Zmc2V0ID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgICBjb2xsaWRlci5vZmZzZXQueCA9PT0gdW5kZWZpbmVkIHx8XG4gICAgICAgICAgY29sbGlkZXIub2Zmc2V0LnkgPT09IHVuZGVmaW5lZCB8fFxuICAgICAgICAgIGNvbGxpZGVyLm9mZnNldC56ID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgICBjb2xsaWRlci5yYWRpdXMgPT09IHVuZGVmaW5lZFxuICAgICAgICApIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBvZmZzZXRNYXRyaXggPSBuZXcgVEhSRUUuTWF0cml4NCgpLm1ha2VUcmFuc2xhdGlvbihcbiAgICAgICAgICBjb2xsaWRlci5vZmZzZXQueCxcbiAgICAgICAgICBjb2xsaWRlci5vZmZzZXQueSxcbiAgICAgICAgICAtY29sbGlkZXIub2Zmc2V0LnosIC8vIHRoaXMgaXMgcHJldHR5IHdlaXJkLiBTZWU6IGh0dHBzOi8vZ2l0aHViLmNvbS9kd2FuZ28vVW5pVlJNL2lzc3Vlcy82NVxuICAgICAgICApO1xuICAgICAgICBjb25zdCB2aXNpYmxlID0gdGhpcy5faXNDb2xpZGVyTWVzaFZpc2libGU7XG4gICAgICAgIGNvbnN0IGNvbGxpZGVyTWVzaCA9IG5ldyBUSFJFRS5NZXNoKFxuICAgICAgICAgIG5ldyBUSFJFRS5TcGhlcmVCdWZmZXJHZW9tZXRyeShjb2xsaWRlci5yYWRpdXMsIDgsIDQpLFxuICAgICAgICAgIG5ldyBUSFJFRS5NZXNoQmFzaWNNYXRlcmlhbCh7XG4gICAgICAgICAgICBjb2xvcjogMHhmZjAwZmYsXG4gICAgICAgICAgICB2aXNpYmxlLFxuICAgICAgICAgICAgd2lyZWZyYW1lOiB0cnVlLFxuICAgICAgICAgICAgdHJhbnNwYXJlbnQ6IHRydWUsXG4gICAgICAgICAgICBkZXB0aFRlc3Q6IGZhbHNlLFxuICAgICAgICAgIH0pLFxuICAgICAgICApO1xuICAgICAgICAoY29sbGlkZXJNZXNoLm1hdGVyaWFsIGFzIGFueSkucmVuZGVyT3JkZXIgPSBHSVpNT19SRU5ERVJfT1JERVI7XG5cbiAgICAgICAgLy8gdGhlIG5hbWUgaGF2ZSB0byBiZSB0aGlzIGluIG9yZGVyIHRvIGV4Y2x1ZGUgY29sbGlkZXJzIGZyb20gYm91bmRpbmcgYm94XG4gICAgICAgIC8vIChTZWUgVmlld2VyLnRzLCBzZWFyY2ggZm9yIGNoaWxkLm5hbWUgPT09ICd2cm1Db2xsaWRlclNwaGVyZScpXG4gICAgICAgIGNvbGxpZGVyTWVzaC5uYW1lID0gJ3ZybUNvbGxpZGVyU3BoZXJlJztcblxuICAgICAgICAvLyBXZSB3aWxsIHVzZSB0aGUgcmFkaXVzIG9mIHRoZSBzcGhlcmUgZm9yIGNvbGxpc2lvbiB2cyBib25lcy5cbiAgICAgICAgLy8gYGJvdW5kaW5nU3BoZXJlYCBtdXN0IGJlIGNyZWF0ZWQgdG8gY29tcHV0ZSB0aGUgcmFkaXVzLlxuICAgICAgICBjb2xsaWRlck1lc2guZ2VvbWV0cnkuY29tcHV0ZUJvdW5kaW5nU3BoZXJlKCk7XG5cbiAgICAgICAgLy8gVGhlIGNvbGxpZGVyTWVzaCBtdXN0IHN5bmMgd2l0aCB0aGUgYm9uZS5cbiAgICAgICAgLy8gQXR0YWNoaW5nIGJvbmUncyBtYXRyaXggdG8gdGhlIGNvbGxpZGVyTWVzaCBhdCBldmVyeSB1cGRhdGUuXG4gICAgICAgIC8vIChjb2xsaWRlck1lc2ggd2lsbCBtb3ZlIGF1dG9tZWNpY2FsbHR5KVxuICAgICAgICBjb2xsaWRlck1lc2gudXBkYXRlTWF0cml4V29ybGQgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgICAgY29sbGlkZXJNZXNoLm1hdHJpeFdvcmxkLmNvcHkoYm9uZS5tYXRyaXhXb3JsZCkubXVsdGlwbHkob2Zmc2V0TWF0cml4KTtcbiAgICAgICAgfTtcbiAgICAgICAgY29sbGlkZXJzLnB1c2goY29sbGlkZXJNZXNoKTtcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCBjb2xsaWRlck1lc2hHcm91cCA9IHtcbiAgICAgICAgbm9kZTogY29sbGlkZXJHcm91cC5ub2RlLFxuICAgICAgICBjb2xsaWRlcnMsXG4gICAgICB9O1xuICAgICAgY29sbGlkZXJHcm91cHMucHVzaChjb2xsaWRlck1lc2hHcm91cCk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gY29sbGlkZXJHcm91cHM7XG4gIH1cbn1cbiIsImltcG9ydCB7IFZSTVNwcmluZ0JvbmUgfSBmcm9tICcuL1ZSTVNwcmluZ0JvbmUnO1xuXG4vKipcbiAqIFJlcHJlc2VudHMgYSBzaW5nbGUgc3ByaW5nIGJvbmUgZ3JvdXAgb2YgYSBWUk0uXG4gKi9cbmV4cG9ydCB0eXBlIFZSTVNwcmluZ0JvbmVHcm91cCA9IFZSTVNwcmluZ0JvbmVbXTtcblxuLyoqXG4gKiBBIGNsYXNzIG1hbmFnZXMgZXZlcnkgc3ByaW5nIGJvbmVzIG9uIGEgVlJNLlxuICovXG5leHBvcnQgY2xhc3MgVlJNU3ByaW5nQm9uZU1hbmFnZXIge1xuICBwdWJsaWMgcmVhZG9ubHkgc3ByaW5nQm9uZUdyb3VwTGlzdDogVlJNU3ByaW5nQm9uZUdyb3VwW10gPSBbXTtcblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IFtbVlJNU3ByaW5nQm9uZU1hbmFnZXJdXVxuICAgKlxuICAgKiBAcGFyYW0gc3ByaW5nQm9uZUdyb3VwTGlzdCBBbiBhcnJheSBvZiBbW1ZSTVNwcmluZ0JvbmVHcm91cF1dXG4gICAqL1xuICBwdWJsaWMgY29uc3RydWN0b3Ioc3ByaW5nQm9uZUdyb3VwTGlzdDogVlJNU3ByaW5nQm9uZUdyb3VwW10pIHtcbiAgICB0aGlzLnNwcmluZ0JvbmVHcm91cExpc3QgPSBzcHJpbmdCb25lR3JvdXBMaXN0O1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSBldmVyeSBzcHJpbmcgYm9uZSBhdHRhY2hlZCB0byB0aGlzIG1hbmFnZXIuXG4gICAqXG4gICAqIEBwYXJhbSBkZWx0YSBkZWx0YVRpbWVcbiAgICovXG4gIHB1YmxpYyBsYXRlVXBkYXRlKGRlbHRhOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLnNwcmluZ0JvbmVHcm91cExpc3QuZm9yRWFjaCgoc3ByaW5nQm9uZUdyb3VwKSA9PiB7XG4gICAgICBzcHJpbmdCb25lR3JvdXAuZm9yRWFjaCgoc3ByaW5nQm9uZSkgPT4ge1xuICAgICAgICBzcHJpbmdCb25lLnVwZGF0ZShkZWx0YSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNldCBldmVyeSBzcHJpbmcgYm9uZSBhdHRhY2hlZCB0byB0aGlzIG1hbmFnZXIuXG4gICAqL1xuICBwdWJsaWMgcmVzZXQoKTogdm9pZCB7XG4gICAgdGhpcy5zcHJpbmdCb25lR3JvdXBMaXN0LmZvckVhY2goKHNwcmluZ0JvbmVHcm91cCkgPT4ge1xuICAgICAgc3ByaW5nQm9uZUdyb3VwLmZvckVhY2goKHNwcmluZ0JvbmUpID0+IHtcbiAgICAgICAgc3ByaW5nQm9uZS5yZXNldCgpO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cbn1cbiIsImV4cG9ydCAqIGZyb20gJy4vVlJNU3ByaW5nQm9uZSc7XG5leHBvcnQgKiBmcm9tICcuL1ZSTVNwcmluZ0JvbmVDb2xsaWRlckdyb3VwJztcbmV4cG9ydCAqIGZyb20gJy4vVlJNU3ByaW5nQm9uZUltcG9ydGVyJztcbmV4cG9ydCAqIGZyb20gJy4vVlJNU3ByaW5nQm9uZU1hbmFnZXInO1xuIiwiLy8gVHlwZWRvYyBkb2VzIG5vdCBzdXBwb3J0IGV4cG9ydCBkZWNsYXJhdGlvbnMgeWV0XG4vLyB0aGVuIHdlIGhhdmUgdG8gdXNlIGBuYW1lc3BhY2VgIGluc3RlYWQgb2YgZXhwb3J0IGRlY2xhcmF0aW9ucyBmb3Igbm93LlxuLy8gU2VlOiBodHRwczovL2dpdGh1Yi5jb20vVHlwZVN0cm9uZy90eXBlZG9jL3B1bGwvODAxXG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tbmFtZXNwYWNlXG5leHBvcnQgbmFtZXNwYWNlIFZSTVNjaGVtYSB7XG4gIC8qKlxuICAgKiBWUk0gZXh0ZW5zaW9uIGlzIGZvciAzZCBodW1hbm9pZCBhdmF0YXJzIChhbmQgbW9kZWxzKSBpbiBWUiBhcHBsaWNhdGlvbnMuXG4gICAqL1xuICBleHBvcnQgaW50ZXJmYWNlIFZSTSB7XG4gICAgYmxlbmRTaGFwZU1hc3Rlcj86IEJsZW5kU2hhcGU7XG4gICAgLyoqXG4gICAgICogVmVyc2lvbiBvZiBleHBvcnRlciB0aGF0IHZybSBjcmVhdGVkLiBVbmlWUk0tMC41My4wXG4gICAgICovXG4gICAgZXhwb3J0ZXJWZXJzaW9uPzogc3RyaW5nO1xuICAgIGZpcnN0UGVyc29uPzogRmlyc3RQZXJzb247XG4gICAgaHVtYW5vaWQ/OiBIdW1hbm9pZDtcbiAgICBtYXRlcmlhbFByb3BlcnRpZXM/OiBNYXRlcmlhbFtdO1xuICAgIG1ldGE/OiBNZXRhO1xuICAgIHNlY29uZGFyeUFuaW1hdGlvbj86IFNlY29uZGFyeUFuaW1hdGlvbjtcbiAgICAvKipcbiAgICAgKiBWZXJzaW9uIG9mIFZSTSBzcGVjaWZpY2F0aW9uLiAwLjBcbiAgICAgKi9cbiAgICBzcGVjVmVyc2lvbj86IHN0cmluZztcbiAgfVxuXG4gIC8qKlxuICAgKiBCbGVuZFNoYXBlQXZhdGFyIG9mIFVuaVZSTVxuICAgKi9cbiAgZXhwb3J0IGludGVyZmFjZSBCbGVuZFNoYXBlIHtcbiAgICBibGVuZFNoYXBlR3JvdXBzPzogQmxlbmRTaGFwZUdyb3VwW107XG4gIH1cblxuICBleHBvcnQgaW50ZXJmYWNlIEJsZW5kU2hhcGVHcm91cCB7XG4gICAgLyoqXG4gICAgICogTG93IGxldmVsIGJsZW5kc2hhcGUgcmVmZXJlbmNlcy5cbiAgICAgKi9cbiAgICBiaW5kcz86IEJsZW5kU2hhcGVCaW5kW107XG4gICAgLyoqXG4gICAgICogMCBvciAxLiBEbyBub3QgYWxsb3cgYW4gaW50ZXJtZWRpYXRlIHZhbHVlLiBWYWx1ZSBzaG91bGQgcm91bmRlZFxuICAgICAqL1xuICAgIGlzQmluYXJ5PzogYm9vbGVhbjtcbiAgICAvKipcbiAgICAgKiBNYXRlcmlhbCBhbmltYXRpb24gcmVmZXJlbmNlcy5cbiAgICAgKi9cbiAgICBtYXRlcmlhbFZhbHVlcz86IEJsZW5kU2hhcGVNYXRlcmlhbGJpbmRbXTtcbiAgICAvKipcbiAgICAgKiBFeHByZXNzaW9uIG5hbWVcbiAgICAgKi9cbiAgICBuYW1lPzogc3RyaW5nO1xuICAgIC8qKlxuICAgICAqIFByZWRlZmluZWQgRXhwcmVzc2lvbiBuYW1lXG4gICAgICovXG4gICAgcHJlc2V0TmFtZT86IEJsZW5kU2hhcGVQcmVzZXROYW1lO1xuICB9XG5cbiAgZXhwb3J0IGludGVyZmFjZSBCbGVuZFNoYXBlQmluZCB7XG4gICAgaW5kZXg/OiBudW1iZXI7XG4gICAgbWVzaD86IG51bWJlcjtcbiAgICAvKipcbiAgICAgKiBTa2lubmVkTWVzaFJlbmRlcmVyLlNldEJsZW5kU2hhcGVXZWlnaHRcbiAgICAgKi9cbiAgICB3ZWlnaHQ/OiBudW1iZXI7XG4gIH1cblxuICBleHBvcnQgaW50ZXJmYWNlIEJsZW5kU2hhcGVNYXRlcmlhbGJpbmQge1xuICAgIG1hdGVyaWFsTmFtZT86IHN0cmluZztcbiAgICBwcm9wZXJ0eU5hbWU/OiBzdHJpbmc7XG4gICAgdGFyZ2V0VmFsdWU/OiBudW1iZXJbXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcmVkZWZpbmVkIEV4cHJlc3Npb24gbmFtZVxuICAgKi9cbiAgZXhwb3J0IGVudW0gQmxlbmRTaGFwZVByZXNldE5hbWUge1xuICAgIEEgPSAnYScsXG4gICAgQW5ncnkgPSAnYW5ncnknLFxuICAgIEJsaW5rID0gJ2JsaW5rJyxcbiAgICBCbGlua0wgPSAnYmxpbmtfbCcsXG4gICAgQmxpbmtSID0gJ2JsaW5rX3InLFxuICAgIEUgPSAnZScsXG4gICAgRnVuID0gJ2Z1bicsXG4gICAgSSA9ICdpJyxcbiAgICBKb3kgPSAnam95JyxcbiAgICBMb29rZG93biA9ICdsb29rZG93bicsXG4gICAgTG9va2xlZnQgPSAnbG9va2xlZnQnLFxuICAgIExvb2tyaWdodCA9ICdsb29rcmlnaHQnLFxuICAgIExvb2t1cCA9ICdsb29rdXAnLFxuICAgIE5ldXRyYWwgPSAnbmV1dHJhbCcsXG4gICAgTyA9ICdvJyxcbiAgICBTb3Jyb3cgPSAnc29ycm93JyxcbiAgICBVID0gJ3UnLFxuICAgIFVua25vd24gPSAndW5rbm93bicsXG4gIH1cblxuICBleHBvcnQgaW50ZXJmYWNlIEZpcnN0UGVyc29uIHtcbiAgICAvKipcbiAgICAgKiBUaGUgYm9uZSB3aG9zZSByZW5kZXJpbmcgc2hvdWxkIGJlIHR1cm5lZCBvZmYgaW4gZmlyc3QtcGVyc29uIHZpZXcuIFVzdWFsbHkgSGVhZCBpc1xuICAgICAqIHNwZWNpZmllZC5cbiAgICAgKi9cbiAgICBmaXJzdFBlcnNvbkJvbmU/OiBudW1iZXI7XG4gICAgLyoqXG4gICAgICogVGhlIHRhcmdldCBwb3NpdGlvbiBvZiB0aGUgVlIgaGVhZHNldCBpbiBmaXJzdC1wZXJzb24gdmlldy4gSXQgaXMgYXNzdW1lZCB0aGF0IGFuIG9mZnNldFxuICAgICAqIGZyb20gdGhlIGhlYWQgYm9uZSB0byB0aGUgVlIgaGVhZHNldCBpcyBhZGRlZC5cbiAgICAgKi9cbiAgICBmaXJzdFBlcnNvbkJvbmVPZmZzZXQ/OiBWZWN0b3IzO1xuICAgIGxvb2tBdEhvcml6b250YWxJbm5lcj86IEZpcnN0UGVyc29uRGVncmVlTWFwO1xuICAgIGxvb2tBdEhvcml6b250YWxPdXRlcj86IEZpcnN0UGVyc29uRGVncmVlTWFwO1xuICAgIC8qKlxuICAgICAqIEV5ZSBjb250cm9sbGVyIG1vZGUuXG4gICAgICovXG4gICAgbG9va0F0VHlwZU5hbWU/OiBGaXJzdFBlcnNvbkxvb2tBdFR5cGVOYW1lO1xuICAgIGxvb2tBdFZlcnRpY2FsRG93bj86IEZpcnN0UGVyc29uRGVncmVlTWFwO1xuICAgIGxvb2tBdFZlcnRpY2FsVXA/OiBGaXJzdFBlcnNvbkRlZ3JlZU1hcDtcbiAgICAvKipcbiAgICAgKiBTd2l0Y2ggZGlzcGxheSAvIHVuZGlzcGxheSBmb3IgZWFjaCBtZXNoIGluIGZpcnN0LXBlcnNvbiB2aWV3IG9yIHRoZSBvdGhlcnMuXG4gICAgICovXG4gICAgbWVzaEFubm90YXRpb25zPzogRmlyc3RQZXJzb25NZXNoYW5ub3RhdGlvbltdO1xuICB9XG5cbiAgLyoqXG4gICAqIEV5ZSBjb250cm9sbGVyIHNldHRpbmcuXG4gICAqL1xuICBleHBvcnQgaW50ZXJmYWNlIEZpcnN0UGVyc29uRGVncmVlTWFwIHtcbiAgICAvKipcbiAgICAgKiBOb25lIGxpbmVhciBtYXBwaW5nIHBhcmFtcy4gdGltZSwgdmFsdWUsIGluVGFuZ2VudCwgb3V0VGFuZ2VudFxuICAgICAqL1xuICAgIGN1cnZlPzogbnVtYmVyW107XG4gICAgLyoqXG4gICAgICogTG9vayBhdCBpbnB1dCBjbGFtcCByYW5nZSBkZWdyZWUuXG4gICAgICovXG4gICAgeFJhbmdlPzogbnVtYmVyO1xuICAgIC8qKlxuICAgICAqIExvb2sgYXQgbWFwIHJhbmdlIGRlZ3JlZSBmcm9tIHhSYW5nZS5cbiAgICAgKi9cbiAgICB5UmFuZ2U/OiBudW1iZXI7XG4gIH1cblxuICAvKipcbiAgICogRXllIGNvbnRyb2xsZXIgbW9kZS5cbiAgICovXG4gIGV4cG9ydCBlbnVtIEZpcnN0UGVyc29uTG9va0F0VHlwZU5hbWUge1xuICAgIEJsZW5kU2hhcGUgPSAnQmxlbmRTaGFwZScsXG4gICAgQm9uZSA9ICdCb25lJyxcbiAgfVxuXG4gIGV4cG9ydCBpbnRlcmZhY2UgRmlyc3RQZXJzb25NZXNoYW5ub3RhdGlvbiB7XG4gICAgZmlyc3RQZXJzb25GbGFnPzogc3RyaW5nO1xuICAgIG1lc2g/OiBudW1iZXI7XG4gIH1cblxuICBleHBvcnQgaW50ZXJmYWNlIEh1bWFub2lkIHtcbiAgICAvKipcbiAgICAgKiBVbml0eSdzIEh1bWFuRGVzY3JpcHRpb24uYXJtU3RyZXRjaFxuICAgICAqL1xuICAgIGFybVN0cmV0Y2g/OiBudW1iZXI7XG4gICAgLyoqXG4gICAgICogVW5pdHkncyBIdW1hbkRlc2NyaXB0aW9uLmZlZXRTcGFjaW5nXG4gICAgICovXG4gICAgZmVldFNwYWNpbmc/OiBudW1iZXI7XG4gICAgLyoqXG4gICAgICogVW5pdHkncyBIdW1hbkRlc2NyaXB0aW9uLmhhc1RyYW5zbGF0aW9uRG9GXG4gICAgICovXG4gICAgaGFzVHJhbnNsYXRpb25Eb0Y/OiBib29sZWFuO1xuICAgIGh1bWFuQm9uZXM/OiBIdW1hbm9pZEJvbmVbXTtcbiAgICAvKipcbiAgICAgKiBVbml0eSdzIEh1bWFuRGVzY3JpcHRpb24ubGVnU3RyZXRjaFxuICAgICAqL1xuICAgIGxlZ1N0cmV0Y2g/OiBudW1iZXI7XG4gICAgLyoqXG4gICAgICogVW5pdHkncyBIdW1hbkRlc2NyaXB0aW9uLmxvd2VyQXJtVHdpc3RcbiAgICAgKi9cbiAgICBsb3dlckFybVR3aXN0PzogbnVtYmVyO1xuICAgIC8qKlxuICAgICAqIFVuaXR5J3MgSHVtYW5EZXNjcmlwdGlvbi5sb3dlckxlZ1R3aXN0XG4gICAgICovXG4gICAgbG93ZXJMZWdUd2lzdD86IG51bWJlcjtcbiAgICAvKipcbiAgICAgKiBVbml0eSdzIEh1bWFuRGVzY3JpcHRpb24udXBwZXJBcm1Ud2lzdFxuICAgICAqL1xuICAgIHVwcGVyQXJtVHdpc3Q/OiBudW1iZXI7XG4gICAgLyoqXG4gICAgICogVW5pdHkncyBIdW1hbkRlc2NyaXB0aW9uLnVwcGVyTGVnVHdpc3RcbiAgICAgKi9cbiAgICB1cHBlckxlZ1R3aXN0PzogbnVtYmVyO1xuICB9XG5cbiAgZXhwb3J0IGludGVyZmFjZSBIdW1hbm9pZEJvbmUge1xuICAgIC8qKlxuICAgICAqIFVuaXR5J3MgSHVtYW5MaW1pdC5heGlzTGVuZ3RoXG4gICAgICovXG4gICAgYXhpc0xlbmd0aD86IG51bWJlcjtcbiAgICAvKipcbiAgICAgKiBIdW1hbiBib25lIG5hbWUuXG4gICAgICovXG4gICAgYm9uZT86IEh1bWFub2lkQm9uZU5hbWU7XG4gICAgLyoqXG4gICAgICogVW5pdHkncyBIdW1hbkxpbWl0LmNlbnRlclxuICAgICAqL1xuICAgIGNlbnRlcj86IFZlY3RvcjM7XG4gICAgLyoqXG4gICAgICogVW5pdHkncyBIdW1hbkxpbWl0Lm1heFxuICAgICAqL1xuICAgIG1heD86IFZlY3RvcjM7XG4gICAgLyoqXG4gICAgICogVW5pdHkncyBIdW1hbkxpbWl0Lm1pblxuICAgICAqL1xuICAgIG1pbj86IFZlY3RvcjM7XG4gICAgLyoqXG4gICAgICogUmVmZXJlbmNlIG5vZGUgaW5kZXhcbiAgICAgKi9cbiAgICBub2RlPzogbnVtYmVyO1xuICAgIC8qKlxuICAgICAqIFVuaXR5J3MgSHVtYW5MaW1pdC51c2VEZWZhdWx0VmFsdWVzXG4gICAgICovXG4gICAgdXNlRGVmYXVsdFZhbHVlcz86IGJvb2xlYW47XG4gIH1cblxuICAvKipcbiAgICogSHVtYW4gYm9uZSBuYW1lLlxuICAgKi9cbiAgZXhwb3J0IGVudW0gSHVtYW5vaWRCb25lTmFtZSB7XG4gICAgQ2hlc3QgPSAnY2hlc3QnLFxuICAgIEhlYWQgPSAnaGVhZCcsXG4gICAgSGlwcyA9ICdoaXBzJyxcbiAgICBKYXcgPSAnamF3JyxcbiAgICBMZWZ0RXllID0gJ2xlZnRFeWUnLFxuICAgIExlZnRGb290ID0gJ2xlZnRGb290JyxcbiAgICBMZWZ0SGFuZCA9ICdsZWZ0SGFuZCcsXG4gICAgTGVmdEluZGV4RGlzdGFsID0gJ2xlZnRJbmRleERpc3RhbCcsXG4gICAgTGVmdEluZGV4SW50ZXJtZWRpYXRlID0gJ2xlZnRJbmRleEludGVybWVkaWF0ZScsXG4gICAgTGVmdEluZGV4UHJveGltYWwgPSAnbGVmdEluZGV4UHJveGltYWwnLFxuICAgIExlZnRMaXR0bGVEaXN0YWwgPSAnbGVmdExpdHRsZURpc3RhbCcsXG4gICAgTGVmdExpdHRsZUludGVybWVkaWF0ZSA9ICdsZWZ0TGl0dGxlSW50ZXJtZWRpYXRlJyxcbiAgICBMZWZ0TGl0dGxlUHJveGltYWwgPSAnbGVmdExpdHRsZVByb3hpbWFsJyxcbiAgICBMZWZ0TG93ZXJBcm0gPSAnbGVmdExvd2VyQXJtJyxcbiAgICBMZWZ0TG93ZXJMZWcgPSAnbGVmdExvd2VyTGVnJyxcbiAgICBMZWZ0TWlkZGxlRGlzdGFsID0gJ2xlZnRNaWRkbGVEaXN0YWwnLFxuICAgIExlZnRNaWRkbGVJbnRlcm1lZGlhdGUgPSAnbGVmdE1pZGRsZUludGVybWVkaWF0ZScsXG4gICAgTGVmdE1pZGRsZVByb3hpbWFsID0gJ2xlZnRNaWRkbGVQcm94aW1hbCcsXG4gICAgTGVmdFJpbmdEaXN0YWwgPSAnbGVmdFJpbmdEaXN0YWwnLFxuICAgIExlZnRSaW5nSW50ZXJtZWRpYXRlID0gJ2xlZnRSaW5nSW50ZXJtZWRpYXRlJyxcbiAgICBMZWZ0UmluZ1Byb3hpbWFsID0gJ2xlZnRSaW5nUHJveGltYWwnLFxuICAgIExlZnRTaG91bGRlciA9ICdsZWZ0U2hvdWxkZXInLFxuICAgIExlZnRUaHVtYkRpc3RhbCA9ICdsZWZ0VGh1bWJEaXN0YWwnLFxuICAgIExlZnRUaHVtYkludGVybWVkaWF0ZSA9ICdsZWZ0VGh1bWJJbnRlcm1lZGlhdGUnLFxuICAgIExlZnRUaHVtYlByb3hpbWFsID0gJ2xlZnRUaHVtYlByb3hpbWFsJyxcbiAgICBMZWZ0VG9lcyA9ICdsZWZ0VG9lcycsXG4gICAgTGVmdFVwcGVyQXJtID0gJ2xlZnRVcHBlckFybScsXG4gICAgTGVmdFVwcGVyTGVnID0gJ2xlZnRVcHBlckxlZycsXG4gICAgTmVjayA9ICduZWNrJyxcbiAgICBSaWdodEV5ZSA9ICdyaWdodEV5ZScsXG4gICAgUmlnaHRGb290ID0gJ3JpZ2h0Rm9vdCcsXG4gICAgUmlnaHRIYW5kID0gJ3JpZ2h0SGFuZCcsXG4gICAgUmlnaHRJbmRleERpc3RhbCA9ICdyaWdodEluZGV4RGlzdGFsJyxcbiAgICBSaWdodEluZGV4SW50ZXJtZWRpYXRlID0gJ3JpZ2h0SW5kZXhJbnRlcm1lZGlhdGUnLFxuICAgIFJpZ2h0SW5kZXhQcm94aW1hbCA9ICdyaWdodEluZGV4UHJveGltYWwnLFxuICAgIFJpZ2h0TGl0dGxlRGlzdGFsID0gJ3JpZ2h0TGl0dGxlRGlzdGFsJyxcbiAgICBSaWdodExpdHRsZUludGVybWVkaWF0ZSA9ICdyaWdodExpdHRsZUludGVybWVkaWF0ZScsXG4gICAgUmlnaHRMaXR0bGVQcm94aW1hbCA9ICdyaWdodExpdHRsZVByb3hpbWFsJyxcbiAgICBSaWdodExvd2VyQXJtID0gJ3JpZ2h0TG93ZXJBcm0nLFxuICAgIFJpZ2h0TG93ZXJMZWcgPSAncmlnaHRMb3dlckxlZycsXG4gICAgUmlnaHRNaWRkbGVEaXN0YWwgPSAncmlnaHRNaWRkbGVEaXN0YWwnLFxuICAgIFJpZ2h0TWlkZGxlSW50ZXJtZWRpYXRlID0gJ3JpZ2h0TWlkZGxlSW50ZXJtZWRpYXRlJyxcbiAgICBSaWdodE1pZGRsZVByb3hpbWFsID0gJ3JpZ2h0TWlkZGxlUHJveGltYWwnLFxuICAgIFJpZ2h0UmluZ0Rpc3RhbCA9ICdyaWdodFJpbmdEaXN0YWwnLFxuICAgIFJpZ2h0UmluZ0ludGVybWVkaWF0ZSA9ICdyaWdodFJpbmdJbnRlcm1lZGlhdGUnLFxuICAgIFJpZ2h0UmluZ1Byb3hpbWFsID0gJ3JpZ2h0UmluZ1Byb3hpbWFsJyxcbiAgICBSaWdodFNob3VsZGVyID0gJ3JpZ2h0U2hvdWxkZXInLFxuICAgIFJpZ2h0VGh1bWJEaXN0YWwgPSAncmlnaHRUaHVtYkRpc3RhbCcsXG4gICAgUmlnaHRUaHVtYkludGVybWVkaWF0ZSA9ICdyaWdodFRodW1iSW50ZXJtZWRpYXRlJyxcbiAgICBSaWdodFRodW1iUHJveGltYWwgPSAncmlnaHRUaHVtYlByb3hpbWFsJyxcbiAgICBSaWdodFRvZXMgPSAncmlnaHRUb2VzJyxcbiAgICBSaWdodFVwcGVyQXJtID0gJ3JpZ2h0VXBwZXJBcm0nLFxuICAgIFJpZ2h0VXBwZXJMZWcgPSAncmlnaHRVcHBlckxlZycsXG4gICAgU3BpbmUgPSAnc3BpbmUnLFxuICAgIFVwcGVyQ2hlc3QgPSAndXBwZXJDaGVzdCcsXG4gIH1cblxuICBleHBvcnQgaW50ZXJmYWNlIE1hdGVyaWFsIHtcbiAgICBmbG9hdFByb3BlcnRpZXM/OiB7IFtrZXk6IHN0cmluZ106IGFueSB9O1xuICAgIGtleXdvcmRNYXA/OiB7IFtrZXk6IHN0cmluZ106IGFueSB9O1xuICAgIG5hbWU/OiBzdHJpbmc7XG4gICAgcmVuZGVyUXVldWU/OiBudW1iZXI7XG4gICAgc2hhZGVyPzogc3RyaW5nO1xuICAgIHRhZ01hcD86IHsgW2tleTogc3RyaW5nXTogYW55IH07XG4gICAgdGV4dHVyZVByb3BlcnRpZXM/OiB7IFtrZXk6IHN0cmluZ106IGFueSB9O1xuICAgIHZlY3RvclByb3BlcnRpZXM/OiB7IFtrZXk6IHN0cmluZ106IGFueSB9O1xuICB9XG5cbiAgZXhwb3J0IGludGVyZmFjZSBNZXRhIHtcbiAgICAvKipcbiAgICAgKiBBIHBlcnNvbiB3aG8gY2FuIHBlcmZvcm0gd2l0aCB0aGlzIGF2YXRhclxuICAgICAqL1xuICAgIGFsbG93ZWRVc2VyTmFtZT86IE1ldGFBbGxvd2VkVXNlck5hbWU7XG4gICAgLyoqXG4gICAgICogQXV0aG9yIG9mIFZSTSBtb2RlbFxuICAgICAqL1xuICAgIGF1dGhvcj86IHN0cmluZztcbiAgICAvKipcbiAgICAgKiBGb3IgY29tbWVyY2lhbCB1c2VcbiAgICAgKi9cbiAgICBjb21tZXJjaWFsVXNzYWdlTmFtZT86IE1ldGFVc3NhZ2VOYW1lO1xuICAgIC8qKlxuICAgICAqIENvbnRhY3QgSW5mb3JtYXRpb24gb2YgVlJNIG1vZGVsIGF1dGhvclxuICAgICAqL1xuICAgIGNvbnRhY3RJbmZvcm1hdGlvbj86IHN0cmluZztcbiAgICAvKipcbiAgICAgKiBMaWNlbnNlIHR5cGVcbiAgICAgKi9cbiAgICBsaWNlbnNlTmFtZT86IE1ldGFMaWNlbnNlTmFtZTtcbiAgICAvKipcbiAgICAgKiBJZiDigJxPdGhlcuKAnSBpcyBzZWxlY3RlZCwgcHV0IHRoZSBVUkwgbGluayBvZiB0aGUgbGljZW5zZSBkb2N1bWVudCBoZXJlLlxuICAgICAqL1xuICAgIG90aGVyTGljZW5zZVVybD86IHN0cmluZztcbiAgICAvKipcbiAgICAgKiBJZiB0aGVyZSBhcmUgYW55IGNvbmRpdGlvbnMgbm90IG1lbnRpb25lZCBhYm92ZSwgcHV0IHRoZSBVUkwgbGluayBvZiB0aGUgbGljZW5zZSBkb2N1bWVudFxuICAgICAqIGhlcmUuXG4gICAgICovXG4gICAgb3RoZXJQZXJtaXNzaW9uVXJsPzogc3RyaW5nO1xuICAgIC8qKlxuICAgICAqIFJlZmVyZW5jZSBvZiBWUk0gbW9kZWxcbiAgICAgKi9cbiAgICByZWZlcmVuY2U/OiBzdHJpbmc7XG4gICAgLyoqXG4gICAgICogUGVybWlzc2lvbiB0byBwZXJmb3JtIHNleHVhbCBhY3RzIHdpdGggdGhpcyBhdmF0YXJcbiAgICAgKi9cbiAgICBzZXh1YWxVc3NhZ2VOYW1lPzogTWV0YVVzc2FnZU5hbWU7XG4gICAgLyoqXG4gICAgICogVGh1bWJuYWlsIG9mIFZSTSBtb2RlbFxuICAgICAqL1xuICAgIHRleHR1cmU/OiBudW1iZXI7XG4gICAgLyoqXG4gICAgICogVGl0bGUgb2YgVlJNIG1vZGVsXG4gICAgICovXG4gICAgdGl0bGU/OiBzdHJpbmc7XG4gICAgLyoqXG4gICAgICogVmVyc2lvbiBvZiBWUk0gbW9kZWxcbiAgICAgKi9cbiAgICB2ZXJzaW9uPzogc3RyaW5nO1xuICAgIC8qKlxuICAgICAqIFBlcm1pc3Npb24gdG8gcGVyZm9ybSB2aW9sZW50IGFjdHMgd2l0aCB0aGlzIGF2YXRhclxuICAgICAqL1xuICAgIHZpb2xlbnRVc3NhZ2VOYW1lPzogTWV0YVVzc2FnZU5hbWU7XG4gIH1cblxuICAvKipcbiAgICogQSBwZXJzb24gd2hvIGNhbiBwZXJmb3JtIHdpdGggdGhpcyBhdmF0YXJcbiAgICovXG4gIGV4cG9ydCBlbnVtIE1ldGFBbGxvd2VkVXNlck5hbWUge1xuICAgIEV2ZXJ5b25lID0gJ0V2ZXJ5b25lJyxcbiAgICBFeHBsaWNpdGx5TGljZW5zZWRQZXJzb24gPSAnRXhwbGljaXRseUxpY2Vuc2VkUGVyc29uJyxcbiAgICBPbmx5QXV0aG9yID0gJ09ubHlBdXRob3InLFxuICB9XG5cbiAgLyoqXG4gICAqIEZvciBjb21tZXJjaWFsIHVzZVxuICAgKlxuICAgKiBQZXJtaXNzaW9uIHRvIHBlcmZvcm0gc2V4dWFsIGFjdHMgd2l0aCB0aGlzIGF2YXRhclxuICAgKlxuICAgKiBQZXJtaXNzaW9uIHRvIHBlcmZvcm0gdmlvbGVudCBhY3RzIHdpdGggdGhpcyBhdmF0YXJcbiAgICovXG4gIGV4cG9ydCBlbnVtIE1ldGFVc3NhZ2VOYW1lIHtcbiAgICBBbGxvdyA9ICdBbGxvdycsXG4gICAgRGlzYWxsb3cgPSAnRGlzYWxsb3cnLFxuICB9XG5cbiAgLyoqXG4gICAqIExpY2Vuc2UgdHlwZVxuICAgKi9cbiAgZXhwb3J0IGVudW0gTWV0YUxpY2Vuc2VOYW1lIHtcbiAgICBDYzAgPSAnQ0MwJyxcbiAgICBDY0J5ID0gJ0NDX0JZJyxcbiAgICBDY0J5TmMgPSAnQ0NfQllfTkMnLFxuICAgIENjQnlOY05kID0gJ0NDX0JZX05DX05EJyxcbiAgICBDY0J5TmNTYSA9ICdDQ19CWV9OQ19TQScsXG4gICAgQ2NCeU5kID0gJ0NDX0JZX05EJyxcbiAgICBDY0J5U2EgPSAnQ0NfQllfU0EnLFxuICAgIE90aGVyID0gJ090aGVyJyxcbiAgICBSZWRpc3RyaWJ1dGlvblByb2hpYml0ZWQgPSAnUmVkaXN0cmlidXRpb25fUHJvaGliaXRlZCcsXG4gIH1cblxuICAvKipcbiAgICogVGhlIHNldHRpbmcgb2YgYXV0b21hdGljIGFuaW1hdGlvbiBvZiBzdHJpbmctbGlrZSBvYmplY3RzIHN1Y2ggYXMgdGFpbHMgYW5kIGhhaXJzLlxuICAgKi9cbiAgZXhwb3J0IGludGVyZmFjZSBTZWNvbmRhcnlBbmltYXRpb24ge1xuICAgIGJvbmVHcm91cHM/OiBTZWNvbmRhcnlBbmltYXRpb25TcHJpbmdbXTtcbiAgICBjb2xsaWRlckdyb3Vwcz86IFNlY29uZGFyeUFuaW1hdGlvbkNvbGxpZGVyZ3JvdXBbXTtcbiAgfVxuXG4gIGV4cG9ydCBpbnRlcmZhY2UgU2Vjb25kYXJ5QW5pbWF0aW9uU3ByaW5nIHtcbiAgICAvKipcbiAgICAgKiBTcGVjaWZ5IHRoZSBub2RlIGluZGV4IG9mIHRoZSByb290IGJvbmUgb2YgdGhlIHN3YXlpbmcgb2JqZWN0LlxuICAgICAqL1xuICAgIGJvbmVzPzogbnVtYmVyW107XG4gICAgLyoqXG4gICAgICogVGhlIHJlZmVyZW5jZSBwb2ludCBvZiBhIHN3YXlpbmcgb2JqZWN0IGNhbiBiZSBzZXQgYXQgYW55IGxvY2F0aW9uIGV4Y2VwdCB0aGUgb3JpZ2luLlxuICAgICAqIFdoZW4gaW1wbGVtZW50aW5nIFVJIG1vdmluZyB3aXRoIHdhcnAsIHRoZSBwYXJlbnQgbm9kZSB0byBtb3ZlIHdpdGggd2FycCBjYW4gYmUgc3BlY2lmaWVkXG4gICAgICogaWYgeW91IGRvbid0IHdhbnQgdG8gbWFrZSB0aGUgb2JqZWN0IHN3YXlpbmcgd2l0aCB3YXJwIG1vdmVtZW50LlxuICAgICAqL1xuICAgIGNlbnRlcj86IG51bWJlcjtcbiAgICAvKipcbiAgICAgKiBTcGVjaWZ5IHRoZSBpbmRleCBvZiB0aGUgY29sbGlkZXIgZ3JvdXAgZm9yIGNvbGxpc2lvbnMgd2l0aCBzd2F5aW5nIG9iamVjdHMuXG4gICAgICovXG4gICAgY29sbGlkZXJHcm91cHM/OiBudW1iZXJbXTtcbiAgICAvKipcbiAgICAgKiBBbm5vdGF0aW9uIGNvbW1lbnRcbiAgICAgKi9cbiAgICBjb21tZW50Pzogc3RyaW5nO1xuICAgIC8qKlxuICAgICAqIFRoZSByZXNpc3RhbmNlIChkZWNlbGVyYXRpb24pIG9mIGF1dG9tYXRpYyBhbmltYXRpb24uXG4gICAgICovXG4gICAgZHJhZ0ZvcmNlPzogbnVtYmVyO1xuICAgIC8qKlxuICAgICAqIFRoZSBkaXJlY3Rpb24gb2YgZ3Jhdml0eS4gU2V0ICgwLCAtMSwgMCkgZm9yIHNpbXVsYXRpbmcgdGhlIGdyYXZpdHkuIFNldCAoMSwgMCwgMCkgZm9yXG4gICAgICogc2ltdWxhdGluZyB0aGUgd2luZC5cbiAgICAgKi9cbiAgICBncmF2aXR5RGlyPzogVmVjdG9yMztcbiAgICAvKipcbiAgICAgKiBUaGUgc3RyZW5ndGggb2YgZ3Jhdml0eS5cbiAgICAgKi9cbiAgICBncmF2aXR5UG93ZXI/OiBudW1iZXI7XG4gICAgLyoqXG4gICAgICogVGhlIHJhZGl1cyBvZiB0aGUgc3BoZXJlIHVzZWQgZm9yIHRoZSBjb2xsaXNpb24gZGV0ZWN0aW9uIHdpdGggY29sbGlkZXJzLlxuICAgICAqL1xuICAgIGhpdFJhZGl1cz86IG51bWJlcjtcbiAgICAvKipcbiAgICAgKiBUaGUgcmVzaWxpZW5jZSBvZiB0aGUgc3dheWluZyBvYmplY3QgKHRoZSBwb3dlciBvZiByZXR1cm5pbmcgdG8gdGhlIGluaXRpYWwgcG9zZSkuXG4gICAgICovXG4gICAgc3RpZmZpbmVzcz86IG51bWJlcjtcbiAgfVxuXG4gIGV4cG9ydCBpbnRlcmZhY2UgU2Vjb25kYXJ5QW5pbWF0aW9uQ29sbGlkZXJncm91cCB7XG4gICAgY29sbGlkZXJzPzogU2Vjb25kYXJ5QW5pbWF0aW9uQ29sbGlkZXJbXTtcbiAgICAvKipcbiAgICAgKiBUaGUgbm9kZSBvZiB0aGUgY29sbGlkZXIgZ3JvdXAgZm9yIHNldHRpbmcgdXAgY29sbGlzaW9uIGRldGVjdGlvbnMuXG4gICAgICovXG4gICAgbm9kZT86IG51bWJlcjtcbiAgfVxuXG4gIGV4cG9ydCBpbnRlcmZhY2UgU2Vjb25kYXJ5QW5pbWF0aW9uQ29sbGlkZXIge1xuICAgIC8qKlxuICAgICAqIFRoZSBsb2NhbCBjb29yZGluYXRlIGZyb20gdGhlIG5vZGUgb2YgdGhlIGNvbGxpZGVyIGdyb3VwLlxuICAgICAqL1xuICAgIG9mZnNldD86IFZlY3RvcjM7XG4gICAgLyoqXG4gICAgICogVGhlIHJhZGl1cyBvZiB0aGUgY29sbGlkZXIuXG4gICAgICovXG4gICAgcmFkaXVzPzogbnVtYmVyO1xuICB9XG5cbiAgZXhwb3J0IGludGVyZmFjZSBWZWN0b3IzIHtcbiAgICB4PzogbnVtYmVyO1xuICAgIHk/OiBudW1iZXI7XG4gICAgej86IG51bWJlcjtcbiAgfVxufVxuIiwiLy8gVHlwZWRvYyBkb2VzIG5vdCBzdXBwb3J0IGV4cG9ydCBkZWNsYXJhdGlvbnMgeWV0XG4vLyB0aGVuIHdlIGhhdmUgdG8gdXNlIGBuYW1lc3BhY2VgIGluc3RlYWQgb2YgZXhwb3J0IGRlY2xhcmF0aW9ucyBmb3Igbm93LlxuLy8gU2VlOiBodHRwczovL2dpdGh1Yi5jb20vVHlwZVN0cm9uZy90eXBlZG9jL3B1bGwvODAxXG5cbi8vIGltcG9ydCAqIGFzIEdMVEZTY2hlbWEgZnJvbSAnLi9HTFRGU2NoZW1hJztcbi8vIGltcG9ydCAqIGFzIFZSTVNjaGVtYSBmcm9tICcuL1ZSTVNjaGVtYSc7XG5cbi8vIGV4cG9ydCB7IEdMVEZTY2hlbWEsIFZSTVNjaGVtYSB9O1xuXG5leHBvcnQgKiBmcm9tICcuL0dMVEZTY2hlbWEnO1xuZXhwb3J0ICogZnJvbSAnLi9WUk1TY2hlbWEnO1xuXG5leHBvcnQgKiBmcm9tICcuL3R5cGVzJztcbiIsImltcG9ydCAqIGFzIFRIUkVFIGZyb20gJ3RocmVlJztcblxuZnVuY3Rpb24gZGlzcG9zZU1hdGVyaWFsKG1hdGVyaWFsOiBhbnkpOiB2b2lkIHtcbiAgT2JqZWN0LmtleXMobWF0ZXJpYWwpLmZvckVhY2goKHByb3BlcnR5TmFtZSkgPT4ge1xuICAgIGlmICghIW1hdGVyaWFsW3Byb3BlcnR5TmFtZV0gJiYgdHlwZW9mIG1hdGVyaWFsW3Byb3BlcnR5TmFtZV0uZGlzcG9zZSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgbWF0ZXJpYWxbcHJvcGVydHlOYW1lXS5kaXNwb3NlKCk7XG4gICAgfVxuICB9KTtcblxuICBtYXRlcmlhbC5kaXNwb3NlKCk7XG4gIG1hdGVyaWFsID0gdW5kZWZpbmVkO1xufVxuXG5mdW5jdGlvbiBkaXNwb3NlKG9iamVjdDNEOiBhbnkpOiB2b2lkIHtcbiAgaWYgKG9iamVjdDNELmdlb21ldHJ5KSB7XG4gICAgb2JqZWN0M0QuZ2VvbWV0cnkuZGlzcG9zZSgpO1xuICAgIG9iamVjdDNELmdlb21ldHJ5ID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgaWYgKCEhb2JqZWN0M0QubWF0ZXJpYWwgJiYgQXJyYXkuaXNBcnJheShvYmplY3QzRC5tYXRlcmlhbCkpIHtcbiAgICBvYmplY3QzRC5tYXRlcmlhbC5mb3JFYWNoKChtYXRlcmlhbDogVEhSRUUuTWF0ZXJpYWwpID0+IGRpc3Bvc2VNYXRlcmlhbChtYXRlcmlhbCkpO1xuICB9IGVsc2UgaWYgKG9iamVjdDNELm1hdGVyaWFsKSB7XG4gICAgZGlzcG9zZU1hdGVyaWFsKG9iamVjdDNELm1hdGVyaWFsKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZGVlcERpc3Bvc2Uob2JqZWN0M0Q6IFRIUkVFLk9iamVjdDNEKTogdm9pZCB7XG4gIG9iamVjdDNELnRyYXZlcnNlKGRpc3Bvc2UpO1xufVxuIiwiaW1wb3J0ICogYXMgVEhSRUUgZnJvbSAndGhyZWUnO1xuXG4vKipcbiAqIENsYW1wIGFuIGlucHV0IG51bWJlciB3aXRoaW4gWyBgMC4wYCAtIGAxLjBgIF0uXG4gKlxuICogQHBhcmFtIHZhbHVlIFRoZSBpbnB1dCB2YWx1ZVxuICovXG5leHBvcnQgZnVuY3Rpb24gc2F0dXJhdGUodmFsdWU6IG51bWJlcik6IG51bWJlciB7XG4gIHJldHVybiBNYXRoLm1heChNYXRoLm1pbih2YWx1ZSwgMS4wKSwgMC4wKTtcbn1cblxuLyoqXG4gKiBNYXAgdGhlIHJhbmdlIG9mIGFuIGlucHV0IHZhbHVlIGZyb20gWyBgbWluYCAtIGBtYXhgIF0gdG8gWyBgMC4wYCAtIGAxLjBgIF0uXG4gKiBJZiBpbnB1dCB2YWx1ZSBpcyBsZXNzIHRoYW4gYG1pbmAgLCBpdCByZXR1cm5zIGAwLjBgLlxuICogSWYgaW5wdXQgdmFsdWUgaXMgZ3JlYXRlciB0aGFuIGBtYXhgICwgaXQgcmV0dXJucyBgMS4wYC5cbiAqXG4gKiBTZWUgYWxzbzogaHR0cHM6Ly90aHJlZWpzLm9yZy9kb2NzLyNhcGkvZW4vbWF0aC9NYXRoLnNtb290aHN0ZXBcbiAqXG4gKiBAcGFyYW0geCBUaGUgdmFsdWUgdGhhdCB3aWxsIGJlIG1hcHBlZCBpbnRvIHRoZSBzcGVjaWZpZWQgcmFuZ2VcbiAqIEBwYXJhbSBtaW4gTWluaW11bSB2YWx1ZSBvZiB0aGUgcmFuZ2VcbiAqIEBwYXJhbSBtYXggTWF4aW11bSB2YWx1ZSBvZiB0aGUgcmFuZ2VcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxpbnN0ZXAoeDogbnVtYmVyLCBtaW46IG51bWJlciwgbWF4OiBudW1iZXIpOiBudW1iZXIge1xuICBpZiAoeCA8PSBtaW4pIHJldHVybiAwO1xuICBpZiAoeCA+PSBtYXgpIHJldHVybiAxO1xuXG4gIHJldHVybiAoeCAtIG1pbikgLyAobWF4IC0gbWluKTtcbn1cblxuY29uc3QgX3Bvc2l0aW9uID0gbmV3IFRIUkVFLlZlY3RvcjMoKTtcbmNvbnN0IF9zY2FsZSA9IG5ldyBUSFJFRS5WZWN0b3IzKCk7XG5jb25zdCBfcm90YXRpb24gPSBuZXcgVEhSRUUuUXVhdGVybmlvbigpO1xuXG4vKipcbiAqIEV4dHJhY3Qgd29ybGQgcG9zaXRpb24gb2YgYW4gb2JqZWN0IGZyb20gaXRzIHdvcmxkIHNwYWNlIG1hdHJpeCwgaW4gY2hlYXBlciB3YXkuXG4gKlxuICogQHBhcmFtIG9iamVjdCBUaGUgb2JqZWN0XG4gKiBAcGFyYW0gb3V0IFRhcmdldCB2ZWN0b3JcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFdvcmxkUG9zaXRpb25MaXRlKG9iamVjdDogVEhSRUUuT2JqZWN0M0QsIG91dDogVEhSRUUuVmVjdG9yMyk6IFRIUkVFLlZlY3RvcjMge1xuICBvYmplY3QubWF0cml4V29ybGQuZGVjb21wb3NlKG91dCwgX3JvdGF0aW9uLCBfc2NhbGUpO1xuICByZXR1cm4gb3V0O1xufVxuXG4vKipcbiAqIEV4dHJhY3Qgd29ybGQgc2NhbGUgb2YgYW4gb2JqZWN0IGZyb20gaXRzIHdvcmxkIHNwYWNlIG1hdHJpeCwgaW4gY2hlYXBlciB3YXkuXG4gKlxuICogQHBhcmFtIG9iamVjdCBUaGUgb2JqZWN0XG4gKiBAcGFyYW0gb3V0IFRhcmdldCB2ZWN0b3JcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFdvcmxkU2NhbGVMaXRlKG9iamVjdDogVEhSRUUuT2JqZWN0M0QsIG91dDogVEhSRUUuVmVjdG9yMyk6IFRIUkVFLlZlY3RvcjMge1xuICBvYmplY3QubWF0cml4V29ybGQuZGVjb21wb3NlKF9wb3NpdGlvbiwgX3JvdGF0aW9uLCBvdXQpO1xuICByZXR1cm4gb3V0O1xufVxuXG4vKipcbiAqIEV4dHJhY3Qgd29ybGQgcm90YXRpb24gb2YgYW4gb2JqZWN0IGZyb20gaXRzIHdvcmxkIHNwYWNlIG1hdHJpeCwgaW4gY2hlYXBlciB3YXkuXG4gKlxuICogQHBhcmFtIG9iamVjdCBUaGUgb2JqZWN0XG4gKiBAcGFyYW0gb3V0IFRhcmdldCB2ZWN0b3JcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFdvcmxkUXVhdGVybmlvbkxpdGUob2JqZWN0OiBUSFJFRS5PYmplY3QzRCwgb3V0OiBUSFJFRS5RdWF0ZXJuaW9uKTogVEhSRUUuUXVhdGVybmlvbiB7XG4gIG9iamVjdC5tYXRyaXhXb3JsZC5kZWNvbXBvc2UoX3Bvc2l0aW9uLCBvdXQsIF9zY2FsZSk7XG4gIHJldHVybiBvdXQ7XG59XG4iLCJleHBvcnQgZnVuY3Rpb24gcmVuYW1lTWF0ZXJpYWxQcm9wZXJ0eShuYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAobmFtZVswXSAhPT0gJ18nKSB7XG4gICAgY29uc29sZS53YXJuKGByZW5hbWVNYXRlcmlhbFByb3BlcnR5OiBHaXZlbiBwcm9wZXJ0eSBuYW1lIFwiJHtuYW1lfVwiIG1pZ2h0IGJlIGludmFsaWRgKTtcbiAgICByZXR1cm4gbmFtZTtcbiAgfVxuICBuYW1lID0gbmFtZS5zdWJzdHJpbmcoMSk7XG5cbiAgaWYgKCEvW0EtWl0vLnRlc3QobmFtZVswXSkpIHtcbiAgICBjb25zb2xlLndhcm4oYHJlbmFtZU1hdGVyaWFsUHJvcGVydHk6IEdpdmVuIHByb3BlcnR5IG5hbWUgXCIke25hbWV9XCIgbWlnaHQgYmUgaW52YWxpZGApO1xuICAgIHJldHVybiBuYW1lO1xuICB9XG4gIHJldHVybiBuYW1lWzBdLnRvTG93ZXJDYXNlKCkgKyBuYW1lLnN1YnN0cmluZygxKTtcbn1cbiIsIm1vZHVsZS5leHBvcnRzID0gVEhSRUU7Il0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file diff --git a/webxr-polyfill.module.js b/webxr-polyfill.module.js new file mode 100644 index 0000000..017698d --- /dev/null +++ b/webxr-polyfill.module.js @@ -0,0 +1,6139 @@ +/** + * @license + * webxr-polyfill + * Copyright (c) 2017 Google + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @license + * cardboard-vr-display + * Copyright (c) 2015-2017 Google + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @license + * webvr-polyfill-dpdb + * Copyright (c) 2017 Google + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @license + * wglu-preserve-state + * Copyright (c) 2016, Brandon Jones. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @license + * nosleep.js + * Copyright (c) 2017, Rich Tibbett + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +if (!/(Helio)/g.test(navigator.userAgent)) { + +const _global = typeof global !== 'undefined' ? global : + typeof self !== 'undefined' ? self : + typeof window !== 'undefined' ? window : {}; + +const PRIVATE = Symbol('@@webxr-polyfill/EventTarget'); +class EventTarget { + constructor() { + this[PRIVATE] = { + listeners: new Map(), + }; + } + addEventListener(type, listener) { + if (typeof type !== 'string') { throw new Error('`type` must be a string'); } + if (typeof listener !== 'function') { throw new Error('`listener` must be a function'); } + const typedListeners = this[PRIVATE].listeners.get(type) || []; + typedListeners.push(listener); + this[PRIVATE].listeners.set(type, typedListeners); + } + removeEventListener(type, listener) { + if (typeof type !== 'string') { throw new Error('`type` must be a string'); } + if (typeof listener !== 'function') { throw new Error('`listener` must be a function'); } + const typedListeners = this[PRIVATE].listeners.get(type) || []; + for (let i = typedListeners.length; i >= 0; i--) { + if (typedListeners[i] === listener) { + typedListeners.pop(); + } + } + } + dispatchEvent(type, event) { + const typedListeners = this[PRIVATE].listeners.get(type) || []; + const queue = []; + for (let i = 0; i < typedListeners.length; i++) { + queue[i] = typedListeners[i]; + } + for (let listener of queue) { + listener(event); + } + if (typeof this[`on${type}`] === 'function') { + this[`on${type}`](event); + } + } +} + +const PRIVATE$1 = Symbol('@@webxr-polyfill/XR'); +const XRSessionModes = ['inline', 'immersive-vr', 'immersive-ar']; +const POLYFILL_REQUEST_SESSION_ERROR = +`Polyfill Error: Must call navigator.xr.supportsSession() with any XRSessionMode +or navigator.xr.requestSession('inline') prior to requesting an immersive +session. This is a limitation specific to the WebXR Polyfill and does not apply +to native implementations of the API.`; +class XR$1 extends EventTarget { + constructor(devicePromise) { + super(); + this[PRIVATE$1] = { + device: null, + devicePromise, + immersiveSession: null, + inlineSessions: new Set(), + }; + devicePromise.then((device) => { this[PRIVATE$1].device = device; }); + } + async supportsSession(mode) { + if (!this[PRIVATE$1].device) { + await this[PRIVATE$1].devicePromise; + } + if (mode != 'inline') { + if (!this[PRIVATE$1].device.supportsSession(mode)) { + return Promise.reject(null); + } + } + return Promise.resolve(null); + } + async requestSession(mode) { + if (!this[PRIVATE$1].device) { + if (mode != 'inline') { + throw new Error(POLYFILL_REQUEST_SESSION_ERROR); + } else { + await this[PRIVATE$1].devicePromise; + } + } + const sessionId = await this[PRIVATE$1].device.requestSession(mode); + const session = new XRSession(this[PRIVATE$1].device, mode, sessionId); + if (mode == 'inline') { + this[PRIVATE$1].inlineSessions.add(session); + } else { + this[PRIVATE$1].immersiveSession = session; + } + const onSessionEnd = () => { + if (mode == 'inline') { + this[PRIVATE$1].inlineSessions.delete(session); + } else { + this[PRIVATE$1].immersiveSession = null; + } + session.removeEventListener('end', onSessionEnd); + }; + session.addEventListener('end', onSessionEnd); + return session; + } +} + +let now; +if ('performance' in _global === false) { + let startTime = Date.now(); + now = () => Date.now() - startTime; +} else { + now = () => performance.now(); +} +var now$1 = now; + +const PRIVATE$2 = Symbol('@@webxr-polyfill/XRPose'); +class XRPose$1 { + constructor(transform, emulatedPosition) { + this[PRIVATE$2] = { + transform, + emulatedPosition, + }; + } + get transform() { return this[PRIVATE$2].transform; } + get emulatedPosition() { return this[PRIVATE$2].emulatedPosition; } + _setTransform(transform) { this[PRIVATE$2].transform = transform; } +} + +const EPSILON = 0.000001; +let ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; + + +const degree = Math.PI / 180; + +function create() { + let out = new ARRAY_TYPE(16); + if(ARRAY_TYPE != Float32Array) { + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + } + out[0] = 1; + out[5] = 1; + out[10] = 1; + out[15] = 1; + return out; +} + +function copy(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +} + + +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +} + +function invert(out, a) { + let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; + let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; + let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; + let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + let b00 = a00 * a11 - a01 * a10; + let b01 = a00 * a12 - a02 * a10; + let b02 = a00 * a13 - a03 * a10; + let b03 = a01 * a12 - a02 * a11; + let b04 = a01 * a13 - a03 * a11; + let b05 = a02 * a13 - a03 * a12; + let b06 = a20 * a31 - a21 * a30; + let b07 = a20 * a32 - a22 * a30; + let b08 = a20 * a33 - a23 * a30; + let b09 = a21 * a32 - a22 * a31; + let b10 = a21 * a33 - a23 * a31; + let b11 = a22 * a33 - a23 * a32; + let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + if (!det) { + return null; + } + det = 1.0 / det; + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + return out; +} + + +function multiply(out, a, b) { + let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; + let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; + let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; + let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +} + + + + + + + + + + + + +function fromRotationTranslation(out, q, v) { + let x = q[0], y = q[1], z = q[2], w = q[3]; + let x2 = x + x; + let y2 = y + y; + let z2 = z + z; + let xx = x * x2; + let xy = x * y2; + let xz = x * z2; + let yy = y * y2; + let yz = y * z2; + let zz = z * z2; + let wx = w * x2; + let wy = w * y2; + let wz = w * z2; + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + return out; +} + +function getTranslation(out, mat) { + out[0] = mat[12]; + out[1] = mat[13]; + out[2] = mat[14]; + return out; +} + +function getRotation(out, mat) { + let trace = mat[0] + mat[5] + mat[10]; + let S = 0; + if (trace > 0) { + S = Math.sqrt(trace + 1.0) * 2; + out[3] = 0.25 * S; + out[0] = (mat[6] - mat[9]) / S; + out[1] = (mat[8] - mat[2]) / S; + out[2] = (mat[1] - mat[4]) / S; + } else if ((mat[0] > mat[5]) && (mat[0] > mat[10])) { + S = Math.sqrt(1.0 + mat[0] - mat[5] - mat[10]) * 2; + out[3] = (mat[6] - mat[9]) / S; + out[0] = 0.25 * S; + out[1] = (mat[1] + mat[4]) / S; + out[2] = (mat[8] + mat[2]) / S; + } else if (mat[5] > mat[10]) { + S = Math.sqrt(1.0 + mat[5] - mat[0] - mat[10]) * 2; + out[3] = (mat[8] - mat[2]) / S; + out[0] = (mat[1] + mat[4]) / S; + out[1] = 0.25 * S; + out[2] = (mat[6] + mat[9]) / S; + } else { + S = Math.sqrt(1.0 + mat[10] - mat[0] - mat[5]) * 2; + out[3] = (mat[1] - mat[4]) / S; + out[0] = (mat[8] + mat[2]) / S; + out[1] = (mat[6] + mat[9]) / S; + out[2] = 0.25 * S; + } + return out; +} + + + + +function perspective(out, fovy, aspect, near, far) { + let f = 1.0 / Math.tan(fovy / 2), nf; + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[15] = 0; + if (far != null && far !== Infinity) { + nf = 1 / (near - far); + out[10] = (far + near) * nf; + out[14] = (2 * far * near) * nf; + } else { + out[10] = -1; + out[14] = -2 * near; + } + return out; +} + +function create$1() { + let out = new ARRAY_TYPE(3); + if(ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + return out; +} +function clone$1(a) { + var out = new ARRAY_TYPE(3); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +} +function length(a) { + let x = a[0]; + let y = a[1]; + let z = a[2]; + return Math.sqrt(x*x + y*y + z*z); +} +function fromValues$1(x, y, z) { + let out = new ARRAY_TYPE(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; +} +function copy$1(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +} + +function add$1(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; +} + + + + + + + + +function scale$1(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return out; +} + + + + + + +function normalize(out, a) { + let x = a[0]; + let y = a[1]; + let z = a[2]; + let len = x*x + y*y + z*z; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; +} +function dot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} +function cross(out, a, b) { + let ax = a[0], ay = a[1], az = a[2]; + let bx = b[0], by = b[1], bz = b[2]; + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; +} + + + + + + +function transformQuat(out, a, q) { + let qx = q[0], qy = q[1], qz = q[2], qw = q[3]; + let x = a[0], y = a[1], z = a[2]; + let uvx = qy * z - qz * y, + uvy = qz * x - qx * z, + uvz = qx * y - qy * x; + let uuvx = qy * uvz - qz * uvy, + uuvy = qz * uvx - qx * uvz, + uuvz = qx * uvy - qy * uvx; + let w2 = qw * 2; + uvx *= w2; + uvy *= w2; + uvz *= w2; + uuvx *= 2; + uuvy *= 2; + uuvz *= 2; + out[0] = x + uvx + uuvx; + out[1] = y + uvy + uuvy; + out[2] = z + uvz + uuvz; + return out; +} + + + +function angle(a, b) { + let tempA = fromValues$1(a[0], a[1], a[2]); + let tempB = fromValues$1(b[0], b[1], b[2]); + normalize(tempA, tempA); + normalize(tempB, tempB); + let cosine = dot(tempA, tempB); + if(cosine > 1.0) { + return 0; + } + else if(cosine < -1.0) { + return Math.PI; + } else { + return Math.acos(cosine); + } +} + + + + + + + + +const len = length; + +const forEach = (function() { + let vec = create$1(); + return function(a, stride, offset, count, fn, arg) { + let i, l; + if(!stride) { + stride = 3; + } + if(!offset) { + offset = 0; + } + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; + } + return a; + }; +})(); + +function create$2() { + let out = new ARRAY_TYPE(9); + if(ARRAY_TYPE != Float32Array) { + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + } + out[0] = 1; + out[4] = 1; + out[8] = 1; + return out; +} + +function create$3() { + let out = new ARRAY_TYPE(4); + if(ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + } + return out; +} +function clone$3(a) { + let out = new ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +} +function fromValues$3(x, y, z, w) { + let out = new ARRAY_TYPE(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +} +function copy$3(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +} + + + + + + + + + + + + + + + + + + +function normalize$1(out, a) { + let x = a[0]; + let y = a[1]; + let z = a[2]; + let w = a[3]; + let len = x*x + y*y + z*z + w*w; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = x * len; + out[1] = y * len; + out[2] = z * len; + out[3] = w * len; + } + return out; +} + + + + + + + + + + + + + + + +const forEach$1 = (function() { + let vec = create$3(); + return function(a, stride, offset, count, fn, arg) { + let i, l; + if(!stride) { + stride = 4; + } + if(!offset) { + offset = 0; + } + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3]; + } + return a; + }; +})(); + +function create$4() { + let out = new ARRAY_TYPE(4); + if(ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + out[3] = 1; + return out; +} + +function setAxisAngle(out, axis, rad) { + rad = rad * 0.5; + let s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; +} + +function multiply$4(out, a, b) { + let ax = a[0], ay = a[1], az = a[2], aw = a[3]; + let bx = b[0], by = b[1], bz = b[2], bw = b[3]; + out[0] = ax * bw + aw * bx + ay * bz - az * by; + out[1] = ay * bw + aw * by + az * bx - ax * bz; + out[2] = az * bw + aw * bz + ax * by - ay * bx; + out[3] = aw * bw - ax * bx - ay * by - az * bz; + return out; +} + + + + +function slerp(out, a, b, t) { + let ax = a[0], ay = a[1], az = a[2], aw = a[3]; + let bx = b[0], by = b[1], bz = b[2], bw = b[3]; + let omega, cosom, sinom, scale0, scale1; + cosom = ax * bx + ay * by + az * bz + aw * bw; + if ( cosom < 0.0 ) { + cosom = -cosom; + bx = - bx; + by = - by; + bz = - bz; + bw = - bw; + } + if ( (1.0 - cosom) > EPSILON ) { + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + scale0 = 1.0 - t; + scale1 = t; + } + out[0] = scale0 * ax + scale1 * bx; + out[1] = scale0 * ay + scale1 * by; + out[2] = scale0 * az + scale1 * bz; + out[3] = scale0 * aw + scale1 * bw; + return out; +} + +function invert$2(out, a) { + let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + let dot$$1 = a0*a0 + a1*a1 + a2*a2 + a3*a3; + let invDot = dot$$1 ? 1.0/dot$$1 : 0; + out[0] = -a0*invDot; + out[1] = -a1*invDot; + out[2] = -a2*invDot; + out[3] = a3*invDot; + return out; +} + +function fromMat3(out, m) { + let fTrace = m[0] + m[4] + m[8]; + let fRoot; + if ( fTrace > 0.0 ) { + fRoot = Math.sqrt(fTrace + 1.0); + out[3] = 0.5 * fRoot; + fRoot = 0.5/fRoot; + out[0] = (m[5]-m[7])*fRoot; + out[1] = (m[6]-m[2])*fRoot; + out[2] = (m[1]-m[3])*fRoot; + } else { + let i = 0; + if ( m[4] > m[0] ) + i = 1; + if ( m[8] > m[i*3+i] ) + i = 2; + let j = (i+1)%3; + let k = (i+2)%3; + fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0); + out[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[3] = (m[j*3+k] - m[k*3+j]) * fRoot; + out[j] = (m[j*3+i] + m[i*3+j]) * fRoot; + out[k] = (m[k*3+i] + m[i*3+k]) * fRoot; + } + return out; +} +function fromEuler(out, x, y, z) { + let halfToRad = 0.5 * Math.PI / 180.0; + x *= halfToRad; + y *= halfToRad; + z *= halfToRad; + let sx = Math.sin(x); + let cx = Math.cos(x); + let sy = Math.sin(y); + let cy = Math.cos(y); + let sz = Math.sin(z); + let cz = Math.cos(z); + out[0] = sx * cy * cz - cx * sy * sz; + out[1] = cx * sy * cz + sx * cy * sz; + out[2] = cx * cy * sz - sx * sy * cz; + out[3] = cx * cy * cz + sx * sy * sz; + return out; +} + +const clone$4 = clone$3; +const fromValues$4 = fromValues$3; +const copy$4 = copy$3; + + + + + + + + + + +const normalize$2 = normalize$1; + + +const rotationTo = (function() { + let tmpvec3 = create$1(); + let xUnitVec3 = fromValues$1(1,0,0); + let yUnitVec3 = fromValues$1(0,1,0); + return function(out, a, b) { + let dot$$1 = dot(a, b); + if (dot$$1 < -0.999999) { + cross(tmpvec3, xUnitVec3, a); + if (len(tmpvec3) < 0.000001) + cross(tmpvec3, yUnitVec3, a); + normalize(tmpvec3, tmpvec3); + setAxisAngle(out, tmpvec3, Math.PI); + return out; + } else if (dot$$1 > 0.999999) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } else { + cross(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot$$1; + return normalize$2(out, out); + } + }; +})(); +const sqlerp = (function () { + let temp1 = create$4(); + let temp2 = create$4(); + return function (out, a, b, c, d, t) { + slerp(temp1, a, d, t); + slerp(temp2, b, c, t); + slerp(out, temp1, temp2, 2 * t * (1 - t)); + return out; + }; +}()); +const setAxes = (function() { + let matr = create$2(); + return function(out, view, right, up) { + matr[0] = right[0]; + matr[3] = right[1]; + matr[6] = right[2]; + matr[1] = up[0]; + matr[4] = up[1]; + matr[7] = up[2]; + matr[2] = -view[0]; + matr[5] = -view[1]; + matr[8] = -view[2]; + return normalize$2(out, fromMat3(out, matr)); + }; +})(); + +const PRIVATE$3 = Symbol('@@webxr-polyfill/XRRigidTransform'); +class XRRigidTransform$1 { + constructor() { + this[PRIVATE$3] = { + matrix: null, + position: null, + orientation: null, + inverse: null, + }; + if (arguments.length === 0) { + this[PRIVATE$3].matrix = identity(new Float32Array(16)); + } else if (arguments.length === 1) { + if (arguments[0] instanceof Float32Array) { + this[PRIVATE$3].matrix = arguments[0]; + } else { + this[PRIVATE$3].position = this._getPoint(arguments[0]); + this[PRIVATE$3].orientation = DOMPointReadOnly.fromPoint({ + x: 0, y: 0, z: 0, w: 1 + }); + } + } else if (arguments.length === 2) { + this[PRIVATE$3].position = this._getPoint(arguments[0]); + this[PRIVATE$3].orientation = this._getPoint(arguments[1]); + } else { + throw new Error("Too many arguments!"); + } + if (this[PRIVATE$3].matrix) { + let position = create$1(); + getTranslation(position, this[PRIVATE$3].matrix); + this[PRIVATE$3].position = DOMPointReadOnly.fromPoint({ + x: position[0], + y: position[1], + z: position[2] + }); + let orientation = create$4(); + getRotation(orientation, this[PRIVATE$3].matrix); + this[PRIVATE$3].orientation = DOMPointReadOnly.fromPoint({ + x: orientation[0], + y: orientation[1], + z: orientation[2], + w: orientation[3] + }); + } else { + this[PRIVATE$3].matrix = identity(new Float32Array(16)); + fromRotationTranslation( + this[PRIVATE$3].matrix, + fromValues$4( + this[PRIVATE$3].orientation.x, + this[PRIVATE$3].orientation.y, + this[PRIVATE$3].orientation.z, + this[PRIVATE$3].orientation.w), + fromValues$1( + this[PRIVATE$3].position.x, + this[PRIVATE$3].position.y, + this[PRIVATE$3].position.z) + ); + } + } + _getPoint(arg) { + if (arg instanceof DOMPointReadOnly) { + return arg; + } + return DOMPointReadOnly.fromPoint(arg); + } + get matrix() { return this[PRIVATE$3].matrix; } + get position() { return this[PRIVATE$3].position; } + get orientation() { return this[PRIVATE$3].orientation; } + get inverse() { + if (this[PRIVATE$3].inverse === null) { + let invMatrix = identity(new Float32Array(16)); + invert(invMatrix, this[PRIVATE$3].matrix); + this[PRIVATE$3].inverse = new XRRigidTransform$1(invMatrix); + this[PRIVATE$3].inverse[PRIVATE$3].inverse = this; + } + return this[PRIVATE$3].inverse; + } +} + +const PRIVATE$4 = Symbol('@@webxr-polyfill/XRViewerPose'); +class XRViewerPose extends XRPose$1 { + constructor(device, views) { + super(new XRRigidTransform$1(), false); + this[PRIVATE$4] = { + device, + views, + leftViewMatrix: identity(new Float32Array(16)), + rightViewMatrix: identity(new Float32Array(16)), + poseModelMatrix: identity(new Float32Array(16)), + }; + } + get poseModelMatrix() { return this[PRIVATE$4].poseModelMatrix; } + get views() { + return this[PRIVATE$4].views; + } + _updateFromReferenceSpace(refSpace) { + const pose = this[PRIVATE$4].device.getBasePoseMatrix(); + const leftViewMatrix = this[PRIVATE$4].device.getBaseViewMatrix('left'); + const rightViewMatrix = this[PRIVATE$4].device.getBaseViewMatrix('right'); + if (pose) { + refSpace._transformBasePoseMatrix(this[PRIVATE$4].poseModelMatrix, pose); + refSpace._adjustForOriginOffset(this[PRIVATE$4].poseModelMatrix); + super._setTransform(new XRRigidTransform$1(this[PRIVATE$4].poseModelMatrix)); + } + if (leftViewMatrix) { + refSpace._transformBaseViewMatrix( + this[PRIVATE$4].leftViewMatrix, + leftViewMatrix, + this[PRIVATE$4].poseModelMatrix); + multiply( + this[PRIVATE$4].leftViewMatrix, + this[PRIVATE$4].leftViewMatrix, + refSpace._originOffsetMatrix()); + } + if (rightViewMatrix) { + refSpace._transformBaseViewMatrix( + this[PRIVATE$4].rightViewMatrix, + rightViewMatrix, + this[PRIVATE$4].poseModelMatrix); + multiply( + this[PRIVATE$4].rightViewMatrix, + this[PRIVATE$4].rightViewMatrix, + refSpace._originOffsetMatrix()); + } + for (let view of this[PRIVATE$4].views) { + if (view.eye == "left") { + view._updateViewMatrix(this[PRIVATE$4].leftViewMatrix); + } else if (view.eye == "right") { + view._updateViewMatrix(this[PRIVATE$4].rightViewMatrix); + } + } + } +} + +const PRIVATE$5 = Symbol('@@webxr-polyfill/XRViewport'); +class XRViewport { + constructor(target) { + this[PRIVATE$5] = { target }; + } + get x() { return this[PRIVATE$5].target.x; } + get y() { return this[PRIVATE$5].target.y; } + get width() { return this[PRIVATE$5].target.width; } + get height() { return this[PRIVATE$5].target.height; } +} + +const XREyes = ['left', 'right']; +const PRIVATE$6 = Symbol('@@webxr-polyfill/XRView'); +class XRView { + constructor(device, eye, sessionId) { + if (!XREyes.includes(eye)) { + throw new Error(`XREye must be one of: ${XREyes}`); + } + const temp = Object.create(null); + const viewport = new XRViewport(temp); + this[PRIVATE$6] = { + device, + eye, + viewport, + temp, + sessionId, + transform: null, + }; + } + get eye() { return this[PRIVATE$6].eye; } + get projectionMatrix() { return this[PRIVATE$6].device.getProjectionMatrix(this.eye); } + get transform() { return this[PRIVATE$6].transform; } + _updateViewMatrix(viewMatrix) { + let invMatrix = identity(new Float32Array(16)); + invert(invMatrix, viewMatrix); + this[PRIVATE$6].transform = new XRRigidTransform$1(invMatrix); + } + _getViewport(layer) { + if (this[PRIVATE$6].device.getViewport(this[PRIVATE$6].sessionId, + this.eye, + layer, + this[PRIVATE$6].temp)) { + return this[PRIVATE$6].viewport; + } + return undefined; + } +} + +var EPSILON$1 = 0.000001; +var ARRAY_TYPE$1 = typeof Float32Array !== 'undefined' ? Float32Array : Array; + + +var degree$1 = Math.PI / 180; + +function create$7() { + var out = new ARRAY_TYPE$1(9); + if (ARRAY_TYPE$1 != Float32Array) { + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + } + out[0] = 1; + out[4] = 1; + out[8] = 1; + return out; +} + +function create$9() { + var out = new ARRAY_TYPE$1(3); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + return out; +} + +function length$3(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + return Math.sqrt(x * x + y * y + z * z); +} +function fromValues$9(x, y, z) { + var out = new ARRAY_TYPE$1(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; +} + + + + + + + + + + + + + + + + + + +function normalize$3(out, a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var len = x * x + y * y + z * z; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; +} +function dot$3(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} +function cross$1(out, a, b) { + var ax = a[0], + ay = a[1], + az = a[2]; + var bx = b[0], + by = b[1], + bz = b[2]; + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; +} + + + + + + + + + + + + + + + + + + + +var len$3 = length$3; + +var forEach$2 = function () { + var vec = create$9(); + return function (a, stride, offset, count, fn, arg) { + var i = void 0, + l = void 0; + if (!stride) { + stride = 3; + } + if (!offset) { + offset = 0; + } + if (count) { + l = Math.min(count * stride + offset, a.length); + } else { + l = a.length; + } + for (i = offset; i < l; i += stride) { + vec[0] = a[i];vec[1] = a[i + 1];vec[2] = a[i + 2]; + fn(vec, vec, arg); + a[i] = vec[0];a[i + 1] = vec[1];a[i + 2] = vec[2]; + } + return a; + }; +}(); + +function create$10() { + var out = new ARRAY_TYPE$1(4); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + } + return out; +} + + + + + + + + + + + + + + + + + + + + + +function normalize$4(out, a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var w = a[3]; + var len = x * x + y * y + z * z + w * w; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = x * len; + out[1] = y * len; + out[2] = z * len; + out[3] = w * len; + } + return out; +} + + + + + + + + + + + + + + + +var forEach$3 = function () { + var vec = create$10(); + return function (a, stride, offset, count, fn, arg) { + var i = void 0, + l = void 0; + if (!stride) { + stride = 4; + } + if (!offset) { + offset = 0; + } + if (count) { + l = Math.min(count * stride + offset, a.length); + } else { + l = a.length; + } + for (i = offset; i < l; i += stride) { + vec[0] = a[i];vec[1] = a[i + 1];vec[2] = a[i + 2];vec[3] = a[i + 3]; + fn(vec, vec, arg); + a[i] = vec[0];a[i + 1] = vec[1];a[i + 2] = vec[2];a[i + 3] = vec[3]; + } + return a; + }; +}(); + +function create$11() { + var out = new ARRAY_TYPE$1(4); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + out[3] = 1; + return out; +} + +function setAxisAngle$1(out, axis, rad) { + rad = rad * 0.5; + var s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; +} + + + + + + +function slerp$1(out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + var bx = b[0], + by = b[1], + bz = b[2], + bw = b[3]; + var omega = void 0, + cosom = void 0, + sinom = void 0, + scale0 = void 0, + scale1 = void 0; + cosom = ax * bx + ay * by + az * bz + aw * bw; + if (cosom < 0.0) { + cosom = -cosom; + bx = -bx; + by = -by; + bz = -bz; + bw = -bw; + } + if (1.0 - cosom > EPSILON$1) { + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + scale0 = 1.0 - t; + scale1 = t; + } + out[0] = scale0 * ax + scale1 * bx; + out[1] = scale0 * ay + scale1 * by; + out[2] = scale0 * az + scale1 * bz; + out[3] = scale0 * aw + scale1 * bw; + return out; +} + + + +function fromMat3$1(out, m) { + var fTrace = m[0] + m[4] + m[8]; + var fRoot = void 0; + if (fTrace > 0.0) { + fRoot = Math.sqrt(fTrace + 1.0); + out[3] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[0] = (m[5] - m[7]) * fRoot; + out[1] = (m[6] - m[2]) * fRoot; + out[2] = (m[1] - m[3]) * fRoot; + } else { + var i = 0; + if (m[4] > m[0]) i = 1; + if (m[8] > m[i * 3 + i]) i = 2; + var j = (i + 1) % 3; + var k = (i + 2) % 3; + fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1.0); + out[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot; + out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot; + out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot; + } + return out; +} + + + + + + + + + + + + + + + +var normalize$5 = normalize$4; + + +var rotationTo$1 = function () { + var tmpvec3 = create$9(); + var xUnitVec3 = fromValues$9(1, 0, 0); + var yUnitVec3 = fromValues$9(0, 1, 0); + return function (out, a, b) { + var dot = dot$3(a, b); + if (dot < -0.999999) { + cross$1(tmpvec3, xUnitVec3, a); + if (len$3(tmpvec3) < 0.000001) cross$1(tmpvec3, yUnitVec3, a); + normalize$3(tmpvec3, tmpvec3); + setAxisAngle$1(out, tmpvec3, Math.PI); + return out; + } else if (dot > 0.999999) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } else { + cross$1(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot; + return normalize$5(out, out); + } + }; +}(); +var sqlerp$1 = function () { + var temp1 = create$11(); + var temp2 = create$11(); + return function (out, a, b, c, d, t) { + slerp$1(temp1, a, d, t); + slerp$1(temp2, b, c, t); + slerp$1(out, temp1, temp2, 2 * t * (1 - t)); + return out; + }; +}(); +var setAxes$1 = function () { + var matr = create$7(); + return function (out, view, right, up) { + matr[0] = right[0]; + matr[3] = right[1]; + matr[6] = right[2]; + matr[1] = up[0]; + matr[4] = up[1]; + matr[7] = up[2]; + matr[2] = -view[0]; + matr[5] = -view[1]; + matr[8] = -view[2]; + return normalize$5(out, fromMat3$1(out, matr)); + }; +}(); + +function create$13() { + var out = new ARRAY_TYPE$1(2); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + } + return out; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +var forEach$4 = function () { + var vec = create$13(); + return function (a, stride, offset, count, fn, arg) { + var i = void 0, + l = void 0; + if (!stride) { + stride = 2; + } + if (!offset) { + offset = 0; + } + if (count) { + l = Math.min(count * stride + offset, a.length); + } else { + l = a.length; + } + for (i = offset; i < l; i += stride) { + vec[0] = a[i];vec[1] = a[i + 1]; + fn(vec, vec, arg); + a[i] = vec[0];a[i + 1] = vec[1]; + } + return a; + }; +}(); + +const PRIVATE$7 = Symbol('@@webxr-polyfill/XRFrame'); +class XRFrame { + constructor(device, session, sessionId) { + const views = [ + new XRView(device, 'left', sessionId), + ]; + if (session.immersive) { + views.push(new XRView(device, 'right', sessionId)); + } + this[PRIVATE$7] = { + device, + viewerPose: new XRViewerPose(device, views), + views, + session, + }; + } + get session() { return this[PRIVATE$7].session; } + getViewerPose(space) { + this[PRIVATE$7].viewerPose._updateFromReferenceSpace(space); + return this[PRIVATE$7].viewerPose; + } + getPose(space, baseSpace) { + if (space._specialType === "viewer") { + let viewerPose = this.getViewerPose(baseSpace); + return new XRPose( + new XRRigidTransform(viewerPose.poseModelMatrix), + viewerPose.emulatedPosition); + } + if (space._specialType === "target-ray" || space._specialType === "grip") { + return this[PRIVATE$7].device.getInputPose( + space._inputSource, baseSpace, space._specialType); + } + return null; + } +} + +const PRIVATE$8 = Symbol('@@webxr-polyfill/XRSpace'); + +class XRSpace { + constructor(specialType = null, inputSource = null) { + this[PRIVATE$8] = { + specialType, + inputSource, + }; + } + get _specialType() { + return this[PRIVATE$8].specialType; + } + get _inputSource() { + return this[PRIVATE$8].inputSource; + } +} + +const DEFAULT_EMULATION_HEIGHT = 1.6; +const PRIVATE$9 = Symbol('@@webxr-polyfill/XRReferenceSpace'); +const XRReferenceSpaceTypes = [ + 'viewer', + 'local', + 'local-floor', + 'bounded-floor', + 'unbounded' +]; +function isFloor(type) { + return type === 'bounded-floor' || type === 'local-floor'; +} +class XRReferenceSpace extends XRSpace { + constructor(device, type, transform) { + if (!XRReferenceSpaceTypes.includes(type)) { + throw new Error(`XRReferenceSpaceType must be one of ${XRReferenceSpaceTypes}`); + } + super((type === 'viewer') ? 'viewer' : null); + if (type === 'bounded-floor' && !transform) { + throw new Error(`XRReferenceSpace cannot use 'bounded-floor' type if the platform does not provide the floor level`); + } + if (isFloor(type) && !transform) { + transform = identity(new Float32Array(16)); + transform[13] = DEFAULT_EMULATION_HEIGHT; + } + if (!transform) { + transform = identity(new Float32Array(16)); + } + this[PRIVATE$9] = { + type, + transform, + device, + originOffset : identity(new Float32Array(16)), + }; + } + _transformBasePoseMatrix(out, pose) { + multiply(out, this[PRIVATE$9].transform, pose); + } + _transformBaseViewMatrix(out, view) { + invert(out, this[PRIVATE$9].transform); + multiply(out, view, out); + } + _originOffsetMatrix() { + return this[PRIVATE$9].originOffset; + } + _adjustForOriginOffset(transformMatrix) { + let inverseOriginOffsetMatrix = identity(new Float32Array(16)); + invert(inverseOriginOffsetMatrix, this[PRIVATE$9].originOffset); + multiply(transformMatrix, inverseOriginOffsetMatrix, transformMatrix); + } + getOffsetReferenceSpace(additionalOffset) { + let newSpace = new XRReferenceSpace( + this[PRIVATE$9].device, + this[PRIVATE$9].type, + this[PRIVATE$9].transform, + this[PRIVATE$9].bounds); + multiply(newSpace[PRIVATE$9].originOffset, this[PRIVATE$9].originOffset, additionalOffset.matrix); + return newSpace; + } +} + +const POLYFILLED_XR_COMPATIBLE = Symbol('@@webxr-polyfill/polyfilled-xr-compatible'); +const XR_COMPATIBLE = Symbol('@@webxr-polyfill/xr-compatible'); + +const PRIVATE$10 = Symbol('@@webxr-polyfill/XRWebGLLayer'); +const XRWebGLLayerInit = Object.freeze({ + antialias: true, + depth: false, + stencil: false, + alpha: true, + multiview: false, + ignoreDepthValues: false, + framebufferScaleFactor: 1.0, +}); +class XRWebGLLayer { + constructor(session, context, layerInit={}) { + const config = Object.assign({}, XRWebGLLayerInit, layerInit); + if (!(session instanceof XRSession$1)) { + throw new Error('session must be a XRSession'); + } + if (session.ended) { + throw new Error(`InvalidStateError`); + } + if (context[POLYFILLED_XR_COMPATIBLE]) { + if (context[XR_COMPATIBLE] !== true) { + throw new Error(`InvalidStateError`); + } + } + const framebuffer = context.getParameter(context.FRAMEBUFFER_BINDING); + this[PRIVATE$10] = { + context, + config, + framebuffer, + session, + }; + } + get context() { return this[PRIVATE$10].context; } + get antialias() { return this[PRIVATE$10].config.antialias; } + get ignoreDepthValues() { return true; } + get framebuffer() { return this[PRIVATE$10].framebuffer; } + get framebufferWidth() { return this[PRIVATE$10].context.drawingBufferWidth; } + get framebufferHeight() { return this[PRIVATE$10].context.drawingBufferHeight; } + get _session() { return this[PRIVATE$10].session; } + getViewport(view) { + return view._getViewport(this); + } +} + +const PRIVATE$11 = Symbol('@@webxr-polyfill/XRSession'); +class XRSession$1 extends EventTarget { + constructor(device, mode, id) { + super(); + let immersive = mode != 'inline'; + let outputContext = null; + this[PRIVATE$11] = { + device, + mode, + immersive, + outputContext, + ended: false, + suspended: false, + suspendedCallback: null, + id, + activeRenderState: null, + pendingRenderState: null, + }; + const frame = new XRFrame(device, this, this[PRIVATE$11].id); + this[PRIVATE$11].frame = frame; + this[PRIVATE$11].onPresentationEnd = sessionId => { + if (sessionId !== this[PRIVATE$11].id) { + this[PRIVATE$11].suspended = false; + this.dispatchEvent('focus', { session: this }); + const suspendedCallback = this[PRIVATE$11].suspendedCallback; + this[PRIVATE$11].suspendedCallback = null; + if (suspendedCallback) { + this.requestAnimationFrame(suspendedCallback); + } + return; + } + this[PRIVATE$11].ended = true; + device.removeEventListener('@webvr-polyfill/vr-present-end', this[PRIVATE$11].onPresentationEnd); + device.removeEventListener('@webvr-polyfill/vr-present-start', this[PRIVATE$11].onPresentationStart); + device.removeEventListener('@@webvr-polyfill/input-select-start', this[PRIVATE$11].onSelectStart); + device.removeEventListener('@@webvr-polyfill/input-select-end', this[PRIVATE$11].onSelectEnd); + this.dispatchEvent('end', { session: this }); + }; + device.addEventListener('@@webxr-polyfill/vr-present-end', this[PRIVATE$11].onPresentationEnd); + this[PRIVATE$11].onPresentationStart = sessionId => { + if (sessionId === this[PRIVATE$11].id) { + return; + } + this[PRIVATE$11].suspended = true; + this.dispatchEvent('blur', { session: this }); + }; + device.addEventListener('@@webxr-polyfill/vr-present-start', this[PRIVATE$11].onPresentationStart); + this[PRIVATE$11].onSelectStart = evt => { + if (evt.sessionId !== this[PRIVATE$11].id) { + return; + } + this.dispatchEvent('selectstart', { + frame: this[PRIVATE$11].frame, + inputSource: evt.inputSource + }); + }; + device.addEventListener('@@webxr-polyfill/input-select-start', this[PRIVATE$11].onSelectStart); + this[PRIVATE$11].onSelectEnd = evt => { + if (evt.sessionId !== this[PRIVATE$11].id) { + return; + } + this.dispatchEvent('selectend', { + frame: this[PRIVATE$11].frame, + inputSource: evt.inputSource + }); + this.dispatchEvent('select', { + frame: this[PRIVATE$11].frame, + inputSource: evt.inputSource + }); + }; + device.addEventListener('@@webxr-polyfill/input-select-end', this[PRIVATE$11].onSelectEnd); + this.onblur = undefined; + this.onfocus = undefined; + this.onresetpose = undefined; + this.onend = undefined; + this.onselect = undefined; + this.onselectstart = undefined; + this.onselectend = undefined; + } + get renderState() { return this[PRIVATE$11].activeRenderState; } + get immersive() { return this[PRIVATE$11].immersive; } + get outputContext() { return this[PRIVATE$11].outputContext; } + get depthNear() { return this[PRIVATE$11].device.depthNear; } + set depthNear(value) { this[PRIVATE$11].device.depthNear = value; } + get depthFar() { return this[PRIVATE$11].device.depthFar; } + set depthFar(value) { this[PRIVATE$11].device.depthFar = value; } + get environmentBlendMode() { + return this[PRIVATE$11].device.environmentBlendMode || 'opaque'; + } + get baseLayer() { return this[PRIVATE$11].baseLayer; } + set baseLayer(value) { + if (this[PRIVATE$11].ended) { + return; + } + this[PRIVATE$11].baseLayer = value; + this[PRIVATE$11].device.onBaseLayerSet(this[PRIVATE$11].id, value); + } + async requestReferenceSpace(type) { + if (this[PRIVATE$11].ended) { + return; + } + if (type === 'unbounded') { + throw new NotSupportedError(`The WebXR polyfill does not support the ${type} reference space`); + } + if (!XRReferenceSpaceTypes.includes(type)) { + throw new TypeError(`XRReferenceSpaceType must be one of ${XRReferenceSpaceTypes}`); + } + let transform = await this[PRIVATE$11].device.requestFrameOfReferenceTransform(type); + if (type === 'bounded-floor') { + if (!transform) { + throw new NotSupportedError(`${type} XRReferenceSpace not supported by this device.`); + } + let bounds = this[PRIVATE$11].device.requestStageBounds(); + if (!bounds) { + throw new NotSupportedError(`${type} XRReferenceSpace not supported by this device.`); + } + throw new NotSupportedError(`The WebXR polyfill does not support the ${type} reference space yet.`); + } + return new XRReferenceSpace(this[PRIVATE$11].device, type, transform); + } + requestAnimationFrame(callback) { + if (this[PRIVATE$11].ended) { + return; + } + if (this[PRIVATE$11].suspended && this[PRIVATE$11].suspendedCallback) { + return; + } + if (this[PRIVATE$11].suspended && !this[PRIVATE$11].suspendedCallback) { + this[PRIVATE$11].suspendedCallback = callback; + } + return this[PRIVATE$11].device.requestAnimationFrame(() => { + if (this[PRIVATE$11].pendingRenderState !== null) { + this[PRIVATE$11].activeRenderState = this[PRIVATE$11].pendingRenderState; + this[PRIVATE$11].pendingRenderState = null; + if (this[PRIVATE$11].activeRenderState.baseLayer) { + this[PRIVATE$11].device.onBaseLayerSet( + this[PRIVATE$11].id, + this[PRIVATE$11].activeRenderState.baseLayer); + } + if (this[PRIVATE$11].activeRenderState.inlineVerticalFieldOfView) { + this[PRIVATE$11].device.onInlineVerticalFieldOfViewSet( + this[PRIVATE$11].id, + this[PRIVATE$11].activeRenderState.inlineVerticalFieldOfView); + } + } + this[PRIVATE$11].device.onFrameStart(this[PRIVATE$11].id); + callback(now$1(), this[PRIVATE$11].frame); + this[PRIVATE$11].device.onFrameEnd(this[PRIVATE$11].id); + }); + } + cancelAnimationFrame(handle) { + if (this[PRIVATE$11].ended) { + return; + } + this[PRIVATE$11].device.cancelAnimationFrame(handle); + } + get inputSources() { + return this[PRIVATE$11].device.getInputSources(); + } + async end() { + if (this[PRIVATE$11].ended) { + return; + } + if (!this.immersive) { + this[PRIVATE$11].ended = true; + this[PRIVATE$11].device.removeEventListener('@@webvr-polyfill/vr-present-start', + this[PRIVATE$11].onPresentationStart); + this[PRIVATE$11].device.removeEventListener('@@webvr-polyfill/vr-present-end', + this[PRIVATE$11].onPresentationEnd); + this[PRIVATE$11].device.removeEventListener('@@webvr-polyfill/input-select-start', + this[PRIVATE$11].onSelectStart); + this[PRIVATE$11].device.removeEventListener('@@webvr-polyfill/input-select-end', + this[PRIVATE$11].onSelectEnd); + this.dispatchEvent('end', { session: this }); + } + return this[PRIVATE$11].device.endSession(this[PRIVATE$11].id); + } + updateRenderState(newState) { + if (this[PRIVATE$11].ended) { + const message = "Can't call updateRenderState on an XRSession " + + "that has already ended."; + throw new Error(message); + } + if (newState.baseLayer && (newState.baseLayer._session !== this)) { + const message = "Called updateRenderState with a base layer that was " + + "created by a different session."; + throw new Error(message); + } + const fovSet = (newState.inlineVerticalFieldOfView !== null) && + (newState.inlineVerticalFieldOfView !== undefined); + if (fovSet) { + if (this[PRIVATE$11].immersive) { + const message = "inlineVerticalFieldOfView must not be set for an " + + "XRRenderState passed to updateRenderState for an " + + "immersive session."; + throw new Error(message); + } else { + newState.inlineVerticalFieldOfView = Math.min( + 3.13, Math.max(0.01, newState.inlineVerticalFieldOfView)); + } + } + if (this[PRIVATE$11].pendingRenderState === null) { + this[PRIVATE$11].pendingRenderState = Object.assign( + {}, this[PRIVATE$11].activeRenderState, newState); + } + } +} + +const PRIVATE$12 = Symbol('@@webxr-polyfill/XRInputSource'); +class XRInputSource { + constructor(impl) { + this[PRIVATE$12] = { + impl, + gripSpace: new XRSpace("grip", this), + targetRaySpace: new XRSpace("target-ray", this) + }; + } + get handedness() { return this[PRIVATE$12].impl.handedness; } + get targetRayMode() { return this[PRIVATE$12].impl.targetRayMode; } + get gripSpace() { + let mode = this[PRIVATE$12].impl.targetRayMode; + if (mode === "gaze" || mode === "screen") { + return null; + } + return this[PRIVATE$12].gripSpace; + } + get targetRaySpace() { return this[PRIVATE$12].targetRaySpace; } + get profiles() { return this[PRIVATE$12].impl.profiles; } + get gamepad() { return this[PRIVATE$12].impl.gamepad; } +} + +const PRIVATE$13 = Symbol('@@webxr-polyfill/XRRenderState'); +const XRRenderStateInit = Object.freeze({ + depthNear: 0.1, + depthFar: 1000.0, + inlineVerticalFieldOfView: null, + baseLayer: null +}); +class XRRenderState { + constructor(stateInit = {}) { + const config = Object.assign({}, XRRenderStateInit, stateInit); + this[PRIVATE$13] = { config }; + } + get depthNear() { return this[PRIVATE$13].depthNear; } + get depthFar() { return this[PRIVATE$13].depthFar; } + get inlineVerticalFieldOfView() { return this[PRIVATE$13].inlineVerticalFieldOfView; } + get baseLayer() { return this[PRIVATE$13].baseLayer; } +} + +var API = { + XR: XR$1, + XRSession: XRSession$1, + XRFrame, + XRView, + XRViewport, + XRViewerPose, + XRWebGLLayer, + XRSpace, + XRReferenceSpace, + XRInputSource, + XRRenderState, + XRRigidTransform: XRRigidTransform$1, + XRPose: XRPose$1, +}; + +const polyfillMakeXRCompatible = Context => { + if (typeof Context.prototype.makeXRCompatible === 'function') { + return false; + } + Context.prototype.makeXRCompatible = function () { + this[XR_COMPATIBLE] = true; + return Promise.resolve(); + }; + return true; +}; +const polyfillGetContext = (Canvas) => { + const getContext = Canvas.prototype.getContext; + Canvas.prototype.getContext = function (contextType, glAttribs) { + const ctx = getContext.call(this, contextType, glAttribs); + if (ctx) { + ctx[POLYFILLED_XR_COMPATIBLE] = true; + if (glAttribs && ('xrCompatible' in glAttribs)) { + ctx[XR_COMPATIBLE] = glAttribs.xrCompatible; + } + } + return ctx; + }; +}; + +const isImageBitmapSupported = global => + !!(global.ImageBitmapRenderingContext && + global.createImageBitmap); + +const applyCanvasStylesForMinimalRendering = canvas => { + canvas.style.display = 'block'; + canvas.style.position = 'absolute'; + canvas.style.width = canvas.style.height = '1px'; + canvas.style.top = canvas.style.left = '0px'; +}; + +var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + + +function unwrapExports (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; +} + +function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; +} + +var cardboardVrDisplay = createCommonjsModule(function (module, exports) { +(function (global, factory) { + module.exports = factory(); +}(commonjsGlobal, (function () { var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; +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 slicedToArray = function () { + function sliceIterator(arr, i) { + var _arr = []; + var _n = true; + var _d = false; + var _e = undefined; + try { + for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { + _arr.push(_s.value); + if (i && _arr.length === i) break; + } + } catch (err) { + _d = true; + _e = err; + } finally { + try { + if (!_n && _i["return"]) _i["return"](); + } finally { + if (_d) throw _e; + } + } + return _arr; + } + return function (arr, i) { + if (Array.isArray(arr)) { + return arr; + } else if (Symbol.iterator in Object(arr)) { + return sliceIterator(arr, i); + } else { + throw new TypeError("Invalid attempt to destructure non-iterable instance"); + } + }; +}(); +var MIN_TIMESTEP = 0.001; +var MAX_TIMESTEP = 1; +var dataUri = function dataUri(mimeType, svg) { + return 'data:' + mimeType + ',' + encodeURIComponent(svg); +}; +var lerp = function lerp(a, b, t) { + return a + (b - a) * t; +}; +var isIOS = function () { + var isIOS = /iPad|iPhone|iPod/.test(navigator.platform); + return function () { + return isIOS; + }; +}(); +var isWebViewAndroid = function () { + var isWebViewAndroid = navigator.userAgent.indexOf('Version') !== -1 && navigator.userAgent.indexOf('Android') !== -1 && navigator.userAgent.indexOf('Chrome') !== -1; + return function () { + return isWebViewAndroid; + }; +}(); +var isSafari = function () { + var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); + return function () { + return isSafari; + }; +}(); +var isFirefoxAndroid = function () { + var isFirefoxAndroid = navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1; + return function () { + return isFirefoxAndroid; + }; +}(); +var getChromeVersion = function () { + var match = navigator.userAgent.match(/.*Chrome\/([0-9]+)/); + var value = match ? parseInt(match[1], 10) : null; + return function () { + return value; + }; +}(); +var isChromeWithoutDeviceMotion = function () { + var value = false; + if (getChromeVersion() === 65) { + var match = navigator.userAgent.match(/.*Chrome\/([0-9\.]*)/); + if (match) { + var _match$1$split = match[1].split('.'), + _match$1$split2 = slicedToArray(_match$1$split, 4), + major = _match$1$split2[0], + minor = _match$1$split2[1], + branch = _match$1$split2[2], + build = _match$1$split2[3]; + value = parseInt(branch, 10) === 3325 && parseInt(build, 10) < 148; + } + } + return function () { + return value; + }; +}(); +var isR7 = function () { + var isR7 = navigator.userAgent.indexOf('R7 Build') !== -1; + return function () { + return isR7; + }; +}(); +var isLandscapeMode = function isLandscapeMode() { + var rtn = window.orientation == 90 || window.orientation == -90; + return isR7() ? !rtn : rtn; +}; +var isTimestampDeltaValid = function isTimestampDeltaValid(timestampDeltaS) { + if (isNaN(timestampDeltaS)) { + return false; + } + if (timestampDeltaS <= MIN_TIMESTEP) { + return false; + } + if (timestampDeltaS > MAX_TIMESTEP) { + return false; + } + return true; +}; +var getScreenWidth = function getScreenWidth() { + return Math.max(window.screen.width, window.screen.height) * window.devicePixelRatio; +}; +var getScreenHeight = function getScreenHeight() { + return Math.min(window.screen.width, window.screen.height) * window.devicePixelRatio; +}; +var requestFullscreen = function requestFullscreen(element) { + if (isWebViewAndroid()) { + return false; + } + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen(); + } else { + return false; + } + return true; +}; +var exitFullscreen = function exitFullscreen() { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } else { + return false; + } + return true; +}; +var getFullscreenElement = function getFullscreenElement() { + return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement; +}; +var linkProgram = function linkProgram(gl, vertexSource, fragmentSource, attribLocationMap) { + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, vertexSource); + gl.compileShader(vertexShader); + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, fragmentSource); + gl.compileShader(fragmentShader); + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + for (var attribName in attribLocationMap) { + gl.bindAttribLocation(program, attribLocationMap[attribName], attribName); + }gl.linkProgram(program); + gl.deleteShader(vertexShader); + gl.deleteShader(fragmentShader); + return program; +}; +var getProgramUniforms = function getProgramUniforms(gl, program) { + var uniforms = {}; + var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + var uniformName = ''; + for (var i = 0; i < uniformCount; i++) { + var uniformInfo = gl.getActiveUniform(program, i); + uniformName = uniformInfo.name.replace('[0]', ''); + uniforms[uniformName] = gl.getUniformLocation(program, uniformName); + } + return uniforms; +}; +var orthoMatrix = function orthoMatrix(out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + nf = 1 / (near - far); + out[0] = -2 * lr; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = -2 * bt; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 2 * nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = (far + near) * nf; + out[15] = 1; + return out; +}; +var isMobile = function isMobile() { + var check = false; + (function (a) { + if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; + })(navigator.userAgent || navigator.vendor || window.opera); + return check; +}; +var extend = function extend(dest, src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + dest[key] = src[key]; + } + } + return dest; +}; +var safariCssSizeWorkaround = function safariCssSizeWorkaround(canvas) { + if (isIOS()) { + var width = canvas.style.width; + var height = canvas.style.height; + canvas.style.width = parseInt(width) + 1 + 'px'; + canvas.style.height = parseInt(height) + 'px'; + setTimeout(function () { + canvas.style.width = width; + canvas.style.height = height; + }, 100); + } + window.canvas = canvas; +}; +var frameDataFromPose = function () { + var piOver180 = Math.PI / 180.0; + var rad45 = Math.PI * 0.25; + function mat4_perspectiveFromFieldOfView(out, fov, near, far) { + var upTan = Math.tan(fov ? fov.upDegrees * piOver180 : rad45), + downTan = Math.tan(fov ? fov.downDegrees * piOver180 : rad45), + leftTan = Math.tan(fov ? fov.leftDegrees * piOver180 : rad45), + rightTan = Math.tan(fov ? fov.rightDegrees * piOver180 : rad45), + xScale = 2.0 / (leftTan + rightTan), + yScale = 2.0 / (upTan + downTan); + out[0] = xScale; + out[1] = 0.0; + out[2] = 0.0; + out[3] = 0.0; + out[4] = 0.0; + out[5] = yScale; + out[6] = 0.0; + out[7] = 0.0; + out[8] = -((leftTan - rightTan) * xScale * 0.5); + out[9] = (upTan - downTan) * yScale * 0.5; + out[10] = far / (near - far); + out[11] = -1.0; + out[12] = 0.0; + out[13] = 0.0; + out[14] = far * near / (near - far); + out[15] = 0.0; + return out; + } + function mat4_fromRotationTranslation(out, q, v) { + var x = q[0], + y = q[1], + z = q[2], + w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + return out; + } + function mat4_translate(out, a, v) { + var x = v[0], + y = v[1], + z = v[2], + a00, + a01, + a02, + a03, + a10, + a11, + a12, + a13, + a20, + a21, + a22, + a23; + if (a === out) { + out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; + out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; + out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; + out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; + } else { + a00 = a[0];a01 = a[1];a02 = a[2];a03 = a[3]; + a10 = a[4];a11 = a[5];a12 = a[6];a13 = a[7]; + a20 = a[8];a21 = a[9];a22 = a[10];a23 = a[11]; + out[0] = a00;out[1] = a01;out[2] = a02;out[3] = a03; + out[4] = a10;out[5] = a11;out[6] = a12;out[7] = a13; + out[8] = a20;out[9] = a21;out[10] = a22;out[11] = a23; + out[12] = a00 * x + a10 * y + a20 * z + a[12]; + out[13] = a01 * x + a11 * y + a21 * z + a[13]; + out[14] = a02 * x + a12 * y + a22 * z + a[14]; + out[15] = a03 * x + a13 * y + a23 * z + a[15]; + } + return out; + } + function mat4_invert(out, a) { + var a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11], + a30 = a[12], + a31 = a[13], + a32 = a[14], + a33 = a[15], + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + if (!det) { + return null; + } + det = 1.0 / det; + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + return out; + } + var defaultOrientation = new Float32Array([0, 0, 0, 1]); + var defaultPosition = new Float32Array([0, 0, 0]); + function updateEyeMatrices(projection, view, pose, fov, offset, vrDisplay) { + mat4_perspectiveFromFieldOfView(projection, fov || null, vrDisplay.depthNear, vrDisplay.depthFar); + var orientation = pose.orientation || defaultOrientation; + var position = pose.position || defaultPosition; + mat4_fromRotationTranslation(view, orientation, position); + if (offset) mat4_translate(view, view, offset); + mat4_invert(view, view); + } + return function (frameData, pose, vrDisplay) { + if (!frameData || !pose) return false; + frameData.pose = pose; + frameData.timestamp = pose.timestamp; + updateEyeMatrices(frameData.leftProjectionMatrix, frameData.leftViewMatrix, pose, vrDisplay._getFieldOfView("left"), vrDisplay._getEyeOffset("left"), vrDisplay); + updateEyeMatrices(frameData.rightProjectionMatrix, frameData.rightViewMatrix, pose, vrDisplay._getFieldOfView("right"), vrDisplay._getEyeOffset("right"), vrDisplay); + return true; + }; +}(); +var isInsideCrossOriginIFrame = function isInsideCrossOriginIFrame() { + var isFramed = window.self !== window.top; + var refOrigin = getOriginFromUrl(document.referrer); + var thisOrigin = getOriginFromUrl(window.location.href); + return isFramed && refOrigin !== thisOrigin; +}; +var getOriginFromUrl = function getOriginFromUrl(url) { + var domainIdx; + var protoSepIdx = url.indexOf("://"); + if (protoSepIdx !== -1) { + domainIdx = protoSepIdx + 3; + } else { + domainIdx = 0; + } + var domainEndIdx = url.indexOf('/', domainIdx); + if (domainEndIdx === -1) { + domainEndIdx = url.length; + } + return url.substring(0, domainEndIdx); +}; +var getQuaternionAngle = function getQuaternionAngle(quat) { + if (quat.w > 1) { + console.warn('getQuaternionAngle: w > 1'); + return 0; + } + var angle = 2 * Math.acos(quat.w); + return angle; +}; +var warnOnce = function () { + var observedWarnings = {}; + return function (key, message) { + if (observedWarnings[key] === undefined) { + console.warn('webvr-polyfill: ' + message); + observedWarnings[key] = true; + } + }; +}(); +var deprecateWarning = function deprecateWarning(deprecated, suggested) { + var alternative = suggested ? 'Please use ' + suggested + ' instead.' : ''; + warnOnce(deprecated, deprecated + ' has been deprecated. ' + 'This may not work on native WebVR displays. ' + alternative); +}; +function WGLUPreserveGLState(gl, bindings, callback) { + if (!bindings) { + callback(gl); + return; + } + var boundValues = []; + var activeTexture = null; + for (var i = 0; i < bindings.length; ++i) { + var binding = bindings[i]; + switch (binding) { + case gl.TEXTURE_BINDING_2D: + case gl.TEXTURE_BINDING_CUBE_MAP: + var textureUnit = bindings[++i]; + if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) { + console.error("TEXTURE_BINDING_2D or TEXTURE_BINDING_CUBE_MAP must be followed by a valid texture unit"); + boundValues.push(null, null); + break; + } + if (!activeTexture) { + activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE); + } + gl.activeTexture(textureUnit); + boundValues.push(gl.getParameter(binding), null); + break; + case gl.ACTIVE_TEXTURE: + activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE); + boundValues.push(null); + break; + default: + boundValues.push(gl.getParameter(binding)); + break; + } + } + callback(gl); + for (var i = 0; i < bindings.length; ++i) { + var binding = bindings[i]; + var boundValue = boundValues[i]; + switch (binding) { + case gl.ACTIVE_TEXTURE: + break; + case gl.ARRAY_BUFFER_BINDING: + gl.bindBuffer(gl.ARRAY_BUFFER, boundValue); + break; + case gl.COLOR_CLEAR_VALUE: + gl.clearColor(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); + break; + case gl.COLOR_WRITEMASK: + gl.colorMask(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); + break; + case gl.CURRENT_PROGRAM: + gl.useProgram(boundValue); + break; + case gl.ELEMENT_ARRAY_BUFFER_BINDING: + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boundValue); + break; + case gl.FRAMEBUFFER_BINDING: + gl.bindFramebuffer(gl.FRAMEBUFFER, boundValue); + break; + case gl.RENDERBUFFER_BINDING: + gl.bindRenderbuffer(gl.RENDERBUFFER, boundValue); + break; + case gl.TEXTURE_BINDING_2D: + var textureUnit = bindings[++i]; + if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) + break; + gl.activeTexture(textureUnit); + gl.bindTexture(gl.TEXTURE_2D, boundValue); + break; + case gl.TEXTURE_BINDING_CUBE_MAP: + var textureUnit = bindings[++i]; + if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) + break; + gl.activeTexture(textureUnit); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, boundValue); + break; + case gl.VIEWPORT: + gl.viewport(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); + break; + case gl.BLEND: + case gl.CULL_FACE: + case gl.DEPTH_TEST: + case gl.SCISSOR_TEST: + case gl.STENCIL_TEST: + if (boundValue) { + gl.enable(binding); + } else { + gl.disable(binding); + } + break; + default: + console.log("No GL restore behavior for 0x" + binding.toString(16)); + break; + } + if (activeTexture) { + gl.activeTexture(activeTexture); + } + } +} +var glPreserveState = WGLUPreserveGLState; +var distortionVS = ['attribute vec2 position;', 'attribute vec3 texCoord;', 'varying vec2 vTexCoord;', 'uniform vec4 viewportOffsetScale[2];', 'void main() {', ' vec4 viewport = viewportOffsetScale[int(texCoord.z)];', ' vTexCoord = (texCoord.xy * viewport.zw) + viewport.xy;', ' gl_Position = vec4( position, 1.0, 1.0 );', '}'].join('\n'); +var distortionFS = ['precision mediump float;', 'uniform sampler2D diffuse;', 'varying vec2 vTexCoord;', 'void main() {', ' gl_FragColor = texture2D(diffuse, vTexCoord);', '}'].join('\n'); +function CardboardDistorter(gl, cardboardUI, bufferScale, dirtySubmitFrameBindings) { + this.gl = gl; + this.cardboardUI = cardboardUI; + this.bufferScale = bufferScale; + this.dirtySubmitFrameBindings = dirtySubmitFrameBindings; + this.ctxAttribs = gl.getContextAttributes(); + this.meshWidth = 20; + this.meshHeight = 20; + this.bufferWidth = gl.drawingBufferWidth; + this.bufferHeight = gl.drawingBufferHeight; + this.realBindFramebuffer = gl.bindFramebuffer; + this.realEnable = gl.enable; + this.realDisable = gl.disable; + this.realColorMask = gl.colorMask; + this.realClearColor = gl.clearColor; + this.realViewport = gl.viewport; + if (!isIOS()) { + this.realCanvasWidth = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'width'); + this.realCanvasHeight = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'height'); + } + this.isPatched = false; + this.lastBoundFramebuffer = null; + this.cullFace = false; + this.depthTest = false; + this.blend = false; + this.scissorTest = false; + this.stencilTest = false; + this.viewport = [0, 0, 0, 0]; + this.colorMask = [true, true, true, true]; + this.clearColor = [0, 0, 0, 0]; + this.attribs = { + position: 0, + texCoord: 1 + }; + this.program = linkProgram(gl, distortionVS, distortionFS, this.attribs); + this.uniforms = getProgramUniforms(gl, this.program); + this.viewportOffsetScale = new Float32Array(8); + this.setTextureBounds(); + this.vertexBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + this.indexCount = 0; + this.renderTarget = gl.createTexture(); + this.framebuffer = gl.createFramebuffer(); + this.depthStencilBuffer = null; + this.depthBuffer = null; + this.stencilBuffer = null; + if (this.ctxAttribs.depth && this.ctxAttribs.stencil) { + this.depthStencilBuffer = gl.createRenderbuffer(); + } else if (this.ctxAttribs.depth) { + this.depthBuffer = gl.createRenderbuffer(); + } else if (this.ctxAttribs.stencil) { + this.stencilBuffer = gl.createRenderbuffer(); + } + this.patch(); + this.onResize(); +} +CardboardDistorter.prototype.destroy = function () { + var gl = this.gl; + this.unpatch(); + gl.deleteProgram(this.program); + gl.deleteBuffer(this.vertexBuffer); + gl.deleteBuffer(this.indexBuffer); + gl.deleteTexture(this.renderTarget); + gl.deleteFramebuffer(this.framebuffer); + if (this.depthStencilBuffer) { + gl.deleteRenderbuffer(this.depthStencilBuffer); + } + if (this.depthBuffer) { + gl.deleteRenderbuffer(this.depthBuffer); + } + if (this.stencilBuffer) { + gl.deleteRenderbuffer(this.stencilBuffer); + } + if (this.cardboardUI) { + this.cardboardUI.destroy(); + } +}; +CardboardDistorter.prototype.onResize = function () { + var gl = this.gl; + var self = this; + var glState = [gl.RENDERBUFFER_BINDING, gl.TEXTURE_BINDING_2D, gl.TEXTURE0]; + glPreserveState(gl, glState, function (gl) { + self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null); + if (self.scissorTest) { + self.realDisable.call(gl, gl.SCISSOR_TEST); + } + self.realColorMask.call(gl, true, true, true, true); + self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + self.realClearColor.call(gl, 0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.framebuffer); + gl.bindTexture(gl.TEXTURE_2D, self.renderTarget); + gl.texImage2D(gl.TEXTURE_2D, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, self.bufferWidth, self.bufferHeight, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, gl.UNSIGNED_BYTE, null); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, self.renderTarget, 0); + if (self.ctxAttribs.depth && self.ctxAttribs.stencil) { + gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthStencilBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, self.bufferWidth, self.bufferHeight); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, self.depthStencilBuffer); + } else if (self.ctxAttribs.depth) { + gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, self.bufferWidth, self.bufferHeight); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, self.depthBuffer); + } else if (self.ctxAttribs.stencil) { + gl.bindRenderbuffer(gl.RENDERBUFFER, self.stencilBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, self.bufferWidth, self.bufferHeight); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, self.stencilBuffer); + } + if (!gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) { + console.error('Framebuffer incomplete!'); + } + self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer); + if (self.scissorTest) { + self.realEnable.call(gl, gl.SCISSOR_TEST); + } + self.realColorMask.apply(gl, self.colorMask); + self.realViewport.apply(gl, self.viewport); + self.realClearColor.apply(gl, self.clearColor); + }); + if (this.cardboardUI) { + this.cardboardUI.onResize(); + } +}; +CardboardDistorter.prototype.patch = function () { + if (this.isPatched) { + return; + } + var self = this; + var canvas = this.gl.canvas; + var gl = this.gl; + if (!isIOS()) { + canvas.width = getScreenWidth() * this.bufferScale; + canvas.height = getScreenHeight() * this.bufferScale; + Object.defineProperty(canvas, 'width', { + configurable: true, + enumerable: true, + get: function get() { + return self.bufferWidth; + }, + set: function set(value) { + self.bufferWidth = value; + self.realCanvasWidth.set.call(canvas, value); + self.onResize(); + } + }); + Object.defineProperty(canvas, 'height', { + configurable: true, + enumerable: true, + get: function get() { + return self.bufferHeight; + }, + set: function set(value) { + self.bufferHeight = value; + self.realCanvasHeight.set.call(canvas, value); + self.onResize(); + } + }); + } + this.lastBoundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); + if (this.lastBoundFramebuffer == null) { + this.lastBoundFramebuffer = this.framebuffer; + this.gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); + } + this.gl.bindFramebuffer = function (target, framebuffer) { + self.lastBoundFramebuffer = framebuffer ? framebuffer : self.framebuffer; + self.realBindFramebuffer.call(gl, target, self.lastBoundFramebuffer); + }; + this.cullFace = gl.getParameter(gl.CULL_FACE); + this.depthTest = gl.getParameter(gl.DEPTH_TEST); + this.blend = gl.getParameter(gl.BLEND); + this.scissorTest = gl.getParameter(gl.SCISSOR_TEST); + this.stencilTest = gl.getParameter(gl.STENCIL_TEST); + gl.enable = function (pname) { + switch (pname) { + case gl.CULL_FACE: + self.cullFace = true;break; + case gl.DEPTH_TEST: + self.depthTest = true;break; + case gl.BLEND: + self.blend = true;break; + case gl.SCISSOR_TEST: + self.scissorTest = true;break; + case gl.STENCIL_TEST: + self.stencilTest = true;break; + } + self.realEnable.call(gl, pname); + }; + gl.disable = function (pname) { + switch (pname) { + case gl.CULL_FACE: + self.cullFace = false;break; + case gl.DEPTH_TEST: + self.depthTest = false;break; + case gl.BLEND: + self.blend = false;break; + case gl.SCISSOR_TEST: + self.scissorTest = false;break; + case gl.STENCIL_TEST: + self.stencilTest = false;break; + } + self.realDisable.call(gl, pname); + }; + this.colorMask = gl.getParameter(gl.COLOR_WRITEMASK); + gl.colorMask = function (r, g, b, a) { + self.colorMask[0] = r; + self.colorMask[1] = g; + self.colorMask[2] = b; + self.colorMask[3] = a; + self.realColorMask.call(gl, r, g, b, a); + }; + this.clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE); + gl.clearColor = function (r, g, b, a) { + self.clearColor[0] = r; + self.clearColor[1] = g; + self.clearColor[2] = b; + self.clearColor[3] = a; + self.realClearColor.call(gl, r, g, b, a); + }; + this.viewport = gl.getParameter(gl.VIEWPORT); + gl.viewport = function (x, y, w, h) { + self.viewport[0] = x; + self.viewport[1] = y; + self.viewport[2] = w; + self.viewport[3] = h; + self.realViewport.call(gl, x, y, w, h); + }; + this.isPatched = true; + safariCssSizeWorkaround(canvas); +}; +CardboardDistorter.prototype.unpatch = function () { + if (!this.isPatched) { + return; + } + var gl = this.gl; + var canvas = this.gl.canvas; + if (!isIOS()) { + Object.defineProperty(canvas, 'width', this.realCanvasWidth); + Object.defineProperty(canvas, 'height', this.realCanvasHeight); + } + canvas.width = this.bufferWidth; + canvas.height = this.bufferHeight; + gl.bindFramebuffer = this.realBindFramebuffer; + gl.enable = this.realEnable; + gl.disable = this.realDisable; + gl.colorMask = this.realColorMask; + gl.clearColor = this.realClearColor; + gl.viewport = this.realViewport; + if (this.lastBoundFramebuffer == this.framebuffer) { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + } + this.isPatched = false; + setTimeout(function () { + safariCssSizeWorkaround(canvas); + }, 1); +}; +CardboardDistorter.prototype.setTextureBounds = function (leftBounds, rightBounds) { + if (!leftBounds) { + leftBounds = [0, 0, 0.5, 1]; + } + if (!rightBounds) { + rightBounds = [0.5, 0, 0.5, 1]; + } + this.viewportOffsetScale[0] = leftBounds[0]; + this.viewportOffsetScale[1] = leftBounds[1]; + this.viewportOffsetScale[2] = leftBounds[2]; + this.viewportOffsetScale[3] = leftBounds[3]; + this.viewportOffsetScale[4] = rightBounds[0]; + this.viewportOffsetScale[5] = rightBounds[1]; + this.viewportOffsetScale[6] = rightBounds[2]; + this.viewportOffsetScale[7] = rightBounds[3]; +}; +CardboardDistorter.prototype.submitFrame = function () { + var gl = this.gl; + var self = this; + var glState = []; + if (!this.dirtySubmitFrameBindings) { + glState.push(gl.CURRENT_PROGRAM, gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING, gl.TEXTURE_BINDING_2D, gl.TEXTURE0); + } + glPreserveState(gl, glState, function (gl) { + self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null); + if (self.cullFace) { + self.realDisable.call(gl, gl.CULL_FACE); + } + if (self.depthTest) { + self.realDisable.call(gl, gl.DEPTH_TEST); + } + if (self.blend) { + self.realDisable.call(gl, gl.BLEND); + } + if (self.scissorTest) { + self.realDisable.call(gl, gl.SCISSOR_TEST); + } + if (self.stencilTest) { + self.realDisable.call(gl, gl.STENCIL_TEST); + } + self.realColorMask.call(gl, true, true, true, true); + self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + if (self.ctxAttribs.alpha || isIOS()) { + self.realClearColor.call(gl, 0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + } + gl.useProgram(self.program); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); + gl.enableVertexAttribArray(self.attribs.position); + gl.enableVertexAttribArray(self.attribs.texCoord); + gl.vertexAttribPointer(self.attribs.position, 2, gl.FLOAT, false, 20, 0); + gl.vertexAttribPointer(self.attribs.texCoord, 3, gl.FLOAT, false, 20, 8); + gl.activeTexture(gl.TEXTURE0); + gl.uniform1i(self.uniforms.diffuse, 0); + gl.bindTexture(gl.TEXTURE_2D, self.renderTarget); + gl.uniform4fv(self.uniforms.viewportOffsetScale, self.viewportOffsetScale); + gl.drawElements(gl.TRIANGLES, self.indexCount, gl.UNSIGNED_SHORT, 0); + if (self.cardboardUI) { + self.cardboardUI.renderNoState(); + } + self.realBindFramebuffer.call(self.gl, gl.FRAMEBUFFER, self.framebuffer); + if (!self.ctxAttribs.preserveDrawingBuffer) { + self.realClearColor.call(gl, 0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + if (!self.dirtySubmitFrameBindings) { + self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer); + } + if (self.cullFace) { + self.realEnable.call(gl, gl.CULL_FACE); + } + if (self.depthTest) { + self.realEnable.call(gl, gl.DEPTH_TEST); + } + if (self.blend) { + self.realEnable.call(gl, gl.BLEND); + } + if (self.scissorTest) { + self.realEnable.call(gl, gl.SCISSOR_TEST); + } + if (self.stencilTest) { + self.realEnable.call(gl, gl.STENCIL_TEST); + } + self.realColorMask.apply(gl, self.colorMask); + self.realViewport.apply(gl, self.viewport); + if (self.ctxAttribs.alpha || !self.ctxAttribs.preserveDrawingBuffer) { + self.realClearColor.apply(gl, self.clearColor); + } + }); + if (isIOS()) { + var canvas = gl.canvas; + if (canvas.width != self.bufferWidth || canvas.height != self.bufferHeight) { + self.bufferWidth = canvas.width; + self.bufferHeight = canvas.height; + self.onResize(); + } + } +}; +CardboardDistorter.prototype.updateDeviceInfo = function (deviceInfo) { + var gl = this.gl; + var self = this; + var glState = [gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING]; + glPreserveState(gl, glState, function (gl) { + var vertices = self.computeMeshVertices_(self.meshWidth, self.meshHeight, deviceInfo); + gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); + if (!self.indexCount) { + var indices = self.computeMeshIndices_(self.meshWidth, self.meshHeight); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + self.indexCount = indices.length; + } + }); +}; +CardboardDistorter.prototype.computeMeshVertices_ = function (width, height, deviceInfo) { + var vertices = new Float32Array(2 * width * height * 5); + var lensFrustum = deviceInfo.getLeftEyeVisibleTanAngles(); + var noLensFrustum = deviceInfo.getLeftEyeNoLensTanAngles(); + var viewport = deviceInfo.getLeftEyeVisibleScreenRect(noLensFrustum); + var vidx = 0; + for (var e = 0; e < 2; e++) { + for (var j = 0; j < height; j++) { + for (var i = 0; i < width; i++, vidx++) { + var u = i / (width - 1); + var v = j / (height - 1); + var s = u; + var t = v; + var x = lerp(lensFrustum[0], lensFrustum[2], u); + var y = lerp(lensFrustum[3], lensFrustum[1], v); + var d = Math.sqrt(x * x + y * y); + var r = deviceInfo.distortion.distortInverse(d); + var p = x * r / d; + var q = y * r / d; + u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]); + v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]); + u = (viewport.x + u * viewport.width - 0.5) * 2.0; + v = (viewport.y + v * viewport.height - 0.5) * 2.0; + vertices[vidx * 5 + 0] = u; + vertices[vidx * 5 + 1] = v; + vertices[vidx * 5 + 2] = s; + vertices[vidx * 5 + 3] = t; + vertices[vidx * 5 + 4] = e; + } + } + var w = lensFrustum[2] - lensFrustum[0]; + lensFrustum[0] = -(w + lensFrustum[0]); + lensFrustum[2] = w - lensFrustum[2]; + w = noLensFrustum[2] - noLensFrustum[0]; + noLensFrustum[0] = -(w + noLensFrustum[0]); + noLensFrustum[2] = w - noLensFrustum[2]; + viewport.x = 1 - (viewport.x + viewport.width); + } + return vertices; +}; +CardboardDistorter.prototype.computeMeshIndices_ = function (width, height) { + var indices = new Uint16Array(2 * (width - 1) * (height - 1) * 6); + var halfwidth = width / 2; + var halfheight = height / 2; + var vidx = 0; + var iidx = 0; + for (var e = 0; e < 2; e++) { + for (var j = 0; j < height; j++) { + for (var i = 0; i < width; i++, vidx++) { + if (i == 0 || j == 0) continue; + if (i <= halfwidth == j <= halfheight) { + indices[iidx++] = vidx; + indices[iidx++] = vidx - width - 1; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx - width - 1; + indices[iidx++] = vidx; + indices[iidx++] = vidx - 1; + } else { + indices[iidx++] = vidx - 1; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx - 1; + indices[iidx++] = vidx - width - 1; + } + } + } + } + return indices; +}; +CardboardDistorter.prototype.getOwnPropertyDescriptor_ = function (proto, attrName) { + var descriptor = Object.getOwnPropertyDescriptor(proto, attrName); + if (descriptor.get === undefined || descriptor.set === undefined) { + descriptor.configurable = true; + descriptor.enumerable = true; + descriptor.get = function () { + return this.getAttribute(attrName); + }; + descriptor.set = function (val) { + this.setAttribute(attrName, val); + }; + } + return descriptor; +}; +var uiVS = ['attribute vec2 position;', 'uniform mat4 projectionMat;', 'void main() {', ' gl_Position = projectionMat * vec4( position, -1.0, 1.0 );', '}'].join('\n'); +var uiFS = ['precision mediump float;', 'uniform vec4 color;', 'void main() {', ' gl_FragColor = color;', '}'].join('\n'); +var DEG2RAD = Math.PI / 180.0; +var kAnglePerGearSection = 60; +var kOuterRimEndAngle = 12; +var kInnerRimBeginAngle = 20; +var kOuterRadius = 1; +var kMiddleRadius = 0.75; +var kInnerRadius = 0.3125; +var kCenterLineThicknessDp = 4; +var kButtonWidthDp = 28; +var kTouchSlopFactor = 1.5; +function CardboardUI(gl) { + this.gl = gl; + this.attribs = { + position: 0 + }; + this.program = linkProgram(gl, uiVS, uiFS, this.attribs); + this.uniforms = getProgramUniforms(gl, this.program); + this.vertexBuffer = gl.createBuffer(); + this.gearOffset = 0; + this.gearVertexCount = 0; + this.arrowOffset = 0; + this.arrowVertexCount = 0; + this.projMat = new Float32Array(16); + this.listener = null; + this.onResize(); +} +CardboardUI.prototype.destroy = function () { + var gl = this.gl; + if (this.listener) { + gl.canvas.removeEventListener('click', this.listener, false); + } + gl.deleteProgram(this.program); + gl.deleteBuffer(this.vertexBuffer); +}; +CardboardUI.prototype.listen = function (optionsCallback, backCallback) { + var canvas = this.gl.canvas; + this.listener = function (event) { + var midline = canvas.clientWidth / 2; + var buttonSize = kButtonWidthDp * kTouchSlopFactor; + if (event.clientX > midline - buttonSize && event.clientX < midline + buttonSize && event.clientY > canvas.clientHeight - buttonSize) { + optionsCallback(event); + } + else if (event.clientX < buttonSize && event.clientY < buttonSize) { + backCallback(event); + } + }; + canvas.addEventListener('click', this.listener, false); +}; +CardboardUI.prototype.onResize = function () { + var gl = this.gl; + var self = this; + var glState = [gl.ARRAY_BUFFER_BINDING]; + glPreserveState(gl, glState, function (gl) { + var vertices = []; + var midline = gl.drawingBufferWidth / 2; + var physicalPixels = Math.max(screen.width, screen.height) * window.devicePixelRatio; + var scalingRatio = gl.drawingBufferWidth / physicalPixels; + var dps = scalingRatio * window.devicePixelRatio; + var lineWidth = kCenterLineThicknessDp * dps / 2; + var buttonSize = kButtonWidthDp * kTouchSlopFactor * dps; + var buttonScale = kButtonWidthDp * dps / 2; + var buttonBorder = (kButtonWidthDp * kTouchSlopFactor - kButtonWidthDp) * dps; + vertices.push(midline - lineWidth, buttonSize); + vertices.push(midline - lineWidth, gl.drawingBufferHeight); + vertices.push(midline + lineWidth, buttonSize); + vertices.push(midline + lineWidth, gl.drawingBufferHeight); + self.gearOffset = vertices.length / 2; + function addGearSegment(theta, r) { + var angle = (90 - theta) * DEG2RAD; + var x = Math.cos(angle); + var y = Math.sin(angle); + vertices.push(kInnerRadius * x * buttonScale + midline, kInnerRadius * y * buttonScale + buttonScale); + vertices.push(r * x * buttonScale + midline, r * y * buttonScale + buttonScale); + } + for (var i = 0; i <= 6; i++) { + var segmentTheta = i * kAnglePerGearSection; + addGearSegment(segmentTheta, kOuterRadius); + addGearSegment(segmentTheta + kOuterRimEndAngle, kOuterRadius); + addGearSegment(segmentTheta + kInnerRimBeginAngle, kMiddleRadius); + addGearSegment(segmentTheta + (kAnglePerGearSection - kInnerRimBeginAngle), kMiddleRadius); + addGearSegment(segmentTheta + (kAnglePerGearSection - kOuterRimEndAngle), kOuterRadius); + } + self.gearVertexCount = vertices.length / 2 - self.gearOffset; + self.arrowOffset = vertices.length / 2; + function addArrowVertex(x, y) { + vertices.push(buttonBorder + x, gl.drawingBufferHeight - buttonBorder - y); + } + var angledLineWidth = lineWidth / Math.sin(45 * DEG2RAD); + addArrowVertex(0, buttonScale); + addArrowVertex(buttonScale, 0); + addArrowVertex(buttonScale + angledLineWidth, angledLineWidth); + addArrowVertex(angledLineWidth, buttonScale + angledLineWidth); + addArrowVertex(angledLineWidth, buttonScale - angledLineWidth); + addArrowVertex(0, buttonScale); + addArrowVertex(buttonScale, buttonScale * 2); + addArrowVertex(buttonScale + angledLineWidth, buttonScale * 2 - angledLineWidth); + addArrowVertex(angledLineWidth, buttonScale - angledLineWidth); + addArrowVertex(0, buttonScale); + addArrowVertex(angledLineWidth, buttonScale - lineWidth); + addArrowVertex(kButtonWidthDp * dps, buttonScale - lineWidth); + addArrowVertex(angledLineWidth, buttonScale + lineWidth); + addArrowVertex(kButtonWidthDp * dps, buttonScale + lineWidth); + self.arrowVertexCount = vertices.length / 2 - self.arrowOffset; + gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); + }); +}; +CardboardUI.prototype.render = function () { + var gl = this.gl; + var self = this; + var glState = [gl.CULL_FACE, gl.DEPTH_TEST, gl.BLEND, gl.SCISSOR_TEST, gl.STENCIL_TEST, gl.COLOR_WRITEMASK, gl.VIEWPORT, gl.CURRENT_PROGRAM, gl.ARRAY_BUFFER_BINDING]; + glPreserveState(gl, glState, function (gl) { + gl.disable(gl.CULL_FACE); + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + gl.disable(gl.SCISSOR_TEST); + gl.disable(gl.STENCIL_TEST); + gl.colorMask(true, true, true, true); + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + self.renderNoState(); + }); +}; +CardboardUI.prototype.renderNoState = function () { + var gl = this.gl; + gl.useProgram(this.program); + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.enableVertexAttribArray(this.attribs.position); + gl.vertexAttribPointer(this.attribs.position, 2, gl.FLOAT, false, 8, 0); + gl.uniform4f(this.uniforms.color, 1.0, 1.0, 1.0, 1.0); + orthoMatrix(this.projMat, 0, gl.drawingBufferWidth, 0, gl.drawingBufferHeight, 0.1, 1024.0); + gl.uniformMatrix4fv(this.uniforms.projectionMat, false, this.projMat); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + gl.drawArrays(gl.TRIANGLE_STRIP, this.gearOffset, this.gearVertexCount); + gl.drawArrays(gl.TRIANGLE_STRIP, this.arrowOffset, this.arrowVertexCount); +}; +function Distortion(coefficients) { + this.coefficients = coefficients; +} +Distortion.prototype.distortInverse = function (radius) { + var r0 = 0; + var r1 = 1; + var dr0 = radius - this.distort(r0); + while (Math.abs(r1 - r0) > 0.0001 ) { + var dr1 = radius - this.distort(r1); + var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0)); + r0 = r1; + r1 = r2; + dr0 = dr1; + } + return r1; +}; +Distortion.prototype.distort = function (radius) { + var r2 = radius * radius; + var ret = 0; + for (var i = 0; i < this.coefficients.length; i++) { + ret = r2 * (ret + this.coefficients[i]); + } + return (ret + 1) * radius; +}; +var degToRad = Math.PI / 180; +var radToDeg = 180 / Math.PI; +var Vector3 = function Vector3(x, y, z) { + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; +}; +Vector3.prototype = { + constructor: Vector3, + set: function set(x, y, z) { + this.x = x; + this.y = y; + this.z = z; + return this; + }, + copy: function copy(v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + return this; + }, + length: function length() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); + }, + normalize: function normalize() { + var scalar = this.length(); + if (scalar !== 0) { + var invScalar = 1 / scalar; + this.multiplyScalar(invScalar); + } else { + this.x = 0; + this.y = 0; + this.z = 0; + } + return this; + }, + multiplyScalar: function multiplyScalar(scalar) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + }, + applyQuaternion: function applyQuaternion(q) { + var x = this.x; + var y = this.y; + var z = this.z; + var qx = q.x; + var qy = q.y; + var qz = q.z; + var qw = q.w; + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = -qx * x - qy * y - qz * z; + this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; + this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; + this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return this; + }, + dot: function dot(v) { + return this.x * v.x + this.y * v.y + this.z * v.z; + }, + crossVectors: function crossVectors(a, b) { + var ax = a.x, + ay = a.y, + az = a.z; + var bx = b.x, + by = b.y, + bz = b.z; + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + return this; + } +}; +var Quaternion = function Quaternion(x, y, z, w) { + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = w !== undefined ? w : 1; +}; +Quaternion.prototype = { + constructor: Quaternion, + set: function set(x, y, z, w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + }, + copy: function copy(quaternion) { + this.x = quaternion.x; + this.y = quaternion.y; + this.z = quaternion.z; + this.w = quaternion.w; + return this; + }, + setFromEulerXYZ: function setFromEulerXYZ(x, y, z) { + var c1 = Math.cos(x / 2); + var c2 = Math.cos(y / 2); + var c3 = Math.cos(z / 2); + var s1 = Math.sin(x / 2); + var s2 = Math.sin(y / 2); + var s3 = Math.sin(z / 2); + this.x = s1 * c2 * c3 + c1 * s2 * s3; + this.y = c1 * s2 * c3 - s1 * c2 * s3; + this.z = c1 * c2 * s3 + s1 * s2 * c3; + this.w = c1 * c2 * c3 - s1 * s2 * s3; + return this; + }, + setFromEulerYXZ: function setFromEulerYXZ(x, y, z) { + var c1 = Math.cos(x / 2); + var c2 = Math.cos(y / 2); + var c3 = Math.cos(z / 2); + var s1 = Math.sin(x / 2); + var s2 = Math.sin(y / 2); + var s3 = Math.sin(z / 2); + this.x = s1 * c2 * c3 + c1 * s2 * s3; + this.y = c1 * s2 * c3 - s1 * c2 * s3; + this.z = c1 * c2 * s3 - s1 * s2 * c3; + this.w = c1 * c2 * c3 + s1 * s2 * s3; + return this; + }, + setFromAxisAngle: function setFromAxisAngle(axis, angle) { + var halfAngle = angle / 2, + s = Math.sin(halfAngle); + this.x = axis.x * s; + this.y = axis.y * s; + this.z = axis.z * s; + this.w = Math.cos(halfAngle); + return this; + }, + multiply: function multiply(q) { + return this.multiplyQuaternions(this, q); + }, + multiplyQuaternions: function multiplyQuaternions(a, b) { + var qax = a.x, + qay = a.y, + qaz = a.z, + qaw = a.w; + var qbx = b.x, + qby = b.y, + qbz = b.z, + qbw = b.w; + this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + return this; + }, + inverse: function inverse() { + this.x *= -1; + this.y *= -1; + this.z *= -1; + this.normalize(); + return this; + }, + normalize: function normalize() { + var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); + if (l === 0) { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 1; + } else { + l = 1 / l; + this.x = this.x * l; + this.y = this.y * l; + this.z = this.z * l; + this.w = this.w * l; + } + return this; + }, + slerp: function slerp(qb, t) { + if (t === 0) return this; + if (t === 1) return this.copy(qb); + var x = this.x, + y = this.y, + z = this.z, + w = this.w; + var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z; + if (cosHalfTheta < 0) { + this.w = -qb.w; + this.x = -qb.x; + this.y = -qb.y; + this.z = -qb.z; + cosHalfTheta = -cosHalfTheta; + } else { + this.copy(qb); + } + if (cosHalfTheta >= 1.0) { + this.w = w; + this.x = x; + this.y = y; + this.z = z; + return this; + } + var halfTheta = Math.acos(cosHalfTheta); + var sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); + if (Math.abs(sinHalfTheta) < 0.001) { + this.w = 0.5 * (w + this.w); + this.x = 0.5 * (x + this.x); + this.y = 0.5 * (y + this.y); + this.z = 0.5 * (z + this.z); + return this; + } + var ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, + ratioB = Math.sin(t * halfTheta) / sinHalfTheta; + this.w = w * ratioA + this.w * ratioB; + this.x = x * ratioA + this.x * ratioB; + this.y = y * ratioA + this.y * ratioB; + this.z = z * ratioA + this.z * ratioB; + return this; + }, + setFromUnitVectors: function () { + var v1, r; + var EPS = 0.000001; + return function (vFrom, vTo) { + if (v1 === undefined) v1 = new Vector3(); + r = vFrom.dot(vTo) + 1; + if (r < EPS) { + r = 0; + if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { + v1.set(-vFrom.y, vFrom.x, 0); + } else { + v1.set(0, -vFrom.z, vFrom.y); + } + } else { + v1.crossVectors(vFrom, vTo); + } + this.x = v1.x; + this.y = v1.y; + this.z = v1.z; + this.w = r; + this.normalize(); + return this; + }; + }() +}; +function Device(params) { + this.width = params.width || getScreenWidth(); + this.height = params.height || getScreenHeight(); + this.widthMeters = params.widthMeters; + this.heightMeters = params.heightMeters; + this.bevelMeters = params.bevelMeters; +} +var DEFAULT_ANDROID = new Device({ + widthMeters: 0.110, + heightMeters: 0.062, + bevelMeters: 0.004 +}); +var DEFAULT_IOS = new Device({ + widthMeters: 0.1038, + heightMeters: 0.0584, + bevelMeters: 0.004 +}); +var Viewers = { + CardboardV1: new CardboardViewer({ + id: 'CardboardV1', + label: 'Cardboard I/O 2014', + fov: 40, + interLensDistance: 0.060, + baselineLensDistance: 0.035, + screenLensDistance: 0.042, + distortionCoefficients: [0.441, 0.156], + inverseCoefficients: [-0.4410035, 0.42756155, -0.4804439, 0.5460139, -0.58821183, 0.5733938, -0.48303202, 0.33299083, -0.17573841, 0.0651772, -0.01488963, 0.001559834] + }), + CardboardV2: new CardboardViewer({ + id: 'CardboardV2', + label: 'Cardboard I/O 2015', + fov: 60, + interLensDistance: 0.064, + baselineLensDistance: 0.035, + screenLensDistance: 0.039, + distortionCoefficients: [0.34, 0.55], + inverseCoefficients: [-0.33836704, -0.18162185, 0.862655, -1.2462051, 1.0560602, -0.58208317, 0.21609078, -0.05444823, 0.009177956, -9.904169E-4, 6.183535E-5, -1.6981803E-6] + }) +}; +function DeviceInfo(deviceParams, additionalViewers) { + this.viewer = Viewers.CardboardV2; + this.updateDeviceParams(deviceParams); + this.distortion = new Distortion(this.viewer.distortionCoefficients); + for (var i = 0; i < additionalViewers.length; i++) { + var viewer = additionalViewers[i]; + Viewers[viewer.id] = new CardboardViewer(viewer); + } +} +DeviceInfo.prototype.updateDeviceParams = function (deviceParams) { + this.device = this.determineDevice_(deviceParams) || this.device; +}; +DeviceInfo.prototype.getDevice = function () { + return this.device; +}; +DeviceInfo.prototype.setViewer = function (viewer) { + this.viewer = viewer; + this.distortion = new Distortion(this.viewer.distortionCoefficients); +}; +DeviceInfo.prototype.determineDevice_ = function (deviceParams) { + if (!deviceParams) { + if (isIOS()) { + console.warn('Using fallback iOS device measurements.'); + return DEFAULT_IOS; + } else { + console.warn('Using fallback Android device measurements.'); + return DEFAULT_ANDROID; + } + } + var METERS_PER_INCH = 0.0254; + var metersPerPixelX = METERS_PER_INCH / deviceParams.xdpi; + var metersPerPixelY = METERS_PER_INCH / deviceParams.ydpi; + var width = getScreenWidth(); + var height = getScreenHeight(); + return new Device({ + widthMeters: metersPerPixelX * width, + heightMeters: metersPerPixelY * height, + bevelMeters: deviceParams.bevelMm * 0.001 + }); +}; +DeviceInfo.prototype.getDistortedFieldOfViewLeftEye = function () { + var viewer = this.viewer; + var device = this.device; + var distortion = this.distortion; + var eyeToScreenDistance = viewer.screenLensDistance; + var outerDist = (device.widthMeters - viewer.interLensDistance) / 2; + var innerDist = viewer.interLensDistance / 2; + var bottomDist = viewer.baselineLensDistance - device.bevelMeters; + var topDist = device.heightMeters - bottomDist; + var outerAngle = radToDeg * Math.atan(distortion.distort(outerDist / eyeToScreenDistance)); + var innerAngle = radToDeg * Math.atan(distortion.distort(innerDist / eyeToScreenDistance)); + var bottomAngle = radToDeg * Math.atan(distortion.distort(bottomDist / eyeToScreenDistance)); + var topAngle = radToDeg * Math.atan(distortion.distort(topDist / eyeToScreenDistance)); + return { + leftDegrees: Math.min(outerAngle, viewer.fov), + rightDegrees: Math.min(innerAngle, viewer.fov), + downDegrees: Math.min(bottomAngle, viewer.fov), + upDegrees: Math.min(topAngle, viewer.fov) + }; +}; +DeviceInfo.prototype.getLeftEyeVisibleTanAngles = function () { + var viewer = this.viewer; + var device = this.device; + var distortion = this.distortion; + var fovLeft = Math.tan(-degToRad * viewer.fov); + var fovTop = Math.tan(degToRad * viewer.fov); + var fovRight = Math.tan(degToRad * viewer.fov); + var fovBottom = Math.tan(-degToRad * viewer.fov); + var halfWidth = device.widthMeters / 4; + var halfHeight = device.heightMeters / 2; + var verticalLensOffset = viewer.baselineLensDistance - device.bevelMeters - halfHeight; + var centerX = viewer.interLensDistance / 2 - halfWidth; + var centerY = -verticalLensOffset; + var centerZ = viewer.screenLensDistance; + var screenLeft = distortion.distort((centerX - halfWidth) / centerZ); + var screenTop = distortion.distort((centerY + halfHeight) / centerZ); + var screenRight = distortion.distort((centerX + halfWidth) / centerZ); + var screenBottom = distortion.distort((centerY - halfHeight) / centerZ); + var result = new Float32Array(4); + result[0] = Math.max(fovLeft, screenLeft); + result[1] = Math.min(fovTop, screenTop); + result[2] = Math.min(fovRight, screenRight); + result[3] = Math.max(fovBottom, screenBottom); + return result; +}; +DeviceInfo.prototype.getLeftEyeNoLensTanAngles = function () { + var viewer = this.viewer; + var device = this.device; + var distortion = this.distortion; + var result = new Float32Array(4); + var fovLeft = distortion.distortInverse(Math.tan(-degToRad * viewer.fov)); + var fovTop = distortion.distortInverse(Math.tan(degToRad * viewer.fov)); + var fovRight = distortion.distortInverse(Math.tan(degToRad * viewer.fov)); + var fovBottom = distortion.distortInverse(Math.tan(-degToRad * viewer.fov)); + var halfWidth = device.widthMeters / 4; + var halfHeight = device.heightMeters / 2; + var verticalLensOffset = viewer.baselineLensDistance - device.bevelMeters - halfHeight; + var centerX = viewer.interLensDistance / 2 - halfWidth; + var centerY = -verticalLensOffset; + var centerZ = viewer.screenLensDistance; + var screenLeft = (centerX - halfWidth) / centerZ; + var screenTop = (centerY + halfHeight) / centerZ; + var screenRight = (centerX + halfWidth) / centerZ; + var screenBottom = (centerY - halfHeight) / centerZ; + result[0] = Math.max(fovLeft, screenLeft); + result[1] = Math.min(fovTop, screenTop); + result[2] = Math.min(fovRight, screenRight); + result[3] = Math.max(fovBottom, screenBottom); + return result; +}; +DeviceInfo.prototype.getLeftEyeVisibleScreenRect = function (undistortedFrustum) { + var viewer = this.viewer; + var device = this.device; + var dist = viewer.screenLensDistance; + var eyeX = (device.widthMeters - viewer.interLensDistance) / 2; + var eyeY = viewer.baselineLensDistance - device.bevelMeters; + var left = (undistortedFrustum[0] * dist + eyeX) / device.widthMeters; + var top = (undistortedFrustum[1] * dist + eyeY) / device.heightMeters; + var right = (undistortedFrustum[2] * dist + eyeX) / device.widthMeters; + var bottom = (undistortedFrustum[3] * dist + eyeY) / device.heightMeters; + return { + x: left, + y: bottom, + width: right - left, + height: top - bottom + }; +}; +DeviceInfo.prototype.getFieldOfViewLeftEye = function (opt_isUndistorted) { + return opt_isUndistorted ? this.getUndistortedFieldOfViewLeftEye() : this.getDistortedFieldOfViewLeftEye(); +}; +DeviceInfo.prototype.getFieldOfViewRightEye = function (opt_isUndistorted) { + var fov = this.getFieldOfViewLeftEye(opt_isUndistorted); + return { + leftDegrees: fov.rightDegrees, + rightDegrees: fov.leftDegrees, + upDegrees: fov.upDegrees, + downDegrees: fov.downDegrees + }; +}; +DeviceInfo.prototype.getUndistortedFieldOfViewLeftEye = function () { + var p = this.getUndistortedParams_(); + return { + leftDegrees: radToDeg * Math.atan(p.outerDist), + rightDegrees: radToDeg * Math.atan(p.innerDist), + downDegrees: radToDeg * Math.atan(p.bottomDist), + upDegrees: radToDeg * Math.atan(p.topDist) + }; +}; +DeviceInfo.prototype.getUndistortedViewportLeftEye = function () { + var p = this.getUndistortedParams_(); + var viewer = this.viewer; + var device = this.device; + var eyeToScreenDistance = viewer.screenLensDistance; + var screenWidth = device.widthMeters / eyeToScreenDistance; + var screenHeight = device.heightMeters / eyeToScreenDistance; + var xPxPerTanAngle = device.width / screenWidth; + var yPxPerTanAngle = device.height / screenHeight; + var x = Math.round((p.eyePosX - p.outerDist) * xPxPerTanAngle); + var y = Math.round((p.eyePosY - p.bottomDist) * yPxPerTanAngle); + return { + x: x, + y: y, + width: Math.round((p.eyePosX + p.innerDist) * xPxPerTanAngle) - x, + height: Math.round((p.eyePosY + p.topDist) * yPxPerTanAngle) - y + }; +}; +DeviceInfo.prototype.getUndistortedParams_ = function () { + var viewer = this.viewer; + var device = this.device; + var distortion = this.distortion; + var eyeToScreenDistance = viewer.screenLensDistance; + var halfLensDistance = viewer.interLensDistance / 2 / eyeToScreenDistance; + var screenWidth = device.widthMeters / eyeToScreenDistance; + var screenHeight = device.heightMeters / eyeToScreenDistance; + var eyePosX = screenWidth / 2 - halfLensDistance; + var eyePosY = (viewer.baselineLensDistance - device.bevelMeters) / eyeToScreenDistance; + var maxFov = viewer.fov; + var viewerMax = distortion.distortInverse(Math.tan(degToRad * maxFov)); + var outerDist = Math.min(eyePosX, viewerMax); + var innerDist = Math.min(halfLensDistance, viewerMax); + var bottomDist = Math.min(eyePosY, viewerMax); + var topDist = Math.min(screenHeight - eyePosY, viewerMax); + return { + outerDist: outerDist, + innerDist: innerDist, + topDist: topDist, + bottomDist: bottomDist, + eyePosX: eyePosX, + eyePosY: eyePosY + }; +}; +function CardboardViewer(params) { + this.id = params.id; + this.label = params.label; + this.fov = params.fov; + this.interLensDistance = params.interLensDistance; + this.baselineLensDistance = params.baselineLensDistance; + this.screenLensDistance = params.screenLensDistance; + this.distortionCoefficients = params.distortionCoefficients; + this.inverseCoefficients = params.inverseCoefficients; +} +DeviceInfo.Viewers = Viewers; +var format = 1; +var last_updated = "2018-12-10T17:01:42Z"; +var devices = [{"type":"android","rules":[{"mdmh":"asus/*/Nexus 7/*"},{"ua":"Nexus 7"}],"dpi":[320.8,323],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"asus/*/ASUS_Z00AD/*"},{"ua":"ASUS_Z00AD"}],"dpi":[403,404.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 2 XL/*"},{"ua":"Pixel 2 XL"}],"dpi":537.9,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 3 XL/*"},{"ua":"Pixel 3 XL"}],"dpi":[558.5,553.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel XL/*"},{"ua":"Pixel XL"}],"dpi":[537.9,533],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 3/*"},{"ua":"Pixel 3"}],"dpi":442.4,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 2/*"},{"ua":"Pixel 2"}],"dpi":441,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Google/*/Pixel/*"},{"ua":"Pixel"}],"dpi":[432.6,436.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC6435LVW/*"},{"ua":"HTC6435LVW"}],"dpi":[449.7,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One XL/*"},{"ua":"HTC One XL"}],"dpi":[315.3,314.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"htc/*/Nexus 9/*"},{"ua":"Nexus 9"}],"dpi":289,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One M9/*"},{"ua":"HTC One M9"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One_M8/*"},{"ua":"HTC One_M8"}],"dpi":[449.7,447.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One/*"},{"ua":"HTC One"}],"dpi":472.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Huawei/*/Nexus 6P/*"},{"ua":"Nexus 6P"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Huawei/*/BLN-L24/*"},{"ua":"HONORBLN-L24"}],"dpi":480,"bw":4,"ac":500},{"type":"android","rules":[{"mdmh":"Huawei/*/BKL-L09/*"},{"ua":"BKL-L09"}],"dpi":403,"bw":3.47,"ac":500},{"type":"android","rules":[{"mdmh":"LENOVO/*/Lenovo PB2-690Y/*"},{"ua":"Lenovo PB2-690Y"}],"dpi":[457.2,454.713],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5X/*"},{"ua":"Nexus 5X"}],"dpi":[422,419.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS345/*"},{"ua":"LGMS345"}],"dpi":[221.7,219.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D800/*"},{"ua":"LG-D800"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D850/*"},{"ua":"LG-D850"}],"dpi":[537.9,541.9],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/VS985 4G/*"},{"ua":"VS985 4G"}],"dpi":[537.9,535.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5/*"},{"ua":"Nexus 5 B"}],"dpi":[442.4,444.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 4/*"},{"ua":"Nexus 4"}],"dpi":[319.8,318.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LG-P769/*"},{"ua":"LG-P769"}],"dpi":[240.6,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS323/*"},{"ua":"LGMS323"}],"dpi":[206.6,204.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGLS996/*"},{"ua":"LGLS996"}],"dpi":[403.4,401.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/4560MMX/*"},{"ua":"4560MMX"}],"dpi":[240,219.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/A250/*"},{"ua":"Micromax A250"}],"dpi":[480,446.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/Micromax AQ4501/*"},{"ua":"Micromax AQ4501"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/G5/*"},{"ua":"Moto G (5) Plus"}],"dpi":[403.4,403],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/DROID RAZR/*"},{"ua":"DROID RAZR"}],"dpi":[368.1,256.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT830C/*"},{"ua":"XT830C"}],"dpi":[254,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1021/*"},{"ua":"XT1021"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1023/*"},{"ua":"XT1023"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1028/*"},{"ua":"XT1028"}],"dpi":[326.6,327.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1034/*"},{"ua":"XT1034"}],"dpi":[326.6,328.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1053/*"},{"ua":"XT1053"}],"dpi":[315.3,316.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1562/*"},{"ua":"XT1562"}],"dpi":[403.4,402.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/Nexus 6/*"},{"ua":"Nexus 6 B"}],"dpi":[494.3,489.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1063/*"},{"ua":"XT1063"}],"dpi":[295,296.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1064/*"},{"ua":"XT1064"}],"dpi":[295,295.6],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1092/*"},{"ua":"XT1092"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1095/*"},{"ua":"XT1095"}],"dpi":[422,423.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/G4/*"},{"ua":"Moto G (4)"}],"dpi":401,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/A0001/*"},{"ua":"A0001"}],"dpi":[403.4,401],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE E1005/*"},{"ua":"ONE E1005"}],"dpi":[442.4,441.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A2005/*"},{"ua":"ONE A2005"}],"dpi":[391.9,405.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONEPLUS A5000/*"},{"ua":"ONEPLUS A5000 "}],"dpi":[403.411,399.737],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A5010/*"},{"ua":"ONEPLUS A5010"}],"dpi":[403,400],"bw":2,"ac":1000},{"type":"android","rules":[{"mdmh":"OPPO/*/X909/*"},{"ua":"X909"}],"dpi":[442.4,444.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9082/*"},{"ua":"GT-I9082"}],"dpi":[184.7,185.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G360P/*"},{"ua":"SM-G360P"}],"dpi":[196.7,205.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Nexus S/*"},{"ua":"Nexus S"}],"dpi":[234.5,229.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[304.8,303.9],"bw":5,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T230NU/*"},{"ua":"SM-T230NU"}],"dpi":216,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-T399/*"},{"ua":"SGH-T399"}],"dpi":[217.7,231.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-M919/*"},{"ua":"SGH-M919"}],"dpi":[440.8,437.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N9005/*"},{"ua":"SM-N9005"}],"dpi":[386.4,387],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SM-N900A/*"},{"ua":"SAMSUNG-SM-N900A"}],"dpi":[386.4,387.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9500/*"},{"ua":"GT-I9500"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9505/*"},{"ua":"GT-I9505"}],"dpi":439.4,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900F/*"},{"ua":"SM-G900F"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900M/*"},{"ua":"SM-G900M"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G800F/*"},{"ua":"SM-G800F"}],"dpi":326.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G906S/*"},{"ua":"SM-G906S"}],"dpi":[562.7,572.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[306.7,304.8],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T535/*"},{"ua":"SM-T535"}],"dpi":[142.6,136.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920C/*"},{"ua":"SM-N920C"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920P/*"},{"ua":"SM-N920P"}],"dpi":[386.3655,390.144],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920W8/*"},{"ua":"SM-N920W8"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300I/*"},{"ua":"GT-I9300I"}],"dpi":[304.8,305.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9195/*"},{"ua":"GT-I9195"}],"dpi":[249.4,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-L520/*"},{"ua":"SPH-L520"}],"dpi":[249.4,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SGH-I717/*"},{"ua":"SAMSUNG-SGH-I717"}],"dpi":285.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-D710/*"},{"ua":"SPH-D710"}],"dpi":[217.7,204.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-N7100/*"},{"ua":"GT-N7100"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SCH-I605/*"},{"ua":"SCH-I605"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Galaxy Nexus/*"},{"ua":"Galaxy Nexus"}],"dpi":[315.3,314.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910H/*"},{"ua":"SM-N910H"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910C/*"},{"ua":"SM-N910C"}],"dpi":[515.2,520.2],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G130M/*"},{"ua":"SM-G130M"}],"dpi":[165.9,164.8],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G928I/*"},{"ua":"SM-G928I"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920F/*"},{"ua":"SM-G920F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920P/*"},{"ua":"SM-G920P"}],"dpi":[522.5,577],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925F/*"},{"ua":"SM-G925F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925V/*"},{"ua":"SM-G925V"}],"dpi":[522.5,576.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G930F/*"},{"ua":"SM-G930F"}],"dpi":576.6,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G935F/*"},{"ua":"SM-G935F"}],"dpi":533,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G950F/*"},{"ua":"SM-G950F"}],"dpi":[562.707,565.293],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G955U/*"},{"ua":"SM-G955U"}],"dpi":[522.514,525.762],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G955F/*"},{"ua":"SM-G955F"}],"dpi":[522.514,525.762],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/C6903/*"},{"ua":"C6903"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/D6653/*"},{"ua":"D6653"}],"dpi":[428.6,427.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6653/*"},{"ua":"E6653"}],"dpi":[428.6,425.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6853/*"},{"ua":"E6853"}],"dpi":[403.4,401.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/SGP321/*"},{"ua":"SGP321"}],"dpi":[224.7,224.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"TCT/*/ALCATEL ONE TOUCH Fierce/*"},{"ua":"ALCATEL ONE TOUCH Fierce"}],"dpi":[240,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"THL/*/thl 5000/*"},{"ua":"thl 5000"}],"dpi":[480,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Fly/*/IQ4412/*"},{"ua":"IQ4412"}],"dpi":307.9,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"ZTE/*/ZTE Blade L2/*"},{"ua":"ZTE Blade L2"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"BENEVE/*/VR518/*"},{"ua":"VR518"}],"dpi":480,"bw":3,"ac":500},{"type":"ios","rules":[{"res":[640,960]}],"dpi":[325.1,328.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[640,1136]}],"dpi":[317.1,320.2],"bw":3,"ac":1000},{"type":"ios","rules":[{"res":[750,1334]}],"dpi":326.4,"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1242,2208]}],"dpi":[453.6,458.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2001]}],"dpi":[410.9,415.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2436]}],"dpi":458,"bw":4,"ac":1000}]; +var DPDB_CACHE = { + format: format, + last_updated: last_updated, + devices: devices +}; +function Dpdb(url, onDeviceParamsUpdated) { + this.dpdb = DPDB_CACHE; + this.recalculateDeviceParams_(); + if (url) { + this.onDeviceParamsUpdated = onDeviceParamsUpdated; + var xhr = new XMLHttpRequest(); + var obj = this; + xhr.open('GET', url, true); + xhr.addEventListener('load', function () { + obj.loading = false; + if (xhr.status >= 200 && xhr.status <= 299) { + obj.dpdb = JSON.parse(xhr.response); + obj.recalculateDeviceParams_(); + } else { + console.error('Error loading online DPDB!'); + } + }); + xhr.send(); + } +} +Dpdb.prototype.getDeviceParams = function () { + return this.deviceParams; +}; +Dpdb.prototype.recalculateDeviceParams_ = function () { + var newDeviceParams = this.calcDeviceParams_(); + if (newDeviceParams) { + this.deviceParams = newDeviceParams; + if (this.onDeviceParamsUpdated) { + this.onDeviceParamsUpdated(this.deviceParams); + } + } else { + console.error('Failed to recalculate device parameters.'); + } +}; +Dpdb.prototype.calcDeviceParams_ = function () { + var db = this.dpdb; + if (!db) { + console.error('DPDB not available.'); + return null; + } + if (db.format != 1) { + console.error('DPDB has unexpected format version.'); + return null; + } + if (!db.devices || !db.devices.length) { + console.error('DPDB does not have a devices section.'); + return null; + } + var userAgent = navigator.userAgent || navigator.vendor || window.opera; + var width = getScreenWidth(); + var height = getScreenHeight(); + if (!db.devices) { + console.error('DPDB has no devices section.'); + return null; + } + for (var i = 0; i < db.devices.length; i++) { + var device = db.devices[i]; + if (!device.rules) { + console.warn('Device[' + i + '] has no rules section.'); + continue; + } + if (device.type != 'ios' && device.type != 'android') { + console.warn('Device[' + i + '] has invalid type.'); + continue; + } + if (isIOS() != (device.type == 'ios')) continue; + var matched = false; + for (var j = 0; j < device.rules.length; j++) { + var rule = device.rules[j]; + if (this.ruleMatches_(rule, userAgent, width, height)) { + matched = true; + break; + } + } + if (!matched) continue; + var xdpi = device.dpi[0] || device.dpi; + var ydpi = device.dpi[1] || device.dpi; + return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw }); + } + console.warn('No DPDB device match.'); + return null; +}; +Dpdb.prototype.ruleMatches_ = function (rule, ua, screenWidth, screenHeight) { + if (!rule.ua && !rule.res) return false; + if (rule.ua && rule.ua.substring(0, 2) === 'SM') rule.ua = rule.ua.substring(0, 7); + if (rule.ua && ua.indexOf(rule.ua) < 0) return false; + if (rule.res) { + if (!rule.res[0] || !rule.res[1]) return false; + var resX = rule.res[0]; + var resY = rule.res[1]; + if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) || Math.max(screenWidth, screenHeight) != Math.max(resX, resY)) { + return false; + } + } + return true; +}; +function DeviceParams(params) { + this.xdpi = params.xdpi; + this.ydpi = params.ydpi; + this.bevelMm = params.bevelMm; +} +function SensorSample(sample, timestampS) { + this.set(sample, timestampS); +} +SensorSample.prototype.set = function (sample, timestampS) { + this.sample = sample; + this.timestampS = timestampS; +}; +SensorSample.prototype.copy = function (sensorSample) { + this.set(sensorSample.sample, sensorSample.timestampS); +}; +function ComplementaryFilter(kFilter, isDebug) { + this.kFilter = kFilter; + this.isDebug = isDebug; + this.currentAccelMeasurement = new SensorSample(); + this.currentGyroMeasurement = new SensorSample(); + this.previousGyroMeasurement = new SensorSample(); + if (isIOS()) { + this.filterQ = new Quaternion(-1, 0, 0, 1); + } else { + this.filterQ = new Quaternion(1, 0, 0, 1); + } + this.previousFilterQ = new Quaternion(); + this.previousFilterQ.copy(this.filterQ); + this.accelQ = new Quaternion(); + this.isOrientationInitialized = false; + this.estimatedGravity = new Vector3(); + this.measuredGravity = new Vector3(); + this.gyroIntegralQ = new Quaternion(); +} +ComplementaryFilter.prototype.addAccelMeasurement = function (vector, timestampS) { + this.currentAccelMeasurement.set(vector, timestampS); +}; +ComplementaryFilter.prototype.addGyroMeasurement = function (vector, timestampS) { + this.currentGyroMeasurement.set(vector, timestampS); + var deltaT = timestampS - this.previousGyroMeasurement.timestampS; + if (isTimestampDeltaValid(deltaT)) { + this.run_(); + } + this.previousGyroMeasurement.copy(this.currentGyroMeasurement); +}; +ComplementaryFilter.prototype.run_ = function () { + if (!this.isOrientationInitialized) { + this.accelQ = this.accelToQuaternion_(this.currentAccelMeasurement.sample); + this.previousFilterQ.copy(this.accelQ); + this.isOrientationInitialized = true; + return; + } + var deltaT = this.currentGyroMeasurement.timestampS - this.previousGyroMeasurement.timestampS; + var gyroDeltaQ = this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample, deltaT); + this.gyroIntegralQ.multiply(gyroDeltaQ); + this.filterQ.copy(this.previousFilterQ); + this.filterQ.multiply(gyroDeltaQ); + var invFilterQ = new Quaternion(); + invFilterQ.copy(this.filterQ); + invFilterQ.inverse(); + this.estimatedGravity.set(0, 0, -1); + this.estimatedGravity.applyQuaternion(invFilterQ); + this.estimatedGravity.normalize(); + this.measuredGravity.copy(this.currentAccelMeasurement.sample); + this.measuredGravity.normalize(); + var deltaQ = new Quaternion(); + deltaQ.setFromUnitVectors(this.estimatedGravity, this.measuredGravity); + deltaQ.inverse(); + if (this.isDebug) { + console.log('Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)', radToDeg * getQuaternionAngle(deltaQ), this.estimatedGravity.x.toFixed(1), this.estimatedGravity.y.toFixed(1), this.estimatedGravity.z.toFixed(1), this.measuredGravity.x.toFixed(1), this.measuredGravity.y.toFixed(1), this.measuredGravity.z.toFixed(1)); + } + var targetQ = new Quaternion(); + targetQ.copy(this.filterQ); + targetQ.multiply(deltaQ); + this.filterQ.slerp(targetQ, 1 - this.kFilter); + this.previousFilterQ.copy(this.filterQ); +}; +ComplementaryFilter.prototype.getOrientation = function () { + return this.filterQ; +}; +ComplementaryFilter.prototype.accelToQuaternion_ = function (accel) { + var normAccel = new Vector3(); + normAccel.copy(accel); + normAccel.normalize(); + var quat = new Quaternion(); + quat.setFromUnitVectors(new Vector3(0, 0, -1), normAccel); + quat.inverse(); + return quat; +}; +ComplementaryFilter.prototype.gyroToQuaternionDelta_ = function (gyro, dt) { + var quat = new Quaternion(); + var axis = new Vector3(); + axis.copy(gyro); + axis.normalize(); + quat.setFromAxisAngle(axis, gyro.length() * dt); + return quat; +}; +function PosePredictor(predictionTimeS, isDebug) { + this.predictionTimeS = predictionTimeS; + this.isDebug = isDebug; + this.previousQ = new Quaternion(); + this.previousTimestampS = null; + this.deltaQ = new Quaternion(); + this.outQ = new Quaternion(); +} +PosePredictor.prototype.getPrediction = function (currentQ, gyro, timestampS) { + if (!this.previousTimestampS) { + this.previousQ.copy(currentQ); + this.previousTimestampS = timestampS; + return currentQ; + } + var axis = new Vector3(); + axis.copy(gyro); + axis.normalize(); + var angularSpeed = gyro.length(); + if (angularSpeed < degToRad * 20) { + if (this.isDebug) { + console.log('Moving slowly, at %s deg/s: no prediction', (radToDeg * angularSpeed).toFixed(1)); + } + this.outQ.copy(currentQ); + this.previousQ.copy(currentQ); + return this.outQ; + } + var predictAngle = angularSpeed * this.predictionTimeS; + this.deltaQ.setFromAxisAngle(axis, predictAngle); + this.outQ.copy(this.previousQ); + this.outQ.multiply(this.deltaQ); + this.previousQ.copy(currentQ); + this.previousTimestampS = timestampS; + return this.outQ; +}; +function FusionPoseSensor(kFilter, predictionTime, yawOnly, isDebug) { + this.yawOnly = yawOnly; + this.accelerometer = new Vector3(); + this.gyroscope = new Vector3(); + this.filter = new ComplementaryFilter(kFilter, isDebug); + this.posePredictor = new PosePredictor(predictionTime, isDebug); + this.isFirefoxAndroid = isFirefoxAndroid(); + this.isIOS = isIOS(); + var chromeVersion = getChromeVersion(); + this.isDeviceMotionInRadians = !this.isIOS && chromeVersion && chromeVersion < 66; + this.isWithoutDeviceMotion = isChromeWithoutDeviceMotion(); + this.filterToWorldQ = new Quaternion(); + if (isIOS()) { + this.filterToWorldQ.setFromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); + } else { + this.filterToWorldQ.setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2); + } + this.inverseWorldToScreenQ = new Quaternion(); + this.worldToScreenQ = new Quaternion(); + this.originalPoseAdjustQ = new Quaternion(); + this.originalPoseAdjustQ.setFromAxisAngle(new Vector3(0, 0, 1), -window.orientation * Math.PI / 180); + this.setScreenTransform_(); + if (isLandscapeMode()) { + this.filterToWorldQ.multiply(this.inverseWorldToScreenQ); + } + this.resetQ = new Quaternion(); + this.orientationOut_ = new Float32Array(4); + this.start(); +} +FusionPoseSensor.prototype.getPosition = function () { + return null; +}; +FusionPoseSensor.prototype.getOrientation = function () { + var orientation = void 0; + if (this.isWithoutDeviceMotion && this._deviceOrientationQ) { + this.deviceOrientationFixQ = this.deviceOrientationFixQ || function () { + var z = new Quaternion().setFromAxisAngle(new Vector3(0, 0, -1), 0); + var y = new Quaternion(); + if (window.orientation === -90) { + y.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / -2); + } else { + y.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / 2); + } + return z.multiply(y); + }(); + this.deviceOrientationFilterToWorldQ = this.deviceOrientationFilterToWorldQ || function () { + var q = new Quaternion(); + q.setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2); + return q; + }(); + orientation = this._deviceOrientationQ; + var out = new Quaternion(); + out.copy(orientation); + out.multiply(this.deviceOrientationFilterToWorldQ); + out.multiply(this.resetQ); + out.multiply(this.worldToScreenQ); + out.multiplyQuaternions(this.deviceOrientationFixQ, out); + if (this.yawOnly) { + out.x = 0; + out.z = 0; + out.normalize(); + } + this.orientationOut_[0] = out.x; + this.orientationOut_[1] = out.y; + this.orientationOut_[2] = out.z; + this.orientationOut_[3] = out.w; + return this.orientationOut_; + } else { + var filterOrientation = this.filter.getOrientation(); + orientation = this.posePredictor.getPrediction(filterOrientation, this.gyroscope, this.previousTimestampS); + } + var out = new Quaternion(); + out.copy(this.filterToWorldQ); + out.multiply(this.resetQ); + out.multiply(orientation); + out.multiply(this.worldToScreenQ); + if (this.yawOnly) { + out.x = 0; + out.z = 0; + out.normalize(); + } + this.orientationOut_[0] = out.x; + this.orientationOut_[1] = out.y; + this.orientationOut_[2] = out.z; + this.orientationOut_[3] = out.w; + return this.orientationOut_; +}; +FusionPoseSensor.prototype.resetPose = function () { + this.resetQ.copy(this.filter.getOrientation()); + this.resetQ.x = 0; + this.resetQ.y = 0; + this.resetQ.z *= -1; + this.resetQ.normalize(); + if (isLandscapeMode()) { + this.resetQ.multiply(this.inverseWorldToScreenQ); + } + this.resetQ.multiply(this.originalPoseAdjustQ); +}; +FusionPoseSensor.prototype.onDeviceOrientation_ = function (e) { + this._deviceOrientationQ = this._deviceOrientationQ || new Quaternion(); + var alpha = e.alpha, + beta = e.beta, + gamma = e.gamma; + alpha = (alpha || 0) * Math.PI / 180; + beta = (beta || 0) * Math.PI / 180; + gamma = (gamma || 0) * Math.PI / 180; + this._deviceOrientationQ.setFromEulerYXZ(beta, alpha, -gamma); +}; +FusionPoseSensor.prototype.onDeviceMotion_ = function (deviceMotion) { + this.updateDeviceMotion_(deviceMotion); +}; +FusionPoseSensor.prototype.updateDeviceMotion_ = function (deviceMotion) { + var accGravity = deviceMotion.accelerationIncludingGravity; + var rotRate = deviceMotion.rotationRate; + var timestampS = deviceMotion.timeStamp / 1000; + var deltaS = timestampS - this.previousTimestampS; + if (deltaS < 0) { + warnOnce('fusion-pose-sensor:invalid:non-monotonic', 'Invalid timestamps detected: non-monotonic timestamp from devicemotion'); + this.previousTimestampS = timestampS; + return; + } else if (deltaS <= MIN_TIMESTEP || deltaS > MAX_TIMESTEP) { + warnOnce('fusion-pose-sensor:invalid:outside-threshold', 'Invalid timestamps detected: Timestamp from devicemotion outside expected range.'); + this.previousTimestampS = timestampS; + return; + } + this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z); + if (isR7()) { + this.gyroscope.set(-rotRate.beta, rotRate.alpha, rotRate.gamma); + } else { + this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma); + } + if (!this.isDeviceMotionInRadians) { + this.gyroscope.multiplyScalar(Math.PI / 180); + } + this.filter.addAccelMeasurement(this.accelerometer, timestampS); + this.filter.addGyroMeasurement(this.gyroscope, timestampS); + this.previousTimestampS = timestampS; +}; +FusionPoseSensor.prototype.onOrientationChange_ = function (screenOrientation) { + this.setScreenTransform_(); +}; +FusionPoseSensor.prototype.onMessage_ = function (event) { + var message = event.data; + if (!message || !message.type) { + return; + } + var type = message.type.toLowerCase(); + if (type !== 'devicemotion') { + return; + } + this.updateDeviceMotion_(message.deviceMotionEvent); +}; +FusionPoseSensor.prototype.setScreenTransform_ = function () { + this.worldToScreenQ.set(0, 0, 0, 1); + switch (window.orientation) { + case 0: + break; + case 90: + this.worldToScreenQ.setFromAxisAngle(new Vector3(0, 0, 1), -Math.PI / 2); + break; + case -90: + this.worldToScreenQ.setFromAxisAngle(new Vector3(0, 0, 1), Math.PI / 2); + break; + case 180: + break; + } + this.inverseWorldToScreenQ.copy(this.worldToScreenQ); + this.inverseWorldToScreenQ.inverse(); +}; +FusionPoseSensor.prototype.start = function () { + this.onDeviceMotionCallback_ = this.onDeviceMotion_.bind(this); + this.onOrientationChangeCallback_ = this.onOrientationChange_.bind(this); + this.onMessageCallback_ = this.onMessage_.bind(this); + this.onDeviceOrientationCallback_ = this.onDeviceOrientation_.bind(this); + if (isIOS() && isInsideCrossOriginIFrame()) { + window.addEventListener('message', this.onMessageCallback_); + } + window.addEventListener('orientationchange', this.onOrientationChangeCallback_); + if (this.isWithoutDeviceMotion) { + window.addEventListener('deviceorientation', this.onDeviceOrientationCallback_); + } else { + window.addEventListener('devicemotion', this.onDeviceMotionCallback_); + } +}; +FusionPoseSensor.prototype.stop = function () { + window.removeEventListener('devicemotion', this.onDeviceMotionCallback_); + window.removeEventListener('deviceorientation', this.onDeviceOrientationCallback_); + window.removeEventListener('orientationchange', this.onOrientationChangeCallback_); + window.removeEventListener('message', this.onMessageCallback_); +}; +var SENSOR_FREQUENCY = 60; +var X_AXIS = new Vector3(1, 0, 0); +var Z_AXIS = new Vector3(0, 0, 1); +var SENSOR_TO_VR = new Quaternion(); +SENSOR_TO_VR.setFromAxisAngle(X_AXIS, -Math.PI / 2); +SENSOR_TO_VR.multiply(new Quaternion().setFromAxisAngle(Z_AXIS, Math.PI / 2)); +var PoseSensor = function () { + function PoseSensor(config) { + classCallCheck(this, PoseSensor); + this.config = config; + this.sensor = null; + this.fusionSensor = null; + this._out = new Float32Array(4); + this.api = null; + this.errors = []; + this._sensorQ = new Quaternion(); + this._outQ = new Quaternion(); + this._onSensorRead = this._onSensorRead.bind(this); + this._onSensorError = this._onSensorError.bind(this); + this.init(); + } + createClass(PoseSensor, [{ + key: 'init', + value: function init() { + var sensor = null; + try { + sensor = new RelativeOrientationSensor({ + frequency: SENSOR_FREQUENCY, + referenceFrame: 'screen' + }); + sensor.addEventListener('error', this._onSensorError); + } catch (error) { + this.errors.push(error); + if (error.name === 'SecurityError') { + console.error('Cannot construct sensors due to the Feature Policy'); + console.warn('Attempting to fall back using "devicemotion"; however this will ' + 'fail in the future without correct permissions.'); + this.useDeviceMotion(); + } else if (error.name === 'ReferenceError') { + this.useDeviceMotion(); + } else { + console.error(error); + } + } + if (sensor) { + this.api = 'sensor'; + this.sensor = sensor; + this.sensor.addEventListener('reading', this._onSensorRead); + this.sensor.start(); + } + } + }, { + key: 'useDeviceMotion', + value: function useDeviceMotion() { + this.api = 'devicemotion'; + this.fusionSensor = new FusionPoseSensor(this.config.K_FILTER, this.config.PREDICTION_TIME_S, this.config.YAW_ONLY, this.config.DEBUG); + if (this.sensor) { + this.sensor.removeEventListener('reading', this._onSensorRead); + this.sensor.removeEventListener('error', this._onSensorError); + this.sensor = null; + } + } + }, { + key: 'getOrientation', + value: function getOrientation() { + if (this.fusionSensor) { + return this.fusionSensor.getOrientation(); + } + if (!this.sensor || !this.sensor.quaternion) { + this._out[0] = this._out[1] = this._out[2] = 0; + this._out[3] = 1; + return this._out; + } + var q = this.sensor.quaternion; + this._sensorQ.set(q[0], q[1], q[2], q[3]); + var out = this._outQ; + out.copy(SENSOR_TO_VR); + out.multiply(this._sensorQ); + if (this.config.YAW_ONLY) { + out.x = out.z = 0; + out.normalize(); + } + this._out[0] = out.x; + this._out[1] = out.y; + this._out[2] = out.z; + this._out[3] = out.w; + return this._out; + } + }, { + key: '_onSensorError', + value: function _onSensorError(event) { + this.errors.push(event.error); + if (event.error.name === 'NotAllowedError') { + console.error('Permission to access sensor was denied'); + } else if (event.error.name === 'NotReadableError') { + console.error('Sensor could not be read'); + } else { + console.error(event.error); + } + this.useDeviceMotion(); + } + }, { + key: '_onSensorRead', + value: function _onSensorRead() {} + }]); + return PoseSensor; +}(); +var rotateInstructionsAsset = ""; +function RotateInstructions() { + this.loadIcon_(); + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.top = 0; + s.right = 0; + s.bottom = 0; + s.left = 0; + s.backgroundColor = 'gray'; + s.fontFamily = 'sans-serif'; + s.zIndex = 1000000; + var img = document.createElement('img'); + img.src = this.icon; + var s = img.style; + s.marginLeft = '25%'; + s.marginTop = '25%'; + s.width = '50%'; + overlay.appendChild(img); + var text = document.createElement('div'); + var s = text.style; + s.textAlign = 'center'; + s.fontSize = '16px'; + s.lineHeight = '24px'; + s.margin = '24px 25%'; + s.width = '50%'; + text.innerHTML = 'Place your phone into your Cardboard viewer.'; + overlay.appendChild(text); + var snackbar = document.createElement('div'); + var s = snackbar.style; + s.backgroundColor = '#CFD8DC'; + s.position = 'fixed'; + s.bottom = 0; + s.width = '100%'; + s.height = '48px'; + s.padding = '14px 24px'; + s.boxSizing = 'border-box'; + s.color = '#656A6B'; + overlay.appendChild(snackbar); + var snackbarText = document.createElement('div'); + snackbarText.style.float = 'left'; + snackbarText.innerHTML = 'No Cardboard viewer?'; + var snackbarButton = document.createElement('a'); + snackbarButton.href = 'https://www.google.com/get/cardboard/get-cardboard/'; + snackbarButton.innerHTML = 'get one'; + snackbarButton.target = '_blank'; + var s = snackbarButton.style; + s.float = 'right'; + s.fontWeight = 600; + s.textTransform = 'uppercase'; + s.borderLeft = '1px solid gray'; + s.paddingLeft = '24px'; + s.textDecoration = 'none'; + s.color = '#656A6B'; + snackbar.appendChild(snackbarText); + snackbar.appendChild(snackbarButton); + this.overlay = overlay; + this.text = text; + this.hide(); +} +RotateInstructions.prototype.show = function (parent) { + if (!parent && !this.overlay.parentElement) { + document.body.appendChild(this.overlay); + } else if (parent) { + if (this.overlay.parentElement && this.overlay.parentElement != parent) this.overlay.parentElement.removeChild(this.overlay); + parent.appendChild(this.overlay); + } + this.overlay.style.display = 'block'; + var img = this.overlay.querySelector('img'); + var s = img.style; + if (isLandscapeMode()) { + s.width = '20%'; + s.marginLeft = '40%'; + s.marginTop = '3%'; + } else { + s.width = '50%'; + s.marginLeft = '25%'; + s.marginTop = '25%'; + } +}; +RotateInstructions.prototype.hide = function () { + this.overlay.style.display = 'none'; +}; +RotateInstructions.prototype.showTemporarily = function (ms, parent) { + this.show(parent); + this.timer = setTimeout(this.hide.bind(this), ms); +}; +RotateInstructions.prototype.disableShowTemporarily = function () { + clearTimeout(this.timer); +}; +RotateInstructions.prototype.update = function () { + this.disableShowTemporarily(); + if (!isLandscapeMode() && isMobile()) { + this.show(); + } else { + this.hide(); + } +}; +RotateInstructions.prototype.loadIcon_ = function () { + this.icon = dataUri('image/svg+xml', rotateInstructionsAsset); +}; +var DEFAULT_VIEWER = 'CardboardV1'; +var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER'; +var CLASS_NAME = 'webvr-polyfill-viewer-selector'; +function ViewerSelector(defaultViewer) { + try { + this.selectedKey = localStorage.getItem(VIEWER_KEY); + } catch (error) { + console.error('Failed to load viewer profile: %s', error); + } + if (!this.selectedKey) { + this.selectedKey = defaultViewer || DEFAULT_VIEWER; + } + this.dialog = this.createDialog_(DeviceInfo.Viewers); + this.root = null; + this.onChangeCallbacks_ = []; +} +ViewerSelector.prototype.show = function (root) { + this.root = root; + root.appendChild(this.dialog); + var selected = this.dialog.querySelector('#' + this.selectedKey); + selected.checked = true; + this.dialog.style.display = 'block'; +}; +ViewerSelector.prototype.hide = function () { + if (this.root && this.root.contains(this.dialog)) { + this.root.removeChild(this.dialog); + } + this.dialog.style.display = 'none'; +}; +ViewerSelector.prototype.getCurrentViewer = function () { + return DeviceInfo.Viewers[this.selectedKey]; +}; +ViewerSelector.prototype.getSelectedKey_ = function () { + var input = this.dialog.querySelector('input[name=field]:checked'); + if (input) { + return input.id; + } + return null; +}; +ViewerSelector.prototype.onChange = function (cb) { + this.onChangeCallbacks_.push(cb); +}; +ViewerSelector.prototype.fireOnChange_ = function (viewer) { + for (var i = 0; i < this.onChangeCallbacks_.length; i++) { + this.onChangeCallbacks_[i](viewer); + } +}; +ViewerSelector.prototype.onSave_ = function () { + this.selectedKey = this.getSelectedKey_(); + if (!this.selectedKey || !DeviceInfo.Viewers[this.selectedKey]) { + console.error('ViewerSelector.onSave_: this should never happen!'); + return; + } + this.fireOnChange_(DeviceInfo.Viewers[this.selectedKey]); + try { + localStorage.setItem(VIEWER_KEY, this.selectedKey); + } catch (error) { + console.error('Failed to save viewer profile: %s', error); + } + this.hide(); +}; +ViewerSelector.prototype.createDialog_ = function (options) { + var container = document.createElement('div'); + container.classList.add(CLASS_NAME); + container.style.display = 'none'; + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.left = 0; + s.top = 0; + s.width = '100%'; + s.height = '100%'; + s.background = 'rgba(0, 0, 0, 0.3)'; + overlay.addEventListener('click', this.hide.bind(this)); + var width = 280; + var dialog = document.createElement('div'); + var s = dialog.style; + s.boxSizing = 'border-box'; + s.position = 'fixed'; + s.top = '24px'; + s.left = '50%'; + s.marginLeft = -width / 2 + 'px'; + s.width = width + 'px'; + s.padding = '24px'; + s.overflow = 'hidden'; + s.background = '#fafafa'; + s.fontFamily = "'Roboto', sans-serif"; + s.boxShadow = '0px 5px 20px #666'; + dialog.appendChild(this.createH1_('Select your viewer')); + for (var id in options) { + dialog.appendChild(this.createChoice_(id, options[id].label)); + } + dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this))); + container.appendChild(overlay); + container.appendChild(dialog); + return container; +}; +ViewerSelector.prototype.createH1_ = function (name) { + var h1 = document.createElement('h1'); + var s = h1.style; + s.color = 'black'; + s.fontSize = '20px'; + s.fontWeight = 'bold'; + s.marginTop = 0; + s.marginBottom = '24px'; + h1.innerHTML = name; + return h1; +}; +ViewerSelector.prototype.createChoice_ = function (id, name) { + var div = document.createElement('div'); + div.style.marginTop = '8px'; + div.style.color = 'black'; + var input = document.createElement('input'); + input.style.fontSize = '30px'; + input.setAttribute('id', id); + input.setAttribute('type', 'radio'); + input.setAttribute('value', id); + input.setAttribute('name', 'field'); + var label = document.createElement('label'); + label.style.marginLeft = '4px'; + label.setAttribute('for', id); + label.innerHTML = name; + div.appendChild(input); + div.appendChild(label); + return div; +}; +ViewerSelector.prototype.createButton_ = function (label, onclick) { + var button = document.createElement('button'); + button.innerHTML = label; + var s = button.style; + s.float = 'right'; + s.textTransform = 'uppercase'; + s.color = '#1094f7'; + s.fontSize = '14px'; + s.letterSpacing = 0; + s.border = 0; + s.background = 'none'; + s.marginTop = '16px'; + button.addEventListener('click', onclick); + return button; +}; +var commonjsGlobal$$1 = typeof window !== 'undefined' ? window : typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof self !== 'undefined' ? self : {}; +function unwrapExports$$1 (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; +} +function createCommonjsModule$$1(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; +} +var NoSleep = createCommonjsModule$$1(function (module, exports) { +(function webpackUniversalModuleDefinition(root, factory) { + module.exports = factory(); +})(commonjsGlobal$$1, function() { +return (function(modules) { + var installedModules = {}; + function __webpack_require__(moduleId) { + if(installedModules[moduleId]) { + return installedModules[moduleId].exports; + } + var module = installedModules[moduleId] = { + i: moduleId, + l: false, + exports: {} + }; + modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + module.l = true; + return module.exports; + } + __webpack_require__.m = modules; + __webpack_require__.c = installedModules; + __webpack_require__.d = function(exports, name, getter) { + if(!__webpack_require__.o(exports, name)) { + Object.defineProperty(exports, name, { + configurable: false, + enumerable: true, + get: getter + }); + } + }; + __webpack_require__.n = function(module) { + var getter = module && module.__esModule ? + function getDefault() { return module['default']; } : + function getModuleExports() { return module; }; + __webpack_require__.d(getter, 'a', getter); + return getter; + }; + __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; + __webpack_require__.p = ""; + return __webpack_require__(__webpack_require__.s = 0); + }) + ([ + (function(module, exports, __webpack_require__) { +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; }; }(); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +var mediaFile = __webpack_require__(1); +var oldIOS = typeof navigator !== 'undefined' && parseFloat(('' + (/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0, ''])[1]).replace('undefined', '3_2').replace('_', '.').replace('_', '')) < 10 && !window.MSStream; +var NoSleep = function () { + function NoSleep() { + _classCallCheck(this, NoSleep); + if (oldIOS) { + this.noSleepTimer = null; + } else { + this.noSleepVideo = document.createElement('video'); + this.noSleepVideo.setAttribute('playsinline', ''); + this.noSleepVideo.setAttribute('src', mediaFile); + this.noSleepVideo.addEventListener('timeupdate', function (e) { + if (this.noSleepVideo.currentTime > 0.5) { + this.noSleepVideo.currentTime = Math.random(); + } + }.bind(this)); + } + } + _createClass(NoSleep, [{ + key: 'enable', + value: function enable() { + if (oldIOS) { + this.disable(); + this.noSleepTimer = window.setInterval(function () { + window.location.href = '/'; + window.setTimeout(window.stop, 0); + }, 15000); + } else { + this.noSleepVideo.play(); + } + } + }, { + key: 'disable', + value: function disable() { + if (oldIOS) { + if (this.noSleepTimer) { + window.clearInterval(this.noSleepTimer); + this.noSleepTimer = null; + } + } else { + this.noSleepVideo.pause(); + } + } + }]); + return NoSleep; +}(); +module.exports = NoSleep; + }), + (function(module, exports, __webpack_require__) { +module.exports = 'data:video/mp4;base64,AAAAIGZ0eXBtcDQyAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACKBtZGF0AAAC8wYF///v3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0MiByMjQ3OSBkZDc5YTYxIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTEgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9MiBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MSBrZXlpbnQ9MzAwIGtleWludF9taW49MzAgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD0xMCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIwLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IHZidl9tYXhyYXRlPTIwMDAwIHZidl9idWZzaXplPTI1MDAwIGNyZl9tYXg9MC4wIG5hbF9ocmQ9bm9uZSBmaWxsZXI9MCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAOWWIhAA3//p+C7v8tDDSTjf97w55i3SbRPO4ZY+hkjD5hbkAkL3zpJ6h/LR1CAABzgB1kqqzUorlhQAAAAxBmiQYhn/+qZYADLgAAAAJQZ5CQhX/AAj5IQAD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'; + }) + ]); +}); +}); +var NoSleep$1 = unwrapExports$$1(NoSleep); +var nextDisplayId = 1000; +var defaultLeftBounds = [0, 0, 0.5, 1]; +var defaultRightBounds = [0.5, 0, 0.5, 1]; +var raf = window.requestAnimationFrame; +var caf = window.cancelAnimationFrame; +function VRFrameData() { + this.leftProjectionMatrix = new Float32Array(16); + this.leftViewMatrix = new Float32Array(16); + this.rightProjectionMatrix = new Float32Array(16); + this.rightViewMatrix = new Float32Array(16); + this.pose = null; +} +function VRDisplayCapabilities(config) { + Object.defineProperties(this, { + hasPosition: { + writable: false, enumerable: true, value: config.hasPosition + }, + hasExternalDisplay: { + writable: false, enumerable: true, value: config.hasExternalDisplay + }, + canPresent: { + writable: false, enumerable: true, value: config.canPresent + }, + maxLayers: { + writable: false, enumerable: true, value: config.maxLayers + }, + hasOrientation: { + enumerable: true, get: function get() { + deprecateWarning('VRDisplayCapabilities.prototype.hasOrientation', 'VRDisplay.prototype.getFrameData'); + return config.hasOrientation; + } + } + }); +} +function VRDisplay(config) { + config = config || {}; + var USE_WAKELOCK = 'wakelock' in config ? config.wakelock : true; + this.isPolyfilled = true; + this.displayId = nextDisplayId++; + this.displayName = ''; + this.depthNear = 0.01; + this.depthFar = 10000.0; + this.isPresenting = false; + Object.defineProperty(this, 'isConnected', { + get: function get() { + deprecateWarning('VRDisplay.prototype.isConnected', 'VRDisplayCapabilities.prototype.hasExternalDisplay'); + return false; + } + }); + this.capabilities = new VRDisplayCapabilities({ + hasPosition: false, + hasOrientation: false, + hasExternalDisplay: false, + canPresent: false, + maxLayers: 1 + }); + this.stageParameters = null; + this.waitingForPresent_ = false; + this.layer_ = null; + this.originalParent_ = null; + this.fullscreenElement_ = null; + this.fullscreenWrapper_ = null; + this.fullscreenElementCachedStyle_ = null; + this.fullscreenEventTarget_ = null; + this.fullscreenChangeHandler_ = null; + this.fullscreenErrorHandler_ = null; + if (USE_WAKELOCK && isMobile()) { + this.wakelock_ = new NoSleep$1(); + } +} +VRDisplay.prototype.getFrameData = function (frameData) { + return frameDataFromPose(frameData, this._getPose(), this); +}; +VRDisplay.prototype.getPose = function () { + deprecateWarning('VRDisplay.prototype.getPose', 'VRDisplay.prototype.getFrameData'); + return this._getPose(); +}; +VRDisplay.prototype.resetPose = function () { + deprecateWarning('VRDisplay.prototype.resetPose'); + return this._resetPose(); +}; +VRDisplay.prototype.getImmediatePose = function () { + deprecateWarning('VRDisplay.prototype.getImmediatePose', 'VRDisplay.prototype.getFrameData'); + return this._getPose(); +}; +VRDisplay.prototype.requestAnimationFrame = function (callback) { + return raf(callback); +}; +VRDisplay.prototype.cancelAnimationFrame = function (id) { + return caf(id); +}; +VRDisplay.prototype.wrapForFullscreen = function (element) { + if (isIOS()) { + return element; + } + if (!this.fullscreenWrapper_) { + this.fullscreenWrapper_ = document.createElement('div'); + var cssProperties = ['height: ' + Math.min(screen.height, screen.width) + 'px !important', 'top: 0 !important', 'left: 0 !important', 'right: 0 !important', 'border: 0', 'margin: 0', 'padding: 0', 'z-index: 999999 !important', 'position: fixed']; + this.fullscreenWrapper_.setAttribute('style', cssProperties.join('; ') + ';'); + this.fullscreenWrapper_.classList.add('webvr-polyfill-fullscreen-wrapper'); + } + if (this.fullscreenElement_ == element) { + return this.fullscreenWrapper_; + } + if (this.fullscreenElement_) { + if (this.originalParent_) { + this.originalParent_.appendChild(this.fullscreenElement_); + } else { + this.fullscreenElement_.parentElement.removeChild(this.fullscreenElement_); + } + } + this.fullscreenElement_ = element; + this.originalParent_ = element.parentElement; + if (!this.originalParent_) { + document.body.appendChild(element); + } + if (!this.fullscreenWrapper_.parentElement) { + var parent = this.fullscreenElement_.parentElement; + parent.insertBefore(this.fullscreenWrapper_, this.fullscreenElement_); + parent.removeChild(this.fullscreenElement_); + } + this.fullscreenWrapper_.insertBefore(this.fullscreenElement_, this.fullscreenWrapper_.firstChild); + this.fullscreenElementCachedStyle_ = this.fullscreenElement_.getAttribute('style'); + var self = this; + function applyFullscreenElementStyle() { + if (!self.fullscreenElement_) { + return; + } + var cssProperties = ['position: absolute', 'top: 0', 'left: 0', 'width: ' + Math.max(screen.width, screen.height) + 'px', 'height: ' + Math.min(screen.height, screen.width) + 'px', 'border: 0', 'margin: 0', 'padding: 0']; + self.fullscreenElement_.setAttribute('style', cssProperties.join('; ') + ';'); + } + applyFullscreenElementStyle(); + return this.fullscreenWrapper_; +}; +VRDisplay.prototype.removeFullscreenWrapper = function () { + if (!this.fullscreenElement_) { + return; + } + var element = this.fullscreenElement_; + if (this.fullscreenElementCachedStyle_) { + element.setAttribute('style', this.fullscreenElementCachedStyle_); + } else { + element.removeAttribute('style'); + } + this.fullscreenElement_ = null; + this.fullscreenElementCachedStyle_ = null; + var parent = this.fullscreenWrapper_.parentElement; + this.fullscreenWrapper_.removeChild(element); + if (this.originalParent_ === parent) { + parent.insertBefore(element, this.fullscreenWrapper_); + } + else if (this.originalParent_) { + this.originalParent_.appendChild(element); + } + parent.removeChild(this.fullscreenWrapper_); + return element; +}; +VRDisplay.prototype.requestPresent = function (layers) { + var wasPresenting = this.isPresenting; + var self = this; + if (!(layers instanceof Array)) { + deprecateWarning('VRDisplay.prototype.requestPresent with non-array argument', 'an array of VRLayers as the first argument'); + layers = [layers]; + } + return new Promise(function (resolve, reject) { + if (!self.capabilities.canPresent) { + reject(new Error('VRDisplay is not capable of presenting.')); + return; + } + if (layers.length == 0 || layers.length > self.capabilities.maxLayers) { + reject(new Error('Invalid number of layers.')); + return; + } + var incomingLayer = layers[0]; + if (!incomingLayer.source) { + resolve(); + return; + } + var leftBounds = incomingLayer.leftBounds || defaultLeftBounds; + var rightBounds = incomingLayer.rightBounds || defaultRightBounds; + if (wasPresenting) { + var layer = self.layer_; + if (layer.source !== incomingLayer.source) { + layer.source = incomingLayer.source; + } + for (var i = 0; i < 4; i++) { + layer.leftBounds[i] = leftBounds[i]; + layer.rightBounds[i] = rightBounds[i]; + } + self.wrapForFullscreen(self.layer_.source); + self.updatePresent_(); + resolve(); + return; + } + self.layer_ = { + predistorted: incomingLayer.predistorted, + source: incomingLayer.source, + leftBounds: leftBounds.slice(0), + rightBounds: rightBounds.slice(0) + }; + self.waitingForPresent_ = false; + if (self.layer_ && self.layer_.source) { + var fullscreenElement = self.wrapForFullscreen(self.layer_.source); + var onFullscreenChange = function onFullscreenChange() { + var actualFullscreenElement = getFullscreenElement(); + self.isPresenting = fullscreenElement === actualFullscreenElement; + if (self.isPresenting) { + if (screen.orientation && screen.orientation.lock) { + screen.orientation.lock('landscape-primary').catch(function (error) { + console.error('screen.orientation.lock() failed due to', error.message); + }); + } + self.waitingForPresent_ = false; + self.beginPresent_(); + resolve(); + } else { + if (screen.orientation && screen.orientation.unlock) { + screen.orientation.unlock(); + } + self.removeFullscreenWrapper(); + self.disableWakeLock(); + self.endPresent_(); + self.removeFullscreenListeners_(); + } + self.fireVRDisplayPresentChange_(); + }; + var onFullscreenError = function onFullscreenError() { + if (!self.waitingForPresent_) { + return; + } + self.removeFullscreenWrapper(); + self.removeFullscreenListeners_(); + self.disableWakeLock(); + self.waitingForPresent_ = false; + self.isPresenting = false; + reject(new Error('Unable to present.')); + }; + self.addFullscreenListeners_(fullscreenElement, onFullscreenChange, onFullscreenError); + if (requestFullscreen(fullscreenElement)) { + self.enableWakeLock(); + self.waitingForPresent_ = true; + } else if (isIOS() || isWebViewAndroid()) { + self.enableWakeLock(); + self.isPresenting = true; + self.beginPresent_(); + self.fireVRDisplayPresentChange_(); + resolve(); + } + } + if (!self.waitingForPresent_ && !isIOS()) { + exitFullscreen(); + reject(new Error('Unable to present.')); + } + }); +}; +VRDisplay.prototype.exitPresent = function () { + var wasPresenting = this.isPresenting; + var self = this; + this.isPresenting = false; + this.layer_ = null; + this.disableWakeLock(); + return new Promise(function (resolve, reject) { + if (wasPresenting) { + if (!exitFullscreen() && isIOS()) { + self.endPresent_(); + self.fireVRDisplayPresentChange_(); + } + if (isWebViewAndroid()) { + self.removeFullscreenWrapper(); + self.removeFullscreenListeners_(); + self.endPresent_(); + self.fireVRDisplayPresentChange_(); + } + resolve(); + } else { + reject(new Error('Was not presenting to VRDisplay.')); + } + }); +}; +VRDisplay.prototype.getLayers = function () { + if (this.layer_) { + return [this.layer_]; + } + return []; +}; +VRDisplay.prototype.fireVRDisplayPresentChange_ = function () { + var event = new CustomEvent('vrdisplaypresentchange', { detail: { display: this } }); + window.dispatchEvent(event); +}; +VRDisplay.prototype.fireVRDisplayConnect_ = function () { + var event = new CustomEvent('vrdisplayconnect', { detail: { display: this } }); + window.dispatchEvent(event); +}; +VRDisplay.prototype.addFullscreenListeners_ = function (element, changeHandler, errorHandler) { + this.removeFullscreenListeners_(); + this.fullscreenEventTarget_ = element; + this.fullscreenChangeHandler_ = changeHandler; + this.fullscreenErrorHandler_ = errorHandler; + if (changeHandler) { + if (document.fullscreenEnabled) { + element.addEventListener('fullscreenchange', changeHandler, false); + } else if (document.webkitFullscreenEnabled) { + element.addEventListener('webkitfullscreenchange', changeHandler, false); + } else if (document.mozFullScreenEnabled) { + document.addEventListener('mozfullscreenchange', changeHandler, false); + } else if (document.msFullscreenEnabled) { + element.addEventListener('msfullscreenchange', changeHandler, false); + } + } + if (errorHandler) { + if (document.fullscreenEnabled) { + element.addEventListener('fullscreenerror', errorHandler, false); + } else if (document.webkitFullscreenEnabled) { + element.addEventListener('webkitfullscreenerror', errorHandler, false); + } else if (document.mozFullScreenEnabled) { + document.addEventListener('mozfullscreenerror', errorHandler, false); + } else if (document.msFullscreenEnabled) { + element.addEventListener('msfullscreenerror', errorHandler, false); + } + } +}; +VRDisplay.prototype.removeFullscreenListeners_ = function () { + if (!this.fullscreenEventTarget_) return; + var element = this.fullscreenEventTarget_; + if (this.fullscreenChangeHandler_) { + var changeHandler = this.fullscreenChangeHandler_; + element.removeEventListener('fullscreenchange', changeHandler, false); + element.removeEventListener('webkitfullscreenchange', changeHandler, false); + document.removeEventListener('mozfullscreenchange', changeHandler, false); + element.removeEventListener('msfullscreenchange', changeHandler, false); + } + if (this.fullscreenErrorHandler_) { + var errorHandler = this.fullscreenErrorHandler_; + element.removeEventListener('fullscreenerror', errorHandler, false); + element.removeEventListener('webkitfullscreenerror', errorHandler, false); + document.removeEventListener('mozfullscreenerror', errorHandler, false); + element.removeEventListener('msfullscreenerror', errorHandler, false); + } + this.fullscreenEventTarget_ = null; + this.fullscreenChangeHandler_ = null; + this.fullscreenErrorHandler_ = null; +}; +VRDisplay.prototype.enableWakeLock = function () { + if (this.wakelock_) { + this.wakelock_.enable(); + } +}; +VRDisplay.prototype.disableWakeLock = function () { + if (this.wakelock_) { + this.wakelock_.disable(); + } +}; +VRDisplay.prototype.beginPresent_ = function () { +}; +VRDisplay.prototype.endPresent_ = function () { +}; +VRDisplay.prototype.submitFrame = function (pose) { +}; +VRDisplay.prototype.getEyeParameters = function (whichEye) { + return null; +}; +var config = { + ADDITIONAL_VIEWERS: [], + DEFAULT_VIEWER: '', + MOBILE_WAKE_LOCK: true, + DEBUG: false, + DPDB_URL: 'https://dpdb.webvr.rocks/dpdb.json', + K_FILTER: 0.98, + PREDICTION_TIME_S: 0.040, + CARDBOARD_UI_DISABLED: false, + ROTATE_INSTRUCTIONS_DISABLED: false, + YAW_ONLY: false, + BUFFER_SCALE: 0.5, + DIRTY_SUBMIT_FRAME_BINDINGS: false +}; +var Eye = { + LEFT: 'left', + RIGHT: 'right' +}; +function CardboardVRDisplay(config$$1) { + var defaults = extend({}, config); + config$$1 = extend(defaults, config$$1 || {}); + VRDisplay.call(this, { + wakelock: config$$1.MOBILE_WAKE_LOCK + }); + this.config = config$$1; + this.displayName = 'Cardboard VRDisplay'; + this.capabilities = new VRDisplayCapabilities({ + hasPosition: false, + hasOrientation: true, + hasExternalDisplay: false, + canPresent: true, + maxLayers: 1 + }); + this.stageParameters = null; + this.bufferScale_ = this.config.BUFFER_SCALE; + this.poseSensor_ = new PoseSensor(this.config); + this.distorter_ = null; + this.cardboardUI_ = null; + this.dpdb_ = new Dpdb(this.config.DPDB_URL, this.onDeviceParamsUpdated_.bind(this)); + this.deviceInfo_ = new DeviceInfo(this.dpdb_.getDeviceParams(), config$$1.ADDITIONAL_VIEWERS); + this.viewerSelector_ = new ViewerSelector(config$$1.DEFAULT_VIEWER); + this.viewerSelector_.onChange(this.onViewerChanged_.bind(this)); + this.deviceInfo_.setViewer(this.viewerSelector_.getCurrentViewer()); + if (!this.config.ROTATE_INSTRUCTIONS_DISABLED) { + this.rotateInstructions_ = new RotateInstructions(); + } + if (isIOS()) { + window.addEventListener('resize', this.onResize_.bind(this)); + } +} +CardboardVRDisplay.prototype = Object.create(VRDisplay.prototype); +CardboardVRDisplay.prototype._getPose = function () { + return { + position: null, + orientation: this.poseSensor_.getOrientation(), + linearVelocity: null, + linearAcceleration: null, + angularVelocity: null, + angularAcceleration: null + }; +}; +CardboardVRDisplay.prototype._resetPose = function () { + if (this.poseSensor_.resetPose) { + this.poseSensor_.resetPose(); + } +}; +CardboardVRDisplay.prototype._getFieldOfView = function (whichEye) { + var fieldOfView; + if (whichEye == Eye.LEFT) { + fieldOfView = this.deviceInfo_.getFieldOfViewLeftEye(); + } else if (whichEye == Eye.RIGHT) { + fieldOfView = this.deviceInfo_.getFieldOfViewRightEye(); + } else { + console.error('Invalid eye provided: %s', whichEye); + return null; + } + return fieldOfView; +}; +CardboardVRDisplay.prototype._getEyeOffset = function (whichEye) { + var offset; + if (whichEye == Eye.LEFT) { + offset = [-this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0]; + } else if (whichEye == Eye.RIGHT) { + offset = [this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0]; + } else { + console.error('Invalid eye provided: %s', whichEye); + return null; + } + return offset; +}; +CardboardVRDisplay.prototype.getEyeParameters = function (whichEye) { + var offset = this._getEyeOffset(whichEye); + var fieldOfView = this._getFieldOfView(whichEye); + var eyeParams = { + offset: offset, + renderWidth: this.deviceInfo_.device.width * 0.5 * this.bufferScale_, + renderHeight: this.deviceInfo_.device.height * this.bufferScale_ + }; + Object.defineProperty(eyeParams, 'fieldOfView', { + enumerable: true, + get: function get() { + deprecateWarning('VRFieldOfView', 'VRFrameData\'s projection matrices'); + return fieldOfView; + } + }); + return eyeParams; +}; +CardboardVRDisplay.prototype.onDeviceParamsUpdated_ = function (newParams) { + if (this.config.DEBUG) { + console.log('DPDB reported that device params were updated.'); + } + this.deviceInfo_.updateDeviceParams(newParams); + if (this.distorter_) { + this.distorter_.updateDeviceInfo(this.deviceInfo_); + } +}; +CardboardVRDisplay.prototype.updateBounds_ = function () { + if (this.layer_ && this.distorter_ && (this.layer_.leftBounds || this.layer_.rightBounds)) { + this.distorter_.setTextureBounds(this.layer_.leftBounds, this.layer_.rightBounds); + } +}; +CardboardVRDisplay.prototype.beginPresent_ = function () { + var gl = this.layer_.source.getContext('webgl'); + if (!gl) gl = this.layer_.source.getContext('experimental-webgl'); + if (!gl) gl = this.layer_.source.getContext('webgl2'); + if (!gl) return; + if (this.layer_.predistorted) { + if (!this.config.CARDBOARD_UI_DISABLED) { + gl.canvas.width = getScreenWidth() * this.bufferScale_; + gl.canvas.height = getScreenHeight() * this.bufferScale_; + this.cardboardUI_ = new CardboardUI(gl); + } + } else { + if (!this.config.CARDBOARD_UI_DISABLED) { + this.cardboardUI_ = new CardboardUI(gl); + } + this.distorter_ = new CardboardDistorter(gl, this.cardboardUI_, this.config.BUFFER_SCALE, this.config.DIRTY_SUBMIT_FRAME_BINDINGS); + this.distorter_.updateDeviceInfo(this.deviceInfo_); + } + if (this.cardboardUI_) { + this.cardboardUI_.listen(function (e) { + this.viewerSelector_.show(this.layer_.source.parentElement); + e.stopPropagation(); + e.preventDefault(); + }.bind(this), function (e) { + this.exitPresent(); + e.stopPropagation(); + e.preventDefault(); + }.bind(this)); + } + if (this.rotateInstructions_) { + if (isLandscapeMode() && isMobile()) { + this.rotateInstructions_.showTemporarily(3000, this.layer_.source.parentElement); + } else { + this.rotateInstructions_.update(); + } + } + this.orientationHandler = this.onOrientationChange_.bind(this); + window.addEventListener('orientationchange', this.orientationHandler); + this.vrdisplaypresentchangeHandler = this.updateBounds_.bind(this); + window.addEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler); + this.fireVRDisplayDeviceParamsChange_(); +}; +CardboardVRDisplay.prototype.endPresent_ = function () { + if (this.distorter_) { + this.distorter_.destroy(); + this.distorter_ = null; + } + if (this.cardboardUI_) { + this.cardboardUI_.destroy(); + this.cardboardUI_ = null; + } + if (this.rotateInstructions_) { + this.rotateInstructions_.hide(); + } + this.viewerSelector_.hide(); + window.removeEventListener('orientationchange', this.orientationHandler); + window.removeEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler); +}; +CardboardVRDisplay.prototype.updatePresent_ = function () { + this.endPresent_(); + this.beginPresent_(); +}; +CardboardVRDisplay.prototype.submitFrame = function (pose) { + if (this.distorter_) { + this.updateBounds_(); + this.distorter_.submitFrame(); + } else if (this.cardboardUI_ && this.layer_) { + var canvas = this.layer_.source.getContext('webgl').canvas; + if (canvas.width != this.lastWidth || canvas.height != this.lastHeight) { + this.cardboardUI_.onResize(); + } + this.lastWidth = canvas.width; + this.lastHeight = canvas.height; + this.cardboardUI_.render(); + } +}; +CardboardVRDisplay.prototype.onOrientationChange_ = function (e) { + this.viewerSelector_.hide(); + if (this.rotateInstructions_) { + this.rotateInstructions_.update(); + } + this.onResize_(); +}; +CardboardVRDisplay.prototype.onResize_ = function (e) { + if (this.layer_) { + var gl = this.layer_.source.getContext('webgl'); + var cssProperties = ['position: absolute', 'top: 0', 'left: 0', + 'width: 100vw', 'height: 100vh', 'border: 0', 'margin: 0', + 'padding: 0px', 'box-sizing: content-box']; + gl.canvas.setAttribute('style', cssProperties.join('; ') + ';'); + safariCssSizeWorkaround(gl.canvas); + } +}; +CardboardVRDisplay.prototype.onViewerChanged_ = function (viewer) { + this.deviceInfo_.setViewer(viewer); + if (this.distorter_) { + this.distorter_.updateDeviceInfo(this.deviceInfo_); + } + this.fireVRDisplayDeviceParamsChange_(); +}; +CardboardVRDisplay.prototype.fireVRDisplayDeviceParamsChange_ = function () { + var event = new CustomEvent('vrdisplaydeviceparamschange', { + detail: { + vrdisplay: this, + deviceInfo: this.deviceInfo_ + } + }); + window.dispatchEvent(event); +}; +CardboardVRDisplay.VRFrameData = VRFrameData; +CardboardVRDisplay.VRDisplay = VRDisplay; +return CardboardVRDisplay; +}))); +}); +var CardboardVRDisplay = unwrapExports(cardboardVrDisplay); + +class XRDevice extends EventTarget { + constructor(global) { + super(); + this.global = global; + this.onWindowResize = this.onWindowResize.bind(this); + this.global.window.addEventListener('resize', this.onWindowResize); + this.environmentBlendMode = 'opaque'; + } + get depthNear() { throw new Error('Not implemented'); } + set depthNear(val) { throw new Error('Not implemented'); } + get depthFar() { throw new Error('Not implemented'); } + set depthFar(val) { throw new Error('Not implemented'); } + onBaseLayerSet(sessionId, layer) { throw new Error('Not implemented'); } + onInlineVerticalFieldOfViewSet(sessionId, value) { throw new Error('Not implemented'); } + supportsSession(mode) { throw new Error('Not implemented'); } + async requestSession(mode) { throw new Error('Not implemented'); } + requestAnimationFrame(callback) { throw new Error('Not implemented'); } + onFrameStart(sessionId) { throw new Error('Not implemented'); } + onFrameEnd(sessionId) { throw new Error('Not implemented'); } + requestStageBounds() { throw new Error('Not implemented'); } + async requestFrameOfReferenceTransform(type, options) { + return undefined; + } + cancelAnimationFrame(handle) { throw new Error('Not implemented'); } + endSession(sessionId) { throw new Error('Not implemented'); } + getViewport(sessionId, eye, layer, target) { throw new Error('Not implemented'); } + getProjectionMatrix(eye) { throw new Error('Not implemented'); } + getBasePoseMatrix() { throw new Error('Not implemented'); } + getBaseViewMatrix(eye) { throw new Error('Not implemented'); } + getInputSources() { throw new Error('Not implemented'); } + getInputPose(inputSource, coordinateSystem, poseType) { throw new Error('Not implemented'); } + onWindowResize() { + this.onWindowResize(); + } +} + +let oculusGo = { + mapping: 'xr-standard', + profiles: ['oculus-go', 'touchpad-controller'], + buttons: { + length: 3, + 0: 1, + 1: null, + 2: 0 + }, + gripTransform: { + orientation: [Math.PI * 0.11, 0, 0, 1] + } +}; +let oculusTouch = { + mapping: 'xr-standard', + displayProfiles: { + 'Oculus Quest': ['oculus-quest', 'oculus-touch', 'thumbstick-controller'] + }, + profiles: ['oculus-touch', 'thumbstick-controller'], + axes: { + length: 4, + 0: null, + 1: null, + 2: 0, + 3: 1 + }, + buttons: { + length: 6, + 0: 1, + 1: 2, + 2: null, + 3: 0, + 4: 3, + 5: 4 + }, + gripTransform: { + position: [0, -0.02, 0.04, 1], + orientation: [Math.PI * 0.11, 0, 0, 1] + } +}; +let openVr = { + mapping: 'xr-standard', + profiles: ['openvr-controller', 'touchpad-controller'], + displayProfiles: { + 'HTC Vive': ['htc-vive', 'touchpad-controller'], + 'HTC Vive DVT': ['htc-vive', 'touchpad-controller'] + }, + buttons: { + length: 3, + 0: 1, + 1: 2, + 2: 0 + }, + gripTransform: { + position: [0, 0, 0.05, 1], + }, + targetRayTransform: { + orientation: [Math.PI * -0.08, 0, 0, 1] + } +}; +let windowsMixedReality = { + mapping: 'xr-standard', + profiles: ['windows-mixed-reality', 'touchpad-thumbstick-controller'], + buttons: { + length: 4, + 0: 1, + 1: 0, + 2: 2, + 3: 4, + }, + gripTransform: { + position: [0, -0.02, 0.04, 1], + orientation: [Math.PI * 0.11, 0, 0, 1] + } +}; +let GamepadMappings = { + 'Oculus Go Controller': oculusGo, + 'Oculus Touch (Right)': oculusTouch, + 'Oculus Touch (Left)': oculusTouch, + 'OpenVR Gamepad': openVr, + 'Windows Mixed Reality (Right)': windowsMixedReality, + 'Windows Mixed Reality (Left)': windowsMixedReality, +}; + +const HEAD_ELBOW_OFFSET_RIGHTHANDED = fromValues$1(0.155, -0.465, -0.15); +const HEAD_ELBOW_OFFSET_LEFTHANDED = fromValues$1(-0.155, -0.465, -0.15); +const ELBOW_WRIST_OFFSET = fromValues$1(0, 0, -0.25); +const WRIST_CONTROLLER_OFFSET = fromValues$1(0, 0, 0.05); +const ARM_EXTENSION_OFFSET = fromValues$1(-0.08, 0.14, 0.08); +const ELBOW_BEND_RATIO = 0.4; +const EXTENSION_RATIO_WEIGHT = 0.4; +const MIN_ANGULAR_SPEED = 0.61; +const MIN_ANGLE_DELTA = 0.175; +const MIN_EXTENSION_COS = 0.12; +const MAX_EXTENSION_COS = 0.87; +const RAD_TO_DEG = 180 / Math.PI; +function eulerFromQuaternion(out, q, order) { + function clamp(value, min$$1, max$$1) { + return (value < min$$1 ? min$$1 : (value > max$$1 ? max$$1 : value)); + } + var sqx = q[0] * q[0]; + var sqy = q[1] * q[1]; + var sqz = q[2] * q[2]; + var sqw = q[3] * q[3]; + if ( order === 'XYZ' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] - q[1] * q[2] ), ( sqw - sqx - sqy + sqz ) ); + out[1] = Math.asin( clamp( 2 * ( q[0] * q[2] + q[1] * q[3] ), -1, 1 ) ); + out[2] = Math.atan2( 2 * ( q[2] * q[3] - q[0] * q[1] ), ( sqw + sqx - sqy - sqz ) ); + } else if ( order === 'YXZ' ) { + out[0] = Math.asin( clamp( 2 * ( q[0] * q[3] - q[1] * q[2] ), -1, 1 ) ); + out[1] = Math.atan2( 2 * ( q[0] * q[2] + q[1] * q[3] ), ( sqw - sqx - sqy + sqz ) ); + out[2] = Math.atan2( 2 * ( q[0] * q[1] + q[2] * q[3] ), ( sqw - sqx + sqy - sqz ) ); + } else if ( order === 'ZXY' ) { + out[0] = Math.asin( clamp( 2 * ( q[0] * q[3] + q[1] * q[2] ), -1, 1 ) ); + out[1] = Math.atan2( 2 * ( q[1] * q[3] - q[2] * q[0] ), ( sqw - sqx - sqy + sqz ) ); + out[2] = Math.atan2( 2 * ( q[2] * q[3] - q[0] * q[1] ), ( sqw - sqx + sqy - sqz ) ); + } else if ( order === 'ZYX' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] + q[2] * q[1] ), ( sqw - sqx - sqy + sqz ) ); + out[1] = Math.asin( clamp( 2 * ( q[1] * q[3] - q[0] * q[2] ), -1, 1 ) ); + out[2] = Math.atan2( 2 * ( q[0] * q[1] + q[2] * q[3] ), ( sqw + sqx - sqy - sqz ) ); + } else if ( order === 'YZX' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] - q[2] * q[1] ), ( sqw - sqx + sqy - sqz ) ); + out[1] = Math.atan2( 2 * ( q[1] * q[3] - q[0] * q[2] ), ( sqw + sqx - sqy - sqz ) ); + out[2] = Math.asin( clamp( 2 * ( q[0] * q[1] + q[2] * q[3] ), -1, 1 ) ); + } else if ( order === 'XZY' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] + q[1] * q[2] ), ( sqw - sqx + sqy - sqz ) ); + out[1] = Math.atan2( 2 * ( q[0] * q[2] + q[1] * q[3] ), ( sqw + sqx - sqy - sqz ) ); + out[2] = Math.asin( clamp( 2 * ( q[2] * q[3] - q[0] * q[1] ), -1, 1 ) ); + } else { + console.log('No order given for quaternion to euler conversion.'); + return; + } +} +class OrientationArmModel { + constructor() { + this.hand = 'right'; + this.headElbowOffset = HEAD_ELBOW_OFFSET_RIGHTHANDED; + this.controllerQ = create$4(); + this.lastControllerQ = create$4(); + this.headQ = create$4(); + this.headPos = create$1(); + this.elbowPos = create$1(); + this.wristPos = create$1(); + this.time = null; + this.lastTime = null; + this.rootQ = create$4(); + this.position = create$1(); + } + setHandedness(hand) { + if (this.hand != hand) { + this.hand = hand; + if (this.hand == 'left') { + this.headElbowOffset = HEAD_ELBOW_OFFSET_LEFTHANDED; + } else { + this.headElbowOffset = HEAD_ELBOW_OFFSET_RIGHTHANDED; + } + } + } + update(controllerOrientation, headPoseMatrix) { + this.time = now$1(); + if (controllerOrientation) { + copy$4(this.lastControllerQ, this.controllerQ); + copy$4(this.controllerQ, controllerOrientation); + } + if (headPoseMatrix) { + getTranslation(this.headPos, headPoseMatrix); + getRotation(this.headQ, headPoseMatrix); + } + let headYawQ = this.getHeadYawOrientation_(); + let angleDelta = this.quatAngle_(this.lastControllerQ, this.controllerQ); + let timeDelta = (this.time - this.lastTime) / 1000; + let controllerAngularSpeed = angleDelta / timeDelta; + if (controllerAngularSpeed > MIN_ANGULAR_SPEED) { + slerp(this.rootQ, this.rootQ, headYawQ, + Math.min(angleDelta / MIN_ANGLE_DELTA, 1.0)); + } else { + copy$4(this.rootQ, headYawQ); + } + let controllerForward = fromValues$1(0, 0, -1.0); + transformQuat(controllerForward, controllerForward, this.controllerQ); + let controllerDotY = dot(controllerForward, [0, 1, 0]); + let extensionRatio = this.clamp_( + (controllerDotY - MIN_EXTENSION_COS) / MAX_EXTENSION_COS, 0.0, 1.0); + let controllerCameraQ = clone$4(this.rootQ); + invert$2(controllerCameraQ, controllerCameraQ); + multiply$4(controllerCameraQ, controllerCameraQ, this.controllerQ); + let elbowPos = this.elbowPos; + copy$1(elbowPos, this.headPos); + add$1(elbowPos, elbowPos, this.headElbowOffset); + let elbowOffset = clone$1(ARM_EXTENSION_OFFSET); + scale$1(elbowOffset, elbowOffset, extensionRatio); + add$1(elbowPos, elbowPos, elbowOffset); + let totalAngle = this.quatAngle_(controllerCameraQ, create$4()); + let totalAngleDeg = totalAngle * RAD_TO_DEG; + let lerpSuppression = 1 - Math.pow(totalAngleDeg / 180, 4);let elbowRatio = ELBOW_BEND_RATIO; + let wristRatio = 1 - ELBOW_BEND_RATIO; + let lerpValue = lerpSuppression * + (elbowRatio + wristRatio * extensionRatio * EXTENSION_RATIO_WEIGHT); + let wristQ = create$4(); + slerp(wristQ, wristQ, controllerCameraQ, lerpValue); + let invWristQ = invert$2(create$4(), wristQ); + let elbowQ = clone$4(controllerCameraQ); + multiply$4(elbowQ, elbowQ, invWristQ); + let wristPos = this.wristPos; + copy$1(wristPos, WRIST_CONTROLLER_OFFSET); + transformQuat(wristPos, wristPos, wristQ); + add$1(wristPos, wristPos, ELBOW_WRIST_OFFSET); + transformQuat(wristPos, wristPos, elbowQ); + add$1(wristPos, wristPos, elbowPos); + let offset = clone$1(ARM_EXTENSION_OFFSET); + scale$1(offset, offset, extensionRatio); + add$1(this.position, this.wristPos, offset); + transformQuat(this.position, this.position, this.rootQ); + this.lastTime = this.time; + } + getPosition() { + return this.position; + } + getHeadYawOrientation_() { + let headEuler = create$1(); + eulerFromQuaternion(headEuler, this.headQ, 'YXZ'); + let destinationQ = fromEuler(create$4(), 0, headEuler[1] * RAD_TO_DEG, 0); + return destinationQ; + } + clamp_(value, min$$1, max$$1) { + return Math.min(Math.max(value, min$$1), max$$1); + } + quatAngle_(q1, q2) { + let vec1 = [0, 0, -1]; + let vec2 = [0, 0, -1]; + transformQuat(vec1, vec1, q1); + transformQuat(vec2, vec2, q2); + return angle(vec1, vec2); + } +} + +const PRIVATE$14 = Symbol('@@webxr-polyfill/XRRemappedGamepad'); +const PLACEHOLDER_BUTTON = { pressed: false, touched: false, value: 0.0 }; +Object.freeze(PLACEHOLDER_BUTTON); +class XRRemappedGamepad { + constructor(gamepad, display, map) { + if (!map) { + map = {}; + } + let axes = new Array(map.axes && map.axes.length ? map.axes.length : gamepad.axes.length); + let buttons = new Array(map.buttons && map.buttons.length ? map.buttons.length : gamepad.buttons.length); + let gripTransform = null; + if (map.gripTransform) { + let orientation = map.gripTransform.orientation || [0, 0, 0, 1]; + gripTransform = create(); + fromRotationTranslation( + gripTransform, + normalize$2(orientation, orientation), + map.gripTransform.position || [0, 0, 0] + ); + } + let targetRayTransform = null; + if (map.targetRayTransform) { + let orientation = map.targetRayTransform.orientation || [0, 0, 0, 1]; + targetRayTransform = create(); + fromRotationTranslation( + targetRayTransform, + normalize$2(orientation, orientation), + map.targetRayTransform.position || [0, 0, 0] + ); + } + let profiles = map.profiles; + if (map.displayProfiles) { + if (display.displayName in map.displayProfiles) { + profiles = map.displayProfiles[display.displayName]; + } + } + this[PRIVATE$14] = { + gamepad, + map, + profiles: profiles || [gamepad.id], + mapping: map.mapping || gamepad.mapping, + axes, + buttons, + gripTransform, + targetRayTransform, + }; + this._update(); + } + _update() { + let gamepad = this[PRIVATE$14].gamepad; + let map = this[PRIVATE$14].map; + let axes = this[PRIVATE$14].axes; + for (let i = 0; i < axes.length; ++i) { + if (map.axes && i in map.axes) { + if (map.axes[i] === null) { + axes[i] = 0; + } else { + axes[i] = gamepad.axes[map.axes[i]]; + } + } else { + axes[i] = gamepad.axes[i]; + } + } + if (map.axes && map.axes.invert) { + for (let axis of map.axes.invert) { + axes[axis] *= -1; + } + } + let buttons = this[PRIVATE$14].buttons; + for (let i = 0; i < buttons.length; ++i) { + if (map.buttons && i in map.buttons) { + if (map.buttons[i] === null) { + buttons[i] = PLACEHOLDER_BUTTON; + } else { + buttons[i] = gamepad.buttons[map.buttons[i]]; + } + } else { + buttons[i] = gamepad.buttons[i]; + } + } + } + get id() { + return ''; + } + get _profiles() { + return this[PRIVATE$14].profiles; + } + get index() { + return -1; + } + get connected() { + return this[PRIVATE$14].gamepad.connected; + } + get timestamp() { + return this[PRIVATE$14].gamepad.timestamp; + } + get mapping() { + return this[PRIVATE$14].mapping; + } + get axes() { + return this[PRIVATE$14].axes; + } + get buttons() { + return this[PRIVATE$14].buttons; + } +} +class GamepadXRInputSource { + constructor(polyfill, display, primaryButtonIndex = 0) { + this.polyfill = polyfill; + this.display = display; + this.nativeGamepad = null; + this.gamepad = null; + this.inputSource = new XRInputSource(this); + this.lastPosition = create$1(); + this.emulatedPosition = false; + this.basePoseMatrix = create(); + this.outputMatrix = create(); + this.primaryButtonIndex = primaryButtonIndex; + this.primaryActionPressed = false; + this.handedness = ''; + this.targetRayMode = 'gaze'; + this.armModel = null; + } + get profiles() { + return this.gamepad ? this.gamepad._profiles : []; + } + updateFromGamepad(gamepad) { + if (this.nativeGamepad !== gamepad) { + this.nativeGamepad = gamepad; + if (gamepad) { + this.gamepad = new XRRemappedGamepad(gamepad, this.display, GamepadMappings[gamepad.id]); + } else { + this.gamepad = null; + } + } + this.handedness = gamepad.hand; + if (this.gamepad) { + this.gamepad._update(); + } + if (gamepad.pose) { + this.targetRayMode = 'tracked-pointer'; + this.emulatedPosition = !gamepad.pose.hasPosition; + } else if (gamepad.hand === '') { + this.targetRayMode = 'gaze'; + this.emulatedPosition = false; + } + } + updateBasePoseMatrix() { + if (this.nativeGamepad && this.nativeGamepad.pose) { + let pose = this.nativeGamepad.pose; + let position = pose.position; + let orientation = pose.orientation; + if (!position && !orientation) { + return; + } + if (!position) { + if (!pose.hasPosition) { + if (!this.armModel) { + this.armModel = new OrientationArmModel(); + } + this.armModel.setHandedness(this.nativeGamepad.hand); + this.armModel.update(orientation, this.polyfill.getBasePoseMatrix()); + position = this.armModel.getPosition(); + } else { + position = this.lastPosition; + } + } else { + this.lastPosition[0] = position[0]; + this.lastPosition[1] = position[1]; + this.lastPosition[2] = position[2]; + } + fromRotationTranslation(this.basePoseMatrix, orientation, position); + } else { + copy(this.basePoseMatrix, this.polyfill.getBasePoseMatrix()); + } + return this.basePoseMatrix; + } + getXRPose(coordinateSystem, poseType) { + this.updateBasePoseMatrix(); + switch(poseType) { + case "target-ray": + coordinateSystem._transformBasePoseMatrix(this.outputMatrix, this.basePoseMatrix); + if (this.gamepad && this.gamepad[PRIVATE$14].targetRayTransform) { + multiply(this.outputMatrix, this.outputMatrix, this.gamepad[PRIVATE$14].targetRayTransform); + } + break; + case "grip": + if (!this.nativeGamepad || !this.nativeGamepad.pose) { + return null; + } + coordinateSystem._transformBasePoseMatrix(this.outputMatrix, this.basePoseMatrix); + if (this.gamepad && this.gamepad[PRIVATE$14].gripTransform) { + multiply(this.outputMatrix, this.outputMatrix, this.gamepad[PRIVATE$14].gripTransform); + } + break; + default: + return null; + } + coordinateSystem._adjustForOriginOffset(this.outputMatrix); + return new XRPose(new XRRigidTransform(this.outputMatrix), this.emulatedPosition); + } +} + +const TEST_ENV = "production" === 'test'; +const EXTRA_PRESENTATION_ATTRIBUTES = { + highRefreshRate: true, +}; +const PRIMARY_BUTTON_MAP = { + oculus: 1, + openvr: 1, + 'spatial controller (spatial interaction source)': 1 +}; +let SESSION_ID = 0; +class Session { + constructor(mode, polyfillOptions={}) { + this.mode = mode; + this.outputContext = null; + this.immersive = mode == 'immersive-vr' || mode == 'immersive-ar'; + this.ended = null; + this.baseLayer = null; + this.inlineVerticalFieldOfView = Math.PI * 0.5; + this.id = ++SESSION_ID; + this.modifiedCanvasLayer = false; + if (this.outputContext && !TEST_ENV) { + const renderContextType = polyfillOptions.renderContextType || '2d'; + this.renderContext = this.outputContext.canvas.getContext(renderContextType); + } + } +} +class WebVRDevice extends XRDevice { + constructor(global, display) { + const { canPresent } = display.capabilities; + super(global); + this.display = display; + this.frame = new global.VRFrameData(); + this.sessions = new Map(); + this.immersiveSession = null; + this.canPresent = canPresent; + this.baseModelMatrix = create(); + this.gamepadInputSources = {}; + this.tempVec3 = new Float32Array(3); + this.onVRDisplayPresentChange = this.onVRDisplayPresentChange.bind(this); + global.window.addEventListener('vrdisplaypresentchange', this.onVRDisplayPresentChange); + this.CAN_USE_GAMEPAD = global.navigator && ('getGamepads' in global.navigator); + this.HAS_BITMAP_SUPPORT = isImageBitmapSupported(global); + } + get depthNear() { return this.display.depthNear; } + set depthNear(val) { this.display.depthNear = val; } + get depthFar() { return this.display.depthFar; } + set depthFar(val) { this.display.depthFar = val; } + onBaseLayerSet(sessionId, layer) { + const session = this.sessions.get(sessionId); + const canvas = layer.context.canvas; + if (session.immersive) { + const left = this.display.getEyeParameters('left'); + const right = this.display.getEyeParameters('right'); + canvas.width = Math.max(left.renderWidth, right.renderWidth) * 2; + canvas.height = Math.max(left.renderHeight, right.renderHeight); + this.display.requestPresent([{ + source: canvas, attributes: EXTRA_PRESENTATION_ATTRIBUTES + }]).then(() => { + if (!TEST_ENV && !this.global.document.body.contains(canvas)) { + session.modifiedCanvasLayer = true; + this.global.document.body.appendChild(canvas); + applyCanvasStylesForMinimalRendering(canvas); + } + session.baseLayer = layer; + }); + } + else { + session.baseLayer = layer; + } + } + onInlineVerticalFieldOfViewSet(sessionId, value) { + const session = this.sessions.get(sessionId); + session.inlineVerticalFieldOfView = value; + } + supportsSession(mode) { + if (XRSessionModes.indexOf(mode) == -1) { + throw new TypeError( + `The provided value '${mode}' is not a valid enum value of type XRSessionMode`); + } + if (mode == 'immersive-ar') { + return false; + } + if (mode == 'immersive-vr' && this.canPresent === false) { + return false; + } + return true; + } + async requestSession(mode) { + if (!this.supportsSession(mode)) { + return Promise.reject(); + } + let immersive = mode == 'immersive-vr'; + if (immersive) { + const canvas = this.global.document.createElement('canvas'); + if (!TEST_ENV) { + const ctx = canvas.getContext('webgl'); + } + await this.display.requestPresent([{ + source: canvas, attributes: EXTRA_PRESENTATION_ATTRIBUTES }]); + } + const session = new Session(mode, { + renderContextType: this.HAS_BITMAP_SUPPORT ? 'bitmaprenderer' : '2d' + }); + this.sessions.set(session.id, session); + if (immersive) { + this.immersiveSession = session; + this.dispatchEvent('@@webxr-polyfill/vr-present-start', session.id); + } + return Promise.resolve(session.id); + } + requestAnimationFrame(callback) { + return this.display.requestAnimationFrame(callback); + } + getPrimaryButtonIndex(gamepad) { + let primaryButton = 0; + let name = gamepad.id.toLowerCase(); + for (let key in PRIMARY_BUTTON_MAP) { + if (name.includes(key)) { + primaryButton = PRIMARY_BUTTON_MAP[key]; + break; + } + } + return Math.min(primaryButton, gamepad.buttons.length - 1); + } + onFrameStart(sessionId) { + this.display.getFrameData(this.frame); + const session = this.sessions.get(sessionId); + if (session.immersive && this.CAN_USE_GAMEPAD) { + let prevInputSources = this.gamepadInputSources; + this.gamepadInputSources = {}; + let gamepads = this.global.navigator.getGamepads(); + for (let i = 0; i < gamepads.length; ++i) { + let gamepad = gamepads[i]; + if (gamepad && gamepad.displayId > 0) { + let inputSourceImpl = prevInputSources[i]; + if (!inputSourceImpl) { + inputSourceImpl = new GamepadXRInputSource(this, this.display, this.getPrimaryButtonIndex(gamepad)); + } + inputSourceImpl.updateFromGamepad(gamepad); + this.gamepadInputSources[i] = inputSourceImpl; + if (inputSourceImpl.primaryButtonIndex != -1) { + let primaryActionPressed = gamepad.buttons[inputSourceImpl.primaryButtonIndex].pressed; + if (primaryActionPressed && !inputSourceImpl.primaryActionPressed) { + this.dispatchEvent('@@webxr-polyfill/input-select-start', { sessionId: session.id, inputSource: inputSourceImpl.inputSource }); + } else if (!primaryActionPressed && inputSourceImpl.primaryActionPressed) { + this.dispatchEvent('@@webxr-polyfill/input-select-end', { sessionId: session.id, inputSource: inputSourceImpl.inputSource }); + } + inputSourceImpl.primaryActionPressed = primaryActionPressed; + } + } + } + } + if (TEST_ENV) { + return; + } + if (!session.immersive && session.baseLayer) { + const canvas = session.baseLayer.context.canvas; + perspective(this.frame.leftProjectionMatrix, session.inlineVerticalFieldOfView, + canvas.width/canvas.height, this.depthNear, this.depthFar); + } + } + onFrameEnd(sessionId) { + const session = this.sessions.get(sessionId); + if (session.ended || !session.baseLayer) { + return; + } + if (session.outputContext && + !(session.immersive && !this.display.capabilities.hasExternalDisplay)) { + const mirroring = + session.immersive && this.display.capabilities.hasExternalDisplay; + const iCanvas = session.baseLayer.context.canvas; + const iWidth = mirroring ? iCanvas.width / 2 : iCanvas.width; + const iHeight = iCanvas.height; + if (!TEST_ENV) { + const oCanvas = session.outputContext.canvas; + const oWidth = oCanvas.width; + const oHeight = oCanvas.height; + const renderContext = session.renderContext; + if (this.HAS_BITMAP_SUPPORT) { + if (iCanvas.transferToImageBitmap) { + renderContext.transferFromImageBitmap(iCanvas.transferToImageBitmap()); + } + else { + this.global.createImageBitmap(iCanvas, 0, 0, iWidth, iHeight, { + resizeWidth: oWidth, + resizeHeight: oHeight, + }).then(bitmap => renderContext.transferFromImageBitmap(bitmap)); + } + } else { + renderContext.drawImage(iCanvas, 0, 0, iWidth, iHeight, + 0, 0, oWidth, oHeight); + } + } + } + if (session.immersive && session.baseLayer) { + this.display.submitFrame(); + } + } + cancelAnimationFrame(handle) { + this.display.cancelAnimationFrame(handle); + } + async endSession(sessionId) { + const session = this.sessions.get(sessionId); + if (session.ended) { + return; + } + if (session.immersive) { + return this.display.exitPresent(); + } else { + session.ended = true; + } + } + requestStageBounds() { + if (this.display.stageParameters) { + const width = this.display.stageParameters.sizeX; + const depth = this.display.stageParameters.sizeZ; + const data = []; + data.push(-width / 2); + data.push(-depth / 2); + data.push(width / 2); + data.push(-depth / 2); + data.push(width / 2); + data.push(depth / 2); + data.push(-width / 2); + data.push(depth / 2); + return data; + } + return null; + } + async requestFrameOfReferenceTransform(type, options) { + if ((type === 'local-floor' || type === 'bounded-floor') && + this.display.stageParameters && + this.display.stageParameters.sittingToStandingTransform) { + return this.display.stageParameters.sittingToStandingTransform; + } + return null; + } + getProjectionMatrix(eye) { + if (eye === 'left') { + return this.frame.leftProjectionMatrix; + } else if (eye === 'right') { + return this.frame.rightProjectionMatrix; + } else if (eye === 'none') { + return this.frame.leftProjectionMatrix; + } else { + throw new Error(`eye must be of type 'left' or 'right'`); + } + } + getViewport(sessionId, eye, layer, target) { + const session = this.sessions.get(sessionId); + const { width, height } = layer.context.canvas; + if (!session.immersive) { + target.x = target.y = 0; + target.width = width; + target.height = height; + return true; + } + if (eye === 'left') { + target.x = 0; + } else if (eye === 'right') { + target.x = width / 2; + } else { + return false; + } + target.y = 0; + target.width = width / 2; + target.height = height; + return true; + } + getBasePoseMatrix() { + let { position, orientation } = this.frame.pose; + if (!position && !orientation) { + return this.baseModelMatrix; + } + if (!position) { + position = this.tempVec3; + position[0] = position[1] = position[2] = 0; + } + fromRotationTranslation(this.baseModelMatrix, orientation, position); + return this.baseModelMatrix; + } + getBaseViewMatrix(eye) { + if (eye === 'left') { + return this.frame.leftViewMatrix; + } else if (eye === 'right') { + return this.frame.rightViewMatrix; + } else { + throw new Error(`eye must be of type 'left' or 'right'`); + } + } + getInputSources() { + let inputSources = []; + for (let i in this.gamepadInputSources) { + inputSources.push(this.gamepadInputSources[i].inputSource); + } + return inputSources; + } + getInputPose(inputSource, coordinateSystem, poseType) { + if (!coordinateSystem) { + return null; + } + for (let i in this.gamepadInputSources) { + let inputSourceImpl = this.gamepadInputSources[i]; + if (inputSourceImpl.inputSource === inputSource) { + return inputSourceImpl.getXRPose(coordinateSystem, poseType); + } + } + return null; + } + onWindowResize() { + } + onVRDisplayPresentChange(e) { + if (!this.display.isPresenting) { + this.sessions.forEach(session => { + if (session.immersive && !session.ended) { + if (session.modifiedCanvasLayer) { + const canvas = session.baseLayer.context.canvas; + document.body.removeChild(canvas); + canvas.setAttribute('style', ''); + } + if (this.immersiveSession === session) { + this.immersiveSession = null; + } + this.dispatchEvent('@@webxr-polyfill/vr-present-end', session.id); + } + }); + } + } +} + +class CardboardXRDevice extends WebVRDevice { + constructor(global, cardboardConfig) { + const display = new CardboardVRDisplay(cardboardConfig || {}); + super(global, display); + this.display = display; + this.frame = { + rightViewMatrix: new Float32Array(16), + leftViewMatrix: new Float32Array(16), + rightProjectionMatrix: new Float32Array(16), + leftProjectionMatrix: new Float32Array(16), + pose: null, + timestamp: null, + }; + } +} + +const getWebVRDevice = async function (global) { + let device = null; + if ('getVRDisplays' in global.navigator) { + try { + const displays = await global.navigator.getVRDisplays(); + if (displays && displays.length) { + device = new WebVRDevice(global, displays[0]); + } + } catch (e) {} + } + return device; +}; +const requestXRDevice = async function (global, config) { + if (config.webvr) { + let xr = await getWebVRDevice(global); + if (xr) { + return xr; + } + } + if (!global.VRFrameData) { + global.VRFrameData = function () { + this.rightViewMatrix = new Float32Array(16); + this.leftViewMatrix = new Float32Array(16); + this.rightProjectionMatrix = new Float32Array(16); + this.leftProjectionMatrix = new Float32Array(16); + this.pose = null; + }; + } + return new CardboardXRDevice(global, config.cardboardConfig); +}; + +const CONFIG_DEFAULTS = { + global: _global, + webvr: true, + cardboard: true, + cardboardConfig: null, + allowCardboardOnDesktop: false, +}; +const partials = ['navigator', 'HTMLCanvasElement', 'WebGLRenderingContext']; +class WebXRPolyfill { + constructor(config={}) { + this.config = Object.freeze(Object.assign({}, CONFIG_DEFAULTS, config)); + this.global = this.config.global; + this.nativeWebXR = 'xr' in this.global.navigator; + this.injected = false; + if (!this.nativeWebXR) { + this._injectPolyfill(this.global); + } else { + this._injectCompatibilityShims(this.global); + } + } + _injectPolyfill(global) { + if (!partials.every(iface => !!global[iface])) { + throw new Error(`Global must have the following attributes : ${partials}`); + } + for (const className of Object.keys(API)) { + if (global[className] !== undefined) { + console.warn(`${className} already defined on global.`); + } else { + global[className] = API[className]; + } + } + { + const polyfilledCtx = polyfillMakeXRCompatible(global.WebGLRenderingContext); + if (polyfilledCtx) { + polyfillGetContext(global.HTMLCanvasElement); + if (global.OffscreenCanvas) { + polyfillGetContext(global.OffscreenCanvas); + } + if (global.WebGL2RenderingContext){ + polyfillMakeXRCompatible(global.WebGL2RenderingContext); + } + } + } + this.injected = true; + this._patchNavigatorXR(); + } + _patchNavigatorXR() { + let devicePromise = requestXRDevice(this.global, this.config); + this.xr = new XR(devicePromise); + Object.defineProperty(this.global.navigator, 'xr', { + value: this.xr, + configurable: true, + }); + } + _injectCompatibilityShims(global) { + if (!partials.every(iface => !!global[iface])) { + throw new Error(`Global must have the following attributes : ${partials}`); + } + if (global.XRWebGLLayer) { + let originalRequestSession = global.navigator.xr.requestSession; + global.navigator.xr.requestSession = function(mode, options) { + return originalRequestSession.call(this, mode, options).then((session) => { + session._session_mode = mode; + return session; + }); + }; + var originalXRLayer = global.XRWebGLLayer; + global.XRWebGLLayer = function(session, gl, options) { + if (!options) { + options = {}; + } + options.compositionDisabled = (session._session_mode === "inline"); + return new originalXRLayer(session, gl, options); + }; + } + } +} + +new WebXRPolyfill(); + +}