/* * The following code will be executed when all elements * in the page have been rendered * TODO: Stop using the ready method as this script should be sourced * from the bottom of the index.html file instead. */ $(document).ready(function() { // $("body").on("mousemove touchmove", function (e) { // console.log("hey"); // }); // If I can figure out the Geoserver ajax issues I may not need these because // Geoserver can do all the projection, server side. Proj4js.defs["EPSG:3005"] = "+proj=aea +lat_1=50 +lat_2=58.5 +lat_0=45 +lon_0=-126 +x_0=1000000 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs"; Proj4js.defs["EPSG:3857"] = "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"; Proj4js.defs["EPSG:4326"] = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs "; // Sometimes tiles don't load on the first attempt. Especially the OSM ones. // So make sure there are up to 3 attempts to download the image. OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3; // error tiles are transparent not pink. OpenLayers.Util.onImageLoadErrorColor = "transparent"; // This is a little jquery plugin to fudge placeholders $("input").placeholder(); // Custom controls mM.mapNav = new OpenLayers.Control.Navigation(); // regular pan and zoom mM.zoom = new OpenLayers.Control.Zoom(); // this is the little zoom control (+/-). // Load our map object into the global object. This allows us to // find the map at any level of enclosure (functions etc...) mM.map = new OpenLayers.Map('map-main',{ projection: mM.config.mapProjection, controls: [mM.mapNav,mM.zoom], // Our set of controls numZoomLevels: mM.config.zooms // Specify number of zoom levels. 18 is pretty detailed. }); // Listen for when the map has been zoomed and fire the onZoom function // This will control zoom sensitive behaviour, like if a layer is visible or not. mM.map.events.register("zoomend",mM.map,onZoom); // Set the default context tab loadContext("about"); /* * ### onZoom * Runs after scale changes * Don't want to run logic every single zoom if the user is really moving * around a lot. Set a timer so functions get fired after a certain amount * of inactivity. */ function onZoom () { if (mM.zoomTimer) {clearTimeout(mM.zoomTimer);} // Cancel previous timer if exists mM.zoomTimer = setTimeout(function () { mM.layerActivity(mM.map.getZoom()); // Check which layers should be on or off updateScale(); // Updated the scale text on the print layout },100); // 1/10 of a second } /* * ### updateScale * There is a little scale text markup on the print template. * It is only visible when the map is printed. Update that here. */ function updateScale () { var scale = mM.addCommas(Math.floor(mM.map.getScale())); $("#print-scale").text("1:"+scale); } // First find the position of the button var p = $("#desktop-lyr-btn").offset().left - $("#contents").offset().left - 22; // Inject the base layer drawer //$("
"). $("
"). css({"left": p}). prependTo("#map-main"); // Register the geocoder mM.geocoder(".geocoder"); // Load all the layers var layersToAdd = []; $.each(mM.config.layers,function (i,l) { // Skip adding the layer if it's a privileged layer // and the user does not have permission. if (l.privileged && mM.perm.is_extended_reader === false) { return; } var layerSwitch = (l.base) ? "icon-circle-blank" : "icon-check-empty"; var layerClass = (l.base) ? "base-layer" : "layer"; var layers = $("#layer-drawer"); /* * Add the layer switching controller if the visible flag is set to true * The layer object from the config file is bound to the layer legend div * This way all layer info can be accessed when the user selects it. */ if (l.layerType == "group") { // Add groups var html = "
"+ "
"+ ""+ ""+l.groupName+ "
"+ "
"; $(html).data(l).appendTo(layers); } if (l.visible && l.layerType != "group") { // Add visible layers, not groups var layer = $("
"+ l.layerName+"
").data(l); var layerMeta; if (l.meta) { // If there is meta data layer.append(""); layerMeta = $("
"); layerMeta.append("
"+l.meta.desc+"
"); } if (layerMeta && l.meta.symbols) { // If there is a legend... with entries var table = $("
"); $.each(l.meta.symbols, function (i,row) { var tr = $(""); tr.append(""); tr.append(""+row.desc+""); table.append(tr); }); layerMeta.append(table); } layer.append(layerMeta); if (l.group) { // If belonging to a group var dest = $("#"+l.group+"-grp ."+l.group+"-box"); layer.addClass("secondary"). // Give it secondary styling appendTo(dest); // Stuff it in there } else { // Otherwise it's just a regular layer layer.appendTo(layers); // Add to layer drawer as is. } } // Add the layer layersToAdd.push(l); }); // Add the layers in reverse order $.each(layersToAdd.reverse(), function(i,l) { if (l.layerType == "bing") {mM.addBing(l);} if (l.layerType == "google") {mM.addGoogle(l);} if (l.layerType == "tms") {mM.addTMS(l);} if (l.layerType == "wms") {mM.addWMS(l);} if (l.layerType == "wfsCluster") {mM.addWFSCluster(l);} }); /* * I'm thinking we'll need to abandon the acetate layer in the motMap file. * Better done here. */ mM.utvsIdent = new OpenLayers.Layer.Vector( "utvsIdent", { // The style is defined in the config file styleMap: new OpenLayers.StyleMap(mM.config.identStyle) } ); // Add the layer to the map mM.map.addLayer(mM.utvsIdent); // Find the drawer height then close it. mM.layerDrawer = $("#layer-drawer"); mM.layerDrawerHeight = mM.layerDrawer.height(); mM.layerDrawer.height(0).css("z-index",1004); // Set the default base layer with workaround // mM.map.setBaseLayer(mM.gSat2); // setTimeout(function () { // mM.map.setBaseLayer(mM[mM.config.baseLayer]); // },1000); // Set the default base layer with workaround mM.map.setBaseLayer(mM.bRoad); /* * Add base layer attribution * The addAttribution function requires the layer object from the * config file. This is grabbed from the legend entry */ mM.addAttribution($("#"+mM.config.baseLayer+"-lyr").data()); // Set the appropriate layer entry to be active in the layer list $("#"+mM.config.baseLayer+"-lyr"). find("a"). addClass("active"). find("i"). attr("class","icon-circle icon-large"); // For some reason it worked better to set the opening position here // as opposed to in the map definition. mM.map.setCenter( new OpenLayers.LonLat(mM.config.startX,mM.config.startY). transform('EPSG:4326',mM.config.mapProjection),mM.config.startZ); //sizeIFrame("search-iframe"); // Calculate the size for the search iFrame. resize(); // Calculate the window size updateScale(); // Set the scale in the print layout // Listen for window resizing and make sure the map resizes with it. //$(window).bind("resize orientationchange",function () { $(window).resize(function () { clearTimeout(mM.tResize); mM.tResize = setTimeout(resize,100); }); // Custom behaviour for cluster layer mM.selectCluster = new OpenLayers.Control.SelectFeature( [ mM.cluster, mM.clusterTMP, mM.clusterTSG ],{ hover: false, multipleKey: 'ctrlKey' } ); mM.map.addControl(mM.selectCluster); mM.selectCluster.activate(); /* * ## Event listening logic */ /* * Listen for the layer button being pressed. * Specific to desktop mode */ $("#desktop-lyr-btn").on("touchstart click", toggleLayerDrawer); /* * Listen for layer button being pressed in mobile mode * Determine the difference between a click and a drag and * behave accordingly */ $("#mobile-layer-btn").on("touchstart mousedown", function (e) { e.preventDefault(); $(this).data({touched: new Date().getTime()}); // mM.layerDrawerTimer = setTimeout(dragLayerDrawer,150); }).on("touchend mouseup",function (e) { // clearTimeout(mM.layerDrawerTimer); // Stop drag behaviour from happening. var touchTime = new Date().getTime() - $(this).data("touched"); if (touchTime > 150) return; // Greater then 150 ms is not a click. if ($(this).data("state") == "open") { closeLayerDrawer(this); } else { openLayerDrawer(this); } }); /* * Register event for the clear geocoder button */ $("#desktop-clear-geocoder-btn").on("touchstart mousedown",function () { mM.destroyGeoCoderMarkers(); $("#start-here").val(''); }); /* * Events for opening layer meta */ $("#layer-drawer .layer-meta-icon").on("touchstart click", function () { var layerMeta = $(this).parent().find(".layer-meta"); var metaHeight = $(this).parent(). // resize layer drawer find(".layer-meta")[0].scrollHeight; var layerDrawerHeight = mM.layerDrawer.height(); var newHeight; if ($(this).hasClass("icon-info-sign")) { // If closed $(this).removeClass("icon-info-sign").addClass("icon-upload"); layerMeta.animate({height: metaHeight}); // Open the meta drawer newHeight = layerDrawerHeight + metaHeight; // Calculate new layer drawer height mM.layerDrawer.animate({height:newHeight}); // Resize layer drawer $("#mobile-layer-btn").animate({top:newHeight}); // reposition mobile button } else { // Otherwise it's open. $(this).removeClass("icon-upload").addClass("icon-info-sign"); layerMeta.animate({height: 0}); // Close the meta drawer newHeight = layerDrawerHeight - metaHeight; // Calculate new layer drawer height mM.layerDrawer.animate({height:newHeight}); // Resize layer drawer $("#mobile-layer-btn").animate({top:newHeight}); // reposition mobile button } }); /* * The custom events for clicking on a layer group * When the group is opened... Grab the custom bootstrap event * and orient the little arrow chevron thing correctly */ $(".layer-box").on("show.bs.collapse",function() { var groupHeight = $(this)[0].scrollHeight; var layerDrawerHeight = mM.layerDrawer.height(); var newHeight = layerDrawerHeight + groupHeight; mM.layerDrawer.animate({height:newHeight}); $("#mobile-layer-btn").animate({top:newHeight}); // reposition mobile button $(this).parent().find(".group-head i"). removeClass("icon-chevron-left"). addClass("icon-chevron-down"); }); $(".layer-box").on("hide.bs.collapse",function() { var groupHeight = $(this)[0].scrollHeight; var layerDrawerHeight = mM.layerDrawer.height(); // Not totally sure why I need the additional '1' here... Just do. var newHeight = layerDrawerHeight - groupHeight + 1; mM.layerDrawer.animate({height:newHeight}); $("#mobile-layer-btn").animate({top:newHeight}); // reposition mobile button $(this).parent().find(".group-head i"). removeClass("icon-chevron-down"). addClass("icon-chevron-left"); }); /* * Listen for context change */ $("#context-btns .btn").on("touchstart mousedown", function (e) { e.preventDefault(); // Stop default behaviour var d = this; // Grab the object if ($(d).hasClass("active")) {return;} // Might as well leave if already active // Toggle the button to be on $("#context-btns .btn").removeClass("active"); $(d).addClass("active"); var win = $(d).attr("data-btn"); // Grab the window type from the data attribute //$(".context-tab:visible").fadeOut(bringBack); // Fade the visible content window // Fade out the visible content pain and trigger the content loading function $(".context-tab:visible").fadeOut( function(){loadContext(win);} ); }); // Listen for context in mobile mode $("#mobile-btns a").on("touchstart mousedown", function (e) { e.preventDefault(); // Stop default behaviour var d = this; // grab the object if ($(d).hasClass("active")) {return;} // Might as well leave if already active // Change the active button $("#mobile-btns a").removeClass("active"); // Remove all others $(d).addClass("active"); // set to active var win = $(d).attr("data-btn"); // Grab the window type from the data attribute if (win == "map") { // Activate scroll activity for report and about windows. $("#contents").css({overflow: "hidden"}); } else { $("#contents").css({overflow: "auto"}); } // fade out the main container $("#contents").children().filter(":visible").fadeOut(bringBack); /* * #### bringBack * Transition to the correct window on mobile. * In identifying points on the map.. The content is * gets placed in the desktop results pane by default. * So we need to check if there is new content in the desktop * pane. If there is some there was a new identifcation to blow * away the old content and bring over the new. */ function bringBack() { var dP = $("#"+win+"-context"); // The desktop landing spot newIdent = (dP.children().length > 0) ? true : false; // flag for new content if (win == "process" && newIdent) { // If a result request and new content $("#"+win+"-main").html(""); // Clear old content } $("#"+win+"-context"). // Suck whatever content from desktop over. children(). appendTo("#"+win+"-main"); $("#"+win+"-main").fadeIn(); // Fade back in. } }); // Listen for layer changes $("#layer-drawer span").on("touchstart click", function () { var d = $(this).parent().data(); if (d.base) { // If a base layer click/tap $("#layer-drawer .base-layer"). // select all layers removeClass("active"). // deactivate all find("i"). attr("class","icon-circle-blank icon-large"); // empty circles $(this).addClass("active"). // Add active layer symbol find("i"). attr("class","icon-circle icon-large"); mM.map.setBaseLayer(mM[d.objName]); // Do the actual change mM.addAttribution(d); // Add base layer attribution } else { // if it's a regular layer if ($("#"+d.objName+"-lyr a").hasClass("active")) { // if it is already active mM.layerOff(d.objName); // Turn it off } else { mM.layerOn(d.objName); // Turn it on. } } }); /* * Listen for click on map * Because this is a map and you can pan around.. * We have to make sure we are clicking and not dragging. * first listen to mousedown */ $(".olMapViewport").children().not(".olControlZoom"). on("mousedown touchstart",function (e) { e.preventDefault(); // Stop default behaviour // Store the page coordinates in the dom element $(this).data('p0',{x:e.pageX,y:e.pageY}); // Now listen for mouse up }).on("mouseup touchstop",function (e) { p0 = $(this).data('p0'); // Retrieve original position if (!p0) return; // Get out if mousdown wasn't in map. p1 = {x: e.pageX, y: e.pageY}; // The current position // Our little calculation to determine the size of movement d = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2)); /* * Wouldn't want it to be too strict. * So if there was a number less then four.. * Fire the identification function. */ var offset = $(this).parent().offset(); if (d < 4) { // Wait a tick for the OpenLayers selected features // objects to get loaded. setTimeout(function () {identTsg(e,offset);},100); } }); }); /* * ### processIt (cluster1,cluster2,cluster3,identFeature) * Take features that have most likely be obtained through an * identification. Do whatever is necessary for discovering * further information ajax... etc. then call the *process.html* * template from *html/process.html* injection in the new * variables/attributes. * * @param cluster1 {object} OpenLayers cluster feature object, layer 1 * @param cluster2 {object} OpenLayers cluster feature object, layer 2 * @param cluster3 {object} OpenLayers cluster feature object, layer 3 * @param identFeature {object} OpenLayers feature object from the segment, layer 3 */ function processIt (cluster1,cluster2,cluster3,identFeature) { var joinCluster = function (clust) { // joining all the features from each cluster var data = []; $.each(clust, function (i,d) { data = data.concat(d.cluster); }); return data; }; var data1 = joinCluster(cluster1); var data2 = joinCluster(cluster2); var data3 = joinCluster(cluster3); // Perform any required sorting data1 = mM.sortFeatures(data1, 'SITE_CODE', false); // Sort Traffic Measurement Sites by SITE_CODE ascending data2 = mM.sortFeatures(data2, 'LAST_YEAR', true); // Sort Traffic Measurement Points by LAST_YEAR descending data3 = mM.sortFeatures(data3, 'START_DATE', true, true); // Sort Traffic Surveys by START_DATE descending (Need to CAST string date to real date) // $("#process-main").html("Hey there"); // Here is our container var container = $("#process-context"); // Here is our container $.get("html/process.html",function (d) { // Grab the template and stuff our data into it. $("#about-context").fadeOut(); $("#process-context").fadeOut( function(){ var template = Handlebars.compile(d); // Compile the template container.html(template({ // Insert compiled html cluster1: data1, cluster2: data2, cluster3: data3, feature1: identFeature, trafficDataReportUrl: mM.env.trafficDataReportUrl, // Required for process.html template url variable substitution tradasReportUrl: mM.env.tradasReportUrl, // Required for process.html template url variable substitution tmpReportUrl: mM.env.tmpReportUrl, // Required for process.html template url variable substitution tsgReportUrl: mM.env.tsgReportUrl, // Required for process.html template url variable substitution utvsReportUrl: mM.env.utvsReportUrl // Required for process.html template url variable substitution })); attachZooms(); // Activate the zoom to buttons var w = $("#context").outerWidth(true) - 30; // Adjust width var h = 800; // Adjust height container.find("iframe").attr({"height":h, "width":w}); loadContext("process"); // Load process window $("#context-btns button").removeClass("active"); // Deactivate buttons $("#desktop-process-btn").addClass("active"); // Activate process button // Transition in context buttons if this is the first identification if (!$("#context-btns").is(":visible")) { setTimeout(function () {$("#context-btns").slideDown();},1000); } /* * If in mobile mode animate the results button to * unobtrusively appear and display that there are results to see. */ var resBtn = $("#mobile-results-div"); resBtn.fadeIn(function () { pulseColor($("#mobile-process-btn"),"#ff0000"); }); } ); }); } /* * ### pulseColor * Pulse an elements colour three times from it's original * colour to the one provided. * @param element {object} jQuery selected element object * @param color {string} Colour string like #ff0000 */ function pulseColor (element,color) { var originalColor = element.css("color"); element.animate({color:color},400,andBack); function andBack () { element.animate({color:"#333"},400,andForth); } function andForth () { element.animate({color:color},400,andBack2); } function andBack2 () { element.animate({color:"#333"},400,andForth2); } function andForth2 () { element.animate({color:color},400,andBackAgain); } function andBackAgain () { element.animate({color:originalColor},400,scrub); } function scrub () { element.removeAttr("style"); } } /* * ### attachZooms () * When the process template is populated each item from * the cluster layers contains a zoom-to button. The template * also binds the coordinates of the feature to the button * in the form of a bounding box. * This function uses jquery to bind a click/touch event to * a OpenLayers zooming event. Utilizing the bound coordinates. */ function attachZooms () { $("#process-content .zoom"). on("mousedown touchstart", function () { var left = $(this).attr("data-left"); var bottom = $(this).attr("data-bottom"); var right = $(this).attr("data-right"); var top = $(this).attr("data-top"); mM.map.zoomToExtent([left,bottom,right,top],true); // Zoom to point $("#mobile-map-btn").trigger("mousedown"); // better move to the map }); $("#process-content a"). // Add nice tool tips as well. tooltip({delay: { show: 1000, hide: 100 }}); } /* * ### loadContext(win) { * Load the context pane from the templates in the *html* directory. * The *win* string should match the name of the html template file. * For example, the win variable should be *process* for loading * a file by the name of *html/process.html*. * * @param win {string} The name of the context window * to which content is being rendered into. * Typically; search, process, & about. */ function loadContext(win) { var container = $("#"+win+"-context"); // Grab the destination container if (container.children().length > 1) { // If there is already content in the window... container.fadeIn(); // Just fade it in and exit. return; } $.get("html/"+win+".html",function (d) { var template = Handlebars.compile(d); container.html(template({ config: mM.config, env: mM.env })); mM.geocoder("#"+win+"-context .geocoder"); bringBack(); // fade back in. }); /* * #### bringBack () * Just in case the window has changed from mobile to desktop. * If there isn't any content in this window copy from the context window. * If not... Just provide a nice fade in effect */ function bringBack() { if ($("#"+win+"-context").children().length < 1) { // TODO: This may not be necessary $("#"+win+"-main").children().appendTo("#"+win+"-context"); $("#"+win+"-context").fadeIn(); } else { // Otherwise just fade in. $("#"+win+"-context").fadeIn(); } } } /* * ###identTsg (e,win) * Identify all layers that are currently turned on. * * @param e {object} The jQuery mouse event taken from the click. * @param offset {object} A position object which represents * represents the position of the clicked div on the screen. * looks like this {top: 60, left: 409.796875} * Typical of the jQuery.offset() method. */ function identTsg (e,offset) { mM.utvsIdent.destroyFeatures(); // Clear acetate layer /* * The cluster layers have all their data in memory. * ie. No ajax required. * So harvest the features within the selected clusters */ var cluster1 = mM.cluster.selectedFeatures; var cluster2 = mM.clusterTMP.selectedFeatures; var cluster3 = mM.clusterTSG.selectedFeatures; /* * The UTVS layer layer requires a call to Geoserver. Which is * more time intensive. So look to see if it is turned on. */ var UTVSon = false; // The flag for tracking if the utvs layer is on. var utvsLayer; // The utvs layer object. // Cycle through the layer array $.each(mM.config.layers,function(i,d) { if (d.objName === "utvs" && d.on) { UTVSon = true; utvsLayer = d; } }); /* * If the UTVS layer is on poll the server. * Otherwise we can just process the cluster data * that is on the acetate layer */ if (UTVSon) { /* * Interim solution: The UTVSIndentify.do request (within TIG) * requires an authenticated session before it would return results. * This simple jQuery request is good enough to satisfy that requirement. * It's placed here b/c the auth should occur before the geoserver poll * function. On a successful GET, pollGeoServer is run. */ $.get(mM.env.tigUrl, function() { pollGeoServer (utvsLayer); }); } else { processIt(cluster1,cluster2,cluster3); } /* * ####pollGeoServer (d) * Send an identification request to Geoserver * * @private * @param d {object} The mapConfig layer object. */ function pollGeoServer (d) { /* * Send jquery mouse event to identBox method. * We should get back a json encoded box. */ var identBox = mM.identBoxJSON(e,offset); // Build the CQL_FILTER string var arrCoords = identBox.split(","); var identPoly = arrCoords[0]+' '+arrCoords[1]+','+arrCoords[0]+' '+arrCoords[3]+','+arrCoords[2]+' '+arrCoords[3]+','+arrCoords[2]+' '+arrCoords[1]+','+arrCoords[0]+' '+arrCoords[1]; var cql_filter = 'INTERSECTS(SHAPE,POLYGON(('+identPoly+')))'; $.getJSON(d.url,{ typeName: d.layerSchema+":"+d.layerWMS, outputFormat: "json", service: "wfs", version: "2.0.0", request: "GetFeature", srsName: "EPSG:3857", maxFeatures: 1, // Only one feature please cql_filter: cql_filter },function (identFeatures) { var jsonReader = new OpenLayers.Format.GeoJSON(); // Openlayers json reader var json = jsonReader.read(identFeatures); // read var identFeature = json[0]; // First feature is the only feature if (identFeature) mM.utvsIdent.addFeatures(identFeature); // Add geometry to vector layer processIt(cluster1,cluster2,cluster3,identFeature); // Load process tab }); } } /* * ### sizeIFrame (id) * Expand the specified iFrame to it's content. This will allow the parent * div to handle the overflow paramenters (auto, scroll etc...). * @param id {string} The html id for the iFrame element to resize */ function sizeIFrame (id) { $("#"+id).load(function () { var size = mM.contextSize(); $(this).attr({"height":size.h,"width":size.w}); }); } /* * ### resize () * Sanity check window sizes when the window is resized */ function resize () { var h = $(window).height() - //Grab the window size $(".navbar-fixed-top").height() - // Subtract navbar height $(".navbar-fixed-bottom").height(); // Subtract bottom bar height var w = $(window).width(); // Make sure both map and context window heights are correct // This was causing problems with the new Google V3 API // $("#context,#contents,#about-main,#layer-main,#search-main").height(h); $("#context").height(h); // If in non-mobile mode make sure the map window is visible if (w > 767 && $("#map-main").not(":visible")) { $("#map-main").show(); } // Make sure the active button matches the content in the context window. // This is imporant if the browser was just resized from mobile to desktop. var win = $("#context-btns button.active").attr("data-btn"); if (w > 767 && $("#context"+win).children().length < 1) { $("#"+win+"-main").children().appendTo("#"+win+"-context"); } // And the same goes for resizing to mobile from desktop if (w <= 767 && $("#map-main").is(":visible")) { $("#mobile-btns a").removeClass("active"); $("#mobile-btns a[data-btn='map']").addClass("active"); } // Should probably realign the layer drawer as well. var p = $("#desktop-lyr-btn").offset().left - $("#contents").offset().left - 22; // Check that it's not half off the screen... For mid sized screens. var combinedWidth = p + $("#layer-drawer").outerWidth(); var availableWidth = $("#contents").outerWidth(); if (combinedWidth > availableWidth) { // If off screen p = p - (combinedWidth - availableWidth); // Correct } if (w <= 767) { // If in mobile mode the calculation is different. p = availableWidth - $("#layer-drawer").outerWidth(); } $("#layer-drawer").css("left",p); /* * If switching between mobile and desktop mode we need * to do a little sanity check here and just reset the * layer drawer by closing it. */ if (w <= 767 && $("#desktop-lyr-btn").data("state") == "open") { closeLayerDrawer($("#desktop-lyr-btn")); } if (w > 767 && $("#mobile-layer-btn").data("state") == "open") { closeLayerDrawer($("#mobile-layer-btn")); } } /* * ### toggleLayerDrawer * Toggle the layer drawer. This gets used by the mobile * and desktop layer buttons. * @para e {object} jQuery event object that is usually * created from a click, or tap event. */ function toggleLayerDrawer (e) { var button = e.currentTarget; if ($(button).data("state") == "open") { closeLayerDrawer(button); } else { openLayerDrawer(button); } } /* * ### dragLayerDrawer * Drag the layer drawer open and closed. Only accessible from * the mobile button. * @param e {object} jQuery event object that is usually * created from a click, or tap event. */ function dragLayerDrawer () { // mM.map.events.register("mousemove",mM.map,function(e) { // console.log("blah"); // },true).attachToElement($("#conents")[0]); // $("#contents").on("mousemove touchmove", function (e) { // e.preventDefault(); // console.log(e); // }); } /* * ### openLayerDrawer * Open the layer drawer * @param button {object} The button element */ function openLayerDrawer (button) { $(button).data({state:"open"}).addClass("drawer-open"); mM.layerDrawer.animate({height:mM.layerDrawerHeight}); $("#mobile-layer-btn").animate({"top":mM.layerDrawerHeight}); } /* * ### closeLayerDrawer * Open the layer drawer * @param button {object} The button element */ function closeLayerDrawer (button) { $(button).data({state:"closed"}).removeClass("drawer-open"); mM.layerDrawer.animate({height:0}); $("#mobile-layer-btn").animate({"top":0}); } /** * Function to return if browser is IE and if so what version * * This function is only required for the OpenLayers drawText * function override. This function can be removed when it is. * * Parameters: none * * Returns: false {boolean} if non-IE browser; or IE version {string} */ var isIE = function() { var browser = navigator.userAgent.toLowerCase(); if (browser.indexOf('msie') != -1) return parseInt(browser.split('msie')[1],10); else return false; }; /** * OpenLayers drawText method override to accommodate cluster * labelling in Internet Explorer 8. * * Once the Ministry (and most of it's clients) have * upgraded to IE 9 or higher, this function override * can be removed. * * Parameters: * featureId - {String} * style - * location - {} */ OpenLayers.Renderer.VML.prototype.drawText = function (featureId, style, location) { var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect"); var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox"); var resolution = this.getResolution(); if (isIE() == 7 || isIE() == 8) { label.style.left = ((((location.x - this.featureDx)/resolution - this.offset.x) | 0)+1) + "px"; // Add px to horizontally centre label.style.top = (((location.y/resolution - this.offset.y) | 0)+9) + "px"; // Add px to vertically centre } else { label.style.left = (((location.x - this.featureDx)/resolution - this.offset.x) | 0) + "px"; label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px"; } label.style.flip = "y"; textbox.innerText = style.label; if (style.cursor != "inherit" && style.cursor) { textbox.style.cursor = style.cursor; } if (style.fontColor) { textbox.style.color = style.fontColor; } if (style.fontOpacity) { textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')'; } if (style.fontFamily) { textbox.style.fontFamily = style.fontFamily; } if (style.fontSize) { textbox.style.fontSize = style.fontSize; } if (style.fontWeight) { textbox.style.fontWeight = style.fontWeight; } if (style.fontStyle) { textbox.style.fontStyle = style.fontStyle; } if (isIE() == 7 || isIE() == 8) { label._featureId = featureId; textbox._featureId = featureId; textbox._geometry = location; textbox._geometryClass = location.CLASS_NAME; } else { if(style.labelSelect === true) { label._featureId = featureId; textbox._featureId = featureId; textbox._geometry = location; textbox._geometryClass = location.CLASS_NAME; } } textbox.style.whiteSpace = "nowrap"; // fun with IE: IE7 in standards compliant mode does not display any // text with a left inset of 0. So we set this to 1px and subtract one // pixel later when we set label.style.left textbox.inset = "1px,0px,0px,0px"; if (isIE() == 7 || isIE() == 8) { label.appendChild(textbox); this.textRoot.appendChild(label); } else { if(!label.parentNode) { label.appendChild(textbox); this.textRoot.appendChild(label); } } var align = style.labelAlign || "cm"; if (align.length == 1) { align += "m"; } var xshift = textbox.clientWidth * (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]); var yshift = textbox.clientHeight * (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]); label.style.left = parseInt(label.style.left,10)-xshift-1+"px"; label.style.top = parseInt(label.style.top,10); };