diff --git a/css/wah.css b/css/wah.css index 3524c57..d02a1e0 100644 --- a/css/wah.css +++ b/css/wah.css @@ -188,3 +188,24 @@ table.current-conditions tr:last-child td:nth-child(3) { font-weight: normal; font-style: italic; } + +/* -------------------------------------------------------------------------- */ + +div.gauge-grid { + padding: 20px !important; + display: flex; + flex-flow: row wrap; + justify-content: space-evenly; +} + +div.gauge-header { + display: grid; + grid-template-columns: auto auto 1fr; + grid-template-rows: 1fr; + grid-column-gap: 10px; + grid-row-gap: 0px; +} + +canvas#canvas_led { + margin-bottom: -7px; +} diff --git a/gauges/index.php b/gauges/index.php index 7421aa1..747f6e9 100644 --- a/gauges/index.php +++ b/gauges/index.php @@ -3,26 +3,91 @@ - + +
- - - Status: +
+
+
+
+
Status: +
+
+
+
+
Temperature
+
+
+ +
+
+
+
+
Other Temps
+
+
+ +
+
+
+
+
Humidity
+
+
+ +
+
+
+
+
Wind
+
+ +
+
+
+
Wind Direction
+
+ +
+
+
+
Wind Rose
+
+ +
+
+
+
Pressure
+
+ +
+
+
+
Rainfall
+
+ +
+
+
+
Rainfall Rate
+
+ +
+
+
- Page data updated + + + + + + +
diff --git a/lib/steelseries/scripts/gauges.js b/lib/steelseries/scripts/gauges.js index 38f1994..a8a647a 100644 --- a/lib/steelseries/scripts/gauges.js +++ b/lib/steelseries/scripts/gauges.js @@ -1,4377 +1,4377 @@ -/*! - * THIS VERSION CUSTOMISED FROM CUMULUS MX DEFAULT WEB SITE - * Last Modified: 2023/09/02 17:15:14 - * - * A starter gauges page for Cumulus and Weather Display, based - * on the JavaScript SteelSeries gauges by Gerrit Grunwald. - * - * Created by Mark Crossley, July 2011 - * see scriptVer below for latest release - * - * Released under GNU GENERAL PUBLIC LICENSE, Version 2, June 1991 - * See the enclosed License file - * - * File encoding = UTF-8 - * - */ - -/* exported gauges */ - -/*! -* Tiny Pub/Sub - v0.7.0 - 2013-01-29 -* https://github.com/cowboy/jquery-tiny-pubsub -* Copyright (c) 2013 "Cowboy" Ben Alman; Licensed MIT -*/ -(function ($) { - 'use strict'; - var o = $({}); - $.subscribe = function () { o.on.apply(o, arguments); }; - $.unsubscribe = function () { o.off.apply(o, arguments); }; - $.publish = function () { o.trigger.apply(o, arguments); }; -}(jQuery)); - -var gauges; -gauges = (function () { - 'use strict'; - var strings = LANG.EN, // Set to your default language. Store all the strings in one object - config = { - // Script configuration parameters you may want to 'tweak' - scriptVer: '2.7.7', - weatherProgram: 0, // Set 0=Cumulus, 1=Weather Display, 2=VWS, 3=WeatherCat, 4=Meteobridge, 5=WView, 6=WeeWX, 7=WLCOM - imgPathURL: './/images/', // *** Change this to the relative path for your 'Trend' graph /images - oldGauges: 'gauges.htm', // *** Change this to the relative path for your 'old' gauges page. - realtimeInterval: 15, // *** Download data interval, set to your realtime data update interval in seconds - longPoll: false, // if enabled, use long polling and PHP generated data !!only enable if you understand how this is implemented!! - gaugeMobileScaling: 0.85, // scaling factor to apply when displaying the gauges mobile devices, set to 1 to disable (default 0.85) - graphUpdateTime: 15, // period of pop-up data graph refresh, in minutes (default 15) - stationTimeout: 3, // period of no data change before we declare the station off-line, in minutes (default 3) - pageUpdateLimit: 20, // period after which the page stops automatically updating, in minutes (default 20), - // - set to 0 (zero) to disable this feature - pageUpdatePswd: 'its-me', // password to over ride the page updates time-out, do not set to blank even if you do not use a password - http://&pageUpdate=its-me - digitalFont: false, // Font control for the gauges & timer - digitalForecast: false, // Font control for the status display, set this to false for languages that use accented characters in the forecasts - showPopupData: true, // Pop-up data displayed - showPopupGraphs: false, // If pop-up data is displayed, show the graphs? - mobileShowGraphs: false, // If false, on a mobile/narrow display, always disable the graphs - showWindVariation: true, // Show variation in wind direction over the last 10 minutes on the direction gauge - showWindMetar: false, // Show the METAR substring for wind speed/direction over the last 10 minutes on the direction gauge popup - showIndoorTempHum: true, // Show the indoor temperature/humidity options - showCloudGauge: true, // Display the Cloud Base gauge - showUvGauge: true, // Display the UV Index gauge - showSolarGauge: true, // Display the Solar gauge - showSunshineLed: true, // Show 'sun shining now' LED on solar gauge - showRoseGauge: true, // Show the optional Wind Rose gauge - showRoseGaugeOdo: true, // Show the optional Wind Rose gauge wind run Odometer - showRoseOnDirGauge: true, // Show the rose data as sectors on the direction gauge - showGaugeShadow: true, // Show a drop shadow outside the gauges - roundCloudbaseVal: true, // Round the value shown on the cloud base gauge to make it easier to read - // The realtime files should be absolute paths, "/xxx.txt" refers to the public root of your web server - realTimeUrlLongPoll: 'realtimegauges-longpoll.php', // *** ALL Users: If using long polling, change to your location of the PHP long poll realtime file *** - // *** the supplied file is for Cumulus only - realTimeUrlCumulus: 'realtimegauges.txt', // *** Cumulus Users: Change to your location of the realtime file *** - realTimeUrlWD: 'customclientraw.txt', // *** WD Users: Change to your location of the ccr file *** - realTimeUrlVWS: 'steelseriesVWSjson.php', // *** VWS Users: Change to your location of the JSON script generator *** - realTimeUrlWC: 'realtimegaugesWC.txt', // *** WeatherCat Users: Change to your location of the JSON script generator *** - realTimeUrlMB: 'MBrealtimegauges.txt', // *** Meteobridge Users: Change to the location of the JSON file - realTimeUrlWView: 'customclientraw.txt', // *** WView Users: Change to your location of the customclientraw.txt file *** - realTimeUrlWeewx: 'gauge-data.txt', // *** WeeWX Users: Change to your location of the gauge data file *** - realTimeUrlWLCOM: 'WLrealtimegauges.php', // *** WLCOM Users: change to location of WLCOMtags.php file *** - useCookies: true, // Persistently store user preferences in a cookie? - tip/ images : [], - dashboardMode: false, // Used by Cumulus MX dashboard - SET TO FALSE OTHERWISE - dewDisplayType: 'dew' // Initial 'scale' to display on the 'dew point' gauge. - // 'dew' - Dewpoint - // 'app' - Apparent temperature - // 'wnd' - Wind Chill - // 'feel' - Feels Like - // 'hea' - Heat Index - // 'hum' - Humidex -}, - - // Gauge global look'n'feel settings - gaugeGlobals = { - minMaxArea: 'rgba(212,132,134,0.3)', // area sector for today's max/min. (red, green, blue, transparency) - windAvgArea: 'rgba(132,212,134,0.3)', - windVariationSector: 'rgba(120,200,120,0.7)', // only used when rose data is shown on direction gauge - frameDesign: steelseries.FrameDesign.TILTED_GRAY, - background: steelseries.BackgroundColor.BEIGE, - foreground: steelseries.ForegroundType.TYPE1, - pointer: steelseries.PointerType.TYPE8, - pointerColour: steelseries.ColorDef.RED, - dirAvgPointer: steelseries.PointerType.TYPE8, - dirAvgPointerColour: steelseries.ColorDef.BLUE, - gaugeType: steelseries.GaugeType.TYPE4, - lcdColour: steelseries.LcdColor.STANDARD, - knob: steelseries.KnobType.STANDARD_KNOB, - knobStyle: steelseries.KnobStyle.SILVER, - labelFormat: steelseries.LabelNumberFormat.STANDARD, - tickLabelOrientation: steelseries.TickLabelOrientation.HORIZONTAL, // was .NORMAL up to v1.6.4 - rainUseSectionColours: false, // Only one of these colour options should be true - rainUseGradientColours: false, // Set both to false to use the pointer colour - tempTrendVisible: true, - pressureTrendVisible: true, - uvLcdDecimals: 1, - // sunshine threshold values - sunshineThreshold: 50, // the value in W/m² above which we can consider the Sun to be shining, *if* the current value exceeds... - sunshineThresholdPct: 75, // the percentage of theoretical solar irradiance above which we consider the Sun to be shining - // default gauge ranges - before auto-scaling/ranging - tempScaleDefMinC: -20, - tempScaleDefMaxC: 40, - tempScaleDefMinF: 0, - tempScaleDefMaxF: 100, - baroScaleDefMinhPa: 990, - baroScaleDefMaxhPa: 1030, - baroScaleDefMinkPa: 99, - baroScaleDefMaxkPa: 103, - baroScaleDefMininHg: 29.2, - baroScaleDefMaxinHg: 30.4, - windScaleDefMaxMph: 20, - windScaleDefMaxKts: 20, - windScaleDefMaxMs: 10, - windScaleDefMaxKmh: 30, - rainScaleDefMaxmm: 10, - rainScaleDefMaxIn: 0.5, - rainRateScaleDefMaxmm: 10, - rainRateScaleDefMaxIn: 0.5, - uvScaleDefMax: 10, // Northern Europe may be lower - max. value recorded in the UK is 8, so use a scale of 10 for UK - solarGaugeScaleMax: 1000, // Max value to be shown on the solar gauge - theoretical max without atmosphere ~ 1374 W/m² - // - but Davis stations can read up to 1800, use 1000 for Northern Europe? - cloudScaleDefMaxft: 3000, - cloudScaleDefMaxm: 1000, - shadowColour: 'rgba(0,0,0,0.3)' // Colour to use for gauge shadows - default 30% transparent black - }, - - commonParams = { - // Common parameters for all the SteelSeries gauges - fullScaleDeflectionTime: 4, // Bigger numbers (seconds) slow the gauge pointer movements more - gaugeType: gaugeGlobals.gaugeType, - minValue: 0, - niceScale: true, - ledVisible: false, - frameDesign: gaugeGlobals.frameDesign, - backgroundColor: gaugeGlobals.background, - foregroundType: gaugeGlobals.foreground, - pointerType: gaugeGlobals.pointer, - pointerColor: gaugeGlobals.pointerColour, - knobType: gaugeGlobals.knob, - knobStyle: gaugeGlobals.knobStyle, - lcdColor: gaugeGlobals.lcdColour, - lcdDecimals: 1, - digitalFont: config.digitalFont, - tickLabelOrientation: gaugeGlobals.tickLabelOrientation, - labelNumberFormat: gaugeGlobals.labelFormat - }, - firstRun = true, // Used to set-up units & scales etc - userUnitsSet = false, // Tracks if the display units have been set by a user preference - data = {}, // Stores all the values from realtime.txt - tickTockInterval, // The 1s clock interval timer - - // ajaxDelay, used by long polling, the delay between getting a response and queueing the next request, as the default PHP - // script runtime timout is 30 seconds, we do not want the PHP task to run for more than 20 seconds. So queue the next - // request half of the realtime interval, or 20 seconds before it is due, which ever is the larger. - ajaxDelay = config.longPoll ? Math.max(config.realtimeInterval - 20, 0) : config.realtimeInterval, - downloadTimer, // Stores a reference to the ajax download setTimout() timer - timestamp = 0, // the timestamp of last data update on the server. - jqXHR = null, // handle to the jQuery web request - displayUnits = null, // Stores the display units cookie settings - sampleDate, - realtimeVer, // minimum version of the realtime JSON file required - programLink = [ - 'Cumulus', - 'Weather Display', - 'Virtual Weather Station', - 'WeatherCat', - 'Meteobridge', - 'Wview', - 'weewx', - 'Weatherlink.com' - ], - - ledIndicator, statusScroller, statusTimer, - - gaugeTemp, gaugeDew, gaugeRain, gaugeRRate, - gaugeHum, gaugeBaro, gaugeWind, gaugeDir, - gaugeUV, gaugeSolar, gaugeCloud, gaugeRose, - - /* _imgBackground, // Uncomment if using a background image on the gauges */ - - // ================================================================================================================== - // Nothing below this line needs to be modified for the gauges as supplied - // - unless you really know what you are doing - // - but remember, if you break it, it's up to you to fix it ;-) - // ================================================================================================================== - - // - // init() Called when the document is ready, pre-draws the Status Display then calls - // the first Ajax fetch of realtimegauges.txt. First draw of the gauges now deferred until - // the Ajax data is available as a 'speed up'. - // - init = function (dashboard) { - // Cumulus, Weather Display, VWS, WeatherCat? - switch (config.weatherProgram) { - case 0: - // Cumulus - realtimeVer = 12; // minimum version of the realtime JSON file required - config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlCumulus; - // the trend /images to be used for the pop-up data, used in conjunction with config.imgPathURL - // by default this is configured for the Cumulus 'standard' web site - // ** If you specify one image in a sub-array, then you MUST provide /images for all the other sub-elements - config.tipImgs = [ // config.tipImgs for Cumulus users using the 'default' weather site - ['temp.png', 'intemp.png'], // Temperature: outdoor, indoor - // Temperature: dewpoint, apparent, windChill, heatIndex, humidex - ['temp.png', 'temp.png', 'temp.png', 'temp.png', 'temp.png'], - 'raint.png', // Rainfall - 'rain.png', // Rainfall rate - ['hum.png', 'hum.png'], // Humidity: outdoor, indoor - 'press.png', // Pressure - 'wind.png', // Wind speed - 'windd.png', // Wind direction - (config.showUvGauge ? 'uv.png' : null), // UV graph if UV sensor is present | =null if no UV sensor - (config.showSolarGauge ? 'solar.png' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor - (config.showRoseGauge ? 'windd.png' : null), // Wind direction if Rose is enabled | =null if Rose is disabled - (config.showCloudGauge ? 'press.png' : null) // Pressure for cloud height | =null if Cloud Height is disabled - ]; - break; - case 1: - // Weather Display - realtimeVer = 14; // minimum version of the realtime JSON file required - config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlWD; - config.tipImgs = [ // config.tipImgs for Weather Display users with wxgraph - ['temp+hum_24hr.php', 'indoor_temp_24hr.php'], // Temperature: outdoor, indoor - // Temperature: dewpnt, apparent, windChill, HeatIndx, humidex - ['temp+dew+hum_1hr.php', 'temp+dew+hum_1hr.php', 'temp+dew+hum_1hr.php', 'temp+dew+hum_1hr.php', 'temp+dew+hum_1hr.php'], - 'rain_24hr.php', // Rainfall - 'rain_1hr.php', // Rainfall rate - ['humidity_1hr.php', 'humidity_7days.php'], // Humidity: outdoor, indoor - 'baro_24hr.php', // Pressure - 'windgust_1hr.php', // Wind speed - 'winddir_24hr.php', // Wind direction - (config.showUvGauge ? 'uv_24hr.php' : null), // UV graph if UV sensor is present | =null if no UV sensor - (config.showSolarGauge ? 'solar_24hr.php' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor - (config.showRoseGauge ? 'winddir_24hr.php' : null), // Wind direction if Rose is enabled | =null if Rose is disabled - (config.showCloudGauge ? 'baro_24hr.php' : null) // Pressure for cloud height | =null if Cloud Height is disabled - ]; - // WD useer generally use wxgraphs - tweak the CSS to accomadate the different aspect ratio - $('.tipimg').css({ - width: '360px', - height: '260px' - }); - break; - case 2: - // WVS - realtimeVer = 11; // minimum version of the realtime JSON file required - config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlVWS; - config.showRoseGauge = false; // no wind rose data from VWS - config.showCloudGauge = false; - config.tipImgs = [ // config.tipImgs for VWS users - ['vws742.jpg', 'vws741.jpg'], // Temperature: outdoor, indoor - // Temperature: dewpnt, apparent, windChill, HeatIndx, humidex - ['vws757.jpg', 'vws762.jpg', 'vws754.jpg', 'vws756.jpg', null], - 'vws744.jpg', // Rainfall - 'vws859.jpg', // Rainfall rate - ['vws740.jpg', 'vws739.jpg'], // Humidity: outdoor, indoor - 'vws758.jpg', // Pressure - 'vws737.jpg', // Wind speed - 'vws736.jpg', // Wind direction - (config.showUvGauge ? 'vws752.jpg' : null), // UV graph if UV sensor is present | =null if no UV sensor - (config.showSolarGauge ? 'vws753.jpg' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor - (config.showRoseGauge ? 'vws736.jpg' : null), // Wind direction if Rose is enabled | =null if Rose is disabled - (config.showCloudGauge ? 'vws758.jpg' : null) // Pressure for cloud height | =null if Cloud Height is disabled - ]; - break; - case 3: - // WeatherCat - realtimeVer = 14; // minimum version of the realtime JSON file required - config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlWC; - config.tipImgs = [ // config.tipImgs - WeatherCat users using the 'default' weather site - ['temperature1.jpg', 'tempin1.jpg'], // Temperature: outdoor, indoor - // Temperature: dewpoint, apparent, windChill, heatIndex, humidex - ['dewpoint1.jpg', 'temperature1.jpg', 'windchill1.jpg', 'heatindex1.jpg', 'temperature1.jpg'], - 'precipitationc1.jpg', // Rainfall - 'precipitation1.jpg', // Rainfall rate - ['rh1.jpg', 'rhin1.jpg'], // Humidity: outdoor, indoor - 'pressure1.jpg', // Pressure - 'windspeed1.jpg', // Wind speed - 'winddirection1.jpg', // Wind direction - (config.showUvGauge ? 'uv1.jpg' : null), // UV graph if UV sensor is present | =null if no UV sensor - (config.showSolarGauge ? 'solarrad1.jpg' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor - (config.showRoseGauge ? 'winddirection1.jpg' : null), // Wind direction if Rose is enabled | =null if Rose is disabled - (config.showCloudGauge ? 'cloudbase1.jpg' : null) // Pressure for cloud height | =null if Cloud Height is disabled - ]; - break; - case 4: - // Meteobridge - realtimeVer = 10; // minimum version of the realtime JSON file required - config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlMB; - config.showPopupGraphs = false; // config.tipImgs - no Meteobridge /images available - config.showRoseGauge = false; // no windrose data from MB - config.showCloudGauge = false; - config.tipImgs = null; // config.tipImgs - no Meteobridge /images available - config.showWindVariation = false; // no wind variation data from MB - break; - case 5: - // WView - realtimeVer = 11; // minimum version of the realtime JSON file required - config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlWView; - config.showSunshineLed = false; // WView does not provide the current theoretical solar max required to determine sunshine - config.showWindVariation = false; // no wind variation from WView - config.showRoseGauge = false; // No wind rose array from WView by default - there is a user supplied modification to enable it though. - config.showCloudGauge = false; - config.tipImgs = [ // config.tipImgs for WView users - ['tempdaycomp.png', 'indoor_temp.png'], // Temperature: outdoor, indoor - // Temperature: dewpnt, apparent, windChill, HeatIndx, humidex - ['tempdaycomp.png', 'tempdaycomp.png', 'heatchillcomp.png', 'heatchillcomp.png', 'tempdaycomp.png'], - 'rainday.png', // Rainfall - 'rainrate.png', // Rainfall rate - ['humidday.png', 'humidday.png'], // Humidity: outdoor, indoor - 'baromday.png', // Pressure - 'wspeeddaycomp.png', // Wind speed - 'wdirday.png', // Wind direction - (config.showUvGauge ? 'uvday.png' : null), // UV graph if UV sensor is present | =null if no UV sensor - (config.showSolarGauge ? 'solarday.png' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor - (config.showRoseGauge ? 'windroseday.png' : null), // Wind direction if Rose is enabled | =null if Rose is disabled - (config.showCloudGauge ? 'baromday.png' : null) // Pressure for cloud height | =null if Cloud Height is disabled - ]; - break; - case 6: - // weewx - realtimeVer = 14; // minimum version of the realtime JSON file required - config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlWeewx; - config.showSunshineLed = true; - config.showWindVariation = true; - config.tipImgs = [ // config.tipImgs for weewx - ['dayinouttemp.png', 'dayinouttemp.png'], // Temperature: outdoor, indoor - // Temperature: dewpnt, apparent, windChill, HeatIndx, humidex - ['dayouttemphum.png', 'dayouttemphum.png', 'dayouttemphum.png', 'dayouttemphum.png', 'dayouttemphum.png'], - 'dayrain.png', // Rainfall - 'dayrainrate.png', // Rainfall rate - ['dayinouthum.png', 'dayinouthum.png'], // Humidity: outdoor, indoor - 'daybarometer.png', // Pressure - 'daywind.png', // Wind speed - 'daywinddir.png', // Wind direction - (config.showUvGauge ? 'dayuv.png' : null), // UV graph if UV sensor is present | =null if no UV sensor - (config.showSolarGauge ? 'dayradiation.png' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor - (config.showRoseGauge ? 'daywindvec.png' : null), // Wind direction if Rose is enabled | =null if Rose is disabled - (config.showCloudGauge ? 'daybarometer.png' : null) // Pressure for cloud height | =null if Cloud Height is disabled - ]; - break; - case 7: - // WLCOM - realtimeVer = 10; // minimum version of the realtime JSON file required - config.realTimeURL = config.realTimeUrlWLCOM; - config.showPopupGraphs = false; // config.tipImgs - no WL /images available - config.showRoseGauge = false; // no windrose data from WL - config.showCloudGauge = false; - config.tipImgs = null; // config.tipImgs - no WL /images available - config.showWindVariation = false; // no wind variation data from WL - break; - default: - // Who knows! - realtimeVer = 0; // minimum version of the realtime JSON file required - config.realtimeURL = null; - config.showPopupGraphs = false; - config.tipImgs = null; // config.tipImgs - unknown - } - - // Are we running on a phone device (or really low res screen)? - if ($(window).width() < 480) { - // Change the gauge scaling - config.gaugeScaling = config.gaugeMobileScaling; - // Switch off graphs? - config.showPopupGraphs = config.mobileShowGraphs; - } else { - config.gaugeScaling = 1; - } - - // Logo /images are used to 'personalise' the gauge backgrounds - // To add a logo to a gauge, add the parameter: - // params.customLayer = _imgBackground; - // to the corresponding drawXxxx() function below. - // - // These are for demo only, to add them remove the comments around the following lines, and - // the _imgBackground definition line above... - /* - _imgBackground = document.createElement('img'); // small logo - $(_imgBackground).attr('src', config.imgPathURL + 'logoSmall.png'); - */ - // End of logo /images - - // Get the display units the user last used when they visited before - if present - displayUnits = getCookie('units'); - // Set 'units' radio buttons to match preferred units - if (displayUnits !== null) { - // User wants specific units - userUnitsSet = true; - - // temperature - setRadioCheck('rad_unitsTemp', displayUnits.temp); - data.tempunit = '°' + displayUnits.temp; - // rain - setRadioCheck('rad_unitsRain', displayUnits.rain); - data.rainunit = displayUnits.rain; - // pressure - setRadioCheck('rad_unitsPress', displayUnits.press); - data.pressunit = displayUnits.press; - // wind - setRadioCheck('rad_unitsWind', displayUnits.wind); - data.windunit = displayUnits.wind; - displayUnits.windrun = getWindrunUnits(data.windunit); - // cloud base - setRadioCheck('rad_unitsCloud', displayUnits.cloud); - data.cloudunit = displayUnits.cloud; - } else { - // Set the defaults to metric ) - // DO NOT CHANGE THESE - THE SCRIPT DEPENDS ON THESE DEFAULTS - // The units actually displayed will be read from the realtime.txt file, or from the users last visit cookie - displayUnits = { - temp: 'C', - rain: 'mm', - press: 'hPa', - wind: 'km/h', - windrun: 'km', - cloud: 'm' - }; - - data.tempunit = '°C'; - data.rainunit = 'mm'; - data.pressunit = 'hPa'; - data.windunit = 'km/h'; - data.cloudunit = 'm'; - } - - // enable popup data - if (config.showPopupData) { - ddimgtooltip.showTips = config.showPopupData; - } - - if (config.showPopupGraphs) { - // Note the number of array elements must match 'i' in ddimgtooltip.tiparray() - ddimgtooltip.tiparray[0][0] = (config.tipImgs[0][0] === null ? null : ''); - ddimgtooltip.tiparray[1][0] = (config.tipImgs[1][0] === null ? null : ''); - ddimgtooltip.tiparray[2][0] = (config.tipImgs[2] === null ? null : ''); - ddimgtooltip.tiparray[3][0] = (config.tipImgs[3] === null ? null : ''); - ddimgtooltip.tiparray[4][0] = (config.tipImgs[4][0] === null ? null : ''); - ddimgtooltip.tiparray[5][0] = (config.tipImgs[5] === null ? null : ''); - ddimgtooltip.tiparray[6][0] = (config.tipImgs[6] === null ? null : ''); - ddimgtooltip.tiparray[7][0] = (config.tipImgs[7] === null ? null : ''); - ddimgtooltip.tiparray[8][0] = (config.tipImgs[8] === null ? null : ''); - ddimgtooltip.tiparray[9][0] = (config.tipImgs[9] === null ? null : ''); - ddimgtooltip.tiparray[10][0] = (config.tipImgs[10] === null ? null : ''); - ddimgtooltip.tiparray[11][0] = (config.tipImgs[11] === null ? null : ''); - } - - // draw the status gadgets first, they will display any errors in the initial set-up - ledIndicator = singleLed.getInstance(); - statusScroller = singleStatus.getInstance(); - statusTimer = singleTimer.getInstance(); - - gaugeTemp = singleTemp.getInstance(); - // Export gaugeTemp.update() so it can be called from the HTML code - if (gaugeTemp) { gauges.doTemp = gaugeTemp.update; } - - gaugeDew = singleDew.getInstance(); - // Export gaugeDew.update() so it can be called from the HTML code - if (gaugeDew) { gauges.doDew = gaugeDew.update; } - - gaugeHum = singleHum.getInstance(); - // Export gaugeHum.update() so it can be called from the HTML code - if (gaugeHum) { gauges.doHum = gaugeHum.update; } - - gaugeBaro = singleBaro.getInstance(); - - gaugeWind = singleWind.getInstance(); - gaugeDir = singleDir.getInstance(); - - gaugeRain = singleRain.getInstance(); - gaugeRRate = singleRRate.getInstance(); - - // remove the UV gauge? - if (!config.showUvGauge) { - $('#canvas_uv').parent().remove(); - } else { - gaugeUV = singleUV.getInstance(); - } - - // remove the Solar gauge? - if (!config.showSolarGauge) { - $('#canvas_solar').parent().remove(); - } else { - gaugeSolar = singleSolar.getInstance(); - } - - // remove the Wind Rose? - if (!config.showRoseGauge) { - $('#canvas_rose').parent().remove(); - } else { - gaugeRose = singleRose.getInstance(); - } - - // remove the cloud base gauge? - if (!config.showCloudGauge) { - $('#canvas_cloud').parent().remove(); - // and remove cloudbase unit selection options - $('#cloud').parent().remove(); - } else { - gaugeCloud = singleCloudBase.getInstance(); - } - - // Set the language - changeLang(strings, false); - - if (!dashboard) { - // Go do get the data! - getRealtime(); - - // start a timer to update the status time - tickTockInterval = setInterval( - function () { - $.publish('gauges.clockTick', null); - }, - 1000); - - // start a timer to stop the page updates after the timeout period - if (config.pageUpdateLimit > 0 && getUrlParam('pageUpdate') !== config.pageUpdatePswd) { - setTimeout(pageTimeout, config.pageUpdateLimit * 60 * 1000); - } - } - }, - - // - // singleXXXX functions define a singleton for each of the gauges - // - - // - // Singleton for the LED Indicator - // - singleLed = (function () { - var instance; // Stores a reference to the Singleton - var led; // Stores a reference to the SS LED - - function init() { - // create led indicator - if ($('#canvas_led').length) { - led = new steelseries.Led( - 'canvas_led', { - ledColor: steelseries.LedColor.GREEN_LED, - size: $('#canvas_led').width() - }); - - setTitle(strings.led_title); - } - - function setTitle(newTitle) { - $('#canvas_led').attr('title', newTitle); - } - - function setLedColor(newColour) { - if (led) { - led.setLedColor(newColour); - } - } - - function setLedOnOff(onState) { - if (led) { - led.setLedOnOff(onState); - } - } - - function blink(blinkState) { - if (led) { - led.blink(blinkState); - } - } - - return { - setTitle: setTitle, - setLedColor: setLedColor, - setLedOnOff: setLedOnOff, - blink: blink - }; - } - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the Status Scroller - // - singleStatus = (function () { - var instance; // Stores a reference to the Singleton - var scroller; // Stores a reference to the SS scrolling display - - function init() { - // create forecast display - if ($('#canvas_status').length) { - scroller = new steelseries.DisplaySingle( - 'canvas_status', { - width: $('#canvas_status').width(), - height: $('#canvas_status').height(), - lcdColor: gaugeGlobals.lcdColour, - unitStringVisible: false, - value: strings.statusStr, - digitalFont: config.digitalForecast, - valuesNumeric: false, - autoScroll: true, - alwaysScroll: false - }); - } - - function setValue(newTxt) { - if (scroller) { - scroller.setValue(newTxt); - } - } - - return { setText: setValue }; - } - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the Status Timer - // - singleTimer = (function () { - var instance, // Stores a reference to the Singleton - lcd, // Stores a reference to the SS LED - count = 1; - - function init() { - function tick() { - if (lcd) { - lcd.setValue(count); - count += config.longPoll ? 1 : -1; - } - } - - function reset(val) { - count = val; - } - - function setValue(newVal) { - if (lcd) { - lcd.setValue(newVal); - } - } - - // create timer display - if ($('#canvas_timer').length) { - lcd = new steelseries.DisplaySingle( - 'canvas_timer', { - width: $('#canvas_timer').width(), - height: $('#canvas_timer').height(), - lcdColor: gaugeGlobals.lcdColour, - lcdDecimals: 0, - unitString: strings.timer, - unitStringVisible: true, - digitalFont: config.digitalFont, - value: count - }); - // subscribe to data updates - $.subscribe('gauges.clockTick', tick); - } - - return { - reset: reset, - setValue: setValue - }; - } - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the Temperature Gauge - // - singleTemp = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - - // define temperature gauge start values - cache.sections = createTempSections(true); - cache.areas = []; - cache.minValue = gaugeGlobals.tempScaleDefMinC; - cache.maxValue = gaugeGlobals.tempScaleDefMaxC; - cache.title = strings.temp_title_out; - cache.value = gaugeGlobals.tempScaleDefMinC + 0.0001; - cache.maxMinVisible = false; - cache.selected = 'out'; - - // create temperature radial gauge - if ($('#canvas_temp').length) { - params.size = Math.ceil($('#canvas_temp').width() * config.gaugeScaling); - params.section = cache.sections; - params.area = cache.areas; - params.minValue = cache.minValue; - params.maxValue = cache.maxValue; - params.thresholdVisible = false; - params.minMeasuredValueVisible = cache.maxMinVisible; - params.maxMeasuredValueVisible = cache.maxMinVisible; - params.titleString = cache.title; - params.unitString = data.tempunit; - params.trendVisible = gaugeGlobals.tempTrendVisible; - // params.customLayer = _imgBackground; // uncomment to add a background image - See Logo /images above - - ssGauge = new steelseries.Radial('canvas_temp', params); - ssGauge.setValue(cache.value); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_temp').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_temp').css(gaugeShadow(params.size)); - } - - // remove indoor temperature/humidity options? - if (!config.showIndoorTempHum) { - $('#rad_temp1').remove(); - $('#lab_temp1').remove(); - $('#rad_temp2').remove(); - $('#lab_temp2').remove(); - $('#rad_hum1').remove(); - $('#lab_hum1').remove(); - $('#rad_hum2').remove(); - $('#lab_hum2').remove(); - } - - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - var sel = cache.selected; - - // Argument length === 1 when called from radio input - // Argument length === 2 when called from event handler - if (arguments.length === 1) { - sel = arguments[0].value; - } - - // if rad isn't specified, just use existing value - var t1, scaleStep, tip; - - cache.minValue = data.tempunit[1] === 'C' ? gaugeGlobals.tempScaleDefMinC : gaugeGlobals.tempScaleDefMinF; - cache.maxValue = data.tempunit[1] === 'C' ? gaugeGlobals.tempScaleDefMaxC : gaugeGlobals.tempScaleDefMaxF; - - if (sel === 'out') { - cache.low = extractDecimal(data.tempTL); - cache.high = extractDecimal(data.tempTH); - cache.lowScale = getMinTemp(cache.minValue); - cache.highScale = getMaxTemp(cache.maxValue); - cache.value = extractDecimal(data.temp); - cache.title = strings.temp_title_out; - cache.loc = strings.temp_out_info; - cache.trendVal = extractDecimal(data.temptrend); - cache.popupImg = 0; - if (gaugeGlobals.tempTrendVisible) { - t1 = tempTrend(+cache.trendVal, data.tempunit, false); - if (t1 === -9999) { - // trend value isn't currently available - cache.trend = steelseries.TrendState.OFF; - } else if (t1 > 0) { - cache.trend = steelseries.TrendState.UP; - } else if (t1 < 0) { - cache.trend = steelseries.TrendState.DOWN; - } else { - cache.trend = steelseries.TrendState.STEADY; - } - } - } else { - // Indoor - cache.title = strings.temp_title_in; - cache.loc = strings.temp_in_info; - cache.value = extractDecimal(data.intemp); - cache.popupImg = 1; - if (data.intempTL && data.intempTH) { - // Indoor - and Max/Min values supplied - cache.low = extractDecimal(data.intempTL); - cache.high = extractDecimal(data.intempTH); - cache.lowScale = getMinTemp(cache.minValue); - cache.highScale = getMaxTemp(cache.maxValue); - } else { - // Indoor - no Max/Min values supplied - cache.low = cache.value; - cache.lowScale = cache.value; - cache.high = cache.value; - cache.highScale = cache.value; - } - if (gaugeGlobals.tempTrendVisible) { - cache.trend = steelseries.TrendState.OFF; - } - } - - // has the gauge type changed? - if (cache.selected !== sel) { - cache.selected = sel; - // Change gauge title - ssGauge.setTitleString(cache.title); - ssGauge.setMaxMeasuredValueVisible(cache.maxMinVisible); - ssGauge.setMinMeasuredValueVisible(cache.maxMinVisible); - if (config.showPopupGraphs && config.tipImgs[0][cache.popupImg] !== null) { - var cacheDefeat = '?' + $('#imgtip0_img').attr('src').split('?')[1]; - $('#imgtip0_img').attr('src', config.imgPathURL + config.tipImgs[0][cache.popupImg] + cacheDefeat); - } - } - - // auto scale the ranges - scaleStep = data.tempunit[1] === 'C' ? 10 : 20; - while (cache.lowScale < cache.minValue) { - cache.minValue -= scaleStep; - if (cache.highScale <= cache.maxValue - scaleStep) { - cache.maxValue -= scaleStep; - } - } - while (cache.highScale > cache.maxValue) { - cache.maxValue += scaleStep; - if (cache.minValue >= cache.minValue + scaleStep) { - cache.minValue += scaleStep; - } - } - - if (cache.minValue !== ssGauge.getMinValue() || cache.maxValue !== ssGauge.getMaxValue()) { - ssGauge.setMinValue(cache.minValue); - ssGauge.setMaxValue(cache.maxValue); - ssGauge.setValue(cache.minValue); - } - if (cache.selected === 'out') { - cache.areas = [steelseries.Section(+cache.low, +cache.high, gaugeGlobals.minMaxArea)]; - } else if (data.intempTL && data.intempTH) { - // Indoor and min/max avaiable - cache.areas = [steelseries.Section(+cache.low, +cache.high, gaugeGlobals.minMaxArea)]; - } else { - // Indoor no min/max avaiable - cache.areas = []; - } - - if (gaugeGlobals.tempTrendVisible) { - ssGauge.setTrend(cache.trend); - } - ssGauge.setArea(cache.areas); - ssGauge.setValueAnimated(+cache.value); - - if (ddimgtooltip.showTips) { - // update tooltip - if (cache.selected === 'out') { - tip = cache.loc + ' - ' + strings.lowestF_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TtempTL + - ' | ' + - strings.highestF_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TtempTH; - if (cache.trendVal !== -9999) { - tip += '
' + - strings.temp_trend_info + ': ' + tempTrend(cache.trendVal, data.tempunit, true) + - ' ' + cache.trendVal + data.tempunit + '/h'; - } - } else if (data.TintempTL && data.TintempTH) { - // Indoor and min/max available - tip = cache.loc + ' - ' + strings.lowestF_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TintempTL + - ' | ' + - strings.highestF_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TintempTH; - } else { - // Indoor no min/max - tip = cache.loc + ': ' + data.intemp + data.tempunit; - } - $('#imgtip0_txt').html(tip); - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[0][cache.popupImg] !== null) { - $('#imgtip0_img').attr('src', config.imgPathURL + config.tipImgs[0][cache.popupImg] + cacheDefeat); - } - } - - return { - data: cache, - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), // End singleTemp() - - // - // Singleton for the Dewpoint Gauge - // - singleDew = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - var tmp; - - // define dew point gauge start values - cache.sections = createTempSections(true); - cache.areas = []; - cache.minValue = gaugeGlobals.tempScaleDefMinC; - cache.maxValue = gaugeGlobals.tempScaleDefMaxC; - cache.value = gaugeGlobals.tempScaleDefMinC + 0.0001; - // Has the end user selected a preferred 'scale' before - tmp = getCookie('dewGauge'); - cache.selected = tmp !== null ? tmp : config.dewDisplayType; - setRadioCheck('rad_dew', cache.selected); - switch (cache.selected) { - case 'dew': - cache.title = strings.dew_title; - cache.popupImg = 0; - break; - case 'app': - cache.title = strings.apptemp_title; - cache.popupImg = 1; - break; - case 'feel': - cache.title = strings.feel_title; - cache.popupImg = 1; - break; - case 'wnd': - cache.title = strings.chill_title; - cache.popupImg = 2; - break; - case 'hea': - cache.title = strings.heat_title; - cache.popupImg = 3; - break; - case 'hum': - cache.title = strings.humdx_title; - cache.popupImg = 4; - // no default - } - cache.minMeasuredVisible = false; - cache.maxMeasuredVisible = false; - - // create dew point radial gauge - if ($('#canvas_dew').length) { - params.size = Math.ceil($('#canvas_dew').width() * config.gaugeScaling); - params.section = cache.sections; - params.area = cache.areas; - params.minValue = cache.minValue; - params.maxValue = cache.maxValue; - params.thresholdVisible = false; - params.titleString = cache.title; - params.unitString = data.tempunit; - - ssGauge = new steelseries.Radial('canvas_dew', params); - ssGauge.setValue(cache.value); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_dew').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_dew').css(gaugeShadow(params.size)); - } - - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - // if rad isn't specified, just use existing value - var sel = cache.selected; - - // Argument length === 2 when called from event handler - if (arguments.length === 1) { - sel = arguments[0].value; - // save the choice in a cookie - setCookie('dewGauge', sel); - } - - var tip, scaleStep; - - cache.minValue = data.tempunit[1] === 'C' ? gaugeGlobals.tempScaleDefMinC : gaugeGlobals.tempScaleDefMinF; - cache.maxValue = data.tempunit[1] === 'C' ? gaugeGlobals.tempScaleDefMaxC : gaugeGlobals.tempScaleDefMaxF; - - cache.lowScale = getMinTemp(cache.minValue); - cache.highScale = getMaxTemp(cache.maxValue); - - switch (sel) { - case 'dew': // dew point - cache.low = extractDecimal(data.dewpointTL); - cache.high = extractDecimal(data.dewpointTH); - cache.value = extractDecimal(data.dew); - cache.areas = [steelseries.Section(+cache.low, +cache.high, gaugeGlobals.minMaxArea)]; - cache.title = strings.dew_title; - cache.minMeasuredVisible = false; - cache.maxMeasuredVisible = false; - cache.popupImg = 0; - tip = strings.dew_info + ':' + - '
' + - '- ' + strings.lowest_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TdewpointTL + - ' | ' + strings.highest_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TdewpointTH; - break; - case 'app': // apparent temperature - cache.low = extractDecimal(data.apptempTL); - cache.high = extractDecimal(data.apptempTH); - cache.value = extractDecimal(data.apptemp); - cache.areas = [steelseries.Section(+cache.low, +cache.high, gaugeGlobals.minMaxArea)]; - cache.title = strings.apptemp_title; - cache.minMeasuredVisible = false; - cache.maxMeasuredVisible = false; - cache.popupImg = 1; - tip = tip = strings.apptemp_info + ':' + - '
' + - '- ' + strings.lowestF_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TapptempTL + - ' | ' + strings.highestF_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TapptempTH; - break; - case 'feel': // feels like - cache.low = extractDecimal(data.feelslikeTL); - cache.high = extractDecimal(data.feelslikeTH); - cache.value = extractDecimal(data.feelslike); - cache.areas = [steelseries.Section(+cache.low, +cache.high, gaugeGlobals.minMaxArea)]; - cache.title = strings.feel_title; - cache.minMeasuredVisible = false; - cache.maxMeasuredVisible = false; - cache.popupImg = 1; - tip = tip = strings.feel_info + ':' + - '
' + - '- ' + strings.lowestF_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TfeelslikeTL + - ' | ' + strings.highestF_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TfeelslikeTH; - break; - case 'wnd': // wind chill - cache.low = extractDecimal(data.wchillTL); - cache.high = extractDecimal(data.wchill); - cache.value = extractDecimal(data.wchill); - cache.areas = []; - cache.title = strings.chill_title; - cache.minMeasuredVisible = true; - cache.maxMeasuredVisible = false; - cache.popupImg = 2; - tip = strings.chill_info + ':' + - '
' + - '- ' + strings.lowest_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TwchillTL; - break; - case 'hea': // heat index - cache.low = extractDecimal(data.heatindex); - cache.high = extractDecimal(data.heatindexTH); - cache.value = extractDecimal(data.heatindex); - cache.areas = []; - cache.title = strings.heat_title; - cache.minMeasuredVisible = false; - cache.maxMeasuredVisible = true; - cache.popupImg = 3; - tip = strings.heat_info + ':' + - '
' + - '- ' + strings.highest_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TheatindexTH; - break; - case 'hum': // humidex - cache.low = extractDecimal(data.humidex); - cache.high = extractDecimal(data.humidex); - cache.value = extractDecimal(data.humidex); - cache.areas = []; - cache.title = strings.humdx_title; - cache.minMeasuredVisible = false; - cache.maxMeasuredVisible = false; - cache.popupImg = 4; - tip = strings.humdx_info + ': ' + cache.value + data.tempunit; - break; - // no default - } - - if (cache.selected !== sel) { - cache.selected = sel; - // change gauge title - ssGauge.setTitleString(cache.title); - // and graph image - if (config.showPopupGraphs && config.tipImgs[1][cache.popupImg] !== null) { - var cacheDefeat = '?' + $('#imgtip1_img').attr('src').split('?')[1]; - $('#imgtip1_img').attr('src', config.imgPathURL + config.tipImgs[1][cache.popupImg] + cacheDefeat); - } - } - - // auto scale the ranges - scaleStep = data.tempunit[1] === 'C' ? 10 : 20; - while (cache.lowScale < cache.minValue) { - cache.minValue -= scaleStep; - if (cache.highScale <= cache.maxValue - scaleStep) { - cache.maxValue -= scaleStep; - } - } - while (cache.highScale > cache.maxValue) { - cache.maxValue += scaleStep; - if (cache.minValue >= cache.minValue + scaleStep) { - cache.minValue += scaleStep; - } - } - - if (cache.minValue !== ssGauge.getMinValue() || cache.maxValue !== ssGauge.getMaxValue()) { - ssGauge.setMinValue(cache.minValue); - ssGauge.setMaxValue(cache.maxValue); - ssGauge.setValue(cache.minValue); - } - ssGauge.setMinMeasuredValueVisible(cache.minMeasuredVisible); - ssGauge.setMaxMeasuredValueVisible(cache.maxMeasuredVisible); - ssGauge.setMinMeasuredValue(+cache.low); - ssGauge.setMaxMeasuredValue(+cache.high); - ssGauge.setArea(cache.areas); - ssGauge.setValueAnimated(+cache.value); - - if (ddimgtooltip.showTips) { - // update tooltip - $('#imgtip1_txt').html(tip); - } - } - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[1][cache.popupImg] !== null) { - $('#imgtip1_img').attr('src', config.imgPathURL + config.tipImgs[1][cache.popupImg] + cacheDefeat); - } - } - - return { - data: cache, - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), // End of singleDew() - - // - // Singleton for the Rainfall Gauge - // - singleRain = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - - // define rain gauge start values - cache.maxValue = gaugeGlobals.rainScaleDefMaxmm; - cache.value = 0.0001; - cache.title = strings.rain_title; - cache.lcdDecimals = 1; - cache.scaleDecimals = 1; - cache.labelNumberFormat = gaugeGlobals.labelFormat; - cache.sections = (gaugeGlobals.rainUseSectionColours ? createRainfallSections(true) : []); - cache.valGrad = (gaugeGlobals.rainUseGradientColours ? createRainfallGradient(true) : null); - - // create rain radial bargraph gauge - if ($('#canvas_rain').length) { - params.size = Math.ceil($('#canvas_rain').width() * config.gaugeScaling); - params.maxValue = cache.maxValue; - params.thresholdVisible = false; - params.titleString = cache.title; - params.unitString = data.rainunit; - params.valueColor = steelseries.ColorDef.BLUE; - params.valueGradient = cache.valGrad; - params.useValueGradient = gaugeGlobals.rainUseGradientColours; - params.useSectionColors = gaugeGlobals.rainUseSectionColour; - params.useSectionColors = gaugeGlobals.rainUseSectionColours; - params.labelNumberFormat = cache.labelNumberFormat; - params.fractionalScaleDecimals = cache.scaleDecimals; - params.niceScale = false; - - ssGauge = new steelseries.RadialBargraph('canvas_rain', params); - ssGauge.setValue(cache.value); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_rain').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_rain').css(gaugeShadow(params.size)); - } - - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - cache.value = extractDecimal(data.rfall); - if (data.rainunit === 'mm') { // 10, 20, 30... - cache.maxValue = Math.max(nextHighest(cache.value, 10), gaugeGlobals.rainScaleDefMaxmm); - } else { - // inches 0.5, 1.0, 2.0, 3.0 ... 10.0, 12.0, 14.0 - if (cache.value <= 1) { - cache.maxValue = Math.max(nextHighest(cache.value, 0.5), gaugeGlobals.rainScaleDefMaxIn); - } else if (cache.value <= 6) { - cache.maxValue = Math.max(nextHighest(cache.value, 1), gaugeGlobals.rainScaleDefMaxIn); - } else { - cache.maxValue = Math.max(nextHighest(cache.value, 2), gaugeGlobals.rainScaleDefMaxIn); - } - cache.scaleDecimals = cache.maxValue < 1 ? 2 : 1; - } - - if (cache.maxValue !== ssGauge.getMaxValue()) { - // Gauge scale is too low, increase it. - // First set the pointer back to zero so we get a nice animation - ssGauge.setValue(0); - // and redraw the gauge with the new scale - ssGauge.setFractionalScaleDecimals(cache.scaleDecimals); - ssGauge.setMaxValue(cache.maxValue); - } - ssGauge.setValueAnimated(cache.value); - - if (ddimgtooltip.showTips) { - // update tooltip - $('#imgtip2_txt').html(strings.LastRain_info + ': ' + data.LastRained); - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[2] !== null) { - $('#imgtip2_img').attr('src', config.imgPathURL + config.tipImgs[2] + cacheDefeat); - } - } - - return { - data: cache, - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the Rainfall Rate Gauge - // - singleRRate = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - - // define rain rate gauge start values - cache.maxMeasured = 0; - cache.maxValue = gaugeGlobals.rainRateScaleDefMaxmm; - cache.value = 0.0001; - cache.title = strings.rrate_title; - cache.lcdDecimals = 1; - cache.scaleDecimals = 0; - cache.labelNumberFormat = gaugeGlobals.labelFormat; - cache.sections = createRainRateSections(true); - - // create rain rate radial gauge - if ($('#canvas_rrate').length) { - params.size = Math.ceil($('#canvas_rrate').width() * config.gaugeScaling); - params.section = cache.sections; - params.maxValue = cache.maxValue; - params.thresholdVisible = false; - params.maxMeasuredValueVisible = true; - params.titleString = cache.title; - params.unitString = data.rainunit + '/h'; - params.lcdDecimals = cache.lcdDecimals; - params.labelNumberFormat = cache.labelNumberFormat; - params.fractionalScaleDecimals = cache.scaleDecimals; - params.niceScale = false; - - ssGauge = new steelseries.Radial('canvas_rrate', params); - ssGauge.setMaxMeasuredValue(cache.maxMeasured); - ssGauge.setValue(cache.value); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_rrate').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_rrate').css(gaugeShadow(params.size)); - } - - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - var tip; - - cache.value = extractDecimal(data.rrate); - cache.maxMeasured = extractDecimal(data.rrateTM); - cache.overallMax = Math.max(cache.maxMeasured, cache.value); // workaround for VWS bug, not supplying correct max value today - - if (data.rainunit === 'mm') { // 10, 20, 30... - cache.maxValue = nextHighest(cache.overallMax, 10); - } else { - // inches 0.5, 1.0, 2.0, 3.0 ... 10, 20, 30... - if (cache.overallMax <= 0.5) { - cache.maxValue = 0.5; - } else if (cache.overallMax <= 10) { - cache.maxValue = nextHighest(cache.overallMax, 1); - } else { - cache.maxValue = nextHighest(cache.overallMax, 10); - } - cache.scaleDecimals = cache.maxValue < 1 ? 2 : (cache.maxValue < 7 ? 1 : 0); - } - - if (cache.maxValue !== ssGauge.getMaxValue()) { - ssGauge.setValue(0); - ssGauge.setFractionalScaleDecimals(cache.scaleDecimals); - ssGauge.setMaxValue(cache.maxValue); - } - - ssGauge.setValueAnimated(cache.value); - ssGauge.setMaxMeasuredValue(cache.maxMeasured); - - if (ddimgtooltip.showTips) { - // update tooltip - tip = strings.rrate_info + ':
' + - '- ' + strings.maximum_info + ': ' + data.rrateTM + ' ' + data.rainunit + '/h ' + strings.at + ' ' + data.TrrateTM + - ' | ' + strings.max_hour_info + ': ' + extractDecimal(data.hourlyrainTH) + ' ' + data.rainunit + ' ' + - strings.at + ' ' + data.ThourlyrainTH; - $('#imgtip3_txt').html(tip); - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[3] !== null) { - $('#imgtip3_img').attr('src', config.imgPathURL + config.tipImgs[3] + cacheDefeat); - } - } - return { - data: cache, - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the Humidity Gauge - // - singleHum = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - - // define humidity gauge start values - cache.areas = []; - cache.value = 0.0001; - cache.title = strings.hum_title_out; - cache.selected = 'out'; - - // create humidity radial gauge - if ($('#canvas_hum').length) { - params.size = Math.ceil($('#canvas_hum').width() * config.gaugeScaling); - params.section = [ - steelseries.Section(0, 20, 'rgba(255,255,0,0.3)'), - steelseries.Section(20, 80, 'rgba(0,255,0,0.3)'), - steelseries.Section(80, 100, 'rgba(255,0,0,0.3)') - ]; - params.area = cache.areas; - params.maxValue = 100; - params.thresholdVisible = false; - params.titleString = cache.title; - params.unitString = 'RH%'; - - ssGauge = new steelseries.Radial('canvas_hum', params); - ssGauge.setValue(cache.value); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_hum').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_hum').css(gaugeShadow(params.size)); - } - - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - var radio; - // Argument length === 2 when called from event handler - if (arguments.length === 1) { - radio = arguments[0]; - } - - // if rad isn't specified, just use existing value - var sel = (typeof radio === 'undefined' ? cache.selected : radio.value), - tip; - - if (sel === 'out') { - cache.value = extractDecimal(data.hum); - cache.areas = [steelseries.Section(+extractDecimal(data.humTL), +extractDecimal(data.humTH), gaugeGlobals.minMaxArea)]; - cache.title = strings.hum_title_out; - cache.popupImg = 0; - } else { - cache.value = extractDecimal(data.inhum); - if (data.inhumTL && data.inhumTH) { - cache.areas = [steelseries.Section(+extractDecimal(data.inhumTL), +extractDecimal(data.inhumTH), gaugeGlobals.minMaxArea)]; - } else { - cache.areas = []; - } - cache.title = strings.hum_title_in; - cache.popupImg = 1; - } - - if (cache.selected !== sel) { - cache.selected = sel; - // Change gauge title - ssGauge.setTitleString(cache.title); - if (config.showPopupGraphs && config.tipImgs[4][cache.popupImg] !== null) { - var cacheDefeat = '?' + $('#imgtip4_img').attr('src').split('?')[1]; - $('#imgtip4_img').attr('src', config.imgPathURL + config.tipImgs[4][cache.popupImg] + cacheDefeat); - } - } - - ssGauge.setArea(cache.areas); - ssGauge.setValueAnimated(cache.value); - - if (ddimgtooltip.showTips) { - // update tooltip - if (cache.selected === 'out') { - tip = strings.hum_out_info + ':' + - '
' + - '- ' + strings.minimum_info + ': ' + extractDecimal(data.humTL) + '% ' + strings.at + ' ' + data.ThumTL + - ' | ' + strings.maximum_info + ': ' + extractDecimal(data.humTH) + '% ' + strings.at + ' ' + data.ThumTH; - } else if (data.inhumTL && data.inhumTH) { - // we have indoor high/low data - tip = strings.hum_in_info + ':' + - '
' + - '- ' + strings.minimum_info + ': ' + extractDecimal(data.inhumTL) + '% ' + strings.at + ' ' + data.TinhumTL + - ' | ' + strings.maximum_info + ': ' + extractDecimal(data.inhumTH) + '% ' + strings.at + ' ' + data.TinhumTH; - } else { - // no indoor high/low data - tip = strings.hum_in_info + ': ' + extractDecimal(data.inhum) + '%'; - } - $('#imgtip4_txt').html(tip); - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[4][cache.popupImg] !== null) { - $('#imgtip4_img').attr('src', config.imgPathURL + config.tipImgs[4][cache.popupImg] + cacheDefeat); - } - } - - return { - data: cache, - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the Barometer Gauge - // - singleBaro = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - - // define pressure/barometer gauge start values - cache.sections = []; - cache.areas = []; - cache.minValue = gaugeGlobals.baroScaleDefMinhPa; - cache.maxValue = gaugeGlobals.baroScaleDefMaxhPa; - cache.value = cache.minValue + 0.0001; - cache.title = strings.baro_title; - cache.lcdDecimals = 1; - cache.scaleDecimals = 0; - cache.labelNumberFormat = gaugeGlobals.labelFormat; - - // create pressure/barometric radial gauge - if ($('#canvas_baro').length) { - params.size = Math.ceil($('#canvas_baro').width() * config.gaugeScaling); - params.section = cache.sections; - params.area = cache.areas; - params.minValue = cache.minValue; - params.maxValue = cache.maxValue; - params.niceScale = false; - params.thresholdVisible = false; - params.titleString = cache.title; - params.unitString = data.pressunit; - params.lcdDecimals = cache.lcdDecimals; - params.trendVisible = gaugeGlobals.pressureTrendVisible; - params.labelNumberFormat = cache.labelNumberFormat; - params.fractionalScaleDecimals = cache.scaleDecimals; - - ssGauge = new steelseries.Radial('canvas_baro', params); - ssGauge.setValue(cache.value); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_baro').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_baro').css(gaugeShadow(params.size)); - } - - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - var tip, t1, dps; - - cache.recLow = +extractDecimal(data.pressL); - cache.recHigh = +extractDecimal(data.pressH); - cache.todayLow = +extractDecimal(data.pressTL); - cache.todayHigh = +extractDecimal(data.pressTH); - cache.value = +extractDecimal(data.press); - // Convert the WD change over 3 hours to an hourly rate - cache.trendVal = +extractDecimal(data.presstrendval) / (config.weatherProgram === 2 ? 3 : 1); - - if (data.pressunit === 'hPa' || data.pressunit === 'mb') { - // default min range 990-1030 - steps of 10 hPa - cache.minValue = Math.min(nextLowest(cache.recLow - 2, 10), gaugeGlobals.baroScaleDefMinhPa); - cache.maxValue = Math.max(nextHighest(cache.recHigh + 2, 10), gaugeGlobals.baroScaleDefMaxhPa); - dps = 1; // 1 decimal place - } else if (data.pressunit === 'kPa') { - // default min range 99-105 - steps of 1 kPa - cache.minValue = Math.min(nextLowest(cache.recLow - 0.2, 1), gaugeGlobals.baroScaleDefMinkPa); - cache.maxValue = Math.max(nextHighest(cache.recHigh + 0.2, 1), gaugeGlobals.baroScaleDefMaxkPa); - dps = 2; - } else { - // inHg: default min range 29.5-30.5 - steps of 0.5 inHg - cache.minValue = Math.min(nextLowest(cache.recLow - 0.1, 0.5), gaugeGlobals.baroScaleDefMininHg); - cache.maxValue = Math.max(nextHighest(cache.recHigh + 0.1, 0.5), gaugeGlobals.baroScaleDefMaxinHg); - dps = 3; - } - cache.trendValRnd = cache.trendVal.toFixed(dps); - cache.todayLowRnd = cache.todayLow.toFixed(dps); - cache.todayHighRnd = cache.todayHigh.toFixed(dps); - - if (cache.minValue !== ssGauge.getMinValue() || cache.maxValue !== ssGauge.getMaxValue()) { - ssGauge.setMinValue(cache.minValue); - ssGauge.setMaxValue(cache.maxValue); - ssGauge.setValue(cache.minValue); - } - if (cache.recHigh === cache.todayHigh && cache.recLow === cache.todayLow) { - // VWS does not provide record hi/lo values - cache.sections = []; - cache.areas = [steelseries.Section(cache.todayLow, cache.todayHigh, gaugeGlobals.minMaxArea)]; - } else { - cache.sections = [ - steelseries.Section(cache.minValue, cache.recLow, 'rgba(255,0,0,0.5)'), - steelseries.Section(cache.recHigh, cache.maxValue, 'rgba(255,0,0,0.5)') - ]; - cache.areas = [ - steelseries.Section(cache.minValue, cache.recLow, 'rgba(255,0,0,0.5)'), - steelseries.Section(cache.recHigh, cache.maxValue, 'rgba(255,0,0,0.5)'), - steelseries.Section(cache.todayLow, cache.todayHigh, gaugeGlobals.minMaxArea) - ]; - } - - if (gaugeGlobals.pressureTrendVisible) { - // Use the baroTrend rather than simple arithmetic test - steady is more/less than zero! - t1 = baroTrend(cache.trendVal, data.pressunit, false); - if (t1 === -9999) { - // trend value isn't currently available - cache.trend = steelseries.TrendState.OFF; - } else if (t1 > 0) { - cache.trend = steelseries.TrendState.UP; - } else if (t1 < 0) { - cache.trend = steelseries.TrendState.DOWN; - } else { - cache.trend = steelseries.TrendState.STEADY; - } - ssGauge.setTrend(cache.trend); - } - - ssGauge.setArea(cache.areas); - ssGauge.setSection(cache.sections); - ssGauge.setValueAnimated(cache.value); - - if (ddimgtooltip.showTips) { - // update tooltip - tip = strings.baro_info + ':' + - '
' + - '- ' + strings.minimum_info + ': ' + cache.todayLowRnd + ' ' + data.pressunit + ' ' + strings.at + ' ' + data.TpressTL + - ' | ' + strings.maximum_info + ': ' + cache.todayHighRnd + ' ' + data.pressunit + ' ' + strings.at + ' ' + data.TpressTH; - if (cache.trendVal !== -9999) { - tip += '
' + - '- ' + strings.baro_trend_info + ': ' + baroTrend(cache.trendVal, data.pressunit, true) + ' ' + - (cache.trendValRnd > 0 ? '+' : '') + cache.trendValRnd + ' ' + data.pressunit + '/h'; - } - $('#imgtip5_txt').html(tip); - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[5] !== null) { - $('#imgtip5_img').attr('src', config.imgPathURL + config.tipImgs[5] + cacheDefeat); - } - } - - return { - data: cache, - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the Wind Speed Gauge - // - singleWind = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - - // define wind gauge start values - cache.maxValue = gaugeGlobals.windScaleDefMaxKph; - cache.areas = []; - cache.maxMeasured = 0; - cache.value = 0.0001; - cache.title = strings.wind_title; - - // create wind speed radial gauge - if ($('#canvas_wind').length) { - params.size = Math.ceil($('#canvas_wind').width() * config.gaugeScaling); - params.area = cache.areas; - params.maxValue = cache.maxValue; - params.niceScale = false; - params.thresholdVisible = false; - params.maxMeasuredValueVisible = true; - params.titleString = cache.title; - params.unitString = data.windunit; - - ssGauge = new steelseries.Radial('canvas_wind', params); - ssGauge.setMaxMeasuredValue(cache.maxMeasured); - ssGauge.setValue(cache.value); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_wind').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_wind').css(gaugeShadow(params.size)); - } - - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - var tip; - - cache.value = extractDecimal(data.wlatest); - cache.average = extractDecimal(data.wspeed); - cache.gust = extractDecimal(data.wgust); - cache.maxGustToday = extractDecimal(data.wgustTM); - cache.maxAvgToday = extractDecimal(data.windTM); - - switch (data.windunit) { - case 'mph': - case 'kts': - cache.maxValue = Math.max(nextHighest(cache.maxGustToday, 10), gaugeGlobals.windScaleDefMaxMph); - break; - case 'm/s': - cache.maxValue = Math.max(nextHighest(cache.maxGustToday, 5), gaugeGlobals.windScaleDefMaxMs); - break; - default: - cache.maxValue = Math.max(nextHighest(cache.maxGustToday, 20), gaugeGlobals.windScaleDefMaxKmh); - } - cache.areas = [ - steelseries.Section(0, +cache.average, gaugeGlobals.windAvgArea), - steelseries.Section(+cache.average, +cache.gust, gaugeGlobals.minMaxArea) - ]; - if (cache.maxValue !== ssGauge.getMaxValue()) { - ssGauge.setValue(0); - ssGauge.setMaxValue(cache.maxValue); - } - - ssGauge.setArea(cache.areas); - ssGauge.setMaxMeasuredValue(cache.maxGustToday); - ssGauge.setValueAnimated(cache.value); - - if (ddimgtooltip.showTips) { - // update tooltip - tip = strings.tenminavgwind_info + ': ' + cache.average + ' ' + data.windunit + ' | ' + - strings.maxavgwind_info + ': ' + cache.maxAvgToday + ' ' + data.windunit + '
' + - strings.tenmingust_info + ': ' + cache.gust + ' ' + data.windunit + ' | ' + - strings.maxgust_info + ': ' + cache.maxGustToday + ' ' + data.windunit + ' ' + - strings.at + ' ' + data.TwgustTM + ' ' + strings.bearing_info + ': ' + data.bearingTM + - (isNaN(parseFloat(data.bearingTM)) ? '' : '° (' + getord(+data.bearingTM) + ')'); - $('#imgtip6_txt').html(tip); - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[6] !== null) { - $('#imgtip6_img').attr('src', config.imgPathURL + config.tipImgs[6] + cacheDefeat); - } - } - - return { - data: cache, - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), // End of singleWind() - - // - // Singleton for the Wind Direction Gauge - // - singleDir = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - - // define wind direction gauge start values - cache.valueLatest = 0; - cache.valueAverage = 0; - cache.titles = [strings.latest_web, strings.tenminavg_web]; - - // create wind direction/compass radial gauge - if ($('#canvas_dir').length) { - params.size = Math.ceil($('#canvas_dir').width() * config.gaugeScaling); - params.pointerTypeLatest = gaugeGlobals.pointer; // default TYPE8, - params.pointerTypeAverage = gaugeGlobals.dirAvgPointer; // default TYPE8 - params.pointerColorAverage = gaugeGlobals.dirAvgPointerColour; - params.degreeScale = true; // Show degree scale rather than ordinal directions - params.pointSymbols = strings.compass; - params.roseVisible = false; - params.lcdTitleStrings = cache.titles; - params.useColorLabels = false; - - ssGauge = new steelseries.WindDirection('canvas_dir', params); - ssGauge.setValueAverage(+cache.valueAverage); - ssGauge.setValueLatest(+cache.valueLatest); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_dir').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_dir').css(gaugeShadow(params.size)); - } - - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - var windSpd, windGst, range, tip, i, - rosePoints = 0, - roseMax = 0, - roseSectionAngle = 0, - roseAreas = []; - - cache.valueLatest = extractInteger(data.bearing); - cache.valueAverage = extractInteger(data.avgbearing); - cache.bearingFrom = extractInteger(data.BearingRangeFrom10); - cache.bearingTo = extractInteger(data.BearingRangeTo10); - - ssGauge.setValueAnimatedAverage(+cache.valueAverage); - if (cache.valueAverage === 0) { - cache.valueLatest = 0; - } - ssGauge.setValueAnimatedLatest(+cache.valueLatest); - - if (config.showWindVariation) { - windSpd = +extractDecimal(data.wspeed); - windGst = +extractDecimal(data.wgust); - switch (data.windunit.toLowerCase()) { - case 'mph': - cache.avgKnots = 0.868976242 * windSpd; - cache.gstKnots = 0.868976242 * windGst; - break; - case 'kts': - cache.avgKnots = windSpd; - cache.gstKnots = windGst; - break; - case 'm/s': - cache.avgKnots = 1.94384449 * windSpd; - cache.gstKnots = 1.94384449 * windGst; - break; - case 'km/h': - cache.avgKnots = 0.539956803 * windSpd; - cache.gstKnots = 0.539956803 * windGst; - break; - // no default - } - cache.avgKnots = Math.round(cache.avgKnots); - cache.gstKnots = Math.round(cache.gstKnots); - if (config.showWindMetar) { - ssGauge.VRB = ' - METAR: ' + ('0' + data.avgbearing).slice(-3) + ('0' + cache.avgKnots).slice(-2) + - 'G' + ('0' + cache.gstKnots).slice(-2) + 'KT '; - } else { - ssGauge.VRB = ''; - } - if (windSpd > 0) { - // If variation less than 60 degrees, then METAR = Steady - // Unless range = 0 and from/to direction = avg + 180 - range = (+cache.bearingTo < +cache.bearingFrom ? 360 + (+cache.bearingTo) : +cache.bearingTo) - (+cache.bearingFrom); - - if (cache.avgKnots < 3) { // Europe uses 3kts, USA 6kts as the threshold - if (config.showRoseOnDirGauge) { - ssGauge.setSection([steelseries.Section(cache.bearingFrom, cache.bearingTo, gaugeGlobals.windVariationSector)]); - ssGauge.setSection([]); - } else { - ssGauge.setSection([steelseries.Section(cache.bearingFrom, cache.bearingTo, gaugeGlobals.minMaxArea)]); - ssGauge.setArea([]); - } - } else if (config.showRoseOnDirGauge) { - ssGauge.setSection([steelseries.Section(cache.bearingFrom, cache.bearingTo, gaugeGlobals.windVariationSector)]); - } else { - ssGauge.setSection([]); - ssGauge.setArea([steelseries.Section(cache.bearingFrom, cache.bearingTo, gaugeGlobals.minMaxArea)]); - } - if (config.showWindMetar) { - if ((range < 60 && range > 0) || range === 0 && cache.bearingFrom === cache.valueAverage) { - ssGauge.VRB += ' STDY'; - } else if (cache.avgKnots < 3) { // Europe uses 3kts, USA 6kts as the threshold - ssGauge.VRB += ' VRB'; - } else { - ssGauge.VRB += ' ' + cache.bearingFrom + 'V' + cache.bearingTo; - } - } - } else { - // Zero wind speed, calm - if (config.showWindMetar) { - ssGauge.VRB = ' - METAR: 00000KT'; - } - ssGauge.setSection([]); - if (!config.showRoseOnDirGauge) { - ssGauge.setArea([]); - } - } - } else { - ssGauge.VRB = ''; - } - - // optional rose data on direction gauge - if (config.showRoseOnDirGauge && data.WindRoseData) { - // Process rose data - rosePoints = data.WindRoseData.length; - roseSectionAngle = 360 / rosePoints; - // Find total for all directions - for (i = 0; i < rosePoints; i++) { - roseMax = Math.max(roseMax, data.WindRoseData[i]); - } - // Check we actually have some data, bad things happen if roseMax=0! - if (roseMax > 0) { - // Find relative value for each point, and create a gauge area for it - for (i = 0; i < rosePoints; i++) { - roseAreas[i] = steelseries.Section( - i * roseSectionAngle - roseSectionAngle / 2, - (i + 1) * roseSectionAngle - roseSectionAngle / 2, - 'rgba(' + gradient('2020D0', 'D04040', data.WindRoseData[i] / roseMax) + ',' + - (data.WindRoseData[i] / roseMax).toFixed(2) + ')' - ); - } - } - ssGauge.setArea(roseAreas); - } - - if (ddimgtooltip.showTips) { - // update tooltip - tip = strings.latest_title + ' ' + strings.bearing_info + ': ' + cache.valueLatest + '° (' + getord(+cache.valueLatest) + ')' + - ssGauge.VRB + '
' + strings.tenminavg_web + ' ' + strings.bearing_info + ': ' + cache.valueAverage + '° (' + - getord(+cache.valueAverage) + '), ' + strings.dominant_bearing + ': ' + data.domwinddir; - if (!config.showRoseGauge) { - // Wind run is shown on the wind rose if it is available - tip += '
' + strings.windruntoday + ': ' + data.windrun + ' ' + displayUnits.windrun; - } - $('#imgtip7_txt').html(tip); - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[7] !== null) { - $('#imgtip7_img').attr('src', config.imgPathURL + config.tipImgs[7] + cacheDefeat); - } - } - - return { - data: cache, - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the Wind Rose Gauge - // - singleRose = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - - var buffers = {}; // Stores references to the various canvas buffers - var cache = {}; // various parameters to store for the life time of gauge - var ctxRoseCanvas; // 2D context for the plotted gauge - - cache.firstRun = true; - cache.odoDigits = 5; // Total number of odometer digits including the decimal - - function init() { - var div, roseCanvas; - // Get the context of the gauge canvas on the HTML page - if ($('#canvas_rose').length) { - cache.gaugeSize = Math.ceil($('#canvas_rose').width() * config.gaugeScaling); - cache.gaugeSize2 = cache.gaugeSize / 2; - cache.showOdo = config.showRoseGaugeOdo || false; - - cache.compassStrings = strings.compass; - cache.titleString = strings.windrose; - cache.gaugeOdoTitle = strings.km; - - // Create a hidden div to host the Rose plot - div = document.createElement('div'); - div.style.display = 'none'; - document.body.appendChild(div); - - // Calcuate the size of the gauge background and so the size of rose plot required - cache.plotSize = Math.floor(cache.gaugeSize * 0.68); - cache.plotSize2 = cache.plotSize / 2; - - // rose plot canvas buffer - buffers.plot = document.createElement('canvas'); - buffers.plot.width = cache.plotSize; - buffers.plot.height = cache.plotSize; - buffers.plot.id = 'rosePlot'; - buffers.ctxPlot = buffers.plot.getContext('2d'); - div.appendChild(buffers.plot); - - // Create a steelseries gauge frame - buffers.frame = document.createElement('canvas'); - buffers.frame.width = cache.gaugeSize; - buffers.frame.height = cache.gaugeSize; - buffers.ctxFrame = buffers.frame.getContext('2d'); - steelseries.drawFrame( - buffers.ctxFrame, - gaugeGlobals.frameDesign, - cache.gaugeSize2, - cache.gaugeSize2, - cache.gaugeSize, - cache.gaugeSize - ); - - // Create a steelseries gauge background - buffers.background = document.createElement('canvas'); - buffers.background.width = cache.gaugeSize; - buffers.background.height = cache.gaugeSize; - buffers.ctxBackground = buffers.background.getContext('2d'); - steelseries.drawBackground( - buffers.ctxBackground, - gaugeGlobals.background, - cache.gaugeSize2, - cache.gaugeSize2, - cache.gaugeSize, - cache.gaugeSize - ); - - // Optional - add a background image - /* - if (g_imgSmall !== null) { - var drawSize = g_size * 0.831775; - var x = (g_size - drawSize) / 2; - buffers.ctxBackground.drawImage(g_imgSmall, x, x, cache.gaugeSize, cache.gaugeSize); - } - */ - // Add the compass points - drawCompassPoints(buffers.ctxBackground, cache.gaugeSize); - - // Create a steelseries gauge foreground - buffers.foreground = document.createElement('canvas'); - buffers.foreground.width = cache.gaugeSize; - buffers.foreground.height = cache.gaugeSize; - buffers.ctxForeground = buffers.foreground.getContext('2d'); - steelseries.drawForeground( - buffers.ctxForeground, - gaugeGlobals.foreground, - cache.gaugeSize, - cache.gaugeSize, - false - ); - - roseCanvas = document.getElementById('canvas_rose'); - ctxRoseCanvas = roseCanvas.getContext('2d'); - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_rose').css({ width: cache.gaugeSize + 'px', height: cache.gaugeSize + 'px' }); - } - // resize canvas on main page - roseCanvas.width = cache.gaugeSize; - roseCanvas.height = cache.gaugeSize; - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_rose').css(gaugeShadow(cache.gaugeSize)); - } - - // Render an empty gauge, looks better than just the shadow background and odometer ;) - // Paint the gauge frame - ctxRoseCanvas.drawImage(buffers.frame, 0, 0); - - // Paint the gauge background - ctxRoseCanvas.drawImage(buffers.background, 0, 0); - - // Paint the gauge foreground - ctxRoseCanvas.drawImage(buffers.foreground, 0, 0); - - // Create an odometer - if (cache.showOdo) { - cache.odoHeight = Math.ceil(cache.gaugeSize * 0.08); // Sets the size of the odometer - cache.odoWidth = Math.ceil(Math.floor(cache.odoHeight * 0.68) * cache.odoDigits); // 'Magic' number, do not alter - // Create a new canvas for the oodometer - buffers.Odo = document.createElement('canvas'); - $(buffers.Odo).attr({ - id: 'canvas_odo', - width: cache.odoWidth, - height: cache.odoHeight - }); - // Position it - $(buffers.Odo).attr('class', 'odo'); - // Insert it into the DOM before the Rose gauge - $(buffers.Odo).insertBefore('#canvas_rose'); - // Create the odometer - ssGauge = new steelseries.Odometer('canvas_odo', { - height: cache.odoHeight, - digits: cache.odoDigits - 1, - decimals: 1 - }); - } - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - cache.firstRun = false; - - function update() { - var rose, offset; - - if (ctxRoseCanvas && !cache.firstRun) { - // Clear the gauge - ctxRoseCanvas.clearRect(0, 0, cache.gaugeSize, cache.gaugeSize); - - // Clear the existing rose plot - buffers.ctxPlot.clearRect(0, 0, cache.plotSize, cache.plotSize); - - // Create a new rose plot - rose = new RGraph.Rose('rosePlot', data.WindRoseData); - rose.Set('chart.strokestyle', 'black'); - rose.Set('chart.background.axes.color', 'gray'); - rose.Set('chart.colors.alpha', 0.5); - rose.Set('chart.colors', ['Gradient(#408040:red:#7070A0)']); - rose.Set('chart.margin', Math.ceil(40 / data.WindRoseData.length)); - - rose.Set('chart.title', cache.titleString); - rose.Set('chart.title.size', Math.ceil(0.05 * cache.plotSize)); - rose.Set('chart.title.bold', false); - rose.Set('chart.title.color', gaugeGlobals.background.labelColor.getRgbColor()); - rose.Set('chart.gutter.top', 0.2 * cache.plotSize); - rose.Set('chart.gutter.bottom', 0.2 * cache.plotSize); - - rose.Set('chart.tooltips.effect', 'snap'); - rose.Set('chart.labels.axes', ''); - rose.Set('chart.background.circles', true); - rose.Set('chart.background.grid.spokes', 16); - rose.Set('chart.radius', cache.plotSize2); - rose.Draw(); - - // Add title to windrun odometer to the plot - if (cache.showOdo) { - drawOdoTitle(buffers.ctxPlot); - } - - // Paint the gauge frame - ctxRoseCanvas.drawImage(buffers.frame, 0, 0); - - // Paint the gauge background - ctxRoseCanvas.drawImage(buffers.background, 0, 0); - - // Paint the rose plot - offset = Math.floor(cache.gaugeSize2 - cache.plotSize2); - ctxRoseCanvas.drawImage(buffers.plot, offset, offset); - - // Paint the gauge foreground - ctxRoseCanvas.drawImage(buffers.foreground, 0, 0); - - // update the odometer - if (cache.showOdo) { - ssGauge.setValueAnimated(extractDecimal(data.windrun)); - } - - // update tooltip - if (ddimgtooltip.showTips) { - $('#imgtip10_txt').html( - strings.dominant_bearing + ': ' + data.domwinddir + '
' + - strings.windruntoday + ': ' + data.windrun + ' ' + displayUnits.windrun - ); - } - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[10] !== null) { - $('#imgtip10_img').attr('src', config.imgPathURL + config.tipImgs[10] + cacheDefeat); - } - } - - // Helper function to put the compass points on the background - function drawCompassPoints(ctx, size) { - ctx.save(); - // set the font - ctx.font = 0.08 * size + 'px serif'; - ctx.strokeStyle = gaugeGlobals.background.labelColor.getRgbaColor(); - ctx.fillStyle = gaugeGlobals.background.labelColor.getRgbColor(); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - - // Draw the compass points - for (var i = 0; i < 4; i++) { - ctx.translate(size / 2, size * 0.125); - ctx.fillText(cache.compassStrings[i * 2], 0, 0, size); - ctx.translate(-size / 2, -size * 0.125); - // Move to center - ctx.translate(size / 2, size / 2); - ctx.rotate(Math.PI / 2); - ctx.translate(-size / 2, -size / 2); - } - ctx.restore(); - } - - function drawOdoTitle(ctx) { - ctx.save(); - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.font = 0.05 * cache.gaugeSize + 'px Arial,Verdana,sans-serif'; - ctx.strokeStyle = gaugeGlobals.background.labelColor.getRgbaColor(); - ctx.fillStyle = gaugeGlobals.background.labelColor.getRgbaColor(); - ctx.fillText(cache.gaugeOdoTitle, cache.plotSize2, cache.plotSize * 0.75, cache.plotSize * 0.5); - ctx.restore(); - } - - function setTitle(newTitle) { - cache.titleString = newTitle; - } - - function setOdoTitle(newTitle) { - cache.gaugeOdoTitle = newTitle; - } - - function setCompassStrings(newArray) { - cache.compassStrings = newArray; - if (!cache.firstRun) { - // Redraw the background - steelseries.drawBackground( - buffers.ctxBackground, - gaugeGlobals.background, - cache.gaugeSize2, - cache.gaugeSize2, - cache.gaugeSize, - cache.gaugeSize - ); - // Add the compass points - drawCompassPoints(buffers.ctxBackground, cache.gaugeSize); - } - } - - return { - update: update, - gauge: ssGauge, - drawCompassPoints: drawCompassPoints, - setTitle: setTitle, - setCompassStrings: setCompassStrings, - setOdoTitle: setOdoTitle - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the UV-Index Gauge - // - singleUV = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - - // define UV start values - cache.value = 0.0001; - cache.sections = [ - steelseries.Section(0, 2.9, '#289500'), - steelseries.Section(2.9, 5.8, '#f7e400'), - steelseries.Section(5.8, 7.8, '#f85900'), - steelseries.Section(7.8, 10.9, '#d8001d'), - steelseries.Section(10.9, 20, '#6b49c8') - ]; - // Define value gradient for UV - cache.gradient = new steelseries.gradientWrapper(0, 16, - [0, 0.1, 0.19, 0.31, 0.45, 0.625, 1], - [ - new steelseries.rgbaColor(0, 200, 0, 1), - new steelseries.rgbaColor(0, 200, 0, 1), - new steelseries.rgbaColor(255, 255, 0, 1), - new steelseries.rgbaColor(248, 89, 0, 1), - new steelseries.rgbaColor(255, 0, 0, 1), - new steelseries.rgbaColor(255, 0, 144, 1), - new steelseries.rgbaColor(153, 140, 255, 1) - ] - ); - cache.useSections = false; - cache.useValueGradient = true; - - // create UV bargraph gauge - if ($('#canvas_uv').length) { - params.size = Math.ceil($('#canvas_uv').width() * config.gaugeScaling); - params.gaugeType = steelseries.GaugeType.TYPE3; - params.maxValue = gaugeGlobals.uvScaleDefMax; - params.titleString = strings.uv_title; - params.niceScale = false; - params.section = cache.sections; - params.useSectionColors = cache.useSections; - params.valueGradient = cache.gradient; - params.useValueGradient = cache.useValueGradient; - params.lcdDecimals = gaugeGlobals.uvLcdDecimals; - - ssGauge = new steelseries.RadialBargraph('canvas_uv', params); - ssGauge.setValue(cache.value); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_uv').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_uv').css(gaugeShadow(params.size)); - } - - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - var tip, indx; - - cache.value = extractDecimal(data.UV); - - if (+cache.value === 0) { - indx = 0; - } else if (cache.value < 2.5) { - indx = 1; - } else if (cache.value < 5.5) { - indx = 2; - } else if (cache.value < 7.5) { - indx = 3; - } else if (cache.value < 10.5) { - indx = 4; - } else { - indx = 5; - } - - cache.maxValue = Math.max(nextHighest(cache.value, 2), gaugeGlobals.uvScaleDefMax); - if (cache.maxValue !== ssGauge.getMaxValue()) { - ssGauge.setValue(0); - ssGauge.setMaxValue(cache.maxValue); - } - - cache.risk = strings.uv_levels[indx]; - cache.headLine = strings.uv_headlines[indx]; - cache.detail = strings.uv_details[indx]; - ssGauge.setUnitString(cache.risk); - ssGauge.setValueAnimated(cache.value); - - if (ddimgtooltip.showTips) { - // update tooltip - tip = '' + strings.uv_title + ': ' + cache.value + ' - ' + strings.solar_maxToday + ': ' + data.UVTH + '
'; - tip += '' + cache.headLine + '
'; - tip += cache.detail; - $('#imgtip8_txt').html(tip); - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[8] !== null) { - $('#imgtip8_img').attr('src', config.imgPathURL + config.tipImgs[8] + cacheDefeat); - } - } - - return { - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the Solar Irradiation Gauge - // - singleSolar = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - - // define Solar start values - cache.value = 0.0001; - cache.sections = [ - steelseries.Section(0, 600, 'rgba(40,149,0,0.3)'), - steelseries.Section(600, 800, 'rgba(248,89,0,0.3)'), - steelseries.Section(800, 1000, 'rgba(216,0,29,0.3)'), - steelseries.Section(1000, 1800, 'rgba(107,73,200,0.3)') - ]; - - // create Solar gauge - if ($('#canvas_solar').length) { - params.size = Math.ceil($('#canvas_solar').width() * config.gaugeScaling); - params.section = cache.sections; - params.maxValue = gaugeGlobals.solarGaugeScaleMax; - params.titleString = strings.solar_title; - params.unitString = 'W/m\u00B2'; - params.niceScale = false; - params.thresholdVisible = false; - params.lcdDecimals = 0; - - if (config.showSunshineLed) { - params.userLedVisible = true; - params.userLedColor = steelseries.LedColor.YELLOW_LED; - } - - ssGauge = new steelseries.Radial('canvas_solar', params); - ssGauge.setValue(cache.value); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_solar').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_solar').css(gaugeShadow(params.size)); - } - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - var tip, percent; - - cache.value = +extractInteger(data.SolarRad); - cache.maxToday = extractInteger(data.SolarTM); - cache.currMaxValue = +extractInteger(data.CurrentSolarMax); - percent = (+cache.currMaxValue === 0 ? '--' : Math.round(+cache.value / +cache.currMaxValue * 100)); - - // Need to rescale the gauge? - cache.maxValue = Math.max(cache.value, cache.currMaxValue, cache.maxToday, gaugeGlobals.solarGaugeScaleMax); - cache.maxValue = nextHighest(cache.maxValue, 100); - if (cache.maxValue !== ssGauge.getMaxValue()) { - ssGauge.setValue(0); - ssGauge.setMaxValue(cache.maxValue); - } - - // Set a section (15% of maxScale wide) to show current theoretical max value - if (data.CurrentSolarMax !== 'N/A') { - ssGauge.setArea([ - // Sunshine threshold - steelseries.Section( - Math.max(cache.currMaxValue * gaugeGlobals.sunshineThresholdPct / 100, gaugeGlobals.sunshineThreshold), - cache.currMaxValue, - 'rgba(255,255,50,0.4)' - ), - // Over max threshold - steelseries.Section( - cache.currMaxValue, - Math.min(cache.currMaxValue + cache.maxValue * 0.15, cache.maxValue), - 'rgba(220,0,0,0.5)' - ) - ]); - } - - // Set the values - ssGauge.setMaxMeasuredValue(cache.maxToday); - ssGauge.setValueAnimated(cache.value); - - if (config.showSunshineLed) { - ssGauge.setUserLedOnOff( - percent !== '--' && - percent >= gaugeGlobals.sunshineThresholdPct && - +cache.value >= gaugeGlobals.sunshineThreshold - ); - } - - if (ddimgtooltip.showTips) { - // update tooltip - tip = '' + strings.solar_title + ': ' + cache.value + ' W/m² - ' + - '' + percent + '% ' + strings.solar_ofMax + '
' + - strings.solar_currentMax + ': ' + cache.currMaxValue + ' W/m²'; - if (typeof data.SolarTM !== 'undefined') { - tip += '
' + strings.solar_maxToday + ': ' + cache.maxToday + ' W/m²'; - } - $('#imgtip9_txt').html(tip); - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[9] !== null) { - $('#imgtip9_img').attr('src', config.imgPathURL + config.tipImgs[9] + cacheDefeat); - } - } - - return { - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // Singleton for the Cloudbase Gauge - // - singleCloudBase = (function () { - var instance; // Stores a reference to the Singleton - var ssGauge; // Stores a reference to the SS Gauge - var cache = {}; // Stores various config values and parameters - - function init() { - var params = $.extend(true, {}, commonParams); - - cache.sections = createCloudBaseSections(true); - cache.value = 0.0001; - cache.maxValue = gaugeGlobals.cloudScaleDefMaxm; - - // create Cloud base radial gauge - if ($('#canvas_cloud').length) { - params.size = Math.ceil($('#canvas_cloud').width() * config.gaugeScaling); - params.section = cache.sections; - params.maxValue = cache.maxValue; - params.titleString = strings.cloudbase_title; - params.unitString = strings.metres; - params.thresholdVisible = false; - params.lcdDecimals = 0; - - ssGauge = new steelseries.Radial('canvas_cloud', params); - ssGauge.setValue(cache.value); - - // over-ride CSS applied size? - if (config.gaugeScaling !== 1) { - $('#canvas_cloud').css({ width: params.size + 'px', height: params.size + 'px' }); - } - - // add a shadow to the gauge - if (config.showGaugeShadow) { - $('#canvas_cloud').css(gaugeShadow(params.size)); - } - // subscribe to data updates - $.subscribe('gauges.dataUpdated', update); - $.subscribe('gauges.graphUpdate', updateGraph); - } else { - // cannot draw gauge, return null - return null; - } - - function update() { - cache.value = extractInteger(data.cloudbasevalue); - - if (data.cloudbaseunit === 'm') { - // adjust metre gauge in jumps of 1000 metres, don't downscale during the session - cache.maxValue = Math.max(nextHighest(cache.value, 1000), gaugeGlobals.cloudScaleDefMaxm, cache.maxValue); - if (cache.value <= 1000 && config.roundCloudbaseVal) { - // and round the value to the nearest 10 m - cache.value = Math.round(cache.value / 10) * 10; - } else if (config.roundCloudbaseVal) { - // and round the value to the nearest 50 m - cache.value = Math.round(cache.value / 50) * 50; - } - } else { - // adjust feet gauge in jumps of 2000 ft, don't downscale during the session - cache.maxValue = Math.max(nextHighest(cache.value, 2000), gaugeGlobals.cloudScaleDefMaxft, cache.maxValue); - if (cache.value <= 2000 && config.roundCloudbaseVal) { - // and round the value to the nearest 50 ft - cache.value = Math.round(cache.value / 50) * 50; - } else if (config.roundCloudbaseVal) { - // and round the value to the nearest 100 ft - cache.value = Math.round(cache.value / 100) * 100; - } - } - - if (cache.maxValue !== ssGauge.getMaxValue()) { - if (ssGauge.getMaxValue() > cache.maxValue) { - // Gauge currently showing more than our max (nice scale effct), - // so reset our max to match - cache.maxValue = ssGauge.getMaxValue(); - } else { - // Gauge scale is too low, increase it. - // First set the pointer back to zero so we get a nice animation - ssGauge.setValue(0); - // and redraw the gauge with teh new scale - ssGauge.setMaxValue(cache.maxValue); - } - } - ssGauge.setValueAnimated(cache.value); - - if (config.showPopupData) { - // static tooltip on cloud gauge - $('#imgtip11_txt').html('' + strings.cloudbase_popup_title + '
' + strings.cloudbase_popup_text); - } - } // End of update() - - function updateGraph(evnt, cacheDefeat) { - if (config.tipImgs[11] !== null) { - $('#imgtip11_img').attr('src', config.imgPathURL + config.tipImgs[11] + cacheDefeat); - } - } - - return { - data: cache, - update: update, - gauge: ssGauge - }; - } // End of init() - - return { - // Get the Singleton instance if one exists - // or create one if it doesn't - getInstance: function () { - if (!instance) { - instance = init(); - } - return instance; - } - }; - })(), - - // - // getRealtime() fetches the realtimegauges JSON data from the server - // - getRealtime = function () { - var url = config.realTimeURL; - if ($.active > 0 && undefined != jqXHR) { - // kill any outstanding requests - jqXHR.abort(); - } - if (config.longPoll) { - url += '?timestamp=' + timestamp; - } - jqXHR = $.ajax({ - url: url, - cache: (config.longPoll), - dataType: 'json', - timeout: config.longPoll ? (Math.min(config.realtimeInterval, 20) + 21) * 1000 : 21000 // 21 second time-out by default - }).done(function (data) { - checkRtResp(data); - }).fail(function (xhr, status, err) { - checkRtError(xhr, status, err); - }); - }, - - // - // checkRtResp() called by the Ajax fetch once data has been downloaded - // - checkRtResp = function (response) { - var delay; - statusTimer.reset(config.longPoll ? 1 : config.realtimeInterval); - if (config.longPoll && response.status !== 'OK') { - checkRtError(null, 'PHP Error', response.status); - } else { - if (processData(response)) { - delay = ajaxDelay; - } else { - delay = 5; - } - if (delay > 0) { - downloadTimer = setTimeout(getRealtime, delay * 1000); - } else { - getRealtime(); - } - } - }, - - // - // checkRtError() called by the Ajax fetch if an error occurs during the fetching realtimegauges.txt - // - checkRtError = function (xhr, status, error) { - if (xhr == null || xhr.statusText !== 'abort') { - // Clear any existing download timer - clearTimeout(downloadTimer); - // Set the status LED to off - ledIndicator.setLedOnOff(false); - ledIndicator.setTitle(strings.led_title_unknown); - statusScroller.setText(status + ': ' + error); - // wait 5 seconds, then try again... - downloadTimer = setTimeout(getRealtime, 5000); - } - }, - - // - // processData() massages the data returned in realtimegauges.txt, and posts a gauges.dataUpdated event to update the page - // - processData = function (dataObj) { - var str, dt, tm, today, now, then, tmp, elapsedMins, retVal; - // copy the realtime fields into the global 'data' object - if (config.longPoll) { - timestamp = dataObj.timestamp; - data = dataObj.data; - } else { - // normal polling - data = dataObj; - } - - // and check we have the expected version number - if (typeof data.ver !== 'undefined' && data.ver >= realtimeVer) { - // manpulate the last rain time into something more friendly - try { - str = data.LastRainTipISO.split(' '); - dt = str[0].replace(/\//g, '-').split('-'); // WD uses dd/mm/yyyy, we use a '-' - tm = str[1].split(':'); - today = new Date(); - today.setHours(0, 0, 0, 0); - if (typeof data.dateFormat === 'undefined') { - data.dateFormat = 'y/m/d'; - } else { - // frig for WD bug which leaves a trailing % character from the tag - data.dateFormat = data.dateFormat.replace('%', ''); - } - if (data.dateFormat === 'y/m/d') { - // ISO/Cumulus format - then = new Date(dt[0], dt[1] - 1, dt[2], tm[0], tm[1], 0, 0); - } else if (data.dateFormat === 'd/m/y') { - then = new Date(dt[2], dt[1] - 1, dt[0], tm[0], tm[1], 0, 0); - } else { // m/d/y - then = new Date(dt[2], dt[0] - 1, dt[1], tm[0], tm[1], 0, 0); - } - if (then.getTime() >= today.getTime()) { - data.LastRained = strings.LastRainedT_info + ' ' + str[1]; - } else if (then.getTime() + 86400000 >= today.getTime()) { - data.LastRained = strings.LastRainedY_info + ' ' + str[1]; - } else { - data.LastRained = then.getDate().toString() + ' ' + strings.months[then.getMonth()] + ' ' + strings.at + ' ' + str[1]; - } - } catch (e) { - data.LastRained = data.LastRainTipISO; - } - if (data.tempunit.length > 1) { - // clean up temperature units - remove html encoded degree symbols - data.tempunit = data.tempunit.replace(/&\S*;/, '°'); // old Cumulus versions uses °, WeatherCat uses ° - } else { - // using new realtimegaugesT.txt with Cumulus > 1.9.2 - data.tempunit = '°' + data.tempunit; - } - - // Check for station off-line - now = Date.now(); - tmp = data.timeUTC.split(','); - sampleDate = Date.UTC(tmp[0], tmp[1] - 1, tmp[2], tmp[3], tmp[4], tmp[5]); - if (now - sampleDate > config.stationTimeout * 60 * 1000) { - elapsedMins = Math.floor((now - sampleDate) / (1000 * 60)); - // the realtimegauges.txt file isn't being updated - ledIndicator.setLedColor(steelseries.LedColor.RED_LED); - ledIndicator.setTitle(strings.led_title_offline); - ledIndicator.blink(true); - if (elapsedMins < 120) { - // up to 2 hours ago - tm = elapsedMins.toString() + ' ' + strings.StatusMinsAgo; - } else if (elapsedMins < 2 * 24 * 60) { - // up to 48 hours ago - tm = Math.floor(elapsedMins / 60).toString() + ' ' + strings.StatusHoursAgo; - } else { - // days ago! - tm = Math.floor(elapsedMins / (60 * 24)).toString() + ' ' + strings.StatusDaysAgo; - } - data.forecast = strings.led_title_offline + ' ' + strings.StatusLastUpdate + ' ' + tm; - } else if (+data.SensorContactLost === 1) { - // Fine Offset sensor status - ledIndicator.setLedColor(steelseries.LedColor.RED_LED); - ledIndicator.setTitle(strings.led_title_lost); - ledIndicator.blink(true); - data.forecast = strings.led_title_lost; - } else { - ledIndicator.setLedColor(steelseries.LedColor.GREEN_LED); - ledIndicator.setTitle(strings.led_title_ok + '. ' + strings.StatusLastUpdate + ': ' + data.date); - ledIndicator.blink(false); - ledIndicator.setLedOnOff(true); - } - - // de-encode the forecast string if required (Cumulus support for extended characters) - data.forecast = $('
').html(data.forecast).text(); - data.forecast = data.forecast.trim(); - - data.pressunit = data.pressunit.trim(); // WView sends ' in', ' mb', or ' hPa' - if (data.pressunit === 'in') { // Cumulus and WView send 'in' - data.pressunit = 'inHg'; - } - - data.windunit = data.windunit.trim(); // WView sends ' kmh' etc - data.windunit = data.windunit.toLowerCase(); // WeatherCat sends "MPH" - if (data.windunit === 'knots') { // WeatherCat/weewx send "Knots", we use "kts" - data.windunit = 'kts'; - } - - if (data.windunit === 'kmh' || data.windunit === 'kph') { // WD wind unit omits '/', weewx sends 'kph' - data.windunit = 'km/h'; - } - - data.rainunit = data.rainunit.trim(); // WView sends ' mm' etc - - // take a look at the cloud base data... - // change WeatherCat units from Metres/Feet to m/ft - try { - if (data.cloudbaseunit.toLowerCase() === 'metres') { - data.cloudbaseunit = 'm'; - } else if (data.cloudbaseunit.toLowerCase() === 'feet') { - data.cloudbaseunit = 'ft'; - } - } catch (e) { - data.cloudbaseunit = ''; - } - if (config.showCloudGauge && ( - (config.weatherProgram === 4 || config.weatherProgram === 5) || - data.cloudbasevalue === '')) { - // WeatherCat and VWS (and WView?) do not provide a cloud base value, so we have to calculate it... - // assume if the station uses an imperial wind speed they want cloud base in feet, otherwise metres - data.cloudbaseunit = (data.windunit === 'mph' || data.windunit === 'kts') ? 'ft' : 'm'; - data.cloudbasevalue = calcCloudbase(data.temp, data.tempunit, data.dew, data.cloudbaseunit); - } - - // Temperature data conversion for display required? - if (data.tempunit[1] !== displayUnits.temp && userUnitsSet) { - // temp needs converting - if (data.tempunit[1] === 'C') { - convTempData(c2f); - } else { - convTempData(f2c); - } - } else if (firstRun) { - displayUnits.temp = data.tempunit[1]; - setRadioCheck('rad_unitsTemp', displayUnits.temp); - } - - // Rain data conversion for display required? - if (data.rainunit !== displayUnits.rain && userUnitsSet) { - // rain needs converting - convRainData(displayUnits.rain === 'mm' ? in2mm : mm2in); - } else if (firstRun) { - displayUnits.rain = data.rainunit; - setRadioCheck('rad_unitsRain', displayUnits.rain); - } - - // Wind data conversion for display required? - if (data.windunit !== displayUnits.wind && userUnitsSet) { - // wind needs converting - convWindData(data.windunit, displayUnits.wind); - } else if (firstRun) { - displayUnits.wind = data.windunit; - displayUnits.windrun = getWindrunUnits(data.windunit); - setRadioCheck('rad_unitsWind', displayUnits.wind); - } - - // Pressure data conversion for display required? - if (data.pressunit !== displayUnits.press && userUnitsSet) { - convBaroData(data.pressunit, displayUnits.press); - } else if (firstRun) { - displayUnits.press = data.pressunit; - setRadioCheck('rad_unitsPress', displayUnits.press); - } - - // Cloud height data conversion for display required? - if (data.cloudbaseunit !== displayUnits.cloud && userUnitsSet) { - // Cloud height needs converting - convCloudBaseData(displayUnits.cloud === 'm' ? ft2m : m2ft); - } else if (firstRun) { - displayUnits.cloud = data.cloudbaseunit; - setRadioCheck('rad_unitsCloud', displayUnits.cloud); - } - - statusScroller.setText(data.forecast); - - // first time only, setup units etc - if (firstRun) { - doFirst(); - } - - // publish the update, use the shared data object rather than transferring it - $.publish('gauges.dataUpdated', {}); - - retVal = true; - } else { - // set an error message - if (data.ver < realtimeVer) { - statusTimer.setValue(0); - statusScroller.setText('Your ' + config.realTimeURL.substr(config.realTimeURL.lastIndexOf('/') + 1) + ' file template needs updating!'); - return false; - } else { - // oh-oh! The number of data fields isn't what we expected - statusScroller.setText(strings.realtimeCorrupt); - } - ledIndicator.setLedOnOff(false); - ledIndicator.setTitle(strings.led_title_unknown); - - retVal = false; - } - return retVal; - }, - - // - // pagetimeout() called once every config.pageUpdateLimit minutes to stop updates and prevent page 'sitters' - // - pageTimeout = function () { - statusScroller.setText(strings.StatusPageLimit); - ledIndicator.setLedColor(steelseries.LedColor.RED_LED); - ledIndicator.setTitle(strings.StatusPageLimit); - ledIndicator.blink(true); - ledIndicator.setTitle(strings.StatusTimeout); - - // stop any pending download - clearTimeout(downloadTimer); - - // stop any long polling in progress - if ($.active > 0) { - jqXHR.abort(); - } - - // stop the clock - clearInterval(tickTockInterval); - - // clear the timer display - statusTimer.setValue(0); - - // set an onclick event on the LED to restart everything - $('#canvas_led').click( - function click() { - // disable the onClick event again - $('#canvas_led').unbind('click'); - // reset the timer count to 1 - statusTimer.reset(1); - // restart the timer to update the status time - tickTockInterval = setInterval( - function tick() { - $.publish('gauges.clockTick', null); - }, - 1000 - ); - - // restart the page timeout timer, so we hit this code again - setTimeout(pageTimeout, config.pageUpdateLimit * 60 * 1000); - - // refresh the page data - getRealtime(); - } - ); - }, - - // - // doFirst() called by doUpdate() the first time the page is updated to set-up various things that are - // only known when the realtimegauges.txt data is available - // - doFirst = function () { - var cacheDefeat = '?' + (new Date()).getTime().toString(); - - if (data.tempunit[1] === 'F') { - displayUnits.temp = 'F'; - setRadioCheck('rad_unitsTemp', 'F'); - setTempUnits(false); - } - - if (data.pressunit !== 'hPa') { - displayUnits.press = data.pressunit; - setRadioCheck('rad_unitsPress', data.pressunit); - setBaroUnits(data.pressunit); - } - - if (data.windunit !== 'km/h') { - displayUnits.wind = data.windunit; - setRadioCheck('rad_unitsWind', data.windunit); - setWindUnits(data.windunit); - } - - if (data.rainunit !== 'mm') { - displayUnits.rain = data.rainunit; - setRadioCheck('rad_unitsRain', data.rainunit); - setRainUnits(false); - } - - if (config.showSolarGauge && typeof data.SolarTM !== 'undefined' && gaugeSolar) { - gaugeSolar.gauge.setMaxMeasuredValueVisible(true); - } - - if (config.showCloudGauge && data.cloudbaseunit !== 'm') { - displayUnits.cloud = data.cloudbaseunit; - setRadioCheck('rad_unitsCloud', data.cloudbaseunit); - setCloudBaseUnits(false); - } - - // set the script version on the page - $('#scriptVer').html(config.scriptVer); - // set the version information from the station - $('#programVersion').html(data.version); - $('#programBuild').html(data.build); - $('#programName').html(programLink[config.weatherProgram]); - - if (config.showPopupData) { - // now initialise the pop-up script and download the trend /images - // - has to be done here as doFirst may remove elements from the page - // - and we delay the download of the /images speeding up page display - ddimgtooltip.init('[id^="tip_"]'); - // Are we running on a phone device (or really low res screen)? - if ($(window).width() < 480) { - $('.ddimgtooltip').filter(':hidden').width('200px'); - } - } - - if (config.showPopupData && config.showPopupGraphs) { - // now download the trend /images - // - has to be done here as doFirst may remove elements from the page - // - and we delay the download of the /images speeding up page display - // - $('#imgtip0_img').attr('src', config.imgPathURL + config.tipImgs[0][0] + cacheDefeat); - if (gaugeDew) { - $('#imgtip1_img').attr('src', config.imgPathURL + config.tipImgs[1][gaugeDew.data.popupImg] + cacheDefeat); - } - $('#imgtip2_img').attr('src', config.imgPathURL + config.tipImgs[2] + cacheDefeat); - $('#imgtip3_img').attr('src', config.imgPathURL + config.tipImgs[3] + cacheDefeat); - $('#imgtip4_img').attr('src', config.imgPathURL + config.tipImgs[4][0] + cacheDefeat); - $('#imgtip5_img').attr('src', config.imgPathURL + config.tipImgs[5] + cacheDefeat); - $('#imgtip6_img').attr('src', config.imgPathURL + config.tipImgs[6] + cacheDefeat); - $('#imgtip7_img').attr('src', config.imgPathURL + config.tipImgs[7] + cacheDefeat); - $('#imgtip8_img').attr('src', config.imgPathURL + config.tipImgs[8] + cacheDefeat); - $('#imgtip9_img').attr('src', config.imgPathURL + config.tipImgs[9] + cacheDefeat); - $('#imgtip10_img').attr('src', config.imgPathURL + config.tipImgs[10] + cacheDefeat); - $('#imgtip11_img').attr('src', config.imgPathURL + config.tipImgs[11] + cacheDefeat); - // start a timer for popup graphic updates - setInterval( - function timeout() { - $.publish('gauges.graphUpdate', '?' + (new Date()).getTime().toString()); - }, - config.graphUpdateTime * 60 * 1000 - ); - } - - firstRun = false; - }, - - // - // createTempSections() creates an array of gauge sections appropriate for Celsius or Fahrenheit scales - // - createTempSections = function (celsius) { - var section; - if (celsius) { - section = [ - steelseries.Section(-100, -35, 'rgba(195, 92, 211, 0.4)'), - steelseries.Section(-35, -30, 'rgba(139, 74, 197, 0.4)'), - steelseries.Section(-30, -25, 'rgba(98, 65, 188, 0.4)'), - steelseries.Section(-25, -20, 'rgba(62, 66, 185, 0.4)'), - steelseries.Section(-20, -15, 'rgba(42, 84, 194, 0.4)'), - steelseries.Section(-15, -10, 'rgba(25, 112, 210, 0.4)'), - steelseries.Section(-10, -5, 'rgba(9, 150, 224, 0.4)'), - steelseries.Section(-5, 0, 'rgba(2, 170, 209, 0.4)'), - steelseries.Section(0, 5, 'rgba(0, 162, 145, 0.4)'), - steelseries.Section(5, 10, 'rgba(0, 158, 122, 0.4)'), - steelseries.Section(10, 15, 'rgba(54, 177, 56, 0.4)'), - steelseries.Section(15, 20, 'rgba(111, 202, 56, 0.4)'), - steelseries.Section(20, 25, 'rgba(248, 233, 45, 0.4)'), - steelseries.Section(25, 30, 'rgba(253, 142, 42, 0.4)'), - steelseries.Section(30, 40, 'rgba(236, 45, 45, 0.4)'), - steelseries.Section(40, 100, 'rgba(245, 109, 205, 0.4)') - ]; - } else { - section = [ - steelseries.Section(-200, -30, 'rgba(195, 92, 211, 0.4)'), - steelseries.Section(-30, -25, 'rgba(139, 74, 197, 0.4)'), - steelseries.Section(-25, -15, 'rgba(98, 65, 188, 0.4)'), - steelseries.Section(-15, -5, 'rgba(62, 66, 185, 0.4)'), - steelseries.Section(-5, 5, 'rgba(42, 84, 194, 0.4)'), - steelseries.Section(5, 15, 'rgba(25, 112, 210, 0.4)'), - steelseries.Section(15, 25, 'rgba(9, 150, 224, 0.4)'), - steelseries.Section(25, 32, 'rgba(2, 170, 209, 0.4)'), - steelseries.Section(32, 40, 'rgba(0, 162, 145, 0.4)'), - steelseries.Section(40, 50, 'rgba(0, 158, 122, 0.4)'), - steelseries.Section(50, 60, 'rgba(54, 177, 56, 0.4)'), - steelseries.Section(60, 70, 'rgba(111, 202, 56, 0.4)'), - steelseries.Section(70, 80, 'rgba(248, 233, 45, 0.4)'), - steelseries.Section(80, 90, 'rgba(253, 142, 42, 0.4)'), - steelseries.Section(90, 110, 'rgba(236, 45, 45, 0.4)'), - steelseries.Section(110, 200, 'rgba(245, 109, 205, 0.4)') - ]; - } - return section; - }, - - // - // createRainSections() returns an array of section highlights for the Rain Rate gauge - // - /* - Assumes 'standard' descriptive limits from UK met office: - < 0.25 mm/hr - Very light rain - 0.25mm/hr to 1.0mm/hr - Light rain - 1.0 mm/hr to 4.0 mm/hr - Moderate rain - 4.0 mm/hr to 16.0 mm/hr - Heavy rain - 16.0 mm/hr to 50 mm/hr - Very heavy rain - > 50.0 mm/hour - Extreme rain - - Roughly translated to the corresponding Inch rates - < 0.001 - 0.001 to 0.05 - 0.05 to 0.20 - 0.20 to 0.60 - 0.60 to 2.0 - > 2.0 - */ - createRainRateSections = function (metric) { - var factor = metric ? 1 : 1 / 25; - return [ - steelseries.Section(0, 0.25 * factor, 'rgba(0, 140, 0, 0.5)'), - steelseries.Section(0.25 * factor, 1 * factor, 'rgba(80, 192, 80, 0.5)'), - steelseries.Section(1 * factor, 4 * factor, 'rgba(150, 203, 150, 0.5)'), - steelseries.Section(4 * factor, 16 * factor, 'rgba(212, 203, 109, 0.5)'), - steelseries.Section(16 * factor, 50 * factor, 'rgba(225, 155, 105, 0.5)'), - steelseries.Section(50 * factor, 1000 * factor, 'rgba(245, 86, 59, 0.5)') - ]; - }, - - // - // createRainFallSections()returns an array of section highlights for total rainfall in mm or inches - // - createRainfallSections = function (metric) { - var factor = metric ? 1 : 1 / 25; - return [ - steelseries.Section(0, 5 * factor, 'rgba(0, 250, 0, 1)'), - steelseries.Section(5 * factor, 10 * factor, 'rgba(0, 250, 117, 1)'), - steelseries.Section(10 * factor, 25 * factor, 'rgba(218, 246, 0, 1)'), - steelseries.Section(25 * factor, 40 * factor, 'rgba(250, 186, 0, 1)'), - steelseries.Section(40 * factor, 50 * factor, 'rgba(250, 95, 0, 1)'), - steelseries.Section(50 * factor, 65 * factor, 'rgba(250, 0, 0, 1)'), - steelseries.Section(65 * factor, 75 * factor, 'rgba(250, 6, 80, 1)'), - steelseries.Section(75 * factor, 100 * factor, 'rgba(205, 18, 158, 1)'), - steelseries.Section(100 * factor, 125 * factor, 'rgba(0, 0, 250, 1)'), - steelseries.Section(125 * factor, 500 * factor, 'rgba(0, 219, 212, 1)') - ]; - }, - - // - // createRainfallGradient() returns an array of SS colours for continuous gradient colouring of the total rainfall LED gauge - // - createRainfallGradient = function (metric) { - var grad = new steelseries.gradientWrapper( - 0, - (metric ? 100 : 4), - [0, 0.1, 0.62, 1], - [ - new steelseries.rgbaColor(15, 148, 0, 1), - new steelseries.rgbaColor(213, 213, 0, 1), - new steelseries.rgbaColor(213, 0, 25, 1), - new steelseries.rgbaColor(250, 0, 0, 1) - ] - ); - return grad; - }, - - // - // createClousBaseSections() returns an array of section highlights for the Cloud Base gauge - // - createCloudBaseSections = function (metric) { - var section; - if (metric) { - section = [ - steelseries.Section(0, 150, 'rgba(245, 86, 59, 0.5)'), - steelseries.Section(150, 300, 'rgba(225, 155, 105, 0.5)'), - steelseries.Section(300, 750, 'rgba(212, 203, 109, 0.5)'), - steelseries.Section(750, 1000, 'rgba(150, 203, 150, 0.5)'), - steelseries.Section(1000, 1500, 'rgba(80, 192, 80, 0.5)'), - steelseries.Section(1500, 2500, 'rgba(0, 140, 0, 0.5)'), - steelseries.Section(2500, 5500, 'rgba(19, 103, 186, 0.5)') - ]; - } else { - section = [ - steelseries.Section(0, 500, 'rgba(245, 86, 59, 0.5)'), - steelseries.Section(500, 1000, 'rgba(225, 155, 105, 0.5)'), - steelseries.Section(1000, 2500, 'rgba(212, 203, 109, 0.5)'), - steelseries.Section(2500, 3500, 'rgba(150, 203, 150, 0.5)'), - steelseries.Section(3500, 5500, 'rgba(80, 192, 80, 0.5)'), - steelseries.Section(5500, 8500, 'rgba(0, 140, 0, 0.5)'), - steelseries.Section(8500, 18000, 'rgba(19, 103, 186, 0.5)') - ]; - } - return section; - }, - - // - // --------------- Helper functions ------------------ - // - - // - // getord() converts a value in degrees (0-360) into a localised compass point (N, ENE, NE, etc) - // - getord = function (deg) { - if (deg === 0) { - // Special case, 0=No wind, 360=North - return strings.calm; - } else { - return (strings.coords[Math.floor((deg + 11.25) / 22.5) % 16]); - } - }, - - // - // getUrlParam() extracts the named parameter from the current page URL - // - getUrlParam = function (paramName) { - var name, regexS, regex, results; - name = paramName.replace(/(\[|\])/g, '\\$1'); - regexS = '[\\?&]' + name + '=([^&#]*)'; - regex = new RegExp(regexS); - results = regex.exec(window.location.href); - if (results === null) { - return ''; - } else { - return results[1]; - } - }, - - // - // extractDecimal() returns a decimal number from a string, the decimal point can be either a dot or a comma - // it ignores any text such as pre/appended units - // - extractDecimal = function (str, errVal) { - try { - return (/[-+]?[0-9]+\.?[0-9]*/).exec(str.replace(',', '.'))[0]; - } catch (e) { - // error condition - return errVal || -9999; - } - }, - - // - // extractInteger() returns an integer from a string - // it ignores any text such as pre/appended units - // - extractInteger = function (str, errVal) { - try { - return (/[-+]?[0-9]+/).exec(str)[0]; - } catch (e) { - // error condition - return errVal || -9999; - } - }, - - // - // tempTrend() converts a temperature trend value into a localised string, or +1, 0, -1 depending on the value of bTxt - // - tempTrend = function (trend, units, bTxt) { - // Scale is over 3 hours, in Celsius - var val = trend * 3 * (units[1] === 'C' ? 1 : (5 / 9)), - ret; - if (trend === -9999) { - ret = (bTxt ? '--' : trend); - } else if (val > 5) { - ret = (bTxt ? strings.RisingVeryRapidly : 1); - } else if (val > 3) { - ret = (bTxt ? strings.RisingQuickly : 1); - } else if (val > 1) { - ret = (bTxt ? strings.Rising : 1); - } else if (val > 0.5) { - ret = (bTxt ? strings.RisingSlowly : 1); - } else if (val >= -0.5) { - ret = (bTxt ? strings.Steady : 0); - } else if (val >= -1) { - ret = (bTxt ? strings.FallingSlowly : -1); - } else if (val >= -3) { - ret = (bTxt ? strings.Falling : -1); - } else if (val >= -5) { - ret = (bTxt ? strings.FallingQuickly : -1); - } else { - ret = (bTxt ? strings.FallingVeryRapidly : -1); - } - return ret; - }, - - // - // baroTrend() converts a pressure trend value into a localised string, or +1, 0, -1 depending on the value of bTxt - // - baroTrend = function (trend, units, bTxt) { - var val = trend * 3, - ret; - // The terms below are the UK Met Office terms for a 3 hour change in hPa - // trend is supplied as an hourly change, so multiply by 3 - if (units === 'inHg') { - val *= 33.8639; - } else if (units === 'kPa') { - val *= 10; - // assume everything else is hPa or mb, could be dangerous! - } - if (trend === -9999) { - ret = (bTxt ? '--' : trend); - } else if (val > 6.0) { - ret = (bTxt ? strings.RisingVeryRapidly : 1); - } else if (val > 3.5) { - ret = (bTxt ? strings.RisingQuickly : 1); - } else if (val > 1.5) { - ret = (bTxt ? strings.Rising : 1); - } else if (val > 0.1) { - ret = (bTxt ? strings.RisingSlowly : 1); - } else if (val >= -0.1) { - ret = (bTxt ? strings.Steady : 0); - } else if (val >= -1.5) { - ret = (bTxt ? strings.FallingSlowly : -1); - } else if (val >= -3.5) { - ret = (bTxt ? strings.Falling : -1); - } else if (val >= -6.0) { - ret = (bTxt ? strings.FallingQuickly : -1); - } else { - ret = (bTxt ? strings.FallingVeryRapidly : -1); - } - return ret; - }, - - // - // getMinTemp() returns the lowest temperature today for gauge scaling - // - getMinTemp = function (deflt) { - return Math.min( - extractDecimal(data.tempTL, deflt), - extractDecimal(data.dewpointTL, deflt), - extractDecimal(data.apptempTL, deflt), - extractDecimal(data.feelslikeTL, deflt), - extractDecimal(data.wchillTL, deflt)); - }, - - // - // getMaxTemp() returns the highest temperature today for gauge scaling - // - getMaxTemp = function (deflt) { - return Math.max( - extractDecimal(data.tempTH, deflt), - extractDecimal(data.apptempTH, deflt), - extractDecimal(data.feelslikeTH, deflt), - extractDecimal(data.heatindexTH, deflt), - extractDecimal(data.humidex, deflt)); - }, - - // Celsius to Fahrenheit - c2f = function (val) { - return (extractDecimal(val) * 9 / 5 + 32).toFixed(1); - }, - // Fahrenheit to Celsius - f2c = function (val) { - return ((extractDecimal(val) - 32) * 5 / 9).toFixed(1); - }, - // kph to ms - kmh2ms = function (val) { - return (extractDecimal(val) * 0.2778).toFixed(1); - }, - // ms to kph - ms2kmh = function (val) { - return (extractDecimal(val) * 3.6).toFixed(1); - }, - kmh2ms = function (val) { - return (extractDecimal(val) / 3.6).toFixed(1); - }, - // mm to inches - mm2in = function (val) { - return (extractDecimal(val) / 25.4).toFixed(2); - }, - // inches to mm - in2mm = function (val) { - return (extractDecimal(val) * 25.4).toFixed(1); - }, - // miles to km - miles2km = function (val) { - return (extractDecimal(val) * 1.609344).toFixed(1); - }, - // nautical miles to km - nmiles2km = function (val) { - return (extractDecimal(val) * 1.85200).toFixed(1); - }, - // km to miles - km2miles = function (val) { - return (extractDecimal(val) / 1.609344).toFixed(1); - }, - // km to nautical miles - km2nmiles = function (val) { - return (extractDecimal(val) / 1.85200).toFixed(1); - }, - // hPa to inHg (@0°C) - hpa2inhg = function (val, decimals) { - return (extractDecimal(val) * 0.029528744).toFixed(decimals || 3); - }, - // inHg to hPa (@0°C) - inhg2hpa = function (val) { - return (extractDecimal(val) / 0.029528744).toFixed(1); - }, - // kPa to hPa - kpa2hpa = function (val) { - return (extractDecimal(val) * 10).toFixed(1); - }, - // hPa to kPa - hpa2kpa = function (val, decimals) { - return (extractDecimal(val) / 10).toFixed(decimals || 2); - }, - // m to ft - m2ft = function (val) { - return (val * 3.2808399).toFixed(0); - }, - // ft to m - ft2m = function (val) { - return (val / 3.2808399).toFixed(0); - }, - - // - // setCookie() writes the 'obj' in cookie 'name' for persistent storage - // - setCookie = function (name, obj) { - var date = new Date(), - expires; - // cookies valid for 1 year - date.setYear(date.getFullYear() + 1); - expires = '; expires=' + date.toGMTString(); - document.cookie = name + '=' + encodeURIComponent(JSON.stringify(obj)) + expires; - }, - - // - // getCookie() reads the value of cookie 'name' from persistent storage - // - getCookie = function (name) { - var i, x, y, - ret = null, - arrCookies = document.cookie.split(';'); - - for (i = arrCookies.length; i--;) { - x = arrCookies[i].split('='); - if (x[0].trim() === name) { - try { - y = decodeURIComponent(x[1]); - } catch (e) { - y = x[1]; - } - ret = JSON.parse(unescape(y)); - break; - } - } - return ret; - }, - - // - // setRadioCheck() sets the desired value of the HTML radio buttons to be selected - // - setRadioCheck = function (obj, val) { - $('input:radio[name="' + obj + '"]').filter('[value="' + val + '"]').prop('checked', true); - }, - - // - // convTempData() converts all the temperature values using the supplied conversion function - // - convTempData = function (convFunc) { - data.apptemp = convFunc(data.apptemp); - data.apptempTH = convFunc(data.apptempTH); - data.apptempTL = convFunc(data.apptempTL); - data.feelslike = convFunc(data.feelslike); - data.feelslikeTH = convFunc(data.feelslikeTH); - data.feelslikeTL = convFunc(data.feelslikeTL); - data.dew = convFunc(data.dew); - data.dewpointTH = convFunc(data.dewpointTH); - data.dewpointTL = convFunc(data.dewpointTL); - data.heatindex = convFunc(data.heatindex); - data.heatindexTH = convFunc(data.heatindexTH); - data.humidex = convFunc(data.humidex); - data.intemp = convFunc(data.intemp); - if (data.intempTL && data.intempTH) { - data.intempTL = convFunc(data.intempTL); - data.intempTH = convFunc(data.intempTH); - } - data.temp = convFunc(data.temp); - data.tempTH = convFunc(data.tempTH); - data.tempTL = convFunc(data.tempTL); - data.wchill = convFunc(data.wchill); - data.wchillTL = convFunc(data.wchillTL); - if (convFunc === c2f) { - data.temptrend = (+extractDecimal(data.temptrend) * 9 / 5).toFixed(1); - data.tempunit = '°F'; - } else { - data.temptrend = (+extractDecimal(data.temptrend) * 5 / 9).toFixed(1); - data.tempunit = '°C'; - } - }, - - // - // convRainData() converts all the rain data units using the supplied conversion function - // - convRainData = function (convFunc) { - data.rfall = convFunc(data.rfall); - data.rrate = convFunc(data.rrate); - data.rrateTM = convFunc(data.rrateTM); - data.hourlyrainTH = convFunc(data.hourlyrainTH); - data.rainunit = convFunc === mm2in ? 'in' : 'mm'; - }, - - // - // convWindData() converts all the wind values using the supplied conversion function - // - convWindData = function (from, to) { - var fromFunc1, toFunc1, - fromFunc2, toFunc2, - dummy = function (val) { - return val; - }; - - // convert to km/h & km - switch (from) { - case 'mph': - fromFunc1 = miles2km; - fromFunc2 = miles2km; - break; - case 'kts': - fromFunc1 = nmiles2km; - fromFunc2 = nmiles2km; - break; - case 'm/s': - fromFunc1 = ms2kmh; - fromFunc2 = dummy; - break; - case 'km/h': - // falls through - default: - fromFunc1 = dummy; - fromFunc2 = dummy; - } - // conversion function from km to required units - switch (to) { - case 'mph': - toFunc1 = km2miles; - toFunc2 = km2miles; - displayUnits.windrun = 'miles'; - break; - case 'kts': - toFunc1 = km2nmiles; - toFunc2 = km2nmiles; - displayUnits.windrun = 'n.miles'; - break; - case 'm/s': - toFunc1 = kmh2ms; - toFunc2 = dummy; - displayUnits.windrun = 'km'; - break; - case 'km/h': - // falls through - default: - toFunc1 = dummy; - toFunc2 = dummy; - displayUnits.windrun = 'km'; - } - // do the conversions - data.wgust = toFunc1(fromFunc1(data.wgust)); - data.wgustTM = toFunc1(fromFunc1(data.wgustTM)); - data.windTM = toFunc1(fromFunc1(data.windTM)); - data.windrun = toFunc2(fromFunc2(data.windrun)); - data.wlatest = toFunc1(fromFunc1(data.wlatest)); - data.wspeed = toFunc1(fromFunc1(data.wspeed)); - data.windunit = to; - }, - - // - // convBaroData() converts all the pressure values using the supplied conversion function - // - convBaroData = function (from, to) { - var fromFunc, toFunc, - dummy = function (val) { - return val; - }; - - // convert to hPa - switch (from) { - case 'hPa': - // falls through - case 'mb': - fromFunc = dummy; - break; - case 'inHg': - fromFunc = inhg2hpa; - break; - case 'kPa': - fromFunc = kpa2hpa; - break; - // no default - } - // convert to required units - switch (to) { - case 'hPa': - // falls through - case 'mb': - toFunc = dummy; - break; - case 'inHg': - toFunc = hpa2inhg; - break; - case 'kPa': - toFunc = hpa2kpa; - break; - // no default - } - - data.press = toFunc(fromFunc(data.press)); - data.pressH = toFunc(fromFunc(data.pressH)); - data.pressL = toFunc(fromFunc(data.pressL)); - data.pressTH = toFunc(fromFunc(data.pressTH)); - data.pressTL = toFunc(fromFunc(data.pressTL)); - data.presstrendval = toFunc(fromFunc(data.presstrendval), 3); - data.pressunit = to; - }, - - // - // convCloudBaseData() converts all the cloud base data units using the supplied conversion function - // - convCloudBaseData = function (convFunc) { - data.cloudbasevalue = convFunc(data.cloudbasevalue); - data.cloudbaseunit = convFunc === m2ft ? 'ft' : 'm'; - }, - - // - // setUnits() Main data conversion routine, calls all the setXXXX() sub-routines - // - setUnits = function (radio) { - var sel = radio.value; - - userUnitsSet = true; - - switch (sel) { - // == Temperature == - case 'C': - displayUnits.temp = sel; - if (data.tempunit[1] !== sel) { - setTempUnits(true); - convTempData(f2c); - if (gaugeTemp) { gaugeTemp.update(); } - if (gaugeDew) { gaugeDew.update(); } - } - break; - case 'F': - displayUnits.temp = sel; - if (data.tempunit[1] !== sel) { - setTempUnits(false); - convTempData(c2f); - if (gaugeTemp) { gaugeTemp.update(); } - if (gaugeDew) { gaugeDew.update(); } - } - break; - // == Rainfall == - case 'mm': - displayUnits.rain = sel; - if (data.rainunit !== sel) { - setRainUnits(true); - convRainData(in2mm); - if (gaugeRain) { gaugeRain.update(); } - if (gaugeRRate) { gaugeRRate.update(); } - } - break; - case 'in': - displayUnits.rain = sel; - if (data.rainunit !== sel) { - setRainUnits(false); - convRainData(mm2in); - if (gaugeRain) { gaugeRain.update(); } - if (gaugeRRate) { gaugeRRate.update(); } - } - break; - // == Pressure == - case 'hPa': - // falls through - case 'inHg': - // falls through - case 'mb': - // falls through - case 'kPa': - displayUnits.press = sel; - if (data.pressunit !== sel) { - convBaroData(data.pressunit, sel); - setBaroUnits(sel); - if (gaugeBaro) { gaugeBaro.update(); } - } - break; - // == Wind speed == - case 'mph': - // falls through - case 'kts': - // falls through - case 'm/s': - // falls through - case 'km/h': - displayUnits.wind = sel; - if (data.windunit !== sel) { - convWindData(data.windunit, sel); - setWindUnits(sel); - if (gaugeWind) { gaugeWind.update(); } - if (gaugeDir) { gaugeDir.update(); } - if (gaugeRose) { gaugeRose.update(); } - } - break; - // == CloudBase == - case 'm': - displayUnits.cloud = sel; - if (data.cloudbaseunit !== sel) { - setCloudBaseUnits(true); - convCloudBaseData(ft2m); - if (gaugeCloud) { gaugeCloud.update(); } - } - break; - case 'ft': - displayUnits.cloud = sel; - if (data.cloudbaseunit !== sel) { - setCloudBaseUnits(false); - convCloudBaseData(m2ft); - if (gaugeCloud) { gaugeCloud.update(); } - } - break; - // no default - } - if (config.useCookies) { - setCookie('units', displayUnits); - } - }, - - setTempUnits = function (celsius) { - if (celsius) { - data.tempunit = '°C'; - if (gaugeTemp) { - gaugeTemp.data.sections = createTempSections(true); - gaugeTemp.data.minValue = gaugeGlobals.tempScaleDefMinC; - gaugeTemp.data.maxValue = gaugeGlobals.tempScaleDefMaxC; - } - if (gaugeDew) { - gaugeDew.data.sections = createTempSections(true); - gaugeDew.data.minValue = gaugeGlobals.tempScaleDefMinC; - gaugeDew.data.maxValue = gaugeGlobals.tempScaleDefMaxC; - } - } else { - data.tempunit = '°F'; - if (gaugeTemp) { - gaugeTemp.data.sections = createTempSections(false); - gaugeTemp.data.minValue = gaugeGlobals.tempScaleDefMinF; - gaugeTemp.data.maxValue = gaugeGlobals.tempScaleDefMaxF; - } - if (gaugeDew) { - gaugeDew.data.sections = createTempSections(false); - gaugeDew.data.minValue = gaugeGlobals.tempScaleDefMinF; - gaugeDew.data.maxValue = gaugeGlobals.tempScaleDefMaxF; - } - } - if (gaugeTemp) { - gaugeTemp.gauge.setUnitString(data.tempunit); - gaugeTemp.gauge.setSection(gaugeTemp.data.sections); - } - if (gaugeDew) { - gaugeDew.gauge.setUnitString(data.tempunit); - gaugeDew.gauge.setSection(gaugeTemp.data.sections); - } - }, - - setRainUnits = function (mm) { - if (mm) { - data.rainunit = 'mm'; - if (gaugeRain) { - gaugeRain.data.lcdDecimals = 1; - gaugeRain.data.scaleDecimals = 1; - gaugeRain.data.labelNumberFormat = gaugeGlobals.labelFormat; - gaugeRain.data.sections = (gaugeGlobals.rainUseSectionColours ? createRainfallSections(true) : []); - gaugeRain.data.maxValue = gaugeGlobals.rainScaleDefMaxmm; - gaugeRain.data.grad = (gaugeGlobals.rainUseGradientColours ? createRainfallGradient(true) : null); - } - if (gaugeRRate) { - gaugeRRate.data.lcdDecimals = 1; - gaugeRRate.data.scaleDecimals = 0; - gaugeRRate.data.labelNumberFormat = gaugeGlobals.labelFormat; - gaugeRRate.data.sections = createRainRateSections(true); - gaugeRRate.data.maxValue = gaugeGlobals.rainRateScaleDefMaxmm; - } - } else { - data.rainunit = 'in'; - if (gaugeRain) { - gaugeRain.data.lcdDecimals = 2; - gaugeRain.data.scaleDecimals = gaugeGlobals.rainScaleDefMaxIn < 1 ? 2 : 1; - gaugeRain.data.labelNumberFormat = steelseries.LabelNumberFormat.FRACTIONAL; - gaugeRain.data.sections = (gaugeGlobals.rainUseSectionColours ? createRainfallSections(false) : []); - gaugeRain.data.maxValue = gaugeGlobals.rainScaleDefMaxIn; - gaugeRain.data.grad = (gaugeGlobals.rainUseGradientColours ? createRainfallGradient(false) : null); - } - if (gaugeRRate) { - gaugeRRate.data.lcdDecimals = 2; - gaugeRRate.data.scaleDecimals = gaugeGlobals.rainRateScaleDefMaxIn < 1 ? 2 : 1; - gaugeRRate.data.labelNumberFormat = steelseries.LabelNumberFormat.FRACTIONAL; - gaugeRRate.data.sections = createRainRateSections(false); - gaugeRRate.data.maxValue = gaugeGlobals.rainRateScaleDefMaxIn; - } - } - if (gaugeRain) { - gaugeRain.data.value = 0; - gaugeRain.gauge.setUnitString(data.rainunit); - gaugeRain.gauge.setSection(gaugeRain.data.sections); - gaugeRain.gauge.setGradient(gaugeRain.data.grad); - gaugeRain.gauge.setFractionalScaleDecimals(gaugeRain.data.scaleDecimals); - gaugeRain.gauge.setLabelNumberFormat(gaugeRain.data.labelNumberFormat); - gaugeRain.gauge.setLcdDecimals(gaugeRain.data.lcdDecimals); - gaugeRain.gauge.setValue(0); - gaugeRain.gauge.setMaxValue(gaugeRain.data.maxValue); - } - if (gaugeRRate) { - gaugeRRate.data.value = 0; - gaugeRRate.gauge.setUnitString(data.rainunit + '/h'); - gaugeRRate.gauge.setSection(gaugeRRate.data.sections); - gaugeRRate.gauge.setFractionalScaleDecimals(gaugeRRate.data.scaleDecimals); - gaugeRRate.gauge.setLabelNumberFormat(gaugeRRate.data.labelNumberFormat); - gaugeRRate.gauge.setLcdDecimals(gaugeRRate.data.lcdDecimals); - gaugeRRate.gauge.setValue(0); - gaugeRRate.gauge.setMaxValue(gaugeRRate.data.maxValue); - } - }, - - setWindUnits = function (to) { - var maxVal; - data.windunit = to; - if (gaugeWind) { - // conversion function to required units - switch (to) { - case 'mph': - maxVal = gaugeGlobals.windScaleDefMaxMph; - break; - case 'kts': - maxVal = gaugeGlobals.windScaleDefMaxKts; - break; - case 'km/h': - maxVal = gaugeGlobals.windScaleDefMaxKmh; - break; - case 'm/s': - maxVal = gaugeGlobals.windScaleDefMaxMs; - break; - // no default - } - // set the gauges - gaugeWind.data.maxValue = maxVal; - gaugeWind.gauge.setUnitString(data.windunit); - gaugeWind.gauge.setValue(0); - } - if (gaugeRose) { - gaugeRose.setOdoTitle(strings[getWindrunUnits(data.windunit)]); - } - }, - - setBaroUnits = function (to) { - var minVal, maxVal; - - if (!gaugeBaro) { return; } - - // set to the required units - switch (to) { - case 'hPa': - // falls through - case 'mb': - minVal = gaugeGlobals.baroScaleDefMinhPa; - maxVal = gaugeGlobals.baroScaleDefMaxhPa; - gaugeBaro.data.lcdDecimals = 1; - gaugeBaro.data.scaleDecimals = 0; - gaugeBaro.data.labelNumberFormat = gaugeGlobals.labelFormat; - break; - case 'inHg': - minVal = gaugeGlobals.baroScaleDefMininHg; - maxVal = gaugeGlobals.baroScaleDefMaxinHg; - gaugeBaro.data.lcdDecimals = 2; - gaugeBaro.data.scaleDecimals = 1; - gaugeBaro.data.labelNumberFormat = steelseries.LabelNumberFormat.FRACTIONAL; - break; - case 'kPa': - minVal = gaugeGlobals.baroScaleDefMinkPa; - maxVal = gaugeGlobals.baroScaleDefMaxkPa; - gaugeBaro.data.lcdDecimals = 2; - gaugeBaro.data.scaleDecimals = 1; - gaugeBaro.data.labelNumberFormat = steelseries.LabelNumberFormat.FRACTIONAL; - break; - // no default - } - - data.pressunit = to; - gaugeBaro.gauge.setUnitString(to); - gaugeBaro.gauge.setLcdDecimals(gaugeBaro.data.lcdDecimals); - gaugeBaro.gauge.setFractionalScaleDecimals(gaugeBaro.data.scaleDecimals); - gaugeBaro.gauge.setLabelNumberFormat(gaugeBaro.data.labelNumberFormat); - gaugeBaro.data.minValue = minVal; - gaugeBaro.data.maxValue = maxVal; - gaugeBaro.data.value = gaugeBaro.data.minValue; - }, - - setCloudBaseUnits = function (m) { - if (!gaugeCloud) { return; } - - if (m) { - gaugeCloud.data.sections = createCloudBaseSections(true); - gaugeCloud.data.maxValue = gaugeGlobals.cloudScaleDefMaxm; - } else { - gaugeCloud.data.sections = createCloudBaseSections(false); - gaugeCloud.data.maxValue = gaugeGlobals.cloudScaleDefMaxft; - } - gaugeCloud.data.value = 0; - gaugeCloud.gauge.setUnitString(m ? strings.metres : strings.feet); - gaugeCloud.gauge.setSection(gaugeCloud.data.sections); - }, - - // - // setLang() switches the HTML page language set, called by changeLang() in language.js - // - setLang = function (newLang) { - // reset to the new language - strings = newLang; - - // temperature - if (gaugeTemp) { - if (config.showIndoorTempHum) { - if ($('#rad_temp1').is(':checked')) { - gaugeTemp.data.title = strings.temp_title_out; - } else { - gaugeTemp.data.title = strings.temp_title_in; - } - } else { - gaugeTemp.data.title = strings.temp_title_out; - } - gaugeTemp.gauge.setTitleString(gaugeTemp.data.title); - if (data.ver) { gaugeTemp.update(); } - } - if (gaugeDew) { - switch ($('input[name="rad_dew"]:checked').val()) { - case 'dew': - gaugeDew.data.title = strings.dew_title; - break; - case 'app': - gaugeDew.data.title = strings.apptemp_title; - break; - case 'feel': - gaugeDew.data.title = strings.feel_title; - break; - case 'wnd': - gaugeDew.data.title = strings.chill_title; - break; - case 'hea': - gaugeDew.data.title = strings.heat_title; - break; - case 'hum': - gaugeDew.data.title = strings.humdx_title; - break; - // no default - } - gaugeDew.gauge.setTitleString(gaugeDew.data.title); - if (data.ver) { gaugeDew.update(); } - } - // rain - if (gaugeRain) { - gaugeRain.data.title = strings.rain_title; - gaugeRain.gauge.setTitleString(gaugeRain.data.title); - if (data.ver) { gaugeRain.update(); } - } - // rrate - if (gaugeRRate) { - gaugeRRate.data.title = strings.rrate_title; - gaugeRRate.gauge.setTitleString(gaugeRRate.data.title); - if (data.ver) { gaugeRRate.update(); } - } - // humidity - if (gaugeHum) { - if (config.showIndoorTempHum) { - if ($('#rad_hum1').is(':checked')) { - gaugeHum.data.title = strings.hum_title_out; - } else { - gaugeHum.data.title = strings.hum_title_in; - } - } else { - gaugeHum.data.title = strings.hum_title_out; - } - gaugeHum.gauge.setTitleString(gaugeHum.data.title); - if (data.ver) { gaugeHum.update(); } - } - // barometer - if (gaugeBaro) { - gaugeBaro.data.title = strings.baro_title; - gaugeBaro.gauge.setTitleString(gaugeBaro.data.title); - if (data.ver) { gaugeBaro.update(); } - } - // wind - if (gaugeWind) { - gaugeWind.data.title = strings.wind_title; - gaugeWind.gauge.setTitleString(gaugeWind.data.title); - if (data.ver) { gaugeWind.update(); } - } - if (gaugeDir) { - gaugeDir.gauge.setPointSymbols(strings.compass); - gaugeDir.data.titles = [strings.latest_web, strings.tenminavg_web]; - gaugeDir.gauge.setLcdTitleStrings(gaugeDir.data.titles); - if (data.ver) { gaugeDir.update(); } - } - if (gaugeUV) { - gaugeUV.gauge.setTitleString(strings.uv_title); - if (data.ver) { gaugeUV.update(); } - } - if (gaugeSolar) { - gaugeSolar.gauge.setTitleString(strings.solar_title); - if (data.ver) { gaugeSolar.update(); } - } - if (gaugeRose) { - gaugeRose.setTitle(strings.windrose); - gaugeRose.setCompassStrings(strings.compass); - gaugeRose.setOdoTitle(strings[getWindrunUnits(displayUnits.wind)]); - if (data.ver) { gaugeRose.update(); } - } - if (gaugeCloud) { - // Cloudbase - gaugeCloud.data.units = data.cloudunit === 'm' ? strings.metres : strings.feet; - gaugeCloud.gauge.setTitleString(strings.cloudbase_title); - gaugeCloud.gauge.setUnitString(gaugeCloud.data.units); - if (data.ver) { gaugeCloud.update(); } - } - }, - - // - // return windrun units based on the windspeed units - // - getWindrunUnits = function (spdUnits) { - var retVal; - switch (spdUnits) { - case 'mph': - retVal = 'miles'; - break; - case 'kts': - retVal = 'n_miles'; - break; - case 'km/h': - // falls through - case 'm/s': - // falls through - default: - retVal = 'km'; - break; - } - return retVal; - }, - - // - // performs a simple cloudbase calculation for those weather programs that don't supply it - // - calcCloudbase = function (temp, tempunit, dew, cloudbaseunit) { - var sprd = temp - dew; - var cb = sprd * (tempunit[1] === 'C' ? 400 : 227.3); // cloud base in feet - if (cloudbaseunit === 'm') { - cb = ft2m(cb); - } - return cb; - }, - - // - // create a shadow effect for the gauge using CSS - // - gaugeShadow = function (size) { - var offset = Math.floor(size * 0.015); - return { - 'box-shadow': offset + 'px ' + offset + 'px ' + offset + 'px ' + gaugeGlobals.shadowColour, - 'border-radius': Math.floor(size / 2) + 'px' - }; - }, - - // - // generate a colour gradient based on start and end values - // - gradient = function (startCol, endCol, fraction) { - var redOrigin, grnOrigin, bluOrigin, - gradientSizeRed, gradientSizeGrn, gradientSizeBlu; - - redOrigin = parseInt(startCol.substr(0, 2), 16); - grnOrigin = parseInt(startCol.substr(2, 2), 16); - bluOrigin = parseInt(startCol.substr(4, 2), 16); - - gradientSizeRed = parseInt(endCol.substr(0, 2), 16) - redOrigin; // Graduation Size Red - gradientSizeGrn = parseInt(endCol.substr(2, 2), 16) - grnOrigin; - gradientSizeBlu = parseInt(endCol.substr(4, 2), 16) - bluOrigin; - - return (redOrigin + (gradientSizeRed * fraction)).toFixed(0) + ',' + - (grnOrigin + (gradientSizeGrn * fraction)).toFixed(0) + ',' + - (bluOrigin + (gradientSizeBlu * fraction)).toFixed(0); - }, - // - // returns the next highest number in the step sequence - // - nextHighest = function (value, step) { - return +value == 0 ? step : Math.ceil(+value / step) * step; - }, - // - // returns the next lowest number in the step sequence - // - nextLowest = function (value, step) { - return +value == 0 ? -step : Math.floor(+value / step) * step; - }; -// ######################################################## -// End of gauges() var declarations -// ######################################################## - -// -// Execution starts here -// - -// test for canvas support before we do anything else, especially reference steelseries which will cause the script to abort! -if (!document.createElement('canvas').getContext) { - // failed, no canvas support detected - $('body').html(strings.canvasnosupport); - setTimeout(function () { - window.location = config.oldGauges; - }, 3000); - return false; -} else { - // - // Called when the document object has loaded - // This starts the whole script. - // - $(document).ready(function () { - // Kick it all off - false for web page, true for dashboard - init(config.dashboardMode); - }); -} - -return { - setLang: setLang, - setUnits: setUnits, - processData: processData, - config: config, - init: init -}; -}()); - -// =============================================================================================================================== -// =============================================================================================================================== -// =============================================================================================================================== - -/*! -* Image w/ description tooltip v2.0 - For FF1+ IE6+ Opr8+ -* Created: April 23rd, 2010. This notice must stay intact for usage -* Author: Dynamic Drive at http://www.dynamicdrive.com/ -* Visit http://www.dynamicdrive.com/ for full source code -* Modified: M Crossley June 2011, January 2012 -* v2.- -*/ -var ddimgtooltip; -ddimgtooltip = { - tiparray: (function () { - var style = { background: '#FFFFFF', color: 'black', border: '2px ridge darkblue' }, - i = 12, // set to number of tooltips required - tooltips = []; - for (; i--;) { - tooltips[i] = [null, ' ', style]; - } - return tooltips; - }()), - - tooltipoffsets: [20, -30], // additional x and y offset from mouse cursor for tooltips - - tipDelay: 1000, - - delayTimer: 0, - - tipprefix: 'imgtip', // tooltip DOM ID prefixes - - createtip: function ($, tipid, tipinfo) { - if ($('#' + tipid).length === 0) { // if this tooltip doesn't exist yet - return $('
') - .html( - ((tipinfo[1]) ? '
' + tipinfo[1] + '
' : '') + - (tipinfo[0] !== null ? '
' : '') - ) - .css(tipinfo[2] || {}) - .appendTo(document.body); - } - return null; - }, - - positiontooltip: function ($, $tooltip, e) { - var x = e.pageX + this.tooltipoffsets[0], - y = e.pageY + this.tooltipoffsets[1], - tipw = $tooltip.outerWidth(), - tiph = $tooltip.outerHeight(), - wWidth = $(window).width(), - wHght = $(window).height(), - dTop = $(document).scrollTop(); - - x = (x + tipw > $(document).scrollLeft() + wWidth) ? x - tipw - (ddimgtooltip.tooltipoffsets[0] * 2) : x; - y = (y + tiph > dTop + wHght) ? dTop + wHght - tiph - 10 : y; - // last ditch attempt to keep the graphs 'on page' - x = Math.max(x, 0); - if (tipw >= wWidth) { - $($tooltip.attr('id') + '_img').css({ width: wWidth - 20 }); - $('#' + $tooltip.attr('id') + '_img').css({ width: wWidth - 20 }); - y = e.pageY + 5; - } - $tooltip.css({ left: x, top: y }); - }, - - delaybox: function ($, $tooltip) { - if (this.showTips) { - ddimgtooltip.delayTimer = setTimeout( - function () { - ddimgtooltip.showbox($tooltip); - }, ddimgtooltip.tipDelay); - } - }, - - showbox: function (tooltip) { - if (this.showTips) { - // $(tooltip).show(); - $(tooltip).fadeIn(); - } - }, - - hidebox: function ($, $tooltip) { - clearTimeout(ddimgtooltip.delayTimer); - // $tooltip.hide(); - $tooltip.fadeOut(); - }, - - showTips: false, - - init: function (targetselector) { - var tiparray = ddimgtooltip.tiparray, - $targets = $(targetselector); - - if ($targets.length === 0) { - return; - } - $targets.each(function () { - var $target = $(this), - tipsuffix, tipid, - $tooltip; - var ind = ($target.attr('id').match(/_(\d+)/) || [])[1] || ''; // match d of attribute id='tip_d' - tipsuffix = parseInt(ind, 10); // get d as integer - tipid = this.tipid = ddimgtooltip.tipprefix + tipsuffix; // construct this tip's ID value and remember it - $tooltip = ddimgtooltip.createtip($, tipid, tiparray[tipsuffix]); - - $target.mouseenter(function (e) { - var $tooltip = $('#' + this.tipid); - // ddimgtooltip.showbox($, $tooltip, e); - ddimgtooltip.delaybox($, $tooltip, e); - }); - $target.mouseleave(function () { - var $tooltip = $('#' + this.tipid); - ddimgtooltip.hidebox($, $tooltip); - }); - $target.mousemove(function (e) { - var $tooltip = $('#' + this.tipid); - ddimgtooltip.positiontooltip($, $tooltip, e); - }); - if ($tooltip) { // add mouseenter to this tooltip (only if event hasn't already been added) - $tooltip.mouseenter(function () { - ddimgtooltip.hidebox($, $(this)); - }); - } - }); - } -}; - -// if String doesn't offer a .trim() method, add it -String.prototype.trim = String.prototype.trim || function trim() { - return this.replace(/^\s+|\s+$/g, ''); -}; +/*! + * THIS VERSION CUSTOMISED FROM CUMULUS MX DEFAULT WEB SITE + * Last Modified: 2023/09/02 17:15:14 + * + * A starter gauges page for Cumulus and Weather Display, based + * on the JavaScript SteelSeries gauges by Gerrit Grunwald. + * + * Created by Mark Crossley, July 2011 + * see scriptVer below for latest release + * + * Released under GNU GENERAL PUBLIC LICENSE, Version 2, June 1991 + * See the enclosed License file + * + * File encoding = UTF-8 + * + */ + +/* exported gauges */ + +/*! +* Tiny Pub/Sub - v0.7.0 - 2013-01-29 +* https://github.com/cowboy/jquery-tiny-pubsub +* Copyright (c) 2013 "Cowboy" Ben Alman; Licensed MIT +*/ +(function ($) { + 'use strict'; + var o = $({}); + $.subscribe = function () { o.on.apply(o, arguments); }; + $.unsubscribe = function () { o.off.apply(o, arguments); }; + $.publish = function () { o.trigger.apply(o, arguments); }; +}(jQuery)); + +var gauges; +gauges = (function () { + 'use strict'; + var strings = LANG.EN, // Set to your default language. Store all the strings in one object + config = { + // Script configuration parameters you may want to 'tweak' + scriptVer: '2.7.7', + weatherProgram: 0, // Set 0=Cumulus, 1=Weather Display, 2=VWS, 3=WeatherCat, 4=Meteobridge, 5=WView, 6=WeeWX, 7=WLCOM + imgPathURL: './/images/', // *** Change this to the relative path for your 'Trend' graph /images + oldGauges: 'gauges.htm', // *** Change this to the relative path for your 'old' gauges page. + realtimeInterval: 15, // *** Download data interval, set to your realtime data update interval in seconds + longPoll: false, // if enabled, use long polling and PHP generated data !!only enable if you understand how this is implemented!! + gaugeMobileScaling: 0.85, // scaling factor to apply when displaying the gauges mobile devices, set to 1 to disable (default 0.85) + graphUpdateTime: 15, // period of pop-up data graph refresh, in minutes (default 15) + stationTimeout: 3, // period of no data change before we declare the station off-line, in minutes (default 3) + pageUpdateLimit: 20, // period after which the page stops automatically updating, in minutes (default 20), + // - set to 0 (zero) to disable this feature + pageUpdatePswd: 'its-me', // password to over ride the page updates time-out, do not set to blank even if you do not use a password - http://&pageUpdate=its-me + digitalFont: false, // Font control for the gauges & timer + digitalForecast: false, // Font control for the status display, set this to false for languages that use accented characters in the forecasts + showPopupData: true, // Pop-up data displayed + showPopupGraphs: false, // If pop-up data is displayed, show the graphs? + mobileShowGraphs: false, // If false, on a mobile/narrow display, always disable the graphs + showWindVariation: true, // Show variation in wind direction over the last 10 minutes on the direction gauge + showWindMetar: false, // Show the METAR substring for wind speed/direction over the last 10 minutes on the direction gauge popup + showIndoorTempHum: true, // Show the indoor temperature/humidity options + showCloudGauge: true, // Display the Cloud Base gauge + showUvGauge: true, // Display the UV Index gauge + showSolarGauge: true, // Display the Solar gauge + showSunshineLed: true, // Show 'sun shining now' LED on solar gauge + showRoseGauge: true, // Show the optional Wind Rose gauge + showRoseGaugeOdo: true, // Show the optional Wind Rose gauge wind run Odometer + showRoseOnDirGauge: true, // Show the rose data as sectors on the direction gauge + showGaugeShadow: true, // Show a drop shadow outside the gauges + roundCloudbaseVal: true, // Round the value shown on the cloud base gauge to make it easier to read + // The realtime files should be absolute paths, "/xxx.txt" refers to the public root of your web server + realTimeUrlLongPoll: 'realtimegauges-longpoll.php', // *** ALL Users: If using long polling, change to your location of the PHP long poll realtime file *** + // *** the supplied file is for Cumulus only + realTimeUrlCumulus: '/data/realtimegauges.txt', // *** Cumulus Users: Change to your location of the realtime file *** + realTimeUrlWD: 'customclientraw.txt', // *** WD Users: Change to your location of the ccr file *** + realTimeUrlVWS: 'steelseriesVWSjson.php', // *** VWS Users: Change to your location of the JSON script generator *** + realTimeUrlWC: 'realtimegaugesWC.txt', // *** WeatherCat Users: Change to your location of the JSON script generator *** + realTimeUrlMB: 'MBrealtimegauges.txt', // *** Meteobridge Users: Change to the location of the JSON file + realTimeUrlWView: 'customclientraw.txt', // *** WView Users: Change to your location of the customclientraw.txt file *** + realTimeUrlWeewx: 'gauge-data.txt', // *** WeeWX Users: Change to your location of the gauge data file *** + realTimeUrlWLCOM: 'WLrealtimegauges.php', // *** WLCOM Users: change to location of WLCOMtags.php file *** + useCookies: true, // Persistently store user preferences in a cookie? + tipImages: [], + dashboardMode: false, // Used by Cumulus MX dashboard - SET TO FALSE OTHERWISE + dewDisplayType: 'dew' // Initial 'scale' to display on the 'dew point' gauge. + // 'dew' - Dewpoint + // 'app' - Apparent temperature + // 'wnd' - Wind Chill + // 'feel' - Feels Like + // 'hea' - Heat Index + // 'hum' - Humidex + }, + + // Gauge global look'n'feel settings + gaugeGlobals = { + minMaxArea: 'rgba(212,132,134,0.3)', // area sector for today's max/min. (red, green, blue, transparency) + windAvgArea: 'rgba(132,212,134,0.3)', + windVariationSector: 'rgba(120,200,120,0.7)', // only used when rose data is shown on direction gauge + frameDesign: steelseries.FrameDesign.TILTED_GRAY, + background: steelseries.BackgroundColor.BEIGE, + foreground: steelseries.ForegroundType.TYPE1, + pointer: steelseries.PointerType.TYPE8, + pointerColour: steelseries.ColorDef.RED, + dirAvgPointer: steelseries.PointerType.TYPE8, + dirAvgPointerColour: steelseries.ColorDef.BLUE, + gaugeType: steelseries.GaugeType.TYPE4, + lcdColour: steelseries.LcdColor.STANDARD, + knob: steelseries.KnobType.STANDARD_KNOB, + knobStyle: steelseries.KnobStyle.SILVER, + labelFormat: steelseries.LabelNumberFormat.STANDARD, + tickLabelOrientation: steelseries.TickLabelOrientation.HORIZONTAL, // was .NORMAL up to v1.6.4 + rainUseSectionColours: false, // Only one of these colour options should be true + rainUseGradientColours: false, // Set both to false to use the pointer colour + tempTrendVisible: true, + pressureTrendVisible: true, + uvLcdDecimals: 1, + // sunshine threshold values + sunshineThreshold: 50, // the value in W/m² above which we can consider the Sun to be shining, *if* the current value exceeds... + sunshineThresholdPct: 75, // the percentage of theoretical solar irradiance above which we consider the Sun to be shining + // default gauge ranges - before auto-scaling/ranging + tempScaleDefMinC: -20, + tempScaleDefMaxC: 40, + tempScaleDefMinF: 0, + tempScaleDefMaxF: 100, + baroScaleDefMinhPa: 990, + baroScaleDefMaxhPa: 1030, + baroScaleDefMinkPa: 99, + baroScaleDefMaxkPa: 103, + baroScaleDefMininHg: 29.2, + baroScaleDefMaxinHg: 30.4, + windScaleDefMaxMph: 20, + windScaleDefMaxKts: 20, + windScaleDefMaxMs: 10, + windScaleDefMaxKmh: 30, + rainScaleDefMaxmm: 10, + rainScaleDefMaxIn: 0.5, + rainRateScaleDefMaxmm: 10, + rainRateScaleDefMaxIn: 0.5, + uvScaleDefMax: 10, // Northern Europe may be lower - max. value recorded in the UK is 8, so use a scale of 10 for UK + solarGaugeScaleMax: 1000, // Max value to be shown on the solar gauge - theoretical max without atmosphere ~ 1374 W/m² + // - but Davis stations can read up to 1800, use 1000 for Northern Europe? + cloudScaleDefMaxft: 3000, + cloudScaleDefMaxm: 1000, + shadowColour: 'rgba(0,0,0,0.3)' // Colour to use for gauge shadows - default 30% transparent black + }, + + commonParams = { + // Common parameters for all the SteelSeries gauges + fullScaleDeflectionTime: 4, // Bigger numbers (seconds) slow the gauge pointer movements more + gaugeType: gaugeGlobals.gaugeType, + minValue: 0, + niceScale: true, + ledVisible: false, + frameDesign: gaugeGlobals.frameDesign, + backgroundColor: gaugeGlobals.background, + foregroundType: gaugeGlobals.foreground, + pointerType: gaugeGlobals.pointer, + pointerColor: gaugeGlobals.pointerColour, + knobType: gaugeGlobals.knob, + knobStyle: gaugeGlobals.knobStyle, + lcdColor: gaugeGlobals.lcdColour, + lcdDecimals: 1, + digitalFont: config.digitalFont, + tickLabelOrientation: gaugeGlobals.tickLabelOrientation, + labelNumberFormat: gaugeGlobals.labelFormat + }, + firstRun = true, // Used to set-up units & scales etc + userUnitsSet = false, // Tracks if the display units have been set by a user preference + data = {}, // Stores all the values from realtime.txt + tickTockInterval, // The 1s clock interval timer + + // ajaxDelay, used by long polling, the delay between getting a response and queueing the next request, as the default PHP + // script runtime timout is 30 seconds, we do not want the PHP task to run for more than 20 seconds. So queue the next + // request half of the realtime interval, or 20 seconds before it is due, which ever is the larger. + ajaxDelay = config.longPoll ? Math.max(config.realtimeInterval - 20, 0) : config.realtimeInterval, + downloadTimer, // Stores a reference to the ajax download setTimout() timer + timestamp = 0, // the timestamp of last data update on the server. + jqXHR = null, // handle to the jQuery web request + displayUnits = null, // Stores the display units cookie settings + sampleDate, + realtimeVer, // minimum version of the realtime JSON file required + programLink = [ + 'Cumulus', + 'Weather Display', + 'Virtual Weather Station', + 'WeatherCat', + 'Meteobridge', + 'Wview', + 'weewx', + 'Weatherlink.com' + ], + + ledIndicator, statusScroller, statusTimer, + + gaugeTemp, gaugeDew, gaugeRain, gaugeRRate, + gaugeHum, gaugeBaro, gaugeWind, gaugeDir, + gaugeUV, gaugeSolar, gaugeCloud, gaugeRose, + + /* _imgBackground, // Uncomment if using a background image on the gauges */ + + // ================================================================================================================== + // Nothing below this line needs to be modified for the gauges as supplied + // - unless you really know what you are doing + // - but remember, if you break it, it's up to you to fix it ;-) + // ================================================================================================================== + + // + // init() Called when the document is ready, pre-draws the Status Display then calls + // the first Ajax fetch of /data/realtimegauges.txt. First draw of the gauges now deferred until + // the Ajax data is available as a 'speed up'. + // + init = function (dashboard) { + // Cumulus, Weather Display, VWS, WeatherCat? + switch (config.weatherProgram) { + case 0: + // Cumulus + realtimeVer = 12; // minimum version of the realtime JSON file required + config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlCumulus; + // the trend /images to be used for the pop-up data, used in conjunction with config.imgPathURL + // by default this is configured for the Cumulus 'standard' web site + // ** If you specify one image in a sub-array, then you MUST provide /images for all the other sub-elements + config.tipImgs = [ // config.tipImgs for Cumulus users using the 'default' weather site + ['temp.png', 'intemp.png'], // Temperature: outdoor, indoor + // Temperature: dewpoint, apparent, windChill, heatIndex, humidex + ['temp.png', 'temp.png', 'temp.png', 'temp.png', 'temp.png'], + 'raint.png', // Rainfall + 'rain.png', // Rainfall rate + ['hum.png', 'hum.png'], // Humidity: outdoor, indoor + 'press.png', // Pressure + 'wind.png', // Wind speed + 'windd.png', // Wind direction + (config.showUvGauge ? 'uv.png' : null), // UV graph if UV sensor is present | =null if no UV sensor + (config.showSolarGauge ? 'solar.png' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor + (config.showRoseGauge ? 'windd.png' : null), // Wind direction if Rose is enabled | =null if Rose is disabled + (config.showCloudGauge ? 'press.png' : null) // Pressure for cloud height | =null if Cloud Height is disabled + ]; + break; + case 1: + // Weather Display + realtimeVer = 14; // minimum version of the realtime JSON file required + config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlWD; + config.tipImgs = [ // config.tipImgs for Weather Display users with wxgraph + ['temp+hum_24hr.php', 'indoor_temp_24hr.php'], // Temperature: outdoor, indoor + // Temperature: dewpnt, apparent, windChill, HeatIndx, humidex + ['temp+dew+hum_1hr.php', 'temp+dew+hum_1hr.php', 'temp+dew+hum_1hr.php', 'temp+dew+hum_1hr.php', 'temp+dew+hum_1hr.php'], + 'rain_24hr.php', // Rainfall + 'rain_1hr.php', // Rainfall rate + ['humidity_1hr.php', 'humidity_7days.php'], // Humidity: outdoor, indoor + 'baro_24hr.php', // Pressure + 'windgust_1hr.php', // Wind speed + 'winddir_24hr.php', // Wind direction + (config.showUvGauge ? 'uv_24hr.php' : null), // UV graph if UV sensor is present | =null if no UV sensor + (config.showSolarGauge ? 'solar_24hr.php' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor + (config.showRoseGauge ? 'winddir_24hr.php' : null), // Wind direction if Rose is enabled | =null if Rose is disabled + (config.showCloudGauge ? 'baro_24hr.php' : null) // Pressure for cloud height | =null if Cloud Height is disabled + ]; + // WD useer generally use wxgraphs - tweak the CSS to accomadate the different aspect ratio + $('.tipimg').css({ + width: '360px', + height: '260px' + }); + break; + case 2: + // WVS + realtimeVer = 11; // minimum version of the realtime JSON file required + config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlVWS; + config.showRoseGauge = false; // no wind rose data from VWS + config.showCloudGauge = false; + config.tipImgs = [ // config.tipImgs for VWS users + ['vws742.jpg', 'vws741.jpg'], // Temperature: outdoor, indoor + // Temperature: dewpnt, apparent, windChill, HeatIndx, humidex + ['vws757.jpg', 'vws762.jpg', 'vws754.jpg', 'vws756.jpg', null], + 'vws744.jpg', // Rainfall + 'vws859.jpg', // Rainfall rate + ['vws740.jpg', 'vws739.jpg'], // Humidity: outdoor, indoor + 'vws758.jpg', // Pressure + 'vws737.jpg', // Wind speed + 'vws736.jpg', // Wind direction + (config.showUvGauge ? 'vws752.jpg' : null), // UV graph if UV sensor is present | =null if no UV sensor + (config.showSolarGauge ? 'vws753.jpg' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor + (config.showRoseGauge ? 'vws736.jpg' : null), // Wind direction if Rose is enabled | =null if Rose is disabled + (config.showCloudGauge ? 'vws758.jpg' : null) // Pressure for cloud height | =null if Cloud Height is disabled + ]; + break; + case 3: + // WeatherCat + realtimeVer = 14; // minimum version of the realtime JSON file required + config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlWC; + config.tipImgs = [ // config.tipImgs - WeatherCat users using the 'default' weather site + ['temperature1.jpg', 'tempin1.jpg'], // Temperature: outdoor, indoor + // Temperature: dewpoint, apparent, windChill, heatIndex, humidex + ['dewpoint1.jpg', 'temperature1.jpg', 'windchill1.jpg', 'heatindex1.jpg', 'temperature1.jpg'], + 'precipitationc1.jpg', // Rainfall + 'precipitation1.jpg', // Rainfall rate + ['rh1.jpg', 'rhin1.jpg'], // Humidity: outdoor, indoor + 'pressure1.jpg', // Pressure + 'windspeed1.jpg', // Wind speed + 'winddirection1.jpg', // Wind direction + (config.showUvGauge ? 'uv1.jpg' : null), // UV graph if UV sensor is present | =null if no UV sensor + (config.showSolarGauge ? 'solarrad1.jpg' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor + (config.showRoseGauge ? 'winddirection1.jpg' : null), // Wind direction if Rose is enabled | =null if Rose is disabled + (config.showCloudGauge ? 'cloudbase1.jpg' : null) // Pressure for cloud height | =null if Cloud Height is disabled + ]; + break; + case 4: + // Meteobridge + realtimeVer = 10; // minimum version of the realtime JSON file required + config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlMB; + config.showPopupGraphs = false; // config.tipImgs - no Meteobridge /images available + config.showRoseGauge = false; // no windrose data from MB + config.showCloudGauge = false; + config.tipImgs = null; // config.tipImgs - no Meteobridge /images available + config.showWindVariation = false; // no wind variation data from MB + break; + case 5: + // WView + realtimeVer = 11; // minimum version of the realtime JSON file required + config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlWView; + config.showSunshineLed = false; // WView does not provide the current theoretical solar max required to determine sunshine + config.showWindVariation = false; // no wind variation from WView + config.showRoseGauge = false; // No wind rose array from WView by default - there is a user supplied modification to enable it though. + config.showCloudGauge = false; + config.tipImgs = [ // config.tipImgs for WView users + ['tempdaycomp.png', 'indoor_temp.png'], // Temperature: outdoor, indoor + // Temperature: dewpnt, apparent, windChill, HeatIndx, humidex + ['tempdaycomp.png', 'tempdaycomp.png', 'heatchillcomp.png', 'heatchillcomp.png', 'tempdaycomp.png'], + 'rainday.png', // Rainfall + 'rainrate.png', // Rainfall rate + ['humidday.png', 'humidday.png'], // Humidity: outdoor, indoor + 'baromday.png', // Pressure + 'wspeeddaycomp.png', // Wind speed + 'wdirday.png', // Wind direction + (config.showUvGauge ? 'uvday.png' : null), // UV graph if UV sensor is present | =null if no UV sensor + (config.showSolarGauge ? 'solarday.png' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor + (config.showRoseGauge ? 'windroseday.png' : null), // Wind direction if Rose is enabled | =null if Rose is disabled + (config.showCloudGauge ? 'baromday.png' : null) // Pressure for cloud height | =null if Cloud Height is disabled + ]; + break; + case 6: + // weewx + realtimeVer = 14; // minimum version of the realtime JSON file required + config.realTimeURL = config.longPoll ? config.realTimeUrlLongPoll : config.realTimeUrlWeewx; + config.showSunshineLed = true; + config.showWindVariation = true; + config.tipImgs = [ // config.tipImgs for weewx + ['dayinouttemp.png', 'dayinouttemp.png'], // Temperature: outdoor, indoor + // Temperature: dewpnt, apparent, windChill, HeatIndx, humidex + ['dayouttemphum.png', 'dayouttemphum.png', 'dayouttemphum.png', 'dayouttemphum.png', 'dayouttemphum.png'], + 'dayrain.png', // Rainfall + 'dayrainrate.png', // Rainfall rate + ['dayinouthum.png', 'dayinouthum.png'], // Humidity: outdoor, indoor + 'daybarometer.png', // Pressure + 'daywind.png', // Wind speed + 'daywinddir.png', // Wind direction + (config.showUvGauge ? 'dayuv.png' : null), // UV graph if UV sensor is present | =null if no UV sensor + (config.showSolarGauge ? 'dayradiation.png' : null), // Solar rad graph if Solar sensor is present | Solar =null if no Solar sensor + (config.showRoseGauge ? 'daywindvec.png' : null), // Wind direction if Rose is enabled | =null if Rose is disabled + (config.showCloudGauge ? 'daybarometer.png' : null) // Pressure for cloud height | =null if Cloud Height is disabled + ]; + break; + case 7: + // WLCOM + realtimeVer = 10; // minimum version of the realtime JSON file required + config.realTimeURL = config.realTimeUrlWLCOM; + config.showPopupGraphs = false; // config.tipImgs - no WL /images available + config.showRoseGauge = false; // no windrose data from WL + config.showCloudGauge = false; + config.tipImgs = null; // config.tipImgs - no WL /images available + config.showWindVariation = false; // no wind variation data from WL + break; + default: + // Who knows! + realtimeVer = 0; // minimum version of the realtime JSON file required + config.realtimeURL = null; + config.showPopupGraphs = false; + config.tipImgs = null; // config.tipImgs - unknown + } + + // Are we running on a phone device (or really low res screen)? + if ($(window).width() < 480) { + // Change the gauge scaling + config.gaugeScaling = config.gaugeMobileScaling; + // Switch off graphs? + config.showPopupGraphs = config.mobileShowGraphs; + } else { + config.gaugeScaling = 1; + } + + // Logo /images are used to 'personalise' the gauge backgrounds + // To add a logo to a gauge, add the parameter: + // params.customLayer = _imgBackground; + // to the corresponding drawXxxx() function below. + // + // These are for demo only, to add them remove the comments around the following lines, and + // the _imgBackground definition line above... + /* + _imgBackground = document.createElement('img'); // small logo + $(_imgBackground).attr('src', config.imgPathURL + 'logoSmall.png'); + */ + // End of logo /images + + // Get the display units the user last used when they visited before - if present + displayUnits = getCookie('units'); + // Set 'units' radio buttons to match preferred units + if (displayUnits !== null) { + // User wants specific units + userUnitsSet = true; + + // temperature + setRadioCheck('rad_unitsTemp', displayUnits.temp); + data.tempunit = '°' + displayUnits.temp; + // rain + setRadioCheck('rad_unitsRain', displayUnits.rain); + data.rainunit = displayUnits.rain; + // pressure + setRadioCheck('rad_unitsPress', displayUnits.press); + data.pressunit = displayUnits.press; + // wind + setRadioCheck('rad_unitsWind', displayUnits.wind); + data.windunit = displayUnits.wind; + displayUnits.windrun = getWindrunUnits(data.windunit); + // cloud base + setRadioCheck('rad_unitsCloud', displayUnits.cloud); + data.cloudunit = displayUnits.cloud; + } else { + // Set the defaults to metric ) + // DO NOT CHANGE THESE - THE SCRIPT DEPENDS ON THESE DEFAULTS + // The units actually displayed will be read from the realtime.txt file, or from the users last visit cookie + displayUnits = { + temp: 'C', + rain: 'mm', + press: 'hPa', + wind: 'km/h', + windrun: 'km', + cloud: 'm' + }; + + data.tempunit = '°C'; + data.rainunit = 'mm'; + data.pressunit = 'hPa'; + data.windunit = 'km/h'; + data.cloudunit = 'm'; + } + + // enable popup data + if (config.showPopupData) { + ddimgtooltip.showTips = config.showPopupData; + } + + if (config.showPopupGraphs) { + // Note the number of array elements must match 'i' in ddimgtooltip.tiparray() + ddimgtooltip.tiparray[0][0] = (config.tipImgs[0][0] === null ? null : ''); + ddimgtooltip.tiparray[1][0] = (config.tipImgs[1][0] === null ? null : ''); + ddimgtooltip.tiparray[2][0] = (config.tipImgs[2] === null ? null : ''); + ddimgtooltip.tiparray[3][0] = (config.tipImgs[3] === null ? null : ''); + ddimgtooltip.tiparray[4][0] = (config.tipImgs[4][0] === null ? null : ''); + ddimgtooltip.tiparray[5][0] = (config.tipImgs[5] === null ? null : ''); + ddimgtooltip.tiparray[6][0] = (config.tipImgs[6] === null ? null : ''); + ddimgtooltip.tiparray[7][0] = (config.tipImgs[7] === null ? null : ''); + ddimgtooltip.tiparray[8][0] = (config.tipImgs[8] === null ? null : ''); + ddimgtooltip.tiparray[9][0] = (config.tipImgs[9] === null ? null : ''); + ddimgtooltip.tiparray[10][0] = (config.tipImgs[10] === null ? null : ''); + ddimgtooltip.tiparray[11][0] = (config.tipImgs[11] === null ? null : ''); + } + + // draw the status gadgets first, they will display any errors in the initial set-up + ledIndicator = singleLed.getInstance(); + statusScroller = singleStatus.getInstance(); + statusTimer = singleTimer.getInstance(); + + gaugeTemp = singleTemp.getInstance(); + // Export gaugeTemp.update() so it can be called from the HTML code + if (gaugeTemp) { gauges.doTemp = gaugeTemp.update; } + + gaugeDew = singleDew.getInstance(); + // Export gaugeDew.update() so it can be called from the HTML code + if (gaugeDew) { gauges.doDew = gaugeDew.update; } + + gaugeHum = singleHum.getInstance(); + // Export gaugeHum.update() so it can be called from the HTML code + if (gaugeHum) { gauges.doHum = gaugeHum.update; } + + gaugeBaro = singleBaro.getInstance(); + + gaugeWind = singleWind.getInstance(); + gaugeDir = singleDir.getInstance(); + + gaugeRain = singleRain.getInstance(); + gaugeRRate = singleRRate.getInstance(); + + // remove the UV gauge? + if (!config.showUvGauge) { + $('#canvas_uv').parent().remove(); + } else { + gaugeUV = singleUV.getInstance(); + } + + // remove the Solar gauge? + if (!config.showSolarGauge) { + $('#canvas_solar').parent().remove(); + } else { + gaugeSolar = singleSolar.getInstance(); + } + + // remove the Wind Rose? + if (!config.showRoseGauge) { + $('#canvas_rose').parent().remove(); + } else { + gaugeRose = singleRose.getInstance(); + } + + // remove the cloud base gauge? + if (!config.showCloudGauge) { + $('#canvas_cloud').parent().remove(); + // and remove cloudbase unit selection options + $('#cloud').parent().remove(); + } else { + gaugeCloud = singleCloudBase.getInstance(); + } + + // Set the language + changeLang(strings, false); + + if (!dashboard) { + // Go do get the data! + getRealtime(); + + // start a timer to update the status time + tickTockInterval = setInterval( + function () { + $.publish('gauges.clockTick', null); + }, + 1000); + + // start a timer to stop the page updates after the timeout period + if (config.pageUpdateLimit > 0 && getUrlParam('pageUpdate') !== config.pageUpdatePswd) { + setTimeout(pageTimeout, config.pageUpdateLimit * 60 * 1000); + } + } + }, + + // + // singleXXXX functions define a singleton for each of the gauges + // + + // + // Singleton for the LED Indicator + // + singleLed = (function () { + var instance; // Stores a reference to the Singleton + var led; // Stores a reference to the SS LED + + function init() { + // create led indicator + if ($('#canvas_led').length) { + led = new steelseries.Led( + 'canvas_led', { + ledColor: steelseries.LedColor.GREEN_LED, + size: $('#canvas_led').width() + }); + + setTitle(strings.led_title); + } + + function setTitle(newTitle) { + $('#canvas_led').attr('title', newTitle); + } + + function setLedColor(newColour) { + if (led) { + led.setLedColor(newColour); + } + } + + function setLedOnOff(onState) { + if (led) { + led.setLedOnOff(onState); + } + } + + function blink(blinkState) { + if (led) { + led.blink(blinkState); + } + } + + return { + setTitle: setTitle, + setLedColor: setLedColor, + setLedOnOff: setLedOnOff, + blink: blink + }; + } + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the Status Scroller + // + singleStatus = (function () { + var instance; // Stores a reference to the Singleton + var scroller; // Stores a reference to the SS scrolling display + + function init() { + // create forecast display + if ($('#canvas_status').length) { + scroller = new steelseries.DisplaySingle( + 'canvas_status', { + width: $('#canvas_status').width(), + height: $('#canvas_status').height(), + lcdColor: gaugeGlobals.lcdColour, + unitStringVisible: false, + value: strings.statusStr, + digitalFont: config.digitalForecast, + valuesNumeric: false, + autoScroll: true, + alwaysScroll: false + }); + } + + function setValue(newTxt) { + if (scroller) { + scroller.setValue(newTxt); + } + } + + return { setText: setValue }; + } + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the Status Timer + // + singleTimer = (function () { + var instance, // Stores a reference to the Singleton + lcd, // Stores a reference to the SS LED + count = 1; + + function init() { + function tick() { + if (lcd) { + lcd.setValue(count); + count += config.longPoll ? 1 : -1; + } + } + + function reset(val) { + count = val; + } + + function setValue(newVal) { + if (lcd) { + lcd.setValue(newVal); + } + } + + // create timer display + if ($('#canvas_timer').length) { + lcd = new steelseries.DisplaySingle( + 'canvas_timer', { + width: $('#canvas_timer').width(), + height: $('#canvas_timer').height(), + lcdColor: gaugeGlobals.lcdColour, + lcdDecimals: 0, + unitString: strings.timer, + unitStringVisible: true, + digitalFont: config.digitalFont, + value: count + }); + // subscribe to data updates + $.subscribe('gauges.clockTick', tick); + } + + return { + reset: reset, + setValue: setValue + }; + } + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the Temperature Gauge + // + singleTemp = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + + // define temperature gauge start values + cache.sections = createTempSections(true); + cache.areas = []; + cache.minValue = gaugeGlobals.tempScaleDefMinC; + cache.maxValue = gaugeGlobals.tempScaleDefMaxC; + cache.title = strings.temp_title_out; + cache.value = gaugeGlobals.tempScaleDefMinC + 0.0001; + cache.maxMinVisible = false; + cache.selected = 'out'; + + // create temperature radial gauge + if ($('#canvas_temp').length) { + params.size = Math.ceil($('#canvas_temp').width() * config.gaugeScaling); + params.section = cache.sections; + params.area = cache.areas; + params.minValue = cache.minValue; + params.maxValue = cache.maxValue; + params.thresholdVisible = false; + params.minMeasuredValueVisible = cache.maxMinVisible; + params.maxMeasuredValueVisible = cache.maxMinVisible; + params.titleString = cache.title; + params.unitString = data.tempunit; + params.trendVisible = gaugeGlobals.tempTrendVisible; + // params.customLayer = _imgBackground; // uncomment to add a background image - See Logo /images above + + ssGauge = new steelseries.Radial('canvas_temp', params); + ssGauge.setValue(cache.value); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_temp').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_temp').css(gaugeShadow(params.size)); + } + + // remove indoor temperature/humidity options? + if (!config.showIndoorTempHum) { + $('#rad_temp1').remove(); + $('#lab_temp1').remove(); + $('#rad_temp2').remove(); + $('#lab_temp2').remove(); + $('#rad_hum1').remove(); + $('#lab_hum1').remove(); + $('#rad_hum2').remove(); + $('#lab_hum2').remove(); + } + + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + var sel = cache.selected; + + // Argument length === 1 when called from radio input + // Argument length === 2 when called from event handler + if (arguments.length === 1) { + sel = arguments[0].value; + } + + // if rad isn't specified, just use existing value + var t1, scaleStep, tip; + + cache.minValue = data.tempunit[1] === 'C' ? gaugeGlobals.tempScaleDefMinC : gaugeGlobals.tempScaleDefMinF; + cache.maxValue = data.tempunit[1] === 'C' ? gaugeGlobals.tempScaleDefMaxC : gaugeGlobals.tempScaleDefMaxF; + + if (sel === 'out') { + cache.low = extractDecimal(data.tempTL); + cache.high = extractDecimal(data.tempTH); + cache.lowScale = getMinTemp(cache.minValue); + cache.highScale = getMaxTemp(cache.maxValue); + cache.value = extractDecimal(data.temp); + cache.title = strings.temp_title_out; + cache.loc = strings.temp_out_info; + cache.trendVal = extractDecimal(data.temptrend); + cache.popupImg = 0; + if (gaugeGlobals.tempTrendVisible) { + t1 = tempTrend(+cache.trendVal, data.tempunit, false); + if (t1 === -9999) { + // trend value isn't currently available + cache.trend = steelseries.TrendState.OFF; + } else if (t1 > 0) { + cache.trend = steelseries.TrendState.UP; + } else if (t1 < 0) { + cache.trend = steelseries.TrendState.DOWN; + } else { + cache.trend = steelseries.TrendState.STEADY; + } + } + } else { + // Indoor + cache.title = strings.temp_title_in; + cache.loc = strings.temp_in_info; + cache.value = extractDecimal(data.intemp); + cache.popupImg = 1; + if (data.intempTL && data.intempTH) { + // Indoor - and Max/Min values supplied + cache.low = extractDecimal(data.intempTL); + cache.high = extractDecimal(data.intempTH); + cache.lowScale = getMinTemp(cache.minValue); + cache.highScale = getMaxTemp(cache.maxValue); + } else { + // Indoor - no Max/Min values supplied + cache.low = cache.value; + cache.lowScale = cache.value; + cache.high = cache.value; + cache.highScale = cache.value; + } + if (gaugeGlobals.tempTrendVisible) { + cache.trend = steelseries.TrendState.OFF; + } + } + + // has the gauge type changed? + if (cache.selected !== sel) { + cache.selected = sel; + // Change gauge title + ssGauge.setTitleString(cache.title); + ssGauge.setMaxMeasuredValueVisible(cache.maxMinVisible); + ssGauge.setMinMeasuredValueVisible(cache.maxMinVisible); + if (config.showPopupGraphs && config.tipImgs[0][cache.popupImg] !== null) { + var cacheDefeat = '?' + $('#imgtip0_img').attr('src').split('?')[1]; + $('#imgtip0_img').attr('src', config.imgPathURL + config.tipImgs[0][cache.popupImg] + cacheDefeat); + } + } + + // auto scale the ranges + scaleStep = data.tempunit[1] === 'C' ? 10 : 20; + while (cache.lowScale < cache.minValue) { + cache.minValue -= scaleStep; + if (cache.highScale <= cache.maxValue - scaleStep) { + cache.maxValue -= scaleStep; + } + } + while (cache.highScale > cache.maxValue) { + cache.maxValue += scaleStep; + if (cache.minValue >= cache.minValue + scaleStep) { + cache.minValue += scaleStep; + } + } + + if (cache.minValue !== ssGauge.getMinValue() || cache.maxValue !== ssGauge.getMaxValue()) { + ssGauge.setMinValue(cache.minValue); + ssGauge.setMaxValue(cache.maxValue); + ssGauge.setValue(cache.minValue); + } + if (cache.selected === 'out') { + cache.areas = [steelseries.Section(+cache.low, +cache.high, gaugeGlobals.minMaxArea)]; + } else if (data.intempTL && data.intempTH) { + // Indoor and min/max avaiable + cache.areas = [steelseries.Section(+cache.low, +cache.high, gaugeGlobals.minMaxArea)]; + } else { + // Indoor no min/max avaiable + cache.areas = []; + } + + if (gaugeGlobals.tempTrendVisible) { + ssGauge.setTrend(cache.trend); + } + ssGauge.setArea(cache.areas); + ssGauge.setValueAnimated(+cache.value); + + if (ddimgtooltip.showTips) { + // update tooltip + if (cache.selected === 'out') { + tip = cache.loc + ' - ' + strings.lowestF_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TtempTL + + ' | ' + + strings.highestF_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TtempTH; + if (cache.trendVal !== -9999) { + tip += '
' + + strings.temp_trend_info + ': ' + tempTrend(cache.trendVal, data.tempunit, true) + + ' ' + cache.trendVal + data.tempunit + '/h'; + } + } else if (data.TintempTL && data.TintempTH) { + // Indoor and min/max available + tip = cache.loc + ' - ' + strings.lowestF_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TintempTL + + ' | ' + + strings.highestF_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TintempTH; + } else { + // Indoor no min/max + tip = cache.loc + ': ' + data.intemp + data.tempunit; + } + $('#imgtip0_txt').html(tip); + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[0][cache.popupImg] !== null) { + $('#imgtip0_img').attr('src', config.imgPathURL + config.tipImgs[0][cache.popupImg] + cacheDefeat); + } + } + + return { + data: cache, + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), // End singleTemp() + + // + // Singleton for the Dewpoint Gauge + // + singleDew = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + var tmp; + + // define dew point gauge start values + cache.sections = createTempSections(true); + cache.areas = []; + cache.minValue = gaugeGlobals.tempScaleDefMinC; + cache.maxValue = gaugeGlobals.tempScaleDefMaxC; + cache.value = gaugeGlobals.tempScaleDefMinC + 0.0001; + // Has the end user selected a preferred 'scale' before + tmp = getCookie('dewGauge'); + cache.selected = tmp !== null ? tmp : config.dewDisplayType; + setRadioCheck('rad_dew', cache.selected); + switch (cache.selected) { + case 'dew': + cache.title = strings.dew_title; + cache.popupImg = 0; + break; + case 'app': + cache.title = strings.apptemp_title; + cache.popupImg = 1; + break; + case 'feel': + cache.title = strings.feel_title; + cache.popupImg = 1; + break; + case 'wnd': + cache.title = strings.chill_title; + cache.popupImg = 2; + break; + case 'hea': + cache.title = strings.heat_title; + cache.popupImg = 3; + break; + case 'hum': + cache.title = strings.humdx_title; + cache.popupImg = 4; + // no default + } + cache.minMeasuredVisible = false; + cache.maxMeasuredVisible = false; + + // create dew point radial gauge + if ($('#canvas_dew').length) { + params.size = Math.ceil($('#canvas_dew').width() * config.gaugeScaling); + params.section = cache.sections; + params.area = cache.areas; + params.minValue = cache.minValue; + params.maxValue = cache.maxValue; + params.thresholdVisible = false; + params.titleString = cache.title; + params.unitString = data.tempunit; + + ssGauge = new steelseries.Radial('canvas_dew', params); + ssGauge.setValue(cache.value); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_dew').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_dew').css(gaugeShadow(params.size)); + } + + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + // if rad isn't specified, just use existing value + var sel = cache.selected; + + // Argument length === 2 when called from event handler + if (arguments.length === 1) { + sel = arguments[0].value; + // save the choice in a cookie + setCookie('dewGauge', sel); + } + + var tip, scaleStep; + + cache.minValue = data.tempunit[1] === 'C' ? gaugeGlobals.tempScaleDefMinC : gaugeGlobals.tempScaleDefMinF; + cache.maxValue = data.tempunit[1] === 'C' ? gaugeGlobals.tempScaleDefMaxC : gaugeGlobals.tempScaleDefMaxF; + + cache.lowScale = getMinTemp(cache.minValue); + cache.highScale = getMaxTemp(cache.maxValue); + + switch (sel) { + case 'dew': // dew point + cache.low = extractDecimal(data.dewpointTL); + cache.high = extractDecimal(data.dewpointTH); + cache.value = extractDecimal(data.dew); + cache.areas = [steelseries.Section(+cache.low, +cache.high, gaugeGlobals.minMaxArea)]; + cache.title = strings.dew_title; + cache.minMeasuredVisible = false; + cache.maxMeasuredVisible = false; + cache.popupImg = 0; + tip = strings.dew_info + ':' + + '
' + + '- ' + strings.lowest_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TdewpointTL + + ' | ' + strings.highest_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TdewpointTH; + break; + case 'app': // apparent temperature + cache.low = extractDecimal(data.apptempTL); + cache.high = extractDecimal(data.apptempTH); + cache.value = extractDecimal(data.apptemp); + cache.areas = [steelseries.Section(+cache.low, +cache.high, gaugeGlobals.minMaxArea)]; + cache.title = strings.apptemp_title; + cache.minMeasuredVisible = false; + cache.maxMeasuredVisible = false; + cache.popupImg = 1; + tip = tip = strings.apptemp_info + ':' + + '
' + + '- ' + strings.lowestF_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TapptempTL + + ' | ' + strings.highestF_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TapptempTH; + break; + case 'feel': // feels like + cache.low = extractDecimal(data.feelslikeTL); + cache.high = extractDecimal(data.feelslikeTH); + cache.value = extractDecimal(data.feelslike); + cache.areas = [steelseries.Section(+cache.low, +cache.high, gaugeGlobals.minMaxArea)]; + cache.title = strings.feel_title; + cache.minMeasuredVisible = false; + cache.maxMeasuredVisible = false; + cache.popupImg = 1; + tip = tip = strings.feel_info + ':' + + '
' + + '- ' + strings.lowestF_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TfeelslikeTL + + ' | ' + strings.highestF_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TfeelslikeTH; + break; + case 'wnd': // wind chill + cache.low = extractDecimal(data.wchillTL); + cache.high = extractDecimal(data.wchill); + cache.value = extractDecimal(data.wchill); + cache.areas = []; + cache.title = strings.chill_title; + cache.minMeasuredVisible = true; + cache.maxMeasuredVisible = false; + cache.popupImg = 2; + tip = strings.chill_info + ':' + + '
' + + '- ' + strings.lowest_info + ': ' + cache.low + data.tempunit + ' ' + strings.at + ' ' + data.TwchillTL; + break; + case 'hea': // heat index + cache.low = extractDecimal(data.heatindex); + cache.high = extractDecimal(data.heatindexTH); + cache.value = extractDecimal(data.heatindex); + cache.areas = []; + cache.title = strings.heat_title; + cache.minMeasuredVisible = false; + cache.maxMeasuredVisible = true; + cache.popupImg = 3; + tip = strings.heat_info + ':' + + '
' + + '- ' + strings.highest_info + ': ' + cache.high + data.tempunit + ' ' + strings.at + ' ' + data.TheatindexTH; + break; + case 'hum': // humidex + cache.low = extractDecimal(data.humidex); + cache.high = extractDecimal(data.humidex); + cache.value = extractDecimal(data.humidex); + cache.areas = []; + cache.title = strings.humdx_title; + cache.minMeasuredVisible = false; + cache.maxMeasuredVisible = false; + cache.popupImg = 4; + tip = strings.humdx_info + ': ' + cache.value + data.tempunit; + break; + // no default + } + + if (cache.selected !== sel) { + cache.selected = sel; + // change gauge title + ssGauge.setTitleString(cache.title); + // and graph image + if (config.showPopupGraphs && config.tipImgs[1][cache.popupImg] !== null) { + var cacheDefeat = '?' + $('#imgtip1_img').attr('src').split('?')[1]; + $('#imgtip1_img').attr('src', config.imgPathURL + config.tipImgs[1][cache.popupImg] + cacheDefeat); + } + } + + // auto scale the ranges + scaleStep = data.tempunit[1] === 'C' ? 10 : 20; + while (cache.lowScale < cache.minValue) { + cache.minValue -= scaleStep; + if (cache.highScale <= cache.maxValue - scaleStep) { + cache.maxValue -= scaleStep; + } + } + while (cache.highScale > cache.maxValue) { + cache.maxValue += scaleStep; + if (cache.minValue >= cache.minValue + scaleStep) { + cache.minValue += scaleStep; + } + } + + if (cache.minValue !== ssGauge.getMinValue() || cache.maxValue !== ssGauge.getMaxValue()) { + ssGauge.setMinValue(cache.minValue); + ssGauge.setMaxValue(cache.maxValue); + ssGauge.setValue(cache.minValue); + } + ssGauge.setMinMeasuredValueVisible(cache.minMeasuredVisible); + ssGauge.setMaxMeasuredValueVisible(cache.maxMeasuredVisible); + ssGauge.setMinMeasuredValue(+cache.low); + ssGauge.setMaxMeasuredValue(+cache.high); + ssGauge.setArea(cache.areas); + ssGauge.setValueAnimated(+cache.value); + + if (ddimgtooltip.showTips) { + // update tooltip + $('#imgtip1_txt').html(tip); + } + } + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[1][cache.popupImg] !== null) { + $('#imgtip1_img').attr('src', config.imgPathURL + config.tipImgs[1][cache.popupImg] + cacheDefeat); + } + } + + return { + data: cache, + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), // End of singleDew() + + // + // Singleton for the Rainfall Gauge + // + singleRain = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + + // define rain gauge start values + cache.maxValue = gaugeGlobals.rainScaleDefMaxmm; + cache.value = 0.0001; + cache.title = strings.rain_title; + cache.lcdDecimals = 1; + cache.scaleDecimals = 1; + cache.labelNumberFormat = gaugeGlobals.labelFormat; + cache.sections = (gaugeGlobals.rainUseSectionColours ? createRainfallSections(true) : []); + cache.valGrad = (gaugeGlobals.rainUseGradientColours ? createRainfallGradient(true) : null); + + // create rain radial bargraph gauge + if ($('#canvas_rain').length) { + params.size = Math.ceil($('#canvas_rain').width() * config.gaugeScaling); + params.maxValue = cache.maxValue; + params.thresholdVisible = false; + params.titleString = cache.title; + params.unitString = data.rainunit; + params.valueColor = steelseries.ColorDef.BLUE; + params.valueGradient = cache.valGrad; + params.useValueGradient = gaugeGlobals.rainUseGradientColours; + params.useSectionColors = gaugeGlobals.rainUseSectionColour; + params.useSectionColors = gaugeGlobals.rainUseSectionColours; + params.labelNumberFormat = cache.labelNumberFormat; + params.fractionalScaleDecimals = cache.scaleDecimals; + params.niceScale = false; + + ssGauge = new steelseries.RadialBargraph('canvas_rain', params); + ssGauge.setValue(cache.value); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_rain').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_rain').css(gaugeShadow(params.size)); + } + + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + cache.value = extractDecimal(data.rfall); + if (data.rainunit === 'mm') { // 10, 20, 30... + cache.maxValue = Math.max(nextHighest(cache.value, 10), gaugeGlobals.rainScaleDefMaxmm); + } else { + // inches 0.5, 1.0, 2.0, 3.0 ... 10.0, 12.0, 14.0 + if (cache.value <= 1) { + cache.maxValue = Math.max(nextHighest(cache.value, 0.5), gaugeGlobals.rainScaleDefMaxIn); + } else if (cache.value <= 6) { + cache.maxValue = Math.max(nextHighest(cache.value, 1), gaugeGlobals.rainScaleDefMaxIn); + } else { + cache.maxValue = Math.max(nextHighest(cache.value, 2), gaugeGlobals.rainScaleDefMaxIn); + } + cache.scaleDecimals = cache.maxValue < 1 ? 2 : 1; + } + + if (cache.maxValue !== ssGauge.getMaxValue()) { + // Gauge scale is too low, increase it. + // First set the pointer back to zero so we get a nice animation + ssGauge.setValue(0); + // and redraw the gauge with the new scale + ssGauge.setFractionalScaleDecimals(cache.scaleDecimals); + ssGauge.setMaxValue(cache.maxValue); + } + ssGauge.setValueAnimated(cache.value); + + if (ddimgtooltip.showTips) { + // update tooltip + $('#imgtip2_txt').html(strings.LastRain_info + ': ' + data.LastRained); + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[2] !== null) { + $('#imgtip2_img').attr('src', config.imgPathURL + config.tipImgs[2] + cacheDefeat); + } + } + + return { + data: cache, + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the Rainfall Rate Gauge + // + singleRRate = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + + // define rain rate gauge start values + cache.maxMeasured = 0; + cache.maxValue = gaugeGlobals.rainRateScaleDefMaxmm; + cache.value = 0.0001; + cache.title = strings.rrate_title; + cache.lcdDecimals = 1; + cache.scaleDecimals = 0; + cache.labelNumberFormat = gaugeGlobals.labelFormat; + cache.sections = createRainRateSections(true); + + // create rain rate radial gauge + if ($('#canvas_rrate').length) { + params.size = Math.ceil($('#canvas_rrate').width() * config.gaugeScaling); + params.section = cache.sections; + params.maxValue = cache.maxValue; + params.thresholdVisible = false; + params.maxMeasuredValueVisible = true; + params.titleString = cache.title; + params.unitString = data.rainunit + '/h'; + params.lcdDecimals = cache.lcdDecimals; + params.labelNumberFormat = cache.labelNumberFormat; + params.fractionalScaleDecimals = cache.scaleDecimals; + params.niceScale = false; + + ssGauge = new steelseries.Radial('canvas_rrate', params); + ssGauge.setMaxMeasuredValue(cache.maxMeasured); + ssGauge.setValue(cache.value); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_rrate').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_rrate').css(gaugeShadow(params.size)); + } + + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + var tip; + + cache.value = extractDecimal(data.rrate); + cache.maxMeasured = extractDecimal(data.rrateTM); + cache.overallMax = Math.max(cache.maxMeasured, cache.value); // workaround for VWS bug, not supplying correct max value today + + if (data.rainunit === 'mm') { // 10, 20, 30... + cache.maxValue = nextHighest(cache.overallMax, 10); + } else { + // inches 0.5, 1.0, 2.0, 3.0 ... 10, 20, 30... + if (cache.overallMax <= 0.5) { + cache.maxValue = 0.5; + } else if (cache.overallMax <= 10) { + cache.maxValue = nextHighest(cache.overallMax, 1); + } else { + cache.maxValue = nextHighest(cache.overallMax, 10); + } + cache.scaleDecimals = cache.maxValue < 1 ? 2 : (cache.maxValue < 7 ? 1 : 0); + } + + if (cache.maxValue !== ssGauge.getMaxValue()) { + ssGauge.setValue(0); + ssGauge.setFractionalScaleDecimals(cache.scaleDecimals); + ssGauge.setMaxValue(cache.maxValue); + } + + ssGauge.setValueAnimated(cache.value); + ssGauge.setMaxMeasuredValue(cache.maxMeasured); + + if (ddimgtooltip.showTips) { + // update tooltip + tip = strings.rrate_info + ':
' + + '- ' + strings.maximum_info + ': ' + data.rrateTM + ' ' + data.rainunit + '/h ' + strings.at + ' ' + data.TrrateTM + + ' | ' + strings.max_hour_info + ': ' + extractDecimal(data.hourlyrainTH) + ' ' + data.rainunit + ' ' + + strings.at + ' ' + data.ThourlyrainTH; + $('#imgtip3_txt').html(tip); + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[3] !== null) { + $('#imgtip3_img').attr('src', config.imgPathURL + config.tipImgs[3] + cacheDefeat); + } + } + return { + data: cache, + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the Humidity Gauge + // + singleHum = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + + // define humidity gauge start values + cache.areas = []; + cache.value = 0.0001; + cache.title = strings.hum_title_out; + cache.selected = 'out'; + + // create humidity radial gauge + if ($('#canvas_hum').length) { + params.size = Math.ceil($('#canvas_hum').width() * config.gaugeScaling); + params.section = [ + steelseries.Section(0, 20, 'rgba(255,255,0,0.3)'), + steelseries.Section(20, 80, 'rgba(0,255,0,0.3)'), + steelseries.Section(80, 100, 'rgba(255,0,0,0.3)') + ]; + params.area = cache.areas; + params.maxValue = 100; + params.thresholdVisible = false; + params.titleString = cache.title; + params.unitString = 'RH%'; + + ssGauge = new steelseries.Radial('canvas_hum', params); + ssGauge.setValue(cache.value); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_hum').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_hum').css(gaugeShadow(params.size)); + } + + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + var radio; + // Argument length === 2 when called from event handler + if (arguments.length === 1) { + radio = arguments[0]; + } + + // if rad isn't specified, just use existing value + var sel = (typeof radio === 'undefined' ? cache.selected : radio.value), + tip; + + if (sel === 'out') { + cache.value = extractDecimal(data.hum); + cache.areas = [steelseries.Section(+extractDecimal(data.humTL), +extractDecimal(data.humTH), gaugeGlobals.minMaxArea)]; + cache.title = strings.hum_title_out; + cache.popupImg = 0; + } else { + cache.value = extractDecimal(data.inhum); + if (data.inhumTL && data.inhumTH) { + cache.areas = [steelseries.Section(+extractDecimal(data.inhumTL), +extractDecimal(data.inhumTH), gaugeGlobals.minMaxArea)]; + } else { + cache.areas = []; + } + cache.title = strings.hum_title_in; + cache.popupImg = 1; + } + + if (cache.selected !== sel) { + cache.selected = sel; + // Change gauge title + ssGauge.setTitleString(cache.title); + if (config.showPopupGraphs && config.tipImgs[4][cache.popupImg] !== null) { + var cacheDefeat = '?' + $('#imgtip4_img').attr('src').split('?')[1]; + $('#imgtip4_img').attr('src', config.imgPathURL + config.tipImgs[4][cache.popupImg] + cacheDefeat); + } + } + + ssGauge.setArea(cache.areas); + ssGauge.setValueAnimated(cache.value); + + if (ddimgtooltip.showTips) { + // update tooltip + if (cache.selected === 'out') { + tip = strings.hum_out_info + ':' + + '
' + + '- ' + strings.minimum_info + ': ' + extractDecimal(data.humTL) + '% ' + strings.at + ' ' + data.ThumTL + + ' | ' + strings.maximum_info + ': ' + extractDecimal(data.humTH) + '% ' + strings.at + ' ' + data.ThumTH; + } else if (data.inhumTL && data.inhumTH) { + // we have indoor high/low data + tip = strings.hum_in_info + ':' + + '
' + + '- ' + strings.minimum_info + ': ' + extractDecimal(data.inhumTL) + '% ' + strings.at + ' ' + data.TinhumTL + + ' | ' + strings.maximum_info + ': ' + extractDecimal(data.inhumTH) + '% ' + strings.at + ' ' + data.TinhumTH; + } else { + // no indoor high/low data + tip = strings.hum_in_info + ': ' + extractDecimal(data.inhum) + '%'; + } + $('#imgtip4_txt').html(tip); + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[4][cache.popupImg] !== null) { + $('#imgtip4_img').attr('src', config.imgPathURL + config.tipImgs[4][cache.popupImg] + cacheDefeat); + } + } + + return { + data: cache, + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the Barometer Gauge + // + singleBaro = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + + // define pressure/barometer gauge start values + cache.sections = []; + cache.areas = []; + cache.minValue = gaugeGlobals.baroScaleDefMinhPa; + cache.maxValue = gaugeGlobals.baroScaleDefMaxhPa; + cache.value = cache.minValue + 0.0001; + cache.title = strings.baro_title; + cache.lcdDecimals = 1; + cache.scaleDecimals = 0; + cache.labelNumberFormat = gaugeGlobals.labelFormat; + + // create pressure/barometric radial gauge + if ($('#canvas_baro').length) { + params.size = Math.ceil($('#canvas_baro').width() * config.gaugeScaling); + params.section = cache.sections; + params.area = cache.areas; + params.minValue = cache.minValue; + params.maxValue = cache.maxValue; + params.niceScale = false; + params.thresholdVisible = false; + params.titleString = cache.title; + params.unitString = data.pressunit; + params.lcdDecimals = cache.lcdDecimals; + params.trendVisible = gaugeGlobals.pressureTrendVisible; + params.labelNumberFormat = cache.labelNumberFormat; + params.fractionalScaleDecimals = cache.scaleDecimals; + + ssGauge = new steelseries.Radial('canvas_baro', params); + ssGauge.setValue(cache.value); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_baro').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_baro').css(gaugeShadow(params.size)); + } + + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + var tip, t1, dps; + + cache.recLow = +extractDecimal(data.pressL); + cache.recHigh = +extractDecimal(data.pressH); + cache.todayLow = +extractDecimal(data.pressTL); + cache.todayHigh = +extractDecimal(data.pressTH); + cache.value = +extractDecimal(data.press); + // Convert the WD change over 3 hours to an hourly rate + cache.trendVal = +extractDecimal(data.presstrendval) / (config.weatherProgram === 2 ? 3 : 1); + + if (data.pressunit === 'hPa' || data.pressunit === 'mb') { + // default min range 990-1030 - steps of 10 hPa + cache.minValue = Math.min(nextLowest(cache.recLow - 2, 10), gaugeGlobals.baroScaleDefMinhPa); + cache.maxValue = Math.max(nextHighest(cache.recHigh + 2, 10), gaugeGlobals.baroScaleDefMaxhPa); + dps = 1; // 1 decimal place + } else if (data.pressunit === 'kPa') { + // default min range 99-105 - steps of 1 kPa + cache.minValue = Math.min(nextLowest(cache.recLow - 0.2, 1), gaugeGlobals.baroScaleDefMinkPa); + cache.maxValue = Math.max(nextHighest(cache.recHigh + 0.2, 1), gaugeGlobals.baroScaleDefMaxkPa); + dps = 2; + } else { + // inHg: default min range 29.5-30.5 - steps of 0.5 inHg + cache.minValue = Math.min(nextLowest(cache.recLow - 0.1, 0.5), gaugeGlobals.baroScaleDefMininHg); + cache.maxValue = Math.max(nextHighest(cache.recHigh + 0.1, 0.5), gaugeGlobals.baroScaleDefMaxinHg); + dps = 3; + } + cache.trendValRnd = cache.trendVal.toFixed(dps); + cache.todayLowRnd = cache.todayLow.toFixed(dps); + cache.todayHighRnd = cache.todayHigh.toFixed(dps); + + if (cache.minValue !== ssGauge.getMinValue() || cache.maxValue !== ssGauge.getMaxValue()) { + ssGauge.setMinValue(cache.minValue); + ssGauge.setMaxValue(cache.maxValue); + ssGauge.setValue(cache.minValue); + } + if (cache.recHigh === cache.todayHigh && cache.recLow === cache.todayLow) { + // VWS does not provide record hi/lo values + cache.sections = []; + cache.areas = [steelseries.Section(cache.todayLow, cache.todayHigh, gaugeGlobals.minMaxArea)]; + } else { + cache.sections = [ + steelseries.Section(cache.minValue, cache.recLow, 'rgba(255,0,0,0.5)'), + steelseries.Section(cache.recHigh, cache.maxValue, 'rgba(255,0,0,0.5)') + ]; + cache.areas = [ + steelseries.Section(cache.minValue, cache.recLow, 'rgba(255,0,0,0.5)'), + steelseries.Section(cache.recHigh, cache.maxValue, 'rgba(255,0,0,0.5)'), + steelseries.Section(cache.todayLow, cache.todayHigh, gaugeGlobals.minMaxArea) + ]; + } + + if (gaugeGlobals.pressureTrendVisible) { + // Use the baroTrend rather than simple arithmetic test - steady is more/less than zero! + t1 = baroTrend(cache.trendVal, data.pressunit, false); + if (t1 === -9999) { + // trend value isn't currently available + cache.trend = steelseries.TrendState.OFF; + } else if (t1 > 0) { + cache.trend = steelseries.TrendState.UP; + } else if (t1 < 0) { + cache.trend = steelseries.TrendState.DOWN; + } else { + cache.trend = steelseries.TrendState.STEADY; + } + ssGauge.setTrend(cache.trend); + } + + ssGauge.setArea(cache.areas); + ssGauge.setSection(cache.sections); + ssGauge.setValueAnimated(cache.value); + + if (ddimgtooltip.showTips) { + // update tooltip + tip = strings.baro_info + ':' + + '
' + + '- ' + strings.minimum_info + ': ' + cache.todayLowRnd + ' ' + data.pressunit + ' ' + strings.at + ' ' + data.TpressTL + + ' | ' + strings.maximum_info + ': ' + cache.todayHighRnd + ' ' + data.pressunit + ' ' + strings.at + ' ' + data.TpressTH; + if (cache.trendVal !== -9999) { + tip += '
' + + '- ' + strings.baro_trend_info + ': ' + baroTrend(cache.trendVal, data.pressunit, true) + ' ' + + (cache.trendValRnd > 0 ? '+' : '') + cache.trendValRnd + ' ' + data.pressunit + '/h'; + } + $('#imgtip5_txt').html(tip); + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[5] !== null) { + $('#imgtip5_img').attr('src', config.imgPathURL + config.tipImgs[5] + cacheDefeat); + } + } + + return { + data: cache, + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the Wind Speed Gauge + // + singleWind = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + + // define wind gauge start values + cache.maxValue = gaugeGlobals.windScaleDefMaxKph; + cache.areas = []; + cache.maxMeasured = 0; + cache.value = 0.0001; + cache.title = strings.wind_title; + + // create wind speed radial gauge + if ($('#canvas_wind').length) { + params.size = Math.ceil($('#canvas_wind').width() * config.gaugeScaling); + params.area = cache.areas; + params.maxValue = cache.maxValue; + params.niceScale = false; + params.thresholdVisible = false; + params.maxMeasuredValueVisible = true; + params.titleString = cache.title; + params.unitString = data.windunit; + + ssGauge = new steelseries.Radial('canvas_wind', params); + ssGauge.setMaxMeasuredValue(cache.maxMeasured); + ssGauge.setValue(cache.value); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_wind').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_wind').css(gaugeShadow(params.size)); + } + + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + var tip; + + cache.value = extractDecimal(data.wlatest); + cache.average = extractDecimal(data.wspeed); + cache.gust = extractDecimal(data.wgust); + cache.maxGustToday = extractDecimal(data.wgustTM); + cache.maxAvgToday = extractDecimal(data.windTM); + + switch (data.windunit) { + case 'mph': + case 'kts': + cache.maxValue = Math.max(nextHighest(cache.maxGustToday, 10), gaugeGlobals.windScaleDefMaxMph); + break; + case 'm/s': + cache.maxValue = Math.max(nextHighest(cache.maxGustToday, 5), gaugeGlobals.windScaleDefMaxMs); + break; + default: + cache.maxValue = Math.max(nextHighest(cache.maxGustToday, 20), gaugeGlobals.windScaleDefMaxKmh); + } + cache.areas = [ + steelseries.Section(0, +cache.average, gaugeGlobals.windAvgArea), + steelseries.Section(+cache.average, +cache.gust, gaugeGlobals.minMaxArea) + ]; + if (cache.maxValue !== ssGauge.getMaxValue()) { + ssGauge.setValue(0); + ssGauge.setMaxValue(cache.maxValue); + } + + ssGauge.setArea(cache.areas); + ssGauge.setMaxMeasuredValue(cache.maxGustToday); + ssGauge.setValueAnimated(cache.value); + + if (ddimgtooltip.showTips) { + // update tooltip + tip = strings.tenminavgwind_info + ': ' + cache.average + ' ' + data.windunit + ' | ' + + strings.maxavgwind_info + ': ' + cache.maxAvgToday + ' ' + data.windunit + '
' + + strings.tenmingust_info + ': ' + cache.gust + ' ' + data.windunit + ' | ' + + strings.maxgust_info + ': ' + cache.maxGustToday + ' ' + data.windunit + ' ' + + strings.at + ' ' + data.TwgustTM + ' ' + strings.bearing_info + ': ' + data.bearingTM + + (isNaN(parseFloat(data.bearingTM)) ? '' : '° (' + getord(+data.bearingTM) + ')'); + $('#imgtip6_txt').html(tip); + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[6] !== null) { + $('#imgtip6_img').attr('src', config.imgPathURL + config.tipImgs[6] + cacheDefeat); + } + } + + return { + data: cache, + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), // End of singleWind() + + // + // Singleton for the Wind Direction Gauge + // + singleDir = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + + // define wind direction gauge start values + cache.valueLatest = 0; + cache.valueAverage = 0; + cache.titles = [strings.latest_web, strings.tenminavg_web]; + + // create wind direction/compass radial gauge + if ($('#canvas_dir').length) { + params.size = Math.ceil($('#canvas_dir').width() * config.gaugeScaling); + params.pointerTypeLatest = gaugeGlobals.pointer; // default TYPE8, + params.pointerTypeAverage = gaugeGlobals.dirAvgPointer; // default TYPE8 + params.pointerColorAverage = gaugeGlobals.dirAvgPointerColour; + params.degreeScale = true; // Show degree scale rather than ordinal directions + params.pointSymbols = strings.compass; + params.roseVisible = false; + params.lcdTitleStrings = cache.titles; + params.useColorLabels = false; + + ssGauge = new steelseries.WindDirection('canvas_dir', params); + ssGauge.setValueAverage(+cache.valueAverage); + ssGauge.setValueLatest(+cache.valueLatest); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_dir').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_dir').css(gaugeShadow(params.size)); + } + + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + var windSpd, windGst, range, tip, i, + rosePoints = 0, + roseMax = 0, + roseSectionAngle = 0, + roseAreas = []; + + cache.valueLatest = extractInteger(data.bearing); + cache.valueAverage = extractInteger(data.avgbearing); + cache.bearingFrom = extractInteger(data.BearingRangeFrom10); + cache.bearingTo = extractInteger(data.BearingRangeTo10); + + ssGauge.setValueAnimatedAverage(+cache.valueAverage); + if (cache.valueAverage === 0) { + cache.valueLatest = 0; + } + ssGauge.setValueAnimatedLatest(+cache.valueLatest); + + if (config.showWindVariation) { + windSpd = +extractDecimal(data.wspeed); + windGst = +extractDecimal(data.wgust); + switch (data.windunit.toLowerCase()) { + case 'mph': + cache.avgKnots = 0.868976242 * windSpd; + cache.gstKnots = 0.868976242 * windGst; + break; + case 'kts': + cache.avgKnots = windSpd; + cache.gstKnots = windGst; + break; + case 'm/s': + cache.avgKnots = 1.94384449 * windSpd; + cache.gstKnots = 1.94384449 * windGst; + break; + case 'km/h': + cache.avgKnots = 0.539956803 * windSpd; + cache.gstKnots = 0.539956803 * windGst; + break; + // no default + } + cache.avgKnots = Math.round(cache.avgKnots); + cache.gstKnots = Math.round(cache.gstKnots); + if (config.showWindMetar) { + ssGauge.VRB = ' - METAR: ' + ('0' + data.avgbearing).slice(-3) + ('0' + cache.avgKnots).slice(-2) + + 'G' + ('0' + cache.gstKnots).slice(-2) + 'KT '; + } else { + ssGauge.VRB = ''; + } + if (windSpd > 0) { + // If variation less than 60 degrees, then METAR = Steady + // Unless range = 0 and from/to direction = avg + 180 + range = (+cache.bearingTo < +cache.bearingFrom ? 360 + (+cache.bearingTo) : +cache.bearingTo) - (+cache.bearingFrom); + + if (cache.avgKnots < 3) { // Europe uses 3kts, USA 6kts as the threshold + if (config.showRoseOnDirGauge) { + ssGauge.setSection([steelseries.Section(cache.bearingFrom, cache.bearingTo, gaugeGlobals.windVariationSector)]); + ssGauge.setSection([]); + } else { + ssGauge.setSection([steelseries.Section(cache.bearingFrom, cache.bearingTo, gaugeGlobals.minMaxArea)]); + ssGauge.setArea([]); + } + } else if (config.showRoseOnDirGauge) { + ssGauge.setSection([steelseries.Section(cache.bearingFrom, cache.bearingTo, gaugeGlobals.windVariationSector)]); + } else { + ssGauge.setSection([]); + ssGauge.setArea([steelseries.Section(cache.bearingFrom, cache.bearingTo, gaugeGlobals.minMaxArea)]); + } + if (config.showWindMetar) { + if ((range < 60 && range > 0) || range === 0 && cache.bearingFrom === cache.valueAverage) { + ssGauge.VRB += ' STDY'; + } else if (cache.avgKnots < 3) { // Europe uses 3kts, USA 6kts as the threshold + ssGauge.VRB += ' VRB'; + } else { + ssGauge.VRB += ' ' + cache.bearingFrom + 'V' + cache.bearingTo; + } + } + } else { + // Zero wind speed, calm + if (config.showWindMetar) { + ssGauge.VRB = ' - METAR: 00000KT'; + } + ssGauge.setSection([]); + if (!config.showRoseOnDirGauge) { + ssGauge.setArea([]); + } + } + } else { + ssGauge.VRB = ''; + } + + // optional rose data on direction gauge + if (config.showRoseOnDirGauge && data.WindRoseData) { + // Process rose data + rosePoints = data.WindRoseData.length; + roseSectionAngle = 360 / rosePoints; + // Find total for all directions + for (i = 0; i < rosePoints; i++) { + roseMax = Math.max(roseMax, data.WindRoseData[i]); + } + // Check we actually have some data, bad things happen if roseMax=0! + if (roseMax > 0) { + // Find relative value for each point, and create a gauge area for it + for (i = 0; i < rosePoints; i++) { + roseAreas[i] = steelseries.Section( + i * roseSectionAngle - roseSectionAngle / 2, + (i + 1) * roseSectionAngle - roseSectionAngle / 2, + 'rgba(' + gradient('2020D0', 'D04040', data.WindRoseData[i] / roseMax) + ',' + + (data.WindRoseData[i] / roseMax).toFixed(2) + ')' + ); + } + } + ssGauge.setArea(roseAreas); + } + + if (ddimgtooltip.showTips) { + // update tooltip + tip = strings.latest_title + ' ' + strings.bearing_info + ': ' + cache.valueLatest + '° (' + getord(+cache.valueLatest) + ')' + + ssGauge.VRB + '
' + strings.tenminavg_web + ' ' + strings.bearing_info + ': ' + cache.valueAverage + '° (' + + getord(+cache.valueAverage) + '), ' + strings.dominant_bearing + ': ' + data.domwinddir; + if (!config.showRoseGauge) { + // Wind run is shown on the wind rose if it is available + tip += '
' + strings.windruntoday + ': ' + data.windrun + ' ' + displayUnits.windrun; + } + $('#imgtip7_txt').html(tip); + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[7] !== null) { + $('#imgtip7_img').attr('src', config.imgPathURL + config.tipImgs[7] + cacheDefeat); + } + } + + return { + data: cache, + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the Wind Rose Gauge + // + singleRose = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + + var buffers = {}; // Stores references to the various canvas buffers + var cache = {}; // various parameters to store for the life time of gauge + var ctxRoseCanvas; // 2D context for the plotted gauge + + cache.firstRun = true; + cache.odoDigits = 5; // Total number of odometer digits including the decimal + + function init() { + var div, roseCanvas; + // Get the context of the gauge canvas on the HTML page + if ($('#canvas_rose').length) { + cache.gaugeSize = Math.ceil($('#canvas_rose').width() * config.gaugeScaling); + cache.gaugeSize2 = cache.gaugeSize / 2; + cache.showOdo = config.showRoseGaugeOdo || false; + + cache.compassStrings = strings.compass; + cache.titleString = strings.windrose; + cache.gaugeOdoTitle = strings.km; + + // Create a hidden div to host the Rose plot + div = document.createElement('div'); + div.style.display = 'none'; + document.body.appendChild(div); + + // Calcuate the size of the gauge background and so the size of rose plot required + cache.plotSize = Math.floor(cache.gaugeSize * 0.68); + cache.plotSize2 = cache.plotSize / 2; + + // rose plot canvas buffer + buffers.plot = document.createElement('canvas'); + buffers.plot.width = cache.plotSize; + buffers.plot.height = cache.plotSize; + buffers.plot.id = 'rosePlot'; + buffers.ctxPlot = buffers.plot.getContext('2d'); + div.appendChild(buffers.plot); + + // Create a steelseries gauge frame + buffers.frame = document.createElement('canvas'); + buffers.frame.width = cache.gaugeSize; + buffers.frame.height = cache.gaugeSize; + buffers.ctxFrame = buffers.frame.getContext('2d'); + steelseries.drawFrame( + buffers.ctxFrame, + gaugeGlobals.frameDesign, + cache.gaugeSize2, + cache.gaugeSize2, + cache.gaugeSize, + cache.gaugeSize + ); + + // Create a steelseries gauge background + buffers.background = document.createElement('canvas'); + buffers.background.width = cache.gaugeSize; + buffers.background.height = cache.gaugeSize; + buffers.ctxBackground = buffers.background.getContext('2d'); + steelseries.drawBackground( + buffers.ctxBackground, + gaugeGlobals.background, + cache.gaugeSize2, + cache.gaugeSize2, + cache.gaugeSize, + cache.gaugeSize + ); + + // Optional - add a background image + /* + if (g_imgSmall !== null) { + var drawSize = g_size * 0.831775; + var x = (g_size - drawSize) / 2; + buffers.ctxBackground.drawImage(g_imgSmall, x, x, cache.gaugeSize, cache.gaugeSize); + } + */ + // Add the compass points + drawCompassPoints(buffers.ctxBackground, cache.gaugeSize); + + // Create a steelseries gauge foreground + buffers.foreground = document.createElement('canvas'); + buffers.foreground.width = cache.gaugeSize; + buffers.foreground.height = cache.gaugeSize; + buffers.ctxForeground = buffers.foreground.getContext('2d'); + steelseries.drawForeground( + buffers.ctxForeground, + gaugeGlobals.foreground, + cache.gaugeSize, + cache.gaugeSize, + false + ); + + roseCanvas = document.getElementById('canvas_rose'); + ctxRoseCanvas = roseCanvas.getContext('2d'); + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_rose').css({ width: cache.gaugeSize + 'px', height: cache.gaugeSize + 'px' }); + } + // resize canvas on main page + roseCanvas.width = cache.gaugeSize; + roseCanvas.height = cache.gaugeSize; + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_rose').css(gaugeShadow(cache.gaugeSize)); + } + + // Render an empty gauge, looks better than just the shadow background and odometer ;) + // Paint the gauge frame + ctxRoseCanvas.drawImage(buffers.frame, 0, 0); + + // Paint the gauge background + ctxRoseCanvas.drawImage(buffers.background, 0, 0); + + // Paint the gauge foreground + ctxRoseCanvas.drawImage(buffers.foreground, 0, 0); + + // Create an odometer + if (cache.showOdo) { + cache.odoHeight = Math.ceil(cache.gaugeSize * 0.08); // Sets the size of the odometer + cache.odoWidth = Math.ceil(Math.floor(cache.odoHeight * 0.68) * cache.odoDigits); // 'Magic' number, do not alter + // Create a new canvas for the oodometer + buffers.Odo = document.createElement('canvas'); + $(buffers.Odo).attr({ + id: 'canvas_odo', + width: cache.odoWidth, + height: cache.odoHeight + }); + // Position it + $(buffers.Odo).attr('class', 'odo'); + // Insert it into the DOM before the Rose gauge + $(buffers.Odo).insertBefore('#canvas_rose'); + // Create the odometer + ssGauge = new steelseries.Odometer('canvas_odo', { + height: cache.odoHeight, + digits: cache.odoDigits - 1, + decimals: 1 + }); + } + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + cache.firstRun = false; + + function update() { + var rose, offset; + + if (ctxRoseCanvas && !cache.firstRun) { + // Clear the gauge + ctxRoseCanvas.clearRect(0, 0, cache.gaugeSize, cache.gaugeSize); + + // Clear the existing rose plot + buffers.ctxPlot.clearRect(0, 0, cache.plotSize, cache.plotSize); + + // Create a new rose plot + rose = new RGraph.Rose('rosePlot', data.WindRoseData); + rose.Set('chart.strokestyle', 'black'); + rose.Set('chart.background.axes.color', 'gray'); + rose.Set('chart.colors.alpha', 0.5); + rose.Set('chart.colors', ['Gradient(#408040:red:#7070A0)']); + rose.Set('chart.margin', Math.ceil(40 / data.WindRoseData.length)); + + rose.Set('chart.title', cache.titleString); + rose.Set('chart.title.size', Math.ceil(0.05 * cache.plotSize)); + rose.Set('chart.title.bold', false); + rose.Set('chart.title.color', gaugeGlobals.background.labelColor.getRgbColor()); + rose.Set('chart.gutter.top', 0.2 * cache.plotSize); + rose.Set('chart.gutter.bottom', 0.2 * cache.plotSize); + + rose.Set('chart.tooltips.effect', 'snap'); + rose.Set('chart.labels.axes', ''); + rose.Set('chart.background.circles', true); + rose.Set('chart.background.grid.spokes', 16); + rose.Set('chart.radius', cache.plotSize2); + rose.Draw(); + + // Add title to windrun odometer to the plot + if (cache.showOdo) { + drawOdoTitle(buffers.ctxPlot); + } + + // Paint the gauge frame + ctxRoseCanvas.drawImage(buffers.frame, 0, 0); + + // Paint the gauge background + ctxRoseCanvas.drawImage(buffers.background, 0, 0); + + // Paint the rose plot + offset = Math.floor(cache.gaugeSize2 - cache.plotSize2); + ctxRoseCanvas.drawImage(buffers.plot, offset, offset); + + // Paint the gauge foreground + ctxRoseCanvas.drawImage(buffers.foreground, 0, 0); + + // update the odometer + if (cache.showOdo) { + ssGauge.setValueAnimated(extractDecimal(data.windrun)); + } + + // update tooltip + if (ddimgtooltip.showTips) { + $('#imgtip10_txt').html( + strings.dominant_bearing + ': ' + data.domwinddir + '
' + + strings.windruntoday + ': ' + data.windrun + ' ' + displayUnits.windrun + ); + } + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[10] !== null) { + $('#imgtip10_img').attr('src', config.imgPathURL + config.tipImgs[10] + cacheDefeat); + } + } + + // Helper function to put the compass points on the background + function drawCompassPoints(ctx, size) { + ctx.save(); + // set the font + ctx.font = 0.08 * size + 'px serif'; + ctx.strokeStyle = gaugeGlobals.background.labelColor.getRgbaColor(); + ctx.fillStyle = gaugeGlobals.background.labelColor.getRgbColor(); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + // Draw the compass points + for (var i = 0; i < 4; i++) { + ctx.translate(size / 2, size * 0.125); + ctx.fillText(cache.compassStrings[i * 2], 0, 0, size); + ctx.translate(-size / 2, -size * 0.125); + // Move to center + ctx.translate(size / 2, size / 2); + ctx.rotate(Math.PI / 2); + ctx.translate(-size / 2, -size / 2); + } + ctx.restore(); + } + + function drawOdoTitle(ctx) { + ctx.save(); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.font = 0.05 * cache.gaugeSize + 'px Arial,Verdana,sans-serif'; + ctx.strokeStyle = gaugeGlobals.background.labelColor.getRgbaColor(); + ctx.fillStyle = gaugeGlobals.background.labelColor.getRgbaColor(); + ctx.fillText(cache.gaugeOdoTitle, cache.plotSize2, cache.plotSize * 0.75, cache.plotSize * 0.5); + ctx.restore(); + } + + function setTitle(newTitle) { + cache.titleString = newTitle; + } + + function setOdoTitle(newTitle) { + cache.gaugeOdoTitle = newTitle; + } + + function setCompassStrings(newArray) { + cache.compassStrings = newArray; + if (!cache.firstRun) { + // Redraw the background + steelseries.drawBackground( + buffers.ctxBackground, + gaugeGlobals.background, + cache.gaugeSize2, + cache.gaugeSize2, + cache.gaugeSize, + cache.gaugeSize + ); + // Add the compass points + drawCompassPoints(buffers.ctxBackground, cache.gaugeSize); + } + } + + return { + update: update, + gauge: ssGauge, + drawCompassPoints: drawCompassPoints, + setTitle: setTitle, + setCompassStrings: setCompassStrings, + setOdoTitle: setOdoTitle + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the UV-Index Gauge + // + singleUV = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + + // define UV start values + cache.value = 0.0001; + cache.sections = [ + steelseries.Section(0, 2.9, '#289500'), + steelseries.Section(2.9, 5.8, '#f7e400'), + steelseries.Section(5.8, 7.8, '#f85900'), + steelseries.Section(7.8, 10.9, '#d8001d'), + steelseries.Section(10.9, 20, '#6b49c8') + ]; + // Define value gradient for UV + cache.gradient = new steelseries.gradientWrapper(0, 16, + [0, 0.1, 0.19, 0.31, 0.45, 0.625, 1], + [ + new steelseries.rgbaColor(0, 200, 0, 1), + new steelseries.rgbaColor(0, 200, 0, 1), + new steelseries.rgbaColor(255, 255, 0, 1), + new steelseries.rgbaColor(248, 89, 0, 1), + new steelseries.rgbaColor(255, 0, 0, 1), + new steelseries.rgbaColor(255, 0, 144, 1), + new steelseries.rgbaColor(153, 140, 255, 1) + ] + ); + cache.useSections = false; + cache.useValueGradient = true; + + // create UV bargraph gauge + if ($('#canvas_uv').length) { + params.size = Math.ceil($('#canvas_uv').width() * config.gaugeScaling); + params.gaugeType = steelseries.GaugeType.TYPE3; + params.maxValue = gaugeGlobals.uvScaleDefMax; + params.titleString = strings.uv_title; + params.niceScale = false; + params.section = cache.sections; + params.useSectionColors = cache.useSections; + params.valueGradient = cache.gradient; + params.useValueGradient = cache.useValueGradient; + params.lcdDecimals = gaugeGlobals.uvLcdDecimals; + + ssGauge = new steelseries.RadialBargraph('canvas_uv', params); + ssGauge.setValue(cache.value); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_uv').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_uv').css(gaugeShadow(params.size)); + } + + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + var tip, indx; + + cache.value = extractDecimal(data.UV); + + if (+cache.value === 0) { + indx = 0; + } else if (cache.value < 2.5) { + indx = 1; + } else if (cache.value < 5.5) { + indx = 2; + } else if (cache.value < 7.5) { + indx = 3; + } else if (cache.value < 10.5) { + indx = 4; + } else { + indx = 5; + } + + cache.maxValue = Math.max(nextHighest(cache.value, 2), gaugeGlobals.uvScaleDefMax); + if (cache.maxValue !== ssGauge.getMaxValue()) { + ssGauge.setValue(0); + ssGauge.setMaxValue(cache.maxValue); + } + + cache.risk = strings.uv_levels[indx]; + cache.headLine = strings.uv_headlines[indx]; + cache.detail = strings.uv_details[indx]; + ssGauge.setUnitString(cache.risk); + ssGauge.setValueAnimated(cache.value); + + if (ddimgtooltip.showTips) { + // update tooltip + tip = '' + strings.uv_title + ': ' + cache.value + ' - ' + strings.solar_maxToday + ': ' + data.UVTH + '
'; + tip += '' + cache.headLine + '
'; + tip += cache.detail; + $('#imgtip8_txt').html(tip); + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[8] !== null) { + $('#imgtip8_img').attr('src', config.imgPathURL + config.tipImgs[8] + cacheDefeat); + } + } + + return { + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the Solar Irradiation Gauge + // + singleSolar = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + + // define Solar start values + cache.value = 0.0001; + cache.sections = [ + steelseries.Section(0, 600, 'rgba(40,149,0,0.3)'), + steelseries.Section(600, 800, 'rgba(248,89,0,0.3)'), + steelseries.Section(800, 1000, 'rgba(216,0,29,0.3)'), + steelseries.Section(1000, 1800, 'rgba(107,73,200,0.3)') + ]; + + // create Solar gauge + if ($('#canvas_solar').length) { + params.size = Math.ceil($('#canvas_solar').width() * config.gaugeScaling); + params.section = cache.sections; + params.maxValue = gaugeGlobals.solarGaugeScaleMax; + params.titleString = strings.solar_title; + params.unitString = 'W/m\u00B2'; + params.niceScale = false; + params.thresholdVisible = false; + params.lcdDecimals = 0; + + if (config.showSunshineLed) { + params.userLedVisible = true; + params.userLedColor = steelseries.LedColor.YELLOW_LED; + } + + ssGauge = new steelseries.Radial('canvas_solar', params); + ssGauge.setValue(cache.value); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_solar').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_solar').css(gaugeShadow(params.size)); + } + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + var tip, percent; + + cache.value = +extractInteger(data.SolarRad); + cache.maxToday = extractInteger(data.SolarTM); + cache.currMaxValue = +extractInteger(data.CurrentSolarMax); + percent = (+cache.currMaxValue === 0 ? '--' : Math.round(+cache.value / +cache.currMaxValue * 100)); + + // Need to rescale the gauge? + cache.maxValue = Math.max(cache.value, cache.currMaxValue, cache.maxToday, gaugeGlobals.solarGaugeScaleMax); + cache.maxValue = nextHighest(cache.maxValue, 100); + if (cache.maxValue !== ssGauge.getMaxValue()) { + ssGauge.setValue(0); + ssGauge.setMaxValue(cache.maxValue); + } + + // Set a section (15% of maxScale wide) to show current theoretical max value + if (data.CurrentSolarMax !== 'N/A') { + ssGauge.setArea([ + // Sunshine threshold + steelseries.Section( + Math.max(cache.currMaxValue * gaugeGlobals.sunshineThresholdPct / 100, gaugeGlobals.sunshineThreshold), + cache.currMaxValue, + 'rgba(255,255,50,0.4)' + ), + // Over max threshold + steelseries.Section( + cache.currMaxValue, + Math.min(cache.currMaxValue + cache.maxValue * 0.15, cache.maxValue), + 'rgba(220,0,0,0.5)' + ) + ]); + } + + // Set the values + ssGauge.setMaxMeasuredValue(cache.maxToday); + ssGauge.setValueAnimated(cache.value); + + if (config.showSunshineLed) { + ssGauge.setUserLedOnOff( + percent !== '--' && + percent >= gaugeGlobals.sunshineThresholdPct && + +cache.value >= gaugeGlobals.sunshineThreshold + ); + } + + if (ddimgtooltip.showTips) { + // update tooltip + tip = '' + strings.solar_title + ': ' + cache.value + ' W/m² - ' + + '' + percent + '% ' + strings.solar_ofMax + '
' + + strings.solar_currentMax + ': ' + cache.currMaxValue + ' W/m²'; + if (typeof data.SolarTM !== 'undefined') { + tip += '
' + strings.solar_maxToday + ': ' + cache.maxToday + ' W/m²'; + } + $('#imgtip9_txt').html(tip); + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[9] !== null) { + $('#imgtip9_img').attr('src', config.imgPathURL + config.tipImgs[9] + cacheDefeat); + } + } + + return { + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // Singleton for the Cloudbase Gauge + // + singleCloudBase = (function () { + var instance; // Stores a reference to the Singleton + var ssGauge; // Stores a reference to the SS Gauge + var cache = {}; // Stores various config values and parameters + + function init() { + var params = $.extend(true, {}, commonParams); + + cache.sections = createCloudBaseSections(true); + cache.value = 0.0001; + cache.maxValue = gaugeGlobals.cloudScaleDefMaxm; + + // create Cloud base radial gauge + if ($('#canvas_cloud').length) { + params.size = Math.ceil($('#canvas_cloud').width() * config.gaugeScaling); + params.section = cache.sections; + params.maxValue = cache.maxValue; + params.titleString = strings.cloudbase_title; + params.unitString = strings.metres; + params.thresholdVisible = false; + params.lcdDecimals = 0; + + ssGauge = new steelseries.Radial('canvas_cloud', params); + ssGauge.setValue(cache.value); + + // over-ride CSS applied size? + if (config.gaugeScaling !== 1) { + $('#canvas_cloud').css({ width: params.size + 'px', height: params.size + 'px' }); + } + + // add a shadow to the gauge + if (config.showGaugeShadow) { + $('#canvas_cloud').css(gaugeShadow(params.size)); + } + // subscribe to data updates + $.subscribe('gauges.dataUpdated', update); + $.subscribe('gauges.graphUpdate', updateGraph); + } else { + // cannot draw gauge, return null + return null; + } + + function update() { + cache.value = extractInteger(data.cloudbasevalue); + + if (data.cloudbaseunit === 'm') { + // adjust metre gauge in jumps of 1000 metres, don't downscale during the session + cache.maxValue = Math.max(nextHighest(cache.value, 1000), gaugeGlobals.cloudScaleDefMaxm, cache.maxValue); + if (cache.value <= 1000 && config.roundCloudbaseVal) { + // and round the value to the nearest 10 m + cache.value = Math.round(cache.value / 10) * 10; + } else if (config.roundCloudbaseVal) { + // and round the value to the nearest 50 m + cache.value = Math.round(cache.value / 50) * 50; + } + } else { + // adjust feet gauge in jumps of 2000 ft, don't downscale during the session + cache.maxValue = Math.max(nextHighest(cache.value, 2000), gaugeGlobals.cloudScaleDefMaxft, cache.maxValue); + if (cache.value <= 2000 && config.roundCloudbaseVal) { + // and round the value to the nearest 50 ft + cache.value = Math.round(cache.value / 50) * 50; + } else if (config.roundCloudbaseVal) { + // and round the value to the nearest 100 ft + cache.value = Math.round(cache.value / 100) * 100; + } + } + + if (cache.maxValue !== ssGauge.getMaxValue()) { + if (ssGauge.getMaxValue() > cache.maxValue) { + // Gauge currently showing more than our max (nice scale effct), + // so reset our max to match + cache.maxValue = ssGauge.getMaxValue(); + } else { + // Gauge scale is too low, increase it. + // First set the pointer back to zero so we get a nice animation + ssGauge.setValue(0); + // and redraw the gauge with teh new scale + ssGauge.setMaxValue(cache.maxValue); + } + } + ssGauge.setValueAnimated(cache.value); + + if (config.showPopupData) { + // static tooltip on cloud gauge + $('#imgtip11_txt').html('' + strings.cloudbase_popup_title + '
' + strings.cloudbase_popup_text); + } + } // End of update() + + function updateGraph(evnt, cacheDefeat) { + if (config.tipImgs[11] !== null) { + $('#imgtip11_img').attr('src', config.imgPathURL + config.tipImgs[11] + cacheDefeat); + } + } + + return { + data: cache, + update: update, + gauge: ssGauge + }; + } // End of init() + + return { + // Get the Singleton instance if one exists + // or create one if it doesn't + getInstance: function () { + if (!instance) { + instance = init(); + } + return instance; + } + }; + })(), + + // + // getRealtime() fetches the realtimegauges JSON data from the server + // + getRealtime = function () { + var url = config.realTimeURL; + if ($.active > 0 && undefined != jqXHR) { + // kill any outstanding requests + jqXHR.abort(); + } + if (config.longPoll) { + url += '?timestamp=' + timestamp; + } + jqXHR = $.ajax({ + url: url, + cache: (config.longPoll), + dataType: 'json', + timeout: config.longPoll ? (Math.min(config.realtimeInterval, 20) + 21) * 1000 : 21000 // 21 second time-out by default + }).done(function (data) { + checkRtResp(data); + }).fail(function (xhr, status, err) { + checkRtError(xhr, status, err); + }); + }, + + // + // checkRtResp() called by the Ajax fetch once data has been downloaded + // + checkRtResp = function (response) { + var delay; + statusTimer.reset(config.longPoll ? 1 : config.realtimeInterval); + if (config.longPoll && response.status !== 'OK') { + checkRtError(null, 'PHP Error', response.status); + } else { + if (processData(response)) { + delay = ajaxDelay; + } else { + delay = 5; + } + if (delay > 0) { + downloadTimer = setTimeout(getRealtime, delay * 1000); + } else { + getRealtime(); + } + } + }, + + // + // checkRtError() called by the Ajax fetch if an error occurs during the fetching /data/realtimegauges.txt + // + checkRtError = function (xhr, status, error) { + if (xhr == null || xhr.statusText !== 'abort') { + // Clear any existing download timer + clearTimeout(downloadTimer); + // Set the status LED to off + ledIndicator.setLedOnOff(false); + ledIndicator.setTitle(strings.led_title_unknown); + statusScroller.setText(status + ': ' + error); + // wait 5 seconds, then try again... + downloadTimer = setTimeout(getRealtime, 5000); + } + }, + + // + // processData() massages the data returned in /data/realtimegauges.txt, and posts a gauges.dataUpdated event to update the page + // + processData = function (dataObj) { + var str, dt, tm, today, now, then, tmp, elapsedMins, retVal; + // copy the realtime fields into the global 'data' object + if (config.longPoll) { + timestamp = dataObj.timestamp; + data = dataObj.data; + } else { + // normal polling + data = dataObj; + } + + // and check we have the expected version number + if (typeof data.ver !== 'undefined' && data.ver >= realtimeVer) { + // manpulate the last rain time into something more friendly + try { + str = data.LastRainTipISO.split(' '); + dt = str[0].replace(/\//g, '-').split('-'); // WD uses dd/mm/yyyy, we use a '-' + tm = str[1].split(':'); + today = new Date(); + today.setHours(0, 0, 0, 0); + if (typeof data.dateFormat === 'undefined') { + data.dateFormat = 'y/m/d'; + } else { + // frig for WD bug which leaves a trailing % character from the tag + data.dateFormat = data.dateFormat.replace('%', ''); + } + if (data.dateFormat === 'y/m/d') { + // ISO/Cumulus format + then = new Date(dt[0], dt[1] - 1, dt[2], tm[0], tm[1], 0, 0); + } else if (data.dateFormat === 'd/m/y') { + then = new Date(dt[2], dt[1] - 1, dt[0], tm[0], tm[1], 0, 0); + } else { // m/d/y + then = new Date(dt[2], dt[0] - 1, dt[1], tm[0], tm[1], 0, 0); + } + if (then.getTime() >= today.getTime()) { + data.LastRained = strings.LastRainedT_info + ' ' + str[1]; + } else if (then.getTime() + 86400000 >= today.getTime()) { + data.LastRained = strings.LastRainedY_info + ' ' + str[1]; + } else { + data.LastRained = then.getDate().toString() + ' ' + strings.months[then.getMonth()] + ' ' + strings.at + ' ' + str[1]; + } + } catch (e) { + data.LastRained = data.LastRainTipISO; + } + if (data.tempunit.length > 1) { + // clean up temperature units - remove html encoded degree symbols + data.tempunit = data.tempunit.replace(/&\S*;/, '°'); // old Cumulus versions uses °, WeatherCat uses ° + } else { + // using new realtimegaugesT.txt with Cumulus > 1.9.2 + data.tempunit = '°' + data.tempunit; + } + + // Check for station off-line + now = Date.now(); + tmp = data.timeUTC.split(','); + sampleDate = Date.UTC(tmp[0], tmp[1] - 1, tmp[2], tmp[3], tmp[4], tmp[5]); + if (now - sampleDate > config.stationTimeout * 60 * 1000) { + elapsedMins = Math.floor((now - sampleDate) / (1000 * 60)); + // the /data/realtimegauges.txt file isn't being updated + ledIndicator.setLedColor(steelseries.LedColor.RED_LED); + ledIndicator.setTitle(strings.led_title_offline); + ledIndicator.blink(true); + if (elapsedMins < 120) { + // up to 2 hours ago + tm = elapsedMins.toString() + ' ' + strings.StatusMinsAgo; + } else if (elapsedMins < 2 * 24 * 60) { + // up to 48 hours ago + tm = Math.floor(elapsedMins / 60).toString() + ' ' + strings.StatusHoursAgo; + } else { + // days ago! + tm = Math.floor(elapsedMins / (60 * 24)).toString() + ' ' + strings.StatusDaysAgo; + } + data.forecast = strings.led_title_offline + ' ' + strings.StatusLastUpdate + ' ' + tm; + } else if (+data.SensorContactLost === 1) { + // Fine Offset sensor status + ledIndicator.setLedColor(steelseries.LedColor.RED_LED); + ledIndicator.setTitle(strings.led_title_lost); + ledIndicator.blink(true); + data.forecast = strings.led_title_lost; + } else { + ledIndicator.setLedColor(steelseries.LedColor.GREEN_LED); + ledIndicator.setTitle(strings.led_title_ok + '. ' + strings.StatusLastUpdate + ': ' + data.date); + ledIndicator.blink(false); + ledIndicator.setLedOnOff(true); + } + + // de-encode the forecast string if required (Cumulus support for extended characters) + data.forecast = $('
').html(data.forecast).text(); + data.forecast = data.forecast.trim(); + + data.pressunit = data.pressunit.trim(); // WView sends ' in', ' mb', or ' hPa' + if (data.pressunit === 'in') { // Cumulus and WView send 'in' + data.pressunit = 'inHg'; + } + + data.windunit = data.windunit.trim(); // WView sends ' kmh' etc + data.windunit = data.windunit.toLowerCase(); // WeatherCat sends "MPH" + if (data.windunit === 'knots') { // WeatherCat/weewx send "Knots", we use "kts" + data.windunit = 'kts'; + } + + if (data.windunit === 'kmh' || data.windunit === 'kph') { // WD wind unit omits '/', weewx sends 'kph' + data.windunit = 'km/h'; + } + + data.rainunit = data.rainunit.trim(); // WView sends ' mm' etc + + // take a look at the cloud base data... + // change WeatherCat units from Metres/Feet to m/ft + try { + if (data.cloudbaseunit.toLowerCase() === 'metres') { + data.cloudbaseunit = 'm'; + } else if (data.cloudbaseunit.toLowerCase() === 'feet') { + data.cloudbaseunit = 'ft'; + } + } catch (e) { + data.cloudbaseunit = ''; + } + if (config.showCloudGauge && ( + (config.weatherProgram === 4 || config.weatherProgram === 5) || + data.cloudbasevalue === '')) { + // WeatherCat and VWS (and WView?) do not provide a cloud base value, so we have to calculate it... + // assume if the station uses an imperial wind speed they want cloud base in feet, otherwise metres + data.cloudbaseunit = (data.windunit === 'mph' || data.windunit === 'kts') ? 'ft' : 'm'; + data.cloudbasevalue = calcCloudbase(data.temp, data.tempunit, data.dew, data.cloudbaseunit); + } + + // Temperature data conversion for display required? + if (data.tempunit[1] !== displayUnits.temp && userUnitsSet) { + // temp needs converting + if (data.tempunit[1] === 'C') { + convTempData(c2f); + } else { + convTempData(f2c); + } + } else if (firstRun) { + displayUnits.temp = data.tempunit[1]; + setRadioCheck('rad_unitsTemp', displayUnits.temp); + } + + // Rain data conversion for display required? + if (data.rainunit !== displayUnits.rain && userUnitsSet) { + // rain needs converting + convRainData(displayUnits.rain === 'mm' ? in2mm : mm2in); + } else if (firstRun) { + displayUnits.rain = data.rainunit; + setRadioCheck('rad_unitsRain', displayUnits.rain); + } + + // Wind data conversion for display required? + if (data.windunit !== displayUnits.wind && userUnitsSet) { + // wind needs converting + convWindData(data.windunit, displayUnits.wind); + } else if (firstRun) { + displayUnits.wind = data.windunit; + displayUnits.windrun = getWindrunUnits(data.windunit); + setRadioCheck('rad_unitsWind', displayUnits.wind); + } + + // Pressure data conversion for display required? + if (data.pressunit !== displayUnits.press && userUnitsSet) { + convBaroData(data.pressunit, displayUnits.press); + } else if (firstRun) { + displayUnits.press = data.pressunit; + setRadioCheck('rad_unitsPress', displayUnits.press); + } + + // Cloud height data conversion for display required? + if (data.cloudbaseunit !== displayUnits.cloud && userUnitsSet) { + // Cloud height needs converting + convCloudBaseData(displayUnits.cloud === 'm' ? ft2m : m2ft); + } else if (firstRun) { + displayUnits.cloud = data.cloudbaseunit; + setRadioCheck('rad_unitsCloud', displayUnits.cloud); + } + + statusScroller.setText(data.forecast); + + // first time only, setup units etc + if (firstRun) { + doFirst(); + } + + // publish the update, use the shared data object rather than transferring it + $.publish('gauges.dataUpdated', {}); + + retVal = true; + } else { + // set an error message + if (data.ver < realtimeVer) { + statusTimer.setValue(0); + statusScroller.setText('Your ' + config.realTimeURL.substr(config.realTimeURL.lastIndexOf('/') + 1) + ' file template needs updating!'); + return false; + } else { + // oh-oh! The number of data fields isn't what we expected + statusScroller.setText(strings.realtimeCorrupt); + } + ledIndicator.setLedOnOff(false); + ledIndicator.setTitle(strings.led_title_unknown); + + retVal = false; + } + return retVal; + }, + + // + // pagetimeout() called once every config.pageUpdateLimit minutes to stop updates and prevent page 'sitters' + // + pageTimeout = function () { + statusScroller.setText(strings.StatusPageLimit); + ledIndicator.setLedColor(steelseries.LedColor.RED_LED); + ledIndicator.setTitle(strings.StatusPageLimit); + ledIndicator.blink(true); + ledIndicator.setTitle(strings.StatusTimeout); + + // stop any pending download + clearTimeout(downloadTimer); + + // stop any long polling in progress + if ($.active > 0) { + jqXHR.abort(); + } + + // stop the clock + clearInterval(tickTockInterval); + + // clear the timer display + statusTimer.setValue(0); + + // set an onclick event on the LED to restart everything + $('#canvas_led').click( + function click() { + // disable the onClick event again + $('#canvas_led').unbind('click'); + // reset the timer count to 1 + statusTimer.reset(1); + // restart the timer to update the status time + tickTockInterval = setInterval( + function tick() { + $.publish('gauges.clockTick', null); + }, + 1000 + ); + + // restart the page timeout timer, so we hit this code again + setTimeout(pageTimeout, config.pageUpdateLimit * 60 * 1000); + + // refresh the page data + getRealtime(); + } + ); + }, + + // + // doFirst() called by doUpdate() the first time the page is updated to set-up various things that are + // only known when the /data/realtimegauges.txt data is available + // + doFirst = function () { + var cacheDefeat = '?' + (new Date()).getTime().toString(); + + if (data.tempunit[1] === 'F') { + displayUnits.temp = 'F'; + setRadioCheck('rad_unitsTemp', 'F'); + setTempUnits(false); + } + + if (data.pressunit !== 'hPa') { + displayUnits.press = data.pressunit; + setRadioCheck('rad_unitsPress', data.pressunit); + setBaroUnits(data.pressunit); + } + + if (data.windunit !== 'km/h') { + displayUnits.wind = data.windunit; + setRadioCheck('rad_unitsWind', data.windunit); + setWindUnits(data.windunit); + } + + if (data.rainunit !== 'mm') { + displayUnits.rain = data.rainunit; + setRadioCheck('rad_unitsRain', data.rainunit); + setRainUnits(false); + } + + if (config.showSolarGauge && typeof data.SolarTM !== 'undefined' && gaugeSolar) { + gaugeSolar.gauge.setMaxMeasuredValueVisible(true); + } + + if (config.showCloudGauge && data.cloudbaseunit !== 'm') { + displayUnits.cloud = data.cloudbaseunit; + setRadioCheck('rad_unitsCloud', data.cloudbaseunit); + setCloudBaseUnits(false); + } + + // set the script version on the page + $('#scriptVer').html(config.scriptVer); + // set the version information from the station + $('#programVersion').html(data.version); + $('#programBuild').html(data.build); + $('#programName').html(programLink[config.weatherProgram]); + + if (config.showPopupData) { + // now initialise the pop-up script and download the trend /images + // - has to be done here as doFirst may remove elements from the page + // - and we delay the download of the /images speeding up page display + ddimgtooltip.init('[id^="tip_"]'); + // Are we running on a phone device (or really low res screen)? + if ($(window).width() < 480) { + $('.ddimgtooltip').filter(':hidden').width('200px'); + } + } + + if (config.showPopupData && config.showPopupGraphs) { + // now download the trend /images + // - has to be done here as doFirst may remove elements from the page + // - and we delay the download of the /images speeding up page display + // + $('#imgtip0_img').attr('src', config.imgPathURL + config.tipImgs[0][0] + cacheDefeat); + if (gaugeDew) { + $('#imgtip1_img').attr('src', config.imgPathURL + config.tipImgs[1][gaugeDew.data.popupImg] + cacheDefeat); + } + $('#imgtip2_img').attr('src', config.imgPathURL + config.tipImgs[2] + cacheDefeat); + $('#imgtip3_img').attr('src', config.imgPathURL + config.tipImgs[3] + cacheDefeat); + $('#imgtip4_img').attr('src', config.imgPathURL + config.tipImgs[4][0] + cacheDefeat); + $('#imgtip5_img').attr('src', config.imgPathURL + config.tipImgs[5] + cacheDefeat); + $('#imgtip6_img').attr('src', config.imgPathURL + config.tipImgs[6] + cacheDefeat); + $('#imgtip7_img').attr('src', config.imgPathURL + config.tipImgs[7] + cacheDefeat); + $('#imgtip8_img').attr('src', config.imgPathURL + config.tipImgs[8] + cacheDefeat); + $('#imgtip9_img').attr('src', config.imgPathURL + config.tipImgs[9] + cacheDefeat); + $('#imgtip10_img').attr('src', config.imgPathURL + config.tipImgs[10] + cacheDefeat); + $('#imgtip11_img').attr('src', config.imgPathURL + config.tipImgs[11] + cacheDefeat); + // start a timer for popup graphic updates + setInterval( + function timeout() { + $.publish('gauges.graphUpdate', '?' + (new Date()).getTime().toString()); + }, + config.graphUpdateTime * 60 * 1000 + ); + } + + firstRun = false; + }, + + // + // createTempSections() creates an array of gauge sections appropriate for Celsius or Fahrenheit scales + // + createTempSections = function (celsius) { + var section; + if (celsius) { + section = [ + steelseries.Section(-100, -35, 'rgba(195, 92, 211, 0.4)'), + steelseries.Section(-35, -30, 'rgba(139, 74, 197, 0.4)'), + steelseries.Section(-30, -25, 'rgba(98, 65, 188, 0.4)'), + steelseries.Section(-25, -20, 'rgba(62, 66, 185, 0.4)'), + steelseries.Section(-20, -15, 'rgba(42, 84, 194, 0.4)'), + steelseries.Section(-15, -10, 'rgba(25, 112, 210, 0.4)'), + steelseries.Section(-10, -5, 'rgba(9, 150, 224, 0.4)'), + steelseries.Section(-5, 0, 'rgba(2, 170, 209, 0.4)'), + steelseries.Section(0, 5, 'rgba(0, 162, 145, 0.4)'), + steelseries.Section(5, 10, 'rgba(0, 158, 122, 0.4)'), + steelseries.Section(10, 15, 'rgba(54, 177, 56, 0.4)'), + steelseries.Section(15, 20, 'rgba(111, 202, 56, 0.4)'), + steelseries.Section(20, 25, 'rgba(248, 233, 45, 0.4)'), + steelseries.Section(25, 30, 'rgba(253, 142, 42, 0.4)'), + steelseries.Section(30, 40, 'rgba(236, 45, 45, 0.4)'), + steelseries.Section(40, 100, 'rgba(245, 109, 205, 0.4)') + ]; + } else { + section = [ + steelseries.Section(-200, -30, 'rgba(195, 92, 211, 0.4)'), + steelseries.Section(-30, -25, 'rgba(139, 74, 197, 0.4)'), + steelseries.Section(-25, -15, 'rgba(98, 65, 188, 0.4)'), + steelseries.Section(-15, -5, 'rgba(62, 66, 185, 0.4)'), + steelseries.Section(-5, 5, 'rgba(42, 84, 194, 0.4)'), + steelseries.Section(5, 15, 'rgba(25, 112, 210, 0.4)'), + steelseries.Section(15, 25, 'rgba(9, 150, 224, 0.4)'), + steelseries.Section(25, 32, 'rgba(2, 170, 209, 0.4)'), + steelseries.Section(32, 40, 'rgba(0, 162, 145, 0.4)'), + steelseries.Section(40, 50, 'rgba(0, 158, 122, 0.4)'), + steelseries.Section(50, 60, 'rgba(54, 177, 56, 0.4)'), + steelseries.Section(60, 70, 'rgba(111, 202, 56, 0.4)'), + steelseries.Section(70, 80, 'rgba(248, 233, 45, 0.4)'), + steelseries.Section(80, 90, 'rgba(253, 142, 42, 0.4)'), + steelseries.Section(90, 110, 'rgba(236, 45, 45, 0.4)'), + steelseries.Section(110, 200, 'rgba(245, 109, 205, 0.4)') + ]; + } + return section; + }, + + // + // createRainSections() returns an array of section highlights for the Rain Rate gauge + // + /* + Assumes 'standard' descriptive limits from UK met office: + < 0.25 mm/hr - Very light rain + 0.25mm/hr to 1.0mm/hr - Light rain + 1.0 mm/hr to 4.0 mm/hr - Moderate rain + 4.0 mm/hr to 16.0 mm/hr - Heavy rain + 16.0 mm/hr to 50 mm/hr - Very heavy rain + > 50.0 mm/hour - Extreme rain + + Roughly translated to the corresponding Inch rates + < 0.001 + 0.001 to 0.05 + 0.05 to 0.20 + 0.20 to 0.60 + 0.60 to 2.0 + > 2.0 + */ + createRainRateSections = function (metric) { + var factor = metric ? 1 : 1 / 25; + return [ + steelseries.Section(0, 0.25 * factor, 'rgba(0, 140, 0, 0.5)'), + steelseries.Section(0.25 * factor, 1 * factor, 'rgba(80, 192, 80, 0.5)'), + steelseries.Section(1 * factor, 4 * factor, 'rgba(150, 203, 150, 0.5)'), + steelseries.Section(4 * factor, 16 * factor, 'rgba(212, 203, 109, 0.5)'), + steelseries.Section(16 * factor, 50 * factor, 'rgba(225, 155, 105, 0.5)'), + steelseries.Section(50 * factor, 1000 * factor, 'rgba(245, 86, 59, 0.5)') + ]; + }, + + // + // createRainFallSections()returns an array of section highlights for total rainfall in mm or inches + // + createRainfallSections = function (metric) { + var factor = metric ? 1 : 1 / 25; + return [ + steelseries.Section(0, 5 * factor, 'rgba(0, 250, 0, 1)'), + steelseries.Section(5 * factor, 10 * factor, 'rgba(0, 250, 117, 1)'), + steelseries.Section(10 * factor, 25 * factor, 'rgba(218, 246, 0, 1)'), + steelseries.Section(25 * factor, 40 * factor, 'rgba(250, 186, 0, 1)'), + steelseries.Section(40 * factor, 50 * factor, 'rgba(250, 95, 0, 1)'), + steelseries.Section(50 * factor, 65 * factor, 'rgba(250, 0, 0, 1)'), + steelseries.Section(65 * factor, 75 * factor, 'rgba(250, 6, 80, 1)'), + steelseries.Section(75 * factor, 100 * factor, 'rgba(205, 18, 158, 1)'), + steelseries.Section(100 * factor, 125 * factor, 'rgba(0, 0, 250, 1)'), + steelseries.Section(125 * factor, 500 * factor, 'rgba(0, 219, 212, 1)') + ]; + }, + + // + // createRainfallGradient() returns an array of SS colours for continuous gradient colouring of the total rainfall LED gauge + // + createRainfallGradient = function (metric) { + var grad = new steelseries.gradientWrapper( + 0, + (metric ? 100 : 4), + [0, 0.1, 0.62, 1], + [ + new steelseries.rgbaColor(15, 148, 0, 1), + new steelseries.rgbaColor(213, 213, 0, 1), + new steelseries.rgbaColor(213, 0, 25, 1), + new steelseries.rgbaColor(250, 0, 0, 1) + ] + ); + return grad; + }, + + // + // createClousBaseSections() returns an array of section highlights for the Cloud Base gauge + // + createCloudBaseSections = function (metric) { + var section; + if (metric) { + section = [ + steelseries.Section(0, 150, 'rgba(245, 86, 59, 0.5)'), + steelseries.Section(150, 300, 'rgba(225, 155, 105, 0.5)'), + steelseries.Section(300, 750, 'rgba(212, 203, 109, 0.5)'), + steelseries.Section(750, 1000, 'rgba(150, 203, 150, 0.5)'), + steelseries.Section(1000, 1500, 'rgba(80, 192, 80, 0.5)'), + steelseries.Section(1500, 2500, 'rgba(0, 140, 0, 0.5)'), + steelseries.Section(2500, 5500, 'rgba(19, 103, 186, 0.5)') + ]; + } else { + section = [ + steelseries.Section(0, 500, 'rgba(245, 86, 59, 0.5)'), + steelseries.Section(500, 1000, 'rgba(225, 155, 105, 0.5)'), + steelseries.Section(1000, 2500, 'rgba(212, 203, 109, 0.5)'), + steelseries.Section(2500, 3500, 'rgba(150, 203, 150, 0.5)'), + steelseries.Section(3500, 5500, 'rgba(80, 192, 80, 0.5)'), + steelseries.Section(5500, 8500, 'rgba(0, 140, 0, 0.5)'), + steelseries.Section(8500, 18000, 'rgba(19, 103, 186, 0.5)') + ]; + } + return section; + }, + + // + // --------------- Helper functions ------------------ + // + + // + // getord() converts a value in degrees (0-360) into a localised compass point (N, ENE, NE, etc) + // + getord = function (deg) { + if (deg === 0) { + // Special case, 0=No wind, 360=North + return strings.calm; + } else { + return (strings.coords[Math.floor((deg + 11.25) / 22.5) % 16]); + } + }, + + // + // getUrlParam() extracts the named parameter from the current page URL + // + getUrlParam = function (paramName) { + var name, regexS, regex, results; + name = paramName.replace(/(\[|\])/g, '\\$1'); + regexS = '[\\?&]' + name + '=([^&#]*)'; + regex = new RegExp(regexS); + results = regex.exec(window.location.href); + if (results === null) { + return ''; + } else { + return results[1]; + } + }, + + // + // extractDecimal() returns a decimal number from a string, the decimal point can be either a dot or a comma + // it ignores any text such as pre/appended units + // + extractDecimal = function (str, errVal) { + try { + return (/[-+]?[0-9]+\.?[0-9]*/).exec(str.replace(',', '.'))[0]; + } catch (e) { + // error condition + return errVal || -9999; + } + }, + + // + // extractInteger() returns an integer from a string + // it ignores any text such as pre/appended units + // + extractInteger = function (str, errVal) { + try { + return (/[-+]?[0-9]+/).exec(str)[0]; + } catch (e) { + // error condition + return errVal || -9999; + } + }, + + // + // tempTrend() converts a temperature trend value into a localised string, or +1, 0, -1 depending on the value of bTxt + // + tempTrend = function (trend, units, bTxt) { + // Scale is over 3 hours, in Celsius + var val = trend * 3 * (units[1] === 'C' ? 1 : (5 / 9)), + ret; + if (trend === -9999) { + ret = (bTxt ? '--' : trend); + } else if (val > 5) { + ret = (bTxt ? strings.RisingVeryRapidly : 1); + } else if (val > 3) { + ret = (bTxt ? strings.RisingQuickly : 1); + } else if (val > 1) { + ret = (bTxt ? strings.Rising : 1); + } else if (val > 0.5) { + ret = (bTxt ? strings.RisingSlowly : 1); + } else if (val >= -0.5) { + ret = (bTxt ? strings.Steady : 0); + } else if (val >= -1) { + ret = (bTxt ? strings.FallingSlowly : -1); + } else if (val >= -3) { + ret = (bTxt ? strings.Falling : -1); + } else if (val >= -5) { + ret = (bTxt ? strings.FallingQuickly : -1); + } else { + ret = (bTxt ? strings.FallingVeryRapidly : -1); + } + return ret; + }, + + // + // baroTrend() converts a pressure trend value into a localised string, or +1, 0, -1 depending on the value of bTxt + // + baroTrend = function (trend, units, bTxt) { + var val = trend * 3, + ret; + // The terms below are the UK Met Office terms for a 3 hour change in hPa + // trend is supplied as an hourly change, so multiply by 3 + if (units === 'inHg') { + val *= 33.8639; + } else if (units === 'kPa') { + val *= 10; + // assume everything else is hPa or mb, could be dangerous! + } + if (trend === -9999) { + ret = (bTxt ? '--' : trend); + } else if (val > 6.0) { + ret = (bTxt ? strings.RisingVeryRapidly : 1); + } else if (val > 3.5) { + ret = (bTxt ? strings.RisingQuickly : 1); + } else if (val > 1.5) { + ret = (bTxt ? strings.Rising : 1); + } else if (val > 0.1) { + ret = (bTxt ? strings.RisingSlowly : 1); + } else if (val >= -0.1) { + ret = (bTxt ? strings.Steady : 0); + } else if (val >= -1.5) { + ret = (bTxt ? strings.FallingSlowly : -1); + } else if (val >= -3.5) { + ret = (bTxt ? strings.Falling : -1); + } else if (val >= -6.0) { + ret = (bTxt ? strings.FallingQuickly : -1); + } else { + ret = (bTxt ? strings.FallingVeryRapidly : -1); + } + return ret; + }, + + // + // getMinTemp() returns the lowest temperature today for gauge scaling + // + getMinTemp = function (deflt) { + return Math.min( + extractDecimal(data.tempTL, deflt), + extractDecimal(data.dewpointTL, deflt), + extractDecimal(data.apptempTL, deflt), + extractDecimal(data.feelslikeTL, deflt), + extractDecimal(data.wchillTL, deflt)); + }, + + // + // getMaxTemp() returns the highest temperature today for gauge scaling + // + getMaxTemp = function (deflt) { + return Math.max( + extractDecimal(data.tempTH, deflt), + extractDecimal(data.apptempTH, deflt), + extractDecimal(data.feelslikeTH, deflt), + extractDecimal(data.heatindexTH, deflt), + extractDecimal(data.humidex, deflt)); + }, + + // Celsius to Fahrenheit + c2f = function (val) { + return (extractDecimal(val) * 9 / 5 + 32).toFixed(1); + }, + // Fahrenheit to Celsius + f2c = function (val) { + return ((extractDecimal(val) - 32) * 5 / 9).toFixed(1); + }, + // kph to ms + kmh2ms = function (val) { + return (extractDecimal(val) * 0.2778).toFixed(1); + }, + // ms to kph + ms2kmh = function (val) { + return (extractDecimal(val) * 3.6).toFixed(1); + }, + kmh2ms = function (val) { + return (extractDecimal(val) / 3.6).toFixed(1); + }, + // mm to inches + mm2in = function (val) { + return (extractDecimal(val) / 25.4).toFixed(2); + }, + // inches to mm + in2mm = function (val) { + return (extractDecimal(val) * 25.4).toFixed(1); + }, + // miles to km + miles2km = function (val) { + return (extractDecimal(val) * 1.609344).toFixed(1); + }, + // nautical miles to km + nmiles2km = function (val) { + return (extractDecimal(val) * 1.85200).toFixed(1); + }, + // km to miles + km2miles = function (val) { + return (extractDecimal(val) / 1.609344).toFixed(1); + }, + // km to nautical miles + km2nmiles = function (val) { + return (extractDecimal(val) / 1.85200).toFixed(1); + }, + // hPa to inHg (@0°C) + hpa2inhg = function (val, decimals) { + return (extractDecimal(val) * 0.029528744).toFixed(decimals || 3); + }, + // inHg to hPa (@0°C) + inhg2hpa = function (val) { + return (extractDecimal(val) / 0.029528744).toFixed(1); + }, + // kPa to hPa + kpa2hpa = function (val) { + return (extractDecimal(val) * 10).toFixed(1); + }, + // hPa to kPa + hpa2kpa = function (val, decimals) { + return (extractDecimal(val) / 10).toFixed(decimals || 2); + }, + // m to ft + m2ft = function (val) { + return (val * 3.2808399).toFixed(0); + }, + // ft to m + ft2m = function (val) { + return (val / 3.2808399).toFixed(0); + }, + + // + // setCookie() writes the 'obj' in cookie 'name' for persistent storage + // + setCookie = function (name, obj) { + var date = new Date(), + expires; + // cookies valid for 1 year + date.setYear(date.getFullYear() + 1); + expires = '; expires=' + date.toGMTString(); + document.cookie = name + '=' + encodeURIComponent(JSON.stringify(obj)) + expires; + }, + + // + // getCookie() reads the value of cookie 'name' from persistent storage + // + getCookie = function (name) { + var i, x, y, + ret = null, + arrCookies = document.cookie.split(';'); + + for (i = arrCookies.length; i--;) { + x = arrCookies[i].split('='); + if (x[0].trim() === name) { + try { + y = decodeURIComponent(x[1]); + } catch (e) { + y = x[1]; + } + ret = JSON.parse(unescape(y)); + break; + } + } + return ret; + }, + + // + // setRadioCheck() sets the desired value of the HTML radio buttons to be selected + // + setRadioCheck = function (obj, val) { + $('input:radio[name="' + obj + '"]').filter('[value="' + val + '"]').prop('checked', true); + }, + + // + // convTempData() converts all the temperature values using the supplied conversion function + // + convTempData = function (convFunc) { + data.apptemp = convFunc(data.apptemp); + data.apptempTH = convFunc(data.apptempTH); + data.apptempTL = convFunc(data.apptempTL); + data.feelslike = convFunc(data.feelslike); + data.feelslikeTH = convFunc(data.feelslikeTH); + data.feelslikeTL = convFunc(data.feelslikeTL); + data.dew = convFunc(data.dew); + data.dewpointTH = convFunc(data.dewpointTH); + data.dewpointTL = convFunc(data.dewpointTL); + data.heatindex = convFunc(data.heatindex); + data.heatindexTH = convFunc(data.heatindexTH); + data.humidex = convFunc(data.humidex); + data.intemp = convFunc(data.intemp); + if (data.intempTL && data.intempTH) { + data.intempTL = convFunc(data.intempTL); + data.intempTH = convFunc(data.intempTH); + } + data.temp = convFunc(data.temp); + data.tempTH = convFunc(data.tempTH); + data.tempTL = convFunc(data.tempTL); + data.wchill = convFunc(data.wchill); + data.wchillTL = convFunc(data.wchillTL); + if (convFunc === c2f) { + data.temptrend = (+extractDecimal(data.temptrend) * 9 / 5).toFixed(1); + data.tempunit = '°F'; + } else { + data.temptrend = (+extractDecimal(data.temptrend) * 5 / 9).toFixed(1); + data.tempunit = '°C'; + } + }, + + // + // convRainData() converts all the rain data units using the supplied conversion function + // + convRainData = function (convFunc) { + data.rfall = convFunc(data.rfall); + data.rrate = convFunc(data.rrate); + data.rrateTM = convFunc(data.rrateTM); + data.hourlyrainTH = convFunc(data.hourlyrainTH); + data.rainunit = convFunc === mm2in ? 'in' : 'mm'; + }, + + // + // convWindData() converts all the wind values using the supplied conversion function + // + convWindData = function (from, to) { + var fromFunc1, toFunc1, + fromFunc2, toFunc2, + dummy = function (val) { + return val; + }; + + // convert to km/h & km + switch (from) { + case 'mph': + fromFunc1 = miles2km; + fromFunc2 = miles2km; + break; + case 'kts': + fromFunc1 = nmiles2km; + fromFunc2 = nmiles2km; + break; + case 'm/s': + fromFunc1 = ms2kmh; + fromFunc2 = dummy; + break; + case 'km/h': + // falls through + default: + fromFunc1 = dummy; + fromFunc2 = dummy; + } + // conversion function from km to required units + switch (to) { + case 'mph': + toFunc1 = km2miles; + toFunc2 = km2miles; + displayUnits.windrun = 'miles'; + break; + case 'kts': + toFunc1 = km2nmiles; + toFunc2 = km2nmiles; + displayUnits.windrun = 'n.miles'; + break; + case 'm/s': + toFunc1 = kmh2ms; + toFunc2 = dummy; + displayUnits.windrun = 'km'; + break; + case 'km/h': + // falls through + default: + toFunc1 = dummy; + toFunc2 = dummy; + displayUnits.windrun = 'km'; + } + // do the conversions + data.wgust = toFunc1(fromFunc1(data.wgust)); + data.wgustTM = toFunc1(fromFunc1(data.wgustTM)); + data.windTM = toFunc1(fromFunc1(data.windTM)); + data.windrun = toFunc2(fromFunc2(data.windrun)); + data.wlatest = toFunc1(fromFunc1(data.wlatest)); + data.wspeed = toFunc1(fromFunc1(data.wspeed)); + data.windunit = to; + }, + + // + // convBaroData() converts all the pressure values using the supplied conversion function + // + convBaroData = function (from, to) { + var fromFunc, toFunc, + dummy = function (val) { + return val; + }; + + // convert to hPa + switch (from) { + case 'hPa': + // falls through + case 'mb': + fromFunc = dummy; + break; + case 'inHg': + fromFunc = inhg2hpa; + break; + case 'kPa': + fromFunc = kpa2hpa; + break; + // no default + } + // convert to required units + switch (to) { + case 'hPa': + // falls through + case 'mb': + toFunc = dummy; + break; + case 'inHg': + toFunc = hpa2inhg; + break; + case 'kPa': + toFunc = hpa2kpa; + break; + // no default + } + + data.press = toFunc(fromFunc(data.press)); + data.pressH = toFunc(fromFunc(data.pressH)); + data.pressL = toFunc(fromFunc(data.pressL)); + data.pressTH = toFunc(fromFunc(data.pressTH)); + data.pressTL = toFunc(fromFunc(data.pressTL)); + data.presstrendval = toFunc(fromFunc(data.presstrendval), 3); + data.pressunit = to; + }, + + // + // convCloudBaseData() converts all the cloud base data units using the supplied conversion function + // + convCloudBaseData = function (convFunc) { + data.cloudbasevalue = convFunc(data.cloudbasevalue); + data.cloudbaseunit = convFunc === m2ft ? 'ft' : 'm'; + }, + + // + // setUnits() Main data conversion routine, calls all the setXXXX() sub-routines + // + setUnits = function (radio) { + var sel = radio.value; + + userUnitsSet = true; + + switch (sel) { + // == Temperature == + case 'C': + displayUnits.temp = sel; + if (data.tempunit[1] !== sel) { + setTempUnits(true); + convTempData(f2c); + if (gaugeTemp) { gaugeTemp.update(); } + if (gaugeDew) { gaugeDew.update(); } + } + break; + case 'F': + displayUnits.temp = sel; + if (data.tempunit[1] !== sel) { + setTempUnits(false); + convTempData(c2f); + if (gaugeTemp) { gaugeTemp.update(); } + if (gaugeDew) { gaugeDew.update(); } + } + break; + // == Rainfall == + case 'mm': + displayUnits.rain = sel; + if (data.rainunit !== sel) { + setRainUnits(true); + convRainData(in2mm); + if (gaugeRain) { gaugeRain.update(); } + if (gaugeRRate) { gaugeRRate.update(); } + } + break; + case 'in': + displayUnits.rain = sel; + if (data.rainunit !== sel) { + setRainUnits(false); + convRainData(mm2in); + if (gaugeRain) { gaugeRain.update(); } + if (gaugeRRate) { gaugeRRate.update(); } + } + break; + // == Pressure == + case 'hPa': + // falls through + case 'inHg': + // falls through + case 'mb': + // falls through + case 'kPa': + displayUnits.press = sel; + if (data.pressunit !== sel) { + convBaroData(data.pressunit, sel); + setBaroUnits(sel); + if (gaugeBaro) { gaugeBaro.update(); } + } + break; + // == Wind speed == + case 'mph': + // falls through + case 'kts': + // falls through + case 'm/s': + // falls through + case 'km/h': + displayUnits.wind = sel; + if (data.windunit !== sel) { + convWindData(data.windunit, sel); + setWindUnits(sel); + if (gaugeWind) { gaugeWind.update(); } + if (gaugeDir) { gaugeDir.update(); } + if (gaugeRose) { gaugeRose.update(); } + } + break; + // == CloudBase == + case 'm': + displayUnits.cloud = sel; + if (data.cloudbaseunit !== sel) { + setCloudBaseUnits(true); + convCloudBaseData(ft2m); + if (gaugeCloud) { gaugeCloud.update(); } + } + break; + case 'ft': + displayUnits.cloud = sel; + if (data.cloudbaseunit !== sel) { + setCloudBaseUnits(false); + convCloudBaseData(m2ft); + if (gaugeCloud) { gaugeCloud.update(); } + } + break; + // no default + } + if (config.useCookies) { + setCookie('units', displayUnits); + } + }, + + setTempUnits = function (celsius) { + if (celsius) { + data.tempunit = '°C'; + if (gaugeTemp) { + gaugeTemp.data.sections = createTempSections(true); + gaugeTemp.data.minValue = gaugeGlobals.tempScaleDefMinC; + gaugeTemp.data.maxValue = gaugeGlobals.tempScaleDefMaxC; + } + if (gaugeDew) { + gaugeDew.data.sections = createTempSections(true); + gaugeDew.data.minValue = gaugeGlobals.tempScaleDefMinC; + gaugeDew.data.maxValue = gaugeGlobals.tempScaleDefMaxC; + } + } else { + data.tempunit = '°F'; + if (gaugeTemp) { + gaugeTemp.data.sections = createTempSections(false); + gaugeTemp.data.minValue = gaugeGlobals.tempScaleDefMinF; + gaugeTemp.data.maxValue = gaugeGlobals.tempScaleDefMaxF; + } + if (gaugeDew) { + gaugeDew.data.sections = createTempSections(false); + gaugeDew.data.minValue = gaugeGlobals.tempScaleDefMinF; + gaugeDew.data.maxValue = gaugeGlobals.tempScaleDefMaxF; + } + } + if (gaugeTemp) { + gaugeTemp.gauge.setUnitString(data.tempunit); + gaugeTemp.gauge.setSection(gaugeTemp.data.sections); + } + if (gaugeDew) { + gaugeDew.gauge.setUnitString(data.tempunit); + gaugeDew.gauge.setSection(gaugeTemp.data.sections); + } + }, + + setRainUnits = function (mm) { + if (mm) { + data.rainunit = 'mm'; + if (gaugeRain) { + gaugeRain.data.lcdDecimals = 1; + gaugeRain.data.scaleDecimals = 1; + gaugeRain.data.labelNumberFormat = gaugeGlobals.labelFormat; + gaugeRain.data.sections = (gaugeGlobals.rainUseSectionColours ? createRainfallSections(true) : []); + gaugeRain.data.maxValue = gaugeGlobals.rainScaleDefMaxmm; + gaugeRain.data.grad = (gaugeGlobals.rainUseGradientColours ? createRainfallGradient(true) : null); + } + if (gaugeRRate) { + gaugeRRate.data.lcdDecimals = 1; + gaugeRRate.data.scaleDecimals = 0; + gaugeRRate.data.labelNumberFormat = gaugeGlobals.labelFormat; + gaugeRRate.data.sections = createRainRateSections(true); + gaugeRRate.data.maxValue = gaugeGlobals.rainRateScaleDefMaxmm; + } + } else { + data.rainunit = 'in'; + if (gaugeRain) { + gaugeRain.data.lcdDecimals = 2; + gaugeRain.data.scaleDecimals = gaugeGlobals.rainScaleDefMaxIn < 1 ? 2 : 1; + gaugeRain.data.labelNumberFormat = steelseries.LabelNumberFormat.FRACTIONAL; + gaugeRain.data.sections = (gaugeGlobals.rainUseSectionColours ? createRainfallSections(false) : []); + gaugeRain.data.maxValue = gaugeGlobals.rainScaleDefMaxIn; + gaugeRain.data.grad = (gaugeGlobals.rainUseGradientColours ? createRainfallGradient(false) : null); + } + if (gaugeRRate) { + gaugeRRate.data.lcdDecimals = 2; + gaugeRRate.data.scaleDecimals = gaugeGlobals.rainRateScaleDefMaxIn < 1 ? 2 : 1; + gaugeRRate.data.labelNumberFormat = steelseries.LabelNumberFormat.FRACTIONAL; + gaugeRRate.data.sections = createRainRateSections(false); + gaugeRRate.data.maxValue = gaugeGlobals.rainRateScaleDefMaxIn; + } + } + if (gaugeRain) { + gaugeRain.data.value = 0; + gaugeRain.gauge.setUnitString(data.rainunit); + gaugeRain.gauge.setSection(gaugeRain.data.sections); + gaugeRain.gauge.setGradient(gaugeRain.data.grad); + gaugeRain.gauge.setFractionalScaleDecimals(gaugeRain.data.scaleDecimals); + gaugeRain.gauge.setLabelNumberFormat(gaugeRain.data.labelNumberFormat); + gaugeRain.gauge.setLcdDecimals(gaugeRain.data.lcdDecimals); + gaugeRain.gauge.setValue(0); + gaugeRain.gauge.setMaxValue(gaugeRain.data.maxValue); + } + if (gaugeRRate) { + gaugeRRate.data.value = 0; + gaugeRRate.gauge.setUnitString(data.rainunit + '/h'); + gaugeRRate.gauge.setSection(gaugeRRate.data.sections); + gaugeRRate.gauge.setFractionalScaleDecimals(gaugeRRate.data.scaleDecimals); + gaugeRRate.gauge.setLabelNumberFormat(gaugeRRate.data.labelNumberFormat); + gaugeRRate.gauge.setLcdDecimals(gaugeRRate.data.lcdDecimals); + gaugeRRate.gauge.setValue(0); + gaugeRRate.gauge.setMaxValue(gaugeRRate.data.maxValue); + } + }, + + setWindUnits = function (to) { + var maxVal; + data.windunit = to; + if (gaugeWind) { + // conversion function to required units + switch (to) { + case 'mph': + maxVal = gaugeGlobals.windScaleDefMaxMph; + break; + case 'kts': + maxVal = gaugeGlobals.windScaleDefMaxKts; + break; + case 'km/h': + maxVal = gaugeGlobals.windScaleDefMaxKmh; + break; + case 'm/s': + maxVal = gaugeGlobals.windScaleDefMaxMs; + break; + // no default + } + // set the gauges + gaugeWind.data.maxValue = maxVal; + gaugeWind.gauge.setUnitString(data.windunit); + gaugeWind.gauge.setValue(0); + } + if (gaugeRose) { + gaugeRose.setOdoTitle(strings[getWindrunUnits(data.windunit)]); + } + }, + + setBaroUnits = function (to) { + var minVal, maxVal; + + if (!gaugeBaro) { return; } + + // set to the required units + switch (to) { + case 'hPa': + // falls through + case 'mb': + minVal = gaugeGlobals.baroScaleDefMinhPa; + maxVal = gaugeGlobals.baroScaleDefMaxhPa; + gaugeBaro.data.lcdDecimals = 1; + gaugeBaro.data.scaleDecimals = 0; + gaugeBaro.data.labelNumberFormat = gaugeGlobals.labelFormat; + break; + case 'inHg': + minVal = gaugeGlobals.baroScaleDefMininHg; + maxVal = gaugeGlobals.baroScaleDefMaxinHg; + gaugeBaro.data.lcdDecimals = 2; + gaugeBaro.data.scaleDecimals = 1; + gaugeBaro.data.labelNumberFormat = steelseries.LabelNumberFormat.FRACTIONAL; + break; + case 'kPa': + minVal = gaugeGlobals.baroScaleDefMinkPa; + maxVal = gaugeGlobals.baroScaleDefMaxkPa; + gaugeBaro.data.lcdDecimals = 2; + gaugeBaro.data.scaleDecimals = 1; + gaugeBaro.data.labelNumberFormat = steelseries.LabelNumberFormat.FRACTIONAL; + break; + // no default + } + + data.pressunit = to; + gaugeBaro.gauge.setUnitString(to); + gaugeBaro.gauge.setLcdDecimals(gaugeBaro.data.lcdDecimals); + gaugeBaro.gauge.setFractionalScaleDecimals(gaugeBaro.data.scaleDecimals); + gaugeBaro.gauge.setLabelNumberFormat(gaugeBaro.data.labelNumberFormat); + gaugeBaro.data.minValue = minVal; + gaugeBaro.data.maxValue = maxVal; + gaugeBaro.data.value = gaugeBaro.data.minValue; + }, + + setCloudBaseUnits = function (m) { + if (!gaugeCloud) { return; } + + if (m) { + gaugeCloud.data.sections = createCloudBaseSections(true); + gaugeCloud.data.maxValue = gaugeGlobals.cloudScaleDefMaxm; + } else { + gaugeCloud.data.sections = createCloudBaseSections(false); + gaugeCloud.data.maxValue = gaugeGlobals.cloudScaleDefMaxft; + } + gaugeCloud.data.value = 0; + gaugeCloud.gauge.setUnitString(m ? strings.metres : strings.feet); + gaugeCloud.gauge.setSection(gaugeCloud.data.sections); + }, + + // + // setLang() switches the HTML page language set, called by changeLang() in language.js + // + setLang = function (newLang) { + // reset to the new language + strings = newLang; + + // temperature + if (gaugeTemp) { + if (config.showIndoorTempHum) { + if ($('#rad_temp1').is(':checked')) { + gaugeTemp.data.title = strings.temp_title_out; + } else { + gaugeTemp.data.title = strings.temp_title_in; + } + } else { + gaugeTemp.data.title = strings.temp_title_out; + } + gaugeTemp.gauge.setTitleString(gaugeTemp.data.title); + if (data.ver) { gaugeTemp.update(); } + } + if (gaugeDew) { + switch ($('input[name="rad_dew"]:checked').val()) { + case 'dew': + gaugeDew.data.title = strings.dew_title; + break; + case 'app': + gaugeDew.data.title = strings.apptemp_title; + break; + case 'feel': + gaugeDew.data.title = strings.feel_title; + break; + case 'wnd': + gaugeDew.data.title = strings.chill_title; + break; + case 'hea': + gaugeDew.data.title = strings.heat_title; + break; + case 'hum': + gaugeDew.data.title = strings.humdx_title; + break; + // no default + } + gaugeDew.gauge.setTitleString(gaugeDew.data.title); + if (data.ver) { gaugeDew.update(); } + } + // rain + if (gaugeRain) { + gaugeRain.data.title = strings.rain_title; + gaugeRain.gauge.setTitleString(gaugeRain.data.title); + if (data.ver) { gaugeRain.update(); } + } + // rrate + if (gaugeRRate) { + gaugeRRate.data.title = strings.rrate_title; + gaugeRRate.gauge.setTitleString(gaugeRRate.data.title); + if (data.ver) { gaugeRRate.update(); } + } + // humidity + if (gaugeHum) { + if (config.showIndoorTempHum) { + if ($('#rad_hum1').is(':checked')) { + gaugeHum.data.title = strings.hum_title_out; + } else { + gaugeHum.data.title = strings.hum_title_in; + } + } else { + gaugeHum.data.title = strings.hum_title_out; + } + gaugeHum.gauge.setTitleString(gaugeHum.data.title); + if (data.ver) { gaugeHum.update(); } + } + // barometer + if (gaugeBaro) { + gaugeBaro.data.title = strings.baro_title; + gaugeBaro.gauge.setTitleString(gaugeBaro.data.title); + if (data.ver) { gaugeBaro.update(); } + } + // wind + if (gaugeWind) { + gaugeWind.data.title = strings.wind_title; + gaugeWind.gauge.setTitleString(gaugeWind.data.title); + if (data.ver) { gaugeWind.update(); } + } + if (gaugeDir) { + gaugeDir.gauge.setPointSymbols(strings.compass); + gaugeDir.data.titles = [strings.latest_web, strings.tenminavg_web]; + gaugeDir.gauge.setLcdTitleStrings(gaugeDir.data.titles); + if (data.ver) { gaugeDir.update(); } + } + if (gaugeUV) { + gaugeUV.gauge.setTitleString(strings.uv_title); + if (data.ver) { gaugeUV.update(); } + } + if (gaugeSolar) { + gaugeSolar.gauge.setTitleString(strings.solar_title); + if (data.ver) { gaugeSolar.update(); } + } + if (gaugeRose) { + gaugeRose.setTitle(strings.windrose); + gaugeRose.setCompassStrings(strings.compass); + gaugeRose.setOdoTitle(strings[getWindrunUnits(displayUnits.wind)]); + if (data.ver) { gaugeRose.update(); } + } + if (gaugeCloud) { + // Cloudbase + gaugeCloud.data.units = data.cloudunit === 'm' ? strings.metres : strings.feet; + gaugeCloud.gauge.setTitleString(strings.cloudbase_title); + gaugeCloud.gauge.setUnitString(gaugeCloud.data.units); + if (data.ver) { gaugeCloud.update(); } + } + }, + + // + // return windrun units based on the windspeed units + // + getWindrunUnits = function (spdUnits) { + var retVal; + switch (spdUnits) { + case 'mph': + retVal = 'miles'; + break; + case 'kts': + retVal = 'n_miles'; + break; + case 'km/h': + // falls through + case 'm/s': + // falls through + default: + retVal = 'km'; + break; + } + return retVal; + }, + + // + // performs a simple cloudbase calculation for those weather programs that don't supply it + // + calcCloudbase = function (temp, tempunit, dew, cloudbaseunit) { + var sprd = temp - dew; + var cb = sprd * (tempunit[1] === 'C' ? 400 : 227.3); // cloud base in feet + if (cloudbaseunit === 'm') { + cb = ft2m(cb); + } + return cb; + }, + + // + // create a shadow effect for the gauge using CSS + // + gaugeShadow = function (size) { + var offset = Math.floor(size * 0.015); + return { + 'box-shadow': offset + 'px ' + offset + 'px ' + offset + 'px ' + gaugeGlobals.shadowColour, + 'border-radius': Math.floor(size / 2) + 'px' + }; + }, + + // + // generate a colour gradient based on start and end values + // + gradient = function (startCol, endCol, fraction) { + var redOrigin, grnOrigin, bluOrigin, + gradientSizeRed, gradientSizeGrn, gradientSizeBlu; + + redOrigin = parseInt(startCol.substr(0, 2), 16); + grnOrigin = parseInt(startCol.substr(2, 2), 16); + bluOrigin = parseInt(startCol.substr(4, 2), 16); + + gradientSizeRed = parseInt(endCol.substr(0, 2), 16) - redOrigin; // Graduation Size Red + gradientSizeGrn = parseInt(endCol.substr(2, 2), 16) - grnOrigin; + gradientSizeBlu = parseInt(endCol.substr(4, 2), 16) - bluOrigin; + + return (redOrigin + (gradientSizeRed * fraction)).toFixed(0) + ',' + + (grnOrigin + (gradientSizeGrn * fraction)).toFixed(0) + ',' + + (bluOrigin + (gradientSizeBlu * fraction)).toFixed(0); + }, + // + // returns the next highest number in the step sequence + // + nextHighest = function (value, step) { + return +value == 0 ? step : Math.ceil(+value / step) * step; + }, + // + // returns the next lowest number in the step sequence + // + nextLowest = function (value, step) { + return +value == 0 ? -step : Math.floor(+value / step) * step; + }; + // ######################################################## + // End of gauges() var declarations + // ######################################################## + + // + // Execution starts here + // + + // test for canvas support before we do anything else, especially reference steelseries which will cause the script to abort! + if (!document.createElement('canvas').getContext) { + // failed, no canvas support detected + $('body').html(strings.canvasnosupport); + setTimeout(function () { + window.location = config.oldGauges; + }, 3000); + return false; + } else { + // + // Called when the document object has loaded + // This starts the whole script. + // + $(document).ready(function () { + // Kick it all off - false for web page, true for dashboard + init(config.dashboardMode); + }); + } + + return { + setLang: setLang, + setUnits: setUnits, + processData: processData, + config: config, + init: init + }; +}()); + +// =============================================================================================================================== +// =============================================================================================================================== +// =============================================================================================================================== + +/*! +* Image w/ description tooltip v2.0 - For FF1+ IE6+ Opr8+ +* Created: April 23rd, 2010. This notice must stay intact for usage +* Author: Dynamic Drive at http://www.dynamicdrive.com/ +* Visit http://www.dynamicdrive.com/ for full source code +* Modified: M Crossley June 2011, January 2012 +* v2.- +*/ +var ddimgtooltip; +ddimgtooltip = { + tiparray: (function () { + var style = { background: '#FFFFFF', color: 'black', border: '2px ridge darkblue' }, + i = 12, // set to number of tooltips required + tooltips = []; + for (; i--;) { + tooltips[i] = [null, ' ', style]; + } + return tooltips; + }()), + + tooltipoffsets: [20, -30], // additional x and y offset from mouse cursor for tooltips + + tipDelay: 1000, + + delayTimer: 0, + + tipprefix: 'imgtip', // tooltip DOM ID prefixes + + createtip: function ($, tipid, tipinfo) { + if ($('#' + tipid).length === 0) { // if this tooltip doesn't exist yet + return $('
') + .html( + ((tipinfo[1]) ? '
' + tipinfo[1] + '
' : '') + + (tipinfo[0] !== null ? '
' : '') + ) + .css(tipinfo[2] || {}) + .appendTo(document.body); + } + return null; + }, + + positiontooltip: function ($, $tooltip, e) { + var x = e.pageX + this.tooltipoffsets[0], + y = e.pageY + this.tooltipoffsets[1], + tipw = $tooltip.outerWidth(), + tiph = $tooltip.outerHeight(), + wWidth = $(window).width(), + wHght = $(window).height(), + dTop = $(document).scrollTop(); + + x = (x + tipw > $(document).scrollLeft() + wWidth) ? x - tipw - (ddimgtooltip.tooltipoffsets[0] * 2) : x; + y = (y + tiph > dTop + wHght) ? dTop + wHght - tiph - 10 : y; + // last ditch attempt to keep the graphs 'on page' + x = Math.max(x, 0); + if (tipw >= wWidth) { + $($tooltip.attr('id') + '_img').css({ width: wWidth - 20 }); + $('#' + $tooltip.attr('id') + '_img').css({ width: wWidth - 20 }); + y = e.pageY + 5; + } + $tooltip.css({ left: x, top: y }); + }, + + delaybox: function ($, $tooltip) { + if (this.showTips) { + ddimgtooltip.delayTimer = setTimeout( + function () { + ddimgtooltip.showbox($tooltip); + }, ddimgtooltip.tipDelay); + } + }, + + showbox: function (tooltip) { + if (this.showTips) { + // $(tooltip).show(); + $(tooltip).fadeIn(); + } + }, + + hidebox: function ($, $tooltip) { + clearTimeout(ddimgtooltip.delayTimer); + // $tooltip.hide(); + $tooltip.fadeOut(); + }, + + showTips: false, + + init: function (targetselector) { + var tiparray = ddimgtooltip.tiparray, + $targets = $(targetselector); + + if ($targets.length === 0) { + return; + } + $targets.each(function () { + var $target = $(this), + tipsuffix, tipid, + $tooltip; + var ind = ($target.attr('id').match(/_(\d+)/) || [])[1] || ''; // match d of attribute id='tip_d' + tipsuffix = parseInt(ind, 10); // get d as integer + tipid = this.tipid = ddimgtooltip.tipprefix + tipsuffix; // construct this tip's ID value and remember it + $tooltip = ddimgtooltip.createtip($, tipid, tiparray[tipsuffix]); + + $target.mouseenter(function (e) { + var $tooltip = $('#' + this.tipid); + // ddimgtooltip.showbox($, $tooltip, e); + ddimgtooltip.delaybox($, $tooltip, e); + }); + $target.mouseleave(function () { + var $tooltip = $('#' + this.tipid); + ddimgtooltip.hidebox($, $tooltip); + }); + $target.mousemove(function (e) { + var $tooltip = $('#' + this.tipid); + ddimgtooltip.positiontooltip($, $tooltip, e); + }); + if ($tooltip) { // add mouseenter to this tooltip (only if event hasn't already been added) + $tooltip.mouseenter(function () { + ddimgtooltip.hidebox($, $(this)); + }); + } + }); + } +}; + +// if String doesn't offer a .trim() method, add it +String.prototype.trim = String.prototype.trim || function trim() { + return this.replace(/^\s+|\s+$/g, ''); +};