Skip to main content

Configuring Custom.JS

note

Before we begin changing anything, please familiarise yourself with how to create your own Custom.JS by following this link Custom CSS & JavaScript. We will also be utilising the Features Selected event.

Below is a snippet of the Custom.JS code for your reference:

/* place your custom JS Code here. */
const onValidateFailErrorMessage = 'Please upload a file or draw a shape'; //error message to display when the map validation errors
const onUploadFailErrorMessage = 'Something has gone wrong during submission, please check your connection and try again.';
const onIntersectionMessage = 'Intersections / Exclusions have been found, please check your shapes and try again.';

async function clientValidationFunction(executionContext, controlId) {
//user defined validation function, can use the following for getting the list of shapes, annotations and uploaded files.
//if the user needs to fix the shapes, throw an error.

console.log('Validating shapes with:');
console.log(executionContext);
console.log(controlId);

let maptaskrControl = globalThis && globalThis.top && globalThis.top.maptaskrCORE && globalThis.top.maptaskrCORE[controlId];
if (!maptaskrControl) {
console.error('Maptaskr Control not found');
return;
}

//let shapes = _getShapes();
//let annotation = _getAnnotation();
//let uploads = _getUploadedFiles();
//testing shape intersections and determine what to do with them
const shapeIntersections = await maptaskrControl.getShapeIntersections();
if (shapeIntersections && shapeIntersections.length > 0) {
if (shapeIntersections.some((res) => res.intersectionType == 'Warning')) {
//decide what to do with warnings
//if you want them resolved, throw an error here..
//throw new Error(onIntersectionMessage);
}
if (shapeIntersections.some((res) => res.intersectionType == 'Error')) {
//decide what to do with warnings
//if you want them resolved, throw an error here..
throw new Error(onIntersectionMessage);
}
if (shapeIntersections.some((res) => res.intersectionType == 'Exclusion')) {
//decide what to do with warnings
//if you want them resolved, throw an error here..
throw new Error(onIntersectionMessage);
}
}

//you can also test to make sure your shapes are in the correct position, orintation, contained within eachother, any geometric tests here.

//shapes will come in the format:
// {
// "type": "FeatureCollection",
// "features": [
// {
// "type": "Feature",
// "geometry": {
// "type": "Polygon",
// "coordinates": [
// [
// [
// 12899598.276481498,
// -3758060.96802893
// ],
// ...
// ]
// ]
// },
// "properties": {
// "uploadDocType": "Envelope",
// "markerType": "MARKER_SHAPE"
// }
// }
// ],
// "DocumentType": "Envelope",
// "annotationId": "1ffb72d6-c7c3-ed11-83fd-002248e1bcf1",
// "longlat": [
// 12899440.776481498,
// -3758143.46802893
// ],
// "styleProperty": {
// "geometry_": null,
// "fill_": {
// "color_": "rgba(149,255,0,0.1)"
// },
// ...
// }
// }

//if you require a specific subset of objects, please look into the shapes, annotations, or uploads to ensure specific number of shapes or named shapes are included.
}

if (globalThis && globalThis.top) {
globalThis.top.maptaskrReady = function (pageContext, controlId) {
console.log('Maptaskr Map ID: ' + controlId + ' has Loaded');

let maptaskrControl = globalThis && globalThis.top && globalThis.top.maptaskrCORE && globalThis.top.maptaskrCORE[controlId];

if (maptaskrControl) {
/* Use the following console logs to uniquely identify your map */
// console.log(pageContext);
// console.log(maptaskrControl.registeredLocation);
// console.log(maptaskrControl.webresourceLocation);

/* register the correct client validation function here */
maptaskrControl.clientValidationFunction = clientValidationFunction;

/* put your setup methods here */
/* e.g. maptaskrControl.disableSaving = true; - this will disable the inbuilt save methods, you an use maptaskrControl.saveShapes() to save your own shapes.*/

/* put your event registrations here. */
/* e.g. maptaskrControl.on("FeaturesSelected", ...) */
}
};
}

