/*
* 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 = "";
$(html).data(l).appendTo(layers);
}
if (l.visible && l.layerType != "group") { // Add visible layers, not groups
var layer = $("").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);
};