//------------------------- Copyright Block --------------------------
/* 

Qibla Direction (ver 0.1.6)
Copyright (C) 2007-2008 Hamid Zarrabi-Zadeh
License: Creative Commons 3.0 (BY-NC-SA)

This program can be used in other websites or applications
provided its source (http://tanzil.info/qibla) is clearly 
indicated in the derived work. See more details of the license 
at http://creativecommons.org/licenses/by-nc-sa/3.0/

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

The above copyright block and permission notice SHALL BE INCLUDED 
in all copies or substantial portions of this program.

*/
//--------------------------------------------------------------------


var Qibla = {

	// constants: translations go here
	addressBarMsg: 'Enter your address',

	consts: { 
		north: 'North',
		south: 'South',
		east:  'East',
		west:  'West',
		cw:    'Clockwise',
		ccw:   'Counter-Clockwise',
		from:  'from'
	},

	//----------------------------------------
	
	// icons used in the map
	kabaIcon: {image: 'images/kaba.png',  width: 17, height: 17},
	homeIcon: {image: 'images/cross.gif', width: 17, height: 17},

	// display options
	geodesicMode : true,    // true: curve, false: line
	directionMode : 2,      // 0: 180 degrees, 1: 90 degrees, 2: 45 degrees
	showRotation : true,    // show CW/CCW info
	toggleUnit : true,      // enable toggling distance unit
	lineColor: '#FF0000',   // qibla line color
	
	// ka'ba coordinates
	kabaLat: 21.42252,
	kabaLng: 39.82621,

	// default values 
	cookieData: [
		{name: 'homeLat', val: 43.4676},
		{name: 'homeLng', val: -80.5356},
		{name: 'mapZoom', val: 14},
		{name: 'mapType', val: 0},      // 0: normal, 1: satellite, 2: hybrid
		{name: 'distanceUnit', val: 0}  // 0: kilometer, 1: mile
	],

	useCookies: true,      // set to false if you don't want cookies
	expireDays: 60,        // number of days cookies are kept in the browser
	cookiesPath: '/qibla'  // the path to which cookies are sent
}



//-------------------------- Public Methods --------------------------


// specify id of html elements into which information is written 
Qibla.setDisplay = function(elements)
{
	this.mapObj = $(elements.map);
	this.addressObj = $(elements.addressBar);
	this.curLatObj = $(elements.currentLat);
	this.curLngObj = $(elements.currentLng);
	this.directionObj = $(elements.qiblaDirection);
	this.dirInfoObj = $(elements.directionInfo);
	this.distanceObj = $(elements.distanceToKaba);
}


// start displaying qibla on map
Qibla.startMap = function(startLat, startLng, startZoom, startType)
{
	if (!GBrowserIsCompatible())
		return;

	startLat = startLat || this.homeLat;
	startLng = startLng || this.homeLng;
	startZoom = startZoom || this.mapZoom;
	startType = startType || this.mapType;

	var home = new GLatLng(startLat, startLng);
	this.kaba = new GLatLng(this.kabaLat, this.kabaLng);

	this.initMap();
	this.initAddressBar();

	this.map.setCenter(home, startZoom);
	this.map.setMapType(this.map.getMapTypes()[startType]);
}


// reurn qibla direction for a given location
Qibla.getQiblaDirection = function(lat, lng)
{
	var qiblaDir = -this.getDirection(lat, lng, this.kabaLat, this.kabaLng);
	if (qiblaDir < 0) 
		qiblaDir += 360;
	return qiblaDir;
}


// set distance mode 
Qibla.setDistanceUnit = function(mode)
{
	this.distanceUnit = mode;  // 0: kilometer, 1: mile
}


// toggle distance mode 
Qibla.toggleDistanceUnit = function()
{
	this.distanceUnit = 1- this.distanceUnit;  
}


//--------------------------- Initializer -----------------------------


// initialize the map 
Qibla.initMap = function()
{
	this.map = new GMap2(this.mapObj);
	this.geocoder = new GClientGeocoder();

	this.map.addControl(new GLargeMapControl());
	this.map.addControl(new GMapTypeControl());
	this.map.addControl(new GScaleControl());

	var icon = new GIcon(null, this.kabaIcon.image);
	icon.iconSize = new GSize(this.kabaIcon.width, this.kabaIcon.height);
	icon.iconAnchor = new GPoint(this.kabaIcon.width >> 1, this.kabaIcon.height >> 1);
	this.kabaMarker = new GMarker(this.kaba, {icon: icon, clickable: false});

	var icon = new GIcon(null, this.homeIcon.image);
	icon.iconSize = new GSize(this.homeIcon.width, this.homeIcon.height);
	icon.iconAnchor = new GPoint(this.homeIcon.width >> 1, this.homeIcon.height >> 1);
	this.centerMarker = new GMarker(this.kaba, {icon: icon, clickable: false});

	GEvent.addListener(this.map, 'move', function(){ Qibla.draw(); });
}


