//Declaring global variables representing DOM elements which will be used throughout the application
var mapDom = document.getElementById('qd30map-cesium');
var placeholderDom = document.getElementById('loading-placeholder');
var navContainer = document.getElementById('nav-ui-wrap');
var navParent = document.getElementById('nav-arrows');
var navReset = document.getElementById('nav-reset');
var mapSearchBtn = document.getElementById('map-search');
var navElements = navParent.getElementsByTagName('button');
var searchForm = document.getElementById('map-ui-form');
var appContainer = document.getElementById('map-search');
//These will represent the coordinates of the App and Form, which will be used in centering functions (this is
//especially helpful for mobile use
var appX = null;
var appY = null;
var formX= null;
var formY = null;
navElements[0].tabIndex = 3;
navElements[1].tabIndex = 4;
navElements[2].tabIndex = 5;
navElements[3].tabIndex = 6;
navReset.tabIndex = 7;
mapSearchBtn.tabIndex = 8;
//Iterate over the navigation elements, add listener and add the appropriate camera function
for (var i=0; i < navElements.length; i++) {
navElements[i].addEventListener('mousedown', function(param) {
switch(param.target.id) {
case 'nav-up':
camera.moveUp(100);
break;
case 'nav-down':
camera.moveDown(100);
break;
case 'nav-left':
camera.moveLeft(100);
break;
case 'nav-right':
camera.moveRight(100);
break;
}
});
navElements[i].addEventListener('keydown', function(param) {
switch(param.target.id) {
case 'nav-up':
if (param.keyCode == 13) {
camera.moveUp(100);
}
break;
case 'nav-down':
if (param.keyCode == 13) {
camera.moveDown(100);
}
break;
case 'nav-left':
if (param.keyCode == 13) {
camera.moveLeft(100);
}
break;
case 'nav-right':
if (param.keyCode == 13) {
camera.moveRight(100);
}
break;
}
});
//Reset the view
navReset.onclick = function(param) {
gotoQD30();
};
navReset.onkeyup = function(param) {
if (param.keyCode == 13) {
gotoQD30();
}
}
}
//Center the form -> especially for mobile users, as the map will take up the entire screen
mapSearchBtn.onclick = function() {
centerForm();
};
mapSearchBtn.onkeydown = function(param) {
if (param.keyCode == 13) {
centerForm();
}
};
var zoomParent = document.getElementById('zoom');
var zoomElements = zoomParent.getElementsByTagName('button');
zoomElements[0].tabIndex = 1;
zoomElements[1].tabIndex = 2;
for (var a=0; a < zoomElements.length; a++) {
zoomElements[a].addEventListener('mousedown', function(param) {
var y = camera.position.y;
switch(param.target.id) {
case 'zoom-in':
camera.moveForward(250);
break;
case 'zoom-out':
camera.moveBackward(250);
break;
}
});
zoomElements[a].addEventListener('keydown', function(param) {
var y = camera.position.y;
switch(param.target.id) {
case 'zoom-in':
if (param.keyCode == 13) {
camera.moveForward(100);
}
break;
case 'zoom-out':
if (param.keyCode == 13) {
camera.moveBackward(100);
}
break;
}
});
}
//To check version
// console.log(Cesium.VERSION);
Cesium.BingMapsApi.defaultKey = 'mqrOOSbN2SMpDuWhHx0W~HVi_IW2A0UwRAV1xLIZuTQ~AnSezO-xLZ4_s1rf8ydB6Wf0aRdKtw_znPffJQ9qKbntyOPWFvitPVAvxT0v6dib';
//Instantiate the viewer. This is the primary object from which most functionality is derived
var viewer = new Cesium.Viewer('qd30map-cesium', {
animation : false,
homeButton : false,
vrButton : false,
infoBox : true,
geocoder : false,
sceneModePicker: false,
selectionIndicator: true,
timeline : false,
navigationHelpButton : false,
navigationInstructionsInitiallyVisible: false,
scene3DOnly : false,
shadows : true,
terrainShadows : true,
baseLayerPicker : false
});
var camera = viewer.camera;
var scene = viewer.scene;
var ellipsoid = scene.globe.ellipsoid;
var canvas = viewer.canvas;
var layers = viewer.scene.imageryLayers;
var globe = viewer.scene.globe;
//Reset the globe's imagery layers and begin with a light grey base
globe.imageryLayers.removeAll();
globe.baseColor = Cesium.Color.fromCssColorString('#f3f3f3');
//Add a map tiling service over top of the base layer, to show the city street details
var tonerLayer = layers.addImageryProvider(Cesium.createOpenStreetMapImageryProvider({
url : 'http://tile.stamen.com/toner/'
}));
tonerLayer.alpha = 0.2;
// var terrainProvider = new Cesium.CesiumTerrainProvider({
// url : '//assets.agi.com/stk-terrain/world',
// requestVertexNormals: true
// });
//Remove tab index from the canvas, as this won't be necessary for keyboard only users
canvas.setAttribute('tabindex', -1);
var reset = document.getElementById('nav-reset');
reset.addEventListener('click', function() {
gotoQD30();
});
//Load the datasource as a promise which triggers the "then" function upon loading
var dataSource = Cesium.GeoJsonDataSource.load('/sites/quartierdix30/files/geojson/quartier_en.json').then(function(data) {
viewer.dataSources.add(data);
gotoQD30();
//entities.values provides the entities as individual features, as found in the geoJson file. Iterate over these and
//endow them with the appropriate properties for display
var entities = data.entities.values;
for (var i = 0; i < entities.length; i++) {
var entity = entities[i];
//Buildings should have a black outline by default
entity.polygon.outline = true;
entity.polygon.outlineWidth = 1.2;
entity.polygon.outlineColor = Cesium.Color.BLACK;
//use extrudedHeight to stretch buildings to their appropriate height, as per their level property (this is
//adjustable for each node)
if (entity.properties.hasOwnProperty('level')) {
entity.polygon.extrudedHeight = 0.5 * (entity.properties.level * 10);
}
//The value of colour for each feature is dependent on which sector the entity has been assigned to.
//Boutique Node is paired with a Zone Node, and Zone Nodes are organized by Sector, which is how they derive their colour.
//The value found in the json is added dynamically everytime the map data is rebuilt
if (entity.properties.hasOwnProperty("colour")) {
if (entity.properties.hasOwnProperty("event")) {
//If the boutique has a special event, we use the StripeMaterialProperty to make it stand out
entity.polygon.material = new Cesium.StripeMaterialProperty({
evenColor : Cesium.Color.fromCssColorString(entity.properties.colour),
oddColor : Cesium.Color.BLACK,
repeat : 10
});
} else {
entity.polygon.material = Cesium.Color.fromCssColorString(entity.properties.colour);
}
}
//The description property provides all of the markup for the infobox which pops up when a boutique is selected
if (entity.properties.hasOwnProperty("description")) {
entity.description = '
' + entity.properties.description
+ '
';
}
}
});
//This function takes us to the default view of the map
function gotoQD30() {
var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(new Cesium.Cartesian3(
1278149.1996018, -4297423.8092246,
4522591.15943));
pos.latitude = Cesium.Math.toDegrees(pos.latitude);
pos.longitude = Cesium.Math.toDegrees(pos.longitude);
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, pos.height),
orientation : {
heading : 5.51747,
pitch : -1.419427,
roll : 6.2675
},
duration: 0.5
});
}
//Not currently used
function clearLayers() {
globe.imageryLayers.removeAll();
globe.baseColor = new Cesium.Color(0, 0, 0.5, 1);
}
//Not currently used
function customLayer() {
globe.imageryLayers.removeAll();
globe.baseColor = Cesium.Color.fromCssColorString('#f3f3f3');
var tonerLayer = layers.addImageryProvider(Cesium.createOpenStreetMapImageryProvider({
url : 'http://tile.stamen.com/toner/'
}));
tonerLayer.alpha = 0.2;
}
//Not currently used
function highlight(chosenColour) {
for (var j = 0; j < viewer.dataSources.get(0).entities.values.length; j++) {
var entity = viewer.dataSources.get(0).entities.values[j];
if (entity.properties.hasOwnProperty('colour')) {
if (entity.properties.colour.toLowerCase() == chosenColour.toLowerCase()) {
entity.polygon.material = Cesium.Color.BLACK;
entity.polygon.outlineWidth = 2.0;
}
}
}
}
//Resets all boutiques to their default initial colour as per their colour and event properties
function colourReset() {
// for (z = viewer.entities.values.length -1; z >= 0; z--) {
// if (viewer.entities.values[z].name.substring(0, 16) == 'selectionOutline') {
// viewer.entities.remove(viewer.entities.values[z]);
// }
// }
for (var j = 0; j < viewer.dataSources.get(0).entities.values.length; j++) {
var entity = viewer.dataSources.get(0).entities.values[j];
if (entity.properties.hasOwnProperty('colour')) {
if (entity.properties.hasOwnProperty("event")) {
entity.polygon.material = new Cesium.StripeMaterialProperty({
evenColor : Cesium.Color.fromCssColorString(entity.properties.colour),
oddColor : Cesium.Color.BLACK,
repeat : 10
});
} else {
entity.polygon.material = Cesium.Color.fromCssColorString(entity.properties.colour);
entity.polygon.outlineWidth = 1.2;
entity.polygon.outlineColor = Cesium.Color.BLACK;
}
}
}
}
//Centers the view over the map canvas
function centerApp() {
window.scrollTo(appX, appY);
}
//Centers the view over the form
function centerForm() {
window.scrollTo(formX, formY);
}
//Helper function to determine position of an element
function getPosition(el) {
var xPos = 0;
var yPos = 0;
while (el) {
if (el.tagName == "BODY") {
// deal with browser quirks with body/window/document and page scroll
var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
var yScroll = el.scrollTop || document.documentElement.scrollTop;
xPos += (el.offsetLeft - xScroll + el.clientLeft);
yPos += (el.offsetTop - yScroll + el.clientTop);
} else {
// for all other non-BODY elements
xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
yPos += (el.offsetTop - el.scrollTop + el.clientTop);
}
el = el.offsetParent;
}
return {
x: xPos,
y: yPos
};
}
//Not currently used
function getOffset( el ) {
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
}
//Determines whether or not an element is visible
function domIsVisible(element) {
var rect = element.getBoundingClientRect();
var html = document.documentElement;
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || html.clientHeight) &&
rect.right <= (window.innerWidth || html.clientWidth)
);
}
window.onload = function() {
mapDom.style.display = 'inline-block';
searchForm.style.display = 'inline-block';
navContainer.style.display = 'block';
placeholderDom.style.display = 'none';
//Set the form and canvas X and Y values after loading the window
var formPosition = getPosition(searchForm);
formX = formPosition.x;
formY = formPosition.y;
var appPosition = getPosition(appContainer);
appX = appPosition.x;
appY = appPosition.y ;
centerApp();
// var formAnchorTags = Array.from(searchForm.querySelectorAll("a"));
// formAnchorTags.forEach(function(anchor) {
// if (!anchor.children[0].classList.contains('hidden-nid')) {
// console.dir(anchor.parentElement);
// console.dir(anchor.parentNode);
// }
// if (anchor.parentElement.nodeName == 'SUMMARY') {
// anchor.parentElement.tabIndex = -1;
// }
//
// });
//Reload the stylesheet for the infobox, as it is an iframe and its styling needs to be enforced after the window has loaded
var frame = viewer.infoBox.frame;
var cssLink = frame.contentDocument.createElement('link');
cssLink.href = Cesium.buildModuleUrl('../../../../css/qd30map.css');
cssLink.rel = 'stylesheet';
cssLink.type = 'text/css';
frame.contentDocument.head.appendChild(cssLink);
//Remove tabindex from elements within the header
var header = document.getElementById('header');
var headChilds = header.getElementsByTagName('*');
for (var c = 0; c < headChilds.length; c++) {
headChilds[c].tabIndex = -1;
}
var qdTitle = document.getElementById('block-quartierdix30-page-title');
var titleChilds = qdTitle.getElementsByTagName('*');
for (var d = 0; d < titleChilds.length; d++) {
titleChilds[d].tabIndex = -1;
}
//Set a variable to represent the primary datasource, which contains all of the geojson features
var data = viewer.dataSources.get(0);
var subBtn = document.getElementById('edit-mapsubmit');
//Set a listener for the form's submit button, so submissions can be overridden
subBtn.onclick = function(v) {
v.preventDefault();
handleSubmit();
};
//Set the same type of listener for the autocomplete field, whose submit logic must also be overridden
var autoComplete = document.getElementById('ui-id-1');
autoComplete.onclick = function() {
handleSubmit();
};
autoComplete.onkeydown = function(param) {
if (param.keyCode == 13) {
handleSubmit();
}
};
//Override selections based on clicking objects within the WebGL Canvas
clickHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
clickHandler.setInputAction(function(movement) {
var pickedObject = scene.pick(movement.position);
if (Cesium.defined(pickedObject)) {
for (var i = 0; i < data.entities.values.length; i++) {
var entity = data.entities.values[i];
if (entity.properties.nid != undefined) {
if (entity.properties.nid == pickedObject.id.properties.nid) {
exploreStore(entity, data);
}
}
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
//Iterate over all the anchor elements in the form and setup appropriate click/keyboard listeners to select stores
var anchorElements = searchForm.querySelectorAll('.form-item');
for (var i = 0; i < anchorElements.length; i++) {
anchorElements[i].tabIndex = 0;
}
for (var k=0; k < anchorElements.length; k++) {
anchorElements[k].addEventListener('click', function(param) {
for (var g=0; g < anchorElements.length; g++) {
//Adding the selected-store class to the elements will cause them to appear highlighted within the form
//We must first remove this from the previous selected element
anchorElements[g].classList.remove('selected-store');
}
var choice = param.target.innerHTML.trim();
for (var i = 0; i < data.entities.values.length; i++) {
var entity = data.entities.values[i];
if (entity.properties.nid != undefined) {
var formNid = choice.substring(choice.indexOf('d">') + 3, choice.indexOf(' 1) {
selectStore(entity, data);
} else {
gotoQD30();
}
if (!domIsVisible(appContainer)) {
centerApp();
}
this.classList.add('selected-store');
}
}
}
});
//This logic is the same above, but for selection via keyboard
anchorElements[k].addEventListener('keyup', function(param) {
for (var g=0; g < anchorElements.length; g++) {
anchorElements[g].classList.remove('selected-store');
}
if (param.keyCode == 13) {
var choice = param.target.innerHTML.trim();
for (var i = 0; i < data.entities.values.length; i++) {
var entity = data.entities.values[i];
if (entity.properties.nid != undefined) {
var formNid = choice.substring(choice.indexOf('d">') + 3, choice.indexOf(' 1) {
selectStore(entity, data);
} else {
gotoQD30();
}
if (!domIsVisible(appContainer)) {
centerApp();
}
this.classList.add('selected-store');
}
}
}
}
});
}
//This listener is used to highlight multiple boutiques by store type
var types = Array.from(document.querySelectorAll('summary > a.details-title'));
types.forEach(function(type) {
type.addEventListener('click', function(param) {
var text = param.srcElement.lastChild.textContent.toLowerCase();
highlightByType(text, data);
});
});
//this function is used to override form submission
function handleSubmit() {
var search = document.getElementById('edit-search');
var formNid = search.value.substring(search.value.indexOf('(') + 1, search.value.indexOf(')'));
var length = data.entities.values.length;
for (var i = 0; i < length; i++) {
var entity = data.entities.values[i];
if (entity.properties.nid != undefined) {
console.log(entity.properties.nid);
console.log(formNid);
if (formNid == entity.properties.nid) {
if (entity.polygon.hierarchy._value.positions.length > 1) {
selectStore(entity, data);
} else {
gotoQD30();
}
}
}
}
}
//The primary function for selecting a boutique: finds the average point between the feature's coordinates, flies the camera
//at an appropriate angle, colours the building and sets the viewer.selectedEntity property to it, triggering the display
//of the infobox popup
function selectStore(entity, data) {
var pCoords = entity.polygon.hierarchy.getValue(viewer.clock.currentTime);
var avgPoint = Math.round(pCoords.positions.length / 2);
var topPoint = 0;
for (var xy = 0; xy < pCoords.positions.length - 1; xy++) {
if (pCoords.positions[xy+1].x > pCoords.positions[xy].x) {
topPoint = xy;
}
}
var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pCoords.positions[avgPoint]);
pos.latitude = Cesium.Math.toDegrees(pos.latitude);
pos.longitude = Cesium.Math.toDegrees(pos.longitude);
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, 500),
orientation : {
heading : 5.51747,
pitch : -1.419427,
roll : 6.2675
},
duration: 1
});
colourReset();
//Previously, we wrapped highlighted buildings with a glowing polyline to improve contrast
// viewer.entities.add({
// name : 'selectionOutline',
// polyline : {
// positions : pCoords.positions,
// width : 4,
// material : new Cesium.PolylineGlowMaterialProperty({
// glowPower : 0.5,
// color : Cesium.Color.fromCssColorString('#008B8B'),
// extrudedHeight : entity.polygon.extrudedHeight + 20
// })
// }
// });
entity.polygon.material = Cesium.Color.fromCssColorString('#000000');
entity.polygon.material.outlineColor = Cesium.Color.fromCssColorString('#008B8B');
entity.polygon.material.outlineWidth = 5;
viewer.selectedEntity = entity;
}
//this function is similar to above, but used for clicking on the canvas. This preserves the zoom height which the user
//is previously using, such as to avoid the confusion of changing height each time a building is clicked on
function exploreStore(entity, data) {
var pCoords = entity.polygon.hierarchy.getValue(viewer.clock.currentTime);
var cartographic = new Cesium.Cartographic();
var camHeight = ellipsoid.cartesianToCartographic(camera.position, cartographic).height;
// var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pCoords.positions[0]);
// pos.latitude = Cesium.Math.toDegrees(pos.latitude);
// pos.longitude = Cesium.Math.toDegrees(pos.longitude);
var avgPoint = Math.round(pCoords.positions.length / 2);
var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pCoords.positions[avgPoint]);
pos.latitude = Cesium.Math.toDegrees(pos.latitude);
pos.longitude = Cesium.Math.toDegrees(pos.longitude);
// var topPoint = 0;
//
// for (var xy = 0; xy < pCoords.positions.length - 1; xy++) {
// if (pCoords.positions[xy+1].x > pCoords.positions[xy].x) {
// topPoint = xy;
// }
// }
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, camHeight),
orientation : {
heading : camera.heading
},
duration: 1
});
colourReset();
// pCoords.positions.forEach(function(position) {
// position.x += 0.33;
// position.y += 0.33;
// });
// var selectionOutline = viewer.entities.add({
// name : 'selectionOutline',
// polyline : {
// positions : pCoords.positions,
// width : 4,
// material : new Cesium.PolylineGlowMaterialProperty({
// glowPower : 0.5,
// color : Cesium.Color.fromCssColorString('#008B8B'),
// extrudedHeight : entity.polygon.extrudedHeight + 20
// })
// }
// });
entity.polygon.material = Cesium.Color.fromCssColorString('#000000');
entity.polygon.material.outlineColor = Cesium.Color.fromCssColorString('#008B8B');
entity.polygon.material.outlineWidth = 5;
viewer.selectedEntity = entity;
}
//This is the function used to highlight multiple boutiques based on their store type (restaurant, entertainment, "accepts gift cards", etc)
function highlightByType(type, data) {
colourReset();
// for (z = 0; z < viewer.entities.values.length; z++) {
// if (viewer.entities.values[z].name.substring(0, 16) == 'selectionOutline') {
// viewer.entities.remove(viewer.entities.values[z]);
// }
// }
for (var j = 0; j < data.entities.values.length; j++) {
var entity = data.entities.values[j];
if (entity.properties.hasOwnProperty('storetypes')) {
if (entity.properties.storetypes.toLowerCase().match(type)) {
var pCoords = entity.polygon.hierarchy.getValue(viewer.clock.currentTime);
matched = true;
entity.polygon.material = Cesium.Color.fromCssColorString('#000000');
entity.polygon.material.outlineColor = Cesium.Color.fromCssColorString('#008B8B');
entity.polygon.material.outlineWidth = 5;
// viewer.entities.add({
// name : 'selectionOutline',
// polyline : {
// positions : pCoords.positions,
// width : 4,
// material : new Cesium.PolylineGlowMaterialProperty({
// glowPower : 0.5,
// color : Cesium.Color.fromCssColorString('#008B8B'),
// extrudedHeight : entity.polygon.extrudedHeight + 20
// })
// }
// });
}
}
}
}
//Logic to close all non-target details elements
//This was important to keep the use of the form from becoming confusing, as there are many collapsible details elements
var details = Array.from(document.querySelectorAll("details"));
details.forEach(function(detail) {
if (detail.constructor.name === 'HTMLDetailsElement') {
detail.addEventListener('click', function() {
for (f = 0; f < details.length; f++) {
details[f].classList.remove('selected-detail');
}
this.classList.add('selected-detail');
subDet = this.querySelectorAll('details');
for (g = 0; g < subDet.length; g++) {subDet[g].classList.add('selected-detail');}
closeOthers(this);
});
}
});
//helper function used in the above listeners
function closeOthers(elem) {
for (var i = 0; i < details.length; i++) {
if (!details[i].contains(elem)) {
var selectedFound = false;
var elems = details[i].querySelectorAll('*');
for (z = 0; z < elems.length; z++) {
if (elems[z].classList.contains('selected-detail') || elems[z].classList.contains('selected-store')) {
selectedFound = true;
}
}
if (selectedFound == false) {
details[i].removeAttribute('open');
}
} else {
details[i].setAttribute('open', 'open');
}
}
}
//This helper function is no longer used, but was helpful during development to find the visible coordinates on the canvas
function getExtentView() {
var cl2 = new Cesium.Cartesian2(0, 0);
var leftTop = viewer.scene.camera.pickEllipsoid(cl2, ellipsoid);
cr2 = new Cesium.Cartesian2(viewer.scene.canvas.width, viewer.scene.canvas.height);
var rightDown = viewer.scene.camera.pickEllipsoid(cr2, ellipsoid);
if (leftTop != null && rightDown != null) {
leftTop = ellipsoid.cartesianToCartographic(leftTop);
rightDown = ellipsoid.cartesianToCartographic(rightDown);
return new Cesium.Rectangle(leftTop.longitude, rightDown.latitude, rightDown.longitude, leftTop.latitude);
} else {
console.log("Sky is visible");
return null;
}
}
};
//It is important to update the values of the form and app positions upon window resize
window.onresize = function() {
var formPosition = getPosition(searchForm);
formX = formPosition.x;
formY = formPosition.y;
var appPosition = getPosition(appContainer);
appX = appPosition.x;
appY = appPosition.y;
};
//This jQuery code was being used for the map legend
(function($, Drupal) {
Drupal.behaviors.qd30map = {
attach: function(context, settings) {
$('.closemenu').on('mouseenter', function() {
$(this).find('span').show();
}).on('mouseleave', function() {
$(this).find('span').hide();
}).on('click tap', function() {
$(this).parent().parent().children('div').fadeOut(150);
$(this).parent().parent().delay(50).animate({width: 'toggle'}, 300);
if ($(this).parent().hasClass('closeinfo')) {
infoOpen = false;
$('.selected').removeClass('selected');
}
});
$('#open-legend').on('click tap', function() {
$('.map-legend-info').animate({width:'toggle'}, 300);
$('.map-legend-info > div').delay(150).fadeIn(150);
});
}}})(jQuery, Drupal);
//Helper function not currently being used
function fade(element) {
var op = 1; // initial opacity
var timer = setInterval(function() {
if (op <= 0.1) {
clearInterval(timer);
element.style.display = 'none';
}
element.style.opacity = op;
element.style.filter = 'alpha(opacity=' + op * 100 + ")";
op -= op * 0.1;
}, 150);
}