﻿/*!
 * Forward Living Utils Laibrary Javascript
 * 
 * Date: 		$LastChangedDate: 2009-06-19 10:17:08 +0100 (Fri, 19 Jun 2009) $
 * Revision:	$Revision: 787 $
 */

var Utils = (function($) {
    
    // private members
    var config
	
	//Setup the Web Trends Tracking
	//dcsVar()
	//dcsMeta()
	//dcsFunc("dcsAdv")
	//dcsTag()
	
	function track() {
		var targetString = ""
		for (var i=0; i<arguments.length; i++) {
			if (i>0) targetString += "&"
			targetString += arguments[i]
		}
		//alert("dcsTrk("+targetString+")")
		dcsTrk(targetString)
	}
	
    function loadConfig (configRoot,mediaHome,callback) {
        var onSuccess = function(xmls) {
            var baseConfig = xmls[0]
			var userConfig = xmls[1]
			var jatoConfig = xmls[2]

			var ctadata =  $(userConfig).find("content > item[id='topleftcta']").map(function(i) {
			    var $j = $(this)
				return { 
				  analytics:		$j.find('analytics').text(), 
				  title:	$j.find('title').text(), 
				  url_link: 	$j.find('url_link').text(),
				  thumbnail:	$j.find('thumbnail').text()
				};
			});
			
			//Handle predefined journeys
			var predefinedJourneys = $(userConfig).find("content > item[id='journey']").map(function(i) {
			    var $j = $(this)
				return { 
				    id:          String(i), 
				    summaryname: $j.find('title').text(), 
				    display:     $j.find('title').text(), // for dropdown
					polylineurl: $j.find('url_link').text(),
				    waypoints:   $j.find('text').map( function() {
				        $w = $(this)
    				    return {
    				        shortAddress: "" , 
    				        longAddress:  $w.text()
    				    }
		            })	
				};
			});

			//Handle content options
			var configOptions = {};
			var cont = { "string"	: String, 
						 "boolean"	: function(p){return p=="true";}, 
						 "number"	: Number, 
						 "object"	: function(p){return eval("("+p+")")} 
						 };
			$(baseConfig).find("options > option").each(function(){
				try {
					var $o = $(this)
					var key = $o.attr('key');
					var type = $o.attr('type');
					var value = $o.text();
					var actualVal = cont[type](value);
					configOptions[key] = actualVal;
				} catch (err) {}
			});

			var countries = []
			//fix#99: updated to allow configuration to be read from the userconfig rather than the more generic config xml
			$(userConfig).find("content > item[id='countries']").each(function(){
				countries.push({ lang:$(this).find('analytics').text(),display:$(this).find('title').text(), url:$(this).find('url_link').text() });
			});


                                                // handle categories
			var baseImage = mediaHome+"/Images/visual2_tcm620-902240.gif"
			function getIconRef(basename,mod) {return mediaHome+""+basename+"_"+mod+".png" }
                                                var categories = {
				fl : {active:false,name:"catname",searchterm:"searchterm",imageurl:baseImage,iconon:"/Images/forward_living_on_tcm620-902054.png", iconoff:"/Images/forward_living_off_tcm620-902053.png", iconover:"/Images/forward_living_over_tcm620-902055.png"},
				la : {active:false,name:"catname",searchterm:"searchterm",imageurl:baseImage,iconon:"/Images/landmarks_on_tcm620-902139.png", iconoff:"/Images/landmarks_off_tcm620-902137.png", iconover:"/Images/landmarks_over_tcm620-902140.png"},
				cu : {active:false,name:"catname",searchterm:"searchterm",imageurl:baseImage,iconon:"/Images/culture_on_tcm620-902020.png", iconoff:"/Images/culture_off_tcm620-902019.png", iconover:"/Images/culture_over_tcm620-902021.png"},
				gr : {active:false,name:"catname",searchterm:"searchterm",imageurl:baseImage,iconon:"/Images/green_on_tcm620-902064.png", iconoff:"/Images/green_off_tcm620-902063.png", iconover:"/Images/green_over_tcm620-902065.png"},
				bc : {active:false,name:"catname",searchterm:"searchterm",imageurl:baseImage,iconon:"/Images/bars_clubs_on_tcm620-901956.png", iconoff:"/Images/bars_clubs_off_tcm620-901955.png", iconover:"/Images/bars_clubs_over_tcm620-901957.png"},
				re : {active:false,name:"catname",searchterm:"searchterm",imageurl:baseImage,iconon:"/Images/restaurants_on_tcm620-902190.png", iconoff:"/Images/restaurants_off_tcm620-902189.png", iconover:"/Images/restaurants_over_tcm620-902191.png"},
				dt : {active:false,name:"catname",searchterm:"searchterm",imageurl:baseImage,iconon:"/Images/design_tech_on_tcm620-902033.png", iconoff:"/Images/design_tech_off_tcm620-902032.png", iconover:"/Images/design_tech_over_tcm620-902034.png"},
				lx : {active:false,name:"catname",searchterm:"searchterm",imageurl:baseImage,iconon:"/Images/car_content_on_tcm620-902008.png", iconoff:"/Images/car_content_off_tcm620-902007.png", iconover:"/Images/car_content_over_tcm620-902009.png"}
			};
			
			for (var category in categories) {
				$(userConfig).find("content > item[id='"+category+"']").each(function() {
				    var $this 	= $(this)
					var thiscat = categories[category];
					if (thiscat) {
						thiscat.active		= true;
						thiscat.name 		= $this.find('title').text();
						thiscat.searchterm 	= $this.find('text').text();
						// NG - Not needed now as iconon,off, and over all specified in collection above to all CT to replace.
                                                                                                //thiscat.iconon		= getIconRef(thiscat.iconbase,"on");
						//thiscat.iconoff		= getIconRef(thiscat.iconbase,"off");
						//thiscat.iconover	= getIconRef(thiscat.iconbase,"over");
					}					
				});
			}

			var carFeatureOptions = [];
			var carPOI = [];
			$(userConfig).find('content > item[id="lexuspoi"]').each(function(i) {
				var id = "cc_"+i;
				var title =	$(this).find('title').text();
				var contentUrl = $(this).find('url_link').text();
				
				carPOI.push( {"id":id, "cat":"cc", "latlng":null, "dist":0, "marker":null, "title":title, "subText":$(this).find('subtitle').text(), "imageurl":$(this).find('thumbnail').text(), "contenturl":contentUrl, "tracking":$(this).find('analytics').text()} );	
				carFeatureOptions.push({url:id,display:title,tracking:$(this).find('analytics').text()});
			});

			// handle jato car data
			var lastRetrieved = $(jatoConfig).find("jato").attr("lastretrieved");

			var jatoCarData = {};
			var jatoCarData = {};
			$(jatoConfig).find("cardata").each(function() {
				var $this = $(this);
				var $fuel = $this.find('fuel');
				var $emissions = $this.find('emissions');
				jatoCarData[String($this.attr('id'))] =
					{	
						fuel:     	{type: $fuel.attr('type'), urban: parseFloat($fuel.attr('urban')), highway: parseFloat($fuel.attr('highway')), combined: parseFloat($fuel.attr('combined'))},
						emissions:  {combinedHigh: parseFloat($emissions.attr('combinedHigh')), combinedLow: parseFloat($emissions.attr('combinedLow'))}
					};
			});
			
			
			// our car data
            var iBaseCarID = -1; //we will get this from the first car in carData
            var carData = {};

			$(userConfig).find('content > item[id="car"]').each(function(i) {
			    var $this = $(this);
				var carID = $this.find('instruction').text();
				var jataData = jatoCarData[carID];
				if (jataData) {
                if (i === 0){ iBaseCarID = carID }; //set base car ID as the first car in the collection

					carData[carID] =
									{	active:		true, 
										name:		$this.find('title').text(),
                                                                                                                                                                make:		$this.find('subtitle').text(),
										imageurl:	$this.find('thumbnail').text(),
										fuel:     	jataData.fuel,
										emissions:  jataData.emissions
									};
				}
			});
			
			// handle labels
			var labels = {}
			$(baseConfig).find("labels > label").each(function() {
			    var $this = $(this);
				labels[$(this).attr('id')] = $this.text();
			});
            
            // handle regExpFilters 
			var regExpFilters =  $(baseConfig).find("contentFilters > filter").map(function() {
			    var $this = $(this);
				if ($this.attr('active')=="true") {
					switch ($this.attr('type')) {
                        case "regex" : return new RegExp($this.text(), "i");
					}
				}
			});	

			// store config 
			config = {
			    predefinedJourneys: predefinedJourneys,
			    configOptions     : configOptions,
			    categories        : categories,
			    carData           : carData,
			    baseCarID         : iBaseCarID,
			    labels            : labels,
			    regExpFilters     : regExpFilters,
			    carFeatureOptions : carFeatureOptions,
			    carPOI			  : carPOI,
			    countries		  : countries,
			    ctadata			  : ctadata, 
			    lastRetrieved     : lastRetrieved
			}

			if (callback) {
			    callback(config)
		    }
        }
        
        // do load config
		function _makeLoadConfig(fileUrl, callback){
			return function(callback){
				$.ajax({
					type: "GET",
					url: fileUrl,
					dataType: "xml",
					success: callback
				});
			}
		}
		
                                var pubCountryLanguage = "HRHR"; 

		var fns = [];
        fns.push( _makeLoadConfig(configRoot + "config.xml", callback) )
        fns.push( _makeLoadConfig(configRoot + "userConfig.xml", callback) )
        fns.push( _makeLoadConfig(configRoot + "jato/" + pubCountryLanguage  + ".xml", callback) )
	    multiCall(fns, onSuccess)
		
		
    }
    
    /**
    Calls multiple async functions and waits for all of them to complete. calls callback when all fns are finished.
    
    fns: [function(callback)] - array of async functions to call
    callback: function([result]) - a callback function. receives an array of results from each fn call
    */
    var multiCall = function(fns, callback) {
        var results = [],
            nResults = 0
        $.each(fns, function(i, fn) {
            fn(function(result) {
                results[i] = result
                nResults += 1
                if (nResults === fns.length) { // this was the last call
                    callback( results )
                }
            })
        })
    }
    
    /**
    Waits until certain condition holds, then calls fn.
    predicate: function - should return true when desired condition holds.
    */
    var safeCall = function(predicate, fn, interval) {
        interval = interval || 500
        if (predicate()) {
            fn()
        }
        else {
            setTimeout(function() {
                safeCall(predicate, fn, interval)
            }, interval)
        }
    }
    
    var parseQueryStr = function(url) {
        function readVal(kv) { return decodeURI(kv.length === 2 ? kv[1] : '') }
        var i = url.indexOf('?')
        if (i === -1) { return {} }
        var q = url.substring(i + 1),
            params = {}
        $.each(q.split('&'), function(i, p) {
            var kv = p.split('=')
            var k = kv[0]
            if (params.hasOwnProperty(k)) {
                var v = params[k]
                if (!$.isArray(v)) {
                    v = [v] // turn it into array
                    params[k] = v
                }
                v.push(readVal(kv))
            }
            else {
                params[k] = readVal(kv)
            }
        })
        return params
    }
    
    function baseURI (url) {
        var index = url.indexOf('?')
        return (index == -1 ? url : url.substring(0, index) )
    }
    
    /**
    name and value given - sets cookie, only name given - gets cookie
    */
    var cookie = function(name, value, options) {
        if (typeof value != 'undefined') { // name and value given, set cookie
            options = options || {};
            if (value === null) {
                value = '';
                options.expires = -1;
            }
            var expires = '';
            if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
                var date;
                if (typeof options.expires == 'number') {
                    date = new Date();
                    date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
                } else {
                    date = options.expires;
                }
                expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
            }
            // CAUTION: Needed to parenthesize options.path and options.domain
            // in the following expressions, otherwise they evaluate to undefined
            // in the packed version for some reason...
            var path = options.path ? '; path=' + (options.path) : '';
            var domain = options.domain ? '; domain=' + (options.domain) : '';
            var secure = options.secure ? '; secure' : '';
            document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
        } else { // only name given, get cookie
            var cookieValue = null;
            if (document.cookie && document.cookie != '') {
                var cookies = document.cookie.split(';');
                for (var i = 0; i < cookies.length; i++) {
                    var cookie = jQuery.trim(cookies[i]);
                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length + 1) == (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                    }
                }
            }
            return cookieValue;
        }
    };
    
    /**
    onLoaded: fn(directions)
    */
    function loadDirections(waypoints, onLoaded, onError) {
        directions = new google.maps.Directions();
        google.maps.Event.addListener(directions, "error", onError);
        google.maps.Event.addListener(directions, "load", function() {
            onLoaded(directions)
        });
        directions.loadFromWaypoints(waypoints, {getSteps: true, getPolyline: true, locale:"en"});
    }
    
    function roundNumber(num, dec) {
    	return Math.round(num * Math.pow(10,dec)) / Math.pow(10,dec);
    }
    
    function max(arr, fnweight) {
        if (arr.length < 1) { return undefined } // arr should have at least one element
        var max_value = fnweight(arr[0]), max_elem = arr[0]
        for (var i=1; i < arr.length; i++) {
            var e = arr[i], value = fnweight(e)
            if (value > max_value) {
                max_value = value;
                max_elem = e
            }
        };
        return max_elem
    }
        
    function amount(number, unit) {
        return {
            number  : number,
            unit    : unit,
            toString: function() { return roundNumber(this.number, 2) + ' ' + this.unit }
        }
    }

    function toBritishMeters(meters) {
        // TODO check multipliers
        return {
    	    distance        : amount( meters.distance.number * 0.62, ''),//miles'),
    	    time            : meters.time, // exception: not amount.
    	    avgspeed        : amount( meters.avgspeed.number * 0.62, ''),//miles/h'),
    	    fuelConsumption : amount( meters.fuelConsumption.number , ''),//l'),  // maybe * 0.219
    	    co2Emission     : amount( meters.co2Emission.number * 1, '')//grams')        
        }
    }
    
    function carCalculations(car, routeduration, routelength, useMeters, labels) {
		var hours = Math.floor(routeduration/3600)
		var minutes = Math.floor((routeduration - (hours *3600 ))/60)
        //calculate distance, duration & average speed etc.
    	var distanceMeters  = roundNumber(routelength,0),
    	    distanceKm      = distanceMeters / 1000,
    	    durationSeconds = routeduration,
    	    averageSpeed    = Math.round((distanceKm * 3600) / durationSeconds);

    	var meters = {
    	    distance        : amount( distanceKm, ''),//km'),
    	    time            : hours+" "+(hours==1 ? config.labels.hourSingular : labels.hourPlural) + " " + minutes +" "+ (hours==1 ? labels.minSingular : labels.minPlural),
    	    avgspeed        : amount( averageSpeed, ''),//km/h'),
    	    fuelConsumption : amount( car.fuel.combined * (distanceKm / 100), ''),//L'),
    	    co2Emission     : amount( (car.emissions.combinedHigh * distanceKm)/1000, '')//Kg')
    	};

    	if (!useMeters) { // TODO check the current locale
    	    meters = toBritishMeters(meters)
    	}
    	return meters
    }
    
	function createShareUrl(title,url) {
		return "/forms/share.aspx?title="+title+"&url="+encodeURIComponent(url)+"&campaign=rx450&TB_iframe=true&height=488&width=388"	
	}
	
    // public interface
    
    return {
        max            : max,        
        carCalculations: carCalculations,
        amount         : amount,
        loadDirections : loadDirections,
        loadConfig     : loadConfig,
        multiCall      : multiCall,
        safeCall       : safeCall,
        cookie         : cookie,
        parseQueryStr  : parseQueryStr,
        baseURI        : baseURI,
		roundNumber	   : roundNumber,
		track		   : track,
		createShareUrl : createShareUrl,
        config         : function() { return config } // getter. loadConfig must be called first. TODO ?
    }
})(jQuery)