// initialize address bar 
Qibla.initAddressBar = function()
{
	this.addressObj.onfocus = function(){ Qibla.focusAddressBar(); };
	this.addressObj.onblur = function(){ Qibla.blurAddressBar(); };
	this.blurAddressBar();
}


// initialize values
Qibla.init = function()
{
	this.readCookies();
}


// initialize values
Qibla.unload = function()
{
	this.saveCookies();
	GUnload(); 
}


//------------------------- Update Functions -----------------------------


// update map 
Qibla.draw = function()
{
	var center = this.map.getCenter();
	var lat = center.lat();
	var lng = center.lng();
		
	var qiblaDir = this.getQiblaDirection(lat, lng);

	this.map.clearOverlays();
	this.centerMarker.setPoint(center);
	this.map.addOverlay(this.centerMarker);
	this.map.addOverlay(this.kabaMarker);

	var line = this.createLine(lat, lng, qiblaDir);
	this.map.addOverlay(line);	

	this.writeData(center, qiblaDir);
	this.homeLat = lat;
	this.homeLng = lng;
}


// create a direction line
Qibla.createLine = function(lat, lng, angle)
{
	var mapScale = Math.max(this.map.getSize().width, this.map.getSize().height)/ 120; 
	var zoom = this.mapZoom = this.map.getZoom();
	var dLng = mapScale/ Math.pow(2, zoom- 7);
	if (!this.geodesicMode && zoom < 7) dLng = mapScale;

	dLng = dLng* Math.sin(this.dtr(angle));

	var from = new GPoint(lng, lat);
	var lat2 = this.getLat(lat, angle, dLng)
	var to = new GPoint(lng+ dLng, lat2);
	if (Math.abs(dLng) > Math.abs(lng- this.kabaLng))
		to = new GPoint(this.kabaLng, this.kabaLat);

	var line = new GPolyline([ from, to ], this.lineColor, 4, 0.8, {geodesic: this.geodesicMode});
	return line;
}


//------------------------- Display Functions -----------------------------


// write information
Qibla.writeData = function(center, qiblaDir)
{
	var distance = center.distanceFrom(this.kaba)/ 1000; 

	if (this.curLatObj) this.curLatObj.innerHTML = center.lat().toFixed(4);						
	if (this.curLngObj) this.curLngObj.innerHTML = center.lng().toFixed(4);

	if (this.directionObj) 
	{
		var dir = this.formatDirection(qiblaDir);
		if (this.dirInfoObj)
		{
			this.directionObj.innerHTML = dir.degrees+ '&deg;';
			this.dirInfoObj.innerHTML = dir.info;
		}
		else
			this.directionObj.innerHTML = dir.degrees+ '&deg; '+ dir.info;
	}

	if (this.distanceObj) this.distanceObj.innerHTML = this.formatDistance(distance);
}


// format direction 
Qibla.formatDirection = function(dir)
{
	var baseAngle = [180, 90, 45][this.directionMode];
	var directions = [
			[this.consts.north, this.consts.north],
			[this.consts.north, this.consts.south, this.consts.north],
			[this.consts.north, this.consts.east, this.consts.south, this.consts.west, this.consts.north]
		];

	for (var i=0; dir>baseAngle; i++)
		dir -= baseAngle;
	dir = (i%2 == 0) ? dir : baseAngle- dir;
	var tag = (i%2 == 0) ? this.consts.cw : this.consts.ccw;
	var dirLabel = directions[this.directionMode][parseInt((i+1)/2)];
	var tagLable = this.showRotation ? tag : '';
	var info = [tagLable, this.consts.from, dirLabel];
	return {degrees: dir.toFixed(2), info: info.join(' ')};
}


// format distance 
Qibla.formatDistance = function(dist)
{
	var tags = ['km', 'mi'];
	if (this.distanceUnit)
		dist = dist/ 1.609344;
	var label = tags[this.distanceUnit]
	if (this.toggleUnit)
		label = '<a href="javascript:Qibla.toggleDistanceUnit(); Qibla.draw();" '+
				'title="Toggle Distance Unit">'+ label+ '</a>';
	return dist.toFixed(0)+ ' '+ label;
}



//-------------------------- Calculating Functions -----------------------

// definitions:
// point1 = (lat1, lng1), point2 = (lat2, lng2)
// dLng = lng1- lng2
// direction = angle of the line connecting point1 to point2 (CW from North)


// find the direction between two points
Qibla.getDirection = function(lat1, lng1, lat2, lng2) 
{
	var dLng = lng1- lng2;
	return this.rtd(this.getDirectionRad(this.dtr(lat1), this.dtr(lat2), this.dtr(dLng)));
}


// find the direction between two points
Qibla.getDirectionRad = function(lat1, lat2, dLng) 
{
	return Math.atan2(Math.sin(dLng), Math.cos(lat1)* Math.tan(lat2)- Math.sin(lat1)* Math.cos(dLng));
}


