// ┌────────────────────────────────────────────────────────────────────┐ \\ // │ 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 jsonDatasource = function (settings, updateCallback) { var self = this; var updateTimer = null; var currentSettings = settings; var errorStage = 0; // 0 = try standard request // 1 = try JSONP // 2 = try thingproxy.freeboard.io var lockErrorStage = false; function updateRefresh(refreshTime) { if (updateTimer) { clearInterval(updateTimer); } updateTimer = setInterval(function () { self.updateNow(); }, refreshTime); } updateRefresh(currentSettings.refresh * 1000); this.updateNow = function () { if ((errorStage > 1 && !currentSettings.use_thingproxy) || errorStage > 2) // We've tried everything, let's quit { return; // TODO: Report an error } var requestURL = currentSettings.url; if (errorStage == 2 && currentSettings.use_thingproxy) { requestURL = (location.protocol == "https:" ? "https:" : "http:") + "//thingproxy.freeboard.io/fetch/" + encodeURI(currentSettings.url); } var body = currentSettings.body; // Can the body be converted to JSON? if (body) { try { body = JSON.parse(body); } catch (e) { } } $.ajax({ url: requestURL, dataType: (errorStage == 1) ? "JSONP" : "JSON", type: currentSettings.method || "GET", data: body, beforeSend: function (xhr) { try { _.each(currentSettings.headers, function (header) { var name = header.name; var value = header.value; if (!_.isUndefined(name) && !_.isUndefined(value)) { xhr.setRequestHeader(name, value); } }); } catch (e) { } }, success: function (data) { lockErrorStage = true; updateCallback(data); }, error: function (xhr, status, error) { if (!lockErrorStage) { // TODO: Figure out a way to intercept CORS errors only. The error message for CORS errors seems to be a standard 404. errorStage++; self.updateNow(); } } }); } this.onDispose = function () { clearInterval(updateTimer); updateTimer = null; } this.onSettingsChanged = function (newSettings) { lockErrorStage = false; errorStage = 0; currentSettings = newSettings; updateRefresh(currentSettings.refresh * 1000); self.updateNow(); } }; freeboard.loadDatasourcePlugin({ type_name: "JSON", settings: [ { name: "url", display_name: "URL", type: "text" }, { name: "use_thingproxy", display_name: "Try thingproxy", description: 'A direct JSON connection will be tried first, if that fails, a JSONP connection will be tried. If that fails, you can use thingproxy, which can solve many connection problems to APIs. More information.', type: "boolean", default_value: true }, { name: "refresh", display_name: "Refresh Every", type: "number", suffix: "seconds", default_value: 5 }, { name: "method", display_name: "Method", type: "option", options: [ { name: "GET", value: "GET" }, { name: "POST", value: "POST" }, { name: "PUT", value: "PUT" }, { name: "DELETE", value: "DELETE" } ] }, { name: "body", display_name: "Body", type: "text", description: "The body of the request. Normally only used if method is POST" }, { name: "headers", display_name: "Headers", type: "array", settings: [ { name: "name", display_name: "Name", type: "text" }, { name: "value", display_name: "Value", type: "text" } ] } ], newInstance: function (settings, newInstanceCallback, updateCallback) { newInstanceCallback(new jsonDatasource(settings, updateCallback)); } }); var openWeatherMapDatasource = function (settings, updateCallback) { var self = this; var updateTimer = null; var currentSettings = settings; function updateRefresh(refreshTime) { if (updateTimer) { clearInterval(updateTimer); } updateTimer = setInterval(function () { self.updateNow(); }, refreshTime); } function toTitleCase(str) { return str.replace(/\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }); } updateRefresh(currentSettings.refresh * 1000); this.updateNow = function () { $.ajax({ url: "http://api.openweathermap.org/data/2.5/weather?q=" + encodeURIComponent(currentSettings.location) + "&units=" + currentSettings.units, dataType: "JSONP", success: function (data) { // Rejigger our data into something easier to understand var newData = { place_name: data.name, sunrise: (new Date(data.sys.sunrise * 1000)).toLocaleTimeString(), sunset: (new Date(data.sys.sunset * 1000)).toLocaleTimeString(), conditions: toTitleCase(data.weather[0].description), current_temp: data.main.temp, high_temp: data.main.temp_max, low_temp: data.main.temp_min, pressure: data.main.pressure, humidity: data.main.humidity, wind_speed: data.wind.speed, wind_direction: data.wind.deg }; updateCallback(newData); }, error: function (xhr, status, error) { } }); } this.onDispose = function () { clearInterval(updateTimer); updateTimer = null; } this.onSettingsChanged = function (newSettings) { currentSettings = newSettings; self.updateNow(); updateRefresh(currentSettings.refresh * 1000); } }; freeboard.loadDatasourcePlugin({ type_name: "openweathermap", display_name: "Open Weather Map API", settings: [ { name: "location", display_name: "Location", type: "text", description: "Example: London, UK" }, { name: "units", display_name: "Units", type: "option", default: "imperial", options: [ { name: "Imperial", value: "imperial" }, { name: "Metric", value: "metric" } ] }, { name: "refresh", display_name: "Refresh Every", type: "number", suffix: "seconds", default_value: 5 } ], newInstance: function (settings, newInstanceCallback, updateCallback) { newInstanceCallback(new openWeatherMapDatasource(settings, updateCallback)); } }); var dweetioDatasource = function (settings, updateCallback) { var self = this; var currentSettings = settings; function onNewDweet(dweet) { updateCallback(dweet); } this.updateNow = function () { dweetio.get_latest_dweet_for(currentSettings.thing_id, function (err, dweet) { if (err) { //onNewDweet({}); } else { onNewDweet(dweet[0].content); } }); } this.onDispose = function () { } this.onSettingsChanged = function (newSettings) { dweetio.stop_listening(); currentSettings = newSettings; dweetio.listen_for(currentSettings.thing_id, function (dweet) { onNewDweet(dweet.content); }); } self.onSettingsChanged(settings); }; freeboard.loadDatasourcePlugin({ "type_name": "dweet_io", "display_name": "Dweet.io", "external_scripts": [ "http://dweet.io/client/dweet.io.min.js" ], "settings": [ { name: "thing_id", display_name: "Thing Name", "description": "Example: salty-dog-1", type: "text" } ], newInstance: function (settings, newInstanceCallback, updateCallback) { newInstanceCallback(new dweetioDatasource(settings, updateCallback)); } }); var playbackDatasource = function (settings, updateCallback) { var self = this; var currentSettings = settings; var currentDataset = []; var currentIndex = 0; var currentTimeout; function moveNext() { if (currentDataset.length > 0) { if (currentIndex < currentDataset.length) { updateCallback(currentDataset[currentIndex]); currentIndex++; } if (currentIndex >= currentDataset.length && currentSettings.loop) { currentIndex = 0; } if (currentIndex < currentDataset.length) { currentTimeout = setTimeout(moveNext, currentSettings.refresh * 1000); } } else { updateCallback({}); } } function stopTimeout() { currentDataset = []; currentIndex = 0; if (currentTimeout) { clearTimeout(currentTimeout); currentTimeout = null; } } this.updateNow = function () { stopTimeout(); $.ajax({ url: currentSettings.datafile, dataType: (currentSettings.is_jsonp) ? "JSONP" : "JSON", success: function (data) { if (_.isArray(data)) { currentDataset = data; } else { currentDataset = []; } currentIndex = 0; moveNext(); }, error: function (xhr, status, error) { } }); } this.onDispose = function () { stopTimeout(); } this.onSettingsChanged = function (newSettings) { currentSettings = newSettings; self.updateNow(); } }; freeboard.loadDatasourcePlugin({ "type_name": "playback", "display_name": "Playback", "settings": [ { "name": "datafile", "display_name": "Data File URL", "type": "text", "description": "A link to a JSON array of data." }, { name: "is_jsonp", display_name: "Is JSONP", type: "boolean" }, { "name": "loop", "display_name": "Loop", "type": "boolean", "description": "Rewind and loop when finished" }, { "name": "refresh", "display_name": "Refresh Every", "type": "number", "suffix": "seconds", "default_value": 5 } ], newInstance: function (settings, newInstanceCallback, updateCallback) { newInstanceCallback(new playbackDatasource(settings, updateCallback)); } }); var clockDatasource = function (settings, updateCallback) { var self = this; var currentSettings = settings; var timer; function stopTimer() { if (timer) { clearTimeout(timer); timer = null; } } function updateTimer() { stopTimer(); timer = setInterval(self.updateNow, currentSettings.refresh * 1000); } this.updateNow = function () { var date = new Date(); var data = { numeric_value: date.getTime(), full_string_value: date.toLocaleString(), date_string_value: date.toLocaleDateString(), time_string_value: date.toLocaleTimeString(), date_object: date }; updateCallback(data); } this.onDispose = function () { stopTimer(); } this.onSettingsChanged = function (newSettings) { currentSettings = newSettings; updateTimer(); } updateTimer(); }; freeboard.loadDatasourcePlugin({ "type_name": "clock", "display_name": "Clock", "settings": [ { "name": "refresh", "display_name": "Refresh Every", "type": "number", "suffix": "seconds", "default_value": 1 } ], newInstance: function (settings, newInstanceCallback, updateCallback) { newInstanceCallback(new clockDatasource(settings, updateCallback)); } }); }()); // ┌────────────────────────────────────────────────────────────────────┐ \\ // │ 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; 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 addValueToSparkline(element, value) { var values = $(element).data().values; if (!values) { values = []; } if (values.length >= SPARKLINE_HISTORY_LENGTH) { values.shift(); } values.push(Number(value)); $(element).data().values = values; $(element).sparkline(values, { type: "line", height: "100%", width: "100%", fillColor: false, lineColor: "#FF9900", lineWidth: 2, spotRadius: 3, spotColor: false, minSpotColor: "#78AB49", maxSpotColor: "#78AB49", highlightSpotColor: "#9D3926", highlightLineColor: "#9D3926" }); } 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 = $('
'); var titleElement = $('

