- LayerControl: topogrphy default of
- performance optimization for vendor file (only importing specific classes from three.js - not the whole library)
This commit is contained in:
parent
069f972c6b
commit
c60220d08d
|
@ -5,7 +5,7 @@ import { BufferGeometry } from 'three/src/core/BufferGeometry';
|
||||||
export class SelectionBoxLine {
|
export class SelectionBoxLine {
|
||||||
|
|
||||||
constructor(v0, v1, f0, f1, selection) {
|
constructor(v0, v1, f0, f1, selection) {
|
||||||
// var lineGeometry = new THREE.Geometry();
|
// var lineGeometry = new Geometry();
|
||||||
// lineGeometry.vertices.push(v0, v1);
|
// lineGeometry.vertices.push(v0, v1);
|
||||||
// lineGeometry.computeLineDistances();
|
// lineGeometry.computeLineDistances();
|
||||||
// lineGeometry.dynamic = true;
|
// lineGeometry.dynamic = true;
|
||||||
|
|
|
@ -14,7 +14,8 @@ export class LayerControl extends Control {
|
||||||
options = {
|
options = {
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
position: 'topright',
|
position: 'topright',
|
||||||
autoZIndex: true
|
autoZIndex: true,
|
||||||
|
parentDiv: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
// private class fields:
|
// private class fields:
|
||||||
|
@ -29,6 +30,7 @@ export class LayerControl extends Control {
|
||||||
_layers;
|
_layers;
|
||||||
_handlingClick;
|
_handlingClick;
|
||||||
_dialog;
|
_dialog;
|
||||||
|
_layerContainer;
|
||||||
|
|
||||||
constructor(overlayLayers, options) {
|
constructor(overlayLayers, options) {
|
||||||
super(options);
|
super(options);
|
||||||
|
@ -101,6 +103,10 @@ export class LayerControl extends Control {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onRemove(){
|
||||||
|
//to do
|
||||||
|
}
|
||||||
|
|
||||||
_addLayer(layer, name, overlay) {
|
_addLayer(layer, name, overlay) {
|
||||||
let id = util.stamp(layer);
|
let id = util.stamp(layer);
|
||||||
|
|
||||||
|
@ -110,6 +116,10 @@ export class LayerControl extends Control {
|
||||||
overlay: overlay
|
overlay: overlay
|
||||||
};
|
};
|
||||||
layer.addListener('visibility-change', this._updateLayerList, this);
|
layer.addListener('visibility-change', this._updateLayerList, this);
|
||||||
|
layer.addListener('visibility-change',(args) => {
|
||||||
|
let visible = args[0];
|
||||||
|
this._onVisibilityChange(id, visible);
|
||||||
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateLayerList() {
|
_updateLayerList() {
|
||||||
|
@ -135,15 +145,14 @@ export class LayerControl extends Control {
|
||||||
}
|
}
|
||||||
|
|
||||||
_addLegendEntry(obj) {
|
_addLegendEntry(obj) {
|
||||||
var checked = obj.layer.visible;//this._map.hasLayer(obj.layer);
|
let container = obj.overlay ? this._overlaysList : this._baseLayersList;
|
||||||
var container = obj.overlay ? this._overlaysList : this._baseLayersList;
|
|
||||||
//container.appendChild(legendEntryRow);
|
//container.appendChild(legendEntryRow);
|
||||||
|
|
||||||
var legendEntryRow = dom.createDom("tr", { style: "display: row-table; height: 20px;" }, container);
|
let legendEntryRow = dom.createDom("tr", { style: "display: row-table; height: 20px;" }, container);
|
||||||
//domStyle.set(legendEntryRow, 'display', rowVisibility);
|
//domStyle.set(legendEntryRow, 'display', rowVisibility);
|
||||||
//dom.setProperties(legendEntryRow, { style: "display: row-table;" });
|
//dom.setProperties(legendEntryRow, { style: "display: row-table;" });
|
||||||
|
|
||||||
var legendDataCell = dom.createDom("td", { "style": "width:25px;vertical-align: top;" }, legendEntryRow);
|
let legendDataCell = dom.createDom("td", { "style": "width:25px;vertical-align: top;" }, legendEntryRow);
|
||||||
let legendDiv = dom.createDom("div", { "style": "width:20px; height:20px;" }, legendDataCell);
|
let legendDiv = dom.createDom("div", { "style": "width:20px; height:20px;" }, legendDataCell);
|
||||||
legendDiv.style.backgroundColor = "#" + obj.layer.color;
|
legendDiv.style.backgroundColor = "#" + obj.layer.color;
|
||||||
|
|
||||||
|
@ -216,6 +225,28 @@ export class LayerControl extends Control {
|
||||||
return legendEntryRow;
|
return legendEntryRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onVisibilityChange(layerId, visible) {
|
||||||
|
if (! this._layerContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let inputs = this._layerContainer.getElementsByTagName('input');
|
||||||
|
// this._handlingClick = true;
|
||||||
|
|
||||||
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
|
let input = inputs[i];
|
||||||
|
if (input.type == 'checkbox' && layerId === input.layerId) {
|
||||||
|
// let objLayer = this._layers[input.layerId];
|
||||||
|
// var isChecked = input.checked;
|
||||||
|
// obj.layer.setVisible(isChecked);
|
||||||
|
input.checked = visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this._handlingClick = false;
|
||||||
|
this._map.update();
|
||||||
|
//this._refocusOnMap();
|
||||||
|
}
|
||||||
|
|
||||||
_onInputClick(layerId) {
|
_onInputClick(layerId) {
|
||||||
let inputs = this._layerContainer.getElementsByTagName('input');
|
let inputs = this._layerContainer.getElementsByTagName('input');
|
||||||
this._handlingClick = true;
|
this._handlingClick = true;
|
|
@ -170,7 +170,7 @@ export class NorthArrow extends Control {
|
||||||
];
|
];
|
||||||
this.boxGeometry = new UpdatableBoxGeometry(vertices);
|
this.boxGeometry = new UpdatableBoxGeometry(vertices);
|
||||||
this.boxMesh = new Mesh(this.boxGeometry,
|
this.boxMesh = new Mesh(this.boxGeometry,
|
||||||
[southMaterial, material.BoxBackFace, eastMaterial, westMaterial, northMaterial, topMaterial]);
|
[southMaterial, material.BoxBackFace, westMaterial, eastMaterial, northMaterial, topMaterial]);
|
||||||
|
|
||||||
// this.boxGeometry = new BoxGeometry(5, 5, 5);
|
// this.boxGeometry = new BoxGeometry(5, 5, 5);
|
||||||
// this.boxMesh = new Mesh(this.boxGeometry,
|
// this.boxMesh = new Mesh(this.boxGeometry,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { TinLayer } from '../layer/TinLayer';
|
||||||
import { DemLayer } from '../layer/DemLayer';
|
import { DemLayer } from '../layer/DemLayer';
|
||||||
import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera';
|
import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera';
|
||||||
import { Vector3 } from 'three/src/math/Vector3';
|
import { Vector3 } from 'three/src/math/Vector3';
|
||||||
import { Group } from 'three';
|
import { Group } from 'three/src/objects/Group';
|
||||||
import { Layer } from '../layer/Layer';
|
import { Layer } from '../layer/Layer';
|
||||||
import { LayerService } from '../services/layer.service';
|
import { LayerService } from '../services/layer.service';
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ class Map extends OrbitControls {
|
||||||
"currentVersion": 10.01,
|
"currentVersion": 10.01,
|
||||||
"services": [
|
"services": [
|
||||||
{ "name": "osm:wms", "type": "MapServer", 'image': 'background_osm_world_topography.png', 'title': 'OSM WMS' },
|
{ "name": "osm:wms", "type": "MapServer", 'image': 'background_osm_world_topography.png', 'title': 'OSM WMS' },
|
||||||
{ "name": "osm:gray-wms", "type": "MapServer", 'image': 'background_esri_world_topography.png', 'title': 'OSM Gray WMS' },
|
{ "name": "osm:gray-wms", "type": "MapServer", 'image': 'background_osm_gray_world_topography.png', 'title': 'OSM Gray WMS' },
|
||||||
// { "name": "esri:topograhy", "type": "MapServer", 'image': 'background_esri_world_topography.png', 'title': 'ESRI Topography' },
|
// { "name": "esri:topograhy", "type": "MapServer", 'image': 'background_esri_world_topography.png', 'title': 'ESRI Topography' },
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,9 +25,9 @@ export class DemBlock {
|
||||||
let heightSegments = yPixel - 1;
|
let heightSegments = yPixel - 1;
|
||||||
|
|
||||||
//appSettings.Options.exportMode = true;
|
//appSettings.Options.exportMode = true;
|
||||||
// let PlaneGeometry = (appSettings.Options.exportMode) ? THREE.PlaneGeometry : THREE.PlaneBufferGeometry;
|
// let PlaneGeometry = (appSettings.Options.exportMode) ? PlaneGeometry : PlaneBufferGeometry;
|
||||||
//var geom = layer.mainGeometry = new PlaneGeometry(this.plane.width, this.plane.height, widthSegments, heightSegments);
|
//var geom = layer.mainGeometry = new PlaneGeometry(this.plane.width, this.plane.height, widthSegments, heightSegments);
|
||||||
let geom = layer.mainGeometry = this.buildPlaneBufferGeometry(this.plane.width, this.plane.height, widthSegments, heightSegments);// new THREE.PlaneBufferGeometry(this.plane.width, this.plane.height, 11, 7);
|
let geom = layer.mainGeometry = this.buildPlaneBufferGeometry(this.plane.width, this.plane.height, widthSegments, heightSegments);// new PlaneBufferGeometry(this.plane.width, this.plane.height, 11, 7);
|
||||||
|
|
||||||
// let geom = layer.mainGeometry = new PlaneBufferGeometry( 10, 5, 20, 20 );
|
// let geom = layer.mainGeometry = new PlaneBufferGeometry( 10, 5, 20, 20 );
|
||||||
|
|
||||||
|
@ -90,12 +90,12 @@ export class DemBlock {
|
||||||
//}
|
//}
|
||||||
geom.computeBoundingBox();//for building border geometry
|
geom.computeBoundingBox();//for building border geometry
|
||||||
|
|
||||||
//var material = new THREE.MeshPhongMaterial({ color: 0x223322, wireframe: true });
|
//var material = new MeshPhongMaterial({ color: 0x223322, wireframe: true });
|
||||||
//var mesh = new THREE.Mesh(geom, material);
|
//var mesh = new Mesh(geom, material);
|
||||||
|
|
||||||
//var wireframe_material = new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: true, wireframe_linewidth: 10 });
|
//var wireframe_material = new MeshBasicMaterial({ color: 0x000000, wireframe: true, wireframe_linewidth: 10 });
|
||||||
//var materials = [layer.materials[this.mIndex].mat, wireframe_material];
|
//var materials = [layer.materials[this.mIndex].mat, wireframe_material];
|
||||||
//var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, materials);
|
//var mesh = SceneUtils.createMultiMaterialObject(geom, materials);
|
||||||
|
|
||||||
|
|
||||||
var mesh = layer.mainMesh = new Mesh(geom, layer.material); //layer.materials[this.mIndex].mat);
|
var mesh = layer.mainMesh = new Mesh(geom, layer.material); //layer.materials[this.mIndex].mat);
|
||||||
|
@ -103,7 +103,7 @@ export class DemBlock {
|
||||||
|
|
||||||
//mesh.matrixAutoUpdate = true;
|
//mesh.matrixAutoUpdate = true;
|
||||||
|
|
||||||
//var egh = new THREE.EdgesHelper(mesh, 0x00ffff);
|
//var egh = new EdgesHelper(mesh, 0x00ffff);
|
||||||
//egh.material.linewidth = 2;
|
//egh.material.linewidth = 2;
|
||||||
//layer.addObject(egh);
|
//layer.addObject(egh);
|
||||||
|
|
||||||
|
@ -194,8 +194,8 @@ export class DemBlock {
|
||||||
|
|
||||||
}
|
}
|
||||||
//geometry.attributes['index'] = { array: indices, itemSize: 1 };
|
//geometry.attributes['index'] = { array: indices, itemSize: 1 };
|
||||||
//geometry.setIndex(new THREE.BufferAttribute(indices, 1).setDynamic(true));
|
//geometry.setIndex(new BufferAttribute(indices, 1).setDynamic(true));
|
||||||
//geometry.addAttribute('index', new THREE.BufferAttribute(indices, 1));
|
//geometry.addAttribute('index', new BufferAttribute(indices, 1));
|
||||||
let index = new BufferAttribute(indices, 1);//setDynamic(true);
|
let index = new BufferAttribute(indices, 1);//setDynamic(true);
|
||||||
geometry.setIndex(index);
|
geometry.setIndex(index);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { EventEmitter } from '../core/EventEmitter';
|
||||||
import { BoreholePopup } from '../controls/BoreholePopup';
|
import { BoreholePopup } from '../controls/BoreholePopup';
|
||||||
import { Map } from '../core/Map';
|
import { Map } from '../core/Map';
|
||||||
import { Scene } from 'three/src/scenes/Scene';
|
import { Scene } from 'three/src/scenes/Scene';
|
||||||
import { Group } from 'three';
|
import { Group } from 'three/src/objects/Group';
|
||||||
|
|
||||||
abstract class Layer extends EventEmitter {
|
abstract class Layer extends EventEmitter {
|
||||||
|
|
||||||
|
|
|
@ -984,7 +984,6 @@ class TinLayer extends Layer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async changeImage(i) {
|
async changeImage(i) {
|
||||||
//this.mainMesh.material.map = THREE.ImageUtils.loadTexture(src);
|
|
||||||
let image = this.images[i];
|
let image = this.images[i];
|
||||||
// if (image.texture === undefined) {
|
// if (image.texture === undefined) {
|
||||||
if (image.type == "esri") {
|
if (image.type == "esri") {
|
||||||
|
|
|
@ -198,7 +198,13 @@ class Application {
|
||||||
this.mapTitle.innerHTML += map.title;
|
this.mapTitle.innerHTML += map.title;
|
||||||
this.menuEmailButton.href = 'mailto:' + map.contact;
|
this.menuEmailButton.href = 'mailto:' + map.contact;
|
||||||
|
|
||||||
|
|
||||||
map.on('ready', () => {
|
map.on('ready', () => {
|
||||||
|
this.layerControl = new LayerControl(this.map.layers, {
|
||||||
|
collapsed: true,
|
||||||
|
parentDiv: 'layer-control-parent-id'
|
||||||
|
}).addTo(this.map);
|
||||||
|
|
||||||
this.selectionBox.setUniforms();
|
this.selectionBox.setUniforms();
|
||||||
|
|
||||||
// this.capsScene.add(this.selectionBox.boxMesh);
|
// this.capsScene.add(this.selectionBox.boxMesh);
|
||||||
|
@ -210,6 +216,9 @@ class Application {
|
||||||
|
|
||||||
for (const [i, layer] of Object.entries(this.map.layers)) {
|
for (const [i, layer] of Object.entries(this.map.layers)) {
|
||||||
// let layer = map.layers[i];
|
// let layer = map.layers[i];
|
||||||
|
if (layer instanceof TinLayer & layer.name == "Topography") {
|
||||||
|
layer.setVisible(false);
|
||||||
|
}
|
||||||
if (layer instanceof TinLayer && layer.name != "Topography") {
|
if (layer instanceof TinLayer && layer.name != "Topography") {
|
||||||
// modelNode.add(layer.mainMesh);
|
// modelNode.add(layer.mainMesh);
|
||||||
|
|
||||||
|
@ -286,7 +295,7 @@ class Application {
|
||||||
this.selectionBox = new Selection(
|
this.selectionBox = new Selection(
|
||||||
{ name: 'Slicing Box' },
|
{ name: 'Slicing Box' },
|
||||||
new Vector3(this.map.x.min, this.map.y.min, this.map.z.min),
|
new Vector3(this.map.x.min, this.map.y.min, this.map.z.min),
|
||||||
new Vector3(this.map.x.max, this.map.y.max, this.map.z.max)
|
new Vector3(this.map.x.max, this.map.y.max, this.map.z.max + 1000)
|
||||||
);
|
);
|
||||||
// this.map.addLayer(this.selectionBox);
|
// this.map.addLayer(this.selectionBox);
|
||||||
this.map.picking = new PickingTool(this.map.size, this.map.center, this);
|
this.map.picking = new PickingTool(this.map.size, this.map.center, this);
|
||||||
|
@ -314,11 +323,6 @@ class Application {
|
||||||
this.gridlayer = new GridLayer({ center: this.map.center, name: "coordinate grid", appWidth: this.width, appHeight: this.height });
|
this.gridlayer = new GridLayer({ center: this.map.center, name: "coordinate grid", appWidth: this.width, appHeight: this.height });
|
||||||
this.map.addLayer(this.gridlayer);
|
this.map.addLayer(this.gridlayer);
|
||||||
|
|
||||||
new LayerControl(this.map.layers, {
|
|
||||||
collapsed: true,
|
|
||||||
parentDiv: 'layer-control-parent-id'
|
|
||||||
}).addTo(this.map);
|
|
||||||
|
|
||||||
this.basemapControl = new BasemapControl('Baselayer', {
|
this.basemapControl = new BasemapControl('Baselayer', {
|
||||||
position: 'topright'
|
position: 'topright'
|
||||||
}).addTo(this.map);
|
}).addTo(this.map);
|
||||||
|
|
|
@ -62,9 +62,9 @@ export class BoreholeIdentify {
|
||||||
vector.sub(this.camera.position);
|
vector.sub(this.camera.position);
|
||||||
vector.normalize();
|
vector.normalize();
|
||||||
let raycaster = new Raycaster(this.camera.position, vector);
|
let raycaster = new Raycaster(this.camera.position, vector);
|
||||||
//var raycaster = new THREE.Raycaster(vector, new THREE.Vector3(0, 1, 0).normalize());
|
//var raycaster = new Raycaster(vector, new Vector3(0, 1, 0).normalize());
|
||||||
//var direction = new THREE.Vector3(0, 0, 1);
|
//var direction = new Vector3(0, 0, 1);
|
||||||
//var raycaster = new THREE.Raycaster();
|
//var raycaster = new Raycaster();
|
||||||
//raycaster.set(vector, direction);
|
//raycaster.set(vector, direction);
|
||||||
|
|
||||||
let visibleMehses = this._getvisibleQueryableObjects();//only the visible layers
|
let visibleMehses = this._getvisibleQueryableObjects();//only the visible layers
|
||||||
|
@ -114,47 +114,47 @@ export class BoreholeIdentify {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_highlightFeature(layerId, featureId) {
|
// _highlightFeature(layerId, featureId) {
|
||||||
//if (app.highlightObject) {
|
// //if (app.highlightObject) {
|
||||||
// // remove highlight object from the scene
|
// // // remove highlight object from the scene
|
||||||
// app.scene.remove(app.highlightObject);
|
// // app.scene.remove(app.highlightObject);
|
||||||
// app.selectedLayerId = null;
|
// // app.selectedLayerId = null;
|
||||||
// app.selectedFeatureId = null;
|
// // app.selectedFeatureId = null;
|
||||||
// app.highlightObject = null;
|
// // app.highlightObject = null;
|
||||||
|
// //}
|
||||||
|
|
||||||
|
// if (layerId === null) return;
|
||||||
|
// var layer = this.layers[layerId];
|
||||||
|
// if (layer === undefined) return;
|
||||||
|
|
||||||
|
// var f = layer.features[featureId];
|
||||||
|
// if (f === undefined) return;
|
||||||
|
|
||||||
|
// var high_mat = this.highlightMaterial;
|
||||||
|
// //var setMaterial = function (obj) {
|
||||||
|
// // obj.material = high_mat;
|
||||||
|
// //};
|
||||||
|
|
||||||
|
// // create a highlight object (if layer type is Point, slightly bigger than the object)
|
||||||
|
// // var highlightObject = new Group();
|
||||||
|
// //var clone;
|
||||||
|
// //var s = (layer.type == Q3D.LayerType.Point) ? 1.01 : 1;
|
||||||
|
|
||||||
|
// var geo = new Geometry();
|
||||||
|
// var v1 = new Vector3(f.Punkt0.x, f.Punkt0.y, f.Punkt0.z);
|
||||||
|
// var v2 = new Vector3(f.Punkt1.x, f.Punkt1.y, f.Punkt1.z);
|
||||||
|
// var v3 = new Vector3(f.Punkt2.x, f.Punkt2.y, f.Punkt2.z);
|
||||||
|
// geo.vertices.push(v1);
|
||||||
|
// geo.vertices.push(v2);
|
||||||
|
// geo.vertices.push(v3);
|
||||||
|
// var face = new Face3(0, 1, 2);
|
||||||
|
// geo.faces.push(face);
|
||||||
|
// //clone.traverse(setMaterial);
|
||||||
|
// var clone = new Mesh(geo, high_mat);
|
||||||
|
|
||||||
|
// return clone;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (layerId === null) return;
|
|
||||||
var layer = this.layers[layerId];
|
|
||||||
if (layer === undefined) return;
|
|
||||||
|
|
||||||
var f = layer.features[featureId];
|
|
||||||
if (f === undefined) return;
|
|
||||||
|
|
||||||
var high_mat = this.highlightMaterial;
|
|
||||||
//var setMaterial = function (obj) {
|
|
||||||
// obj.material = high_mat;
|
|
||||||
//};
|
|
||||||
|
|
||||||
// create a highlight object (if layer type is Point, slightly bigger than the object)
|
|
||||||
// var highlightObject = new THREE.Group();
|
|
||||||
//var clone;
|
|
||||||
//var s = (layer.type == Q3D.LayerType.Point) ? 1.01 : 1;
|
|
||||||
|
|
||||||
var geo = new THREE.Geometry();
|
|
||||||
var v1 = new Vector3(f.Punkt0.x, f.Punkt0.y, f.Punkt0.z);
|
|
||||||
var v2 = new Vector3(f.Punkt1.x, f.Punkt1.y, f.Punkt1.z);
|
|
||||||
var v3 = new Vector3(f.Punkt2.x, f.Punkt2.y, f.Punkt2.z);
|
|
||||||
geo.vertices.push(v1);
|
|
||||||
geo.vertices.push(v2);
|
|
||||||
geo.vertices.push(v3);
|
|
||||||
var face = new THREE.Face3(0, 1, 2);
|
|
||||||
geo.faces.push(face);
|
|
||||||
//clone.traverse(setMaterial);
|
|
||||||
var clone = new THREE.Mesh(geo, high_mat);
|
|
||||||
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getvisibleQueryableObjects() {
|
_getvisibleQueryableObjects() {
|
||||||
let _queryableObjects = [];
|
let _queryableObjects = [];
|
||||||
// this.layers.forEach(function (layer) {
|
// this.layers.forEach(function (layer) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ export class BoreholeTool extends EventEmitter {
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
zIndex: 1000,
|
zIndex: 1000,
|
||||||
markerClass: MarkerLayer,//THREE.CylinderGeometry,
|
markerClass: MarkerLayer,// CylinderGeometry,
|
||||||
drawingCSSClass: 'gba-editable-drawing',
|
drawingCSSClass: 'gba-editable-drawing',
|
||||||
drawingCursor: 'crosshair'
|
drawingCursor: 'crosshair'
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user