UNPKG

freeboard-leaflet-realtime

Version:
1,049 lines (897 loc) 34.1 kB
// ┌────────────────────────────────────────────────────────────────────┐ \\ // │ F R E E B O A R D │ \\ // ├────────────────────────────────────────────────────────────────────┤ \\ // │ Copyright © 2013 Jim Heising (https://github.com/jheising) │ \\ // │ Copyright © 2013 Bug Labs, Inc. (http://buglabs.net) │ \\ // ├────────────────────────────────────────────────────────────────────┤ \\ // │ Licensed under the MIT license. │ \\ // └────────────────────────────────────────────────────────────────────┘ \\ (function () { var SPARKLINE_HISTORY_LENGTH = 100; var SPARKLINE_COLORS = ["#FF9900", "#FFFFFF", "#B3B4B4", "#6B6B6B", "#28DE28", "#13F7F9", "#E6EE18", "#C41204", "#CA3CB8", "#0B1CFB"]; function easeTransitionText(newValue, textElement, duration) { var currentValue = $(textElement).text(); if (currentValue == newValue) return; if ($.isNumeric(newValue) && $.isNumeric(currentValue)) { var numParts = newValue.toString().split('.'); var endingPrecision = 0; if (numParts.length > 1) { endingPrecision = numParts[1].length; } numParts = currentValue.toString().split('.'); var startingPrecision = 0; if (numParts.length > 1) { startingPrecision = numParts[1].length; } jQuery({transitionValue: Number(currentValue), precisionValue: startingPrecision}).animate({transitionValue: Number(newValue), precisionValue: endingPrecision}, { duration: duration, step: function () { $(textElement).text(this.transitionValue.toFixed(this.precisionValue)); }, done: function () { $(textElement).text(newValue); } }); } else { $(textElement).text(newValue); } } function addSparklineLegend(element, legend) { var legendElt = $("<div class='sparkline-legend'></div>"); for(var i=0; i<legend.length; i++) { var color = SPARKLINE_COLORS[i % SPARKLINE_COLORS.length]; var label = legend[i]; legendElt.append("<div class='sparkline-legend-value'><span style='color:" + color + "'>&#9679;</span>" + label + "</div>"); } element.empty().append(legendElt); freeboard.addStyle('.sparkline-legend', "margin:5px;"); freeboard.addStyle('.sparkline-legend-value', 'color:white; font:10px arial,san serif; float:left; overflow:hidden; width:50%;'); freeboard.addStyle('.sparkline-legend-value span', 'font-weight:bold; padding-right:5px;'); } function addValueToSparkline(element, value, legend) { var values = $(element).data().values; var valueMin = $(element).data().valueMin; var valueMax = $(element).data().valueMax; if (!values) { values = []; valueMin = undefined; valueMax = undefined; } var collateValues = function(val, plotIndex) { if(!values[plotIndex]) { values[plotIndex] = []; } if (values[plotIndex].length >= SPARKLINE_HISTORY_LENGTH) { values[plotIndex].shift(); } values[plotIndex].push(Number(val)); if(valueMin === undefined || val < valueMin) { valueMin = val; } if(valueMax === undefined || val > valueMax) { valueMax = val; } } if(_.isArray(value)) { _.each(value, collateValues); } else { collateValues(value, 0); } $(element).data().values = values; $(element).data().valueMin = valueMin; $(element).data().valueMax = valueMax; var tooltipHTML = '<span style="color: {{color}}">&#9679;</span> {{y}}'; var composite = false; _.each(values, function(valueArray, valueIndex) { $(element).sparkline(valueArray, { type: "line", composite: composite, height: "100%", width: "100%", fillColor: false, lineColor: SPARKLINE_COLORS[valueIndex % SPARKLINE_COLORS.length], lineWidth: 2, spotRadius: 3, spotColor: false, minSpotColor: "#78AB49", maxSpotColor: "#78AB49", highlightSpotColor: "#9D3926", highlightLineColor: "#9D3926", chartRangeMin: valueMin, chartRangeMax: valueMax, tooltipFormat: (legend && legend[valueIndex])?tooltipHTML + ' (' + legend[valueIndex] + ')':tooltipHTML }); composite = true; }); } var valueStyle = freeboard.getStyleString("values"); freeboard.addStyle('.widget-big-text', valueStyle + "font-size:75px;"); freeboard.addStyle('.tw-display', 'width: 100%; height:100%; display:table; table-layout:fixed;'); freeboard.addStyle('.tw-tr', 'display:table-row;'); freeboard.addStyle('.tw-tg', 'display:table-row-group;'); freeboard.addStyle('.tw-tc', 'display:table-caption;'); freeboard.addStyle('.tw-td', 'display:table-cell;'); freeboard.addStyle('.tw-value', valueStyle + 'overflow: hidden;' + 'display: inline-block;' + 'text-overflow: ellipsis;'); freeboard.addStyle('.tw-unit', 'display: inline-block;' + 'padding-left: 10px;' + 'padding-bottom: 1.1em;' + 'vertical-align: bottom;'); freeboard.addStyle('.tw-value-wrapper', 'position: relative;' + 'vertical-align: middle;' + 'height:100%;'); freeboard.addStyle('.tw-sparkline', 'height:20px;'); var textWidget = function (settings) { var self = this; var currentSettings = settings; var displayElement = $('<div class="tw-display"></div>'); var titleElement = $('<h2 class="section-title tw-title tw-td"></h2>'); var valueElement = $('<div class="tw-value"></div>'); var unitsElement = $('<div class="tw-unit"></div>'); var sparklineElement = $('<div class="tw-sparkline tw-td"></div>'); function updateValueSizing() { if(!_.isUndefined(currentSettings.units) && currentSettings.units != "") // If we're displaying our units { valueElement.css("max-width", (displayElement.innerWidth() - unitsElement.outerWidth(true)) + "px"); } else { valueElement.css("max-width", "100%"); } } this.render = function (element) { $(element).empty(); $(displayElement) .append($('<div class="tw-tr"></div>').append(titleElement)) .append($('<div class="tw-tr"></div>').append($('<div class="tw-value-wrapper tw-td"></div>').append(valueElement).append(unitsElement))) .append($('<div class="tw-tr"></div>').append(sparklineElement)); $(element).append(displayElement); updateValueSizing(); } this.onSettingsChanged = function (newSettings) { currentSettings = newSettings; var shouldDisplayTitle = (!_.isUndefined(newSettings.title) && newSettings.title != ""); var shouldDisplayUnits = (!_.isUndefined(newSettings.units) && newSettings.units != ""); if(newSettings.sparkline) { sparklineElement.attr("style", null); } else { delete sparklineElement.data().values; sparklineElement.empty(); sparklineElement.hide(); } if(shouldDisplayTitle) { titleElement.html((_.isUndefined(newSettings.title) ? "" : newSettings.title)); titleElement.attr("style", null); } else { titleElement.empty(); titleElement.hide(); } if(shouldDisplayUnits) { unitsElement.html((_.isUndefined(newSettings.units) ? "" : newSettings.units)); unitsElement.attr("style", null); } else { unitsElement.empty(); unitsElement.hide(); } var valueFontSize = 30; if(newSettings.size == "big") { valueFontSize = 75; if(newSettings.sparkline) { valueFontSize = 60; } } valueElement.css({"font-size" : valueFontSize + "px"}); updateValueSizing(); } this.onSizeChanged = function() { updateValueSizing(); } this.onCalculatedValueChanged = function (settingName, newValue) { if (settingName == "value") { if (currentSettings.animate) { easeTransitionText(newValue, valueElement, 500); } else { valueElement.text(newValue); } if (currentSettings.sparkline) { addValueToSparkline(sparklineElement, newValue); } } } this.onDispose = function () { } this.getHeight = function () { if (currentSettings.size == "big" || currentSettings.sparkline) { return 2; } else { return 1; } } this.onSettingsChanged(settings); }; freeboard.loadWidgetPlugin({ type_name: "text_widget", display_name: "Text", "external_scripts" : [ "plugins/thirdparty/jquery.sparkline.min.js" ], settings: [ { name: "title", display_name: "Title", type: "text" }, { name: "size", display_name: "Size", type: "option", options: [ { name: "Regular", value: "regular" }, { name: "Big", value: "big" } ] }, { name: "value", display_name: "Value", type: "calculated" }, { name: "sparkline", display_name: "Include Sparkline", type: "boolean" }, { name: "animate", display_name: "Animate Value Changes", type: "boolean", default_value: true }, { name: "units", display_name: "Units", type: "text" } ], newInstance: function (settings, newInstanceCallback) { newInstanceCallback(new textWidget(settings)); } }); var gaugeID = 0; freeboard.addStyle('.gauge-widget-wrapper', "width: 100%;text-align: center;"); freeboard.addStyle('.gauge-widget', "width:200px;height:160px;display:inline-block;"); var gaugeWidget = function (settings) { var self = this; var thisGaugeID = "gauge-" + gaugeID++; var titleElement = $('<h2 class="section-title"></h2>'); var gaugeElement = $('<div class="gauge-widget" id="' + thisGaugeID + '"></div>'); var gaugeObject; var rendered = false; var currentSettings = settings; function createGauge() { if (!rendered) { return; } gaugeElement.empty(); gaugeObject = new JustGage({ id: thisGaugeID, value: (_.isUndefined(currentSettings.min_value) ? 0 : currentSettings.min_value), min: (_.isUndefined(currentSettings.min_value) ? 0 : currentSettings.min_value), max: (_.isUndefined(currentSettings.max_value) ? 0 : currentSettings.max_value), label: currentSettings.units, showInnerShadow: false, valueFontColor: "#d3d4d4" }); } this.render = function (element) { rendered = true; $(element).append(titleElement).append($('<div class="gauge-widget-wrapper"></div>').append(gaugeElement)); createGauge(); } this.onSettingsChanged = function (newSettings) { if (newSettings.min_value != currentSettings.min_value || newSettings.max_value != currentSettings.max_value || newSettings.units != currentSettings.units) { currentSettings = newSettings; createGauge(); } else { currentSettings = newSettings; } titleElement.html(newSettings.title); } this.onCalculatedValueChanged = function (settingName, newValue) { if (!_.isUndefined(gaugeObject)) { gaugeObject.refresh(Number(newValue)); } } this.onDispose = function () { } this.getHeight = function () { return 3; } this.onSettingsChanged(settings); }; freeboard.loadWidgetPlugin({ type_name: "gauge", display_name: "Gauge", "external_scripts" : [ "plugins/thirdparty/raphael.2.1.0.min.js", "plugins/thirdparty/justgage.1.0.1.js" ], settings: [ { name: "title", display_name: "Title", type: "text" }, { name: "value", display_name: "Value", type: "calculated" }, { name: "units", display_name: "Units", type: "text" }, { name: "min_value", display_name: "Minimum", type: "text", default_value: 0 }, { name: "max_value", display_name: "Maximum", type: "text", default_value: 100 } ], newInstance: function (settings, newInstanceCallback) { newInstanceCallback(new gaugeWidget(settings)); } }); freeboard.addStyle('.sparkline', "width:100%;height: 75px;"); var sparklineWidget = function (settings) { var self = this; var titleElement = $('<h2 class="section-title"></h2>'); var sparklineElement = $('<div class="sparkline"></div>'); var sparklineLegend = $('<div></div>'); var currentSettings = settings; this.render = function (element) { $(element).append(titleElement).append(sparklineElement).append(sparklineLegend); } this.onSettingsChanged = function (newSettings) { currentSettings = newSettings; titleElement.html((_.isUndefined(newSettings.title) ? "" : newSettings.title)); if(newSettings.include_legend) { addSparklineLegend(sparklineLegend, newSettings.legend.split(",")); } } this.onCalculatedValueChanged = function (settingName, newValue) { if (currentSettings.legend) { addValueToSparkline(sparklineElement, newValue, currentSettings.legend.split(",")); } else { addValueToSparkline(sparklineElement, newValue); } } this.onDispose = function () { } this.getHeight = function () { var legendHeight = 0; if (currentSettings.include_legend && currentSettings.legend) { var legendLength = currentSettings.legend.split(",").length; if (legendLength > 4) { legendHeight = Math.floor((legendLength-1) / 4) * 0.5; } else if (legendLength) { legendHeight = 0.5; } } return 2 + legendHeight; } this.onSettingsChanged(settings); }; freeboard.loadWidgetPlugin({ type_name: "sparkline", display_name: "Sparkline", "external_scripts" : [ "plugins/thirdparty/jquery.sparkline.min.js" ], settings: [ { name: "title", display_name: "Title", type: "text" }, { name: "value", display_name: "Value", type: "calculated", multi_input: "true" }, { name: "include_legend", display_name: "Include Legend", type: "boolean" }, { name: "legend", display_name: "Legend", type: "text", description: "Comma-separated for multiple sparklines" } ], newInstance: function (settings, newInstanceCallback) { newInstanceCallback(new sparklineWidget(settings)); } }); freeboard.addStyle('div.pointer-value', "position:absolute;height:95px;margin: auto;top: 0px;bottom: 0px;width: 100%;text-align:center;"); var pointerWidget = function (settings) { var self = this; var paper; var strokeWidth = 3; var triangle; var width, height; var currentValue = 0; var valueDiv = $('<div class="widget-big-text"></div>'); var unitsDiv = $('<div></div>'); function polygonPath(points) { if (!points || points.length < 2) return []; var path = []; //will use path object type path.push(['m', points[0], points[1]]); for (var i = 2; i < points.length; i += 2) { path.push(['l', points[i], points[i + 1]]); } path.push(['z']); return path; } this.render = function (element) { width = $(element).width(); height = $(element).height(); var radius = Math.min(width, height) / 2 - strokeWidth * 2; paper = Raphael($(element).get()[0], width, height); var circle = paper.circle(width / 2, height / 2, radius); circle.attr("stroke", "#FF9900"); circle.attr("stroke-width", strokeWidth); triangle = paper.path(polygonPath([width / 2, (height / 2) - radius + strokeWidth, 15, 20, -30, 0])); triangle.attr("stroke-width", 0); triangle.attr("fill", "#fff"); $(element).append($('<div class="pointer-value"></div>').append(valueDiv).append(unitsDiv)); } this.onSettingsChanged = function (newSettings) { unitsDiv.html(newSettings.units); } this.onCalculatedValueChanged = function (settingName, newValue) { if (settingName == "direction") { if (!_.isUndefined(triangle)) { var direction = "r"; var oppositeCurrent = currentValue + 180; if (oppositeCurrent < newValue) { //direction = "l"; } triangle.animate({transform: "r" + newValue + "," + (width / 2) + "," + (height / 2)}, 250, "bounce"); } currentValue = newValue; } else if (settingName == "value_text") { valueDiv.html(newValue); } } this.onDispose = function () { } this.getHeight = function () { return 4; } this.onSettingsChanged(settings); }; freeboard.loadWidgetPlugin({ type_name: "pointer", display_name: "Pointer", "external_scripts" : [ "plugins/thirdparty/raphael.2.1.0.min.js" ], settings: [ { name: "direction", display_name: "Direction", type: "calculated", description: "In degrees" }, { name: "value_text", display_name: "Value Text", type: "calculated" }, { name: "units", display_name: "Units", type: "text" } ], newInstance: function (settings, newInstanceCallback) { newInstanceCallback(new pointerWidget(settings)); } }); var pictureWidget = function(settings) { var self = this; var widgetElement; var timer; var imageURL; function stopTimer() { if(timer) { clearInterval(timer); timer = null; } } function updateImage() { if(widgetElement && imageURL) { var cacheBreakerURL = imageURL + (imageURL.indexOf("?") == -1 ? "?" : "&") + Date.now(); $(widgetElement).css({ "background-image" : "url(" + cacheBreakerURL + ")" }); } } this.render = function(element) { $(element).css({ width : "100%", height: "100%", "background-size" : "cover", "background-position" : "center" }); widgetElement = element; } this.onSettingsChanged = function(newSettings) { stopTimer(); if(newSettings.refresh && newSettings.refresh > 0) { timer = setInterval(updateImage, Number(newSettings.refresh) * 1000); } } this.onCalculatedValueChanged = function(settingName, newValue) { if(settingName == "src") { imageURL = newValue; } updateImage(); } this.onDispose = function() { stopTimer(); } this.getHeight = function() { return 4; } this.onSettingsChanged(settings); }; freeboard.loadWidgetPlugin({ type_name: "picture", display_name: "Picture", fill_size: true, settings: [ { name: "src", display_name: "Image URL", type: "calculated" }, { "type": "number", "display_name": "Refresh every", "name": "refresh", "suffix": "seconds", "description":"Leave blank if the image doesn't need to be refreshed" } ], newInstance: function (settings, newInstanceCallback) { newInstanceCallback(new pictureWidget(settings)); } }); freeboard.addStyle('.indicator-light', "border-radius:50%;width:22px;height:22px;border:2px solid #3d3d3d;margin-top:5px;float:left;background-color:#222;margin-right:10px;"); freeboard.addStyle('.indicator-light.on', "background-color:#FFC773;box-shadow: 0px 0px 15px #FF9900;border-color:#FDF1DF;"); freeboard.addStyle('.indicator-text', "margin-top:10px;"); var indicatorWidget = function (settings) { var self = this; var titleElement = $('<h2 class="section-title"></h2>'); var stateElement = $('<div class="indicator-text"></div>'); var indicatorElement = $('<div class="indicator-light"></div>'); var currentSettings = settings; var isOn = false; var onText; var offText; function updateState() { indicatorElement.toggleClass("on", isOn); if (isOn) { stateElement.text((_.isUndefined(onText) ? (_.isUndefined(currentSettings.on_text) ? "" : currentSettings.on_text) : onText)); } else { stateElement.text((_.isUndefined(offText) ? (_.isUndefined(currentSettings.off_text) ? "" : currentSettings.off_text) : offText)); } } this.render = function (element) { $(element).append(titleElement).append(indicatorElement).append(stateElement); } this.onSettingsChanged = function (newSettings) { currentSettings = newSettings; titleElement.html((_.isUndefined(newSettings.title) ? "" : newSettings.title)); updateState(); } this.onCalculatedValueChanged = function (settingName, newValue) { if (settingName == "value") { isOn = Boolean(newValue); } if (settingName == "on_text") { onText = newValue; } if (settingName == "off_text") { offText = newValue; } updateState(); } this.onDispose = function () { } this.getHeight = function () { return 1; } this.onSettingsChanged(settings); }; freeboard.loadWidgetPlugin({ type_name: "indicator", display_name: "Indicator Light", settings: [ { name: "title", display_name: "Title", type: "text" }, { name: "value", display_name: "Value", type: "calculated" }, { name: "on_text", display_name: "On Text", type: "calculated" }, { name: "off_text", display_name: "Off Text", type: "calculated" } ], newInstance: function (settings, newInstanceCallback) { newInstanceCallback(new indicatorWidget(settings)); } }); freeboard.addStyle('.gm-style-cc a', "text-shadow:none;"); var googleMapWidget = function (settings) { var self = this; var currentSettings = settings; var map; var marker; var currentPosition = {}; function updatePosition() { if (map && marker && currentPosition.lat && currentPosition.lon) { var newLatLon = new google.maps.LatLng(currentPosition.lat, currentPosition.lon); marker.setPosition(newLatLon); map.panTo(newLatLon); } } this.render = function (element) { function initializeMap() { var mapOptions = { zoom: 13, center: new google.maps.LatLng(37.235, -115.811111), disableDefaultUI: true, draggable: false, styles: [ {"featureType": "water", "elementType": "geometry", "stylers": [ {"color": "#2a2a2a"} ]}, {"featureType": "landscape", "elementType": "geometry", "stylers": [ {"color": "#000000"}, {"lightness": 20} ]}, {"featureType": "road.highway", "elementType": "geometry.fill", "stylers": [ {"color": "#000000"}, {"lightness": 17} ]}, {"featureType": "road.highway", "elementType": "geometry.stroke", "stylers": [ {"color": "#000000"}, {"lightness": 29}, {"weight": 0.2} ]}, {"featureType": "road.arterial", "elementType": "geometry", "stylers": [ {"color": "#000000"}, {"lightness": 18} ]}, {"featureType": "road.local", "elementType": "geometry", "stylers": [ {"color": "#000000"}, {"lightness": 16} ]}, {"featureType": "poi", "elementType": "geometry", "stylers": [ {"color": "#000000"}, {"lightness": 21} ]}, {"elementType": "labels.text.stroke", "stylers": [ {"visibility": "on"}, {"color": "#000000"}, {"lightness": 16} ]}, {"elementType": "labels.text.fill", "stylers": [ {"saturation": 36}, {"color": "#000000"}, {"lightness": 40} ]}, {"elementType": "labels.icon", "stylers": [ {"visibility": "off"} ]}, {"featureType": "transit", "elementType": "geometry", "stylers": [ {"color": "#000000"}, {"lightness": 19} ]}, {"featureType": "administrative", "elementType": "geometry.fill", "stylers": [ {"color": "#000000"}, {"lightness": 20} ]}, {"featureType": "administrative", "elementType": "geometry.stroke", "stylers": [ {"color": "#000000"}, {"lightness": 17}, {"weight": 1.2} ]} ] }; map = new google.maps.Map(element, mapOptions); google.maps.event.addDomListener(element, 'mouseenter', function (e) { e.cancelBubble = true; if (!map.hover) { map.hover = true; map.setOptions({zoomControl: true}); } }); google.maps.event.addDomListener(element, 'mouseleave', function (e) { if (map.hover) { map.setOptions({zoomControl: false}); map.hover = false; } }); marker = new google.maps.Marker({map: map}); updatePosition(); } if (window.google && window.google.maps) { initializeMap(); } else { window.gmap_initialize = initializeMap; head.js("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=gmap_initialize"); } } this.onSettingsChanged = function (newSettings) { currentSettings = newSettings; } this.onCalculatedValueChanged = function (settingName, newValue) { if (settingName == "lat") { currentPosition.lat = newValue; } else if (settingName == "lon") { currentPosition.lon = newValue; } updatePosition(); } this.onDispose = function () { } this.getHeight = function () { return 4; } this.onSettingsChanged(settings); }; freeboard.loadWidgetPlugin({ type_name: "google_map", display_name: "Google Map", fill_size: true, settings: [ { name: "lat", display_name: "Latitude", type: "calculated" }, { name: "lon", display_name: "Longitude", type: "calculated" } ], newInstance: function (settings, newInstanceCallback) { newInstanceCallback(new googleMapWidget(settings)); } }); freeboard.addStyle('.html-widget', "white-space:normal;width:100%;height:100%"); var htmlWidget = function (settings) { var self = this; var htmlElement = $('<div class="html-widget"></div>'); var currentSettings = settings; this.render = function (element) { $(element).append(htmlElement); } this.onSettingsChanged = function (newSettings) { currentSettings = newSettings; } this.onCalculatedValueChanged = function (settingName, newValue) { if (settingName == "html") { htmlElement.html(newValue); } } this.onDispose = function () { } this.getHeight = function () { return Number(currentSettings.height); } this.onSettingsChanged(settings); }; freeboard.loadWidgetPlugin({ "type_name": "html", "display_name": "HTML", "fill_size": true, "settings": [ { "name": "html", "display_name": "HTML", "type": "calculated", "description": "Can be literal HTML, or javascript that outputs HTML." }, { "name": "height", "display_name": "Height Blocks", "type": "number", "default_value": 4, "description": "A height block is around 60 pixels" } ], newInstance: function (settings, newInstanceCallback) { newInstanceCallback(new htmlWidget(settings)); } }); }());