// ----------------------------------------------------------------------------+
// VERSION: 9.05 - 10-Jan-2009 - www.bashewa.com - rainer@bashewa.com          |
// ----------------------------------------------------------------------------|
// NOTES  : This version of "ajaxWDwx.js" has been modified by www.bashewa.com |
//          Download latest version of this script from:                       |
//          ... http://www.bashewa.com/downloads/bws-script-ajaxWDwx.zip       |
//          To see how changes to this script have been implemented go to:     |
//          ... http://www.bashewa.com/ajax-dashboard.php?sce=view             |
//          For available "ajax tags" and descriptions see bottom of this page.|
// ----------------------------------------------------------------------------|
// JS SIZE REDUCTION:                                                          |
//   If you want to pack this script after you have made your changes, go here:|
//   ... http://dean.edwards.name/packer/                                      |
//   ... make a "ajaxWDwx-packed.js" file and change this line in "top.php" to:|
//       <script type="text/javascript" src="ajaxWDwx-packed.js"></script>     |
//   ... This will reduce the size to ±34% of it's original size (±38kb).      |
// ----------------------------------------------------------------------------|
// SCRIPT DOWNLOADS:                                                           |
//       Cloud base graphic:  http://www.bashewa.com/download-cloud-base.php   |
//     Gradient thermometer:  http://www.bashewa.com/download-thermometer.php  |
//   Dynamic condition icon:  (not yet implemented)                            |
// ----------------------------------------------------------------------------+

// -------------------------------------------------------------------------------------------|
// ICON & GRAPHIC DOWNLOADS:                                                                  |
// ... unzip in root with "use folder names" when unziping                                    |
// All of the below icons  :  http://www.bashewa.com/downloads/bws-iconsets.zip (2.5mb)       |
// -------------------------------------------------------------------------------------------|
// Current conditions icons:  http://www.bashewa.com/downloads/bws-icons-current.zip   (269kb)|
//            Forcast icons:  http://www.bashewa.com/downloads/bws-icons-forecast.zip  (1.1mb)|
//  Sun/Moon bg, Moon ph x4:  http://www.bashewa.com/downloads/bws-icons-moonphase.zip ( 19kb)|
//        Moon pictures x28:  http://www.bashewa.com/downloads/bws-icons-moon.zip      (188kb)|
//       Fire Weather Index:  http://www.bashewa.com/downloads/bws-icons-fire.zip      (291kb)|
//                 UV icons:  http://www.bashewa.com/downloads/bws-icons-uv.zip        ( 20kb)|
//          Wind Rose icons:  http://www.bashewa.com/downloads/bws-icons-wind.zip      (822kb)|
// -------------------------------------------------------------------------------------------|


// ========================================================================== //
// USAGE and WARRANTY:                                                        //
// -------------------------------------------------------------------------- //
// You may copy/modify/use this script as you see fit.                        //
// No warranty is expressed or implied.                                       //
//                                                                            //
// This program is free software; you can redistribute it and/or modify it    //
// under the terms of the GNU General Public License as published by the Free //
// Software Foundation; either version 2 of the License, or (at your option)  //
// any later version.                                                         //
//                                                                            //
// This program is distributed in the hope that it will be useful, but        //
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY //
// or FITNESS FOR A PARTICULAR PURPOSE.                                       //
// See the GNU General Public License for more details.                       //
//                                                                            //
// You should have received a copy of the GNU General Public License along    //
// with this program; if not, write to the:                                   //
//    Free Software Foundation, Inc.,                                         //
//    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA                //
// ========================================================================== //


// -----------------------------------------------------------------------------
// VERSION HISTORY v2.00 - Saratoga Weather ... http://saratoga-weather.org
// -----------------------------------------------------------------------------
// Special thanks to: Kevin Reed http://www.tnetweather.com/
// Kevin was the first to decode the clientraw in PHP
// Special thanks to: Pinto http://www.joske-online.be/
// Pinto wrote the basic AJAX code for this page!
// Cheerfully borrowed from Tom at CarterLake.org and adapted by Ken True - Saratoga-weather.org  21-May-2006
// Updates available at: http://saratoga-weather.org/scripts-WD-AJAX.php
// Announcements of new versions will be on weather-watch.com and wxforum.net
// --- added flash-green on data update functions - Ken True  24-Nov-2006
// --- Version 2.00 - 13-Dec-2006 -- Ken True -repackaged AJAX function, added metric/english units
//     also included Mike Challis' counter script to display seconds since last update and error
//     handling for the fetch to fix to fix random error: NS_ERROR_NOT_AVAILABLE
//     Mike's site: http://www.carmosaic.com/weather/index.php
//     Thanks to FourOhFour on wxforum.net ( http://skigod.us/ ) for replacing all the
//     x.responseText.split(' ')[n] calls with a simple array lookup.. much better speed and
//     for his streamlined version of getUVrange.
// --- Version 2.01 - 17-Dec-2006 -- Corrected cloud height calculation
// --- Version 2.02 - 20-Dec-2006 -- added unescape to set_ajax_obs comparison for lastobs
// --- Version 2.03 - 07-Jan-2006 -- added wind m/s or km/h for metric variables
// --- Version 2.04 - 08-Jan-2006 -- use epoch time for get (thanks to johnnywx on WD forum)
//                                   so a numeric time without HTMLencoded characters is used
// --- Version 2.05 - 30-Jan-2006 -- added new 'anti-NaN' check from johnnywx to make sure full
//                                   clientraw.txt is read by looking for
//                                   '12345' at start and '!!' at end of record
// --- Version 2.06 - 24-Jun-2007 -- added '/' as delimiter for currentcond
// --- Version 2.07 - 21-Sep-2007 -- added support for dynamic thermometer.php display refresh
// --- Version 2.08 - 07-Nov-2007 -- added useMPH to force wind display in Miles-per-hour and
//                                   added optional Wind-Rose, optional new current icon display graphics
// --- Version 2.09 - 23-Dec-2007 -- added maxupdates feature, new ajax variables from K. Reed www.tnetweather.com
// --- Version 2.10 - 18-Jan-2008 -- fixed icon=34 for ra1.jpg
// --- Version 2.11 - 21-Feb-2008 -- added icon=35 for windyrain.gif/.jpg
// --- Version 2.12 - 07-Mar-2008 -- added fix for 'flashing icon/thermometer' from Jim at jcweather.us
// --- Version 2.13 - 11-Mar-2008 -- changed Wind-rose defaults to .png type (Carterlake/AJAX/PHP templates)
// --- Version 2.14 - 29-Mar-2008 -- fixed UV words with  color: black; for display on dark/black template (MCHALLIS)
// --- Version 2.15 - 28-Apr-2008 -- added ajaxFixConditions() and translation capability
// --- Version 2.16 - 20-May-2008 -- added headcolorword processing V1.0 from MCHALLIS
// --- Version 2.17 - 25-Jun-2008 -- added gizmo-specific ajax variables
// --- Version 2.18 - 20-Mar-2008 -- added fix for 'green-flash' issue with Internet Explorer 8
// --- Version 2.19 - 03-Jul-2009 -- additional gizmo-specific ajax added, and useHpa variable for pressure

// -----------------------------------------------------------------------------
// VERSION HISTORY v3.00 - Webster Weather ... http://www.WebsterWeatherLIVE.com
// -----------------------------------------------------------------------------
// Updates available at: www.WebsterWeatherLIVE.com or scott@websterweatherlive.com
// --- Version 2.80 - 15-Apr-2009 -- added Lightning ajax variables
// --- Version 3.00 - 14-May-2009 -- added many additional ajax variables
// --- Version 4.10 - 25-Sep-2009 -- added support for Nexstorm Lightning distance and direction
// --- Version 4.20 - 11-Oct-2009 -- added support for UV reading 0 during the day - shows "updating..." if so.

// -----------------------------------------------------------------------------
// VERSION HISTORY v9.00 - Bashewa Weather ... http://www.bashewa.com
// -----------------------------------------------------------------------------
// Updates available at: http://www.bashewa.com/downloads.php
// --- Version 9.00 - 15-Apr-2009 -- modifications by Rainer Finkeldeh (www.bashewa.com)
//       Added fetching of "clientrawextra.txt" for history data (e.g. rain etc.).
//       Added tag "ajaxrainSun" ...... for last Sunday's rain
//                    ..thru..
//                 "ajaxrainSat" ...... for last Saturday's rain
//       Added tag "ajaxwinddeg" ...... for wind in degrees e.g. 334°
//       Added tag "ajaxsnowDepth" .... for current snow depth
//                 "ajaxsnowToday" .... for todays snow
//                 "ajaxsnowMonth" .... for this months snow
//                 "ajaxsnowSeason" ... for this seasons snow
//       Added tag "ajaxwebcamimg" .... for webcam image support
//       Added tag "ajaxmoonimg" ...... for moon image support
//       Added tag "ajaxmoonphase" .... for current phase e.g. "First Quarter"
//       Added tag "ajaxmoonpct" ...... for current moon illumination e.g. 24%
//       Added tag "ajaxcloudheightimg" for cloud height graphic
//       Added tag "ajaxuvimg" ........ for UV graphic (current UV)
//       Added tag "ajaxuvburnrate" ... for UV skin burn rate (in minutes)
//       Added "REFRESH" hyperlink to "Updates paused" for restarting ajax script.
//       Added window.onload function to prevent early ajax loading.
//             -----------------------------------------------------------------
//       Added support for UOM switching [ metric | english ].
//             ... see: http://www.bashewa.com/ajax-dashboard.php?sce=view
//             Add the following code to your "ajax-dashboard.php":
//             -----------------------------------------------------------------
//             <div onClick="javascript:ajax_changeUnits();"
//                  style="cursor: pointer;"><b>
//                  <span id="uomM" style="color: gray;">METRIC</span> |
//                  <span id="uomE" style="color: blue;">ENGLISH</span></b>
//             </div>
//             -----------------------------------------------------------------
//       Added support for UOM switching on static field values.
//             ... see: http://www.bashewa.com/ajax-dashboard.php?sce=view
//             Add one of the following classes to your fields:
//             NOTE: the value in the span must include the units-of-measure for them be converted.
//             -----------------------------------------------------------------
//             <span class="convTemp">82.7&deg;F</span>   ...for temp
//             <span class="convWind">   2.7 mph</span>   ...for wind
//             <span class="convBaro">30.30 inHg</span>   ...for temp
//             <span class="convRain">   1.04 in</span>   ...for rain
//             <span class="convSnow">   12.4 in</span>   ...for snow
//             <span class="convAlti">   2793 ft</span>   ...for altitude
//             <span class="convDist">  15 miles</span>   ...for distance
//             -----------------------------------------------------------------
//    Modified thermometer.php to support UOM switching and different styles.
//    Modified UV color words to official web colors as specified by WHO, WMO & UNEP
//    Modified overall settings setup for easier configuration.
//    Modified UOM conversions and added convertSnow & convertDist.
// End Version 9.00 ------------------------------------------------------------
// --- Version 9.01 - 05-Jan-2010 - Fixed problem with Moon Phase.
// --- Version 9.02 - 07-Jan-2010 - Added Webster Weather modifications (v2.80, v3.00, v4.10)
// --- Version 9.03 - 10-Jan-2010 - Added the following ajax tags:
//        Added tag "ajaxwebcamimg" .... for webcam image support
//        Added tag "ajaxRcdRain" ......... rain record (record all-time daily rain)
//        Added tag "ajaxRcdRainTime" ..... rain record time
//        Added tag "ajaxRcdRainDate" ..... rain record date
//        Added tag "ajaxRcdRainHr" ....... rain record Hr (record all-time daily rain in 1 hour)
//        Added tag "ajaxRcdRainHrTime" ... rain record Hr time
//        Added tag "ajaxRcdRainHrDate" ... rain record Hr date
//        Added tag "ajaxDaysRain" ........ rain days (MTD)
//        Added tag "ajaxDaysNoRain" ...... rain absent days
// --- Version 9.04 - 10-Jan-2010 - Implemented Webcam support for useCamAtNight and delayDayNight
// --- Version 9.05 - 10-Jan-2010 ----------------------------------------------
//        Fixed problem with "number_format" function not showing correct decimal value.
//        Added ALERT when "clientraw.txt" file can not be found (error 404)
//        ajaxLoaderInBody no longer possible i.e. <body onload="ajaxLoader(...)">
//        ClientExtra will now always be loaded as well.
//        Implemented external "ajaxWDwx-settings.js" for seperate settings outside of this script (see below).
// -----------------------------------------------------------------------------




//============================================================================\\
//                             S E T I N G S                                  \\
// You can have seperate settings in "ajaxWDwx-settings.js" so that you don't \\
// have to redo them each time a new version of this script is released.      \\
// -------------------------------------------------------------------------- \\
// If you have seperate settings in a file "ajaxWDwx-settings.js" then you    \\
// need to add the following line to your "top.php" file:                     \\
//     <script type="text/javascript" src="ajaxWDwx-settings.js"></script>    \\
// below this line in "top.php":                                              \\
//     <script type="text/javascript" src="ajaxWDwx.js"></script>             \\
//============================================================================\\

// -----------------------------------------------------------------------------
// Settings (required)                                                         .
// -----------------------------------------------------------------------------
var clientrawFile = '../script/clientraw.php'; // location of clientraw.txt relative to this page on website
var useUOM = 'M';   // [ E | M ] ... Units Of Measure: 'E'=English/Imperial or 'M'=Metric
var stationAlt = 15; // your station altitude in meters for cloud height graphic if you want ASL (meters = ft / 3.2808399)


// -----------------------------------------------------------------------------
// Settings (optional)                                                         .
// -----------------------------------------------------------------------------
var reloadTime = 10000;      // reload AJAX conditions every 5 seconds (= 5000 ms)
var maxupdates = 100;        // Maximum number of updates/fetches allowed (set to zero for unlimited)
                            // maxupdates * reloadTime / 1000 = number of seconds to update
                            // i.e. 36 x 5000 / 1000 = 180 seconds (3 minutes)
                            // Note: if you upload your clientraw.txt file every 30 seconds
                            //       then the visitor will see (180/30) = 6 updates in total.
var allowRefresh = true;    // show REFRESH hyperlink after "maxupdates" have been exceeded.

var flashcolor = '#999999'; // color to flash for changed observations (RGB) (def #00CC00 = green)
var flashRed   = '#FF0000'; // color to flash red for warnings/alarms  (RGB) (def #FF0000 = red)
var flashtime  = 400;      // milliseconds to keep flash color on (2000 = 2 seconds);

var showUOM = true;         // set to false if no units should be displayed
                            // i.e. if the UOM is outside the "ajaxtag" spans.
                            // Note: If you have implemented UOM switching this needs to be TRUE.


//------------------------------------------------------------------------------
// STATIC IMAGES                                                               .
//------------------------------------------------------------------------------
// IMAGE LOCATION: root folder for graphics images/icons
// ... (for root folder specify "./")
var imgDir = '..images/ajax-images/'; // with trailing slash

// NOTE: do not delete any of the words below between the #'s  e.g  #PIC#
//       They'll get replaced by their corresponding values later in the script.