Now at the top of the file, let's declare a function called addButtonToDialog. This function will target the appropriate HTML element to find a given attribute property and also render a custom button with its own onclick function that you can customise to your requirements.

note

In the example code below, we are specifically targetting an attribute property called tenid. This will may not exist in the layer you are using so please adjust your code accordingly.

function addButtonToDialog() {
// Check if the button already exists in the .sidebar-content-right
let sidenavContent = document.querySelector('.sidebar-content-right');
//remove the button
let existingButton = sidenavContent.querySelector('#selectProperty');
if (existingButton) {
existingButton.remove();
}
// Identify the current sidebar tenement id
let tenid = null;

let rows = sidenavContent.querySelectorAll('table tr');
rows.forEach((row) => {
let cells = row.querySelectorAll('td');
if (cells.length > 1) {
if (cells[0].textContent.trim() === 'tenid') {
tenid = cells[1].textContent.trim();
}
}
});

if (!tenid) {
console.warn('tenid not found in sidebar content.');
return;
}

// Create the button element
let button = document.createElement('button');
button.id = 'selectProperty';
button.className = 'btn btn-primary';
button.type = 'button';
button.style.backgroundColor = 'var(--main-colour)';
button.style.padding = '10px';
button.style.margin = '10px';
button.style.width = 'calc(100% - 20px)';
button.style.fontSize = '14px';

button.textContent = 'Do something cool';

// Set the button's click event
button.onclick = function (event) {
event.preventDefault(); // Prevent default behavior

//retrieve tenid again to get latest tenid in case it has changed
let rows = sidenavContent.querySelectorAll('table tr');
rows.forEach((row) => {
let cells = row.querySelectorAll('td');
if (cells.length > 1) {
if (cells[0].textContent.trim() === 'tenid') {
tenid = cells[1].textContent.trim();
}
}
});

alert("I can do something with the tenement id: " + tenid);
};

// Add the button to the sidenav content
let targetDiv = document.querySelector(".sidebar-content-right .sidebar-section table");
if (targetDiv) {
targetDiv.insertBefore(button, targetDiv.firstChild);
}

// Hook up to all <a> elements in the dialog to re-call this function after timeout
let links = sidenavContent.querySelectorAll('a');
links.forEach((link) => {
link.addEventListener('click', () => {
setTimeout(addButtonToDialog, 100); // Re-call after a short delay
});
});
}

Now we need to register the appropriate extension function within the maptaskrReady functional scope. For this, we will use the Features Selected event to ensure we fire addButtonToDialog only if a layer feature has been selected. You can also customise this to limit it based on a specific layer.

As shown in the highlighted code snippet below, we listen to FeaturesSelected and trigger addButtonToDialog in a timeout to cater for any delays in the UI.

if (globalThis && globalThis.top) {
globalThis.top.maptaskrReady = function (pageContext, controlId) {
console.log('Maptaskr Map ID: ' + controlId + ' has Loaded');

let maptaskrControl = globalThis && globalThis.top && globalThis.top.maptaskrCORE && globalThis.top.maptaskrCORE[controlId];

if (maptaskrControl) {

maptaskrControl.clientValidationFunction = clientValidationFunction;

//put your event registrations here
maptaskrControl.on("FeaturesSelected", function(featureArray) {
setTimeout(addButtonToDialog, 100); // Delay to allow for UI adjustments if necessary
});
}
};
}

Let's see this code in action!

When a layer feature is selected,an attribute popup appears on the right-hand side of the screen and we should see our custom button appear on the top. Clicking this button should display an alert message containing the tenid value from the attribute popup.

Below is a gif demonstrating this functionality:

Success! Using the code snippets above we have successfully listened to the FeaturesSelected event and extended it to call a function that will generate a custom button with a custom action that can retrieve specific attribute data from the feature.