// SHAREDvar cameras = []; // array of active camera IDsvar camCookie = "camCookie";var camCookieParams = { expires: 365 };// SINGLE CAMERAvar camIdx = 0; // index into the array of cameras being displayedvar camRefreshTimer; // timed interval for refreshing the next cameravar camRefreshSpeed = 180000; // refresh speed in milliseconds (3min = 180000)var camRotateTimer; // timed interval for moving to the next cameravar camRotateSpeed = 5000; // rotation speed in millisecondsvar isCamRotating = true;// DASHBOARDvar camDashID = "cameraDashboard";var camDashRefresh = true; // default is true when a cookie doesn't existvar camDashRefreshTimer;var camDashRefreshSpeed = 180000;  // (3min = 180000)var camDashRefreshCookie = "camDashRefresh";// MAPvar camMapIcon;var camMap; // Google mapvar camMapID = "camMap";var camMapCenterX = 49.252568;var camMapCenterY = -123.023529;// GRAPHICS//var camGraphicsPath = "images/traffic/"; // path to the traffic imagesvar camGraphicsPath = "/images/traffic/";var camGraphics = [];camGraphics["single-loading"] = camGraphicsPath+"camera-loading.gif";camGraphics["single-notfound"] = camGraphicsPath+"camera-not-found.gif";camGraphics["single-none"] = camGraphicsPath+"camera-none.gif";camGraphics["dash-loading"] = camGraphicsPath+"camera-loading-s.gif";camGraphics["dash-notfound"] = camGraphicsPath+"camera-not-found.gif";camGraphics["map-icon"] = camGraphicsPath+"cameraIcon-car.png";// LANGUAGEvar camLang = [];camLang["cam-loading"] = "Loading...";camLang["fav-add"] = "Add to Favourites";camLang["fav-del"] = "Remove";camLang["fav-edit"] = "Edit Cameras";camLang["btn-stop"] = "stop";camLang["btn-play"] = "play";camLang["msg-incompat"] = "<p>Sorry, the Google Maps API is not compatible with this browser</p>";camLang["msg-cookies"] = "You must <a href='http://www.google.com/cookies.html'>enable cookies</a> to save your favourite cameras.";camLang["msg-nofavs"] = "You haven't picked any favourite traffic cameras.";// DEBUGvar camDebug = false;/***************************************************************CAMERA ROTATOR (FOR SIDEBAR)***************************************************************//** * Initialization function for traffic cameras displayed * in a user configurable rotation. * Only call this function once! */function initCameraSingle() {		cameraLog("initCameraSingle():");		if (loadCameraFavouites()) {		$("#camerasMore").text(camLang["fav-edit"]);	}		// Bind click events to the camera controls/buttons	$("#cameraPrev").click(function() { prevCamera(); return false;	});	$("#cameraNext").click(function() { setCameraTimers(false); nextCamera(); return false;	});	$("#cameraPlayStop").click(function() {		setCameraTimers(!isCamRotating);		if(isCamRotating) {			nextCamera();		}		return false;	});		updateCameraControls(); 	// set the state of the buttons	updateCameraSingle(); 	// display the first camera immediately	}/** * Update the current camera image displayed. Call this function after * changing the camIdx value. The src attribute of the image (homeImgCamera)  * is updated to reflect the new image URL. */function updateCameraSingle() {		if(cameras[camIdx]) {			var src = imgArray[cameras[camIdx]];		var label = labelArray[cameras[camIdx]];				// temporarily stop rotation until the next camera loads		rotateTimer(false);				// Display a loading placeholder		if(cameras.length > 1) {			cameraLabel(camLang["cam-loading"]);			cameraImage(camGraphics["single-loading"]);		}				// Preload the next camera image (to reduce flicker) and		// display the image if successfully loaded. Load a Not Found		// placeholder if the image is not found.		$.preload( [ src ], {			onFinish : function(data) {				if(data.found) {					cameraLabel(label);					cameraImage(src);				} else {					cameraLabel(label);					cameraImage(camGraphics["single-notfound"]);				}				rotateTimer(isCamRotating); // continue rotating			}		});						} else {		// The camera was removed. The existing timed intervals		// will move to the next one. If no cameras exist, we should		// display a special graphic until one does.		if(cameras.length < 1) {			cameraImage(camGraphics["single-none"]);			cameraLabel(camLang["msg-nofavs"]);		}	}		cameraLog("updateCameraSingle()",cameras[camIdx],labelArray[camIdx],imgArray[camIdx]);		}/** * Turns the rotation interval on or off. */function rotateTimer(on) {	if(on) {		camRotateTimer = setInterval("nextCamera()",camRotateSpeed);		} else {		clearInterval(camRotateTimer); // stops the interval		}}/** * Turns the refreshing interval on or off. */function refreshTimer(on) {	if(on) {		camRefreshTimer = setInterval("updateCameraSingle()",camRefreshSpeed);		} else {		clearInterval(camRefreshTimer); // stops the interval		}}/** * Sets the camera timers: *  - If rotation is on, set a timer to go to the next camera *  - If rotation is off, set a timer to refresh the camera */function setCameraTimers(rotate) {		isCamRotating = rotate;	clearInterval(camRotateTimer);	clearInterval(camRefreshTimer);		if(isCamRotating) {		rotateTimer(true); // stops the interval	} else {		refreshTimer(true);	}		updateCameraControls();}/** * Display the next traffic camera in the home Camera Array */function nextCamera() {	camIdx = getNextcamIdx();	updateCameraSingle(); // calls function to switch camera passing in new ID}/** * Returns the index for the next camera in the cameras. * This implementation supports wrapping around to the first camera. */function getNextcamIdx() {	if (cameras[camIdx+1]) {		return camIdx+1;	} else {		return 0;	}}/** * Display the previous traffic camera in the cameras. */function prevCamera() {	setCameraTimers(false); // if we are going backwards, we automatically stop the rotation	camIdx = getPrevcamIdx();	updateCameraSingle(); // calls function to switch camera passing in new ID}/** * Returns the index for the previous camera in the cameras. * This implementation supports wrapping around to the last camera if * the current display is the first camera. */function getPrevcamIdx() {	if (camIdx == 1) {		return cameras.length-1;	} else {		return camIdx-1;	}}/** * Sets the state of the camera navigation buttons * depending on the current settings. */function updateCameraControls() {	if(isCamRotating) {		$("#cameraPlayStop").text(camLang["btn-stop"]);	} else {		$("#cameraPlayStop").text(camLang["btn-play"]);	}	}/***************************************************************DASHBOARD FUNCTIONS***************************************************************//** * Initialize the display of the visitors favourite traffic cameras * (dashboard) as stored in a cookie. */function initCameraDashboard() {		cameraLog("initCameraDashboard()");		if($.isCookiesOn()) {			// if there are no favourites		// don't show the default cameras.		if(!loadCameraFavouites()) {			cameras = [];		}		bindDashboardControls();		updateCameraDashboard();		} else {				dashboardMsg(camLang["msg-cookies"]);			}}/** * Update all cameras in the dashboard with the latest camera image. */function updateCameraDashboard() {		if(cameras.length > 0) {				$("#cameraDashMsg").hide();		$("#cameraRefresh").show();		$("#cameraRemoveAll").show();				// show all favourite cameras		for(var i=0; i< cameras.length; i++) {			showCamera(cameras[i]);		}			} else {				dashboardMsg(camLang["msg-nofavs"]);			}}/** * Binds click events to the dashboard controls (refresh, remove all) */function bindDashboardControls() {		$("#cameraRemoveAll").click(function() {		removeCameraFavs();		updateCameraDashboard();	});		camDashRefresh = $.cookie(camDashRefreshCookie) == null;	setDashboardRefreshTimer(camDashRefresh);}/** * Adds a given camera (ID) to the user's list of favourites. * Updates the camera array and saves it to a cookie. */function addCameraFav(id) {		cameraLog("addCameraFav()",id);	if(indexOfCamFav(id) == -1) {				urchinTracker('/traffic/favourite-added');				cameras.push(id);		saveCameras();		bindCameraPopAction(id);		updateCameraDashboard();					}	camMap.closeInfoWindow();}/** * Removes a given camera (ID) from the user's list of favourites. * Updates the camera array and saves it to a cookie. */function removeCameraFav(id) {		var removeID = indexOfCamFav(id);	cameraLog("removeCameraFav()",id);		if(removeID > -1) {		cameras.remove(removeID);		saveCameras();		bindCameraPopAction(id);		$("#camera"+id).fadeOut("slow"); // hides the camera from the dashboard		updateCameraDashboard();	}	camMap.closeInfoWindow();}/** * Removes ALL camera favourites */function removeCameraFavs() {	for(var i=0; i<= cameras.length; i++) {		$("#camera"+cameras[i]).fadeOut("slow"); // hides the camera from the dashboard	}	cameras = []; // empty the array	saveCameras();}/** * Shows a camera (by ID) in the favourites dashboard. The generated DOM  * chunk is a new list item. */function showCamera(id) {		var camID= "camera"+id;	var imgID = "img"+id;		// checks if camera exists as it could have been	// added and hidden when removed	if( $("#"+camID).length ) { 			$("#"+camID).show(); // reshows the hidden camera			// add the html to the dashboard	} else {		var cam = [];		cam.push("<li id='camera"+id+"' class='camera'>");		cam.push("<label>"+labelArray[id]+"</label>");		cam.push("<img id='"+imgID+"' src='"+camGraphics["dash-loading"]+"'>");		cam.push("<a>Remove</a></li>");		$("#"+camDashID).append(cam.join(''));				$("#"+camID+" a").click(function(){			removeCameraFav(id);			return false;		});			}		var imgSrc = imgArray[id] + cacheKill();		$.preload( [ imgSrc ], {		onFinish : function(data) {			if(data.found) {				cameraImage(imgSrc,id);			} else {				cameraImage(camGraphics["dash-notfound"],id);			}		}	});				cameraLog("showCamera()",id);	}/** * Returns a query parameter to kill any caching that * might occur with the traffic camera image. */function cacheKill(){		return "?ck=" + new Date().getTime();}/** * Turns dashboard refreshing on or off. */function setDashboardRefreshTimer(on){		// check if the user turned refresh off	if(!on) {				$("#cameraRefreshOff").addClass("set");		$("#cameraRefreshOn").removeClass("set");		$("#cameraRefreshOff").unbind("click");		$("#cameraRefreshOn").click(function(){setDashboardRefreshTimer(true);});				$.cookie(camDashRefreshCookie,"off",camCookieParams);		clearInterval(camDashRefreshTimer);			// otherwise, turn on dashboard refreshing	} else {				$("#cameraRefreshOn").addClass("set");		$("#cameraRefreshOff").removeClass("set");		$("#cameraRefreshOn").unbind("click");		$("#cameraRefreshOff").click(function(){setDashboardRefreshTimer(false);});				$.cookie(camDashRefreshCookie,null); // delete cookie		camDashRefreshTimer = setInterval("dashboardRefresh()",camDashRefreshSpeed);	}			cameraLog("setDashboardRefreshTimer()",on);	}/** * Refreshes all camera images in the dashboard by looping * through the cameras and setting their source to the same * source, prompting the browser to get the latest version. */function dashboardRefresh(){	var src;	for(var i=0; i<=cameras.length; i++) {		src = imgArray[cameras[i]] + cacheKill();		//src = $("#camera"+cameras[i]+" img").attr("src");		$("#camera"+cameras[i]+" img").attr("src",src);	}		cameraLog("dashboardRefresh()",cameras);	}/** * Saves the current array of cameras to a * cookie for loading later (favourites). */function dashboardMsg(msg) {	$("#cameraRefresh").hide();	$("#cameraRemoveAll").hide();	$("#cameraDashMsg").html(msg);	$("#cameraDashMsg").show();}/** * Saves the current array of cameras to a * cookie for loading later (favourites). */function saveCameras() {	if(cameras.length) {		$.cookie(camCookie,cameras.join('|'), camCookieParams);	} else {		$.cookie(camCookie,null);	}	cameraLog("saveCameras()",cameras);}/***************************************************************MAP FUNCTIONS***************************************************************//** * Initialize the Google Map and adds all of the camera Map Markers */function initCameraMap() {				$(function(){ // called after the page has finished loading			cameraLog("initCameraMap()");				if (GBrowserIsCompatible()) {						camMapIcon = new GIcon();			camMapIcon.image = camGraphics["map-icon"];			camMapIcon.iconSize = new GSize(12, 11);			camMapIcon.iconAnchor = new GPoint(0, 30);			camMapIcon.infoWindowAnchor = new GPoint(10, 10);							camMap = new GMap2(document.getElementById(camMapID));			camMap.addControl(new GLargeMapControl());			camMap.addControl(new GMapTypeControl());			camMap.setCenter(new GLatLng(camMapCenterX, camMapCenterY), 11);						// Add a map marker for each camera			for (var id = 0; id <= latArray.length; id++) {				if(latArray[id] && lngArray[id]) {					addCameraMarker(id);				}			}							} else {			$("#camMap").html(camLang["msg-incompat"]);		}				  });}/** * Creates a Google Map Marker (GMarker), attaches click events to the marker * for displaying the informatino window, and adds the marker to the map. */function addCameraMarker(id) {		var point = new GLatLng(latArray[id],lngArray[id]);	var html = cameraMapPopupHtml(id);	var marker = new GMarker(point,camMapIcon);		GEvent.addListener(marker, "click", function() {		marker.openInfoWindowHtml(html);		bindCameraPopAction(id);				$.preload( [ imgArray[id] ], {			onFinish : function(data) {				if(data.found) {					$("#cameraPop"+id+" img").attr("src",imgArray[id]);				} else {					$("#cameraPop"+id+" img").attr("src",camGraphics["dash-notfound"]);				}			}		});				});		camMap.addOverlay(marker);	return true;	}function bindCameraPopAction(id) {		if($.isCookiesOn()) {  	// is a favourite, so it can be removed		if (indexOfCamFav(id) > -1) {					$("#cameraPop"+id+" a").unbind("click");			$("#cameraPop"+id+" a").text(camLang["fav-del"]);			$("#cameraPop"+id+" a").click(function(){				removeCameraFav(id);																 			});						// not a favourite, can be added		}	else {					$("#cameraPop"+id+" a").unbind("click");			$("#cameraPop"+id+" a").text(camLang["fav-add"]);			$("#cameraPop"+id+" a").click(function(){				addCameraFav(id);																 			});			}	}		}/** * Generates and returns the HTML pop up for display in the map */function cameraMapPopupHtml(id) {	var h = [];	h.push("<div class='cameraMapPopup camera' id='cameraPop"+id+"'>");	h.push("<label id='label"+id+"'>"+labelArray[id]+"</label>");	h.push("<img src='"+camGraphics["dash-loading"]+"'>");	h.push("<a></a>");		h.push("</div>");	return h.join('');}/***************************************************************GENERIC FUNCTIONS***************************************************************//** * Checks if a camera is a favouirte by searching for * the existence of the given id in the array * @return index of the favourite. */function indexOfCamFav(id) {	for(var i=0; i<= cameras.length; i++) {		if(parseInt(cameras[i],10) === parseInt(id,10)) return i;	}	return -1;}/** * Load any favouite camera IDs into the cameras array. If no * favourite are saved, load the default cameras. * @return true Returns true if there are saved favouites, false otherwise */function loadCameraFavouites() {		var tmpCookie = $.cookie(camCookie);			if (tmpCookie) {		cameras = tmpCookie.split("|");	}	cameraLog("loadCameraFavouites()",tmpCookie,cameras);		if(cameras.length > 0) {		return true;	} else {		cameras = defaultCameras.split("|");		return false;	}}/** * A debugging function for the traffic cameras (requires Firebug). */ function cameraLog() {	if(camDebug) {		if(window.console) {			console.debug.apply( console, arguments );		} else if($("#cameraDebug").length) {			var out = [];			out.push("<p>");			for(var i=0; i<arguments.length; i++) {				out.push(arguments[i].toString());				out.push(", ");			}			out.push("</p>");			$("#cameraDebug").prepend(out.join(''));		}	}}/** * Sets the text of the label for the current camera * or a specific camera id. */function cameraLabel(text, id) {	if(arguments.length== 1) {		$("#camera label").text(text);		} else {		$("#camera"+id+" label").text(text);		}}/** * Sets the image for the current camera or * for a specific camera id. */function cameraImage(url, id) {	if(arguments.length == 1) {		$("#camera img").attr("src",url);		} else {		$("#camera"+id+" img").attr("src",url);		}}/***************************************************************UTILS***************************************************************/// Array Remove - By John Resig (MIT Licensed)Array.prototype.remove = function(from, to) {  var rest = this.slice((to || from) + 1 || this.length);  this.length = from < 0 ? this.length + from : from;  return this.push.apply(this, rest);};