//------------------------------------------------------------------------------
// STICKER ICONS: optional settings for the current conditions graphic for Anole's wxsticker icon.
// tag: "ajaxconditionicon"
// e.g. graphic name: "./ajax-images/icon/0/day_partly_cloudy.jpg"
var wxIconSet0 =
   {src    : imgDir+"icon/0/#PIC#.gif" // where #PIC# will = picname from table in ajax_getIconName0() function
   ,width  : 25 ,height: 25 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style  : ""  // eg. "border: 2px solid silver"
   ,align  : ""  // [ left | right | top | bottom | middle | texttop | absbottom | absmiddle ]
   ,useName: 0   // must be 0 for this icon set ... Anole's icon nameing convention ... see ajax_getIconName0()
   };


//------------------------------------------------------------------------------
// DASHBOARD ICONS: optional settings for current conditions graphic for dashboard
// tag: "ajaxconditionicon2"
// .... grab the animated icons for set 2 here: http://www.bashewa.com/downloads/bws-icons-current.zip

// Specify the "wxIconSet" to use further down below ... at the end of this section
// and set up the corresponding set below.

// NOTE: Do not delete the #PIC# part in the "src" settings.

// 1: NWS icons (55x58)
//    e.g. graphic name: "./ajax-images/bkn.gif"
var wxIconSet1 =
   {src    : imgDir+"#PIC#.jpg" // where #PIC# will = picname from table in ajax_getIconName1() function
   ,width  : 55 ,height: 58 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style  : ""  // eg. "border: 2px solid silver"
   ,align  : ""  // [ left | right | top | bottom | middle | texttop | absbottom | absmiddle ]
   ,useName: 1   // must be 1 for this icon set ... NWS icon nameing convention ... see ajax_getIconName1())
   };

// 2: Bashewa animated icons (75x60)
//    e.g. graphic name: "./ajax-images/icon/2/2-bkn.gif"
var wxIconSet2 =
   {src    : imgDir+"icon/2/#PIC#.gif" // where #PIC# will = picname from table in ajax_getIconName2() function
   ,width  : 75 ,height: 60 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style  : ""  // eg. "border: 2px solid silver"
   ,align  : ""  // [ left | right | top | bottom | middle | texttop | absbottom | absmiddle ]
   ,useName: 2   // must be 2 for this icon set ... Bashewa.com  icon nameing convention ... see ajax_getIconName2())
   };

// 3: Your own icons (any size)
//    e.g. graphic name: "./ajax-images/icon/3/bkn.gif"
var wxIconSet3 =
   {src    : imgDir+"icon/3/#PIC#.jpg" // where #PIC# will = picname from table specified in wxIconSet3.useName
   ,width  : 75 ,height: 60 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style  : ""  // eg. "border: 2px solid silver"
   ,align  : ""  // [ left | right | top | bottom | middle | texttop | absbottom | absmiddle ]
   ,useName: 1   // [ 0| 1 | 2 ] function for getting icon names ...
   };            //              0 = Anole's icon names ....... see ajax_getIconName0()
                 //              1 = NWS icon names ........... see ajax_getIconName1()
                 //              2 = Bashewa.com icon names ... see ajax_getIconName2()
// 4: You can add more icon sets here

// D: Dynamic generated icon set (170x127) ... NOT IMPLEMENTED
//    ... you'll need: http://www.bashewa.com/download-dynamic-icon.php
var wxIconSetD =
   {src    : "./dynamic-icon.php" // path and script for dynamic conditions image (optional)
   ,width  : 170 ,height: 127 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style  : "border: 2px solid silver;"
   ,align  : ""
   ,useName: 9 // must be 9 for generating dynamic icons
   };

var wxIconSet = wxIconSet1; // choose from an icon set above that you want to use.
                            // If we had a uniform icon nameing convention the above would not be necessary.

//------------------------------------------------------------------------------
// WIND ROSE: optional settings for the Wind Rose graphic
// tag: "ajaxwindiconwr"
// e.g. graphic name: "./ajax-images/wr-SSE.gif"  or  "./ajax-images/wr-nl-SSE.gif"
// .... grab the wind rose icons here: http://www.bashewa.com/downloads/bws-icons-wind.zip
wrImg =
   {src   : imgDir+"wr-#LANG##DIR#.gif"  // where #DIR# will = wind dir (e.g. NNW), #LANG# = wrImg.lang
   ,lang  : ""     // with trailing dash (e.g. "nl-" will result in graphic name: "./ajax-images/wind/wr-nl-SSE.gif")
   ,calm  : "calm" // substitution for #DIR# (e.g "wr-calm.gif") ... specify "" if not to be shown or not available
   ,width : 58 ,height: 58 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style : ""
   ,align : "" // [ left | right | top | bottom | middle | texttop | absbottom | absmiddle ]
   };


//------------------------------------------------------------------------------
// UV: optional settings for the UV graphic
// tag: "ajaxuvimg"
// e.g. graphic name: "./ajax-images/uv/1/uv12.gif"
// .... grab UV pics here: http://www.bashewa.com/download/bws-icons-uv.zip
var uvImg =
   {src   : imgDir+"uv/1/uv#INDEX#.gif" // where #INDEX# will = UV index (0 to 11)
   ,width : 34 ,height: 65 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style : ""
   ,align : ""
   };


//------------------------------------------------------------------------------
// MOON: optional settings for the moon phase graphic
// tag: "ajaxmoonimg"
// e.g. graphic name: "./ajax-images/moon/w/NH-moon07.gif"
// .... grab the Moon images here: http://www.bashewa.com/downloads/bws-icons-moon.zip
var moonImg =
   {src   : imgDir+"moon/#BG#/#NS#-moon#AGE#.gif" // #BG# = [ w | b ] from moonImg.bg, #NS# = [ NH | SH ] from latitude, #AGE# = 2 digit moonage (0 to 28) from getMoonInfo
   ,bg    : "w" // [ w | b ] moon background (w=white, b=black)
   ,width : 50 ,height: 50 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style : ""
   ,align : ""
   };


//------------------------------------------------------------------------------
// FIRE INDEX: optional setting for the fire risk graphic
// tag: "ajaxfireimg"
// e.g. graphic name: "./ajax-images/fire/fire3.gif"
// .... grab the FWI icons here: http://www.bashewa.com/downloads/bws-icons-fire.zip
var fireImg =
   {src   : imgDir+"fire/7/fire-#INDEX#.jpg" // where #INDEX# will = fire index (0=very low, 1=low, 2=moderate, 3=high, 4=extreme) from clientraw
   ,width : 100 ,height: 65 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style : "border: 2px solid silver;"
   ,align : ""
   };


//------------------------------------------------------------------------------
// WEBCAM IMAGE: optional setting for the webcam graphic
// tag: "ajaxwebcamimg"
// e.g. picture name: "./ajax-images/webcam/snap00.jpg"
var webcamUpdate =  5; // Webcam update interval in minutes (set to 0 for no updates i.e. visitor must reload page for updates)
var swapIconWithWebcam = false; // swap current conditions icon (tag "ajaxconditionicon2") with webcam image during day and night unless useCamAtNight=false;
var useCamAtNight = false; // use current conditions icon at night time instead of webcam image
var delayDayNight = 15;    // day starts given minutes before sunrise and night starts given min after sunset - for night icon switch
var webcamImg =
   {src   : imgDir+"webcam/snap00.jpg" // full path and image name of your current webcam picture
   ,srcNgt: imgDir+"webcam/snap00.jpg" // full path and image name of your current nightcam picture (after/before "delayDayNight")
   ,width : 170 ,height: 127 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style : "border: 2px solid silver;"
   ,align : ""
   };


//------------------------------------------------------------------------------
// DYNAMIC SCRIPT IMAGES                                                       .
//------------------------------------------------------------------------------

// THERMOMETER: optional setting for the dynamic thermometer graphic
// tag: "ajaxthermometer"
// ... you'll need the new thermometer.php script from www.bashewa.com for UOM switching.
// ... read here  : http://www.weather-watch.com/smf/index.php/topic,39180.0.html
// ... get it here: http://www.bashewa.com/download-thermometer.php
var thermometerImg =
   {src   : "./thermometer.php" // path and script for dynamic thermometer PNG image (optional)
   ,width : 54 ,height: 170 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style : ""
   ,align : ""
   };


//------------------------------------------------------------------------------
// CLOUD BASE: optional setting for the dynamic cloud height graphic
// tag: "ajaxcloudheightimg"
// ... you'll need the cloud-base.php script from www.bashewa.com
// ... get it here: http://www.bashewa.com/download-cloud-base.php
var cloudImg =
   {src   : "./cloud-base.php" // path and script for dynamic cloud height image (optional)
   ,width : 100 ,height: 200 ,hspace: 0 ,vspace: 0 ,border: 0
   ,style : "border: 2px solid silver;"
   ,align : ""
   };


//------------------------------------------------------------------------------
// DYNAMIC CURRENT ICON: optional setting for the dynamic current conditions graphic
// tag: "ajaxconditionicon2"
// ... you'll need the dynamic-icon.php script from www.bashewa.com
// ... get it here: (NOT YET IMPLEMENTED)
// See wxIconSetD above in DASHBOARD ICONS.


//------------------------------------------------------------------------------
// UNITS-OF-MEASURE / UOM (optional)
// ... change to your desired UOM's and decimal places
//----------------------------------------------------------------------
// ENGLISH (Imperial)
var imperialUOM =
   {Temp: '&deg;F' // [ &dec;C | &dec;F ] ......................... Temp
   ,Wind: 'mph'    // [ kts | mph | kph  | m/s  ] ................. Wind
   ,Baro: 'inHg'   // [ hPa | mb  | inHg | mmHg ] ................. Baro
   ,Rain: 'in'     // [ mm  | in  ] ............................... Rain
   ,Snow: 'in'     // [ cm  | in  ] ............................... Snow
   ,Alti: 'ft'     // [ m   | ft  ] ........................... Altitude
   ,Dist: 'miles'  // [ m   | km  | miles] .................... Distance
   ,Deg : '&deg;'  // [ deg | &deg; ] ..................... Direction/UV
   ,dateFmt: "m/j/Y"  // 04/26/2009 ... same format as "date" function in php ( eg. for US use "m/j/Y", for GB use "d/M/Y" )
   ,timeFmt: "g:i:s a" // 3:27:01 pm ... same format as "date" function in php ( for 24h clock use "H:i:s" )
   ,timeHM : "g:i a"   // 3:27 pm ...... same format as "date" function in php ( for 24h clock use "H:i" )
   };
var imperialDP = // decimal places
   { Temp:1 ,Wind:1 ,Baro:2 ,Rain:2 ,Snow:2 ,Alti:0 ,Dist:2 ,UV:0 };
//----------------------------------------------------------------------
// METRIC
var metricUOM =
   {Temp: '&deg;C' // [ &dec;C | &dec;F ] ..........................Temp
   ,Wind: 'm/s'    // [ kts | mph | kph  | m/s  ] ................. Wind
   ,Baro: 'hPa'    // [ hPa | mb  | inHg | mmHg ] ................. Baro
   ,Rain: 'mm'     // [ mm  | in  ] ............................... Rain
   ,Snow: 'cm'     // [ cm  | in  ] ............................... Snow
   ,Alti: 'm'      // [ m   | ft  ] ........................... Altitude
   ,Dist: 'km'     // [ m   | km  | miles] .................... Distance
   ,Deg : '&deg;'  // [ deg | &deg; ] ........................ Direction
   ,dateFmt: "Y-m-d" // same format as "date" function in php (eg. for DE use "j.M.Y", for NL use "j-M-Y)
   ,timeFmt: "H:i:s" // 15:27:01 ...same format as "date" function in php  ( for 12h clock use "g:i:s a" )
   ,timeHM : "H:i"   // 15:27 ......same format as "date" function in php  ( for 12h clock use "g:i a" )
   };
var metricDP = // decimal places
   { Temp:1 ,Wind:1 ,Baro:0 ,Rain:1 ,Snow:0 ,Alti:0 ,Dist:2 ,UV:0 };


