Improved lesion removing / renumbering after removal

This commit is contained in:
Erik Ziegler 2016-01-09 23:04:22 +01:00
parent 44491c639d
commit 96ddd36151
11 changed files with 161 additions and 147 deletions

View File

@ -158,9 +158,7 @@ Template.viewer.onCreated(function() {
} }
log.info('Measurement added'); log.info('Measurement added');
syncMeasurementAndToolData(data);
addMeasurementAsToolData(data);
updateRelatedElements(data.imageId); updateRelatedElements(data.imageId);
}, },
removed: function(data) { removed: function(data) {
@ -174,34 +172,43 @@ Template.viewer.onCreated(function() {
var measurementId = data._id; var measurementId = data._id;
var toolType = data.isTarget ? 'lesion' : 'nonTarget'; var toolType = data.isTarget ? 'lesion' : 'nonTarget';
// Find the list of imageIds that needs to be updated // Remove the measurement from all the imageIds on which it exists
var imageIds = []; // as toolData
Object.keys(data.timepoints).forEach(function(timepointID) { Object.keys(data.timepoints).forEach(function(timepointID) {
// Clear the toolData for this timepoint // Clear the toolData for this timepoint
var imageId = data.timepoints[timepointID].imageId; var imageId = data.timepoints[timepointID].imageId;
removeToolDataWithMeasurementId(imageId, toolType, measurementId); removeToolDataWithMeasurementId(imageId, toolType, measurementId);
// Add this imageId to the list to be updated
// (if they are currently displayed)
imageIds.push(imageId);
}); });
// Find the enabled Cornerstone elements currently displaying these image IDs // Update all Measurements to decrement the lesion numbers for those
var enabledElements = []; // that were created after the current lesion by 1
imageIds.forEach(function(imageId) { Meteor.call('decrementLesionNumbers', data, function(error, response) {
var elems = cornerstone.getEnabledElementsByImageId(imageId); if (error) {
enabledElements = enabledElements.concat(elems); log.warn(error)
});
// Update each related viewport
enabledElements.forEach(function(enabledElement) {
// Skip thumbnails or other elements that are not primary viewports
var element = enabledElement.element;
if (!element.classList.contains('imageViewerViewport')) {
return;
} }
// Sync database data with toolData for all the measurements
// that have just been updated
// Note that here we need to use greater than and equals to
// find the Measurements, whereas on the server it's
// only "greater than", since inside this callback the
// Measurements have already been decremented.
Measurements.find({
patientId: data.patientId,
lesionNumberAbsolute: {
$gte: data.lesionNumberAbsolute
}
}).forEach(function(measurementData) {
syncMeasurementAndToolData(measurementData);
});
// Update each displayed viewport
var viewports = $('.imageViewerViewport').not('.empty');
viewports.each(function(index, element) {
cornerstone.updateImage(element); cornerstone.updateImage(element);
}); });
});
} }
}); });
} }
@ -224,7 +231,7 @@ function updateRelatedElements(imageId) {
}); });
} }
function addMeasurementAsToolData(data) { function syncMeasurementAndToolData(data) {
// Check what toolType we should be adding this to, based on the isTarget value // Check what toolType we should be adding this to, based on the isTarget value
// of the stored Measurement // of the stored Measurement
var toolType = data.isTarget ? 'lesion' : 'nonTarget'; var toolType = data.isTarget ? 'lesion' : 'nonTarget';
@ -266,6 +273,9 @@ function addMeasurementAsToolData(data) {
toolState[imageId][toolType].data.forEach(function(measurement) { toolState[imageId][toolType].data.forEach(function(measurement) {
if (measurement.id === data._id) { if (measurement.id === data._id) {
alreadyExists = true; alreadyExists = true;
// Update the toolData lesionNumber from the Measurement
measurement.lesionNumber = data.lesionNumber;
return false; return false;
} }
}); });
@ -282,7 +292,6 @@ function addMeasurementAsToolData(data) {
measurementData.isTarget = data.isTarget; measurementData.isTarget = data.isTarget;
measurementData.lesionNumber = data.lesionNumber; measurementData.lesionNumber = data.lesionNumber;
measurementData.measurementText = data.measurementText; measurementData.measurementText = data.measurementText;
measurementData.lesionName = data.lesionName;
measurementData.isDeleted = data.isDeleted; measurementData.isDeleted = data.isDeleted;
measurementData.location = data.location; measurementData.location = data.location;
measurementData.locationUID = data.locationUID; measurementData.locationUID = data.locationUID;

View File

@ -1,10 +1,8 @@
var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneTools) { (function($, cornerstone, cornerstoneMath, cornerstoneTools) {
"use strict"; "use strict";
if (cornerstoneTools === undefined) { var toolType = "lesion";
cornerstoneTools = {};
}
var configuration = { var configuration = {
setLesionNumberCallback: setLesionNumberCallback, setLesionNumberCallback: setLesionNumberCallback,
@ -29,15 +27,12 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
//doneCallback(prompt('Change your lesion location:')); //doneCallback(prompt('Change your lesion location:'));
} }
var toolType = "lesion";
///////// BEGIN ACTIVE TOOL /////// ///////// BEGIN ACTIVE TOOL ///////
function addNewMeasurement(mouseEventData) { function addNewMeasurement(mouseEventData) {
var element = mouseEventData.element; var element = mouseEventData.element;
function doneCallback(lesionNumber) { function doneCallback(lesionNumber) {
measurementData.lesionName = "Target " + lesionNumber;
measurementData.lesionNumber = lesionNumber; measurementData.lesionNumber = lesionNumber;
measurementData.active = false; measurementData.active = false;
cornerstone.updateImage(element); cornerstone.updateImage(element);
@ -82,8 +77,6 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
$(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback); $(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback);
$(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.lesion.mouseDownActivateCallback); $(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.lesion.mouseDownActivateCallback);
cornerstone.updateImage(element); cornerstone.updateImage(element);
//updateLesionCollection(measurementData);
}); });
} }
@ -91,7 +84,6 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
var element = touchEventData.element; var element = touchEventData.element;
function doneCallback(lesionNumber) { function doneCallback(lesionNumber) {
measurementData.lesionName = "Bi-Directional " + lesionNumber;
measurementData.lesionNumber = lesionNumber; measurementData.lesionNumber = lesionNumber;
measurementData.active = false; measurementData.active = false;
cornerstone.updateImage(element); cornerstone.updateImage(element);
@ -199,7 +191,6 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
measurementText: 0, measurementText: 0,
widthMeasurement: 0, widthMeasurement: 0,
perpendicularMeasurement: 0, perpendicularMeasurement: 0,
lesionName: 'Target',
isDeleted: false, isDeleted: false,
isTarget: true, isTarget: true,
uid: uuid.v4() uid: uuid.v4()
@ -262,7 +253,10 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
var k = distanceToLineP2 / newLineLength; var k = distanceToLineP2 / newLineLength;
var newIntersection = { x: data.handles.end.x + ((eventData.currentPoints.image.x - data.handles.end.x) * k), y: data.handles.end.y + ((eventData.currentPoints.image.y - data.handles.end.y) * k)}; var newIntersection = {
x: data.handles.end.x + ((eventData.currentPoints.image.x - data.handles.end.x) * k),
y: data.handles.end.y + ((eventData.currentPoints.image.y - data.handles.end.y) * k)
};
data.handles.perpendicularStart.x = newIntersection.x - distanceFromPerpendicularP1 * dy; data.handles.perpendicularStart.x = newIntersection.x - distanceFromPerpendicularP1 * dy;
data.handles.perpendicularStart.y = newIntersection.y + distanceFromPerpendicularP1 * dx; data.handles.perpendicularStart.y = newIntersection.y + distanceFromPerpendicularP1 * dx;
@ -294,7 +288,10 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
var k = distanceToLineP2 / newLineLength; var k = distanceToLineP2 / newLineLength;
var newIntersection = { x: data.handles.start.x + ((eventData.currentPoints.image.x - data.handles.start.x) * k), y: data.handles.start.y + ((eventData.currentPoints.image.y - data.handles.start.y) * k)}; var newIntersection = {
x: data.handles.start.x + ((eventData.currentPoints.image.x - data.handles.start.x) * k),
y: data.handles.start.y + ((eventData.currentPoints.image.y - data.handles.start.y) * k)
};
data.handles.perpendicularStart.x = newIntersection.x + distanceFromPerpendicularP1 * dy; data.handles.perpendicularStart.x = newIntersection.x + distanceFromPerpendicularP1 * dy;
data.handles.perpendicularStart.y = newIntersection.y - distanceFromPerpendicularP1 * dx; data.handles.perpendicularStart.y = newIntersection.y - distanceFromPerpendicularP1 * dx;
@ -330,8 +327,14 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
var dx = (data.handles.start.x - data.handles.end.x) / length; var dx = (data.handles.start.x - data.handles.end.x) / length;
var dy = (data.handles.start.y - data.handles.end.y) / length; var dy = (data.handles.start.y - data.handles.end.y) / length;
var adjustedLineP1 = {x: data.handles.start.x - fudgeFactor * dx, y: data.handles.start.y - fudgeFactor * dy}; var adjustedLineP1 = {
var adjustedLineP2 = {x: data.handles.end.x + fudgeFactor * dx, y: data.handles.end.y + fudgeFactor * dy}; x: data.handles.start.x - fudgeFactor * dx,
y: data.handles.start.y - fudgeFactor * dy
};
var adjustedLineP2 = {
x: data.handles.end.x + fudgeFactor * dx,
y: data.handles.end.y + fudgeFactor * dy
};
data.handles.perpendicularStart.x = movedPoint.x; data.handles.perpendicularStart.x = movedPoint.x;
data.handles.perpendicularStart.y = movedPoint.y; data.handles.perpendicularStart.y = movedPoint.y;
@ -386,8 +389,14 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
var dx = (data.handles.start.x - data.handles.end.x) / length; var dx = (data.handles.start.x - data.handles.end.x) / length;
var dy = (data.handles.start.y - data.handles.end.y) / length; var dy = (data.handles.start.y - data.handles.end.y) / length;
var adjustedLineP1 = {x: data.handles.start.x - fudgeFactor * dx, y: data.handles.start.y - fudgeFactor * dy}; var adjustedLineP1 = {
var adjustedLineP2 = {x: data.handles.end.x + fudgeFactor * dx, y: data.handles.end.y + fudgeFactor * dy}; x: data.handles.start.x - fudgeFactor * dx,
y: data.handles.start.y - fudgeFactor * dy
};
var adjustedLineP2 = {
x: data.handles.end.x + fudgeFactor * dx,
y: data.handles.end.y + fudgeFactor * dy
};
data.handles.perpendicularStart.x = movedPoint.x + total * dy; data.handles.perpendicularStart.x = movedPoint.x + total * dy;
data.handles.perpendicularStart.y = movedPoint.y - total * dx; data.handles.perpendicularStart.y = movedPoint.y - total * dx;
@ -729,18 +738,27 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
// Find distances between handles and textBox // Find distances between handles and textBox
var distanceToStart = getDistance(data.handles.start, data.handles.textBox); var distanceToStart = getDistance(data.handles.start, data.handles.textBox);
distancesArr.push({distance: distanceToStart, point: data.handles.start}); distancesArr.push({
distance: distanceToStart,
point: data.handles.start
});
minDistance = distanceToStart; minDistance = distanceToStart;
var distanceToEnd = getDistance(data.handles.end, data.handles.textBox); var distanceToEnd = getDistance(data.handles.end, data.handles.textBox);
distancesArr.push({distance: distanceToEnd, point: data.handles.end}); distancesArr.push({
distance: distanceToEnd,
point: data.handles.end
});
if (distanceToEnd < minDistance) { if (distanceToEnd < minDistance) {
minDistance = distanceToEnd; minDistance = distanceToEnd;
} }
if (data.handles.perpendicularStart.x && data.handles.perpendicularStart.y) { if (data.handles.perpendicularStart.x && data.handles.perpendicularStart.y) {
var distanceToPerpendicularStart = getDistance(data.handles.perpendicularStart, data.handles.textBox); var distanceToPerpendicularStart = getDistance(data.handles.perpendicularStart, data.handles.textBox);
distancesArr.push({distance: distanceToPerpendicularStart, point: data.handles.perpendicularStart}); distancesArr.push({
distance: distanceToPerpendicularStart,
point: data.handles.perpendicularStart
});
if (distanceToPerpendicularStart < minDistance) { if (distanceToPerpendicularStart < minDistance) {
minDistance = distanceToPerpendicularStart; minDistance = distanceToPerpendicularStart;
} }
@ -749,7 +767,10 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
if (data.handles.perpendicularEnd.x && data.handles.perpendicularEnd.y) { if (data.handles.perpendicularEnd.x && data.handles.perpendicularEnd.y) {
var distanceToPerpendicularEnd = getDistance(data.handles.perpendicularEnd, data.handles.textBox); var distanceToPerpendicularEnd = getDistance(data.handles.perpendicularEnd, data.handles.textBox);
distancesArr.push({distance: distanceToPerpendicularEnd, point: data.handles.perpendicularEnd}); distancesArr.push({
distance: distanceToPerpendicularEnd,
point: data.handles.perpendicularEnd
});
if (distanceToPerpendicularEnd < minDistance) { if (distanceToPerpendicularEnd < minDistance) {
minDistance = distanceToPerpendicularEnd; minDistance = distanceToPerpendicularEnd;
} }
@ -845,7 +866,7 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
var lengthText = ' L ' + length.toFixed(1) + suffix; var lengthText = ' L ' + length.toFixed(1) + suffix;
var widthText = ' W ' + width.toFixed(1) + suffix; var widthText = ' W ' + width.toFixed(1) + suffix;
var textLines = [data.lesionName, lengthText, widthText]; var textLines = ['Target ' + data.lesionNumber, lengthText, widthText];
var boundingBox = cornerstoneTools.drawTextBox(context, var boundingBox = cornerstoneTools.drawTextBox(context,
textLines, textLines,
@ -861,18 +882,6 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
} }
} }
function updateLesionCollection(lesionData) {
// TODO = Remove this in favour of measurement events
if (!lesionData.active) {
return;
}
if (lesionData.timepointID && lesionData.timepointID !== "") {
// Update Measurements Collection
LesionManager.updateLesionData(lesionData);
}
}
function doubleClickCallback(e, eventData) { function doubleClickCallback(e, eventData) {
// Prevent other double click handlers from firing after this one // Prevent other double click handlers from firing after this one
//e.stopImmediatePropagation(); //e.stopImmediatePropagation();
@ -884,16 +893,7 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
if (deleteTool === true) { if (deleteTool === true) {
cornerstoneTools.removeToolState(element, toolType, data); cornerstoneTools.removeToolState(element, toolType, data);
cornerstone.updateImage(element); cornerstone.updateImage(element);
//return;
} }
/*// TODO= Find a better way to do this! This is very messy
config.setLesionNumberCallback(data, eventData, function(lesionNumber) {
data.lesionName = "Target " + lesionNumber;
data.lesionNumber = lesionNumber;
data.active = false;
cornerstone.updateImage(element);
});*/
} }
if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
@ -948,6 +948,4 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
cornerstoneTools.lesion.setConfiguration(configuration); cornerstoneTools.lesion.setConfiguration(configuration);
return cornerstoneTools; })($, cornerstone, cornerstoneMath, cornerstoneTools);
}($, cornerstone, cornerstoneMath, cornerstoneTools));

View File

@ -35,7 +35,6 @@
var element = mouseEventData.element; var element = mouseEventData.element;
function doneCallback(lesionNumber) { function doneCallback(lesionNumber) {
measurementData.lesionName = "Non-Target " + lesionNumber;
measurementData.lesionNumber = lesionNumber; measurementData.lesionNumber = lesionNumber;
measurementData.active = true; measurementData.active = true;
cornerstone.updateImage(element); cornerstone.updateImage(element);
@ -208,7 +207,7 @@
} }
// Draw the text // Draw the text
if (data.lesionName && data.lesionName !== '') { if (data.lesionNumber) {
// Draw linked line as dashed // Draw linked line as dashed
var mid = { var mid = {
x: (handleStartCanvas.x + handleEndCanvas.x) / 2, x: (handleStartCanvas.x + handleEndCanvas.x) / 2,
@ -224,7 +223,7 @@
context.lineTo(canvasTextLocation.x + 20, canvasTextLocation.y + 20); context.lineTo(canvasTextLocation.x + 20, canvasTextLocation.y + 20);
context.stroke(); context.stroke();
var boundingBox = cornerstoneTools.drawTextBox(context, data.lesionName, canvasTextLocation.x, canvasTextLocation.y, color); var boundingBox = cornerstoneTools.drawTextBox(context, 'Non-Target ' + data.lesionNumber, canvasTextLocation.x, canvasTextLocation.y, color);
data.handles.textBox.boundingBox = boundingBox; data.handles.textBox.boundingBox = boundingBox;
} }
@ -239,7 +238,6 @@
var element = touchEventData.element; var element = touchEventData.element;
function doneCallback(lesionNumber) { function doneCallback(lesionNumber) {
measurementData.lesionName = "Non-Target " + lesionNumber;
measurementData.lesionNumber = lesionNumber; measurementData.lesionNumber = lesionNumber;
measurementData.active = true; measurementData.active = true;
cornerstone.updateImage(element); cornerstone.updateImage(element);

View File

@ -12,9 +12,9 @@
border-radius: 5px border-radius: 5px
padding: 10px 20px 10px 20px padding: 10px 20px 10px 20px
background-color: rgba(255,255,255,1) background-color: rgba(255,255,255,1)
outline: none
.btn .btn
outline: none
text-decoration: none text-decoration: none
#cancel #cancel

View File

@ -11,6 +11,7 @@
border-radius: 5px border-radius: 5px
padding: 10px 20px 10px 20px padding: 10px 20px 10px 20px
background-color: rgba(255,255,255,1) background-color: rgba(255,255,255,1)
outline: none
#closeLesionPopup #closeLesionPopup
outline: none outline: none

View File

@ -10,7 +10,9 @@ function doneCallback(measurementData, deleteTool) {
// the specified Timepoint Cell // the specified Timepoint Cell
if (deleteTool === true) { if (deleteTool === true) {
Meteor.call("removeMeasurement", measurementData.id, function(error, response) { Meteor.call("removeMeasurement", measurementData.id, function(error, response) {
console.log('Removed!'); if (error) {
log.warn(error);
}
}); });
} }
} }
@ -47,7 +49,6 @@ Template.lesionTableRow.events({
if (error) { if (error) {
log.warn(error); log.warn(error);
} }
log.info(response);
}); });
}, options); }, options);
} }

