<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Sky Viewport" 
	height="400" scrolling="true">
	<Require feature="dynamic-height"/>
	<Require feature="opensocial-0.8"/>
    <Require feature="minimessage"/>

</ModulePrefs>


<Content type="html"><![CDATA[

<script src="http://maps.google.com/jsapi?key=ABQIAAAAKYHb1B3NpT-mpL4s83EIgRSop_PCxtS7_vOEwBFafrYxod0JxBQK6BarsnpmKY7-BFhZ1QAd3zSaLg"
	type="text/javascript"></script>
<script src="http://sky.astro.washington.edu/gadgets/js/gDataInterface.js" type="text/javascript"></script>
<script src="http://students.washington.edu/csayres/widgetlib1.js" type="text/javascript"></script>
<script src="http://sky.astro.washington.edu/gadgets/js/astroObjectLib.js" type="text/javascript"></script>

<script type="text/javascript">

    // run some triggers just in case it took us a while to start up and there are already some public vars we need to react to
//    refreshKMLFromList("VP_Point_List", _KMLPointList);

// Writes to the console if debug (global) is true  
function debugLog(msg){
    if ((debug === true) && (window.console && console.log)) { 
         console.log(GADGET_NAME + ": " + msg);
    }
    return;
}


var vpe = null;

// CONSTANTS
var GADGET_NAME = "ViewportSky" + "_" + Math.floor(Math.random()*100000+1).toString()
    debug = true;

function init(){
//debugger;
    vpe = new ViewportEarth();
	gadgets.window.adjustHeight();
}

function ViewportEarth() {
    var my = new Object();


    // CONSTANTS
    var DEFAULT_NAMESPACE = "NS1",
        MM_TIMER_DURATION = 4,
        TIMER_VIEWCHANGEEND = 200;  // ms to wait to trigger the ge viewchangeend event

    // PRIVATE
    var namespaceList = [ DEFAULT_NAMESPACE ];      // list of our current Namespaces. Just init with the default
    var di = new gDataInterface(GADGET_NAME, DEFAULT_NAMESPACE); // make dataInterface object, init with default
    var miniMsg = new gadgets.MiniMessage();// make MiniMessage object
    
    // private Point List
    // first make the list item constructor
    function KMLListItem(){}
    KMLListItem.prototype = {
        KMLobj : null,
        listItemId : -1,
        isPlotted : false,
    };

    var _KMLPointList = [];

    var msg = miniMsg.createStaticMessage("Attempting to register gadget...");
    
    var tidsViewchangeend = [];   // array of timer IDs for ge viewchangeend event

    // INIT Functions
    
    // init google earth (ge)
    var ge = null;     // the google earth object is within the viewport scope
	google.load("earth", "1");
    google.setOnLoadCallback(initGE);
    
    function initGE() {
        google.earth.createInstance('sky_map', initCB, failureCB);
    }

    // callback on successful creation of ge plugin instance
    function initCB(instance) {
        ge = instance;
        ge.getWindow().setVisibility(true);
        ge.getOptions().setMapType(ge.MAP_TYPE_SKY);
        ge.getNavigationControl().setVisibility(ge.VISIBILITY_AUTO);
        ge.getOptions().setStatusBarVisibility(true);

        // Add event listeners
        google.earth.addEventListener(ge.getView(), "viewchangeend", function(){
                    // write the new VP bounds to the public variable
                    // TODO: This will need a timer, because the viewchangeend event fires so often when dragging slowly.
                    // We need to make sure that we arent writing the current bounds more often than once every 250ms or so
                    writeCurrentBounds();
                });
        // we can implement this stuff later if we want...
           //Initialize map to Sombrero Galaxy  
        //   setTimeout(function() {
        //     var oldFlyToSpeed = ge.getOptions().getFlyToSpeed();
        //     ge.getOptions().setFlyToSpeed(.2);  // Slow down the camera flyTo speed.
        //     var lookAt = ge.getView().copyAsLookAt(ge.ALTITUDE_RELATIVE_TO_GROUND);
        //     lookAt.set(-11.623055555555556, 9.997500000000002, 0,ge.ALTITUDE_RELATIVE_TO_GROUND, 262.87, 0, 6039.328125684229);
        //     ge.getView().setAbstractView(lookAt);
        //     ge.getOptions().setFlyToSpeed(oldFlyToSpeed);
             

        //    }, 1000);  // Start the zoom-in after one second.
    }  

function failureCB(errorCode) {
   alert("*** Error initializing google earth plugin ***");
}

   
    // REGISTRATION
    doRegistration(namespaceList);
    
    
    // REGISTRATION stuff
    // do complete registration for all namespaces in the array
    function doRegistration(namespaceArray){
        // TODO: check that namespaceArray is actually and array
        for (var i=0, len=namespaceArray.length; i<len; i++){
            registerMe(namespaceArray[i]);
        }
    }
    // register this gadget in a particular namespace
    function registerMe(namespaceName){
        //F irst, Register the gadget with success and failure callbacks
        di.registerGadget(namespaceName, function(){continueRegistration(namespaceName);}, 
                                                function(){registrationFailure(namespaceName);});
    }    
    
    // Callback after a successful registration of this gadget in a namespace
    // Displays messages and registers variables
    function continueRegistration(namespaceName){
        debugLog('Registration was total Success!');
        if (msg !== undefined) {
            miniMsg.dismissMessage(msg);
            miniMsg.createTimerMessage("Registration Succesful.", MM_TIMER_DURATION);
        }
        registerVariables(namespaceName);
        // di.registerVariable();
        // di.addTrigger();
    }
    
    // Callback after a failure of registration
    // displays message showing failure
    function registrationFailure(namespaceName){
        debugLog('Registration was total failure!');
        miniMsg.dismissMessage(msg);
        miniMsg.createDismissibleMessage("Unable to register gadget.\nWe cannot communicate with other gadgets.");
    }
    
    // Registers variables, and if successful, calls registerTriggers
    function registerVariables(namespaceName){
        debugLog('Attempting to register variables...');
        var varList = [ "VP_Coord", "KML_List", "img_ovly", "VP_Point_List", "VP_Current_Bounds", "VP_Speed" ] // list of variables to register

        for (var i=0, len=varList.length; i<len; i++) {
            // TODO: should we do this for every namespace?
            if (di.registerVariable(namespaceName, varList[i], null)) {
                debugLog('Successfully registered variable "' + varList[i] + '"');
            }
            else {
                debugLog('FAILED to register variable "' + varList[i] + '"');
            }
        }
        // TODO: should we check for successful variable registration?
        // posisbly register one trigger at a time for each successful var?
        registerTriggers(namespaceName);
    }
    
    // registers Triggers
    function registerTriggers(namespaceName){
        if (di.registerTrigger(namespaceName, "VP_Coord", 
                            function(){
                                my.moveMap();
                            })) {
            debugLog('Successfully registered trigger for "' + 'VP_Coord' + '."');
        }
        if (di.registerTrigger(namespaceName, "KML_List", 
                            function(){
                                //clearKML();
                                showKML();
                            })) {
            debugLog('Successfully registered trigger for "' + 'KML_List' + '."');
        }
        if (di.registerTrigger(namespaceName, "VP_Point_List", 
                            function(){
                                refreshKMLFromList("VP_Point_List", _KMLPointList);
                            })) {
            debugLog('Successfully registered trigger for "' + 'VP_Point_List' + '."');
        }
	if (di.registerTrigger(namespaceName, "img_ovly", 
                            function(){
                                //clearKML();
                                createGroundOverlay();
                            })) {
            debugLog('Successfully registered trigger for "' + 'img_ovly' + '."');
        }
	if (di.registerTrigger(namespaceName, "VP_Speed", 
                            function(){
                                my.setFlyToSpeed();
                            })) {
            debugLog('Successfully registered trigger for "' + 'VP_Speed' + '."');
        }

    }
    
    // METHODS
	// Sets the flytospeed between 0 and 5
	my.setFlyToSpeed = function() {
		var speed = parseFloat(di.readVariable(DEFAULT_NAMESPACE, "VP_Speed"));
		if (!isNaN(speed)) {
			if ((speed >= 0) && (speed <= 5)) {
            	ge.getOptions().setFlyToSpeed(speed);	// fastest speed before ge.SPEED_TELEPORT
			}
		}
	}

    // Slews map to point (center) at new coordinate
    // TODO: with range? or bounding box?
	my.moveMap = function (){
        var coord = di.readVariable(DEFAULT_NAMESPACE, "VP_Coord");
        var lon = coord.getLongitude();
        var lat = coord.getLatitude();
        //alert("RA: " + ra + "\nDEC: " + dec);
        // check that ra and dec are numbers
         if (!isNaN(lon) && !isNaN(lat)){
            var lookAt = ge.createLookAt('');
            lookAt.setLatitude(lat);
            lookAt.setLongitude(lon);
            lookAt.setAltitude(0);
            lookAt.setHeading(0);
            lookAt.setRange(7000.0);
        
            ge.getView().setAbstractView(lookAt);
        }   
    } 
    
    
    writeCurrentBounds = function(){
        // we need to wait a few ms so we don't trigger this event too often
        if (tidsViewchangeend.length !== 0){
            // if the timer was already set, then clear them to reset them.
            clearTimerArray(tidsViewchangeend);    
        }
        // now there is no timer set
        // so make one
        var tid = setTimeout(function(){
                    boxBounds = ge.getView().getViewportGlobeBounds();
            
                    topLeft = new UW.astro.CelestialCoordinate(0,0);
                    topLeft.setLongitude(boxBounds.getWest());
                    topLeft.setLatitude(boxBounds.getNorth());

                    bottomRight = new UW.astro.CelestialCoordinate(0,0);
                    bottomRight.setLongitude(boxBounds.getEast());
                    bottomRight.setLatitude(boxBounds.getSouth());

                    curBounds = [topLeft, bottomRight];     // 2-element array of CelestialCoordinates. [topLeft, bottomRight]
                    
                    di.writeVariable(DEFAULT_NAMESPACE, "VP_Current_Bounds", curBounds);
                    // alert('viewchangeend: ' + curBounds[0].getRa() + ' ' + curBounds[0].getDec() + ' ' + curBounds[1].getRa() + ' ' + curBounds[1].getDec());
  
                }, TIMER_VIEWCHANGEEND);
        // add the new timer id to the array
        tidsViewchangeend.push(tid);
    }
    
    // KML
    function createGroundOverlay() {
      var img_ovly=di.readVariable(DEFAULT_NAMESPACE, "img_ovly");
      var url=img_ovly[0];
      var north=Number(img_ovly[1]);
      var south=Number(img_ovly[2]);
      var east=Number(img_ovly[3]);
      var west=Number(img_ovly[4]);
      var rotation=Number(img_ovly[5]);
     // alert(url);
      //alert('north '+north)
      //alert('south '+south)
      //alert('east '+east)
      //alert('west '+west)
      //alert('rotation '+rotation)
     

     var groundOverlay = ge.createGroundOverlay('');
     groundOverlay.setIcon(ge.createIcon(''))
     groundOverlay.getIcon().setHref(url);
     groundOverlay.setLatLonBox(ge.createLatLonBox(''));
     
     //var center = ge.getView().copyAsLookAt(ge.ALTITUDE_RELATIVE_TO_GROUND);
     //var north = center.getLatitude() + .35;
     //alert(north)
     //var south = center.getLatitude() - .35;
     //var east = center.getLongitude() + .55;
     //var west = center.getLongitude() - .55;
     //var rotation = 0;
     var latLonBox = groundOverlay.getLatLonBox();
     latLonBox.setBox(north, south, east, west, rotation);

     ge.getFeatures().appendChild(groundOverlay);
     // var groundOverlay = ge.createGroundOverlay('');
      //groundOverlay.setIcon(ge.createIcon(''))
      //groundOverlay.getIcon().setHref("http://www.google.com/intl/en_ALL/images/logo.gif");
      //groundOverlay.setLatLonBox(ge.createLatLonBox(''));
    
//      var center = ge.getView().copyAsLookAt(ge.ALTITUDE_RELATIVE_TO_GROUND);
//      alert(center.toSource())
      //var latLonBox = groundOverlay.getLatLonBox();
      //groundOverlay.getLatLonBox().setBox(north, south, east, west, rotation);
    
     // ge.getFeatures().appendChild(groundOverlay);
    }
    function clearKML() {
        if (ge !== null){
            var features = ge.getFeatures();
            while (features.getFirstChild()) {
                features.removeChild(features.getFirstChild());
            }
        }
    }
    function showKML() {
        var kmlList = di.readVariable(DEFAULT_NAMESPACE, "KML_List");
        if (kmlList !== null) {
            if (kmlList.length === 0){
                //clearKML();
            }
            for (var i=0, len=kmlList.length; i<len; i++) {
                parsed = ge.parseKml(kmlList[i]);
                if (parsed !== null){
                    // TODO: better test for ge
                    if (ge !== null){
                        ge.getFeatures().appendChild(parsed);
                    }
                }
                else{
                    debugLog('Problem parsing element ' + i + ' in KML_List');
                }
            }
        }
    }
    function refreshKMLFromList(pubListName, privList) {
        
        // Sync the pub and priv lists

        var pubList = di.readVariable(DEFAULT_NAMESPACE, pubListName);
        var toPlot = [];    // array of ids to plot
        var toRemove = [];  // array of ids to remove
        // compare the ids from pubList to the ids in the privList
        // first find points to remove
        // TODO: Is there a faster way to search these arrays?
        // loop thru privList looking for points NOT in pubList
        // loop backwards so we can splice array in loop
        for (var i=privList.length-1; i>=0; i--) { 
            if ((pubList.getItemById(privList[i].listItemId) === null) || (pubList.isIdModified(privList[i].listItemId))) {
                // has been removed from the pubList, so add it to the toRemove list
                // and remove it from the privList
                toRemove.push(privList[i].KMLobj);  // add the KMLobj to the remove list
                privList.splice(i,1); // remove it from privList
            }
        }
        // TODO: check isPlotted!
        // next find the new points to plot
        // loop thru pub list looking for points NOT in priv list (they are new points)
        for (var i=0, len=pubList.getLength(); i<len; i++){
            var pubItemId = pubList.getIdByIndex(i);
            var found = false;
			// check for this id in the priv list
            for (var i2=0, len2=privList.length; i2<len2; i2++){
                if (privList[i2].listItemId === pubItemId) {
                    found = true;
                    //break;
                    // break actually breaks out of both for's, so lets just reset i=len
                    // TODO: CHANGE THIS BAD FORM
                    i2 = len;
                }
            }
            if (!found){
                // we found a NEW point in the pubList (not in privList)
                // add it to the toPlot array
                toPlot.push(pubItemId)
            }
        }
       	
		// Find indexes of modified points
		var modifiedIdsList = pubList.getModifiedIds();
	    for (var i=0, len=modifiedIdsList.length; i<len; i++){
		//	toPlot.push(modifiedIdsList[i]);
			pubList.setModifiedById(modifiedIdsList[i]);
		}
		
		
//		alert('Modified IDs: ' + modifiedIdsList);

        // sync the GE to the priv list
        // TODO: better test for GE
        if (ge !== null){ 
            var geFeatures = ge.getFeatures();  // speed up the getFeatures methods
            // REMOVE the kmlobjects in toRemove
            for (var i=0,len=toRemove.length; i<len; i++){
                geFeatures.removeChild(toRemove[i]);
            }
		
			
			// PLOT the kml objects in toPlot
            for (var i=0,len=toPlot.length; i<len; i++){
                // first we plot it
                var kmlText = pubList.getItemByIndex(toPlot[i]).getKML();
                parsed = ge.parseKml(kmlText);
                geFeatures.appendChild(parsed);
			
                // then add it to the privList
                var privItem = new KMLListItem();
                privItem.KMLobj = parsed;
                privItem.listItemId = toPlot[i];
                privItem.isPlotted = true;
                privList.push(privItem);
            } 
        }
   
    }
    
    // HELPER
    function clearTimerArray(timers){
        if (timers instanceof Array){
            for (var i=0, len=timers.length; i<len; i++){
                clearTimeout(timers.splice(i,1));    // splice returns the removed element
            }
        }
    }

}



            
            //	loopGet();
		//function loopGet(){
		//  timeIntervalId = setInterval ( "updateCoords()", 1910);
		//}

		//function updateCoords() {
		  //makeRequestGet("http://128.95.99.32:8891/c" + tid, 0);
		  //tid += 1;

		  //var coords = dataGadget.readVar("VPCoords");
		  //response(coords);

		//}

//function moveMap(){
//  ge.getOptions().setFlyToSpeed(ge.SPEED_TELEPORT);
//  var lookAt = ge.createLookAt('');
//  lookAt.setLatitude(newdec);
//  lookAt.setLongitude(radeg);
//  lookAt.setAltitude(0);
//  lookAt.setHeading(0);
//  lookAt.setRange(range);
//  ge.getView().setAbstractView(lookAt);
//}


//function eventListener(){    
//  var lookAt = ge.getView().copyAsLookAt(ge.ALTITUDE_RELATIVE_TO_GROUND);
//  var globeBounds = ge.getView().getViewportGlobeBounds();
//  range = lookAt.getRange();
//  heading = lookAt.getHeading();
//  dataWrap.range = range;
//  dataWrap.heading = heading;
//  dataWrap.earthlng = lookAt.getLongitude();
//  dataWrap.earthlat = lookAt.getLatitude();  
//  dataWrap.north=globeBounds.getNorth();
//  dataWrap.south=globeBounds.getSouth();
//  dataWrap.east=(globeBounds.getEast())+180;
//  dataWrap.west=(globeBounds.getWest())+180;
//  post();
//}
//
//
//function target(){
//  var placemark = ge.createPlacemark('');
//  placemark.setName("");
//  var features = ge.getFeatures();
//  var icon = ge.createIcon('');
//  icon.setHref('http://students.washington.edu/csayres/target.png');
//  var style = ge.createStyle(''); //create a new style
//  style.getIconStyle().setIcon(icon); //apply the icon to the style
//  placemark.setStyleSelector(style); //apply the style to the placemark 
//  var point = ge.createPoint('');
//  point.setLatitude(tarDEC);
//  point.setLongitude(tarRA-180);
//  placemark.setGeometry(point);
//  features.appendChild(placemark);
//}
//
//function loadKml() {
//    google.earth.fetchKml(ge, kmlurl, function(kmlObject) {
//      if (kmlObject) {
//        ge.getFeatures().appendChild(kmlObject);
//      } 
//      else {
//        setTimeout(function() {
//          alert('Bad or null KML.');
//          }, 0);
//
//      }
//    })
//}
//
//
//function response(obj) {
//
//  if (obj.text.search(/^cellng.*$/gm) != -1){    
//    newRA = ((obj.text.match(/^cellng.*$/gm)).toString()).slice(7)-0;
//  }
//  if (obj.text.search(/^cellat.*$/gm) != -1){
//    newDec = ((obj.text.match(/^cellat.*$/gm)).toString()).slice(7)-0;
//  }
//  if (obj.text.search(/^tarRA.*$/gm) != -1){
//    tarRAtest = ((obj.text.match(/^tarRA.*$/gm)).toString()).slice(6)-0;
//  }
//  if (obj.text.search(/^tarDEC.*$/gm) != -1){
//    tarDECtest = ((obj.text.match(/^tarDEC.*$/gm)).toString()).slice(7)-0;
//  }
//  if (obj.text.search(/^kmlurl.*$/gm) != -1){
//    kmlurlnew=((obj.text.match(/^kmlurl.*$/gm)).toString()).slice(7).toString();
//      kmlurlnew=kmlurlnew.replace(/%3A/g, ":");
//      kmlurlnew=kmlurlnew.replace(/%2F/g, "/");
//      kmlurlnew=kmlurlnew.replace(/%3D/g, "=");
//      kmlurlnew=kmlurlnew.replace(/%26/g, "&");
//      kmlurlnew=kmlurlnew.replace(/%3F/g, "?");
//  }
//  else{ 
//    setTimeout("doNothing()", 10);
//  }
//
//  if (newRA != radeg || newDec != newdec) {
//    radeg = newRA; newdec = newDec;
//    moveMap();
//  }
//  if (tarRAtest != tarRA || tarDECtest != tarDEC){ 
//    tarRA=tarRAtest;tarDEC=tarDECtest;
//    target();
//  }
//  if (kmlurlnew != kmlurl){
//    kmlurl=kmlurlnew;
//    loadKml();
//  }
//}

function doNothing(){}


gadgets.util.registerOnLoadHandler(init);

</script>
<div id="sky_map" style="height:500px; width:100%;">
<!-- <input type="button" value="clear KML" onclick="ks.clearKML()"> -->
</div>



]]></Content>
</Module>
