Is there an easy way to auto-resize a jqplot chart when resizing the browser? I have been looking on Google, and haven't found anything.
Resizing jqplots is discussed here.
To make it work when the browser is resized, bind the replot function up with the window.resize event:
$(window).resize(function() {
plot1.replot( { resetAxes: true } );
});
Running code:
$(document).ready(function(){
var plot1 = $.jqplot ('container', [[3,7,9,1,4,6,8,2,5]], {});
$(window).resize(function() {
plot1.replot( { resetAxes: true } );
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.8/jquery.jqplot.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.8/jquery.jqplot.min.css">
<div id="container"></div>
I've found that using replot doesn't always give consistent results if you've got a lot of options on your graphs. The only way I've found to have complex graphs cope with window resizes is to get brutal and destroy it and rebuild it.
function () {
var plot;
var renderGraph = function() {
plot = $.jqplot('chartId', yourChartData, yourChartOptions);
}
renderGraph();
var resizeGraph = function() {
if (plot)
plot.destroy();
renderGraph();
}
$(window).resize(function() {
resizeGraph();
});
}
Related
Following my previous question:Load json map into aframe by aframe-geo-projection-component created by earthlinginteractive
Now, I am trying to find a way to show different geometries (country or city) in AFrame each with a different color,
looking for something like this.
I do not mind which library or approach are used, Found this but not sure how to convert this D3.js to Aframe. However as the community bot asked me to provide more details, I have put the code that I am working on below. Not sure which part I need to modify to have such a color coded map but I am working on that. Again, any working example works for me even if someone can show what I need to modify in the below code.
<!-- https://github.com/EarthlingInteractive/aframe-geo-projection-component
https://earthlinginteractive.github.io/aframe-geo-projection-component/ -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>A-Frame Geo Projection Component - U.S. Population 2017 (est.)</title>
<meta name="description" content="Visualization of estimated U.S. population by state in 2017"></meta>
<script src="https://aframe.io/releases/0.7.1/aframe.min.js"></script>
<script src="//cdn.rawgit.com/donmccurdy/aframe-extras/v3.12.4/dist/aframe-extras.min.js"></script>
<script src="https://unpkg.com/super-hands#2.1.0/dist/super-hands.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-queue.v3.js"></script>
<script src="https://earthlinginteractive.github.io/aframe-geo-projection-component/dist/aframe-geo-projection-component.min.js"></script>
</head>
<body>
<script>
AFRAME.registerComponent('geo-extrude-population-renderer', {
dependencies: ['geo-projection'],
schema: {
maxExtrudeHeight: {
default: 2
}
},
init: function () {
this.system = this.el.sceneEl.systems['geo-projection'];
this.geoProjectionComponent = this.el.components['geo-projection'];
this.geoJsonReady = this.geoJsonReady.bind(this);
// Wait for geoJson to finish loading to avoid race conditions
this.el.addEventListener('geo-src-loaded', this.geoJsonReady);
},
update: function (oldData) {
if (!this.geoProjectionComponent.geoJson) {
return;
}
if (this.data.maxExtrudeHeight !== oldData.maxExtrudeHeight) {
this.geoJsonReady();
}
},
geoJsonReady: function () {
// Now kick off loading the data
d3.queue()
.defer(d3.csv, 'https://cdn.glitch.global/c153e3cf-7430-444d-9897-4e97f1ef8d35/us-population-2017.csv?v=1657704183925', function (d) {
return {
state: d.state,
population: +d.population
}
})
.defer(d3.csv, 'https://cdn.glitch.global/c153e3cf-7430-444d-9897-4e97f1ef8d35/us-state-county-geocodes-v2016.csv?v=1657704190160')
.await(this.onDataLoaded.bind(this));
},
onDataLoaded: function(error, populationData, geocodes) {
if (error) throw error;
var stateFips = geocodes.filter(function (row) {
return row['Summary_Level'] === '040'
});
var maxPopulation = d3.max(populationData, function (d) {
return d.population;
});
var populationByFipsCode = populationData.reduce(function (accum, d) {
var fipsForState = stateFips.find(function (fipsRow) { return fipsRow['Area_Name'] === d.state; });
var fipsCode = fipsForState['State_Code_FIPS'];
accum[fipsCode] = d.population;
return accum;
}, {});
this.render(populationByFipsCode, maxPopulation, this.data.maxExtrudeHeight);
},
render: function (populationByFipsCode, maxPopulation, maxExtrudeHeight) {
if (!populationByFipsCode) return;
var material = [];
var extrudeGeometry = null;
var stateOutlineVertices = [];
// Split the geoJson into features and render each one individually so that we can set a different
// extrusion height for each based on the population.
this.geoProjectionComponent.geoJson.features.forEach(function (feature) {
var population = populationByFipsCode[feature.id];
var extrudeAmount = (population / maxPopulation) * maxExtrudeHeight;
var material="#999999";
const extrudeSettings = {
amount: extrudeAmount,
bevelEnabled: false
};
//Math.floor(Math.random()*16777215).toString(16);
var mapRenderContext = this.system.renderToContext(feature, this.geoProjectionComponent.projection);
const stateShapes = mapRenderContext.toShapes();
// Gather the outline of the state and set the height of the outline to the extrude level
// so that the top of the state is outlined
stateOutlineVertices = stateOutlineVertices.concat(mapRenderContext.toVertices(extrudeAmount));
// Merge all the extruded feature geometries together for better rendering performance
// Need to use ExtrudeGeometry here instead of ExtrudeBufferGeometry because the latter doesn't merge properly
// in this version of Three.js
var extrudedFeatureGeometry = new THREE.ExtrudeGeometry(stateShapes, extrudeSettings);
if (!extrudeGeometry) {
extrudeGeometry = extrudedFeatureGeometry;
} else {
extrudeGeometry.merge(extrudedFeatureGeometry);
}
}.bind(this));
// Convert the extrude geometry into a buffer geometry for better rendering performance
var extrudeBufferGeometry = new THREE.BufferGeometry();
extrudeBufferGeometry.fromGeometry(extrudeGeometry);
var sideMaterial = new THREE.MeshStandardMaterial( { color: 0xaaafff } );
var extrudedMap = new THREE.Mesh(extrudeBufferGeometry, [material, sideMaterial]);
this.el.setObject3D('map', extrudedMap);
var stateOutlineGeometry = new THREE.BufferGeometry();
stateOutlineGeometry.addAttribute('position', new THREE.Float32BufferAttribute(stateOutlineVertices, 3));
var stateOutlineMaterial = new THREE.LineBasicMaterial( { color: 0xcccaaa } );
var stateOutlines = new THREE.LineSegments(stateOutlineGeometry, stateOutlineMaterial);
this.el.setObject3D('lines', stateOutlines);
}
});
</script>
<a-scene>
<a-assets>
<a-asset-item id="json-us" src="https://unpkg.com/us-atlas#1/us/10m.json" />
</a-assets>
<a-sky color="#ECECEC"></a-sky>
<a-entity id="map"
rotation="-90 0 0"
material="color: #123456;"
topojson-loader="src: #json-us; topologyObject: states;"
geo-projection="
projection: geoIdentity;
height: 6;
width: 10;"
geo-extrude-population-renderer
>
</a-entity>
<a-locomotor>
<a-entity hand-controls="left" super-hands></a-entity>
<a-entity hand-controls="right" super-hands></a-entity>
</a-locomotor>
</a-scene>
</body>
</html>
<!--material="color: #123456;"-->
getting this error as well if I use aframe 1.3.0:
Uncaught TypeError: extrudeBufferGeometry.fromGeometry is not a function
at i.render ((index):104:29)
at i.onDataLoaded ((index):66:12)
at Queue._call (d3-queue.v3.js:43:54)
at maybeNotify (d3-queue.v3.js:120:7)
at d3-queue.v3.js:91:12
at Object.<anonymous> (d3.v4.js:11470:86)
at Dispatch.call (d3.v4.js:792:72)
at XMLHttpRequest.respond (d3.v4.js:11395:13)
What I'm trying to do
I'm trying to load a CSV with 65 thousand rows into a dataTable. Trying to make a static webpage to showcase some data I just parsed,
I'm using the following libraries
<script src="https://code.jquery.com/jquery-3.5.1.js" charset="utf-8"></script>
<script src="https://cdn.datatables.net/1.10.23/js/jquery.dataTables.min.js" charset="utf-8"> </script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/dt-1.10.23/datatables.min.css" />
What I did
Here is my code on the javascript side. My HTML just has a body and head. The head is then populated with a table
var tabulate = function (data, columns) {
var table = d3.select('body').append('table').attr("id", "example").attr("class", "display nowrap")
var thead = table.append('thead')
var tbody = table.append('tbody')
thead.append('tr')
.selectAll('th')
.data(columns)
.enter()
.append('th')
.text(function (d) {
return d
})
var rows = tbody.selectAll('tr')
.data(data)
.enter()
.append('tr')
var cells = rows.selectAll('td')
.data(function (row) {
return columns.map(function (column) {
return {
column: column,
value: row[column]
}
})
})
.enter()
.append('td')
.text(function (d) {
return d.value
})
return table;
}
d3.csv("all_engineers.csv", function (data) {
var columns = d3.keys(data[0]);
tabulate(data, columns);
$('#example').DataTable();
})
What is the problem?
It's tooooooo slow. It could be due to the big data set. I was wondering if there was an easy fix. I know D3 is super efficient and there might be something I'm not leveraging.
I also can't seem to use this code with d3 v6. I ended up using d3 v3 instead because this is the example I found that worked with datatables.
Thank you in advance for taking the time.
Update
Thanks to altocumulus
We figured out how to use d3 v6
The old implementation (using D3 V3)
jsfiddle.net/2nwasz43
the Updated Implementation (using D3 V6)
jsfiddle.net/gndv6rq0/1
Loading time before was taking more than 2 minutes before. I removed a few columns from the CSV and decided to add a loading gif to the HTML page since making this instantaneous was out of the question in terms of what I wanted to do
What really helped reduce the load time was loading the d3 data object directly into the datable instead of putting it in the DOM. I was getting page unresponsive in the latter but that seemed to have gone away in the former.
I'm using d3 version 3 instead of version 6 since the syntax seems cleaner and more familiar to me. If you want to use d3 version 6 then you would need to watch out for the API differences (look at https://jsfiddle.net/gndv6rq0/1 for reference)
What I ended up with was the following script
function hideLoader() {
$('#loading').hide();
}
setTimeout(hideLoader, 80 * 1000);
var tabulate = function (columns) {
var table = d3.select('body').select('table')
var thead = table.append('thead')
thead.append('tr')
.selectAll('th')
.data(columns)
.enter()
.append('th')
.text(function (d) {
return d
})
return table;
}
d3.csv("All_engineers_reduced.csv", function (data) {
var columns = d3.keys(data[0]);
tabulate(columns);
$('#example').DataTable({
data: data,
columns: [
{
"data": "Engineer_ID"
},
{
"data": "Arabic_Names"
},
{
"data": "Latin_Names"
},
{
"data": "Field_ID"
},
{
"data": "SubField_ID"
},
{
"data": "Field"
},
{
"data": "SubField"
}
],
processing: true,
language: {
processing: "<img src='https://media1.giphy.com/media/feN0YJbVs0fwA/giphy.gif'>"
}
});
hideLoader();
})
The loaded libraries are still
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://code.jquery.com/jquery-3.5.1.js" charset="utf-8"></script>
<script src="https://cdn.datatables.net/1.10.23/js/jquery.dataTables.min.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/dt-1.10.23/datatables.min.css" />
You can see the final result in the link below
https://ebrahimkaram.github.io/LebaneseEngineers/
I am trying to implement QuaggaJS in a static file way, but clearly I am missing something.
I am very new to javascript and as such hoping I am missing something very simple.
Assuming i have a .jpg file in the same directory as this code , named 123456.jpg,
I want the code to simply return the barcode value as an alert.
Any help greatly appreciated (and totally ready to be blasted for my lack of understanding of javascript!)
My code is below:
<script src="http://www.myserver.com/v3/javascripts/jquery-2.0.0.min.js" type="text/javascript"></script>
<script src="js/quagga.min.js" type="text/javascript"></script>
<script>
Quagga.decodeSingle({
decoder: {
readers: ["code_39_reader"] // List of active readers
},
locate: true, // try to locate the barcode in the image
src: '123456.jpg' // or 'data:image/jpg;base64,' + data
}, function(result){
if(result.codeResult) {
console.log("result", result.codeResult.code);
alert(result.codeResult.code);
} else {
console.log("not detected");
alert("not detected");
}
});
</script>
Maybe clumsy...but I got the following to decode a Code_39 barcode in a static .jpg file located in same directory, on page load:
<div>
<div id="resultdiv">scanning... </div>
</div>
<script src="../js/quagga.js" type="text/javascript"></script>
<script type="text/javascript">
var Quagga = window.Quagga;
var App = {
_scanner: null,
init: function() {
this.decode();
},
decode: function(file) {
Quagga
.decoder({readers: ['code_39_reader']})
.locator({patchSize: 'x-small'})
.fromSource('converted.jpg', {size: 1920})
.toPromise()
.then(function(result) {
document.getElementById("resultdiv").innerHTML=result.codeResult.code;
})
.catch(function() {
document.getElementById("resultdiv").innerHTML= "Not Found";
})
}
};
App.init();
</script>
Look at this code:
This creates four circles on the map in a same position and it creates addListener click event for each one too but I just can click on the last one. I want to fix it in a way that I can click on all of them to make setEditable(true) for each one.
<!DOCTYPE html>
<html>
<head>
<script
src="http://maps.googleapis.com/maps/api/js?key=AIzaSyDY0kkJiTPVd2U7aTOAwhc9ySH6oHxOIYM&sensor=false">
</script>
<script>
var selectedShape;
function clearSelection()
{
if(selectedShape)
{
selectedShape.setEditable(false);
selectedShape = null;
}
}
function setSelection(shape)
{
clearSelection();
selectedShape = shape;
shape.setEditable(true);
}
</script>
<script>
var amsterdam=new google.maps.LatLng(52.395715,4.888916);
function initialize()
{
var mapProp = {center:amsterdam, zoom:7, mapTypeId:google.maps.MapTypeId.ROADMAP};
var map = new google.maps.Map(document.getElementById("googleMap"),mapProp);
var myArray = [];
var myCity;
for(var i = 0; i < 4; i++)
{
myCity = new google.maps.Circle({
center:amsterdam,
radius:20000,
strokeColor:"#0000FF",
strokeOpacity:0.8,
strokeWeight:2,
fillColor:"#0000FF",
fillOpacity:0.4
});
myArray.push(myCity);
google.maps.event.addListener(myCity, 'click', function() {setSelection(myCity)});
myArray[i].setMap(map);
}
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="googleMap" style="width:500px;height:380px;"></div>
</body>
</html>
Use this instead of myCity :
google.maps.event.addListener(myCity, 'click', function() {
setSelection(this)
});
Using setSelection(myCity) will refer to the last myCity created.
I have problem with Firefox6 (don't know if it also concerns earlier versions).
I want to embed Google Map on page, and when page has scrollbars (is longer than viewport) mouse wheel not only zooms map but also scrolls page. I tried to catch mousewheel event and stop propagation but this event isn catchable when cursor os over map. When cursor is over map controls (zoom control, google logo, etc) i can catch event and stop propagation.
What is more strange it not happens always. Sometimes page srolls and after few scrolls it stops and mousewheel only zooms map (as expected). Sometimes page doesn't scroll and sometimes it scrolls with zoom all the time. Can't find pattern.
Source code is simple:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>test</title>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
window.onload = function(){
var latlng = new google.maps.LatLng(52.25, 21.01);
mapOptions = {
zoom: 12,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP,
streetViewControl: false,
zoomControl:true,
mapTypeControl:false
};
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
}
</script>
</head>
<body>
<p style="height:500px;">-lot of text-</p>
<div id="map_canvas" style="width:500px; height:500px;"></div>
<p style="height:500px;">-lot of text-</p>
</body>
</html>
Your problem is also described on code.google.com, this problem is only in Firefox, but it isn't a Firefox bug:
http://code.google.com/p/gmaps-api-issues/issues/detail?id=3652
http://code.google.com/p/gmaps-api-issues/issues/detail?id=1605
Have also found out a workaround, that is not re-scrolling or re-zooming and works fine:
A new ScrollInterceptOverlay derived from google.maps.OverlayView, prepending a div on MapPanes.overlayMouseTarget:
Version with jQuery
// Ensure to have google.maps loaded:
// var gmap = new google.maps.Map($googlemap[0], mapOptions);
// Define a ScrollInterceptOverlay function
var ScrollInterceptOverlay = function (gmap) {
if (!(this instanceof ScrollInterceptOverlay)) return;
var $div;
var $mapDiv;
var initialize = function () {
$div = $('<div />').css({
position: 'absolute', top: 0, left: 0,
display: 'inline-block'
});
var div = $div[0];
if (div && div.addEventListener) {
// Internet Explorer, Opera, Google Chrome and Safari
div.addEventListener("mousewheel", mouseScrollStop);
// Firefox
div.addEventListener("DOMMouseScroll", mouseScrollStop);
div.addEventListener("MozMousePixelScroll", mouseScrollStop);
}
else if (div && div.attachEvent) { // IE before version 9
div.attachEvent("onmousewheel", mouseScrollStop);
}
this.setMap(gmap);
};
var mouseScrollStop = function (e) {
if (e && e.preventDefault) e.preventDefault();
};
this.onAdd = function () {
$div.prependTo(this.getPanes().overlayMouseTarget);
};
this.onRemove = function () {
var div = $div[0];
if (div && div.addEventListener) {
// Internet Explorer, Opera, Google Chrome and Safari
div.addEventListener("mousewheel", mouseScrollStop);
// Firefox
div.addEventListener("DOMMouseScroll", mouseScrollStop);
div.addEventListener("MozMousePixelScroll", mouseScrollStop);
}
else if (div && div.attachEvent) { // IE before version 9
div.attachEvent("onmousewheel", mouseScrollStop);
}
$div.detach();
};
this.draw = function () {
if ($mapDiv && $mapDiv.length === 1) {
$div.css({
width: $mapDiv.outerWidth(),
height: $mapDiv.outerHeight()
});
}
};
var base_setMap = this.setMap;
this.setMap = function (map) {
$mapDiv = $(map.getDiv());
base_setMap.call(this, map);
};
initialize.call(this);
};
// Setup prototype as OverlayView object
ScrollInterceptOverlay.prototype = new google.maps.OverlayView();
// Now create a new ScrollInterceptOverlay OverlayView object:
var mapScrollInterceptor = new ScrollInterceptOverlay(gmap);
This workaround is using jQuery, required for calculating outerWidth and outerHeight, but also for better reading.
Version with pure javaScript
Tested live: http://fiddle.jshell.net/fhSMM/7/
// Ensure to have google.maps loaded:
// var gmap = new google.maps.Map(googlemap, mapOptions);
// Define a ScrollInterceptOverlay class function
var ScrollInterceptOverlay = function () {
if (!(this instanceof ScrollInterceptOverlay)) return;
var div;
// private instance function
var mouseScrollStop = function (e) {
if (e && e.preventDefault) e.preventDefault();
};
// public instance function
this.onAdd = function () {
div = document.createElement('div');
div.style.display = 'inline-block';
div.style.position = 'absolute';
div.style.top = div.style.left = 0;
if (div.addEventListener) {
// Internet Explorer, Opera, Google Chrome and Safari
div.addEventListener("mousewheel", mouseScrollStop);
// Firefox
div.addEventListener("DOMMouseScroll", mouseScrollStop);
div.addEventListener("MozMousePixelScroll", mouseScrollStop);
}
else if (div.attachEvent) { // IE before version 9
div.attachEvent("onmousewheel", mouseScrollStop);
}
var pane = this.getPanes().overlayMouseTarget;
var firstChild = pane.firstChild;
if (!firstChild) {
pane.appendChild(div);
}
else {
pane.insertBefore(div, firstChild);
}
};
// public instance function
this.onRemove = function () {
if (div) {
if (div.removeEventListener) {
// Internet Explorer, Opera, Google Chrome and Safari
div.removeEventListener("mousewheel", mouseScrollStop);
// Firefox
div.removeEventListener("DOMMouseScroll", mouseScrollStop);
div.removeEventListener("MozMousePixelScroll", mouseScrollStop);
}
else if (div.detachEvent) { // IE before version 9
div.detachEvent("onmousewheel", mouseScrollStop);
}
var parent = div.parentNode;
parent.removeChild(div);
}
// do not delete div var'iable
div = undefined;
};
// public instance function
this.draw = function () {
var map = this.getMap();
if (map) {
var mapDiv = map.getDiv();
if (mapDiv) {
var rect = mapDiv.getBoundingClientRect();
div.style.width = rect.width + 'px';
div.style.height = rect.height + 'px';
}
}
};
};
// Setup prototype as OverlayView object
ScrollInterceptOverlay.prototype = new google.maps.OverlayView();
// Now create a new ScrollInterceptOverlay OverlayView object:
var mapScrollInterceptor = new ScrollInterceptOverlay();
mapScrollInterceptor.setMap(gmap);
Please visit also http://metadea.de/V/ about what (real) javaScript class functions are, and why I like jQuery :)
Works now for me.
Also in Firefox, the map is zooming on mousescroll, but no more scrolling the document.
Edit: Updated support for MozMousePixelScroll, refined jS
For now it looks like firefox bug. Will close question when bug will be fixed.
I had the same issue.
You could try to start Firefox with a brand new profile (e.g. by starting the Profile Manager - executing 'firefox -P' on windows systems - and choosing 'Create...') and see if the problem persists.
I had several old but seemingly empty user profiles lying around in the VMs I used to verify if this was a bug in FF 6 and obviously installing only the new binaries didn't help. On the other hand, creating a blank profile did, so I can only think of this being a migration glitch. If major versions of FF are going to be released on a bi-monthly basis, though, a lot of people are going to suffer from similar issues.
Why not take UI control of zooming? This works well for me.
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
</head>
<body>
<div class="canvas" style="width:600px;height:400px;"></div>
<script>
// Load event
$(function() {
var myOptions = {
zoom: 8,
center: new google.maps.LatLng(-34.397, 150.644),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map($('.canvas')[0], myOptions);
var overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(map);
// Only a Mozilla bug
if($.browser.mozilla) {
// Wait for the map DOM to be ready
google.maps.event.addListenerOnce(map, 'idle', function() {
$('.canvas > div > div:first-child > div').bind('DOMMouseScroll', function(e) {
// setTimeout needed otherwise the return false has no effect
setTimeout(function() {
// Calculate new center
var offset = $('.canvas').offset();
var pos = overlay.getProjection().fromContainerPixelToLatLng(new google.maps.Point(e.pageX-offset.left, e.pageY-offset.top));
// Calculate new zoom level
var zoom = map.getZoom();
if(e.detail < 0) zoom++;
else if(e.detail > 0) zoom--;
map.setCenter(pos);
map.setZoom(zoom);
}, 1);
// Stop propagation (prevent default)
return false;
});
});
}
});
</script>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</body>
</html>