// find latitude of the second point
Qibla.getLat = function(lat1, angle, dLng) 
{
	return this.rtd(this.getLatRad(this.dtr(lat1), this.dtr(angle), this.dtr(dLng)));
}


// find latitude of the second point
Qibla.getLatRad = function(lat1, angle, dLng) 
{
	return Math.atan((Math.sin(dLng)+ Math.tan(angle)* Math.sin(lat1)* Math.cos(dLng))/ (Math.tan(angle)* Math.cos(lat1)));
}


// convert degree to radian
Qibla.dtr = function(d)
{
    return (d* Math.PI)/ 180.0;
}

// convert radian to degree
Qibla.rtd = function(r)
{
    return (r* 180.0)/ Math.PI;
}


//-------------------------- Geocoder Functions -----------------------


// locate address
Qibla.locateAddress = function(address) 
{
	address = address || this.addressObj.value;
	if (address == '' || address == this.addressBarMsg)
	{
		alert(this.addressBarMsg);
		return;
	}

	this.geocoder.getLocations(address, function(resp){ Qibla.showAddressOnMap(resp) });
}


// show address on map
Qibla.showAddressOnMap = function(response) 
{
	if (!response || response.Status.code != 200) 
		alert('Address not found');
	else 
	{
		var place = response.Placemark[0];
		var point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
		this.map.setCenter(point, 4+ parseInt(1.5* place.AddressDetails.Accuracy));
		var marker = new GMarker(point);
		this.map.addOverlay(marker);
		marker.openInfoWindowHtml(place.address);
	}
}


// get current map type 
Qibla.getMapType = function()
{
	var list = this.map.getMapTypes();
	var type = this.map.getCurrentMapType()
	for (var i in list)
		if (list[i] == type)
			return i;
	return 0;
}


//-------------------------- Address-Bar Functions -----------------------


// called when address bar is focused
Qibla.focusAddressBar = function()
{
	this.addressObj.style.color = '#000';
	this.selectText(this.addressObj);
	if (this.addressObj.value == this.addressBarMsg)
		this.addressObj.value = '';
}


// called when address bar lose focus
Qibla.blurAddressBar = function()
{
	if (this.addressObj.value == '' || this.addressObj.value == this.addressBarMsg)
	{
		this.addressObj.style.color = '#999';
		this.addressObj.value = this.addressBarMsg;
	}
	this.deselectText(this.addressObj);
}


// select all text in obj
Qibla.selectText = function(obj)
{
	obj.select();
}


// deselect text in obj
Qibla.deselectText = function(obj)
{
	if (document.selection)
		document.selection.empty();
	else
		window.getSelection().removeAllRanges();
}


//----------------------------- Cookies ---------------------------


// grabbed off the web

var Cookies = {};

// set a cookie value
Cookies.set = function(name, value, expireDays)
{
	 var argv = arguments;
	 var argc = arguments.length;
	 var today = new Date();
	 var expires = (expireDays) ? new Date(today.getTime()+ expireDays* 86400000) : null;
	 var path = (argc > 3) ? argv[3] : this.cookiesPath;
	 var domain = (argc > 4) ? argv[4] : null;
	 var secure = (argc > 5) ? argv[5] : false;
	 document.cookie = name + "=" + escape (value) +
		((expires == null) ? "" : ("; expires=" + expires.toGMTString())) +
		((path == null) ? "" : ("; path=" + path)) +
		((domain == null) ? "" : ("; domain=" + domain)) +
		((secure == true) ? "; secure" : "");
};


// get a cookie value
Cookies.get = function(name)
{
	var arg = name + "=";
	var alen = arg.length;
	var clen = document.cookie.length;
	var i = 0;
	var j = 0;
	while (i < clen) {
		j = i + alen;
		if (document.cookie.substring(i, j) == arg)
			return Cookies.getCookieVal(j);
		i = document.cookie.indexOf(" ", i) + 1;
		if (i == 0)
			break;
	}
	return null;
}


// get cookie value
Cookies.getCookieVal = function(offset)
{
	var endstr = document.cookie.indexOf(";", offset);
	if (endstr == -1)
		endstr = document.cookie.length;
	return unescape(document.cookie.substring(offset, endstr));
}


// clear a cookie
Cookies.clear = function(name) 
{
	if (Cookies.get(name))
		document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
};


// read cookies
Qibla.readCookies = function()
{
	for (var i in this.cookieData)
	{
		var w = Cookies.get(this.cookieData[i].name);
		if (w == null) w = this.cookieData[i].val;
		Qibla[this.cookieData[i].name] = 1* w; 
	}
}


// save cookies
Qibla.saveCookies = function()
{
	this.mapType = this.getMapType();
	if (this.useCookies)
		for (var i in this.cookieData)
			Cookies.set(this.cookieData[i].name, Qibla[this.cookieData[i].name], Qibla.expireDays);
}


//-------------------------- Misc Functions -----------------------

// Prototype $ method
function $(element)
{
	return document.getElementById(element);
}

Qibla.init();

