src/utility/configurablesDataSearch/dataConfigurableDataSearch.ts
constructor(injector: Injector, distributionDetails: DistributionDetails, paramValues: Array
|
||||||||||||||||||
Parameters :
|
Public Readonly actions |
Type : Array<DataConfigurableAction>
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:43
|
Public Readonly isDataConfigurableDataSearchLoading |
Type : boolean
|
Private layerBboxSrc |
Default value : new BehaviorSubject<number[] | null>(null)
|
Public levels |
Type : Array<DistributionLevel>
|
Public pinnedObs |
Default value : this.pinnedSrc.asObservable()
|
Private Readonly pinnedSrc |
Default value : new BehaviorSubject<boolean>(false)
|
Public selectedObs |
Default value : this.selectedSrc.asObservable()
|
Private Readonly selectedSrc |
Default value : new BehaviorSubject<boolean>(false)
|
Public changed |
Default value : false
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:49
|
Whether any of the #newParamValues have changed or not. |
Public Readonly context |
Type : string
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:44
|
Public Readonly currentParamValues |
Type : Array<ParameterValue>
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:61
|
An array of ParameterValue objects representing values that have been applied to any visualizations etc. |
Public Readonly distributionDetails |
Type : DistributionDetails
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:107
|
The detailed search result (distribution) object that a
configuration is applied to.
|
Public Readonly id |
Type : string
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:64
|
the object's identifier. |
Public Readonly isDownloadable |
Type : boolean
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:38
|
Copied from DistributionDetails |
Public Readonly isGraphable |
Type : boolean
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:40
|
Copied from DistributionDetails |
Public Readonly isMappable |
Type : boolean
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:36
|
Copied from DistributionDetails |
Public Readonly isTabularable |
Type : boolean
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:42
|
Copied from DistributionDetails |
Public loading |
Default value : false
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:53
|
Whether the #newParamValues are in the process of being applied or not. |
Public Readonly name |
Type : string
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:66
|
the object's name. |
Private newParamValues |
Type : Array<ParameterValue>
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:89
|
An array of ParameterValue objects that have been changed and have not yet been applied to any visualizations etc. |
Private Readonly parameterDefinitions |
Type : ParameterDefinitions
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:69
|
The ParameterDefinitions retrieved from a DistributionDetails item. |
Public sameAsDefaults |
Default value : true
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:51
|
Whether all of the #newParamValues are the same as the defaults or not. |
Private Readonly showSpatialCoverageSrc |
Default value : new BehaviorSubject<boolean>(false)
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:72
|
Whether a user has selected to show the spatial coverage for this item or not. |
Public showSpatialObs |
Default value : this.showSpatialCoverageSrc.asObservable()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:74
|
Private spatialLinked |
Default value : false
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:92
|
Whether any spatial parameter values in this configuration are linked to page-wide values. |
Public Readonly styleObs |
Default value : this.styleSource.asObservable()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:79
|
Private Readonly styleSource |
Default value : new BehaviorSubject<null | Style>(null)
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:77
|
An rxjs/BehaviorSubject holding the Style object for this configurable. |
Private temporalLinked |
Default value : false
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:94
|
Whether any temporal parameter values in this configuration are linked to page-wide values. |
Protected triggerReloadFunc |
Type : function
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:83
|
A function that will trigger a reload of visual representations of this object. |
Public valid |
Default value : true
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:47
|
Whether all #newParamValues are valid or not. |
Private copyToClipboard | ||||||
copyToClipboard(val: string)
|
||||||
Parameters :
Returns :
boolean
|
Private copyToClipboardWithDelay | ||||||
copyToClipboardWithDelay(text: null | string)
|
||||||
Parameters :
Returns :
void
|
Protected doApplyAction | ||||||||||||
doApplyAction(newCurrentParams: Array
|
||||||||||||
The function
Parameters :
Returns :
DataConfigurableDataSearch
The method |
Private doCopyUrlAction |
doCopyUrlAction()
|
Returns :
void
|
Private doSetToDefaultsAction |
doSetToDefaultsAction()
|
Returns :
void
|
Public getLayerBbox |
getLayerBbox()
|
Returns :
any
|
Public getLevels |
getLevels()
|
Returns :
Array<DistributionLevel>
|
Public isPinned |
isPinned()
|
Returns :
boolean
|
Public isSelected |
isSelected()
|
Returns :
boolean
|
Static makeFromSimpleObject | ||||||||||||
makeFromSimpleObject(object: Record
|
||||||||||||
Parameters :
|
Public setLayerBbox | ||||||
setLayerBbox(layerBbox: Array
|
||||||
Parameters :
Returns :
this
|
Public setLevels | ||||||
setLevels(levels: Array<DistributionLevel>)
|
||||||
Parameters :
|
Public setPinned | ||||||
setPinned(pinned: boolean)
|
||||||
Parameters :
|
Public setSelected | ||||||
setSelected(selected: boolean)
|
||||||
Parameters :
|
Public toSimpleObject |
toSimpleObject()
|
Returns :
Record<string, >
|
Public updateLinkedSpatialParams | ||||||||||||
updateLinkedSpatialParams(boundingBox: BoundingBox, newParams)
|
||||||||||||
Parameters :
Returns :
void
|
Public updateLinkedTemporalParams | ||||||||||||
updateLinkedTemporalParams(tempRange: TemporalRange, newParams)
|
||||||||||||
Parameters :
Returns :
void
|
Protected evaluateActionVisibility |
evaluateActionVisibility()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:365
|
Calls the DataConfigurableAction function on all #actions.
Returns :
void
|
Public getCurrentSpatialBounds |
getCurrentSpatialBounds()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:249
|
Returns :
BoundingBox
|
Public getCurrentTemporalRange |
getCurrentTemporalRange()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:264
|
Returns :
TemporalRange
|
Public getDistributionDetails |
getDistributionDetails()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:141
|
Retrieves the DistributionDetails that this object was created with.
Returns :
DistributionDetails
|
Public getDownloadableFormats |
getDownloadableFormats()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:176
|
Returns an Array of DistributionFormats available on this configurable, that are classified as downloadable.
Returns :
Array<DistributionFormat>
|
Public getGraphableFormats |
getGraphableFormats()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:184
|
Returns an Array of DistributionFormats available on this configurable, that are classified as graphable.
Returns :
Array<DistributionFormat>
|
Public getMappableableFormats |
getMappableableFormats()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:169
|
Returns an Array of DistributionFormats available on this configurable, that are classified as mappable.
Returns :
Array<DistributionFormat>
|
Public getNewParameterValues |
getNewParameterValues()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:320
|
Retrieves the #newParamValues variable.
Returns :
Array<ParameterValue>
|
Public getNewSpatialBounds |
getNewSpatialBounds()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:252
|
Returns :
BoundingBox
|
Public getNewTemporalRange |
getNewTemporalRange()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:267
|
Returns :
TemporalRange
|
Public getParameterDefinitions |
getParameterDefinitions()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:314
|
Retrieves the ParameterDefinitions from the DistributionDetails that this object was created with.
Returns :
ParameterDefinitions
|
Public getShowSpatialCoverage |
getShowSpatialCoverage()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:302
|
Returns :
boolean
|
Public getSpatialCoverage |
getSpatialCoverage()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:306
|
Returns :
null | SpatialRange
|
Public getStyle |
getStyle()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:207
|
Returns the style in #styleSource.
Returns :
null | Style
|
Public isOnlyDownloadable |
isOnlyDownloadable()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:324
|
Returns :
boolean
|
Public isSpatialLinked |
isSpatialLinked()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:237
|
returns the value of the the #spatialLinked variable.
Returns :
boolean
|
Public isTemporalLinked |
isTemporalLinked()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:256
|
Returns :
boolean
|
Protected paramsSameAsDefaults |
paramsSameAsDefaults()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:393
|
Returns :
boolean
|
Protected paramValuesSame | ||||||||||||
paramValuesSame(params1: Array
|
||||||||||||
Inherited from
DataConfigurable
|
||||||||||||
Defined in
DataConfigurable:380
|
||||||||||||
This function compares two Arrays of ParameterValues. As default value on param definitions are now set to "" on init if not set, ths number of param values should be constant.
Parameters :
Returns :
boolean
true if all values in params1 are equivalent to the item with the same key in params2. As default value on param definitions are now set to "" on init if not set, ths number of param values should be constant. |
Public reload | ||||||||
reload(newConfigurable?: DataConfigurable)
|
||||||||
Inherited from
DataConfigurable
|
||||||||
Defined in
DataConfigurable:286
|
||||||||
The
Parameters :
|
Protected resetParameterValues |
resetParameterValues()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:332
|
Sets the #newParamValues variable to a copy of the #currentParamValues variable, as it was after initialisation.
Returns :
void
|
Public setLoading | ||||||
setLoading(loading: boolean)
|
||||||
Inherited from
DataConfigurable
|
||||||
Defined in
DataConfigurable:159
|
||||||
Sets the internal #loading variable before calling #updateActionsEnabledStatus.
Parameters :
|
Public setNewParams | ||||||||
setNewParams(newParamValues: Array
|
||||||||
Inherited from
DataConfigurable
|
||||||||
Defined in
DataConfigurable:227
|
||||||||
The function
Parameters :
|
Protected setParameterValuesToDefaults |
setParameterValuesToDefaults()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:339
|
Sets the #newParamValues variable to the default values obtained from the #distributionDetails object.
Returns :
void
|
Public setShowSpatialCoverage | ||||||
setShowSpatialCoverage(show: boolean)
|
||||||
Inherited from
DataConfigurable
|
||||||
Defined in
DataConfigurable:296
|
||||||
Parameters :
|
Public setSpatialLinked | ||||||||
setSpatialLinked(linked: boolean)
|
||||||||
Inherited from
DataConfigurable
|
||||||||
Defined in
DataConfigurable:244
|
||||||||
Sets the value of the the #spatialLinked variable and calls reload.
Parameters :
|
Public setStyle | |||||||||||||||
setStyle(style: null | Style, force)
|
|||||||||||||||
Inherited from
DataConfigurable
|
|||||||||||||||
Defined in
DataConfigurable:198
|
|||||||||||||||
The function
Parameters :
|
Public setTemporalLinked | ||||||
setTemporalLinked(linked: boolean)
|
||||||
Inherited from
DataConfigurable
|
||||||
Defined in
DataConfigurable:259
|
||||||
Parameters :
|
Public setTriggerReloadFunc | ||||||
setTriggerReloadFunc(func: (configurable: DataConfigurable) => void)
|
||||||
Inherited from
DataConfigurable
|
||||||
Defined in
DataConfigurable:271
|
||||||
Parameters :
|
Public setValid | ||||||||
setValid(valid: boolean)
|
||||||||
Inherited from
DataConfigurable
|
||||||||
Defined in
DataConfigurable:149
|
||||||||
Sets the internal #valid variable before calling #updateActionsEnabledStatus.
Parameters :
|
Protected updateActionsEnabledStatus |
updateActionsEnabledStatus()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:355
|
Calls the DataConfigurableAction function on all #actions using the #valid, #changed and #loading values.
Returns :
void
|
Public watchStyle |
use styleobs instead |
watchStyle()
|
Inherited from
DataConfigurable
|
Defined in
DataConfigurable:215
|
Returns an rxjs/Observable object from #styleSource, containing the current style.
Returns :
Observable<null | Style>
|
import { ParameterValue } from 'api/webApi/data/parameterValue.interface';
import { DistributionDetails } from 'api/webApi/data/distributionDetails.interface';
import { Injector } from '@angular/core';
import { DataConfigurableAction, DataConfigurableActionType } from 'utility/configurables/dataConfigurableAction';
import { SearchService } from 'services/search.service';
import { SimpleParameterValue } from 'api/webApi/data/impl/simpleParameterValue';
import { Style } from 'utility/styler/style';
import { DataConfigurableParamValues } from 'utility/configurables/dataConfigurableParamValues';
import { BoundingBox } from 'api/webApi/data/boundingBox.interface';
import { TemporalRange } from 'api/webApi/data/temporalRange.interface';
import { SimpleBoundingBox } from 'api/webApi/data/impl/simpleBoundingBox';
import { SimpleTemporalRange } from 'api/webApi/data/impl/simpleTemporalRange';
import { ExecutionService } from 'services/execution.service';
import { BehaviorSubject } from 'rxjs';
import { DataConfigurableDataSearchI } from './dataConfigurableDataSearchI.interface';
import { DistributionLevel } from 'api/webApi/data/distributionLevel.interface';
import { SimpleDistributionLevel } from 'api/webApi/data/impl/SimpleDistributionLevel';
import { NotificationService } from 'services/notification.service';
import { Tracker } from 'utility/tracker/tracker.service';
import { TrackerAction, TrackerCategory } from 'utility/tracker/tracker.enum';
export class DataConfigurableDataSearch
extends DataConfigurableParamValues
implements DataConfigurableDataSearchI {
public readonly isDataConfigurableDataSearchLoading: boolean;
public readonly actions: Array<DataConfigurableAction>;
public levels: Array<DistributionLevel>;
private readonly pinnedSrc = new BehaviorSubject<boolean>(false);
// eslint-disable-next-line @typescript-eslint/member-ordering
public pinnedObs = this.pinnedSrc.asObservable();
private layerBboxSrc = new BehaviorSubject<number[] | null>(null);
private readonly selectedSrc = new BehaviorSubject<boolean>(false);
// eslint-disable-next-line @typescript-eslint/member-ordering
public selectedObs = this.selectedSrc.asObservable();
constructor(
injector: Injector,
distributionDetails: DistributionDetails,
paramValues: Array<ParameterValue>,
spatialOverrides?: BoundingBox,
temporalOverrides?: TemporalRange,
) {
super(injector, distributionDetails, paramValues, spatialOverrides, temporalOverrides);
const copyUrlAction = new DataConfigurableAction(
'Copy URL',
() => this.doCopyUrlAction(),
() => (!this.changed && this.valid),
);
copyUrlAction.setActionTypeAction(DataConfigurableActionType.LINK);
// set up actions
this.actions = [
copyUrlAction,
new DataConfigurableAction(
'Set to defaults',
() => this.doSetToDefaultsAction(),
() => (!this.loading && !this.sameAsDefaults),
),
new DataConfigurableAction(
'Apply',
() => this.doApplyAction(this.getNewParameterValues()),
() => (!this.loading && this.changed && this.valid),
),
];
this.evaluateActionVisibility();
}
// should be kept in sync with object "toSimpleObject" method
public static makeFromSimpleObject(
object: Record<string, unknown>,
injector: Injector,
context: string,
): Promise<null | DataConfigurableDataSearch> {
const searchService = injector.get(SearchService);
return Promise.resolve<null | DataConfigurableDataSearch>(searchService.getDetailsById(String(object.id), context)
.then((dist: DistributionDetails) => {
const paramValues = (object.paramValues as Array<Record<string, unknown>>)
.map((obj: Record<string, unknown>) => new SimpleParameterValue(String(obj.name), String(obj.value)));
const layerBbox: Array<number> = [];
const levels = (object.levels as Array<Record<string, unknown>>)
.map((obj: Record<string, unknown>) => new SimpleDistributionLevel(Number(obj.id), String(obj.value)));
return new DataConfigurableDataSearch(injector, dist, paramValues)
.setStyle(Style.makeFromSimpleObject(object.style as Record<string, string | number>))
.setPinned(!!object.pinned)
.setSelected(!!object.selected)
.setSpatialLinked(!!object.spatialLinked)
.setTemporalLinked(!!object.temporalLinked)
.setShowSpatialCoverage(!!object.showSpatialCoverage)
.setLevels(levels)
.setLayerBbox(layerBbox);
})
.catch(() => null)
);
}
public setLayerBbox(layerBbox: Array<number> | null){
this.layerBboxSrc.next(layerBbox);
return this;
}
public getLayerBbox(){
return this.layerBboxSrc.value;
}
public isPinned(): boolean {
return this.pinnedSrc.value;
}
public setPinned(pinned: boolean): this {
this.pinnedSrc.next(pinned);
return this;
}
public setLevels(levels: Array<DistributionLevel>): this {
this.levels = levels;
return this;
}
public getLevels(): Array<DistributionLevel> {
return this.levels;
}
public isSelected(): boolean {
return this.selectedSrc.value;
}
public setSelected(selected: boolean): this {
this.selectedSrc.next(selected);
return this;
}
public updateLinkedSpatialParams(boundingBox: BoundingBox, newParams = true): void {
if (this.isSpatialLinked()) {
const paramDefs = this.getParameterDefinitions();
const updatedNewParams = paramDefs.updateSpatialParamsUsingBounds(boundingBox, this.getNewParameterValues());
if (newParams || (!SimpleBoundingBox.isDifferent(this.getCurrentSpatialBounds(), boundingBox))) {
this.setNewParams(updatedNewParams);
} else {
const updatedCurrentParams = paramDefs.updateSpatialParamsUsingBounds(boundingBox, this.currentParamValues.slice());
this.doApplyAction(updatedCurrentParams, updatedNewParams);
}
}
}
public updateLinkedTemporalParams(tempRange: TemporalRange, newParams = true): void {
if (this.isTemporalLinked()) {
const paramDefs = this.getParameterDefinitions();
const updatedNewParams = paramDefs.updateTemporalParamsUsingRange(tempRange, this.getNewParameterValues());
if (newParams || (!SimpleTemporalRange.isDifferent(this.getCurrentTemporalRange(), tempRange))) {
this.setNewParams(updatedNewParams);
} else {
const updatedCurrentParams = paramDefs.updateTemporalParamsUsingRange(tempRange, this.currentParamValues.slice());
this.doApplyAction(updatedCurrentParams, updatedNewParams);
}
}
}
// should be kept in sync with static "makeFromSimpleObject" method
public toSimpleObject(): Record<string, unknown> {
return {
id: this.id,
paramValues: this.currentParamValues.slice(),
style: this.getStyle(),
pinned: this.isPinned(),
selected: this.isSelected(),
spatialLinked: this.isSpatialLinked(),
temporalLinked: this.isTemporalLinked(),
showSpatialCoverage: this.getShowSpatialCoverage(),
levels: this.getLevels(),
layerBbox: this.getLayerBbox(),
};
}
/**
* The function `getOriginatorUrl` returns a promise that resolves to either `null` or a string
* representing the originator URL.
* @param [fromNewParamValue=false] - The `fromNewParamValue` parameter is a boolean value that
* indicates whether to use the new parameter values or the current parameter values. If
* `fromNewParamValue` is `true`, the method will use the new parameter values; otherwise, it will use
* the current parameter values.
* @returns a Promise that resolves to either null or a string.
*/
public getOriginatorUrl(fromNewParamValue = false): Promise<null | string> {
const exe = this.injector.get(ExecutionService);
const paramValues = fromNewParamValue ? this.getNewParameterValues().slice() : this.currentParamValues.slice();
return exe.getOriginatorUrl(this.distributionDetails, this.getParameterDefinitions(), paramValues);
}
/**
* The function `doApplyAction` sets loading to true, creates a new `DataConfigurableDataSearch`
* object with updated parameters, and reloads the data with the new configuration.
* @param newCurrentParams - The `newCurrentParams` parameter is an array of `ParameterValue` objects
* that are used as input for the `doApplyAction` method.
* @param [newNewParams] - The `newNewParams` parameter in the `doApplyAction` method is an optional
* array of `ParameterValue` objects. It is used to update the parameters of a
* `DataConfigurableDataSearch` instance along with the `newCurrentParams`. If `newNewParams` is
* provided, it
* @returns The method `doApplyAction` is returning an instance of `DataConfigurableDataSearch`.
*/
protected doApplyAction(
newCurrentParams: Array<ParameterValue>,
newNewParams?: Array<ParameterValue>,
): DataConfigurableDataSearch {
this.setLoading(true);
const tracker = this.injector.get(Tracker);
tracker.trackEvent(TrackerCategory.DISTRIBUTION, TrackerAction.APPLY_PARAMETERS, this.distributionDetails.getDomainCode() + Tracker.TARCKER_DATA_SEPARATION + this.distributionDetails.getName());
const newConfigurable = new DataConfigurableDataSearch(
this.injector,
this.distributionDetails,
newCurrentParams,
)
.setStyle(this.getStyle())
.setPinned(this.isPinned())
.setSelected(this.isSelected())
.setSpatialLinked(this.isSpatialLinked())
.setTemporalLinked(this.isTemporalLinked())
.setShowSpatialCoverage(this.getShowSpatialCoverage())
.setLevels(this.getLevels())
.setLayerBbox(this.getLayerBbox());
if (null != newNewParams) {
newConfigurable.setNewParams(newNewParams);
}
this.reload(newConfigurable);
return newConfigurable;
}
private doSetToDefaultsAction(): void {
this.setParameterValuesToDefaults();
this.reload();
}
private doCopyUrlAction(): void {
if ((null != this.distributionDetails) && (null != this.injector)) {
void this.getOriginatorUrl()
.then(url => {
this.copyToClipboardWithDelay(url);
});
}
}
private copyToClipboardWithDelay(text: null | string): void {
console.log(text);
if (null != text) {
// try to add to clipboard (fails in some ie)
setTimeout(() => {
let success = false;
try {
success = this.copyToClipboard(text);
} finally {
if (null != this.injector) {
const notifier = this.injector.get(NotificationService);
if (success) {
notifier.sendNotification('Successfully copied URL', 'x', NotificationService.TYPE_SUCCESS, 5000);
} else {
notifier.sendNotification('Failed to copy URL', 'x', NotificationService.TYPE_ERROR, 5000);
}
}
}
}, 100);
}
}
private copyToClipboard(val: string): boolean {
const selBox = document.createElement('textarea');
selBox.style.position = 'fixed';
selBox.style.left = '0';
selBox.style.top = '0';
selBox.style.opacity = '0';
selBox.value = val;
document.body.appendChild(selBox);
selBox.focus();
selBox.select();
const success = document.execCommand('copy');
document.body.removeChild(selBox);
return success;
}
}