home *** CD-ROM | disk | FTP | other *** search
/ Freelog 117 / FreelogNo117-OctobreNovembre2013.iso / Theme / 8GadgetPack / 8GadgetPackSetup.msi / weather.js < prev    next >
Text (UTF-16)  |  2012-05-19  |  135KB  |  2,047 lines

  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // THIS CODE IS NOT APPROVED FOR USE IN/ON ANY OTHER UI ELEMENT OR PRODUCT COMPONENT.
  4. // Copyright (c) 2009 Microsoft Corporation. All rights reserved.
  5. //
  6. ////////////////////////////////////////////////////////////////////////////////
  7.  
  8. var g_locationAPIAvailable = false;
  9. var g_functionToCall = "MicrosoftGadget.requestUpdate()";
  10.  
  11. var gDefaultDisplayMode = "docked"; 
  12. var gDisplaySizeDocked = { width: 130, height: 67 }
  13. var gDisplaySizeUnDocked = { width: 264, height: 194 }
  14. var gDefaultRefreshInterval = 60;    
  15. var gDefaultPollingForServiceExistence = 1; 
  16. var gDefaultSunRise = "06:30:00";
  17. var gDefaultSunSet = "18:30:00";
  18. var gDefaultWeatherLocation = getLocalizedString('DefaultCity');
  19. var gDefaultWeatherLocationCode = getLocalizedString('DefaultLocationCode');
  20. var gDefaultDisplayDegreesIn = 'Celsius';
  21.  
  22. var gTimeStampLastRefreshAvailable = false;
  23. var gTimeToNextRefresh = 1; // default to 1 minute
  24.  
  25. var gMinimumDistance = 10; // Threshold distance (in kilometres) to do a location update ( when recieving location change events )
  26.  
  27. var bUseCelsius = false;
  28.  
  29. var LCID_ARRAY = new Array(1025,1028,1029,1030,1031,1032,1033,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1053,1054,1055,1056,1057,1058,1060,1061,1062,1063,1068,1070,1080,1081,1083,1106,1117,1122,1124,1131,1134,1135,1146,1153,1158,2049,2052,2055,2057,2058,2060,2064,2067,2068,2070,2074,2092,2094,2107,2108,2141,2155,3073,3076,3079,3081,3082,3084,3098,3131,3179,4097,4100,4103,4105,4106,4108,4155,5121,5127,5129,5130,5132,5146,5179,6145,6153,6154,6156,6203,7169,7177,7178,7227,8193,8202,8218,8251,9217,9226,9275,10241,10250,11265,11274,12289,12298,13313,13321,13322,14337,15361,15370,16385,16393,16394,17417,17418,18441,18442,19466,20490,21514);
  30. var LOCCODE_ARRAY = new Array("wc:SAXX0017","wc:TWXX0021","wc:EZXX0012","wc:DAXX0009","wc:GMXX0007","wc:GRXX0004","wc:USNY0996","wc:FIXX0002","wc:FRXX0076","wc:ISXX0010","wc:HUXX0002","wc:ICXX0002","wc:ITXX0067","wc:JAXX0085","wc:KSXX0037","wc:NLXX0002","wc:NOXX0029","wc:PLXX0028","wc:BRXX0232","wc:7162","wc:ROXX0003","wc:RSXX0063","wc:HRXX0005","wc:LOXX0001","wc:SWXX0031","wc:THXX0002","wc:TUXX0014","wc:PKXX0008","wc:IDXX0022","wc:UPXX0016","wc:SIXX0002","wc:ENXX0004","wc:LGXX0004","wc:LHXX0005","wc:AJXX0001","wc:3166","wc:33739","wc:INXX0096","wc:9734330","wc:UKXX0030","wc:CAXX0202","wc:NLXX0002","wc:RPXX0017","wc:BLXX0006","wc:LUXX0003","wc:GLXX0003","wc:CIXX0020","wc:NZXX0049","wc:GTXX0002","wc:IZXX0008","wc:CHXX0008","wc:SZXX0033","wc:UKXX0085","wc:MXDF0132","wc:BEXX0005","wc:SZXX0005","wc:BEXX0005","wc:NOXX0029","wc:POXX0016","wc:YIXX0005","wc:AJXX0001","wc:GMXX0171","wc:SWXX0019","wc:EIXX0014","wc:CAXX0202","wc:ECXX0008","wc:EGXX0004","wc:CHXX0049","wc:AUXX0025","wc:ASXX0023","wc:SPXX0050","wc:CAXX0385","wc:YIXX0005","wc:8633427","wc:PEXX0011","wc:LYXX0009","wc:SNXX0006","wc:LUXX0003","wc:CAXX0343","wc:GTXX0002","wc:SZXX0013","wc:9746201","wc:AGXX0001","wc:LSXX0002","wc:NZXX0049","wc:CSXX0009","wc:LUXX0003","wc:BKXX0004","wc:SWXX0019","wc:MOXX0007","wc:EIXX0014","wc:PMXX0004","wc:8548036","wc:NOXX0049","wc:TSXX0010","wc:SFXX0023","wc:DRXX0009","wc:24724","wc:MUXX0003","wc:VEXX0008","wc:BKXX0004","wc:8633427","wc:YMXX0005","wc:COXX0004","wc:8633427","wc:SYXX0004","wc:PEXX0011","wc:JOXX0002","wc:ARBA0009","wc:LEXX0003","wc:ECXX0008","wc:KUXX0003","wc:RPXX0017","wc:CIXX0020","wc:AEXX0004","wc:BAXX0001","wc:PAXX0001","wc:QAXX0003","wc:INXX0096","wc:BLXX0006","wc:MYXX0008","wc:ESXX0001","wc:SNXX0006","wc:HOXX0008","wc:NUXX0004","wc:USPR0087","wc:USCA0638","wc:MXDF0132","wc:USWA0367","wc:AEXX0004","wc:AEXX0004");
  31. var UNIT_ARRAY = new Array("C","C","C","C","C","C","F","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","C","F","C","C","C","C");
  32.  
  33. var MicrosoftGadget;
  34.  
  35.  
  36. ////////////////////////////////////////////////////////////////////////////////
  37. //
  38. // cleanup() - triggered by body.onunload event
  39. //
  40. ////////////////////////////////////////////////////////////////////////////////
  41. function cleanup() 
  42. {
  43.  System.Debug.outputString("Releasing resources...");
  44.  try
  45.  {
  46.     factory.object.StopListeningForReports();
  47.     System.Debug.outputString("StopListeningForReports succeeded");
  48.  }
  49.  catch(err)
  50.  {
  51.     System.Debug.outputString("StopListeningForReports failed");
  52.  }
  53.  
  54.  try
  55.  {
  56.     MicrosoftGadget.factory = null;
  57.     System.Debug.outputString("Pointer to node cleared");
  58.     factory.removeNode( true );
  59.     System.Debug.outputString("RemoveNode succeeded");
  60.  }
  61.  catch(err)
  62.  {
  63.     System.Debug.outputString("RemoveNode failed");
  64.  }
  65.  
  66.  System.Debug.outputString("Done");
  67. }
  68.  
  69. ////////////////////////////////////////////////////////////////////////////////
  70. //
  71. // setup() - triggered by body.onload event
  72. //
  73. ////////////////////////////////////////////////////////////////////////////////
  74. function setup() 
  75. {
  76.  var OSLocale = vbsGetLocale();
  77.  for (i = 0; i < LCID_ARRAY.length ; i++)
  78.  {
  79.     if ( LCID_ARRAY[i] == OSLocale )
  80.     {
  81.         gDefaultWeatherLocation = LOCNAME_ARRAY[i];
  82.         gDefaultWeatherLocationCode = LOCCODE_ARRAY[i];
  83.         if ( UNIT_ARRAY[i] == "C" )
  84.         {
  85.         gDefaultDisplayDegreesIn = 'Celsius';
  86.         }
  87.         else
  88.         {
  89.         gDefaultDisplayDegreesIn = 'Fahrenheit';
  90.         }
  91.     }
  92.  }
  93.  
  94.  MicrosoftGadget = new WeatherGadget();
  95.  
  96.  // If we are in BIDI Mode, apply some special css to help folks read things Right to Left
  97.  if (gBIDIMode) 
  98.  {
  99.     document.body.className = 'BIDI'; 
  100.  }
  101.  setDisplayMode();
  102.  
  103.  MicrosoftGadget.factory = document.getElementById("factory").object;
  104.  
  105.  MicrosoftGadget.refreshState();
  106.  
  107.  if (MicrosoftGadget.isValid) 
  108.  {
  109.     MicrosoftGadget.refreshSettings();
  110.  } 
  111.  else 
  112.  {
  113.     // The only way that we can get here is if the Service itself is invalid. 
  114.     // Therefore, no need to set up refresh intervals, etc.
  115.     showOrHideServiceError( true );
  116.  }
  117.  
  118.  // Hook the various events to our custom support functions
  119.  if (gGadgetMode) 
  120.  {    
  121.     System.Gadget.settingsUI="settings.html";
  122.     System.Gadget.onSettingsClosed = function(event) 
  123.     { 
  124.         MicrosoftGadget.refreshSettings();
  125.     }
  126.     System.Gadget.onDock = function()
  127.     { 
  128.         setDisplayMode(); 
  129.     }    
  130.     System.Gadget.onUndock = function()
  131.     { 
  132.         setDisplayMode(); 
  133.     }    
  134.     System.Gadget.onShowSettings = function() 
  135.     {
  136.         MicrosoftGadget.suspendPeriodicRefresh();
  137.         if ( MicrosoftGadget.pollingForServiceExistenceIsRunning ) 
  138.         {
  139.         MicrosoftGadget.endPollingForServiceExistence();
  140.         }
  141.         MicrosoftGadget.wasPollingForServiceExistence = false;
  142.     }
  143.     System.Gadget.visibilityChanged = function() 
  144.     {
  145.         if ( System.Gadget.visible ) 
  146.         {
  147.         if ( !IsWeatherDataFresh() && !MicrosoftGadget.pollingForServiceExistenceIsRunning )
  148.         {
  149.             MicrosoftGadget.refreshSettings();
  150.         }
  151.         }
  152.     }
  153.  }    
  154. }
  155.  
  156.  
  157. ////////////////////////////////////////////////////////////////////////////////
  158. //
  159. // IsWeatherDataFresh ( )
  160. //
  161. // return true if weather data last pulled is still fresh
  162. // Also, if the data is still fresh, update global gTimeToNextRefresh to indicate time in minutes when refresh must be triggered to get fresh data
  163. ////////////////////////////////////////////////////////////////////////////////
  164. function IsWeatherDataFresh ( )
  165. {
  166.  if ( gTimeStampLastRefreshAvailable && MicrosoftGadget.oMSNWeatherServiceTimeStamp !== undefined && MicrosoftGadget.oMSNWeatherServiceTimeStamp !== null )
  167.  {
  168.     var ageLastRefresh = calculateAge( MicrosoftGadget.oMSNWeatherServiceTimeStamp );
  169.     var serviceRefreshInterval = MicrosoftGadget.refreshInterval / ( 60 * 1000 );
  170.     var ageLastRefreshInterval = ageLastRefresh.minutesAge + ( ageLastRefresh.hoursAge * 60 );
  171.  
  172.     if ( ageLastRefresh.daysAge !== 0 || ageLastRefreshInterval >= serviceRefreshInterval )
  173.     {
  174.         return false;
  175.     }
  176.     else
  177.     {
  178.         gTimeToNextRefresh = serviceRefreshInterval - ageLastRefreshInterval;
  179.         return true;
  180.     }
  181.  }
  182.  else
  183.  {
  184.     return false;
  185.  }
  186. }
  187.  
  188. ////////////////////////////////////////////////////////////////////////////////
  189. //
  190. // TemperatureInSelectedUnit (degreesFahrenheit) 
  191. //
  192. // If Fahrenheit selected, return temperature passed as-is, else
  193. // Return Tc = (5/9)*(Tf-32); 
  194. ////////////////////////////////////////////////////////////////////////////////
  195. function TemperatureInSelectedUnit (degreesFahrenheit) 
  196. {
  197.     bUseCelsius = ( MicrosoftGadget.displayDegreesIn == "Celsius" );
  198.     if ( bUseCelsius )
  199.     {
  200.         var degreesCelsius = 0;
  201.  
  202.         if ( degreesFahrenheit == undefined )
  203.             return (degreesFahrenheit);
  204.  
  205.         if ( !( degreesFahrenheit == parseFloat( degreesFahrenheit ) ) )
  206.             return (degreesFahrenheit);
  207.  
  208.         degreesFahrenheit = parseFloat( degreesFahrenheit );
  209.         degreesCelsius = ( degreesFahrenheit - 32 )*5.0/9.0;
  210.         degreesCelsius = Math.round( degreesCelsius );
  211.  
  212.         return (degreesCelsius);
  213.     }
  214.     else
  215.     {
  216.         return (degreesFahrenheit);
  217.     }
  218. }
  219.  
  220.  
  221. ////////////////////////////////////////////////////////////////////////////////
  222. //
  223. // WeatherGadget() - main Constructor
  224. //
  225. ////////////////////////////////////////////////////////////////////////////////
  226. function WeatherGadget() 
  227. {
  228.  var self = this;
  229.  
  230.  
  231.  ////////////////////////////////////////////////////////////////////////////////
  232.  //
  233.  // Public Members
  234.  //
  235.  ////////////////////////////////////////////////////////////////////////////////
  236.  this.isValid = true;
  237.  this.isStaleData = false;
  238.  this.dataExpired = false;
  239.  this.ageStampText = ""; // XX <units> ago (holds the localized string indicating staleness of data
  240.  this.SkyText = "";
  241.  this.statusMessage = "";
  242.  this.weatherLocation    = gDefaultWeatherLocation;
  243.  this.weatherLocationCode = gDefaultWeatherLocationCode;
  244.  this.displayDegreesIn    = gDefaultDisplayDegreesIn;
  245.  this.SunRise    = gDefaultSunRise;
  246.  this.SunSet    = gDefaultSunSet;
  247.  this.offsetFromLocalTime = 0;
  248.  this.refreshInterval    = gDefaultRefreshInterval;
  249.  this.displayMode    = gDefaultDisplayMode;
  250.  this.currentState = null;
  251.  
  252.  this.oMSNWeatherServiceTimeStamp = null;
  253.  
  254.  try 
  255.  {
  256.     this.spinner = new getSpinner( "PleaseWaitLoadingSpinner" ); 
  257.  }
  258.  catch (objException) 
  259.  {
  260.     this.spinner = null;
  261.  }
  262.  
  263.  this.status = 200;
  264.  try 
  265.  {
  266.     // Connect to Weather Service .dll
  267.     var oMSN = new ActiveXObject("wlsrvc.WLServices");
  268.     var oMSN2 = new ActiveXObject("wlsrvc.WLServices");
  269.     this.oMSN = oMSN.GetService("weather"); // Object to send and recieve weather data queries and to poll for service existence
  270.     this.oMSN2 = oMSN2.GetService("weather"); // Object to send a latlong query and recieve a location code corresponding to the latlong
  271.  }
  272.  catch (objException) 
  273.  {
  274.     this.isValid = false;
  275.     this.statusMessage = getLocalizedString('ServiceNotAvailable');
  276.     this.oMSN = new Object();
  277.     this.oMSN2 = new Object();
  278.  }
  279.  
  280.  ////////////////////////////////////////////////////////////////////////////////
  281.  //
  282.  // Public Methods
  283.  //
  284.  ////////////////////////////////////////////////////////////////////////////////
  285.  this.onUpdate    = refreshEverything;
  286.  this.lastPositionUsedForGPSQuery = null;
  287.  this.onLocationEventFired = function( currentPosition )
  288.  {
  289.     var bTimerReset = false;
  290.      try
  291.     {
  292.         var bReadyToUpdate = true;
  293.         if( self.lastPositionUsedForGPSQuery !== null )
  294.         {
  295.             bReadyToUpdate = ( DistanceMovedSinceLastPositionUpdate(self.lastPositionUsedForGPSQuery, currentPosition) > gMinimumDistance );
  296.         }
  297.  
  298.          if ( bReadyToUpdate )
  299.           // If new latlong event fired with location coordinates more than the 
  300.           // threshold distance since the last attempt to update using location coordinates, update using new coordinates recieved
  301.           // NOTE: This assumes that the location sensor is working ie REPORT_RUNNING state (4) since latlong is only recieved through events when reports are running
  302.         {
  303.             self.clearWeatherUpdateTimer();
  304.             bTimerReset = true;
  305.  
  306.             System.Debug.outputString("Updating weather to new location");
  307.             self.lastPositionUsedForGPSQuery = currentPosition;    // Always maintain current GPS position locally before calling getCurrentLocationCode()
  308.             self.getCurrentLocationCode ( currentPosition.Latitude, currentPosition.Longitude );
  309.         }
  310.      }
  311.     catch(err)
  312.     {
  313.         System.Debug.outputString("Error while processing location change event");
  314.         System.Debug.outputString( "Error Description: " + err.description + "");
  315.         if ( bTimerReset ) // Incase exception occurred after clearing the timer.. re-set it.. to prevent gadget from completely aborting periodic refresh
  316.         {
  317.             self.beginPeriodicRefresh();
  318.         }
  319.     }
  320.  }
  321.  
  322.  ////////////////////////////////////////////////////////////////////////////////
  323.  //
  324.  // getWeatherUpdate - location sensitive request of update from Weather Feed
  325.  // ignoreAPIStatusUpdate - indicates if status check before update should be skipped
  326.  ////////////////////////////////////////////////////////////////////////////////
  327.  this.getWeatherUpdate = function( ignoreAPIStatusUpdate ) 
  328.  {
  329.     if ( !ignoreAPIStatusUpdate )
  330.     {
  331.         self.APIStatusChanged( getAPIStatus( MicrosoftGadget ) ); // check api status - is it ready to provide location?
  332.     }
  333.  
  334.     if ( self.currentState == LOCATION_SENSING_STATE_OK_AUTO ) // gadget configured for location updates
  335.     {
  336.         try
  337.         {
  338.         var report = getAPILatLongReport( self );
  339.         System.Debug.outputString( "Current Geo-coordinates: (" + report.Latitude + "," + report.Longitude + ")");
  340.         self.lastPositionUsedForGPSQuery = report;    // Always maintain current GPS position locally before calling getCurrentLocationCode()
  341.         self.getCurrentLocationCode ( report.Latitude, report.Longitude );
  342.         return;
  343.         }
  344.         catch(err)
  345.         {
  346.         System.Debug.outputString( "Error getting geo-coordinates from Location API");
  347.         System.Debug.outputString( "Error Description: " + err.description + "");
  348.         self.initializeState ( LOCATION_SENSING_STATE_ERROR_AUTO , false );
  349.         }
  350.     }
  351.     self.requestUpdate();
  352.  }
  353.  
  354.  ////////////////////////////////////////////////////////////////////////////////
  355.  //
  356.  // getCurrentLocationCode - queries weather service to find location for geo-coordinates
  357.  //
  358.  ////////////////////////////////////////////////////////////////////////////////
  359.  this.getCurrentLocationCode = function( latitude , longitude ) 
  360.  {
  361.     var coordinates = latitude + ", " + longitude;
  362.  
  363.     this.oMSN2.OnDataReady = getLocationCode;
  364.  
  365.     self.statusMessage='Requesting location Update...';
  366.  
  367.     if ( self.spinner !== null ) 
  368.     {
  369.         self.spinner.start();
  370.     }
  371.  
  372.     showOrHideGettingLocationMessage( true );
  373.     self.oMSN2.SearchByLocation( coordinates );
  374.  }
  375.  
  376.  ////////////////////////////////////////////////////////////////////////////////
  377.  //
  378.  // requestUpdate - request update from Weather Feed.
  379.  //
  380.  ////////////////////////////////////////////////////////////////////////////////
  381.  this.requestUpdate = function() 
  382.  {
  383.     if ( IsWeatherDataFresh ( ) )
  384.     {
  385.         self.setWeatherUpdateTimer( g_functionToCall , gTimeToNextRefresh * 60 * 1000 );
  386.         return;
  387.     }
  388.  
  389.     self.statusMessage='Requesting Update...';
  390.  
  391.     if (self.spinner !== null) 
  392.     {
  393.         self.spinner.start();
  394.     }
  395.  
  396.     showOrHideGettingDataMessage( true ); 
  397.     self.oMSN.Celsius = false;
  398.     self.oMSN.OnDataReady = onDataReadyHandler;
  399.     self.oMSN.SearchByCode( self.weatherLocationCode );
  400.  }
  401.  ////////////////////////////////////////////////////////////////////////////////
  402.  //
  403.  // refreshSettings - populate values with stored settings 
  404.  //    and request update(s) of Weather data from Service
  405.  //
  406.  ////////////////////////////////////////////////////////////////////////////////
  407.  this.refreshSettings = function () 
  408.  {
  409.     var savedWeatherLocationCode = URLDecode(readSetting("WeatherLocationCode")) || gDefaultWeatherLocationCode;
  410.     if ( savedWeatherLocationCode != self.weatherLocationCode )
  411.     {
  412.         gTimeStampLastRefreshAvailable = false;
  413.     }
  414.  
  415.     self.statusMessage='RefreshSettings';
  416.     self.weatherLocation    = unescape(readSetting("WeatherLocation")) || gDefaultWeatherLocation;
  417.     self.weatherLocationCode = savedWeatherLocationCode;
  418.     self.displayDegreesIn    = readSetting("DisplayDegreesIn") || gDefaultDisplayDegreesIn;
  419.  
  420.     saveSetting("WeatherLocation", self.weatherLocation );
  421.     saveSetting("WeatherLocationCode", self.weatherLocationCode );
  422.     saveSetting("DisplayDegreesIn", self.displayDegreesIn );
  423.  
  424.     self.refreshState();
  425.  
  426.     this.updateRefreshInterval();
  427.     if (self.spinner !== null) 
  428.     {
  429.         self.spinner.hide();
  430.     }
  431.  
  432.     self.updateWeather( false );
  433.     MicrosoftGadget.onUpdate();
  434.  }
  435.  
  436.  ////////////////////////////////////////////////////////////////////////////////
  437.  //
  438.  // updateRefreshInterval - update MSN Weather service refresh frequency
  439.  //
  440.  ////////////////////////////////////////////////////////////////////////////////
  441.  this.updateRefreshInterval = function( )
  442.  {
  443.     // Compute frequency of refresh 
  444.     if (self.oMSN.RefreshInterval > 0 )
  445.     {
  446.         self.refreshInterval = self.oMSN.RefreshInterval;
  447.     }
  448.     else
  449.     {
  450.         self.refreshInterval = readSetting("RefreshInterval");
  451.     }
  452.     self.refreshInterval = ( self.refreshInterval || gDefaultRefreshInterval ) * 60 * 1000;
  453.  }
  454.  ////////////////////////////////////////////////////////////////////////////////
  455.  //
  456.  // updateWeather - update weather data and schedule periodic updates
  457.  //
  458.  ////////////////////////////////////////////////////////////////////////////////
  459.  this.updateWeather = function( bIgnoreAPIStatusCheck )
  460.  {
  461.     if ( g_locationAPIAvailable )
  462.     {
  463.         self.getWeatherUpdate( bIgnoreAPIStatusCheck );
  464.     }
  465.     else
  466.     {
  467.         self.requestUpdate();
  468.     }
  469.  
  470.     self.beginPeriodicRefresh();
  471.  }
  472.  
  473.  
  474.  
  475.  ////////////////////////////////////////////////////////////////////////////////
  476.  //
  477.  // beginPeriodicRefresh - begins periodic polling of weather service for updates
  478.  //
  479.  ////////////////////////////////////////////////////////////////////////////////
  480.  this.beginPeriodicRefresh = function() 
  481.  {
  482.     // Clear any pending refresh requests first
  483.     self.suspendPeriodicRefresh();    
  484.     self.endPollingForServiceExistence();
  485.     // Set up recurring requests for updates*    
  486.     self.setWeatherUpdateTimer( g_functionToCall , self.refreshInterval);
  487.     self.periodicRefreshIsRunning = true;
  488.  }
  489.  
  490.  ////////////////////////////////////////////////////////////////////////////////
  491.  //
  492.  // setWeatherUpdateTimer - sets timer to request weather update
  493.  // ( also clears any existing timers for the same )
  494.  ////////////////////////////////////////////////////////////////////////////////
  495.  this.setWeatherUpdateTimer = function( updateFunction, timeout )
  496.  {
  497.     this.clearWeatherUpdateTimer();
  498.     self.interval_RefreshTemperature = setTimeout( updateFunction, timeout ); 
  499.  }
  500.  
  501.  ////////////////////////////////////////////////////////////////////////////////
  502.  //
  503.  // clearWeatherUpdateTimer - clears currently set timer (if any) to request weather update
  504.  //
  505.  ////////////////////////////////////////////////////////////////////////////////
  506.  this.clearWeatherUpdateTimer = function( )
  507.  {
  508.     clearTimeout( self.interval_RefreshTemperature ); 
  509.  }
  510.  
  511.  ////////////////////////////////////////////////////////////////////////////////
  512.  //
  513.  // suspendPeriodicRefresh - cancels polling of weather service for updates
  514.  //
  515.  ////////////////////////////////////////////////////////////////////////////////
  516.  this.suspendPeriodicRefresh = function() 
  517.  {
  518.     this.clearWeatherUpdateTimer();
  519.     self.periodicRefreshIsRunning = false;
  520.  }
  521.  ////////////////////////////////////////////////////////////////////////////////
  522.  //
  523.  // beginPollingForServiceExistence - when network connectivity is lost, 
  524.  // begin special polling testing for it to come back.
  525.  //
  526.  ////////////////////////////////////////////////////////////////////////////////
  527.  this.beginPollingForServiceExistence = function() 
  528.  {
  529.     // Clear any pending refresh requests first
  530.     self.suspendPeriodicRefresh();    
  531.     self.endPollingForServiceExistence();
  532.  
  533.     // Remap the onDataReady Handler
  534.     self.oMSN.OnDataReady = isDataReadyHandler; 
  535.     self.pollingForServiceExistence = setInterval( "MicrosoftGadget.oMSN.SearchByCode('" + self.weatherLocationCode + "')", gDefaultPollingForServiceExistence * 60 * 1000);
  536.     self.pollingForServiceExistenceIsRunning = true;
  537.  }
  538.  ////////////////////////////////////////////////////////////////////////////////
  539.  //
  540.  // endPollingForServiceExistence - cancel special network connectivity polling
  541.  //
  542.  ////////////////////////////////////////////////////////////////////////////////
  543.  this.endPollingForServiceExistence = function() 
  544.  {
  545.     clearInterval( self.pollingForServiceExistence );
  546.     clearInterval( MicrosoftGadget.pollingForServiceExistence ); 
  547.     self.pollingForServiceExistenceIsRunning = false;
  548.  }
  549.  ////////////////////////////////////////////////////////////////////////////////
  550.  //
  551.  // weatherState() - Computes generalized state of weather [Sunny, Cloudy, etc.]
  552.  // for all SkyCodes. Determines what image is used to represent...
  553.  //
  554.  ////////////////////////////////////////////////////////////////////////////////
  555.  self.WeatherState = function() 
  556.  {
  557.     switch ( self.SkyCode ) 
  558.     {
  559.         case (26) : case (27) : case (28) :
  560.             theWeatherState = "cloudy";
  561.             break;
  562.         case (35) : case (39) : case (45) : case (46) : 
  563.             theWeatherState = "few-showers";
  564.             break;
  565.         case (19) : case (20) : case (21) : case (22) :
  566.             theWeatherState = "foggy";
  567.             break;
  568.         case (29) : case (30) : case (33) :
  569.             theWeatherState = "partly-cloudy";
  570.             break;
  571.         case (5) : case (13) : case (14) : case (15) : case (16) : case (18) : case (25) : case (41) : case (42) : case (43) : 
  572.             theWeatherState = "snow";
  573.             break;
  574.         case (1) : case (2) : case (3) : case (4) : case (37) : case (38) : case (47) : 
  575.             theWeatherState = "thunderstorm";
  576.             break;
  577.         case (31) : case (32) : case (34) : case (36) : case (44) :        // Note 44- "Data Not Available"
  578.             theWeatherState = "sun";
  579.             break;
  580.         case (23) : case (24) :
  581.             theWeatherState = "windy";
  582.             break;
  583.         case (9) : case (10) : case (11) : case (12) : case (40) :
  584.             theWeatherState = "Rainy";
  585.             break;
  586.         case (6) : case (7) : case (8) : case (17) : 
  587.             theWeatherState = "hail";
  588.             break;
  589.         default:
  590.             theWeatherState = "sun";
  591.             break;
  592.         }
  593.         return theWeatherState;
  594.     }
  595.  ////////////////////////////////////////////////////////////////////////////////
  596.  //
  597.  // isNight - boolean indicating whether its currently night *wherever*
  598.  //
  599.  ////////////////////////////////////////////////////////////////////////////////
  600.  this.isNight = function() 
  601.  {
  602.     var curTime = GMTTime(); 
  603.     // Before SunRise or after Sunset means it's Night
  604.     var deltaTime = self.SunSet - curTime;
  605.     deltaTime = deltaTime / (1000 * 60 * 60);
  606.     if (deltaTime > 24)
  607.         curTime = new Date(curTime.getTime() + 24 * 60 * 60 * 1000);
  608.     else if (deltaTime < 0)
  609.         curTime = new Date(curTime.getTime() - 24 * 60 * 60 * 1000);
  610.  
  611.     return ( ( curTime < self.SunRise ) || ( curTime > self.SunSet ) );
  612.  }
  613.  ////////////////////////////////////////////////////////////////////////////////
  614.  //
  615.  // makesSenseToDisplayTheMoon - boolean indicating whether it makes sense to 
  616.  //    display the moon. Dependant on weather state
  617.  //
  618.  ////////////////////////////////////////////////////////////////////////////////
  619.  this.makesSenseToDisplayTheMoon = function() 
  620.  {
  621.     var retVal = false;
  622.     if ( self.isNight() ) 
  623.     { 
  624.         var theWeatherState = self.WeatherState();
  625.         if ( ( theWeatherState=="sun" ) || ( theWeatherState=="partly-cloudy" ) ) 
  626.         {
  627.         retVal = true;
  628.         }
  629.     }
  630.     return retVal;
  631.  }
  632.  ////////////////////////////////////////////////////////////////////////////////
  633.  //
  634.  // self.backdrop - returns backdrop color required for active weather state
  635.  //
  636.  ////////////////////////////////////////////////////////////////////////////////
  637. self.backdrop = function() 
  638.  {
  639.     var theBackground = "BLUE";
  640.     var theDisplayMode = activeDisplayMode();
  641.     
  642.     switch ( self.SkyCode ) 
  643.     {
  644.         case (26) : case (27) : case (28) :
  645.         case (35) : case (39) : case (45) : case (46) : 
  646.         case (19) : case (20) : case (21) : case (22) :
  647.         case (1) : case (2) : case (3) : case (4) : case (5) : case (37) : case (38) : case (47) : 
  648.         case (9) : case (10) : case (11) : case (12) : case (40) : case (41) : case (42) : case (43) :
  649.         case (6) : case (7) : case (8) : case (17) : case (13) : case (14) : case (15) : case (16) : case (18) :
  650.         theBackground = "GRAY";
  651.         break;
  652.         case (29) : case (30) : case (33) : case (34) :
  653.         case (31) : case (32) : case (36) : case (44) :        
  654.         case (23) : case (24) : case (25) : default : 
  655.         theBackground = "BLUE";
  656.         break;
  657.     } 
  658.     
  659.     if (self.isNight()) 
  660.     { 
  661.         theBackground = "BLACK"; 
  662.     }
  663.     if ( !self.isValid )
  664.     { 
  665.         theBackground = "BLUE"; 
  666.     }
  667.     return theBackground;
  668. }
  669.  
  670.  
  671.  ////////////////////////////////////////////////////////////////////////////////
  672.  //
  673.  // HighTemp() - use tomorrow's High as an approximation 
  674.  //    if today's High cannot be returned by service
  675.  //
  676.  ////////////////////////////////////////////////////////////////////////////////
  677.  this.HighTemp = function() 
  678.  {
  679.     var theHighTemp = 0;
  680.     try 
  681.     {
  682.         theHighTemp = TemperatureInSelectedUnit( self.oMSNWeatherService.ForeCast(0).High );
  683.         if (theHighTemp == 0) 
  684.         {
  685.         theHighTemp = TemperatureInSelectedUnit( self.oMSNWeatherService.Forecast(1).High );
  686.         }
  687.     }
  688.     catch (objException) 
  689.     {
  690.     }
  691.     return theHighTemp;
  692.  }
  693.  
  694.  
  695.  ////////////////////////////////////////////////////////////////////////////////
  696.  //
  697.  // refreshState( )
  698.  //    - re-check and assign state on reload/load/settings finalized
  699.  //
  700.  ////////////////////////////////////////////////////////////////////////////////
  701.  this.refreshState = function()
  702.  {
  703.     if ( self.factory == null )
  704.     {
  705.         self.DisableLocationAware();
  706.         self.initializeState ( LOCATION_SENSING_STATE_DISABLED , false );
  707.     }
  708.     else
  709.     {
  710.         g_locationAPIAvailable = true;
  711.         saveSetting ( "LocationAPIAvailable" , g_locationAPIAvailable );
  712.  
  713.         try
  714.         {
  715.         self.factory.StopListeningForReports();
  716.         }
  717.         catch(err)
  718.         {
  719.         }
  720.  
  721.         g_functionToCall = "MicrosoftGadget.getWeatherUpdate( false )";
  722.  
  723.         self.currentState = readSetting("CurrentState") || LOCATION_SENSING_STATE_INVALID;
  724.         self.currentState = parseInt ( self.currentState );
  725.  
  726.         if ( !LOCATION_SENSING_VALID_STATES_ARRAY.exists( self.currentState ) ) // setting not saved yet
  727.         {
  728.         self.currentState = null;
  729.         }
  730.  
  731.         self.APIStatusChanged( getAPIStatus( MicrosoftGadget ) );
  732.  
  733.         try // try hooking up status changed event
  734.         {
  735.         self.factory.ListenForReports(0);
  736.         }
  737.         catch(err)
  738.         {
  739.         System.Debug.outputString( "Error hooking up status changed event.");
  740.         System.Debug.outputString( "Error number: " + err.number + "");
  741.         System.Debug.outputString( "Error Description: " + err.description + "");
  742.         }
  743.  
  744.     }
  745.  }
  746.  
  747.  ////////////////////////////////////////////////////////////////////////////////
  748.  //
  749.  // DisableLocationAware( )
  750.  //    - disable gadget's location aware feature
  751.  //
  752.  ////////////////////////////////////////////////////////////////////////////////
  753.  this.DisableLocationAware = function( )
  754.  {
  755.     self.initializeState( LOCATION_SENSING_STATE_DISABLED , false );
  756.     g_locationAPIAvailable = false;
  757.     g_functionToCall = "MicrosoftGadget.requestUpdate()";
  758.     saveSetting ( "LocationAPIAvailable" , g_locationAPIAvailable );
  759.  }
  760.  
  761.  ////////////////////////////////////////////////////////////////////////////////
  762.  //
  763.  // APIStatusChanged( status ) - triggered when status of location api
  764.  //    has changed. This updates gadget state and UI to reflect the new status
  765.  ////////////////////////////////////////////////////////////////////////////////
  766.  this.APIStatusChanged = function( status )
  767.  {
  768.     var newState = this.currentState;
  769.     var baseVal;
  770.  
  771.     if ( status == 3 )
  772.     {
  773.         return;
  774.     }
  775.     else if ( status == 0 )
  776.     {
  777.         baseVal = LOCATION_SENSING_STATE_DISCONNECTED_AUTO;
  778.     }
  779.     else if ( status == 1 || status == 2 )
  780.     {
  781.         baseVal = LOCATION_SENSING_STATE_ERROR_AUTO;
  782.     }
  783.     else if ( status == 4 )
  784.     {
  785.         try
  786.         {
  787.         var report = getAPILatLongReport( this );
  788.         baseVal = LOCATION_SENSING_STATE_OK_AUTO;
  789.         }
  790.         catch(err)
  791.         {
  792.         System.Debug.outputString( "Error getting geo-coordinates from Location API");
  793.         System.Debug.outputString( "Error Description: " + err.description + "");
  794.         baseVal = LOCATION_SENSING_STATE_ERROR_AUTO;
  795.         }
  796.     }
  797.     else
  798.     {
  799.         baseVal = LOCATION_SENSING_STATE_ERROR_AUTO;
  800.     }
  801.  
  802.  
  803.     if ( this.currentState == null ) // gadget state not initialized yet
  804.     {
  805.         if ( baseVal == LOCATION_SENSING_STATE_DISCONNECTED_AUTO )
  806.         {
  807.         newState = LOCATION_SENSING_STATE_DISCONNECTED_MANUAL;
  808.         }
  809.         else
  810.         {
  811.         newState = baseVal;
  812.         }
  813.     }
  814.     else
  815.     {
  816.         if ( LOCATION_SENSING_MANUAL_STATES_ARRAY.exists( this.currentState ) ) // State with Automatic Updates not enabled
  817.         {
  818.         switch( baseVal )
  819.         {
  820.             case LOCATION_SENSING_STATE_ERROR_AUTO:
  821.             newState = LOCATION_SENSING_STATE_ERROR_MANUAL;
  822.             break;
  823.  
  824.             case LOCATION_SENSING_STATE_OK_AUTO:
  825.             newState = LOCATION_SENSING_STATE_OK_MANUAL;
  826.             break;
  827.  
  828.             case LOCATION_SENSING_STATE_DISCONNECTED_AUTO:
  829.             newState = LOCATION_SENSING_STATE_DISCONNECTED_MANUAL;
  830.             break;
  831.         }
  832.         }
  833.         else
  834.         {
  835.         newState = baseVal;
  836.         }
  837.     }
  838.  
  839.     self.initializeState ( newState , false );
  840.  
  841.     if ( this.currentState == LOCATION_SENSING_STATE_OK_AUTO )
  842.     {
  843.         self.updateWeather( true );
  844.     }
  845.  }
  846.  
  847.  //////////////////////////////////////////////////////////////////////////////////////////////
  848.  //
  849.  // initializeState( stateNum , alertStatus ) - set gadget current state to one of 4 location aware states
  850.  //    (default to 2)
  851.  //
  852.  //////////////////////////////////////////////////////////////////////////////////////////////
  853.  this.initializeState = function( stateNum , alertStatus )
  854.  {
  855.     var sensorIconElementDocked = document.getElementById("SensorIconDockedMode");
  856.     var sensorIconElementUndocked = document.getElementById("SensorIconUndockedMode");
  857.     var imageURL = "";
  858.     var hoverMessage = "";
  859.     
  860.     if (( stateNum == undefined ) || ( stateNum == null ) || !LOCATION_SENSING_VALID_STATES_ARRAY.exists( stateNum ) )
  861.     {
  862.         stateNum = LOCATION_SENSING_STATE_ERROR_MANUAL;
  863.         System.Debug.outputString("Unable to determine state. Location Awareness may not work correctly when enabled.");
  864.     }
  865.     stateNum = parseInt( stateNum );
  866.  
  867.     this.currentState = stateNum;
  868.  
  869.     saveSetting ( "CurrentState" , stateNum );
  870.     
  871.     switch( stateNum )
  872.     {
  873.         case LOCATION_SENSING_STATE_ERROR_AUTO:
  874.         imageURL = "images\\" + dpiImageFolderPrefix + "redStateIcon.png";
  875.         hoverMessage = getLocalizedString('SensorIconRed');
  876.         break;
  877.         
  878.         case LOCATION_SENSING_STATE_OK_AUTO:
  879.         if ( alertStatus )
  880.         {
  881.             imageURL = "images\\" + dpiImageFolderPrefix + "alertIcon.png";
  882.             hoverMessage = getLocalizedString('EC4');
  883.         }
  884.         else
  885.         {
  886.             imageURL = "images\\" + dpiImageFolderPrefix + "greenStateIcon.png";
  887.             hoverMessage = getLocalizedString('SensorIconGreen');
  888.         }
  889.         break;
  890.         
  891.         case LOCATION_SENSING_STATE_OK_MANUAL:
  892.         imageURL = "images\\" + dpiImageFolderPrefix + "grayStateIcon.png";
  893.         hoverMessage = getLocalizedString('SensorIconGray');
  894.         break;
  895.  
  896.         case LOCATION_SENSING_STATE_DISCONNECTED_AUTO:
  897.         imageURL = "images\\" + dpiImageFolderPrefix + "notConnectedStateIcon.png";
  898.         hoverMessage = getLocalizedString('SensorIconNotConnected');
  899.         break;
  900.  
  901.         default:
  902.         sensorIconElementDocked.style.visibility = "hidden";
  903.         sensorIconElementUndocked.style.visibility = "hidden";
  904.  
  905.         document.getElementById('PlaceUnDockedMode').style.width = document.getElementById('PlaceHrefUnDockedMode').style.width = '222px';
  906.         document.getElementById('PlaceDockedMode').style.width = document.getElementById('PlaceHrefDockedMode').style.width = '116px';
  907.         if ( document.dir=='rtl')
  908.         {
  909.             document.getElementById('PlaceUnDockedMode').style.marginRight = '26px';
  910.         }
  911.  
  912.         return;
  913.         break;
  914.     }
  915.  
  916.     sensorIconElementDocked.alt = hoverMessage;
  917.     sensorIconElementUndocked.alt = hoverMessage;
  918.  
  919.     sensorIconElementDocked.style.visibility = "visible";
  920.     sensorIconElementUndocked.style.visibility = "visible";
  921.  
  922.     setImage(sensorIconElementDocked, imageURL);
  923.     setImage(sensorIconElementUndocked, imageURL);
  924.  
  925.     document.getElementById('PlaceUnDockedMode').style.width = document.getElementById('PlaceHrefUnDockedMode').style.width = '210px';
  926.     document.getElementById('PlaceDockedMode').style.width = document.getElementById('PlaceHrefDockedMode').style.width = '104px';
  927.     if ( document.dir=='rtl')
  928.     {
  929.         document.getElementById('PlaceUnDockedMode').style.marginRight='38px';
  930.     }
  931.  }
  932.  
  933.  
  934.  ////////////////////////////////////////////////////////////////////////////////
  935.  //
  936.  // Private Methods
  937.  //
  938.  ////////////////////////////////////////////////////////////////////////////////
  939.     
  940.  ////////////////////////////////////////////////////////////////////////////////
  941.  //
  942.  // getLocationCode(object data) - asynchronous callback to get location
  943.  //    for current geo-coordinates
  944.  //
  945.  ////////////////////////////////////////////////////////////////////////////////
  946.  function getLocationCode(data) 
  947.  {
  948.     if ( data.RequestPending )
  949.     {
  950.         return;
  951.     }
  952.     
  953.     if (self.spinner !== null) 
  954.     {        
  955.         self.spinner.stop();
  956.     }
  957.  
  958.     showOrHideGettingLocationMessage( false );    
  959.  
  960.     if (data!==undefined) 
  961.     {
  962.         self.statusMessage='Location code Received.';
  963.  
  964.         if (data.Count > 0 && data.item(0)) // Service returned non-zero items
  965.         {
  966.         self.initializeState ( LOCATION_SENSING_STATE_OK_AUTO , false );
  967.  
  968.         if ( data.item(0).LocationCode != self.weatherLocationCode )
  969.         {
  970.             gTimeStampLastRefreshAvailable = false;
  971.         }
  972.         self.weatherLocation    = data.item(0).Location;
  973.         self.weatherLocationCode = data.item(0).LocationCode;
  974.         saveSetting ( "WeatherLocation" , self.weatherLocation );
  975.         saveSetting ( "WeatherLocationCode" , self.weatherLocationCode );
  976.         }
  977.         else // Service returned 0 items
  978.         {
  979.         if ( data.RetCode == 200 ) 
  980.         {
  981.             self.initializeState ( LOCATION_SENSING_STATE_OK_AUTO , true );
  982.         }
  983.         else if ( data.RetCode == 1506 )
  984.         {
  985.             showOrHideServiceError( true, self.status );
  986.             self.suspendPeriodicRefresh();
  987.             self.beginPollingForServiceExistence();
  988.             setDisplayMode();
  989.             return;
  990.         }
  991.         // else, just continue to fetch data for last location
  992.         }
  993.         
  994.     }
  995.  
  996.  
  997.     self.requestUpdate();
  998.  
  999.  }
  1000.  
  1001.  ////////////////////////////////////////////////////////////////////////////////
  1002.  //
  1003.  // processDataAndStatus( data ) - processes data returned by Weather Feed
  1004.  //
  1005.  ////////////////////////////////////////////////////////////////////////////////
  1006.  function processDataAndStatus( data ) 
  1007.  {
  1008.     MicrosoftGadget.isStaleData = false;
  1009.     MicrosoftGadget.dataExpired = false;
  1010.     MicrosoftGadget.ageStampText = "";
  1011.     MicrosoftGadget.SkyText = "";
  1012.     MicrosoftGadget.oMSNWeatherServiceDataAge = calculateAge( new Date() );
  1013.     
  1014.     if ( data !== undefined ) 
  1015.     { 
  1016.         MicrosoftGadget.statusMessage = 'Update Received.';
  1017.         MicrosoftGadget.status = data.RetCode;
  1018.  
  1019.         MicrosoftGadget.oMSNWeatherServiceTimeStamp = data.Timestamp;
  1020.  
  1021.         MicrosoftGadget.oMSNWeatherServiceDataAge = calculateAge( MicrosoftGadget.oMSNWeatherServiceTimeStamp );
  1022.         MicrosoftGadget.dataExpired = MicrosoftGadget.oMSNWeatherServiceDataAge.dataExpired;
  1023.         MicrosoftGadget.ageStampText = calculateAgeStampText( MicrosoftGadget.oMSNWeatherServiceDataAge );
  1024.  
  1025.         if( MicrosoftGadget.status == 1507 )
  1026.         {
  1027.         MicrosoftGadget.isStaleData = true;
  1028.         }
  1029.         else
  1030.         {
  1031.         MicrosoftGadget.isStaleData = false;
  1032.         }
  1033.  
  1034.         if ( data.Count > 0 && data.item(0) ) 
  1035.         {
  1036.         MicrosoftGadget.oMSNWeatherService = data.item(0);
  1037.  
  1038.         if ( !gTimeStampLastRefreshAvailable ) // First time pull of data (on load / on location change)
  1039.         {
  1040.             MicrosoftGadget.weatherLocation = MicrosoftGadget.oMSNWeatherService.Location;
  1041.             saveSetting("WeatherLocation", MicrosoftGadget.weatherLocation );
  1042.         }
  1043.  
  1044.         var gmt = GMTTime();
  1045.  
  1046.         // Compute SunRise/SunSet times based on latitude/longitude returned by the feed for location
  1047.         var theSunRiseSunset = computeSunRiseSunSet( MicrosoftGadget.oMSNWeatherService.Latitude, MicrosoftGadget.oMSNWeatherService.Longitude, 0, gmt.getFullYear(), gmt.getMonth()+1, gmt.getDate()); // Note - using GMT (no TimeZone offset)
  1048.         MicrosoftGadget.SunRise = theSunRiseSunset.SunRise;
  1049.         MicrosoftGadget.SunSet = theSunRiseSunset.SunSet;
  1050.         
  1051.         MicrosoftGadget.MoonState = function() 
  1052.         { 
  1053.             return computePhaseOfMoon(new Date().getFullYear(), new Date().getMonth()+1, new Date().getDate()); 
  1054.         }
  1055.         
  1056.         MicrosoftGadget.SkyCode = MicrosoftGadget.oMSNWeatherService.SkyCode;
  1057.         MicrosoftGadget.SkyText = MicrosoftGadget.oMSNWeatherService.SkyText;
  1058.         if ( !MicrosoftGadget.dataExpired && MicrosoftGadget.isStaleData && MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge > 0 && MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge <= 4 )
  1059.         {
  1060.             MicrosoftGadget.SkyCode = MicrosoftGadget.oMSNWeatherService.Forecast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge).SkyCode;
  1061.             MicrosoftGadget.SkyText = MicrosoftGadget.oMSNWeatherService.Forecast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge).SkyText;
  1062.         }
  1063.  
  1064.         gTimeStampLastRefreshAvailable = true;
  1065.         }
  1066.         else
  1067.         {
  1068.         // In the case of "No Content", service will return 200 "success", but no data
  1069.         if (MicrosoftGadget.status==200) 
  1070.         {
  1071.             // Actual HTTP Error code for "No Content"
  1072.             MicrosoftGadget.status = 204;
  1073.         }
  1074.         }
  1075.  
  1076.         // Gadget is valid if we have a 200 / 1507 retVal
  1077.         MicrosoftGadget.isValid = ( MicrosoftGadget.status == 200 || MicrosoftGadget.status == 1507) && !MicrosoftGadget.dataExpired; 
  1078.  
  1079.     }
  1080.     document.getElementById('AgeStampTextUndockedMode').innerText = MicrosoftGadget.ageStampText;
  1081.     if ( MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge > 0 )
  1082.     {
  1083.         document.getElementById('AgeStampTextDockedMode').innerText = getLocalizedString( 'Forecasted' );
  1084.         document.getElementById('AgeStampTextDockedMode').title = MicrosoftGadget.ageStampText;
  1085.     }
  1086.     else
  1087.     {
  1088.         document.getElementById('AgeStampTextDockedMode').innerText = MicrosoftGadget.ageStampText;
  1089.         document.getElementById('AgeStampTextDockedMode').title = "";
  1090.     }
  1091.  }
  1092.  ////////////////////////////////////////////////////////////////////////////////
  1093.  //
  1094.  // onDataReadyHandler( data ) - processes data returned by Weather Feed
  1095.  //    (asynchronous callback) 
  1096.  //
  1097.  ////////////////////////////////////////////////////////////////////////////////
  1098.  function onDataReadyHandler( data ) 
  1099.  {
  1100.     if ( data.RequestPending )
  1101.     {
  1102.         return;
  1103.     }
  1104.  
  1105.     processDataAndStatus( data );
  1106.  
  1107.     if (self.spinner !== null) 
  1108.     {        
  1109.         self.spinner.stop();
  1110.     }
  1111.  
  1112.     showOrHideGettingDataMessage( false );    
  1113.  
  1114.     if (self.isValid)
  1115.     {
  1116.         if ( self.isStaleData )
  1117.         {
  1118.         self.onUpdate();
  1119.         self.suspendPeriodicRefresh();
  1120.         self.beginPollingForServiceExistence();
  1121.         }
  1122.         else
  1123.         {
  1124.         self.onUpdate();
  1125.         }
  1126.     } 
  1127.     else 
  1128.     {
  1129.         // When we get in this state, begin a special polling for the service coming
  1130.         // back online or otherwise correcting itself
  1131.         showOrHideServiceError( true, self.status );
  1132.         self.suspendPeriodicRefresh();
  1133.         // Only poll for Service Existence if it's available in the market
  1134.         if ( self.status != 1506 )
  1135.         {
  1136.         self.beginPollingForServiceExistence();
  1137.         }
  1138.         setDisplayMode();
  1139.     }
  1140.  
  1141.     if ( !self.pollingForServiceExistenceIsRunning )
  1142.     {
  1143.         self.updateRefreshInterval();
  1144.         self.setWeatherUpdateTimer( g_functionToCall , self.refreshInterval);        
  1145.     }
  1146.  }
  1147.  ////////////////////////////////////////////////////////////////////////////////
  1148.  //
  1149.  // isDataReadyHandler( data ) - special handler for use when the service is 
  1150.  //    down or otherwise unresponsive
  1151.  //
  1152.  ////////////////////////////////////////////////////////////////////////////////
  1153.  function isDataReadyHandler( data ) 
  1154.  {
  1155.     processDataAndStatus( data );
  1156.  
  1157.     if ( MicrosoftGadget.isValid )
  1158.     {
  1159.         if ( MicrosoftGadget.isStaleData )
  1160.         {
  1161.         MicrosoftGadget.onUpdate();
  1162.         }
  1163.         else
  1164.         {
  1165.         if ( !data.RequestPending )
  1166.         {
  1167.             self.endPollingForServiceExistence();
  1168.             if ( g_locationAPIAvailable )
  1169.             {
  1170.             self.getWeatherUpdate( false );
  1171.             }
  1172.             else
  1173.             {
  1174.             self.requestUpdate();
  1175.             }
  1176.             self.beginPeriodicRefresh();
  1177.         }
  1178.         MicrosoftGadget.onUpdate();
  1179.         }
  1180.     }
  1181.     else
  1182.     {
  1183.         showOrHideServiceError( true, self.status );
  1184.         if ( self.status == 1506 )
  1185.         {
  1186.         MicrosoftGadget.endPollingForServiceExistence();
  1187.         }
  1188.         setDisplayMode();
  1189.     }
  1190.  }
  1191.  ////////////////////////////////////////////////////////////////////////////////
  1192.  //
  1193.  // computeSunRiseSunSet(Latitude, Longitude, TimeZone, Year, Month, Day)
  1194.  //    Computes SunRise/SunSet based on Latitude/Longitude
  1195.  //
  1196.  ////////////////////////////////////////////////////////////////////////////////
  1197.  function computeSunRiseSunSet(Latitude, Longitude, TimeZone, Year, Month, Day) 
  1198.  {
  1199.     // Variable names used: B5, C, C2, C3, CD, D, DR, H, HR, HS, L0, L5, M, MR, MS, N, PI, R1, RD, S1, SC, SD, str
  1200.     var retVal = new Object();
  1201.     var str = "";
  1202.     var PI=Math.PI;
  1203.     var DR=PI/180;
  1204.     var RD=1/DR;
  1205.     var B5=Latitude;
  1206.     var L5=Longitude;
  1207.     var H =TimeZone;
  1208.     // Overriding TimeZone to standardize on UTC
  1209.     H = 0; 
  1210.     var M =Month;
  1211.     var D =Day;
  1212.     B5=DR*B5;
  1213.     var N=parseInt(275*M/9)-2*parseInt((M+9)/12)+D-30;
  1214.     var L0=4.8771+.0172*(N+.5-L5/360);
  1215.     var C=.03342*Math.sin(L0+1.345);
  1216.     var C2=RD*(Math.atan(Math.tan(L0+C)) - Math.atan(.9175*Math.tan(L0+C))-C);
  1217.     var SD=.3978*Math.sin(L0+C);
  1218.     var CD=Math.sqrt(1-SD*SD);
  1219.     var SC=(SD * Math.sin(B5) + .0145) / (Math.cos(B5) * CD);
  1220.     if (Math.abs(SC)<=1) 
  1221.     {
  1222.         var C3=RD*Math.atan(SC/Math.sqrt(1-SC*SC));
  1223.         var R1=6-H-(L5+C2+C3)/15;
  1224.         var HR=parseInt(R1);
  1225.         var MR=parseInt((R1-HR)*60);
  1226.         str = "Sunrise at " + HR + ":" + MR;
  1227.         retVal.SunRise = parseTime(HR + ":" + MR);
  1228.         var S1=18-H-(L5+C2-C3)/15;
  1229.         var HS=parseInt(S1);
  1230.         var MS=parseInt((S1-HS)*60);
  1231.         retVal.SunSet = parseTime(HS + ":" + MS);
  1232.         str += "\nSunset at " + HS + ":" + MS;
  1233.     } 
  1234.     else 
  1235.     {
  1236.         if (SC>1) 
  1237.         { 
  1238.         str="Sun up all day"; 
  1239.         var tDate = new Date(); 
  1240.         // Set Sunset to be in the future ...
  1241.         retVal.SunSet = new Date( tDate.getFullYear()+1, tDate.getMonth(), tDate.getDay(), tDate.getHours() );
  1242.         // Set Sunrise to be in the past ...
  1243.         retVal.SunRise = new Date( tDate.getFullYear()-1, tDate.getMonth(), tDate.getDay(), tDate.getHours()-1 );
  1244.         }
  1245.         if (SC<-1) 
  1246.         { 
  1247.         str="Sun down all day"; 
  1248.         // Set Sunrise and Sunset to be in the future ...
  1249.         retVal.SunRise = new Date( tDate.getFullYear()+1, tDate.getMonth(), tDate.getDay(), tDate.getHours() );
  1250.         retVal.SunSet = new Date( tDate.getFullYear()+1, tDate.getMonth(), tDate.getDay(), tDate.getHours() );
  1251.         }
  1252.     }
  1253.     retVal.str = str;
  1254.     return retVal;
  1255.  }
  1256.  
  1257.  ////////////////////////////////////////////////////////////////////////////////
  1258.  //
  1259.  // computePhaseOfMoon(Year, Month, Day) - Computes Phase of Moon based on Date
  1260.  //
  1261.  ////////////////////////////////////////////////////////////////////////////////
  1262.  function computePhaseOfMoon(Year, Month, Day) 
  1263.  {
  1264.     // Variable names used: J, K1, K2, K3, MM, P2, V, YY
  1265.     var P2 = 3.14159 * 2;    
  1266.     var YY = Year - parseInt((12 - Month)/10);
  1267.     var MM = Month + 9;
  1268.     if (MM >= 12) { MM = MM-12; }
  1269.     var K1 = parseInt(365.25 * (YY+4712));
  1270.     var K2 = parseInt(30.6 * MM + .5);
  1271.     var K3 = parseInt(parseInt((YY/100) + 49) * .75) - 38;
  1272.     // J is the Julian date at 12h UT on day in question
  1273.     var J = K1+K2+Day+59;            
  1274.     // Adjust for Gregorian calendar, if applicable 
  1275.     if (J > 2299160) { J = J-K3; }    
  1276.     // Calculate illumination (synodic) phase
  1277.     var V = (J - 2451550.1)/29.530588853;
  1278.     V = V - parseInt(V);
  1279.     // Normalize values to range from 0 to 1
  1280.     if (V<0) { V=V+1; }
  1281.     // Moon's age in days from New Moon
  1282.     var AG = V*29.53;    
  1283.  
  1284.     switch (true) 
  1285.     { 
  1286.         // Each phase lasts approximately 3.28 days
  1287.         case ((AG > 27.6849270496875) || (AG <= 1.8456618033125)) :
  1288.         var retVal = 'New';
  1289.         break;
  1290.         case ((AG > 1.8456618033125) && (AG <= 5.5369854099375)) :
  1291.         var retVal = 'Waxing-Crescent';
  1292.         break;
  1293.         case ((AG > 5.5369854099375) && (AG <= 9.2283090165625)) :
  1294.         var retVal = 'First-Quarter';
  1295.         break;
  1296.         case ((AG > 9.2283090165625) && (AG <= 12.9196326231875)) : 
  1297.         var retVal = 'Waxing-Gibbous';
  1298.         break;
  1299.         case ((AG > 12.9196326231875) && (AG <= 16.6109562298125)) :
  1300.         var retVal = 'Full';
  1301.         break;
  1302.         case ((AG > 16.6109562298125) && (AG <= 20.3022798364375)) :
  1303.         var retVal = 'Waning-Gibbous';
  1304.         break;
  1305.         case ((AG > 20.3022798364375) && (AG <= 23.9936034430625)) :
  1306.         var retVal = 'Last-Quarter';
  1307.         break;
  1308.         case ((AG > 23.9936034430625) && (AG <= 27.6849270496875)) :
  1309.         var retVal = 'Waning-Crescent';
  1310.         break;
  1311.         default : 
  1312.         var retVal = 'Full';
  1313.         break;
  1314.     }    
  1315.     return retVal;
  1316.  }
  1317.  
  1318.  ////////////////////////////////////////////////////////////////////////////////
  1319.  //
  1320.  // SGN(aNumber) - Returns an integer indicating the sign of a number
  1321.  //
  1322.  ////////////////////////////////////////////////////////////////////////////////
  1323.  function SGN(aNumber) {
  1324.     if (aNumber===undefined) { aNumber = 0; } 
  1325.     var theNumber = parseFloat(aNumber);
  1326.     retVal = 0;
  1327.     if ( theNumber != 0 )
  1328.     {
  1329.         if (theNumber>0)
  1330.         {
  1331.         retVal = 1;
  1332.         }
  1333.         else
  1334.         {
  1335.         retVal = -1;
  1336.         }
  1337.     }
  1338.     return retVal;
  1339.  }
  1340.  ////////////////////////////////////////////////////////////////////////////////
  1341.  //
  1342.  // parseTime(string aTime) - takes a string of time in the format HH:MM:SS 
  1343.  //    and returns Javascript Date Object 
  1344.  //
  1345.  ////////////////////////////////////////////////////////////////////////////////
  1346.  function parseTime(aTime) 
  1347.  {
  1348.     var aDateTimeObject = 'none';
  1349.     if (aTime!==undefined && aTime.length) 
  1350.     {
  1351.         aDateTimeObject = GMTTime();
  1352.         try 
  1353.         {
  1354.         var theHour    = parseInt(aTime.split(':')[0]);
  1355.         var theMinutes = parseInt(aTime.split(':')[1]);
  1356.         aDateTimeObject.setHours(theHour);
  1357.         aDateTimeObject.setMinutes(theMinutes);
  1358.         }
  1359.         catch (ex) 
  1360.         {
  1361.         }
  1362.     }
  1363.     return aDateTimeObject;
  1364.  }
  1365.  ////////////////////////////////////////////////////////////////////////////////
  1366.  //
  1367.  // GMTTime() - returns time adjusted to GMT (Universal Time)
  1368.  //
  1369.  ////////////////////////////////////////////////////////////////////////////////
  1370.  function GMTTime() 
  1371.  { 
  1372.     var aDate = new Date();
  1373.     var aDateAdjustedToGMTInMS = aDate.getTime() + (aDate.getTimezoneOffset() * 60 * 1000);
  1374.     return ( new Date( aDateAdjustedToGMTInMS ) );
  1375.  } 
  1376. }
  1377.  
  1378. // *** END WeatherGadget Object Constructor ***
  1379.  
  1380. ////////////////////////////////////////////////////////////////////////////////
  1381. //
  1382. // Routines for Refreshing Display
  1383. //
  1384. ////////////////////////////////////////////////////////////////////////////////
  1385.  
  1386. function WeatherStateToDisplay(oWeatherGadget)
  1387. {
  1388.     var theState = "";
  1389.     if (oWeatherGadget.makesSenseToDisplayTheMoon())
  1390.     {
  1391.         theState = 'moon-' + oWeatherGadget.MoonState();
  1392.  
  1393.         // if we are displaying the moon, the only weather patterns
  1394.         // are "sun" or "partly-cloudy". we don't show the sun and the moon
  1395.         // at the same time (we don't support an eclipse), but we do show
  1396.         // light clouds over the moon.
  1397.         if (oWeatherGadget.WeatherState() == 'partly-cloudy')
  1398.         {
  1399.             theState = theState + '_partly-cloudy';
  1400.         }
  1401.     }
  1402.     else
  1403.     {
  1404.         theState = oWeatherGadget.WeatherState();
  1405.     }
  1406.     return theState;
  1407. }
  1408.  
  1409. function GetBackgroundImageFileName(oWeatherGadget)
  1410. {  
  1411.     var path = 'url(images/' + activeDisplayMode();
  1412.     if (oWeatherGadget.SkyCode != 44)
  1413.     {
  1414.         path = path + '_' + oWeatherGadget.backdrop() + '_' + WeatherStateToDisplay(oWeatherGadget) + '.png)';
  1415.     }
  1416.     else
  1417.     {
  1418.         path = path + '-loading.png)';
  1419.     }
  1420.     return path;
  1421. }
  1422.  
  1423.  
  1424. ////////////////////////////////////////////////////////////////////////////////
  1425. //
  1426. // setDisplayMode() - resets gadget size and background based on current host
  1427. //
  1428. ////////////////////////////////////////////////////////////////////////////////
  1429. function setDisplayMode( ) 
  1430. {
  1431.  showOrHide('DockedModeDisplayArea',false);
  1432.  showOrHide('UnDockedModeDisplayArea',false);
  1433.  
  1434.  var theWidth = gDisplaySizeUnDocked.width;
  1435.  var theHeight = gDisplaySizeUnDocked.height;
  1436.  var theActiveDisplayArea = 'DisplayArea' + gDefaultDisplayMode;
  1437.  
  1438.  switch ( activeDisplayMode() ) 
  1439.  {
  1440.     case "undocked" :
  1441.         theActiveDisplayArea = 'UnDockedModeDisplayArea';
  1442.         theWidth = gDisplaySizeUnDocked.width;
  1443.         theHeight = gDisplaySizeUnDocked.height;
  1444.         document.getElementById('PlaceHrefUnDockedMode').tabIndex = 1;
  1445.         document.getElementById('DayOfWeek1').tabIndex = 5;
  1446.         document.getElementById('DayOfWeek2').tabIndex = 5;
  1447.         document.getElementById('DayOfWeek3').tabIndex = 5;
  1448.         document.getElementById('UnDockedModeDisplayArea').className = MicrosoftGadget.backdrop(); 
  1449.         refreshUnDockedModeValues( MicrosoftGadget );
  1450.         if(MicrosoftGadget.isStaleData)
  1451.         {
  1452.         document.getElementById("AgeStampTextUndockedMode").style.visibility = "visible";
  1453.         }
  1454.         else
  1455.         {
  1456.         document.getElementById("AgeStampTextUndockedMode").style.visibility = "hidden";
  1457.         }
  1458.         break;
  1459.  
  1460.     case "docked" : default : 
  1461.         theActiveDisplayArea = 'DockedModeDisplayArea';
  1462.         theWidth = gDisplaySizeDocked.width;
  1463.         theHeight = gDisplaySizeDocked.height;
  1464.         document.getElementById('PlaceHrefDockedMode').tabIndex = 1;
  1465.         document.getElementById('DockedModeDisplayArea').className = MicrosoftGadget.backdrop(); 
  1466.         refreshDockedModeValues( MicrosoftGadget );
  1467.         if(MicrosoftGadget.isStaleData)
  1468.         {
  1469.         document.getElementById("AgeStampTextDockedMode").style.visibility = "visible";
  1470.         }
  1471.         else
  1472.         {
  1473.         document.getElementById("AgeStampTextDockedMode").style.visibility = "hidden";
  1474.         }
  1475.         break;
  1476.  }
  1477.  
  1478.  document.body.style.width = theWidth;
  1479.  document.body.style.height = theHeight;
  1480.  
  1481.  // Only show the data layers if we have data
  1482.  showOrHide(theActiveDisplayArea, MicrosoftGadget.isValid); 
  1483.  
  1484.  if (!MicrosoftGadget.isValid) 
  1485.  {
  1486.     showOrHide('WeatherMessage', true);
  1487.  }
  1488.  else
  1489.  {
  1490.      setBackground( GetBackgroundImageFileName(MicrosoftGadget));
  1491.  }
  1492. }
  1493.  
  1494.  
  1495.  
  1496. ////////////////////////////////////////////////////////////////////////////////
  1497. //
  1498. // refreshEverything() - update display for all modes with active data
  1499. //
  1500. ////////////////////////////////////////////////////////////////////////////////
  1501. function refreshEverything() 
  1502.  refreshDockedModeValues( this );
  1503.  refreshUnDockedModeValues( this );
  1504.  setDisplayMode();
  1505. }
  1506. ////////////////////////////////////////////////////////////////////////////////
  1507. //
  1508. // refreshDockedModeValues() - Refreshes display of Docked window
  1509. //
  1510. ////////////////////////////////////////////////////////////////////////////////
  1511. function refreshDockedModeValues( oWeatherGadget ) 
  1512. {
  1513.  if ( !oWeatherGadget.isValid )
  1514.  { 
  1515.     // Since the weather service can become invalidated at any time (not necessarily
  1516.     // onRefreshSettings), we must manually set isValid to false if the service 
  1517.     // becomes undefined
  1518.     showOrHideServiceError(true, oWeatherGadget.status);
  1519.     return; 
  1520.  }
  1521.  if ( oWeatherGadget.oMSNWeatherService===undefined )
  1522.  { 
  1523.     // NO data to display.
  1524.     return; 
  1525.  }
  1526.  
  1527.  showOrHideServiceError(false);
  1528.  document.getElementById('PlaceHrefDockedMode').href = cleanURL( oWeatherGadget.oMSNWeatherService.Url );
  1529.  document.getElementById('PlaceHrefDockedMode').innerText = oWeatherGadget.oMSNWeatherService.Location;
  1530.  
  1531.  var theAltText;
  1532.  if ( MicrosoftGadget.isStaleData )
  1533.  {
  1534.     theAltText = getLocalizedString( 'ServiceNotAvailable' );
  1535.  }
  1536.  else
  1537.  {
  1538.     theAltText = oWeatherGadget.SkyText;    
  1539.  }
  1540.  
  1541.  // only need to update the background image (which is shared) if we are docked
  1542.  if (activeDisplayMode() == "docked")
  1543.  {
  1544.     setBackground( GetBackgroundImageFileName(oWeatherGadget));
  1545.  }
  1546.  
  1547.  if ( oWeatherGadget.makesSenseToDisplayTheMoon() ) 
  1548.  {
  1549.  
  1550.     // For NightTime Weather states, show additional Moon Phase tooltip
  1551.     if ( !MicrosoftGadget.isStaleData )
  1552.     {
  1553.         theAltText += " - " + getLocalizedString( 'Night-' + oWeatherGadget.MoonState());
  1554.     }
  1555.  } 
  1556.  else 
  1557.  {
  1558.     showOrHide('DropShadowDockedMode', false);
  1559.  }
  1560.  
  1561.   document.getElementById('DockedModeAccessibilityInformation').alt = theAltText;
  1562.  
  1563.  
  1564.  if ( MicrosoftGadget.dataExpired || !MicrosoftGadget.isStaleData || MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge == 0 )
  1565.  {
  1566.     if ( oWeatherGadget.oMSNWeatherService.Temperature && oWeatherGadget.oMSNWeatherService.Temperature != "" ) 
  1567.     {
  1568.         document.getElementById('TemperatureCurrent').innerText = TemperatureInSelectedUnit( oWeatherGadget.oMSNWeatherService.Temperature ) + "°";
  1569.     } 
  1570.     else
  1571.     {
  1572.         // In the unlikely event data is “Not Available”, do not display a temperature
  1573.         document.getElementById('TemperatureCurrent').innerText = " - ";
  1574.     }
  1575.  }
  1576.  else
  1577.  {
  1578.     if ( oWeatherGadget.oMSNWeatherService.ForeCast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge) )
  1579.     {
  1580.         var highPresent = false;
  1581.         if ( oWeatherGadget.oMSNWeatherService.ForeCast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge).High && oWeatherGadget.oMSNWeatherService.ForeCast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge).High != "" )
  1582.         {
  1583.         document.getElementById('TemperatureCurrent').innerText = TemperatureInSelectedUnit( oWeatherGadget.oMSNWeatherService.ForeCast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge).High ) + "°";
  1584.         highPresent = true;
  1585.         }
  1586.         if ( oWeatherGadget.oMSNWeatherService.ForeCast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge).Low && oWeatherGadget.oMSNWeatherService.ForeCast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge).Low != "" )
  1587.         {
  1588.         if ( highPresent )
  1589.         {
  1590.             document.getElementById('TemperatureCurrent').innerText = document.getElementById('TemperatureCurrent').innerText + " - ";
  1591.         }
  1592.         else
  1593.         {
  1594.             document.getElementById('TemperatureCurrent').innerText = "";
  1595.         }
  1596.  
  1597.         document.getElementById('TemperatureCurrent').innerText = document.getElementById('TemperatureCurrent').innerText + TemperatureInSelectedUnit( oWeatherGadget.oMSNWeatherService.ForeCast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge).Low ) + "°";
  1598.         }
  1599.         else
  1600.         {
  1601.         if ( !highPresent )
  1602.         {
  1603.             document.getElementById('TemperatureCurrent').innerText = " - ";
  1604.         }
  1605.         }
  1606.     } 
  1607.     else
  1608.     {
  1609.         // In the unlikely event data is “Not Available”, do not display a temperature
  1610.         document.getElementById('TemperatureCurrent').innerText = " - ";
  1611.     }
  1612.  }
  1613.  
  1614. }
  1615. ////////////////////////////////////////////////////////////////////////////////
  1616. //
  1617. // cleanURL( sUrl ) - Verifies that the URL starts with http://
  1618. //
  1619. ////////////////////////////////////////////////////////////////////////////////
  1620. function cleanURL( sURL )
  1621. {
  1622.     var safeUrl = "javascript:void();";
  1623.     var httpIndex = sURL.search( "http://" );
  1624.  
  1625.     if ( httpIndex != 0 )
  1626.     {
  1627.         return safeUrl;
  1628.     }
  1629.     return window.toStaticHTML(sURL);        
  1630. }
  1631. ////////////////////////////////////////////////////////////////////////////////
  1632. //
  1633. // refreshUnDockedModeValues( oWeatherGadget ) - Refreshes UnDocked window
  1634. //
  1635. ////////////////////////////////////////////////////////////////////////////////
  1636. function refreshUnDockedModeValues( oWeatherGadget ) 
  1637. {
  1638.  if ( !oWeatherGadget.isValid )
  1639.  { 
  1640.     showOrHideServiceError(true, oWeatherGadget.status);
  1641.     return; 
  1642.  }
  1643.  if ( oWeatherGadget.oMSNWeatherService===undefined )
  1644.  { 
  1645.     // NO data to display.
  1646.     return; 
  1647.  }
  1648.  
  1649.  showOrHideServiceError(false);
  1650.  document.getElementById('Attribution').innerText = oWeatherGadget.oMSNWeatherService.Attribution2;
  1651.  
  1652.  // Update Today's Forecast Temperatures
  1653.  document.getElementById('PlaceHrefUnDockedMode').href = cleanURL( oWeatherGadget.oMSNWeatherService.Url );
  1654.  document.getElementById('PlaceHrefUnDockedMode').innerText = oWeatherGadget.oMSNWeatherService.Location;
  1655.  document.getElementById('ConditionCurrentUnDockedMode').innerText = oWeatherGadget.SkyText;
  1656.  document.getElementById('TemperatureSeparator').innerText = '-';
  1657.  
  1658.  if ( MicrosoftGadget.dataExpired || !MicrosoftGadget.isStaleData || MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge == 0 )
  1659.  {
  1660.     document.getElementById('TemperatureHigh0').innerText = oWeatherGadget.HighTemp() + "°";
  1661.     document.getElementById('TemperatureLow0').innerText = TemperatureInSelectedUnit( oWeatherGadget.oMSNWeatherService.ForeCast(0).Low ) + "°";
  1662.  }
  1663.  else
  1664.  {
  1665.     document.getElementById('TemperatureHigh0').innerText = TemperatureInSelectedUnit( oWeatherGadget.oMSNWeatherService.ForeCast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge).High ) + "°";
  1666.     document.getElementById('TemperatureLow0').innerText = TemperatureInSelectedUnit( oWeatherGadget.oMSNWeatherService.ForeCast(MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge).Low ) + "°";
  1667.  }
  1668.  
  1669.  var theAltText;
  1670.  if ( MicrosoftGadget.isStaleData )
  1671.  {
  1672.     theAltText = getLocalizedString( 'ServiceNotAvailable' );
  1673.  }
  1674.  else
  1675.  {
  1676.     theAltText = oWeatherGadget.SkyText;    
  1677.  }
  1678.  
  1679.  // only need to update the background image (which is shared) if we are undocked
  1680.  if (activeDisplayMode() == "undocked")
  1681.  {
  1682.     setBackground( GetBackgroundImageFileName(oWeatherGadget));
  1683.  }
  1684.  
  1685.  if ( oWeatherGadget.makesSenseToDisplayTheMoon() ) 
  1686.  {
  1687.     // For NightTime Weather states, show additional Moon Phase tooltip
  1688.     if ( !MicrosoftGadget.isStaleData )
  1689.     {
  1690.         theAltText += " - " + getLocalizedString( 'Night-' + oWeatherGadget.MoonState());
  1691.     }
  1692.  } 
  1693.  
  1694.  if ( !MicrosoftGadget.isStaleData || MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge == 0 )
  1695.  {
  1696.     if ( oWeatherGadget.oMSNWeatherService.Temperature && oWeatherGadget.oMSNWeatherService.Temperature != "" ) 
  1697.     {
  1698.         document.getElementById('TemperatureCurrentUnDockedMode').innerText = TemperatureInSelectedUnit( oWeatherGadget.oMSNWeatherService.Temperature ) + "°";
  1699.     } 
  1700.     else
  1701.     {
  1702.         // In the unlikely event data is “Not Available”, do not display a temperature
  1703.         document.getElementById('TemperatureCurrentUnDockedMode').innerText = " - ";
  1704.     }
  1705.  }
  1706.  else
  1707.  {
  1708.     document.getElementById('TemperatureCurrentUnDockedMode').innerText = "";
  1709.  }
  1710.  
  1711.  document.getElementById('UnDockedModeAccessibilityInformation').alt = theAltText;
  1712.  
  1713.  for (var i=1;i<4;i++) 
  1714.  {
  1715.     var counterI;
  1716.     if ( !MicrosoftGadget.dataExpired && MicrosoftGadget.isStaleData && MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge > 0 )
  1717.     {
  1718.         counterI = MicrosoftGadget.oMSNWeatherServiceDataAge.daysAge + i;
  1719.     }
  1720.     else
  1721.     {
  1722.         counterI = i;
  1723.     }
  1724.     if ( counterI < 5 )
  1725.     {
  1726.         with ( oWeatherGadget.oMSNWeatherService.Forecast( counterI ) )
  1727.         {
  1728.         var theDate = parseDateFromString(Date);
  1729.         var theLowTemp = TemperatureInSelectedUnit( Low );
  1730.         var theHighTemp = TemperatureInSelectedUnit( High );
  1731.         // Code from 1 to 47 indicating Weather State (used to compute Icon to display)
  1732.         var theSkyCode = SkyCode;
  1733.         
  1734.         document.getElementById('SkyCodeImage'+ i).alt = SkyText; 
  1735.         setImage( 'SkyCodeImage'+ i, 'images/' + theSkyCode + '.png');
  1736.         document.getElementById('DayOfWeek' + i).innerText = Day; 
  1737.         document.getElementById('DayOfWeek' + i).href = cleanURL( oWeatherGadget.oMSNWeatherService.Url );
  1738.         document.getElementById('TemperatureHigh' + i).innerText = theHighTemp + "°";
  1739.         document.getElementById('TemperatureLow' + i).innerText = theLowTemp + "°";
  1740.         }
  1741.     }
  1742.     else
  1743.     {
  1744.         document.getElementById('SkyCodeImage'+ i).alt = ""; 
  1745.         setImage( 'SkyCodeImage'+ i, 'images/1px.gif');
  1746.         document.getElementById('DayOfWeek' + i).innerText = ""; 
  1747.         document.getElementById('DayOfWeek' + i).href = "javascript:void(0);";
  1748.         document.getElementById('TemperatureHigh' + i).innerText = "";
  1749.         document.getElementById('TemperatureLow' + i).innerText = "";
  1750.     }
  1751.  }
  1752. }
  1753. ////////////////////////////////////////////////////////////////////////////////
  1754. //
  1755. // activeDisplayMode() - returns active display mode
  1756. //
  1757. ////////////////////////////////////////////////////////////////////////////////
  1758. function activeDisplayMode() 
  1759. {
  1760.  retVal = gDefaultDisplayMode;
  1761.  
  1762.  if (gGadgetMode) 
  1763.  {
  1764.     if (System.Gadget.docked) 
  1765.     {
  1766.         retVal = "docked";
  1767.     } 
  1768.     else 
  1769.     {
  1770.         retVal = "undocked";    
  1771.     }
  1772.  } 
  1773.  return retVal;
  1774. }
  1775. ////////////////////////////////////////////////////////////////////////////////
  1776. //
  1777. // parseDateFromString(string aString) - parses Date from a string extracted 
  1778. // from MSN Weather Feed. Returns a Date Object set to that time.
  1779. //
  1780. ////////////////////////////////////////////////////////////////////////////////
  1781. function parseDateFromString(aString) 
  1782. {
  1783.  if (aString.length) {                    
  1784.     // String received should be formatted like: "2006-01-10" (yyyy-mm-dd)
  1785.     var aDateTimeObject = new Date();
  1786.     aDateTimeObject.setFullYear(parseInt(aString.split('-')[0]));
  1787.     aDateTimeObject.setMonth(parseInt(aString.split('-')[1])-1);
  1788.     aDateTimeObject.setDate(parseInt(aString.split('-')[2]));
  1789.     return aDateTimeObject;
  1790.  }
  1791. }
  1792. ////////////////////////////////////////////////////////////////////////////////
  1793. //
  1794. // showOrHideGettingDataMessage() - Display/Hide "Getting Data" Message 
  1795. //
  1796. ////////////////////////////////////////////////////////////////////////////////
  1797. function showOrHideGettingDataMessage(bShow) 
  1798. {
  1799.  showOrHide('WeatherMessage', bShow);
  1800.  showOrHide('PleaseWaitLoadingSpinner', bShow);
  1801.  showOrHide(activeDisplayMode() + 'ModeDisplayArea', !bShow);
  1802.  
  1803.  var oMessage = document.getElementById('message');
  1804.  if (bShow) 
  1805.  { 
  1806.     if (activeDisplayMode()=="undocked")
  1807.     {
  1808.         document.getElementById("WeatherMessage").className = 'unDockedWeatherMessage';
  1809.     }
  1810.     else
  1811.     {
  1812.         document.getElementById("WeatherMessage").className = 'dockedWeatherMessage';
  1813.     }
  1814.     oMessage.innerHTML = getLocalizedString('GettingData');
  1815.     if (activeDisplayMode()=='docked')
  1816.     {
  1817.         setBackground( 'url(images/docked-loading.png)' );
  1818.     }
  1819.     else
  1820.     {
  1821.         setBackground( 'url(images/undocked-loading.png)' );
  1822.     }
  1823.  } 
  1824.  else 
  1825.  {
  1826.     oMessage.innerHTML = "";
  1827.  }
  1828.  showOrHide( document.getElementById('WeatherMessageIcon'), false );
  1829. }
  1830.  
  1831.  
  1832. ////////////////////////////////////////////////////////////////////////////////
  1833. //
  1834. // showOrHideGettingLocationMessage() - Display/Hide "Getting current location" Message 
  1835. //
  1836. ////////////////////////////////////////////////////////////////////////////////
  1837. function showOrHideGettingLocationMessage(bShow) 
  1838. {
  1839.  showOrHide('WeatherMessage', bShow);
  1840.  showOrHide('PleaseWaitLoadingSpinner', bShow);
  1841.  showOrHide(activeDisplayMode() + 'ModeDisplayArea', !bShow);
  1842.  
  1843.  var oMessage = document.getElementById('message');
  1844.  if (bShow) 
  1845.  { 
  1846.     if (activeDisplayMode()=="undocked")
  1847.     {
  1848.         document.getElementById("WeatherMessage").className = 'unDockedWeatherMessage';
  1849.     }
  1850.     else
  1851.     {
  1852.         document.getElementById("WeatherMessage").className = 'dockedWeatherMessage';
  1853.     }
  1854.     oMessage.innerHTML = getLocalizedString('GettingLocation');
  1855.     if (activeDisplayMode()=='docked')
  1856.     {
  1857.         setBackground( 'url(images/docked-loading.png)' );
  1858.     }
  1859.     else
  1860.     {
  1861.         setBackground( 'url(images/undocked-loading.png)' );
  1862.     }
  1863.  } 
  1864.  else 
  1865.  {
  1866.     oMessage.innerHTML = "";
  1867.  }
  1868.  showOrHide( document.getElementById('WeatherMessageIcon'), false );
  1869. }
  1870.  
  1871. ////////////////////////////////////////////////////////////////////////////////
  1872. //
  1873. // showOrHideServiceError() - Display/Hide "Service Not Available" Message 
  1874. //
  1875. ////////////////////////////////////////////////////////////////////////////////
  1876. function showOrHideServiceError(bShow, theStatusCode) 
  1877. {
  1878.  var theImage = "";
  1879.  var theMessage = "";
  1880.  showOrHide('WeatherMessage', bShow);
  1881.  showOrHide('PleaseWaitLoadingSpinner', false);
  1882.  var oMessage    = document.getElementById('message');
  1883.  if (bShow) 
  1884.  {
  1885.     if (activeDisplayMode()=="undocked")
  1886.     {
  1887.         document.getElementById("WeatherMessage").className = 'unDockedWeatherMessage';
  1888.     }
  1889.     else
  1890.     {
  1891.         document.getElementById("WeatherMessage").className = 'dockedWeatherMessage';
  1892.     }
  1893.     switch( theStatusCode ) 
  1894.     {
  1895.         case 1506: 
  1896.         // Forbidden
  1897.         if (activeDisplayMode()=="undocked") {
  1898.             theMessage = getLocalizedString( 'ServiceNotAvailableInYourArea' );
  1899.         } else {
  1900.             theMessage = getLocalizedString( 'ServiceNotAvailableInYourArea' );
  1901.         }
  1902.         document.getElementById('WeatherMessageIcon').alt = getLocalizedString( 'ServiceNotAvailableInYourArea' );
  1903.         oMessage.title = getLocalizedString( 'ServiceNotAvailableInYourArea' );
  1904.         break;
  1905.         
  1906.         
  1907.         case 404: default: 
  1908.         // Not Found
  1909.         theMessage = getLocalizedString( 'ServiceNotAvailable' );
  1910.         document.getElementById('WeatherMessageIcon').alt = getLocalizedString( 'ServiceNotAvailable' );
  1911.         break;
  1912.     }
  1913.     if (activeDisplayMode()=='docked')
  1914.     {
  1915.         setBackground( 'url(images/docked-loading.png)' );
  1916.     }
  1917.     else
  1918.     {
  1919.         setBackground( 'url(images/undocked-loading.png)' );
  1920.     }
  1921.  }
  1922.  showOrHide( document.getElementById('WeatherMessageIcon'), true );
  1923.  oMessage.innerHTML = '<span>' + theMessage + '<\/span>';
  1924. }
  1925. ////////////////////////////////////////////////////////////////////////////////
  1926. //
  1927. // setBackground ( string theImageURL ) - sets the background image of our gadget
  1928. //
  1929. ////////////////////////////////////////////////////////////////////////////////
  1930. function setBackground( theImageURL ) 
  1931. {
  1932.  // if you switch backgrounds on the fly, you must set the style size to zero
  1933.  // so it dynamically refreshes
  1934.  WeatherBG.style.width = 0;
  1935.  WeatherBG.style.height = 0;
  1936.  WeatherBG.src = theImageURL;
  1937. }
  1938.  
  1939. function calculateAge ( timestamp )
  1940. {
  1941.  var timestampDay = new Date( timestamp );
  1942.  var currentDay = new Date();
  1943.  
  1944.  var millisecondsTillTimestamp = Date.parse( timestamp );
  1945.  var millisecondsTillNow = (new Date()).getTime();
  1946.  var minutesDifference = parseInt( ( millisecondsTillNow - millisecondsTillTimestamp ) / ( 60 * 1000 ) );
  1947.  var hoursDifference = parseInt ( minutesDifference / 60 );
  1948.  var daysDifference = parseInt ( hoursDifference / 24 );
  1949.  
  1950.  this.minutesAge = 0;
  1951.  this.hoursAge = 0;
  1952.  this.daysAge = 0;
  1953.  this.dataExpired = false;
  1954.  
  1955.  if ( minutesDifference <= 0 )
  1956.  {
  1957.     return this;
  1958.  }
  1959.  
  1960. // shows hours and mins only for the same day
  1961.  if ( timestampDay.getDate() == currentDay.getDate()
  1962.     && timestampDay.getMonth() == currentDay.getMonth()
  1963.     && timestampDay.getFullYear() == currentDay.getFullYear() )
  1964.  {
  1965.     this.minutesAge = minutesDifference % 60;
  1966.     this.hoursAge = hoursDifference % 24;
  1967.     this.daysAge = daysDifference;
  1968.  }
  1969.  else // only calculate total no of days past
  1970.  {
  1971.     timestampDay.setHours(currentDay.getHours(),currentDay.getMinutes(),currentDay.getSeconds(),currentDay.getMilliseconds());
  1972.     this.daysAge = parseInt ( ( currentDay.getTime() - timestampDay.getTime() ) / ( 60 * 1000 * 60 * 24 ) );
  1973.     if ( this.daysAge < 0 )
  1974.     {
  1975.         this.daysAge = 0;
  1976.     }
  1977.  }
  1978.  
  1979.  if ( this.daysAge > 4 )
  1980.  {
  1981.     this.dataExpired = true;
  1982.  }
  1983.  
  1984.  return this;
  1985. }
  1986.  
  1987. function calculateAgeStampText( age )
  1988. {
  1989.  var isForecastedData = false;
  1990.  var ageStampText = "";
  1991.  var unitText = "";
  1992.  var durationText = "";
  1993.  
  1994.  if ( age.dataExpired )
  1995.  {
  1996.     return getLocalizedString('DataExpired');
  1997.  }
  1998.  
  1999.  if ( age.daysAge > 0 )
  2000.  {
  2001.     isForecastedData = true;
  2002.     durationText = getLocalizedString('day');
  2003.     if ( age.daysAge > 1 )
  2004.     {
  2005.         durationText = getLocalizedString('days');
  2006.     }
  2007.     unitText = age.daysAge;
  2008.  }
  2009.  else if ( age.hoursAge > 0 )
  2010.  {
  2011.     durationText = getLocalizedString('hr');
  2012.     if ( age.hoursAge > 1 )
  2013.     {
  2014.         durationText = getLocalizedString('hrs');
  2015.     }
  2016.     unitText = age.hoursAge;
  2017.  }
  2018.  else
  2019.  {
  2020.     if ( age.minutesAge == 0 )
  2021.     {
  2022.         return "";
  2023.     }
  2024.     else
  2025.     {
  2026.         durationText = getLocalizedString('min');
  2027.         unitText = age.minutesAge;
  2028.     }
  2029.  }
  2030.  
  2031.  if( isForecastedData )
  2032.  {
  2033.         ageStampText = getLocalizedString('ageStampMessageForecastedData')
  2034.         ageStampText = ageStampText.replace("%1", unitText);
  2035.         ageStampText = ageStampText.replace("%2", durationText);
  2036.  }
  2037.  else
  2038.  {
  2039.         ageStampText = getLocalizedString('ageStampMessage')
  2040.         ageStampText = ageStampText.replace("%1", unitText);
  2041.         ageStampText = ageStampText.replace("%2", durationText);
  2042.  }
  2043.  
  2044.  return ageStampText;
  2045. }
  2046.