// =============================================================================
//                            I M P O R T A N T                                .
//                          LANGUAGE TRANSLATION                               .
//           No need to change if you're using the Multilingual Templates:     .
//           (af, de, dk, el, en, es, fi, fr, it, nl, no, pl, pt, se, us)      .
// =============================================================================
// You don't need to customize this area if you're using English or if you're  .
// using the Canada/World Mutilingual templates. Instead change your language  .
// in "Settings.php" i.e. $SITE['lang']='nl';                                  .
// =============================================================================
var langRestart  = 'Restart live updates';
var langRefresh  = 'REFRESH'; // RESTART
var langPauseMsg = 'Updates paused';
var langMonths   = new Array ("January","February","March","April","May","June","July","August","September","October","November","December");
var langDays     = new Array ("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
var langDaySuf   = new Array ("st","nd","rd","th"); // day suffix as in 1st, 2nd, 3rd, 4th
var langAM_PM    = new Array ("am","pm","AM","PM");
var langWindUOM  = new Array ("kts", "mph", "km/h", "m/s");

var langBaroTrend = new Array ("Steady", "Rising Slowly", "Rising Rapidly", "Falling Slowly", "Falling Rapidly");

var langUVWords = new Array (
 "None", "Low", "Medium", "High",
 "Very&nbsp;High", /* be sure to include &nbsp; for space */
 "Extreme", "Unknown" );

var langBeaufort = new Array ( /* Beaufort 0 to 12 in array */
 "Calm", "Light air", "Light breeze", "Gentle breeze", "Moderate breeze", "Fresh breeze",
 "Strong breeze", "Near gale", "Gale", "Strong gale", "Storm", "Violent storm", "Hurricane");

var langWindDir = new Array( /* used for alt and title tags on wind dir arrow and wind direction display */
   "N", "NNE", "NE", "ENE",
   "E", "ESE", "SE", "SSE",
   "S", "SSW", "SW", "WSW",
   "W", "WNW", "NW", "NNW");

var langWindCalm = 'Calm';
var langGustNone = 'None';
var langWindFrom = 'Wind from '; /* used on alt/title tags on wind direction arrow*/

var langThermoCurrently = 'Currently: '; /* used on alt/title tags for thermometer */
var langThermoMax = 'Max: ';
var langThermoMin = 'Min: ';

var langTempRising    = 'Warmer %s'; /* used for trend arrow alt/title tags .. %s marks where value will be placed */
var langTempFalling   = 'Colder %s';
var langTempLastHour  = ' than last hour.';
var langTempYesterday = ' than yesterday.';

var langBaroRising  = 'Rising %s '; /* used for trend arrow alt/title tags .. %s marks where value will be placed */
var langBaroFalling = 'Falling %s ';
var langBaroPerHour = '/hour.'; /* will be assembled as rising/falling + value + uom + perhour text */

var langHeatWords = new Array (
 'Unknown', 'Extreme Heat Danger', 'Heat Danger', 'Extreme Heat Caution', 'Extremely Hot', 'Uncomfortably Hot',
 'Hot', 'Warm', 'Comfortable', 'Cool', 'Cold', 'Uncomfortably Cold', 'Very Cold', 'Extreme Cold' );

var langMoonPhases = new Array (
 'NEW MOON' , 'Evening Crescent', 'FIRST QUARTER', 'Waxing Gibbous',
 'FULL MOON', 'Waning Gibbous'  , 'LAST QUARTER' , 'Morning Crescent' );

// =============================================================================
//                      E N D   of   S E T T I N G S                           .
// =============================================================================





// =============================================================================
//                            I M P O R T A N T                                .
//           DO NOT customize any of the stuff below this point.               .
//            Everything is controlled by your settings above.                 .
// =============================================================================

// FOR DEBUGING --
var doTooltip = 0; // set to 1 to have ajaxed variable names appear as tooltips (except for graphics)

// always override the settings above if testing on a local host
if (document.location.protocol=="file:"
||  document.location.href.search("localhost")!=-1) {
   maxupdates = 0;
   reloadTime = 5000;
}
// END DEBUGING --

// -----------------------------------------------------------------------------
var ie4     = document.all;
var browser = navigator.appName;
var ie8 = false;
if (ie4 && /MSIE (\d+\.\d+);/.test(navigator.userAgent)) { //test for MSIE x.x;
var ieversion = new Number(RegExp.$1); // capture x.x portion and store as a number
   if (ieversion>=8) {
      ie4=false;
      ie8=true;
    }
}

var langTransLookup = new Object;  // storage area for key/value for current conditions translation
var timerAjaxLoader = null; // for resetting loading timeouts
var notify = 0;     // for error 404 alerts
var extraRetry = 6; // retry time in seconds to fetch clientrawextra.txt
var extraRetryInterval = 10000; // fetch retry in milliseconds for clientrawextra.txt
var clientrawextra = null;

var updates = 0; // update counter for limit by maxupdates
var counterSecs = 0; // for MCHALLIS counter script from weather-watch.com (adapted by K. True)
var lastajaxtimeformat = null; // used for reseting the counter when a real update is done

var ajaxElements = new Array(); // used for reseting green flashing elements
var uomElements  = new Array(); // used for storing page values for switching units-of-measure

var PauseMsg  = ""; // leave blank
var Indicator = ""; // leave blank
var gotSettings, uom, dp;

//-------------------------------------------------------------- functions start
function initialize() {
   extraUrl = clientrawFile.replace("clientraw","clientrawextra");
   uom = (useUOM=='M')? metricUOM : imperialUOM;
   dp  = (useUOM=='M')? metricDP  : imperialDP;

   if (!showUOM) { // Strip UOM if not required i.e. if the UOM is outside the ajaxtag spans
      imperialUOM.Temp=''; metricUOM.Temp='';
      imperialUOM.Wind=''; metricUOM.Wind='';
      imperialUOM.Baro=''; metricUOM.Baro='';
      imperialUOM.Rain=''; metricUOM.Rain='';
      imperialUOM.Snow=''; metricUOM.Snow='';
      imperialUOM.Alti=''; metricUOM.Alti='';
      imperialUOM.Dist=''; metricUOM.Dist='';
      imperialUOM.Deg =''; metricUOM.Deg ='';
   }
};

function ajaxLoader_restart() {
updates = 0;
counterSecs = 0;
lastajaxtimeformat = null;
set_ajax_obs("ajaxindicator",Indicator);
ajaxLoader_clientextra(extraUrl + '?' + new Date().getTime());
if (timerAjaxLoader!="null") clearTimeout(timerAjaxLoader);
timerAjaxLoader = setTimeout("ajaxLoader(clientrawFile + '?' + new Date().getTime())", 20);
};


// toggle the units-of-measure between METRIC and ENGLISH
function ajax_changeUnits () {
useUOM  = (useUOM=="E") ? "M" : "E" ;
uom     = (useUOM=="M") ? metricUOM : imperialUOM;
dp      = (useUOM=="M") ? metricDP  : imperialDP ;
colorM  = (useUOM=="E") ? "#cccccc"    : "#666666";
colorE  = (useUOM=="M") ? "#cccccc"    : "#666666";
var uomM = document.getElementById("uomM"); if (uomM) uomM.style.color = colorM;
var uomE = document.getElementById("uomE"); if (uomE) uomE.style.color = colorE;

switch (uom.Wind) {
case "mph": uom.langWind = langWindUOM[1]; break;
case "kph": uom.langWind = langWindUOM[2]; break;
case "m/s": uom.langWind = langWindUOM[3]; break;
default   : uom.langWind = langWindUOM[0];
}
// also convert tags convTemp, convWind ... etc.
ajax_page_convertTags();
updates = 0;
set_ajax_obs("ajaxindicator",new Date().getTime(),Indicator,flashRed);
ajaxLoader_restart();
};


// return all the elements for a given tag name on a page e.g. <span ..>
function ajax_getElements ( name ) { // name = [span, div, ...]
if (ie4 && browser != "Opera" && ! ie8)
     return (document.body.getElementsByTagName(name));
else return (document     .getElementsByTagName(name));
};

// return the "class name" for a given element (as in: class="ajax convTemp noprint")
function ajax_getClassName( element ) {
if (ie4 && browser != "Opera" && ! ie8)
     return (element.getAttribute("className"));
else return (element.getAttribute("class"));
};

String.prototype.getUnits = function() {
// match and return any [ °C |°F | &dec;C | &dec;F | mm | in | cm | ft | mb | hPa | inHg | mmHg | kts | m/s | mph | kph ]
return (this.match(/°C|°F|&dec;F|&dec;C|[a-z]+\/?[a-z]?/i));
};

// return the value
function ajax_page_getTagValue ( element, type ) {
var html = element.innerHTML;
var val  = parseFloat(html);
var uom  = html.getUnits();
return ( val.converToMetric(uom, type) );
};

// utility functions to navigate the <span class="..."> tags on the page and save their values
function ajax_page_saveTags () {
uom     = (useUOM=="M") ? metricUOM : imperialUOM;
dp      = (useUOM=="M") ? metricDP  : imperialDP ;
colorM  = (useUOM=="E") ? "gray"    : "blue";
colorE  = (useUOM=="M") ? "gray"    : "blue";
var uomM = document.getElementById("uomM"); if (uomM) uomM.style.color = colorM;
var uomE = document.getElementById("uomE"); if (uomE) uomE.style.color = colorE;
switch (uom.Wind) {
case "mph": uom.langWind = langWindUOM[1]; break;
case "kph": uom.langWind = langWindUOM[2]; break;
case "m/s": uom.langWind = langWindUOM[3]; break;
default   : uom.langWind = langWindUOM[0];
}
var i, name , iC = iF =0;
var pgElements = ajax_getElements('span');
   for(i=0; i<pgElements.length; i++) {
      name = ajax_getClassName(pgElements[i]); // could be: class="ajax convTemp"
      if (name!=null) {
         // conversion elements
         if (name.search(/convTemp|convWind|convBaro|convRain|convSnow|convDist|convAlti/i) > -1) {
            uomElements[iC++]  = [ pgElements[i], name, ajax_page_getTagValue(pgElements[i],name) ];
            ajaxElements[iF++] = pgElements[i]; // also add to flashing elements
         }
         // flashing elements
         if (name.search(/ajax/i) > -1)
            ajaxElements[iF++] = pgElements[i];
      }
   }
   var element = document.getElementById("ajaxindicator");
   if (element) Indicator = element.innerHTML;
   ajax_page_convertTags();
};

// convert "conv..." tags to required UOM
function ajax_page_convertTags () {
var i, name, val, ret;
   for(i=0; i<uomElements.length; i++) {
      name = uomElements[i][1];
      val  = uomElements[i][2];
      ret = null;
      switch (name) {
      case "convTemp": ret = val.convertTemp().toFixed(dp.Temp)+uom.Temp; break;
      case "convWind": ret = val.convertWind().toFixed(dp.Wind)+' '+uom.langWind; break;
      case "convBaro": ret = val.convertBaro().toFixed(dp.Baro)+' '+uom.Baro; break;
      case "convRain": ret = val.convertRain().toFixed(dp.Rain)+' '+uom.Rain; break;
      case "convSnow": ret = val.convertSnow().toFixed(dp.Snow)+' '+uom.Snow; break;
      case "convDist": ret = val.convertDist().toFixed(dp.Dist)+' '+uom.Dist; break;
      case "convAlti": ret = val.convertAlti().toFixed(dp.Alti)+' '+uom.Alti; break;
      }
      if (ret!=null) {
         uomElements[i][0].innerHTML = ret;
         uomElements[i][0].style.color = flashcolor;
      }
   }
};


// function for setting green flashing object
function ajax_flash_green ( name ) {
   var element = document.getElementById(name);
   if (element)  element.style.color = flashcolor;
};

// function for resetting green flashing object
function ajax_flash_reset( usecolor ) {
// reset all the <span class="ajax"...> styles to have no color override
   for(var i=0; i<ajaxElements.length; i++) {
      element = ajaxElements[i];
      element.style.color = usecolor;
   }
};


function set_ajax_obs( name, value , html , red) {
// store away the current value in both the doc and the span as lastobs="value"
// change color if value != lastobs
   var element = document.getElementById(name);
   if (! element ) { return; } // V1.04 -- don't set if missing the <span id=name> tag
   var lastobs = element.getAttribute("lastobs");
   element.setAttribute("lastobs",value);
   if (value != unescape(lastobs)) {
      element.style.color = (red)? flashRed : flashcolor;
      if ( doTooltip ) element.setAttribute("title",'AJAX tag '+name);
      element.innerHTML = ((typeof html=='undefined')?value:html); // moved inside to fix flashing issue (Jim at jcweather.us)
   }
};

function set_ajax_uom( name, show ) {
// this function will set an ID= to visible or hidden by setting the style="display: " from 'inline' or 'none'
   var element = document.getElementById(name);
   if (! element ) { return; }
   if (show) element.style.display='inline';
   else      element.style.display='none';
};
// --- end of flash-green functions


function ajax_getWindDir (winddir) {
// Take wind direction value, return the
// text label based upon 16 point compass -- function by beeker425
//  see http://www.weather-watch.com/smf/index.php/topic,20097.0.html
   windlabel = new Array("N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW");
   return windlabel[Math.floor(((parseInt(winddir) + 11) / 22.5) % 16 )];
};

function ajax_getWindDirLang (winddir) {
// Take wind direction value, return the
// text label based upon 16 point compass -- function by beeker425
// ... see http://www.weather-watch.com/smf/index.php/topic,20097.0.html
   return langWindDir[Math.floor(((parseInt(winddir) + 11) / 22.5) % 16 )];
};

function ajax_getIconName0 ( iconNr ,iconSet ) {
// perform a lookup and return the graphic name for the condition icon
// (using anole's wxsticker icon nameing convention)
  iconList = new Array(
   "day_clear",           //  0 sunny
   "night_clear",         //  1 clearnight
   "day_partly_cloudy",   //  2 cloudy
   "day_partly_cloudy",   //  3 cloudy2
   "night_partly_cloudy", //  4 cloudynight
   "day_clear",           //  5 dry
   "fog",                 //  6 fog
   "haze",                //  7 haze
   "day_heavy_rain",      //  8 heavyrain
   "day_mostly_sunny",    //  9 mainlyfine
   "mist",                // 10 mist
   "fog",                 // 11 nightfog
   "night_heavy_rain",    // 12 nightheavyrain
   "night_cloudy",        // 13 nightovercast
   "night_rain",          // 14 nightrain
   "night_light_rain",    // 15 nightshowers
   "night_snow",          // 16 nightsnow
   "night_tstorm",        // 17 nightthunder
   "day_cloudy",          // 18 overcast
   "day_partly_cloudy",   // 19 partlycloudy
   "day_rain",            // 20 rain
   "day_rain",            // 21 rain2
   "day_light_rain",      // 22 showers2
   "day_sleet",           // 23 sleet
   "day_sleet",           // 24 sleetshowers
   "day_snow",            // 25 snow
   "day_snow",            // 26 snowmelt
   "day_snow",            // 27 snowshowers2
   "day_clear",           // 28 sunny
   "day_tstorm",          // 29 thundershowers
   "day_tstorm",          // 30 thundershowers2
   "day_tstorm",          // 31 thunderstorms
   "tornado",             // 32 tornado
   "windy",               // 33 windy
   "day_partly_cloudy",   // 34 stopped rainning
   "windyrain"            // 35 windy/rain (new with V2.11)
   );

   if (iconNr >= 0 && iconNr <= 35) {
      return ('<img src="'+ iconSet.src.replace("#PIC#", iconList[iconNr])
             +'" width="' + iconSet.width
             +'" height="'+ iconSet.height
             +'" border="'+ iconSet.border
             +'" vspace="'+ iconSet.vspace
             +'" hspace="'+ iconSet.hspace
             +'" style="' + iconSet.style
             +'" align="' + iconSet.align
             +'" alt="Current conditions" />');
   }  else return '';
};


function ajax_getIconName1 ( iconNr ,iconSet ) {
// perform a lookup and return the graphic for the condition icon
// (using NWS icon nameing convention)
  iconList = new Array(
   "skc",          //  0 sunny
   "nskc",         //  1 clearnight
   "bkn",          //  2 cloudy
   "sct",          //  3 cloudy2
   "nbkn",         //  4 cloudynight
   "sct",          //  5 dry
   "fg",           //  6 fog
   "hazy",         //  7 haze
   "ra",           //  8 heavyrain
   "few",          //  9 mainlyfine
   "mist",         // 10 mist
   "nfg",          // 11 nightfog
   "nra",          // 12 nightheavyrain
   "novc",         // 13 nightovercast
   "nra",          // 14 nightrain
   "nshra",        // 15 nightshowers
   "nsn",          // 16 nightsnow
   "ntsra",        // 17 nightthunder
   "ovc",          // 18 overcast
   "bkn",          // 19 partlycloudy
   "ra",           // 20 rain
   "ra",           // 21 rain2
   "shra",         // 22 showers2
   "ip",           // 23 sleet
   "ip",           // 24 sleetshowers
   "sn",           // 25 snow
   "sn",           // 26 snowmelt
   "sn",           // 27 snowshowers2
   "skc",          // 28 sunny
   "scttsra",      // 29 thundershowers
   "hi_tsra",      // 30 thundershowers2
   "tsra",         // 31 thunderstorms
   "nsvrtsra",     // 32 tornado
   "wind",         // 33 windy
   "ra1",          // 34 stopped rainning
   "windyrain",    // 35 windy/rain (new with V2.11)
   "sunrise",      // 36 sunrise
   "sunset"        // 37 sunset
   );

   if (iconNr >= 0 && iconNr <= 37) {
      return ('<img src="'+ iconSet.src.replace("#PIC#", iconList[iconNr])
             +'" width="' + iconSet.width
             +'" height="'+ iconSet.height
             +'" border="'+ iconSet.border
             +'" vspace="'+ iconSet.vspace
             +'" hspace="'+ iconSet.hspace
             +'" style="' + iconSet.style
             +'" align="' + iconSet.align
             +'" alt="Current conditions" />');
   }  else return '';
};


function ajax_getIconName2 ( iconNr , iconSet ) {
// perform a lookup and return the graphic image for the condition icon
// (using Bashewa icon nameing convention)
iconList = new Array(
   "0-skc",           //  0 sunny
   "1-nskc",          //  1 clear night
   "2-bkn",           //  2 cloudy
   "3-sct",           //  3 mainly fine (only a few clouds)
   "4-nbkn",          //  4 cloudy night
   "5-skc",           //  5 dry (cloudy periods)
   "6-fg",            //  6 fog
   "7-hazy",          //  7 haze
   "8-hra",           //  8 heavy rain
   "9-few",           //  9 mainly fine
   "10-mist",         // 10 mist
   "11-nfg",          // 11 night fog
   "12-nhra",         // 12 night heavy rain
   "13-novc",         // 13 night overcast
   "14-nra",          // 14 night rain
   "15-nshra",        // 15 night showers
   "16-nsn",          // 16 night snow
   "17-ntsra",        // 17 night thunder
   "18-ovc",          // 18 overcast
   "19-pcld",         // 19 partly cloudy
   "20-ra",           // 20 rain
   "21-lra",          // 21 light rain
   "22-shra",         // 22 showers (light rain/drizzle)
   "23-ip",           // 23 sleet
   "24-ipra",         // 24 sleetshowers
   "25-sn",           // 25 snow
   "26-snm",          // 26 snowmelt
   "27-snsh",         // 27 snow showers (light snow/snow showers)
   "28-skc",          // 28 sunny
   "29-scttsra",      // 29 thundershowers
   "30-hi-tsra",      // 30 thundershowers2
   "31-tsra",         // 31 thunderstorms
   "32-nsvrtsra",     // 32 tornado
   "33-windy",        // 33 windy
   "34-ras",          // 34 stopped rainning
   "35-windyrain",    // 35 windy & rain
   "36-sunrise",      // 36 sunrise
   "37-sunset"        // 37 sunset
   );

   if (iconNr >= 0 && iconNr <= 37) {
      return ('<img src="'+ iconSet.src.replace("#PIC#", iconList[iconNr])
             +'" width="' + iconSet.width
             +'" height="'+ iconSet.height
             +'" border="'+ iconSet.border
             +'" vspace="'+ iconSet.vspace
             +'" hspace="'+ iconSet.hspace
             +'" style="' + iconSet.style
             +'" align="' + iconSet.align
             +'" alt="Current conditions" />');
   }  else return '';
};

//------------------------------------------------------------------------------
// CONVERSIONS: prototypes to handle conversions from clientraw data to desired units-of-measure
//------------------------------------------------------------------------------
Number.prototype.converToMetric = function ( units, type ) { // convert to C, kts, hPa, mm, cm or meters
type = (typeof type == 'undefined')? "" : type;
   switch (units.toString().toLowerCase()) { // lowercase in case we get FT or KMH etc
// temp to °C
   case "°f"    :
   case "&deg;f": return((this - 32) / 1.8); // Celsius = (this - 32) * 0.556
// wind mph, km/h and m/s to kts
   case "kmh"   :
   case "kph"   :
   case "km/h"  : return (this * 0.539956803);
   case "mph"   : return (this / 1.150779450);
   case "m/s"   : return (this / 0.514444444);
// baro inHg, mmHg and kPa to hPa
   case "inhg"  : return (this * 33.86388667);
   case "mmhg"  : return (this * 1.3332239);
   case "kpa"   : return (this * 10.0);
// rain to mm and snow to cm
   case "in"    : if (type=="convSnow") return (this * 2.54);
                  else                  return (this * 25.4);
// distance miles and meters to km
   case "m"     : return (this / 1000);
   case "miles" : return (this / 1.150779450);
// altitude to meters
   case "ft"    : return (this / 3.2808399);
   default      : return (this * 1.0);
   }
};


Number.prototype.convertTemp = function() { // °C
   switch (uom.Temp) {
   case "&deg;F": return((this * 1.8) + 32.0);
   default      : return (this * 1.0);
   }
};

Number.prototype.convertWind = function() { // kts
   switch (uom.Wind) {
   case "mph" : return (this * 1.15077945);
   case "kph" : return (this * 1.852);
   case "m/s" : return (this * 0.514444444);
   default    : return (this * 1.0);
   }
};

Number.prototype.convertBaro = function() { // hPa
   switch (uom.Baro) {
   case "inHg": return (this / 33.863886666667);
   case "mmHg": return (this / 1.3332239);
   case "kPa" : return (this * 0.1);
   default    : return (this * 1.0);
   }
};

Number.prototype.convertRain = function() { // mm
   switch (uom.Rain) {
   case "in" : return (this * 0.0393700787402);
   case "cm" : return (this * 0.1);
   default   : return (this * 1.0);
   }
};

Number.prototype.convertAlti = function() { // meters
   switch (uom.Alti) {
   case "ft" : return (this * 3.2808399); break;
   default   : return (this * 1.0);
   }
};

Number.prototype.convertSnow = function() { // cm
   switch (uom.Snow) {
   case "in" : return (this * 0.3937007874020);
   case "ft" : return (this * 0.0328083989501);
   case "mm" : return (this * 10.0);
   case "m"  : return (this * 0.1);
   default   : return (this * 1.0);
   }
};

Number.prototype.convertDist = function() { // km
   switch (uom.Dist) {
   case "m"     : return (this / 1000);
   case "miles" : return (this / 1.150779450);
   default   : return (this * 1.0);
   }
};
//----------------------------------------------------------- end of conversions


function ajax_getBeaufortNumber ( wind ) {
// return a number for the beaufort scale based on wind knots (native WD format)
  if (wind <  1 ) {return("0"); }
  if (wind <  4 ) {return("1"); }
  if (wind <  7 ) {return("2"); }
  if (wind < 11 ) {return("3"); }
  if (wind < 17 ) {return("4"); }
  if (wind < 22 ) {return("5"); }
  if (wind < 28 ) {return("6"); }
  if (wind < 34 ) {return("7"); }
  if (wind < 41 ) {return("8"); }
  if (wind < 48 ) {return("9"); }
  if (wind < 56 ) {return("10"); }
  if (wind < 64 ) {return("11"); }
  if (wind >=64 ) {return("12"); }
  return("0");
};


function ajax_getBaroTrend(btrnd) {
// routine from Anole's wxsticker PHP (adapted to JS by Ken True)
// input: trend in hPa or millibars
//   Barometric Trend(3 hour)

// Change Rates
// Rapidly: =.06 inHg; 1.5 mmHg; 2.0 hPa; 2.0 mb
// Slowly : =.02 inHg; 0.5 mmHg; 0.7 hPa; 0.7 mb

// 5 conditions
// Rising Rapidly
// Rising Slowly
// Steady
// Falling Slowly
// Falling Rapidly

// Page 52 of the PDF Manual
// http://www.davisnet.com/product_documents/weather/manuals/07395.234-VP2_Manual.pdf
// figure out a text value for barometric pressure trend
   if ((btrnd >= -0.7) && (btrnd <= 0.7)) { return(langBaroTrend[0]); }
   if ((btrnd >   0.7) && (btrnd <  2.0)) { return(langBaroTrend[1]); }
   if (btrnd >=   2.0)                    { return(langBaroTrend[2]); }
   if ((btrnd <  -0.7) && (btrnd > -2.0)) { return(langBaroTrend[3]); }
   if (btrnd <=  -2.0)                    { return(langBaroTrend[4]); }
  return(btrnd);
};

// Official colours for UV as specified by WHO, WMO & UNEP
var UVColor = new Array
   ( "none"    //  0
   , "#4EB400" //  1 Low
   , "#A0CE00" //  2
   , "#F7E400" //  3 Medium
   , "#F8B600" //  4
   , "#F88700" //  5
   , "#F85900" //  6 High
   , "#E82C0E" //  7
   , "#D8001D" //  8 Very High
   , "#FF0099" //  9
   , "#B54CFF" // 10
   , "#998CFF" // 11+Extreme
   );

function ajax_getUVrange ( uv ) { // code simplified by FourOhFour on wxforum.net
   var uvword = "None";
   var uvclr1  = UVColor[Math.min(11,Math.round(uv))];
   switch (true) {
   case (uv < 0): uvword = langUVWords[6]; uvclr2 = 'none';    break; // UV sensor is not sending
   case (uv = 0): uvword = langUVWords[0]; uvclr2 = 'none';    break; // None
   case (uv < 3): uvword = langUVWords[1]; uvclr2 = '#4EB400'; break; // Low
   case (uv < 6): uvword = langUVWords[2]; uvclr2 = '#F7E400'; break; // Medium
   case (uv < 8): uvword = langUVWords[3]; uvclr2 = '#F85900'; break; // High
   case (uv <11): uvword = langUVWords[4]; uvclr2 = '#D8001D'; break; // Very High
   case (uv >18): uvword = langUVWords[6]; uvclr2 = 'none';    break; // UV sensor is not sending
   default      : uvword = langUVWords[5]; uvclr2 = '#998CFF';        // Extreme
   }
   return ('<span style="border: 1px solid gray; text-transform: uppercase; font-weight: bold; padding-bottom: 1px; background-color: '+ uvclr1 +';">&nbsp;'+Math.round(uv) +'&nbsp;'+ uvword +'&nbsp;</span>');
}; // end ajax_getUVrange function


function ajax_getUVburnRate ( uv ) {
   var uvIdx  = Math.min(18,Math.round(uv));
   switch (uvIdx) {
   case  1: uvrate = 112;  break;
   case  2: uvrate =  56;  break;
   case  3: uvrate =  37;  break;
   case  4: uvrate =  28;  break;
   case  5: uvrate =  22;  break;
   case  6: uvrate =  19;  break;
   case  7: uvrate =  16;  break;
   case  8: uvrate =  14;  break;
   case  9: uvrate =  12;  break;
   case 10: uvrate =  11;  break;
   case 11: uvrate =  10;  break;
   case 12: uvrate =   9;  break;
   case 13: uvrate =   9;  break;
   case 14: uvrate =   8;  break;
   case 15: uvrate =   7;  break;
   case 16: uvrate =   6;  break;
   case 17: uvrate =   5;  break;
   case 18: uvrate =   4;  break;
   default: uvrate =   "--";
   }
   return uvrate;
}; // end ajax_getUVburnRate function


function ajax_getTrendArrow( nowTemp, yesterTemp, Legend, textUP, textDN, numDp) {
// generate an <img> tag with alt= and title= for rising/falling values
var diff = nowTemp.toFixed(3) - yesterTemp.toFixed(3);
var absDiff = Math.abs(diff);
var diffStr = '' + diff.toFixed(numDp);
var absDiffStr = '' + absDiff.toFixed(numDp);
var image = '';
var msg = '';

   if (diff == 0) { // no change
      msg = '';
      image = '<img src="' + imgDir + 'steady.gif'
            + '" alt="'    + msg
            + '" title="'  + msg
            + '" width="7" height="8" style="border: 0; margin: 1px 3px;" />';
   } else if (diff > 0) { // today is greater
      msg = textUP.replace(/\%s/,absDiffStr);
      image = '<img src="' + imgDir + 'rising.gif'
            + '" alt="'    + msg
            + '" title="'  + msg
            + '" width="7" height="8" style="border: 0; margin: 1px 3px;" />';
   } else { // today is lesser
      msg = textDN.replace(/\%s/,absDiffStr);
      image = '<img src="' + imgDir + 'falling.gif'
            + '" alt="'    + msg
            + '" title="'  + msg
            + '" width="7" height="8" style="border: 0; margin: 1px 3px;" />';
   }
   return ((Legend) ? (diff + Legend + image) : image);
}; // end ajax_getTrendArrow function


// function to add colored heatColorWord by Mike Challis
function ajax_getHeatColor(temp ,WindChill, Humidex) {
var fg = bg = hw = "";
var hcWord = langHeatWords[0];
var val = { fg:'', bg:'', hw:'' };
   if (temp > 32 && Humidex > 29) {
      switch (true) {
      case (Humidex   > 54): fg = "white"; bg = "#BA1928"; hw = langHeatWords[ 1]; break;
      case (Humidex   > 45): fg = "white"; bg = "#E02538"; hw = langHeatWords[ 2]; break;
      case (Humidex   > 39): fg = "black"; bg = "#E178A1"; hw = langHeatWords[ 4]; break;
      case (Humidex   > 29): fg = "white"; bg = "#CC6633"; hw = langHeatWords[ 6]; break;
      }
   } else if (WindChill < 16 ) {
      switch (true) {
      case (WindChill <-18): fg = "black"; bg = "#91ACFF"; hw = langHeatWords[13]; break;
      case (WindChill < -9): fg = "white"; bg = "#806AF9"; hw = langHeatWords[12]; break;
      case (WindChill < -1): fg = "white"; bg = "#3366FF"; hw = langHeatWords[11]; break;
      case (WindChill <  8): fg = "white"; bg = "#6699FF"; hw = langHeatWords[10]; break;
      case (WindChill < 16): fg = "black"; bg = "#89B2EA"; hw = langHeatWords[ 9]; break;
      }
   }  else if (WindChill >= 16 && temp <= 32) {
      switch (true) {
      case (temp      < 26): fg = "black"; bg = "#C6EF8C"; hw = langHeatWords[ 8]; break;
      case (temp      <=32): fg = "black"; bg = "#CC9933"; hw = langHeatWords[ 7]; break;
      }
   }
   val.fg = fg;
   val.bg = bg;
   val.hw = hw;
   return val;
}; // end ajax_getHeatColor function

function ajax_getHeatColorWord(temp ,WindChill, Humidex) {
var val = ajax_getHeatColor(temp ,WindChill, Humidex);
return ((val.fg=="")? val.hw
                    : '<span style="margin:1px;padding:2px;border: 1px #999999 solid; color: '+val.fg+'; background-color: '+val.bg+';">&nbsp;'+val.hw+'&nbsp;</span>'
                    );
};

function ajax_getHeatColorTemp(temp ,WindChill, Humidex, actTemp) {
var val = ajax_getHeatColor(temp ,WindChill, Humidex);
return ((val.fg=="")? actTemp.toFixed(dp.Temp)+uom.Temp
                    : '<span style="border: solid 1px; color: '+val.fg+'; background-color: '+val.bg+';">&nbsp;'+actTemp.toFixed(dp.Temp)+uom.Temp+'&nbsp;</span>'
                    );
};

//
// slice and dice the clientraw[49] for possible translation of current weather
//
function ajax_fixupCondition( rawcond ) {
var cond  = rawcond;
    cond  = cond.replace(/_/gm,' ');     // replace any _ with blank.
    cond  = cond.replace(/[\r\n]/gm,''); // remove embedded CR and/or LF
var conds = cond.split('/');             // split up the arguments.
var tstr = '';
   for (var i = 0;i<conds.length;i++) {
      var t = conds[i];
      t = t.toLowerCase();
      t = t.ucfirst();
      t = t.replace(/\s+$/,'');  // trim trailing blanks
      if(langTransLookup[t]) conds[i] = langTransLookup[t];
      else                   conds[i] = t;
   }
   if (conds[0].length == 0) { conds.splice(0,1);  } // remove blank entry
   if (conds[0] == conds[2]) { conds.splice(2,1); } // remove duplicate last entry
   return(conds.join(', '));
}; // end ajax_fixupCondition function


// Mike Challis' counter function (adapted by Ken True)
function ajax_countup() {
var element = document.getElementById("ajaxcounter");
   if (element) {
      element.innerHTML = counterSecs;
      counterSecs++;
   }
};


function ajax_getFireIndex ( fwi ) {
   switch (true) {
     case (fwi==0): idx = 0; break;
     case (fwi< 7): idx = 1; break;
     case (fwi<16): idx = 2; break;
     case (fwi<31): idx = 3; break;
     default      : idx = 4;
   }
return idx;
};

// -----------------------------------------------------------------------------
// MOON FUNTIONS                                                               .
// -----------------------------------------------------------------------------
function getMoonInfo () { // very crude way of determining moon phase (but very accurate)
newMoons = new Array( // Timestamp of New Moons (UNIX seconds UTC)
/* 2009 */ 1232956694, 1235525859, 1238083702, 1240629891, 1243167207, 1245699441, 1248230173, 1250762572, 1253299546, 1255844071, 1258398911, 1260965011,
/* 2010 */ 1263539579, 1266116026, 1268687080, 1271248355, 1273799231, 1276341392, 1278877313, 1281409748, 1283941880, 1286477210, 1289019244, 1291570681,
/* 2011 */ 1294131882, 1294131882, 1296700310, 1299271605, 1301841272, 1304405628, 1306962312, 1309510520, 1312051217, 1314587095, 1317121886, 1319659182, 1322201551, 1324750113,
/* 2012 */ 1327304495, 1329863812, 1332427106, 1334992779, 1337557778, 1340118351, 1342672008, 1345218916, 1347761485, 1350302723, 1352844709, 1355388222,
/* 2013 */ 1357933472, 1360480904, 1363031660, 1365586707, 1368145814, 1370707094, 1373267883, 1375826087, 1378381094, 1380933332, 1383483138, 1386030341,
/* 2014 */ 1388574989, 1388574989, 1391117983, 1393660864, 1396205280, 1398752305, 1401302565, 1403856551, 1406414595, 1408976167, 1411539417, 1414101518, 1416659662, 1419212321,
/* 2015 */ 1421759787, 1424303328, 1426844236, 1429383570, 1431922626, 1434463716, 1437009944, 1439564065, 1442126635, 1444694970, 1447264236, 1449829922,
/* 2016 */ 1452389564, 1454942453, 1457488541, 1460028286, 1462563113, 1465095782, 1467630271, 1470170826, 1472720703, 1475280802, 1477849249, 1480422119, 1482994606,
/* 2017 */ 1485562175, 1488121237, 1490669959, 1493209111, 1495741652, 1498271627, 1500803297, 1503340348, 1505885501, 1508440395, 1511005389, 1513578751,
/* 2018 */ 1516155614, 1518728902, 1521292492, 1523844016, 1526385048, 1528919174, 1531450217, 1533981591, 1536516226, 1539056950, 1541606661, 1544167329,
/* 2019 */ 1546738169, 1546738169, 1549314291, 1551888383, 1554454433, 1557010114, 1559556250, 1562095076, 1564629201, 1567161553, 1569695379, 1572234119, 1574780933, 1577337352,
/* 2020 */ 1579902223, 1582471950, 1585042163, 1587608928, 1590169341, 1592721853, 1595266463, 1597804950, 1600340550, 1602876914, 1605417056, 1607962750,
/* 2021 */ 1610514135, 1613070484, 1615630968, 1618194705, 1620759694, 1623322579, 1625880033, 1628430728, 1630975950, 1633518455, 1636060737, 1638604010,
/* 2022 */ 1641148542);

Q1Moons = new Array ( // Timestamp of First Quarter moons
/* 2009 */ 1233616466, 1236152804, 1238682880, 1241210710, 1243740198, 1246274983, 1248818470, 1251373426, 1253940727, 1256517860, 1259098851, 1261676274,
/* 2010 */ 1264244160, 1266799526, 1269342167, 1271874084, 1274399029, 1276921861, 1279447915, 1281982452, 1284529794, 1287091742, 1289666474, 1292248863,
/* 2011 */ 1294831983, 1297408815, 1299973706, 1302523729, 1305059664, 1307585465, 1310106634, 1312628984, 1315157981, 1317698107, 1320251955, 1322819685,
/* 2012 */ 1325398640, 1325398640, 1327983093, 1330564986, 1333136607, 1335693660, 1338236313, 1340767931, 1343293087, 1345816543, 1348342960, 1350876793, 1353421929, 1355980814,
/* 2013 */ 1358552834, 1361133174, 1363714105, 1366288392, 1368851851, 1371403608, 1373944894, 1376477938, 1379005859, 1381532690, 1384063167, 1386601987,
/* 2014 */ 1389152424, 1391714650, 1394285377, 1396859597, 1399432632, 1402000814, 1404561612, 1407113513, 1409656417, 1412192069, 1414723797, 1417255693, 1419791608,
/* 2015 */ 1422334224, 1424884553, 1427442299, 1430006302, 1432574532, 1435143885, 1437710718, 1440271962, 1442826096, 1445373215, 1447914541, 1450451752,
/* 2016 */ 1452986905, 1455522519, 1458061466, 1460606450, 1463159100, 1465719254, 1468284944, 1470853418, 1473421879, 1475987724, 1478548421, 1481101538,
/* 2017 */ 1483645726, 1483645726, 1486182021, 1488713651, 1491244864, 1493779693, 1496321050, 1498870464, 1501428399, 1503994581, 1506567362, 1509143005, 1511715876, 1514280154,
/* 2018 */ 1516832539, 1519373438, 1521905803, 1524433630, 1526961046, 1529491957, 1532030045, 1534578648, 1537139881, 1539713098, 1542293801, 1544874670,
/* 2019 */ 1547448455, 1550010518, 1552559383, 1555096065, 1557623598, 1560146437, 1562669805, 1565199138, 1567739474, 1570294143, 1572863214, 1575442912,
/* 2020 */ 1578026860, 1578026860, 1580607780, 1583179179, 1585736697, 1588279266, 1590809474, 1593332232, 1595853289, 1598378356, 1600912525, 1603459429, 1606020444, 1608594245,
/* 2021 */ 1611176623, 1613760504, 1616337726, 1618902128, 1621451743, 1623988602, 1626516776, 1629040931, 1631565713, 1634095636, 1636634866, 1639186642);

fullMoons = new Array ( // Timestamp of Full moons
/* 2009 */ 1234190988, 1236739201, 1239289163, 1241841830, 1244398374, 1246958600, 1249520262, 1252080269, 1254636636, 1257189302, 1259739205, 1262286957,
/* 2010 */ 1264832305, 1267375090, 1269916047, 1272457350, 1275001851, 1277551927, 1280108252, 1282669583, 1285233603, 1287797924, 1290360530, 1292919327,
/* 2011 */ 1295472210, 1298018189, 1300558247, 1303094731, 1305630683, 1308168989, 1310712091, 1313261900, 1315819668, 1318385317, 1320956377, 1323527942,
/* 2012 */ 1326094328, 1328651742, 1331199679, 1333740049, 1336275429, 1338808434, 1341341662, 1343878168, 1346421577, 1348975200, 1351540279, 1354114099, 1356690230,
/* 2013 */ 1359261660, 1361824109, 1364376586, 1366919999, 1369456050, 1371987276, 1374517074, 1377049609, 1379589306, 1382139600, 1384701470, 1387272584,
/* 2014 */ 1389848076, 1392422184, 1394989887, 1397547905, 1400095110, 1402632803, 1405164397, 1407694291, 1410226824, 1412765578, 1415312729, 1417868963,
/* 2015 */ 1420433724, 1420433724, 1423005055, 1425578868, 1428149319, 1430711135, 1433262117, 1435803682, 1438339471, 1440873466, 1443408809, 1445947679, 1448491600, 1451042032,
/* 2016 */ 1453600098, 1456165312, 1458734525, 1461302743, 1463865496, 1466420769, 1468969123, 1471512470, 1474052842, 1476591987, 1479131745, 1481674090,
/* 2017 */ 1484220954, 1486773338, 1489330638, 1491891034, 1494452655, 1497013965, 1499573437, 1502129587, 1504681408, 1507228860, 1509773133, 1512316253,
/* 2018 */ 1514859977, 1514859977, 1517405248, 1519951989, 1522500055, 1525050132, 1527603711, 1530161701, 1532723016, 1535284782, 1537844070, 1540399595, 1542951703, 1545501121,
/* 2019 */ 1548047852, 1550591600, 1553132599, 1555672512, 1558214120, 1560760403, 1563313184, 1565872275, 1568435781, 1571001115, 1573565842, 1576127677,
/* 2020 */ 1578684217, 1581233663, 1583776093, 1586313366, 1588848475, 1591384562, 1593924454, 1596470448, 1599024204, 1601586454, 1604155975, 1606728791, 1609299038,
/* 2021 */ 1611861485, 1614413932, 1616957405, 1619494418, 1622027786, 1624560153, 1627094368, 1629633848, 1632182189, 1634741926, 1637312421, 1639888739);

Q3Moons = new Array ( // Timestamp of Last Quarter moons
/* 2009 */ 1237398554, 1239975466, 1242545259, 1245104206, 1247651745, 1250189868, 1252721896, 1255251494, 1257782304, 1260317778,
/* 2010 */ 1262860934, 1262860934, 1265413841, 1267976670, 1270546815, 1273119452, 1275689669, 1278254175, 1280811609, 1283361852, 1285905310, 1288442904, 1290976733, 1293510087,
/* 2011 */ 1296046804, 1298590089, 1301141351, 1303699776, 1306263281, 1308829815, 1311397391, 1313963712, 1316526039, 1319081627, 1321629108, 1324169377,
/* 2012 */ 1326705010, 1329239192, 1331774851, 1334314300, 1336859342, 1339411467, 1341971482, 1344538657, 1347110203, 1349681719, 1352248700, 1354808040,
/* 2013 */ 1357358410, 1357358410, 1359899928, 1362434127, 1364963957, 1367493412, 1370026835, 1372568168, 1375119984, 1377682703, 1380254316, 1382830996, 1385407802, 1387979429,
/* 2014 */ 1390540983, 1393089540, 1395625715, 1398153221, 1400677281, 1403203235, 1405735774, 1408278416, 1410833214, 1413400494, 1415978328, 1418561609,
/* 2015 */ 1421142546, 1423713235, 1426269127, 1428810457, 1431340708, 1433864649, 1436387200, 1438913113, 1441446945, 1443992837, 1446553565, 1449128630,
/* 2016 */ 1451712795, 1451712795, 1454297382, 1456873985, 1459437627, 1461987181, 1464524152, 1467051723, 1469574205, 1472096693, 1474624743, 1477163701, 1479717285, 1482285534,
/* 2017 */ 1484864187, 1487446512, 1490025613, 1492595980, 1495154214, 1497699414, 1500233342, 1502759895, 1505284098, 1507811282, 1510346276, 1512892376,
/* 2018 */ 1515450448, 1518018995, 1520594566, 1523172020, 1525745451, 1528310038, 1530863620, 1533406873, 1535942448, 1538473719, 1541004204, 1543537321, 1546076238,
/* 2019 */ 1548623544, 1551180574, 1553746356, 1556317272, 1558888523, 1561456062, 1564017575, 1566572324, 1569120293, 1571661795, 1574198048, 1576731612,
/* 2020 */ 1579266095, 1581805160, 1584351341, 1586905111, 1589465159, 1592029616, 1594596688, 1597164374, 1599730051, 1602290567, 1604843375, 1607387941,
/* 2021 */ 1609925954, 1609925954, 1612460371, 1614994355, 1617530669, 1620071534, 1622618844, 1625174059, 1627737579, 1630307754, 1632880756, 1635451674, 1638016263, 1640572030);

   dt = new Date().getTime()/1000;  // Unix timestamp
   if (dt < newMoons[1])                 alert("Moon date must be after " +newMoons[1].date());
   if (dt > newMoons[newMoons.length-1]) alert("Moon date must be before "+newMoons[newMoons.length-1].date());

   for (mi in newMoons) // find next New Moon from given date
      if (newMoons[mi]>dt) break;

   // Get Moon dates
   NM = newMoons [mi-1]; // previous new moon
   Q1 = Q1Moons  [mi-1]; // 1st Q end
   Q2 = fullMoons[mi-1]; // 2nd Q end - Full moon
   Q3 = Q3Moons  [mi-1]; // 3rd Q end
   Q4 = newMoons [mi  ]; // 4th Q end - next new moon

   // Divide each phase into 7 periods (4 phases x 7 = 28 periods)
   Q1p = Math.round((Q1-NM)/7);
   Q2p = Math.round((Q2-Q1)/7);
   Q3p = Math.round((Q3-Q2)/7);
   Q4p = Math.round((Q4-Q3)/7);

   // Determine start and end times for major phases (lasting 1 period of 28)
   NMe = NM+(Q1p/2);                      //  0% .... - New moon
   Q1s = Q1-(Q1p/2);  Q1e = Q1+(Q2p/2);   // 50% 1stQ - First Quarter
   Q2s = Q2-(Q2p/2);  Q2e = Q2+(Q3p/2);   //100% 2ndQ - Full moon
   Q3s = Q3-(Q3p/2);  Q3e = Q3+(Q4p/2);   // 50% 3rdQ - Last Quarter
   NMs = Q4-(Q4p/2);                      //  0% 4thQ - New Moon

// Determine age of moon in days since last new moon
   age = (dt - newMoons[mi-1])/86400; // age in days since last new moon
   dd  = Math.floor(age);
   hh  = Math.floor((age-dd)*24);
   mm  = Math.floor((((age-dd)*24)-hh)*60);
   age = dd+' days, '+hh+' hours, '+mm+' minutes';

// Illumination
   switch (true) { // Determine moon age in degrees (0 to 360)
   case (dt<=Q1): ma = (dt - NM) * (90 / (Q1 - NM))+  0; break; // NM to Q1
   case (dt<=Q2): ma = (dt - Q1) * (90 / (Q2 - Q1))+ 90; break; // Q1 to FM
   case (dt<=Q3): ma = (dt - Q2) * (90 / (Q3 - Q2))+180; break; // FM to Q3
   case (dt<=Q4): ma = (dt - Q3) * (90 / (Q4 - Q3))+270; break; // Q3 to NM
   }
   ill = Math.abs(Math.round(100*(1+Math.cos(ma*(Math.PI/180)))/2)-100);

// Deterime picture number (0-27) and moon phase
   switch (true) {
   case (dt<=NMe): pic =  0;                  ph = langMoonPhases[0]; break;
   case (dt< Q1s): pic =  1  +((dt-NMe)/Q1p); ph = langMoonPhases[1]; break; // or Waxing Crescent
   case (dt<=Q1e): pic =  7;                  ph = langMoonPhases[2]; break;
   case (dt< Q2s): pic =  7.5+((dt-Q1e)/Q2p); ph = langMoonPhases[3]; break;
   case (dt<=Q2e): pic = 14;                  ph = langMoonPhases[4]; break;
   case (dt< Q3s): pic = 14.5+((dt-Q2e)/Q3p); ph = langMoonPhases[5]; break;
   case (dt<=Q3e): pic = 21;                  ph = langMoonPhases[6]; break;
   case (dt< NMs): pic = 21.5+((dt-Q3e)/Q4p); ph = langMoonPhases[7]; break; // or Waning Crecent
   default       : pic =  0;                  ph = langMoonPhases[0];
   }
   info =
      {pic  : Math.round(pic)
      ,phase: ph
      ,age  : age
      ,ill  : ill
      };
return info;
}; // end getMoonInfo function


// ----------
// PROTOTYPES (PHP equivalents)
// ----------

// Padding: left and right .. similar to "str_pad" function in php
String.prototype.str_pad = function ( cnt, chr, typ ) {
var sPad = "";
var char = (typeof chr == 'undefined')? "0" : chr;
var type = (typeof typ == 'undefined')? "R" : typ;
var iPad = cnt - Math.floor(this).toString().length;
   for (var i=0;i<iPad;i++) sPad+=char;
   return ((type=="R")? (sPad+this) : (this+sPad));
};

// Make a string's first character uppercase ... similar to "ucfirst" function in php
String.prototype.ucfirst = function () {
   return this.substr(0,1).toUpperCase() + this.substr(1,this.length);
};

// Date formating ... similar to "date" function in php
Date.prototype.date = function ( fmt ) { // for fmt see "date" function in php eg. str = dt.date("d/m/Y H:i:s") = "28/04/2009 17:37:09"
// formats not supported: N, z, W, t, L, o, B, u, e, I, O, P, T, c, r, U
   if (typeof fmt == 'undefined')
      return (this.toLocaleString()); // ... 26 April 2009 02:12:58 PM

   day = this.getDay();      // 0-6  (0=Sunday)
   dat = this.getDate();     // 1-31
   mth = this.getMonth()+1;  // 1-12 (1=January)
   yer = this.getFullYear(); // eg. 2009
   hrs = this.getHours();    // 0-23
   min = this.getMinutes();  // 0-59
   sec = this.getSeconds();  // 0-59
   tzo = this.getTimezoneOffset()*60; // -43200 through 50400 seconds
   switch (dat%10) { // day suffix eg. 23rd
   case  1: suf = dat==11? langDaySuf[3] : langDaySuf[0];
   case  2: suf = dat==12? langDaySuf[3] : langDaySuf[1];
   case  3: suf = dat==13? langDaySuf[3] : langDaySuf[2];
   default: suf =          langDaySuf[3];
   }

// date (d, j)
   fmt = fmt.replace("d" ,dat.toString().str_pad(2)); // 01 to 31
   fmt = fmt.replace("j" ,dat); //  1 to 31
// week (w)
   fmt = fmt.replace("w" ,day); //  0 to 6
// month (m, n)
   fmt = fmt.replace("m" ,mth.toString().str_pad(2)); //  01 to 12
   fmt = fmt.replace("n" ,mth); // 1 to 12
// year (Y, y)
   fmt = fmt.replace("Y" ,yer);
   fmt = fmt.replace("y" ,yer%100);
// am/pm (A, a)
   ampm = (hrs<12)? langAM_PM[0] : langAM_PM[1];
   AMPM = (hrs<12)? langAM_PM[2] : langAM_PM[3];
   fmt = fmt.replace("a" ,ampm);
   fmt = fmt.replace("A" ,AMPM);
// hours (g, G, h, H)
   fmt = fmt.replace("g" ,hrs%12); // 1 to 12
   fmt = fmt.replace("G" ,hrs);    // 1 to 23
   fmt = fmt.replace("h" ,(hrs%12).toString().str_pad(2)); // 00 to 12
   fmt = fmt.replace("H" ,hrs.toString().str_pad(2));      // 00 to 23
// minutes (i)
   fmt = fmt.replace("i" ,min.toString().str_pad(2)); // 00 to 59
// seconds (s)
   fmt = fmt.replace("s" ,sec.toString().str_pad(2)); // 00 to 59
// timezone (Z)
   fmt = fmt.replace("Z" ,tzo); // -43200 through 50400 seconds
// other (S ,l ,D ,F, M)
   fmt = fmt.replace(/(\d+)S/g ,"$1"+suf); // eg. rd
   fmt = fmt.replace(/l(\s|,|-|\.)/ ,langDays[day]+"$1");                    // eg. Sunday
   fmt = fmt.replace(/D(\s|,|-|\.)/ ,langDays[day].substring(0,3)+"$1");     // eg. Sun
   fmt = fmt.replace(/F(\s|,|-|\.)/ ,langMonths[mth-1]+"$1");                // eg. January
   fmt = fmt.replace(/M(\s|,|-|\.)/ ,langMonths[mth-1].substring(0,3)+"$1"); // eg. Jan
   return (fmt);
};

// returns "-" if number is less than 0
Number.prototype.sign = function () {
   return (this<0)? "-" : "";
};
// return rounded integer value of a number
Number.prototype.intval = function( dp ) {
   return (Math.floor(Math.abs(this.toFixed(dp))));
};
// return fraction as a rounded integer number
Number.prototype.fraction = function( dp ) {
   frac = Math.abs(this) - Math.floor(Math.abs(this));
   return frac.toFixed(dp);
};

// Number formating ... thousands seperator and decimal point ... similar to "number_format" function in php
// Format a number with grouped thousands eg. str = num.number_format("23823.87273") = "23,823.87"
Number.prototype.number_format = function ( dp ,dpSep ,thouSep ) {
if (typeof dp == 'undefined') dp = 0;
if (typeof dpSep == 'undefined') dpSep = ".";
if (typeof thouSep == 'undefined') thouSep = ",";
var inum = this.intval(dp).toString();
var frac = this.fraction(dp).toString().substr(2);
var ret  = this.sign();
var f = (inum.length%3==0) ? 3 : inum.length%3;
   ret+= inum.substr(0, f);
   for (i=f; i<inum.length; i+=3)
      ret+= thouSep+inum.substr(i,3);
   if (dp>0) ret+=dpSep+frac;
return (ret);
};

// =============================================================================
// Get clientrawextra.txt data  (only needs to be loaded once)                 .
// -----------------------------------------------------------------------------
function ajaxLoader_clientextra(urlExtra) {
var rawextra;
if (document.getElementById)
var x = (window.ActiveXObject) ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(urlExtra);

if (x) { // got something back
   x.onreadystatechange = function() {
      try {
         if (x.readyState==4 && x.status==404) { // not found
            if (notify==1) alert("DASHBOARD ERROR:\nYour \""+extraUrl+"\" file could not be found.");
            notify++;
         } else
         if (x.readyState==4 && x.status==200) { // Mike Challis added fix to fix random error: NS_ERROR_NOT_AVAILABLE
            rawextra = x.responseText.split(' ');
            var wdpattern=/\d+\.\d+.*!!/; // looks for '!!nn.nn!!' version string
            if (rawextra[0]=='12345' && (wdpattern.test(x.responseText)) )
               clientrawextra = rawextra;
            else // only save if fully downloaded
               setTimeout("ajaxLoader_clientextra(extraUrl + '?' + new Date().getTime())", extraRetryInterval);
            notify=0;
         } // END if (x.readyState == 4 && x.status == 200)
      } // END try
      catch(e){}  // Mike Challis added fix to fix random error: NS_ERROR_NOT_AVAILABLE
   }; // END x.onreadystatechange = function()

   x.open("GET", urlExtra, true);
   x.send(null);
} // END if(x)
};

// ================================================================================
//  Get clientraw.txt data and format <span class="ajax" id="ajax..."></span> areas
// --------------------------------------------------------------------------------
function ajaxLoader(url) {
var rand = new Date().getTime();

if (document.getElementById) {
   var x = (window.ActiveXObject) ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(url);
} else
   set_ajax_obs('ajaxProgress',rand+'elem','ERROR: No elements');

if (x) { // got something back
x.onreadystatechange = function() {
try {
   set_ajax_obs('ajaxProgress',rand+'ready','-- READY --');
   if (x.readyState==4 && x.status==200) { // Mike Challis added fix to fix random error: NS_ERROR_NOT_AVAILABLE
   var clientraw = x.responseText.split(' ');
   // now make sure we got the entire clientraw.txt  -- thanks to Johnnywx
   // valid clientraw.txt has '12345' at start and '!!' at end of record
   var wdpattern=/\d+\.\d+.*!!/; // looks for '!!nn.nn!!' version string
   // If we have a valid clientraw file AND updates is < maxupdates

   if (clientraw[0] == '12345'
   &&  wdpattern.test(x.responseText)
   && (updates <= maxupdates || maxupdates > 0) ) {
   set_ajax_obs('ajaxProgress',rand+'start','-- START --');

   if (maxupdates > 0) updates++; // increment counter if needed

// -----------------------------------------------------------------------------
// Date / Time (WD)
      set_ajax_obs('ajaxProgress',rand+'date','in Dates');
      WDdt = new Date( clientraw[141], clientraw[36]-1, clientraw[35], clientraw[29], clientraw[30], clientraw[31]);

      set_ajax_obs("ajaxndate"   , WDdt.date("M j, Y")); // Apr 16, 2009
      set_ajax_obs("ajaxndate2"  , WDdt.date("d-M-Y"));  // 16-Apr-2009
      set_ajax_obs("ajaxntime"   , WDdt.date("H:i"));    // 14:37
      set_ajax_obs("ajaxntimess" , WDdt.date("H:i:s"));  // 14:37:07
      set_ajax_obs("ajaxdname"   , WDdt.date("D"));      // Mon

      // current date and time of observation in clientraw.txt
      set_ajax_obs("ajaxdatetime", WDdt.date(uom.dateFmt+' '+uom.timeFmt)); // 16/4/2009 02:37:07pm
      set_ajax_obs("ajaxdate@"   , WDdt.date(uom.dateFmt) + ' @ '); // 6/4/2009 @
      set_ajax_obs("ajaxdate"    , WDdt.date(uom.dateFmt)); // 16/4/2009
      set_ajax_obs("ajaxtime"    , WDdt.date(uom.timeFmt)); // 02:37:07pm
      set_ajax_obs("gizmodate"   , WDdt.date(uom.dateFmt)); // 16/4/2009
      set_ajax_obs("gizmotime"   , WDdt.date(uom.timeFmt)); // 02:37:07pm


// Outdoor temperature ... (°C)
//clientraw[4] = 40.7; // uncomment this for debugging
      set_ajax_obs('ajaxProgress',rand+'temp','in Temp'); // "ajaxProgress" tag is for debugging and can be deleted
      temp     = parseFloat(clientraw[ 4]).convertTemp();
      templast = parseFloat(clientraw[90]).convertTemp(); // last hours temp
      tempmax  = parseFloat(clientraw[46]).convertTemp();
      tempmin  = parseFloat(clientraw[47]).convertTemp();
      if (clientrawextra!=null)
           temp24hAgo = parseFloat(clientrawextra[21]).convertTemp();
      else temp24hAgo = temp;
      temp24hDiff = temp - temp24hAgo;

      set_ajax_obs("ajaxtemp"   , temp.toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxtempNoU", temp.toFixed(dp.Temp));
      set_ajax_obs("gizmotemp"  , temp.toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxbigtemp", temp.toFixed(dp.Temp) + uom.Temp);

      temparrow = ajax_getTrendArrow( temp
                                    , templast
                                    , ''
                                    , langTempRising+uom.Temp+langTempLastHour
                                    , langTempFalling+uom.Temp+langTempLastHour
                                    , 1
                                    );
      set_ajax_obs("ajaxtemparrow" , (temp-templast).toFixed(dp.Temp), temparrow);
      set_ajax_obs("gizmotemparrow", temparrow);

      temprate = temp - templast;
      temprate = temprate.toFixed(1);
      if (temprate > 0.0) { temprate = '+' + temprate; } // add a '+' for positive rates
      set_ajax_obs("ajaxtemprate" , temprate + uom.Temp);
      set_ajax_obs("gizmotemprate", temprate + uom.Temp);

      set_ajax_obs("ajaxtempmax"   , tempmax    .toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxtempmin"   , tempmin    .toFixed(dp.Temp) + uom.Temp);

      set_ajax_obs("ajax24hDiff"   , temp24hDiff.toFixed(dp.Temp) + uom.Temp);
      diffarrow = ajax_getTrendArrow( temp
                                    , temp24hAgo
                                    , ''
                                    , langTempRising+uom.Temp+langTempYesterday
                                    , langTempFalling+uom.Temp+langTempYesterday
                                    , 1
                                    );
      set_ajax_obs("ajax24hDiffarrow", (temp-temp24hDiff).toFixed(dp.Temp), diffarrow);


// Thermometer ...
      set_ajax_obs('ajaxProgress',rand+'term','in Thermometer');
      thermometerText = langThermoCurrently + temp.toFixed(dp.Temp) + uom.Temp
                  + ", " + langThermoMax + tempmax.toFixed(dp.Temp) + uom.Temp
                  + ", " + langThermoMin + tempmin.toFixed(dp.Temp) + uom.Temp;

      param = '?uom=' + useUOM
            + '&t='   + temp.toFixed(dp.Temp)
            + '&iW='  + thermometerImg.width
            + '&iH='  + thermometerImg.height;
      set_ajax_obs("ajaxthermometer", param
         , '<img src="' + thermometerImg.src + param
         + '" alt="'    + thermometerText
         + '" title="'  + thermometerText
         + '" width="'  + thermometerImg.width
         + '" height="' + thermometerImg.height
         + '" hspace="' + thermometerImg.hspace
         + '" vspace="' + thermometerImg.vspace
         + '" border="' + thermometerImg.border
         + '" align="'  + thermometerImg.align
         + '" style="'  + thermometerImg.style
         + '" />');


// Dewpoint ... (°C)
//clientraw[72] = -2.4
      set_ajax_obs('ajaxProgress',rand+'dewp','in Dewpoint');
      dew    = parseFloat(clientraw[ 72]).convertTemp();
      dewmin = parseFloat(clientraw[139]).convertTemp();
      dewmax = parseFloat(clientraw[138]).convertTemp();

      set_ajax_obs("ajaxdew"   , dew   .toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("gizmodew"  , dew   .toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxdewmin", dewmin.toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxdewmax", dewmax.toFixed(dp.Temp) + uom.Temp);


// WetBulb ... (°C)
      set_ajax_obs('ajaxProgress',rand+'wetb','in Wetbulb');
      wetbulb = parseFloat(clientraw[159]).convertTemp();
      set_ajax_obs("ajaxwetbulb",wetbulb.toFixed(dp.Temp) + uom.Temp);

// Humidex ... (°C)
      humidex    = parseFloat(clientraw[45]).convertTemp();
      humidexmin = parseFloat(clientraw[76]).convertTemp();
      humidexmax = parseFloat(clientraw[75]).convertTemp();

      set_ajax_obs("ajaxhumidex"   , humidex   .toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxhumidexmin", humidexmin.toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxhumidexmax", humidexmax.toFixed(dp.Temp) + uom.Temp);


// Heat Index ... (°C)
      heatidx    = parseFloat(clientraw[112]).convertTemp();
      heatidxmin = parseFloat(clientraw[111]).convertTemp();
      heatidxmax = parseFloat(clientraw[110]).convertTemp();

      set_ajax_obs("ajaxheatidx"   , heatidx   .toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxheatidxmin", heatidxmin.toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxheatidxmax", heatidxmax.toFixed(dp.Temp) + uom.Temp);


// WindChill ... (°C)
      windchill    = parseFloat(clientraw[44]).convertTemp();
      windchillmin = parseFloat(clientraw[78]).convertTemp();
      windchillmax = parseFloat(clientraw[77]).convertTemp();

      set_ajax_obs("ajaxwindchill"   , windchill   .toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxwindchillmin", windchillmin.toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxwindchillmax", windchillmax.toFixed(dp.Temp) + uom.Temp);


// FeelsLike ... (°C)
      set_ajax_obs('ajaxProgress',rand+'feel','in Feelslike');
      temp = clientraw[4]; // note.. temp in C
      switch (true) {
      case (temp<=16.0): feelslike = clientraw[44]; break; // use WindChill
      case (temp>=27.0): feelslike = clientraw[45]; break; // use Humidex
      default          : feelslike = temp;                 // use temperature
      }
      feelslike  = Math.round(parseFloat(feelslike).convertTemp());
      set_ajax_obs("ajaxfeelslike" , feelslike + uom.Temp);
      
      //Apptemp own addition
      set_ajax_obs("ajaxapparent" , clientraw[130] + uom.Temp);

      // mike challis added heatColorWord feature
      heatColorWord = ajax_getHeatColorWord(clientraw[4],clientraw[44],clientraw[45]);
      set_ajax_obs("ajaxheatcolorword", heatColorWord);

      temp = parseFloat(clientraw[ 4]).convertTemp();
      tempColorBg = ajax_getHeatColorTemp(clientraw[4],clientraw[44],clientraw[45], temp);
      set_ajax_obs("ajaxtempcolor", temp.toFixed(dp.Temp), tempColorBg);


// Apparent temperature ... (°C)
      apparenttemp    = parseFloat(clientraw[130]).convertTemp();
      apparenttempmin = parseFloat(clientraw[136]).convertTemp();
      apparenttempmax = parseFloat(clientraw[137]).convertTemp();

      set_ajax_obs("ajaxapparenttemp"   , apparenttemp   .toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxapparenttempmin", apparenttempmin.toFixed(dp.Temp) + uom.Temp);
      set_ajax_obs("ajaxapparenttempmax", apparenttempmax.toFixed(dp.Temp) + uom.Temp);


// Humidity ... %
//clientraw[5] = 100;
      set_ajax_obs('ajaxProgress',rand+'humi','in Humidity');
      humidity = clientraw[5];
      set_ajax_obs("ajaxhumidity" , humidity);
      set_ajax_obs("gizmohumidity", humidity);
      // sorry.. no min/max data for humidity available in clientraw.txt


// Pressure ... (hPa)
      set_ajax_obs('ajaxProgress',rand+'baro','in Baro');
      pressure      = parseFloat(clientraw[  6]).convertBaro();
      pressuremin   = parseFloat(clientraw[132]).convertBaro();;
      pressuremax   = parseFloat(clientraw[131]).convertBaro();;
      pressuretrend = parseFloat(clientraw[ 50]).convertBaro();
      barotrendtext = ajax_getBaroTrend(clientraw[50]);

      set_ajax_obs("ajaxbaro"      , pressure.toFixed(dp.Baro) +' '+ uom.Baro);
      set_ajax_obs("ajaxbaroNoU"   , pressure.toFixed(dp.Baro));
      set_ajax_obs("gizmobaro"     , pressure.toFixed(dp.Baro) +' '+ uom.Baro);

      pressuretrend = pressuretrend.toFixed(dp.Baro+1);
      if (pressuretrend > 0.0) {pressuretrend = '+' + pressuretrend; } // add '+' to rate
      set_ajax_obs("ajaxbarotrend" , pressuretrend + uom.Baro);
      set_ajax_obs("gizmobarotrend", pressuretrend + uom.Baro);
      baroarrow = ajax_getTrendArrow( pressure
                                    , pressure-pressuretrend
                                    , ''
                                    , langBaroRising +uom.Baro+langBaroPerHour
                                    , langBaroFalling+uom.Baro+langBaroPerHour
                                    , 2
                                    );
      set_ajax_obs("ajaxbaroarrow"     , pressuretrend, baroarrow);
      set_ajax_obs("ajaxbarotrendtext" , barotrendtext);
      set_ajax_obs("gizmobarotrendtext", barotrendtext);
      set_ajax_obs("ajaxbaromin"       , pressuremin.toFixed(dp.Baro) +' '+ uom.Baro);
      set_ajax_obs("ajaxbaromax"       , pressuremax.toFixed(dp.Baro) +' '+ uom.Baro);


// Wind ... (kts)
//clientraw[  2] = 10.2;
//clientraw[140] = 20.2;
      set_ajax_obs('ajaxProgress',rand+'wspd','in WindSpeed');
      wind        = parseFloat(clientraw[  2]).convertWind(); // Wind gust
      gust        = parseFloat(clientraw[140]).convertWind(); // Max wind gust in last minute
      windmaxavg  = parseFloat(clientraw[113]).convertWind(); // Max avg speed
      maxgust     = parseFloat(clientraw[ 71]).convertWind(); // Max wind gust
      maxgusttime = clientraw[135];                           // Max wind gust time
      beaufortnum = ajax_getBeaufortNumber(clientraw[2]);

   // Wind gust
      if (maxgust > 0.0 )
           set_ajax_obs("ajaxmaxgust"   , maxgust.toFixed(dp.Wind) +' '+ uom.langWind);
      else set_ajax_obs("ajaxmaxgust"   , 'None');

   // Max wind gust
      set_ajax_obs("ajaxwindmaxgust"    , maxgust.toFixed(dp.Wind) +' '+ uom.langWind);
      set_ajax_obs("ajaxwindmaxgusttime", maxgusttime);

   // Windspeed ... beaufort
      set_ajax_obs("ajaxbeaufortnum"    , beaufortnum);
      set_ajax_obs("ajaxbeaufort"       , langBeaufort[beaufortnum]);

   // Max avg speed
      set_ajax_obs("ajaxwindmaxavg"     , windmaxavg.toFixed(dp.Wind) +' '+ uom.langWind);

   // Current wind speed
      if (wind > 0.0) {
      set_ajax_obs("ajaxwind"      , wind.toFixed(dp.Wind) +' '+ uom.langWind);
      set_ajax_obs("ajaxwindNoU"   , wind.toFixed(dp.Wind));
      set_ajax_obs("gizmowind"     , wind.toFixed(dp.Wind) +' '+ uom.langWind);
      set_ajax_uom("ajaxwinduom"   , true);
      } else {
      set_ajax_obs("ajaxwind"      , langWindCalm);
      set_ajax_obs("ajaxwindNoU"   , langWindCalm);
      set_ajax_obs("gizmowind"     , langWindCalm);
      set_ajax_uom("ajaxwinduom"   , false);
      }

   // Current gust speed
      if (gust > 0.0) {
      set_ajax_obs("ajaxgust"      , gust.toFixed(dp.Wind) +' '+ uom.langWind);
      set_ajax_obs("ajaxgustNoU"   , gust.toFixed(dp.Wind));
      set_ajax_obs("gizmogust"     , gust.toFixed(dp.Wind) +' '+ uom.langWind);
      set_ajax_uom("ajaxgustuom"   , true);
      } else {
      set_ajax_obs("ajaxgust"      , langGustNone);
      set_ajax_obs("ajaxgustNoU"   , langGustNone);
      set_ajax_obs("gizmogust"     , langGustNone);
      set_ajax_uom("ajaxgustuom"   , false);
      }


// Wind direction & wind rose grapic ...
      set_ajax_obs('ajaxProgress',rand+'wdir','in WindDir');
      windDeg = parseFloat(clientraw[3]);
      windDir = ajax_getWindDir(clientraw[3]);
      valLang = ajax_getWindDirLang(clientraw[3]); /* to enable translations */

      set_ajax_obs('ajaxProgress',rand+'rose','in WindRose');
      if (gust > 0.0 || wind > 0.0 || wrImg.calm=="") {
         windicon = '<img src="' + imgDir + windDir + '.png'
            + '" alt="'   + langWindFrom + valLang
            + '" title="' + langWindFrom + valLang
            + '" width="14" height="14" />';
         set_ajax_obs("ajaxwindicon"  , valLang, windicon);
         set_ajax_obs("gizmowindicon" , valLang, windicon);
         set_ajax_obs("ajaxwinddir"   , valLang);
         set_ajax_obs("gizmowinddir"  , valLang);
         set_ajax_obs("ajaxwinddeg"   , windDeg + uom.Deg);
         set_ajax_obs("gizmowinddeg"  , windDeg + uom.Deg);

         src = wrImg.src.replace("#LANG#" , wrImg.lang);
         src = src.replace      ("#DIR#"  , windDir );
         set_ajax_obs("ajaxwindiconwr", src
            , '<img src="' + src
            + '" alt="'    + langWindFrom + valLang
            + '" title="'  + langWindFrom + valLang
            + '" width="'  + wrImg.width
            + '" height="' + wrImg.height
            + '" hspace="' + wrImg.hspace
            + '" vspace="' + wrImg.vspace
            + '" border="' + wrImg.border
            + '" align="'  + wrImg.align
            + '" style="'  + wrImg.style
            + '" />');
      } else { // Wind calm
         set_ajax_obs("ajaxwindicon"  , "");
         set_ajax_obs("gizmowindicon" , "");
         set_ajax_obs("ajaxwinddir"   , "");
         set_ajax_obs("gizmowinddir"  , "");
         set_ajax_obs("ajaxwinddeg"   , "");
         set_ajax_obs("gizmowinddeg"  , "");

         src = wrImg.src.replace("#LANG#", wrImg.lang);
         src = src.replace      ("#DIR#" , wrImg.calm);
         set_ajax_obs("ajaxwindiconwr" , src
            , '<img src="' + src
            + '" alt="'    + langBeaufort[0]
            + '" title="'  + langBeaufort[0]
            + '" width="'  + wrImg.width
            + '" height="' + wrImg.height
            + '" hspace="' + wrImg.hspace
            + '" vspace="' + wrImg.vspace
            + '" border="' + wrImg.border
            + '" align="'  + wrImg.align
            + '" style="'  + wrImg.style
            + '" />');
      }


// Solar Radiation
      set_ajax_obs('ajaxProgress',rand+'sol','in Solar');
      solar    = clientraw[127] * 1.0;
      solarpct = clientraw[34];
      set_ajax_obs("ajaxsolar"   ,solar.toFixed(0));
      set_ajax_obs("ajaxsolarpct",solarpct);


// UV Index
      set_ajax_obs('ajaxProgress',rand+'uv','in UV');
      uv = parseInt(clientraw[79]);
      set_ajax_obs("ajaxuv"   ,uv.toFixed(dp.UV));
      set_ajax_obs("ajaxuvNoD",uv.toFixed(0));
      set_ajax_obs("gizmouv"  ,uv.toFixed(dp.UV)) ;

      uvword = ajax_getUVrange(uv);
      set_ajax_obs("ajaxuvword" ,uv, uvword);
      set_ajax_obs("gizmouvword",uv, uvword);

      uvburnrate = ajax_getUVburnRate(uv);
      set_ajax_obs("ajaxuvburnrate",uvburnrate);

      UVI = Math.min(11,Math.round(uv));
      src = uvImg.src.replace("#INDEX#" ,UVI);
      set_ajax_obs("ajaxuvimg", UVI
         ,'<img src="'  + src
         + '" alt="'    + UVI
         + '" title="'  + UVI
         + '" width="'  + uvImg.width
         + '" height="' + uvImg.height
         + '" hspace="' + uvImg.hspace
         + '" vspace="' + uvImg.vspace
         + '" border="' + uvImg.border
         + '" align="'  + uvImg.align
         + '" style="'  + uvImg.style
         + '" />');

// Rain ... (mm)
      set_ajax_obs('ajaxProgress',rand+'rain','in Rain');
      rain        = parseFloat( clientraw[ 7]).convertRain();
      rainYes     = parseFloat( clientraw[19]).convertRain();
      rainMonth   = parseFloat( clientraw[ 8]).convertRain();
      rainYear    = parseFloat( clientraw[ 9]).convertRain();
      rainratehr  = parseFloat((clientraw[10])*60).convertRain(); // make per hour rate.
      rainratemax = parseFloat((clientraw[11])*60).convertRain(); // make per hour rate

      set_ajax_obs("ajaxrainratehr" ,rainratehr .toFixed(dp.Rain+1) +' '+ uom.Rain);
      set_ajax_obs("ajaxrainratemax",rainratemax.toFixed(dp.Rain+1) +' '+ uom.Rain);

      set_ajax_obs("ajaxrain"       ,rain   .toFixed(dp.Rain)   +' '+ uom.Rain);
      set_ajax_obs("ajaxrainYes"    ,rainYes.toFixed(dp.Rain)   +' '+ uom.Rain);
      set_ajax_obs("ajaxrainydy"    ,rainYes.toFixed(dp.Rain)   +' '+ uom.Rain); // old yesterdays rain tag for compatibility

      set_ajax_obs("ajaxrainmo"     ,rainMonth.toFixed(dp.Rain) +' '+ uom.Rain);
      set_ajax_obs("ajaxrainyr"     ,rainYear .toFixed(dp.Rain) +' '+ uom.Rain);

      set_ajax_obs("ajaxrainNoU"    ,rain     .toFixed(dp.Rain));
      set_ajax_obs("gizmorain"      ,rain     .toFixed(dp.Rain) +' '+ uom.Rain);

      if (clientrawextra!=null) {
         set_ajax_obs('ajaxProgress',rand+'rainex','in Rain Extra');
         // Rain in last week
         rainMon = parseFloat((clientrawextra[484])/10).convertRain();
         rainTue = parseFloat((clientrawextra[485])/10).convertRain();
         rainWed = parseFloat((clientrawextra[486])/10).convertRain();
         rainThu = parseFloat((clientrawextra[487])/10).convertRain();
         rainFri = parseFloat((clientrawextra[488])/10).convertRain();
         rainSat = parseFloat((clientrawextra[489])/10).convertRain();
         rainSun = parseFloat((clientrawextra[490])/10).convertRain();
         set_ajax_obs("ajaxrainSun" ,rainSun.toFixed(dp.Rain) +' '+ uom.Rain);
         set_ajax_obs("ajaxrainMon" ,rainMon.toFixed(dp.Rain) +' '+ uom.Rain);
         set_ajax_obs("ajaxrainTue" ,rainTue.toFixed(dp.Rain) +' '+ uom.Rain);
         set_ajax_obs("ajaxrainWed" ,rainWed.toFixed(dp.Rain) +' '+ uom.Rain);
         set_ajax_obs("ajaxrainThu" ,rainThu.toFixed(dp.Rain) +' '+ uom.Rain);
         set_ajax_obs("ajaxrainFri" ,rainFri.toFixed(dp.Rain) +' '+ uom.Rain);
         set_ajax_obs("ajaxrainSat" ,rainSat.toFixed(dp.Rain) +' '+ uom.Rain);

         // Record alltime daily rain
         rcdRain   = parseFloat(clientrawextra[349]).convertRain();
         rcdRainDt = new Date ( clientrawextra[354], clientrawextra[353]-1, clientrawextra[352] // YMD
                              , clientrawextra[350], clientrawextra[351]  , 0); //HMS
         set_ajax_obs("ajaxRcdRain"    , rcdRain.toFixed(dp.Rain) +' '+ uom.Rain);
         set_ajax_obs("ajaxRcdRainTime", rcdRainDt.date(uom.timeHM));
         set_ajax_obs("ajaxRcdRainDate", rcdRainDt.date(uom.dateFmt));

         // Record alltime daily rain in 1 hour
         rcdRainHr   = parseFloat(clientrawextra[355]).convertRain();
         rcdRainHrDt = new Date ( clientrawextra[360], clientrawextra[359]-1, clientrawextra[358] // YMD
                                , clientrawextra[356], clientrawextra[357]  , 0); //HMS
         set_ajax_obs("ajaxRcdRainHr"    , rcdRainHr.toFixed(dp.Rain) +' '+ uom.Rain);
         set_ajax_obs("ajaxRcdRainHrTime", rcdRainHrDt.date(uom.timeHM));
         set_ajax_obs("ajaxRcdRainHrDate", rcdRainHrDt.date(uom.dateFmt));

         // Days with/without rain
         daysRain   = parseInt( clientrawextra[483]); // nr of days with rain (MTD)
         daysNoRain = parseInt( clientrawextra[482]); // nr of days since last rain
         set_ajax_obs("ajaxDaysRain"  , daysRain   );
         set_ajax_obs("ajaxDaysNoRain", daysNoRain );
      }


// Snow ... (cm)
      set_ajax_obs('ajaxProgress',rand+'snow','in Snow');
      if (clientrawextra!=null) {
         snowToday  = parseFloat(clientrawextra[479]).convertSnow();
         snowMonth  = parseFloat(clientrawextra[480]).convertSnow();
         snowSeason = parseFloat(clientrawextra[481]).convertSnow();
         snowDepth  = parseFloat(clientrawextra[697]).convertSnow();
         set_ajax_obs("ajaxsnowToday" ,snowToday .toFixed(dp.Snow) +' '+ uom.Snow);
         set_ajax_obs("ajaxsnowMonth" ,snowMonth .toFixed(dp.Snow) +' '+ uom.Snow);
         set_ajax_obs("ajaxsnowSeason",snowSeason.toFixed(dp.Snow) +' '+ uom.Snow);
         set_ajax_obs("ajaxsnowDepth" ,snowDepth .toFixed(dp.Snow) +' '+ uom.Snow);
      }


// Lightning ...
      set_ajax_obs('ajaxProgress',rand+'ligh','in Lightning');

      lighteningcountlastminute = clientraw[114];
      set_ajax_obs("ajaxlightning",lighteningcountlastminute);

      lighteningcountlasttime = clientraw[115] + " " + clientraw[116];
      set_ajax_obs("ajaxlightningtime",lighteningcountlasttime);

      lighteningdistance = parseFloat(clientraw[118]).convertDist();
      set_ajax_obs("ajaxlightningdist",lighteningdistance +' '+ uom.Dist);

      lighteningbearing = clientraw[119];
      set_ajax_obs("ajaxlightningbearing",lighteningbearing + uom.Deg);


// Cloud Height ... (meters)
      set_ajax_obs('ajaxProgress',rand+'cloud','in Clouds');
      temp = clientraw[ 4];
      dewp = clientraw[72];
      pres = parseFloat(clientraw[6]).convertBaro();
      rain = parseFloat(clientraw[7]).convertRain();
      wind = parseFloat(clientraw[1]).convertWind(); // gust = clientraw[2]
      wdir = ajax_getWindDirLang(clientraw[3]);
      cloudheight = Math.max(0,(temp-dewp)/0.00802)*1.0 + stationAlt; // in meters
      altitude = Math.round(cloudheight.convertAlti());
      above = (stationAlt>0)? ' ASL' : ' AGL';
      cloudText = altitude.toString() + uom.Alti + above;
//    param = '?uom=' + useUOM  + '&alti=' + uom.Alti + '&wind=' + uom.Wind + '&buster=' + altitude + pres + wind + wdir + rain;
      param = '?uom=' + useUOM + '&buster=' + altitude;
      set_ajax_obs("ajaxcloudheightimg", param
         ,'<img src="'  + cloudImg.src + param
         + '" alt="'    + cloudText
         + '" title="'  + cloudText
         + '" width="'  + cloudImg.width
         + '" height="' + cloudImg.height
         + '" hspace="' + cloudImg.hspace
         + '" vspace="' + cloudImg.vspace
         + '" align="'  + cloudImg.align
         + '" border="' + cloudImg.border
         + '" style="'  + cloudImg.style
         + '" />');
      set_ajax_obs("ajaxcloudheight",cloudText);


// Fire Risk ... (FWI)
      set_ajax_obs('ajaxProgress',rand+'fire','in Fire Risk');
      if (clientrawextra!=null) {
         FWI = parseFloat(clientrawextra[635]);
         set_ajax_obs("ajaxfireindex",FWI);
         idx = ajax_getFireIndex(FWI);
         src = fireImg.src.replace("#INDEX#" ,idx);
         set_ajax_obs("ajaxfireimg", idx
            ,'<img src="'  + src
            + '" alt="'    + FWI
            + '" title="'  + FWI
            + '" width="'  + fireImg.width
            + '" height="' + fireImg.height
            + '" hspace="' + fireImg.hspace
            + '" vspace="' + fireImg.vspace
            + '" border="' + fireImg.border
            + '" align="'  + fireImg.align
            + '" style="'  + fireImg.style
            + '" />');
      }


// Sun/Moon rise/set (needed by functions below) ...
      if (clientrawextra!=null) {
         // Sun rise/set
         sunriseWD  = clientrawextra[556].split(":");
         sunsetWD   = clientrawextra[557].split(":");
         sunrise    = new Date( clientraw[141], clientraw[36]-1, clientraw[35], sunriseWD[0], sunriseWD[1], 0);
         sunset     = new Date( clientraw[141], clientraw[36]-1, clientraw[35], sunsetWD[0] , sunsetWD[1] , 0);
         sunriseT   = sunrise.getTime() - (delayDayNight*60*1000);
         sunsetT    = sunset.getTime()  + (delayDayNight*60*1000);
         // Moon rise/set
         moonriseWD = clientrawextra[558].split(":");
         moonsetWD  = clientrawextra[559].split(":");
         moonrise   = new Date( clientraw[141], clientraw[36]-1, clientraw[35], moonriseWD[0], moonriseWD[1], 0);
         moonset    = new Date( clientraw[141], clientraw[36]-1, clientraw[35], moonsetWD[0] , moonsetWD[1] , 0);
         // Time now
      }
      WDnow = WDdt.getTime();

// Moon / Sun ... rise & set, pic, phase, illumination
      set_ajax_obs('ajaxProgress',rand+'moon','in Moon');
      mooninfo = getMoonInfo();
      moonNS   = parseFloat(clientraw[160])>0 ? "NH" : "SH"; // latitude
      set_ajax_obs("ajaxmoonphase",mooninfo.phase);
      set_ajax_obs("ajaxmoonpct"  ,mooninfo.ill+'%');
      moonText = mooninfo.phase + ', Moon at ' + mooninfo.age + ' in cycle (' + mooninfo.ill + '%)';
      src = moonImg.src.replace("#BG#" , moonImg.bg);
      src = src.replace        ("#NS#" , moonNS);
      src = src.replace        ("#AGE#", mooninfo.pic.toString().str_pad(2));
      set_ajax_obs("ajaxmoonimg", mooninfo.age+mooninfo.ill+mooninfo.pic
         ,'<img src="'  + src
         + '" alt="'    + moonText
         + '" title="'  + moonText
         + '" width="'  + moonImg.width
         + '" height="' + moonImg.height
         + '" hspace="' + moonImg.hspace
         + '" vspace="' + moonImg.vspace
         + '" border="' + moonImg.border
         + '" align="'  + moonImg.align
         + '" style="'  + moonImg.style
         + '" />');

      src = moonImg.src.replace("#BG#" , 'b');
      src = src.replace        ("#NS#" , moonNS);
      src = src.replace        ("#AGE#", mooninfo.pic.toString().str_pad(2));
      set_ajax_obs("ajaxmoonimg100", mooninfo.age+mooninfo.ill+mooninfo.pic
         ,'<img src="'  + src
         + '" alt="'    + moonText
         + '" title="'  + moonText
         + '" width="100" height="100" />');

      if (clientrawextra!=null) {
         set_ajax_obs("ajaxsunrise" ,sunrise.date(uom.timeHM));
         set_ajax_obs("ajaxsunset"  ,sunset.date(uom.timeHM));
         set_ajax_obs("ajaxmoonrise",moonrise.date(uom.timeHM));
         set_ajax_obs("ajaxmoonset" ,moonset.date(uom.timeHM));
      }


// Current condition icon and description ...
      set_ajax_obs('ajaxProgress',rand+'cond','in Conditions');
      // check if we need the icon instead of the webcam image at night
      if (!useCamAtNight && clientrawextra!=null) {
         if (WDnow < sunriseT || WDnow > sunsetT)
            swapIconWithWebcam = false;
      }
      currentcond = clientraw[49];
      // currentcond = currentcond.replace(/_/g,' ');
      // currentcond = currentcond.replace(/^\/Dry\//g,'');
      currentcond = currentcond.replace(/\\/g,', ');
      // currentcond = currentcond.replace(/\//g,', ');
      currentcond = ajax_fixupCondition(currentcond);
      currIconNr  = parseInt(clientraw[48]);
      set_ajax_obs("ajaxcurrentcond" ,currentcond);
      set_ajax_obs("gizmocurrentcond",currentcond);

      set_ajax_obs("ajaxconditionicon",clientraw[48],ajax_getIconName0(currIconNr,wxIconSet0)); // sticker icon

      if (!swapIconWithWebcam) {
         switch (wxIconSet.useName) { // dashboard icon
          case 1: set_ajax_obs("ajaxconditionicon2",clientraw[48],ajax_getIconName1(currIconNr,wxIconSet)); break;
          case 2: set_ajax_obs("ajaxconditionicon2",clientraw[48],ajax_getIconName2(currIconNr,wxIconSet)); break;
          case 9: //Dynamic icon creation similar to cloud height (not yet implemented)
                  param = '?uom=' + useUOM
                        + '&con=' + currentcond
                        + '&iW='  + wxIconSet.width
                        + '&iH='  + wxIconSet.height;
                  set_ajax_obs("ajaxconditionicon2", param
                     ,'<img src="'  + wxIconSet.src + param
                     + '" alt="'    + currentcond
                     + '" title="'  + currentcond
                     + '" width="'  + wxIconSet.width
                     + '" height="' + wxIconSet.height
                     + '" hspace="' + wxIconSet.hspace
                     + '" vspace="' + wxIconSet.vspace
                     + '" border="' + wxIconSet.border
                     + '" align="'  + wxIconSet.align
                     + '" style="'  + wxIconSet.style
                     + '" />');
                  break;
         default: set_ajax_obs("ajaxconditionicon2",'<div style="border: 2px solid red">INVALID ICON NAME #'+wxIconSet.useName+'"</div>'); break;
         }
      }

// LOKALTVADER addition - Own wx picture
/*	set_ajax_obs("ajaxwxcondition", ajax_wxpicture(currIconNr,currentcond) );
  if (iconWD >= 0 && iconWD <= 37) { 
    return ("<img src=\"/images/wx/wx" + iconWD + ".png\" " + " alt=\"Nuvarande väder: (" + ntime + ")\n" + condition + "\" title=\"Nuvarande väder: (" + ntime + ")\n" + condition + "\" height=\"128\" width=\"128\" style=\"border:0px;padding:3px;\" />" );
  } else {
    return ("<img src=\"/images/wx/na.png\" " + " alt=\"Picture not availible\" title=\"Picture not availible\" height=\"128\" width=\"128\" style=\"border:0px;padding:3px;\" />" );
  }
*/	
         set_ajax_obs("ajaxwxcondition" , src
            , '<img src="/images/wx/wx' + currIconNr + '.png'
            + '" alt="'    + currentcond
            + '" title="'  + currentcond
            + '" width="'  + menywxImg.width
            + '" height="' + menywxImg.height
            + '" hspace="' + menywxImg.hspace
            + '" vspace="' + menywxImg.vspace
            + '" border="' + menywxImg.border
            + '" align="'  + menywxImg.align
            + '" style="'  + menywxImg.style
            + '" />');

// Current webcam snap ...
      set_ajax_obs('ajaxProgress',rand+'webcam','in Webcam');
      if (swapIconWithWebcam)
           ajaxTag = "ajaxconditionicon2";
      else ajaxTag = "ajaxwebcamimg";

      if (webcamUpdate!=0) { // if 0 then no ajax updates requested
         now = new Date();
         minutes  = (now.getHours()*60) + now.getMinutes();
         interval = Math.floor(minutes / webcamUpdate);
         param = '?buster=' + interval;
         if (clientrawextra!=null && (WDnow<sunriseT || WDnow>sunsetT))
              src = webcamImg.srcNgt;
         else src = webcamImg.src;
         set_ajax_obs(ajaxTag, interval
            ,'<img src="'  + src + param
            + '" alt="'    + currentcond
            + '" title="'  + currentcond
            + '" width="'  + webcamImg.width
            + '" height="' + webcamImg.height
            + '" hspace="' + webcamImg.hspace
            + '" vspace="' + webcamImg.vspace
            + '" border="' + webcamImg.border
            + '" align="'  + webcamImg.align
            + '" style="'  + webcamImg.style
            + '" />');
      }
// ----------------------------------------------------------------- end of tags

      if (lastajaxtimeformat != WDdt.date("d-m-Y H:i:s")) {
         counterSecs = 0; // reset timer
         lastajaxtimeformat = WDdt.date("d-m-Y H:i:s"); // remember this time
      }

     } // END if(clientraw[0] = '12345' and '!!' at end)
     notify=0;
    } // END if (x.readyState == 4 && x.status == 200)

    set_ajax_obs('ajaxupdatecount'   ,updates   ); /* for test pages */
    set_ajax_obs('ajaxmaxupdatecount',maxupdates); /* for test pages */
    set_ajax_obs('ajaxState' , x.readyState);      /* for test pages */
    set_ajax_obs('ajaxStatus', x.statusText);      /* for test pages */
    if (x.readyState==4 && x.status==404) { // not found
       if (notify==1) alert("DASHBOARD ERROR:\nYour \""+clientrawFile+"\" file could not be found.");
       notify++;
    }
    set_ajax_obs('ajaxProgress',rand+'done','-- DONE --');
    } // END try

    catch(e){}  // Mike Challis added fix to fix random error: NS_ERROR_NOT_AVAILABLE

    }; // END x.onreadystatechange = function()

    x.open("GET", url, true);
    x.send(null);

    // now ensure that the indicator flashes on every AJAX fetch
    ajax_flash_green("ajaxindicator");
    ajax_flash_green("gizmoindicator");

    //  reset the flash colors, and restart the update unless maxupdate limit is reached
    setTimeout("ajax_flash_reset('')",flashtime); // change text back to default color

    if (maxupdates == 0 || updates < maxupdates-1) {
       timerAjaxLoader = setTimeout("ajaxLoader(clientrawFile + '?' + new Date().getTime())", reloadTime); // get new data
    } else {
       counterSecs = 0; // reset timer
       stopTime = new Date().getTime();
       PauseMsg = '';
       if (allowRefresh)
          PauseMsg = '<span onclick="javascript:ajaxLoader_restart();" style="color:blue; cursor:pointer;" title="'+langRestart+'"><b>'+langRefresh+'</b></span>&nbsp;&nbsp;&nbsp;';
       PauseMsg+= '<b>'+langPauseMsg+'</b>';
       set_ajax_obs("ajaxindicator",stopTime,PauseMsg);
    }
  } // if (x)
  else set_ajax_obs('ajaxProgress',rand+'http','ERROR: No HTTP Request');
}; // end ajaxLoader function


window.setInterval("ajax_countup()", 1000); // run the counter for seconds since update

// invoke when first loaded on page
window.onload=function() {
   if (gotSettings) { // seperate settings file
      if (SettingsVersion!=9.00)
         alert("DASHBOARD ERROR:\nYour \"ajaxWDwx-settings.js\" version "+SettingsVersion.number_format(2)
              +" is not\ncompatible with this version of \"ajaxWDwx.js\""
              +"\n\nPlease upgrade to the new version.");
      else loadSettings();
   }
   initialize();
   ajaxLoader_clientextra(extraUrl + '?' + new Date().getTime());
   ajax_page_saveTags();
   ajaxLoader(clientrawFile + '?' + new Date().getTime());
};