'); var valueElement = $('
'); var unitsElement = $('
'); var sparklineElement = $('
'); 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($('
').append(titleElement)) .append($('
').append($('
').append(valueElement).append(unitsElement))) .append($('
').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 = $('

'); var gaugeElement = $('
'); 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($('
').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 = $('

'); var sparklineElement = $('
'); this.render = function (element) { $(element).append(titleElement).append(sparklineElement); } this.onSettingsChanged = function (newSettings) { titleElement.html((_.isUndefined(newSettings.title) ? "" : newSettings.title)); } this.onCalculatedValueChanged = function (settingName, newValue) { addValueToSparkline(sparklineElement, newValue); } this.onDispose = function () { } this.getHeight = function () { return 2; } 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" } ], 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 = $('
'); var unitsDiv = $('
'); 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($('
').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 = $('

'); var stateElement = $('
'); var indicatorElement = $('
'); var currentSettings = settings; var isOn = false; function updateState() { indicatorElement.toggleClass("on", isOn); if (isOn) { stateElement.text((_.isUndefined(currentSettings.on_text) ? "" : currentSettings.on_text)); } else { stateElement.text((_.isUndefined(currentSettings.off_text) ? "" : currentSettings.off_text)); } } 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); } 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 = $('
'); 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)); } }); }());