diff --git a/docs/html/backEnd.js.html b/docs/html/backEnd.js.html index 943143425..3956a50b4 100644 --- a/docs/html/backEnd.js.html +++ b/docs/html/backEnd.js.html @@ -1,6 +1,6 @@ Source: backEnd.js
On this page

backEnd.js

/**
+    
On this page

backEnd.js

/**
  * @file Sets up an Express backend server for examples, serving static files,
  * replacing HTML strings, and enabling a game socket service.
  * The behavior adapts based on the environment mode (NODE_ENV) inject in process.env with cross-env package.
@@ -108,27 +108,32 @@
       new Object3D({
         name: 'Note game',
         static: true,
+        matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1844753.5, 5174961, 0, 1],
         components: {
           GameScript: {
-            idScripts: [NoteManager.ID_SCRIPT, NativeCommandManager.ID_SCRIPT],
+            scriptParams: [
+              { id: NoteManager.ID_SCRIPT },
+              { id: NativeCommandManager.ID_SCRIPT },
+            ],
           },
           ExternalScript: {
-            idScripts: [
-              constant.ID_SCRIPT.NOTE_UI,
-              constant.ID_SCRIPT.CAMERA_MANAGER,
+            scriptParams: [
+              { id: constant.ID_SCRIPT.NOTE_UI },
+              { id: constant.ID_SCRIPT.CAMERA_MANAGER },
             ],
           },
         },
       }),
       new Object3D({
         uuid: 'dom_element_3d_cube_game_uuid',
-        name: 'Avatar jitsi game',
+        name: 'DomElement 3D cube game',
         static: true,
+        matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1844753.5, 5174961, 0, 1],
         components: {
           GameScript: {
-            idScripts: [
-              DomElement3DCubeManager.ID_SCRIPT,
-              NativeCommandManager.ID_SCRIPT,
+            scriptParams: [
+              { id: DomElement3DCubeManager.ID_SCRIPT },
+              { id: NativeCommandManager.ID_SCRIPT },
             ],
             variables: {
               idRenderData: 'cube',
@@ -142,7 +147,7 @@
             },
           },
           ExternalScript: {
-            idScripts: [],
+            scriptParams: [],
           },
         },
       }),
@@ -157,4 +162,4 @@
     }
     reload(app, { port: RELOAD_PORT });
   });
-
\ No newline at end of file +
@ud-viz
\ No newline at end of file diff --git a/docs/html/devExamples.js.html b/docs/html/devExamples.js.html index a0e313f8a..7a9f2efe1 100644 --- a/docs/html/devExamples.js.html +++ b/docs/html/devExamples.js.html @@ -1,6 +1,6 @@ Source: devExamples.js
On this page

devExamples.js

/**
+    
On this page

devExamples.js

/**
  * @file This script executes build and runs the backend script.
  *
  * requires {@link https://www.npmjs.com/package/child-process-promise}
@@ -58,4 +58,4 @@
 };
 
 exec('npm run build-dev-examples').then(printExec).then(runBackEnd);
-
\ No newline at end of file +
@ud-viz
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalBatchTable.html b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalBatchTable.html index bf64fdbf9..d0dd79c7a 100644 --- a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalBatchTable.html +++ b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalBatchTable.html @@ -1,3 +1,3 @@ Class: C3DTTemporalBatchTable
On this page

C3DTTemporalBatchTable

Implements the batch table of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.batchTable.schema.json

Constructor

new C3DTTemporalBatchTable(json)

Verifies the integrity and stores the data corresponding to the batch table part of the 3DTiles_temporal extension.

Parameters:
NameTypeDescription
jsonobject

The json containing the 3DTiles_temporal extension batch table part for a given tile.

Classes

C3DTTemporalBatchTable

Methods

getInfoById(batchId) → {Object}

Returns information for the given batchId. Can be used to display information associated with an object picked with the mouse for instance.

Parameters:
NameTypeDescription
batchIdnumber

The given identifier (identifier in the batch, i.e. position in the arrays).

Returns:

object containing infos

Type: 
Object

hasInfoForId(batchId) → {boolean}

Checks that the batch table temporal extension has values for a given identifier.

Parameters:
NameTypeDescription
batchIdnumber

The identifier to check (identifier in the batch, i.e. position in the arrays).

Returns:

True if this.startDates[batchId] is filled

Type: 
boolean
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

C3DTTemporalBatchTable

Implements the batch table of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.batchTable.schema.json

Constructor

new C3DTTemporalBatchTable(json)

Verifies the integrity and stores the data corresponding to the batch table part of the 3DTiles_temporal extension.

Parameters:
NameTypeDescription
jsonobject

The json containing the 3DTiles_temporal extension batch table part for a given tile.

Classes

C3DTTemporalBatchTable

Methods

getInfoById(batchId) → {Object}

Returns information for the given batchId. Can be used to display information associated with an object picked with the mouse for instance.

Parameters:
NameTypeDescription
batchIdnumber

The given identifier (identifier in the batch, i.e. position in the arrays).

Returns:

object containing infos

Type: 
Object

hasInfoForId(batchId) → {boolean}

Checks that the batch table temporal extension has values for a given identifier.

Parameters:
NameTypeDescription
batchIdnumber

The identifier to check (identifier in the batch, i.e. position in the arrays).

Returns:

True if this.startDates[batchId] is filled

Type: 
boolean
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalBoundingVolume.html b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalBoundingVolume.html index a4e0539c4..4cba9cdd4 100644 --- a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalBoundingVolume.html +++ b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalBoundingVolume.html @@ -1,3 +1,3 @@ Class: C3DTTemporalBoundingVolume
On this page

C3DTTemporalBoundingVolume

new C3DTTemporalBoundingVolume(json)

Verifies the integrity and stores the data corresponding to the bounding volume part of the 3DTiles_temporal extension.

Parameters:
NameTypeDescription
jsonobject

The json containing the 3DTiles_temporal extension bounding volume part for a given tile.

Classes

C3DTTemporalBoundingVolume

Members

endDate :number

Type:
  • number

startDate :number

Type:
  • number
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

C3DTTemporalBoundingVolume

new C3DTTemporalBoundingVolume(json)

Verifies the integrity and stores the data corresponding to the bounding volume part of the 3DTiles_temporal extension.

Parameters:
NameTypeDescription
jsonobject

The json containing the 3DTiles_temporal extension bounding volume part for a given tile.

Classes

C3DTTemporalBoundingVolume

Members

endDate :number

Type:
  • number

startDate :number

Type:
  • number
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalPrimaryTransaction.html b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalPrimaryTransaction.html index c230e6b28..c38bbd543 100644 --- a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalPrimaryTransaction.html +++ b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalPrimaryTransaction.html @@ -1,3 +1,3 @@ Class: C3DTTemporalPrimaryTransaction
On this page

C3DTTemporalPrimaryTransaction

Implements the primary transaction of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.primaryTransaction.schema.json

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

C3DTTemporalPrimaryTransaction

Implements the primary transaction of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.primaryTransaction.schema.json

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTileset.html b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTileset.html index 2db7dd928..6da7b6633 100644 --- a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTileset.html +++ b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTileset.html @@ -1,3 +1,3 @@ Class: C3DTTemporalTileset
On this page

C3DTTemporalTileset

Implements the tileset part of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.tileset.schema.json

Constructor

new C3DTTemporalTileset(json)

A constructor function that creates a new object.

Parameters:
NameTypeDescription
jsonobject

The JSON object that was returned from the server.

Properties
NameTypeDescription
startDatestring

startDate

endDatestring

endDate

transactionsobject

transactions

versionsobject

versions

Classes

C3DTTemporalTileset

Members

endDate :string

Type:
  • string

startDate :string

Type:
  • string

temporalVersions :C3DTTemporalVersion

transactions :Array.<object>

Type:
  • Array.<object>

versionTransitions :object

Type:
  • object

Methods

parseTransactions(transactions)

Parses transactions from a json file and creates primary and aggregated transactions.

Parameters:
NameTypeDescription
transactionsobject

The json holding the transactions.

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

C3DTTemporalTileset

Implements the tileset part of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.tileset.schema.json

Constructor

new C3DTTemporalTileset(json)

A constructor function that creates a new object.

Parameters:
NameTypeDescription
jsonobject

The JSON object that was returned from the server.

Properties
NameTypeDescription
startDatestring

startDate

endDatestring

endDate

transactionsobject

transactions

versionsobject

versions

Classes

C3DTTemporalTileset

Members

endDate :string

Type:
  • string

startDate :string

Type:
  • string

temporalVersions :C3DTTemporalVersion

transactions :Array.<object>

Type:
  • Array.<object>

versionTransitions :object

Type:
  • object

Methods

parseTransactions(transactions)

Parses transactions from a json file and creates primary and aggregated transactions.

Parameters:
NameTypeDescription
transactionsobject

The json holding the transactions.

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTransaction.html b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTransaction.html index c1b44d8a1..52702554a 100644 --- a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTransaction.html +++ b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTransaction.html @@ -1,3 +1,3 @@ Class: C3DTTemporalTransaction
On this page

C3DTTemporalTransaction

Implements the transaction concept of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.transaction.schema.json

Constructor

new C3DTTemporalTransaction(json)

The constructor function is a special function that is called when an object is created from a class

Parameters:
NameTypeDescription
jsonobject

The JSON object that is passed in from the server.

Properties
NameTypeDescription
idnumber

id.

startDateany

startDate.

endDateany

endDate.

sourceany

source.

destinationstring

destination.

tagsstring

tags.

Classes

C3DTTemporalTransaction

Members

destination :string

Type:
  • string

endDate :any

Type:
  • any

id :number

Type:
  • number

source :any

Type:
  • any

startDate :any

Type:
  • any

tags :string

Type:
  • string
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

C3DTTemporalTransaction

Implements the transaction concept of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.transaction.schema.json

Constructor

new C3DTTemporalTransaction(json)

The constructor function is a special function that is called when an object is created from a class

Parameters:
NameTypeDescription
jsonobject

The JSON object that is passed in from the server.

Properties
NameTypeDescription
idnumber

id.

startDateany

startDate.

endDateany

endDate.

sourceany

source.

destinationstring

destination.

tagsstring

tags.

Classes

C3DTTemporalTransaction

Members

destination :string

Type:
  • string

endDate :any

Type:
  • any

id :number

Type:
  • number

source :any

Type:
  • any

startDate :any

Type:
  • any

tags :string

Type:
  • string
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTransactionAggregate.html b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTransactionAggregate.html index 28cd593fa..522622fa7 100644 --- a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTransactionAggregate.html +++ b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalTransactionAggregate.html @@ -1,3 +1,3 @@ Class: C3DTTemporalTransactionAggregate
On this page

C3DTTemporalTransactionAggregate

Implements the aggregated transaction of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.transactionAggregate.schema.json

Constructor

new C3DTTemporalTransactionAggregate(json)

It's a constructor for the Aggregate class

Parameters:
NameTypeDescription
jsonobject

The JSON object that is used to create the transaction.

Classes

C3DTTemporalTransactionAggregate

Members

isAggregate :boolean

A boolean flag that is used to check if the object is an aggregate.

Type:
  • boolean

transactions :object

Creating an empty object.

Type:
  • object
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

C3DTTemporalTransactionAggregate

Implements the aggregated transaction of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.transactionAggregate.schema.json

Constructor

new C3DTTemporalTransactionAggregate(json)

It's a constructor for the Aggregate class

Parameters:
NameTypeDescription
jsonobject

The JSON object that is used to create the transaction.

Classes

C3DTTemporalTransactionAggregate

Members

isAggregate :boolean

A boolean flag that is used to check if the object is an aggregate.

Type:
  • boolean

transactions :object

Creating an empty object.

Type:
  • object
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalVersion.html b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalVersion.html index 6ac5769e2..6f8d9840a 100644 --- a/docs/html/extensions_3d_tiles_temporal/C3DTTemporalVersion.html +++ b/docs/html/extensions_3d_tiles_temporal/C3DTTemporalVersion.html @@ -1,3 +1,3 @@ Class: C3DTTemporalVersion
On this page

C3DTTemporalVersion

Implements the version concept of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.version.schema.json

Constructor

new C3DTTemporalVersion(json)

It takes a JSON object as input, and returns a JSON object that can be used by the graph window

Parameters:
NameTypeDescription
jsonArray.<object>

the json object containing the data

Properties
NameTypeDescription
namestring

name

inumber

index

groupstring

group

descriptionstring

description

Classes

C3DTTemporalVersion

Members

versions :Array.<{label:string, level:number, group:string, title:string}>

Assigning the value of the parameter json to the property versions of the object.

Type:
  • Array.<{label:string, level:number, group:string, title:string}>
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

C3DTTemporalVersion

Implements the version concept of the 3DTILES_temporal extension. See the spec in ./jsonSchemas/3DTILES_temporal.version.schema.json

Constructor

new C3DTTemporalVersion(json)

It takes a JSON object as input, and returns a JSON object that can be used by the graph window

Parameters:
NameTypeDescription
jsonArray.<object>

the json object containing the data

Properties
NameTypeDescription
namestring

name

inumber

index

groupstring

group

descriptionstring

description

Classes

C3DTTemporalVersion

Members

versions :Array.<{label:string, level:number, group:string, title:string}>

Assigning the value of the parameter json to the property versions of the object.

Type:
  • Array.<{label:string, level:number, group:string, title:string}>
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/Temporal3DTilesLayerWrapper_Temporal3DTilesLayerWrapper.html b/docs/html/extensions_3d_tiles_temporal/Temporal3DTilesLayerWrapper_Temporal3DTilesLayerWrapper.html index cc4a2bcd8..7ebb0113d 100644 --- a/docs/html/extensions_3d_tiles_temporal/Temporal3DTilesLayerWrapper_Temporal3DTilesLayerWrapper.html +++ b/docs/html/extensions_3d_tiles_temporal/Temporal3DTilesLayerWrapper_Temporal3DTilesLayerWrapper.html @@ -1,3 +1,3 @@ Class: Temporal3DTilesLayerWrapper
On this page

Temporal3DTilesLayerWrapper

new Temporal3DTilesLayerWrapper(temporalC3DTilesLayer)

A constructor that initializes a temporal style for a C3DTilesLayer by computing tile maps based on the 3DTILES_temporal batch table hierarchy content found in the tile content.

Parameters:
NameTypeDescription
temporalC3DTilesLayeritowns.C3DTilesLayer

An instance of the itowns.C3DTilesLayer class. It represents a layer that displays 3D tiles with temporal data.

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

Temporal3DTilesLayerWrapper

new Temporal3DTilesLayerWrapper(temporalC3DTilesLayer)

A constructor that initializes a temporal style for a C3DTilesLayer by computing tile maps based on the 3DTILES_temporal batch table hierarchy content found in the tile content.

Parameters:
NameTypeDescription
temporalC3DTilesLayeritowns.C3DTilesLayer

An instance of the itowns.C3DTilesLayer class. It represents a layer that displays 3D tiles with temporal data.

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/index.html b/docs/html/extensions_3d_tiles_temporal/index.html index 0ba7f24fa..b2c367515 100644 --- a/docs/html/extensions_3d_tiles_temporal/index.html +++ b/docs/html/extensions_3d_tiles_temporal/index.html @@ -1,3 +1,3 @@ Home
On this page

@ud-viz/extensions_3d_tiles_temporal

NPM package version

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

@ud-viz/extensions_3d_tiles_temporal

NPM package version

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/index.js.html b/docs/html/extensions_3d_tiles_temporal/index.js.html index 383fb5b67..1184f8a92 100644 --- a/docs/html/extensions_3d_tiles_temporal/index.js.html +++ b/docs/html/extensions_3d_tiles_temporal/index.js.html @@ -1,6 +1,6 @@ Source: index.js
On this page

index.js

import { C3DTTemporalBatchTable } from './model/C3DTTemporalBatchTable';
+    
On this page

index.js

import { C3DTTemporalBatchTable } from './model/C3DTTemporalBatchTable';
 import { C3DTTemporalBoundingVolume } from './model/C3DTTemporalBoundingVolume';
 import { C3DTTemporalTileset } from './model/C3DTTemporalTileset';
 import * as itowns from 'itowns';
@@ -200,8 +200,6 @@
 
         if (this._styleDate == null)
           this.styleDate = this.knownDatesForAllTiles[0]; // init with a default value
-        // TODO: because onTileContentLoaded of C3DTilesLayer is doing initFeature/updateStyle/dispatchEvent so this is called after updateStyle
-        this.temporalC3DTilesLayer.updateStyle([tileContent.tileId]);
       }
     );
 
@@ -272,4 +270,4 @@
     this.temporalC3DTilesLayer.updateStyle();
   }
 }
-
\ No newline at end of file +
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalBatchTable.js.html b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalBatchTable.js.html index 4ddcbe63d..39e731d11 100644 --- a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalBatchTable.js.html +++ b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalBatchTable.js.html @@ -1,6 +1,6 @@ Source: model/C3DTTemporalBatchTable.js
On this page

model_C3DTTemporalBatchTable.js

/**
+    
On this page

model_C3DTTemporalBatchTable.js

/**
  * Implements the batch table of the 3DTILES_temporal
  * extension. See the spec in
  * ./jsonSchemas/3DTILES_temporal.batchTable.schema.json
@@ -92,4 +92,4 @@
     };
   }
 }
-
\ No newline at end of file +
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalBoundingVolume.js.html b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalBoundingVolume.js.html index ffbe2216f..c4f3890ab 100644 --- a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalBoundingVolume.js.html +++ b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalBoundingVolume.js.html @@ -1,6 +1,6 @@ Source: model/C3DTTemporalBoundingVolume.js
On this page

model_C3DTTemporalBoundingVolume.js

/**
+    
On this page

model_C3DTTemporalBoundingVolume.js

/**
  * Implements the bounding volume part of the 3DTILES_temporal
  * extension. See the spec in
  * ./jsonSchemas/3DTILES_temporal.boundingVolume.schema.json
@@ -35,4 +35,4 @@
     this.endDate = json.endDate;
   }
 }
-
\ No newline at end of file +
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalPrimaryTransaction.js.html b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalPrimaryTransaction.js.html index be6bbcffe..1fc4f50b3 100644 --- a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalPrimaryTransaction.js.html +++ b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalPrimaryTransaction.js.html @@ -1,6 +1,6 @@ Source: model/C3DTTemporalPrimaryTransaction.js
On this page

model_C3DTTemporalPrimaryTransaction.js

import { C3DTTemporalTransaction } from './C3DTTemporalTransaction';
+    
On this page

model_C3DTTemporalPrimaryTransaction.js

import { C3DTTemporalTransaction } from './C3DTTemporalTransaction';
 
 /**
  * Implements the primary transaction of the 3DTILES_temporal
@@ -25,4 +25,4 @@
     this.isPrimary = true;
   }
 }
-
\ No newline at end of file +
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTileset.js.html b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTileset.js.html index a86209596..2af3406d2 100644 --- a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTileset.js.html +++ b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTileset.js.html @@ -1,6 +1,6 @@ Source: model/C3DTTemporalTileset.js
On this page

model_C3DTTemporalTileset.js

import { C3DTTemporalPrimaryTransaction } from './C3DTTemporalPrimaryTransaction.js';
+    
On this page

model_C3DTTemporalTileset.js

import { C3DTTemporalPrimaryTransaction } from './C3DTTemporalPrimaryTransaction.js';
 import { C3DTTemporalTransactionAggregate } from './C3DTTemporalTransactionAggregate.js';
 import { C3DTTemporalVersion } from './C3DTTemporalVersion.js';
 
@@ -62,4 +62,4 @@
     }
   }
 }
-
\ No newline at end of file +
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTransaction.js.html b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTransaction.js.html index 1c1c70311..12504f13d 100644 --- a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTransaction.js.html +++ b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTransaction.js.html @@ -1,6 +1,6 @@ Source: model/C3DTTemporalTransaction.js
On this page

model_C3DTTemporalTransaction.js

/**
+    
On this page

model_C3DTTemporalTransaction.js

/**
  * Implements the transaction concept of the 3DTILES_temporal
  * extension. See the spec in
  * ./jsonSchemas/3DTILES_temporal.transaction.schema.json
@@ -35,4 +35,4 @@
     this.tags = json.tags;
   }
 }
-
\ No newline at end of file +
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTransactionAggregate.js.html b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTransactionAggregate.js.html index 313fcf44f..bc6a95c2e 100644 --- a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTransactionAggregate.js.html +++ b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalTransactionAggregate.js.html @@ -1,6 +1,6 @@ Source: model/C3DTTemporalTransactionAggregate.js
On this page

model_C3DTTemporalTransactionAggregate.js

import { C3DTTemporalTransaction } from './C3DTTemporalTransaction';
+    
On this page

model_C3DTTemporalTransactionAggregate.js

import { C3DTTemporalTransaction } from './C3DTTemporalTransaction';
 
 /**
  * Implements the aggregated transaction of the 3DTILES_temporal
@@ -34,4 +34,4 @@
     this.isAggregate = true;
   }
 }
-
\ No newline at end of file +
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalVersion.js.html b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalVersion.js.html index aeac3e76b..091c1d24f 100644 --- a/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalVersion.js.html +++ b/docs/html/extensions_3d_tiles_temporal/model_C3DTTemporalVersion.js.html @@ -1,6 +1,6 @@ Source: model/C3DTTemporalVersion.js
On this page

model_C3DTTemporalVersion.js

/**
+    
On this page

model_C3DTTemporalVersion.js

/**
  * Implements the version concept of the 3DTILES_temporal
  * extension. See the spec in
  * ./jsonSchemas/3DTILES_temporal.version.schema.json
@@ -33,4 +33,4 @@
     }
   }
 }
-
\ No newline at end of file +
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-Changelog.html b/docs/html/extensions_3d_tiles_temporal/tutorial-Changelog.html index 2ee35ab33..838cbe939 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-Changelog.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-Changelog.html @@ -1,6 +1,17 @@ Tutorial: Changelog
On this page

Changelog

v4.0.1

@ud-viz

- Examples:
+    
On this page

Changelog

v4.0.2

@ud-viz

- npm cmd webpack-bundle-analyzer 
+- script setPackageVersion.js
+- update to itowns 2.41.1-next.9
+

@ud-viz/game_shared

- compute bandwidth
+- idScritp:string => scriptParam:{id:string, priority:number}
+- applyCommandCallback API
+- update(ColliderComp): add SHAPE_TYPE
+

@ud-viz/game_browser_template

- optimization of the camera manager obstacle computation
+

@ud-viz/utils_shared

- computeFilename
+- throttle
+

@ud-viz/game_editor

- creation of the package
+

v4.0.1

@ud-viz

- Examples:
   - point_cloud_visualizer.html
   - game_dom_element_3d_cube.html
   - widget_guided_tour.html
@@ -13,7 +24,7 @@
 

@ud-viz/widget_layer_choice

- Complete refacto
 - Can order ColorLayer
 - LocalStorage ui element
-

@ud-viz/utils_shared

- round
+

@ud-viz/utils_shared

- round
 - vector3ToLabel
 - insert
 

@ud-viz/utils_browser

- createLocalStorageNumberInput
@@ -22,16 +33,16 @@
 - createLocalStorageDetails
 

@ud-viz/utils_node

- fix(test):splice first '/' of req.url, statusCode
 

@ud-viz/game_node_template

- DomElement3DCubeManager script
-

@ud-viz/game_browser_template

- DomElement3DCube script
+

@ud-viz/game_browser_template

- DomElement3DCube script
 

@ud-viz/widget_guided_tour

- Creation of the package
-

v4.0.0

@ud-viz

- Refact(ud-viz)! split into multiples packages
+

v4.0.0

@ud-viz

- Refact(ud-viz)! split into multiples packages
 - Add loading screen in examples
 - Fix show room
 - Add avatar jitsi game example
 - Doc:
     - fix doc
     - update doc
-

v3.2.0

@ud-viz

- Fix socket.io dependencies
+

v3.2.0

@ud-viz

- Fix socket.io dependencies
 - Update with node18
 - Prettier harmonization
 - Improve `Readme.md`
@@ -53,7 +64,7 @@
 - Fix bug Frame3DPlanar render labels
 - THREEUtil encoding => colorSpace
 - Update itowns 2.40.0
-

v3.1.1

@ud-viz

- NPM :
+

v3.1.1

@ud-viz

- NPM :
     - Remove `^` in all package.json
 - Examples:
     - 2D Visualisation Mode
@@ -233,4 +244,4 @@
 * Delete old ifc feature, aimed to be in a demo
 * Don't override material if the material has a texture
 * Remove dependency towards remote data
-
\ No newline at end of file +
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-Contributing.html b/docs/html/extensions_3d_tiles_temporal/tutorial-Contributing.html index eda6bc764..9d5a1e24f 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-Contributing.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-Contributing.html @@ -1,3 +1,3 @@ Tutorial: Contributing
On this page

Contributing

Contributing

Coding style (Linter)

The JavaScript files coding style is defined with eslint through the .eslintrc.js configuration file. It can be checked (e.g. prior to a commit) with the npm run eslint command. Notice that UD-Viz coding style uses a unix linebreak-style (aka LF as newline character).

Submitting an issue

  • Create an issue with explicit name and a description
  • Add at least one label of task category, priority and related package
  • If it's a bug report add steps to reproduce.

Submiting Pull Request (PR)

Prior to PR-submission

  • Commit (git commit) with representative messages (commit messages end-up collected in the PR message and eventually release explanations).
  • Make sure your code is mature for a review.
  • git rebase origin/master, in order to resolve merge conflicts to master. Doc : git-rebase. If you are not able to resolve all of the conflicts of your rebase, please open a new discussion.

note that after a git rebase you have to git push --force

  • npm run eslint must finish with no errors.
  • npm run test-unit must finish with no errors.
  • npm run test-functional must finish with no errors.
  • npm audit must finish without vulnerabilities lower than high.
  • npm run test-links must finish with no errors.

⚠️ When your PR is open each push on your branch will trigger Travis CI jobs.

See here for more information.

Before submitting a pull request, and because UD-Viz still misses some tests, non-regression testing must be done manually. A developer must thus at least check that all the demo examples (they should function similar to their online deployment) are still effective.

Note that you should interact with ui (user interface) for complete tests.

PR Submission

Once your PR is ready to be submitted here are the steps to follow:

  • Create a PR via the Github interface
  • Describe synthetically the PR via the title and the description
  • Add labels if necessary
  • Link the related issues. (The linked issues are automatically closed when the PR is rebased and merged)
  • Add one or more reviewers.
  • Iterate over potential change requests until the PR is approved.
  • Click on Rebase and merge button
  • Delete the branch
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

Contributing

Contributing

Coding style (Linter)

The JavaScript files coding style is defined with eslint through the .eslintrc.js configuration file. It can be checked (e.g. prior to a commit) with the npm run eslint command. Notice that UD-Viz coding style uses a unix linebreak-style (aka LF as newline character).

Submitting an issue

  • Create an issue with explicit name and a description
  • Add at least one label of task category, priority and related package
  • If it's a bug report add steps to reproduce.

Submiting Pull Request (PR)

Prior to PR-submission

  • Commit (git commit) with representative messages (commit messages end-up collected in the PR message and eventually release explanations).
  • Make sure your code is mature for a review.
  • git rebase origin/master, in order to resolve merge conflicts to master. Doc : git-rebase. If you are not able to resolve all of the conflicts of your rebase, please open a new discussion.

note that after a git rebase you have to git push --force

  • npm run eslint must finish with no errors.
  • npm run test-unit must finish with no errors.
  • npm run test-functional must finish with no errors.
  • npm audit must finish without vulnerabilities lower than high.
  • npm run test-links must finish with no errors.

⚠️ When your PR is open each push on your branch will trigger Travis CI jobs.

See here for more information.

Before submitting a pull request, and because UD-Viz still misses some tests, non-regression testing must be done manually. A developer must thus at least check that all the demo examples (they should function similar to their online deployment) are still effective.

Note that you should interact with ui (user interface) for complete tests.

PR Submission

Once your PR is ready to be submitted here are the steps to follow:

  • Create a PR via the Github interface
  • Describe synthetically the PR via the title and the description
  • Add labels if necessary
  • Link the related issues. (The linked issues are automatically closed when the PR is rebased and merged)
  • Add one or more reviewers.
  • Iterate over potential change requests until the PR is approved.
  • Click on Rebase and merge button
  • Delete the branch
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-Contributors.html b/docs/html/extensions_3d_tiles_temporal/tutorial-Contributors.html index 90be827ee..751cd5031 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-Contributors.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-Contributors.html @@ -1,3 +1,3 @@ Tutorial: Contributors
On this page

Contributors

See Contributing.md for details on how to contribute to UD-Viz.

The following people have contributed to UD-Viz:

See the VCity Team Members page here.

Commiters:

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

Contributors

See Contributing.md for details on how to contribute to UD-Viz.

The following people have contributed to UD-Viz:

See the VCity Team Members page here.

Commiters:

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-Definitions.html b/docs/html/extensions_3d_tiles_temporal/tutorial-Definitions.html index 90afcb848..aec4b6394 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-Definitions.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-Definitions.html @@ -1,3 +1,3 @@ Tutorial: Definitions
On this page

Definitions

Definitions (within the UD-Viz context)

  • Component everything thats is necessary to execute only one aspect of a desired functionality (see also module).
  • Widget is used in a itowns context meaning it's a class extending itowns Widget which is a class containing a domElement and an itowns View.
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

Definitions

Definitions (within the UD-Viz context)

  • Component everything thats is necessary to execute only one aspect of a desired functionality (see also module).
  • Widget is used in a itowns context meaning it's a class extending itowns Widget which is a class containing a domElement and an itowns View.
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-Developers.html b/docs/html/extensions_3d_tiles_temporal/tutorial-Developers.html index 5bfbb8961..623289f77 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-Developers.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-Developers.html @@ -1,4 +1,4 @@ Tutorial: Developers
On this page

Developers

Developers

Pre-requisites

Developing UD-Viz applications requires knowledge about :

NVM

Developpers are advised to use node version manager (nvm). nvm allows you to quickly install and use different versions of node via the command line.

To download and install follow this link: https://github.com/nvm-sh/nvm#installing-and-updating

Environment Tips

IDE

VSCode is recommended.

VisualStudio Code

When using Visual Studio Code, you can install the following extentions to make your life easier:

Tips for Windows developers

As configured, the coding style requires a Linux style newline characters which might be overwritten in Windows environments (both by git and/or your editor) to become CRLF. When such changes happen eslint will warn about "incorrect" newline characters (which can always be fixed with npm run eslint-fix but this process quickly gets painful). In order to avoid such difficulties, the recommended pratice consists in

  1. setting git's core.autocrlf to false (e.g. with git config --global core.autocrlf false)
  2. configure your editor/IDE to use Unix-style endings
  3. In order to use scripts that launch a shell script with Powershell: npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"

Npm Scripts

ScriptDescription
npm run cleanRemove files and folders generated by npm install and the npm run build-(dev-)examples script such as ./node_modules, package-lock.json, and ./dist
npm run clear-nodeLinux Command: Kill all node process (this is a sudo script)
npm run resetReinstalls npm dependencies. This script runs npm run clean and npm install command
npm run build-examplesBuild a production bundle base on this webpack config
npm run build-dev-examplesBuild a development bundle base on this webpack config
npm run dev-examplesLaunch a watcher running this script. See here for more information
npm run eslintRun the linter. See here for more information
npm run eslint-quietRun the linter without displaying warnings, only errors
npm run eslint-fixRun the linter and attempt to fix errors and warning automatically
npm run test-unitBuild examples bundle if needed and run npm run test in each packages . Also ran by CI. See here for more information
npm run test-functionalBuild examples bundle if needed and run each *.html in ./examples folder . Also ran by CI. See here for more information
npm run test-linksRun this script to detect dead links in markdown. Also ran by CI. See here for more information
npm run local-ciRun CI on your local computer. See here for more information
npm run pre-publishChange version in all package.json ( eg npm run pre-publish x.x.x ). See this script
npm run docsDelete old docs and run this script which generate docs
npm run dev-docsLaunch a watcher for generating and debugging the documentation. See here for more information
npm run back-endrun an http server (with some string-replace) + a game socket service.
http://locahost:8000/
npm run startRun npm run build-examples and npm run back-end

Debugging the examples

npm run dev-examples
-

Continuous Integration (Travis CI)

Each time origin/master branch is impacted by changes, Travis CI is triggered. It does a set of jobs describe in travis.yml.

Jobs list :

  • npm run eslint: Lint code sources
  • npm run test-unit: Unit test
  • npm run test-functional: Functional test
  • npm audit --audit-level=low: Npm native command (npm-audit) which check packages dependencies vulnerabilities.
  • npm run test-links: Run this script which check if links are not broken.
  • node ./test/docs.js: Run this script which check if documentation is generated without warning or error.

Contributing

For information on the accepted coding style, submitting Issues, and submitting Pull Requests see Contributing.md

Publishing

For creating a new release see ReleasePublish.md

Generating the documentation

The Online documentation can be re-generated by following these instructions.

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

Developers

Developers

Pre-requisites

Developing UD-Viz applications requires knowledge about :

NVM

Developpers are advised to use node version manager (nvm). nvm allows you to quickly install and use different versions of node via the command line.

To download and install follow this link: https://github.com/nvm-sh/nvm#installing-and-updating

Environment Tips

IDE

VSCode is recommended.

VisualStudio Code

When using Visual Studio Code, you can install the following extentions to make your life easier:

Tips for Windows developers

As configured, the coding style requires a Linux style newline characters which might be overwritten in Windows environments (both by git and/or your editor) to become CRLF. When such changes happen eslint will warn about "incorrect" newline characters (which can always be fixed with npm run eslint-fix but this process quickly gets painful). In order to avoid such difficulties, the recommended pratice consists in

  1. setting git's core.autocrlf to false (e.g. with git config --global core.autocrlf false)
  2. configure your editor/IDE to use Unix-style endings
  3. In order to use scripts that launch a shell script with Powershell: npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"

Npm Scripts

ScriptDescription
npm run cleanRemove files and folders generated by npm install and the npm run build-(dev-)examples script such as ./node_modules, package-lock.json, and ./dist
npm run clear-nodeLinux Command: Kill all node process (this is a sudo script)
npm run resetReinstalls npm dependencies. This script runs npm run clean and npm install command
npm run build-examplesBuild a production bundle base on this webpack config
npm run build-dev-examplesBuild a development bundle base on this webpack config
npm run dev-examplesLaunch a watcher running this script. See here for more information
npm run eslintRun the linter. See here for more information
npm run eslint-quietRun the linter without displaying warnings, only errors
npm run eslint-fixRun the linter and attempt to fix errors and warning automatically
npm run test-unitBuild examples bundle if needed and run npm run test in each packages . Also ran by CI. See here for more information
npm run test-functionalBuild examples bundle if needed and run each *.html in ./examples folder . Also ran by CI. See here for more information
npm run test-linksRun this script to detect dead links in markdown. Also ran by CI. See here for more information
npm run local-ciRun CI on your local computer. See here for more information
npm run pre-publishChange version in all package.json ( eg npm run pre-publish x.x.x ). See this script
npm run docsDelete old docs and run this script which generate docs
npm run dev-docsLaunch a watcher for generating and debugging the documentation. See here for more information
npm run back-endrun an http server (with some string-replace) + a game socket service.
http://locahost:8000/
npm run analyze-bundleUse webpack-bundle-analyzer to see what's inside the examples bundle
npm run analyze-dev-bundleUse webpack-bundle-analyzer to see what's inside the examples dev bundle
npm run startRun npm run build-examples and npm run back-end

Debugging the examples

npm run dev-examples
+

Continuous Integration (Travis CI)

Each time origin/master branch is impacted by changes, Travis CI is triggered. It does a set of jobs describe in travis.yml.

Jobs list :

  • npm run eslint: Lint code sources
  • npm run test-unit: Unit test
  • npm run test-functional: Functional test
  • npm audit --audit-level=low: Npm native command (npm-audit) which check packages dependencies vulnerabilities.
  • npm run test-links: Run this script which check if links are not broken.
  • node ./test/docs.js: Run this script which check if documentation is generated without warning or error.

Contributing

For information on the accepted coding style, submitting Issues, and submitting Pull Requests see Contributing.md

Publishing

For creating a new release see ReleasePublish.md

Generating the documentation

The Online documentation can be re-generated by following these instructions.

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-ReleasePublish.html b/docs/html/extensions_3d_tiles_temporal/tutorial-ReleasePublish.html index 53b934ff3..26022dc40 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-ReleasePublish.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-ReleasePublish.html @@ -1,6 +1,6 @@ Tutorial: ReleasePublish
On this page

ReleasePublish

Release creation process

  1. Create a new branch for the release, e.g. release-x.x.x from the master branch.

  2. Change the monorepo version npm run pre-publish x.x.x. It will change the version in all package.json and generate a Changelog file with all commits' names since the last release. (See the script here)

  3. Pick relevant commit message in ./docs/static/ChangelogDiff.txt to put them in ./docs/static/Changelog.md (Reformulate if necessary). Arrange commits in theses sections:

    # vx.x.x
    +    
    On this page

    ReleasePublish

    Release creation process

    1. Create a new branch for the release, e.g. release-x.x.x from the master branch.

    2. Change the monorepo version npm run pre-publish x.x.x. It will change the version in all package.json and generate a Changelog file with all commits' names since the last release. (See the script here)

    3. Pick relevant commit message in ./docs/static/ChangelogDiff.txt to put them in ./docs/static/Changelog.md (Reformulate if necessary). Arrange commits in theses sections:

      # vx.x.x
       
       ## @ud-viz
           - Examples:
      @@ -14,4 +14,4 @@
       
       ....
       
      -
    4. Run npm run docs. Generate the documentation with JSDoc.

    5. Follow the prior PR submission

    6. Push your branch to the main repository (This branch will be used to create the release tag)

    7. Create a PR

    8. Assign yourself to the PR

    9. Have a reviewer approve your PR

    10. Create a Github release (through the GUI):

      • Copy the new content given in Changelog.md: this duplication doesn't seem to be DRY but the Release comments are hosted by github (and will be lost if you migrate to e.g. gitlab) and are not browsable offline.
        • You might consider using the "Auto-generate release notes" feature
        • Note that creating a Github release creates an underlying git tag (which can previously be created with a git tag)

    Publish the package

    To publish the package to npmjs package repository

    1. Authenticate on npmjs with npm login and use the vcity account together with proper credentials. Note:
      • Ask username and password to login at an admin
      • Because the npmjs authentication mode of the vcityaccount is currently configured to One-Time-Password (OTP) over email you will need to be part of the vcity@liris.cnrs.fr email alias forwarder to receive the OTP and be patient about it (the reception delay can be up to a couple minutes).
    2. npm publish --workspaces --access public (workspaces option refer to the workspaces field in the package.json).
    \ No newline at end of file +
  4. Run npm run docs. Generate the documentation with JSDoc.

  5. Follow the prior PR submission

  6. Push your branch to the main repository (This branch will be used to create the release tag)

  7. Create a PR

  8. Assign yourself to the PR

  9. Have a reviewer approve your PR

  10. Create a Github release (through the GUI):

    • Copy the new content given in Changelog.md: this duplication doesn't seem to be DRY but the Release comments are hosted by github (and will be lost if you migrate to e.g. gitlab) and are not browsable offline.
      • You might consider using the "Auto-generate release notes" feature
      • Note that creating a Github release creates an underlying git tag (which can previously be created with a git tag)

Publish the package

To publish the package to npmjs package repository

  1. Authenticate on npmjs with npm login and use the vcity account together with proper credentials. Note:
    • Ask username and password to login at an admin
    • Because the npmjs authentication mode of the vcityaccount is currently configured to One-Time-Password (OTP) over email you will need to be part of the vcity@liris.cnrs.fr email alias forwarder to receive the OTP and be patient about it (the reception delay can be up to a couple minutes).
  2. npm publish --workspaces --access public (workspaces option refer to the workspaces field in the package.json).
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-Reproducibility.html b/docs/html/extensions_3d_tiles_temporal/tutorial-Reproducibility.html index 9cae51064..f87feefe9 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-Reproducibility.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-Reproducibility.html @@ -1,3 +1,3 @@ Tutorial: Reproducibility
On this page

Reproducibility

The following stale branches

  • reproductibility/article-temporel-3DTiles
  • reproductibility/article-temporel-3DTilesTemporal

that have been deleted for sake of simplicity, can still be respectively retrieved through their associated tags with

  • git checkout -b reproductibility/article-temporel-3DTiles reproductibility/article-temporel-3DTiles-tag
  • git checkout -b reproductibility/article-temporel-3DTilesTemporal reproductibility/article-temporel-3DTilesTemporal-tag

Refer to this StackOverflow for a description of the branch "archiving"/retrieval process.

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

Reproducibility

The following stale branches

  • reproductibility/article-temporel-3DTiles
  • reproductibility/article-temporel-3DTilesTemporal

that have been deleted for sake of simplicity, can still be respectively retrieved through their associated tags with

  • git checkout -b reproductibility/article-temporel-3DTiles reproductibility/article-temporel-3DTiles-tag
  • git checkout -b reproductibility/article-temporel-3DTilesTemporal reproductibility/article-temporel-3DTilesTemporal-tag

Refer to this StackOverflow for a description of the branch "archiving"/retrieval process.

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-architecture.html b/docs/html/extensions_3d_tiles_temporal/tutorial-architecture.html index 1e3ff0998..5bdeae98e 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-architecture.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-architecture.html @@ -1,11 +1,12 @@ Tutorial: architecture
On this page

architecture

@ud-viz packages architecture

flowchart TB
+    
On this page

architecture

@ud-viz packages architecture

flowchart TB
 subgraph ud-viz/packages
 extensions_3d_tiles_temporal
 frame3d
 game_browser
 game_browser_template
+game_editor
 game_node
 game_node_template
 game_shared
@@ -40,6 +41,10 @@
 game_browser_template-->game_shared_template
 game_browser_template-->utils_browser
 game_browser_template-->utils_shared
+game_editor-->game_shared
+game_editor-->game_browser
+game_editor-->utils_browser
+game_editor-->utils_shared
 game_node-->game_shared
 game_node-->utils_shared
 game_node_template-->game_node
@@ -78,4 +83,4 @@
 widget_slide_show-->utils_browser
 widget_sparql-->utils_browser
 widget_workspace-->utils_browser
-widget_workspace-->widget_sparql
\ No newline at end of file +widget_workspace-->widget_sparql
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-game.html b/docs/html/extensions_3d_tiles_temporal/tutorial-game.html index 75a574721..3072f90f5 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-game.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-game.html @@ -1,6 +1,6 @@ Tutorial: game
On this page

game

Game

This document gives an overview of how the game part works. Here are some different implementations of the game part:

Prerequisites

  • Run a back-end in node.js with express. See MDN DOC.
  • See here how to import @ud-viz/game_browser framework.

Creating a singleplayer simple game

The goal of this section is to learn how to set a singleplayer simple game structure:

Create a scene

Initialize a Planar part of @ud-viz/frame3d which creates a itowns PlanarView.

// Define geographic extent: CRS, min/max X, min/max Y
+    
On this page

game

Game

This document gives an overview of how the game part works. Here are some different implementations of the game part:

Prerequisites

  • Run a back-end in node.js with express. See MDN DOC.
  • See here how to import @ud-viz/game_browser framework.

Creating a singleplayer simple game

The goal of this section is to learn how to set a singleplayer simple game structure:

Create a scene

Initialize a Planar part of @ud-viz/frame3d which creates a itowns PlanarView.

// Define geographic extent: CRS, min/max X, min/max Y
 const extent = new udvizBrowser.itowns.Extent(
   'EPSG:4326',
   1837816.94334,
@@ -12,7 +12,6 @@
 const frame3DPlanar = new frame3d.Planar(extent, {
   hasItownsControls: true,
 });
-
 

Create a SinglePlanarProcess

Initialize a SinglePlanarProcess with a AssetManager and a InputManager part of @ud-viz/game_browser, an Object3D part of @ud-viz/game_shared defines your game model and SinglePlanarProcess is stepping your game over time.

const gameObject3D = new Object3D({
   static: true,
 });
@@ -47,10 +46,10 @@
   static: true,
   components: {
     GameScript: {
-      idScripts: [GameContextScript.ID_SCRIPT],
+      scriptParams: [{ id: GameContextScript.ID_SCRIPT }],
     },
     ExternalScript: {
-      idScripts: [GameExternalContextScript.ID_SCRIPT],
+      scriptParams: [{ id: GameExternalContextScript.ID_SCRIPT }],
     },
   },
 });
@@ -63,7 +62,6 @@
   {
     gameScriptClass: [GameContextScript],
     externalGameScriptClass: [GameExternalContextScript],
-    gameOrigin: { x: extent.center().x, y: extent.center().y, z: 100}
   }
 );
 

Spawn cubes

In the init of GameContextScript add these lines

this.goCubes = [];
@@ -87,7 +85,7 @@
   for (let index = this.goCubes.length - 1; index >= 0; index--) {
     const cube = this.goCubes[index];
     cube.position.z += 0.1 * this.context.dt;
-    cube.setOutdated(true);// notify game external context that this gameobject need update 
+    cube.setOutdated(true);// notify game external context that this gameobject need update
 
     // sky is the limit
     if (cube.position.z > 2000) {
@@ -96,23 +94,19 @@
     }
   }
 }
-

Send command

Add in the init of GameExternalContextScript the following code

this.context.inputManager.addMouseCommand(
-  'command_id',
-  'click',
-  () => {
-    return new Command({
-      type: 'toggle_pause',
-    });
-  }
-);
+

Send command

Add in the init of GameExternalContextScript the following code

this.context.inputManager.addMouseCommand('command_id', 'click', () => {
+  return new Command({
+    type: 'toggle_pause',
+  });
+});
 

Command is part of @ud-viz/game_shared

This sends a command on the mouse click to GameContextScript. Then in the init of GameContextScript add these lines:

this.pause = false;
 setInterval(() => {
   if (this.pause) return;
   ...
 

In the tick of GameContextScript

tick() {
+
+  this.applyCommandCallbackOf('toggle_pause', () => { this.pause = !this.pause })
+
   if (this.pause) return;
   ...
-

And finally add onCommand method:

onCommand(type) {
-  if (type === 'toggle_pause') this.pause = !this.pause;
-}
-

Now you have learned how to build a singleplayer simple game, let's see how to modify it to make a multiplayer one.

Create a multiplayer simple game (WIP)

1 Create a backend import udviz node with require as before run an express app running a http server Final result backend WIP

import game part of shared create a socket service by passsing the http server load gameobject3D where gameobject3D is the same one as the previous example the both script are unknown hard coded value of ids to keep it simple create another file gamethreadchild ok now you have to give the entry point to your thread the game script is the one running backend side copy paste it to your gamethread you have to give him the game script because he is going to run there replace udvizBrowser.Shared.Game by Game ouais backend okay

front end create a new html file base on previous one delete game script delete gameobject 3D Replace singleprocess by multiprocess create a socket io wrapper + connect it run enjoy

Examples

Singleplayer one:

This example requires knowledge about @ud-viz/game_shared and @ud-viz/game_browser

Multiplayer one:

This example requires the same knowledge as singleplayer plus @ud-viz/game_node

\ No newline at end of file +

Now you have learned how to build a singleplayer simple game, let's see how to modify it to make a multiplayer one.

Create a multiplayer simple game (WIP)

1 Create a backend import udviz node with require as before run an express app running a http server Final result backend WIP

import game part of shared create a socket service by passsing the http server load gameobject3D where gameobject3D is the same one as the previous example the both script are unknown hard coded value of ids to keep it simple create another file gamethreadchild ok now you have to give the entry point to your thread the game script is the one running backend side copy paste it to your gamethread you have to give him the game script because he is going to run there replace udvizBrowser.Shared.Game by Game ouais backend okay

front end create a new html file base on previous one delete game script delete gameobject 3D Replace singleprocess by multiprocess create a socket io wrapper + connect it run enjoy

Examples

Singleplayer one:

This example requires knowledge about @ud-viz/game_shared and @ud-viz/game_browser

Multiplayer one:

This example requires the same knowledge as singleplayer plus @ud-viz/game_node

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-how_to_import.html b/docs/html/extensions_3d_tiles_temporal/tutorial-how_to_import.html index 8f22c6b42..c3a07f930 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-how_to_import.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-how_to_import.html @@ -1,4 +1,4 @@ Tutorial: how_to_import
On this page

how_to_import

How to use it in your demo?

You can use it through npm

In your demo:

npm install --save @ud-viz/xxx
-

See https://github.com/VCityTeam/UD-Viz-template

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file +
On this page

how_to_import

How to use it in your demo?

You can use it through npm

In your demo:

npm install --save @ud-viz/xxx
+

See https://github.com/VCityTeam/UD-Viz-template

@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/extensions_3d_tiles_temporal/tutorial-initial_camera_transform.html b/docs/html/extensions_3d_tiles_temporal/tutorial-initial_camera_transform.html index 7e2cd5a3a..9bcb22d57 100644 --- a/docs/html/extensions_3d_tiles_temporal/tutorial-initial_camera_transform.html +++ b/docs/html/extensions_3d_tiles_temporal/tutorial-initial_camera_transform.html @@ -1,6 +1,6 @@ Tutorial: initial_camera_transform
On this page

initial_camera_transform

When creating a Planar part of @ud-viz/frame3d you may want your camera to be placed at a specific point of view. In this tutorial we are going to see differents options available to achieve that.

Configure Planar

Planar take an options object at the construction, where following fields will initialize camera point of view:

  • coordinates: camera look at geographic coordinate
  • heading: camera's heading, in degree
  • range: camera distance to target coordinate, in meter
  • tilt: camera's tilt, in degree

For more details check Planar documentation.

If you don't specify how the camera should be placed and you typed something like that:

const frame3DPlanar = new Planar(extent); // <-- no options passed
+    
On this page

initial_camera_transform

When creating a Planar part of @ud-viz/frame3d you may want your camera to be placed at a specific point of view. In this tutorial we are going to see differents options available to achieve that.

Configure Planar

Planar take an options object at the construction, where following fields will initialize camera point of view:

  • coordinates: camera look at geographic coordinate
  • heading: camera's heading, in degree
  • range: camera distance to target coordinate, in meter
  • tilt: camera's tilt, in degree

For more details check Planar documentation.

If you don't specify how the camera should be placed and you typed something like that:

const frame3DPlanar = new Planar(extent); // <-- no options passed
 

then the camera will look at the center of the extent with these default values:

  • heading: -50°
  • range: 3000m
  • tilt: 10°

if you want to configure that you should then write something like:

const frame3DPlanar = new Planar(extent, {
   coordinates: {
     x: some_x_coordinate
@@ -15,4 +15,4 @@
 

Note: the first time you will run your webpage the camera point of view will be the one pass to the Planar

Use URL

You can also use url util function to initialize default camera point of view. In that case you should first produce an url with camera matrix encoded in it.

const url = new URL(window.location.origin + window.location.pathname);
 appendCameraMatrixToURL(url, yourCamera);
 

then to initialize your camera with this url you would have to write something like that:

const isCameraMatrixInitializedWithURL = URLSetCameraMatrix(yourCamera);
-
\ No newline at end of file +
@ud-viz/extensions_3d_tiles_temporal
\ No newline at end of file diff --git a/docs/html/frame3d/Base.html b/docs/html/frame3d/Base.html index 2d60f1204..d5b6bd268 100644 --- a/docs/html/frame3d/Base.html +++ b/docs/html/frame3d/Base.html @@ -1,3 +1,3 @@ Class: Base
On this page

Base

new Base(options, init3Dopt)

Basic Frame3D wrap different html element to handle CSS3D rendering CSS3DRenderer. It's possible to add DomElement3D to this. Composed with THREE.Scene + THREE.PerspectiveCamera + THREE.WebGLRenderer.

Parameters:
NameTypeAttributesDefaultDescription
optionsobject

options to configure frame3Dbase

Properties
NameTypeAttributesDefaultDescription
domElementClassstring<optional>

dom element class name

parentDomElementHTMLElement<optional>
document.body

dom parent element of domElement frame3DBase

catchEventsCSS3Dboolean<optional>
false

event are catch by css3D element (ie DomElement3D)

init3Dboolean<optional>
true

THREE.Scene + THREE.PerspectiveCamera + THREE.WebGLRenderer should be init

Classes

Base

Members

camera :THREE.PerspectiveCamera

camera 3D

Type:
  • THREE.PerspectiveCamera

css3DRenderer :CSS3DRenderer

css renderer

Type:
  • CSS3DRenderer

css3DScene :THREE.Scene

css scene

Type:
  • THREE.Scene

domElement :HTMLDivElement

root html

Type:
  • HTMLDivElement

domElement3DArray :Array.<DomElement3D>

current domElements 3D in frame3D

Type:

domElementCss :HTMLDivElement

root css (where css3Delement are added)

Type:
  • HTMLDivElement

domElementUI :HTMLDivElement

where ui element should be added (note that you have to handle manually z-index element composing ui, should it be automatically ?)

Type:
  • HTMLDivElement

domElementWebGL :HTMLDivElement

root webgl (where canvas is added)

Type:
  • HTMLDivElement

isRendering :boolean

flag to stop rendering 3D

Type:
  • boolean

listeners :Object.<string, Array.<function()>>

listeners of Base.EVENT

Type:
  • Object.<string, Array.<function()>>

renderer :THREE.WebGLRenderer

canvas renderer

Type:
  • THREE.WebGLRenderer

resizeListener :function

reference resize listener to remove it on dispose

Type:
  • function

scene :THREE.Scene

canvas scene 3D

Type:
  • THREE.Scene

(static) EVENT

Events triggered by Base

Methods

appendDomElement3D(domElement3D, parent)

Parameters:
NameTypeDescription
domElement3DDomElement3D

domElement3D to add in frame3D

parentTHREE.Object3D

parent of the maskElement

catchEventsCSS3D(value)

Parameters:
NameTypeDescription
valueboolean

if true allow css3D html elements to catch user events, otherwise no

dispose()

Remove html from the DOM and stop listeners

initCSS3D()

Init the css3D renderer

isCatchingEventsCSS3D() → {boolean}

Returns:
  • false if root webgl is catching events, true if it's root css
Type: 
boolean

on(eventID, listener)

Register a listener on a Base.EVENT

Parameters:
NameTypeDescription
eventIDstring

event to add listener Base.EVENT

listenerfunction

callback to call on eventID

onResize(updateTHREEVariablesopt)

Resize frame3D

Parameters:
NameTypeAttributesDefaultDescription
updateTHREEVariablesboolean<optional>
true

camera and renderer should be updated

removeDomElement3D(domElement3D)

Parameters:
NameTypeDescription
domElement3DDomElement3D

domElement3D to remove

render() → {void}

Render scene3D

Returns:
Type: 
void

renderCSS3D() → {void}

Render css3D

Returns:
Type: 
void

setRender(f)

Customize how to render the frame3D

Parameters:
NameTypeDescription
ffunction

custom rendering function

@ud-viz/frame3d
\ No newline at end of file +
On this page

Base

new Base(options, init3Dopt)

Basic Frame3D wrap different html element to handle CSS3D rendering CSS3DRenderer. It's possible to add DomElement3D to this. Composed with THREE.Scene + THREE.PerspectiveCamera + THREE.WebGLRenderer.

Parameters:
NameTypeAttributesDefaultDescription
optionsobject

options to configure frame3Dbase

Properties
NameTypeAttributesDefaultDescription
domElementClassstring<optional>

dom element class name

parentDomElementHTMLElement<optional>
document.body

dom parent element of domElement frame3DBase

catchEventsCSS3Dboolean<optional>
false

event are catch by css3D element (ie DomElement3D)

sceneConfigmodule:@ud-viz/utils_browser~SceneConfig<optional>

scene config

init3Dboolean<optional>
true

THREE.Scene + THREE.PerspectiveCamera + THREE.WebGLRenderer should be init

Classes

Base

Members

camera :THREE.PerspectiveCamera

camera 3D

Type:
  • THREE.PerspectiveCamera

css3DRenderer :CSS3DRenderer

css renderer

Type:
  • CSS3DRenderer

css3DScene :THREE.Scene

css scene

Type:
  • THREE.Scene

directionalLight :THREE.DirectionalLight|null

Type:
  • THREE.DirectionalLight | null

domElement :HTMLDivElement

root html

Type:
  • HTMLDivElement

domElement3DArray :Array.<DomElement3D>

current domElements 3D in frame3D

Type:

domElementCss :HTMLDivElement

root css (where css3Delement are added)

Type:
  • HTMLDivElement

domElementUI :HTMLDivElement

where ui element should be added (note that you have to handle manually z-index element composing ui, should it be automatically ?)

Type:
  • HTMLDivElement

domElementWebGL :HTMLDivElement

root webgl (where canvas is added)

Type:
  • HTMLDivElement

isRendering :boolean

flag to stop rendering 3D

Type:
  • boolean

listeners :Object.<string, Array.<function()>>

listeners of Base.EVENT

Type:
  • Object.<string, Array.<function()>>

renderer :THREE.WebGLRenderer

canvas renderer

Type:
  • THREE.WebGLRenderer

resizeListener :function

reference resize listener to remove it on dispose

Type:
  • function

scene :THREE.Scene

canvas scene 3D

Type:
  • THREE.Scene

sceneConfig :SceneConfig

Type:
  • SceneConfig

(static) EVENT

Events triggered by Base

Methods

appendDomElement3D(domElement3D, parent)

Parameters:
NameTypeDescription
domElement3DDomElement3D

domElement3D to add in frame3D

parentTHREE.Object3D

parent of the maskElement

catchEventsCSS3D(value)

Parameters:
NameTypeDescription
valueboolean

if true allow css3D html elements to catch user events, otherwise no

dispose()

Remove html from the DOM and stop listeners

initCSS3D()

Init the css3D renderer

isCatchingEventsCSS3D() → {boolean}

Returns:
  • false if root webgl is catching events, true if it's root css
Type: 
boolean

on(eventID, listener)

Register a listener on a Base.EVENT

Parameters:
NameTypeDescription
eventIDstring

event to add listener Base.EVENT

listenerfunction

callback to call on eventID

onResize(updateTHREEVariablesopt)

Resize frame3D

Parameters:
NameTypeAttributesDefaultDescription
updateTHREEVariablesboolean<optional>
true

camera and renderer should be updated

removeDomElement3D(domElement3D)

Parameters:
NameTypeDescription
domElement3DDomElement3D

domElement3D to remove

render() → {void}

Render scene3D

Returns:
Type: 
void

renderCSS3D() → {void}

Render css3D

Returns:
Type: 
void

setRender(f)

Customize how to render the frame3D

Parameters:
NameTypeDescription
ffunction

custom rendering function

@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/Base.js.html b/docs/html/frame3d/Base.js.html index 5e5857b12..58e3b4d65 100644 --- a/docs/html/frame3d/Base.js.html +++ b/docs/html/frame3d/Base.js.html @@ -1,9 +1,13 @@ Source: Base.js
On this page

Base.js

import * as THREE from 'three';
+    
On this page

Base.js

import * as THREE from 'three';
 import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer';
-import { checkParentChild } from '@ud-viz/utils_browser';
 import { DomElement3D } from './DomElement3D';
+import {
+  defaultConfigScene,
+  initScene,
+  checkParentChild,
+} from '@ud-viz/utils_browser';
 
 /** @class*/
 export class Base {
@@ -16,6 +20,7 @@
    * @param {string} [options.domElementClass] - dom element class name
    * @param {HTMLElement} [options.parentDomElement=document.body] - dom parent element of domElement frame3DBase
    * @param {boolean} [options.catchEventsCSS3D=false] - event are catch by css3D element (ie {@link DomElement3D})
+   * @param {import('@ud-viz/utils_browser').SceneConfig} [options.sceneConfig] - scene config
    * @param {boolean} [init3D=true] - {@link THREE.Scene} + {@link THREE.PerspectiveCamera} + {@link THREE.WebGLRenderer} should be init
    */
   constructor(options = {}, init3D = true) {
@@ -126,6 +131,12 @@
       this.listeners[Base.EVENT[key]] = [];
     }
 
+    /** @type {import('../THREEUtil').SceneConfig} */
+    this.sceneConfig = options.sceneConfig || defaultConfigScene;
+
+    /** @type {THREE.DirectionalLight|null} */
+    this.directionalLight = null;
+
     if (init3D) {
       // Initialize 3D
 
@@ -133,6 +144,8 @@
 
       this.scene = new THREE.Scene();
       const canvas = document.createElement('canvas');
+      if (options.domElementClass)
+        canvas.classList.add(options.domElementClass);
       this.domElementWebGL.appendChild(canvas);
       this.renderer = new THREE.WebGLRenderer({
         canvas: canvas,
@@ -142,6 +155,14 @@
       });
       this.camera = new THREE.PerspectiveCamera(60, 1, 1, 1000); // Default params
       this.scene.add(this.camera);
+
+      // init with sceneconfig
+      this.directionalLight = initScene(
+        this.camera,
+        this.renderer,
+        this.scene,
+        this.sceneConfig
+      );
     }
   }
 
@@ -355,4 +376,4 @@
   DISPOSE: 'dispose',
   RESIZE: 'resize',
 };
-
\ No newline at end of file +
@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/DomElement3D.html b/docs/html/frame3d/DomElement3D.html index 97b0c0fd1..aa56a7a2c 100644 --- a/docs/html/frame3d/DomElement3D.html +++ b/docs/html/frame3d/DomElement3D.html @@ -1,3 +1,3 @@ Class: DomElement3D
On this page

DomElement3D

new DomElement3D(domElement, scalar)

Composed of a CSS3DObject containing html and a THREE.Object3D superposing each other

Parameters:
NameTypeDefaultDescription
domElementHTMLElement

dom element

scalarnumber1

scale domelement content

Classes

DomElement3D

Members

css3DObject :CSS3DObject

css3D object

Type:
  • CSS3DObject

domElement :HTMLElement

html element of css3Dobject

Type:
  • HTMLElement

isSelected :boolean

selected (css style is different if true or false)

Type:
  • boolean

maskObject :THREE.Object3D

mask superposing css3DObject

Type:
  • THREE.Object3D

scalar :number

Type:
  • number

uuid :string

uuid

Type:
  • string

Methods

select(value)

Set if this is selected or not and update css style

Parameters:
NameTypeDescription
valueboolean

new selected value

@ud-viz/frame3d
\ No newline at end of file +
On this page

DomElement3D

new DomElement3D(domElement, scalar)

Composed of a CSS3DObject containing html and a THREE.Object3D superposing each other

Parameters:
NameTypeDefaultDescription
domElementHTMLElement

dom element

scalarnumber1

scale domelement content

Classes

DomElement3D

Members

css3DObject :CSS3DObject

css3D object

Type:
  • CSS3DObject

domElement :HTMLElement

html element of css3Dobject

Type:
  • HTMLElement

isSelected :boolean

selected (css style is different if true or false)

Type:
  • boolean

maskObject :THREE.Object3D

mask superposing css3DObject

Type:
  • THREE.Object3D

scalar :number

Type:
  • number

uuid :string

uuid

Type:
  • string

Methods

select(value)

Set if this is selected or not and update css style

Parameters:
NameTypeDescription
valueboolean

new selected value

@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/DomElement3D.js.html b/docs/html/frame3d/DomElement3D.js.html index 123aa36c7..3004c83e2 100644 --- a/docs/html/frame3d/DomElement3D.js.html +++ b/docs/html/frame3d/DomElement3D.js.html @@ -1,6 +1,6 @@ Source: DomElement3D.js
On this page

DomElement3D.js

import * as THREE from 'three';
+    
On this page

DomElement3D.js

import * as THREE from 'three';
 import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer';
 import { Color } from 'three';
 
@@ -102,4 +102,4 @@
     }
   }
 }
-
\ No newline at end of file +
@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/Planar.html b/docs/html/frame3d/Planar.html index 5812e3b0e..72b6acbff 100644 --- a/docs/html/frame3d/Planar.html +++ b/docs/html/frame3d/Planar.html @@ -1,3 +1,3 @@ Class: Planar
On this page

Planar

It's a class that extends the Base class and adds a PlanarView to it

Constructor

new Planar(extent, optionsopt)

Parameters:
NameTypeAttributesDefaultDescription
extentitowns.Extent

Geographical bounding rectangle. Extent

optionsPlanarOption<optional>
{}

PlanarOption

Classes

Planar

Members

camera :THREE.PerspectiveCamera

Type:
  • THREE.PerspectiveCamera

directionalLight :THREE.DirectionalLight

Type:
  • THREE.DirectionalLight

itownsView :itowns.PlanarView

planar view

Type:
  • itowns.PlanarView

renderer :THREE.WebGLRenderer

Type:
  • THREE.WebGLRenderer

scene :THREE.Scene

Type:
  • THREE.Scene

sceneConfig :SceneConfig

Type:
  • SceneConfig

Methods

dispose()

Dispose Frame3D

enableItownsViewRendering(value)

Disable/enable itowns rendering from itowns.mainLoop

Parameters:
NameTypeDescription
valueboolean

true enable / false disable

onResize()

Resize Frame3D

render()

Render scene3D + labels

@ud-viz/frame3d
\ No newline at end of file +
On this page

Planar

It's a class that extends the Base class and adds a PlanarView to it

Constructor

new Planar(extent, optionsopt)

Parameters:
NameTypeAttributesDefaultDescription
extentitowns.Extent

Geographical bounding rectangle. Extent

optionsPlanarOption<optional>
{}

PlanarOption

Classes

Planar

Members

camera :THREE.PerspectiveCamera

Type:
  • THREE.PerspectiveCamera

directionalLight :THREE.DirectionalLight

Type:
  • THREE.DirectionalLight

itownsView :itowns.PlanarView

planar view

Type:
  • itowns.PlanarView

notifyChangeToItownsView :function

Type:
  • function

renderer :THREE.WebGLRenderer

Type:
  • THREE.WebGLRenderer

scene :THREE.Scene

Type:
  • THREE.Scene

sceneConfig :SceneConfig

Type:
  • SceneConfig

Methods

dispose()

Dispose Frame3D

enableItownsViewRendering(value)

Disable/enable itowns rendering from itowns.mainLoop

Parameters:
NameTypeDescription
valueboolean

true enable / false disable

onResize()

Resize Frame3D

render()

Render scene3D + labels

@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/Planar.js.html b/docs/html/frame3d/Planar.js.html index 4cbb4cc93..7499cef3f 100644 --- a/docs/html/frame3d/Planar.js.html +++ b/docs/html/frame3d/Planar.js.html @@ -1,11 +1,12 @@ Source: Planar.js
On this page

Planar.js

import { Base } from './Base';
+    
On this page

Planar.js

import { Base } from './Base';
 import {
   defaultConfigScene,
   initScene,
   computeNearFarCamera,
 } from '@ud-viz/utils_browser';
+import { throttle } from '@ud-viz/utils_shared';
 import * as itowns from 'itowns';
 import * as THREE from 'three';
 
@@ -18,6 +19,7 @@
  * @property {number} [range=3000] -  Camera range placement
  * @property {number} [tilt=10] - Camera tilt placement
  * @property {number} [maxSubdivisionLevel=5] - Maximum subdivision level for PlanarLayer
+ * @property {number} [delayNotifyChange=2000] - delay in ms to notify itowns view change
  * @property {import('@ud-viz/utils_browser').SceneConfig} sceneConfig - scene config
  */
 
@@ -57,7 +59,6 @@
         tilt: tilt,
       },
       maxSubdivisionLevel: maxSubdivisionLevel,
-      noControls: !hasItownsControls,
       controls: {
         handleCollision: false,
         focusOnMouseOver: false,
@@ -65,6 +66,8 @@
       },
     });
 
+    this.itownsView.controls.enabled = hasItownsControls;
+
     // fill parent class attributes create by the itownsView
     /** @type {THREE.Scene} */
     this.scene = this.itownsView.scene;
@@ -84,6 +87,11 @@
       this.sceneConfig
     );
 
+    /** @type {Function} */
+    this.notifyChangeToItownsView = throttle(() => {
+      this.itownsView.notifyChange(this.camera);
+    }, options.delayNotifyChange || 2000); // => to load 3DTiles and trigger mainLoop event
+
     let useItownsMainLoop = true;
     if (options.useItownsMainLoop != undefined)
       useItownsMainLoop = options.useItownsMainLoop;
@@ -128,6 +136,8 @@
   render() {
     super.render();
 
+    this.notifyChangeToItownsView();
+
     // render also label layers
     if (this.isRendering && this.itownsView.tileLayer) {
       this.itownsView.mainLoop.gfxEngine.label2dRenderer.render(
@@ -152,4 +162,4 @@
     this.itownsView.dispose();
   }
 }
-
\ No newline at end of file +
@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/global.html b/docs/html/frame3d/global.html index b9aa1ba17..227331d57 100644 --- a/docs/html/frame3d/global.html +++ b/docs/html/frame3d/global.html @@ -1,3 +1,3 @@ Global
On this page

Members

(constant) BLANK_MATERIAL :THREE.MeshBasicMaterial

Material making an "hole" in a THREE.Scene to see html css3D behind

Type:
  • THREE.MeshBasicMaterial

Type Definitions

PlanarOption

Type:
  • object
Properties
NameTypeAttributesDefaultDescription
hasItownsControlsboolean<optional>
false

If true enable Itowns View Controls

useItownsMainLoopboolean<optional>
true

Rendering is done in itowns.mainLoop

coordinatesitowns.Coordinates

Coordinates

headingnumber<optional>
-50

Camera heading placement

rangenumber<optional>
3000

Camera range placement

tiltnumber<optional>
10

Camera tilt placement

maxSubdivisionLevelnumber<optional>
5

Maximum subdivision level for PlanarLayer

sceneConfigmodule:@ud-viz/utils_browser~SceneConfig

scene config

@ud-viz/frame3d
\ No newline at end of file +
On this page

Members

(constant) BLANK_MATERIAL :THREE.MeshBasicMaterial

Material making an "hole" in a THREE.Scene to see html css3D behind

Type:
  • THREE.MeshBasicMaterial

Type Definitions

PlanarOption

Type:
  • object
Properties
NameTypeAttributesDefaultDescription
hasItownsControlsboolean<optional>
false

If true enable Itowns View Controls

useItownsMainLoopboolean<optional>
true

Rendering is done in itowns.mainLoop

coordinatesitowns.Coordinates

Coordinates

headingnumber<optional>
-50

Camera heading placement

rangenumber<optional>
3000

Camera range placement

tiltnumber<optional>
10

Camera tilt placement

maxSubdivisionLevelnumber<optional>
5

Maximum subdivision level for PlanarLayer

delayNotifyChangenumber<optional>
2000

delay in ms to notify itowns view change

sceneConfigmodule:@ud-viz/utils_browser~SceneConfig

scene config

@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/index.html b/docs/html/frame3d/index.html index b237d3c73..a545b6f94 100644 --- a/docs/html/frame3d/index.html +++ b/docs/html/frame3d/index.html @@ -1,3 +1,3 @@ Home
On this page
@ud-viz/frame3d
\ No newline at end of file +
On this page
@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-Changelog.html b/docs/html/frame3d/tutorial-Changelog.html index 4e299f17c..ddad03dad 100644 --- a/docs/html/frame3d/tutorial-Changelog.html +++ b/docs/html/frame3d/tutorial-Changelog.html @@ -1,6 +1,17 @@ Tutorial: Changelog
On this page

Changelog

v4.0.1

@ud-viz

- Examples:
+    
On this page

Changelog

v4.0.2

@ud-viz

- npm cmd webpack-bundle-analyzer 
+- script setPackageVersion.js
+- update to itowns 2.41.1-next.9
+

@ud-viz/game_shared

- compute bandwidth
+- idScritp:string => scriptParam:{id:string, priority:number}
+- applyCommandCallback API
+- update(ColliderComp): add SHAPE_TYPE
+

@ud-viz/game_browser_template

- optimization of the camera manager obstacle computation
+

@ud-viz/utils_shared

- computeFilename
+- throttle
+

@ud-viz/game_editor

- creation of the package
+

v4.0.1

@ud-viz

- Examples:
   - point_cloud_visualizer.html
   - game_dom_element_3d_cube.html
   - widget_guided_tour.html
@@ -13,7 +24,7 @@
 

@ud-viz/widget_layer_choice

- Complete refacto
 - Can order ColorLayer
 - LocalStorage ui element
-

@ud-viz/utils_shared

- round
+

@ud-viz/utils_shared

- round
 - vector3ToLabel
 - insert
 

@ud-viz/utils_browser

- createLocalStorageNumberInput
@@ -22,16 +33,16 @@
 - createLocalStorageDetails
 

@ud-viz/utils_node

- fix(test):splice first '/' of req.url, statusCode
 

@ud-viz/game_node_template

- DomElement3DCubeManager script
-

@ud-viz/game_browser_template

- DomElement3DCube script
+

@ud-viz/game_browser_template

- DomElement3DCube script
 

@ud-viz/widget_guided_tour

- Creation of the package
-

v4.0.0

@ud-viz

- Refact(ud-viz)! split into multiples packages
+

v4.0.0

@ud-viz

- Refact(ud-viz)! split into multiples packages
 - Add loading screen in examples
 - Fix show room
 - Add avatar jitsi game example
 - Doc:
     - fix doc
     - update doc
-

v3.2.0

@ud-viz

- Fix socket.io dependencies
+

v3.2.0

@ud-viz

- Fix socket.io dependencies
 - Update with node18
 - Prettier harmonization
 - Improve `Readme.md`
@@ -53,7 +64,7 @@
 - Fix bug Frame3DPlanar render labels
 - THREEUtil encoding => colorSpace
 - Update itowns 2.40.0
-

v3.1.1

@ud-viz

- NPM :
+

v3.1.1

@ud-viz

- NPM :
     - Remove `^` in all package.json
 - Examples:
     - 2D Visualisation Mode
@@ -233,4 +244,4 @@
 * Delete old ifc feature, aimed to be in a demo
 * Don't override material if the material has a texture
 * Remove dependency towards remote data
-
\ No newline at end of file +
@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-Contributing.html b/docs/html/frame3d/tutorial-Contributing.html index 42696736a..16de7bb98 100644 --- a/docs/html/frame3d/tutorial-Contributing.html +++ b/docs/html/frame3d/tutorial-Contributing.html @@ -1,3 +1,3 @@ Tutorial: Contributing
On this page

Contributing

Contributing

Coding style (Linter)

The JavaScript files coding style is defined with eslint through the .eslintrc.js configuration file. It can be checked (e.g. prior to a commit) with the npm run eslint command. Notice that UD-Viz coding style uses a unix linebreak-style (aka LF as newline character).

Submitting an issue

  • Create an issue with explicit name and a description
  • Add at least one label of task category, priority and related package
  • If it's a bug report add steps to reproduce.

Submiting Pull Request (PR)

Prior to PR-submission

  • Commit (git commit) with representative messages (commit messages end-up collected in the PR message and eventually release explanations).
  • Make sure your code is mature for a review.
  • git rebase origin/master, in order to resolve merge conflicts to master. Doc : git-rebase. If you are not able to resolve all of the conflicts of your rebase, please open a new discussion.

note that after a git rebase you have to git push --force

  • npm run eslint must finish with no errors.
  • npm run test-unit must finish with no errors.
  • npm run test-functional must finish with no errors.
  • npm audit must finish without vulnerabilities lower than high.
  • npm run test-links must finish with no errors.

⚠️ When your PR is open each push on your branch will trigger Travis CI jobs.

See here for more information.

Before submitting a pull request, and because UD-Viz still misses some tests, non-regression testing must be done manually. A developer must thus at least check that all the demo examples (they should function similar to their online deployment) are still effective.

Note that you should interact with ui (user interface) for complete tests.

PR Submission

Once your PR is ready to be submitted here are the steps to follow:

  • Create a PR via the Github interface
  • Describe synthetically the PR via the title and the description
  • Add labels if necessary
  • Link the related issues. (The linked issues are automatically closed when the PR is rebased and merged)
  • Add one or more reviewers.
  • Iterate over potential change requests until the PR is approved.
  • Click on Rebase and merge button
  • Delete the branch
@ud-viz/frame3d
\ No newline at end of file +
On this page

Contributing

Contributing

Coding style (Linter)

The JavaScript files coding style is defined with eslint through the .eslintrc.js configuration file. It can be checked (e.g. prior to a commit) with the npm run eslint command. Notice that UD-Viz coding style uses a unix linebreak-style (aka LF as newline character).

Submitting an issue

  • Create an issue with explicit name and a description
  • Add at least one label of task category, priority and related package
  • If it's a bug report add steps to reproduce.

Submiting Pull Request (PR)

Prior to PR-submission

  • Commit (git commit) with representative messages (commit messages end-up collected in the PR message and eventually release explanations).
  • Make sure your code is mature for a review.
  • git rebase origin/master, in order to resolve merge conflicts to master. Doc : git-rebase. If you are not able to resolve all of the conflicts of your rebase, please open a new discussion.

note that after a git rebase you have to git push --force

  • npm run eslint must finish with no errors.
  • npm run test-unit must finish with no errors.
  • npm run test-functional must finish with no errors.
  • npm audit must finish without vulnerabilities lower than high.
  • npm run test-links must finish with no errors.

⚠️ When your PR is open each push on your branch will trigger Travis CI jobs.

See here for more information.

Before submitting a pull request, and because UD-Viz still misses some tests, non-regression testing must be done manually. A developer must thus at least check that all the demo examples (they should function similar to their online deployment) are still effective.

Note that you should interact with ui (user interface) for complete tests.

PR Submission

Once your PR is ready to be submitted here are the steps to follow:

  • Create a PR via the Github interface
  • Describe synthetically the PR via the title and the description
  • Add labels if necessary
  • Link the related issues. (The linked issues are automatically closed when the PR is rebased and merged)
  • Add one or more reviewers.
  • Iterate over potential change requests until the PR is approved.
  • Click on Rebase and merge button
  • Delete the branch
@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-Contributors.html b/docs/html/frame3d/tutorial-Contributors.html index 2266a1718..5b5739551 100644 --- a/docs/html/frame3d/tutorial-Contributors.html +++ b/docs/html/frame3d/tutorial-Contributors.html @@ -1,3 +1,3 @@ Tutorial: Contributors
On this page

Contributors

See Contributing.md for details on how to contribute to UD-Viz.

The following people have contributed to UD-Viz:

See the VCity Team Members page here.

Commiters:

@ud-viz/frame3d
\ No newline at end of file +
On this page

Contributors

See Contributing.md for details on how to contribute to UD-Viz.

The following people have contributed to UD-Viz:

See the VCity Team Members page here.

Commiters:

@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-Definitions.html b/docs/html/frame3d/tutorial-Definitions.html index a0feae0d6..3155203d3 100644 --- a/docs/html/frame3d/tutorial-Definitions.html +++ b/docs/html/frame3d/tutorial-Definitions.html @@ -1,3 +1,3 @@ Tutorial: Definitions
On this page

Definitions

Definitions (within the UD-Viz context)

  • Component everything thats is necessary to execute only one aspect of a desired functionality (see also module).
  • Widget is used in a itowns context meaning it's a class extending itowns Widget which is a class containing a domElement and an itowns View.
@ud-viz/frame3d
\ No newline at end of file +
On this page

Definitions

Definitions (within the UD-Viz context)

  • Component everything thats is necessary to execute only one aspect of a desired functionality (see also module).
  • Widget is used in a itowns context meaning it's a class extending itowns Widget which is a class containing a domElement and an itowns View.
@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-Developers.html b/docs/html/frame3d/tutorial-Developers.html index b56f1dd8b..43aca056b 100644 --- a/docs/html/frame3d/tutorial-Developers.html +++ b/docs/html/frame3d/tutorial-Developers.html @@ -1,4 +1,4 @@ Tutorial: Developers
On this page

Developers

Developers

Pre-requisites

Developing UD-Viz applications requires knowledge about :

NVM

Developpers are advised to use node version manager (nvm). nvm allows you to quickly install and use different versions of node via the command line.

To download and install follow this link: https://github.com/nvm-sh/nvm#installing-and-updating

Environment Tips

IDE

VSCode is recommended.

VisualStudio Code

When using Visual Studio Code, you can install the following extentions to make your life easier:

Tips for Windows developers

As configured, the coding style requires a Linux style newline characters which might be overwritten in Windows environments (both by git and/or your editor) to become CRLF. When such changes happen eslint will warn about "incorrect" newline characters (which can always be fixed with npm run eslint-fix but this process quickly gets painful). In order to avoid such difficulties, the recommended pratice consists in

  1. setting git's core.autocrlf to false (e.g. with git config --global core.autocrlf false)
  2. configure your editor/IDE to use Unix-style endings
  3. In order to use scripts that launch a shell script with Powershell: npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"

Npm Scripts

ScriptDescription
npm run cleanRemove files and folders generated by npm install and the npm run build-(dev-)examples script such as ./node_modules, package-lock.json, and ./dist
npm run clear-nodeLinux Command: Kill all node process (this is a sudo script)
npm run resetReinstalls npm dependencies. This script runs npm run clean and npm install command
npm run build-examplesBuild a production bundle base on this webpack config
npm run build-dev-examplesBuild a development bundle base on this webpack config
npm run dev-examplesLaunch a watcher running this script. See here for more information
npm run eslintRun the linter. See here for more information
npm run eslint-quietRun the linter without displaying warnings, only errors
npm run eslint-fixRun the linter and attempt to fix errors and warning automatically
npm run test-unitBuild examples bundle if needed and run npm run test in each packages . Also ran by CI. See here for more information
npm run test-functionalBuild examples bundle if needed and run each *.html in ./examples folder . Also ran by CI. See here for more information
npm run test-linksRun this script to detect dead links in markdown. Also ran by CI. See here for more information
npm run local-ciRun CI on your local computer. See here for more information
npm run pre-publishChange version in all package.json ( eg npm run pre-publish x.x.x ). See this script
npm run docsDelete old docs and run this script which generate docs
npm run dev-docsLaunch a watcher for generating and debugging the documentation. See here for more information
npm run back-endrun an http server (with some string-replace) + a game socket service.
http://locahost:8000/
npm run startRun npm run build-examples and npm run back-end

Debugging the examples

npm run dev-examples
-

Continuous Integration (Travis CI)

Each time origin/master branch is impacted by changes, Travis CI is triggered. It does a set of jobs describe in travis.yml.

Jobs list :

  • npm run eslint: Lint code sources
  • npm run test-unit: Unit test
  • npm run test-functional: Functional test
  • npm audit --audit-level=low: Npm native command (npm-audit) which check packages dependencies vulnerabilities.
  • npm run test-links: Run this script which check if links are not broken.
  • node ./test/docs.js: Run this script which check if documentation is generated without warning or error.

Contributing

For information on the accepted coding style, submitting Issues, and submitting Pull Requests see Contributing.md

Publishing

For creating a new release see ReleasePublish.md

Generating the documentation

The Online documentation can be re-generated by following these instructions.

@ud-viz/frame3d
\ No newline at end of file +
On this page

Developers

Developers

Pre-requisites

Developing UD-Viz applications requires knowledge about :

NVM

Developpers are advised to use node version manager (nvm). nvm allows you to quickly install and use different versions of node via the command line.

To download and install follow this link: https://github.com/nvm-sh/nvm#installing-and-updating

Environment Tips

IDE

VSCode is recommended.

VisualStudio Code

When using Visual Studio Code, you can install the following extentions to make your life easier:

Tips for Windows developers

As configured, the coding style requires a Linux style newline characters which might be overwritten in Windows environments (both by git and/or your editor) to become CRLF. When such changes happen eslint will warn about "incorrect" newline characters (which can always be fixed with npm run eslint-fix but this process quickly gets painful). In order to avoid such difficulties, the recommended pratice consists in

  1. setting git's core.autocrlf to false (e.g. with git config --global core.autocrlf false)
  2. configure your editor/IDE to use Unix-style endings
  3. In order to use scripts that launch a shell script with Powershell: npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"

Npm Scripts

ScriptDescription
npm run cleanRemove files and folders generated by npm install and the npm run build-(dev-)examples script such as ./node_modules, package-lock.json, and ./dist
npm run clear-nodeLinux Command: Kill all node process (this is a sudo script)
npm run resetReinstalls npm dependencies. This script runs npm run clean and npm install command
npm run build-examplesBuild a production bundle base on this webpack config
npm run build-dev-examplesBuild a development bundle base on this webpack config
npm run dev-examplesLaunch a watcher running this script. See here for more information
npm run eslintRun the linter. See here for more information
npm run eslint-quietRun the linter without displaying warnings, only errors
npm run eslint-fixRun the linter and attempt to fix errors and warning automatically
npm run test-unitBuild examples bundle if needed and run npm run test in each packages . Also ran by CI. See here for more information
npm run test-functionalBuild examples bundle if needed and run each *.html in ./examples folder . Also ran by CI. See here for more information
npm run test-linksRun this script to detect dead links in markdown. Also ran by CI. See here for more information
npm run local-ciRun CI on your local computer. See here for more information
npm run pre-publishChange version in all package.json ( eg npm run pre-publish x.x.x ). See this script
npm run docsDelete old docs and run this script which generate docs
npm run dev-docsLaunch a watcher for generating and debugging the documentation. See here for more information
npm run back-endrun an http server (with some string-replace) + a game socket service.
http://locahost:8000/
npm run analyze-bundleUse webpack-bundle-analyzer to see what's inside the examples bundle
npm run analyze-dev-bundleUse webpack-bundle-analyzer to see what's inside the examples dev bundle
npm run startRun npm run build-examples and npm run back-end

Debugging the examples

npm run dev-examples
+

Continuous Integration (Travis CI)

Each time origin/master branch is impacted by changes, Travis CI is triggered. It does a set of jobs describe in travis.yml.

Jobs list :

  • npm run eslint: Lint code sources
  • npm run test-unit: Unit test
  • npm run test-functional: Functional test
  • npm audit --audit-level=low: Npm native command (npm-audit) which check packages dependencies vulnerabilities.
  • npm run test-links: Run this script which check if links are not broken.
  • node ./test/docs.js: Run this script which check if documentation is generated without warning or error.

Contributing

For information on the accepted coding style, submitting Issues, and submitting Pull Requests see Contributing.md

Publishing

For creating a new release see ReleasePublish.md

Generating the documentation

The Online documentation can be re-generated by following these instructions.

@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-ReleasePublish.html b/docs/html/frame3d/tutorial-ReleasePublish.html index 3934007c5..26b7f8377 100644 --- a/docs/html/frame3d/tutorial-ReleasePublish.html +++ b/docs/html/frame3d/tutorial-ReleasePublish.html @@ -1,6 +1,6 @@ Tutorial: ReleasePublish
On this page

ReleasePublish

Release creation process

  1. Create a new branch for the release, e.g. release-x.x.x from the master branch.

  2. Change the monorepo version npm run pre-publish x.x.x. It will change the version in all package.json and generate a Changelog file with all commits' names since the last release. (See the script here)

  3. Pick relevant commit message in ./docs/static/ChangelogDiff.txt to put them in ./docs/static/Changelog.md (Reformulate if necessary). Arrange commits in theses sections:

    # vx.x.x
    +    
    On this page

    ReleasePublish

    Release creation process

    1. Create a new branch for the release, e.g. release-x.x.x from the master branch.

    2. Change the monorepo version npm run pre-publish x.x.x. It will change the version in all package.json and generate a Changelog file with all commits' names since the last release. (See the script here)

    3. Pick relevant commit message in ./docs/static/ChangelogDiff.txt to put them in ./docs/static/Changelog.md (Reformulate if necessary). Arrange commits in theses sections:

      # vx.x.x
       
       ## @ud-viz
           - Examples:
      @@ -14,4 +14,4 @@
       
       ....
       
      -
    4. Run npm run docs. Generate the documentation with JSDoc.

    5. Follow the prior PR submission

    6. Push your branch to the main repository (This branch will be used to create the release tag)

    7. Create a PR

    8. Assign yourself to the PR

    9. Have a reviewer approve your PR

    10. Create a Github release (through the GUI):

      • Copy the new content given in Changelog.md: this duplication doesn't seem to be DRY but the Release comments are hosted by github (and will be lost if you migrate to e.g. gitlab) and are not browsable offline.
        • You might consider using the "Auto-generate release notes" feature
        • Note that creating a Github release creates an underlying git tag (which can previously be created with a git tag)

    Publish the package

    To publish the package to npmjs package repository

    1. Authenticate on npmjs with npm login and use the vcity account together with proper credentials. Note:
      • Ask username and password to login at an admin
      • Because the npmjs authentication mode of the vcityaccount is currently configured to One-Time-Password (OTP) over email you will need to be part of the vcity@liris.cnrs.fr email alias forwarder to receive the OTP and be patient about it (the reception delay can be up to a couple minutes).
    2. npm publish --workspaces --access public (workspaces option refer to the workspaces field in the package.json).
    \ No newline at end of file +
  4. Run npm run docs. Generate the documentation with JSDoc.

  5. Follow the prior PR submission

  6. Push your branch to the main repository (This branch will be used to create the release tag)

  7. Create a PR

  8. Assign yourself to the PR

  9. Have a reviewer approve your PR

  10. Create a Github release (through the GUI):

    • Copy the new content given in Changelog.md: this duplication doesn't seem to be DRY but the Release comments are hosted by github (and will be lost if you migrate to e.g. gitlab) and are not browsable offline.
      • You might consider using the "Auto-generate release notes" feature
      • Note that creating a Github release creates an underlying git tag (which can previously be created with a git tag)

Publish the package

To publish the package to npmjs package repository

  1. Authenticate on npmjs with npm login and use the vcity account together with proper credentials. Note:
    • Ask username and password to login at an admin
    • Because the npmjs authentication mode of the vcityaccount is currently configured to One-Time-Password (OTP) over email you will need to be part of the vcity@liris.cnrs.fr email alias forwarder to receive the OTP and be patient about it (the reception delay can be up to a couple minutes).
  2. npm publish --workspaces --access public (workspaces option refer to the workspaces field in the package.json).
@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-Reproducibility.html b/docs/html/frame3d/tutorial-Reproducibility.html index 5f3c8551c..243aed307 100644 --- a/docs/html/frame3d/tutorial-Reproducibility.html +++ b/docs/html/frame3d/tutorial-Reproducibility.html @@ -1,3 +1,3 @@ Tutorial: Reproducibility
On this page

Reproducibility

The following stale branches

  • reproductibility/article-temporel-3DTiles
  • reproductibility/article-temporel-3DTilesTemporal

that have been deleted for sake of simplicity, can still be respectively retrieved through their associated tags with

  • git checkout -b reproductibility/article-temporel-3DTiles reproductibility/article-temporel-3DTiles-tag
  • git checkout -b reproductibility/article-temporel-3DTilesTemporal reproductibility/article-temporel-3DTilesTemporal-tag

Refer to this StackOverflow for a description of the branch "archiving"/retrieval process.

@ud-viz/frame3d
\ No newline at end of file +
On this page

Reproducibility

The following stale branches

  • reproductibility/article-temporel-3DTiles
  • reproductibility/article-temporel-3DTilesTemporal

that have been deleted for sake of simplicity, can still be respectively retrieved through their associated tags with

  • git checkout -b reproductibility/article-temporel-3DTiles reproductibility/article-temporel-3DTiles-tag
  • git checkout -b reproductibility/article-temporel-3DTilesTemporal reproductibility/article-temporel-3DTilesTemporal-tag

Refer to this StackOverflow for a description of the branch "archiving"/retrieval process.

@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-architecture.html b/docs/html/frame3d/tutorial-architecture.html index 503bc2477..fa0680f34 100644 --- a/docs/html/frame3d/tutorial-architecture.html +++ b/docs/html/frame3d/tutorial-architecture.html @@ -1,11 +1,12 @@ Tutorial: architecture
On this page

architecture

@ud-viz packages architecture

flowchart TB
+    
On this page

architecture

@ud-viz packages architecture

flowchart TB
 subgraph ud-viz/packages
 extensions_3d_tiles_temporal
 frame3d
 game_browser
 game_browser_template
+game_editor
 game_node
 game_node_template
 game_shared
@@ -40,6 +41,10 @@
 game_browser_template-->game_shared_template
 game_browser_template-->utils_browser
 game_browser_template-->utils_shared
+game_editor-->game_shared
+game_editor-->game_browser
+game_editor-->utils_browser
+game_editor-->utils_shared
 game_node-->game_shared
 game_node-->utils_shared
 game_node_template-->game_node
@@ -78,4 +83,4 @@
 widget_slide_show-->utils_browser
 widget_sparql-->utils_browser
 widget_workspace-->utils_browser
-widget_workspace-->widget_sparql
\ No newline at end of file +widget_workspace-->widget_sparql
@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-game.html b/docs/html/frame3d/tutorial-game.html index d54043637..75f79f41d 100644 --- a/docs/html/frame3d/tutorial-game.html +++ b/docs/html/frame3d/tutorial-game.html @@ -1,6 +1,6 @@ Tutorial: game
On this page

game

Game

This document gives an overview of how the game part works. Here are some different implementations of the game part:

Prerequisites

  • Run a back-end in node.js with express. See MDN DOC.
  • See here how to import @ud-viz/game_browser framework.

Creating a singleplayer simple game

The goal of this section is to learn how to set a singleplayer simple game structure:

Create a scene

Initialize a Planar part of @ud-viz/frame3d which creates a itowns PlanarView.

// Define geographic extent: CRS, min/max X, min/max Y
+    
On this page

game

Game

This document gives an overview of how the game part works. Here are some different implementations of the game part:

Prerequisites

  • Run a back-end in node.js with express. See MDN DOC.
  • See here how to import @ud-viz/game_browser framework.

Creating a singleplayer simple game

The goal of this section is to learn how to set a singleplayer simple game structure:

Create a scene

Initialize a Planar part of @ud-viz/frame3d which creates a itowns PlanarView.

// Define geographic extent: CRS, min/max X, min/max Y
 const extent = new udvizBrowser.itowns.Extent(
   'EPSG:4326',
   1837816.94334,
@@ -12,7 +12,6 @@
 const frame3DPlanar = new frame3d.Planar(extent, {
   hasItownsControls: true,
 });
-
 

Create a SinglePlanarProcess

Initialize a SinglePlanarProcess with a AssetManager and a InputManager part of @ud-viz/game_browser, an Object3D part of @ud-viz/game_shared defines your game model and SinglePlanarProcess is stepping your game over time.

const gameObject3D = new Object3D({
   static: true,
 });
@@ -47,10 +46,10 @@
   static: true,
   components: {
     GameScript: {
-      idScripts: [GameContextScript.ID_SCRIPT],
+      scriptParams: [{ id: GameContextScript.ID_SCRIPT }],
     },
     ExternalScript: {
-      idScripts: [GameExternalContextScript.ID_SCRIPT],
+      scriptParams: [{ id: GameExternalContextScript.ID_SCRIPT }],
     },
   },
 });
@@ -63,7 +62,6 @@
   {
     gameScriptClass: [GameContextScript],
     externalGameScriptClass: [GameExternalContextScript],
-    gameOrigin: { x: extent.center().x, y: extent.center().y, z: 100}
   }
 );
 

Spawn cubes

In the init of GameContextScript add these lines

this.goCubes = [];
@@ -87,7 +85,7 @@
   for (let index = this.goCubes.length - 1; index >= 0; index--) {
     const cube = this.goCubes[index];
     cube.position.z += 0.1 * this.context.dt;
-    cube.setOutdated(true);// notify game external context that this gameobject need update 
+    cube.setOutdated(true);// notify game external context that this gameobject need update
 
     // sky is the limit
     if (cube.position.z > 2000) {
@@ -96,23 +94,19 @@
     }
   }
 }
-

Send command

Add in the init of GameExternalContextScript the following code

this.context.inputManager.addMouseCommand(
-  'command_id',
-  'click',
-  () => {
-    return new Command({
-      type: 'toggle_pause',
-    });
-  }
-);
+

Send command

Add in the init of GameExternalContextScript the following code

this.context.inputManager.addMouseCommand('command_id', 'click', () => {
+  return new Command({
+    type: 'toggle_pause',
+  });
+});
 

Command is part of @ud-viz/game_shared

This sends a command on the mouse click to GameContextScript. Then in the init of GameContextScript add these lines:

this.pause = false;
 setInterval(() => {
   if (this.pause) return;
   ...
 

In the tick of GameContextScript

tick() {
+
+  this.applyCommandCallbackOf('toggle_pause', () => { this.pause = !this.pause })
+
   if (this.pause) return;
   ...
-

And finally add onCommand method:

onCommand(type) {
-  if (type === 'toggle_pause') this.pause = !this.pause;
-}
-

Now you have learned how to build a singleplayer simple game, let's see how to modify it to make a multiplayer one.

Create a multiplayer simple game (WIP)

1 Create a backend import udviz node with require as before run an express app running a http server Final result backend WIP

import game part of shared create a socket service by passsing the http server load gameobject3D where gameobject3D is the same one as the previous example the both script are unknown hard coded value of ids to keep it simple create another file gamethreadchild ok now you have to give the entry point to your thread the game script is the one running backend side copy paste it to your gamethread you have to give him the game script because he is going to run there replace udvizBrowser.Shared.Game by Game ouais backend okay

front end create a new html file base on previous one delete game script delete gameobject 3D Replace singleprocess by multiprocess create a socket io wrapper + connect it run enjoy

Examples

Singleplayer one:

This example requires knowledge about @ud-viz/game_shared and @ud-viz/game_browser

Multiplayer one:

This example requires the same knowledge as singleplayer plus @ud-viz/game_node

\ No newline at end of file +

Now you have learned how to build a singleplayer simple game, let's see how to modify it to make a multiplayer one.

Create a multiplayer simple game (WIP)

1 Create a backend import udviz node with require as before run an express app running a http server Final result backend WIP

import game part of shared create a socket service by passsing the http server load gameobject3D where gameobject3D is the same one as the previous example the both script are unknown hard coded value of ids to keep it simple create another file gamethreadchild ok now you have to give the entry point to your thread the game script is the one running backend side copy paste it to your gamethread you have to give him the game script because he is going to run there replace udvizBrowser.Shared.Game by Game ouais backend okay

front end create a new html file base on previous one delete game script delete gameobject 3D Replace singleprocess by multiprocess create a socket io wrapper + connect it run enjoy

Examples

Singleplayer one:

This example requires knowledge about @ud-viz/game_shared and @ud-viz/game_browser

Multiplayer one:

This example requires the same knowledge as singleplayer plus @ud-viz/game_node

@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-how_to_import.html b/docs/html/frame3d/tutorial-how_to_import.html index c5ed88437..1b5db650a 100644 --- a/docs/html/frame3d/tutorial-how_to_import.html +++ b/docs/html/frame3d/tutorial-how_to_import.html @@ -1,4 +1,4 @@ Tutorial: how_to_import
On this page

how_to_import

How to use it in your demo?

You can use it through npm

In your demo:

npm install --save @ud-viz/xxx
-

See https://github.com/VCityTeam/UD-Viz-template

@ud-viz/frame3d
\ No newline at end of file +
On this page

how_to_import

How to use it in your demo?

You can use it through npm

In your demo:

npm install --save @ud-viz/xxx
+

See https://github.com/VCityTeam/UD-Viz-template

@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/frame3d/tutorial-initial_camera_transform.html b/docs/html/frame3d/tutorial-initial_camera_transform.html index 83e3034b3..d07aea491 100644 --- a/docs/html/frame3d/tutorial-initial_camera_transform.html +++ b/docs/html/frame3d/tutorial-initial_camera_transform.html @@ -1,6 +1,6 @@ Tutorial: initial_camera_transform
On this page

initial_camera_transform

When creating a Planar part of @ud-viz/frame3d you may want your camera to be placed at a specific point of view. In this tutorial we are going to see differents options available to achieve that.

Configure Planar

Planar take an options object at the construction, where following fields will initialize camera point of view:

  • coordinates: camera look at geographic coordinate
  • heading: camera's heading, in degree
  • range: camera distance to target coordinate, in meter
  • tilt: camera's tilt, in degree

For more details check Planar documentation.

If you don't specify how the camera should be placed and you typed something like that:

const frame3DPlanar = new Planar(extent); // <-- no options passed
+    
On this page

initial_camera_transform

When creating a Planar part of @ud-viz/frame3d you may want your camera to be placed at a specific point of view. In this tutorial we are going to see differents options available to achieve that.

Configure Planar

Planar take an options object at the construction, where following fields will initialize camera point of view:

  • coordinates: camera look at geographic coordinate
  • heading: camera's heading, in degree
  • range: camera distance to target coordinate, in meter
  • tilt: camera's tilt, in degree

For more details check Planar documentation.

If you don't specify how the camera should be placed and you typed something like that:

const frame3DPlanar = new Planar(extent); // <-- no options passed
 

then the camera will look at the center of the extent with these default values:

  • heading: -50°
  • range: 3000m
  • tilt: 10°

if you want to configure that you should then write something like:

const frame3DPlanar = new Planar(extent, {
   coordinates: {
     x: some_x_coordinate
@@ -15,4 +15,4 @@
 

Note: the first time you will run your webpage the camera point of view will be the one pass to the Planar

Use URL

You can also use url util function to initialize default camera point of view. In that case you should first produce an url with camera matrix encoded in it.

const url = new URL(window.location.origin + window.location.pathname);
 appendCameraMatrixToURL(url, yourCamera);
 

then to initialize your camera with this url you would have to write something like that:

const isCameraMatrixInitializedWithURL = URLSetCameraMatrix(yourCamera);
-
\ No newline at end of file +
@ud-viz/frame3d
\ No newline at end of file diff --git a/docs/html/game_browser/AssetManager.html b/docs/html/game_browser/AssetManager.html index 710e2f6ae..a52e5a530 100644 --- a/docs/html/game_browser/AssetManager.html +++ b/docs/html/game_browser/AssetManager.html @@ -1,3 +1,3 @@ Class: AssetManager
On this page

AssetManager

Load async assets (gltf, JSON, ...) from a config file and create render data, sounds, and native objects.

Constructor

new AssetManager()

Initialize the native render data.

Classes

AssetManager

Members

conf :AssetManagerConfig

renderData :Object.<string, RenderData>

Type:

sounds :Object.<string, SoundsConfig>

Type:

Methods

createRenderData(idRenderData) → {RenderData}

Return new renderData corresponding to the id passed

Parameters:
NameTypeDescription
idRenderDatastring

Id of the renderData

Returns:
  • A clone of the renderData object
Type: 
RenderData

createSound(idSound, optionsopt) → {Howl}

Create a a new Howl object with the given idSound and options.

Parameters:
NameTypeAttributesDefaultDescription
idSoundstring

Id of sounds in config

optionsobject<optional>
{}

Arguments to create Howl object.

Properties
NameTypeDescription
loopboolean

Set to true to automatically loop the sound forever.

Returns:
  • Used to control the sound
Type: 
Howl

initNativeRenderData()

Build native objects (procedural objects) and stores them in this.renderData object.

loadFromConfig(config, parentDivopt) → {Promise}

Load a 3D render data from a config. Then create the LoadingView process.

Parameters:
NameTypeAttributesDefaultDescription
configAssetManagerConfig

configuration details

parentDivHTMLDivElement<optional>
document.body

where to add the loadingView

Returns:

promise processed to load assets

Type: 
Promise
@ud-viz/game_browser
\ No newline at end of file +
On this page

AssetManager

Load async assets (gltf, JSON, ...) from a config file and create render data, sounds, and native objects.

Constructor

new AssetManager()

Initialize the native render data.

Classes

AssetManager

Members

conf :AssetManagerConfig

renderData :Object.<string, RenderData>

Type:

sounds :Object.<string, SoundsConfig>

Type:

Methods

createRenderData(idRenderData) → {RenderData}

Return new renderData corresponding to the id passed

Parameters:
NameTypeDescription
idRenderDatastring

Id of the renderData

Returns:
  • A clone of the renderData object
Type: 
RenderData

createSound(idSound, optionsopt) → {Howl}

Create a a new Howl object with the given idSound and options.

Parameters:
NameTypeAttributesDefaultDescription
idSoundstring

Id of sounds in config

optionsobject<optional>
{}

Arguments to create Howl object.

Properties
NameTypeDescription
loopboolean

Set to true to automatically loop the sound forever.

Returns:
  • Used to control the sound
Type: 
Howl

initNativeRenderData()

Build native objects (procedural objects) and stores them in this.renderData object.

loadFromConfig(config, parentDivopt) → {Promise}

Load a 3D render data from a config. Then create the LoadingView process.

Parameters:
NameTypeAttributesDefaultDescription
configAssetManagerConfig

configuration details

parentDivHTMLDivElement<optional>
document.body

where to add the loadingView

Returns:

promise processed to load assets

Type: 
Promise
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/AssetManager.js.html b/docs/html/game_browser/AssetManager.js.html index 0c3b6b29f..c9a0416f6 100644 --- a/docs/html/game_browser/AssetManager.js.html +++ b/docs/html/game_browser/AssetManager.js.html @@ -1,6 +1,6 @@ Source: AssetManager.js
On this page

AssetManager.js

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
+    
On this page

AssetManager.js

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
 import * as THREE from 'three';
 import { Howl } from 'howler';
 import { colorSpace } from '@ud-viz/utils_browser';
@@ -381,5 +381,10 @@
 
     return new RenderData(cloneObject, {}, this.animations);
   }
+
+  dispose() {
+    if (this.object3D && this.object3D.parent)
+      this.object3D.parent.remove(this.object3D);
+  }
 }
-
\ No newline at end of file +
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/AudioController.html b/docs/html/game_browser/AudioController.html index 19a63eeb6..9ee790346 100644 --- a/docs/html/game_browser/AudioController.html +++ b/docs/html/game_browser/AudioController.html @@ -1,3 +1,3 @@ Class: AudioController
On this page

AudioController

new AudioController(model, object3D, assetManager)

Audio component controller

Parameters:
NameTypeDescription
modelModel

component model

object3DObject3D

object3D of component

assetManagerAssetManager

asset manager

Classes

AudioController

Members

assetManager :AssetManager

asset manager

sounds :Object.<string, Howl>

sounds of controller

Type:
  • Object.<string, Howl>

Methods

dispose()

Unload all Howl sounds

getSounds() → {Object.<string, Howl>}

Returns:
  • sounds controller
Type: 
Object.<string, Howl>

play(id)

Parameters:
NameTypeDescription
idstring

id of the sound to play

tick(cameraMatrixWorldInverse)

Tick controller

Parameters:
NameTypeDescription
cameraMatrixWorldInverseTHREE.Matrix4

camera matrix world inverse

@ud-viz/game_browser
\ No newline at end of file +
On this page

AudioController

new AudioController(model, object3D, assetManager)

Audio component controller

Parameters:
NameTypeDescription
modelModel

component model

object3DObject3D

object3D of component

assetManagerAssetManager

asset manager

Classes

AudioController

Members

assetManager :AssetManager

asset manager

sounds :Object.<string, Howl>

sounds of controller

Type:
  • Object.<string, Howl>

Methods

dispose()

Unload all Howl sounds

getSounds() → {Object.<string, Howl>}

Returns:
  • sounds controller
Type: 
Object.<string, Howl>

play(id)

Parameters:
NameTypeDescription
idstring

id of the sound to play

tick(cameraMatrixWorldInverse)

Tick controller

Parameters:
NameTypeDescription
cameraMatrixWorldInverseTHREE.Matrix4

camera matrix world inverse

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/AudioController.js.html b/docs/html/game_browser/AudioController.js.html index 5c559cea8..5b95e9da6 100644 --- a/docs/html/game_browser/AudioController.js.html +++ b/docs/html/game_browser/AudioController.js.html @@ -1,6 +1,6 @@ Source: AudioController.js
On this page

AudioController.js

import { AssetManager } from './AssetManager';
+    
On this page

AudioController.js

import { AssetManager } from './AssetManager';
 
 import { Model, Object3D, Controller } from '@ud-viz/game_shared';
 import { Howl } from 'howler';
@@ -78,7 +78,7 @@
       if (sound.state() != 'loaded') continue;
 
       if (this.model.getConf().autoplay && !sound.playing()) sound.play();
-      if (this.model.getConf().volume)
+      if (!isNaN(this.model.getConf().volume))
         sound.volume(this.model.getConf().volume);
 
       // https://github.com/goldfire/howler.js#documentation
@@ -99,4 +99,4 @@
     }
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/Context.html b/docs/html/game_browser/Context.html index 0dcad4a61..0ac9261fe 100644 --- a/docs/html/game_browser/Context.html +++ b/docs/html/game_browser/Context.html @@ -1,3 +1,3 @@ Class: Context
On this page

Context

new Context(frame3D, assetManager, inputManager, externalGameScriptClass, options)

Parameters:
NameTypeDescription
frame3Dframe3d.Base | frame3d.Planar

frame3D view of the game

assetManagerAssetManager

asset manager AssetManager

inputManagerInputManager

input manager InputManager

externalGameScriptClassObject.<string, ScriptBase>

custom external script ScriptBase

optionsobject

options of context

Properties
NameTypeDescription
userDataobject

user data of context

socketIOWrapperobject

socket io wrapper if multi

interpolatorobject

interpolator

Members

assetManager :AssetManager

asset manager

currentGameObject3D :Object3D

current root gameobject3D (child of this.object3D)

Type:
  • Object3D

currentUUID :Object.<string, boolean>

register uuid of object3D in context to identify new one incoming

Type:
  • Object.<string, boolean>

dt :number

delta time of context

Type:
  • number

externalGameScriptClass :Object.<string, ScriptBase>

custom ScriptBase that can be used by object3D

Type:

frame3D :frame3d.Base|frame3d.Planar

frame3D view of game

Type:
  • frame3d.Base | frame3d.Planar

inputManager :InputManager

input manager

interpolator :object|null

interpolator

Type:
  • object | null

object3D :THREE.Object3D

root object3D

Type:
  • THREE.Object3D

socketIOWrapper :undefined|null

socket io wrapper

Type:
  • undefined | null

userData :object

user data context

Type:
  • object

(static) EVENT

Event triggered by context to ScriptBase

Methods

createInstanceOf(id, object3D, modelVariables) → {ScriptBase}

Create a class instance of external game script class for an object3D given an id

Parameters:
NameTypeDescription
idstring

id of the class

object3DObject3D

object3D that is going to use this instance

modelVariablesobject

custom variables associated to this instance

Returns:
  • instance of the class bind with object3D and modelVariables
Type: 
ScriptBase

findExternalScriptWithID(id, object3Dopt) → {ScriptBase|null}

Parameters:
NameTypeAttributesDefaultDescription
idstring

id of script

object3DObject3D<optional>
this.object3D

object3D to traverse to find the external script (default is the root game object3D)

Returns:
  • first external script with id or null if none are found
Type: 
ScriptBase | null

findGameObjectWithExternalScriptID(id) → {Object3D|null}

Parameters:
NameTypeDescription
idstring

id of script

Returns:
  • first game object3D with external script id or null if none are found
Type: 
Object3D | null

initComponentControllers(go)

Parameters:
NameTypeDescription
goObject3D

gameobject3D to init controllers

initFrame3D(frame3D)

Parameters:
NameTypeDescription
frame3Dframe3d.Planar | frame3d.Base

intialize frame3D of context

reset(newFrame3D)

Reset context state and initialize the new frame3D

Parameters:
NameTypeDescription
newFrame3Dframe3d.Planar | frame3d.Base

new frame3D to reset with

sendCommandsToGameContext(cmds)

This method need to be implemented by user

Parameters:
NameTypeDescription
cmdsArray.<Command>

commands to send to game context

step(dt, states, updateGameObjectopt)

Step context

Parameters:
NameTypeAttributesDefaultDescription
dtnumber

new delta time context

statesArray.<State>

new states to update context current gameobject3D

updateGameObjectboolean<optional>
true

if false controllers are not going to tick

To Do
  • remove updateGameObject
@ud-viz/game_browser
\ No newline at end of file +
On this page

Context

new Context(frame3D, assetManager, inputManager, externalGameScriptClass, options)

Parameters:
NameTypeDescription
frame3Dframe3d.Base | frame3d.Planar

frame3D view of the game

assetManagerAssetManager

asset manager AssetManager

inputManagerInputManager

input manager InputManager

externalGameScriptClassObject.<string, ScriptBase>

custom external script ScriptBase

optionsobject

options of context

Properties
NameTypeDescription
userDataobject

user data of context

socketIOWrapperobject

socket io wrapper if multi

interpolatorobject

interpolator

Members

assetManager :AssetManager

asset manager

currentGameObject3D :Object3D

current root gameobject3D (child of this.object3D)

Type:
  • Object3D

currentUUID :Object.<string, boolean>

register uuid of object3D in context to identify new one incoming

Type:
  • Object.<string, boolean>

dt :number

delta time of context

Type:
  • number

externalGameScriptClass :Object.<string, ScriptBase>

custom ScriptBase that can be used by object3D

Type:

frame3D :frame3d.Base|frame3d.Planar

frame3D view of game

Type:
  • frame3d.Base | frame3d.Planar

inputManager :InputManager

input manager

interpolator :object|null

interpolator

Type:
  • object | null

object3D :THREE.Object3D

root object3D

Type:
  • THREE.Object3D

socketIOWrapper :undefined|null

socket io wrapper

Type:
  • undefined | null

userData :object

user data context

Type:
  • object

(static) EVENT

Event triggered by context to ScriptBase

Methods

createInstanceOf(id, object3D, modelVariables) → {ScriptBase}

Create a class instance of external game script class for an object3D given an id

Parameters:
NameTypeDescription
idstring

id of the class

object3DObject3D

object3D that is going to use this instance

modelVariablesobject

custom variables associated to this instance

Returns:
  • instance of the class bind with object3D and modelVariables
Type: 
ScriptBase

findExternalScriptWithID(id, object3Dopt) → {ScriptBase|null}

Parameters:
NameTypeAttributesDefaultDescription
idstring

id of script

object3DObject3D<optional>
this.object3D

object3D to traverse to find the external script (default is the root game object3D)

Returns:
  • first external script with id or null if none are found
Type: 
ScriptBase | null

initComponentControllers(go)

Parameters:
NameTypeDescription
goObject3D

gameobject3D to init controllers

initFrame3D(frame3D)

Parameters:
NameTypeDescription
frame3Dframe3d.Planar | frame3d.Base

intialize frame3D of context

reset(newFrame3D)

Reset context state and initialize the new frame3D

Parameters:
NameTypeDescription
newFrame3Dframe3d.Planar | frame3d.Base

new frame3D to reset with

sendCommandsToGameContext(cmds)

This method need to be implemented by user

Parameters:
NameTypeDescription
cmdsArray.<Command>

commands to send to game context

step(dt, states)

Step context

Parameters:
NameTypeDescription
dtnumber

new delta time context

statesArray.<State>

new states to update context current gameobject3D

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/Context.js.html b/docs/html/game_browser/Context.js.html index 0a6e25581..0ff2e4718 100644 --- a/docs/html/game_browser/Context.js.html +++ b/docs/html/game_browser/Context.js.html @@ -1,6 +1,6 @@ Source: Context.js
On this page

Context.js

import { RenderController } from './RenderController';
+    
On this page

Context.js

import { RenderController } from './RenderController';
 import { AudioController } from './AudioController';
 import { AssetManager } from './AssetManager';
 import { InputManager } from './InputManager';
@@ -209,10 +209,8 @@
    *
    * @param {number} dt - new delta time context
    * @param {Array<State>} states - new states to update context current gameobject3D
-   * @param {boolean} [updateGameObject=true] - if false controllers are not going to tick
-   * @todo remove updateGameObject
    */
-  step(dt, states, updateGameObject = true) {
+  step(dt, states) {
     this.dt = dt; // ref it for external scripts
 
     /** @type {Object3D[]} */
@@ -223,167 +221,153 @@
 
     // Update currentGameObject3D with the new states
     if (this.currentGameObject3D) {
-      if (updateGameObject) {
-        this.currentGameObject3D.traverse((child) => {
-          if (!child.isGameObject3D) return;
+      this.currentGameObject3D.traverse((child) => {
+        if (!child.isGameObject3D) return;
 
-          const gameContextChild = state
-            .getObject3D()
-            .getObjectByProperty('uuid', child.uuid);
-          if (gameContextChild) {
-            // still present in game context
-            if (child.hasGameContextUpdate()) {
-              if (!child.isStatic()) {
-                // if no static update transform
-                child.position.copy(gameContextChild.position);
-                child.scale.copy(gameContextChild.scale);
-                child.rotation.copy(gameContextChild.rotation);
-              }
+        const gameContextChild = state
+          .getObject3D()
+          .getObjectByProperty('uuid', child.uuid);
+        if (gameContextChild) {
+          // still present in game context
+          if (child.hasGameContextUpdate()) {
+            if (!child.isStatic()) {
+              // if no static update transform
+              child.position.copy(gameContextChild.position);
+              child.scale.copy(gameContextChild.scale);
+              child.rotation.copy(gameContextChild.rotation);
+            }
 
-              // Stack the same go of all states not consumed yet
-              const bufferedGO = [];
-              states.forEach((s) => {
-                const bGO = s
-                  .getObject3D()
-                  .getObjectByProperty('uuid', child.uuid);
-                if (bGO) bufferedGO.push(bGO);
-              });
-
-              // Update local component for bufferedGO
-              let componentHasBeenUpdated = false; // Flag to know if a change of state occured
-
-              const childRenderComp = child.getComponent(RenderComponent.TYPE);
-              const childExternalScriptComp = child.getComponent(
-                ExternalScriptComponent.TYPE
-              );
-
-              for (let index = 0; index < bufferedGO.length; index++) {
-                const gameContextGONotConsumned = bufferedGO[index];
-
-                // Render comp
-                if (childRenderComp) {
-                  const bufferedRenderComp =
-                    gameContextGONotConsumned.getComponent(
-                      RenderComponent.TYPE
-                    );
-
-                  // Check if color change
-                  if (
-                    !arrayEquals(
-                      childRenderComp.getModel().getColor(),
-                      bufferedRenderComp.getModel().getColor()
-                    )
-                  ) {
-                    console.error('DEPRECATED');
-                    childRenderComp.setColor(bufferedRenderComp.getColor());
-                    componentHasBeenUpdated = true; // Notify change
-                  }
-
-                  // Check if idModel change
-                  if (
-                    childRenderComp.getModel().getIdRenderData() !=
-                    bufferedRenderComp.getModel().getIdRenderData()
-                  ) {
-                    console.error('DEPRECATED');
-                    childRenderComp
-                      .getController()
-                      .setIdRenderData(
-                        bufferedRenderComp.getModel().getIdRenderData()
-                      );
-
-                    componentHasBeenUpdated = true;
-                  }
-                }
+            // Stack the same go of all states not consumed yet
+            const bufferedGO = [];
+            states.forEach((s) => {
+              const bGO = s
+                .getObject3D()
+                .getObjectByProperty('uuid', child.uuid);
+              if (bGO) bufferedGO.push(bGO);
+            });
+
+            const childRenderComp = child.getComponent(RenderComponent.TYPE);
+            const childExternalScriptComp = child.getComponent(
+              ExternalScriptComponent.TYPE
+            );
+
+            let renderCompHasChanged = false;
+            for (let index = 0; index < bufferedGO.length; index++) {
+              const gameContextGONotConsumned = bufferedGO[index];
+
+              // Render comp
+              if (childRenderComp) {
+                const bufferedRenderComp =
+                  gameContextGONotConsumned.getComponent(RenderComponent.TYPE);
 
+                // Check if color change
                 if (
-                  childExternalScriptComp &&
-                  gameContextGONotConsumned.isOutdated()
+                  !arrayEquals(
+                    childRenderComp.model.color,
+                    bufferedRenderComp.model.color
+                  )
                 ) {
-                  const bufferedExternalScriptComp =
-                    gameContextGONotConsumned.getComponent(
-                      ExternalScriptComponent.TYPE
-                    );
+                  childRenderComp
+                    .getController()
+                    .setColor(bufferedRenderComp.model.color);
+                  renderCompHasChanged = true;
+                }
 
-                  // Replace variables in external script
-                  childExternalScriptComp
+                // Check if idRenderData change
+                if (
+                  childRenderComp.model.idRenderData !=
+                  bufferedRenderComp.model.idRenderData
+                ) {
+                  childRenderComp
                     .getController()
-                    .setVariables(
-                      bufferedExternalScriptComp.getModel().getVariables()
-                    );
-
-                  // Launch event onOutdated
-                  componentHasBeenUpdated =
-                    componentHasBeenUpdated ||
-                    childExternalScriptComp
-                      .getController()
-                      .execute(Context.EVENT.ON_OUTDATED);
+                    .setIdRenderData(bufferedRenderComp.model.idRenderData);
+                  renderCompHasChanged = true;
                 }
               }
 
-              if (componentHasBeenUpdated && childExternalScriptComp) {
-                // Launch event onComponentUpdate
+              if (childExternalScriptComp && renderCompHasChanged) {
                 childExternalScriptComp
                   .getController()
-                  .execute(Context.EVENT.ON_COMPONENT_UPDATE);
+                  .execute(Context.EVENT.ON_RENDER_COMPONENT_CHANGED);
               }
-            }
-          } else {
-            // Do not exist remove it
-            child.removeFromParent();
 
-            // external script event remove
-            const scriptComponent = child.getComponent(
-              ExternalScriptComponent.TYPE
-            );
-            if (scriptComponent) {
-              scriptComponent.getController().execute(Context.EVENT.ON_REMOVE);
-            }
+              if (
+                childExternalScriptComp &&
+                gameContextGONotConsumned.isOutdated()
+              ) {
+                const bufferedExternalScriptComp =
+                  gameContextGONotConsumned.getComponent(
+                    ExternalScriptComponent.TYPE
+                  );
 
-            // Audio removal
-            const audioComponent = child.getComponent(AudioComponent.TYPE);
-            if (audioComponent) {
-              audioComponent.getController().dispose();
-            }
+                // Replace variables in external script
+                childExternalScriptComp
+                  .getController()
+                  .setVariables(bufferedExternalScriptComp.model.variables);
 
-            // notify other that child is removed
-            this.currentGameObject3D.traverse((otherGameObject) => {
-              if (!otherGameObject.isGameObject3D) return;
-              const externalComp = otherGameObject.getComponent(
-                ExternalScriptComponent.TYPE
-              );
-              if (externalComp) {
-                externalComp
+                // Launch event onOutdated
+                childExternalScriptComp
                   .getController()
-                  .execute(Context.EVENT.ON_GAMEOBJECT_REMOVED, [child]);
+                  .execute(Context.EVENT.ON_OUTDATED);
               }
-            });
+            }
+          }
+        } else {
+          // Do not exist remove it
+          child.removeFromParent();
 
-            delete this.currentUUID[child.uuid];
+          // external script event remove
+          const scriptComponent = child.getComponent(
+            ExternalScriptComponent.TYPE
+          );
+          if (scriptComponent) {
+            scriptComponent.getController().execute(Context.EVENT.ON_REMOVE);
           }
-        });
 
-        state.getObject3D().traverse((child) => {
-          if (!child.isGameObject3D) return; // => this one should be useless since State should be only compose of GameObject3D
+          // Audio removal
+          const audioComponent = child.getComponent(AudioComponent.TYPE);
+          if (audioComponent) {
+            audioComponent.getController().dispose();
+          }
 
-          const old = this.currentGameObject3D.getObjectByProperty(
+          // notify other that child is removed
+          this.currentGameObject3D.traverse((otherGameObject) => {
+            if (!otherGameObject.isGameObject3D) return;
+            const externalComp = otherGameObject.getComponent(
+              ExternalScriptComponent.TYPE
+            );
+            if (externalComp) {
+              externalComp
+                .getController()
+                .execute(Context.EVENT.ON_GAMEOBJECT_REMOVED, [child]);
+            }
+          });
+
+          delete this.currentUUID[child.uuid];
+        }
+      });
+
+      state.getObject3D().traverse((child) => {
+        if (!child.isGameObject3D) return; // => this one should be useless since State should be only compose of GameObject3D
+
+        const old = this.currentGameObject3D.getObjectByProperty(
+          'uuid',
+          child.uuid
+        );
+        if (!old) {
+          // New one add it
+          const parent = this.currentGameObject3D.getObjectByProperty(
             'uuid',
-            child.uuid
+            child.parentUUID
           );
-          if (!old) {
-            // New one add it
-            const parent = this.currentGameObject3D.getObjectByProperty(
-              'uuid',
-              child.parentUUID
-            );
 
-            parent.add(child);
-          }
+          parent.add(child);
+        }
 
-          if (!this.currentUUID[child.uuid]) {
-            newGO.push(child);
-          }
-        });
-      }
+        if (!this.currentUUID[child.uuid]) {
+          newGO.push(child);
+        }
+      });
     } else {
       // first state
       this.currentGameObject3D = state.getObject3D();
@@ -431,7 +415,7 @@
     this.object3D.updateMatrixWorld();
 
     // Update shadow
-    if (newGO.length) {
+    if (newGO.length && this.frame3D.sceneConfig) {
       bindLightTransform(
         this.frame3D.sceneConfig.sky.sun_position.offset,
         this.frame3D.sceneConfig.sky.sun_position.phi,
@@ -441,33 +425,28 @@
       );
     }
 
-    // TODO updateGameObject ??? refacto editor
-    if (updateGameObject) {
-      this.currentGameObject3D.traverse((child) => {
-        if (!child.isGameObject3D) return;
+    this.currentGameObject3D.traverse((child) => {
+      if (!child.isGameObject3D) return;
 
-        // Tick local script
-        const scriptComponent = child.getComponent(
-          ExternalScriptComponent.TYPE
-        );
-        if (scriptComponent) {
-          scriptComponent.getController().execute(Context.EVENT.TICK);
-        }
+      // Tick external script
+      const scriptComponent = child.getComponent(ExternalScriptComponent.TYPE);
+      if (scriptComponent) {
+        scriptComponent.getController().execute(Context.EVENT.TICK);
+      }
 
-        // Tick audio component
-        const audioComp = child.getComponent(AudioComponent.TYPE);
-        // Position in world referential
-        if (audioComp) {
-          const camera = this.frame3D.camera;
-          const cameraMatWorldInverse = camera.matrixWorldInverse;
-          audioComp.getController().tick(cameraMatWorldInverse);
-        }
+      // Tick audio component
+      const audioComp = child.getComponent(AudioComponent.TYPE);
+      // Position in world referential
+      if (audioComp) {
+        const camera = this.frame3D.camera;
+        const cameraMatWorldInverse = camera.matrixWorldInverse;
+        audioComp.getController().tick(cameraMatWorldInverse);
+      }
 
-        // Render component
-        const renderComp = child.getComponent(RenderComponent.TYPE);
-        if (renderComp) renderComp.getController().tick(dt);
-      });
-    }
+      // Render component
+      const renderComp = child.getComponent(RenderComponent.TYPE);
+      if (renderComp) renderComp.getController().tick(dt);
+    });
   }
 
   /**
@@ -481,7 +460,7 @@
       if (component.getController()) {
         throw new Error('controller already init ' + go.name);
       }
-      const scripts = {};
+      let scripts = null;
       switch (type) {
         case AudioComponent.TYPE:
           component.initController(
@@ -494,16 +473,37 @@
           );
           break;
         case ExternalScriptComponent.TYPE:
-          component
-            .getModel()
-            .getIdScripts()
-            .forEach((idScript) => {
-              scripts[idScript] = this.createInstanceOf(
-                idScript,
+          scripts = new Map();
+          component.getModel().scriptParams.forEach((sParams) => {
+            scripts.set(
+              sParams.id,
+              this.createInstanceOf(
+                sParams.id,
                 go,
-                component.getModel().getVariables()
-              );
-            });
+                component.getModel().variables
+              )
+            );
+          });
+
+          scripts = new Map(
+            [...scripts.entries()].sort((a, b) => {
+              const aSParam = component
+                .getModel()
+                .scriptParams.filter((el) => el.id === a[0]);
+              const bSParam = component
+                .getModel()
+                .scriptParams.filter((el) => el.id === b[0]);
+
+              const aPrio = !isNaN(aSParam[0].priority)
+                ? aSParam[0].priority
+                : -Infinity;
+              const bPrio = !isNaN(bSParam[0].priority)
+                ? bSParam[0].priority
+                : -Infinity;
+
+              return bPrio - aPrio;
+            })
+          );
           component.initController(
             new ScriptController(component.getModel(), go, scripts)
           );
@@ -552,36 +552,9 @@
 
       if (!externalScriptComp) return;
 
-      const scripts = externalScriptComp.getController().getScripts();
-      if (scripts && scripts[id]) {
-        result = scripts[id];
-        return true;
-      }
-      return false;
-    });
-
-    return result;
-  }
-
-  /**
-   *
-   * @param {string} id - id of script
-   * @returns {Object3D|null} - first game object3D with external script id or null if none are found
-   */
-  findGameObjectWithExternalScriptID(id) {
-    let result = null;
-    this.object3D.traverse(function (child) {
-      if (!child.isGameObject3D) return;
-
-      const externalScriptComp = child.getComponent(
-        ExternalScriptComponent.TYPE
-      );
-
-      if (!externalScriptComp) return;
-
-      const scripts = externalScriptComp.getController().getScripts();
-      if (scripts && scripts[id]) {
-        result = child;
+      const scripts = externalScriptComp.getController().scripts;
+      if (scripts && scripts.has(id)) {
+        result = scripts.get(id);
         return true;
       }
       return false;
@@ -612,11 +585,11 @@
   ON_OUTDATED: 'onOutdated',
   DISPOSE: 'dispose',
   ON_REMOVE: 'onRemove',
-  ON_COMPONENT_UPDATE: 'onComponentUpdate',
+  ON_RENDER_COMPONENT_CHANGED: 'onRenderComponentChanged',
   ON_RESIZE: 'onResize',
 };
 
-export class ScriptBase {
+export class ScriptBase extends THREE.EventDispatcher {
   /**
    * Skeleton of a game context script, different {@link Context.EVENT} are trigger by {@link Context}
    *
@@ -625,6 +598,7 @@
    * @param {object} variables - custom variables bind (attach) to this script
    */
   constructor(context, object3D, variables) {
+    super();
     /**
      * context of this script
      *
@@ -671,10 +645,6 @@
    * call when this gameobject 3D is removed from context
    */
   onRemove() {}
-  /**
-   * call when an external script onOutdated return true
-   */
-  onComponentUpdate() {}
   /**
    * call when frame3D is disposed
    */
@@ -683,10 +653,14 @@
    * call when frame3D is resized
    */
   onResize() {}
+  /**
+   * call when the render component of the object has changed
+   */
+  onRenderComponentChanged() {}
 
   static get ID_SCRIPT() {
     console.error(this.name);
     throw new Error('this is abstract class you should override ID_SCRIPT');
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/InputManager.html b/docs/html/game_browser/InputManager.html index 98ee5a276..79b28cce0 100644 --- a/docs/html/game_browser/InputManager.html +++ b/docs/html/game_browser/InputManager.html @@ -1,3 +1,3 @@ Class: InputManager
On this page

InputManager

new InputManager(isCaseSensitiveopt)

Manage user inputs with a poll system (https://en.wikipedia.org/wiki/Polling_(computer_science))

Parameters:
NameTypeAttributesDefaultDescription
isCaseSensitiveboolean<optional>
false

true so that the inputs are case sensitive

Classes

InputManager

Members

commandsBuffer :Object.<string, boolean>

avoid a command to be computed two times if multiple keys are attach to it

Type:
  • Object.<string, boolean>

element :HTMLElement

element catching mouse event

Type:
  • HTMLElement

isCaseSensitive :boolean

true so that the inputs are case sensitive

Type:
  • boolean

keyCommands :CommandCallback

register a callback associated to a key event

keyMap :Object.<string, boolean>

register if a key is pressed or not

Type:
  • Object.<string, boolean>

keyMapKeyDown :Array.<string>

register if a key state is down

Type:
  • Array.<string>

keyMapKeyUp :Array.<string>

register if a key state is up

Type:
  • Array.<string>

listeners :Array.<{element:HTMLElement, id:string, cb:EventCallback, listener:EventCallback}>

register listeners to dispose them

Type:
  • Array.<{element:HTMLElement, id:string, cb:EventCallback, listener:EventCallback}>

mouseCommands :Object.<string, Object.<string, function(MouseState): Command>>

register callback associated to an event + command id

Type:
  • Object.<string, Object.<string, function(MouseState): Command>>

mouseState :MouseState

store state of the mouse

pause :boolean

if true EventCallback and CommandCallback are not called

Type:
  • boolean

Methods

addKeyCommand(commandID, keys, cb)

Add a command for severals keys

Parameters:
NameTypeDescription
commandIDstring

command id

keysArray.<string>

keys id assigned

cbfunction

callback called

addKeyInput(key, eventID, cb)

Register a callback for a particular key and event

Parameters:
NameTypeDescription
keystring | null

id of the key if null every key trigger the event

eventIDstring

id of the event (keyup, keydown)

cbEventCallback

callback called for this event

addMouseCommand(commandID, eventID, cb)

Add a command for mouse

Parameters:
NameTypeDescription
commandIDstring

id of the command

eventIDstring

id of the mouse to listen to

cbfunction

callback called at event

addMouseInput(element, eventID, cb)

Register a callback for a particular mouse event

Parameters:
NameTypeDescription
elementHTMLElement

element listened

eventIDstring

id of the event (mousedown, mouseup, mousemove)

cbEventCallback

callback called for this event

computeCommands() → {Array.<Command>}

Compute Commands with the last state stored of keys and mouse

Returns:
  • commands computed
Type: 
Array.<Command>

controlSensitivity(key) → {string}

Converts a key to lowercase if case sensitivity is not required.

Parameters:
NameTypeDescription
keystring

keyboard input

Returns:

the input key in lowercase if the isCaseSensitive is false and the input key is a letter. Returns the base key otherwise.

Type: 
string

dispose()

Remove listeners and reset variables

getElement() → {HTMLElement}

Returns:
  • input manager element
Type: 
HTMLElement

getPointerLock() → {boolean}

Returns:
  • input manager pointer lock value
Type: 
boolean

initPointerLock()

Initialize pointer lock management On keypress keyup and click event try to request pointer lock on this.element if this.pointerLock is true

isKeyALetter(key) → {boolean}

Parameters:
NameTypeDescription
keystring

keyboard input

Returns:
  • returns true if the key is a letter
Type: 
boolean

isKeyDown(key) → {boolean}

Parameters:
NameTypeDescription
keystring

key id

Returns:
  • true if the key state is down
Type: 
boolean

isKeyUp(key) → {boolean}

Parameters:
NameTypeDescription
keystring

key id

Returns:
  • true if the key state is up
Type: 
boolean

isPressed(key) → {boolean}

Return true if the key is pressed, dont forget to listenKeys if no addKeyCommand has been used for this key

Parameters:
NameTypeDescription
keystring

key id

Returns:
  • true if pressed, false otherwise
Type: 
boolean

listenKeys(keys)

Used this if a key has not been register in addKeyCommand and you need to know if it's isPressed

Parameters:
NameTypeDescription
keysArray.<string>

ids of the key to listen to

removeInputListener(cb)

Remove event listener with the callback used to register it

Parameters:
NameTypeDescription
cbEventCallback

listener pass at the registration

removeKeyCommand(commandID, keys)

Dispose a command associated to keys

Parameters:
NameTypeDescription
commandIDstring

id command

keysArray.<string>

keys id

removeMouseCommand(commandID, eventID)

Parameters:
NameTypeDescription
commandIDstring

command id

eventIDstring

mouse event id MOUSE_STATE_EVENTS

setPause(pause)

Parameters:
NameTypeDescription
pauseboolean

new inputmanager pause value

setPointerLock(value)

If value is true pointerLock mode is activated else it's exited

Parameters:
NameTypeDescription
valueboolean

new pointerlock value

startListening(element)

Start listening

Parameters:
NameTypeDescription
elementHTMLElement

element listened by mouse

@ud-viz/game_browser
\ No newline at end of file +
On this page

InputManager

new InputManager(isCaseSensitiveopt)

Manage user inputs with a poll system (https://en.wikipedia.org/wiki/Polling_(computer_science))

Parameters:
NameTypeAttributesDefaultDescription
isCaseSensitiveboolean<optional>
false

true so that the inputs are case sensitive

Classes

InputManager

Members

commandsBuffer :Object.<string, boolean>

avoid a command to be computed two times if multiple keys are attach to it

Type:
  • Object.<string, boolean>

element :HTMLElement

element catching mouse event

Type:
  • HTMLElement

isCaseSensitive :boolean

true so that the inputs are case sensitive

Type:
  • boolean

keyCommands :CommandCallback

register a callback associated to a key event

keyMap :Object.<string, boolean>

register if a key is pressed or not

Type:
  • Object.<string, boolean>

keyMapKeyDown :Array.<string>

register if a key state is down

Type:
  • Array.<string>

keyMapKeyUp :Array.<string>

register if a key state is up

Type:
  • Array.<string>

listeners :Array.<{element:HTMLElement, id:string, cb:EventCallback, listener:EventCallback}>

register listeners to dispose them

Type:
  • Array.<{element:HTMLElement, id:string, cb:EventCallback, listener:EventCallback}>

mouseCommands :Object.<string, Object.<string, function(MouseState): Command>>

register callback associated to an event + command id

Type:
  • Object.<string, Object.<string, function(MouseState): Command>>

mouseState :MouseState

store state of the mouse

pause :boolean

if true EventCallback and CommandCallback are not called

Type:
  • boolean

Methods

addKeyCommand(commandID, keys, cb)

Add a command for severals keys

Parameters:
NameTypeDescription
commandIDstring

command id

keysArray.<string>

keys id assigned

cbfunction

callback called

addKeyInput(key, eventID, cb)

Register a callback for a particular key and event

Parameters:
NameTypeDescription
keystring | null

id of the key if null every key trigger the event

eventIDstring

id of the event (keyup, keydown)

cbEventCallback

callback called for this event

addMouseCommand(commandID, eventID, cb)

Add a command for mouse

Parameters:
NameTypeDescription
commandIDstring

id of the command

eventIDstring

id of the mouse to listen to

cbfunction

callback called at event

addMouseInput(element, eventID, cb)

Register a callback for a particular mouse event

Parameters:
NameTypeDescription
elementHTMLElement

element listened

eventIDstring

id of the event (mousedown, mouseup, mousemove)

cbEventCallback

callback called for this event

computeCommands() → {Array.<Command>}

Compute Commands with the last state stored of keys and mouse

Returns:
  • commands computed
Type: 
Array.<Command>

controlSensitivity(key) → {string}

Converts a key to lowercase if case sensitivity is not required.

Parameters:
NameTypeDescription
keystring

keyboard input

Returns:

the input key in lowercase if the isCaseSensitive is false and the input key is a letter. Returns the base key otherwise.

Type: 
string

dispose()

Remove listeners and reset variables

getElement() → {HTMLElement}

Returns:
  • input manager element
Type: 
HTMLElement

getPointerLock() → {boolean}

Returns:
  • input manager pointer lock value
Type: 
boolean

initPointerLock()

Initialize pointer lock management On keypress keyup and click event try to request pointer lock on this.element if this.pointerLock is true

isKeyALetter(key) → {boolean}

Parameters:
NameTypeDescription
keystring

keyboard input

Returns:
  • returns true if the key is a letter
Type: 
boolean

isKeyDown(key) → {boolean}

Parameters:
NameTypeDescription
keystring

key id

Returns:
  • true if the key state is down
Type: 
boolean

isKeyUp(key) → {boolean}

Parameters:
NameTypeDescription
keystring

key id

Returns:
  • true if the key state is up
Type: 
boolean

isPressed(key) → {boolean}

Return true if the key is pressed, dont forget to listenKeys if no addKeyCommand has been used for this key

Parameters:
NameTypeDescription
keystring

key id

Returns:
  • true if pressed, false otherwise
Type: 
boolean

listenKeys(keys)

Used this if a key has not been register in addKeyCommand and you need to know if it's isPressed

Parameters:
NameTypeDescription
keysArray.<string>

ids of the key to listen to

removeInputListener(cb)

Remove event listener with the callback used to register it

Parameters:
NameTypeDescription
cbEventCallback

listener pass at the registration

removeKeyCommand(commandID, keys)

Dispose a command associated to keys

Parameters:
NameTypeDescription
commandIDstring

id command

keysArray.<string>

keys id

removeMouseCommand(commandID, eventID)

Parameters:
NameTypeDescription
commandIDstring

command id

eventIDstring

mouse event id MOUSE_STATE_EVENTS

setPause(pause)

Parameters:
NameTypeDescription
pauseboolean

new inputmanager pause value

setPointerLock(value)

If value is true pointerLock mode is activated else it's exited

Parameters:
NameTypeDescription
valueboolean

new pointerlock value

startListening(element)

Start listening

Parameters:
NameTypeDescription
elementHTMLElement

element listened by mouse

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/InputManager.js.html b/docs/html/game_browser/InputManager.js.html index 3413ce460..e326a9fcc 100644 --- a/docs/html/game_browser/InputManager.js.html +++ b/docs/html/game_browser/InputManager.js.html @@ -1,6 +1,6 @@ Source: InputManager.js
On this page

InputManager.js

import { Command } from '@ud-viz/game_shared';
+    
On this page

InputManager.js

import { Command } from '@ud-viz/game_shared';
 
 /**
  * @callback EventCallback
@@ -593,4 +593,4 @@
     }
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/LoadingView.html b/docs/html/game_browser/LoadingView.html index f634ba2fb..e2d54fe1e 100644 --- a/docs/html/game_browser/LoadingView.html +++ b/docs/html/game_browser/LoadingView.html @@ -1,3 +1,3 @@ Class: LoadingView
On this page

LoadingView

A view in which loading bar can be added

Constructor

new LoadingView()

It creates a root HTML, then adds HTML elements for the loading bar.

Members

domElement :HTMLDivElement

Type:
  • HTMLDivElement

loadingBars :Object.<string, HTMLDivElement>

Loading bars

Type:
  • Object.<string, HTMLDivElement>

parentLoadingBar :HTMLDivElement

Type:
  • HTMLDivElement

Methods

addLoadingBar(id)

Add a loading bar to this view with a label equals to the id

Parameters:
NameTypeDescription
idstring

if of the loading bar to add

updateProgress(id, percent)

Updates the progress bar of a loading bar with the given id. Sets the width of the loading bar with the given percent.

Parameters:
NameTypeDescription
idstring

of the loading bar

percentnumber

the new percent of the bar

@ud-viz/game_browser
\ No newline at end of file +
On this page

LoadingView

A view in which loading bar can be added

Constructor

new LoadingView()

It creates a root HTML, then adds HTML elements for the loading bar.

Members

domElement :HTMLDivElement

Type:
  • HTMLDivElement

loadingBars :Object.<string, HTMLDivElement>

Loading bars

Type:
  • Object.<string, HTMLDivElement>

parentLoadingBar :HTMLDivElement

Type:
  • HTMLDivElement

Methods

addLoadingBar(id)

Add a loading bar to this view with a label equals to the id

Parameters:
NameTypeDescription
idstring

if of the loading bar to add

updateProgress(id, percent)

Updates the progress bar of a loading bar with the given id. Sets the width of the loading bar with the given percent.

Parameters:
NameTypeDescription
idstring

of the loading bar

percentnumber

the new percent of the bar

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/MouseState.html b/docs/html/game_browser/MouseState.html index 94d388e21..f32b0badd 100644 --- a/docs/html/game_browser/MouseState.html +++ b/docs/html/game_browser/MouseState.html @@ -1,3 +1,3 @@ Class: MouseState
On this page

MouseState

new MouseState()

Poll system (https://en.wikipedia.org/wiki/Polling_(computer_science)) Listen to the MOUSE_STATE_EVENTS and store the mouse state to then be access synchronously

Classes

MouseState

Members

dragging :boolean

true if the mouse is dragging

Type:
  • boolean

listeners :Array.<{element:HTMLElement, listener:EventCallback, id:string}>

register all listeners to well dispose them on dipose

Type:
  • Array.<{element:HTMLElement, listener:EventCallback, id:string}>

mouseEvent :Object.<string, Event>

register event native js to pass it later synchronously

Type:
  • Object.<string, Event>

mouseMap :Object.<string, boolean>

register if a mouse event is trigger or not

Type:
  • Object.<string, boolean>

Methods

addEvent(element, idEvent) → {EventCallback}

Add a listener for a particular event on element

Parameters:
NameTypeDescription
elementHTMLElement

element to listen to

idEventstring

mouse events

Returns:
  • Callback call for this event
Type: 
EventCallback

dispose()

Remove listeners and reset variables

event(eventID) → {Event}

Access the last Event stored for eventID

Parameters:
NameTypeDescription
eventIDstring

id of the mouse event

Returns:
  • The last event store for this event
Type: 
Event

isDragging() → {boolean}

Returns:
  • true if the mouse is dragging, false otherwise
Type: 
boolean

isTrigger(eventID) → {boolean}

Return true if this event has been triggered on the last poll

Parameters:
NameTypeDescription
eventIDstring

event id

Returns:
  • true if the eventID has been triggered
Type: 
boolean

reset()

Reset Event triggered

startListening(element)

Start listening MOUSE_STATE_EVENTS on the element

Parameters:
NameTypeDescription
elementHTMLElement

html catching events

@ud-viz/game_browser
\ No newline at end of file +
On this page

MouseState

new MouseState()

Poll system (https://en.wikipedia.org/wiki/Polling_(computer_science)) Listen to the MOUSE_STATE_EVENTS and store the mouse state to then be access synchronously

Classes

MouseState

Members

dragging :boolean

true if the mouse is dragging

Type:
  • boolean

listeners :Array.<{element:HTMLElement, listener:EventCallback, id:string}>

register all listeners to well dispose them on dipose

Type:
  • Array.<{element:HTMLElement, listener:EventCallback, id:string}>

mouseEvent :Object.<string, Event>

register event native js to pass it later synchronously

Type:
  • Object.<string, Event>

mouseMap :Object.<string, boolean>

register if a mouse event is trigger or not

Type:
  • Object.<string, boolean>

Methods

addEvent(element, idEvent) → {EventCallback}

Add a listener for a particular event on element

Parameters:
NameTypeDescription
elementHTMLElement

element to listen to

idEventstring

mouse events

Returns:
  • Callback call for this event
Type: 
EventCallback

dispose()

Remove listeners and reset variables

event(eventID) → {Event}

Access the last Event stored for eventID

Parameters:
NameTypeDescription
eventIDstring

id of the mouse event

Returns:
  • The last event store for this event
Type: 
Event

isDragging() → {boolean}

Returns:
  • true if the mouse is dragging, false otherwise
Type: 
boolean

isTrigger(eventID) → {boolean}

Return true if this event has been triggered on the last poll

Parameters:
NameTypeDescription
eventIDstring

event id

Returns:
  • true if the eventID has been triggered
Type: 
boolean

reset()

Reset Event triggered

startListening(element)

Start listening MOUSE_STATE_EVENTS on the element

Parameters:
NameTypeDescription
elementHTMLElement

html catching events

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/MultiPlanarProcess.html b/docs/html/game_browser/MultiPlanarProcess.html index 84cd30465..a37f31a7f 100644 --- a/docs/html/game_browser/MultiPlanarProcess.html +++ b/docs/html/game_browser/MultiPlanarProcess.html @@ -1,3 +1,3 @@ Class: MultiPlanarProcess
On this page

MultiPlanarProcess

Create a multi player game in a Planar

Constructor

new MultiPlanarProcess(socketIOWrapper, extent, assetManager, inputManager, options)

Parameters:
NameTypeDescription
socketIOWrapperSocketIOWrapper

socket to communicate with gamesocketservice

extentitowns.Extent

extent of the itowns view

assetManagerAssetManager

assetManager of the game AssetManager

inputManagerInputManager

input manager of the game InputManager

optionsobject

multi player game planar options

Properties
NameTypeAttributesDescription
frame3DPlanarOptionsPlanarOption

options frame3Dplanar PlanarOption

gameOriginObject<optional>

position of the external game context object3D

externalGameScriptClassObject.<string, ExternalScriptBase><optional>

custom external scripts class of your object3D

interpolatorDelaynumber<optional>

delay between state computed in game process and the ones in external context

Classes

MultiPlanarProcess

Members

extent :itowns.Extent

buffer to rebuild a frame3Dplanar on demand

Type:
  • itowns.Extent

externalGameContext :ExternalContext

render audio external script context

Type:
  • ExternalContext

frame3DPlanar :Planar

game view

Type:
  • Planar

frame3DPlanarOptions :PlanarOption

buffer to rebuild a frame3Dplanar on demand

Type:
  • PlanarOption

inputManager :InputManager

interpolator :StateInterpolator

interpolator to smooth comminucation between the two process

Type:
  • StateInterpolator

socketIOWrapper :SocketIOWrapper

websocket communication

Methods

start(options)

Start game communication with server

Parameters:
NameTypeDescription
optionsobject

options

Properties
NameTypeDescription
entryGameObject3DUUIDstring

uuid of the game object3D to connect with SocketService

@ud-viz/game_browser
\ No newline at end of file +
On this page

MultiPlanarProcess

Create a multi player game in a Planar

Constructor

new MultiPlanarProcess(socketIOWrapper, extent, assetManager, inputManager, options)

Parameters:
NameTypeDescription
socketIOWrapperSocketIOWrapper

socket to communicate with gamesocketservice

extentitowns.Extent

extent of the itowns view

assetManagerAssetManager

assetManager of the game AssetManager

inputManagerInputManager

input manager of the game InputManager

optionsobject

multi player game planar options

Properties
NameTypeAttributesDescription
frame3DPlanarOptionsPlanarOption

options frame3Dplanar PlanarOption

externalGameScriptClassObject.<string, ExternalScriptBase><optional>

custom external scripts class of your object3D

interpolatorDelaynumber<optional>

delay between state computed in game process and the ones in external context

computeBandWidthboolean

compute bandwidth of the interpolator or not

Classes

MultiPlanarProcess

Members

extent :itowns.Extent

buffer to rebuild a frame3Dplanar on demand

Type:
  • itowns.Extent

externalGameContext :ExternalContext

render audio external script context

Type:
  • ExternalContext

frame3DPlanar :Planar

game view

Type:
  • Planar

frame3DPlanarOptions :PlanarOption

buffer to rebuild a frame3Dplanar on demand

Type:
  • PlanarOption

inputManager :InputManager

interpolator :StateInterpolator

interpolator to smooth comminucation between the two process

Type:
  • StateInterpolator

socketIOWrapper :SocketIOWrapper

websocket communication

Methods

start(readyForGameSocketServiceParams)

Start game communication with server

Parameters:
NameTypeDescription
readyForGameSocketServiceParamsobject

object serialize then send when game is ready to SocketService

Properties
NameTypeDescription
entryGameObject3DUUIDstring

uuid of the game object3D to connect with SocketService

userDataobject

other information to emit to SocketService

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/MultiPlanarProcess.js.html b/docs/html/game_browser/MultiPlanarProcess.js.html index eee300114..692287e24 100644 --- a/docs/html/game_browser/MultiPlanarProcess.js.html +++ b/docs/html/game_browser/MultiPlanarProcess.js.html @@ -1,6 +1,6 @@ Source: MultiPlanarProcess.js
On this page

MultiPlanarProcess.js

import { SocketIOWrapper } from './SocketIOWrapper';
+    
On this page

MultiPlanarProcess.js

import { SocketIOWrapper } from './SocketIOWrapper';
 import { InputManager } from './InputManager';
 import { AssetManager } from './AssetManager';
 import {
@@ -8,7 +8,7 @@
   Context as ExternalContext,
 } from './Context';
 
-import { objectOverWrite } from '@ud-viz/utils_shared';
+import { objectOverWrite, objectParseNumeric } from '@ud-viz/utils_shared';
 import {
   StateInterpolator,
   constant,
@@ -32,9 +32,9 @@
    * @param {InputManager} inputManager - input manager of the game {@link InputManager}
    * @param {object} options - multi player game planar options
    * @param {PlanarOption} options.frame3DPlanarOptions - options frame3Dplanar {@link PlanarOption}
-   * @param {{x:number,y:number,z:number}=} options.gameOrigin - position of the external game context object3D
    * @param {Object<string,ExternalScriptBase>=} options.externalGameScriptClass - custom external scripts class of your object3D
    * @param {number=} options.interpolatorDelay - delay between state computed in game process and the ones in external context
+   * @param {boolean} options.computeBandWidth - compute bandwidth of the interpolator or not
    */
   constructor(
     socketIOWrapper,
@@ -74,7 +74,10 @@
      * interpolator to smooth comminucation between the two process
      *  
      @type {StateInterpolator} */
-    this.interpolator = new StateInterpolator(options.interpolatorDelay);
+    this.interpolator = new StateInterpolator(
+      options.interpolatorDelay,
+      options.computeBandWidth
+    );
 
     /** 
      * render audio external script context
@@ -90,26 +93,22 @@
         interpolator: this.interpolator,
       }
     );
-
-    if (options.gameOrigin) {
-      this.externalGameContext.object3D.position.set(
-        options.gameOrigin.x,
-        options.gameOrigin.y,
-        options.gameOrigin.z
-      );
-      this.externalGameContext.object3D.updateMatrixWorld();
-    }
   }
 
   /**
    * Start game communication with server
    *
-   * @param {object} options - options
-   * @param {string} options.entryGameObject3DUUID - uuid of the game object3D to connect with SocketService
+   * @param {object} readyForGameSocketServiceParams - object serialize then send when game is ready to SocketService
+   * @param {string} readyForGameSocketServiceParams.entryGameObject3DUUID - uuid of the game object3D to connect with SocketService
+   * @param {object} readyForGameSocketServiceParams.userData - other information to emit to SocketService
    */
-  start(options = {}) {
+  start(readyForGameSocketServiceParams = {}) {
     this.externalGameContext.sendCommandsToGameContext = (cmds) => {
-      this.socketIOWrapper.emit(constant.WEBSOCKET.MSG_TYPE.COMMANDS, cmds);
+      if (!cmds.length) return;
+      this.socketIOWrapper.emit(
+        constant.WEBSOCKET.MSG_TYPE.COMMANDS,
+        cmds.map((el) => el.toJSON())
+      );
     };
 
     // start listening on socket events
@@ -125,9 +124,12 @@
         );
 
         objectOverWrite(this.externalGameContext.userData, gameData.userData);
+        objectParseNumeric(this.externalGameContext.userData);
 
         // check if a game was already running
         if (this.interpolator._getLastStateReceived()) {
+          // TODO: reuse the same view
+
           // replace frame3D
           this.frame3DPlanar.dispose();
 
@@ -143,23 +145,22 @@
           const process = new RequestAnimationFrameProcess(30);
           process.start((dt) => {
             // send commands
-            const commands = this.inputManager.computeCommands();
-
-            this.socketIOWrapper.emit(
-              constant.WEBSOCKET.MSG_TYPE.COMMANDS,
-              commands
-            );
-
+            const commands = this.inputManager
+              .computeCommands()
+              .map((el) => el.toJSON());
+
+            if (commands.length) {
+              this.socketIOWrapper.emit(
+                constant.WEBSOCKET.MSG_TYPE.COMMANDS,
+                commands
+              );
+            }
             // simulation
             this.externalGameContext.step(
               dt,
               this.interpolator.computeCurrentStates()
             );
 
-            // render
-            this.frame3DPlanar.itownsView.notifyChange(
-              this.frame3DPlanar.camera
-            ); // => to load 3DTiles + trigger mainloop event
             this.frame3DPlanar.render();
           });
         }
@@ -167,7 +168,7 @@
         // init start
         this.frame3DPlanar.enableItownsViewRendering(false);
         this.interpolator.onFirstState(state);
-        this.inputManager.startListening(this.frame3DPlanar.domElementWebGL);
+        this.inputManager.startListening(this.frame3DPlanar.domElement);
       }
     );
 
@@ -177,8 +178,8 @@
 
     this.socketIOWrapper.emit(
       constant.WEBSOCKET.MSG_TYPE.READY_FOR_GAME,
-      options.entryGameObject3DUUID
+      readyForGameSocketServiceParams
     );
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/RenderController.js.html b/docs/html/game_browser/RenderController.js.html index 702731701..d5bbcb6f5 100644 --- a/docs/html/game_browser/RenderController.js.html +++ b/docs/html/game_browser/RenderController.js.html @@ -1,6 +1,6 @@ Source: RenderController.js
On this page

RenderController.js

import { AssetManager, RenderData } from './AssetManager';
+    
On this page

RenderController.js

import { AssetManager, RenderData } from './AssetManager';
 
 import { Model, Controller, Object3D } from '@ud-viz/game_shared';
 import * as THREE from 'three';
@@ -31,9 +31,25 @@
     /** @type {THREE.AnimationMixer} */
     this.animationMixer = null;
 
-    if (this.model.getIdRenderData()) {
+    this.setIdRenderData(this.model.idRenderData);
+
+    // update color
+    this.setColor(this.model.getColor());
+  }
+
+  setIdRenderData(idRenderData) {
+    // change model
+    this.model.idRenderData = idRenderData;
+
+    // remove old one
+    if (this.renderData) {
+      this.renderData.dispose();
+      this.renderData = null;
+    }
+
+    if (this.model.idRenderData) {
       this.renderData = this.assetManager.createRenderData(
-        this.model.getIdRenderData()
+        this.model.idRenderData
       );
       const animations = this.renderData.animations;
       if (animations && animations.length) {
@@ -51,9 +67,9 @@
 
     // register in parent
     this.object3D.add(this.renderData.object3D);
+    this.renderData.object3D.name = 'RENDER_OBJECT_3D_' + this.object3D.name;
 
-    // update color
-    this.setColor(this.model.getColor());
+    this.setColor(this.model.color); // update color
   }
 
   /**
@@ -83,7 +99,7 @@
 
     const alpha = color[3];
 
-    // only change color TODO handle alpha
+    // only change color
     this.model.setColor(color);
     // update color in the controller attributes
     const threeColor = new THREE.Color().fromArray(color);
@@ -94,10 +110,11 @@
           // handle opacity
           child.material.opacity = alpha;
           child.material.transparent = true;
-          child.renderOrder = 1; // patch for futurologue not working
+          child.renderOrder = 1;
         } else {
           child.material.transparent = false;
         }
+        child.material.needsUpdate = true;
       }
     });
   }
@@ -112,4 +129,4 @@
     }
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/RenderController_RenderController.html b/docs/html/game_browser/RenderController_RenderController.html index b177601a3..c493be410 100644 --- a/docs/html/game_browser/RenderController_RenderController.html +++ b/docs/html/game_browser/RenderController_RenderController.html @@ -1,3 +1,3 @@ Class: RenderController
On this page

RenderController

new RenderController(model, object3D, assetManager)

Render component controller

Parameters:
NameTypeDescription
modelModel

model of component controller

object3DObject3D

object3D of controller

assetManagerAssetManager

asset manager

@ud-viz/game_browser
\ No newline at end of file +
On this page

RenderController

new RenderController(model, object3D, assetManager)

Render component controller

Parameters:
NameTypeDescription
modelModel

model of component controller

object3DObject3D

object3D of controller

assetManagerAssetManager

asset manager

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/RenderData.html b/docs/html/game_browser/RenderData.html index 260550511..9d5e18239 100644 --- a/docs/html/game_browser/RenderData.html +++ b/docs/html/game_browser/RenderData.html @@ -1,3 +1,3 @@ Class: RenderData
On this page

RenderData

Contains a THREE.Object3D and an array of animations

Constructor

new RenderData(childObject3D, renderDataConfigopt, animationsopt)

It takes an object3D and an optional animations object, and sets the object3D and animations properties of the object

Parameters:
NameTypeAttributesDefaultDescription
childObject3DTHREE.Object3D

The object to add.

renderDataConfigRenderDataConfig<optional>
{}

Contains path, anchor, scale and rotation.

animationsArray.<THREE.AnimationClip><optional>
null

An array of animations.

Classes

RenderData

Members

object3D :THREE.Object3D

Parent object of the object3D to set up

Type:
  • THREE.Object3D

Methods

clone() → {RenderData}

It clones the object3D and then clones all of the materials in the object3D

Returns:

A new RenderData object with a cloned object3D and the same animations.

Type: 
RenderData
@ud-viz/game_browser
\ No newline at end of file +
On this page

RenderData

Contains a THREE.Object3D and an array of animations

Constructor

new RenderData(childObject3D, renderDataConfigopt, animationsopt)

It takes an object3D and an optional animations object, and sets the object3D and animations properties of the object

Parameters:
NameTypeAttributesDefaultDescription
childObject3DTHREE.Object3D

The object to add.

renderDataConfigRenderDataConfig<optional>
{}

Contains path, anchor, scale and rotation.

animationsArray.<THREE.AnimationClip><optional>
null

An array of animations.

Classes

RenderData

Members

object3D :THREE.Object3D

Parent object of the object3D to set up

Type:
  • THREE.Object3D

Methods

clone() → {RenderData}

It clones the object3D and then clones all of the materials in the object3D

Returns:

A new RenderData object with a cloned object3D and the same animations.

Type: 
RenderData
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/ScriptBase_ScriptBase.html b/docs/html/game_browser/ScriptBase_ScriptBase.html index 61da3248f..2a45af30b 100644 --- a/docs/html/game_browser/ScriptBase_ScriptBase.html +++ b/docs/html/game_browser/ScriptBase_ScriptBase.html @@ -1,3 +1,3 @@ Class: ScriptBase
On this page

ScriptBase

new ScriptBase(context, object3D, variables)

Skeleton of a game context script, different Context.EVENT are trigger by Context

Parameters:
NameTypeDescription
contextContext

context of this script

object3DObject3D

object3D bind (attach) to this script

variablesobject

custom variables bind (attach) to this script

@ud-viz/game_browser
\ No newline at end of file +
On this page

ScriptBase

new ScriptBase(context, object3D, variables)

Skeleton of a game context script, different Context.EVENT are trigger by Context

Parameters:
NameTypeDescription
contextContext

context of this script

object3DObject3D

object3D bind (attach) to this script

variablesobject

custom variables bind (attach) to this script

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/SingleBaseProcess.html b/docs/html/game_browser/SingleBaseProcess.html new file mode 100644 index 000000000..485838a1c --- /dev/null +++ b/docs/html/game_browser/SingleBaseProcess.html @@ -0,0 +1,3 @@ +Class: SingleBaseProcess
On this page

SingleBaseProcess

Create a single player game in a Base

Constructor

new SingleBaseProcess(gameObject3D, frame3DBase, assetManager, inputManager, options)

Parameters:
NameTypeDescription
gameObject3DObject3D

root game object3D of your game

frame3DBaseBase

frame3DBase where the game is taking place

assetManagerAssetManager

assetManager of the game AssetManager

inputManagerInputManager

input manager of the game InputManager

optionsobject

single player game planar options

Properties
NameTypeAttributesDescription
gameScriptClassObject.<string, ScriptBase><optional>

custom game scripts class of your object3D

externalGameScriptClassObject.<string, ExternalScriptBase><optional>

custom external scripts class of your object3D

interpolatorDelaynumber<optional>

delay between state computed in game process and the ones in external context

Classes

SingleBaseProcess

Members

assetManager :AssetManager

asset manager

externalGameContext :ExternalContext

render audio external script context

Type:
  • ExternalContext

frame3DBase :Base

game view

Type:
  • Base

gameContext :Context

game script + collision context

Type:

inputManager :InputManager

input manager

interpolator :StateInterpolator

interpolator to smooth comminucation between the two process

Type:
  • StateInterpolator

process :RequestAnimationFrameProcess

Type:
  • RequestAnimationFrameProcess

Methods

start() → {Promise}

Returns:
  • promise resolving when game has started
Type: 
Promise
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/SingleBaseProcess.js.html b/docs/html/game_browser/SingleBaseProcess.js.html new file mode 100644 index 000000000..8c4772897 --- /dev/null +++ b/docs/html/game_browser/SingleBaseProcess.js.html @@ -0,0 +1,152 @@ +Source: SingleBaseProcess.js
On this page

SingleBaseProcess.js

import { AssetManager } from './AssetManager';
+import { InputManager } from './InputManager';
+import {
+  ScriptBase as ExternalScriptBase,
+  Context as ExternalContext,
+} from './Context';
+
+import { Base } from '@ud-viz/frame3d';
+import {
+  Object3D,
+  ScriptBase,
+  StateInterpolator,
+  Context,
+} from '@ud-viz/game_shared';
+import { RequestAnimationFrameProcess } from '@ud-viz/utils_browser';
+
+/**
+ * @classdesc Create a single player game in a {@link Base}
+ */
+export class SingleBaseProcess {
+  /**
+   *
+   * @param {Object3D} gameObject3D - root game object3D of your game
+   * @param {Base} frame3DBase - frame3DBase where the game is taking place
+   * @param {AssetManager} assetManager - assetManager of the game {@link AssetManager}
+   * @param {InputManager} inputManager - input manager of the game {@link InputManager}
+   * @param {object} options - single player game planar options
+   * @param {Object<string,ScriptBase>=} options.gameScriptClass - custom game scripts class of your object3D
+   * @param {Object<string,ExternalScriptBase>=} options.externalGameScriptClass - custom external scripts class of your object3D
+   * @param {number=} options.interpolatorDelay - delay between state computed in game process and the ones in external context
+   */
+  constructor(
+    gameObject3D,
+    frame3DBase,
+    assetManager,
+    inputManager,
+    options = {}
+  ) {
+    /**
+     * game script + collision context
+     *
+      @type {Context} */
+    this.gameContext = new Context(options.gameScriptClass || {}, gameObject3D);
+
+    /**
+     * game view
+     *
+      @type {Base}  */
+    this.frame3DBase = frame3DBase;
+
+    /**
+     * asset manager
+     *
+      @type {AssetManager} */
+    this.assetManager = assetManager;
+
+    /**
+     * input manager
+     *
+      @type {InputManager} */
+    this.inputManager = inputManager;
+
+    /**
+     * interpolator to smooth comminucation between the two process
+     *
+      @type {StateInterpolator} */
+    this.interpolator = new StateInterpolator(options.interpolatorDelay);
+
+    /**
+     * render audio external script context
+     *
+      @type {ExternalContext} */
+    this.externalGameContext = new ExternalContext(
+      this.frame3DBase,
+      assetManager,
+      inputManager,
+      options.externalGameScriptClass || {},
+      { interpolator: this.interpolator }
+    );
+
+    /** @type {RequestAnimationFrameProcess} */
+    this.process = new RequestAnimationFrameProcess(30);
+  }
+
+  dispose() {
+    this.frame3DBase.dispose();
+    this.inputManager.dispose();
+    this.process.stop();
+  }
+
+  /**
+   *
+   * @returns {Promise} - promise resolving when game has started
+   */
+  start() {
+    return new Promise((resolve) => {
+      this.gameContext.load().then(() => {
+        // initialize interpolator
+        let previousGameState = this.gameContext.toState(false); // false because no need to send game component
+        this.interpolator.onFirstState(previousGameState);
+
+        // start process gameContext
+        const gameProcess = new RequestAnimationFrameProcess(60);
+
+        // plug inputmanager directly in game process
+        this.inputManager.startListening(this.frame3DBase.domElement);
+
+        gameProcess.start((dt) => {
+          // game loop
+          this.gameContext.onCommands(this.inputManager.computeCommands()); // pull commands
+          this.gameContext.step(dt); // simulate
+
+          // here we compute a diff with the last game state (we could just send a newState to the interpolator)
+          // but this is to test multiplayer (isOutdated is used in diff forcing the update transform in external context)
+          // isOutdated is also used to notify external script maybe we should use two boolean ? but no since isOutdated means object3D model has changed anyway
+          const newState = this.gameContext.toState(false);
+          const stateDiff = newState.sub(previousGameState);
+          previousGameState = newState;
+
+          // console.log(stateDiff);
+
+          this.interpolator.onNewDiff(stateDiff); // send new diff of the game to interpolator
+        });
+
+        // indicate to the external context how to send command to gamecontext (could be with websocket)
+        this.externalGameContext.sendCommandsToGameContext = (cmds) => {
+          this.gameContext.onCommands(cmds);
+        };
+
+        // step external game context
+        this.process.start((dt) => {
+          // external game loop
+          this.externalGameContext.step(
+            dt,
+            this.interpolator.computeCurrentStates()
+          ); // simulate
+          this.frame3DBase.render();
+        });
+
+        // DEBUG PRINT
+        this.inputManager.addKeyInput('p', 'keyup', () => {
+          console.log('game ', this);
+        });
+
+        resolve();
+      });
+    });
+  }
+}
+
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/SinglePlanarProcess.html b/docs/html/game_browser/SinglePlanarProcess.html index 8859ab499..c6f3df286 100644 --- a/docs/html/game_browser/SinglePlanarProcess.html +++ b/docs/html/game_browser/SinglePlanarProcess.html @@ -1,3 +1,3 @@ Class: SinglePlanarProcess
On this page

SinglePlanarProcess

Create a single player game in a Planar

Constructor

new SinglePlanarProcess(gameObject3D, frame3DPlanar, assetManager, inputManager, options)

Parameters:
NameTypeDescription
gameObject3DObject3D

root game object3D of your game

frame3DPlanarPlanar

frame3DPlanar where the game is taking place

assetManagerAssetManager

assetManager of the game AssetManager

inputManagerInputManager

input manager of the game InputManager

optionsobject

single player game planar options

Properties
NameTypeAttributesDescription
gameScriptClassObject.<string, ScriptBase><optional>

custom game scripts class of your object3D

gameOriginObject<optional>

position of the external game context object3D

externalGameScriptClassObject.<string, ExternalScriptBase><optional>

custom external scripts class of your object3D

interpolatorDelaynumber<optional>

delay between state computed in game process and the ones in external context

Classes

SinglePlanarProcess

Members

assetManager :AssetManager

asset manager

externalGameContext :ExternalContext

render audio external script context

Type:
  • ExternalContext

frame3DPlanar :Planar

game view

Type:
  • Planar

gameContext :Context

game script + collision context

Type:

inputManager :InputManager

input manager

interpolator :StateInterpolator

interpolator to smooth comminucation between the two process

Type:
  • StateInterpolator

Methods

start() → {Promise}

Returns:
  • promise resolving when game has started
Type: 
Promise
@ud-viz/game_browser
\ No newline at end of file +
On this page

SinglePlanarProcess

Create a single player game in a Planar

Constructor

new SinglePlanarProcess(gameObject3D, frame3DPlanar, assetManager, inputManager, options)

Parameters:
NameTypeDescription
gameObject3DObject3D

root game object3D of your game

frame3DPlanarPlanar

frame3DPlanar where the game is taking place

assetManagerAssetManager

assetManager of the game AssetManager

inputManagerInputManager

input manager of the game InputManager

optionsobject

single player game planar options

Properties
NameTypeAttributesDescription
gameScriptClassObject.<string, ScriptBase><optional>

custom game scripts class of your object3D

externalGameScriptClassObject.<string, ExternalScriptBase><optional>

custom external scripts class of your object3D

interpolatorDelaynumber<optional>

delay between state computed in game process and the ones in external context

Classes

SinglePlanarProcess

Members

assetManager :AssetManager

asset manager

externalGameContext :ExternalContext

render audio external script context

Type:
  • ExternalContext

frame3DPlanar :Planar

game view

Type:
  • Planar

gameContext :Context

game script + collision context

Type:

inputManager :InputManager

input manager

interpolator :StateInterpolator

interpolator to smooth comminucation between the two process

Type:
  • StateInterpolator

Methods

start() → {Promise}

Returns:
  • promise resolving when game has started
Type: 
Promise
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/SinglePlanarProcess.js.html b/docs/html/game_browser/SinglePlanarProcess.js.html index 06d7c58f9..86ddde0f3 100644 --- a/docs/html/game_browser/SinglePlanarProcess.js.html +++ b/docs/html/game_browser/SinglePlanarProcess.js.html @@ -1,6 +1,6 @@ Source: SinglePlanarProcess.js
On this page

SinglePlanarProcess.js

import { AssetManager } from './AssetManager';
+    
On this page

SinglePlanarProcess.js

import { AssetManager } from './AssetManager';
 import { InputManager } from './InputManager';
 import {
   ScriptBase as ExternalScriptBase,
@@ -28,7 +28,6 @@
    * @param {InputManager} inputManager - input manager of the game {@link InputManager}
    * @param {object} options - single player game planar options
    * @param {Object<string,ScriptBase>=} options.gameScriptClass - custom game scripts class of your object3D
-   * @param {{x:number,y:number,z:number}=} options.gameOrigin - position of the external game context object3D
    * @param {Object<string,ExternalScriptBase>=} options.externalGameScriptClass - custom external scripts class of your object3D
    * @param {number=} options.interpolatorDelay - delay between state computed in game process and the ones in external context
    */
@@ -80,15 +79,6 @@
       options.externalGameScriptClass || {},
       { interpolator: this.interpolator }
     );
-
-    if (options.gameOrigin) {
-      this.externalGameContext.object3D.position.set(
-        options.gameOrigin.x,
-        options.gameOrigin.y,
-        options.gameOrigin.z
-      );
-      this.externalGameContext.object3D.updateMatrixWorld();
-    }
   }
 
   /**
@@ -150,7 +140,6 @@
             dt,
             this.interpolator.computeCurrentStates()
           ); // simulate
-          this.frame3DPlanar.itownsView.notifyChange(this.frame3DPlanar.camera); // => to load 3DTiles
           this.frame3DPlanar.render();
         });
 
@@ -164,4 +153,4 @@
     });
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/SocketIOWrapper.html b/docs/html/game_browser/SocketIOWrapper.html index dbe0f3d82..78f075991 100644 --- a/docs/html/game_browser/SocketIOWrapper.html +++ b/docs/html/game_browser/SocketIOWrapper.html @@ -1,3 +1,3 @@ Class: SocketIOWrapper
On this page

SocketIOWrapper

new SocketIOWrapper()

Socket io client wrapper https://www.npmjs.com/package/socket.io-client, used to manage a websocket communication

Classes

SocketIOWrapper

Methods

connectToServer()

Start websocket communication with window.location.host

emit(event, data)

Emit an event to the server with attach data

Parameters:
NameTypeDescription
eventstring

event id

dataobject

attached data (all serializable data are supported)

on(event, callback)

Register a callback on an event

Parameters:
NameTypeDescription
eventstring

event id

callbackcbSocketIO

callback called when the event is received

reset(events)

Reset all events listened or only the ones listed in events parameter

Parameters:
NameTypeDescription
eventsArray.<string>

list of events to reset

@ud-viz/game_browser
\ No newline at end of file +
On this page

SocketIOWrapper

new SocketIOWrapper()

Socket io client wrapper https://www.npmjs.com/package/socket.io-client, used to manage a websocket communication

Classes

SocketIOWrapper

Methods

connectToServer()

Start websocket communication with window.location.host

emit(event, data)

Emit an event to the server with attach data

Parameters:
NameTypeDescription
eventstring

event id

dataobject

attached data (all serializable data are supported)

on(event, callback)

Register a callback on an event

Parameters:
NameTypeDescription
eventstring

event id

callbackcbSocketIO

callback called when the event is received

reset(events)

Reset all events listened or only the ones listed in events parameter

Parameters:
NameTypeDescription
eventsArray.<string>

list of events to reset

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/SocketIOWrapper.js.html b/docs/html/game_browser/SocketIOWrapper.js.html index b2bf1b092..2ce2eb216 100644 --- a/docs/html/game_browser/SocketIOWrapper.js.html +++ b/docs/html/game_browser/SocketIOWrapper.js.html @@ -1,6 +1,6 @@ Source: SocketIOWrapper.js
On this page

SocketIOWrapper.js

import io from 'socket.io-client';
+    
On this page

SocketIOWrapper.js

import io from 'socket.io-client';
 
 /** @class */
 export class SocketIOWrapper {
@@ -93,4 +93,4 @@
     this.socket.emit(event, data);
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/data/search.json b/docs/html/game_browser/data/search.json index 3121cd878..88cb37b3a 100644 --- a/docs/html/game_browser/data/search.json +++ b/docs/html/game_browser/data/search.json @@ -1 +1 @@ -{"list":[{"title":"AssetManager","link":"AssetManager","description":"

Initialize the native render data.

"},{"title":"AudioController","link":"AudioController","description":"

Audio component controller

"},{"title":"Context","link":"Context"},{"title":"InputManager","link":"InputManager","description":"

Manage user inputs with a poll system (https://en.wikipedia.org/wiki/Polling_(computer_science))

"},{"title":"LoadingView","link":"LoadingView","description":"

It creates a root HTML, then adds HTML elements for the loading bar.

"},{"title":"MouseState","link":"MouseState","description":"

Poll system (https://en.wikipedia.org/wiki/Polling_(computer_science))\nListen to the MOUSE_STATE_EVENTS and store the mouse state to then be access synchronously

"},{"title":"MultiPlanarProcess","link":"MultiPlanarProcess"},{"title":"RenderController","link":"RenderController","description":"

Render component controller

"},{"title":"RenderData","link":"RenderData","description":"

It takes an object3D and an optional animations object, and sets the object3D and animations\nproperties of the object

"},{"title":"ScriptBase","link":"ScriptBase","description":"

Skeleton of a game context script, different {@link Context.EVENT} are trigger by {@link Context}

"},{"title":"SinglePlanarProcess","link":"SinglePlanarProcess"},{"title":"SocketIOWrapper","link":"SocketIOWrapper","description":"

Socket io client wrapper {@link https://www.npmjs.com/package/socket.io-client}, used to manage a websocket communication

"},{"title":"Changelog","link":"Changelog"},{"title":"Contributing","link":"Contributing"},{"title":"Contributors","link":"Contributors"},{"title":"Definitions","link":"Definitions"},{"title":"Developers","link":"Developers"},{"title":"ReleasePublish","link":"ReleasePublish"},{"title":"Reproducibility","link":"Reproducibility"},{"title":"architecture","link":"architecture"},{"title":"game","link":"game"},{"title":"how_to_import","link":"how_to_import"},{"title":"initial_camera_transform","link":"initial_camera_transform"},{"title":"AssetManagerConfig","link":"AssetManagerConfig","description":"

Contains configs of assets.

"},{"title":"CommandCallback","link":"CommandCallback"},{"title":"DEFAULT_MATERIAL","link":"DEFAULT_MATERIAL","description":"

Default material used by native objects

"},{"title":"EventCallback","link":"EventCallback"},{"title":"MOUSE_STATE_EVENTS","link":"MOUSE_STATE_EVENTS","description":"

List of mouse event handle by MouseState

"},{"title":"RenderDataConfig","link":"RenderDataConfig","description":"

Contains path, anchor, scale and rotation.

"},{"title":"SoundsConfig","link":"SoundsConfig","description":"

Contains path

"},{"title":"cbSocketIO","link":"cbSocketIO"}]} \ No newline at end of file +{"list":[{"title":"AssetManager","link":"AssetManager","description":"

Initialize the native render data.

"},{"title":"AudioController","link":"AudioController","description":"

Audio component controller

"},{"title":"Context","link":"Context"},{"title":"InputManager","link":"InputManager","description":"

Manage user inputs with a poll system (https://en.wikipedia.org/wiki/Polling_(computer_science))

"},{"title":"LoadingView","link":"LoadingView","description":"

It creates a root HTML, then adds HTML elements for the loading bar.

"},{"title":"MouseState","link":"MouseState","description":"

Poll system (https://en.wikipedia.org/wiki/Polling_(computer_science))\nListen to the MOUSE_STATE_EVENTS and store the mouse state to then be access synchronously

"},{"title":"MultiPlanarProcess","link":"MultiPlanarProcess"},{"title":"RenderController","link":"RenderController","description":"

Render component controller

"},{"title":"RenderData","link":"RenderData","description":"

It takes an object3D and an optional animations object, and sets the object3D and animations\nproperties of the object

"},{"title":"ScriptBase","link":"ScriptBase","description":"

Skeleton of a game context script, different {@link Context.EVENT} are trigger by {@link Context}

"},{"title":"SingleBaseProcess","link":"SingleBaseProcess"},{"title":"SinglePlanarProcess","link":"SinglePlanarProcess"},{"title":"SocketIOWrapper","link":"SocketIOWrapper","description":"

Socket io client wrapper {@link https://www.npmjs.com/package/socket.io-client}, used to manage a websocket communication

"},{"title":"Changelog","link":"Changelog"},{"title":"Contributing","link":"Contributing"},{"title":"Contributors","link":"Contributors"},{"title":"Definitions","link":"Definitions"},{"title":"Developers","link":"Developers"},{"title":"ReleasePublish","link":"ReleasePublish"},{"title":"Reproducibility","link":"Reproducibility"},{"title":"architecture","link":"architecture"},{"title":"game","link":"game"},{"title":"how_to_import","link":"how_to_import"},{"title":"initial_camera_transform","link":"initial_camera_transform"},{"title":"AssetManagerConfig","link":"AssetManagerConfig","description":"

Contains configs of assets.

"},{"title":"CommandCallback","link":"CommandCallback"},{"title":"DEFAULT_MATERIAL","link":"DEFAULT_MATERIAL","description":"

Default material used by native objects

"},{"title":"EventCallback","link":"EventCallback"},{"title":"MOUSE_STATE_EVENTS","link":"MOUSE_STATE_EVENTS","description":"

List of mouse event handle by MouseState

"},{"title":"RenderDataConfig","link":"RenderDataConfig","description":"

Contains path, anchor, scale and rotation.

"},{"title":"SoundsConfig","link":"SoundsConfig","description":"

Contains path

"},{"title":"cbSocketIO","link":"cbSocketIO"}]} \ No newline at end of file diff --git a/docs/html/game_browser/global.html b/docs/html/game_browser/global.html index da614eb2d..a245f253a 100644 --- a/docs/html/game_browser/global.html +++ b/docs/html/game_browser/global.html @@ -1,3 +1,3 @@ Global
On this page

Members

(constant) DEFAULT_MATERIAL

Default material used by native objects

(constant) MOUSE_STATE_EVENTS

List of mouse event handle by MouseState

Type Definitions

AssetManagerConfig

Contains configs of assets.

Type:
  • object
Properties
NameTypeDescription
soundsObject.<string, SoundsConfig>

SoundsConfig

renderDataObject.<string, RenderDataConfig>

RenderDataConfig

CommandCallback() → {Command}

Returns:
Type: 
Command

EventCallback(event)

Parameters:
NameTypeDescription
eventEvent

native event

RenderDataConfig

Contains path, anchor, scale and rotation.

Type:
  • object
Properties
NameTypeDescription
anchorstring

Values: center | max | min | center_min

scaleObject

Object's local scale

rotationObject

Object's local rotation

pathstring

Path to the 3D data file

SoundsConfig

Contains path

Type:
  • object
Properties
NameTypeDescription
pathstring

Path to the audio file

cbSocketIO(…args)

Parameters:
NameTypeAttributesDescription
args*<repeatable>

Args

@ud-viz/game_browser
\ No newline at end of file +
On this page

Members

(constant) DEFAULT_MATERIAL

Default material used by native objects

(constant) MOUSE_STATE_EVENTS

List of mouse event handle by MouseState

Type Definitions

AssetManagerConfig

Contains configs of assets.

Type:
  • object
Properties
NameTypeDescription
soundsObject.<string, SoundsConfig>

SoundsConfig

renderDataObject.<string, RenderDataConfig>

RenderDataConfig

CommandCallback() → {Command}

Returns:
Type: 
Command

EventCallback(event)

Parameters:
NameTypeDescription
eventEvent

native event

RenderDataConfig

Contains path, anchor, scale and rotation.

Type:
  • object
Properties
NameTypeDescription
anchorstring

Values: center | max | min | center_min

scaleObject

Object's local scale

rotationObject

Object's local rotation

pathstring

Path to the 3D data file

SoundsConfig

Contains path

Type:
  • object
Properties
NameTypeDescription
pathstring

Path to the audio file

cbSocketIO(…args)

Parameters:
NameTypeAttributesDescription
args*<repeatable>

Args

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/index.html b/docs/html/game_browser/index.html index 618998022..91d63e814 100644 --- a/docs/html/game_browser/index.html +++ b/docs/html/game_browser/index.html @@ -1,3 +1,3 @@ Home
On this page

@ud-viz/game_browser

NPM package version

@ud-viz/game_browser
\ No newline at end of file +
On this page

@ud-viz/game_browser

NPM package version

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-Changelog.html b/docs/html/game_browser/tutorial-Changelog.html index 5256bab4d..cc098def9 100644 --- a/docs/html/game_browser/tutorial-Changelog.html +++ b/docs/html/game_browser/tutorial-Changelog.html @@ -1,6 +1,17 @@ Tutorial: Changelog
On this page

Changelog

v4.0.1

@ud-viz

- Examples:
+    
On this page

Changelog

v4.0.2

@ud-viz

- npm cmd webpack-bundle-analyzer 
+- script setPackageVersion.js
+- update to itowns 2.41.1-next.9
+

@ud-viz/game_shared

- compute bandwidth
+- idScritp:string => scriptParam:{id:string, priority:number}
+- applyCommandCallback API
+- update(ColliderComp): add SHAPE_TYPE
+

@ud-viz/game_browser_template

- optimization of the camera manager obstacle computation
+

@ud-viz/utils_shared

- computeFilename
+- throttle
+

@ud-viz/game_editor

- creation of the package
+

v4.0.1

@ud-viz

- Examples:
   - point_cloud_visualizer.html
   - game_dom_element_3d_cube.html
   - widget_guided_tour.html
@@ -13,7 +24,7 @@
 

@ud-viz/widget_layer_choice

- Complete refacto
 - Can order ColorLayer
 - LocalStorage ui element
-

@ud-viz/utils_shared

- round
+

@ud-viz/utils_shared

- round
 - vector3ToLabel
 - insert
 

@ud-viz/utils_browser

- createLocalStorageNumberInput
@@ -22,16 +33,16 @@
 - createLocalStorageDetails
 

@ud-viz/utils_node

- fix(test):splice first '/' of req.url, statusCode
 

@ud-viz/game_node_template

- DomElement3DCubeManager script
-

@ud-viz/game_browser_template

- DomElement3DCube script
+

@ud-viz/game_browser_template

- DomElement3DCube script
 

@ud-viz/widget_guided_tour

- Creation of the package
-

v4.0.0

@ud-viz

- Refact(ud-viz)! split into multiples packages
+

v4.0.0

@ud-viz

- Refact(ud-viz)! split into multiples packages
 - Add loading screen in examples
 - Fix show room
 - Add avatar jitsi game example
 - Doc:
     - fix doc
     - update doc
-

v3.2.0

@ud-viz

- Fix socket.io dependencies
+

v3.2.0

@ud-viz

- Fix socket.io dependencies
 - Update with node18
 - Prettier harmonization
 - Improve `Readme.md`
@@ -53,7 +64,7 @@
 - Fix bug Frame3DPlanar render labels
 - THREEUtil encoding => colorSpace
 - Update itowns 2.40.0
-

v3.1.1

@ud-viz

- NPM :
+

v3.1.1

@ud-viz

- NPM :
     - Remove `^` in all package.json
 - Examples:
     - 2D Visualisation Mode
@@ -233,4 +244,4 @@
 * Delete old ifc feature, aimed to be in a demo
 * Don't override material if the material has a texture
 * Remove dependency towards remote data
-
\ No newline at end of file +
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-Contributing.html b/docs/html/game_browser/tutorial-Contributing.html index 54e77de43..d8d471678 100644 --- a/docs/html/game_browser/tutorial-Contributing.html +++ b/docs/html/game_browser/tutorial-Contributing.html @@ -1,3 +1,3 @@ Tutorial: Contributing
On this page

Contributing

Contributing

Coding style (Linter)

The JavaScript files coding style is defined with eslint through the .eslintrc.js configuration file. It can be checked (e.g. prior to a commit) with the npm run eslint command. Notice that UD-Viz coding style uses a unix linebreak-style (aka LF as newline character).

Submitting an issue

  • Create an issue with explicit name and a description
  • Add at least one label of task category, priority and related package
  • If it's a bug report add steps to reproduce.

Submiting Pull Request (PR)

Prior to PR-submission

  • Commit (git commit) with representative messages (commit messages end-up collected in the PR message and eventually release explanations).
  • Make sure your code is mature for a review.
  • git rebase origin/master, in order to resolve merge conflicts to master. Doc : git-rebase. If you are not able to resolve all of the conflicts of your rebase, please open a new discussion.

note that after a git rebase you have to git push --force

  • npm run eslint must finish with no errors.
  • npm run test-unit must finish with no errors.
  • npm run test-functional must finish with no errors.
  • npm audit must finish without vulnerabilities lower than high.
  • npm run test-links must finish with no errors.

⚠️ When your PR is open each push on your branch will trigger Travis CI jobs.

See here for more information.

Before submitting a pull request, and because UD-Viz still misses some tests, non-regression testing must be done manually. A developer must thus at least check that all the demo examples (they should function similar to their online deployment) are still effective.

Note that you should interact with ui (user interface) for complete tests.

PR Submission

Once your PR is ready to be submitted here are the steps to follow:

  • Create a PR via the Github interface
  • Describe synthetically the PR via the title and the description
  • Add labels if necessary
  • Link the related issues. (The linked issues are automatically closed when the PR is rebased and merged)
  • Add one or more reviewers.
  • Iterate over potential change requests until the PR is approved.
  • Click on Rebase and merge button
  • Delete the branch
@ud-viz/game_browser
\ No newline at end of file +
On this page

Contributing

Contributing

Coding style (Linter)

The JavaScript files coding style is defined with eslint through the .eslintrc.js configuration file. It can be checked (e.g. prior to a commit) with the npm run eslint command. Notice that UD-Viz coding style uses a unix linebreak-style (aka LF as newline character).

Submitting an issue

  • Create an issue with explicit name and a description
  • Add at least one label of task category, priority and related package
  • If it's a bug report add steps to reproduce.

Submiting Pull Request (PR)

Prior to PR-submission

  • Commit (git commit) with representative messages (commit messages end-up collected in the PR message and eventually release explanations).
  • Make sure your code is mature for a review.
  • git rebase origin/master, in order to resolve merge conflicts to master. Doc : git-rebase. If you are not able to resolve all of the conflicts of your rebase, please open a new discussion.

note that after a git rebase you have to git push --force

  • npm run eslint must finish with no errors.
  • npm run test-unit must finish with no errors.
  • npm run test-functional must finish with no errors.
  • npm audit must finish without vulnerabilities lower than high.
  • npm run test-links must finish with no errors.

⚠️ When your PR is open each push on your branch will trigger Travis CI jobs.

See here for more information.

Before submitting a pull request, and because UD-Viz still misses some tests, non-regression testing must be done manually. A developer must thus at least check that all the demo examples (they should function similar to their online deployment) are still effective.

Note that you should interact with ui (user interface) for complete tests.

PR Submission

Once your PR is ready to be submitted here are the steps to follow:

  • Create a PR via the Github interface
  • Describe synthetically the PR via the title and the description
  • Add labels if necessary
  • Link the related issues. (The linked issues are automatically closed when the PR is rebased and merged)
  • Add one or more reviewers.
  • Iterate over potential change requests until the PR is approved.
  • Click on Rebase and merge button
  • Delete the branch
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-Contributors.html b/docs/html/game_browser/tutorial-Contributors.html index 4623c933a..d5e3a0bbe 100644 --- a/docs/html/game_browser/tutorial-Contributors.html +++ b/docs/html/game_browser/tutorial-Contributors.html @@ -1,3 +1,3 @@ Tutorial: Contributors
On this page

Contributors

See Contributing.md for details on how to contribute to UD-Viz.

The following people have contributed to UD-Viz:

See the VCity Team Members page here.

Commiters:

@ud-viz/game_browser
\ No newline at end of file +
On this page

Contributors

See Contributing.md for details on how to contribute to UD-Viz.

The following people have contributed to UD-Viz:

See the VCity Team Members page here.

Commiters:

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-Definitions.html b/docs/html/game_browser/tutorial-Definitions.html index bf1df0610..c6d31c9dd 100644 --- a/docs/html/game_browser/tutorial-Definitions.html +++ b/docs/html/game_browser/tutorial-Definitions.html @@ -1,3 +1,3 @@ Tutorial: Definitions
On this page

Definitions

Definitions (within the UD-Viz context)

  • Component everything thats is necessary to execute only one aspect of a desired functionality (see also module).
  • Widget is used in a itowns context meaning it's a class extending itowns Widget which is a class containing a domElement and an itowns View.
@ud-viz/game_browser
\ No newline at end of file +
On this page

Definitions

Definitions (within the UD-Viz context)

  • Component everything thats is necessary to execute only one aspect of a desired functionality (see also module).
  • Widget is used in a itowns context meaning it's a class extending itowns Widget which is a class containing a domElement and an itowns View.
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-Developers.html b/docs/html/game_browser/tutorial-Developers.html index 396363783..804501887 100644 --- a/docs/html/game_browser/tutorial-Developers.html +++ b/docs/html/game_browser/tutorial-Developers.html @@ -1,4 +1,4 @@ Tutorial: Developers
On this page

Developers

Developers

Pre-requisites

Developing UD-Viz applications requires knowledge about :

NVM

Developpers are advised to use node version manager (nvm). nvm allows you to quickly install and use different versions of node via the command line.

To download and install follow this link: https://github.com/nvm-sh/nvm#installing-and-updating

Environment Tips

IDE

VSCode is recommended.

VisualStudio Code

When using Visual Studio Code, you can install the following extentions to make your life easier:

Tips for Windows developers

As configured, the coding style requires a Linux style newline characters which might be overwritten in Windows environments (both by git and/or your editor) to become CRLF. When such changes happen eslint will warn about "incorrect" newline characters (which can always be fixed with npm run eslint-fix but this process quickly gets painful). In order to avoid such difficulties, the recommended pratice consists in

  1. setting git's core.autocrlf to false (e.g. with git config --global core.autocrlf false)
  2. configure your editor/IDE to use Unix-style endings
  3. In order to use scripts that launch a shell script with Powershell: npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"

Npm Scripts

ScriptDescription
npm run cleanRemove files and folders generated by npm install and the npm run build-(dev-)examples script such as ./node_modules, package-lock.json, and ./dist
npm run clear-nodeLinux Command: Kill all node process (this is a sudo script)
npm run resetReinstalls npm dependencies. This script runs npm run clean and npm install command
npm run build-examplesBuild a production bundle base on this webpack config
npm run build-dev-examplesBuild a development bundle base on this webpack config
npm run dev-examplesLaunch a watcher running this script. See here for more information
npm run eslintRun the linter. See here for more information
npm run eslint-quietRun the linter without displaying warnings, only errors
npm run eslint-fixRun the linter and attempt to fix errors and warning automatically
npm run test-unitBuild examples bundle if needed and run npm run test in each packages . Also ran by CI. See here for more information
npm run test-functionalBuild examples bundle if needed and run each *.html in ./examples folder . Also ran by CI. See here for more information
npm run test-linksRun this script to detect dead links in markdown. Also ran by CI. See here for more information
npm run local-ciRun CI on your local computer. See here for more information
npm run pre-publishChange version in all package.json ( eg npm run pre-publish x.x.x ). See this script
npm run docsDelete old docs and run this script which generate docs
npm run dev-docsLaunch a watcher for generating and debugging the documentation. See here for more information
npm run back-endrun an http server (with some string-replace) + a game socket service.
http://locahost:8000/
npm run startRun npm run build-examples and npm run back-end

Debugging the examples

npm run dev-examples
-

Continuous Integration (Travis CI)

Each time origin/master branch is impacted by changes, Travis CI is triggered. It does a set of jobs describe in travis.yml.

Jobs list :

  • npm run eslint: Lint code sources
  • npm run test-unit: Unit test
  • npm run test-functional: Functional test
  • npm audit --audit-level=low: Npm native command (npm-audit) which check packages dependencies vulnerabilities.
  • npm run test-links: Run this script which check if links are not broken.
  • node ./test/docs.js: Run this script which check if documentation is generated without warning or error.

Contributing

For information on the accepted coding style, submitting Issues, and submitting Pull Requests see Contributing.md

Publishing

For creating a new release see ReleasePublish.md

Generating the documentation

The Online documentation can be re-generated by following these instructions.

@ud-viz/game_browser
\ No newline at end of file +
On this page

Developers

Developers

Pre-requisites

Developing UD-Viz applications requires knowledge about :

NVM

Developpers are advised to use node version manager (nvm). nvm allows you to quickly install and use different versions of node via the command line.

To download and install follow this link: https://github.com/nvm-sh/nvm#installing-and-updating

Environment Tips

IDE

VSCode is recommended.

VisualStudio Code

When using Visual Studio Code, you can install the following extentions to make your life easier:

Tips for Windows developers

As configured, the coding style requires a Linux style newline characters which might be overwritten in Windows environments (both by git and/or your editor) to become CRLF. When such changes happen eslint will warn about "incorrect" newline characters (which can always be fixed with npm run eslint-fix but this process quickly gets painful). In order to avoid such difficulties, the recommended pratice consists in

  1. setting git's core.autocrlf to false (e.g. with git config --global core.autocrlf false)
  2. configure your editor/IDE to use Unix-style endings
  3. In order to use scripts that launch a shell script with Powershell: npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"

Npm Scripts

ScriptDescription
npm run cleanRemove files and folders generated by npm install and the npm run build-(dev-)examples script such as ./node_modules, package-lock.json, and ./dist
npm run clear-nodeLinux Command: Kill all node process (this is a sudo script)
npm run resetReinstalls npm dependencies. This script runs npm run clean and npm install command
npm run build-examplesBuild a production bundle base on this webpack config
npm run build-dev-examplesBuild a development bundle base on this webpack config
npm run dev-examplesLaunch a watcher running this script. See here for more information
npm run eslintRun the linter. See here for more information
npm run eslint-quietRun the linter without displaying warnings, only errors
npm run eslint-fixRun the linter and attempt to fix errors and warning automatically
npm run test-unitBuild examples bundle if needed and run npm run test in each packages . Also ran by CI. See here for more information
npm run test-functionalBuild examples bundle if needed and run each *.html in ./examples folder . Also ran by CI. See here for more information
npm run test-linksRun this script to detect dead links in markdown. Also ran by CI. See here for more information
npm run local-ciRun CI on your local computer. See here for more information
npm run pre-publishChange version in all package.json ( eg npm run pre-publish x.x.x ). See this script
npm run docsDelete old docs and run this script which generate docs
npm run dev-docsLaunch a watcher for generating and debugging the documentation. See here for more information
npm run back-endrun an http server (with some string-replace) + a game socket service.
http://locahost:8000/
npm run analyze-bundleUse webpack-bundle-analyzer to see what's inside the examples bundle
npm run analyze-dev-bundleUse webpack-bundle-analyzer to see what's inside the examples dev bundle
npm run startRun npm run build-examples and npm run back-end

Debugging the examples

npm run dev-examples
+

Continuous Integration (Travis CI)

Each time origin/master branch is impacted by changes, Travis CI is triggered. It does a set of jobs describe in travis.yml.

Jobs list :

  • npm run eslint: Lint code sources
  • npm run test-unit: Unit test
  • npm run test-functional: Functional test
  • npm audit --audit-level=low: Npm native command (npm-audit) which check packages dependencies vulnerabilities.
  • npm run test-links: Run this script which check if links are not broken.
  • node ./test/docs.js: Run this script which check if documentation is generated without warning or error.

Contributing

For information on the accepted coding style, submitting Issues, and submitting Pull Requests see Contributing.md

Publishing

For creating a new release see ReleasePublish.md

Generating the documentation

The Online documentation can be re-generated by following these instructions.

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-ReleasePublish.html b/docs/html/game_browser/tutorial-ReleasePublish.html index e16f1fe35..83fbd1b53 100644 --- a/docs/html/game_browser/tutorial-ReleasePublish.html +++ b/docs/html/game_browser/tutorial-ReleasePublish.html @@ -1,6 +1,6 @@ Tutorial: ReleasePublish
On this page

ReleasePublish

Release creation process

  1. Create a new branch for the release, e.g. release-x.x.x from the master branch.

  2. Change the monorepo version npm run pre-publish x.x.x. It will change the version in all package.json and generate a Changelog file with all commits' names since the last release. (See the script here)

  3. Pick relevant commit message in ./docs/static/ChangelogDiff.txt to put them in ./docs/static/Changelog.md (Reformulate if necessary). Arrange commits in theses sections:

    # vx.x.x
    +    
    On this page

    ReleasePublish

    Release creation process

    1. Create a new branch for the release, e.g. release-x.x.x from the master branch.

    2. Change the monorepo version npm run pre-publish x.x.x. It will change the version in all package.json and generate a Changelog file with all commits' names since the last release. (See the script here)

    3. Pick relevant commit message in ./docs/static/ChangelogDiff.txt to put them in ./docs/static/Changelog.md (Reformulate if necessary). Arrange commits in theses sections:

      # vx.x.x
       
       ## @ud-viz
           - Examples:
      @@ -14,4 +14,4 @@
       
       ....
       
      -
    4. Run npm run docs. Generate the documentation with JSDoc.

    5. Follow the prior PR submission

    6. Push your branch to the main repository (This branch will be used to create the release tag)

    7. Create a PR

    8. Assign yourself to the PR

    9. Have a reviewer approve your PR

    10. Create a Github release (through the GUI):

      • Copy the new content given in Changelog.md: this duplication doesn't seem to be DRY but the Release comments are hosted by github (and will be lost if you migrate to e.g. gitlab) and are not browsable offline.
        • You might consider using the "Auto-generate release notes" feature
        • Note that creating a Github release creates an underlying git tag (which can previously be created with a git tag)

    Publish the package

    To publish the package to npmjs package repository

    1. Authenticate on npmjs with npm login and use the vcity account together with proper credentials. Note:
      • Ask username and password to login at an admin
      • Because the npmjs authentication mode of the vcityaccount is currently configured to One-Time-Password (OTP) over email you will need to be part of the vcity@liris.cnrs.fr email alias forwarder to receive the OTP and be patient about it (the reception delay can be up to a couple minutes).
    2. npm publish --workspaces --access public (workspaces option refer to the workspaces field in the package.json).
    \ No newline at end of file +
  4. Run npm run docs. Generate the documentation with JSDoc.

  5. Follow the prior PR submission

  6. Push your branch to the main repository (This branch will be used to create the release tag)

  7. Create a PR

  8. Assign yourself to the PR

  9. Have a reviewer approve your PR

  10. Create a Github release (through the GUI):

    • Copy the new content given in Changelog.md: this duplication doesn't seem to be DRY but the Release comments are hosted by github (and will be lost if you migrate to e.g. gitlab) and are not browsable offline.
      • You might consider using the "Auto-generate release notes" feature
      • Note that creating a Github release creates an underlying git tag (which can previously be created with a git tag)

Publish the package

To publish the package to npmjs package repository

  1. Authenticate on npmjs with npm login and use the vcity account together with proper credentials. Note:
    • Ask username and password to login at an admin
    • Because the npmjs authentication mode of the vcityaccount is currently configured to One-Time-Password (OTP) over email you will need to be part of the vcity@liris.cnrs.fr email alias forwarder to receive the OTP and be patient about it (the reception delay can be up to a couple minutes).
  2. npm publish --workspaces --access public (workspaces option refer to the workspaces field in the package.json).
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-Reproducibility.html b/docs/html/game_browser/tutorial-Reproducibility.html index 3099554c2..7ef882b53 100644 --- a/docs/html/game_browser/tutorial-Reproducibility.html +++ b/docs/html/game_browser/tutorial-Reproducibility.html @@ -1,3 +1,3 @@ Tutorial: Reproducibility
On this page

Reproducibility

The following stale branches

  • reproductibility/article-temporel-3DTiles
  • reproductibility/article-temporel-3DTilesTemporal

that have been deleted for sake of simplicity, can still be respectively retrieved through their associated tags with

  • git checkout -b reproductibility/article-temporel-3DTiles reproductibility/article-temporel-3DTiles-tag
  • git checkout -b reproductibility/article-temporel-3DTilesTemporal reproductibility/article-temporel-3DTilesTemporal-tag

Refer to this StackOverflow for a description of the branch "archiving"/retrieval process.

@ud-viz/game_browser
\ No newline at end of file +
On this page

Reproducibility

The following stale branches

  • reproductibility/article-temporel-3DTiles
  • reproductibility/article-temporel-3DTilesTemporal

that have been deleted for sake of simplicity, can still be respectively retrieved through their associated tags with

  • git checkout -b reproductibility/article-temporel-3DTiles reproductibility/article-temporel-3DTiles-tag
  • git checkout -b reproductibility/article-temporel-3DTilesTemporal reproductibility/article-temporel-3DTilesTemporal-tag

Refer to this StackOverflow for a description of the branch "archiving"/retrieval process.

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-architecture.html b/docs/html/game_browser/tutorial-architecture.html index cfc6dfe45..8ae0fe404 100644 --- a/docs/html/game_browser/tutorial-architecture.html +++ b/docs/html/game_browser/tutorial-architecture.html @@ -1,11 +1,12 @@ Tutorial: architecture
On this page

architecture

@ud-viz packages architecture

flowchart TB
+    
On this page

architecture

@ud-viz packages architecture

flowchart TB
 subgraph ud-viz/packages
 extensions_3d_tiles_temporal
 frame3d
 game_browser
 game_browser_template
+game_editor
 game_node
 game_node_template
 game_shared
@@ -40,6 +41,10 @@
 game_browser_template-->game_shared_template
 game_browser_template-->utils_browser
 game_browser_template-->utils_shared
+game_editor-->game_shared
+game_editor-->game_browser
+game_editor-->utils_browser
+game_editor-->utils_shared
 game_node-->game_shared
 game_node-->utils_shared
 game_node_template-->game_node
@@ -78,4 +83,4 @@
 widget_slide_show-->utils_browser
 widget_sparql-->utils_browser
 widget_workspace-->utils_browser
-widget_workspace-->widget_sparql
\ No newline at end of file +widget_workspace-->widget_sparql
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-game.html b/docs/html/game_browser/tutorial-game.html index e92d79f2e..95b246fe4 100644 --- a/docs/html/game_browser/tutorial-game.html +++ b/docs/html/game_browser/tutorial-game.html @@ -1,6 +1,6 @@ Tutorial: game
On this page

game

Game

This document gives an overview of how the game part works. Here are some different implementations of the game part:

Prerequisites

  • Run a back-end in node.js with express. See MDN DOC.
  • See here how to import @ud-viz/game_browser framework.

Creating a singleplayer simple game

The goal of this section is to learn how to set a singleplayer simple game structure:

Create a scene

Initialize a Planar part of @ud-viz/frame3d which creates a itowns PlanarView.

// Define geographic extent: CRS, min/max X, min/max Y
+    
On this page

game

Game

This document gives an overview of how the game part works. Here are some different implementations of the game part:

Prerequisites

  • Run a back-end in node.js with express. See MDN DOC.
  • See here how to import @ud-viz/game_browser framework.

Creating a singleplayer simple game

The goal of this section is to learn how to set a singleplayer simple game structure:

Create a scene

Initialize a Planar part of @ud-viz/frame3d which creates a itowns PlanarView.

// Define geographic extent: CRS, min/max X, min/max Y
 const extent = new udvizBrowser.itowns.Extent(
   'EPSG:4326',
   1837816.94334,
@@ -12,7 +12,6 @@
 const frame3DPlanar = new frame3d.Planar(extent, {
   hasItownsControls: true,
 });
-
 

Create a SinglePlanarProcess

Initialize a SinglePlanarProcess with a AssetManager and a InputManager part of @ud-viz/game_browser, an Object3D part of @ud-viz/game_shared defines your game model and SinglePlanarProcess is stepping your game over time.

const gameObject3D = new Object3D({
   static: true,
 });
@@ -47,10 +46,10 @@
   static: true,
   components: {
     GameScript: {
-      idScripts: [GameContextScript.ID_SCRIPT],
+      scriptParams: [{ id: GameContextScript.ID_SCRIPT }],
     },
     ExternalScript: {
-      idScripts: [GameExternalContextScript.ID_SCRIPT],
+      scriptParams: [{ id: GameExternalContextScript.ID_SCRIPT }],
     },
   },
 });
@@ -63,7 +62,6 @@
   {
     gameScriptClass: [GameContextScript],
     externalGameScriptClass: [GameExternalContextScript],
-    gameOrigin: { x: extent.center().x, y: extent.center().y, z: 100}
   }
 );
 

Spawn cubes

In the init of GameContextScript add these lines

this.goCubes = [];
@@ -87,7 +85,7 @@
   for (let index = this.goCubes.length - 1; index >= 0; index--) {
     const cube = this.goCubes[index];
     cube.position.z += 0.1 * this.context.dt;
-    cube.setOutdated(true);// notify game external context that this gameobject need update 
+    cube.setOutdated(true);// notify game external context that this gameobject need update
 
     // sky is the limit
     if (cube.position.z > 2000) {
@@ -96,23 +94,19 @@
     }
   }
 }
-

Send command

Add in the init of GameExternalContextScript the following code

this.context.inputManager.addMouseCommand(
-  'command_id',
-  'click',
-  () => {
-    return new Command({
-      type: 'toggle_pause',
-    });
-  }
-);
+

Send command

Add in the init of GameExternalContextScript the following code

this.context.inputManager.addMouseCommand('command_id', 'click', () => {
+  return new Command({
+    type: 'toggle_pause',
+  });
+});
 

Command is part of @ud-viz/game_shared

This sends a command on the mouse click to GameContextScript. Then in the init of GameContextScript add these lines:

this.pause = false;
 setInterval(() => {
   if (this.pause) return;
   ...
 

In the tick of GameContextScript

tick() {
+
+  this.applyCommandCallbackOf('toggle_pause', () => { this.pause = !this.pause })
+
   if (this.pause) return;
   ...
-

And finally add onCommand method:

onCommand(type) {
-  if (type === 'toggle_pause') this.pause = !this.pause;
-}
-

Now you have learned how to build a singleplayer simple game, let's see how to modify it to make a multiplayer one.

Create a multiplayer simple game (WIP)

1 Create a backend import udviz node with require as before run an express app running a http server Final result backend WIP

import game part of shared create a socket service by passsing the http server load gameobject3D where gameobject3D is the same one as the previous example the both script are unknown hard coded value of ids to keep it simple create another file gamethreadchild ok now you have to give the entry point to your thread the game script is the one running backend side copy paste it to your gamethread you have to give him the game script because he is going to run there replace udvizBrowser.Shared.Game by Game ouais backend okay

front end create a new html file base on previous one delete game script delete gameobject 3D Replace singleprocess by multiprocess create a socket io wrapper + connect it run enjoy

Examples

Singleplayer one:

This example requires knowledge about @ud-viz/game_shared and @ud-viz/game_browser

Multiplayer one:

This example requires the same knowledge as singleplayer plus @ud-viz/game_node

\ No newline at end of file +

Now you have learned how to build a singleplayer simple game, let's see how to modify it to make a multiplayer one.

Create a multiplayer simple game (WIP)

1 Create a backend import udviz node with require as before run an express app running a http server Final result backend WIP

import game part of shared create a socket service by passsing the http server load gameobject3D where gameobject3D is the same one as the previous example the both script are unknown hard coded value of ids to keep it simple create another file gamethreadchild ok now you have to give the entry point to your thread the game script is the one running backend side copy paste it to your gamethread you have to give him the game script because he is going to run there replace udvizBrowser.Shared.Game by Game ouais backend okay

front end create a new html file base on previous one delete game script delete gameobject 3D Replace singleprocess by multiprocess create a socket io wrapper + connect it run enjoy

Examples

Singleplayer one:

This example requires knowledge about @ud-viz/game_shared and @ud-viz/game_browser

Multiplayer one:

This example requires the same knowledge as singleplayer plus @ud-viz/game_node

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-how_to_import.html b/docs/html/game_browser/tutorial-how_to_import.html index ae525a7e4..ffadb792e 100644 --- a/docs/html/game_browser/tutorial-how_to_import.html +++ b/docs/html/game_browser/tutorial-how_to_import.html @@ -1,4 +1,4 @@ Tutorial: how_to_import
On this page

how_to_import

How to use it in your demo?

You can use it through npm

In your demo:

npm install --save @ud-viz/xxx
-

See https://github.com/VCityTeam/UD-Viz-template

@ud-viz/game_browser
\ No newline at end of file +
On this page

how_to_import

How to use it in your demo?

You can use it through npm

In your demo:

npm install --save @ud-viz/xxx
+

See https://github.com/VCityTeam/UD-Viz-template

@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser/tutorial-initial_camera_transform.html b/docs/html/game_browser/tutorial-initial_camera_transform.html index 51730e66c..14f873ed2 100644 --- a/docs/html/game_browser/tutorial-initial_camera_transform.html +++ b/docs/html/game_browser/tutorial-initial_camera_transform.html @@ -1,6 +1,6 @@ Tutorial: initial_camera_transform
On this page

initial_camera_transform

When creating a Planar part of @ud-viz/frame3d you may want your camera to be placed at a specific point of view. In this tutorial we are going to see differents options available to achieve that.

Configure Planar

Planar take an options object at the construction, where following fields will initialize camera point of view:

  • coordinates: camera look at geographic coordinate
  • heading: camera's heading, in degree
  • range: camera distance to target coordinate, in meter
  • tilt: camera's tilt, in degree

For more details check Planar documentation.

If you don't specify how the camera should be placed and you typed something like that:

const frame3DPlanar = new Planar(extent); // <-- no options passed
+    
On this page

initial_camera_transform

When creating a Planar part of @ud-viz/frame3d you may want your camera to be placed at a specific point of view. In this tutorial we are going to see differents options available to achieve that.

Configure Planar

Planar take an options object at the construction, where following fields will initialize camera point of view:

  • coordinates: camera look at geographic coordinate
  • heading: camera's heading, in degree
  • range: camera distance to target coordinate, in meter
  • tilt: camera's tilt, in degree

For more details check Planar documentation.

If you don't specify how the camera should be placed and you typed something like that:

const frame3DPlanar = new Planar(extent); // <-- no options passed
 

then the camera will look at the center of the extent with these default values:

  • heading: -50°
  • range: 3000m
  • tilt: 10°

if you want to configure that you should then write something like:

const frame3DPlanar = new Planar(extent, {
   coordinates: {
     x: some_x_coordinate
@@ -15,4 +15,4 @@
 

Note: the first time you will run your webpage the camera point of view will be the one pass to the Planar

Use URL

You can also use url util function to initialize default camera point of view. In that case you should first produce an url with camera matrix encoded in it.

const url = new URL(window.location.origin + window.location.pathname);
 appendCameraMatrixToURL(url, yourCamera);
 

then to initialize your camera with this url you would have to write something like that:

const isCameraMatrixInitializedWithURL = URLSetCameraMatrix(yourCamera);
-
\ No newline at end of file +
@ud-viz/game_browser
\ No newline at end of file diff --git a/docs/html/game_browser_template/CameraManager.js.html b/docs/html/game_browser_template/CameraManager.js.html index afe393766..d206fe80a 100644 --- a/docs/html/game_browser_template/CameraManager.js.html +++ b/docs/html/game_browser_template/CameraManager.js.html @@ -1,9 +1,10 @@ Source: CameraManager.js
On this page

CameraManager.js

import * as THREE from 'three';
+    
On this page

CameraManager.js

import * as THREE from 'three';
 import { ScriptBase, Context } from '@ud-viz/game_browser';
 import { Object3D } from '@ud-viz/game_shared';
 import { constant } from '@ud-viz/game_shared_template';
+import { throttle } from '@ud-viz/utils_shared';
 
 /**
  * @callback Movement
@@ -35,11 +36,27 @@
      @type {THREE.Object3D} */
     this.obstacle = null;
 
+    /** @type {number} */
+    this.cameraDistance = 0;
+
+    /** @type {Function} */
+    this.computeCameraDistance = throttle((position, dir, distance) => {
+      // compute intersection
+      this.raycaster.set(position, dir.clone().negate());
+      const intersects = this.raycaster.intersectObject(this.obstacle, true);
+      if (intersects.length) {
+        this.cameraDistance = Math.min(distance, intersects[0].distance);
+      } else {
+        this.cameraDistance = distance;
+      }
+    }, 1000);
+
     /** 
      * Raycaster to avoid obstacle
      *  
      @type {THREE.Raycaster} */
     this.raycaster = new THREE.Raycaster();
+    this.raycaster.camera = this.context.frame3D.camera; // patch to intersect sprites
   }
 
   /**
@@ -98,15 +115,12 @@
 
     // if there is an obstacle compute distance so camera postion is not inside obstacle
     if (this.obstacle) {
-      // compute intersection
-      this.raycaster.set(position, dir.clone().negate());
-      const intersects = this.raycaster.intersectObject(this.obstacle, true);
-      if (intersects.length) {
-        distance = Math.min(distance, intersects[0].distance);
-      }
+      this.computeCameraDistance(position, dir, distance);
+    } else {
+      this.cameraDistance = distance;
     }
 
-    position.sub(dir.setLength(distance));
+    position.sub(dir.setLength(this.cameraDistance));
     quaternion.multiply(
       new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI * 0.5, 0, 0))
     );
@@ -285,4 +299,4 @@
     this.angle = angle;
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/CameraManager_CameraManager.html b/docs/html/game_browser_template/CameraManager_CameraManager.html index 8bbced2ce..ed36d146e 100644 --- a/docs/html/game_browser_template/CameraManager_CameraManager.html +++ b/docs/html/game_browser_template/CameraManager_CameraManager.html @@ -1,3 +1,3 @@ Class: CameraManager
On this page

CameraManager

new CameraManager(context, object3D, variables)

Parameters:
NameTypeDescription
contextContext

game external context

object3DObject3D

object3D attach to this script

variablesobject

script variables

@ud-viz/game_browser_template
\ No newline at end of file +
On this page

CameraManager

new CameraManager(context, object3D, variables)

Parameters:
NameTypeDescription
contextContext

game external context

object3DObject3D

object3D attach to this script

variablesobject

script variables

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/DragAndDropAvatar.html b/docs/html/game_browser_template/DragAndDropAvatar.html index a6c57c01e..1cfb1d78c 100644 --- a/docs/html/game_browser_template/DragAndDropAvatar.html +++ b/docs/html/game_browser_template/DragAndDropAvatar.html @@ -1,3 +1,3 @@ Class: DragAndDropAvatar
On this page

DragAndDropAvatar

new DragAndDropAvatar(context, object3D, variables)

Drag and drop ui element in city to pass in avatar mode

Parameters:
NameTypeDescription
contextContext

external game context

object3DObject3D

object3D attach to this script

variablesDragAndDropAvatarVariables

variables of script

Classes

DragAndDropAvatar

Members

avatar :Object3D

Type:
  • Object3D

cameraManager :CameraManager

camera manager

domElement :HTMLElement

Type:
  • HTMLElement

dragAndDropElement :HTMLDivElement

Type:
  • HTMLDivElement

itownsCameraPosition :THREE.Vector3

Type:
  • THREE.Vector3

itownsCameraQuaternion :THREE.Quaternion

record where was camera quaternion

Type:
  • THREE.Quaternion

leaveAvatarModeButton :HTMLDivElement

Type:
  • HTMLDivElement

Methods

setAvatar(avatar)

Update state of this based on there is an avatar or not

Parameters:
NameTypeDescription
avatarObject3D

avatar game object3D

tick()

It computes the elevation of the avatar and sends it to the game context

@ud-viz/game_browser_template
\ No newline at end of file +
On this page

DragAndDropAvatar

new DragAndDropAvatar(context, object3D, variables)

Drag and drop ui element in city to pass in avatar mode

Parameters:
NameTypeDescription
contextContext

external game context

object3DObject3D

object3D attach to this script

variablesDragAndDropAvatarVariables

variables of script

Classes

DragAndDropAvatar

Members

avatar :Object3D

Type:
  • Object3D

cameraManager :CameraManager

camera manager

domElement :HTMLElement

Type:
  • HTMLElement

dragAndDropElement :HTMLDivElement

Type:
  • HTMLDivElement

itownsCameraPosition :THREE.Vector3

Type:
  • THREE.Vector3

itownsCameraQuaternion :THREE.Quaternion

record where was camera quaternion

Type:
  • THREE.Quaternion

leaveAvatarModeButton :HTMLDivElement

Type:
  • HTMLDivElement

Methods

setAvatar(avatar)

Update state of this based on there is an avatar or not

Parameters:
NameTypeDescription
avatarObject3D

avatar game object3D

tick()

It computes the elevation of the avatar and sends it to the game context

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/Element.html b/docs/html/game_browser_template/Element.html index c766aa954..f059634da 100644 --- a/docs/html/game_browser_template/Element.html +++ b/docs/html/game_browser_template/Element.html @@ -1,3 +1,3 @@ Class: Element
On this page

Element

  • note game object script handle ui and some camera movement
@ud-viz/game_browser_template
\ No newline at end of file +
On this page

Element

  • note game object script handle ui and some camera movement
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/MenuEditNote.html b/docs/html/game_browser_template/MenuEditNote.html index 0176b56c0..200916156 100644 --- a/docs/html/game_browser_template/MenuEditNote.html +++ b/docs/html/game_browser_template/MenuEditNote.html @@ -1,3 +1,3 @@ Class: MenuEditNote
On this page

MenuEditNote

Parameters:
NameTypeDescription
worldPositionTHREE.Vector3

where in scene this menu should be

Members

worldPosition :THREE.Vector3

Type:
  • THREE.Vector3

Methods

getWorldPosition() → {THREE.Vector3}

Returns:
  • menu position in world
Type: 
THREE.Vector3

setAddNoteButtonCallback(f)

Parameters:
NameTypeDescription
faddButtonNoteCallback

callback call when add note button is clicked (first param is the textarea value of menu)

setCloseButtonCallback(f)

Parameters:
NameTypeDescription
ffunction

callback call when close button is clicked

@ud-viz/game_browser_template
\ No newline at end of file +
On this page

MenuEditNote

Parameters:
NameTypeDescription
worldPositionTHREE.Vector3

where in scene this menu should be

Members

worldPosition :THREE.Vector3

Type:
  • THREE.Vector3

Methods

getWorldPosition() → {THREE.Vector3}

Returns:
  • menu position in world
Type: 
THREE.Vector3

setAddNoteButtonCallback(f)

Parameters:
NameTypeDescription
faddButtonNoteCallback

callback call when add note button is clicked (first param is the textarea value of menu)

setCloseButtonCallback(f)

Parameters:
NameTypeDescription
ffunction

callback call when close button is clicked

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/SocketService.html b/docs/html/game_browser_template/SocketService.html index 0e9acbf2b..782a5cde6 100644 --- a/docs/html/game_browser_template/SocketService.html +++ b/docs/html/game_browser_template/SocketService.html @@ -1,3 +1,3 @@ Class: SocketService
On this page

SocketService

  • Manage note for a specific socket
@ud-viz/game_browser_template
\ No newline at end of file +
On this page

SocketService

  • Manage note for a specific socket
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/Target.html b/docs/html/game_browser_template/Target.html index 5f70a526e..62ea7f072 100644 --- a/docs/html/game_browser_template/Target.html +++ b/docs/html/game_browser_template/Target.html @@ -1,3 +1,3 @@ Class: Target
On this page

Target

new Target(object3D, distance, offset, angle)

Buffer object which store all data to keep following an object3D target

Parameters:
NameTypeDescription
object3DTHREE.Object3D

object3D to focus

distancenumber

distance from object3D

offsetObject

offset camera position

anglenumber

angle on x

@ud-viz/game_browser_template
\ No newline at end of file +
On this page

Target

new Target(object3D, distance, offset, angle)

Buffer object which store all data to keep following an object3D target

Parameters:
NameTypeDescription
object3DTHREE.Object3D

object3D to focus

distancenumber

distance from object3D

offsetObject

offset camera position

anglenumber

angle on x

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/UI.html b/docs/html/game_browser_template/UI.html index 4a0db6bb9..d856a0462 100644 --- a/docs/html/game_browser_template/UI.html +++ b/docs/html/game_browser_template/UI.html @@ -1,3 +1,3 @@ Class: UI
On this page

UI

  • Manage global ui of the note feature
@ud-viz/game_browser_template
\ No newline at end of file +
On this page

UI

  • Manage global ui of the note feature
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/dragAndDropAvatar_DragAndDropAvatar.js.html b/docs/html/game_browser_template/dragAndDropAvatar_DragAndDropAvatar.js.html index 1bdb5bf2c..9d3c7e8ad 100644 --- a/docs/html/game_browser_template/dragAndDropAvatar_DragAndDropAvatar.js.html +++ b/docs/html/game_browser_template/dragAndDropAvatar_DragAndDropAvatar.js.html @@ -1,6 +1,6 @@ Source: dragAndDropAvatar/DragAndDropAvatar.js
On this page

dragAndDropAvatar_DragAndDropAvatar.js

import {
+    
On this page

dragAndDropAvatar_DragAndDropAvatar.js

import {
   computeRelativeElevationFromGround,
   addNativeCommands,
   removeNativeCommands,
@@ -115,7 +115,7 @@
       this.context.sendCommandsToGameContext([
         new Command({
           type: constant.COMMAND.ADD_AVATAR,
-          data: worldPosition.sub(this.context.object3D.position), // position in game context referential
+          data: worldPosition,
         }),
       ]);
     };
@@ -232,4 +232,4 @@
     return 'drag_and_drop_avatar_id';
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/global.html b/docs/html/game_browser_template/global.html index aeefc358b..107ae4141 100644 --- a/docs/html/game_browser_template/global.html +++ b/docs/html/game_browser_template/global.html @@ -1,3 +1,3 @@ Global
On this page

Members

(constant) MAPPING :Object.<string, Mapping>

Default mapping of controller

Type:

(constant) defaultVariables :DragAndDropAvatarVariables

Methods

addNativeCommands(inputManager, object3DUUID, withMap)

Add native commands in input manager

Parameters:
NameTypeDescription
inputManagerInputManager

manager

object3DUUIDstring

uuid of the object3D to contol

withMapboolean

move command are ignoring map

computeRelativeElevationFromGround(object3D, tileLayer, crsopt) → {number}

Compute relative elevation from ground of a Object3D

Parameters:
NameTypeAttributesDefaultDescription
object3DTHREE.Object3D

object3D

tileLayeritowns.TiledGeometryLayer

tile layer used to compute elevation

crsstring<optional>
EPSG:3946

coordinates referential system

Returns:
  • relative elevation
Type: 
number

moveHtmlToWorldPosition(htmlToMove, worldPos, camera)

Modify left and top css attributes of your html element to place in scene3D

Parameters:
NameTypeDescription
htmlToMoveHTMLElement

html element to modify left and top attributes

worldPosTHREE.Vector3

world position where to move your html

cameraTHREE.PerspectiveCamera

camera of the scene3D

removeNativeCommands(inputManager)

Remove native commands in input manager

Parameters:
NameTypeDescription
inputManagerInputManager

manager

Type Definitions

DragAndDropAvatarVariables

Properties
NameTypeDescription
camera_durationnumber

time for camera movement in ms

camera_offsetObject

offset to positioned camera behind object3D

camera_anglenumber

angle on x to positioned camera behind object3D

camera_distancenumber

distance to positioned camera behind object3D

update_z_crsstring

projection used to update z elevation of avatar

Mapping

Properties
NameTypeDescription
idstring

id of the command

keysArray.<string>

keys to trigger this command

cmdTypestring

type command to send to game context

Movement(dt)

Parameters:
NameTypeDescription
dtnumber

delta time movement

addButtonNoteCallback(textAreaValue)

Parameters:
NameTypeDescription
textAreaValuestring

current value of the textarea

@ud-viz/game_browser_template
\ No newline at end of file +
On this page

Members

(constant) MAPPING :Object.<string, Mapping>

Default mapping of controller

Type:

(constant) defaultVariables :DragAndDropAvatarVariables

Methods

addNativeCommands(inputManager, object3DUUID, withMap)

Add native commands in input manager

Parameters:
NameTypeDescription
inputManagerInputManager

manager

object3DUUIDstring

uuid of the object3D to contol

withMapboolean

move command are ignoring map

computeRelativeElevationFromGround(object3D, tileLayer, crsopt) → {number}

Compute relative elevation from ground of a Object3D

Parameters:
NameTypeAttributesDefaultDescription
object3DTHREE.Object3D

object3D

tileLayeritowns.TiledGeometryLayer

tile layer used to compute elevation

crsstring<optional>
EPSG:3946

coordinates referential system

Returns:
  • relative elevation
Type: 
number

moveHtmlToWorldPosition(htmlToMove, worldPos, camera)

Modify left and top css attributes of your html element to place in scene3D

Parameters:
NameTypeDescription
htmlToMoveHTMLElement

html element to modify left and top attributes

worldPosTHREE.Vector3

world position where to move your html

cameraTHREE.PerspectiveCamera

camera of the scene3D

removeNativeCommands(inputManager)

Remove native commands in input manager

Parameters:
NameTypeDescription
inputManagerInputManager

manager

Type Definitions

DragAndDropAvatarVariables

Properties
NameTypeDescription
camera_durationnumber

time for camera movement in ms

camera_offsetObject

offset to positioned camera behind object3D

camera_anglenumber

angle on x to positioned camera behind object3D

camera_distancenumber

distance to positioned camera behind object3D

update_z_crsstring

projection used to update z elevation of avatar

Mapping

Properties
NameTypeDescription
idstring

id of the command

keysArray.<string>

keys to trigger this command

cmdTypestring

type command to send to game context

Movement(dt)

Parameters:
NameTypeDescription
dtnumber

delta time movement

addButtonNoteCallback(textAreaValue)

Parameters:
NameTypeDescription
textAreaValuestring

current value of the textarea

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/index.html b/docs/html/game_browser_template/index.html index ddf7f9266..c56a49492 100644 --- a/docs/html/game_browser_template/index.html +++ b/docs/html/game_browser_template/index.html @@ -1,3 +1,3 @@ Home
On this page

@ud-viz/game_browser_template

NPM package version

@ud-viz/game_browser_template
\ No newline at end of file +
On this page

@ud-viz/game_browser_template

NPM package version

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/note_element_Element.js.html b/docs/html/game_browser_template/note_element_Element.js.html index eb6f28afc..ecf02e693 100644 --- a/docs/html/game_browser_template/note_element_Element.js.html +++ b/docs/html/game_browser_template/note_element_Element.js.html @@ -1,6 +1,6 @@ Source: note/element/Element.js
On this page

note_element_Element.js

import { CameraManager } from '../../CameraManager';
+    
On this page

note_element_Element.js

import { CameraManager } from '../../CameraManager';
 import { moveHtmlToWorldPosition } from '../../utils';
 import { SocketService } from '../note';
 import './style.css';
@@ -67,7 +67,7 @@
     const scriptParent = this.object3D.parent
       .getComponent(ExternalScriptComponent.TYPE)
       .getController()
-      .getScripts()[SocketService.ID_SCRIPT];
+      .scripts.get(SocketService.ID_SCRIPT);
 
     scriptParent.addNoteButton(this.containerButtons);
 
@@ -112,4 +112,4 @@
     return constant.ID_SCRIPT.NOTE_ELEMENT;
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/note_socketService_SocketService.js.html b/docs/html/game_browser_template/note_socketService_SocketService.js.html index 5d40e572b..8a8f2d2ae 100644 --- a/docs/html/game_browser_template/note_socketService_SocketService.js.html +++ b/docs/html/game_browser_template/note_socketService_SocketService.js.html @@ -1,6 +1,6 @@ Source: note/socketService/SocketService.js
On this page

note_socketService_SocketService.js

import { UI } from '../note';
+    
On this page

note_socketService_SocketService.js

import { UI } from '../note';
 import { moveHtmlToWorldPosition } from '../../utils';
 
 import { ScriptBase } from '@ud-viz/game_browser';
@@ -149,11 +149,18 @@
             worldPosition
           );
 
+          const parentWorldPosition = new THREE.Vector3();
+          pointerObject.parent.matrixWorld.decompose(
+            parentWorldPosition,
+            new THREE.Quaternion(),
+            new THREE.Vector3()
+          );
+
           return new Command({
             type: constant.COMMAND.UPDATE_TRANSFORM,
             data: {
               object3DUUID: pointerObject.uuid,
-              position: worldPosition.sub(this.context.object3D.position), // position in game context referential
+              position: worldPosition.sub(parentWorldPosition),
             },
           });
         }
@@ -291,4 +298,4 @@
     };
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/note_ui_UI.js.html b/docs/html/game_browser_template/note_ui_UI.js.html index b8862aef1..332a7c741 100644 --- a/docs/html/game_browser_template/note_ui_UI.js.html +++ b/docs/html/game_browser_template/note_ui_UI.js.html @@ -1,6 +1,6 @@ Source: note/ui/UI.js
On this page

note_ui_UI.js

import { Element } from '../note';
+    
On this page

note_ui_UI.js

import { Element } from '../note';
 
 import { checkParentChild } from '@ud-viz/utils_browser';
 import * as THREE from 'three';
@@ -79,8 +79,8 @@
           );
           externalScriptComp
             .getController()
-            .getScripts()
-            [Element.ID_SCRIPT].displayNoteMessageHtml();
+            .scripts.get(Element.ID_SCRIPT)
+            .displayNoteMessageHtml();
         }
       }
     );
@@ -94,4 +94,4 @@
     return constant.ID_SCRIPT.NOTE_UI;
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-Changelog.html b/docs/html/game_browser_template/tutorial-Changelog.html index 9c4864a5c..afc54adb6 100644 --- a/docs/html/game_browser_template/tutorial-Changelog.html +++ b/docs/html/game_browser_template/tutorial-Changelog.html @@ -1,6 +1,17 @@ Tutorial: Changelog
On this page

Changelog

v4.0.1

@ud-viz

- Examples:
+    
On this page

Changelog

v4.0.2

@ud-viz

- npm cmd webpack-bundle-analyzer 
+- script setPackageVersion.js
+- update to itowns 2.41.1-next.9
+

@ud-viz/game_shared

- compute bandwidth
+- idScritp:string => scriptParam:{id:string, priority:number}
+- applyCommandCallback API
+- update(ColliderComp): add SHAPE_TYPE
+

@ud-viz/game_browser_template

- optimization of the camera manager obstacle computation
+

@ud-viz/utils_shared

- computeFilename
+- throttle
+

@ud-viz/game_editor

- creation of the package
+

v4.0.1

@ud-viz

- Examples:
   - point_cloud_visualizer.html
   - game_dom_element_3d_cube.html
   - widget_guided_tour.html
@@ -13,7 +24,7 @@
 

@ud-viz/widget_layer_choice

- Complete refacto
 - Can order ColorLayer
 - LocalStorage ui element
-

@ud-viz/utils_shared

- round
+

@ud-viz/utils_shared

- round
 - vector3ToLabel
 - insert
 

@ud-viz/utils_browser

- createLocalStorageNumberInput
@@ -22,16 +33,16 @@
 - createLocalStorageDetails
 

@ud-viz/utils_node

- fix(test):splice first '/' of req.url, statusCode
 

@ud-viz/game_node_template

- DomElement3DCubeManager script
-

@ud-viz/game_browser_template

- DomElement3DCube script
+

@ud-viz/game_browser_template

- DomElement3DCube script
 

@ud-viz/widget_guided_tour

- Creation of the package
-

v4.0.0

@ud-viz

- Refact(ud-viz)! split into multiples packages
+

v4.0.0

@ud-viz

- Refact(ud-viz)! split into multiples packages
 - Add loading screen in examples
 - Fix show room
 - Add avatar jitsi game example
 - Doc:
     - fix doc
     - update doc
-

v3.2.0

@ud-viz

- Fix socket.io dependencies
+

v3.2.0

@ud-viz

- Fix socket.io dependencies
 - Update with node18
 - Prettier harmonization
 - Improve `Readme.md`
@@ -53,7 +64,7 @@
 - Fix bug Frame3DPlanar render labels
 - THREEUtil encoding => colorSpace
 - Update itowns 2.40.0
-

v3.1.1

@ud-viz

- NPM :
+

v3.1.1

@ud-viz

- NPM :
     - Remove `^` in all package.json
 - Examples:
     - 2D Visualisation Mode
@@ -233,4 +244,4 @@
 * Delete old ifc feature, aimed to be in a demo
 * Don't override material if the material has a texture
 * Remove dependency towards remote data
-
\ No newline at end of file +
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-Contributing.html b/docs/html/game_browser_template/tutorial-Contributing.html index 0c790826b..8cb6921e1 100644 --- a/docs/html/game_browser_template/tutorial-Contributing.html +++ b/docs/html/game_browser_template/tutorial-Contributing.html @@ -1,3 +1,3 @@ Tutorial: Contributing
On this page

Contributing

Contributing

Coding style (Linter)

The JavaScript files coding style is defined with eslint through the .eslintrc.js configuration file. It can be checked (e.g. prior to a commit) with the npm run eslint command. Notice that UD-Viz coding style uses a unix linebreak-style (aka LF as newline character).

Submitting an issue

  • Create an issue with explicit name and a description
  • Add at least one label of task category, priority and related package
  • If it's a bug report add steps to reproduce.

Submiting Pull Request (PR)

Prior to PR-submission

  • Commit (git commit) with representative messages (commit messages end-up collected in the PR message and eventually release explanations).
  • Make sure your code is mature for a review.
  • git rebase origin/master, in order to resolve merge conflicts to master. Doc : git-rebase. If you are not able to resolve all of the conflicts of your rebase, please open a new discussion.

note that after a git rebase you have to git push --force

  • npm run eslint must finish with no errors.
  • npm run test-unit must finish with no errors.
  • npm run test-functional must finish with no errors.
  • npm audit must finish without vulnerabilities lower than high.
  • npm run test-links must finish with no errors.

⚠️ When your PR is open each push on your branch will trigger Travis CI jobs.

See here for more information.

Before submitting a pull request, and because UD-Viz still misses some tests, non-regression testing must be done manually. A developer must thus at least check that all the demo examples (they should function similar to their online deployment) are still effective.

Note that you should interact with ui (user interface) for complete tests.

PR Submission

Once your PR is ready to be submitted here are the steps to follow:

  • Create a PR via the Github interface
  • Describe synthetically the PR via the title and the description
  • Add labels if necessary
  • Link the related issues. (The linked issues are automatically closed when the PR is rebased and merged)
  • Add one or more reviewers.
  • Iterate over potential change requests until the PR is approved.
  • Click on Rebase and merge button
  • Delete the branch
@ud-viz/game_browser_template
\ No newline at end of file +
On this page

Contributing

Contributing

Coding style (Linter)

The JavaScript files coding style is defined with eslint through the .eslintrc.js configuration file. It can be checked (e.g. prior to a commit) with the npm run eslint command. Notice that UD-Viz coding style uses a unix linebreak-style (aka LF as newline character).

Submitting an issue

  • Create an issue with explicit name and a description
  • Add at least one label of task category, priority and related package
  • If it's a bug report add steps to reproduce.

Submiting Pull Request (PR)

Prior to PR-submission

  • Commit (git commit) with representative messages (commit messages end-up collected in the PR message and eventually release explanations).
  • Make sure your code is mature for a review.
  • git rebase origin/master, in order to resolve merge conflicts to master. Doc : git-rebase. If you are not able to resolve all of the conflicts of your rebase, please open a new discussion.

note that after a git rebase you have to git push --force

  • npm run eslint must finish with no errors.
  • npm run test-unit must finish with no errors.
  • npm run test-functional must finish with no errors.
  • npm audit must finish without vulnerabilities lower than high.
  • npm run test-links must finish with no errors.

⚠️ When your PR is open each push on your branch will trigger Travis CI jobs.

See here for more information.

Before submitting a pull request, and because UD-Viz still misses some tests, non-regression testing must be done manually. A developer must thus at least check that all the demo examples (they should function similar to their online deployment) are still effective.

Note that you should interact with ui (user interface) for complete tests.

PR Submission

Once your PR is ready to be submitted here are the steps to follow:

  • Create a PR via the Github interface
  • Describe synthetically the PR via the title and the description
  • Add labels if necessary
  • Link the related issues. (The linked issues are automatically closed when the PR is rebased and merged)
  • Add one or more reviewers.
  • Iterate over potential change requests until the PR is approved.
  • Click on Rebase and merge button
  • Delete the branch
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-Contributors.html b/docs/html/game_browser_template/tutorial-Contributors.html index 7825f45f4..eeed3b3ec 100644 --- a/docs/html/game_browser_template/tutorial-Contributors.html +++ b/docs/html/game_browser_template/tutorial-Contributors.html @@ -1,3 +1,3 @@ Tutorial: Contributors
On this page

Contributors

See Contributing.md for details on how to contribute to UD-Viz.

The following people have contributed to UD-Viz:

See the VCity Team Members page here.

Commiters:

@ud-viz/game_browser_template
\ No newline at end of file +
On this page

Contributors

See Contributing.md for details on how to contribute to UD-Viz.

The following people have contributed to UD-Viz:

See the VCity Team Members page here.

Commiters:

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-Definitions.html b/docs/html/game_browser_template/tutorial-Definitions.html index 786fe1d08..0c14105cf 100644 --- a/docs/html/game_browser_template/tutorial-Definitions.html +++ b/docs/html/game_browser_template/tutorial-Definitions.html @@ -1,3 +1,3 @@ Tutorial: Definitions
On this page

Definitions

Definitions (within the UD-Viz context)

  • Component everything thats is necessary to execute only one aspect of a desired functionality (see also module).
  • Widget is used in a itowns context meaning it's a class extending itowns Widget which is a class containing a domElement and an itowns View.
@ud-viz/game_browser_template
\ No newline at end of file +
On this page

Definitions

Definitions (within the UD-Viz context)

  • Component everything thats is necessary to execute only one aspect of a desired functionality (see also module).
  • Widget is used in a itowns context meaning it's a class extending itowns Widget which is a class containing a domElement and an itowns View.
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-Developers.html b/docs/html/game_browser_template/tutorial-Developers.html index 0f76ddb65..55317d766 100644 --- a/docs/html/game_browser_template/tutorial-Developers.html +++ b/docs/html/game_browser_template/tutorial-Developers.html @@ -1,4 +1,4 @@ Tutorial: Developers
On this page

Developers

Developers

Pre-requisites

Developing UD-Viz applications requires knowledge about :

NVM

Developpers are advised to use node version manager (nvm). nvm allows you to quickly install and use different versions of node via the command line.

To download and install follow this link: https://github.com/nvm-sh/nvm#installing-and-updating

Environment Tips

IDE

VSCode is recommended.

VisualStudio Code

When using Visual Studio Code, you can install the following extentions to make your life easier:

Tips for Windows developers

As configured, the coding style requires a Linux style newline characters which might be overwritten in Windows environments (both by git and/or your editor) to become CRLF. When such changes happen eslint will warn about "incorrect" newline characters (which can always be fixed with npm run eslint-fix but this process quickly gets painful). In order to avoid such difficulties, the recommended pratice consists in

  1. setting git's core.autocrlf to false (e.g. with git config --global core.autocrlf false)
  2. configure your editor/IDE to use Unix-style endings
  3. In order to use scripts that launch a shell script with Powershell: npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"

Npm Scripts

ScriptDescription
npm run cleanRemove files and folders generated by npm install and the npm run build-(dev-)examples script such as ./node_modules, package-lock.json, and ./dist
npm run clear-nodeLinux Command: Kill all node process (this is a sudo script)
npm run resetReinstalls npm dependencies. This script runs npm run clean and npm install command
npm run build-examplesBuild a production bundle base on this webpack config
npm run build-dev-examplesBuild a development bundle base on this webpack config
npm run dev-examplesLaunch a watcher running this script. See here for more information
npm run eslintRun the linter. See here for more information
npm run eslint-quietRun the linter without displaying warnings, only errors
npm run eslint-fixRun the linter and attempt to fix errors and warning automatically
npm run test-unitBuild examples bundle if needed and run npm run test in each packages . Also ran by CI. See here for more information
npm run test-functionalBuild examples bundle if needed and run each *.html in ./examples folder . Also ran by CI. See here for more information
npm run test-linksRun this script to detect dead links in markdown. Also ran by CI. See here for more information
npm run local-ciRun CI on your local computer. See here for more information
npm run pre-publishChange version in all package.json ( eg npm run pre-publish x.x.x ). See this script
npm run docsDelete old docs and run this script which generate docs
npm run dev-docsLaunch a watcher for generating and debugging the documentation. See here for more information
npm run back-endrun an http server (with some string-replace) + a game socket service.
http://locahost:8000/
npm run startRun npm run build-examples and npm run back-end

Debugging the examples

npm run dev-examples
-

Continuous Integration (Travis CI)

Each time origin/master branch is impacted by changes, Travis CI is triggered. It does a set of jobs describe in travis.yml.

Jobs list :

  • npm run eslint: Lint code sources
  • npm run test-unit: Unit test
  • npm run test-functional: Functional test
  • npm audit --audit-level=low: Npm native command (npm-audit) which check packages dependencies vulnerabilities.
  • npm run test-links: Run this script which check if links are not broken.
  • node ./test/docs.js: Run this script which check if documentation is generated without warning or error.

Contributing

For information on the accepted coding style, submitting Issues, and submitting Pull Requests see Contributing.md

Publishing

For creating a new release see ReleasePublish.md

Generating the documentation

The Online documentation can be re-generated by following these instructions.

@ud-viz/game_browser_template
\ No newline at end of file +
On this page

Developers

Developers

Pre-requisites

Developing UD-Viz applications requires knowledge about :

NVM

Developpers are advised to use node version manager (nvm). nvm allows you to quickly install and use different versions of node via the command line.

To download and install follow this link: https://github.com/nvm-sh/nvm#installing-and-updating

Environment Tips

IDE

VSCode is recommended.

VisualStudio Code

When using Visual Studio Code, you can install the following extentions to make your life easier:

Tips for Windows developers

As configured, the coding style requires a Linux style newline characters which might be overwritten in Windows environments (both by git and/or your editor) to become CRLF. When such changes happen eslint will warn about "incorrect" newline characters (which can always be fixed with npm run eslint-fix but this process quickly gets painful). In order to avoid such difficulties, the recommended pratice consists in

  1. setting git's core.autocrlf to false (e.g. with git config --global core.autocrlf false)
  2. configure your editor/IDE to use Unix-style endings
  3. In order to use scripts that launch a shell script with Powershell: npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"

Npm Scripts

ScriptDescription
npm run cleanRemove files and folders generated by npm install and the npm run build-(dev-)examples script such as ./node_modules, package-lock.json, and ./dist
npm run clear-nodeLinux Command: Kill all node process (this is a sudo script)
npm run resetReinstalls npm dependencies. This script runs npm run clean and npm install command
npm run build-examplesBuild a production bundle base on this webpack config
npm run build-dev-examplesBuild a development bundle base on this webpack config
npm run dev-examplesLaunch a watcher running this script. See here for more information
npm run eslintRun the linter. See here for more information
npm run eslint-quietRun the linter without displaying warnings, only errors
npm run eslint-fixRun the linter and attempt to fix errors and warning automatically
npm run test-unitBuild examples bundle if needed and run npm run test in each packages . Also ran by CI. See here for more information
npm run test-functionalBuild examples bundle if needed and run each *.html in ./examples folder . Also ran by CI. See here for more information
npm run test-linksRun this script to detect dead links in markdown. Also ran by CI. See here for more information
npm run local-ciRun CI on your local computer. See here for more information
npm run pre-publishChange version in all package.json ( eg npm run pre-publish x.x.x ). See this script
npm run docsDelete old docs and run this script which generate docs
npm run dev-docsLaunch a watcher for generating and debugging the documentation. See here for more information
npm run back-endrun an http server (with some string-replace) + a game socket service.
http://locahost:8000/
npm run analyze-bundleUse webpack-bundle-analyzer to see what's inside the examples bundle
npm run analyze-dev-bundleUse webpack-bundle-analyzer to see what's inside the examples dev bundle
npm run startRun npm run build-examples and npm run back-end

Debugging the examples

npm run dev-examples
+

Continuous Integration (Travis CI)

Each time origin/master branch is impacted by changes, Travis CI is triggered. It does a set of jobs describe in travis.yml.

Jobs list :

  • npm run eslint: Lint code sources
  • npm run test-unit: Unit test
  • npm run test-functional: Functional test
  • npm audit --audit-level=low: Npm native command (npm-audit) which check packages dependencies vulnerabilities.
  • npm run test-links: Run this script which check if links are not broken.
  • node ./test/docs.js: Run this script which check if documentation is generated without warning or error.

Contributing

For information on the accepted coding style, submitting Issues, and submitting Pull Requests see Contributing.md

Publishing

For creating a new release see ReleasePublish.md

Generating the documentation

The Online documentation can be re-generated by following these instructions.

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-ReleasePublish.html b/docs/html/game_browser_template/tutorial-ReleasePublish.html index 3feaa1c59..e716544af 100644 --- a/docs/html/game_browser_template/tutorial-ReleasePublish.html +++ b/docs/html/game_browser_template/tutorial-ReleasePublish.html @@ -1,6 +1,6 @@ Tutorial: ReleasePublish
On this page

ReleasePublish

Release creation process

  1. Create a new branch for the release, e.g. release-x.x.x from the master branch.

  2. Change the monorepo version npm run pre-publish x.x.x. It will change the version in all package.json and generate a Changelog file with all commits' names since the last release. (See the script here)

  3. Pick relevant commit message in ./docs/static/ChangelogDiff.txt to put them in ./docs/static/Changelog.md (Reformulate if necessary). Arrange commits in theses sections:

    # vx.x.x
    +    
    On this page

    ReleasePublish

    Release creation process

    1. Create a new branch for the release, e.g. release-x.x.x from the master branch.

    2. Change the monorepo version npm run pre-publish x.x.x. It will change the version in all package.json and generate a Changelog file with all commits' names since the last release. (See the script here)

    3. Pick relevant commit message in ./docs/static/ChangelogDiff.txt to put them in ./docs/static/Changelog.md (Reformulate if necessary). Arrange commits in theses sections:

      # vx.x.x
       
       ## @ud-viz
           - Examples:
      @@ -14,4 +14,4 @@
       
       ....
       
      -
    4. Run npm run docs. Generate the documentation with JSDoc.

    5. Follow the prior PR submission

    6. Push your branch to the main repository (This branch will be used to create the release tag)

    7. Create a PR

    8. Assign yourself to the PR

    9. Have a reviewer approve your PR

    10. Create a Github release (through the GUI):

      • Copy the new content given in Changelog.md: this duplication doesn't seem to be DRY but the Release comments are hosted by github (and will be lost if you migrate to e.g. gitlab) and are not browsable offline.
        • You might consider using the "Auto-generate release notes" feature
        • Note that creating a Github release creates an underlying git tag (which can previously be created with a git tag)

    Publish the package

    To publish the package to npmjs package repository

    1. Authenticate on npmjs with npm login and use the vcity account together with proper credentials. Note:
      • Ask username and password to login at an admin
      • Because the npmjs authentication mode of the vcityaccount is currently configured to One-Time-Password (OTP) over email you will need to be part of the vcity@liris.cnrs.fr email alias forwarder to receive the OTP and be patient about it (the reception delay can be up to a couple minutes).
    2. npm publish --workspaces --access public (workspaces option refer to the workspaces field in the package.json).
    \ No newline at end of file +
  4. Run npm run docs. Generate the documentation with JSDoc.

  5. Follow the prior PR submission

  6. Push your branch to the main repository (This branch will be used to create the release tag)

  7. Create a PR

  8. Assign yourself to the PR

  9. Have a reviewer approve your PR

  10. Create a Github release (through the GUI):

    • Copy the new content given in Changelog.md: this duplication doesn't seem to be DRY but the Release comments are hosted by github (and will be lost if you migrate to e.g. gitlab) and are not browsable offline.
      • You might consider using the "Auto-generate release notes" feature
      • Note that creating a Github release creates an underlying git tag (which can previously be created with a git tag)

Publish the package

To publish the package to npmjs package repository

  1. Authenticate on npmjs with npm login and use the vcity account together with proper credentials. Note:
    • Ask username and password to login at an admin
    • Because the npmjs authentication mode of the vcityaccount is currently configured to One-Time-Password (OTP) over email you will need to be part of the vcity@liris.cnrs.fr email alias forwarder to receive the OTP and be patient about it (the reception delay can be up to a couple minutes).
  2. npm publish --workspaces --access public (workspaces option refer to the workspaces field in the package.json).
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-Reproducibility.html b/docs/html/game_browser_template/tutorial-Reproducibility.html index 05df35fec..4c2ec5397 100644 --- a/docs/html/game_browser_template/tutorial-Reproducibility.html +++ b/docs/html/game_browser_template/tutorial-Reproducibility.html @@ -1,3 +1,3 @@ Tutorial: Reproducibility
On this page

Reproducibility

The following stale branches

  • reproductibility/article-temporel-3DTiles
  • reproductibility/article-temporel-3DTilesTemporal

that have been deleted for sake of simplicity, can still be respectively retrieved through their associated tags with

  • git checkout -b reproductibility/article-temporel-3DTiles reproductibility/article-temporel-3DTiles-tag
  • git checkout -b reproductibility/article-temporel-3DTilesTemporal reproductibility/article-temporel-3DTilesTemporal-tag

Refer to this StackOverflow for a description of the branch "archiving"/retrieval process.

@ud-viz/game_browser_template
\ No newline at end of file +
On this page

Reproducibility

The following stale branches

  • reproductibility/article-temporel-3DTiles
  • reproductibility/article-temporel-3DTilesTemporal

that have been deleted for sake of simplicity, can still be respectively retrieved through their associated tags with

  • git checkout -b reproductibility/article-temporel-3DTiles reproductibility/article-temporel-3DTiles-tag
  • git checkout -b reproductibility/article-temporel-3DTilesTemporal reproductibility/article-temporel-3DTilesTemporal-tag

Refer to this StackOverflow for a description of the branch "archiving"/retrieval process.

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-architecture.html b/docs/html/game_browser_template/tutorial-architecture.html index 4ec8c89d5..de5c9ac02 100644 --- a/docs/html/game_browser_template/tutorial-architecture.html +++ b/docs/html/game_browser_template/tutorial-architecture.html @@ -1,11 +1,12 @@ Tutorial: architecture
On this page

architecture

@ud-viz packages architecture

flowchart TB
+    
On this page

architecture

@ud-viz packages architecture

flowchart TB
 subgraph ud-viz/packages
 extensions_3d_tiles_temporal
 frame3d
 game_browser
 game_browser_template
+game_editor
 game_node
 game_node_template
 game_shared
@@ -40,6 +41,10 @@
 game_browser_template-->game_shared_template
 game_browser_template-->utils_browser
 game_browser_template-->utils_shared
+game_editor-->game_shared
+game_editor-->game_browser
+game_editor-->utils_browser
+game_editor-->utils_shared
 game_node-->game_shared
 game_node-->utils_shared
 game_node_template-->game_node
@@ -78,4 +83,4 @@
 widget_slide_show-->utils_browser
 widget_sparql-->utils_browser
 widget_workspace-->utils_browser
-widget_workspace-->widget_sparql
\ No newline at end of file +widget_workspace-->widget_sparql
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-game.html b/docs/html/game_browser_template/tutorial-game.html index 94e70073a..a0b14601c 100644 --- a/docs/html/game_browser_template/tutorial-game.html +++ b/docs/html/game_browser_template/tutorial-game.html @@ -1,6 +1,6 @@ Tutorial: game
On this page

game

Game

This document gives an overview of how the game part works. Here are some different implementations of the game part:

Prerequisites

  • Run a back-end in node.js with express. See MDN DOC.
  • See here how to import @ud-viz/game_browser framework.

Creating a singleplayer simple game

The goal of this section is to learn how to set a singleplayer simple game structure:

Create a scene

Initialize a Planar part of @ud-viz/frame3d which creates a itowns PlanarView.

// Define geographic extent: CRS, min/max X, min/max Y
+    
On this page

game

Game

This document gives an overview of how the game part works. Here are some different implementations of the game part:

Prerequisites

  • Run a back-end in node.js with express. See MDN DOC.
  • See here how to import @ud-viz/game_browser framework.

Creating a singleplayer simple game

The goal of this section is to learn how to set a singleplayer simple game structure:

Create a scene

Initialize a Planar part of @ud-viz/frame3d which creates a itowns PlanarView.

// Define geographic extent: CRS, min/max X, min/max Y
 const extent = new udvizBrowser.itowns.Extent(
   'EPSG:4326',
   1837816.94334,
@@ -12,7 +12,6 @@
 const frame3DPlanar = new frame3d.Planar(extent, {
   hasItownsControls: true,
 });
-
 

Create a SinglePlanarProcess

Initialize a SinglePlanarProcess with a AssetManager and a InputManager part of @ud-viz/game_browser, an Object3D part of @ud-viz/game_shared defines your game model and SinglePlanarProcess is stepping your game over time.

const gameObject3D = new Object3D({
   static: true,
 });
@@ -47,10 +46,10 @@
   static: true,
   components: {
     GameScript: {
-      idScripts: [GameContextScript.ID_SCRIPT],
+      scriptParams: [{ id: GameContextScript.ID_SCRIPT }],
     },
     ExternalScript: {
-      idScripts: [GameExternalContextScript.ID_SCRIPT],
+      scriptParams: [{ id: GameExternalContextScript.ID_SCRIPT }],
     },
   },
 });
@@ -63,7 +62,6 @@
   {
     gameScriptClass: [GameContextScript],
     externalGameScriptClass: [GameExternalContextScript],
-    gameOrigin: { x: extent.center().x, y: extent.center().y, z: 100}
   }
 );
 

Spawn cubes

In the init of GameContextScript add these lines

this.goCubes = [];
@@ -87,7 +85,7 @@
   for (let index = this.goCubes.length - 1; index >= 0; index--) {
     const cube = this.goCubes[index];
     cube.position.z += 0.1 * this.context.dt;
-    cube.setOutdated(true);// notify game external context that this gameobject need update 
+    cube.setOutdated(true);// notify game external context that this gameobject need update
 
     // sky is the limit
     if (cube.position.z > 2000) {
@@ -96,23 +94,19 @@
     }
   }
 }
-

Send command

Add in the init of GameExternalContextScript the following code

this.context.inputManager.addMouseCommand(
-  'command_id',
-  'click',
-  () => {
-    return new Command({
-      type: 'toggle_pause',
-    });
-  }
-);
+

Send command

Add in the init of GameExternalContextScript the following code

this.context.inputManager.addMouseCommand('command_id', 'click', () => {
+  return new Command({
+    type: 'toggle_pause',
+  });
+});
 

Command is part of @ud-viz/game_shared

This sends a command on the mouse click to GameContextScript. Then in the init of GameContextScript add these lines:

this.pause = false;
 setInterval(() => {
   if (this.pause) return;
   ...
 

In the tick of GameContextScript

tick() {
+
+  this.applyCommandCallbackOf('toggle_pause', () => { this.pause = !this.pause })
+
   if (this.pause) return;
   ...
-

And finally add onCommand method:

onCommand(type) {
-  if (type === 'toggle_pause') this.pause = !this.pause;
-}
-

Now you have learned how to build a singleplayer simple game, let's see how to modify it to make a multiplayer one.

Create a multiplayer simple game (WIP)

1 Create a backend import udviz node with require as before run an express app running a http server Final result backend WIP

import game part of shared create a socket service by passsing the http server load gameobject3D where gameobject3D is the same one as the previous example the both script are unknown hard coded value of ids to keep it simple create another file gamethreadchild ok now you have to give the entry point to your thread the game script is the one running backend side copy paste it to your gamethread you have to give him the game script because he is going to run there replace udvizBrowser.Shared.Game by Game ouais backend okay

front end create a new html file base on previous one delete game script delete gameobject 3D Replace singleprocess by multiprocess create a socket io wrapper + connect it run enjoy

Examples

Singleplayer one:

This example requires knowledge about @ud-viz/game_shared and @ud-viz/game_browser

Multiplayer one:

This example requires the same knowledge as singleplayer plus @ud-viz/game_node

\ No newline at end of file +

Now you have learned how to build a singleplayer simple game, let's see how to modify it to make a multiplayer one.

Create a multiplayer simple game (WIP)

1 Create a backend import udviz node with require as before run an express app running a http server Final result backend WIP

import game part of shared create a socket service by passsing the http server load gameobject3D where gameobject3D is the same one as the previous example the both script are unknown hard coded value of ids to keep it simple create another file gamethreadchild ok now you have to give the entry point to your thread the game script is the one running backend side copy paste it to your gamethread you have to give him the game script because he is going to run there replace udvizBrowser.Shared.Game by Game ouais backend okay

front end create a new html file base on previous one delete game script delete gameobject 3D Replace singleprocess by multiprocess create a socket io wrapper + connect it run enjoy

Examples

Singleplayer one:

This example requires knowledge about @ud-viz/game_shared and @ud-viz/game_browser

Multiplayer one:

This example requires the same knowledge as singleplayer plus @ud-viz/game_node

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-how_to_import.html b/docs/html/game_browser_template/tutorial-how_to_import.html index 84f0e392a..e57164f3c 100644 --- a/docs/html/game_browser_template/tutorial-how_to_import.html +++ b/docs/html/game_browser_template/tutorial-how_to_import.html @@ -1,4 +1,4 @@ Tutorial: how_to_import
On this page

how_to_import

How to use it in your demo?

You can use it through npm

In your demo:

npm install --save @ud-viz/xxx
-

See https://github.com/VCityTeam/UD-Viz-template

@ud-viz/game_browser_template
\ No newline at end of file +
On this page

how_to_import

How to use it in your demo?

You can use it through npm

In your demo:

npm install --save @ud-viz/xxx
+

See https://github.com/VCityTeam/UD-Viz-template

@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/tutorial-initial_camera_transform.html b/docs/html/game_browser_template/tutorial-initial_camera_transform.html index 47eff5977..3ab4cd2bc 100644 --- a/docs/html/game_browser_template/tutorial-initial_camera_transform.html +++ b/docs/html/game_browser_template/tutorial-initial_camera_transform.html @@ -1,6 +1,6 @@ Tutorial: initial_camera_transform
On this page

initial_camera_transform

When creating a Planar part of @ud-viz/frame3d you may want your camera to be placed at a specific point of view. In this tutorial we are going to see differents options available to achieve that.

Configure Planar

Planar take an options object at the construction, where following fields will initialize camera point of view:

  • coordinates: camera look at geographic coordinate
  • heading: camera's heading, in degree
  • range: camera distance to target coordinate, in meter
  • tilt: camera's tilt, in degree

For more details check Planar documentation.

If you don't specify how the camera should be placed and you typed something like that:

const frame3DPlanar = new Planar(extent); // <-- no options passed
+    
On this page

initial_camera_transform

When creating a Planar part of @ud-viz/frame3d you may want your camera to be placed at a specific point of view. In this tutorial we are going to see differents options available to achieve that.

Configure Planar

Planar take an options object at the construction, where following fields will initialize camera point of view:

  • coordinates: camera look at geographic coordinate
  • heading: camera's heading, in degree
  • range: camera distance to target coordinate, in meter
  • tilt: camera's tilt, in degree

For more details check Planar documentation.

If you don't specify how the camera should be placed and you typed something like that:

const frame3DPlanar = new Planar(extent); // <-- no options passed
 

then the camera will look at the center of the extent with these default values:

  • heading: -50°
  • range: 3000m
  • tilt: 10°

if you want to configure that you should then write something like:

const frame3DPlanar = new Planar(extent, {
   coordinates: {
     x: some_x_coordinate
@@ -15,4 +15,4 @@
 

Note: the first time you will run your webpage the camera point of view will be the one pass to the Planar

Use URL

You can also use url util function to initialize default camera point of view. In that case you should first produce an url with camera matrix encoded in it.

const url = new URL(window.location.origin + window.location.pathname);
 appendCameraMatrixToURL(url, yourCamera);
 

then to initialize your camera with this url you would have to write something like that:

const isCameraMatrixInitializedWithURL = URLSetCameraMatrix(yourCamera);
-
\ No newline at end of file +
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_browser_template/utils.js.html b/docs/html/game_browser_template/utils.js.html index 5f5d70de8..5f2f0c733 100644 --- a/docs/html/game_browser_template/utils.js.html +++ b/docs/html/game_browser_template/utils.js.html @@ -1,6 +1,6 @@ Source: utils.js
On this page

utils.js

import { Command } from '@ud-viz/game_shared';
+    
On this page

utils.js

import { Command } from '@ud-viz/game_shared';
 import { constant } from '@ud-viz/game_shared_template';
 import { InputManager } from '@ud-viz/game_browser';
 import * as itowns from 'itowns';
@@ -125,4 +125,4 @@
     inputManager.removeKeyCommand(map.id, map.keys);
   }
 }
-
\ No newline at end of file +
@ud-viz/game_browser_template
\ No newline at end of file diff --git a/docs/html/game_editor/-_home_imuv_projects_UD-Viz_packages_game_editor_src_index.js.html b/docs/html/game_editor/-_home_imuv_projects_UD-Viz_packages_game_editor_src_index.js.html new file mode 100644 index 000000000..8fde8f862 --- /dev/null +++ b/docs/html/game_editor/-_home_imuv_projects_UD-Viz_packages_game_editor_src_index.js.html @@ -0,0 +1,1524 @@ +Source: /home/imuv/projects/UD-Viz/packages/game_editor/src/index.js
On this page

-_home_imuv_projects_UD-Viz_packages_game_editor_src_index.js

import {
+  AudioComponent,
+  ColliderComponent,
+  Context,
+  Object3D as GameObject3D,
+  GameScriptComponent,
+  RenderComponent,
+} from '@ud-viz/game_shared';
+import { AssetManager, RenderController } from '@ud-viz/game_browser';
+import {
+  Object3D,
+  Box3,
+  Vector3,
+  Quaternion,
+  Mesh,
+  BoxGeometry,
+  MeshBasicMaterial,
+  CircleGeometry,
+  Color,
+  Raycaster,
+  Vector2,
+  SphereGeometry,
+  Scene,
+  OrthographicCamera,
+  WebGLRenderer,
+  AmbientLight,
+} from 'three';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
+import { TransformControls } from 'three/examples/jsm/controls/TransformControls';
+import { ConvexGeometry } from 'three/examples/jsm/geometries/ConvexGeometry';
+
+import './style.css';
+import {
+  cameraFitRectangle,
+  createLabelInput,
+  createLocalStorageSlider,
+  RequestAnimationFrameProcess,
+  Vector3Input,
+} from '@ud-viz/utils_browser';
+import {
+  arrayPushOnce,
+  objectParseNumeric,
+  removeFromArray,
+} from '@ud-viz/utils_shared';
+
+const COLLIDER_MATERIAL = new MeshBasicMaterial({ color: 'green' });
+const COLLIDER_MATERIAL_SELECTED = new MeshBasicMaterial({ color: 'red' });
+const COLLIDER_POINT_MATERIAL = new MeshBasicMaterial({ color: 'yellow' });
+
+export class ScriptInput {
+  /**
+   * private constructor user should not extends it !!
+   *
+   * @param {Editor} editor - editor running this script
+   * @param {object} variables - variables to edit
+   * @param {HTMLElement} domElement - where ui element should be appended
+   */
+  constructor(editor, variables, domElement) {
+    /** @type {Editor} */
+    this.editor = editor;
+
+    /** @type {object} */
+    this.variables = variables;
+
+    /** @type {HTMLElement} */
+    this.domElement = domElement;
+
+    /** @type {HTMLElement} */
+    const closeButton = document.createElement('button');
+    this.domElement.appendChild(closeButton);
+    closeButton.innerText = 'Fermer';
+
+    closeButton.onclick = this.dispose.bind(this);
+  }
+
+  /**
+   * Call when this is disposed
+   */
+  dispose() {
+    while (this.domElement.firstChild) this.domElement.firstChild.remove();
+  }
+
+  /**
+   * Call when this is instanciated
+   */
+  init() {}
+
+  /**
+   * Call every frame computed
+   */
+  tick() {}
+
+  get ID_EDIT_SCRIPT() {
+    return ScriptInput.ID_EDIT_SCRIPT;
+  }
+
+  static get ID_EDIT_SCRIPT() {
+    throw new Error(
+      'abstract method, you have to specify which script is controlled'
+    );
+  }
+}
+
+export class Editor {
+  /**
+   *
+   * @param {import("@ud-viz/frame3d").Planar|import("@ud-viz/frame3d").Base} frame3D - frame 3d
+   * @param {AssetManager} assetManager - asset manager
+   * @param {object} externalScriptInputs - external script input to edit GameScript
+   * @param {object} gameScriptInputs - game script input to edit GameScript
+   * @param {object} userData - user data
+   */
+  constructor(
+    frame3D,
+    assetManager,
+    externalScriptInputs,
+    gameScriptInputs,
+    userData
+  ) {
+    /** @type {import("@ud-viz/frame3d").Planar|import("@ud-viz/frame3d").Base} */
+    this.frame3D = frame3D;
+
+    /** @type {AssetManager} */
+    this.assetManager = assetManager;
+
+    /** @type {Array<ScriptInput>} */
+    this.externalScriptInputs = externalScriptInputs;
+
+    /** @type {Array<ScriptInput>} */
+    this.gameScriptInputs = gameScriptInputs;
+
+    /** @type {ScriptInput|null} */
+    this.scriptInput = null;
+
+    /** @type {object} */
+    this.userData = userData;
+
+    /** @type {HTMLElement} */
+    this.leftPan = document.createElement('div');
+    this.leftPan.setAttribute('id', 'left_pan');
+    this.frame3D.domElementUI.appendChild(this.leftPan);
+
+    const leftPanWidthInput = createLocalStorageSlider(
+      'editor_left_width_range_key',
+      'Taille UI ',
+      this.leftPan,
+      {
+        min: 100,
+        max: 700,
+        defaultValue: 300,
+      }
+    );
+    const updateLeftWidth = () => {
+      this.leftPan.style.width = leftPanWidthInput.value + 'px';
+    };
+    leftPanWidthInput.onchange = updateLeftWidth;
+    updateLeftWidth();
+
+    /** @type {HTMLElement} */
+    this.currentGODomelement = document.createElement('div');
+    this.currentGODomelement.setAttribute('id', 'current_game_object_3d');
+    this.leftPan.appendChild(this.currentGODomelement);
+
+    /** @type {HTMLElement} */
+    this.toolsDomElement = document.createElement('div');
+    this.toolsDomElement.setAttribute('id', 'editor_tools');
+    this.leftPan.appendChild(this.toolsDomElement);
+
+    const possibleIdRenderData = [];
+    for (const id in assetManager.renderData) possibleIdRenderData.push(id);
+    const possibleIdSounds = [];
+    for (const id in assetManager.sounds) possibleIdSounds.push(id);
+    const possibleIdGameScripts = [];
+    for (const id in this.gameScriptInputs)
+      possibleIdGameScripts.push(this.gameScriptInputs[id].ID_EDIT_SCRIPT);
+    /** @type {GameObject3DInput} */
+    this.gameObjectInput = new GameObject3DInput(
+      possibleIdRenderData,
+      possibleIdSounds,
+      possibleIdGameScripts
+    );
+    this.gameObjectInput.setAttribute('id', 'select_game_object_3d');
+    this.leftPan.appendChild(this.gameObjectInput);
+
+    /** @type {OrbitControls} */
+    this.orbitControls = new OrbitControls(
+      this.frame3D.camera,
+      this.frame3D.domElementWebGL
+    );
+
+    /** @type {TransformControls} */
+    this.transformControls = new TransformControls(
+      this.frame3D.camera,
+      this.frame3D.domElementWebGL
+    );
+    {
+      this.frame3D.scene.add(this.transformControls);
+
+      this.transformControls.addEventListener('dragging-changed', (event) => {
+        this.orbitControls.enabled = !event.value;
+      });
+
+      this.transformControls.addEventListener('change', () => {
+        this.gameObjectInput.updateTransform();
+      });
+      this.transformControls.addEventListener('mouseUp', () => {
+        if (!this.shapeContext.pointMesh) {
+          // editing go transform
+          this.updateCollider();
+          this.updateBox3();
+        } else {
+          if (
+            this.shapeContext.pointMesh.userData.shapeJSON.type ==
+            ColliderComponent.SHAPE_TYPE.POLYGON
+          ) {
+            // editing point shape in model
+            this.shapeContext.pointMesh.userData.shapeJSON.points[
+              this.shapeContext.pointMesh.userData.index
+            ] = {
+              x: this.shapeContext.pointMesh.position.x,
+              y: this.shapeContext.pointMesh.position.y,
+              z: this.shapeContext.pointMesh.position.z,
+            };
+            const indexPointSelected =
+              this.shapeContext.pointMesh.userData.index;
+            this.updateShapeSelected();
+            this.selectPointMesh(
+              this.pointsParent.children[indexPointSelected]
+            );
+          } else {
+            // circle center edited
+            this.shapeContext.pointMesh.userData.shapeJSON.center = {
+              x: this.shapeContext.pointMesh.position.x,
+              y: this.shapeContext.pointMesh.position.y,
+              z: this.shapeContext.pointMesh.position.z,
+            };
+            this.updateShapeSelected();
+          }
+        }
+      });
+    }
+
+    // gizmo mode ui
+    {
+      const addButtonMode = (mode) => {
+        const buttonMode = document.createElement('button');
+        buttonMode.innerText = mode;
+        this.toolsDomElement.appendChild(buttonMode);
+
+        buttonMode.onclick = () => {
+          this.transformControls.setMode(mode);
+        };
+      };
+      addButtonMode('translate');
+      addButtonMode('rotate');
+      addButtonMode('scale');
+    }
+
+    /** @type {HTMLElement} */
+    this.buttonTargetGameObject3D = document.createElement('button');
+    this.buttonTargetGameObject3D.innerText = 'Target';
+    this.toolsDomElement.appendChild(this.buttonTargetGameObject3D);
+
+    /** @type {RequestAnimationFrameProcess} */
+    this.process = new RequestAnimationFrameProcess(20);
+    this.process.start((dt) => {
+      this.transformControls.updateMatrixWorld();
+      this.frame3D.render();
+      if (this.scriptInput) this.scriptInput.tick(dt);
+    });
+
+    /** @type {GameObject3D|null} */
+    this.currentGameObject3D = null;
+
+    /** @type {Box3} */
+    this.currentGameObjectMeshBox3 = new Mesh(
+      new BoxGeometry(),
+      new MeshBasicMaterial({ color: 'black', wireframe: true })
+    );
+    {
+      this.currentGameObjectMeshBox3.name = 'currentGameObjectMeshBox3';
+      this.frame3D.scene.add(this.currentGameObjectMeshBox3);
+      this.gameObjectInput.addEventListener(
+        GameObject3DInput.EVENT.TRANSFORM_CHANGED,
+        () => {
+          this.updateBox3();
+          this.updateCollider();
+        }
+      );
+    }
+
+    // camera move
+    {
+      const selectCameraPOV = document.createElement('select');
+      this.toolsDomElement.appendChild(selectCameraPOV);
+
+      const buffer = new Map();
+      const addOption = (label, callback) => {
+        const option = document.createElement('option');
+        option.innerText = label;
+        option.value = label;
+        buffer.set(label, callback);
+        selectCameraPOV.appendChild(option);
+      };
+
+      selectCameraPOV.oninput = () => {
+        const radius = this.frame3D.camera.position.distanceTo(
+          this.orbitControls.target
+        );
+        this.frame3D.camera.position.copy(this.orbitControls.target);
+        buffer.get(selectCameraPOV.selectedOptions[0].value)(radius);
+        this.frame3D.camera.updateMatrixWorld();
+        this.orbitControls.update();
+      };
+
+      addOption('+X', (radius) => {
+        this.frame3D.camera.position.x += radius;
+      });
+      addOption('-X', (radius) => {
+        this.frame3D.camera.position.x -= radius;
+      });
+      addOption('+Y', (radius) => {
+        this.frame3D.camera.position.y += radius;
+      });
+      addOption('-Y', (radius) => {
+        this.frame3D.camera.position.y -= radius;
+      });
+      addOption('+Z', (radius) => {
+        this.frame3D.camera.position.z += radius;
+      });
+      addOption('-Z', (radius) => {
+        this.frame3D.camera.position.z -= radius;
+      });
+    }
+
+    // collider object3d
+    /** @type {Object3D} */
+    this.colliderParent = new Object3D();
+    this.colliderParent.name = 'colliderParent';
+    this.frame3D.scene.add(this.colliderParent);
+
+    this.gameObjectInput.addEventListener(
+      GameObject3DInput.EVENT.SHAPE_ADDED,
+      (event) => {
+        this.updateCollider();
+        this.selectShape(event.detail.shapeIndexCreated);
+      }
+    );
+
+    /** @type {object} */
+    this.shapeContext = {
+      mesh: null,
+      deleteButton: null,
+      radiusUI: null,
+      pointMesh: null,
+    };
+    this.pointsParent = new Object3D();
+    {
+      this.frame3D.scene.add(this.pointsParent);
+
+      const raycaster = new Raycaster();
+      window.addEventListener('keydown', (event) => {
+        if (event.key == 'Escape') this.selectShape(-1);
+        else if (event.key == 'Delete' && this.shapeContext.pointMesh) {
+          if (
+            this.shapeContext.pointMesh.userData.shapeJSON.points.length < 5
+          ) {
+            alert('a polygon must have at least 4 points');
+          } else {
+            // delete this point
+            this.shapeContext.pointMesh.userData.shapeJSON.points.splice(
+              this.shapeContext.pointMesh.userData.index,
+              1
+            );
+            this.updateShapeSelected();
+          }
+        }
+      });
+      this.frame3D.domElementWebGL.addEventListener('click', (event) => {
+        const mouse = new Vector2(
+          (event.clientX / this.frame3D.domElementWebGL.clientWidth) * 2 - 1,
+          -(event.clientY / this.frame3D.domElementWebGL.clientHeight) * 2 + 1
+        );
+        raycaster.setFromCamera(mouse, this.frame3D.camera);
+
+        if (!this.shapeContext.mesh) {
+          // look for intersect with shape mesh
+          const intersects = raycaster.intersectObject(
+            this.colliderParent,
+            true
+          );
+          if (intersects.length) {
+            const index = this.colliderParent.children.indexOf(
+              intersects[0].object
+            );
+            this.selectShape(index);
+          }
+        } else {
+          if (event.ctrlKey) {
+            // add a point
+            const intersects = raycaster.intersectObject(
+              this.gameObjectInput.gameObject3D,
+              true
+            );
+            if (intersects.length) {
+              const point = intersects[0].point;
+              const index = this.colliderParent.children.indexOf(
+                this.shapeContext.mesh
+              );
+              const colliderComp =
+                this.gameObjectInput.gameObject3D.getComponent(
+                  ColliderComponent.TYPE
+                );
+              // in gameobject referential
+              const invMatrixWorld = this.pointsParent.matrixWorld
+                .clone()
+                .invert();
+              point.applyMatrix4(invMatrixWorld);
+
+              colliderComp.model.shapesJSON[index].points.push({
+                x: point.x,
+                y: point.y,
+                z: point.z,
+              });
+              this.updateShapeSelected();
+            }
+          } else {
+            // look for intersect with points shape
+            const intersects = raycaster.intersectObject(
+              this.pointsParent,
+              true
+            );
+            if (intersects.length) {
+              this.selectPointMesh(intersects[0].object);
+            }
+          }
+        }
+      });
+    }
+
+    const createScriptInputOf = (idEditScript) => {
+      const variables = this.gameObjectInput.gameObject3D.getComponent(
+        GameScriptComponent.TYPE
+      ).model.variables;
+      for (const id in this.gameScriptInputs) {
+        if (this.gameScriptInputs[id].ID_EDIT_SCRIPT == idEditScript) {
+          return new this.gameScriptInputs[id](
+            this,
+            variables,
+            this.gameObjectInput.gameScriptInputDomElement
+          );
+        }
+      }
+    };
+
+    // game script edition
+    this.gameObjectInput.addEventListener(
+      GameObject3DInput.EVENT.GAME_SCRIPT_EDIT,
+      (event) => {
+        if (this.scriptInput) this.scriptInput.dispose();
+        this.scriptInput = createScriptInputOf(event.detail.id);
+        this.scriptInput.init();
+      }
+    );
+
+    this.gameObjectInput.addEventListener(
+      GameObject3DInput.EVENT.GAME_SCRIPT_DELETED,
+      (event) => {
+        if (
+          this.scriptInput &&
+          this.scriptInput.ID_EDIT_SCRIPT == event.detail.id
+        )
+          this.scriptInput.dispose();
+      }
+    );
+  }
+
+  /**
+   *
+   * @param {object} gameObject3DJSON - json of the gameobject to set
+   */
+  setCurrentGameObject3DJSON(gameObject3DJSON) {
+    const gameObject3D = new GameObject3D(objectParseNumeric(gameObject3DJSON));
+
+    console.log('editor open ', gameObject3D);
+    if (this.currentGameObject3D) {
+      this.currentGameObject3D.removeFromParent();
+    }
+
+    this.currentGameObject3D = gameObject3D;
+
+    // init render gameobject3d
+    this.currentGameObject3D.traverse((child) => {
+      if (!child.isGameObject3D) return;
+      child.matrixAutoUpdate = true; // disable .static optimization
+
+      const renderComp = child.getComponent(RenderComponent.TYPE);
+      if (renderComp) {
+        renderComp.initController(
+          new RenderController(renderComp.getModel(), child, this.assetManager)
+        );
+      }
+    });
+
+    this.frame3D.scene.add(this.currentGameObject3D);
+
+    const createGameObject3DUI = (go, indent = 0) => {
+      if (!go.isGameObject3D) return null;
+
+      let result = null;
+
+      const hasChildrenGameObject =
+        go.children.filter((el) => el.isGameObject3D).length != 0;
+      const _this = this;
+
+      if (hasChildrenGameObject) {
+        result = document.createElement('details');
+        const summary = document.createElement('summary');
+        summary.classList.add('editor_clickable');
+        summary.innerText = go.name;
+        this.gameObjectInput.addEventListener(
+          GameObject3DInput.EVENT.NAME_CHANGED,
+          () => {
+            summary.innerText = go.name;
+          }
+        );
+
+        result.style.marginLeft = 10 * indent + 'px';
+        result.appendChild(summary);
+
+        summary.onclick = function (event) {
+          if (this == event.target) {
+            _this.selectGameObject3D(go);
+          }
+        };
+
+        indent++;
+        go.children.forEach((child) => {
+          const childResult = createGameObject3DUI(child, indent);
+          if (!childResult) return;
+          result.appendChild(childResult);
+        });
+      } else {
+        result = document.createElement('div');
+        result.classList.add('editor_clickable');
+        result.innerText = go.name;
+        this.gameObjectInput.addEventListener(
+          GameObject3DInput.EVENT.NAME_CHANGED,
+          () => {
+            result.innerText = go.name;
+          }
+        );
+
+        result.style.marginLeft = 20 * indent + 'px';
+        result.onclick = function (event) {
+          if (this == event.target) {
+            _this.selectGameObject3D(go);
+          }
+        };
+      }
+
+      return result;
+    };
+
+    while (this.currentGODomelement.firstChild) {
+      this.currentGODomelement.firstChild.remove();
+    }
+    this.currentGODomelement.appendChild(
+      createGameObject3DUI(this.currentGameObject3D)
+    );
+
+    this.selectGameObject3D(this.currentGameObject3D);
+
+    // move camera to fit the scene
+    const bb = Editor.computeBox3GameObject3D(this.currentGameObject3D);
+    const center = new Vector3();
+    bb.getCenter(center);
+    cameraFitRectangle(
+      this.frame3D.camera,
+      bb.min,
+      bb.max,
+      this.currentGameObject3D.position.z
+    );
+    this.setOrbitControlsTargetTo(this.currentGameObject3D);
+  }
+
+  /**
+   *
+   * @param {Object3D} obj - object 3d to target
+   */
+  setOrbitControlsTargetTo(obj) {
+    const bb = Editor.computeBox3GameObject3D(obj);
+    const center = new Vector3();
+    bb.getCenter(center);
+    this.orbitControls.target.copy(center);
+    this.orbitControls.update();
+  }
+
+  /**
+   *
+   * @param {GameObject3D} go - game object 3d to select for edition
+   */
+  selectGameObject3D(go) {
+    if (go == this.gameObjectInput.gameObject3D) return;
+
+    if (this.scriptInput) this.scriptInput.dispose();
+
+    // game input dom element
+    this.gameObjectInput.setGameObject3D(go);
+    // bind
+    this.buttonTargetGameObject3D.onclick = this.setOrbitControlsTargetTo.bind(
+      this,
+      go
+    );
+    this.transformControls.attach(go);
+    this.updateBox3();
+    this.updateCollider();
+  }
+
+  /**
+   * Update collider shapes in the 3D scene
+   */
+  updateCollider() {
+    for (let i = this.colliderParent.children.length - 1; i >= 0; i--) {
+      this.colliderParent.children[i].removeFromParent();
+    }
+    this.selectShape(-1);
+
+    /** @type {GameObject3D} */
+    const go = this.gameObjectInput.gameObject3D;
+
+    const colliderComp = go.getComponent(ColliderComponent.TYPE);
+    if (colliderComp) {
+      const worldPosition = new Vector3();
+      const worldQuaternion = new Quaternion();
+      const worldScale = new Vector3();
+
+      go.matrixWorld.decompose(worldPosition, worldQuaternion, worldScale);
+
+      this.colliderParent.position.copy(worldPosition);
+      this.colliderParent.quaternion.copy(worldQuaternion);
+      this.colliderParent.scale.copy(worldScale);
+
+      colliderComp.model.shapesJSON.forEach((shape) => {
+        let geometry = null;
+
+        switch (shape.type) {
+          case ColliderComponent.SHAPE_TYPE.CIRCLE:
+            geometry = new CircleGeometry(shape.radius, 32);
+            geometry.translate(shape.center.x, shape.center.y, shape.center.z);
+            break;
+          case ColliderComponent.SHAPE_TYPE.POLYGON:
+            geometry = new ConvexGeometry(
+              shape.points.map((el) => new Vector3(el.x, el.y, el.z))
+            );
+            break;
+          default:
+            throw new Error('unknown shape type');
+        }
+        this.colliderParent.add(new Mesh(geometry, COLLIDER_MATERIAL));
+      });
+    }
+  }
+
+  /**
+   *
+   * @param {number} shapeIndex - index of the shape to select
+   */
+  selectShape(shapeIndex) {
+    // reset state
+    if (this.shapeContext.mesh) {
+      this.shapeContext.mesh.material = COLLIDER_MATERIAL;
+      this.shapeContext.mesh = null;
+    }
+    if (this.shapeContext.deleteButton) {
+      this.shapeContext.deleteButton.remove();
+      this.shapeContext.deleteButton = null;
+    }
+    if (this.shapeContext.radiusUI) {
+      this.shapeContext.radiusUI.parent.remove();
+      this.shapeContext.radiusUI = null;
+    }
+    this.shapeContext.pointMesh = null;
+
+    for (let i = this.pointsParent.children.length - 1; i >= 0; i--) {
+      this.pointsParent.children[i].removeFromParent();
+    }
+
+    // assign new index
+    this.shapeContext.mesh = this.colliderParent.children[shapeIndex];
+
+    // set new stateContext state
+    if (this.shapeContext.mesh) {
+      this.setOrbitControlsTargetTo(this.shapeContext.mesh);
+      this.shapeContext.mesh.material = COLLIDER_MATERIAL_SELECTED;
+
+      const colliderComp = this.gameObjectInput.gameObject3D.getComponent(
+        ColliderComponent.TYPE
+      );
+
+      this.shapeContext.deleteButton = document.createElement('button');
+      this.shapeContext.deleteButton.innerText = 'delete shape';
+      this.shapeContext.deleteButton.onclick = () => {
+        colliderComp.model.shapesJSON.splice(shapeIndex, 1);
+        this.updateCollider();
+      };
+
+      if (
+        colliderComp.model.shapesJSON[shapeIndex].type ==
+        ColliderComponent.SHAPE_TYPE.CIRCLE
+      ) {
+        // add ui to set radius
+        this.shapeContext.radiusUI = createLabelInput('Radius ', 'number');
+        this.gameObjectInput.detailsCollider.appendChild(
+          this.shapeContext.radiusUI.parent
+        );
+
+        // init
+        this.shapeContext.radiusUI.input.value =
+          colliderComp.model.shapesJSON[shapeIndex].radius;
+
+        this.shapeContext.radiusUI.input.onchange = () => {
+          const newRadius = this.shapeContext.radiusUI.input.valueAsNumber;
+          if (newRadius < 0.01) {
+            alert('radius must superior at 0.01');
+          } else {
+            colliderComp.model.shapesJSON[shapeIndex].radius = newRadius;
+            this.updateShapeSelected();
+          }
+        };
+      }
+
+      this.gameObjectInput.detailsCollider.appendChild(
+        this.shapeContext.deleteButton
+      );
+
+      this.updateShapeSelected(false); // no need to rebuild shape geometry
+    } else {
+      this.transformControls.attach(this.gameObjectInput.gameObject3D);
+    }
+  }
+
+  /**
+   *
+   * @param {Mesh} mesh - point mesh to select with transform controls
+   */
+  selectPointMesh(mesh) {
+    this.shapeContext.pointMesh = mesh;
+    this.transformControls.attach(this.shapeContext.pointMesh);
+  }
+
+  /**
+   * Update shape selected (when a property of the shape has changed)
+   *
+   * @param {boolean} rebuildShapeGeometry - shape selected needs to rebuild its geometry
+   */
+  updateShapeSelected(rebuildShapeGeometry = true) {
+    // remove all old point meshes
+    for (let i = this.pointsParent.children.length - 1; i >= 0; i--) {
+      this.pointsParent.children[i].removeFromParent();
+    }
+
+    // reset transform controls
+    this.transformControls.detach();
+
+    // unreference this.shapeContext.pointMesh
+    this.shapeContext.pointMesh = null;
+
+    // transform pointParent in referential of the current gameobject3D
+    const worldPosition = new Vector3();
+    const worldQuaternion = new Quaternion();
+    const worldScale = new Vector3();
+    this.gameObjectInput.gameObject3D.matrixWorld.decompose(
+      worldPosition,
+      worldQuaternion,
+      worldScale
+    );
+
+    this.pointsParent.position.copy(worldPosition);
+    this.pointsParent.quaternion.copy(worldQuaternion);
+    this.pointsParent.scale.copy(worldScale);
+
+    // retrieve shapeJSON
+    const colliderComp = this.gameObjectInput.gameObject3D.getComponent(
+      ColliderComponent.TYPE
+    );
+    const index = this.colliderParent.children.indexOf(this.shapeContext.mesh);
+    const shapeJSON = colliderComp.model.shapesJSON[index];
+
+    // rebuild shape + point mesh
+    if (shapeJSON.type == ColliderComponent.SHAPE_TYPE.POLYGON) {
+      if (rebuildShapeGeometry) {
+        this.shapeContext.mesh.geometry = new ConvexGeometry(
+          shapeJSON.points.map((el) => new Vector3(el.x, el.y, el.z))
+        );
+        console.trace('shape rebuilded with ', shapeJSON);
+      }
+
+      shapeJSON.points.forEach((point, index) => {
+        const pointMesh = new Mesh(
+          new SphereGeometry(0.6),
+          COLLIDER_POINT_MATERIAL
+        );
+        pointMesh.position.set(point.x, point.y, point.z);
+        pointMesh.userData.index = index;
+        pointMesh.userData.shapeJSON = shapeJSON; // userdata used at the end of transform controls
+        this.pointsParent.add(pointMesh);
+      });
+    } else if (shapeJSON.type == ColliderComponent.SHAPE_TYPE.CIRCLE) {
+      if (rebuildShapeGeometry) {
+        this.shapeContext.mesh.geometry = new CircleGeometry(
+          shapeJSON.radius,
+          32
+        );
+        this.shapeContext.mesh.geometry.translate(
+          shapeJSON.center.x,
+          shapeJSON.center.y,
+          shapeJSON.center.z
+        );
+        console.trace('shape rebuilded with ', shapeJSON);
+      }
+      const pointMesh = new Mesh(
+        new SphereGeometry(0.6),
+        COLLIDER_POINT_MATERIAL
+      );
+      pointMesh.position.set(
+        shapeJSON.center.x,
+        shapeJSON.center.y,
+        shapeJSON.center.z
+      );
+      pointMesh.userData.shapeJSON = shapeJSON; // userdata used at the end of transform controls
+      this.pointsParent.add(pointMesh);
+      this.selectPointMesh(pointMesh);
+    }
+  }
+
+  /**
+   * Update box3 wrapping selected game object 3d
+   */
+  updateBox3() {
+    this.gameObjectInput.gameObject3D.updateMatrixWorld();
+    const worldQuaternion = new Quaternion();
+    this.gameObjectInput.gameObject3D.matrixWorld.decompose(
+      new Vector3(),
+      worldQuaternion,
+      new Vector3()
+    );
+
+    const inverseWorldQuaternion = worldQuaternion.clone().invert();
+
+    // cancel quaternion
+    this.gameObjectInput.gameObject3D.quaternion.multiply(
+      inverseWorldQuaternion
+    );
+
+    const bbScale = Editor.computeBox3GameObject3D(
+      this.gameObjectInput.gameObject3D
+    );
+    this.currentGameObjectMeshBox3.scale.copy(
+      bbScale.max.clone().sub(bbScale.min)
+    );
+
+    // restore quaternion
+    this.gameObjectInput.gameObject3D.quaternion.multiply(worldQuaternion);
+
+    const bbPosition = Editor.computeBox3GameObject3D(
+      this.gameObjectInput.gameObject3D
+    );
+    bbPosition.getCenter(this.currentGameObjectMeshBox3.position);
+
+    this.currentGameObjectMeshBox3.quaternion.copy(worldQuaternion);
+    this.currentGameObjectMeshBox3.updateMatrixWorld();
+  }
+
+  /**
+   *
+   * @param {Object3D} obj - object 3d to compute box3
+   * @returns {Box3} - box 3 of the object 3d
+   */
+  static computeBox3GameObject3D(obj) {
+    const bb = new Box3().setFromObject(obj);
+
+    // avoid bug if no renderdata on this gameobject
+    const checkIfCoordInfinite = function (value) {
+      return value === Infinity || value === -Infinity;
+    };
+    const checkIfVectorHasCoordInfinite = function (vector) {
+      return (
+        checkIfCoordInfinite(vector.x) ||
+        checkIfCoordInfinite(vector.y) ||
+        checkIfCoordInfinite(vector.z)
+      );
+    };
+
+    if (
+      checkIfVectorHasCoordInfinite(bb.min) ||
+      checkIfVectorHasCoordInfinite(bb.max)
+    ) {
+      // cube 1,1,1
+      bb.min.set(-0.5, -0.5, -0.5);
+      bb.max.set(0.5, 0.5, 0.5);
+
+      bb.applyMatrix4(obj.matrixWorld);
+    }
+    return bb;
+  }
+}
+
+class GameObject3DInput extends HTMLElement {
+  /**
+   *
+   * @param {Array<string>} idRenderDatas - possible id render datas to set in RenderComponent of the current game object 3d
+   * @param {Array<string>} idSounds - possible id sound to set in AudioComponent of the current game object 3d
+   * @param {Array<string>} idGameScripts - possible id game script to set in GameScriptComponent of the current game object 3d
+   */
+  constructor(idRenderDatas, idSounds, idGameScripts) {
+    super();
+
+    /** @type {Array} */
+    this.idRenderDatas = idRenderDatas;
+
+    /** @type {Array} */
+    this.idSounds = idSounds;
+
+    /** @type {Array} */
+    this.idGameScripts = idGameScripts;
+
+    /** @type {GameObject3D|null} */
+    this.gameObject3D = null;
+
+    // Name
+    this.nameLabelInput = createLabelInput('Name: ', 'text');
+    this.appendChild(this.nameLabelInput.parent);
+
+    this.nameLabelInput.input.onchange = () => {
+      this.gameObject3D.name = this.nameLabelInput.input.value;
+      this.dispatchEvent(new CustomEvent(GameObject3DInput.EVENT.NAME_CHANGED));
+    };
+
+    // Static
+    this.static = createLabelInput('Static: ', 'checkbox');
+    this.appendChild(this.static.parent);
+
+    this.static.input.onchange = () => {
+      this.gameObject3D.static = this.static.input.checked;
+    };
+
+    // transform
+    const detailsTransform = document.createElement('details');
+    this.appendChild(detailsTransform);
+    const summaryTransform = document.createElement('summary');
+    summaryTransform.innerText = 'Transform';
+    detailsTransform.appendChild(summaryTransform);
+
+    this.position = new Vector3Input('Position', 0.1);
+    detailsTransform.appendChild(this.position);
+
+    this.rotation = new Vector3Input('Rotation', 0.01);
+    detailsTransform.appendChild(this.rotation);
+
+    this.scale = new Vector3Input('Scale', 0.1);
+    detailsTransform.appendChild(this.scale);
+
+    this.position.addEventListener('change', () => {
+      this.gameObject3D.position.set(
+        this.position.x.input.valueAsNumber,
+        this.position.y.input.valueAsNumber,
+        this.position.z.input.valueAsNumber
+      );
+    });
+    this.rotation.addEventListener('change', () => {
+      this.gameObject3D.rotation.set(
+        this.rotation.x.input.valueAsNumber,
+        this.rotation.y.input.valueAsNumber,
+        this.rotation.z.input.valueAsNumber
+      );
+    });
+    this.scale.addEventListener('change', () => {
+      this.gameObject3D.scale.set(
+        this.scale.x.input.valueAsNumber,
+        this.scale.y.input.valueAsNumber,
+        this.scale.z.input.valueAsNumber
+      );
+    });
+
+    // collider
+    this.detailsCollider = document.createElement('details');
+    this.appendChild(this.detailsCollider);
+
+    // render
+    this.detailsRender = document.createElement('details');
+    this.appendChild(this.detailsRender);
+
+    // audio
+    this.detailsAudio = document.createElement('details');
+    this.appendChild(this.detailsAudio);
+
+    // game script
+    this.detailsGameScript = document.createElement('details');
+    this.appendChild(this.detailsGameScript);
+  }
+
+  /**
+   *
+   * @param {GameObject3D} go - go to select in the game object 3d input
+   */
+  setGameObject3D(go) {
+    this.gameObject3D = go;
+
+    this.nameLabelInput.input.value = go.name;
+
+    this.static.input.checked = go.static;
+
+    // transform
+    this.updateTransform();
+
+    // collider
+    this.updateCollider();
+
+    // render
+    this.updateRender();
+
+    // audio
+    this.updateAudio();
+
+    // game script
+    this.updateGameScript();
+  }
+
+  /**
+   * Update GameScript component edition of the current game object 3d
+   */
+  updateGameScript() {
+    const gameScriptComp = this.gameObject3D.getComponent(
+      GameScriptComponent.TYPE
+    );
+    this.detailsGameScript.hidden = !gameScriptComp;
+    if (gameScriptComp) {
+      while (this.detailsGameScript.firstChild)
+        this.detailsGameScript.firstChild.remove();
+
+      // rebuild domelement
+      const summaryAudio = document.createElement('summary');
+      summaryAudio.innerText = 'GameScript';
+      this.detailsGameScript.appendChild(summaryAudio);
+
+      const listScript = document.createElement('ul');
+      this.detailsGameScript.appendChild(listScript);
+
+      const updateList = () => {
+        while (listScript.firstChild) listScript.firstChild.remove();
+
+        gameScriptComp.model.scriptParams.forEach((param, index) => {
+          const li = document.createElement('li');
+          li.innerText = param.id;
+          listScript.appendChild(li);
+
+          if (this.idGameScripts.includes(param.id)) {
+            // call editor script input
+            const editButton = document.createElement('button');
+            editButton.innerText = 'Edit';
+            li.appendChild(editButton);
+            editButton.onclick = () => {
+              this.dispatchEvent(
+                new CustomEvent(GameObject3DInput.EVENT.GAME_SCRIPT_EDIT, {
+                  detail: { id: param.id },
+                })
+              );
+            };
+          }
+
+          const deleteButton = document.createElement('button');
+          deleteButton.innerText = 'delete';
+          li.appendChild(deleteButton);
+
+          deleteButton.onclick = () => {
+            gameScriptComp.model.scriptParams.splice(index, 1);
+            this.dispatchEvent(
+              new CustomEvent(GameObject3DInput.EVENT.GAME_SCRIPT_DELETED, {
+                detail: { id: param.id },
+              })
+            );
+            updateList();
+          };
+
+          const priority = createLabelInput('Priorité: ', 'number');
+          li.appendChild(priority.parent);
+          priority.input.value = !isNaN(param.priority) ? param.priority : 0;
+          priority.input.onchange = () => {
+            const newPriority = Math.round(priority.input.value);
+            if (isNaN(newPriority)) return;
+            priority.input.value = newPriority; // the rounded one
+            param.priority = newPriority;
+          };
+        });
+      };
+      updateList();
+
+      const selectIdScript = document.createElement('select');
+      this.detailsGameScript.appendChild(selectIdScript);
+
+      this.idGameScripts.forEach((id) => {
+        const option = document.createElement('option');
+        option.innerText = id;
+        option.value = id;
+        selectIdScript.appendChild(option);
+      });
+
+      const addScriptButton = document.createElement('button');
+      addScriptButton.innerText = 'Add game script';
+      this.detailsGameScript.appendChild(addScriptButton);
+
+      addScriptButton.onclick = () => {
+        const idToAdd = selectIdScript.selectedOptions[0].value;
+        const alreadyThere =
+          gameScriptComp.model.scriptParams.filter(
+            (param) => param.id == idToAdd
+          ).length != 0;
+        if (!alreadyThere) {
+          gameScriptComp.model.scriptParams.push({ id: idToAdd, priority: 0 });
+          updateList();
+        }
+      };
+
+      this.gameScriptInputDomElement = document.createElement('div');
+      this.detailsGameScript.appendChild(this.gameScriptInputDomElement);
+    }
+  }
+
+  /**
+   * Update Audio component edition of the current game object 3d
+   */
+  updateAudio() {
+    const audioComp = this.gameObject3D.getComponent(AudioComponent.TYPE);
+    this.detailsAudio.hidden = !audioComp;
+    if (audioComp) {
+      while (this.detailsAudio.firstChild)
+        this.detailsAudio.firstChild.remove();
+
+      // rebuild domelement
+      const summaryAudio = document.createElement('summary');
+      summaryAudio.innerText = 'Audio';
+      this.detailsAudio.appendChild(summaryAudio);
+
+      // sounds
+      const listIdSounds = document.createElement('ul');
+      this.detailsAudio.appendChild(listIdSounds);
+
+      const updateList = () => {
+        while (listIdSounds.firstChild) listIdSounds.firstChild.remove();
+
+        audioComp.model.soundsJSON.forEach((idSound) => {
+          const li = document.createElement('li');
+          li.innerText = idSound;
+          listIdSounds.appendChild(li);
+
+          const deleteButton = document.createElement('button');
+          deleteButton.innerText = 'delete';
+          li.appendChild(deleteButton);
+
+          deleteButton.onclick = () => {
+            removeFromArray(audioComp.model.soundsJSON, idSound);
+            updateList();
+          };
+        });
+      };
+      updateList();
+
+      // sounds
+      const selectIdSound = document.createElement('select');
+      this.detailsAudio.appendChild(selectIdSound);
+      this.idSounds.forEach((idSound) => {
+        const option = document.createElement('option');
+        option.value = idSound;
+        option.innerText = idSound;
+        selectIdSound.appendChild(option);
+      });
+
+      const addIdSound = document.createElement('button');
+      this.detailsAudio.appendChild(addIdSound);
+      addIdSound.innerText = 'Add sound';
+      addIdSound.onclick = () => {
+        if (
+          arrayPushOnce(
+            audioComp.model.soundsJSON,
+            selectIdSound.selectedOptions[0].value
+          )
+        ) {
+          // has been added
+          updateList();
+        }
+      };
+
+      // conf audio
+
+      // loop
+      const loop = createLabelInput('Loop: ', 'checkbox');
+      this.detailsAudio.appendChild(loop.parent);
+      loop.input.checked = audioComp.model.conf.loop;
+      loop.input.onchange = () => {
+        audioComp.model.conf.loop = loop.input.checked;
+      };
+
+      // autoplay
+      const autoplay = createLabelInput('Autoplay: ', 'checkbox');
+      this.detailsAudio.appendChild(autoplay.parent);
+      autoplay.input.checked = audioComp.model.conf.autoplay;
+      autoplay.input.onchange = () => {
+        audioComp.model.conf.autoplay = autoplay.input.checked;
+      };
+
+      // spatialized
+      const spatialized = createLabelInput('Spatialized: ', 'checkbox');
+      this.detailsAudio.appendChild(spatialized.parent);
+      spatialized.input.checked = audioComp.model.conf.spatialized;
+      spatialized.input.onchange = () => {
+        audioComp.model.conf.spatialized = spatialized.input.checked;
+      };
+
+      // volume
+      const volume = createLabelInput('Volume: ', 'range');
+      this.detailsAudio.appendChild(volume.parent);
+      volume.input.min = 0;
+      volume.input.max = 1;
+      volume.input.step = 'any';
+      volume.input.value = isNaN(audioComp.model.conf.volume)
+        ? 1
+        : audioComp.model.conf.volume;
+      volume.input.onchange = () => {
+        audioComp.model.conf.volume = volume.input.valueAsNumber;
+      };
+    }
+  }
+
+  /**
+   * Update Render component edition of the current game object 3d
+   */
+  updateRender() {
+    const renderComp = this.gameObject3D.getComponent(RenderComponent.TYPE);
+    this.detailsRender.hidden = !renderComp;
+    if (renderComp) {
+      while (this.detailsRender.firstChild)
+        this.detailsRender.firstChild.remove();
+
+      // rebuild domelement
+      const summaryRender = document.createElement('summary');
+      summaryRender.innerText = 'Render';
+      this.detailsRender.appendChild(summaryRender);
+
+      // color
+      const color = createLabelInput('Couleur: ', 'color');
+      this.detailsRender.appendChild(color.parent);
+      color.input.value =
+        '#' + new Color().fromArray(renderComp.model.color).getHexString();
+
+      // opacity
+      const opacity = createLabelInput('Opacité ', 'range');
+      opacity.input.min = 0;
+      opacity.input.max = 1;
+      opacity.input.step = 'any';
+      this.detailsRender.appendChild(opacity.parent);
+      opacity.input.value = renderComp.model.color[3];
+
+      const updateColor = () => {
+        renderComp.controller.setColor([
+          ...new Color(color.input.value).toArray(),
+          opacity.input.valueAsNumber,
+        ]);
+      };
+
+      opacity.input.onchange = updateColor;
+
+      color.input.onchange = updateColor;
+
+      // id model
+      const selectIdRenderData = document.createElement('select');
+      this.detailsRender.appendChild(selectIdRenderData);
+      this.idRenderDatas.forEach((id) => {
+        const option = document.createElement('option');
+        option.innerText = id;
+        option.value = id;
+        selectIdRenderData.appendChild(option);
+        if (renderComp.model.idRenderData == id) {
+          selectIdRenderData.value = id;
+        }
+      });
+
+      selectIdRenderData.onchange = () => {
+        renderComp.controller.setIdRenderData(
+          selectIdRenderData.selectedOptions[0].value
+        );
+      };
+    }
+  }
+
+  /**
+   * Update Collider component edition of the current game object 3d
+   */
+  updateCollider() {
+    const colliderComp = this.gameObject3D.getComponent(ColliderComponent.TYPE);
+    this.detailsCollider.hidden = !colliderComp;
+    if (colliderComp) {
+      while (this.detailsCollider.firstChild)
+        this.detailsCollider.firstChild.remove();
+
+      // rebuild domelement
+      const summaryCollider = document.createElement('summary');
+      summaryCollider.innerText = 'Collider';
+      this.detailsCollider.appendChild(summaryCollider);
+
+      // edit body attr
+      const bodyCheckbox = createLabelInput('body', 'checkbox');
+      bodyCheckbox.input.checked = colliderComp.model.body;
+      bodyCheckbox.input.onchange = () =>
+        (colliderComp.model.body = bodyCheckbox.input.checked);
+      this.detailsCollider.appendChild(bodyCheckbox.parent);
+
+      // add a polygon
+      const addPolygonButton = document.createElement('button');
+      addPolygonButton.innerText = 'Add Polygon';
+      this.detailsCollider.appendChild(addPolygonButton);
+
+      addPolygonButton.onclick = () => {
+        // add a square
+        colliderComp.model.shapesJSON.push({
+          type: ColliderComponent.SHAPE_TYPE.POLYGON,
+          points: [
+            { x: -5, y: -5, z: 0 },
+            { x: 5, y: -5, z: 0 },
+            { x: 5, y: 5, z: 0 },
+            { x: -5, y: 5, z: 0 },
+          ],
+        });
+        this.dispatchEvent(
+          new CustomEvent(GameObject3DInput.EVENT.SHAPE_ADDED, {
+            detail: {
+              shapeIndexCreated: colliderComp.model.shapesJSON.length - 1,
+            },
+          })
+        );
+      };
+
+      // add a circle
+      const addCircleButton = document.createElement('button');
+      addCircleButton.innerText = 'Add Circle';
+      this.detailsCollider.appendChild(addCircleButton);
+
+      addCircleButton.onclick = () => {
+        // add a circle
+        colliderComp.model.shapesJSON.push({
+          type: ColliderComponent.SHAPE_TYPE.CIRCLE,
+          radius: 2.5,
+          center: { x: 0, y: 0, z: 0 },
+        });
+        this.dispatchEvent(
+          new CustomEvent(GameObject3DInput.EVENT.SHAPE_ADDED, {
+            detail: {
+              shapeIndexCreated: colliderComp.model.shapesJSON.length - 1,
+            },
+          })
+        );
+      };
+
+      const visualization2DImage = document.createElement('img');
+      this.detailsCollider.appendChild(visualization2DImage);
+
+      // visualize collision in 2d
+      const visualizeCollision2DButton = document.createElement('button');
+      visualizeCollision2DButton.innerText = 'Update visualize 2D collision';
+      this.detailsCollider.appendChild(visualizeCollision2DButton);
+      const drawVisualization2D = () => {
+        const scene = new Scene();
+        scene.add(new AmbientLight('white', 0.6));
+
+        // move gameobject to another scene
+        const oldParent = this.gameObject3D.parent;
+        this.gameObject3D.removeFromParent();
+
+        scene.add(this.gameObject3D);
+        this.gameObject3D.updateMatrixWorld();
+
+        // compute bb with collider points
+        const bb = new Box3();
+
+        const colliderComp = this.gameObject3D.getComponent(
+          ColliderComponent.TYPE
+        );
+        colliderComp.model.shapesJSON.forEach((shape) => {
+          if (shape.type == ColliderComponent.SHAPE_TYPE.POLYGON) {
+            shape.points.forEach((point) => {
+              bb.expandByPoint(
+                new Vector3(point.x, point.y, point.z).applyMatrix4(
+                  this.gameObject3D.matrixWorld
+                )
+              );
+            });
+          } else {
+            bb.expandByPoint(
+              new Vector3(
+                shape.center.x + shape.radius,
+                shape.center.y + shape.radius,
+                shape.center.z
+              )
+                .multiply(this.gameObject3D.scale)
+                .add(this.gameObject3D.position)
+            );
+            bb.expandByPoint(
+              new Vector3(
+                shape.center.x - shape.radius,
+                shape.center.y - shape.radius,
+                shape.center.z
+              )
+                .multiply(this.gameObject3D.scale)
+                .add(this.gameObject3D.position)
+            );
+          }
+        });
+
+        const maxDim = Math.max(bb.max.x - bb.min.x, bb.max.y - bb.min.y);
+        const halfSize = maxDim * 0.5;
+        const camera = new OrthographicCamera(
+          -halfSize,
+          halfSize,
+          halfSize,
+          -halfSize,
+          0.001,
+          10000
+        );
+        bb.getCenter(camera.position);
+        camera.position.z = 1000;
+        camera.updateProjectionMatrix();
+        const renderer = new WebGLRenderer({
+          canvas: document.createElement('canvas'),
+          antialias: true,
+          alpha: true,
+        });
+
+        const size = 512;
+
+        renderer.setSize(size, size);
+        renderer.setClearColor(0xffffff, 0);
+        renderer.render(scene, camera);
+
+        // compute offset to translate collisions after
+        const offset = bb
+          .getCenter(new Vector3())
+          .sub(this.gameObject3D.position);
+
+        this.gameObject3D.removeFromParent();
+        oldParent.add(this.gameObject3D);
+
+        // draw image
+        const image = new Image();
+        image.src = renderer.domElement.toDataURL();
+
+        image.onload = async () => {
+          // render collisions
+          const json = this.gameObject3D.toJSON();
+          delete json.children; // remove children (they should be removed also in rendering above ?)
+          delete json.components.GameScript; // remove GameScript only Collider is needed
+          const context = new Context({}, new GameObject3D(json));
+          await context.load();
+
+          const canvas2D = document.createElement('canvas');
+          canvas2D.width = renderer.domElement.width;
+          canvas2D.height = renderer.domElement.height;
+          const ctx = canvas2D.getContext('2d');
+
+          ctx.drawImage(image, 0, 0);
+
+          ctx.save();
+          ctx.translate(
+            (0.5 - offset.x / maxDim) * renderer.domElement.width,
+            (0.5 + offset.y / maxDim) * renderer.domElement.height
+          );
+          ctx.scale(
+            (this.gameObject3D.scale.x * renderer.domElement.width) / maxDim,
+            (this.gameObject3D.scale.y * -renderer.domElement.height) / maxDim
+          );
+          context.collisions.draw(ctx);
+          ctx.fillStyle = 'green';
+          ctx.fill();
+          ctx.restore();
+
+          visualization2DImage.src = canvas2D.toDataURL();
+        };
+      };
+      visualizeCollision2DButton.onclick = drawVisualization2D;
+      drawVisualization2D();
+    }
+  }
+
+  /**
+   * Update Transform component edition of the current game object 3d
+   */
+  updateTransform() {
+    this.position.x.input.value = this.gameObject3D.position.x;
+    this.position.y.input.value = this.gameObject3D.position.y;
+    this.position.z.input.value = this.gameObject3D.position.z;
+    this.rotation.x.input.value = this.gameObject3D.rotation.x;
+    this.rotation.y.input.value = this.gameObject3D.rotation.y;
+    this.rotation.z.input.value = this.gameObject3D.rotation.z;
+    this.scale.x.input.value = this.gameObject3D.scale.x;
+    this.scale.y.input.value = this.gameObject3D.scale.y;
+    this.scale.z.input.value = this.gameObject3D.scale.z;
+  }
+
+  /**
+   *
+   * @returns {object} - EVENT enum of the game object 3d input
+   */
+  static get EVENT() {
+    return {
+      NAME_CHANGED: 'name_changed',
+      SHAPE_ADDED: 'polygon_added',
+      GAME_SCRIPT_EDIT: 'game_script_edit',
+      GAME_SCRIPT_DELETED: 'game_script_deleted',
+    };
+  }
+}
+window.customElements.define('transform-input', GameObject3DInput); // mandatory to extends HTMLElement
+
@ud-viz/game_editor
\ No newline at end of file diff --git a/docs/html/game_editor/Editor_Editor.html b/docs/html/game_editor/Editor_Editor.html new file mode 100644 index 000000000..f547200eb --- /dev/null +++ b/docs/html/game_editor/Editor_Editor.html @@ -0,0 +1,3 @@ +Class: Editor
On this page

Editor

new Editor(frame3D, assetManager, externalScriptInputs, gameScriptInputs, userData)

Parameters:
NameTypeDescription
frame3Dmodule:@ud-viz/frame3d~Planar | module:@ud-viz/frame3d~Base

frame 3d

assetManagerAssetManager

asset manager

externalScriptInputsobject

external script input to edit GameScript

gameScriptInputsobject

game script input to edit GameScript

userDataobject

user data

@ud-viz/game_editor
\ No newline at end of file diff --git a/docs/html/game_editor/GameObject3DInput.html b/docs/html/game_editor/GameObject3DInput.html new file mode 100644 index 000000000..4fc7c6992 --- /dev/null +++ b/docs/html/game_editor/GameObject3DInput.html @@ -0,0 +1,3 @@ +Class: GameObject3DInput
On this page

GameObject3DInput

new GameObject3DInput(idRenderDatas, idSounds, idGameScripts)

Parameters:
NameTypeDescription
idRenderDatasArray.<string>

possible id render datas to set in RenderComponent of the current game object 3d

idSoundsArray.<string>

possible id sound to set in AudioComponent of the current game object 3d

idGameScriptsArray.<string>

possible id game script to set in GameScriptComponent of the current game object 3d

Members

gameObject3D :GameObject3D|null

Type:
  • GameObject3D | null

idGameScripts :Array

Type:
  • Array

idRenderDatas :Array

Type:
  • Array

idSounds :Array

Type:
  • Array

(static) EVENT

Methods

setGameObject3D(go)

Parameters:
NameTypeDescription
goGameObject3D

go to select in the game object 3d input

updateAudio()

Update Audio component edition of the current game object 3d

updateCollider()

Update Collider component edition of the current game object 3d

updateGameScript()

Update GameScript component edition of the current game object 3d

updateRender()

Update Render component edition of the current game object 3d

updateTransform()

Update Transform component edition of the current game object 3d

@ud-viz/game_editor
\ No newline at end of file diff --git a/docs/html/game_editor/ScriptInput_ScriptInput.html b/docs/html/game_editor/ScriptInput_ScriptInput.html new file mode 100644 index 000000000..53576fa15 --- /dev/null +++ b/docs/html/game_editor/ScriptInput_ScriptInput.html @@ -0,0 +1,3 @@ +Class: ScriptInput
On this page

ScriptInput

@ud-viz/game_editor
\ No newline at end of file diff --git a/docs/html/game_editor/custom.css b/docs/html/game_editor/custom.css new file mode 100644 index 000000000..93748b70e --- /dev/null +++ b/docs/html/game_editor/custom.css @@ -0,0 +1,21 @@ +.method-member-container { + overflow: auto; +} + +div.member-item-container { + display: none; +} + +.navbar-left-items { + overflow-y: hidden; + overflow-x: scroll !important; +} + +.navbar-item { + align-self: center; + overflow: initial !important; +} + +.navbar-item a { + text-align: center; +} diff --git a/docs/html/game_editor/data/search.json b/docs/html/game_editor/data/search.json new file mode 100644 index 000000000..eaab94160 --- /dev/null +++ b/docs/html/game_editor/data/search.json @@ -0,0 +1 @@ +{"list":[{"title":"Editor","link":"Editor"},{"title":"GameObject3DInput","link":"GameObject3DInput"},{"title":"ScriptInput","link":"ScriptInput","description":"

private constructor user should not extends it !!

"},{"title":"Changelog","link":"Changelog"},{"title":"Contributing","link":"Contributing"},{"title":"Contributors","link":"Contributors"},{"title":"Definitions","link":"Definitions"},{"title":"Developers","link":"Developers"},{"title":"ReleasePublish","link":"ReleasePublish"},{"title":"Reproducibility","link":"Reproducibility"},{"title":"architecture","link":"architecture"},{"title":"game","link":"game"},{"title":"how_to_import","link":"how_to_import"},{"title":"initial_camera_transform","link":"initial_camera_transform"}]} \ No newline at end of file diff --git a/docs/html/game_editor/fonts/Inconsolata-Regular.ttf b/docs/html/game_editor/fonts/Inconsolata-Regular.ttf new file mode 100644 index 000000000..457d262cf Binary files /dev/null and b/docs/html/game_editor/fonts/Inconsolata-Regular.ttf differ diff --git a/docs/html/game_editor/fonts/OpenSans-Regular.ttf b/docs/html/game_editor/fonts/OpenSans-Regular.ttf new file mode 100644 index 000000000..e21ff5f1e Binary files /dev/null and b/docs/html/game_editor/fonts/OpenSans-Regular.ttf differ diff --git a/docs/html/game_editor/fonts/WorkSans-Bold.ttf b/docs/html/game_editor/fonts/WorkSans-Bold.ttf new file mode 100644 index 000000000..0caaf4d4d Binary files /dev/null and b/docs/html/game_editor/fonts/WorkSans-Bold.ttf differ diff --git a/docs/html/game_editor/index.html b/docs/html/game_editor/index.html new file mode 100644 index 000000000..dfd2ad15c --- /dev/null +++ b/docs/html/game_editor/index.html @@ -0,0 +1,3 @@ +Home
On this page

@ud-viz/game_editor

NPM package version

@ud-viz/game_editor
\ No newline at end of file diff --git a/docs/html/game_editor/scripts/core.js b/docs/html/game_editor/scripts/core.js new file mode 100644 index 000000000..ac5a35c84 --- /dev/null +++ b/docs/html/game_editor/scripts/core.js @@ -0,0 +1,662 @@ +/* global document */ +var accordionLocalStorageKey = 'accordion-id'; +var themeLocalStorageKey = 'theme'; +var fontSizeLocalStorageKey = 'font-size'; +var html = document.querySelector('html'); + +var MAX_FONT_SIZE = 30; +var MIN_FONT_SIZE = 10; + +// eslint-disable-next-line no-undef +var localStorage = window.localStorage; + +function getTheme() { + var body = document.body; + + return body.getAttribute('data-theme'); +} + +function updateTheme(theme) { + var body = document.body; + var svgUse = document.querySelectorAll('.theme-svg-use'); + var iconID = theme === 'dark' ? '#light-theme-icon' : '#dark-theme-icon'; + + body.setAttribute('data-theme', theme); + body.classList.remove('dark', 'light'); + body.classList.add(theme); + + svgUse.forEach(function (svg) { + svg.setAttribute('xlink:href', iconID); + }); + + localStorage.setItem(themeLocalStorageKey, theme); +} + +function toggleTheme() { + var body = document.body; + var theme = body.getAttribute('data-theme'); + + var newTheme = theme === 'dark' ? 'light' : 'dark'; + + updateTheme(newTheme); +} + +(function () { + var theme = getTheme(); + + var themeStoredInLocalStorage = localStorage.getItem(themeLocalStorageKey); + + if (themeStoredInLocalStorage) { + if (theme === themeStoredInLocalStorage) { + return; + } + + updateTheme(themeStoredInLocalStorage); + } else { + localStorage.setItem(themeLocalStorageKey, theme); + } +})(); + +/** + * Function to set accordion id to localStorage. + * @param {string} id Accordion id + */ +function setAccordionIdToLocalStorage(id) { + /** + * @type {object} + */ + var ids = JSON.parse(localStorage.getItem(accordionLocalStorageKey)); + + ids[id] = id; + localStorage.setItem(accordionLocalStorageKey, JSON.stringify(ids)); +} + +/** + * Function to remove accordion id from localStorage. + * @param {string} id Accordion id + */ +function removeAccordionIdFromLocalStorage(id) { + /** + * @type {object} + */ + var ids = JSON.parse(localStorage.getItem(accordionLocalStorageKey)); + + delete ids[id]; + localStorage.setItem(accordionLocalStorageKey, JSON.stringify(ids)); +} + +/** + * Function to get all accordion ids from localStorage. + * + * @returns {object} + */ +function getAccordionIdsFromLocalStorage() { + /** + * @type {object} + */ + var ids = JSON.parse(localStorage.getItem(accordionLocalStorageKey)); + + return ids || {}; +} + +function toggleAccordion(element) { + var currentNode = element; + var isCollapsed = currentNode.getAttribute('data-isopen') === 'false'; + + if (isCollapsed) { + currentNode.setAttribute('data-isopen', 'true'); + setAccordionIdToLocalStorage(currentNode.id); + } else { + currentNode.setAttribute('data-isopen', 'false'); + removeAccordionIdFromLocalStorage(currentNode.id); + } +} + +function initAccordion() { + if ( + localStorage.getItem(accordionLocalStorageKey) === undefined || + localStorage.getItem(accordionLocalStorageKey) === null + ) { + localStorage.setItem(accordionLocalStorageKey, '{}'); + } + var allAccordion = document.querySelectorAll('.sidebar-section-title'); + var ids = getAccordionIdsFromLocalStorage(); + + allAccordion.forEach(function (item) { + item.addEventListener('click', function () { + toggleAccordion(item); + }); + if (item.id in ids) { + toggleAccordion(item); + } + }); +} + +function isSourcePage() { + return Boolean(document.querySelector('#source-page')); +} + +function bringElementIntoView(element, updateHistory = true) { + // If element is null then we are not going further + if (!element) { + return; + } + + /** + * tocbotInstance is defined in layout.tmpl + * It is defined when we are initializing tocbot. + * + */ + // eslint-disable-next-line no-undef + if (tocbotInstance) { + // eslint-disable-next-line no-undef + setTimeout(() => tocbotInstance.updateTocListActiveElement(element), 60) + } + var navbar = document.querySelector('.navbar-container'); + var body = document.querySelector('.main-content'); + var elementTop = element.getBoundingClientRect().top; + + var offset = 16; + + if (navbar) { + offset += navbar.scrollHeight; + } + + if (body) { + body.scrollBy(0, elementTop - offset); + } + + if (updateHistory) { + // eslint-disable-next-line no-undef + history.pushState(null, null, '#' + element.id); + } +} + +// eslint-disable-next-line no-unused-vars +function bringLinkToView(event) { + event.preventDefault(); + event.stopPropagation(); + var id = event.currentTarget.getAttribute('href'); + + if (!id) { + return; + } + + var element = document.getElementById(id.slice(1)); + + if (element) { + bringElementIntoView(element); + } +} + +function bringIdToViewOnMount() { + if (isSourcePage()) { + return; + } + + // eslint-disable-next-line no-undef + var id = window.location.hash; + + if (id === '') { + return; + } + + var element = document.getElementById(id.slice(1)); + + if (!element) { + id = decodeURI(id); + element = document.getElementById(id.slice(1)); + } + + if (element) { + bringElementIntoView(element, false); + } +} + +function createAnchorElement(id) { + var anchor = document.createElement('a'); + + anchor.textContent = '#'; + anchor.href = '#' + id; + anchor.classList.add('link-anchor'); + anchor.onclick = bringLinkToView; + + return anchor; +} + +function addAnchor() { + var main = document.querySelector('.main-content').querySelector('section'); + + var h1 = main.querySelectorAll('h1'); + var h2 = main.querySelectorAll('h2'); + var h3 = main.querySelectorAll('h3'); + var h4 = main.querySelectorAll('h4'); + + var targets = [h1, h2, h3, h4]; + + targets.forEach(function (target) { + target.forEach(function (heading) { + var anchor = createAnchorElement(heading.id); + + heading.classList.add('has-anchor'); + heading.append(anchor); + }); + }); +} + +/** + * + * @param {string} value + */ +function copy(value) { + console.log(value); + const el = document.createElement('textarea'); + + el.value = value; + document.body.appendChild(el); + el.select(); + document.execCommand('copy'); + document.body.removeChild(el); +} + +function showTooltip(id) { + var tooltip = document.getElementById(id); + + tooltip.classList.add('show-tooltip'); + setTimeout(function () { + tooltip.classList.remove('show-tooltip'); + }, 3000); +} + +/* eslint-disable-next-line */ +function copyFunction(id) { + // selecting the pre element + var code = document.getElementById(id); + + // selecting the ol.linenums + var element = code.querySelector('.linenums'); + + if (!element) { + // selecting the code block + element = code.querySelector('code'); + } + + // copy + copy(element.innerText.trim().replace(/(^\t)/gm, '')); + + // show tooltip + showTooltip('tooltip-' + id); +} + +function hideTocOnSourcePage() { + if (isSourcePage()) { + document.querySelector('.toc-container').style.display = 'none'; + } +} + +function getPreTopBar(id, lang = '') { + // tooltip + var tooltip = '
Copied!
'; + + // template of copy to clipboard icon container + var copyToClipboard = + ''; + + var langNameDiv = + '
' + + lang.toLocaleUpperCase() + + '
'; + + var topBar = + '
' + + langNameDiv + + copyToClipboard + + '
'; + + return topBar; +} + +function getPreDiv() { + var divElement = document.createElement('div'); + + divElement.classList.add('pre-div'); + + return divElement; +} + +function processAllPre() { + var targets = document.querySelectorAll('pre'); + var footer = document.querySelector('#PeOAagUepe'); + var navbar = document.querySelector('#VuAckcnZhf'); + + var navbarHeight = 0; + var footerHeight = 0; + + if (footer) { + footerHeight = footer.getBoundingClientRect().height; + } + + if (navbar) { + navbarHeight = navbar.getBoundingClientRect().height; + } + + // eslint-disable-next-line no-undef + var preMaxHeight = window.innerHeight - navbarHeight - footerHeight - 250; + + targets.forEach(function (pre, idx) { + var parent = pre.parentNode; + + if (parent && parent.getAttribute('data-skip-pre-process') === 'true') { + return; + } + + var div = getPreDiv(); + var id = 'ScDloZOMdL' + idx; + + var lang = pre.getAttribute('data-lang') || 'code'; + var topBar = getPreTopBar(id, lang); + + div.innerHTML = topBar; + + pre.style.maxHeight = preMaxHeight + 'px'; + pre.id = id; + pre.classList.add('prettyprint'); + pre.parentNode.insertBefore(div, pre); + div.appendChild(pre); + }); +} + +function highlightAndBringLineIntoView() { + // eslint-disable-next-line no-undef + var lineNumber = window.location.hash.replace('#line', ''); + + try { + var selector = '[data-line-number="' + lineNumber + '"'; + + var element = document.querySelector(selector); + + element.scrollIntoView(); + element.parentNode.classList.add('selected'); + } catch (error) { + console.error(error); + } +} + +function getFontSize() { + var currentFontSize = 16; + + try { + currentFontSize = Number.parseInt( + html.style.fontSize.split('px')[0], + 10 + ); + } catch (error) { + console.log(error); + } + + return currentFontSize; +} + +function updateFontSize(fontSize) { + html.style.fontSize = fontSize + 'px'; + localStorage.setItem(fontSizeLocalStorageKey, fontSize); + var fontSizeText = document.querySelector( + '#b77a68a492f343baabea06fad81f651e' + ); + + if (fontSizeText) { + fontSizeText.innerHTML = fontSize; + } +} + +(function () { + var fontSize = getFontSize(); + var fontSizeInLocalStorage = localStorage.getItem(fontSizeLocalStorageKey); + + if (fontSizeInLocalStorage) { + var n = Number.parseInt(fontSizeInLocalStorage, 10); + + if (n === fontSize) { + return; + } + updateFontSize(n); + } else { + updateFontSize(fontSize); + } +})(); + +// eslint-disable-next-line no-unused-vars +function incrementFont(event) { + var n = getFontSize(); + + if (n < MAX_FONT_SIZE) { + updateFontSize(n + 1); + } +} + +// eslint-disable-next-line no-unused-vars +function decrementFont(event) { + var n = getFontSize(); + + if (n > MIN_FONT_SIZE) { + updateFontSize(n - 1); + } +} + +function fontSizeTooltip() { + var fontSize = getFontSize(); + + return ` +
+ +
+ ${fontSize} +
+ + +
+ + `; +} + +function initTooltip() { + // add tooltip to navbar item + // eslint-disable-next-line no-undef + tippy('.theme-toggle', { + content: 'Toggle Theme', + delay: 500 + }); + + // eslint-disable-next-line no-undef + tippy('.search-button', { + content: 'Search', + delay: 500 + }); + + // eslint-disable-next-line no-undef + tippy('.font-size', { + content: 'Change font size', + delay: 500 + }); + + // eslint-disable-next-line no-undef + tippy('.codepen-button', { + content: 'Open code in CodePen', + placement: 'left' + }); + + // eslint-disable-next-line no-undef + tippy('.copy-code', { + content: 'Copy this code', + placement: 'left' + }); + + // eslint-disable-next-line no-undef + tippy('.font-size', { + content: fontSizeTooltip(), + trigger: 'click', + interactive: true, + allowHTML: true, + placement: 'left' + }); +} + +function fixTable() { + var tables = document.querySelectorAll('table'); + var table; + + // eslint-disable-next-line no-undef + if (window.innerWidth > 900) { + // Only fixing table if width is smaller than 900px + return; + } + + for (table of tables) { + if (table.classList.contains('hljs-ln')) { + // don't want to wrap code blocks. + return; + } + + var div = document.createElement('div'); + + div.classList.add('table-div'); + table.parentNode.insertBefore(div, table); + div.appendChild(table); + } +} + +function hideMobileMenu() { + var mobileMenuContainer = document.querySelector('#mobile-sidebar'); + var target = document.querySelector('#mobile-menu'); + var svgUse = target.querySelector('use'); + + if (mobileMenuContainer) { + mobileMenuContainer.classList.remove('show'); + } + if (target) { + target.setAttribute('data-isopen', 'false'); + } + if (svgUse) { + svgUse.setAttribute('xlink:href', '#menu-icon'); + } +} + +function showMobileMenu() { + var mobileMenuContainer = document.querySelector('#mobile-sidebar'); + var target = document.querySelector('#mobile-menu'); + var svgUse = target.querySelector('use'); + + if (mobileMenuContainer) { + mobileMenuContainer.classList.add('show'); + } + if (target) { + target.setAttribute('data-isopen', 'true'); + } + if (svgUse) { + svgUse.setAttribute('xlink:href', '#close-icon'); + } +} + +function onMobileMenuClick() { + var target = document.querySelector('#mobile-menu'); + var isOpen = target.getAttribute('data-isopen') === 'true'; + + if (isOpen) { + hideMobileMenu(); + } else { + showMobileMenu(); + } +} + +function initMobileMenu() { + var menu = document.querySelector('#mobile-menu'); + + if (menu) { + menu.addEventListener('click', onMobileMenuClick); + } +} + +function addHrefToSidebarTitle() { + var titles = document.querySelectorAll('.sidebar-title-anchor'); + + titles.forEach(function (title) { + // eslint-disable-next-line no-undef + title.setAttribute('href', baseURL); + }); +} + +function onDomContentLoaded() { + var themeButton = document.querySelectorAll('.theme-toggle'); + + initMobileMenu(); + + if (themeButton) { + themeButton.forEach(function (button) { + button.addEventListener('click', toggleTheme); + }); + } + + // Highlighting code + + // eslint-disable-next-line no-undef + hljs.addPlugin({ + 'after:highlightElement': function (obj) { + // Replace 'code' with result.language when + // we are able to cross-check the correctness of + // result. + obj.el.parentNode.setAttribute('data-lang', 'code'); + } + }); + // eslint-disable-next-line no-undef + hljs.highlightAll(); + // eslint-disable-next-line no-undef + hljs.initLineNumbersOnLoad({ + singleLine: true + }); + + // Highlight complete + + initAccordion(); + addAnchor(); + processAllPre(); + hideTocOnSourcePage(); + setTimeout(function () { + bringIdToViewOnMount(); + if (isSourcePage()) { + highlightAndBringLineIntoView(); + } + }, 1000); + initTooltip(); + fixTable(); + addHrefToSidebarTitle(); +} + +// eslint-disable-next-line no-undef +window.addEventListener('DOMContentLoaded', onDomContentLoaded); + +// eslint-disable-next-line no-undef +window.addEventListener('hashchange', (event) => { + const url = new URL(event.newURL); + + if (url.hash !== '') { + bringIdToViewOnMount(url.hash); + } +}); diff --git a/docs/html/game_editor/scripts/core.min.js b/docs/html/game_editor/scripts/core.min.js new file mode 100644 index 000000000..1821b44ca --- /dev/null +++ b/docs/html/game_editor/scripts/core.min.js @@ -0,0 +1,23 @@ +var accordionLocalStorageKey="accordion-id",themeLocalStorageKey="theme",fontSizeLocalStorageKey="font-size",html=document.querySelector("html"),MAX_FONT_SIZE=30,MIN_FONT_SIZE=10,localStorage=window.localStorage;function getTheme(){return document.body.getAttribute("data-theme")}function updateTheme(e){var t=document.body,o=document.querySelectorAll(".theme-svg-use"),n="dark"===e?"#light-theme-icon":"#dark-theme-icon";t.setAttribute("data-theme",e),t.classList.remove("dark","light"),t.classList.add(e),o.forEach(function(e){e.setAttribute("xlink:href",n)}),localStorage.setItem(themeLocalStorageKey,e)}function toggleTheme(){updateTheme("dark"===document.body.getAttribute("data-theme")?"light":"dark")}function setAccordionIdToLocalStorage(e){var t=JSON.parse(localStorage.getItem(accordionLocalStorageKey));t[e]=e,localStorage.setItem(accordionLocalStorageKey,JSON.stringify(t))}function removeAccordionIdFromLocalStorage(e){var t=JSON.parse(localStorage.getItem(accordionLocalStorageKey));delete t[e],localStorage.setItem(accordionLocalStorageKey,JSON.stringify(t))}function getAccordionIdsFromLocalStorage(){return JSON.parse(localStorage.getItem(accordionLocalStorageKey))||{}}function toggleAccordion(e){"false"===e.getAttribute("data-isopen")?(e.setAttribute("data-isopen","true"),setAccordionIdToLocalStorage(e.id)):(e.setAttribute("data-isopen","false"),removeAccordionIdFromLocalStorage(e.id))}function initAccordion(){void 0!==localStorage.getItem(accordionLocalStorageKey)&&null!==localStorage.getItem(accordionLocalStorageKey)||localStorage.setItem(accordionLocalStorageKey,"{}");var e=document.querySelectorAll(".sidebar-section-title"),t=getAccordionIdsFromLocalStorage();e.forEach(function(e){e.addEventListener("click",function(){toggleAccordion(e)}),e.id in t&&toggleAccordion(e)})}function isSourcePage(){return Boolean(document.querySelector("#source-page"))}function bringElementIntoView(e,t=!0){var o,n,i,c;e&&(tocbotInstance&&setTimeout(()=>tocbotInstance.updateTocListActiveElement(e),60),o=document.querySelector(".navbar-container"),n=document.querySelector(".main-content"),i=e.getBoundingClientRect().top,c=16,o&&(c+=o.scrollHeight),n&&n.scrollBy(0,i-c),t&&history.pushState(null,null,"#"+e.id))}function bringLinkToView(e){e.preventDefault(),e.stopPropagation();var e=e.currentTarget.getAttribute("href");!e||(e=document.getElementById(e.slice(1)))&&bringElementIntoView(e)}function bringIdToViewOnMount(){var e,t;isSourcePage()||""!==(e=window.location.hash)&&((t=document.getElementById(e.slice(1)))||(e=decodeURI(e),t=document.getElementById(e.slice(1))),t&&bringElementIntoView(t,!1))}function createAnchorElement(e){var t=document.createElement("a");return t.textContent="#",t.href="#"+e,t.classList.add("link-anchor"),t.onclick=bringLinkToView,t}function addAnchor(){var e=document.querySelector(".main-content").querySelector("section");[e.querySelectorAll("h1"),e.querySelectorAll("h2"),e.querySelectorAll("h3"),e.querySelectorAll("h4")].forEach(function(e){e.forEach(function(e){var t=createAnchorElement(e.id);e.classList.add("has-anchor"),e.append(t)})})}function copy(e){console.log(e);const t=document.createElement("textarea");t.value=e,document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t)}function showTooltip(e){var t=document.getElementById(e);t.classList.add("show-tooltip"),setTimeout(function(){t.classList.remove("show-tooltip")},3e3)}function copyFunction(e){var t=document.getElementById(e);copy((t.querySelector(".linenums")||t.querySelector("code")).innerText.trim().replace(/(^\t)/gm,"")),showTooltip("tooltip-"+e)}function hideTocOnSourcePage(){isSourcePage()&&(document.querySelector(".toc-container").style.display="none")}function getPreTopBar(e,t=""){e='";return'
'+('
'+t.toLocaleUpperCase()+"
")+e+"
"}function getPreDiv(){var e=document.createElement("div");return e.classList.add("pre-div"),e}function processAllPre(){var e=document.querySelectorAll("pre"),t=document.querySelector("#PeOAagUepe"),o=document.querySelector("#VuAckcnZhf"),n=0,i=0,c=(t&&(i=t.getBoundingClientRect().height),o&&(n=o.getBoundingClientRect().height),window.innerHeight-n-i-250);e.forEach(function(e,t){var o,n=e.parentNode;n&&"true"===n.getAttribute("data-skip-pre-process")||(n=getPreDiv(),o=getPreTopBar(t="ScDloZOMdL"+t,e.getAttribute("data-lang")||"code"),n.innerHTML=o,e.style.maxHeight=c+"px",e.id=t,e.classList.add("prettyprint"),e.parentNode.insertBefore(n,e),n.appendChild(e))})}function highlightAndBringLineIntoView(){var e=window.location.hash.replace("#line","");try{var t='[data-line-number="'+e+'"',o=document.querySelector(t);o.scrollIntoView(),o.parentNode.classList.add("selected")}catch(e){console.error(e)}}function getFontSize(){var e=16;try{e=Number.parseInt(html.style.fontSize.split("px")[0],10)}catch(e){console.log(e)}return e}function updateFontSize(e){html.style.fontSize=e+"px",localStorage.setItem(fontSizeLocalStorageKey,e);var t=document.querySelector("#b77a68a492f343baabea06fad81f651e");t&&(t.innerHTML=e)}function incrementFont(e){var t=getFontSize();t + +
+ ${e} +
+ + + + + `}function initTooltip(){tippy(".theme-toggle",{content:"Toggle Theme",delay:500}),tippy(".search-button",{content:"Search",delay:500}),tippy(".font-size",{content:"Change font size",delay:500}),tippy(".codepen-button",{content:"Open code in CodePen",placement:"left"}),tippy(".copy-code",{content:"Copy this code",placement:"left"}),tippy(".font-size",{content:fontSizeTooltip(),trigger:"click",interactive:!0,allowHTML:!0,placement:"left"})}function fixTable(){var e,t=document.querySelectorAll("table");if(!(900{e=new URL(e.newURL);""!==e.hash&&bringIdToViewOnMount(e.hash)}); \ No newline at end of file diff --git a/docs/html/game_editor/scripts/resize.js b/docs/html/game_editor/scripts/resize.js new file mode 100644 index 000000000..d8e3a9cf7 --- /dev/null +++ b/docs/html/game_editor/scripts/resize.js @@ -0,0 +1,90 @@ +/* global document */ +// This file is @deprecated + +var NAVBAR_OPTIONS = {}; + +(function() { + var NAVBAR_RESIZE_LOCAL_STORAGE_KEY = 'NAVBAR_RESIZE_LOCAL_STORAGE_KEY'; + + var navbar = document.querySelector('#navbar'); + var footer = document.querySelector('#footer'); + var mainSection = document.querySelector('#main'); + var localStorageResizeObject = JSON.parse( + // eslint-disable-next-line no-undef + localStorage.getItem(NAVBAR_RESIZE_LOCAL_STORAGE_KEY) + ); + + /** + * Check whether we have any resize value in local storage or not. + * If we have resize value then resize the navbar. + **/ + if (localStorageResizeObject) { + navbar.style.width = localStorageResizeObject.width; + mainSection.style.marginLeft = localStorageResizeObject.width; + footer.style.marginLeft = localStorageResizeObject.width; + } + + var navbarSlider = document.querySelector('#navbar-resize'); + + function resizeNavbar(event) { + var pageX = event.pageX, + pageXPlusPx = event.pageX + 'px', + min = Number.parseInt(NAVBAR_OPTIONS.min, 10) || 300, + max = Number.parseInt(NAVBAR_OPTIONS.max, 10) || 600; + + /** + * Just to add some checks. If min is smaller than 10 then + * user may accidentally end up reducing the size of navbar + * less than 10. In that case user will not able to resize navbar + * because navbar slider will be hidden. + */ + if (min < 10) { + min = 10; + } + + /** + * Only resize if pageX in range between min and max + * allowed value. + */ + if (min < pageX && pageX < max) { + navbar.style.width = pageXPlusPx; + mainSection.style.marginLeft = pageXPlusPx; + footer.style.marginLeft = pageXPlusPx; + } + } + + function setupEventListeners() { + // eslint-disable-next-line no-undef + window.addEventListener('mousemove', resizeNavbar); + // eslint-disable-next-line no-undef + window.addEventListener('touchmove', resizeNavbar); + } + + function afterRemovingEventListeners() { + // eslint-disable-next-line no-undef + localStorage.setItem( + NAVBAR_RESIZE_LOCAL_STORAGE_KEY, + JSON.stringify({ + width: navbar.style.width + }) + ); + } + + function removeEventListeners() { + // eslint-disable-next-line no-undef + window.removeEventListener('mousemove', resizeNavbar); + // eslint-disable-next-line no-undef + window.removeEventListener('touchend', resizeNavbar); + afterRemovingEventListeners(); + } + + navbarSlider.addEventListener('mousedown', setupEventListeners); + navbarSlider.addEventListener('touchstart', setupEventListeners); + // eslint-disable-next-line no-undef + window.addEventListener('mouseup', removeEventListeners); +})(); + +// eslint-disable-next-line no-unused-vars +function setupResizeOptions(options) { + NAVBAR_OPTIONS = options; +} diff --git a/docs/html/game_editor/scripts/search.js b/docs/html/game_editor/scripts/search.js new file mode 100644 index 000000000..450bafe07 --- /dev/null +++ b/docs/html/game_editor/scripts/search.js @@ -0,0 +1,262 @@ +/* global document */ + +const searchId = 'LiBfqbJVcV'; +const searchHash = '#' + searchId; +const searchContainer = document.querySelector('#PkfLWpAbet'); +const searchWrapper = document.querySelector('#iCxFxjkHbP'); +const searchCloseButton = document.querySelector('#VjLlGakifb'); +const searchInput = document.querySelector('#vpcKVYIppa'); +const resultBox = document.querySelector('#fWwVHRuDuN'); + +function showResultText(text) { + resultBox.innerHTML = `${text}`; +} + +function hideSearch() { + // eslint-disable-next-line no-undef + if (window.location.hash === searchHash) { + // eslint-disable-next-line no-undef + history.go(-1); + } + + // eslint-disable-next-line no-undef + window.onhashchange = null; + + if (searchContainer) { + searchContainer.style.display = 'none'; + } +} + +function listenCloseKey(event) { + if (event.key === 'Escape') { + hideSearch(); + // eslint-disable-next-line no-undef + window.removeEventListener('keyup', listenCloseKey); + } +} + +function showSearch() { + try { + // Closing mobile menu before opening + // search box. + // It is defined in core.js + // eslint-disable-next-line no-undef + hideMobileMenu(); + } catch (error) { + console.error(error); + } + + // eslint-disable-next-line no-undef + window.onhashchange = hideSearch; + + // eslint-disable-next-line no-undef + if (window.location.hash !== searchHash) { + // eslint-disable-next-line no-undef + history.pushState(null, null, searchHash); + } + + if (searchContainer) { + searchContainer.style.display = 'flex'; + // eslint-disable-next-line no-undef + window.addEventListener('keyup', listenCloseKey); + } + + if (searchInput) { + searchInput.focus(); + } +} + +async function fetchAllData() { + // eslint-disable-next-line no-undef + const { hostname, protocol, port } = location; + + // eslint-disable-next-line no-undef + const base = protocol + '//' + hostname + (port !== '' ? ':' + port : '') + baseURL; + // eslint-disable-next-line no-undef + const url = new URL('data/search.json', base); + const result = await fetch(url); + const { list } = await result.json(); + + return list; +} + +// eslint-disable-next-line no-unused-vars +function onClickSearchItem(event) { + const target = event.currentTarget; + + if (target) { + const href = target.getAttribute('href') || ''; + let elementId = href.split('#')[1] || ''; + let element = document.getElementById(elementId); + + if (!element) { + elementId = decodeURI(elementId); + element = document.getElementById(elementId); + } + + if (element) { + setTimeout(function() { + // eslint-disable-next-line no-undef + bringElementIntoView(element); // defined in core.js + }, 100); + } + } +} + +function buildSearchResult(result) { + let output = ''; + + for (const res of result) { + const { title, description } = res.item; + + const link = res.item.link.replace('.*/, ''); + + output += ` + +
${title}
+
${description || 'No description available.'}
+
+ `; + } + + return output; +} + +function getSearchResult(list, keys, searchKey) { + const defaultOptions = { + shouldSort: true, + threshold: 0.4, + location: 0, + distance: 100, + maxPatternLength: 32, + minMatchCharLength: 1, + keys: keys + }; + + const options = { ...defaultOptions }; + + // eslint-disable-next-line no-undef + const searchIndex = Fuse.createIndex(options.keys, list); + + // eslint-disable-next-line no-undef + const fuse = new Fuse(list, options, searchIndex); + + const result = fuse.search(searchKey); + + if (result.length > 20) { + return result.slice(0, 20); + } + + return result; +} + +function debounce(func, wait, immediate) { + let timeout; + + return function() { + const args = arguments; + + clearTimeout(timeout); + timeout = setTimeout(() => { + timeout = null; + if (!immediate) { + // eslint-disable-next-line consistent-this, no-invalid-this + func.apply(this, args); + } + }, wait); + + if (immediate && !timeout) { + // eslint-disable-next-line consistent-this, no-invalid-this + func.apply(this, args); + } + }; +} + +let searchData; + +async function search(event) { + const value = event.target.value; + const keys = ['title', 'description']; + + if (!resultBox) { + console.error('Search result container not found'); + + return; + } + + if (!value) { + showResultText('Type anything to view search result'); + + return; + } + + if (!searchData) { + showResultText('Loading...'); + + try { + // eslint-disable-next-line require-atomic-updates + searchData = await fetchAllData(); + } catch (e) { + console.log(e); + showResultText('Failed to load result.'); + + return; + } + } + + const result = getSearchResult(searchData, keys, value); + + if (!result.length) { + showResultText('No result found! Try some different combination.'); + + return; + } + + // eslint-disable-next-line require-atomic-updates + resultBox.innerHTML = buildSearchResult(result); +} + +function onDomContentLoaded() { + const searchButton = document.querySelectorAll('.search-button'); + const debouncedSearch = debounce(search, 300); + + if (searchCloseButton) { + searchCloseButton.addEventListener('click', hideSearch); + } + + if (searchButton) { + searchButton.forEach(function(item) { + item.addEventListener('click', showSearch); + }); + } + + if (searchContainer) { + searchContainer.addEventListener('click', hideSearch); + } + + if (searchWrapper) { + searchWrapper.addEventListener('click', function(event) { + event.stopPropagation(); + }); + } + + if (searchInput) { + searchInput.addEventListener('keyup', debouncedSearch); + } + + // eslint-disable-next-line no-undef + if (window.location.hash === searchHash) { + showSearch(); + } +} + +// eslint-disable-next-line no-undef +window.addEventListener('DOMContentLoaded', onDomContentLoaded); + +// eslint-disable-next-line no-undef +window.addEventListener('hashchange', function() { + // eslint-disable-next-line no-undef + if (window.location.hash === searchHash) { + showSearch(); + } +}); diff --git a/docs/html/game_editor/scripts/search.min.js b/docs/html/game_editor/scripts/search.min.js new file mode 100644 index 000000000..c889c6e0b --- /dev/null +++ b/docs/html/game_editor/scripts/search.min.js @@ -0,0 +1,6 @@ +const searchId="LiBfqbJVcV",searchHash="#"+searchId,searchContainer=document.querySelector("#PkfLWpAbet"),searchWrapper=document.querySelector("#iCxFxjkHbP"),searchCloseButton=document.querySelector("#VjLlGakifb"),searchInput=document.querySelector("#vpcKVYIppa"),resultBox=document.querySelector("#fWwVHRuDuN");function showResultText(e){resultBox.innerHTML=`${e}`}function hideSearch(){window.location.hash===searchHash&&history.go(-1),window.onhashchange=null,searchContainer&&(searchContainer.style.display="none")}function listenCloseKey(e){"Escape"===e.key&&(hideSearch(),window.removeEventListener("keyup",listenCloseKey))}function showSearch(){try{hideMobileMenu()}catch(e){console.error(e)}window.onhashchange=hideSearch,window.location.hash!==searchHash&&history.pushState(null,null,searchHash),searchContainer&&(searchContainer.style.display="flex",window.addEventListener("keyup",listenCloseKey)),searchInput&&searchInput.focus()}async function fetchAllData(){var{hostname:e,protocol:t,port:n}=location,t=t+"//"+e+(""!==n?":"+n:"")+baseURL,e=new URL("data/search.json",t);const a=await fetch(e);n=(await a.json()).list;return n}function onClickSearchItem(t){const n=t.currentTarget;if(n){const a=n.getAttribute("href")||"";t=a.split("#")[1]||"";let e=document.getElementById(t);e||(t=decodeURI(t),e=document.getElementById(t)),e&&setTimeout(function(){bringElementIntoView(e)},100)}}function buildSearchResult(e){let t="";for(const s of e){var{title:n,description:a}=s.item,o=s.item.link.replace('.*/,"");t+=` + +
${n}
+
${a||"No description available."}
+
+ `}return t}function getSearchResult(e,t,n){var t={...{shouldSort:!0,threshold:.4,location:0,distance:100,maxPatternLength:32,minMatchCharLength:1,keys:t}},a=Fuse.createIndex(t.keys,e);const o=new Fuse(e,t,a),s=o.search(n);return 20{o=null,a||t.apply(this,e)},n),a&&!o&&t.apply(this,e)}}let searchData;async function search(e){e=e.target.value;if(resultBox)if(e){if(!searchData){showResultText("Loading...");try{searchData=await fetchAllData()}catch(e){return console.log(e),void showResultText("Failed to load result.")}}e=getSearchResult(searchData,["title","description"],e);e.length?resultBox.innerHTML=buildSearchResult(e):showResultText("No result found! Try some different combination.")}else showResultText("Type anything to view search result");else console.error("Search result container not found")}function onDomContentLoaded(){const e=document.querySelectorAll(".search-button");var t=debounce(search,300);searchCloseButton&&searchCloseButton.addEventListener("click",hideSearch),e&&e.forEach(function(e){e.addEventListener("click",showSearch)}),searchContainer&&searchContainer.addEventListener("click",hideSearch),searchWrapper&&searchWrapper.addEventListener("click",function(e){e.stopPropagation()}),searchInput&&searchInput.addEventListener("keyup",t),window.location.hash===searchHash&&showSearch()}window.addEventListener("DOMContentLoaded",onDomContentLoaded),window.addEventListener("hashchange",function(){window.location.hash===searchHash&&showSearch()}); \ No newline at end of file diff --git a/docs/html/game_editor/scripts/third-party/Apache-License-2.0.txt b/docs/html/game_editor/scripts/third-party/Apache-License-2.0.txt new file mode 100644 index 000000000..75b52484e --- /dev/null +++ b/docs/html/game_editor/scripts/third-party/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/docs/html/game_editor/scripts/third-party/fuse.js b/docs/html/game_editor/scripts/third-party/fuse.js new file mode 100644 index 000000000..a55c5daa0 --- /dev/null +++ b/docs/html/game_editor/scripts/third-party/fuse.js @@ -0,0 +1,9 @@ +/** + * Fuse.js v6.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2021 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +var e,t;e=this,t=function(){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:3,t=new Map,n=Math.pow(10,e);return{get:function(e){var r=e.match(I).length;if(t.has(r))return t.get(r);var i=1/Math.sqrt(r),o=parseFloat(Math.round(i*n)/n);return t.set(r,o),o},clear:function(){t.clear()}}}var E=function(){function e(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=n.getFn,i=void 0===r?A.getFn:r;t(this,e),this.norm=C(3),this.getFn=i,this.isCreated=!1,this.setIndexRecords()}return r(e,[{key:"setSources",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.docs=e}},{key:"setIndexRecords",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.records=e}},{key:"setKeys",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.keys=t,this._keysMap={},t.forEach((function(t,n){e._keysMap[t.id]=n}))}},{key:"create",value:function(){var e=this;!this.isCreated&&this.docs.length&&(this.isCreated=!0,g(this.docs[0])?this.docs.forEach((function(t,n){e._addString(t,n)})):this.docs.forEach((function(t,n){e._addObject(t,n)})),this.norm.clear())}},{key:"add",value:function(e){var t=this.size();g(e)?this._addString(e,t):this._addObject(e,t)}},{key:"removeAt",value:function(e){this.records.splice(e,1);for(var t=e,n=this.size();t2&&void 0!==arguments[2]?arguments[2]:{},r=n.getFn,i=void 0===r?A.getFn:r,o=new E({getFn:i});return o.setKeys(e.map(_)),o.setSources(t),o.create(),o}function R(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.errors,r=void 0===n?0:n,i=t.currentLocation,o=void 0===i?0:i,c=t.expectedLocation,a=void 0===c?0:c,s=t.distance,u=void 0===s?A.distance:s,h=t.ignoreLocation,f=void 0===h?A.ignoreLocation:h,l=r/e.length;if(f)return l;var d=Math.abs(a-o);return u?l+d/u:d?1:l}function F(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:A.minMatchCharLength,n=[],r=-1,i=-1,o=0,c=e.length;o=t&&n.push([r,i]),r=-1)}return e[o-1]&&o-r>=t&&n.push([r,o-1]),n}function P(e){for(var t={},n=0,r=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},o=i.location,c=void 0===o?A.location:o,a=i.threshold,s=void 0===a?A.threshold:a,u=i.distance,h=void 0===u?A.distance:u,f=i.includeMatches,l=void 0===f?A.includeMatches:f,d=i.findAllMatches,v=void 0===d?A.findAllMatches:d,g=i.minMatchCharLength,y=void 0===g?A.minMatchCharLength:g,p=i.isCaseSensitive,m=void 0===p?A.isCaseSensitive:p,k=i.ignoreLocation,M=void 0===k?A.ignoreLocation:k;if(t(this,e),this.options={location:c,threshold:s,distance:h,includeMatches:l,findAllMatches:v,minMatchCharLength:y,isCaseSensitive:m,ignoreLocation:M},this.pattern=m?n:n.toLowerCase(),this.chunks=[],this.pattern.length){var b=function(e,t){r.chunks.push({pattern:e,alphabet:P(e),startIndex:t})},x=this.pattern.length;if(x>32){for(var L=0,S=x%32,w=x-S;L3&&void 0!==arguments[3]?arguments[3]:{},i=r.location,o=void 0===i?A.location:i,c=r.distance,a=void 0===c?A.distance:c,s=r.threshold,u=void 0===s?A.threshold:s,h=r.findAllMatches,f=void 0===h?A.findAllMatches:h,l=r.minMatchCharLength,d=void 0===l?A.minMatchCharLength:l,v=r.includeMatches,g=void 0===v?A.includeMatches:v,y=r.ignoreLocation,p=void 0===y?A.ignoreLocation:y;if(t.length>32)throw new Error(L(32));for(var m,k=t.length,M=e.length,b=Math.max(0,Math.min(o,M)),x=u,S=b,w=d>1||g,_=w?Array(M):[];(m=e.indexOf(t,S))>-1;){var O=R(t,{currentLocation:m,expectedLocation:b,distance:a,ignoreLocation:p});if(x=Math.min(O,x),S=m+k,w)for(var j=0;j=K;J-=1){var T=J-1,U=n[e.charAt(T)];if(w&&(_[T]=+!!U),W[J]=(W[J+1]<<1|1)&U,P&&(W[J]|=(I[J+1]|I[J])<<1|1|I[J+1]),W[J]&$&&(C=R(t,{errors:P,currentLocation:T,expectedLocation:b,distance:a,ignoreLocation:p}))<=x){if(x=C,(S=T)<=b)break;K=Math.max(1,2*b-S)}}var V=R(t,{errors:P+1,currentLocation:b,expectedLocation:b,distance:a,ignoreLocation:p});if(V>x)break;I=W}var B={isMatch:S>=0,score:Math.max(.001,C)};if(w){var G=F(_,d);G.length?g&&(B.indices=G):B.isMatch=!1}return B}(e,n,i,{location:c+o,distance:a,threshold:s,findAllMatches:u,minMatchCharLength:h,includeMatches:r,ignoreLocation:f}),p=y.isMatch,m=y.score,k=y.indices;p&&(g=!0),v+=m,p&&k&&(d=[].concat(l(d),l(k)))}));var y={isMatch:g,score:g?v/this.chunks.length:1};return g&&r&&(y.indices=d),y}}]),e}(),D=function(){function e(n){t(this,e),this.pattern=n}return r(e,[{key:"search",value:function(){}}],[{key:"isMultiMatch",value:function(e){return z(e,this.multiRegex)}},{key:"isSingleMatch",value:function(e){return z(e,this.singleRegex)}}]),e}();function z(e,t){var n=e.match(t);return n?n[1]:null}var K=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"exact"}},{key:"multiRegex",get:function(){return/^="(.*)"$/}},{key:"singleRegex",get:function(){return/^=(.*)$/}}]),i}(D),q=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"$/}},{key:"singleRegex",get:function(){return/^!(.*)$/}}]),i}(D),W=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"prefix-exact"}},{key:"multiRegex",get:function(){return/^\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^\^(.*)$/}}]),i}(D),J=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-prefix-exact"}},{key:"multiRegex",get:function(){return/^!\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^!\^(.*)$/}}]),i}(D),T=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}}],[{key:"type",get:function(){return"suffix-exact"}},{key:"multiRegex",get:function(){return/^"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^(.*)\$$/}}]),i}(D),U=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){var t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-suffix-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^!(.*)\$$/}}]),i}(D),V=function(e){a(i,e);var n=f(i);function i(e){var r,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=o.location,a=void 0===c?A.location:c,s=o.threshold,u=void 0===s?A.threshold:s,h=o.distance,f=void 0===h?A.distance:h,l=o.includeMatches,d=void 0===l?A.includeMatches:l,v=o.findAllMatches,g=void 0===v?A.findAllMatches:v,y=o.minMatchCharLength,p=void 0===y?A.minMatchCharLength:y,m=o.isCaseSensitive,k=void 0===m?A.isCaseSensitive:m,M=o.ignoreLocation,b=void 0===M?A.ignoreLocation:M;return t(this,i),(r=n.call(this,e))._bitapSearch=new N(e,{location:a,threshold:u,distance:f,includeMatches:d,findAllMatches:g,minMatchCharLength:p,isCaseSensitive:k,ignoreLocation:b}),r}return r(i,[{key:"search",value:function(e){return this._bitapSearch.searchIn(e)}}],[{key:"type",get:function(){return"fuzzy"}},{key:"multiRegex",get:function(){return/^"(.*)"$/}},{key:"singleRegex",get:function(){return/^(.*)$/}}]),i}(D),B=function(e){a(i,e);var n=f(i);function i(e){return t(this,i),n.call(this,e)}return r(i,[{key:"search",value:function(e){for(var t,n=0,r=[],i=this.pattern.length;(t=e.indexOf(this.pattern,n))>-1;)n=t+i,r.push([t,n-1]);var o=!!r.length;return{isMatch:o,score:o?0:1,indices:r}}}],[{key:"type",get:function(){return"include"}},{key:"multiRegex",get:function(){return/^'"(.*)"$/}},{key:"singleRegex",get:function(){return/^'(.*)$/}}]),i}(D),G=[K,B,W,J,U,T,q,V],H=G.length,Q=/ +(?=([^\"]*\"[^\"]*\")*[^\"]*$)/;function X(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.split("|").map((function(e){for(var n=e.trim().split(Q).filter((function(e){return e&&!!e.trim()})),r=[],i=0,o=n.length;i1&&void 0!==arguments[1]?arguments[1]:{},i=r.isCaseSensitive,o=void 0===i?A.isCaseSensitive:i,c=r.includeMatches,a=void 0===c?A.includeMatches:c,s=r.minMatchCharLength,u=void 0===s?A.minMatchCharLength:s,h=r.ignoreLocation,f=void 0===h?A.ignoreLocation:h,l=r.findAllMatches,d=void 0===l?A.findAllMatches:l,v=r.location,g=void 0===v?A.location:v,y=r.threshold,p=void 0===y?A.threshold:y,m=r.distance,k=void 0===m?A.distance:m;t(this,e),this.query=null,this.options={isCaseSensitive:o,includeMatches:a,minMatchCharLength:u,findAllMatches:d,ignoreLocation:f,location:g,threshold:p,distance:k},this.pattern=o?n:n.toLowerCase(),this.query=X(this.pattern,this.options)}return r(e,[{key:"searchIn",value:function(e){var t=this.query;if(!t)return{isMatch:!1,score:1};var n=this.options,r=n.includeMatches;e=n.isCaseSensitive?e:e.toLowerCase();for(var i=0,o=[],c=0,a=0,s=t.length;a-1&&(n.refIndex=e.idx),t.matches.push(n)}}))}function le(e,t){t.score=e.score}function de(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,i=void 0===r?A.includeMatches:r,o=n.includeScore,c=void 0===o?A.includeScore:o,a=[];return i&&a.push(fe),c&&a.push(le),e.map((function(e){var n=e.idx,r={item:t[n],refIndex:n};return a.length&&a.forEach((function(t){t(e,r)})),r}))}var ve=function(){function e(n){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2?arguments[2]:void 0;t(this,e),this.options=c({},A,{},r),this.options.useExtendedSearch,this._keyStore=new w(this.options.keys),this.setCollection(n,i)}return r(e,[{key:"setCollection",value:function(e,t){if(this._docs=e,t&&!(t instanceof E))throw new Error("Incorrect 'index' type");this._myIndex=t||$(this.options.keys,this._docs,{getFn:this.options.getFn})}},{key:"add",value:function(e){k(e)&&(this._docs.push(e),this._myIndex.add(e))}},{key:"remove",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!1},t=[],n=0,r=this._docs.length;n1&&void 0!==arguments[1]?arguments[1]:{},n=t.limit,r=void 0===n?-1:n,i=this.options,o=i.includeMatches,c=i.includeScore,a=i.shouldSort,s=i.sortFn,u=i.ignoreFieldNorm,h=g(e)?g(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return he(h,{ignoreFieldNorm:u}),a&&h.sort(s),y(r)&&r>-1&&(h=h.slice(0,r)),de(h,this._docs,{includeMatches:o,includeScore:c})}},{key:"_searchStringList",value:function(e){var t=te(e,this.options),n=this._myIndex.records,r=[];return n.forEach((function(e){var n=e.v,i=e.i,o=e.n;if(k(n)){var c=t.searchIn(n),a=c.isMatch,s=c.score,u=c.indices;a&&r.push({item:n,idx:i,matches:[{score:s,value:n,norm:o,indices:u}]})}})),r}},{key:"_searchLogical",value:function(e){var t=this,n=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.auto,i=void 0===r||r,o=function e(n){var r=Object.keys(n),o=ae(n);if(!o&&r.length>1&&!ce(n))return e(ue(n));if(se(n)){var c=o?n[ie]:r[0],a=o?n[oe]:n[c];if(!g(a))throw new Error(x(c));var s={keyId:j(c),pattern:a};return i&&(s.searcher=te(a,t)),s}var u={children:[],operator:r[0]};return r.forEach((function(t){var r=n[t];v(r)&&r.forEach((function(t){u.children.push(e(t))}))})),u};return ce(e)||(e=ue(e)),o(e)}(e,this.options),r=this._myIndex.records,i={},o=[];return r.forEach((function(e){var r=e.$,c=e.i;if(k(r)){var a=function e(n,r,i){if(!n.children){var o=n.keyId,c=n.searcher,a=t._findMatches({key:t._keyStore.get(o),value:t._myIndex.getValueForItemAtKeyId(r,o),searcher:c});return a&&a.length?[{idx:i,item:r,matches:a}]:[]}switch(n.operator){case ne:for(var s=[],u=0,h=n.children.length;u1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?A.getFn:n,i=e.keys,o=e.records,c=new E({getFn:r});return c.setKeys(i),c.setIndexRecords(o),c},ve.config=A,function(){ee.push.apply(ee,arguments)}(Z),ve},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Fuse=t(); \ No newline at end of file diff --git a/docs/html/game_editor/scripts/third-party/hljs-line-num-original.js b/docs/html/game_editor/scripts/third-party/hljs-line-num-original.js new file mode 100644 index 000000000..9b8e18f77 --- /dev/null +++ b/docs/html/game_editor/scripts/third-party/hljs-line-num-original.js @@ -0,0 +1,369 @@ +// jshint multistr:true + +(function (w, d) { + 'use strict'; + + var TABLE_NAME = 'hljs-ln', + LINE_NAME = 'hljs-ln-line', + CODE_BLOCK_NAME = 'hljs-ln-code', + NUMBERS_BLOCK_NAME = 'hljs-ln-numbers', + NUMBER_LINE_NAME = 'hljs-ln-n', + DATA_ATTR_NAME = 'data-line-number', + BREAK_LINE_REGEXP = /\r\n|\r|\n/g; + + if (w.hljs) { + w.hljs.initLineNumbersOnLoad = initLineNumbersOnLoad; + w.hljs.lineNumbersBlock = lineNumbersBlock; + w.hljs.lineNumbersValue = lineNumbersValue; + + addStyles(); + } else { + w.console.error('highlight.js not detected!'); + } + + function isHljsLnCodeDescendant(domElt) { + var curElt = domElt; + while (curElt) { + if (curElt.className && curElt.className.indexOf('hljs-ln-code') !== -1) { + return true; + } + curElt = curElt.parentNode; + } + return false; + } + + function getHljsLnTable(hljsLnDomElt) { + var curElt = hljsLnDomElt; + while (curElt.nodeName !== 'TABLE') { + curElt = curElt.parentNode; + } + return curElt; + } + + // Function to workaround a copy issue with Microsoft Edge. + // Due to hljs-ln wrapping the lines of code inside a element, + // itself wrapped inside a
 element, window.getSelection().toString()
+    // does not contain any line breaks. So we need to get them back using the
+    // rendered code in the DOM as reference.
+    function edgeGetSelectedCodeLines(selection) {
+        // current selected text without line breaks
+        var selectionText = selection.toString();
+
+        // get the 
' + + '' + + '' + + '', + [ + LINE_NAME, + NUMBERS_BLOCK_NAME, + NUMBER_LINE_NAME, + DATA_ATTR_NAME, + CODE_BLOCK_NAME, + i + options.startFrom, + lines[i].length > 0 ? lines[i] : ' ' + ]); + } + + return format('
element wrapping the first line of selected code + var tdAnchor = selection.anchorNode; + while (tdAnchor.nodeName !== 'TD') { + tdAnchor = tdAnchor.parentNode; + } + + // get the element wrapping the last line of selected code + var tdFocus = selection.focusNode; + while (tdFocus.nodeName !== 'TD') { + tdFocus = tdFocus.parentNode; + } + + // extract line numbers + var firstLineNumber = parseInt(tdAnchor.dataset.lineNumber); + var lastLineNumber = parseInt(tdFocus.dataset.lineNumber); + + // multi-lines copied case + if (firstLineNumber != lastLineNumber) { + + var firstLineText = tdAnchor.textContent; + var lastLineText = tdFocus.textContent; + + // if the selection was made backward, swap values + if (firstLineNumber > lastLineNumber) { + var tmp = firstLineNumber; + firstLineNumber = lastLineNumber; + lastLineNumber = tmp; + tmp = firstLineText; + firstLineText = lastLineText; + lastLineText = tmp; + } + + // discard not copied characters in first line + while (selectionText.indexOf(firstLineText) !== 0) { + firstLineText = firstLineText.slice(1); + } + + // discard not copied characters in last line + while (selectionText.lastIndexOf(lastLineText) === -1) { + lastLineText = lastLineText.slice(0, -1); + } + + // reconstruct and return the real copied text + var selectedText = firstLineText; + var hljsLnTable = getHljsLnTable(tdAnchor); + for (var i = firstLineNumber + 1 ; i < lastLineNumber ; ++i) { + var codeLineSel = format('.{0}[{1}="{2}"]', [CODE_BLOCK_NAME, DATA_ATTR_NAME, i]); + var codeLineElt = hljsLnTable.querySelector(codeLineSel); + selectedText += '\n' + codeLineElt.textContent; + } + selectedText += '\n' + lastLineText; + return selectedText; + // single copied line case + } else { + return selectionText; + } + } + + // ensure consistent code copy/paste behavior across all browsers + // (see https://github.com/wcoder/highlightjs-line-numbers.js/issues/51) + document.addEventListener('copy', function(e) { + // get current selection + var selection = window.getSelection(); + // override behavior when one wants to copy line of codes + if (isHljsLnCodeDescendant(selection.anchorNode)) { + var selectionText; + // workaround an issue with Microsoft Edge as copied line breaks + // are removed otherwise from the selection string + if (window.navigator.userAgent.indexOf('Edge') !== -1) { + selectionText = edgeGetSelectedCodeLines(selection); + } else { + // other browsers can directly use the selection string + selectionText = selection.toString(); + } + e.clipboardData.setData( + 'text/plain', + selectionText + .replace(/(^\t)/gm, '') + ); + e.preventDefault(); + } + }); + + function addStyles () { + var css = d.createElement('style'); + css.type = 'text/css'; + css.innerHTML = format( + '.{0}{border-collapse:collapse}' + + '.{0} td{padding:0}' + + '.{1}:before{content:attr({2})}', + [ + TABLE_NAME, + NUMBER_LINE_NAME, + DATA_ATTR_NAME + ]); + d.getElementsByTagName('head')[0].appendChild(css); + } + + function initLineNumbersOnLoad (options) { + if (d.readyState === 'interactive' || d.readyState === 'complete') { + documentReady(options); + } else { + w.addEventListener('DOMContentLoaded', function () { + documentReady(options); + }); + } + } + + function documentReady (options) { + try { + var blocks = d.querySelectorAll('code.hljs,code.nohighlight'); + + for (var i in blocks) { + if (blocks.hasOwnProperty(i)) { + if (!isPluginDisabledForBlock(blocks[i])) { + lineNumbersBlock(blocks[i], options); + } + } + } + } catch (e) { + w.console.error('LineNumbers error: ', e); + } + } + + function isPluginDisabledForBlock(element) { + return element.classList.contains('nohljsln'); + } + + function lineNumbersBlock (element, options) { + if (typeof element !== 'object') return; + + async(function () { + element.innerHTML = lineNumbersInternal(element, options); + }); + } + + function lineNumbersValue (value, options) { + if (typeof value !== 'string') return; + + var element = document.createElement('code') + element.innerHTML = value + + return lineNumbersInternal(element, options); + } + + function lineNumbersInternal (element, options) { + + var internalOptions = mapOptions(element, options); + + duplicateMultilineNodes(element); + + return addLineNumbersBlockFor(element.innerHTML, internalOptions); + } + + function addLineNumbersBlockFor (inputHtml, options) { + var lines = getLines(inputHtml); + + // if last line contains only carriage return remove it + if (lines[lines.length-1].trim() === '') { + lines.pop(); + } + + if (lines.length > 1 || options.singleLine) { + var html = ''; + + for (var i = 0, l = lines.length; i < l; i++) { + html += format( + '
' + + '' + + '{6}' + + '
{1}
', [ TABLE_NAME, html ]); + } + + return inputHtml; + } + + /** + * @param {HTMLElement} element Code block. + * @param {Object} options External API options. + * @returns {Object} Internal API options. + */ + function mapOptions (element, options) { + options = options || {}; + return { + singleLine: getSingleLineOption(options), + startFrom: getStartFromOption(element, options) + }; + } + + function getSingleLineOption (options) { + var defaultValue = false; + if (!!options.singleLine) { + return options.singleLine; + } + return defaultValue; + } + + function getStartFromOption (element, options) { + var defaultValue = 1; + var startFrom = defaultValue; + + if (isFinite(options.startFrom)) { + startFrom = options.startFrom; + } + + // can be overridden because local option is priority + var value = getAttribute(element, 'data-ln-start-from'); + if (value !== null) { + startFrom = toNumber(value, defaultValue); + } + + return startFrom; + } + + /** + * Recursive method for fix multi-line elements implementation in highlight.js + * Doing deep passage on child nodes. + * @param {HTMLElement} element + */ + function duplicateMultilineNodes (element) { + var nodes = element.childNodes; + for (var node in nodes) { + if (nodes.hasOwnProperty(node)) { + var child = nodes[node]; + if (getLinesCount(child.textContent) > 0) { + if (child.childNodes.length > 0) { + duplicateMultilineNodes(child); + } else { + duplicateMultilineNode(child.parentNode); + } + } + } + } + } + + /** + * Method for fix multi-line elements implementation in highlight.js + * @param {HTMLElement} element + */ + function duplicateMultilineNode (element) { + var className = element.className; + + if ( ! /hljs-/.test(className)) return; + + var lines = getLines(element.innerHTML); + + for (var i = 0, result = ''; i < lines.length; i++) { + var lineText = lines[i].length > 0 ? lines[i] : ' '; + result += format('{1}\n', [ className, lineText ]); + } + + element.innerHTML = result.trim(); + } + + function getLines (text) { + if (text.length === 0) return []; + return text.split(BREAK_LINE_REGEXP); + } + + function getLinesCount (text) { + return (text.trim().match(BREAK_LINE_REGEXP) || []).length; + } + + /// + /// HELPERS + /// + + function async (func) { + w.setTimeout(func, 0); + } + + /** + * {@link https://wcoder.github.io/notes/string-format-for-string-formating-in-javascript} + * @param {string} format + * @param {array} args + */ + function format (format, args) { + return format.replace(/\{(\d+)\}/g, function(m, n){ + return args[n] !== undefined ? args[n] : m; + }); + } + + /** + * @param {HTMLElement} element Code block. + * @param {String} attrName Attribute name. + * @returns {String} Attribute value or empty. + */ + function getAttribute (element, attrName) { + return element.hasAttribute(attrName) ? element.getAttribute(attrName) : null; + } + + /** + * @param {String} str Source string. + * @param {Number} fallback Fallback value. + * @returns Parsed number or fallback value. + */ + function toNumber (str, fallback) { + if (!str) return fallback; + var number = Number(str); + return isFinite(number) ? number : fallback; + } + +}(window, document)); diff --git a/docs/html/game_editor/scripts/third-party/hljs-line-num.js b/docs/html/game_editor/scripts/third-party/hljs-line-num.js new file mode 100644 index 000000000..facdf6bed --- /dev/null +++ b/docs/html/game_editor/scripts/third-party/hljs-line-num.js @@ -0,0 +1 @@ +!function(r,o){"use strict";var e,l="hljs-ln",s="hljs-ln-line",f="hljs-ln-code",c="hljs-ln-numbers",u="hljs-ln-n",h="data-line-number",n=/\r\n|\r|\n/g;function t(e){for(var n=e.toString(),t=e.anchorNode;"TD"!==t.nodeName;)t=t.parentNode;for(var r=e.focusNode;"TD"!==r.nodeName;)r=r.parentNode;var e=parseInt(t.dataset.lineNumber),o=parseInt(r.dataset.lineNumber);if(e==o)return n;var a,i=t.textContent,l=r.textContent;for(o{6}',[s,c,u,h,f,a+t.startFrom,0{1}',[l,o])}return e}function m(e){var n=e.className;if(/hljs-/.test(n)){for(var t=g(e.innerHTML),r=0,o="";r{1}\n',[n,0/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + /** + * performs a shallow merge of multiple objects into one + * + * @template T + * @param {T} original + * @param {Record[]} objects + * @returns {T} a single new object + */ + function inherit$1(original, ...objects) { + /** @type Record */ + const result = Object.create(null); + + for (const key in original) { + result[key] = original[key]; + } + objects.forEach(function (obj) { + for (const key in obj) { + result[key] = obj[key]; + } + }); + return /** @type {T} */ (result); + } + + /** + * @typedef {object} Renderer + * @property {(text: string) => void} addText + * @property {(node: Node) => void} openNode + * @property {(node: Node) => void} closeNode + * @property {() => string} value + */ + + /** @typedef {{kind?: string, sublanguage?: boolean}} Node */ + /** @typedef {{walk: (r: Renderer) => void}} Tree */ + /** */ + + const SPAN_CLOSE = ''; + + /** + * Determines if a node needs to be wrapped in + * + * @param {Node} node */ + const emitsWrappingTags = (node) => { + return !!node.kind; + }; + + /** + * + * @param {string} name + * @param {{prefix:string}} options + */ + const expandScopeName = (name, { prefix }) => { + if (name.includes(".")) { + const pieces = name.split("."); + return [ + `${prefix}${pieces.shift()}`, + ...(pieces.map((x, i) => `${x}${"_".repeat(i + 1)}`)) + ].join(" "); + } + return `${prefix}${name}`; + }; + + /** @type {Renderer} */ + class HTMLRenderer { + /** + * Creates a new HTMLRenderer + * + * @param {Tree} parseTree - the parse tree (must support `walk` API) + * @param {{classPrefix: string}} options + */ + constructor(parseTree, options) { + this.buffer = ""; + this.classPrefix = options.classPrefix; + parseTree.walk(this); + } + + /** + * Adds texts to the output stream + * + * @param {string} text */ + addText(text) { + this.buffer += escapeHTML(text); + } + + /** + * Adds a node open to the output stream (if needed) + * + * @param {Node} node */ + openNode(node) { + if (!emitsWrappingTags(node)) return; + + let scope = node.kind; + if (node.sublanguage) { + scope = `language-${scope}`; + } else { + scope = expandScopeName(scope, { prefix: this.classPrefix }); + } + this.span(scope); + } + + /** + * Adds a node close to the output stream (if needed) + * + * @param {Node} node */ + closeNode(node) { + if (!emitsWrappingTags(node)) return; + + this.buffer += SPAN_CLOSE; + } + + /** + * returns the accumulated buffer + */ + value() { + return this.buffer; + } + + // helpers + + /** + * Builds a span element + * + * @param {string} className */ + span(className) { + this.buffer += ``; + } + } + + /** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} | string} Node */ + /** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} } DataNode */ + /** @typedef {import('highlight.js').Emitter} Emitter */ + /** */ + + class TokenTree { + constructor() { + /** @type DataNode */ + this.rootNode = { children: [] }; + this.stack = [this.rootNode]; + } + + get top() { + return this.stack[this.stack.length - 1]; + } + + get root() { return this.rootNode; } + + /** @param {Node} node */ + add(node) { + this.top.children.push(node); + } + + /** @param {string} kind */ + openNode(kind) { + /** @type Node */ + const node = { kind, children: [] }; + this.add(node); + this.stack.push(node); + } + + closeNode() { + if (this.stack.length > 1) { + return this.stack.pop(); + } + // eslint-disable-next-line no-undefined + return undefined; + } + + closeAllNodes() { + while (this.closeNode()); + } + + toJSON() { + return JSON.stringify(this.rootNode, null, 4); + } + + /** + * @typedef { import("./html_renderer").Renderer } Renderer + * @param {Renderer} builder + */ + walk(builder) { + // this does not + return this.constructor._walk(builder, this.rootNode); + // this works + // return TokenTree._walk(builder, this.rootNode); + } + + /** + * @param {Renderer} builder + * @param {Node} node + */ + static _walk(builder, node) { + if (typeof node === "string") { + builder.addText(node); + } else if (node.children) { + builder.openNode(node); + node.children.forEach((child) => this._walk(builder, child)); + builder.closeNode(node); + } + return builder; + } + + /** + * @param {Node} node + */ + static _collapse(node) { + if (typeof node === "string") return; + if (!node.children) return; + + if (node.children.every(el => typeof el === "string")) { + // node.text = node.children.join(""); + // delete node.children; + node.children = [node.children.join("")]; + } else { + node.children.forEach((child) => { + TokenTree._collapse(child); + }); + } + } + } + + /** + Currently this is all private API, but this is the minimal API necessary + that an Emitter must implement to fully support the parser. + + Minimal interface: + + - addKeyword(text, kind) + - addText(text) + - addSublanguage(emitter, subLanguageName) + - finalize() + - openNode(kind) + - closeNode() + - closeAllNodes() + - toHTML() + + */ + + /** + * @implements {Emitter} + */ + class TokenTreeEmitter extends TokenTree { + /** + * @param {*} options + */ + constructor(options) { + super(); + this.options = options; + } + + /** + * @param {string} text + * @param {string} kind + */ + addKeyword(text, kind) { + if (text === "") { return; } + + this.openNode(kind); + this.addText(text); + this.closeNode(); + } + + /** + * @param {string} text + */ + addText(text) { + if (text === "") { return; } + + this.add(text); + } + + /** + * @param {Emitter & {root: DataNode}} emitter + * @param {string} name + */ + addSublanguage(emitter, name) { + /** @type DataNode */ + const node = emitter.root; + node.kind = name; + node.sublanguage = true; + this.add(node); + } + + toHTML() { + const renderer = new HTMLRenderer(this, this.options); + return renderer.value(); + } + + finalize() { + return true; + } + } + + /** + * @param {string} value + * @returns {RegExp} + * */ + + /** + * @param {RegExp | string } re + * @returns {string} + */ + function source(re) { + if (!re) return null; + if (typeof re === "string") return re; + + return re.source; + } + + /** + * @param {RegExp | string } re + * @returns {string} + */ + function lookahead(re) { + return concat('(?=', re, ')'); + } + + /** + * @param {RegExp | string } re + * @returns {string} + */ + function optional(re) { + return concat('(?:', re, ')?'); + } + + /** + * @param {...(RegExp | string) } args + * @returns {string} + */ + function concat(...args) { + const joined = args.map((x) => source(x)).join(""); + return joined; + } + + function stripOptionsFromArgs(args) { + const opts = args[args.length - 1]; + + if (typeof opts === 'object' && opts.constructor === Object) { + args.splice(args.length - 1, 1); + return opts; + } else { + return {}; + } + } + + /** + * Any of the passed expresssions may match + * + * Creates a huge this | this | that | that match + * @param {(RegExp | string)[] } args + * @returns {string} + */ + function either(...args) { + const opts = stripOptionsFromArgs(args); + const joined = '(' + + (opts.capture ? "" : "?:") + + args.map((x) => source(x)).join("|") + ")"; + return joined; + } + + /** + * @param {RegExp} re + * @returns {number} + */ + function countMatchGroups(re) { + return (new RegExp(re.toString() + '|')).exec('').length - 1; + } + + /** + * Does lexeme start with a regular expression match at the beginning + * @param {RegExp} re + * @param {string} lexeme + */ + function startsWith(re, lexeme) { + const match = re && re.exec(lexeme); + return match && match.index === 0; + } + + // BACKREF_RE matches an open parenthesis or backreference. To avoid + // an incorrect parse, it additionally matches the following: + // - [...] elements, where the meaning of parentheses and escapes change + // - other escape sequences, so we do not misparse escape sequences as + // interesting elements + // - non-matching or lookahead parentheses, which do not capture. These + // follow the '(' with a '?'. + const BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./; + + // **INTERNAL** Not intended for outside usage + // join logically computes regexps.join(separator), but fixes the + // backreferences so they continue to match. + // it also places each individual regular expression into it's own + // match group, keeping track of the sequencing of those match groups + // is currently an exercise for the caller. :-) + /** + * @param {(string | RegExp)[]} regexps + * @param {{joinWith: string}} opts + * @returns {string} + */ + function _rewriteBackreferences(regexps, { joinWith }) { + let numCaptures = 0; + + return regexps.map((regex) => { + numCaptures += 1; + const offset = numCaptures; + let re = source(regex); + let out = ''; + + while (re.length > 0) { + const match = BACKREF_RE.exec(re); + if (!match) { + out += re; + break; + } + out += re.substring(0, match.index); + re = re.substring(match.index + match[0].length); + if (match[0][0] === '\\' && match[1]) { + // Adjust the backreference. + out += '\\' + String(Number(match[1]) + offset); + } else { + out += match[0]; + if (match[0] === '(') { + numCaptures++; + } + } + } + return out; + }).map(re => `(${re})`).join(joinWith); + } + + /** @typedef {import('highlight.js').Mode} Mode */ + /** @typedef {import('highlight.js').ModeCallback} ModeCallback */ + + // Common regexps + const MATCH_NOTHING_RE = /\b\B/; + const IDENT_RE$1 = '[a-zA-Z]\\w*'; + const UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\w*'; + const NUMBER_RE = '\\b\\d+(\\.\\d+)?'; + const C_NUMBER_RE = '(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float + const BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b... + const RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~'; + + /** + * @param { Partial & {binary?: string | RegExp} } opts + */ + const SHEBANG = (opts = {}) => { + const beginShebang = /^#![ ]*\//; + if (opts.binary) { + opts.begin = concat( + beginShebang, + /.*\b/, + opts.binary, + /\b.*/); + } + return inherit$1({ + scope: 'meta', + begin: beginShebang, + end: /$/, + relevance: 0, + /** @type {ModeCallback} */ + "on:begin": (m, resp) => { + if (m.index !== 0) resp.ignoreMatch(); + } + }, opts); + }; + + // Common modes + const BACKSLASH_ESCAPE = { + begin: '\\\\[\\s\\S]', relevance: 0 + }; + const APOS_STRING_MODE = { + scope: 'string', + begin: '\'', + end: '\'', + illegal: '\\n', + contains: [BACKSLASH_ESCAPE] + }; + const QUOTE_STRING_MODE = { + scope: 'string', + begin: '"', + end: '"', + illegal: '\\n', + contains: [BACKSLASH_ESCAPE] + }; + const PHRASAL_WORDS_MODE = { + begin: /\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ + }; + /** + * Creates a comment mode + * + * @param {string | RegExp} begin + * @param {string | RegExp} end + * @param {Mode | {}} [modeOptions] + * @returns {Partial} + */ + const COMMENT = function (begin, end, modeOptions = {}) { + const mode = inherit$1( + { + scope: 'comment', + begin, + end, + contains: [] + }, + modeOptions + ); + mode.contains.push({ + scope: 'doctag', + // hack to avoid the space from being included. the space is necessary to + // match here to prevent the plain text rule below from gobbling up doctags + begin: '[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)', + end: /(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/, + excludeBegin: true, + relevance: 0 + }); + const ENGLISH_WORD = either( + // list of common 1 and 2 letter words in English + "I", + "a", + "is", + "so", + "us", + "to", + "at", + "if", + "in", + "it", + "on", + // note: this is not an exhaustive list of contractions, just popular ones + /[A-Za-z]+['](d|ve|re|ll|t|s|n)/, // contractions - can't we'd they're let's, etc + /[A-Za-z]+[-][a-z]+/, // `no-way`, etc. + /[A-Za-z][a-z]{2,}/ // allow capitalized words at beginning of sentences + ); + // looking like plain text, more likely to be a comment + mode.contains.push( + { + // TODO: how to include ", (, ) without breaking grammars that use these for + // comment delimiters? + // begin: /[ ]+([()"]?([A-Za-z'-]{3,}|is|a|I|so|us|[tT][oO]|at|if|in|it|on)[.]?[()":]?([.][ ]|[ ]|\))){3}/ + // --- + + // this tries to find sequences of 3 english words in a row (without any + // "programming" type syntax) this gives us a strong signal that we've + // TRULY found a comment - vs perhaps scanning with the wrong language. + // It's possible to find something that LOOKS like the start of the + // comment - but then if there is no readable text - good chance it is a + // false match and not a comment. + // + // for a visual example please see: + // https://github.com/highlightjs/highlight.js/issues/2827 + + begin: concat( + /[ ]+/, // necessary to prevent us gobbling up doctags like /* @author Bob Mcgill */ + '(', + ENGLISH_WORD, + /[.]?[:]?([.][ ]|[ ])/, + '){3}') // look for 3 words in a row + } + ); + return mode; + }; + const C_LINE_COMMENT_MODE = COMMENT('//', '$'); + const C_BLOCK_COMMENT_MODE = COMMENT('/\\*', '\\*/'); + const HASH_COMMENT_MODE = COMMENT('#', '$'); + const NUMBER_MODE = { + scope: 'number', + begin: NUMBER_RE, + relevance: 0 + }; + const C_NUMBER_MODE = { + scope: 'number', + begin: C_NUMBER_RE, + relevance: 0 + }; + const BINARY_NUMBER_MODE = { + scope: 'number', + begin: BINARY_NUMBER_RE, + relevance: 0 + }; + const REGEXP_MODE = { + // this outer rule makes sure we actually have a WHOLE regex and not simply + // an expression such as: + // + // 3 / something + // + // (which will then blow up when regex's `illegal` sees the newline) + begin: /(?=\/[^/\n]*\/)/, + contains: [{ + scope: 'regexp', + begin: /\//, + end: /\/[gimuy]*/, + illegal: /\n/, + contains: [ + BACKSLASH_ESCAPE, + { + begin: /\[/, + end: /\]/, + relevance: 0, + contains: [BACKSLASH_ESCAPE] + } + ] + }] + }; + const TITLE_MODE = { + scope: 'title', + begin: IDENT_RE$1, + relevance: 0 + }; + const UNDERSCORE_TITLE_MODE = { + scope: 'title', + begin: UNDERSCORE_IDENT_RE, + relevance: 0 + }; + const METHOD_GUARD = { + // excludes method names from keyword processing + begin: '\\.\\s*' + UNDERSCORE_IDENT_RE, + relevance: 0 + }; + + /** + * Adds end same as begin mechanics to a mode + * + * Your mode must include at least a single () match group as that first match + * group is what is used for comparison + * @param {Partial} mode + */ + const END_SAME_AS_BEGIN = function (mode) { + return Object.assign(mode, + { + /** @type {ModeCallback} */ + 'on:begin': (m, resp) => { resp.data._beginMatch = m[1]; }, + /** @type {ModeCallback} */ + 'on:end': (m, resp) => { if (resp.data._beginMatch !== m[1]) resp.ignoreMatch(); } + }); + }; + + var MODES$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + MATCH_NOTHING_RE: MATCH_NOTHING_RE, + IDENT_RE: IDENT_RE$1, + UNDERSCORE_IDENT_RE: UNDERSCORE_IDENT_RE, + NUMBER_RE: NUMBER_RE, + C_NUMBER_RE: C_NUMBER_RE, + BINARY_NUMBER_RE: BINARY_NUMBER_RE, + RE_STARTERS_RE: RE_STARTERS_RE, + SHEBANG: SHEBANG, + BACKSLASH_ESCAPE: BACKSLASH_ESCAPE, + APOS_STRING_MODE: APOS_STRING_MODE, + QUOTE_STRING_MODE: QUOTE_STRING_MODE, + PHRASAL_WORDS_MODE: PHRASAL_WORDS_MODE, + COMMENT: COMMENT, + C_LINE_COMMENT_MODE: C_LINE_COMMENT_MODE, + C_BLOCK_COMMENT_MODE: C_BLOCK_COMMENT_MODE, + HASH_COMMENT_MODE: HASH_COMMENT_MODE, + NUMBER_MODE: NUMBER_MODE, + C_NUMBER_MODE: C_NUMBER_MODE, + BINARY_NUMBER_MODE: BINARY_NUMBER_MODE, + REGEXP_MODE: REGEXP_MODE, + TITLE_MODE: TITLE_MODE, + UNDERSCORE_TITLE_MODE: UNDERSCORE_TITLE_MODE, + METHOD_GUARD: METHOD_GUARD, + END_SAME_AS_BEGIN: END_SAME_AS_BEGIN + }); + + /** + @typedef {import('highlight.js').CallbackResponse} CallbackResponse + @typedef {import('highlight.js').CompilerExt} CompilerExt + */ + + // Grammar extensions / plugins + // See: https://github.com/highlightjs/highlight.js/issues/2833 + + // Grammar extensions allow "syntactic sugar" to be added to the grammar modes + // without requiring any underlying changes to the compiler internals. + + // `compileMatch` being the perfect small example of now allowing a grammar + // author to write `match` when they desire to match a single expression rather + // than being forced to use `begin`. The extension then just moves `match` into + // `begin` when it runs. Ie, no features have been added, but we've just made + // the experience of writing (and reading grammars) a little bit nicer. + + // ------ + + // TODO: We need negative look-behind support to do this properly + /** + * Skip a match if it has a preceding dot + * + * This is used for `beginKeywords` to prevent matching expressions such as + * `bob.keyword.do()`. The mode compiler automatically wires this up as a + * special _internal_ 'on:begin' callback for modes with `beginKeywords` + * @param {RegExpMatchArray} match + * @param {CallbackResponse} response + */ + function skipIfHasPrecedingDot(match, response) { + const before = match.input[match.index - 1]; + if (before === ".") { + response.ignoreMatch(); + } + } + + /** + * + * @type {CompilerExt} + */ + function scopeClassName(mode, _parent) { + // eslint-disable-next-line no-undefined + if (mode.className !== undefined) { + mode.scope = mode.className; + delete mode.className; + } + } + + /** + * `beginKeywords` syntactic sugar + * @type {CompilerExt} + */ + function beginKeywords(mode, parent) { + if (!parent) return; + if (!mode.beginKeywords) return; + + // for languages with keywords that include non-word characters checking for + // a word boundary is not sufficient, so instead we check for a word boundary + // or whitespace - this does no harm in any case since our keyword engine + // doesn't allow spaces in keywords anyways and we still check for the boundary + // first + mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?!\\.)(?=\\b|\\s)'; + mode.__beforeBegin = skipIfHasPrecedingDot; + mode.keywords = mode.keywords || mode.beginKeywords; + delete mode.beginKeywords; + + // prevents double relevance, the keywords themselves provide + // relevance, the mode doesn't need to double it + // eslint-disable-next-line no-undefined + if (mode.relevance === undefined) mode.relevance = 0; + } + + /** + * Allow `illegal` to contain an array of illegal values + * @type {CompilerExt} + */ + function compileIllegal(mode, _parent) { + if (!Array.isArray(mode.illegal)) return; + + mode.illegal = either(...mode.illegal); + } + + /** + * `match` to match a single expression for readability + * @type {CompilerExt} + */ + function compileMatch(mode, _parent) { + if (!mode.match) return; + if (mode.begin || mode.end) throw new Error("begin & end are not supported with match"); + + mode.begin = mode.match; + delete mode.match; + } + + /** + * provides the default 1 relevance to all modes + * @type {CompilerExt} + */ + function compileRelevance(mode, _parent) { + // eslint-disable-next-line no-undefined + if (mode.relevance === undefined) mode.relevance = 1; + } + + // allow beforeMatch to act as a "qualifier" for the match + // the full match begin must be [beforeMatch][begin] + const beforeMatchExt = (mode, parent) => { + if (!mode.beforeMatch) return; + // starts conflicts with endsParent which we need to make sure the child + // rule is not matched multiple times + if (mode.starts) throw new Error("beforeMatch cannot be used with starts"); + + const originalMode = Object.assign({}, mode); + Object.keys(mode).forEach((key) => { delete mode[key]; }); + + mode.keywords = originalMode.keywords; + mode.begin = concat(originalMode.beforeMatch, lookahead(originalMode.begin)); + mode.starts = { + relevance: 0, + contains: [ + Object.assign(originalMode, { endsParent: true }) + ] + }; + mode.relevance = 0; + + delete originalMode.beforeMatch; + }; + + // keywords that should have no default relevance value + const COMMON_KEYWORDS = [ + 'of', + 'and', + 'for', + 'in', + 'not', + 'or', + 'if', + 'then', + 'parent', // common variable name + 'list', // common variable name + 'value' // common variable name + ]; + + const DEFAULT_KEYWORD_SCOPE = "keyword"; + + /** + * Given raw keywords from a language definition, compile them. + * + * @param {string | Record | Array} rawKeywords + * @param {boolean} caseInsensitive + */ + function compileKeywords(rawKeywords, caseInsensitive, scopeName = DEFAULT_KEYWORD_SCOPE) { + /** @type KeywordDict */ + const compiledKeywords = Object.create(null); + + // input can be a string of keywords, an array of keywords, or a object with + // named keys representing scopeName (which can then point to a string or array) + if (typeof rawKeywords === 'string') { + compileList(scopeName, rawKeywords.split(" ")); + } else if (Array.isArray(rawKeywords)) { + compileList(scopeName, rawKeywords); + } else { + Object.keys(rawKeywords).forEach(function (scopeName) { + // collapse all our objects back into the parent object + Object.assign( + compiledKeywords, + compileKeywords(rawKeywords[scopeName], caseInsensitive, scopeName) + ); + }); + } + return compiledKeywords; + + // --- + + /** + * Compiles an individual list of keywords + * + * Ex: "for if when while|5" + * + * @param {string} scopeName + * @param {Array} keywordList + */ + function compileList(scopeName, keywordList) { + if (caseInsensitive) { + keywordList = keywordList.map(x => x.toLowerCase()); + } + keywordList.forEach(function (keyword) { + const pair = keyword.split('|'); + compiledKeywords[pair[0]] = [scopeName, scoreForKeyword(pair[0], pair[1])]; + }); + } + } + + /** + * Returns the proper score for a given keyword + * + * Also takes into account comment keywords, which will be scored 0 UNLESS + * another score has been manually assigned. + * @param {string} keyword + * @param {string} [providedScore] + */ + function scoreForKeyword(keyword, providedScore) { + // manual scores always win over common keywords + // so you can force a score of 1 if you really insist + if (providedScore) { + return Number(providedScore); + } + + return commonKeyword(keyword) ? 0 : 1; + } + + /** + * Determines if a given keyword is common or not + * + * @param {string} keyword */ + function commonKeyword(keyword) { + return COMMON_KEYWORDS.includes(keyword.toLowerCase()); + } + + /* + + For the reasoning behind this please see: + https://github.com/highlightjs/highlight.js/issues/2880#issuecomment-747275419 + + */ + + /** + * @type {Record} + */ + const seenDeprecations = {}; + + /** + * @param {string} message + */ + const error = (message) => { + console.error(message); + }; + + /** + * @param {string} message + * @param {any} args + */ + const warn = (message, ...args) => { + console.log(`WARN: ${message}`, ...args); + }; + + /** + * @param {string} version + * @param {string} message + */ + const deprecated = (version, message) => { + if (seenDeprecations[`${version}/${message}`]) return; + + console.log(`Deprecated as of ${version}. ${message}`); + seenDeprecations[`${version}/${message}`] = true; + }; + + /* eslint-disable no-throw-literal */ + + /** + @typedef {import('highlight.js').CompiledMode} CompiledMode + */ + + const MultiClassError = new Error(); + + /** + * Renumbers labeled scope names to account for additional inner match + * groups that otherwise would break everything. + * + * Lets say we 3 match scopes: + * + * { 1 => ..., 2 => ..., 3 => ... } + * + * So what we need is a clean match like this: + * + * (a)(b)(c) => [ "a", "b", "c" ] + * + * But this falls apart with inner match groups: + * + * (a)(((b)))(c) => ["a", "b", "b", "b", "c" ] + * + * Our scopes are now "out of alignment" and we're repeating `b` 3 times. + * What needs to happen is the numbers are remapped: + * + * { 1 => ..., 2 => ..., 5 => ... } + * + * We also need to know that the ONLY groups that should be output + * are 1, 2, and 5. This function handles this behavior. + * + * @param {CompiledMode} mode + * @param {Array} regexes + * @param {{key: "beginScope"|"endScope"}} opts + */ + function remapScopeNames(mode, regexes, { key }) { + let offset = 0; + const scopeNames = mode[key]; + /** @type Record */ + const emit = {}; + /** @type Record */ + const positions = {}; + + for (let i = 1; i <= regexes.length; i++) { + positions[i + offset] = scopeNames[i]; + emit[i + offset] = true; + offset += countMatchGroups(regexes[i - 1]); + } + // we use _emit to keep track of which match groups are "top-level" to avoid double + // output from inside match groups + mode[key] = positions; + mode[key]._emit = emit; + mode[key]._multi = true; + } + + /** + * @param {CompiledMode} mode + */ + function beginMultiClass(mode) { + if (!Array.isArray(mode.begin)) return; + + if (mode.skip || mode.excludeBegin || mode.returnBegin) { + error("skip, excludeBegin, returnBegin not compatible with beginScope: {}"); + throw MultiClassError; + } + + if (typeof mode.beginScope !== "object" || mode.beginScope === null) { + error("beginScope must be object"); + throw MultiClassError; + } + + remapScopeNames(mode, mode.begin, { key: "beginScope" }); + mode.begin = _rewriteBackreferences(mode.begin, { joinWith: "" }); + } + + /** + * @param {CompiledMode} mode + */ + function endMultiClass(mode) { + if (!Array.isArray(mode.end)) return; + + if (mode.skip || mode.excludeEnd || mode.returnEnd) { + error("skip, excludeEnd, returnEnd not compatible with endScope: {}"); + throw MultiClassError; + } + + if (typeof mode.endScope !== "object" || mode.endScope === null) { + error("endScope must be object"); + throw MultiClassError; + } + + remapScopeNames(mode, mode.end, { key: "endScope" }); + mode.end = _rewriteBackreferences(mode.end, { joinWith: "" }); + } + + /** + * this exists only to allow `scope: {}` to be used beside `match:` + * Otherwise `beginScope` would necessary and that would look weird + + { + match: [ /def/, /\w+/ ] + scope: { 1: "keyword" , 2: "title" } + } + + * @param {CompiledMode} mode + */ + function scopeSugar(mode) { + if (mode.scope && typeof mode.scope === "object" && mode.scope !== null) { + mode.beginScope = mode.scope; + delete mode.scope; + } + } + + /** + * @param {CompiledMode} mode + */ + function MultiClass(mode) { + scopeSugar(mode); + + if (typeof mode.beginScope === "string") { + mode.beginScope = { _wrap: mode.beginScope }; + } + if (typeof mode.endScope === "string") { + mode.endScope = { _wrap: mode.endScope }; + } + + beginMultiClass(mode); + endMultiClass(mode); + } + + /** + @typedef {import('highlight.js').Mode} Mode + @typedef {import('highlight.js').CompiledMode} CompiledMode + @typedef {import('highlight.js').Language} Language + @typedef {import('highlight.js').HLJSPlugin} HLJSPlugin + @typedef {import('highlight.js').CompiledLanguage} CompiledLanguage + */ + + // compilation + + /** + * Compiles a language definition result + * + * Given the raw result of a language definition (Language), compiles this so + * that it is ready for highlighting code. + * @param {Language} language + * @returns {CompiledLanguage} + */ + function compileLanguage(language) { + /** + * Builds a regex with the case sensitivity of the current language + * + * @param {RegExp | string} value + * @param {boolean} [global] + */ + function langRe(value, global) { + return new RegExp( + source(value), + 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '') + ); + } + + /** + Stores multiple regular expressions and allows you to quickly search for + them all in a string simultaneously - returning the first match. It does + this by creating a huge (a|b|c) regex - each individual item wrapped with () + and joined by `|` - using match groups to track position. When a match is + found checking which position in the array has content allows us to figure + out which of the original regexes / match groups triggered the match. + + The match object itself (the result of `Regex.exec`) is returned but also + enhanced by merging in any meta-data that was registered with the regex. + This is how we keep track of which mode matched, and what type of rule + (`illegal`, `begin`, end, etc). + */ + class MultiRegex { + constructor() { + this.matchIndexes = {}; + // @ts-ignore + this.regexes = []; + this.matchAt = 1; + this.position = 0; + } + + // @ts-ignore + addRule(re, opts) { + opts.position = this.position++; + // @ts-ignore + this.matchIndexes[this.matchAt] = opts; + this.regexes.push([opts, re]); + this.matchAt += countMatchGroups(re) + 1; + } + + compile() { + if (this.regexes.length === 0) { + // avoids the need to check length every time exec is called + // @ts-ignore + this.exec = () => null; + } + const terminators = this.regexes.map(el => el[1]); + this.matcherRe = langRe(_rewriteBackreferences(terminators, { joinWith: '|' }), true); + this.lastIndex = 0; + } + + /** @param {string} s */ + exec(s) { + this.matcherRe.lastIndex = this.lastIndex; + const match = this.matcherRe.exec(s); + if (!match) { return null; } + + // eslint-disable-next-line no-undefined + const i = match.findIndex((el, i) => i > 0 && el !== undefined); + // @ts-ignore + const matchData = this.matchIndexes[i]; + // trim off any earlier non-relevant match groups (ie, the other regex + // match groups that make up the multi-matcher) + match.splice(0, i); + + return Object.assign(match, matchData); + } + } + + /* + Created to solve the key deficiently with MultiRegex - there is no way to + test for multiple matches at a single location. Why would we need to do + that? In the future a more dynamic engine will allow certain matches to be + ignored. An example: if we matched say the 3rd regex in a large group but + decided to ignore it - we'd need to started testing again at the 4th + regex... but MultiRegex itself gives us no real way to do that. + + So what this class creates MultiRegexs on the fly for whatever search + position they are needed. + + NOTE: These additional MultiRegex objects are created dynamically. For most + grammars most of the time we will never actually need anything more than the + first MultiRegex - so this shouldn't have too much overhead. + + Say this is our search group, and we match regex3, but wish to ignore it. + + regex1 | regex2 | regex3 | regex4 | regex5 ' ie, startAt = 0 + + What we need is a new MultiRegex that only includes the remaining + possibilities: + + regex4 | regex5 ' ie, startAt = 3 + + This class wraps all that complexity up in a simple API... `startAt` decides + where in the array of expressions to start doing the matching. It + auto-increments, so if a match is found at position 2, then startAt will be + set to 3. If the end is reached startAt will return to 0. + + MOST of the time the parser will be setting startAt manually to 0. + */ + class ResumableMultiRegex { + constructor() { + // @ts-ignore + this.rules = []; + // @ts-ignore + this.multiRegexes = []; + this.count = 0; + + this.lastIndex = 0; + this.regexIndex = 0; + } + + // @ts-ignore + getMatcher(index) { + if (this.multiRegexes[index]) return this.multiRegexes[index]; + + const matcher = new MultiRegex(); + this.rules.slice(index).forEach(([re, opts]) => matcher.addRule(re, opts)); + matcher.compile(); + this.multiRegexes[index] = matcher; + return matcher; + } + + resumingScanAtSamePosition() { + return this.regexIndex !== 0; + } + + considerAll() { + this.regexIndex = 0; + } + + // @ts-ignore + addRule(re, opts) { + this.rules.push([re, opts]); + if (opts.type === "begin") this.count++; + } + + /** @param {string} s */ + exec(s) { + const m = this.getMatcher(this.regexIndex); + m.lastIndex = this.lastIndex; + let result = m.exec(s); + + // The following is because we have no easy way to say "resume scanning at the + // existing position but also skip the current rule ONLY". What happens is + // all prior rules are also skipped which can result in matching the wrong + // thing. Example of matching "booger": + + // our matcher is [string, "booger", number] + // + // ....booger.... + + // if "booger" is ignored then we'd really need a regex to scan from the + // SAME position for only: [string, number] but ignoring "booger" (if it + // was the first match), a simple resume would scan ahead who knows how + // far looking only for "number", ignoring potential string matches (or + // future "booger" matches that might be valid.) + + // So what we do: We execute two matchers, one resuming at the same + // position, but the second full matcher starting at the position after: + + // /--- resume first regex match here (for [number]) + // |/---- full match here for [string, "booger", number] + // vv + // ....booger.... + + // Which ever results in a match first is then used. So this 3-4 step + // process essentially allows us to say "match at this position, excluding + // a prior rule that was ignored". + // + // 1. Match "booger" first, ignore. Also proves that [string] does non match. + // 2. Resume matching for [number] + // 3. Match at index + 1 for [string, "booger", number] + // 4. If #2 and #3 result in matches, which came first? + if (this.resumingScanAtSamePosition()) { + if (result && result.index === this.lastIndex); else { // use the second matcher result + const m2 = this.getMatcher(0); + m2.lastIndex = this.lastIndex + 1; + result = m2.exec(s); + } + } + + if (result) { + this.regexIndex += result.position + 1; + if (this.regexIndex === this.count) { + // wrap-around to considering all matches again + this.considerAll(); + } + } + + return result; + } + } + + /** + * Given a mode, builds a huge ResumableMultiRegex that can be used to walk + * the content and find matches. + * + * @param {CompiledMode} mode + * @returns {ResumableMultiRegex} + */ + function buildModeRegex(mode) { + const mm = new ResumableMultiRegex(); + + mode.contains.forEach(term => mm.addRule(term.begin, { rule: term, type: "begin" })); + + if (mode.terminatorEnd) { + mm.addRule(mode.terminatorEnd, { type: "end" }); + } + if (mode.illegal) { + mm.addRule(mode.illegal, { type: "illegal" }); + } + + return mm; + } + + /** skip vs abort vs ignore + * + * @skip - The mode is still entered and exited normally (and contains rules apply), + * but all content is held and added to the parent buffer rather than being + * output when the mode ends. Mostly used with `sublanguage` to build up + * a single large buffer than can be parsed by sublanguage. + * + * - The mode begin ands ends normally. + * - Content matched is added to the parent mode buffer. + * - The parser cursor is moved forward normally. + * + * @abort - A hack placeholder until we have ignore. Aborts the mode (as if it + * never matched) but DOES NOT continue to match subsequent `contains` + * modes. Abort is bad/suboptimal because it can result in modes + * farther down not getting applied because an earlier rule eats the + * content but then aborts. + * + * - The mode does not begin. + * - Content matched by `begin` is added to the mode buffer. + * - The parser cursor is moved forward accordingly. + * + * @ignore - Ignores the mode (as if it never matched) and continues to match any + * subsequent `contains` modes. Ignore isn't technically possible with + * the current parser implementation. + * + * - The mode does not begin. + * - Content matched by `begin` is ignored. + * - The parser cursor is not moved forward. + */ + + /** + * Compiles an individual mode + * + * This can raise an error if the mode contains certain detectable known logic + * issues. + * @param {Mode} mode + * @param {CompiledMode | null} [parent] + * @returns {CompiledMode | never} + */ + function compileMode(mode, parent) { + const cmode = /** @type CompiledMode */ (mode); + if (mode.isCompiled) return cmode; + + [ + scopeClassName, + // do this early so compiler extensions generally don't have to worry about + // the distinction between match/begin + compileMatch, + MultiClass, + beforeMatchExt + ].forEach(ext => ext(mode, parent)); + + language.compilerExtensions.forEach(ext => ext(mode, parent)); + + // __beforeBegin is considered private API, internal use only + mode.__beforeBegin = null; + + [ + beginKeywords, + // do this later so compiler extensions that come earlier have access to the + // raw array if they wanted to perhaps manipulate it, etc. + compileIllegal, + // default to 1 relevance if not specified + compileRelevance + ].forEach(ext => ext(mode, parent)); + + mode.isCompiled = true; + + let keywordPattern = null; + if (typeof mode.keywords === "object" && mode.keywords.$pattern) { + // we need a copy because keywords might be compiled multiple times + // so we can't go deleting $pattern from the original on the first + // pass + mode.keywords = Object.assign({}, mode.keywords); + keywordPattern = mode.keywords.$pattern; + delete mode.keywords.$pattern; + } + keywordPattern = keywordPattern || /\w+/; + + if (mode.keywords) { + mode.keywords = compileKeywords(mode.keywords, language.case_insensitive); + } + + cmode.keywordPatternRe = langRe(keywordPattern, true); + + if (parent) { + if (!mode.begin) mode.begin = /\B|\b/; + cmode.beginRe = langRe(mode.begin); + if (!mode.end && !mode.endsWithParent) mode.end = /\B|\b/; + if (mode.end) cmode.endRe = langRe(mode.end); + cmode.terminatorEnd = source(mode.end) || ''; + if (mode.endsWithParent && parent.terminatorEnd) { + cmode.terminatorEnd += (mode.end ? '|' : '') + parent.terminatorEnd; + } + } + if (mode.illegal) cmode.illegalRe = langRe(/** @type {RegExp | string} */(mode.illegal)); + if (!mode.contains) mode.contains = []; + + mode.contains = [].concat(...mode.contains.map(function (c) { + return expandOrCloneMode(c === 'self' ? mode : c); + })); + mode.contains.forEach(function (c) { compileMode(/** @type Mode */(c), cmode); }); + + if (mode.starts) { + compileMode(mode.starts, parent); + } + + cmode.matcher = buildModeRegex(cmode); + return cmode; + } + + if (!language.compilerExtensions) language.compilerExtensions = []; + + // self is not valid at the top-level + if (language.contains && language.contains.includes('self')) { + throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation."); + } + + // we need a null object, which inherit will guarantee + language.classNameAliases = inherit$1(language.classNameAliases || {}); + + return compileMode(/** @type Mode */(language)); + } + + /** + * Determines if a mode has a dependency on it's parent or not + * + * If a mode does have a parent dependency then often we need to clone it if + * it's used in multiple places so that each copy points to the correct parent, + * where-as modes without a parent can often safely be re-used at the bottom of + * a mode chain. + * + * @param {Mode | null} mode + * @returns {boolean} - is there a dependency on the parent? + * */ + function dependencyOnParent(mode) { + if (!mode) return false; + + return mode.endsWithParent || dependencyOnParent(mode.starts); + } + + /** + * Expands a mode or clones it if necessary + * + * This is necessary for modes with parental dependenceis (see notes on + * `dependencyOnParent`) and for nodes that have `variants` - which must then be + * exploded into their own individual modes at compile time. + * + * @param {Mode} mode + * @returns {Mode | Mode[]} + * */ + function expandOrCloneMode(mode) { + if (mode.variants && !mode.cachedVariants) { + mode.cachedVariants = mode.variants.map(function (variant) { + return inherit$1(mode, { variants: null }, variant); + }); + } + + // EXPAND + // if we have variants then essentially "replace" the mode with the variants + // this happens in compileMode, where this function is called from + if (mode.cachedVariants) { + return mode.cachedVariants; + } + + // CLONE + // if we have dependencies on parents then we need a unique + // instance of ourselves, so we can be reused with many + // different parents without issue + if (dependencyOnParent(mode)) { + return inherit$1(mode, { starts: mode.starts ? inherit$1(mode.starts) : null }); + } + + if (Object.isFrozen(mode)) { + return inherit$1(mode); + } + + // no special dependency issues, just return ourselves + return mode; + } + + var version = "11.0.0-beta1"; + + /* + Syntax highlighting with language autodetection. + https://highlightjs.org/ + */ + + /** + @typedef {import('highlight.js').Mode} Mode + @typedef {import('highlight.js').CompiledMode} CompiledMode + @typedef {import('highlight.js').Language} Language + @typedef {import('highlight.js').HLJSApi} HLJSApi + @typedef {import('highlight.js').HLJSPlugin} HLJSPlugin + @typedef {import('highlight.js').PluginEvent} PluginEvent + @typedef {import('highlight.js').HLJSOptions} HLJSOptions + @typedef {import('highlight.js').LanguageFn} LanguageFn + @typedef {import('highlight.js').HighlightedHTMLElement} HighlightedHTMLElement + @typedef {import('highlight.js').BeforeHighlightContext} BeforeHighlightContext + @typedef {import('highlight.js/private').MatchType} MatchType + @typedef {import('highlight.js/private').KeywordData} KeywordData + @typedef {import('highlight.js/private').EnhancedMatch} EnhancedMatch + @typedef {import('highlight.js/private').AnnotatedError} AnnotatedError + @typedef {import('highlight.js').AutoHighlightResult} AutoHighlightResult + @typedef {import('highlight.js').HighlightOptions} HighlightOptions + @typedef {import('highlight.js').HighlightResult} HighlightResult + */ + + + const escape = escapeHTML; + const inherit = inherit$1; + const NO_MATCH = Symbol("nomatch"); + const MAX_KEYWORD_HITS = 7; + + /** + * @param {any} hljs - object that is extended (legacy) + * @returns {HLJSApi} + */ + const HLJS = function (hljs) { + // Global internal variables used within the highlight.js library. + /** @type {Record} */ + const languages = Object.create(null); + /** @type {Record} */ + const aliases = Object.create(null); + /** @type {HLJSPlugin[]} */ + const plugins = []; + + // safe/production mode - swallows more errors, tries to keep running + // even if a single syntax or parse hits a fatal error + let SAFE_MODE = true; + const LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language module?"; + /** @type {Language} */ + const PLAINTEXT_LANGUAGE = { disableAutodetect: true, name: 'Plain text', contains: [] }; + + // Global options used when within external APIs. This is modified when + // calling the `hljs.configure` function. + /** @type HLJSOptions */ + let options = { + ignoreUnescapedHTML: false, + noHighlightRe: /^(no-?highlight)$/i, + languageDetectRe: /\blang(?:uage)?-([\w-]+)\b/i, + classPrefix: 'hljs-', + cssSelector: 'pre code', + languages: null, + // beta configuration options, subject to change, welcome to discuss + // https://github.com/highlightjs/highlight.js/issues/1086 + __emitter: TokenTreeEmitter + }; + + /* Utility functions */ + + /** + * Tests a language name to see if highlighting should be skipped + * @param {string} languageName + */ + function shouldNotHighlight(languageName) { + return options.noHighlightRe.test(languageName); + } + + /** + * @param {HighlightedHTMLElement} block - the HTML element to determine language for + */ + function blockLanguage(block) { + let classes = block.className + ' '; + + classes += block.parentNode ? block.parentNode.className : ''; + + // language-* takes precedence over non-prefixed class names. + const match = options.languageDetectRe.exec(classes); + if (match) { + const language = getLanguage(match[1]); + if (!language) { + warn(LANGUAGE_NOT_FOUND.replace("{}", match[1])); + warn("Falling back to no-highlight mode for this block.", block); + } + return language ? match[1] : 'no-highlight'; + } + + return classes + .split(/\s+/) + .find((_class) => shouldNotHighlight(_class) || getLanguage(_class)); + } + + /** + * Core highlighting function. + * + * OLD API + * highlight(lang, code, ignoreIllegals, continuation) + * + * NEW API + * highlight(code, {lang, ignoreIllegals}) + * + * @param {string} codeOrLanguageName - the language to use for highlighting + * @param {string | HighlightOptions} optionsOrCode - the code to highlight + * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail + * @param {CompiledMode} [continuation] - current continuation mode, if any + * + * @returns {HighlightResult} Result - an object that represents the result + * @property {string} language - the language name + * @property {number} relevance - the relevance score + * @property {string} value - the highlighted HTML code + * @property {string} code - the original raw code + * @property {CompiledMode} top - top of the current mode stack + * @property {boolean} illegal - indicates whether any illegal matches were found + */ + function highlight(codeOrLanguageName, optionsOrCode, ignoreIllegals, continuation) { + let code = ""; + let languageName = ""; + if (typeof optionsOrCode === "object") { + code = codeOrLanguageName; + ignoreIllegals = optionsOrCode.ignoreIllegals; + languageName = optionsOrCode.language; + // continuation not supported at all via the new API + // eslint-disable-next-line no-undefined + continuation = undefined; + } else { + // old API + deprecated("10.7.0", "highlight(lang, code, ...args) has been deprecated."); + deprecated("10.7.0", "Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"); + languageName = codeOrLanguageName; + code = optionsOrCode; + } + + // https://github.com/highlightjs/highlight.js/issues/3149 + // eslint-disable-next-line no-undefined + if (ignoreIllegals === undefined) { ignoreIllegals = true; } + + /** @type {BeforeHighlightContext} */ + const context = { + code, + language: languageName + }; + // the plugin can change the desired language or the code to be highlighted + // just be changing the object it was passed + fire("before:highlight", context); + + // a before plugin can usurp the result completely by providing it's own + // in which case we don't even need to call highlight + const result = context.result + ? context.result + : _highlight(context.language, context.code, ignoreIllegals, continuation); + + result.code = context.code; + // the plugin can change anything in result to suite it + fire("after:highlight", result); + + return result; + } + + /** + * private highlight that's used internally and does not fire callbacks + * + * @param {string} languageName - the language to use for highlighting + * @param {string} codeToHighlight - the code to highlight + * @param {boolean?} [ignoreIllegals] - whether to ignore illegal matches, default is to bail + * @param {CompiledMode?} [continuation] - current continuation mode, if any + * @returns {HighlightResult} - result of the highlight operation + */ + function _highlight(languageName, codeToHighlight, ignoreIllegals, continuation) { + const keywordHits = Object.create(null); + + /** + * Return keyword data if a match is a keyword + * @param {CompiledMode} mode - current mode + * @param {string} matchText - the textual match + * @returns {KeywordData | false} + */ + function keywordData(mode, matchText) { + return mode.keywords[matchText]; + } + + function processKeywords() { + if (!top.keywords) { + emitter.addText(modeBuffer); + return; + } + + let lastIndex = 0; + top.keywordPatternRe.lastIndex = 0; + let match = top.keywordPatternRe.exec(modeBuffer); + let buf = ""; + + while (match) { + buf += modeBuffer.substring(lastIndex, match.index); + const word = language.case_insensitive ? match[0].toLowerCase() : match[0]; + const data = keywordData(top, word); + if (data) { + const [kind, keywordRelevance] = data; + emitter.addText(buf); + buf = ""; + + keywordHits[word] = (keywordHits[word] || 0) + 1; + if (keywordHits[word] <= MAX_KEYWORD_HITS) relevance += keywordRelevance; + if (kind.startsWith("_")) { + // _ implied for relevance only, do not highlight + // by applying a class name + buf += match[0]; + } else { + const cssClass = language.classNameAliases[kind] || kind; + emitter.addKeyword(match[0], cssClass); + } + } else { + buf += match[0]; + } + lastIndex = top.keywordPatternRe.lastIndex; + match = top.keywordPatternRe.exec(modeBuffer); + } + buf += modeBuffer.substr(lastIndex); + emitter.addText(buf); + } + + function processSubLanguage() { + if (modeBuffer === "") return; + /** @type HighlightResult */ + let result = null; + + if (typeof top.subLanguage === 'string') { + if (!languages[top.subLanguage]) { + emitter.addText(modeBuffer); + return; + } + result = _highlight(top.subLanguage, modeBuffer, true, continuations[top.subLanguage]); + continuations[top.subLanguage] = /** @type {CompiledMode} */ (result._top); + } else { + result = highlightAuto(modeBuffer, top.subLanguage.length ? top.subLanguage : null); + } + + // Counting embedded language score towards the host language may be disabled + // with zeroing the containing mode relevance. Use case in point is Markdown that + // allows XML everywhere and makes every XML snippet to have a much larger Markdown + // score. + if (top.relevance > 0) { + relevance += result.relevance; + } + emitter.addSublanguage(result._emitter, result.language); + } + + function processBuffer() { + if (top.subLanguage != null) { + processSubLanguage(); + } else { + processKeywords(); + } + modeBuffer = ''; + } + + /** + * @param {CompiledMode} mode + * @param {RegExpMatchArray} match + */ + function emitMultiClass(scope, match) { + let i = 1; + // eslint-disable-next-line no-undefined + while (match[i] !== undefined) { + if (!scope._emit[i]) { i++; continue; } + const klass = language.classNameAliases[scope[i]] || scope[i]; + const text = match[i]; + if (klass) { + emitter.addKeyword(text, klass); + } else { + modeBuffer = text; + processKeywords(); + modeBuffer = ""; + } + i++; + } + } + + /** + * @param {CompiledMode} mode - new mode to start + * @param {RegExpMatchArray} match + */ + function startNewMode(mode, match) { + if (mode.scope && typeof mode.scope === "string") { + emitter.openNode(language.classNameAliases[mode.scope] || mode.scope); + } + if (mode.beginScope) { + // beginScope just wraps the begin match itself in a scope + if (mode.beginScope._wrap) { + emitter.addKeyword(modeBuffer, language.classNameAliases[mode.beginScope._wrap] || mode.beginScope._wrap); + modeBuffer = ""; + } else if (mode.beginScope._multi) { + // at this point modeBuffer should just be the match + emitMultiClass(mode.beginScope, match); + modeBuffer = ""; + } + } + + top = Object.create(mode, { parent: { value: top } }); + return top; + } + + /** + * @param {CompiledMode } mode - the mode to potentially end + * @param {RegExpMatchArray} match - the latest match + * @param {string} matchPlusRemainder - match plus remainder of content + * @returns {CompiledMode | void} - the next mode, or if void continue on in current mode + */ + function endOfMode(mode, match, matchPlusRemainder) { + let matched = startsWith(mode.endRe, matchPlusRemainder); + + if (matched) { + if (mode["on:end"]) { + const resp = new Response(mode); + mode["on:end"](match, resp); + if (resp.isMatchIgnored) matched = false; + } + + if (matched) { + while (mode.endsParent && mode.parent) { + mode = mode.parent; + } + return mode; + } + } + // even if on:end fires an `ignore` it's still possible + // that we might trigger the end node because of a parent mode + if (mode.endsWithParent) { + return endOfMode(mode.parent, match, matchPlusRemainder); + } + } + + /** + * Handle matching but then ignoring a sequence of text + * + * @param {string} lexeme - string containing full match text + */ + function doIgnore(lexeme) { + if (top.matcher.regexIndex === 0) { + // no more regexes to potentially match here, so we move the cursor forward one + // space + modeBuffer += lexeme[0]; + return 1; + } else { + // no need to move the cursor, we still have additional regexes to try and + // match at this very spot + resumeScanAtSamePosition = true; + return 0; + } + } + + /** + * Handle the start of a new potential mode match + * + * @param {EnhancedMatch} match - the current match + * @returns {number} how far to advance the parse cursor + */ + function doBeginMatch(match) { + const lexeme = match[0]; + const newMode = match.rule; + + const resp = new Response(newMode); + // first internal before callbacks, then the public ones + const beforeCallbacks = [newMode.__beforeBegin, newMode["on:begin"]]; + for (const cb of beforeCallbacks) { + if (!cb) continue; + cb(match, resp); + if (resp.isMatchIgnored) return doIgnore(lexeme); + } + + if (newMode.skip) { + modeBuffer += lexeme; + } else { + if (newMode.excludeBegin) { + modeBuffer += lexeme; + } + processBuffer(); + if (!newMode.returnBegin && !newMode.excludeBegin) { + modeBuffer = lexeme; + } + } + startNewMode(newMode, match); + return newMode.returnBegin ? 0 : lexeme.length; + } + + /** + * Handle the potential end of mode + * + * @param {RegExpMatchArray} match - the current match + */ + function doEndMatch(match) { + const lexeme = match[0]; + const matchPlusRemainder = codeToHighlight.substr(match.index); + + const endMode = endOfMode(top, match, matchPlusRemainder); + if (!endMode) { return NO_MATCH; } + + const origin = top; + if (top.endScope && top.endScope._wrap) { + processBuffer(); + emitter.addKeyword(lexeme, top.endScope._wrap); + } else if (top.endScope && top.endScope._multi) { + processBuffer(); + emitMultiClass(top.endScope, match); + } else if (origin.skip) { + modeBuffer += lexeme; + } else { + if (!(origin.returnEnd || origin.excludeEnd)) { + modeBuffer += lexeme; + } + processBuffer(); + if (origin.excludeEnd) { + modeBuffer = lexeme; + } + } + do { + if (top.scope && !top.isMultiClass) { + emitter.closeNode(); + } + if (!top.skip && !top.subLanguage) { + relevance += top.relevance; + } + top = top.parent; + } while (top !== endMode.parent); + if (endMode.starts) { + startNewMode(endMode.starts, match); + } + return origin.returnEnd ? 0 : lexeme.length; + } + + function processContinuations() { + const list = []; + for (let current = top; current !== language; current = current.parent) { + if (current.scope) { + list.unshift(current.scope); + } + } + list.forEach(item => emitter.openNode(item)); + } + + /** @type {{type?: MatchType, index?: number, rule?: Mode}}} */ + let lastMatch = {}; + + /** + * Process an individual match + * + * @param {string} textBeforeMatch - text preceding the match (since the last match) + * @param {EnhancedMatch} [match] - the match itself + */ + function processLexeme(textBeforeMatch, match) { + const lexeme = match && match[0]; + + // add non-matched text to the current mode buffer + modeBuffer += textBeforeMatch; + + if (lexeme == null) { + processBuffer(); + return 0; + } + + // we've found a 0 width match and we're stuck, so we need to advance + // this happens when we have badly behaved rules that have optional matchers to the degree that + // sometimes they can end up matching nothing at all + // Ref: https://github.com/highlightjs/highlight.js/issues/2140 + if (lastMatch.type === "begin" && match.type === "end" && lastMatch.index === match.index && lexeme === "") { + // spit the "skipped" character that our regex choked on back into the output sequence + modeBuffer += codeToHighlight.slice(match.index, match.index + 1); + if (!SAFE_MODE) { + /** @type {AnnotatedError} */ + const err = new Error(`0 width match regex (${languageName})`); + err.languageName = languageName; + err.badRule = lastMatch.rule; + throw err; + } + return 1; + } + lastMatch = match; + + if (match.type === "begin") { + return doBeginMatch(match); + } else if (match.type === "illegal" && !ignoreIllegals) { + // illegal match, we do not continue processing + /** @type {AnnotatedError} */ + const err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.scope || '') + '"'); + err.mode = top; + throw err; + } else if (match.type === "end") { + const processed = doEndMatch(match); + if (processed !== NO_MATCH) { + return processed; + } + } + + // edge case for when illegal matches $ (end of line) which is technically + // a 0 width match but not a begin/end match so it's not caught by the + // first handler (when ignoreIllegals is true) + if (match.type === "illegal" && lexeme === "") { + // advance so we aren't stuck in an infinite loop + return 1; + } + + // infinite loops are BAD, this is a last ditch catch all. if we have a + // decent number of iterations yet our index (cursor position in our + // parsing) still 3x behind our index then something is very wrong + // so we bail + if (iterations > 100000 && iterations > match.index * 3) { + const err = new Error('potential infinite loop, way more iterations than matches'); + throw err; + } + + /* + Why might be find ourselves here? An potential end match that was + triggered but could not be completed. IE, `doEndMatch` returned NO_MATCH. + (this could be because a callback requests the match be ignored, etc) + + This causes no real harm other than stopping a few times too many. + */ + + modeBuffer += lexeme; + return lexeme.length; + } + + const language = getLanguage(languageName); + if (!language) { + error(LANGUAGE_NOT_FOUND.replace("{}", languageName)); + throw new Error('Unknown language: "' + languageName + '"'); + } + + const md = compileLanguage(language); + let result = ''; + /** @type {CompiledMode} */ + let top = continuation || md; + /** @type Record */ + const continuations = {}; // keep continuations for sub-languages + const emitter = new options.__emitter(options); + processContinuations(); + let modeBuffer = ''; + let relevance = 0; + let index = 0; + let iterations = 0; + let resumeScanAtSamePosition = false; + + try { + top.matcher.considerAll(); + + for (; ;) { + iterations++; + if (resumeScanAtSamePosition) { + // only regexes not matched previously will now be + // considered for a potential match + resumeScanAtSamePosition = false; + } else { + top.matcher.considerAll(); + } + top.matcher.lastIndex = index; + + const match = top.matcher.exec(codeToHighlight); + // console.log("match", match[0], match.rule && match.rule.begin) + + if (!match) break; + + const beforeMatch = codeToHighlight.substring(index, match.index); + const processedCount = processLexeme(beforeMatch, match); + index = match.index + processedCount; + } + processLexeme(codeToHighlight.substr(index)); + emitter.closeAllNodes(); + emitter.finalize(); + result = emitter.toHTML(); + + return { + language: languageName, + value: result, + relevance: relevance, + illegal: false, + _emitter: emitter, + _top: top + }; + } catch (err) { + if (err.message && err.message.includes('Illegal')) { + return { + language: languageName, + value: escape(codeToHighlight), + illegal: true, + relevance: 0, + _illegalBy: { + message: err.message, + index: index, + context: codeToHighlight.slice(index - 100, index + 100), + mode: err.mode, + resultSoFar: result + }, + _emitter: emitter + }; + } else if (SAFE_MODE) { + return { + language: languageName, + value: escape(codeToHighlight), + illegal: false, + relevance: 0, + errorRaised: err, + _emitter: emitter, + _top: top + }; + } else { + throw err; + } + } + } + + /** + * returns a valid highlight result, without actually doing any actual work, + * auto highlight starts with this and it's possible for small snippets that + * auto-detection may not find a better match + * @param {string} code + * @returns {HighlightResult} + */ + function justTextHighlightResult(code) { + const result = { + value: escape(code), + illegal: false, + relevance: 0, + _top: PLAINTEXT_LANGUAGE, + _emitter: new options.__emitter(options) + }; + result._emitter.addText(code); + return result; + } + + /** + Highlighting with language detection. Accepts a string with the code to + highlight. Returns an object with the following properties: + + - language (detected language) + - relevance (int) + - value (an HTML string with highlighting markup) + - secondBest (object with the same structure for second-best heuristically + detected language, may be absent) + + @param {string} code + @param {Array} [languageSubset] + @returns {AutoHighlightResult} + */ + function highlightAuto(code, languageSubset) { + languageSubset = languageSubset || options.languages || Object.keys(languages); + const plaintext = justTextHighlightResult(code); + + const results = languageSubset.filter(getLanguage).filter(autoDetection).map(name => + _highlight(name, code, false) + ); + results.unshift(plaintext); // plaintext is always an option + + const sorted = results.sort((a, b) => { + // sort base on relevance + if (a.relevance !== b.relevance) return b.relevance - a.relevance; + + // always award the tie to the base language + // ie if C++ and Arduino are tied, it's more likely to be C++ + if (a.language && b.language) { + if (getLanguage(a.language).supersetOf === b.language) { + return 1; + } else if (getLanguage(b.language).supersetOf === a.language) { + return -1; + } + } + + // otherwise say they are equal, which has the effect of sorting on + // relevance while preserving the original ordering - which is how ties + // have historically been settled, ie the language that comes first always + // wins in the case of a tie + return 0; + }); + + const [best, secondBest] = sorted; + + /** @type {AutoHighlightResult} */ + const result = best; + result.secondBest = secondBest; + + return result; + } + + /** + * Builds new class name for block given the language name + * + * @param {HTMLElement} element + * @param {string} [currentLang] + * @param {string} [resultLang] + */ + function updateClassName(element, currentLang, resultLang) { + const language = (currentLang && aliases[currentLang]) || resultLang; + + element.classList.add("hljs"); + element.classList.add(`language-${language}`); + } + + /** + * Applies highlighting to a DOM node containing code. + * + * @param {HighlightedHTMLElement} element - the HTML element to highlight + */ + function highlightElement(element) { + /** @type HTMLElement */ + let node = null; + const language = blockLanguage(element); + + if (shouldNotHighlight(language)) return; + + fire("before:highlightElement", + { el: element, language: language }); + + // we should be all text, no child nodes + if (!options.ignoreUnescapedHTML && element.children.length > 0) { + console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."); + console.warn("https://github.com/highlightjs/highlight.js/issues/2886"); + console.warn(element); + } + + node = element; + const text = node.textContent; + const result = language ? highlight(text, { language, ignoreIllegals: true }) : highlightAuto(text); + + fire("after:highlightElement", { el: element, result, text }); + + element.innerHTML = result.value; + updateClassName(element, language, result.language); + element.result = { + language: result.language, + // TODO: remove with version 11.0 + re: result.relevance, + relevance: result.relevance + }; + if (result.secondBest) { + element.secondBest = { + language: result.secondBest.language, + relevance: result.secondBest.relevance + }; + } + } + + /** + * Updates highlight.js global options with the passed options + * + * @param {Partial} userOptions + */ + function configure(userOptions) { + options = inherit(options, userOptions); + } + + // TODO: remove v12, deprecated + const initHighlighting = () => { + highlightAll(); + deprecated("10.6.0", "initHighlighting() deprecated. Use highlightAll() now."); + }; + + // TODO: remove v12, deprecated + function initHighlightingOnLoad() { + highlightAll(); + deprecated("10.6.0", "initHighlightingOnLoad() deprecated. Use highlightAll() now."); + } + + let wantsHighlight = false; + + /** + * auto-highlights all pre>code elements on the page + */ + function highlightAll() { + // if we are called too early in the loading process + if (document.readyState === "loading") { + wantsHighlight = true; + return; + } + + const blocks = document.querySelectorAll(options.cssSelector); + blocks.forEach(highlightElement); + } + + function boot() { + // if a highlight was requested before DOM was loaded, do now + if (wantsHighlight) highlightAll(); + } + + // make sure we are in the browser environment + if (typeof window !== 'undefined' && window.addEventListener) { + window.addEventListener('DOMContentLoaded', boot, false); + } + + /** + * Register a language grammar module + * + * @param {string} languageName + * @param {LanguageFn} languageDefinition + */ + function registerLanguage(languageName, languageDefinition) { + let lang = null; + try { + lang = languageDefinition(hljs); + } catch (error$1) { + error("Language definition for '{}' could not be registered.".replace("{}", languageName)); + // hard or soft error + if (!SAFE_MODE) { throw error$1; } else { error(error$1); } + // languages that have serious errors are replaced with essentially a + // "plaintext" stand-in so that the code blocks will still get normal + // css classes applied to them - and one bad language won't break the + // entire highlighter + lang = PLAINTEXT_LANGUAGE; + } + // give it a temporary name if it doesn't have one in the meta-data + if (!lang.name) lang.name = languageName; + languages[languageName] = lang; + lang.rawDefinition = languageDefinition.bind(null, hljs); + + if (lang.aliases) { + registerAliases(lang.aliases, { languageName }); + } + } + + /** + * Remove a language grammar module + * + * @param {string} languageName + */ + function unregisterLanguage(languageName) { + delete languages[languageName]; + for (const alias of Object.keys(aliases)) { + if (aliases[alias] === languageName) { + delete aliases[alias]; + } + } + } + + /** + * @returns {string[]} List of language internal names + */ + function listLanguages() { + return Object.keys(languages); + } + + /** + * @param {string} name - name of the language to retrieve + * @returns {Language | undefined} + */ + function getLanguage(name) { + name = (name || '').toLowerCase(); + return languages[name] || languages[aliases[name]]; + } + + /** + * + * @param {string|string[]} aliasList - single alias or list of aliases + * @param {{languageName: string}} opts + */ + function registerAliases(aliasList, { languageName }) { + if (typeof aliasList === 'string') { + aliasList = [aliasList]; + } + aliasList.forEach(alias => { aliases[alias.toLowerCase()] = languageName; }); + } + + /** + * Determines if a given language has auto-detection enabled + * @param {string} name - name of the language + */ + function autoDetection(name) { + const lang = getLanguage(name); + return lang && !lang.disableAutodetect; + } + + /** + * Upgrades the old highlightBlock plugins to the new + * highlightElement API + * @param {HLJSPlugin} plugin + */ + function upgradePluginAPI(plugin) { + // TODO: remove with v12 + if (plugin["before:highlightBlock"] && !plugin["before:highlightElement"]) { + plugin["before:highlightElement"] = (data) => { + plugin["before:highlightBlock"]( + Object.assign({ block: data.el }, data) + ); + }; + } + if (plugin["after:highlightBlock"] && !plugin["after:highlightElement"]) { + plugin["after:highlightElement"] = (data) => { + plugin["after:highlightBlock"]( + Object.assign({ block: data.el }, data) + ); + }; + } + } + + /** + * @param {HLJSPlugin} plugin + */ + function addPlugin(plugin) { + upgradePluginAPI(plugin); + plugins.push(plugin); + } + + /** + * + * @param {PluginEvent} event + * @param {any} args + */ + function fire(event, args) { + const cb = event; + plugins.forEach(function (plugin) { + if (plugin[cb]) { + plugin[cb](args); + } + }); + } + + /** + * + * @param {HighlightedHTMLElement} el + */ + function deprecateHighlightBlock(el) { + deprecated("10.7.0", "highlightBlock will be removed entirely in v12.0"); + deprecated("10.7.0", "Please use highlightElement now."); + + return highlightElement(el); + } + + /* Interface definition */ + Object.assign(hljs, { + highlight, + highlightAuto, + highlightAll, + highlightElement, + // TODO: Remove with v12 API + highlightBlock: deprecateHighlightBlock, + configure, + initHighlighting, + initHighlightingOnLoad, + registerLanguage, + unregisterLanguage, + listLanguages, + getLanguage, + registerAliases, + autoDetection, + inherit, + addPlugin + }); + + hljs.debugMode = function () { SAFE_MODE = false; }; + hljs.safeMode = function () { SAFE_MODE = true; }; + hljs.versionString = version; + + for (const key in MODES$1) { + // @ts-ignore + if (typeof MODES$1[key] === "object") { + // @ts-ignore + deepFreeze$1(MODES$1[key]); + } + } + + // merge all the modes/regexes into our main object + Object.assign(hljs, MODES$1); + + return hljs; + }; + + // export an "instance" of the highlighter + var HighlightJS = HLJS({}); + + /* + Language: Bash + Author: vah + Contributrors: Benjamin Pannell + Website: https://www.gnu.org/software/bash/ + Category: common + */ + + /** @type LanguageFn */ + function bash(hljs) { + const VAR = {}; + const BRACED_VAR = { + begin: /\$\{/, + end: /\}/, + contains: [ + "self", + { + begin: /:-/, + contains: [VAR] + } // default values + ] + }; + Object.assign(VAR, { + className: 'variable', + variants: [ + { + begin: concat(/\$[\w\d#@][\w\d_]*/, + // negative look-ahead tries to avoid matching patterns that are not + // Perl at all like $ident$, @ident@, etc. + `(?![\\w\\d])(?![$])`) + }, + BRACED_VAR + ] + }); + + const SUBST = { + className: 'subst', + begin: /\$\(/, end: /\)/, + contains: [hljs.BACKSLASH_ESCAPE] + }; + const HERE_DOC = { + begin: /<<-?\s*(?=\w+)/, + starts: { + contains: [ + hljs.END_SAME_AS_BEGIN({ + begin: /(\w+)/, + end: /(\w+)/, + className: 'string' + }) + ] + } + }; + const QUOTE_STRING = { + className: 'string', + begin: /"/, end: /"/, + contains: [ + hljs.BACKSLASH_ESCAPE, + VAR, + SUBST + ] + }; + SUBST.contains.push(QUOTE_STRING); + const ESCAPED_QUOTE = { + className: '', + begin: /\\"/ + + }; + const APOS_STRING = { + className: 'string', + begin: /'/, end: /'/ + }; + const ARITHMETIC = { + begin: /\$\(\(/, + end: /\)\)/, + contains: [ + { begin: /\d+#[0-9a-f]+/, className: "number" }, + hljs.NUMBER_MODE, + VAR + ] + }; + const SH_LIKE_SHELLS = [ + "fish", + "bash", + "zsh", + "sh", + "csh", + "ksh", + "tcsh", + "dash", + "scsh", + ]; + const KNOWN_SHEBANG = hljs.SHEBANG({ + binary: `(${SH_LIKE_SHELLS.join("|")})`, + relevance: 10 + }); + const FUNCTION = { + className: 'function', + begin: /\w[\w\d_]*\s*\(\s*\)\s*\{/, + returnBegin: true, + contains: [hljs.inherit(hljs.TITLE_MODE, { begin: /\w[\w\d_]*/ })], + relevance: 0 + }; + + const KEYWORDS = [ + "if", + "then", + "else", + "elif", + "fi", + "for", + "while", + "in", + "do", + "done", + "case", + "esac", + "function" + ]; + + const LITERALS = [ + "true", + "false" + ]; + + return { + name: 'Bash', + aliases: ['sh'], + keywords: { + $pattern: /\b[a-z._-]+\b/, + keyword: KEYWORDS, + literal: LITERALS, + built_in: + // Shell built-ins + // http://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html + 'break cd continue eval exec exit export getopts hash pwd readonly return shift test times ' + + 'trap umask unset ' + + // Bash built-ins + 'alias bind builtin caller command declare echo enable help let local logout mapfile printf ' + + 'read readarray source type typeset ulimit unalias ' + + // Shell modifiers + 'set shopt ' + + // Zsh built-ins + 'autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles ' + + 'compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate ' + + 'fc fg float functions getcap getln history integer jobs kill limit log noglob popd print ' + + 'pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit ' + + 'unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof ' + + 'zpty zregexparse zsocket zstyle ztcp' + }, + contains: [ + KNOWN_SHEBANG, // to catch known shells and boost relevancy + hljs.SHEBANG(), // to catch unknown shells but still highlight the shebang + FUNCTION, + ARITHMETIC, + hljs.HASH_COMMENT_MODE, + HERE_DOC, + QUOTE_STRING, + ESCAPED_QUOTE, + APOS_STRING, + VAR + ] + }; + } + + + const MODES = (hljs) => { + return { + IMPORTANT: { + scope: 'meta', + begin: '!important' + }, + HEXCOLOR: { + scope: 'number', + begin: '#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})' + }, + ATTRIBUTE_SELECTOR_MODE: { + scope: 'selector-attr', + begin: /\[/, + end: /\]/, + illegal: '$', + contains: [ + hljs.APOS_STRING_MODE, + hljs.QUOTE_STRING_MODE + ] + }, + CSS_NUMBER_MODE: { + scope: 'number', + begin: hljs.NUMBER_RE + '(' + + '%|em|ex|ch|rem' + + '|vw|vh|vmin|vmax' + + '|cm|mm|in|pt|pc|px' + + '|deg|grad|rad|turn' + + '|s|ms' + + '|Hz|kHz' + + '|dpi|dpcm|dppx' + + ')?', + relevance: 0 + } + }; + }; + + const TAGS = [ + 'a', + 'abbr', + 'address', + 'article', + 'aside', + 'audio', + 'b', + 'blockquote', + 'body', + 'button', + 'canvas', + 'caption', + 'cite', + 'code', + 'dd', + 'del', + 'details', + 'dfn', + 'div', + 'dl', + 'dt', + 'em', + 'fieldset', + 'figcaption', + 'figure', + 'footer', + 'form', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'header', + 'hgroup', + 'html', + 'i', + 'iframe', + 'img', + 'input', + 'ins', + 'kbd', + 'label', + 'legend', + 'li', + 'main', + 'mark', + 'menu', + 'nav', + 'object', + 'ol', + 'p', + 'q', + 'quote', + 'samp', + 'section', + 'span', + 'strong', + 'summary', + 'sup', + 'table', + 'tbody', + 'td', + 'textarea', + 'tfoot', + 'th', + 'thead', + 'time', + 'tr', + 'ul', + 'var', + 'video' + ]; + + const MEDIA_FEATURES = [ + 'any-hover', + 'any-pointer', + 'aspect-ratio', + 'color', + 'color-gamut', + 'color-index', + 'device-aspect-ratio', + 'device-height', + 'device-width', + 'display-mode', + 'forced-colors', + 'grid', + 'height', + 'hover', + 'inverted-colors', + 'monochrome', + 'orientation', + 'overflow-block', + 'overflow-inline', + 'pointer', + 'prefers-color-scheme', + 'prefers-contrast', + 'prefers-reduced-motion', + 'prefers-reduced-transparency', + 'resolution', + 'scan', + 'scripting', + 'update', + 'width', + // TODO: find a better solution? + 'min-width', + 'max-width', + 'min-height', + 'max-height' + ]; + + // https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes + const PSEUDO_CLASSES = [ + 'active', + 'any-link', + 'blank', + 'checked', + 'current', + 'default', + 'defined', + 'dir', // dir() + 'disabled', + 'drop', + 'empty', + 'enabled', + 'first', + 'first-child', + 'first-of-type', + 'fullscreen', + 'future', + 'focus', + 'focus-visible', + 'focus-within', + 'has', // has() + 'host', // host or host() + 'host-context', // host-context() + 'hover', + 'indeterminate', + 'in-range', + 'invalid', + 'is', // is() + 'lang', // lang() + 'last-child', + 'last-of-type', + 'left', + 'link', + 'local-link', + 'not', // not() + 'nth-child', // nth-child() + 'nth-col', // nth-col() + 'nth-last-child', // nth-last-child() + 'nth-last-col', // nth-last-col() + 'nth-last-of-type', //nth-last-of-type() + 'nth-of-type', //nth-of-type() + 'only-child', + 'only-of-type', + 'optional', + 'out-of-range', + 'past', + 'placeholder-shown', + 'read-only', + 'read-write', + 'required', + 'right', + 'root', + 'scope', + 'target', + 'target-within', + 'user-invalid', + 'valid', + 'visited', + 'where' // where() + ]; + + // https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements + const PSEUDO_ELEMENTS = [ + 'after', + 'backdrop', + 'before', + 'cue', + 'cue-region', + 'first-letter', + 'first-line', + 'grammar-error', + 'marker', + 'part', + 'placeholder', + 'selection', + 'slotted', + 'spelling-error' + ]; + + const ATTRIBUTES = [ + 'align-content', + 'align-items', + 'align-self', + 'animation', + 'animation-delay', + 'animation-direction', + 'animation-duration', + 'animation-fill-mode', + 'animation-iteration-count', + 'animation-name', + 'animation-play-state', + 'animation-timing-function', + 'auto', + 'backface-visibility', + 'background', + 'background-attachment', + 'background-clip', + 'background-color', + 'background-image', + 'background-origin', + 'background-position', + 'background-repeat', + 'background-size', + 'border', + 'border-bottom', + 'border-bottom-color', + 'border-bottom-left-radius', + 'border-bottom-right-radius', + 'border-bottom-style', + 'border-bottom-width', + 'border-collapse', + 'border-color', + 'border-image', + 'border-image-outset', + 'border-image-repeat', + 'border-image-slice', + 'border-image-source', + 'border-image-width', + 'border-left', + 'border-left-color', + 'border-left-style', + 'border-left-width', + 'border-radius', + 'border-right', + 'border-right-color', + 'border-right-style', + 'border-right-width', + 'border-spacing', + 'border-style', + 'border-top', + 'border-top-color', + 'border-top-left-radius', + 'border-top-right-radius', + 'border-top-style', + 'border-top-width', + 'border-width', + 'bottom', + 'box-decoration-break', + 'box-shadow', + 'box-sizing', + 'break-after', + 'break-before', + 'break-inside', + 'caption-side', + 'clear', + 'clip', + 'clip-path', + 'color', + 'column-count', + 'column-fill', + 'column-gap', + 'column-rule', + 'column-rule-color', + 'column-rule-style', + 'column-rule-width', + 'column-span', + 'column-width', + 'columns', + 'content', + 'counter-increment', + 'counter-reset', + 'cursor', + 'direction', + 'display', + 'empty-cells', + 'filter', + 'flex', + 'flex-basis', + 'flex-direction', + 'flex-flow', + 'flex-grow', + 'flex-shrink', + 'flex-wrap', + 'float', + 'font', + 'font-display', + 'font-family', + 'font-feature-settings', + 'font-kerning', + 'font-language-override', + 'font-size', + 'font-size-adjust', + 'font-smoothing', + 'font-stretch', + 'font-style', + 'font-variant', + 'font-variant-ligatures', + 'font-variation-settings', + 'font-weight', + 'height', + 'hyphens', + 'icon', + 'image-orientation', + 'image-rendering', + 'image-resolution', + 'ime-mode', + 'inherit', + 'initial', + 'justify-content', + 'left', + 'letter-spacing', + 'line-height', + 'list-style', + 'list-style-image', + 'list-style-position', + 'list-style-type', + 'margin', + 'margin-bottom', + 'margin-left', + 'margin-right', + 'margin-top', + 'marks', + 'mask', + 'max-height', + 'max-width', + 'min-height', + 'min-width', + 'nav-down', + 'nav-index', + 'nav-left', + 'nav-right', + 'nav-up', + 'none', + 'normal', + 'object-fit', + 'object-position', + 'opacity', + 'order', + 'orphans', + 'outline', + 'outline-color', + 'outline-offset', + 'outline-style', + 'outline-width', + 'overflow', + 'overflow-wrap', + 'overflow-x', + 'overflow-y', + 'padding', + 'padding-bottom', + 'padding-left', + 'padding-right', + 'padding-top', + 'page-break-after', + 'page-break-before', + 'page-break-inside', + 'perspective', + 'perspective-origin', + 'pointer-events', + 'position', + 'quotes', + 'resize', + 'right', + 'src', // @font-face + 'tab-size', + 'table-layout', + 'text-align', + 'text-align-last', + 'text-decoration', + 'text-decoration-color', + 'text-decoration-line', + 'text-decoration-style', + 'text-indent', + 'text-overflow', + 'text-rendering', + 'text-shadow', + 'text-transform', + 'text-underline-position', + 'top', + 'transform', + 'transform-origin', + 'transform-style', + 'transition', + 'transition-delay', + 'transition-duration', + 'transition-property', + 'transition-timing-function', + 'unicode-bidi', + 'vertical-align', + 'visibility', + 'white-space', + 'widows', + 'width', + 'word-break', + 'word-spacing', + 'word-wrap', + 'z-index' + // reverse makes sure longer attributes `font-weight` are matched fully + // instead of getting false positives on say `font` + ].reverse(); + + // some grammars use them all as a single group + const PSEUDO_SELECTORS = PSEUDO_CLASSES.concat(PSEUDO_ELEMENTS); + + + // https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10 + var decimalDigits = '[0-9](_*[0-9])*'; + var frac = `\\.(${decimalDigits})`; + var hexDigits = '[0-9a-fA-F](_*[0-9a-fA-F])*'; + var NUMERIC = { + className: 'number', + variants: [ + // DecimalFloatingPointLiteral + // including ExponentPart + { + begin: `(\\b(${decimalDigits})((${frac})|\\.)?|(${frac}))` + + `[eE][+-]?(${decimalDigits})[fFdD]?\\b` + }, + // excluding ExponentPart + { begin: `\\b(${decimalDigits})((${frac})[fFdD]?\\b|\\.([fFdD]\\b)?)` }, + { begin: `(${frac})[fFdD]?\\b` }, + { begin: `\\b(${decimalDigits})[fFdD]\\b` }, + + // HexadecimalFloatingPointLiteral + { + begin: `\\b0[xX]((${hexDigits})\\.?|(${hexDigits})?\\.(${hexDigits}))` + + `[pP][+-]?(${decimalDigits})[fFdD]?\\b` + }, + + // DecimalIntegerLiteral + { begin: '\\b(0|[1-9](_*[0-9])*)[lL]?\\b' }, + + // HexIntegerLiteral + { begin: `\\b0[xX](${hexDigits})[lL]?\\b` }, + + // OctalIntegerLiteral + { begin: '\\b0(_*[0-7])*[lL]?\\b' }, + + // BinaryIntegerLiteral + { begin: '\\b0[bB][01](_*[01])*[lL]?\\b' }, + ], + relevance: 0 + }; + + + /** + * Allows recursive regex expressions to a given depth + * + * ie: recurRegex("(abc~~~)", /~~~/g, 2) becomes: + * (abc(abc(abc))) + * + * @param {string} re + * @param {RegExp} substitution (should be a g mode regex) + * @param {number} depth + * @returns {string}`` + */ + function recurRegex(re, substitution, depth) { + if (depth === -1) return ""; + + return re.replace(substitution, _ => { + return recurRegex(re, substitution, depth - 1); + }); + } + + const IDENT_RE = '[A-Za-z$_][0-9A-Za-z$_]*'; + const KEYWORDS = [ + "as", // for exports + "in", + "of", + "if", + "for", + "while", + "finally", + "var", + "new", + "function", + "do", + "return", + "void", + "else", + "break", + "catch", + "instanceof", + "with", + "throw", + "case", + "default", + "try", + "switch", + "continue", + "typeof", + "delete", + "let", + "yield", + "const", + "class", + // JS handles these with a special rule + // "get", + // "set", + "debugger", + "async", + "await", + "static", + "import", + "from", + "export", + "extends" + ]; + const LITERALS = [ + "true", + "false", + "null", + "undefined", + "NaN", + "Infinity" + ]; + + const TYPES = [ + "Intl", + "DataView", + "Number", + "Math", + "Date", + "String", + "RegExp", + "Object", + "Function", + "Boolean", + "Error", + "Symbol", + "Set", + "Map", + "WeakSet", + "WeakMap", + "Proxy", + "Reflect", + "JSON", + "Promise", + "Float64Array", + "Int16Array", + "Int32Array", + "Int8Array", + "Uint16Array", + "Uint32Array", + "Float32Array", + "Array", + "Uint8Array", + "Uint8ClampedArray", + "ArrayBuffer", + "BigInt64Array", + "BigUint64Array", + "BigInt" + ]; + + const ERROR_TYPES = [ + "EvalError", + "InternalError", + "RangeError", + "ReferenceError", + "SyntaxError", + "TypeError", + "URIError" + ]; + + const BUILT_IN_GLOBALS = [ + "setInterval", + "setTimeout", + "clearInterval", + "clearTimeout", + + "require", + "exports", + + "eval", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "escape", + "unescape" + ]; + + const BUILT_IN_VARIABLES = [ + "arguments", + "this", + "super", + "console", + "window", + "document", + "localStorage", + "module", + "global" // Node.js + ]; + + const BUILT_INS = [].concat( + BUILT_IN_GLOBALS, + TYPES, + ERROR_TYPES + ); + + /* + Language: JavaScript + Description: JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions. + Category: common, scripting, web + Website: https://developer.mozilla.org/en-US/docs/Web/JavaScript + */ + + /** @type LanguageFn */ + function javascript(hljs) { + /** + * Takes a string like " { + const tag = "', + end: '' + }; + const XML_TAG = { + begin: /<[A-Za-z0-9\\._:-]+/, + end: /\/[A-Za-z0-9\\._:-]+>|\/>/, + /** + * @param {RegExpMatchArray} match + * @param {CallbackResponse} response + */ + isTrulyOpeningTag: (match, response) => { + const afterMatchIndex = match[0].length + match.index; + const nextChar = match.input[afterMatchIndex]; + // nested type? + // HTML should not include another raw `<` inside a tag + // But a type might: `>`, etc. + if (nextChar === "<") { + response.ignoreMatch(); + return; + } + // + // This is now either a tag or a type. + if (nextChar === ">") { + // if we cannot find a matching closing tag, then we + // will ignore it + if (!hasClosingTag(match, { after: afterMatchIndex })) { + response.ignoreMatch(); + } + } + } + }; + const KEYWORDS$1 = { + $pattern: IDENT_RE, + keyword: KEYWORDS, + literal: LITERALS, + built_in: BUILT_INS, + "variable.language": BUILT_IN_VARIABLES + }; + + // https://tc39.es/ecma262/#sec-literals-numeric-literals + const decimalDigits = '[0-9](_?[0-9])*'; + const frac = `\\.(${decimalDigits})`; + // DecimalIntegerLiteral, including Annex B NonOctalDecimalIntegerLiteral + // https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals + const decimalInteger = `0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*`; + const NUMBER = { + className: 'number', + variants: [ + // DecimalLiteral + { + begin: `(\\b(${decimalInteger})((${frac})|\\.)?|(${frac}))` + + `[eE][+-]?(${decimalDigits})\\b` + }, + { begin: `\\b(${decimalInteger})\\b((${frac})\\b|\\.)?|(${frac})\\b` }, + + // DecimalBigIntegerLiteral + { begin: `\\b(0|[1-9](_?[0-9])*)n\\b` }, + + // NonDecimalIntegerLiteral + { begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b" }, + { begin: "\\b0[bB][0-1](_?[0-1])*n?\\b" }, + { begin: "\\b0[oO][0-7](_?[0-7])*n?\\b" }, + + // LegacyOctalIntegerLiteral (does not include underscore separators) + // https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals + { begin: "\\b0[0-7]+n?\\b" }, + ], + relevance: 0 + }; + + const SUBST = { + className: 'subst', + begin: '\\$\\{', + end: '\\}', + keywords: KEYWORDS$1, + contains: [] // defined later + }; + const HTML_TEMPLATE = { + begin: 'html`', + end: '', + starts: { + end: '`', + returnEnd: false, + contains: [ + hljs.BACKSLASH_ESCAPE, + SUBST + ], + subLanguage: 'xml' + } + }; + const CSS_TEMPLATE = { + begin: 'css`', + end: '', + starts: { + end: '`', + returnEnd: false, + contains: [ + hljs.BACKSLASH_ESCAPE, + SUBST + ], + subLanguage: 'css' + } + }; + const TEMPLATE_STRING = { + className: 'string', + begin: '`', + end: '`', + contains: [ + hljs.BACKSLASH_ESCAPE, + SUBST + ] + }; + const JSDOC_COMMENT = hljs.COMMENT( + /\/\*\*(?!\/)/, + '\\*/', + { + relevance: 0, + contains: [ + { + begin: '(?=@[A-Za-z]+)', + relevance: 0, + contains: [ + { + className: 'doctag', + begin: '@[A-Za-z]+' + }, + { + className: 'type', + begin: '\\{', + end: '\\}', + excludeEnd: true, + excludeBegin: true, + relevance: 0 + }, + { + className: 'variable', + begin: IDENT_RE$1 + '(?=\\s*(-)|$)', + endsParent: true, + relevance: 0 + }, + // eat spaces (not newlines) so we can find + // types or variables + { + begin: /(?=[^\n])\s/, + relevance: 0 + } + ] + } + ] + } + ); + const COMMENT = { + className: "comment", + variants: [ + JSDOC_COMMENT, + hljs.C_BLOCK_COMMENT_MODE, + hljs.C_LINE_COMMENT_MODE + ] + }; + const SUBST_INTERNALS = [ + hljs.APOS_STRING_MODE, + hljs.QUOTE_STRING_MODE, + HTML_TEMPLATE, + CSS_TEMPLATE, + TEMPLATE_STRING, + NUMBER, + hljs.REGEXP_MODE + ]; + SUBST.contains = SUBST_INTERNALS + .concat({ + // we need to pair up {} inside our subst to prevent + // it from ending too early by matching another } + begin: /\{/, + end: /\}/, + keywords: KEYWORDS$1, + contains: [ + "self" + ].concat(SUBST_INTERNALS) + }); + const SUBST_AND_COMMENTS = [].concat(COMMENT, SUBST.contains); + const PARAMS_CONTAINS = SUBST_AND_COMMENTS.concat([ + // eat recursive parens in sub expressions + { + begin: /\(/, + end: /\)/, + keywords: KEYWORDS$1, + contains: ["self"].concat(SUBST_AND_COMMENTS) + } + ]); + const PARAMS = { + className: 'params', + begin: /\(/, + end: /\)/, + excludeBegin: true, + excludeEnd: true, + keywords: KEYWORDS$1, + contains: PARAMS_CONTAINS + }; + + // ES6 classes + const CLASS_OR_EXTENDS = { + variants: [ + { + match: [ + /class/, + /\s+/, + IDENT_RE$1 + ], + scope: { + 1: "keyword", + 3: "title.class" + } + }, + { + match: [ + /extends/, + /\s+/, + concat(IDENT_RE$1, "(", concat(/\./, IDENT_RE$1), ")*") + ], + scope: { + 1: "keyword", + 3: "title.class.inherited" + } + } + ] + }; + + const CLASS_REFERENCE = { + relevance: 0, + match: /\b[A-Z][a-z]+([A-Z][a-z]+)*/, + className: "title.class", + keywords: { + _: [ + // se we still get relevance credit for JS library classes + ...TYPES, + ...ERROR_TYPES + ] + } + }; + + const USE_STRICT = { + label: "use_strict", + className: 'meta', + relevance: 10, + begin: /^\s*['"]use (strict|asm)['"]/ + }; + + const FUNCTION_DEFINITION = { + variants: [ + { + match: [ + /function/, + /\s+/, + IDENT_RE$1, + /(?=\s*\()/ + ] + }, + // anonymous function + { + match: [ + /function/, + /\s*(?=\()/ + ] + } + ], + className: { + 1: "keyword", + 3: "title.function" + }, + label: "func.def", + contains: [PARAMS], + illegal: /%/ + }; + + const UPPER_CASE_CONSTANT = { + relevance: 0, + match: /\b[A-Z][A-Z_]+\b/, + className: "variable.constant" + }; + + function noneOf(list) { + return concat("(?!", list.join("|"), ")"); + } + + const FUNCTION_CALL = { + match: concat( + /\b/, + noneOf([ + ...BUILT_IN_GLOBALS, + "super" + ]), + IDENT_RE$1, lookahead(/\(/)), + className: "title.function", + relevance: 0 + }; + + const PROPERTY_ACCESS = { + begin: concat(/\./, lookahead( + concat(IDENT_RE$1, /(?![0-9A-Za-z$_(])/) + )), + end: IDENT_RE$1, + excludeBegin: true, + keywords: "prototype", + className: "property", + relevance: 0 + }; + + const GETTER_OR_SETTER = { + match: [ + /get|set/, + /\s+/, + IDENT_RE$1, + /(?=\()/ + ], + className: { + 1: "keyword", + 3: "title.function" + }, + contains: [ + { // eat to avoid empty params + begin: /\(\)/ + }, + PARAMS + ] + }; + + const FUNC_LEAD_IN_RE = '(\\(' + + '[^()]*(\\(' + + '[^()]*(\\(' + + '[^()]*' + + '\\)[^()]*)*' + + '\\)[^()]*)*' + + '\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>'; + + const FUNCTION_VARIABLE = { + match: [ + /const|var|let/, /\s+/, + IDENT_RE$1, /\s*/, + /=\s*/, + lookahead(FUNC_LEAD_IN_RE) + ], + className: { + 1: "keyword", + 3: "title.function" + }, + contains: [ + PARAMS + ] + }; + + return { + name: 'Javascript', + aliases: ['js', 'jsx', 'mjs', 'cjs'], + keywords: KEYWORDS$1, + // this will be extended by TypeScript + exports: { PARAMS_CONTAINS }, + illegal: /#(?![$_A-z])/, + contains: [ + hljs.SHEBANG({ + label: "shebang", + binary: "node", + relevance: 5 + }), + USE_STRICT, + hljs.APOS_STRING_MODE, + hljs.QUOTE_STRING_MODE, + HTML_TEMPLATE, + CSS_TEMPLATE, + TEMPLATE_STRING, + COMMENT, + NUMBER, + CLASS_REFERENCE, + { + className: 'attr', + begin: IDENT_RE$1 + lookahead(':'), + relevance: 0 + }, + FUNCTION_VARIABLE, + { // "value" container + begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*', + keywords: 'return throw case', + relevance: 0, + contains: [ + COMMENT, + hljs.REGEXP_MODE, + { + className: 'function', + // we have to count the parens to make sure we actually have the + // correct bounding ( ) before the =>. There could be any number of + // sub-expressions inside also surrounded by parens. + begin: FUNC_LEAD_IN_RE, + returnBegin: true, + end: '\\s*=>', + contains: [ + { + className: 'params', + variants: [ + { + begin: hljs.UNDERSCORE_IDENT_RE, + relevance: 0 + }, + { + className: null, + begin: /\(\s*\)/, + skip: true + }, + { + begin: /\(/, + end: /\)/, + excludeBegin: true, + excludeEnd: true, + keywords: KEYWORDS$1, + contains: PARAMS_CONTAINS + } + ] + } + ] + }, + { // could be a comma delimited list of params to a function call + begin: /,/, + relevance: 0 + }, + { + match: /\s+/, + relevance: 0 + }, + { // JSX + variants: [ + { begin: FRAGMENT.begin, end: FRAGMENT.end }, + { + begin: XML_TAG.begin, + // we carefully check the opening tag to see if it truly + // is a tag and not a false positive + 'on:begin': XML_TAG.isTrulyOpeningTag, + end: XML_TAG.end + } + ], + subLanguage: 'xml', + contains: [ + { + begin: XML_TAG.begin, + end: XML_TAG.end, + skip: true, + contains: ['self'] + } + ] + } + ], + }, + FUNCTION_DEFINITION, + { + // prevent this from getting swallowed up by function + // since they appear "function like" + beginKeywords: "while if switch catch for" + }, + { + // we have to count the parens to make sure we actually have the correct + // bounding ( ). There could be any number of sub-expressions inside + // also surrounded by parens. + begin: '\\b(?!function)' + hljs.UNDERSCORE_IDENT_RE + + '\\(' + // first parens + '[^()]*(\\(' + + '[^()]*(\\(' + + '[^()]*' + + '\\)[^()]*)*' + + '\\)[^()]*)*' + + '\\)\\s*\\{', // end parens + returnBegin: true, + label: "func.def", + contains: [ + PARAMS, + hljs.inherit(hljs.TITLE_MODE, { begin: IDENT_RE$1, className: "title.function" }) + ] + }, + // catch ... so it won't trigger the property rule below + { + match: /\.\.\./, + relevance: 0 + }, + PROPERTY_ACCESS, + // hack: prevents detection of keywords in some circumstances + // .keyword() + // $keyword = x + { + match: '\\$' + IDENT_RE$1, + relevance: 0 + }, + { + match: [/\bconstructor(?=\s*\()/], + className: { 1: "title.function" }, + contains: [PARAMS] + }, + FUNCTION_CALL, + UPPER_CASE_CONSTANT, + CLASS_OR_EXTENDS, + GETTER_OR_SETTER, + { + match: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something` + } + ] + }; + } + + /* + Language: JSON + Description: JSON (JavaScript Object Notation) is a lightweight data-interchange format. + Author: Ivan Sagalaev + Website: http://www.json.org + Category: common, protocols, web + */ + + function json(hljs) { + const ATTRIBUTE = { + className: 'attr', + begin: /"(\\.|[^\\"\r\n])*"(?=\s*:)/, + relevance: 1.01 + }; + const PUNCTUATION = { + match: /[{}[\],:]/, + className: "punctuation", + relevance: 0 + }; + // normally we would rely on `keywords` for this but using a mode here allows us + // to use the very tight `illegal: \S` rule later to flag any other character + // as illegal indicating that despite looking like JSON we do not truly have + // JSON and thus improve false-positively greatly since JSON will try and claim + // all sorts of JSON looking stuff + const LITERALS = { + beginKeywords: [ + "true", + "false", + "null" + ].join(" ") + }; + + return { + name: 'JSON', + contains: [ + ATTRIBUTE, + PUNCTUATION, + hljs.QUOTE_STRING_MODE, + LITERALS, + hljs.C_NUMBER_MODE, + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE + ], + illegal: '\\S' + }; + } + + + /** @type LanguageFn */ + function xml(hljs) { + // Element names can contain letters, digits, hyphens, underscores, and periods + const TAG_NAME_RE = concat(/[A-Z_]/, optional(/[A-Z0-9_.-]*:/), /[A-Z0-9_.-]*/); + const XML_IDENT_RE = /[A-Za-z0-9._:-]+/; + const XML_ENTITIES = { + className: 'symbol', + begin: /&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/ + }; + const XML_META_KEYWORDS = { + begin: /\s/, + contains: [ + { + className: 'keyword', + begin: /#?[a-z_][a-z1-9_-]+/, + illegal: /\n/ + } + ] + }; + const XML_META_PAR_KEYWORDS = hljs.inherit(XML_META_KEYWORDS, { + begin: /\(/, + end: /\)/ + }); + const APOS_META_STRING_MODE = hljs.inherit(hljs.APOS_STRING_MODE, { + className: 'string' + }); + const QUOTE_META_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, { + className: 'string' + }); + const TAG_INTERNALS = { + endsWithParent: true, + illegal: /`]+/ + } + ] + } + ] + } + ] + }; + return { + name: 'HTML, XML', + aliases: [ + 'html', + 'xhtml', + 'rss', + 'atom', + 'xjb', + 'xsd', + 'xsl', + 'plist', + 'wsf', + 'svg' + ], + case_insensitive: true, + contains: [ + { + className: 'meta', + begin: //, + relevance: 10, + contains: [ + XML_META_KEYWORDS, + QUOTE_META_STRING_MODE, + APOS_META_STRING_MODE, + XML_META_PAR_KEYWORDS, + { + begin: /\[/, + end: /\]/, + contains: [ + { + className: 'meta', + begin: //, + contains: [ + XML_META_KEYWORDS, + XML_META_PAR_KEYWORDS, + QUOTE_META_STRING_MODE, + APOS_META_STRING_MODE + ] + } + ] + } + ] + }, + hljs.COMMENT( + //, + { + relevance: 10 + } + ), + { + begin: //, + relevance: 10 + }, + XML_ENTITIES, + { + className: 'meta', + begin: /<\?xml/, + end: /\?>/, + relevance: 10 + }, + { + className: 'tag', + /* + The lookahead pattern (?=...) ensures that 'begin' only matches + ')/, + end: />/, + keywords: { + name: 'style' + }, + contains: [TAG_INTERNALS], + starts: { + end: /<\/style>/, + returnEnd: true, + subLanguage: [ + 'css', + 'xml' + ] + } + }, + { + className: 'tag', + // See the comment in the