View File

@ -11,6 +11,7 @@
border-radius: 5px border-radius: 5px
padding: 10px 20px 10px 20px padding: 10px 20px 10px 20px
background-color: rgba(255,255,255,1) background-color: rgba(255,255,255,1)
outline: none
#selectNonTargetLesionLocation, #selectNonTargetLesionLocationResponse #selectNonTargetLesionLocation, #selectNonTargetLesionLocationResponse
width: 100% width: 100%

View File

@ -11,6 +11,7 @@
border-radius: 5px border-radius: 5px
padding: 10px 20px 10px 20px padding: 10px 20px 10px 20px
background-color: rgba(255,255,255,1) background-color: rgba(255,255,255,1)
outline: none
#selectNonTargetLesionLocationResponse #selectNonTargetLesionLocationResponse
width: 100% width: 100%

View File

@ -1,14 +1,10 @@
Meteor.publish('timepoints', function(patientId) { Meteor.publish('timepoints', function(patientId) {
console.log('Publish timepoints');
console.log('patientId ' + patientId);
return Timepoints.find({ return Timepoints.find({
patientId: patientId patientId: patientId
}); });
}); });
Meteor.publish('measurements', function(patientId) { Meteor.publish('measurements', function(patientId) {
console.log('Publish measurements');
console.log('patientId ' + patientId);
return Measurements.find({ return Measurements.find({
patientId: patientId patientId: patientId
}); });

View File

@ -1,54 +1,44 @@
Meteor.methods({ Meteor.methods({
"removeMeasurement": function(id) { "removeMeasurement": function(id) {
console.log(Measurements.find().fetch());
console.log("Removing: " + id);
Measurements.remove(id); Measurements.remove(id);
console.log(Measurements.find().fetch());
}, },
"removeMeasurementsByPatientId": function(patientId) { "removeMeasurementsByPatientId": function(patientId) {
Measurements.remove({patientId: patientId}); Measurements.remove({patientId: patientId});
}, },
// TODO= Check where this is used? Seems like nowhere.. "decrementLesionNumbers": function(lesionData) {
"removePatientMeasurement": function(lesionObject) { // Update all Measurements to decrement the lesion numbers for those
// Find patient data // that were created after the current lesion by 1
var measurementData = Measurements.findOne({
patientId: lesionObject.patientId,
isTarget: lesionObject.isTarget,
lesionNumber: lesionObject.lesionNumber
});
// Get timepoints // TODO: Update this when we have more criteria than patientId
var timepoints = measurementData.timepoints; // Decrement the absolute lesion number of all lesions with
// absolute lesion numbers greater than the current lesion by 1
// Create an array to hold keys of timepoints
var timepointsIds = Object.keys(timepoints).slice(0);
timepointsIds.forEach(function(timepointId) {
if (timepointId === lesionObject.timepointId) {
delete timepoints[timepointId];
}
});
var newTimepointsIds = Object.keys(timepoints);
// If there is no timepoints object, remove measurement data
if (newTimepointsIds.length) {
//Update measurement timepoints
Measurements.update({ Measurements.update({
patientId: lesionObject.patientId, patientId: lesionData.patientId,
isTarget: lesionObject.isTarget, lesionNumberAbsolute: {
lesionNumber: lesionObject.lesionNumber $gt: lesionData.lesionNumberAbsolute
}
}, { }, {
$set: { $inc: {
timepoints: timepoints lesionNumberAbsolute: -1
} }
}, {
multi: true
}); });
} else {
// Remove all data // Decrement the lesion number of all related (i.e. target or non-target)
Measurements.remove({ // lesions with lesion numbers greater than the current lesion by 1
patientId: lesionObject.patientId, Measurements.update({
isTarget: lesionObject.isTarget, patientId: lesionData.patientId,
lesionNumber: lesionObject.lesionNumber lesionNumber: {
$gt: lesionData.lesionNumber
},
isTarget: lesionData.isTarget
}, {
$inc: {
lesionNumber: -1
}
}, {
multi: true
}); });
} }
}
}); });

View File

@ -0,0 +1,19 @@
/**
* Restores the browser focus to the currently specified active viewport
* as determined from Meteor's Session variable.
*
* This is allows keydown events to be captured on the focused element
*/
setFocusToActiveViewport = function() {
// Get the list of viewports
var viewports = $('.imageViewerViewport');
// Get the current active viewport index from Session
var activeViewportIndex = Session.get('activeViewport');
// Find the div from the list of viewports
var activeViewport = viewports.eq(activeViewportIndex);
// Set the browser focus to this div
activeViewport.focus();
};