diff --git a/js/freeboard+plugins.js b/js/freeboard+plugins.js index 8b4b1e6..b4745f0 100644 --- a/js/freeboard+plugins.js +++ b/js/freeboard+plugins.js @@ -1283,9 +1283,9 @@ PluginEditor = function(jsEditor, valueEditor) if($("#setting-row-instance-name").length) { $("#setting-row-instance-name").nextAll().remove(); - } - else - { + } + else + { $("#setting-row-plugin-types").nextAll().remove(); } } @@ -1322,18 +1322,18 @@ PluginEditor = function(jsEditor, valueEditor) valueEditor.createValueEditor(input); - var datasourceToolbox = $(''); + var datasourceToolbox = $(''); var wrapperDiv = $('
'); wrapperDiv.append(input).append(datasourceToolbox); - var datasourceTool = $('
  • ') + var datasourceTool = $('
  • ') .mousedown(function(e) { e.preventDefault(); $(input).val("").focus().insertAtCaret("datasources[\"").trigger("freeboard-eval"); }); datasourceToolbox.append(datasourceTool); - var jsEditorTool = $('
  • ') + var jsEditorTool = $('
  • ') .mousedown(function(e) { e.preventDefault(); jsEditor.displayJSEditor(input.val(), function(result) { @@ -1349,7 +1349,7 @@ PluginEditor = function(jsEditor, valueEditor) e.preventDefault(); wrapperDiv.remove(); $(valueCell).find('textarea:first').change(); - }); + }); datasourceToolbox.prepend(removeButton); } @@ -1377,7 +1377,7 @@ PluginEditor = function(jsEditor, valueEditor) var pluginDescriptionElement = $('
    ').hide(); form.append(pluginDescriptionElement); - function createSettingsFromDefinition(settingsDefs) + function createSettingsFromDefinition(settingsDefs, typeaheadSource, typeaheadDataSegment) { _.each(settingsDefs, function(settingDef) { @@ -1471,16 +1471,16 @@ PluginEditor = function(jsEditor, valueEditor) }); subsettingRow.append($('').append($('').append($('
  • ').append($('').click(function() - { - var subSettingIndex = newSettings.settings[settingDef.name].indexOf(newSetting); + { + var subSettingIndex = newSettings.settings[settingDef.name].indexOf(newSetting); - if(subSettingIndex != -1) - { - newSettings.settings[settingDef.name].splice(subSettingIndex, 1); - subsettingRow.remove(); - processHeaderVisibility(); - } - }))))); + if(subSettingIndex != -1) + { + newSettings.settings[settingDef.name].splice(subSettingIndex, 1); + subsettingRow.remove(); + processHeaderVisibility(); + } + }))))); subTableDiv.scrollTop(subTableDiv[0].scrollHeight); @@ -1511,7 +1511,7 @@ PluginEditor = function(jsEditor, valueEditor) { newSettings.settings[settingDef.name] = currentSettingsValues[settingDef.name]; - var onOffSwitch = $('
    ').appendTo(valueCell); + var onOffSwitch = $('
    ').appendTo(valueCell); var input = $('').prependTo(onOffSwitch).change(function() { @@ -1606,30 +1606,86 @@ PluginEditor = function(jsEditor, valueEditor) { var input = $('').appendTo(valueCell).change(function() { - if(settingDef.type == "number") - { - newSettings.settings[settingDef.name] = Number($(this).val()); - } - else - { - newSettings.settings[settingDef.name] = $(this).val(); - } + if(settingDef.type == "number") + { + newSettings.settings[settingDef.name] = Number($(this).val()); + } + else + { + newSettings.settings[settingDef.name] = $(this).val(); + } }); if(settingDef.name in currentSettingsValues) { input.val(currentSettingsValues[settingDef.name]); } + + if(typeaheadSource && settingDef.typeahead_data_field){ + input.addClass('typeahead_data_field-' + settingDef.typeahead_data_field); + } + + if(typeaheadSource && settingDef.typeahead_field){ + var typeaheadValues = []; + + input.keyup(function(event){ + if(event.which >= 65 && event.which <= 91) { + input.trigger('change'); + } + }); + + $(input).autocomplete({ + source: typeaheadValues, + select: function(event, ui){ + input.val(ui.item.value); + input.trigger('change'); + } + }); + + input.change(function(event){ + var value = input.val(); + var source = _.template(typeaheadSource)({input: value}); + $.get(source, function(data){ + if(typeaheadDataSegment){ + data = data[typeaheadDataSegment]; + } + data = _.select(data, function(elm){ + return elm[settingDef.typeahead_field][0] == value[0]; + }); + + typeaheadValues = _.map(data, function(elm){ + return elm[settingDef.typeahead_field]; + }); + $(input).autocomplete("option", "source", typeaheadValues); + + if(data.length == 1){ + data = data[0]; + //we found the one. let's use it to populate the other info + for(var field in data){ + if(data.hasOwnProperty(field)){ + var otherInput = $(_.template('input.typeahead_data_field-<%= field %>')({field: field})); + if(otherInput){ + otherInput.val(data[field]); + if(otherInput.val() != input.val()) { + otherInput.trigger('change'); + } + } + } + } + } + }); + }); + } } break; } } - if(!_.isUndefined(settingDef.suffix)) - { - valueCell.append($('
    ' + settingDef.suffix + '
    ')); - } + if(!_.isUndefined(settingDef.suffix)) + { + valueCell.append($('
    ' + settingDef.suffix + '
    ')); + } if(!_.isUndefined(settingDef.description)) { @@ -1642,7 +1698,7 @@ PluginEditor = function(jsEditor, valueEditor) new DialogBox(form, title, "Save", "Cancel", function() { $(".validation-error").remove(); - + // Loop through each setting and validate it for(var index = 0; index < selectedType.settings.length; index++) { @@ -1650,12 +1706,12 @@ PluginEditor = function(jsEditor, valueEditor) if(settingDef.required && (_.isUndefined(newSettings.settings[settingDef.name]) || newSettings.settings[settingDef.name] == "")) { - _displayValidationError(settingDef.name, "This is required."); + _displayValidationError(settingDef.name, "This is required."); return true; } else if(settingDef.type == "number" && !_isNumerical(newSettings.settings[settingDef.name])) { - _displayValidationError(settingDef.name, "Must be a number."); + _displayValidationError(settingDef.name, "Must be a number."); return true; } } @@ -1699,19 +1755,19 @@ PluginEditor = function(jsEditor, valueEditor) } else { - $("#setting-row-instance-name").show(); + $("#setting-row-instance-name").show(); - if(selectedType.description && selectedType.description.length > 0) - { - pluginDescriptionElement.html(selectedType.description).show(); - } - else - { - pluginDescriptionElement.hide(); - } + if(selectedType.description && selectedType.description.length > 0) + { + pluginDescriptionElement.html(selectedType.description).show(); + } + else + { + pluginDescriptionElement.hide(); + } $("#dialog-ok").show(); - createSettingsFromDefinition(selectedType.settings); + createSettingsFromDefinition(selectedType.settings, selectedType.typeahead_source, selectedType.typeahead_data_segment); } }); } @@ -1723,30 +1779,30 @@ PluginEditor = function(jsEditor, valueEditor) createSettingsFromDefinition(selectedType.settings); } - if(typeSelect) - { - if(_.isUndefined(currentTypeName)) - { - $("#setting-row-instance-name").hide(); - $("#dialog-ok").hide(); - } - else - { - $("#dialog-ok").show(); - typeSelect.val(currentTypeName).trigger("change"); - } - } + if(typeSelect) + { + if(_.isUndefined(currentTypeName)) + { + $("#setting-row-instance-name").hide(); + $("#dialog-ok").hide(); + } + else + { + $("#dialog-ok").show(); + typeSelect.val(currentTypeName).trigger("change"); + } + } } // Public API return { createPluginEditor : function( - title, - pluginTypes, - currentInstanceName, - currentTypeName, - currentSettingsValues, - settingsSavedCallback) + title, + pluginTypes, + currentInstanceName, + currentTypeName, + currentSettingsValues, + settingsSavedCallback) { createPluginEditor(title, pluginTypes, currentInstanceName, currentTypeName, currentSettingsValues, settingsSavedCallback); } @@ -3444,8 +3500,159 @@ $.extend(freeboard, jQuery.eventEmitter); newInstanceCallback(new clockDatasource(settings, updateCallback)); } }); +freeboard.loadDatasourcePlugin({ + // **type_name** (required) : A unique name for this plugin. This name should be as unique as possible to avoid collisions with other plugins, and should follow naming conventions for javascript variable and function declarations. + "type_name" : "meshblu", + // **display_name** : The pretty name that will be used for display purposes for this plugin. If the name is not defined, type_name will be used instead. + "display_name": "Octoblu", + // **description** : A description of the plugin. This description will be displayed when the plugin is selected or within search results (in the future). The description may contain HTML if needed. + "description" : "app.octoblu.com", + // **external_scripts** : Any external scripts that should be loaded before the plugin instance is created. + "external_scripts" : [ + "http://meshblu.octoblu.com/js/meshblu.js" + ], + // **settings** : An array of settings that will be displayed for this plugin when the user adds it. + "settings" : [ + { + // **name** (required) : The name of the setting. This value will be used in your code to retrieve the value specified by the user. This should follow naming conventions for javascript variable and function declarations. + "name" : "uuid", + // **display_name** : The pretty name that will be shown to the user when they adjust this setting. + "display_name" : "UUID", + // **type** (required) : The type of input expected for this setting. "text" will display a single text box input. Examples of other types will follow in this documentation. + "type" : "text", + // **default_value** : A default value for this setting. + "default_value": "device uuid", + // **description** : Text that will be displayed below the setting to give the user any extra information. + "description" : "your device UUID", + // **required** : Set to true if this setting is required for the datasource to be created. + "required" : true + }, + { + // **name** (required) : The name of the setting. This value will be used in your code to retrieve the value specified by the user. This should follow naming conventions for javascript variable and function declarations. + "name" : "token", + // **display_name** : The pretty name that will be shown to the user when they adjust this setting. + "display_name" : "Token", + // **type** (required) : The type of input expected for this setting. "text" will display a single text box input. Examples of other types will follow in this documentation. + "type" : "text", + // **default_value** : A default value for this setting. + "default_value": "device token", + // **description** : Text that will be displayed below the setting to give the user any extra information. + "description" : "your device TOKEN", + // **required** : Set to true if this setting is required for the datasource to be created. + "required" : true + }, + { + // **name** (required) : The name of the setting. This value will be used in your code to retrieve the value specified by the user. This should follow naming conventions for javascript variable and function declarations. + "name" : "server", + // **display_name** : The pretty name that will be shown to the user when they adjust this setting. + "display_name" : "Server", + // **type** (required) : The type of input expected for this setting. "text" will display a single text box input. Examples of other types will follow in this documentation. + "type" : "text", + // **default_value** : A default value for this setting. + "default_value": "meshblu.octoblu.com", + // **description** : Text that will be displayed below the setting to give the user any extra information. + "description" : "your server", + // **required** : Set to true if this setting is required for the datasource to be created. + "required" : true + }, + { + // **name** (required) : The name of the setting. This value will be used in your code to retrieve the value specified by the user. This should follow naming conventions for javascript variable and function declarations. + "name" : "port", + // **display_name** : The pretty name that will be shown to the user when they adjust this setting. + "display_name" : "Port", + // **type** (required) : The type of input expected for this setting. "text" will display a single text box input. Examples of other types will follow in this documentation. + "type" : "number", + // **default_value** : A default value for this setting. + "default_value": 80, + // **description** : Text that will be displayed below the setting to give the user any extra information. + "description" : "server port", + // **required** : Set to true if this setting is required for the datasource to be created. + "required" : true + } + + ], + // **newInstance(settings, newInstanceCallback, updateCallback)** (required) : A function that will be called when a new instance of this plugin is requested. + // * **settings** : A javascript object with the initial settings set by the user. The names of the properties in the object will correspond to the setting names defined above. + // * **newInstanceCallback** : A callback function that you'll call when the new instance of the plugin is ready. This function expects a single argument, which is the new instance of your plugin object. + // * **updateCallback** : A callback function that you'll call if and when your datasource has an update for freeboard to recalculate. This function expects a single parameter which is a javascript object with the new, updated data. You should hold on to this reference and call it when needed. + newInstance : function(settings, newInstanceCallback, updateCallback) + { + // myDatasourcePlugin is defined below. + newInstanceCallback(new meshbluSource(settings, updateCallback)); + } + }); + + + // ### Datasource Implementation + // + // ------------------- + // Here we implement the actual datasource plugin. We pass in the settings and updateCallback. + var meshbluSource = function(settings, updateCallback) + { + // Always a good idea... + var self = this; + + // Good idea to create a variable to hold on to our settings, because they might change in the future. See below. + var currentSettings = settings; + + + + /* This is some function where I'll get my data from somewhere */ + + + function getData() + { + + + var conn = skynet.createConnection({ + "uuid": currentSettings.uuid, + "token": currentSettings.token, + "server": currentSettings.server, + "port": currentSettings.port + }); + + conn.on('ready', function(data){ + + conn.on('message', function(message){ + + var newData = message; + updateCallback(newData); + + }); + + }); + } + + + + // **onSettingsChanged(newSettings)** (required) : A public function we must implement that will be called when a user makes a change to the settings. + self.onSettingsChanged = function(newSettings) + { + // Here we update our current settings with the variable that is passed in. + currentSettings = newSettings; + } + + // **updateNow()** (required) : A public function we must implement that will be called when the user wants to manually refresh the datasource + self.updateNow = function() + { + // Most likely I'll just call getData() here. + getData(); + } + + // **onDispose()** (required) : A public function we must implement that will be called when this instance of this plugin is no longer needed. Do anything you need to cleanup after yourself here. + self.onDispose = function() + { + + //conn.close(); + } + + // Here we call createRefreshTimer with our current settings, to kick things off, initially. Notice how we make use of one of the user defined settings that we setup earlier. + // createRefreshTimer(currentSettings.refresh_time); + } + }()); + // ┌────────────────────────────────────────────────────────────────────┐ \\ // │ F R E E B O A R D │ \\ // ├────────────────────────────────────────────────────────────────────┤ \\ diff --git a/js/freeboard+plugins.min.js b/js/freeboard+plugins.min.js index 25beb52..ce622b7 100644 --- a/js/freeboard+plugins.min.js +++ b/js/freeboard+plugins.min.js @@ -1,2 +1,3 @@ -function DialogBox(e,t,n,i,a){function s(){o.fadeOut(200,function(){$(this).remove()})}var o=$(''),r=$('');r.append('

    '+t+"

    "),$("
    ").appendTo(r).append(e);var l=$("").appendTo(r);n&&$(''+n+"").appendTo(l).click(function(){var e=!1;_.isFunction(a)&&(e=a()),e||s()}),i&&$(''+i+"").appendTo(l).click(function(){s()}),o.append(r),$("body").append(o),o.fadeIn(200)}function FreeboardModel(e,t,n){var i=this,a=1;this.version=0,this.isEditing=ko.observable(!1),this.allow_edit=ko.observable(!1),this.allow_edit.subscribe(function(e){e?$("#main-header").show():$("#main-header").hide()}),this.header_image=ko.observable(),this.plugins=ko.observableArray(),this.datasources=ko.observableArray(),this.panes=ko.observableArray(),this.datasourceData={},this.processDatasourceUpdate=function(e,t){var n=e.name();i.datasourceData[n]=t,_.each(i.panes(),function(e){_.each(e.widgets(),function(e){e.processDatasourceUpdate(n)})})},this._datasourceTypes=ko.observable(),this.datasourceTypes=ko.computed({read:function(){i._datasourceTypes();var t=[];return _.each(e,function(e){var n=e.type_name,i=n;_.isUndefined(e.display_name)||(i=e.display_name),t.push({name:n,display_name:i})}),t}}),this._widgetTypes=ko.observable(),this.widgetTypes=ko.computed({read:function(){i._widgetTypes();var e=[];return _.each(t,function(t){var n=t.type_name,i=n;_.isUndefined(t.display_name)||(i=t.display_name),e.push({name:n,display_name:i})}),e}}),this.addPluginSource=function(e){e&&-1==i.plugins.indexOf(e)&&i.plugins.push(e)},this.serialize=function(){var e=[];_.each(i.panes(),function(t){e.push(t.serialize())});var t=[];return _.each(i.datasources(),function(e){t.push(e.serialize())}),{version:a,header_image:i.header_image(),allow_edit:i.allow_edit(),plugins:i.plugins(),panes:e,datasources:t,columns:n.getUserColumns()}},this.deserialize=function(a,s){function o(){n.setUserColumns(a.columns),_.isUndefined(a.allow_edit)?i.allow_edit(!0):i.allow_edit(a.allow_edit),i.version=a.version||0,i.header_image(a.header_image),_.each(a.datasources,function(t){var n=new DatasourceModel(i,e);n.deserialize(t),i.addDatasource(n)});var o=_.sortBy(a.panes,function(e){return n.getPositionForScreenSize(e).row});_.each(o,function(e){var n=new PaneModel(i,t);n.deserialize(e),i.panes.push(n)}),i.allow_edit()&&0==i.panes().length&&i.setEditing(!0),_.isFunction(s)&&s(),n.processResize(!0)}i.clearDashboard(),_.each(a.plugins,function(e){i.addPluginSource(e)}),_.isArray(a.plugins)&&a.plugins.length>0?head.js(a.plugins,function(){o()}):o()},this.clearDashboard=function(){n.removeAllPanes(),_.each(i.datasources(),function(e){e.dispose()}),_.each(i.panes(),function(e){e.dispose()}),i.plugins.removeAll(),i.datasources.removeAll(),i.panes.removeAll()},this.loadDashboard=function(e,t){n.showLoadingIndicator(!0),i.deserialize(e,function(){n.showLoadingIndicator(!1),_.isFunction(t)&&t(),freeboard.emit("dashboard_loaded")})},this.loadDashboardFromLocalFile=function(){if(window.File&&window.FileReader&&window.FileList&&window.Blob){var e=document.createElement("input");e.type="file",$(e).on("change",function(e){var t=e.target.files;if(t&&t.length>0){var n=t[0],a=new FileReader;a.addEventListener("load",function(e){var t=e.target,n=JSON.parse(t.result);i.loadDashboard(n),i.setEditing(!1)}),a.readAsText(n)}}),$(e).trigger("click")}else alert("Unable to load a file in this browser.")},this.saveDashboard=function(){var e="application/octet-stream",t=document.createElement("a"),n=new Blob([JSON.stringify(i.serialize())],{type:e});document.body.appendChild(t),t.href=window.URL.createObjectURL(n),t.download="dashboard.json",t.target="_self",t.click()},this.addDatasource=function(e){i.datasources.push(e)},this.deleteDatasource=function(e){delete i.datasourceData[e.name()],e.dispose(),i.datasources.remove(e)},this.createPane=function(){var e=new PaneModel(i,t);i.addPane(e)},this.addGridColumnLeft=function(){n.addGridColumnLeft()},this.addGridColumnRight=function(){n.addGridColumnRight()},this.subGridColumnLeft=function(){n.subGridColumnLeft()},this.subGridColumnRight=function(){n.subGridColumnRight()},this.addPane=function(e){i.panes.push(e)},this.deletePane=function(e){e.dispose(),i.panes.remove(e)},this.deleteWidget=function(e){ko.utils.arrayForEach(i.panes(),function(t){t.widgets.remove(e)}),e.dispose()},this.setEditing=function(e,t){if(i.allow_edit()||!e){i.isEditing(e),_.isUndefined(t)&&(t=!0);var a=t?250:0,s=$("#admin-bar").outerHeight();e?($("#toggle-header-icon").addClass("icon-chevron-up").removeClass("icon-wrench"),$(".gridster .gs_w").css({cursor:"pointer"}),$("#main-header").animate({top:"0px"},a),$("#board-content").animate({top:s+20+"px"},a),$("#main-header").data().shown=!0,n.attachWidgetEditIcons($(".sub-section")),n.enableGrid()):($("#toggle-header-icon").addClass("icon-wrench").removeClass("icon-chevron-up"),$(".gridster .gs_w").css({cursor:"default"}),$("#main-header").animate({top:"-"+s+"px"},a),$("#board-content").animate({top:"20"},a),$("#main-header").data().shown=!1,$(".sub-section").unbind(),n.disableGrid()),n.showPaneEditIcons(e,t)}},this.toggleEditing=function(){var e=!i.isEditing();i.setEditing(e)}}function FreeboardUI(){function e(e){var t=a(),n=function(){};e&&(n=function(){var e=this,n=ko.dataFor(e),i=m(n);$(e).attr("data-sizex",Math.min(n.col_width(),t,v.cols)).attr("data-row",i.row).attr("data-col",i.col),n.processSizeChange()}),s(Math.min(t,C)),o(n),i()}function t(e){var t=v.cols+1;s(t)&&o(function(){var t,n=this,i=ko.dataFor(n),a=v.cols>1?v.cols-1:1,s=i.col[a],o=i.row[a];if(e){leftPreviewCol=!0;var r=v.cols>s?s+1:v.cols;t={row:o,col:r}}else rightPreviewCol=!0,t={row:o,col:s};$(n).attr("data-sizex",Math.min(i.col_width(),v.cols)).attr("data-row",t.row).attr("data-col",t.col)}),i(),C=v.cols}function n(e){var t=v.cols-1;s(t)&&o(function(){var t,n=this,i=ko.dataFor(n),a=v.cols+1,s=i.col[a],o=i.row[a];if(e){var r=s>1?s-1:1;t={row:o,col:r}}else{var r=v.cols>=s?s:v.cols;t={row:o,col:r}}$(n).attr("data-sizex",Math.min(i.col_width(),v.cols)).attr("data-row",t.row).attr("data-col",t.col)}),i(),C=v.cols}function i(){var e=$(".column-tool"),t=$("#board-content").width(),n=Math.floor(t/x);b>=v.cols?e.addClass("min"):e.removeClass("min"),v.cols>=n?e.addClass("max"):e.removeClass("max")}function a(){var e=$("#board-content").width();return Math.floor(e/x)}function s(e){(void 0===e||b>e)&&(e=b);var t=a();e>t&&(e=t);var n=x*e+e;return $(".responsive-column-width").css("max-width",n),e===v.cols?!1:!0}function o(e){var t=v.$el;t.find("> li").unbind().removeData(),$(".responsive-column-width").css("width",""),v.generate_grid_and_stylesheet(),t.find("> li").each(e),v.init(),$(".responsive-column-width").css("width",v.cols*w+2*v.cols*y)}function r(){return C}function l(e){C=Math.max(b,e)}function d(e,t,n){var i=m(t),a=i.col,s=i.row,o=Number(t.width()),r=Number(t.getCalculatedHeight());v.add_widget(e,o,r,a,s),n&&f(!0),u(t,s,a),$(e).attrchange({trackValues:!0,callback:function(e){"data-row"==e.attributeName?u(t,Number(e.newValue),void 0):"data-col"==e.attributeName&&u(t,void 0,Number(e.newValue))}})}function c(e,t){var n=t.getCalculatedHeight(),i=Number($(e).attr("data-sizey")),a=Number($(e).attr("data-sizex"));(n!=i||t.col_width()!=a)&&v.resize_widget($(e),t.col_width(),n,function(){v.set_dom_grid_height()})}function u(e,t,n){var i=v.cols;_.isUndefined(t)||(e.row[i]=t),_.isUndefined(n)||(e.col[i]=n)}function p(e){e?S.fadeOut(0).appendTo("body").fadeIn(500):S.fadeOut(500).remove()}function f(e,t){_.isUndefined(t)&&(t=!0);var n=t?250:0;e?($(".pane-tools").fadeIn(n),$("#column-tools").fadeIn(n)):($(".pane-tools").fadeOut(n),$("#column-tools").fadeOut(n))}function h(e){$(e).hover(function(){g(this,!0)},function(){g(this,!1)})}function g(e,t){t?$(e).find(".sub-section-tools").fadeIn(250):$(e).find(".sub-section-tools").fadeOut(250)}function m(e){var t=v.cols;if(_.isNumber(e.row)&&_.isNumber(e.col)){var n={};n[t]=e.row,e.row=n,n={},n[t]=e.col,e.col=n}var i=1,a=1e3;for(var s in e.col){if(s==t)return{row:e.row[s],col:e.col[s]};if(e.col[s]>t)i=t;else{var o=t-s;a>o&&(i=s,a=o)}}return i in e.col&&i in e.row?{row:e.row[i],col:e.col[i]}:{row:1,col:i}}var v,y=10,w=300,b=3,x=y+w+y,C=b,S=$('
    ');return ko.bindingHandlers.grid={init:function(t){v=$(t).gridster({widget_margins:[y,y],widget_base_dimensions:[w,10],resize:{enabled:!1,axes:"x"}}).data("gridster"),e(!1),v.disable()}},{showLoadingIndicator:function(e){p(e)},showPaneEditIcons:function(e,t){f(e,t)},attachWidgetEditIcons:function(e){h(e)},getPositionForScreenSize:function(e){return m(e)},processResize:function(t){e(t)},disableGrid:function(){v.disable()},enableGrid:function(){v.enable()},addPane:function(e,t,n){d(e,t,n)},updatePane:function(e,t){c(e,t)},removePane:function(e){v.remove_widget(e)},removeAllPanes:function(){v.remove_all_widgets()},addGridColumnLeft:function(){t(!0)},addGridColumnRight:function(){t(!1)},subGridColumnLeft:function(){n(!0)},subGridColumnRight:function(){n(!1)},getUserColumns:function(){return r()},setUserColumns:function(e){l(e)}}}function PaneModel(e,t){var n=this;this.title=ko.observable(),this.width=ko.observable(1),this.row={},this.col={},this.col_width=ko.observable(1),this.col_width.subscribe(function(){n.processSizeChange()}),this.widgets=ko.observableArray(),this.addWidget=function(e){this.widgets.push(e)},this.widgetCanMoveUp=function(e){return n.widgets.indexOf(e)>=1},this.widgetCanMoveDown=function(e){var t=n.widgets.indexOf(e);return n.widgets().length-1>t},this.moveWidgetUp=function(e){if(n.widgetCanMoveUp(e)){var t=n.widgets.indexOf(e),i=n.widgets();n.widgets.splice(t-1,2,i[t],i[t-1])}},this.moveWidgetDown=function(e){if(n.widgetCanMoveDown(e)){var t=n.widgets.indexOf(e),i=n.widgets();n.widgets.splice(t,2,i[t+1],i[t])}},this.processSizeChange=function(){setTimeout(function(){_.each(n.widgets(),function(e){e.processSizeChange()})},1e3)},this.getCalculatedHeight=function(){var e=_.reduce(n.widgets(),function(e,t){return e+t.height()},0);e*=6,e+=3,e*=10;var t=Math.ceil((e+20)/30);return Math.max(4,t)},this.serialize=function(){var e=[];return _.each(n.widgets(),function(t){e.push(t.serialize())}),{title:n.title(),width:n.width(),row:n.row,col:n.col,col_width:n.col_width(),widgets:e}},this.deserialize=function(i){n.title(i.title),n.width(i.width),n.row=i.row,n.col=i.col,n.col_width(i.col_width||1),_.each(i.widgets,function(i){var a=new WidgetModel(e,t);a.deserialize(i),n.widgets.push(a)})},this.dispose=function(){_.each(n.widgets(),function(e){e.dispose()})}}function WidgetModel(e,t){function n(){_.isUndefined(i.widgetInstance)||(_.isFunction(i.widgetInstance.onDispose)&&i.widgetInstance.onDispose(),i.widgetInstance=void 0)}var i=this;this.datasourceRefreshNotifications={},this.calculatedSettingScripts={},this.title=ko.observable(),this.fillSize=ko.observable(!1),this.type=ko.observable(),this.type.subscribe(function(e){function a(){s.newInstance(i.settings(),function(e){i.fillSize(s.fill_size===!0),i.widgetInstance=e,i.shouldRender(!0),i._heightUpdate.valueHasMutated()})}if(n(),e in t&&_.isFunction(t[e].newInstance)){var s=t[e];s.external_scripts?head.js(s.external_scripts.slice(0),a):a()}}),this.settings=ko.observable({}),this.settings.subscribe(function(e){!_.isUndefined(i.widgetInstance)&&_.isFunction(i.widgetInstance.onSettingsChanged)&&i.widgetInstance.onSettingsChanged(e),i.updateCalculatedSettings(),i._heightUpdate.valueHasMutated()}),this.processDatasourceUpdate=function(e){var t=i.datasourceRefreshNotifications[e];_.isArray(t)&&_.each(t,function(e){i.processCalculatedSetting(e)})},this.callValueFunction=function(t){return t.call(void 0,e.datasourceData)},this.processSizeChange=function(){!_.isUndefined(i.widgetInstance)&&_.isFunction(i.widgetInstance.onSizeChanged)&&i.widgetInstance.onSizeChanged()},this.processCalculatedSetting=function(e){if(_.isFunction(i.calculatedSettingScripts[e])){var t=void 0;try{t=i.callValueFunction(i.calculatedSettingScripts[e])}catch(n){var a=i.settings()[e];n instanceof ReferenceError&&/^\w+$/.test(a)&&(t=a)}if(!_.isUndefined(i.widgetInstance)&&_.isFunction(i.widgetInstance.onCalculatedValueChanged)&&!_.isUndefined(t))try{i.widgetInstance.onCalculatedValueChanged(e,t)}catch(n){console.log(""+n)}}},this.updateCalculatedSettings=function(){if(i.datasourceRefreshNotifications={},i.calculatedSettingScripts={},!_.isUndefined(i.type())){var e=t[i.type()].settings,n=RegExp("datasources.([\\w_-]+)|datasources\\[['\"]([^'\"]+)","g"),a=i.settings();_.each(e,function(e){if("calculated"==e.type){var t=a[e.name];if(!_.isUndefined(t)){_.isArray(t)&&(t="["+t.join(",")+"]"),1>=(t.match(/;/g)||[]).length&&-1==t.indexOf("return")&&(t="return "+t);var s;try{s=Function("datasources",t)}catch(o){var r=a[e.name].replace(/"/g,'\\"').replace(/[\r\n]/g," \\\n");s=Function("datasources",'return "'+r+'";')}i.calculatedSettingScripts[e.name]=s,i.processCalculatedSetting(e.name);for(var l;l=n.exec(t);){var d=l[1]||l[2],c=i.datasourceRefreshNotifications[d];_.isUndefined(c)&&(c=[],i.datasourceRefreshNotifications[d]=c),-1==_.indexOf(c,e.name)&&c.push(e.name)}}}})}},this._heightUpdate=ko.observable(),this.height=ko.computed({read:function(){return i._heightUpdate(),!_.isUndefined(i.widgetInstance)&&_.isFunction(i.widgetInstance.getHeight)?i.widgetInstance.getHeight():1}}),this.shouldRender=ko.observable(!1),this.render=function(e){i.shouldRender(!1),!_.isUndefined(i.widgetInstance)&&_.isFunction(i.widgetInstance.render)&&(i.widgetInstance.render(e),i.updateCalculatedSettings())},this.dispose=function(){},this.serialize=function(){return{title:i.title(),type:i.type(),settings:i.settings()}},this.deserialize=function(e){i.title(e.title),i.settings(e.settings),i.type(e.type)}}DatasourceModel=function(e,t){function n(){_.isUndefined(i.datasourceInstance)||(_.isFunction(i.datasourceInstance.onDispose)&&i.datasourceInstance.onDispose(),i.datasourceInstance=void 0)}var i=this;this.name=ko.observable(),this.latestData=ko.observable(),this.settings=ko.observable({}),this.settings.subscribe(function(e){!_.isUndefined(i.datasourceInstance)&&_.isFunction(i.datasourceInstance.onSettingsChanged)&&i.datasourceInstance.onSettingsChanged(e)}),this.updateCallback=function(t){e.processDatasourceUpdate(i,t),i.latestData(t);var n=new Date;i.last_updated(n.toLocaleTimeString())},this.type=ko.observable(),this.type.subscribe(function(e){function a(){s.newInstance(i.settings(),function(e){i.datasourceInstance=e,e.updateNow()},i.updateCallback)}if(n(),e in t&&_.isFunction(t[e].newInstance)){var s=t[e];s.external_scripts?head.js(s.external_scripts.slice(0),a):a()}}),this.last_updated=ko.observable("never"),this.last_error=ko.observable(),this.serialize=function(){return{name:i.name(),type:i.type(),settings:i.settings()}},this.deserialize=function(e){i.settings(e.settings),i.name(e.name),i.type(e.type)},this.getDataRepresentation=function(e){var t=Function("data","return "+e+";");return t.call(void 0,i.latestData())},this.updateNow=function(){!_.isUndefined(i.datasourceInstance)&&_.isFunction(i.datasourceInstance.updateNow)&&i.datasourceInstance.updateNow()},this.dispose=function(){n()}},DeveloperConsole=function(e){function t(){function t(e){var t=$(""),i=$(''),a=$(''),s=$('
  • ').click(function(){n=_.without(n,a),t.remove()});n.push(a),e&&a.val(e),i.append(s),o.append(t.append($("").append(a)).append($('').append(i)))}var n=[],i=$("
    "),a=$('
    ADD
    '),s=$('
    ');s.append($('Plugin Script URL'));var o=$("");s.append(o),i.append($("

    Here you can add references to other scripts to load datasource or widget plugins.

    ")).append(s).append(a).append('

    To learn how to build plugins for freeboard, please visit http://freeboard.github.io/freeboard/docs/plugin_example.html

    '),_.each(e.plugins(),function(e){t(e)}),a.click(function(){t()}),new DialogBox(i,"Developer Console","OK",null,function(){_.each(e.plugins(),function(e){$('script[src^="'+e+'"]').remove()}),e.plugins.removeAll(),_.each(n,function(t){var n=t.val();n&&n.length>0&&(e.addPluginSource(n),head.js(n+"?"+Date.now()))})})}return{showDeveloperConsole:function(){t()}}},JSEditor=function(){function e(e){n=e}function t(e,t){var n='// Example: Convert temp from C to F and truncate to 2 decimal places.\n// return (datasources["MyDatasource"].sensor.tempInF * 1.8 + 32).toFixed(2);';e||(e=n);var i=$('
    '),a=$('
    '),s=$(''),o=$('
    This javascript will be re-evaluated any time a datasource referenced here is updated, and the value you return will be displayed in the widget. You can assume this javascript is wrapped in a function of the form function(datasources) where datasources is a collection of javascript objects (keyed by their name) corresponding to the most current data in a datasource.
    ');i.append([o,a,s]),$("body").append(i);var r=CodeMirror(a.get(0),{value:e,mode:"javascript",theme:"ambiance",indentUnit:4,lineNumbers:!0,matchBrackets:!0,autoCloseBrackets:!0}),l=$('Close').click(function(){if(t){var e=r.getValue();e===n&&(e=""),t(e),i.remove()}});s.append(l)}var n="";return{displayJSEditor:function(e,n){t(e,n)},setAssetRoot:function(t){e(t)}}},PluginEditor=function(e,t){function n(e,t){var n=$('
    ').html(t);$("#setting-value-container-"+e).append(n)}function i(){$("#setting-row-instance-name").length?$("#setting-row-instance-name").nextAll().remove():$("#setting-row-plugin-types").nextAll().remove()}function a(e){return!isNaN(parseFloat(e))&&isFinite(e)}function s(n,i,a,s,o){var r=$("");a.multi_input?r.change(function(){var e=[];$(n).find("textarea").each(function(){var t=$(this).val();t&&(e=e.concat(t))}),i.settings[a.name]=e}):r.change(function(){i.settings[a.name]=$(this).val()}),s&&r.val(s),t.createValueEditor(r);var l=$(''),d=$('
    ');d.append(r).append(l);var c=$('
  • ').mousedown(function(e){e.preventDefault(),$(r).val("").focus().insertAtCaret('datasources["').trigger("freeboard-eval")});l.append(c);var u=$('
  • ').mousedown(function(t){t.preventDefault(),e.displayJSEditor(r.val(),function(e){r.val(e),r.change()})});if(l.append(u),o){var p=$('
  • ').mousedown(function(e){e.preventDefault(),d.remove(),$(n).find("textarea:first").change()});l.prepend(p)}$(n).append(d)}function o(e,t,o,r,l){function d(e,t){var n=$('
    ').appendTo(f);return n.append('
    "),$('
    ').appendTo(n)}function c(e){_.each(e,function(e){function t(){p.settings[e.name].length>0?c.show():c.hide()}function n(n){var i=$("").appendTo(f),a={};_.isArray(p.settings[e.name])||(p.settings[e.name]=[]),p.settings[e.name].push(a),_.each(e.settings,function(e){var t=$("").appendTo(i),s="";_.isUndefined(n[e.name])||(s=n[e.name]),a[e.name]=s,$('').appendTo(t).val(s).change(function(){a[e.name]=$(this).val()})}),i.append($('').append($('').append($("
  • ").append($('').click(function(){var n=p.settings[e.name].indexOf(a);-1!=n&&(p.settings[e.name].splice(n,1),i.remove(),t())}))))),o.scrollTop(o[0].scrollHeight),t()}!_.isUndefined(e.default_value)&&_.isUndefined(r[e.name])&&(r[e.name]=e.default_value);var i=e.name;_.isUndefined(e.display_name)||(i=e.display_name);var a=d(e.name,i);switch(e.type){case"array":var o=$('
    ').appendTo(a),l=$('
    ').appendTo(o),c=$("").hide().appendTo(l),u=$("").appendTo(c),f=$("").appendTo(l),h=[];_.each(e.settings,function(e){var t=e.name;_.isUndefined(e.display_name)||(t=e.display_name),$(""+t+"").appendTo(u)}),e.name in r&&(h=r[e.name]),$('
    ADD
    ').appendTo(a).click(function(){var t={};_.each(e.settings,function(e){t[e.name]=""}),n(t)}),_.each(h,function(e){n(e)});break;case"boolean":p.settings[e.name]=r[e.name];var g=$('
    ').appendTo(a),m=$('').prependTo(g).change(function(){p.settings[e.name]=this.checked});e.name in r&&m.prop("checked",r[e.name]);break;case"option":var v=r[e.name],m=$("").appendTo($('
    ').appendTo(a)).change(function(){p.settings[e.name]=$(this).val()});_.each(e.options,function(e){var t,n;_.isObject(e)?(t=e.name,n=e.value):t=e,_.isUndefined(n)&&(n=t),_.isUndefined(v)&&(v=n),$("").text(t).attr("value",n).appendTo(m)}),p.settings[e.name]=v,e.name in r&&m.val(r[e.name]);break;default:if(p.settings[e.name]=r[e.name],"calculated"==e.type){if(e.name in r){var y=r[e.name];if(e.multi_input&&_.isArray(y))for(var w=!1,b=0;y.length>b;b++)s(a,p,e,y[b],w),w=!0;else s(a,p,e,y,!1)}else s(a,p,e,null,!1);if(e.multi_input){var x=$('').mousedown(function(t){t.preventDefault(),s(a,p,e,null,!0)});$(a).siblings(".form-label").append(x)}}else{var m=$('').appendTo(a).change(function(){p.settings[e.name]="number"==e.type?Number($(this).val()):$(this).val()});e.name in r&&m.val(r[e.name])}}_.isUndefined(e.suffix)||a.append($('
    '+e.suffix+"
    ")),_.isUndefined(e.description)||a.append($('
    '+e.description+"
    "))})}var u,p={type:o,settings:{}},f=$("
    "),h=$('
    ').hide();f.append(h),new DialogBox(f,e,"Save","Cancel",function(){$(".validation-error").remove();for(var e=0;u.settings.length>e;e++){var t=u.settings[e];if(t.required&&(_.isUndefined(p.settings[t.name])||""==p.settings[t.name]))return n(t.name,"This is required."),!0;if("number"==t.type&&!a(p.settings[t.name]))return n(t.name,"Must be a number."),!0}_.isFunction(l)&&l(p)});var g,m=_.keys(t);if(m.length>1){var v=d("plugin-types","Type");g=$("").appendTo($('
    ').appendTo(v)),g.append($("").attr("value","undefined")),_.each(t,function(e){g.append($("").text(e.display_name).attr("value",e.type_name))}),g.change(function(){p.type=$(this).val(),p.settings={},i(),u=t[g.val()],_.isUndefined(u)?($("#setting-row-instance-name").hide(),$("#dialog-ok").hide()):($("#setting-row-instance-name").show(),u.description&&u.description.length>0?h.html(u.description).show():h.hide(),$("#dialog-ok").show(),c(u.settings))})}else 1==m.length&&(u=t[m[0]],p.type=u.type_name,p.settings={},c(u.settings));g&&(_.isUndefined(o)?($("#setting-row-instance-name").hide(),$("#dialog-ok").hide()):($("#dialog-ok").show(),g.val(o).trigger("change")))}return{createPluginEditor:function(e,t,n,i,a,s){o(e,t,n,i,a,s)}}},ValueEditor=function(e){function t(e,t){return _.isArray(e)||_.isObject(e)?!0:n(e,t)}function n(e,t){switch(t){case h.ANY:return!0;case h.ARRAY:return _.isArray(e);case h.OBJECT:return _.isObject(e);case h.STRING:return _.isString(e);case h.NUMBER:return _.isNumber(e);case h.BOOLEAN:return _.isBoolean(e)}}function i(e,t){$(e).parent().find(".validation-error").remove(),n(f,t)||$(e).parent().append("
    This field expects an expression that evaluates to type "+t+".
    ")}function a(e){var t=($(e).val().match(/\n/g)||[]).length,n=Math.min(200,20*(t+1));$(e).css({height:n+"px"})}function s(e,n,i){var a=d.exec(e),s=[];if(a)if(""==a[1])_.each(n,function(e){s.push({value:e.name(),entity:void 0,precede_char:"",follow_char:'"]'})});else if(""!=a[1]&&_.isUndefined(a[2])){var o=a[1];_.each(n,function(e){var t=e.name();t!=o&&0==t.indexOf(o)&&s.push({value:t,entity:void 0,precede_char:"",follow_char:'"]'})})}else{var r=_.find(n,function(e){return e.name()===a[1]});if(!_.isUndefined(r)){var l="data",c="";if(!_.isUndefined(a[2])){var u=a[3].lastIndexOf("]")+1;l+=a[3].substring(0,u),c=a[3].substring(u,a[3].length),c=c.replace(/^[\[\"]*/,""),c=c.replace(/[\"\]]*$/,"")}var h=r.getDataRepresentation(l);if(f=h,_.isArray(h)){for(var g=0;h.length>g;g++)if(0==(""+g).indexOf(c)){var m=h[g];t(m,i)&&s.push({value:g,entity:m,precede_char:"[",follow_char:"]",preview:""+m})}}else _.isObject(h)&&_.each(h,function(e,n){0==n.indexOf(c)&&t(e,i)&&s.push({value:n,entity:e,precede_char:'["',follow_char:'"]'})})}}p=s}function o(t,n){var a=$(t).val().substring(0,$(t).getCaretPosition());if(a=a.replace(String.fromCharCode(160)," "),s(a,e.datasources(),n),p.length>0){c||(c=$('').insertAfter(t).width($(t).outerWidth()-2).css("left",$(t).position().left).css("top",$(t).position().top+$(t).outerHeight()-1)),c.empty(),c.scrollTop(0);var o=!0;u=0,_.each(p,function(e,n){var i=r(t,a,e,n);o&&($(i).addClass("selected"),o=!1)})}else i(t,n),$(t).next("ul#value-selector").remove(),c=null,u=-1}function r(e,t,n,i){var a=n.value;n.preview&&(a=a+""+n.preview+"");var s=$("
  • "+a+"
  • ").appendTo(c).mouseenter(function(){$(this).trigger("freeboard-select")}).mousedown(function(e){$(this).trigger("freeboard-insertValue"),e.preventDefault()}).data("freeboard-optionIndex",i).data("freeboard-optionValue",n.value).bind("freeboard-insertValue",function(){var i=n.value;i=n.precede_char+i+n.follow_char;var a=t.lastIndexOf("]");-1!=a?$(e).replaceTextAt(a+1,$(e).val().length,i):$(e).insertAtCaret(i),f=n.entity,$(e).triggerHandler("mouseup")}).bind("freeboard-select",function(){$(this).parent().find("li.selected").removeClass("selected"),$(this).addClass("selected"),u=$(this).data("freeboard-optionIndex")});return s}function l(e,t){$(e).addClass("calculated-value-input").bind("keyup mouseup freeboard-eval",function(n){return!c||"keyup"!=n.type||38!=n.keyCode&&40!=n.keyCode&&13!=n.keyCode?(o(e,t),void 0):(n.preventDefault(),void 0)}).focus(function(){$(e).css({"z-index":3001}),a(e)}).focusout(function(){i(e,t),$(e).css({height:"","z-index":3e3}),$(e).next("ul#value-selector").remove(),c=null,u=-1}).bind("keydown",function(e){if(c)if(38==e.keyCode||40==e.keyCode){e.preventDefault();var t=$(c).find("li");38==e.keyCode?u--:40==e.keyCode&&u++,0>u?u=t.size()-1:u>=t.size()&&(u=0);var n=$(t).eq(u);n.trigger("freeboard-select"),$(c).scrollTop($(n).position().top)}else 13==e.keyCode&&(e.preventDefault(),-1!=u&&$(c).find("li").eq(u).trigger("freeboard-insertValue"))})}var d=RegExp('.*datasources\\["([^"]*)("\\])?(.*)$'),c=null,u=0,p=[],f=null,h={ANY:"any",ARRAY:"array",OBJECT:"object",STRING:"string",NUMBER:"number",BOOLEAN:"boolean"};return{createValueEditor:function(e,t){t?l(e,t):l(e,h.ANY)},EXPECTED_TYPE:h}},function(e){function t(){var e=document.createElement("p"),t=!1;if(e.addEventListener)e.addEventListener("DOMAttrModified",function(){t=!0},!1);else{if(!e.attachEvent)return!1;e.attachEvent("onDOMAttrModified",function(){t=!0})}return e.setAttribute("id","target"),t}function n(t,n){if(t){var i=this.data("attr-old-value");if(n.attributeName.indexOf("style")>=0){i.style||(i.style={});var a=n.attributeName.split(".");n.attributeName=a[0],n.oldValue=i.style[a[1]],n.newValue=a[1]+":"+this.prop("style")[e.camelCase(a[1])],i.style[a[1]]=n.newValue}else n.oldValue=i[n.attributeName],n.newValue=this.attr(n.attributeName),i[n.attributeName]=n.newValue;this.data("attr-old-value",i)}}var i=window.MutationObserver||window.WebKitMutationObserver;e.fn.attrchange=function(a){var s={trackValues:!1,callback:e.noop};if("function"==typeof a?s.callback=a:e.extend(s,a),s.trackValues&&e(this).each(function(t,n){for(var i,a={},t=0,s=n.attributes,o=s.length;o>t;t++)i=s.item(t),a[i.nodeName]=i.value;e(this).data("attr-old-value",a)}),i){var o={subtree:!1,attributes:!0,attributeOldValue:s.trackValues},r=new i(function(t){t.forEach(function(t){var n=t.target;s.trackValues&&(t.newValue=e(n).attr(t.attributeName)),s.callback.call(n,t)})});return this.each(function(){r.observe(this,o)})}return t()?this.on("DOMAttrModified",function(e){e.originalEvent&&(e=e.originalEvent),e.attributeName=e.attrName,e.oldValue=e.prevValue,s.callback.call(this,e)}):"onpropertychange"in document.body?this.on("propertychange",function(t){t.attributeName=window.event.propertyName,n.call(e(this),s.trackValues,t),s.callback.call(this,t)}):this}}(jQuery),function(e){e.eventEmitter={_JQInit:function(){this._JQ=e(this)},emit:function(e,t){!this._JQ&&this._JQInit(),this._JQ.trigger(e,t)},once:function(e,t){!this._JQ&&this._JQInit(),this._JQ.one(e,t)},on:function(e,t){!this._JQ&&this._JQInit(),this._JQ.bind(e,t)},off:function(e,t){!this._JQ&&this._JQInit(),this._JQ.unbind(e,t)}}}(jQuery);var freeboard=function(){function e(e){e=e.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");var t=RegExp("[\\?&]"+e+"=([^&#]*)"),n=t.exec(location.search);return null==n?"":decodeURIComponent(n[1].replace(/\+/g," "))}var t={},n={},i=new FreeboardUI,a=new FreeboardModel(t,n,i),s=new JSEditor,o=new ValueEditor(a),r=new PluginEditor(s,o),l=new DeveloperConsole(a),d={values:{"font-family":'"HelveticaNeue-UltraLight", "Helvetica Neue Ultra Light", "Helvetica Neue", sans-serif',color:"#d3d4d4","font-weight":100}};return ko.bindingHandlers.pluginEditor={init:function(e,s,o,l){var d=ko.unwrap(s()),c={},u=void 0,p="";"datasource"==d.type?(c=t,p="Datasource"):"widget"==d.type?(c=n,p="Widget"):"pane"==d.type&&(p="Pane"),$(e).click(function(){if("delete"==d.operation){var s=$("

    Are you sure you want to delete this "+p+"?

    ");new DialogBox(s,"Confirm Delete","Yes","No",function(){"datasource"==d.type?a.deleteDatasource(l):"widget"==d.type?a.deleteWidget(l):"pane"==d.type&&a.deletePane(l)})}else{var o=void 0;"datasource"==d.type?"add"==d.operation?u={}:(o=l.type(),u=l.settings(),u.name=l.name()):"widget"==d.type?"add"==d.operation?u={}:(o=l.type(),u=l.settings()):"pane"==d.type&&(u={},"edit"==d.operation&&(u.title=l.title(),u.col_width=l.col_width()),c={settings:{settings:[{name:"title",display_name:"Title",type:"text"},{name:"col_width",display_name:"Columns",type:"number",default_value:1,required:!0}]}}),r.createPluginEditor(p,c,o,u,function(s){if("add"==d.operation){if("datasource"==d.type){var o=new DatasourceModel(a,t);a.addDatasource(o),o.name(s.settings.name),delete s.settings.name,o.settings(s.settings),o.type(s.type)}else if("widget"==d.type){var o=new WidgetModel(a,n);o.settings(s.settings),o.type(s.type),l.widgets.push(o),i.attachWidgetEditIcons(e) -}}else"edit"==d.operation&&("pane"==d.type?(l.title(s.settings.title),l.col_width(s.settings.col_width),i.processResize(!1)):("datasource"==d.type&&(l.name(s.settings.name),delete s.settings.name),l.type(s.type),l.settings(s.settings)))})}})}},ko.virtualElements.allowedBindings.datasourceTypeSettings=!0,ko.bindingHandlers.datasourceTypeSettings={update:function(e,t,n,i,a){processPluginSettings(e,t,n,i,a)}},ko.bindingHandlers.pane={init:function(e,t,n,s,o){a.isEditing()&&$(e).css({cursor:"pointer"}),i.addPane(e,s,o.$root.isEditing())},update:function(e,t,n,s){-1==a.panes.indexOf(s)&&i.removePane(e),i.updatePane(e,s)}},ko.bindingHandlers.widget={init:function(e){a.isEditing()&&i.attachWidgetEditIcons($(e).parent())},update:function(e,t,n,i){i.shouldRender()&&($(e).empty(),i.render(e))}},$(function(){function e(){i.processResize(!0)}i.showLoadingIndicator(!0);var t;$(window).resize(function(){clearTimeout(t),t=setTimeout(e,500)})}),{initialize:function(t,n){ko.applyBindings(a);var s=e("load");""!=s?$.ajax({url:s,success:function(e){a.loadDashboard(e),_.isFunction(n)&&n()}}):(a.allow_edit(t),a.setEditing(t),i.showLoadingIndicator(!1),_.isFunction(n)&&n(),freeboard.emit("initialized"))},newDashboard:function(){a.loadDashboard({allow_edit:!0})},loadDashboard:function(e,t){a.loadDashboard(e,t)},serialize:function(){return a.serialize()},setEditing:function(e,t){a.setEditing(e,t)},isEditing:function(){return a.isEditing()},loadDatasourcePlugin:function(e){_.isUndefined(e.display_name)&&(e.display_name=e.type_name),e.settings.unshift({name:"name",display_name:"Name",type:"text",required:!0}),a.addPluginSource(e.source),t[e.type_name]=e,a._datasourceTypes.valueHasMutated()},resize:function(){i.processResize(!0)},loadWidgetPlugin:function(e){_.isUndefined(e.display_name)&&(e.display_name=e.type_name),a.addPluginSource(e.source),n[e.type_name]=e,a._widgetTypes.valueHasMutated()},setAssetRoot:function(e){s.setAssetRoot(e)},addStyle:function(e,t){var n=e+"{"+t+"}",i=$("style#fb-styles");0==i.length&&(i=$(''),$("head").append(i)),i[0].styleSheet?i[0].styleSheet.cssText+=n:i.text(i.text()+n)},showLoadingIndicator:function(e){i.showLoadingIndicator(e)},showDialog:function(e,t,n,i,a){new DialogBox(e,t,n,i,a)},getDatasourceSettings:function(e){var t=a.datasources(),n=_.find(t,function(t){return t.name()===e});return n?n.settings():null},setDatasourceSettings:function(e,t){var n=a.datasources(),i=_.find(n,function(t){return t.name()===e});if(!i)return console.log("Datasource not found"),void 0;var s=_.defaults(t,i.settings());i.settings(s)},getStyleString:function(e){var t="";return _.each(d[e],function(e,n){t=t+n+":"+e+";"}),t},getStyleObject:function(e){return d[e]},showDeveloperConsole:function(){l.showDeveloperConsole()}}}();$.extend(freeboard,jQuery.eventEmitter),function(){var e=function(e,t){function n(e){a&&clearInterval(a),a=setInterval(function(){i.updateNow()},e)}var i=this,a=null,s=e,o=0,r=!1;n(1e3*s.refresh),this.updateNow=function(){if(!(o>1&&!s.use_thingproxy||o>2)){var e=s.url;2==o&&s.use_thingproxy&&(e=("https:"==location.protocol?"https:":"http:")+"//thingproxy.freeboard.io/fetch/"+encodeURI(s.url));var n=s.body;if(n)try{n=JSON.parse(n)}catch(a){}$.ajax({url:e,dataType:1==o?"JSONP":"JSON",type:s.method||"GET",data:n,beforeSend:function(e){try{_.each(s.headers,function(t){var n=t.name,i=t.value;_.isUndefined(n)||_.isUndefined(i)||e.setRequestHeader(n,i)})}catch(t){}},success:function(e){r=!0,t(e)},error:function(){r||(o++,i.updateNow())}})}},this.onDispose=function(){clearInterval(a),a=null},this.onSettingsChanged=function(e){r=!1,o=0,s=e,n(1e3*s.refresh),i.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:!0},{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(t,n,i){n(new e(t,i))}});var t=function(e,t){function n(e){s&&clearInterval(s),s=setInterval(function(){a.updateNow()},e)}function i(e){return e.replace(/\w\S*/g,function(e){return e.charAt(0).toUpperCase()+e.substr(1).toLowerCase()})}var a=this,s=null,o=e;n(1e3*o.refresh),this.updateNow=function(){$.ajax({url:"http://api.openweathermap.org/data/2.5/weather?q="+encodeURIComponent(o.location)+"&units="+o.units,dataType:"JSONP",success:function(e){var n={place_name:e.name,sunrise:new Date(1e3*e.sys.sunrise).toLocaleTimeString(),sunset:new Date(1e3*e.sys.sunset).toLocaleTimeString(),conditions:i(e.weather[0].description),current_temp:e.main.temp,high_temp:e.main.temp_max,low_temp:e.main.temp_min,pressure:e.main.pressure,humidity:e.main.humidity,wind_speed:e.wind.speed,wind_direction:e.wind.deg};t(n)},error:function(){}})},this.onDispose=function(){clearInterval(s),s=null},this.onSettingsChanged=function(e){o=e,a.updateNow(),n(1e3*o.refresh)}};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(e,n,i){n(new t(e,i))}});var n=function(e,t){function n(e){t(e)}var i=this,a=e;this.updateNow=function(){dweetio.get_latest_dweet_for(a.thing_id,function(e,t){e||n(t[0].content)})},this.onDispose=function(){},this.onSettingsChanged=function(e){dweetio.stop_listening(),a=e,dweetio.listen_for(a.thing_id,function(e){n(e.content)})},i.onSettingsChanged(e)};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(e,t,i){t(new n(e,i))}});var i=function(e,t){function n(){r.length>0?(r.length>l&&(t(r[l]),l++),l>=r.length&&o.loop&&(l=0),r.length>l&&(a=setTimeout(n,1e3*o.refresh))):t({})}function i(){r=[],l=0,a&&(clearTimeout(a),a=null)}var a,s=this,o=e,r=[],l=0;this.updateNow=function(){i(),$.ajax({url:o.datafile,dataType:o.is_jsonp?"JSONP":"JSON",success:function(e){r=_.isArray(e)?e:[],l=0,n()},error:function(){}})},this.onDispose=function(){i()},this.onSettingsChanged=function(e){o=e,s.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(e,t,n){t(new i(e,n))}});var a=function(e,t){function n(){a&&(clearTimeout(a),a=null)}function i(){n(),a=setInterval(s.updateNow,1e3*o.refresh)}var a,s=this,o=e;this.updateNow=function(){var e=new Date,n={numeric_value:e.getTime(),full_string_value:e.toLocaleString(),date_string_value:e.toLocaleDateString(),time_string_value:e.toLocaleTimeString(),date_object:e};t(n)},this.onDispose=function(){n()},this.onSettingsChanged=function(e){o=e,i()},i()};freeboard.loadDatasourcePlugin({type_name:"clock",display_name:"Clock",settings:[{name:"refresh",display_name:"Refresh Every",type:"number",suffix:"seconds",default_value:1}],newInstance:function(e,t,n){t(new a(e,n))}})}(),function(){function e(e,t,n){var i=$(t).text();if(i!=e)if($.isNumeric(e)&&$.isNumeric(i)){var a=(""+e).split("."),s=0;a.length>1&&(s=a[1].length),a=(""+i).split(".");var o=0;a.length>1&&(o=a[1].length),jQuery({transitionValue:Number(i),precisionValue:o}).animate({transitionValue:Number(e),precisionValue:s},{duration:n,step:function(){$(t).text(this.transitionValue.toFixed(this.precisionValue))},done:function(){$(t).text(e)}})}else $(t).text(e)}function t(e,t){for(var n=$("
    "),i=0;t.length>i;i++){var s=a[i%a.length],o=t[i];n.append("
    "+o+"
    ")}e.empty().append(n),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 n(e,t,n){var s=$(e).data().values,o=$(e).data().valueMin,r=$(e).data().valueMax;s||(s=[],o=void 0,r=void 0);var l=function(e,t){s[t]||(s[t]=[]),s[t].length>=i&&s[t].shift(),s[t].push(Number(e)),(void 0===o||o>e)&&(o=e),(void 0===r||e>r)&&(r=e)};_.isArray(t)?_.each(t,l):l(t,0),$(e).data().values=s,$(e).data().valueMin=o,$(e).data().valueMax=r;var d=' {{y}}',c=!1;_.each(s,function(t,i){$(e).sparkline(t,{type:"line",composite:c,height:"100%",width:"100%",fillColor:!1,lineColor:a[i%a.length],lineWidth:2,spotRadius:3,spotColor:!1,minSpotColor:"#78AB49",maxSpotColor:"#78AB49",highlightSpotColor:"#9D3926",highlightLineColor:"#9D3926",chartRangeMin:o,chartRangeMax:r,tooltipFormat:n&&n[i]?d+" ("+n[i]+")":d}),c=!0})}var i=100,a=["#FF9900","#FFFFFF","#B3B4B4","#6B6B6B","#28DE28","#13F7F9","#E6EE18","#C41204","#CA3CB8","#0B1CFB"],s=freeboard.getStyleString("values");freeboard.addStyle(".widget-big-text",s+"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",s+"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 o=function(t){function i(){_.isUndefined(a.units)||""==a.units?r.css("max-width","100%"):r.css("max-width",s.innerWidth()-l.outerWidth(!0)+"px")}var a=t,s=$('
    '),o=$('

    '),r=$('
    '),l=$('
    '),d=$('
    ');this.render=function(e){$(e).empty(),$(s).append($('
    ').append(o)).append($('
    ').append($('
    ').append(r).append(l))).append($('
    ').append(d)),$(e).append(s),i()},this.onSettingsChanged=function(e){a=e;var t=!_.isUndefined(e.title)&&""!=e.title,n=!_.isUndefined(e.units)&&""!=e.units;e.sparkline?d.attr("style",null):(delete d.data().values,d.empty(),d.hide()),t?(o.html(_.isUndefined(e.title)?"":e.title),o.attr("style",null)):(o.empty(),o.hide()),n?(l.html(_.isUndefined(e.units)?"":e.units),l.attr("style",null)):(l.empty(),l.hide());var s=30;"big"==e.size&&(s=75,e.sparkline&&(s=60)),r.css({"font-size":s+"px"}),i()},this.onSizeChanged=function(){i()},this.onCalculatedValueChanged=function(t,i){"value"==t&&(a.animate?e(i,r,500):r.text(i),a.sparkline&&n(d,i))},this.onDispose=function(){},this.getHeight=function(){return"big"==a.size||a.sparkline?2:1},this.onSettingsChanged(t)};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:!0},{name:"units",display_name:"Units",type:"text"}],newInstance:function(e,t){t(new o(e))}});var r=0;freeboard.addStyle(".gauge-widget-wrapper","width: 100%;text-align: center;"),freeboard.addStyle(".gauge-widget","width:200px;height:160px;display:inline-block;");var l=function(e){function t(){o&&(s.empty(),n=new JustGage({id:i,value:_.isUndefined(l.min_value)?0:l.min_value,min:_.isUndefined(l.min_value)?0:l.min_value,max:_.isUndefined(l.max_value)?0:l.max_value,label:l.units,showInnerShadow:!1,valueFontColor:"#d3d4d4"}))}var n,i="gauge-"+r++,a=$('

    '),s=$('
    '),o=!1,l=e;this.render=function(e){o=!0,$(e).append(a).append($('
    ').append(s)),t()},this.onSettingsChanged=function(e){e.min_value!=l.min_value||e.max_value!=l.max_value||e.units!=l.units?(l=e,t()):l=e,a.html(e.title)},this.onCalculatedValueChanged=function(e,t){_.isUndefined(n)||n.refresh(Number(t))},this.onDispose=function(){},this.getHeight=function(){return 3},this.onSettingsChanged(e)};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(e,t){t(new l(e))}}),freeboard.addStyle(".sparkline","width:100%;height: 75px;");var d=function(e){var i=$('

    '),a=$('
    '),s=$("
    "),o=e;this.render=function(e){$(e).append(i).append(a).append(s)},this.onSettingsChanged=function(e){o=e,i.html(_.isUndefined(e.title)?"":e.title),e.include_legend&&t(s,e.legend.split(","))},this.onCalculatedValueChanged=function(e,t){o.legend?n(a,t,o.legend.split(",")):n(a,t)},this.onDispose=function(){},this.getHeight=function(){var e=0;if(o.include_legend&&o.legend){var t=o.legend.split(",").length;t>4?e=.5*Math.floor((t-1)/4):t&&(e=.5)}return 2+e},this.onSettingsChanged(e)};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(e,t){t(new d(e))}}),freeboard.addStyle("div.pointer-value","position:absolute;height:95px;margin: auto;top: 0px;bottom: 0px;width: 100%;text-align:center;");var c=function(e){function t(e){if(!e||2>e.length)return[];var t=[];t.push(["m",e[0],e[1]]);for(var n=2;e.length>n;n+=2)t.push(["l",e[n],e[n+1]]);return t.push(["z"]),t}var n,i,a,s,o=3,r=0,l=$('
    '),d=$("
    ");this.render=function(e){a=$(e).width(),s=$(e).height();var r=Math.min(a,s)/2-2*o;n=Raphael($(e).get()[0],a,s);var c=n.circle(a/2,s/2,r);c.attr("stroke","#FF9900"),c.attr("stroke-width",o),i=n.path(t([a/2,s/2-r+o,15,20,-30,0])),i.attr("stroke-width",0),i.attr("fill","#fff"),$(e).append($('
    ').append(l).append(d))},this.onSettingsChanged=function(e){d.html(e.units)},this.onCalculatedValueChanged=function(e,t){if("direction"==e){if(!_.isUndefined(i)){i.animate({transform:"r"+t+","+a/2+","+s/2},250,"bounce")}r=t}else"value_text"==e&&l.html(t)},this.onDispose=function(){},this.getHeight=function(){return 4},this.onSettingsChanged(e)};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(e,t){t(new c(e))}});var u=function(e){function t(){a&&(clearInterval(a),a=null)}function n(){if(i&&s){var e=s+(-1==s.indexOf("?")?"?":"&")+Date.now();$(i).css({"background-image":"url("+e+")"})}}var i,a,s;this.render=function(e){$(e).css({width:"100%",height:"100%","background-size":"cover","background-position":"center"}),i=e},this.onSettingsChanged=function(e){t(),e.refresh&&e.refresh>0&&(a=setInterval(n,1e3*Number(e.refresh)))},this.onCalculatedValueChanged=function(e,t){"src"==e&&(s=t),n()},this.onDispose=function(){t()},this.getHeight=function(){return 4},this.onSettingsChanged(e)};freeboard.loadWidgetPlugin({type_name:"picture",display_name:"Picture",fill_size:!0,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(e,t){t(new u(e))}}),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 p=function(e){function t(){a.toggleClass("on",o),o?i.text(_.isUndefined(s.on_text)?"":s.on_text):i.text(_.isUndefined(s.off_text)?"":s.off_text)}var n=$('

    '),i=$('
    '),a=$('
    '),s=e,o=!1;this.render=function(e){$(e).append(n).append(a).append(i)},this.onSettingsChanged=function(e){s=e,n.html(_.isUndefined(e.title)?"":e.title),t()},this.onCalculatedValueChanged=function(e,n){"value"==e&&(o=Boolean(n)),t()},this.onDispose=function(){},this.getHeight=function(){return 1},this.onSettingsChanged(e)};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(e,t){t(new p(e))}}),freeboard.addStyle(".gm-style-cc a","text-shadow:none;");var f=function(e){function t(){if(n&&i&&s.lat&&s.lon){var e=new google.maps.LatLng(s.lat,s.lon);i.setPosition(e),n.panTo(e)}}var n,i,a=e,s={};this.render=function(e){function a(){var a={zoom:13,center:new google.maps.LatLng(37.235,-115.811111),disableDefaultUI:!0,draggable:!1,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:.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}]}]};n=new google.maps.Map(e,a),google.maps.event.addDomListener(e,"mouseenter",function(e){e.cancelBubble=!0,n.hover||(n.hover=!0,n.setOptions({zoomControl:!0}))}),google.maps.event.addDomListener(e,"mouseleave",function(){n.hover&&(n.setOptions({zoomControl:!1}),n.hover=!1)}),i=new google.maps.Marker({map:n}),t()}window.google&&window.google.maps?a():(window.gmap_initialize=a,head.js("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=gmap_initialize"))},this.onSettingsChanged=function(e){a=e},this.onCalculatedValueChanged=function(e,n){"lat"==e?s.lat=n:"lon"==e&&(s.lon=n),t()},this.onDispose=function(){},this.getHeight=function(){return 4},this.onSettingsChanged(e)};freeboard.loadWidgetPlugin({type_name:"google_map",display_name:"Google Map",fill_size:!0,settings:[{name:"lat",display_name:"Latitude",type:"calculated"},{name:"lon",display_name:"Longitude",type:"calculated"}],newInstance:function(e,t){t(new f(e))}}),freeboard.addStyle(".html-widget","white-space:normal;width:100%;height:100%");var h=function(e){var t=$('
    '),n=e;this.render=function(e){$(e).append(t)},this.onSettingsChanged=function(e){n=e},this.onCalculatedValueChanged=function(e,n){"html"==e&&t.html(n)},this.onDispose=function(){},this.getHeight=function(){return Number(n.height)},this.onSettingsChanged(e)};freeboard.loadWidgetPlugin({type_name:"html",display_name:"HTML",fill_size:!0,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(e,t){t(new h(e))}})}(); \ No newline at end of file +function DialogBox(a,b,c,d,e){function f(){g.fadeOut(200,function(){$(this).remove()})}var g=$(''),h=$('');h.append('

    '+b+"

    "),$("
    ").appendTo(h).append(a);var i=$("").appendTo(h);c&&$(''+c+"").appendTo(i).click(function(){var a=!1;_.isFunction(e)&&(a=e()),a||f()}),d&&$(''+d+"").appendTo(i).click(function(){f()}),g.append(h),$("body").append(g),g.fadeIn(200)}function FreeboardModel(a,b,c){var d=this,e=1;this.version=0,this.isEditing=ko.observable(!1),this.allow_edit=ko.observable(!1),this.allow_edit.subscribe(function(a){a?$("#main-header").show():$("#main-header").hide()}),this.header_image=ko.observable(),this.plugins=ko.observableArray(),this.datasources=ko.observableArray(),this.panes=ko.observableArray(),this.datasourceData={},this.processDatasourceUpdate=function(a,b){var c=a.name();d.datasourceData[c]=b,_.each(d.panes(),function(a){_.each(a.widgets(),function(a){a.processDatasourceUpdate(c)})})},this._datasourceTypes=ko.observable(),this.datasourceTypes=ko.computed({read:function(){d._datasourceTypes();var b=[];return _.each(a,function(a){var c=a.type_name,d=c;_.isUndefined(a.display_name)||(d=a.display_name),b.push({name:c,display_name:d})}),b}}),this._widgetTypes=ko.observable(),this.widgetTypes=ko.computed({read:function(){d._widgetTypes();var a=[];return _.each(b,function(b){var c=b.type_name,d=c;_.isUndefined(b.display_name)||(d=b.display_name),a.push({name:c,display_name:d})}),a}}),this.addPluginSource=function(a){a&&-1==d.plugins.indexOf(a)&&d.plugins.push(a)},this.serialize=function(){var a=[];_.each(d.panes(),function(b){a.push(b.serialize())});var b=[];return _.each(d.datasources(),function(a){b.push(a.serialize())}),{version:e,header_image:d.header_image(),allow_edit:d.allow_edit(),plugins:d.plugins(),panes:a,datasources:b,columns:c.getUserColumns()}},this.deserialize=function(e,f){function g(){c.setUserColumns(e.columns),d.allow_edit(_.isUndefined(e.allow_edit)?!0:e.allow_edit),d.version=e.version||0,d.header_image(e.header_image),_.each(e.datasources,function(b){var c=new DatasourceModel(d,a);c.deserialize(b),d.addDatasource(c)});var g=_.sortBy(e.panes,function(a){return c.getPositionForScreenSize(a).row});_.each(g,function(a){var c=new PaneModel(d,b);c.deserialize(a),d.panes.push(c)}),d.allow_edit()&&0==d.panes().length&&d.setEditing(!0),_.isFunction(f)&&f(),c.processResize(!0)}d.clearDashboard(),_.each(e.plugins,function(a){d.addPluginSource(a)}),_.isArray(e.plugins)&&e.plugins.length>0?head.js(e.plugins,function(){g()}):g()},this.clearDashboard=function(){c.removeAllPanes(),_.each(d.datasources(),function(a){a.dispose()}),_.each(d.panes(),function(a){a.dispose()}),d.plugins.removeAll(),d.datasources.removeAll(),d.panes.removeAll()},this.loadDashboard=function(a,b){c.showLoadingIndicator(!0),d.deserialize(a,function(){c.showLoadingIndicator(!1),_.isFunction(b)&&b(),freeboard.emit("dashboard_loaded")})},this.loadDashboardFromLocalFile=function(){if(window.File&&window.FileReader&&window.FileList&&window.Blob){var a=document.createElement("input");a.type="file",$(a).on("change",function(a){var b=a.target.files;if(b&&b.length>0){var c=b[0],e=new FileReader;e.addEventListener("load",function(a){var b=a.target,c=JSON.parse(b.result);d.loadDashboard(c),d.setEditing(!1)}),e.readAsText(c)}}),$(a).trigger("click")}else alert("Unable to load a file in this browser.")},this.saveDashboard=function(){var a="application/octet-stream",b=document.createElement("a"),c=new Blob([JSON.stringify(d.serialize())],{type:a});document.body.appendChild(b),b.href=window.URL.createObjectURL(c),b.download="dashboard.json",b.target="_self",b.click()},this.addDatasource=function(a){d.datasources.push(a)},this.deleteDatasource=function(a){delete d.datasourceData[a.name()],a.dispose(),d.datasources.remove(a)},this.createPane=function(){var a=new PaneModel(d,b);d.addPane(a)},this.addGridColumnLeft=function(){c.addGridColumnLeft()},this.addGridColumnRight=function(){c.addGridColumnRight()},this.subGridColumnLeft=function(){c.subGridColumnLeft()},this.subGridColumnRight=function(){c.subGridColumnRight()},this.addPane=function(a){d.panes.push(a)},this.deletePane=function(a){a.dispose(),d.panes.remove(a)},this.deleteWidget=function(a){ko.utils.arrayForEach(d.panes(),function(b){b.widgets.remove(a)}),a.dispose()},this.setEditing=function(a,b){if(d.allow_edit()||!a){d.isEditing(a),_.isUndefined(b)&&(b=!0);var e=b?250:0,f=$("#admin-bar").outerHeight();a?($("#toggle-header-icon").addClass("icon-chevron-up").removeClass("icon-wrench"),$(".gridster .gs_w").css({cursor:"pointer"}),$("#main-header").animate({top:"0px"},e),$("#board-content").animate({top:f+20+"px"},e),$("#main-header").data().shown=!0,c.attachWidgetEditIcons($(".sub-section")),c.enableGrid()):($("#toggle-header-icon").addClass("icon-wrench").removeClass("icon-chevron-up"),$(".gridster .gs_w").css({cursor:"default"}),$("#main-header").animate({top:"-"+f+"px"},e),$("#board-content").animate({top:"20"},e),$("#main-header").data().shown=!1,$(".sub-section").unbind(),c.disableGrid()),c.showPaneEditIcons(a,b)}},this.toggleEditing=function(){var a=!d.isEditing();d.setEditing(a)}}function FreeboardUI(){function a(a){var b=e(),c=function(){};a&&(c=function(){var a=this,c=ko.dataFor(a),d=q(c);$(a).attr("data-sizex",Math.min(c.col_width(),b,r.cols)).attr("data-row",d.row).attr("data-col",d.col),c.processSizeChange()}),f(Math.min(b,w)),g(c),d()}function b(a){var b=r.cols+1;f(b)&&g(function(){var b,c=this,d=ko.dataFor(c),e=r.cols>1?r.cols-1:1,f=d.col[e],g=d.row[e];if(a){leftPreviewCol=!0;var h=f1?f-1:1;b={row:g,col:h}}else{var h=f<=r.cols?f:r.cols;b={row:g,col:h}}$(c).attr("data-sizex",Math.min(d.col_width(),r.cols)).attr("data-row",b.row).attr("data-col",b.col)}),d(),w=r.cols}function d(){var a=$(".column-tool"),b=$("#board-content").width(),c=Math.floor(b/v);r.cols<=u?a.addClass("min"):a.removeClass("min"),r.cols>=c?a.addClass("max"):a.removeClass("max")}function e(){var a=$("#board-content").width();return Math.floor(a/v)}function f(a){(void 0===a||u>a)&&(a=u);var b=e();a>b&&(a=b);var c=v*a+a;return $(".responsive-column-width").css("max-width",c),a===r.cols?!1:!0}function g(a){var b=r.$el;b.find("> li").unbind().removeData(),$(".responsive-column-width").css("width",""),r.generate_grid_and_stylesheet(),b.find("> li").each(a),r.init(),$(".responsive-column-width").css("width",r.cols*t+r.cols*s*2)}function h(){return w}function i(a){w=Math.max(u,a)}function j(a,b,c){var d=q(b),e=d.col,f=d.row,g=Number(b.width()),h=Number(b.getCalculatedHeight());r.add_widget(a,g,h,e,f),c&&n(!0),l(b,f,e),$(a).attrchange({trackValues:!0,callback:function(a){"data-row"==a.attributeName?l(b,Number(a.newValue),void 0):"data-col"==a.attributeName&&l(b,void 0,Number(a.newValue))}})}function k(a,b){var c=b.getCalculatedHeight(),d=Number($(a).attr("data-sizey")),e=Number($(a).attr("data-sizex"));(c!=d||b.col_width()!=e)&&r.resize_widget($(a),b.col_width(),c,function(){r.set_dom_grid_height()})}function l(a,b,c){var d=r.cols;_.isUndefined(b)||(a.row[d]=b),_.isUndefined(c)||(a.col[d]=c)}function m(a){a?x.fadeOut(0).appendTo("body").fadeIn(500):x.fadeOut(500).remove()}function n(a,b){_.isUndefined(b)&&(b=!0);var c=b?250:0;a?($(".pane-tools").fadeIn(c),$("#column-tools").fadeIn(c)):($(".pane-tools").fadeOut(c),$("#column-tools").fadeOut(c))}function o(a){$(a).hover(function(){p(this,!0)},function(){p(this,!1)})}function p(a,b){b?$(a).find(".sub-section-tools").fadeIn(250):$(a).find(".sub-section-tools").fadeOut(250)}function q(a){var b=r.cols;if(_.isNumber(a.row)&&_.isNumber(a.col)){var c={};c[b]=a.row,a.row=c,c={},c[b]=a.col,a.col=c}var d=1,e=1e3;for(var f in a.col){if(f==b)return{row:a.row[f],col:a.col[f]};if(a.col[f]>b)d=b;else{var g=b-f;e>g&&(d=f,e=g)}}return d in a.col&&d in a.row?{row:a.row[d],col:a.col[d]}:{row:1,col:d}}var r,s=10,t=300,u=3,v=s+t+s,w=u,x=$('
    ');return ko.bindingHandlers.grid={init:function(b){r=$(b).gridster({widget_margins:[s,s],widget_base_dimensions:[t,10],resize:{enabled:!1,axes:"x"}}).data("gridster"),a(!1),r.disable()}},{showLoadingIndicator:function(a){m(a)},showPaneEditIcons:function(a,b){n(a,b)},attachWidgetEditIcons:function(a){o(a)},getPositionForScreenSize:function(a){return q(a)},processResize:function(b){a(b)},disableGrid:function(){r.disable()},enableGrid:function(){r.enable()},addPane:function(a,b,c){j(a,b,c)},updatePane:function(a,b){k(a,b)},removePane:function(a){r.remove_widget(a)},removeAllPanes:function(){r.remove_all_widgets()},addGridColumnLeft:function(){b(!0)},addGridColumnRight:function(){b(!1)},subGridColumnLeft:function(){c(!0)},subGridColumnRight:function(){c(!1)},getUserColumns:function(){return h()},setUserColumns:function(a){i(a)}}}function PaneModel(a,b){var c=this;this.title=ko.observable(),this.width=ko.observable(1),this.row={},this.col={},this.col_width=ko.observable(1),this.col_width.subscribe(function(){c.processSizeChange()}),this.widgets=ko.observableArray(),this.addWidget=function(a){this.widgets.push(a)},this.widgetCanMoveUp=function(a){return c.widgets.indexOf(a)>=1},this.widgetCanMoveDown=function(a){var b=c.widgets.indexOf(a);return b"),d=$(''),e=$(''),f=$('
  • ').click(function(){c=_.without(c,e),b.remove()});c.push(e),a&&e.val(a),d.append(f),g.append(b.append($("").append(e)).append($('').append(d)))}var c=[],d=$("
    "),e=$('
    ADD
    '),f=$('
    ');f.append($('Plugin Script URL'));var g=$("");f.append(g),d.append($("

    Here you can add references to other scripts to load datasource or widget plugins.

    ")).append(f).append(e).append('

    To learn how to build plugins for freeboard, please visit http://freeboard.github.io/freeboard/docs/plugin_example.html

    '),_.each(a.plugins(),function(a){b(a)}),e.click(function(){b()}),new DialogBox(d,"Developer Console","OK",null,function(){_.each(a.plugins(),function(a){$('script[src^="'+a+'"]').remove()}),a.plugins.removeAll(),_.each(c,function(b){var c=b.val();c&&c.length>0&&(a.addPluginSource(c),head.js(c+"?"+Date.now()))})})}return{showDeveloperConsole:function(){b()}}},JSEditor=function(){function a(a){c=a}function b(a,b){var c='// Example: Convert temp from C to F and truncate to 2 decimal places.\n// return (datasources["MyDatasource"].sensor.tempInF * 1.8 + 32).toFixed(2);';a||(a=c);var d=$('
    '),e=$('
    '),f=$(''),g=$('
    This javascript will be re-evaluated any time a datasource referenced here is updated, and the value you return will be displayed in the widget. You can assume this javascript is wrapped in a function of the form function(datasources) where datasources is a collection of javascript objects (keyed by their name) corresponding to the most current data in a datasource.
    ');d.append([g,e,f]),$("body").append(d);var h=CodeMirror(e.get(0),{value:a,mode:"javascript",theme:"ambiance",indentUnit:4,lineNumbers:!0,matchBrackets:!0,autoCloseBrackets:!0}),i=$('Close').click(function(){if(b){var a=h.getValue();a===c&&(a=""),b(a),d.remove()}});f.append(i)}var c="";return{displayJSEditor:function(a,c){b(a,c)},setAssetRoot:function(b){a(b)}}},PluginEditor=function(a,b){function c(a,b){var c=$('
    ').html(b);$("#setting-value-container-"+a).append(c)}function d(){$("#setting-row-instance-name").length?$("#setting-row-instance-name").nextAll().remove():$("#setting-row-plugin-types").nextAll().remove()}function e(a){return!isNaN(parseFloat(a))&&isFinite(a)}function f(c,d,e,f,g){var h=$("");h.change(e.multi_input?function(){var a=[];$(c).find("textarea").each(function(){var b=$(this).val();b&&(a=a.concat(b))}),d.settings[e.name]=a}:function(){d.settings[e.name]=$(this).val()}),f&&h.val(f),b.createValueEditor(h);var i=$(''),j=$('
    ');j.append(h).append(i);var k=$('
  • ').mousedown(function(a){a.preventDefault(),$(h).val("").focus().insertAtCaret('datasources["').trigger("freeboard-eval")});i.append(k);var l=$('
  • ').mousedown(function(b){b.preventDefault(),a.displayJSEditor(h.val(),function(a){h.val(a),h.change()})});if(i.append(l),g){var m=$('
  • ').mousedown(function(a){a.preventDefault(),j.remove(),$(c).find("textarea:first").change()});i.prepend(m)}$(c).append(j)}function g(a,b,g,h,i){function j(a,b){var c=$('
    ').appendTo(n);return c.append('
    "),$('
    ').appendTo(c)}function k(a,b,c){_.each(a,function(a){function d(){m.settings[a.name].length>0?n.show():n.hide()}function e(b){var c=$("").appendTo(p),e={};_.isArray(m.settings[a.name])||(m.settings[a.name]=[]),m.settings[a.name].push(e),_.each(a.settings,function(a){var d=$("").appendTo(c),f="";_.isUndefined(b[a.name])||(f=b[a.name]),e[a.name]=f,$('').appendTo(d).val(f).change(function(){e[a.name]=$(this).val()})}),c.append($('').append($('').append($("
  • ").append($('').click(function(){var b=m.settings[a.name].indexOf(e);-1!=b&&(m.settings[a.name].splice(b,1),c.remove(),d())}))))),k.scrollTop(k[0].scrollHeight),d()}!_.isUndefined(a.default_value)&&_.isUndefined(h[a.name])&&(h[a.name]=a.default_value);var g=a.name;_.isUndefined(a.display_name)||(g=a.display_name);var i=j(a.name,g);switch(a.type){case"array":var k=$('
    ').appendTo(i),l=$('
    ').appendTo(k),n=$("").hide().appendTo(l),o=$("").appendTo(n),p=$("").appendTo(l),q=[];_.each(a.settings,function(a){var b=a.name;_.isUndefined(a.display_name)||(b=a.display_name),$(""+b+"").appendTo(o)}),a.name in h&&(q=h[a.name]),$('
    ADD
    ').appendTo(i).click(function(){var b={};_.each(a.settings,function(a){b[a.name]=""}),e(b)}),_.each(q,function(a){e(a)});break;case"boolean":m.settings[a.name]=h[a.name];var r=$('
    ').appendTo(i),s=$('').prependTo(r).change(function(){m.settings[a.name]=this.checked});a.name in h&&s.prop("checked",h[a.name]);break;case"option":var t=h[a.name],s=$("").appendTo($('
    ').appendTo(i)).change(function(){m.settings[a.name]=$(this).val()});_.each(a.options,function(a){var b,c;_.isObject(a)?(b=a.name,c=a.value):b=a,_.isUndefined(c)&&(c=b),_.isUndefined(t)&&(t=c),$("").text(b).attr("value",c).appendTo(s)}),m.settings[a.name]=t,a.name in h&&s.val(h[a.name]);break;default:if(m.settings[a.name]=h[a.name],"calculated"==a.type){if(a.name in h){var u=h[a.name];if(a.multi_input&&_.isArray(u))for(var v=!1,w=0;w
  • ').mousedown(function(b){b.preventDefault(),f(i,m,a,null,!0)});$(i).siblings(".form-label").append(x)}}else{var s=$('').appendTo(i).change(function(){m.settings[a.name]="number"==a.type?Number($(this).val()):$(this).val()});if(a.name in h&&s.val(h[a.name]),b&&a.typeahead_data_field&&s.addClass("typeahead_data_field-"+a.typeahead_data_field),b&&a.typeahead_field){var y=[];s.keyup(function(a){a.which>=65&&a.which<=91&&s.trigger("change")}),$(s).autocomplete({source:y,select:function(a,b){s.val(b.item.value),s.trigger("change")}}),s.change(function(){var d=s.val(),e=_.template(b)({input:d});$.get(e,function(b){if(c&&(b=b[c]),b=_.select(b,function(b){return b[a.typeahead_field][0]==d[0]}),y=_.map(b,function(b){return b[a.typeahead_field]}),$(s).autocomplete("option","source",y),1==b.length){b=b[0];for(var e in b)if(b.hasOwnProperty(e)){var f=$(_.template("input.typeahead_data_field-<%= field %>")({field:e}));f&&(f.val(b[e]),f.val()!=s.val()&&f.trigger("change"))}}})})}}}_.isUndefined(a.suffix)||i.append($('
    '+a.suffix+"
    ")),_.isUndefined(a.description)||i.append($('
    '+a.description+"
    "))})}var l,m={type:g,settings:{}},n=$("
    "),o=$('
    ').hide();n.append(o),new DialogBox(n,a,"Save","Cancel",function(){$(".validation-error").remove();for(var a=0;a1){var r=j("plugin-types","Type");p=$("").appendTo($('
    ').appendTo(r)),p.append($("").attr("value","undefined")),_.each(b,function(a){p.append($("").text(a.display_name).attr("value",a.type_name))}),p.change(function(){m.type=$(this).val(),m.settings={},d(),l=b[p.val()],_.isUndefined(l)?($("#setting-row-instance-name").hide(),$("#dialog-ok").hide()):($("#setting-row-instance-name").show(),l.description&&l.description.length>0?o.html(l.description).show():o.hide(),$("#dialog-ok").show(),k(l.settings,l.typeahead_source,l.typeahead_data_segment))})}else 1==q.length&&(l=b[q[0]],m.type=l.type_name,m.settings={},k(l.settings));p&&(_.isUndefined(g)?($("#setting-row-instance-name").hide(),$("#dialog-ok").hide()):($("#dialog-ok").show(),p.val(g).trigger("change")))}return{createPluginEditor:function(a,b,c,d,e,f){g(a,b,c,d,e,f)}}},ValueEditor=function(a){function b(a,b){return _.isArray(a)||_.isObject(a)?!0:c(a,b)}function c(a,b){switch(b){case o.ANY:return!0;case o.ARRAY:return _.isArray(a);case o.OBJECT:return _.isObject(a);case o.STRING:return _.isString(a);case o.NUMBER:return _.isNumber(a);case o.BOOLEAN:return _.isBoolean(a)}}function d(a,b){$(a).parent().find(".validation-error").remove(),c(n,b)||$(a).parent().append("
    This field expects an expression that evaluates to type "+b+".
    ")}function e(a){var b=($(a).val().match(/\n/g)||[]).length,c=Math.min(200,20*(b+1));$(a).css({height:c+"px"})}function f(a,c,d){var e=j.exec(a),f=[];if(e)if(""==e[1])_.each(c,function(a){f.push({value:a.name(),entity:void 0,precede_char:"",follow_char:'"]'})});else if(""!=e[1]&&_.isUndefined(e[2])){var g=e[1];_.each(c,function(a){var b=a.name();b!=g&&0==b.indexOf(g)&&f.push({value:b,entity:void 0,precede_char:"",follow_char:'"]'})})}else{var h=_.find(c,function(a){return a.name()===e[1]});if(!_.isUndefined(h)){var i="data",k="";if(!_.isUndefined(e[2])){var l=e[3].lastIndexOf("]")+1;i+=e[3].substring(0,l),k=e[3].substring(l,e[3].length),k=k.replace(/^[\[\"]*/,""),k=k.replace(/[\"\]]*$/,"")}var o=h.getDataRepresentation(i);if(n=o,_.isArray(o)){for(var p=0;p0){k||(k=$('
      ').insertAfter(b).width($(b).outerWidth()-2).css("left",$(b).position().left).css("top",$(b).position().top+$(b).outerHeight()-1)),k.empty(),k.scrollTop(0);var g=!0;l=0,_.each(m,function(a,c){var d=h(b,e,a,c);g&&($(d).addClass("selected"),g=!1)})}else d(b,c),$(b).next("ul#value-selector").remove(),k=null,l=-1}function h(a,b,c,d){var e=c.value;c.preview&&(e=e+""+c.preview+"");var f=$("
    • "+e+"
    • ").appendTo(k).mouseenter(function(){$(this).trigger("freeboard-select")}).mousedown(function(a){$(this).trigger("freeboard-insertValue"),a.preventDefault()}).data("freeboard-optionIndex",d).data("freeboard-optionValue",c.value).bind("freeboard-insertValue",function(){var d=c.value;d=c.precede_char+d+c.follow_char;var e=b.lastIndexOf("]");-1!=e?$(a).replaceTextAt(e+1,$(a).val().length,d):$(a).insertAtCaret(d),n=c.entity,$(a).triggerHandler("mouseup")}).bind("freeboard-select",function(){$(this).parent().find("li.selected").removeClass("selected"),$(this).addClass("selected"),l=$(this).data("freeboard-optionIndex")});return f}function i(a,b){$(a).addClass("calculated-value-input").bind("keyup mouseup freeboard-eval",function(c){return!k||"keyup"!=c.type||38!=c.keyCode&&40!=c.keyCode&&13!=c.keyCode?void g(a,b):void c.preventDefault()}).focus(function(){$(a).css({"z-index":3001}),e(a)}).focusout(function(){d(a,b),$(a).css({height:"","z-index":3e3}),$(a).next("ul#value-selector").remove(),k=null,l=-1}).bind("keydown",function(a){if(k)if(38==a.keyCode||40==a.keyCode){a.preventDefault();var b=$(k).find("li");38==a.keyCode?l--:40==a.keyCode&&l++,0>l?l=b.size()-1:l>=b.size()&&(l=0);var c=$(b).eq(l);c.trigger("freeboard-select"),$(k).scrollTop($(c).position().top)}else 13==a.keyCode&&(a.preventDefault(),-1!=l&&$(k).find("li").eq(l).trigger("freeboard-insertValue"))})}var j=new RegExp('.*datasources\\["([^"]*)("\\])?(.*)$'),k=null,l=0,m=[],n=null,o={ANY:"any",ARRAY:"array",OBJECT:"object",STRING:"string",NUMBER:"number",BOOLEAN:"boolean"};return{createValueEditor:function(a,b){b?i(a,b):i(a,o.ANY)},EXPECTED_TYPE:o}},function(a){function b(){var a=document.createElement("p"),b=!1;if(a.addEventListener)a.addEventListener("DOMAttrModified",function(){b=!0},!1);else{if(!a.attachEvent)return!1;a.attachEvent("onDOMAttrModified",function(){b=!0})}return a.setAttribute("id","target"),b}function c(b,c){if(b){var d=this.data("attr-old-value");if(c.attributeName.indexOf("style")>=0){d.style||(d.style={});var e=c.attributeName.split(".");c.attributeName=e[0],c.oldValue=d.style[e[1]],c.newValue=e[1]+":"+this.prop("style")[a.camelCase(e[1])],d.style[e[1]]=c.newValue}else c.oldValue=d[c.attributeName],c.newValue=this.attr(c.attributeName),d[c.attributeName]=c.newValue;this.data("attr-old-value",d)}}var d=window.MutationObserver||window.WebKitMutationObserver;a.fn.attrchange=function(e){var f={trackValues:!1,callback:a.noop};if("function"==typeof e?f.callback=e:a.extend(f,e),f.trackValues&&a(this).each(function(b,c){for(var d,e={},b=0,f=c.attributes,g=f.length;g>b;b++)d=f.item(b),e[d.nodeName]=d.value;a(this).data("attr-old-value",e)}),d){var g={subtree:!1,attributes:!0,attributeOldValue:f.trackValues},h=new d(function(b){b.forEach(function(b){var c=b.target;f.trackValues&&(b.newValue=a(c).attr(b.attributeName)),f.callback.call(c,b)})});return this.each(function(){h.observe(this,g)})}return b()?this.on("DOMAttrModified",function(a){a.originalEvent&&(a=a.originalEvent),a.attributeName=a.attrName,a.oldValue=a.prevValue,f.callback.call(this,a)}):"onpropertychange"in document.body?this.on("propertychange",function(b){b.attributeName=window.event.propertyName,c.call(a(this),f.trackValues,b),f.callback.call(this,b)}):this}}(jQuery),function(a){a.eventEmitter={_JQInit:function(){this._JQ=a(this)},emit:function(a,b){!this._JQ&&this._JQInit(),this._JQ.trigger(a,b)},once:function(a,b){!this._JQ&&this._JQInit(),this._JQ.one(a,b)},on:function(a,b){!this._JQ&&this._JQInit(),this._JQ.bind(a,b)},off:function(a,b){!this._JQ&&this._JQInit(),this._JQ.unbind(a,b)}}}(jQuery);var freeboard=function(){function a(a){a=a.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");var b=new RegExp("[\\?&]"+a+"=([^&#]*)"),c=b.exec(location.search);return null==c?"":decodeURIComponent(c[1].replace(/\+/g," "))}var b={},c={},d=new FreeboardUI,e=new FreeboardModel(b,c,d),f=new JSEditor,g=new ValueEditor(e),h=new PluginEditor(f,g),i=new DeveloperConsole(e),j={values:{"font-family":'"HelveticaNeue-UltraLight", "Helvetica Neue Ultra Light", "Helvetica Neue", sans-serif',color:"#d3d4d4","font-weight":100}};return ko.bindingHandlers.pluginEditor={init:function(a,f,g,i){var j=ko.unwrap(f()),k={},l=void 0,m="";"datasource"==j.type?(k=b,m="Datasource"):"widget"==j.type?(k=c,m="Widget"):"pane"==j.type&&(m="Pane"),$(a).click(function(){if("delete"==j.operation){var f=$("

      Are you sure you want to delete this "+m+"?

      ");new DialogBox(f,"Confirm Delete","Yes","No",function(){"datasource"==j.type?e.deleteDatasource(i):"widget"==j.type?e.deleteWidget(i):"pane"==j.type&&e.deletePane(i); + +})}else{var g=void 0;"datasource"==j.type?"add"==j.operation?l={}:(g=i.type(),l=i.settings(),l.name=i.name()):"widget"==j.type?"add"==j.operation?l={}:(g=i.type(),l=i.settings()):"pane"==j.type&&(l={},"edit"==j.operation&&(l.title=i.title(),l.col_width=i.col_width()),k={settings:{settings:[{name:"title",display_name:"Title",type:"text"},{name:"col_width",display_name:"Columns",type:"number",default_value:1,required:!0}]}}),h.createPluginEditor(m,k,g,l,function(f){if("add"==j.operation){if("datasource"==j.type){var g=new DatasourceModel(e,b);e.addDatasource(g),g.name(f.settings.name),delete f.settings.name,g.settings(f.settings),g.type(f.type)}else if("widget"==j.type){var g=new WidgetModel(e,c);g.settings(f.settings),g.type(f.type),i.widgets.push(g),d.attachWidgetEditIcons(a)}}else"edit"==j.operation&&("pane"==j.type?(i.title(f.settings.title),i.col_width(f.settings.col_width),d.processResize(!1)):("datasource"==j.type&&(i.name(f.settings.name),delete f.settings.name),i.type(f.type),i.settings(f.settings)))})}})}},ko.virtualElements.allowedBindings.datasourceTypeSettings=!0,ko.bindingHandlers.datasourceTypeSettings={update:function(a,b,c,d,e){processPluginSettings(a,b,c,d,e)}},ko.bindingHandlers.pane={init:function(a,b,c,f,g){e.isEditing()&&$(a).css({cursor:"pointer"}),d.addPane(a,f,g.$root.isEditing())},update:function(a,b,c,f){-1==e.panes.indexOf(f)&&d.removePane(a),d.updatePane(a,f)}},ko.bindingHandlers.widget={init:function(a){e.isEditing()&&d.attachWidgetEditIcons($(a).parent())},update:function(a,b,c,d){d.shouldRender()&&($(a).empty(),d.render(a))}},$(function(){function a(){d.processResize(!0)}d.showLoadingIndicator(!0);var b;$(window).resize(function(){clearTimeout(b),b=setTimeout(a,500)})}),{initialize:function(b,c){ko.applyBindings(e);var f=a("load");""!=f?$.ajax({url:f,success:function(a){e.loadDashboard(a),_.isFunction(c)&&c()}}):(e.allow_edit(b),e.setEditing(b),d.showLoadingIndicator(!1),_.isFunction(c)&&c(),freeboard.emit("initialized"))},newDashboard:function(){e.loadDashboard({allow_edit:!0})},loadDashboard:function(a,b){e.loadDashboard(a,b)},serialize:function(){return e.serialize()},setEditing:function(a,b){e.setEditing(a,b)},isEditing:function(){return e.isEditing()},loadDatasourcePlugin:function(a){_.isUndefined(a.display_name)&&(a.display_name=a.type_name),a.settings.unshift({name:"name",display_name:"Name",type:"text",required:!0}),e.addPluginSource(a.source),b[a.type_name]=a,e._datasourceTypes.valueHasMutated()},resize:function(){d.processResize(!0)},loadWidgetPlugin:function(a){_.isUndefined(a.display_name)&&(a.display_name=a.type_name),e.addPluginSource(a.source),c[a.type_name]=a,e._widgetTypes.valueHasMutated()},setAssetRoot:function(a){f.setAssetRoot(a)},addStyle:function(a,b){var c=a+"{"+b+"}",d=$("style#fb-styles");0==d.length&&(d=$(''),$("head").append(d)),d[0].styleSheet?d[0].styleSheet.cssText+=c:d.text(d.text()+c)},showLoadingIndicator:function(a){d.showLoadingIndicator(a)},showDialog:function(a,b,c,d,e){new DialogBox(a,b,c,d,e)},getDatasourceSettings:function(a){var b=e.datasources(),c=_.find(b,function(b){return b.name()===a});return c?c.settings():null},setDatasourceSettings:function(a,b){var c=e.datasources(),d=_.find(c,function(b){return b.name()===a});if(!d)return void console.log("Datasource not found");var f=_.defaults(b,d.settings());d.settings(f)},getStyleString:function(a){var b="";return _.each(j[a],function(a,c){b=b+c+":"+a+";"}),b},getStyleObject:function(a){return j[a]},showDeveloperConsole:function(){i.showDeveloperConsole()}}}();$.extend(freeboard,jQuery.eventEmitter),function(){var a=function(a,b){function c(a){e&&clearInterval(e),e=setInterval(function(){d.updateNow()},a)}var d=this,e=null,f=a,g=0,h=!1;c(1e3*f.refresh),this.updateNow=function(){if(!(g>1&&!f.use_thingproxy||g>2)){var a=f.url;2==g&&f.use_thingproxy&&(a=("https:"==location.protocol?"https:":"http:")+"//thingproxy.freeboard.io/fetch/"+encodeURI(f.url));var c=f.body;if(c)try{c=JSON.parse(c)}catch(e){}$.ajax({url:a,dataType:1==g?"JSONP":"JSON",type:f.method||"GET",data:c,beforeSend:function(a){try{_.each(f.headers,function(b){var c=b.name,d=b.value;_.isUndefined(c)||_.isUndefined(d)||a.setRequestHeader(c,d)})}catch(b){}},success:function(a){h=!0,b(a)},error:function(){h||(g++,d.updateNow())}})}},this.onDispose=function(){clearInterval(e),e=null},this.onSettingsChanged=function(a){h=!1,g=0,f=a,c(1e3*f.refresh),d.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:!0},{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(b,c,d){c(new a(b,d))}});var b=function(a,b){function c(a){f&&clearInterval(f),f=setInterval(function(){e.updateNow()},a)}function d(a){return a.replace(/\w\S*/g,function(a){return a.charAt(0).toUpperCase()+a.substr(1).toLowerCase()})}var e=this,f=null,g=a;c(1e3*g.refresh),this.updateNow=function(){$.ajax({url:"http://api.openweathermap.org/data/2.5/weather?q="+encodeURIComponent(g.location)+"&units="+g.units,dataType:"JSONP",success:function(a){var c={place_name:a.name,sunrise:new Date(1e3*a.sys.sunrise).toLocaleTimeString(),sunset:new Date(1e3*a.sys.sunset).toLocaleTimeString(),conditions:d(a.weather[0].description),current_temp:a.main.temp,high_temp:a.main.temp_max,low_temp:a.main.temp_min,pressure:a.main.pressure,humidity:a.main.humidity,wind_speed:a.wind.speed,wind_direction:a.wind.deg};b(c)},error:function(){}})},this.onDispose=function(){clearInterval(f),f=null},this.onSettingsChanged=function(a){g=a,e.updateNow(),c(1e3*g.refresh)}};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(a,c,d){c(new b(a,d))}});var c=function(a,b){function c(a){b(a)}var d=this,e=a;this.updateNow=function(){dweetio.get_latest_dweet_for(e.thing_id,function(a,b){a||c(b[0].content)})},this.onDispose=function(){},this.onSettingsChanged=function(a){dweetio.stop_listening(),e=a,dweetio.listen_for(e.thing_id,function(a){c(a.content)})},d.onSettingsChanged(a)};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(a,b,d){b(new c(a,d))}});var d=function(a,b){function c(){h.length>0?(i=h.length&&g.loop&&(i=0),i1&&(f=e[1].length),e=d.toString().split(".");var g=0;e.length>1&&(g=e[1].length),jQuery({transitionValue:Number(d),precisionValue:g}).animate({transitionValue:Number(a),precisionValue:f},{duration:c,step:function(){$(b).text(this.transitionValue.toFixed(this.precisionValue))},done:function(){$(b).text(a)}})}else $(b).text(a)}function b(a,b){for(var c=$("
      "),d=0;d"+g+"")}a.empty().append(c),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 c(a,b,c){var f=$(a).data().values,g=$(a).data().valueMin,h=$(a).data().valueMax;f||(f=[],g=void 0,h=void 0);var i=function(a,b){f[b]||(f[b]=[]),f[b].length>=d&&f[b].shift(),f[b].push(Number(a)),(void 0===g||g>a)&&(g=a),(void 0===h||a>h)&&(h=a)};_.isArray(b)?_.each(b,i):i(b,0),$(a).data().values=f,$(a).data().valueMin=g,$(a).data().valueMax=h;var j=' {{y}}',k=!1;_.each(f,function(b,d){$(a).sparkline(b,{type:"line",composite:k,height:"100%",width:"100%",fillColor:!1,lineColor:e[d%e.length],lineWidth:2,spotRadius:3,spotColor:!1,minSpotColor:"#78AB49",maxSpotColor:"#78AB49",highlightSpotColor:"#9D3926",highlightLineColor:"#9D3926",chartRangeMin:g,chartRangeMax:h,tooltipFormat:c&&c[d]?j+" ("+c[d]+")":j}),k=!0})}var d=100,e=["#FF9900","#FFFFFF","#B3B4B4","#6B6B6B","#28DE28","#13F7F9","#E6EE18","#C41204","#CA3CB8","#0B1CFB"],f=freeboard.getStyleString("values");freeboard.addStyle(".widget-big-text",f+"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",f+"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 g=function(b){function d(){_.isUndefined(e.units)||""==e.units?h.css("max-width","100%"):h.css("max-width",f.innerWidth()-i.outerWidth(!0)+"px")}var e=b,f=$('
      '),g=$('

      '),h=$('
      '),i=$('
      '),j=$('
      ');this.render=function(a){$(a).empty(),$(f).append($('
      ').append(g)).append($('
      ').append($('
      ').append(h).append(i))).append($('
      ').append(j)),$(a).append(f),d()},this.onSettingsChanged=function(a){e=a;var b=!_.isUndefined(a.title)&&""!=a.title,c=!_.isUndefined(a.units)&&""!=a.units;a.sparkline?j.attr("style",null):(delete j.data().values,j.empty(),j.hide()),b?(g.html(_.isUndefined(a.title)?"":a.title),g.attr("style",null)):(g.empty(),g.hide()),c?(i.html(_.isUndefined(a.units)?"":a.units),i.attr("style",null)):(i.empty(),i.hide());var f=30;"big"==a.size&&(f=75,a.sparkline&&(f=60)),h.css({"font-size":f+"px"}),d()},this.onSizeChanged=function(){d()},this.onCalculatedValueChanged=function(b,d){"value"==b&&(e.animate?a(d,h,500):h.text(d),e.sparkline&&c(j,d))},this.onDispose=function(){},this.getHeight=function(){return"big"==e.size||e.sparkline?2:1},this.onSettingsChanged(b)};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:!0},{name:"units",display_name:"Units",type:"text"}],newInstance:function(a,b){b(new g(a))}});var h=0;freeboard.addStyle(".gauge-widget-wrapper","width: 100%;text-align: center;"),freeboard.addStyle(".gauge-widget","width:200px;height:160px;display:inline-block;");var i=function(a){function b(){g&&(f.empty(),c=new JustGage({id:d,value:_.isUndefined(i.min_value)?0:i.min_value,min:_.isUndefined(i.min_value)?0:i.min_value,max:_.isUndefined(i.max_value)?0:i.max_value,label:i.units,showInnerShadow:!1,valueFontColor:"#d3d4d4"}))}var c,d="gauge-"+h++,e=$('

      '),f=$('
      '),g=!1,i=a;this.render=function(a){g=!0,$(a).append(e).append($('
      ').append(f)),b()},this.onSettingsChanged=function(a){a.min_value!=i.min_value||a.max_value!=i.max_value||a.units!=i.units?(i=a,b()):i=a,e.html(a.title)},this.onCalculatedValueChanged=function(a,b){_.isUndefined(c)||c.refresh(Number(b))},this.onDispose=function(){},this.getHeight=function(){return 3},this.onSettingsChanged(a)};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(a,b){b(new i(a))}}),freeboard.addStyle(".sparkline","width:100%;height: 75px;");var j=function(a){var d=$('

      '),e=$('
      '),f=$("
      "),g=a;this.render=function(a){$(a).append(d).append(e).append(f)},this.onSettingsChanged=function(a){g=a,d.html(_.isUndefined(a.title)?"":a.title),a.include_legend&&b(f,a.legend.split(","))},this.onCalculatedValueChanged=function(a,b){g.legend?c(e,b,g.legend.split(",")):c(e,b)},this.onDispose=function(){},this.getHeight=function(){var a=0;if(g.include_legend&&g.legend){var b=g.legend.split(",").length;b>4?a=.5*Math.floor((b-1)/4):b&&(a=.5)}return 2+a},this.onSettingsChanged(a)};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(a,b){b(new j(a))}}),freeboard.addStyle("div.pointer-value","position:absolute;height:95px;margin: auto;top: 0px;bottom: 0px;width: 100%;text-align:center;");var k=function(a){function b(a){if(!a||a.length<2)return[];var b=[];b.push(["m",a[0],a[1]]);for(var c=2;c'),j=$("
      ");this.render=function(a){e=$(a).width(),f=$(a).height();var h=Math.min(e,f)/2-2*g;c=Raphael($(a).get()[0],e,f);var k=c.circle(e/2,f/2,h);k.attr("stroke","#FF9900"),k.attr("stroke-width",g),d=c.path(b([e/2,f/2-h+g,15,20,-30,0])),d.attr("stroke-width",0),d.attr("fill","#fff"),$(a).append($('
      ').append(i).append(j))},this.onSettingsChanged=function(a){j.html(a.units)},this.onCalculatedValueChanged=function(a,b){if("direction"==a){if(!_.isUndefined(d)){d.animate({transform:"r"+b+","+e/2+","+f/2},250,"bounce")}h=b}else"value_text"==a&&i.html(b)},this.onDispose=function(){},this.getHeight=function(){return 4},this.onSettingsChanged(a)};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(a,b){b(new k(a))}});var l=function(a){function b(){e&&(clearInterval(e),e=null)}function c(){if(d&&f){var a=f+(-1==f.indexOf("?")?"?":"&")+Date.now();$(d).css({"background-image":"url("+a+")"})}}var d,e,f;this.render=function(a){$(a).css({width:"100%",height:"100%","background-size":"cover","background-position":"center"}),d=a},this.onSettingsChanged=function(a){b(),a.refresh&&a.refresh>0&&(e=setInterval(c,1e3*Number(a.refresh)))},this.onCalculatedValueChanged=function(a,b){"src"==a&&(f=b),c()},this.onDispose=function(){b()},this.getHeight=function(){return 4},this.onSettingsChanged(a)};freeboard.loadWidgetPlugin({type_name:"picture",display_name:"Picture",fill_size:!0,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(a,b){b(new l(a))}}),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 m=function(a){function b(){e.toggleClass("on",g),d.text(g?_.isUndefined(f.on_text)?"":f.on_text:_.isUndefined(f.off_text)?"":f.off_text)}var c=$('

      '),d=$('
      '),e=$('
      '),f=a,g=!1;this.render=function(a){$(a).append(c).append(e).append(d)},this.onSettingsChanged=function(a){f=a,c.html(_.isUndefined(a.title)?"":a.title),b()},this.onCalculatedValueChanged=function(a,c){"value"==a&&(g=Boolean(c)),b()},this.onDispose=function(){},this.getHeight=function(){return 1},this.onSettingsChanged(a)};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(a,b){b(new m(a))}}),freeboard.addStyle(".gm-style-cc a","text-shadow:none;");var n=function(a){function b(){if(c&&d&&f.lat&&f.lon){var a=new google.maps.LatLng(f.lat,f.lon);d.setPosition(a),c.panTo(a)}}var c,d,e=a,f={};this.render=function(a){function e(){var e={zoom:13,center:new google.maps.LatLng(37.235,-115.811111),disableDefaultUI:!0,draggable:!1,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:.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}]}]};c=new google.maps.Map(a,e),google.maps.event.addDomListener(a,"mouseenter",function(a){a.cancelBubble=!0,c.hover||(c.hover=!0,c.setOptions({zoomControl:!0}))}),google.maps.event.addDomListener(a,"mouseleave",function(){c.hover&&(c.setOptions({zoomControl:!1}),c.hover=!1)}),d=new google.maps.Marker({map:c}),b()}window.google&&window.google.maps?e():(window.gmap_initialize=e,head.js("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=gmap_initialize"))},this.onSettingsChanged=function(a){e=a},this.onCalculatedValueChanged=function(a,c){"lat"==a?f.lat=c:"lon"==a&&(f.lon=c),b()},this.onDispose=function(){},this.getHeight=function(){return 4},this.onSettingsChanged(a)};freeboard.loadWidgetPlugin({type_name:"google_map",display_name:"Google Map",fill_size:!0,settings:[{name:"lat",display_name:"Latitude",type:"calculated"},{name:"lon",display_name:"Longitude",type:"calculated"}],newInstance:function(a,b){b(new n(a))}}),freeboard.addStyle(".html-widget","white-space:normal;width:100%;height:100%");var o=function(a){var b=$('
      '),c=a;this.render=function(a){$(a).append(b)},this.onSettingsChanged=function(a){c=a},this.onCalculatedValueChanged=function(a,c){"html"==a&&b.html(c)},this.onDispose=function(){},this.getHeight=function(){return Number(c.height)},this.onSettingsChanged(a)};freeboard.loadWidgetPlugin({type_name:"html",display_name:"HTML",fill_size:!0,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(a,b){b(new o(a))}})}(); \ No newline at end of file diff --git a/js/freeboard.js b/js/freeboard.js index 207b07c..5e1f410 100644 --- a/js/freeboard.js +++ b/js/freeboard.js @@ -1,2369 +1,2369 @@ DatasourceModel = function(theFreeboardModel, datasourcePlugins) { - var self = this; + var self = this; - function disposeDatasourceInstance() - { - if(!_.isUndefined(self.datasourceInstance)) - { - if(_.isFunction(self.datasourceInstance.onDispose)) - { - self.datasourceInstance.onDispose(); - } + function disposeDatasourceInstance() + { + if(!_.isUndefined(self.datasourceInstance)) + { + if(_.isFunction(self.datasourceInstance.onDispose)) + { + self.datasourceInstance.onDispose(); + } - self.datasourceInstance = undefined; - } - } + self.datasourceInstance = undefined; + } + } - this.name = ko.observable(); - this.latestData = ko.observable(); - this.settings = ko.observable({}); - this.settings.subscribe(function(newValue) - { - if(!_.isUndefined(self.datasourceInstance) && _.isFunction(self.datasourceInstance.onSettingsChanged)) - { - self.datasourceInstance.onSettingsChanged(newValue); - } - }); + this.name = ko.observable(); + this.latestData = ko.observable(); + this.settings = ko.observable({}); + this.settings.subscribe(function(newValue) + { + if(!_.isUndefined(self.datasourceInstance) && _.isFunction(self.datasourceInstance.onSettingsChanged)) + { + self.datasourceInstance.onSettingsChanged(newValue); + } + }); - this.updateCallback = function(newData) - { - theFreeboardModel.processDatasourceUpdate(self, newData); + this.updateCallback = function(newData) + { + theFreeboardModel.processDatasourceUpdate(self, newData); - self.latestData(newData); + self.latestData(newData); - var now = new Date(); - self.last_updated(now.toLocaleTimeString()); - } + var now = new Date(); + self.last_updated(now.toLocaleTimeString()); + } - this.type = ko.observable(); - this.type.subscribe(function(newValue) - { - disposeDatasourceInstance(); + this.type = ko.observable(); + this.type.subscribe(function(newValue) + { + disposeDatasourceInstance(); - if((newValue in datasourcePlugins) && _.isFunction(datasourcePlugins[newValue].newInstance)) - { - var datasourceType = datasourcePlugins[newValue]; + if((newValue in datasourcePlugins) && _.isFunction(datasourcePlugins[newValue].newInstance)) + { + var datasourceType = datasourcePlugins[newValue]; - function finishLoad() - { - datasourceType.newInstance(self.settings(), function(datasourceInstance) - { + function finishLoad() + { + datasourceType.newInstance(self.settings(), function(datasourceInstance) + { - self.datasourceInstance = datasourceInstance; - datasourceInstance.updateNow(); + self.datasourceInstance = datasourceInstance; + datasourceInstance.updateNow(); - }, self.updateCallback); - } + }, self.updateCallback); + } - // Do we need to load any external scripts? - if(datasourceType.external_scripts) - { - head.js(datasourceType.external_scripts.slice(0), finishLoad); // Need to clone the array because head.js adds some weird functions to it - } - else - { - finishLoad(); - } - } - }); + // Do we need to load any external scripts? + if(datasourceType.external_scripts) + { + head.js(datasourceType.external_scripts.slice(0), finishLoad); // Need to clone the array because head.js adds some weird functions to it + } + else + { + finishLoad(); + } + } + }); - this.last_updated = ko.observable("never"); - this.last_error = ko.observable(); + this.last_updated = ko.observable("never"); + this.last_error = ko.observable(); - this.serialize = function() - { - return { - name : self.name(), - type : self.type(), - settings: self.settings() - }; - } + this.serialize = function() + { + return { + name : self.name(), + type : self.type(), + settings: self.settings() + }; + } - this.deserialize = function(object) - { - self.settings(object.settings); - self.name(object.name); - self.type(object.type); - } + this.deserialize = function(object) + { + self.settings(object.settings); + self.name(object.name); + self.type(object.type); + } - this.getDataRepresentation = function(dataPath) - { - var valueFunction = new Function("data", "return " + dataPath + ";"); - return valueFunction.call(undefined, self.latestData()); - } + this.getDataRepresentation = function(dataPath) + { + var valueFunction = new Function("data", "return " + dataPath + ";"); + return valueFunction.call(undefined, self.latestData()); + } - this.updateNow = function() - { - if(!_.isUndefined(self.datasourceInstance) && _.isFunction(self.datasourceInstance.updateNow)) - { - self.datasourceInstance.updateNow(); - } - } + this.updateNow = function() + { + if(!_.isUndefined(self.datasourceInstance) && _.isFunction(self.datasourceInstance.updateNow)) + { + self.datasourceInstance.updateNow(); + } + } - this.dispose = function() - { - disposeDatasourceInstance(); - } + this.dispose = function() + { + disposeDatasourceInstance(); + } } DeveloperConsole = function(theFreeboardModel) { - function showDeveloperConsole() - { - var pluginScriptsInputs = []; - var container = $('
      '); - var addScript = $('
      ADD
      '); - var table = $('
      '); + function showDeveloperConsole() + { + var pluginScriptsInputs = []; + var container = $('
      '); + var addScript = $('
      ADD
      '); + var table = $('
      '); - table.append($('Plugin Script URL')); + table.append($('Plugin Script URL')); - var tableBody = $(""); + var tableBody = $(""); - table.append(tableBody); + table.append(tableBody); - container.append($("

      Here you can add references to other scripts to load datasource or widget plugins.

      ")) - .append(table) - .append(addScript) + container.append($("

      Here you can add references to other scripts to load datasource or widget plugins.

      ")) + .append(table) + .append(addScript) .append('

      To learn how to build plugins for freeboard, please visit http://freeboard.github.io/freeboard/docs/plugin_example.html

      '); - function refreshScript(scriptURL) - { - $('script[src="' + scriptURL + '"]').remove(); - } + function refreshScript(scriptURL) + { + $('script[src="' + scriptURL + '"]').remove(); + } - function addNewScriptRow(scriptURL) - { - var tableRow = $(''); - var tableOperations = $('
        '); - var scriptInput = $(''); - var deleteOperation = $('
      • ').click(function(e){ - pluginScriptsInputs = _.without(pluginScriptsInputs, scriptInput); - tableRow.remove(); - }); + function addNewScriptRow(scriptURL) + { + var tableRow = $(''); + var tableOperations = $('
          '); + var scriptInput = $(''); + var deleteOperation = $('
        • ').click(function(e){ + pluginScriptsInputs = _.without(pluginScriptsInputs, scriptInput); + tableRow.remove(); + }); - pluginScriptsInputs.push(scriptInput); + pluginScriptsInputs.push(scriptInput); - if(scriptURL) - { - scriptInput.val(scriptURL); - } + if(scriptURL) + { + scriptInput.val(scriptURL); + } - tableOperations.append(deleteOperation); - tableBody - .append(tableRow - .append($('').append(scriptInput)) - .append($('').append(tableOperations))); - } + tableOperations.append(deleteOperation); + tableBody + .append(tableRow + .append($('').append(scriptInput)) + .append($('').append(tableOperations))); + } - _.each(theFreeboardModel.plugins(), function(pluginSource){ + _.each(theFreeboardModel.plugins(), function(pluginSource){ - addNewScriptRow(pluginSource); + addNewScriptRow(pluginSource); - }); + }); - addScript.click(function(e) - { - addNewScriptRow(); - }); + addScript.click(function(e) + { + addNewScriptRow(); + }); - new DialogBox(container, "Developer Console", "OK", null, function(){ + new DialogBox(container, "Developer Console", "OK", null, function(){ - // Unload our previous scripts - _.each(theFreeboardModel.plugins(), function(pluginSource){ + // Unload our previous scripts + _.each(theFreeboardModel.plugins(), function(pluginSource){ - $('script[src^="' + pluginSource + '"]').remove(); + $('script[src^="' + pluginSource + '"]').remove(); - }); + }); - theFreeboardModel.plugins.removeAll(); + theFreeboardModel.plugins.removeAll(); - _.each(pluginScriptsInputs, function(scriptInput){ + _.each(pluginScriptsInputs, function(scriptInput){ - var scriptURL = scriptInput.val(); + var scriptURL = scriptInput.val(); - if(scriptURL && scriptURL.length > 0) - { - theFreeboardModel.addPluginSource(scriptURL); + if(scriptURL && scriptURL.length > 0) + { + theFreeboardModel.addPluginSource(scriptURL); - // Load the script with a cache buster - head.js(scriptURL + "?" + Date.now()); - } - }); + // Load the script with a cache buster + head.js(scriptURL + "?" + Date.now()); + } + }); - }); - } + }); + } - // Public API - return { - showDeveloperConsole : function() - { - showDeveloperConsole(); - } - } + // Public API + return { + showDeveloperConsole : function() + { + showDeveloperConsole(); + } + } } function DialogBox(contentElement, title, okTitle, cancelTitle, okCallback) { - var modal_width = 900; + var modal_width = 900; - // Initialize our modal overlay - var overlay = $(''); + // Initialize our modal overlay + var overlay = $(''); - var modalDialog = $(''); + var modalDialog = $(''); - function closeModal() - { - overlay.fadeOut(200, function() - { - $(this).remove(); - }); - } + function closeModal() + { + overlay.fadeOut(200, function() + { + $(this).remove(); + }); + } - // Create our header - modalDialog.append('

          ' + title + "

          "); + // Create our header + modalDialog.append('

          ' + title + "

          "); - $('
          ').appendTo(modalDialog).append(contentElement); + $('
          ').appendTo(modalDialog).append(contentElement); - // Create our footer - var footer = $('
          ').appendTo(modalDialog); + // Create our footer + var footer = $('
          ').appendTo(modalDialog); - if(okTitle) - { - $('' + okTitle + '').appendTo(footer).click(function() - { - var hold = false; + if(okTitle) + { + $('' + okTitle + '').appendTo(footer).click(function() + { + var hold = false; - if(_.isFunction(okCallback)) - { - hold = okCallback(); - } + if(_.isFunction(okCallback)) + { + hold = okCallback(); + } - if(!hold) - { - closeModal(); - } - }); - } + if(!hold) + { + closeModal(); + } + }); + } - if(cancelTitle) - { - $('' + cancelTitle + '').appendTo(footer).click(function() - { - closeModal(); - }); - } + if(cancelTitle) + { + $('' + cancelTitle + '').appendTo(footer).click(function() + { + closeModal(); + }); + } - overlay.append(modalDialog); - $("body").append(overlay); - overlay.fadeIn(200); + overlay.append(modalDialog); + $("body").append(overlay); + overlay.fadeIn(200); } function FreeboardModel(datasourcePlugins, widgetPlugins, freeboardUI) { - var self = this; - - var SERIALIZATION_VERSION = 1; - - this.version = 0; - this.isEditing = ko.observable(false); - this.allow_edit = ko.observable(false); - this.allow_edit.subscribe(function(newValue) - { - if(newValue) - { - $("#main-header").show(); - } - else - { - $("#main-header").hide(); - } - }); - - this.header_image = ko.observable(); - this.plugins = ko.observableArray(); - this.datasources = ko.observableArray(); - this.panes = ko.observableArray(); - this.datasourceData = {}; - this.processDatasourceUpdate = function(datasourceModel, newData) - { - var datasourceName = datasourceModel.name(); - - self.datasourceData[datasourceName] = newData; - - _.each(self.panes(), function(pane) - { - _.each(pane.widgets(), function(widget) - { - widget.processDatasourceUpdate(datasourceName); - }); - }); - } - - this._datasourceTypes = ko.observable(); - this.datasourceTypes = ko.computed({ - read: function() - { - self._datasourceTypes(); - - var returnTypes = []; - - _.each(datasourcePlugins, function(datasourcePluginType) - { - var typeName = datasourcePluginType.type_name; - var displayName = typeName; - - if(!_.isUndefined(datasourcePluginType.display_name)) - { - displayName = datasourcePluginType.display_name; - } - - returnTypes.push({ - name : typeName, - display_name: displayName - }); - }); - - return returnTypes; - } - }); - - this._widgetTypes = ko.observable(); - this.widgetTypes = ko.computed({ - read: function() - { - self._widgetTypes(); - - var returnTypes = []; - - _.each(widgetPlugins, function(widgetPluginType) - { - var typeName = widgetPluginType.type_name; - var displayName = typeName; - - if(!_.isUndefined(widgetPluginType.display_name)) - { - displayName = widgetPluginType.display_name; - } - - returnTypes.push({ - name : typeName, - display_name: displayName - }); - }); - - return returnTypes; - } - }); - - this.addPluginSource = function(pluginSource) - { - if(pluginSource && self.plugins.indexOf(pluginSource) == -1) - { - self.plugins.push(pluginSource); - } - } - - this.serialize = function() - { - var panes = []; - - _.each(self.panes(), function(pane) - { - panes.push(pane.serialize()); - }); - - var datasources = []; - - _.each(self.datasources(), function(datasource) - { - datasources.push(datasource.serialize()); - }); - - return { - version : SERIALIZATION_VERSION, - header_image: self.header_image(), - allow_edit : self.allow_edit(), - plugins : self.plugins(), - panes : panes, - datasources : datasources, - columns : freeboardUI.getUserColumns() - }; - } - - this.deserialize = function(object, finishedCallback) - { - self.clearDashboard(); - - function finishLoad() - { - freeboardUI.setUserColumns(object.columns); - - if(!_.isUndefined(object.allow_edit)) - { - self.allow_edit(object.allow_edit); - } - else - { - self.allow_edit(true); - } - self.version = object.version || 0; - self.header_image(object.header_image); - - _.each(object.datasources, function(datasourceConfig) - { - var datasource = new DatasourceModel(self, datasourcePlugins); - datasource.deserialize(datasourceConfig); - self.addDatasource(datasource); - }); - - var sortedPanes = _.sortBy(object.panes, function(pane){ - return freeboardUI.getPositionForScreenSize(pane).row; - }); - - _.each(sortedPanes, function(paneConfig) - { - var pane = new PaneModel(self, widgetPlugins); - pane.deserialize(paneConfig); - self.panes.push(pane); - }); - - if(self.allow_edit() && self.panes().length == 0) - { - self.setEditing(true); - } - - if(_.isFunction(finishedCallback)) - { - finishedCallback(); - } - - freeboardUI.processResize(true); - } - - // This could have been self.plugins(object.plugins), but for some weird reason head.js was causing a function to be added to the list of plugins. - _.each(object.plugins, function(plugin) - { - self.addPluginSource(plugin); - }); - - // Load any plugins referenced in this definition - if(_.isArray(object.plugins) && object.plugins.length > 0) - { - head.js(object.plugins, function() - { - finishLoad(); - }); - } - else - { - finishLoad(); - } - } - - this.clearDashboard = function() - { - freeboardUI.removeAllPanes(); - - _.each(self.datasources(), function(datasource) - { - datasource.dispose(); - }); - - _.each(self.panes(), function(pane) - { - pane.dispose(); - }); - - self.plugins.removeAll(); - self.datasources.removeAll(); - self.panes.removeAll(); - } - - this.loadDashboard = function(dashboardData, callback) - { - freeboardUI.showLoadingIndicator(true); - self.deserialize(dashboardData, function() - { - freeboardUI.showLoadingIndicator(false); - - if(_.isFunction(callback)) - { - callback(); - } - - freeboard.emit("dashboard_loaded"); - }); - } - - this.loadDashboardFromLocalFile = function() - { - // Check for the various File API support. - if(window.File && window.FileReader && window.FileList && window.Blob) - { - var input = document.createElement('input'); - input.type = "file"; - $(input).on("change", function(event) - { - var files = event.target.files; - - if(files && files.length > 0) - { - var file = files[0]; - var reader = new FileReader(); - - reader.addEventListener("load", function(fileReaderEvent) - { - - var textFile = fileReaderEvent.target; - var jsonObject = JSON.parse(textFile.result); - - - self.loadDashboard(jsonObject); - self.setEditing(false); - }); - - reader.readAsText(file); - } - - }); - $(input).trigger("click"); - } - else - { - alert('Unable to load a file in this browser.'); - } - } - - this.saveDashboard = function() - { - var contentType = 'application/octet-stream'; - var a = document.createElement('a'); - var blob = new Blob([JSON.stringify(self.serialize())], {'type': contentType}); - document.body.appendChild(a); - a.href = window.URL.createObjectURL(blob); - a.download = "dashboard.json"; - a.target="_self"; - a.click(); - } - - this.addDatasource = function(datasource) - { - self.datasources.push(datasource); - } - - this.deleteDatasource = function(datasource) - { - delete self.datasourceData[datasource.name()]; - datasource.dispose(); - self.datasources.remove(datasource); - } - - this.createPane = function() - { - var newPane = new PaneModel(self, widgetPlugins); - self.addPane(newPane); - } - - this.addGridColumnLeft = function() - { - freeboardUI.addGridColumnLeft(); - } - - this.addGridColumnRight = function() - { - freeboardUI.addGridColumnRight(); - } - - this.subGridColumnLeft = function() - { - freeboardUI.subGridColumnLeft(); - } - - this.subGridColumnRight = function() - { - freeboardUI.subGridColumnRight(); - } - - this.addPane = function(pane) - { - self.panes.push(pane); - } - - this.deletePane = function(pane) - { - pane.dispose(); - self.panes.remove(pane); - } - - this.deleteWidget = function(widget) - { - ko.utils.arrayForEach(self.panes(), function(pane) - { - pane.widgets.remove(widget); - }); - - widget.dispose(); - } - - this.setEditing = function(editing, animate) - { - // Don't allow editing if it's not allowed - if(!self.allow_edit() && editing) - { - return; - } - - self.isEditing(editing); - - if(_.isUndefined(animate)) - { - animate = true; - } - - var animateLength = (animate) ? 250 : 0; - var barHeight = $("#admin-bar").outerHeight(); - - if(!editing) - { - $("#toggle-header-icon").addClass("icon-wrench").removeClass("icon-chevron-up"); - $(".gridster .gs_w").css({cursor: "default"}); - $("#main-header").animate({"top": "-" + barHeight + "px"}, animateLength); - $("#board-content").animate({"top": "20"}, animateLength); - $("#main-header").data().shown = false; - $(".sub-section").unbind(); - freeboardUI.disableGrid(); - } - else - { - $("#toggle-header-icon").addClass("icon-chevron-up").removeClass("icon-wrench"); - $(".gridster .gs_w").css({cursor: "pointer"}); - $("#main-header").animate({"top": "0px"}, animateLength); - $("#board-content").animate({"top": (barHeight + 20) + "px"}, animateLength); - $("#main-header").data().shown = true; - freeboardUI.attachWidgetEditIcons($(".sub-section")); - freeboardUI.enableGrid(); - } - - freeboardUI.showPaneEditIcons(editing, animate); - } - - this.toggleEditing = function() - { - var editing = !self.isEditing(); - self.setEditing(editing); - } + var self = this; + + var SERIALIZATION_VERSION = 1; + + this.version = 0; + this.isEditing = ko.observable(false); + this.allow_edit = ko.observable(false); + this.allow_edit.subscribe(function(newValue) + { + if(newValue) + { + $("#main-header").show(); + } + else + { + $("#main-header").hide(); + } + }); + + this.header_image = ko.observable(); + this.plugins = ko.observableArray(); + this.datasources = ko.observableArray(); + this.panes = ko.observableArray(); + this.datasourceData = {}; + this.processDatasourceUpdate = function(datasourceModel, newData) + { + var datasourceName = datasourceModel.name(); + + self.datasourceData[datasourceName] = newData; + + _.each(self.panes(), function(pane) + { + _.each(pane.widgets(), function(widget) + { + widget.processDatasourceUpdate(datasourceName); + }); + }); + } + + this._datasourceTypes = ko.observable(); + this.datasourceTypes = ko.computed({ + read: function() + { + self._datasourceTypes(); + + var returnTypes = []; + + _.each(datasourcePlugins, function(datasourcePluginType) + { + var typeName = datasourcePluginType.type_name; + var displayName = typeName; + + if(!_.isUndefined(datasourcePluginType.display_name)) + { + displayName = datasourcePluginType.display_name; + } + + returnTypes.push({ + name : typeName, + display_name: displayName + }); + }); + + return returnTypes; + } + }); + + this._widgetTypes = ko.observable(); + this.widgetTypes = ko.computed({ + read: function() + { + self._widgetTypes(); + + var returnTypes = []; + + _.each(widgetPlugins, function(widgetPluginType) + { + var typeName = widgetPluginType.type_name; + var displayName = typeName; + + if(!_.isUndefined(widgetPluginType.display_name)) + { + displayName = widgetPluginType.display_name; + } + + returnTypes.push({ + name : typeName, + display_name: displayName + }); + }); + + return returnTypes; + } + }); + + this.addPluginSource = function(pluginSource) + { + if(pluginSource && self.plugins.indexOf(pluginSource) == -1) + { + self.plugins.push(pluginSource); + } + } + + this.serialize = function() + { + var panes = []; + + _.each(self.panes(), function(pane) + { + panes.push(pane.serialize()); + }); + + var datasources = []; + + _.each(self.datasources(), function(datasource) + { + datasources.push(datasource.serialize()); + }); + + return { + version : SERIALIZATION_VERSION, + header_image: self.header_image(), + allow_edit : self.allow_edit(), + plugins : self.plugins(), + panes : panes, + datasources : datasources, + columns : freeboardUI.getUserColumns() + }; + } + + this.deserialize = function(object, finishedCallback) + { + self.clearDashboard(); + + function finishLoad() + { + freeboardUI.setUserColumns(object.columns); + + if(!_.isUndefined(object.allow_edit)) + { + self.allow_edit(object.allow_edit); + } + else + { + self.allow_edit(true); + } + self.version = object.version || 0; + self.header_image(object.header_image); + + _.each(object.datasources, function(datasourceConfig) + { + var datasource = new DatasourceModel(self, datasourcePlugins); + datasource.deserialize(datasourceConfig); + self.addDatasource(datasource); + }); + + var sortedPanes = _.sortBy(object.panes, function(pane){ + return freeboardUI.getPositionForScreenSize(pane).row; + }); + + _.each(sortedPanes, function(paneConfig) + { + var pane = new PaneModel(self, widgetPlugins); + pane.deserialize(paneConfig); + self.panes.push(pane); + }); + + if(self.allow_edit() && self.panes().length == 0) + { + self.setEditing(true); + } + + if(_.isFunction(finishedCallback)) + { + finishedCallback(); + } + + freeboardUI.processResize(true); + } + + // This could have been self.plugins(object.plugins), but for some weird reason head.js was causing a function to be added to the list of plugins. + _.each(object.plugins, function(plugin) + { + self.addPluginSource(plugin); + }); + + // Load any plugins referenced in this definition + if(_.isArray(object.plugins) && object.plugins.length > 0) + { + head.js(object.plugins, function() + { + finishLoad(); + }); + } + else + { + finishLoad(); + } + } + + this.clearDashboard = function() + { + freeboardUI.removeAllPanes(); + + _.each(self.datasources(), function(datasource) + { + datasource.dispose(); + }); + + _.each(self.panes(), function(pane) + { + pane.dispose(); + }); + + self.plugins.removeAll(); + self.datasources.removeAll(); + self.panes.removeAll(); + } + + this.loadDashboard = function(dashboardData, callback) + { + freeboardUI.showLoadingIndicator(true); + self.deserialize(dashboardData, function() + { + freeboardUI.showLoadingIndicator(false); + + if(_.isFunction(callback)) + { + callback(); + } + + freeboard.emit("dashboard_loaded"); + }); + } + + this.loadDashboardFromLocalFile = function() + { + // Check for the various File API support. + if(window.File && window.FileReader && window.FileList && window.Blob) + { + var input = document.createElement('input'); + input.type = "file"; + $(input).on("change", function(event) + { + var files = event.target.files; + + if(files && files.length > 0) + { + var file = files[0]; + var reader = new FileReader(); + + reader.addEventListener("load", function(fileReaderEvent) + { + + var textFile = fileReaderEvent.target; + var jsonObject = JSON.parse(textFile.result); + + + self.loadDashboard(jsonObject); + self.setEditing(false); + }); + + reader.readAsText(file); + } + + }); + $(input).trigger("click"); + } + else + { + alert('Unable to load a file in this browser.'); + } + } + + this.saveDashboard = function() + { + var contentType = 'application/octet-stream'; + var a = document.createElement('a'); + var blob = new Blob([JSON.stringify(self.serialize())], {'type': contentType}); + document.body.appendChild(a); + a.href = window.URL.createObjectURL(blob); + a.download = "dashboard.json"; + a.target="_self"; + a.click(); + } + + this.addDatasource = function(datasource) + { + self.datasources.push(datasource); + } + + this.deleteDatasource = function(datasource) + { + delete self.datasourceData[datasource.name()]; + datasource.dispose(); + self.datasources.remove(datasource); + } + + this.createPane = function() + { + var newPane = new PaneModel(self, widgetPlugins); + self.addPane(newPane); + } + + this.addGridColumnLeft = function() + { + freeboardUI.addGridColumnLeft(); + } + + this.addGridColumnRight = function() + { + freeboardUI.addGridColumnRight(); + } + + this.subGridColumnLeft = function() + { + freeboardUI.subGridColumnLeft(); + } + + this.subGridColumnRight = function() + { + freeboardUI.subGridColumnRight(); + } + + this.addPane = function(pane) + { + self.panes.push(pane); + } + + this.deletePane = function(pane) + { + pane.dispose(); + self.panes.remove(pane); + } + + this.deleteWidget = function(widget) + { + ko.utils.arrayForEach(self.panes(), function(pane) + { + pane.widgets.remove(widget); + }); + + widget.dispose(); + } + + this.setEditing = function(editing, animate) + { + // Don't allow editing if it's not allowed + if(!self.allow_edit() && editing) + { + return; + } + + self.isEditing(editing); + + if(_.isUndefined(animate)) + { + animate = true; + } + + var animateLength = (animate) ? 250 : 0; + var barHeight = $("#admin-bar").outerHeight(); + + if(!editing) + { + $("#toggle-header-icon").addClass("icon-wrench").removeClass("icon-chevron-up"); + $(".gridster .gs_w").css({cursor: "default"}); + $("#main-header").animate({"top": "-" + barHeight + "px"}, animateLength); + $("#board-content").animate({"top": "20"}, animateLength); + $("#main-header").data().shown = false; + $(".sub-section").unbind(); + freeboardUI.disableGrid(); + } + else + { + $("#toggle-header-icon").addClass("icon-chevron-up").removeClass("icon-wrench"); + $(".gridster .gs_w").css({cursor: "pointer"}); + $("#main-header").animate({"top": "0px"}, animateLength); + $("#board-content").animate({"top": (barHeight + 20) + "px"}, animateLength); + $("#main-header").data().shown = true; + freeboardUI.attachWidgetEditIcons($(".sub-section")); + freeboardUI.enableGrid(); + } + + freeboardUI.showPaneEditIcons(editing, animate); + } + + this.toggleEditing = function() + { + var editing = !self.isEditing(); + self.setEditing(editing); + } } function FreeboardUI() { - var PANE_MARGIN = 10; - var PANE_WIDTH = 300; - var MIN_COLUMNS = 3; - var COLUMN_WIDTH = PANE_MARGIN + PANE_WIDTH + PANE_MARGIN; + var PANE_MARGIN = 10; + var PANE_WIDTH = 300; + var MIN_COLUMNS = 3; + var COLUMN_WIDTH = PANE_MARGIN + PANE_WIDTH + PANE_MARGIN; - var userColumns = MIN_COLUMNS; + var userColumns = MIN_COLUMNS; - var loadingIndicator = $('
          '); - var grid; + var loadingIndicator = $('
          '); + var grid; - function processResize(layoutWidgets) - { - var maxDisplayableColumns = getMaxDisplayableColumnCount(); - var repositionFunction = function(){}; - if(layoutWidgets) - { - repositionFunction = function(index) - { - var paneElement = this; - var paneModel = ko.dataFor(paneElement); + function processResize(layoutWidgets) + { + var maxDisplayableColumns = getMaxDisplayableColumnCount(); + var repositionFunction = function(){}; + if(layoutWidgets) + { + repositionFunction = function(index) + { + var paneElement = this; + var paneModel = ko.dataFor(paneElement); - var newPosition = getPositionForScreenSize(paneModel); - $(paneElement).attr("data-sizex", Math.min(paneModel.col_width(), - maxDisplayableColumns, grid.cols)) - .attr("data-row", newPosition.row) - .attr("data-col", newPosition.col); + var newPosition = getPositionForScreenSize(paneModel); + $(paneElement).attr("data-sizex", Math.min(paneModel.col_width(), + maxDisplayableColumns, grid.cols)) + .attr("data-row", newPosition.row) + .attr("data-col", newPosition.col); - paneModel.processSizeChange(); - } - } + paneModel.processSizeChange(); + } + } - updateGridWidth(Math.min(maxDisplayableColumns, userColumns)); + updateGridWidth(Math.min(maxDisplayableColumns, userColumns)); - repositionGrid(repositionFunction); - updateGridColumnControls(); - } + repositionGrid(repositionFunction); + updateGridColumnControls(); + } - function addGridColumn(shift) - { - var num_cols = grid.cols + 1; - if(updateGridWidth(num_cols)) - { - repositionGrid(function() { - var paneElement = this; - var paneModel = ko.dataFor(paneElement); + function addGridColumn(shift) + { + var num_cols = grid.cols + 1; + if(updateGridWidth(num_cols)) + { + repositionGrid(function() { + var paneElement = this; + var paneModel = ko.dataFor(paneElement); - var prevColumnIndex = grid.cols > 1 ? grid.cols - 1 : 1; - var prevCol = paneModel.col[prevColumnIndex]; - var prevRow = paneModel.row[prevColumnIndex]; - var newPosition; - if(shift) - { - leftPreviewCol = true; - var newCol = prevCol < grid.cols ? prevCol + 1 : grid.cols; - newPosition = {row: prevRow, col: newCol}; - } - else - { - rightPreviewCol = true; - newPosition = {row: prevRow, col: prevCol}; - } - $(paneElement).attr("data-sizex", Math.min(paneModel.col_width(), grid.cols)) - .attr("data-row", newPosition.row) - .attr("data-col", newPosition.col); - }); - } - updateGridColumnControls(); - userColumns = grid.cols; - } + var prevColumnIndex = grid.cols > 1 ? grid.cols - 1 : 1; + var prevCol = paneModel.col[prevColumnIndex]; + var prevRow = paneModel.row[prevColumnIndex]; + var newPosition; + if(shift) + { + leftPreviewCol = true; + var newCol = prevCol < grid.cols ? prevCol + 1 : grid.cols; + newPosition = {row: prevRow, col: newCol}; + } + else + { + rightPreviewCol = true; + newPosition = {row: prevRow, col: prevCol}; + } + $(paneElement).attr("data-sizex", Math.min(paneModel.col_width(), grid.cols)) + .attr("data-row", newPosition.row) + .attr("data-col", newPosition.col); + }); + } + updateGridColumnControls(); + userColumns = grid.cols; + } - function subtractGridColumn(shift) - { - var num_cols = grid.cols - 1; - if(updateGridWidth(num_cols)) - { - repositionGrid(function() { - var paneElement = this; - var paneModel = ko.dataFor(paneElement); + function subtractGridColumn(shift) + { + var num_cols = grid.cols - 1; + if(updateGridWidth(num_cols)) + { + repositionGrid(function() { + var paneElement = this; + var paneModel = ko.dataFor(paneElement); - var prevColumnIndex = grid.cols + 1; - var prevCol = paneModel.col[prevColumnIndex]; - var prevRow = paneModel.row[prevColumnIndex]; - var newPosition; - if(shift) - { - var newCol = prevCol > 1 ? prevCol - 1 : 1; - newPosition = {row: prevRow, col: newCol}; - } - else - { - var newCol = prevCol <= grid.cols ? prevCol : grid.cols; - newPosition = {row: prevRow, col: newCol}; - } - $(paneElement).attr("data-sizex", Math.min(paneModel.col_width(), grid.cols)) - .attr("data-row", newPosition.row) - .attr("data-col", newPosition.col); - }); - } - updateGridColumnControls(); - userColumns = grid.cols; - } + var prevColumnIndex = grid.cols + 1; + var prevCol = paneModel.col[prevColumnIndex]; + var prevRow = paneModel.row[prevColumnIndex]; + var newPosition; + if(shift) + { + var newCol = prevCol > 1 ? prevCol - 1 : 1; + newPosition = {row: prevRow, col: newCol}; + } + else + { + var newCol = prevCol <= grid.cols ? prevCol : grid.cols; + newPosition = {row: prevRow, col: newCol}; + } + $(paneElement).attr("data-sizex", Math.min(paneModel.col_width(), grid.cols)) + .attr("data-row", newPosition.row) + .attr("data-col", newPosition.col); + }); + } + updateGridColumnControls(); + userColumns = grid.cols; + } - function updateGridColumnControls() - { - var col_controls = $(".column-tool"); - var available_width = $("#board-content").width(); - var max_columns = Math.floor(available_width / COLUMN_WIDTH); + function updateGridColumnControls() + { + var col_controls = $(".column-tool"); + var available_width = $("#board-content").width(); + var max_columns = Math.floor(available_width / COLUMN_WIDTH); - if(grid.cols <= MIN_COLUMNS) - { - col_controls.addClass("min"); - } - else - { - col_controls.removeClass("min"); - } + if(grid.cols <= MIN_COLUMNS) + { + col_controls.addClass("min"); + } + else + { + col_controls.removeClass("min"); + } - if(grid.cols >= max_columns) - { - col_controls.addClass("max"); - } - else - { - col_controls.removeClass("max"); - } - } + if(grid.cols >= max_columns) + { + col_controls.addClass("max"); + } + else + { + col_controls.removeClass("max"); + } + } - function getMaxDisplayableColumnCount() - { - var available_width = $("#board-content").width(); - return Math.floor(available_width / COLUMN_WIDTH); - } + function getMaxDisplayableColumnCount() + { + var available_width = $("#board-content").width(); + return Math.floor(available_width / COLUMN_WIDTH); + } - function updateGridWidth(newCols) - { - if(newCols === undefined || newCols < MIN_COLUMNS) - { - newCols = MIN_COLUMNS; - } + function updateGridWidth(newCols) + { + if(newCols === undefined || newCols < MIN_COLUMNS) + { + newCols = MIN_COLUMNS; + } - var max_columns = getMaxDisplayableColumnCount(); - if(newCols > max_columns) - { - newCols = max_columns; - } + var max_columns = getMaxDisplayableColumnCount(); + if(newCols > max_columns) + { + newCols = max_columns; + } - // +newCols to account for scaling on zoomed browsers - var new_width = (COLUMN_WIDTH * newCols) + newCols; - $(".responsive-column-width").css("max-width", new_width); + // +newCols to account for scaling on zoomed browsers + var new_width = (COLUMN_WIDTH * newCols) + newCols; + $(".responsive-column-width").css("max-width", new_width); - if(newCols === grid.cols) - { - return false; - } - else - { - return true; - } - } + if(newCols === grid.cols) + { + return false; + } + else + { + return true; + } + } - function repositionGrid(repositionFunction) - { - var rootElement = grid.$el; + function repositionGrid(repositionFunction) + { + var rootElement = grid.$el; - rootElement.find("> li").unbind().removeData(); - $(".responsive-column-width").css("width", ""); - grid.generate_grid_and_stylesheet(); + rootElement.find("> li").unbind().removeData(); + $(".responsive-column-width").css("width", ""); + grid.generate_grid_and_stylesheet(); - rootElement.find("> li").each(repositionFunction); + rootElement.find("> li").each(repositionFunction); - grid.init(); - $(".responsive-column-width").css("width", grid.cols * PANE_WIDTH + (grid.cols * PANE_MARGIN * 2)); - } + grid.init(); + $(".responsive-column-width").css("width", grid.cols * PANE_WIDTH + (grid.cols * PANE_MARGIN * 2)); + } - function getUserColumns() - { - return userColumns; - } + function getUserColumns() + { + return userColumns; + } - function setUserColumns(numCols) - { - userColumns = Math.max(MIN_COLUMNS, numCols); - } + function setUserColumns(numCols) + { + userColumns = Math.max(MIN_COLUMNS, numCols); + } - ko.bindingHandlers.grid = { - init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) - { - // Initialize our grid - grid = $(element).gridster({ - widget_margins : [PANE_MARGIN, PANE_MARGIN], - widget_base_dimensions: [PANE_WIDTH, 10], - resize: { - enabled : false, - axes : "x" - } - }).data("gridster"); + ko.bindingHandlers.grid = { + init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) + { + // Initialize our grid + grid = $(element).gridster({ + widget_margins : [PANE_MARGIN, PANE_MARGIN], + widget_base_dimensions: [PANE_WIDTH, 10], + resize: { + enabled : false, + axes : "x" + } + }).data("gridster"); - processResize(false) + processResize(false) - grid.disable(); - } - } + grid.disable(); + } + } - function addPane(element, viewModel, isEditing) - { - var position = getPositionForScreenSize(viewModel); - var col = position.col; - var row = position.row; - var width = Number(viewModel.width()); - var height = Number(viewModel.getCalculatedHeight()); + function addPane(element, viewModel, isEditing) + { + var position = getPositionForScreenSize(viewModel); + var col = position.col; + var row = position.row; + var width = Number(viewModel.width()); + var height = Number(viewModel.getCalculatedHeight()); - grid.add_widget(element, width, height, col, row); + grid.add_widget(element, width, height, col, row); - if(isEditing) - { - showPaneEditIcons(true); - } + if(isEditing) + { + showPaneEditIcons(true); + } - updatePositionForScreenSize(viewModel, row, col); + updatePositionForScreenSize(viewModel, row, col); - $(element).attrchange({ - trackValues: true, - callback : function(event) - { - if(event.attributeName == "data-row") - { + $(element).attrchange({ + trackValues: true, + callback : function(event) + { + if(event.attributeName == "data-row") + { updatePositionForScreenSize(viewModel, Number(event.newValue), undefined); - } - else if(event.attributeName == "data-col") - { + } + else if(event.attributeName == "data-col") + { updatePositionForScreenSize(viewModel, undefined, Number(event.newValue)); - } - } - }); - } + } + } + }); + } - function updatePane(element, viewModel) - { - // If widget has been added or removed - var calculatedHeight = viewModel.getCalculatedHeight(); + function updatePane(element, viewModel) + { + // If widget has been added or removed + var calculatedHeight = viewModel.getCalculatedHeight(); - var elementHeight = Number($(element).attr("data-sizey")); - var elementWidth = Number($(element).attr("data-sizex")); + var elementHeight = Number($(element).attr("data-sizey")); + var elementWidth = Number($(element).attr("data-sizex")); - if(calculatedHeight != elementHeight || viewModel.col_width() != elementWidth) - { - grid.resize_widget($(element), viewModel.col_width(), calculatedHeight, function(){ - grid.set_dom_grid_height(); - }); - } - } + if(calculatedHeight != elementHeight || viewModel.col_width() != elementWidth) + { + grid.resize_widget($(element), viewModel.col_width(), calculatedHeight, function(){ + grid.set_dom_grid_height(); + }); + } + } - function updatePositionForScreenSize(paneModel, row, col) - { - var displayCols = grid.cols; + function updatePositionForScreenSize(paneModel, row, col) + { + var displayCols = grid.cols; - if(!_.isUndefined(row)) paneModel.row[displayCols] = row; - if(!_.isUndefined(col)) paneModel.col[displayCols] = col; - } + if(!_.isUndefined(row)) paneModel.row[displayCols] = row; + if(!_.isUndefined(col)) paneModel.col[displayCols] = col; + } - function showLoadingIndicator(show) - { - if(show) - { - loadingIndicator.fadeOut(0).appendTo("body").fadeIn(500); - } - else - { - loadingIndicator.fadeOut(500).remove(); - } - } + function showLoadingIndicator(show) + { + if(show) + { + loadingIndicator.fadeOut(0).appendTo("body").fadeIn(500); + } + else + { + loadingIndicator.fadeOut(500).remove(); + } + } - function showPaneEditIcons(show, animate) - { - if(_.isUndefined(animate)) - { - animate = true; - } + function showPaneEditIcons(show, animate) + { + if(_.isUndefined(animate)) + { + animate = true; + } - var animateLength = (animate) ? 250 : 0; + var animateLength = (animate) ? 250 : 0; - if(show) - { - $(".pane-tools").fadeIn(animateLength);//.css("display", "block").animate({opacity: 1.0}, animateLength); - $("#column-tools").fadeIn(animateLength); - } - else - { - $(".pane-tools").fadeOut(animateLength);//.animate({opacity: 0.0}, animateLength).css("display", "none");//, function() - $("#column-tools").fadeOut(animateLength); - } - } + if(show) + { + $(".pane-tools").fadeIn(animateLength);//.css("display", "block").animate({opacity: 1.0}, animateLength); + $("#column-tools").fadeIn(animateLength); + } + else + { + $(".pane-tools").fadeOut(animateLength);//.animate({opacity: 0.0}, animateLength).css("display", "none");//, function() + $("#column-tools").fadeOut(animateLength); + } + } - function attachWidgetEditIcons(element) - { - $(element).hover(function() - { - showWidgetEditIcons(this, true); - }, function() - { - showWidgetEditIcons(this, false); - }); - } + function attachWidgetEditIcons(element) + { + $(element).hover(function() + { + showWidgetEditIcons(this, true); + }, function() + { + showWidgetEditIcons(this, false); + }); + } - function showWidgetEditIcons(element, show) - { - if(show) - { - $(element).find(".sub-section-tools").fadeIn(250); - } - else - { - $(element).find(".sub-section-tools").fadeOut(250); - } - } + function showWidgetEditIcons(element, show) + { + if(show) + { + $(element).find(".sub-section-tools").fadeIn(250); + } + else + { + $(element).find(".sub-section-tools").fadeOut(250); + } + } - function getPositionForScreenSize(paneModel) - { - var cols = grid.cols; + function getPositionForScreenSize(paneModel) + { + var cols = grid.cols; - if(_.isNumber(paneModel.row) && _.isNumber(paneModel.col)) // Support for legacy format - { - var obj = {}; - obj[cols] = paneModel.row; - paneModel.row = obj; + if(_.isNumber(paneModel.row) && _.isNumber(paneModel.col)) // Support for legacy format + { + var obj = {}; + obj[cols] = paneModel.row; + paneModel.row = obj; - obj = {}; - obj[cols] = paneModel.col; - paneModel.col = obj; - } + obj = {}; + obj[cols] = paneModel.col; + paneModel.col = obj; + } - var newColumnIndex = 1; - var columnDiff = 1000; + var newColumnIndex = 1; + var columnDiff = 1000; - for(var columnIndex in paneModel.col) - { - if(columnIndex == cols) // If we already have a position defined for this number of columns, return that position - { - return {row: paneModel.row[columnIndex], col: paneModel.col[columnIndex]}; - } - else if(paneModel.col[columnIndex] > cols) // If it's greater than our display columns, put it in the last column - { - newColumnIndex = cols; - } - else // If it's less than, pick whichever one is closest - { - var delta = cols - columnIndex; + for(var columnIndex in paneModel.col) + { + if(columnIndex == cols) // If we already have a position defined for this number of columns, return that position + { + return {row: paneModel.row[columnIndex], col: paneModel.col[columnIndex]}; + } + else if(paneModel.col[columnIndex] > cols) // If it's greater than our display columns, put it in the last column + { + newColumnIndex = cols; + } + else // If it's less than, pick whichever one is closest + { + var delta = cols - columnIndex; - if(delta < columnDiff) - { - newColumnIndex = columnIndex; - columnDiff = delta; - } - } - } + if(delta < columnDiff) + { + newColumnIndex = columnIndex; + columnDiff = delta; + } + } + } - if(newColumnIndex in paneModel.col && newColumnIndex in paneModel.row) - { - return {row: paneModel.row[newColumnIndex], col: paneModel.col[newColumnIndex]}; - } + if(newColumnIndex in paneModel.col && newColumnIndex in paneModel.row) + { + return {row: paneModel.row[newColumnIndex], col: paneModel.col[newColumnIndex]}; + } - return {row:1,col:newColumnIndex}; - } + return {row:1,col:newColumnIndex}; + } - // Public Functions - return { - showLoadingIndicator : function(show) - { - showLoadingIndicator(show); - }, - showPaneEditIcons : function(show, animate) - { - showPaneEditIcons(show, animate); - }, - attachWidgetEditIcons : function(element) - { - attachWidgetEditIcons(element); - }, - getPositionForScreenSize : function(paneModel) - { - return getPositionForScreenSize(paneModel); - }, - processResize : function(layoutWidgets) - { - processResize(layoutWidgets); - }, - disableGrid : function() - { - grid.disable(); - }, - enableGrid : function() - { - grid.enable(); - }, - addPane : function(element, viewModel, isEditing) - { - addPane(element, viewModel, isEditing); - }, - updatePane : function(element, viewModel) - { - updatePane(element, viewModel); - }, - removePane : function(element) - { - grid.remove_widget(element); - }, - removeAllPanes : function() - { - grid.remove_all_widgets(); - }, - addGridColumnLeft : function() - { - addGridColumn(true); - }, - addGridColumnRight : function() - { - addGridColumn(false); - }, - subGridColumnLeft : function() - { - subtractGridColumn(true); - }, - subGridColumnRight : function() - { - subtractGridColumn(false); - }, - getUserColumns : function() - { - return getUserColumns(); - }, - setUserColumns : function(numCols) - { - setUserColumns(numCols); - } - } + // Public Functions + return { + showLoadingIndicator : function(show) + { + showLoadingIndicator(show); + }, + showPaneEditIcons : function(show, animate) + { + showPaneEditIcons(show, animate); + }, + attachWidgetEditIcons : function(element) + { + attachWidgetEditIcons(element); + }, + getPositionForScreenSize : function(paneModel) + { + return getPositionForScreenSize(paneModel); + }, + processResize : function(layoutWidgets) + { + processResize(layoutWidgets); + }, + disableGrid : function() + { + grid.disable(); + }, + enableGrid : function() + { + grid.enable(); + }, + addPane : function(element, viewModel, isEditing) + { + addPane(element, viewModel, isEditing); + }, + updatePane : function(element, viewModel) + { + updatePane(element, viewModel); + }, + removePane : function(element) + { + grid.remove_widget(element); + }, + removeAllPanes : function() + { + grid.remove_all_widgets(); + }, + addGridColumnLeft : function() + { + addGridColumn(true); + }, + addGridColumnRight : function() + { + addGridColumn(false); + }, + subGridColumnLeft : function() + { + subtractGridColumn(true); + }, + subGridColumnRight : function() + { + subtractGridColumn(false); + }, + getUserColumns : function() + { + return getUserColumns(); + }, + setUserColumns : function(numCols) + { + setUserColumns(numCols); + } + } } JSEditor = function () { - var assetRoot = "" + var assetRoot = "" - function setAssetRoot(_assetRoot) { - assetRoot = _assetRoot; - } + function setAssetRoot(_assetRoot) { + assetRoot = _assetRoot; + } - function displayJSEditor(value, callback) { + function displayJSEditor(value, callback) { - var exampleText = "// Example: Convert temp from C to F and truncate to 2 decimal places.\n// return (datasources[\"MyDatasource\"].sensor.tempInF * 1.8 + 32).toFixed(2);"; + var exampleText = "// Example: Convert temp from C to F and truncate to 2 decimal places.\n// return (datasources[\"MyDatasource\"].sensor.tempInF * 1.8 + 32).toFixed(2);"; - // If value is empty, go ahead and suggest something - if (!value) { - value = exampleText; - } + // If value is empty, go ahead and suggest something + if (!value) { + value = exampleText; + } - var codeWindow = $('
          '); - var codeMirrorWrapper = $('
          '); - var codeWindowFooter = $(''); - var codeWindowHeader = $('
          This javascript will be re-evaluated any time a datasource referenced here is updated, and the value you return will be displayed in the widget. You can assume this javascript is wrapped in a function of the form function(datasources) where datasources is a collection of javascript objects (keyed by their name) corresponding to the most current data in a datasource.
          '); + var codeWindow = $('
          '); + var codeMirrorWrapper = $('
          '); + var codeWindowFooter = $(''); + var codeWindowHeader = $('
          This javascript will be re-evaluated any time a datasource referenced here is updated, and the value you return will be displayed in the widget. You can assume this javascript is wrapped in a function of the form function(datasources) where datasources is a collection of javascript objects (keyed by their name) corresponding to the most current data in a datasource.
          '); - codeWindow.append([codeWindowHeader, codeMirrorWrapper, codeWindowFooter]); + codeWindow.append([codeWindowHeader, codeMirrorWrapper, codeWindowFooter]); - $("body").append(codeWindow); + $("body").append(codeWindow); - var codeMirrorEditor = CodeMirror(codeMirrorWrapper.get(0), - { - value: value, - mode: "javascript", - theme: "ambiance", - indentUnit: 4, - lineNumbers: true, - matchBrackets: true, - autoCloseBrackets: true - } - ); + var codeMirrorEditor = CodeMirror(codeMirrorWrapper.get(0), + { + value: value, + mode: "javascript", + theme: "ambiance", + indentUnit: 4, + lineNumbers: true, + matchBrackets: true, + autoCloseBrackets: true + } + ); - var closeButton = $('Close').click(function () { - if (callback) { - var newValue = codeMirrorEditor.getValue(); + var closeButton = $('Close').click(function () { + if (callback) { + var newValue = codeMirrorEditor.getValue(); - if (newValue === exampleText) { - newValue = ""; - } + if (newValue === exampleText) { + newValue = ""; + } - callback(newValue); - codeWindow.remove(); - } - }); + callback(newValue); + codeWindow.remove(); + } + }); - codeWindowFooter.append(closeButton); - } + codeWindowFooter.append(closeButton); + } - // Public API - return { - displayJSEditor: function (value, callback) { - displayJSEditor(value, callback); - }, - setAssetRoot: function (assetRoot) { - setAssetRoot(assetRoot) - } - } + // Public API + return { + displayJSEditor: function (value, callback) { + displayJSEditor(value, callback); + }, + setAssetRoot: function (assetRoot) { + setAssetRoot(assetRoot) + } + } } function PaneModel(theFreeboardModel, widgetPlugins) { - var self = this; + var self = this; - this.title = ko.observable(); - this.width = ko.observable(1); - this.row = {}; - this.col = {}; + this.title = ko.observable(); + this.width = ko.observable(1); + this.row = {}; + this.col = {}; - this.col_width = ko.observable(1); - this.col_width.subscribe(function(newValue) - { - self.processSizeChange(); - }); + this.col_width = ko.observable(1); + this.col_width.subscribe(function(newValue) + { + self.processSizeChange(); + }); - this.widgets = ko.observableArray(); + this.widgets = ko.observableArray(); - this.addWidget = function (widget) { - this.widgets.push(widget); - } + this.addWidget = function (widget) { + this.widgets.push(widget); + } - this.widgetCanMoveUp = function (widget) { - return (self.widgets.indexOf(widget) >= 1); - } + this.widgetCanMoveUp = function (widget) { + return (self.widgets.indexOf(widget) >= 1); + } - this.widgetCanMoveDown = function (widget) { - var i = self.widgets.indexOf(widget); + this.widgetCanMoveDown = function (widget) { + var i = self.widgets.indexOf(widget); - return (i < self.widgets().length - 1); - } + return (i < self.widgets().length - 1); + } - this.moveWidgetUp = function (widget) { - if (self.widgetCanMoveUp(widget)) { - var i = self.widgets.indexOf(widget); - var array = self.widgets(); - self.widgets.splice(i - 1, 2, array[i], array[i - 1]); - } - } + this.moveWidgetUp = function (widget) { + if (self.widgetCanMoveUp(widget)) { + var i = self.widgets.indexOf(widget); + var array = self.widgets(); + self.widgets.splice(i - 1, 2, array[i], array[i - 1]); + } + } - this.moveWidgetDown = function (widget) { - if (self.widgetCanMoveDown(widget)) { - var i = self.widgets.indexOf(widget); - var array = self.widgets(); - self.widgets.splice(i, 2, array[i + 1], array[i]); - } - } + this.moveWidgetDown = function (widget) { + if (self.widgetCanMoveDown(widget)) { + var i = self.widgets.indexOf(widget); + var array = self.widgets(); + self.widgets.splice(i, 2, array[i + 1], array[i]); + } + } - this.processSizeChange = function() - { - // Give the animation a moment to complete. Really hacky. - // TODO: Make less hacky. Also, doesn't work when screen resizes. - setTimeout(function(){ - _.each(self.widgets(), function (widget) { - widget.processSizeChange(); - }); - }, 1000); - } + this.processSizeChange = function() + { + // Give the animation a moment to complete. Really hacky. + // TODO: Make less hacky. Also, doesn't work when screen resizes. + setTimeout(function(){ + _.each(self.widgets(), function (widget) { + widget.processSizeChange(); + }); + }, 1000); + } - this.getCalculatedHeight = function () { - var sumHeights = _.reduce(self.widgets(), function (memo, widget) { - return memo + widget.height(); - }, 0); + this.getCalculatedHeight = function () { + var sumHeights = _.reduce(self.widgets(), function (memo, widget) { + return memo + widget.height(); + }, 0); - sumHeights *= 6; - sumHeights += 3; + sumHeights *= 6; + sumHeights += 3; - sumHeights *= 10; + sumHeights *= 10; - var rows = Math.ceil((sumHeights + 20) / 30); + var rows = Math.ceil((sumHeights + 20) / 30); - return Math.max(4, rows); - } + return Math.max(4, rows); + } - this.serialize = function () { - var widgets = []; + this.serialize = function () { + var widgets = []; - _.each(self.widgets(), function (widget) { - widgets.push(widget.serialize()); - }); + _.each(self.widgets(), function (widget) { + widgets.push(widget.serialize()); + }); - return { - title: self.title(), - width: self.width(), - row: self.row, - col: self.col, - col_width: self.col_width(), - widgets: widgets - }; - } + return { + title: self.title(), + width: self.width(), + row: self.row, + col: self.col, + col_width: self.col_width(), + widgets: widgets + }; + } - this.deserialize = function (object) { - self.title(object.title); - self.width(object.width); + this.deserialize = function (object) { + self.title(object.title); + self.width(object.width); - self.row = object.row; - self.col = object.col; - self.col_width(object.col_width || 1); + self.row = object.row; + self.col = object.col; + self.col_width(object.col_width || 1); - _.each(object.widgets, function (widgetConfig) { - var widget = new WidgetModel(theFreeboardModel, widgetPlugins); - widget.deserialize(widgetConfig); - self.widgets.push(widget); - }); - } + _.each(object.widgets, function (widgetConfig) { + var widget = new WidgetModel(theFreeboardModel, widgetPlugins); + widget.deserialize(widgetConfig); + self.widgets.push(widget); + }); + } - this.dispose = function () { - _.each(self.widgets(), function (widget) { - widget.dispose(); - }); - } + this.dispose = function () { + _.each(self.widgets(), function (widget) { + widget.dispose(); + }); + } } PluginEditor = function(jsEditor, valueEditor) { - function _displayValidationError(settingName, errorMessage) - { - var errorElement = $('
          ').html(errorMessage); - $("#setting-value-container-" + settingName).append(errorElement); - } - - function _removeSettingsRows() - { - if($("#setting-row-instance-name").length) - { - $("#setting-row-instance-name").nextAll().remove(); - } - else - { - $("#setting-row-plugin-types").nextAll().remove(); - } - } - - function _isNumerical(n) - { - return !isNaN(parseFloat(n)) && isFinite(n); - } - - function _appendCalculatedSettingRow(valueCell, newSettings, settingDef, currentValue, includeRemove) - { - var input = $(''); - - if(settingDef.multi_input) { - input.change(function() { - var arrayInput = []; - $(valueCell).find('textarea').each(function() { - var thisVal = $(this).val(); - if(thisVal) { - arrayInput = arrayInput.concat(thisVal); - } - }); - newSettings.settings[settingDef.name] = arrayInput; - }); - } else { - input.change(function() { - newSettings.settings[settingDef.name] = $(this).val(); - }); - } - - if(currentValue) { - input.val(currentValue); - } - - valueEditor.createValueEditor(input); - - var datasourceToolbox = $('
            '); - var wrapperDiv = $('
            '); - wrapperDiv.append(input).append(datasourceToolbox); - - var datasourceTool = $('
          • ') - .mousedown(function(e) { - e.preventDefault(); - $(input).val("").focus().insertAtCaret("datasources[\"").trigger("freeboard-eval"); - }); - datasourceToolbox.append(datasourceTool); - - var jsEditorTool = $('
          • ') - .mousedown(function(e) { - e.preventDefault(); - jsEditor.displayJSEditor(input.val(), function(result) { - input.val(result); - input.change(); - }); - }); - datasourceToolbox.append(jsEditorTool); - - if(includeRemove) { - var removeButton = $('
          • ') - .mousedown(function(e) { - e.preventDefault(); - wrapperDiv.remove(); - $(valueCell).find('textarea:first').change(); - }); - datasourceToolbox.prepend(removeButton); - } - - $(valueCell).append(wrapperDiv); - } - - function createPluginEditor(title, pluginTypes, currentTypeName, currentSettingsValues, settingsSavedCallback) - { - var newSettings = { - type : currentTypeName, - settings: {} - }; - - function createSettingRow(name, displayName) - { - var tr = $('
            ').appendTo(form); - - tr.append('
            '); - return $('
            ').appendTo(tr); - } - - var selectedType; - var form = $('
            '); - - var pluginDescriptionElement = $('
            ').hide(); - form.append(pluginDescriptionElement); - - function createSettingsFromDefinition(settingsDefs, typeaheadSource, typeaheadDataSegment) - { - _.each(settingsDefs, function(settingDef) - { - // Set a default value if one doesn't exist - if(!_.isUndefined(settingDef.default_value) && _.isUndefined(currentSettingsValues[settingDef.name])) - { - currentSettingsValues[settingDef.name] = settingDef.default_value; - } - - var displayName = settingDef.name; - - if(!_.isUndefined(settingDef.display_name)) - { - displayName = settingDef.display_name; - } - - var valueCell = createSettingRow(settingDef.name, displayName); - - switch (settingDef.type) - { - case "array": - { - var subTableDiv = $('
            ').appendTo(valueCell); - - var subTable = $('
            ').appendTo(subTableDiv); - var subTableHead = $("").hide().appendTo(subTable); - var subTableHeadRow = $("").appendTo(subTableHead); - var subTableBody = $('').appendTo(subTable); - - var currentSubSettingValues = []; - - // Create our headers - _.each(settingDef.settings, function(subSettingDef) - { - var subsettingDisplayName = subSettingDef.name; - - if(!_.isUndefined(subSettingDef.display_name)) - { - subsettingDisplayName = subSettingDef.display_name; - } - - $('' + subsettingDisplayName + '').appendTo(subTableHeadRow); - }); - - if(settingDef.name in currentSettingsValues) - { - currentSubSettingValues = currentSettingsValues[settingDef.name]; - } - - function processHeaderVisibility() - { - if(newSettings.settings[settingDef.name].length > 0) - { - subTableHead.show(); - } - else - { - subTableHead.hide(); - } - } - - function createSubsettingRow(subsettingValue) - { - var subsettingRow = $('').appendTo(subTableBody); - - var newSetting = {}; - - if(!_.isArray(newSettings.settings[settingDef.name])) - { - newSettings.settings[settingDef.name] = []; - } - - newSettings.settings[settingDef.name].push(newSetting); - - _.each(settingDef.settings, function(subSettingDef) - { - var subsettingCol = $('').appendTo(subsettingRow); - var subsettingValueString = ""; - - if(!_.isUndefined(subsettingValue[subSettingDef.name])) - { - subsettingValueString = subsettingValue[subSettingDef.name]; - } - - newSetting[subSettingDef.name] = subsettingValueString; - - $('').appendTo(subsettingCol).val(subsettingValueString).change(function() - { - newSetting[subSettingDef.name] = $(this).val(); - }); - }); - - subsettingRow.append($('').append($('
              ').append($('
            • ').append($('').click(function() - { - var subSettingIndex = newSettings.settings[settingDef.name].indexOf(newSetting); - - if(subSettingIndex != -1) - { - newSettings.settings[settingDef.name].splice(subSettingIndex, 1); - subsettingRow.remove(); - processHeaderVisibility(); - } - }))))); - - subTableDiv.scrollTop(subTableDiv[0].scrollHeight); - - processHeaderVisibility(); - } - - $('
              ADD
              ').appendTo(valueCell).click(function() - { - var newSubsettingValue = {}; - - _.each(settingDef.settings, function(subSettingDef) - { - newSubsettingValue[subSettingDef.name] = ""; - }); - - createSubsettingRow(newSubsettingValue); - }); - - // Create our rows - _.each(currentSubSettingValues, function(currentSubSettingValue, subSettingIndex) - { - createSubsettingRow(currentSubSettingValue); - }); - - break; - } - case "boolean": - { - newSettings.settings[settingDef.name] = currentSettingsValues[settingDef.name]; - - var onOffSwitch = $('
              ').appendTo(valueCell); - - var input = $('').prependTo(onOffSwitch).change(function() - { - newSettings.settings[settingDef.name] = this.checked; - }); - - if(settingDef.name in currentSettingsValues) - { - input.prop("checked", currentSettingsValues[settingDef.name]); - } - - break; - } - case "option": - { - var defaultValue = currentSettingsValues[settingDef.name]; - - var input = $('').appendTo($('
              ').appendTo(valueCell)).change(function() - { - newSettings.settings[settingDef.name] = $(this).val(); - }); - - _.each(settingDef.options, function(option) - { - - var optionName; - var optionValue; - - if(_.isObject(option)) - { - optionName = option.name; - optionValue = option.value; - } - else - { - optionName = option; - } - - if(_.isUndefined(optionValue)) - { - optionValue = optionName; - } - - if(_.isUndefined(defaultValue)) - { - defaultValue = optionValue; - } - - $("").text(optionName).attr("value", optionValue).appendTo(input); - }); - - newSettings.settings[settingDef.name] = defaultValue; - - if(settingDef.name in currentSettingsValues) - { - input.val(currentSettingsValues[settingDef.name]); - } - - break; - } - default: - { - newSettings.settings[settingDef.name] = currentSettingsValues[settingDef.name]; - - if(settingDef.type == "calculated") - { - if(settingDef.name in currentSettingsValues) { - var currentValue = currentSettingsValues[settingDef.name]; - if(settingDef.multi_input && _.isArray(currentValue)) { - var includeRemove = false; - for(var i=0; i
            • ') - .mousedown(function(e) { - e.preventDefault(); - _appendCalculatedSettingRow(valueCell, newSettings, settingDef, null, true); - }); - $(valueCell).siblings('.form-label').append(inputAdder); - } - } - else - { - var input = $('').appendTo(valueCell).change(function() - { - if(settingDef.type == "number") - { - newSettings.settings[settingDef.name] = Number($(this).val()); - } - else - { - newSettings.settings[settingDef.name] = $(this).val(); - } - }); - - if(settingDef.name in currentSettingsValues) - { - input.val(currentSettingsValues[settingDef.name]); - } - - if(typeaheadSource && settingDef.typeahead_data_field){ - input.addClass('typeahead_data_field-' + settingDef.typeahead_data_field); - } - - if(typeaheadSource && settingDef.typeahead_field){ - var typeaheadValues = []; - - input.keyup(function(event){ - if(event.which >= 65 && event.which <= 91) { - input.trigger('change'); - } - }); - - $(input).autocomplete({ - source: typeaheadValues, - select: function(event, ui){ - input.val(ui.item.value); - input.trigger('change'); - } - }); - - input.change(function(event){ - var value = input.val(); - var source = _.template(typeaheadSource)({input: value}); - $.get(source, function(data){ - if(typeaheadDataSegment){ - data = data[typeaheadDataSegment]; - } - data = _.select(data, function(elm){ - return elm[settingDef.typeahead_field][0] == value[0]; - }); - - typeaheadValues = _.map(data, function(elm){ - return elm[settingDef.typeahead_field]; - }); - $(input).autocomplete("option", "source", typeaheadValues); - - if(data.length == 1){ - data = data[0]; - //we found the one. let's use it to populate the other info - for(var field in data){ - if(data.hasOwnProperty(field)){ - var otherInput = $(_.template('input.typeahead_data_field-<%= field %>')({field: field})); - if(otherInput){ - otherInput.val(data[field]); - if(otherInput.val() != input.val()) { - otherInput.trigger('change'); - } - } - } - } - } - }); - }); - } - } - - break; - } - } - - if(!_.isUndefined(settingDef.suffix)) - { - valueCell.append($('
              ' + settingDef.suffix + '
              ')); - } - - if(!_.isUndefined(settingDef.description)) - { - valueCell.append($('
              ' + settingDef.description + '
              ')); - } - }); - } - - - new DialogBox(form, title, "Save", "Cancel", function() - { - $(".validation-error").remove(); - - // Loop through each setting and validate it - for(var index = 0; index < selectedType.settings.length; index++) - { - var settingDef = selectedType.settings[index]; - - if(settingDef.required && (_.isUndefined(newSettings.settings[settingDef.name]) || newSettings.settings[settingDef.name] == "")) - { - _displayValidationError(settingDef.name, "This is required."); - return true; - } - else if(settingDef.type == "number" && !_isNumerical(newSettings.settings[settingDef.name])) - { - _displayValidationError(settingDef.name, "Must be a number."); - return true; - } - } - - if(_.isFunction(settingsSavedCallback)) - { - settingsSavedCallback(newSettings); - } - }); - - // Create our body - var pluginTypeNames = _.keys(pluginTypes); - var typeSelect; - - if(pluginTypeNames.length > 1) - { - var typeRow = createSettingRow("plugin-types", "Type"); - typeSelect = $('').appendTo($('
              ').appendTo(typeRow)); - - typeSelect.append($("").attr("value", "undefined")); - - _.each(pluginTypes, function(pluginType) - { - typeSelect.append($("").text(pluginType.display_name).attr("value", pluginType.type_name)); - }); - - typeSelect.change(function() - { - newSettings.type = $(this).val(); - newSettings.settings = {}; - - // Remove all the previous settings - _removeSettingsRows(); - - selectedType = pluginTypes[typeSelect.val()]; - - if(_.isUndefined(selectedType)) - { - $("#setting-row-instance-name").hide(); - $("#dialog-ok").hide(); - } - else - { - $("#setting-row-instance-name").show(); - - if(selectedType.description && selectedType.description.length > 0) - { - pluginDescriptionElement.html(selectedType.description).show(); - } - else - { - pluginDescriptionElement.hide(); - } - - $("#dialog-ok").show(); - createSettingsFromDefinition(selectedType.settings, selectedType.typeahead_source, selectedType.typeahead_data_segment); - } - }); - } - else if(pluginTypeNames.length == 1) - { - selectedType = pluginTypes[pluginTypeNames[0]]; - newSettings.type = selectedType.type_name; - newSettings.settings = {}; - createSettingsFromDefinition(selectedType.settings); - } - - if(typeSelect) - { - if(_.isUndefined(currentTypeName)) - { - $("#setting-row-instance-name").hide(); - $("#dialog-ok").hide(); - } - else - { - $("#dialog-ok").show(); - typeSelect.val(currentTypeName).trigger("change"); - } - } - } - - // Public API - return { - createPluginEditor : function( - title, - pluginTypes, - currentInstanceName, - currentTypeName, - currentSettingsValues, - settingsSavedCallback) - { - createPluginEditor(title, pluginTypes, currentInstanceName, currentTypeName, currentSettingsValues, settingsSavedCallback); - } - } + function _displayValidationError(settingName, errorMessage) + { + var errorElement = $('
              ').html(errorMessage); + $("#setting-value-container-" + settingName).append(errorElement); + } + + function _removeSettingsRows() + { + if($("#setting-row-instance-name").length) + { + $("#setting-row-instance-name").nextAll().remove(); + } + else + { + $("#setting-row-plugin-types").nextAll().remove(); + } + } + + function _isNumerical(n) + { + return !isNaN(parseFloat(n)) && isFinite(n); + } + + function _appendCalculatedSettingRow(valueCell, newSettings, settingDef, currentValue, includeRemove) + { + var input = $(''); + + if(settingDef.multi_input) { + input.change(function() { + var arrayInput = []; + $(valueCell).find('textarea').each(function() { + var thisVal = $(this).val(); + if(thisVal) { + arrayInput = arrayInput.concat(thisVal); + } + }); + newSettings.settings[settingDef.name] = arrayInput; + }); + } else { + input.change(function() { + newSettings.settings[settingDef.name] = $(this).val(); + }); + } + + if(currentValue) { + input.val(currentValue); + } + + valueEditor.createValueEditor(input); + + var datasourceToolbox = $('
                '); + var wrapperDiv = $('
                '); + wrapperDiv.append(input).append(datasourceToolbox); + + var datasourceTool = $('
              • ') + .mousedown(function(e) { + e.preventDefault(); + $(input).val("").focus().insertAtCaret("datasources[\"").trigger("freeboard-eval"); + }); + datasourceToolbox.append(datasourceTool); + + var jsEditorTool = $('
              • ') + .mousedown(function(e) { + e.preventDefault(); + jsEditor.displayJSEditor(input.val(), function(result) { + input.val(result); + input.change(); + }); + }); + datasourceToolbox.append(jsEditorTool); + + if(includeRemove) { + var removeButton = $('
              • ') + .mousedown(function(e) { + e.preventDefault(); + wrapperDiv.remove(); + $(valueCell).find('textarea:first').change(); + }); + datasourceToolbox.prepend(removeButton); + } + + $(valueCell).append(wrapperDiv); + } + + function createPluginEditor(title, pluginTypes, currentTypeName, currentSettingsValues, settingsSavedCallback) + { + var newSettings = { + type : currentTypeName, + settings: {} + }; + + function createSettingRow(name, displayName) + { + var tr = $('
                ').appendTo(form); + + tr.append('
                '); + return $('
                ').appendTo(tr); + } + + var selectedType; + var form = $('
                '); + + var pluginDescriptionElement = $('
                ').hide(); + form.append(pluginDescriptionElement); + + function createSettingsFromDefinition(settingsDefs, typeaheadSource, typeaheadDataSegment) + { + _.each(settingsDefs, function(settingDef) + { + // Set a default value if one doesn't exist + if(!_.isUndefined(settingDef.default_value) && _.isUndefined(currentSettingsValues[settingDef.name])) + { + currentSettingsValues[settingDef.name] = settingDef.default_value; + } + + var displayName = settingDef.name; + + if(!_.isUndefined(settingDef.display_name)) + { + displayName = settingDef.display_name; + } + + var valueCell = createSettingRow(settingDef.name, displayName); + + switch (settingDef.type) + { + case "array": + { + var subTableDiv = $('
                ').appendTo(valueCell); + + var subTable = $('
                ').appendTo(subTableDiv); + var subTableHead = $("").hide().appendTo(subTable); + var subTableHeadRow = $("").appendTo(subTableHead); + var subTableBody = $('').appendTo(subTable); + + var currentSubSettingValues = []; + + // Create our headers + _.each(settingDef.settings, function(subSettingDef) + { + var subsettingDisplayName = subSettingDef.name; + + if(!_.isUndefined(subSettingDef.display_name)) + { + subsettingDisplayName = subSettingDef.display_name; + } + + $('' + subsettingDisplayName + '').appendTo(subTableHeadRow); + }); + + if(settingDef.name in currentSettingsValues) + { + currentSubSettingValues = currentSettingsValues[settingDef.name]; + } + + function processHeaderVisibility() + { + if(newSettings.settings[settingDef.name].length > 0) + { + subTableHead.show(); + } + else + { + subTableHead.hide(); + } + } + + function createSubsettingRow(subsettingValue) + { + var subsettingRow = $('').appendTo(subTableBody); + + var newSetting = {}; + + if(!_.isArray(newSettings.settings[settingDef.name])) + { + newSettings.settings[settingDef.name] = []; + } + + newSettings.settings[settingDef.name].push(newSetting); + + _.each(settingDef.settings, function(subSettingDef) + { + var subsettingCol = $('').appendTo(subsettingRow); + var subsettingValueString = ""; + + if(!_.isUndefined(subsettingValue[subSettingDef.name])) + { + subsettingValueString = subsettingValue[subSettingDef.name]; + } + + newSetting[subSettingDef.name] = subsettingValueString; + + $('').appendTo(subsettingCol).val(subsettingValueString).change(function() + { + newSetting[subSettingDef.name] = $(this).val(); + }); + }); + + subsettingRow.append($('').append($('
                  ').append($('
                • ').append($('').click(function() + { + var subSettingIndex = newSettings.settings[settingDef.name].indexOf(newSetting); + + if(subSettingIndex != -1) + { + newSettings.settings[settingDef.name].splice(subSettingIndex, 1); + subsettingRow.remove(); + processHeaderVisibility(); + } + }))))); + + subTableDiv.scrollTop(subTableDiv[0].scrollHeight); + + processHeaderVisibility(); + } + + $('
                  ADD
                  ').appendTo(valueCell).click(function() + { + var newSubsettingValue = {}; + + _.each(settingDef.settings, function(subSettingDef) + { + newSubsettingValue[subSettingDef.name] = ""; + }); + + createSubsettingRow(newSubsettingValue); + }); + + // Create our rows + _.each(currentSubSettingValues, function(currentSubSettingValue, subSettingIndex) + { + createSubsettingRow(currentSubSettingValue); + }); + + break; + } + case "boolean": + { + newSettings.settings[settingDef.name] = currentSettingsValues[settingDef.name]; + + var onOffSwitch = $('
                  ').appendTo(valueCell); + + var input = $('').prependTo(onOffSwitch).change(function() + { + newSettings.settings[settingDef.name] = this.checked; + }); + + if(settingDef.name in currentSettingsValues) + { + input.prop("checked", currentSettingsValues[settingDef.name]); + } + + break; + } + case "option": + { + var defaultValue = currentSettingsValues[settingDef.name]; + + var input = $('').appendTo($('
                  ').appendTo(valueCell)).change(function() + { + newSettings.settings[settingDef.name] = $(this).val(); + }); + + _.each(settingDef.options, function(option) + { + + var optionName; + var optionValue; + + if(_.isObject(option)) + { + optionName = option.name; + optionValue = option.value; + } + else + { + optionName = option; + } + + if(_.isUndefined(optionValue)) + { + optionValue = optionName; + } + + if(_.isUndefined(defaultValue)) + { + defaultValue = optionValue; + } + + $("").text(optionName).attr("value", optionValue).appendTo(input); + }); + + newSettings.settings[settingDef.name] = defaultValue; + + if(settingDef.name in currentSettingsValues) + { + input.val(currentSettingsValues[settingDef.name]); + } + + break; + } + default: + { + newSettings.settings[settingDef.name] = currentSettingsValues[settingDef.name]; + + if(settingDef.type == "calculated") + { + if(settingDef.name in currentSettingsValues) { + var currentValue = currentSettingsValues[settingDef.name]; + if(settingDef.multi_input && _.isArray(currentValue)) { + var includeRemove = false; + for(var i=0; i
                • ') + .mousedown(function(e) { + e.preventDefault(); + _appendCalculatedSettingRow(valueCell, newSettings, settingDef, null, true); + }); + $(valueCell).siblings('.form-label').append(inputAdder); + } + } + else + { + var input = $('').appendTo(valueCell).change(function() + { + if(settingDef.type == "number") + { + newSettings.settings[settingDef.name] = Number($(this).val()); + } + else + { + newSettings.settings[settingDef.name] = $(this).val(); + } + }); + + if(settingDef.name in currentSettingsValues) + { + input.val(currentSettingsValues[settingDef.name]); + } + + if(typeaheadSource && settingDef.typeahead_data_field){ + input.addClass('typeahead_data_field-' + settingDef.typeahead_data_field); + } + + if(typeaheadSource && settingDef.typeahead_field){ + var typeaheadValues = []; + + input.keyup(function(event){ + if(event.which >= 65 && event.which <= 91) { + input.trigger('change'); + } + }); + + $(input).autocomplete({ + source: typeaheadValues, + select: function(event, ui){ + input.val(ui.item.value); + input.trigger('change'); + } + }); + + input.change(function(event){ + var value = input.val(); + var source = _.template(typeaheadSource)({input: value}); + $.get(source, function(data){ + if(typeaheadDataSegment){ + data = data[typeaheadDataSegment]; + } + data = _.select(data, function(elm){ + return elm[settingDef.typeahead_field][0] == value[0]; + }); + + typeaheadValues = _.map(data, function(elm){ + return elm[settingDef.typeahead_field]; + }); + $(input).autocomplete("option", "source", typeaheadValues); + + if(data.length == 1){ + data = data[0]; + //we found the one. let's use it to populate the other info + for(var field in data){ + if(data.hasOwnProperty(field)){ + var otherInput = $(_.template('input.typeahead_data_field-<%= field %>')({field: field})); + if(otherInput){ + otherInput.val(data[field]); + if(otherInput.val() != input.val()) { + otherInput.trigger('change'); + } + } + } + } + } + }); + }); + } + } + + break; + } + } + + if(!_.isUndefined(settingDef.suffix)) + { + valueCell.append($('
                  ' + settingDef.suffix + '
                  ')); + } + + if(!_.isUndefined(settingDef.description)) + { + valueCell.append($('
                  ' + settingDef.description + '
                  ')); + } + }); + } + + + new DialogBox(form, title, "Save", "Cancel", function() + { + $(".validation-error").remove(); + + // Loop through each setting and validate it + for(var index = 0; index < selectedType.settings.length; index++) + { + var settingDef = selectedType.settings[index]; + + if(settingDef.required && (_.isUndefined(newSettings.settings[settingDef.name]) || newSettings.settings[settingDef.name] == "")) + { + _displayValidationError(settingDef.name, "This is required."); + return true; + } + else if(settingDef.type == "number" && !_isNumerical(newSettings.settings[settingDef.name])) + { + _displayValidationError(settingDef.name, "Must be a number."); + return true; + } + } + + if(_.isFunction(settingsSavedCallback)) + { + settingsSavedCallback(newSettings); + } + }); + + // Create our body + var pluginTypeNames = _.keys(pluginTypes); + var typeSelect; + + if(pluginTypeNames.length > 1) + { + var typeRow = createSettingRow("plugin-types", "Type"); + typeSelect = $('').appendTo($('
                  ').appendTo(typeRow)); + + typeSelect.append($("").attr("value", "undefined")); + + _.each(pluginTypes, function(pluginType) + { + typeSelect.append($("").text(pluginType.display_name).attr("value", pluginType.type_name)); + }); + + typeSelect.change(function() + { + newSettings.type = $(this).val(); + newSettings.settings = {}; + + // Remove all the previous settings + _removeSettingsRows(); + + selectedType = pluginTypes[typeSelect.val()]; + + if(_.isUndefined(selectedType)) + { + $("#setting-row-instance-name").hide(); + $("#dialog-ok").hide(); + } + else + { + $("#setting-row-instance-name").show(); + + if(selectedType.description && selectedType.description.length > 0) + { + pluginDescriptionElement.html(selectedType.description).show(); + } + else + { + pluginDescriptionElement.hide(); + } + + $("#dialog-ok").show(); + createSettingsFromDefinition(selectedType.settings, selectedType.typeahead_source, selectedType.typeahead_data_segment); + } + }); + } + else if(pluginTypeNames.length == 1) + { + selectedType = pluginTypes[pluginTypeNames[0]]; + newSettings.type = selectedType.type_name; + newSettings.settings = {}; + createSettingsFromDefinition(selectedType.settings); + } + + if(typeSelect) + { + if(_.isUndefined(currentTypeName)) + { + $("#setting-row-instance-name").hide(); + $("#dialog-ok").hide(); + } + else + { + $("#dialog-ok").show(); + typeSelect.val(currentTypeName).trigger("change"); + } + } + } + + // Public API + return { + createPluginEditor : function( + title, + pluginTypes, + currentInstanceName, + currentTypeName, + currentSettingsValues, + settingsSavedCallback) + { + createPluginEditor(title, pluginTypes, currentInstanceName, currentTypeName, currentSettingsValues, settingsSavedCallback); + } + } } ValueEditor = function(theFreeboardModel) { - var _veDatasourceRegex = new RegExp(".*datasources\\[\"([^\"]*)(\"\\])?(.*)$"); + var _veDatasourceRegex = new RegExp(".*datasources\\[\"([^\"]*)(\"\\])?(.*)$"); - var dropdown = null; - var selectedOptionIndex = 0; - var _autocompleteOptions = []; - var currentValue = null; + var dropdown = null; + var selectedOptionIndex = 0; + var _autocompleteOptions = []; + var currentValue = null; - var EXPECTED_TYPE = { - ANY : "any", - ARRAY : "array", - OBJECT : "object", - STRING : "string", - NUMBER : "number", - BOOLEAN : "boolean" - }; + var EXPECTED_TYPE = { + ANY : "any", + ARRAY : "array", + OBJECT : "object", + STRING : "string", + NUMBER : "number", + BOOLEAN : "boolean" + }; - function _isPotentialTypeMatch(value, expectsType) - { - if(_.isArray(value) || _.isObject(value)) - { - return true; - } - return _isTypeMatch(value, expectsType); - } + function _isPotentialTypeMatch(value, expectsType) + { + if(_.isArray(value) || _.isObject(value)) + { + return true; + } + return _isTypeMatch(value, expectsType); + } - function _isTypeMatch(value, expectsType) { - switch(expectsType) - { - case EXPECTED_TYPE.ANY: return true; - case EXPECTED_TYPE.ARRAY: return _.isArray(value); - case EXPECTED_TYPE.OBJECT: return _.isObject(value); - case EXPECTED_TYPE.STRING: return _.isString(value); - case EXPECTED_TYPE.NUMBER: return _.isNumber(value); - case EXPECTED_TYPE.BOOLEAN: return _.isBoolean(value); - } - } + function _isTypeMatch(value, expectsType) { + switch(expectsType) + { + case EXPECTED_TYPE.ANY: return true; + case EXPECTED_TYPE.ARRAY: return _.isArray(value); + case EXPECTED_TYPE.OBJECT: return _.isObject(value); + case EXPECTED_TYPE.STRING: return _.isString(value); + case EXPECTED_TYPE.NUMBER: return _.isNumber(value); + case EXPECTED_TYPE.BOOLEAN: return _.isBoolean(value); + } + } - function _checkCurrentValueType(element, expectsType) { - $(element).parent().find(".validation-error").remove(); - if(!_isTypeMatch(currentValue, expectsType)) { - $(element).parent().append("
                  " + - "This field expects an expression that evaluates to type " + - expectsType + ".
                  "); - } - } + function _checkCurrentValueType(element, expectsType) { + $(element).parent().find(".validation-error").remove(); + if(!_isTypeMatch(currentValue, expectsType)) { + $(element).parent().append("
                  " + + "This field expects an expression that evaluates to type " + + expectsType + ".
                  "); + } + } - function _resizeValueEditor(element) - { - var lineBreakCount = ($(element).val().match(/\n/g) || []).length; + function _resizeValueEditor(element) + { + var lineBreakCount = ($(element).val().match(/\n/g) || []).length; - var newHeight = Math.min(200, 20 * (lineBreakCount + 1)); + var newHeight = Math.min(200, 20 * (lineBreakCount + 1)); - $(element).css({height: newHeight + "px"}); - } + $(element).css({height: newHeight + "px"}); + } - function _autocompleteFromDatasource(inputString, datasources, expectsType) - { - var match = _veDatasourceRegex.exec(inputString); + function _autocompleteFromDatasource(inputString, datasources, expectsType) + { + var match = _veDatasourceRegex.exec(inputString); - var options = []; + var options = []; - if(match) - { - // Editor value is: datasources["; List all datasources - if(match[1] == "") - { - _.each(datasources, function(datasource) - { - options.push({value: datasource.name(), entity: undefined, - precede_char: "", follow_char: "\"]"}); - }); - } - // Editor value is a partial match for a datasource; list matching datasources - else if(match[1] != "" && _.isUndefined(match[2])) - { - var replacementString = match[1]; + if(match) + { + // Editor value is: datasources["; List all datasources + if(match[1] == "") + { + _.each(datasources, function(datasource) + { + options.push({value: datasource.name(), entity: undefined, + precede_char: "", follow_char: "\"]"}); + }); + } + // Editor value is a partial match for a datasource; list matching datasources + else if(match[1] != "" && _.isUndefined(match[2])) + { + var replacementString = match[1]; - _.each(datasources, function(datasource) - { - var dsName = datasource.name(); + _.each(datasources, function(datasource) + { + var dsName = datasource.name(); - if(dsName != replacementString && dsName.indexOf(replacementString) == 0) - { - options.push({value: dsName, entity: undefined, - precede_char: "", follow_char: "\"]"}); - } - }); - } - // Editor value matches a datasources; parse JSON in order to populate list - else - { - // We already have a datasource selected; find it - var datasource = _.find(datasources, function(datasource) - { - return (datasource.name() === match[1]); - }); + if(dsName != replacementString && dsName.indexOf(replacementString) == 0) + { + options.push({value: dsName, entity: undefined, + precede_char: "", follow_char: "\"]"}); + } + }); + } + // Editor value matches a datasources; parse JSON in order to populate list + else + { + // We already have a datasource selected; find it + var datasource = _.find(datasources, function(datasource) + { + return (datasource.name() === match[1]); + }); - if(!_.isUndefined(datasource)) - { - var dataPath = "data"; - var remainder = ""; + if(!_.isUndefined(datasource)) + { + var dataPath = "data"; + var remainder = ""; - // Parse the partial JSON selectors - if(!_.isUndefined(match[2])) - { - // Strip any incomplete field values, and store the remainder - var remainderIndex = match[3].lastIndexOf("]") + 1; - dataPath = dataPath + match[3].substring(0, remainderIndex); - remainder = match[3].substring(remainderIndex, match[3].length); - remainder = remainder.replace(/^[\[\"]*/, ""); - remainder = remainder.replace(/[\"\]]*$/, ""); - } + // Parse the partial JSON selectors + if(!_.isUndefined(match[2])) + { + // Strip any incomplete field values, and store the remainder + var remainderIndex = match[3].lastIndexOf("]") + 1; + dataPath = dataPath + match[3].substring(0, remainderIndex); + remainder = match[3].substring(remainderIndex, match[3].length); + remainder = remainder.replace(/^[\[\"]*/, ""); + remainder = remainder.replace(/[\"\]]*$/, ""); + } - // Get the data for the last complete JSON field - var dataValue = datasource.getDataRepresentation(dataPath); - currentValue = dataValue; + // Get the data for the last complete JSON field + var dataValue = datasource.getDataRepresentation(dataPath); + currentValue = dataValue; - // For arrays, list out the indices - if(_.isArray(dataValue)) - { - for(var index = 0; index < dataValue.length; index++) - { - if(index.toString().indexOf(remainder) == 0) - { - var value = dataValue[index]; - if(_isPotentialTypeMatch(value, expectsType)) - { - options.push({value: index, entity: value, - precede_char: "[", follow_char: "]", - preview: value.toString()}); - } - } - } - } - // For objects, list out the keys - else if(_.isObject(dataValue)) - { - _.each(dataValue, function(value, name) - { - if(name.indexOf(remainder) == 0) - { - if(_isPotentialTypeMatch(value, expectsType)) - { - options.push({value: name, entity: value, - precede_char: "[\"", follow_char: "\"]"}); - } - } - }); - } - // For everything else, do nothing (no further selection possible) - else - { - // no-op - } - } - } - } - _autocompleteOptions = options; - } + // For arrays, list out the indices + if(_.isArray(dataValue)) + { + for(var index = 0; index < dataValue.length; index++) + { + if(index.toString().indexOf(remainder) == 0) + { + var value = dataValue[index]; + if(_isPotentialTypeMatch(value, expectsType)) + { + options.push({value: index, entity: value, + precede_char: "[", follow_char: "]", + preview: value.toString()}); + } + } + } + } + // For objects, list out the keys + else if(_.isObject(dataValue)) + { + _.each(dataValue, function(value, name) + { + if(name.indexOf(remainder) == 0) + { + if(_isPotentialTypeMatch(value, expectsType)) + { + options.push({value: name, entity: value, + precede_char: "[\"", follow_char: "\"]"}); + } + } + }); + } + // For everything else, do nothing (no further selection possible) + else + { + // no-op + } + } + } + } + _autocompleteOptions = options; + } - function _renderAutocompleteDropdown(element, expectsType) - { - var inputString = $(element).val().substring(0, $(element).getCaretPosition()); + function _renderAutocompleteDropdown(element, expectsType) + { + var inputString = $(element).val().substring(0, $(element).getCaretPosition()); - // Weird issue where the textarea box was putting in ASCII (nbsp) for spaces. - inputString = inputString.replace(String.fromCharCode(160), " "); + // Weird issue where the textarea box was putting in ASCII (nbsp) for spaces. + inputString = inputString.replace(String.fromCharCode(160), " "); - _autocompleteFromDatasource(inputString, theFreeboardModel.datasources(), expectsType); + _autocompleteFromDatasource(inputString, theFreeboardModel.datasources(), expectsType); - if(_autocompleteOptions.length > 0) - { - if(!dropdown) - { - dropdown = $('
                    ') - .insertAfter(element) - .width($(element).outerWidth() - 2) - .css("left", $(element).position().left) - .css("top", $(element).position().top + $(element).outerHeight() - 1); - } + if(_autocompleteOptions.length > 0) + { + if(!dropdown) + { + dropdown = $('
                      ') + .insertAfter(element) + .width($(element).outerWidth() - 2) + .css("left", $(element).position().left) + .css("top", $(element).position().top + $(element).outerHeight() - 1); + } - dropdown.empty(); - dropdown.scrollTop(0); + dropdown.empty(); + dropdown.scrollTop(0); - var selected = true; - selectedOptionIndex = 0; + var selected = true; + selectedOptionIndex = 0; - _.each(_autocompleteOptions, function(option, index) - { - var li = _renderAutocompleteDropdownOption(element, inputString, option, index); - if(selected) - { - $(li).addClass("selected"); - selected = false; - } - }); - } - else - { - _checkCurrentValueType(element, expectsType); - $(element).next("ul#value-selector").remove(); - dropdown = null; - selectedOptionIndex = -1; - } - } + _.each(_autocompleteOptions, function(option, index) + { + var li = _renderAutocompleteDropdownOption(element, inputString, option, index); + if(selected) + { + $(li).addClass("selected"); + selected = false; + } + }); + } + else + { + _checkCurrentValueType(element, expectsType); + $(element).next("ul#value-selector").remove(); + dropdown = null; + selectedOptionIndex = -1; + } + } - function _renderAutocompleteDropdownOption(element, inputString, option, currentIndex) - { - var optionLabel = option.value; - if(option.preview) - { - optionLabel = optionLabel + "" + option.preview + ""; - } - var li = $('
                    • ' + optionLabel + '
                    • ').appendTo(dropdown) - .mouseenter(function() - { - $(this).trigger("freeboard-select"); - }) - .mousedown(function(event) - { - $(this).trigger("freeboard-insertValue"); - event.preventDefault(); - }) - .data("freeboard-optionIndex", currentIndex) - .data("freeboard-optionValue", option.value) - .bind("freeboard-insertValue", function() - { - var optionValue = option.value; - optionValue = option.precede_char + optionValue + option.follow_char; + function _renderAutocompleteDropdownOption(element, inputString, option, currentIndex) + { + var optionLabel = option.value; + if(option.preview) + { + optionLabel = optionLabel + "" + option.preview + ""; + } + var li = $('
                    • ' + optionLabel + '
                    • ').appendTo(dropdown) + .mouseenter(function() + { + $(this).trigger("freeboard-select"); + }) + .mousedown(function(event) + { + $(this).trigger("freeboard-insertValue"); + event.preventDefault(); + }) + .data("freeboard-optionIndex", currentIndex) + .data("freeboard-optionValue", option.value) + .bind("freeboard-insertValue", function() + { + var optionValue = option.value; + optionValue = option.precede_char + optionValue + option.follow_char; - var replacementIndex = inputString.lastIndexOf("]"); - if(replacementIndex != -1) - { - $(element).replaceTextAt(replacementIndex+1, $(element).val().length, - optionValue); - } - else - { - $(element).insertAtCaret(optionValue); - } + var replacementIndex = inputString.lastIndexOf("]"); + if(replacementIndex != -1) + { + $(element).replaceTextAt(replacementIndex+1, $(element).val().length, + optionValue); + } + else + { + $(element).insertAtCaret(optionValue); + } - currentValue = option.entity; - $(element).triggerHandler("mouseup"); - }) - .bind("freeboard-select", function() - { - $(this).parent().find("li.selected").removeClass("selected"); - $(this).addClass("selected"); - selectedOptionIndex = $(this).data("freeboard-optionIndex"); - }); - return li; - } + currentValue = option.entity; + $(element).triggerHandler("mouseup"); + }) + .bind("freeboard-select", function() + { + $(this).parent().find("li.selected").removeClass("selected"); + $(this).addClass("selected"); + selectedOptionIndex = $(this).data("freeboard-optionIndex"); + }); + return li; + } - function createValueEditor(element, expectsType) - { - $(element).addClass("calculated-value-input") - .bind("keyup mouseup freeboard-eval", function(event) { - // Ignore arrow keys and enter keys - if(dropdown && event.type == "keyup" - && (event.keyCode == 38 || event.keyCode == 40 || event.keyCode == 13)) - { - event.preventDefault(); - return; - } - _renderAutocompleteDropdown(element, expectsType); - }) - .focus(function() - { - $(element).css({"z-index" : 3001}); - _resizeValueEditor(element); - }) - .focusout(function() - { - _checkCurrentValueType(element, expectsType); - $(element).css({ - "height": "", - "z-index" : 3000 - }); - $(element).next("ul#value-selector").remove(); - dropdown = null; - selectedOptionIndex = -1; - }) - .bind("keydown", function(event) - { + function createValueEditor(element, expectsType) + { + $(element).addClass("calculated-value-input") + .bind("keyup mouseup freeboard-eval", function(event) { + // Ignore arrow keys and enter keys + if(dropdown && event.type == "keyup" + && (event.keyCode == 38 || event.keyCode == 40 || event.keyCode == 13)) + { + event.preventDefault(); + return; + } + _renderAutocompleteDropdown(element, expectsType); + }) + .focus(function() + { + $(element).css({"z-index" : 3001}); + _resizeValueEditor(element); + }) + .focusout(function() + { + _checkCurrentValueType(element, expectsType); + $(element).css({ + "height": "", + "z-index" : 3000 + }); + $(element).next("ul#value-selector").remove(); + dropdown = null; + selectedOptionIndex = -1; + }) + .bind("keydown", function(event) + { - if(dropdown) - { - if(event.keyCode == 38 || event.keyCode == 40) // Handle Arrow keys - { - event.preventDefault(); + if(dropdown) + { + if(event.keyCode == 38 || event.keyCode == 40) // Handle Arrow keys + { + event.preventDefault(); - var optionItems = $(dropdown).find("li"); + var optionItems = $(dropdown).find("li"); - if(event.keyCode == 38) // Up Arrow - { - selectedOptionIndex--; - } - else if(event.keyCode == 40) // Down Arrow - { - selectedOptionIndex++; - } + if(event.keyCode == 38) // Up Arrow + { + selectedOptionIndex--; + } + else if(event.keyCode == 40) // Down Arrow + { + selectedOptionIndex++; + } - if(selectedOptionIndex < 0) - { - selectedOptionIndex = optionItems.size() - 1; - } - else if(selectedOptionIndex >= optionItems.size()) - { - selectedOptionIndex = 0; - } + if(selectedOptionIndex < 0) + { + selectedOptionIndex = optionItems.size() - 1; + } + else if(selectedOptionIndex >= optionItems.size()) + { + selectedOptionIndex = 0; + } - var optionElement = $(optionItems).eq(selectedOptionIndex); + var optionElement = $(optionItems).eq(selectedOptionIndex); - optionElement.trigger("freeboard-select"); - $(dropdown).scrollTop($(optionElement).position().top); - } - else if(event.keyCode == 13) // Handle enter key - { - event.preventDefault(); + optionElement.trigger("freeboard-select"); + $(dropdown).scrollTop($(optionElement).position().top); + } + else if(event.keyCode == 13) // Handle enter key + { + event.preventDefault(); - if(selectedOptionIndex != -1) - { - $(dropdown).find("li").eq(selectedOptionIndex) - .trigger("freeboard-insertValue"); - } - } - } - }); - } + if(selectedOptionIndex != -1) + { + $(dropdown).find("li").eq(selectedOptionIndex) + .trigger("freeboard-insertValue"); + } + } + } + }); + } - // Public API - return { - createValueEditor : function(element, expectsType) - { - if(expectsType) - { - createValueEditor(element, expectsType); - } - else { - createValueEditor(element, EXPECTED_TYPE.ANY); - } - }, - EXPECTED_TYPE : EXPECTED_TYPE - } + // Public API + return { + createValueEditor : function(element, expectsType) + { + if(expectsType) + { + createValueEditor(element, expectsType); + } + else { + createValueEditor(element, EXPECTED_TYPE.ANY); + } + }, + EXPECTED_TYPE : EXPECTED_TYPE + } } function WidgetModel(theFreeboardModel, widgetPlugins) { - function disposeWidgetInstance() { - if (!_.isUndefined(self.widgetInstance)) { - if (_.isFunction(self.widgetInstance.onDispose)) { - self.widgetInstance.onDispose(); - } + function disposeWidgetInstance() { + if (!_.isUndefined(self.widgetInstance)) { + if (_.isFunction(self.widgetInstance.onDispose)) { + self.widgetInstance.onDispose(); + } - self.widgetInstance = undefined; - } - } + self.widgetInstance = undefined; + } + } - var self = this; + var self = this; - this.datasourceRefreshNotifications = {}; - this.calculatedSettingScripts = {}; + this.datasourceRefreshNotifications = {}; + this.calculatedSettingScripts = {}; - this.title = ko.observable(); - this.fillSize = ko.observable(false); + this.title = ko.observable(); + this.fillSize = ko.observable(false); - this.type = ko.observable(); - this.type.subscribe(function (newValue) { - disposeWidgetInstance(); + this.type = ko.observable(); + this.type.subscribe(function (newValue) { + disposeWidgetInstance(); - if ((newValue in widgetPlugins) && _.isFunction(widgetPlugins[newValue].newInstance)) { - var widgetType = widgetPlugins[newValue]; + if ((newValue in widgetPlugins) && _.isFunction(widgetPlugins[newValue].newInstance)) { + var widgetType = widgetPlugins[newValue]; - function finishLoad() { - widgetType.newInstance(self.settings(), function (widgetInstance) { + function finishLoad() { + widgetType.newInstance(self.settings(), function (widgetInstance) { - self.fillSize((widgetType.fill_size === true)); - self.widgetInstance = widgetInstance; - self.shouldRender(true); - self._heightUpdate.valueHasMutated(); + self.fillSize((widgetType.fill_size === true)); + self.widgetInstance = widgetInstance; + self.shouldRender(true); + self._heightUpdate.valueHasMutated(); - }); - } + }); + } - // Do we need to load any external scripts? - if (widgetType.external_scripts) { - head.js(widgetType.external_scripts.slice(0), finishLoad); // Need to clone the array because head.js adds some weird functions to it - } - else { - finishLoad(); - } - } - }); + // Do we need to load any external scripts? + if (widgetType.external_scripts) { + head.js(widgetType.external_scripts.slice(0), finishLoad); // Need to clone the array because head.js adds some weird functions to it + } + else { + finishLoad(); + } + } + }); - this.settings = ko.observable({}); - this.settings.subscribe(function (newValue) { - if (!_.isUndefined(self.widgetInstance) && _.isFunction(self.widgetInstance.onSettingsChanged)) { - self.widgetInstance.onSettingsChanged(newValue); - } + this.settings = ko.observable({}); + this.settings.subscribe(function (newValue) { + if (!_.isUndefined(self.widgetInstance) && _.isFunction(self.widgetInstance.onSettingsChanged)) { + self.widgetInstance.onSettingsChanged(newValue); + } - self.updateCalculatedSettings(); - self._heightUpdate.valueHasMutated(); - }); + self.updateCalculatedSettings(); + self._heightUpdate.valueHasMutated(); + }); - this.processDatasourceUpdate = function (datasourceName) { - var refreshSettingNames = self.datasourceRefreshNotifications[datasourceName]; + this.processDatasourceUpdate = function (datasourceName) { + var refreshSettingNames = self.datasourceRefreshNotifications[datasourceName]; - if (_.isArray(refreshSettingNames)) { - _.each(refreshSettingNames, function (settingName) { - self.processCalculatedSetting(settingName); - }); - } - } + if (_.isArray(refreshSettingNames)) { + _.each(refreshSettingNames, function (settingName) { + self.processCalculatedSetting(settingName); + }); + } + } - this.callValueFunction = function (theFunction) { - return theFunction.call(undefined, theFreeboardModel.datasourceData); - } + this.callValueFunction = function (theFunction) { + return theFunction.call(undefined, theFreeboardModel.datasourceData); + } - this.processSizeChange = function () { - if (!_.isUndefined(self.widgetInstance) && _.isFunction(self.widgetInstance.onSizeChanged)) { - self.widgetInstance.onSizeChanged(); - } - } + this.processSizeChange = function () { + if (!_.isUndefined(self.widgetInstance) && _.isFunction(self.widgetInstance.onSizeChanged)) { + self.widgetInstance.onSizeChanged(); + } + } - this.processCalculatedSetting = function (settingName) { - if (_.isFunction(self.calculatedSettingScripts[settingName])) { - var returnValue = undefined; + this.processCalculatedSetting = function (settingName) { + if (_.isFunction(self.calculatedSettingScripts[settingName])) { + var returnValue = undefined; - try { - returnValue = self.callValueFunction(self.calculatedSettingScripts[settingName]); - } - catch (e) { - var rawValue = self.settings()[settingName]; + try { + returnValue = self.callValueFunction(self.calculatedSettingScripts[settingName]); + } + catch (e) { + var rawValue = self.settings()[settingName]; - // If there is a reference error and the value just contains letters and numbers, then - if (e instanceof ReferenceError && (/^\w+$/).test(rawValue)) { - returnValue = rawValue; - } - } + // If there is a reference error and the value just contains letters and numbers, then + if (e instanceof ReferenceError && (/^\w+$/).test(rawValue)) { + returnValue = rawValue; + } + } - if (!_.isUndefined(self.widgetInstance) && _.isFunction(self.widgetInstance.onCalculatedValueChanged) && !_.isUndefined(returnValue)) { - try { - self.widgetInstance.onCalculatedValueChanged(settingName, returnValue); - } - catch (e) { - console.log(e.toString()); - } - } - } - } + if (!_.isUndefined(self.widgetInstance) && _.isFunction(self.widgetInstance.onCalculatedValueChanged) && !_.isUndefined(returnValue)) { + try { + self.widgetInstance.onCalculatedValueChanged(settingName, returnValue); + } + catch (e) { + console.log(e.toString()); + } + } + } + } - this.updateCalculatedSettings = function () { - self.datasourceRefreshNotifications = {}; - self.calculatedSettingScripts = {}; + this.updateCalculatedSettings = function () { + self.datasourceRefreshNotifications = {}; + self.calculatedSettingScripts = {}; - if (_.isUndefined(self.type())) { - return; - } + if (_.isUndefined(self.type())) { + return; + } - // Check for any calculated settings - var settingsDefs = widgetPlugins[self.type()].settings; - var datasourceRegex = new RegExp("datasources.([\\w_-]+)|datasources\\[['\"]([^'\"]+)", "g"); - var currentSettings = self.settings(); + // Check for any calculated settings + var settingsDefs = widgetPlugins[self.type()].settings; + var datasourceRegex = new RegExp("datasources.([\\w_-]+)|datasources\\[['\"]([^'\"]+)", "g"); + var currentSettings = self.settings(); - _.each(settingsDefs, function (settingDef) { - if (settingDef.type == "calculated") { - var script = currentSettings[settingDef.name]; + _.each(settingsDefs, function (settingDef) { + if (settingDef.type == "calculated") { + var script = currentSettings[settingDef.name]; - if (!_.isUndefined(script)) { + if (!_.isUndefined(script)) { - if(_.isArray(script)) { - script = "[" + script.join(",") + "]"; - } + if(_.isArray(script)) { + script = "[" + script.join(",") + "]"; + } - // If there is no return, add one - if ((script.match(/;/g) || []).length <= 1 && script.indexOf("return") == -1) { - script = "return " + script; - } + // If there is no return, add one + if ((script.match(/;/g) || []).length <= 1 && script.indexOf("return") == -1) { + script = "return " + script; + } - var valueFunction; + var valueFunction; - try { - valueFunction = new Function("datasources", script); - } - catch (e) { - var literalText = currentSettings[settingDef.name].replace(/"/g, '\\"').replace(/[\r\n]/g, ' \\\n'); + try { + valueFunction = new Function("datasources", script); + } + catch (e) { + var literalText = currentSettings[settingDef.name].replace(/"/g, '\\"').replace(/[\r\n]/g, ' \\\n'); - // If the value function cannot be created, then go ahead and treat it as literal text - valueFunction = new Function("datasources", "return \"" + literalText + "\";"); - } + // If the value function cannot be created, then go ahead and treat it as literal text + valueFunction = new Function("datasources", "return \"" + literalText + "\";"); + } - self.calculatedSettingScripts[settingDef.name] = valueFunction; - self.processCalculatedSetting(settingDef.name); + self.calculatedSettingScripts[settingDef.name] = valueFunction; + self.processCalculatedSetting(settingDef.name); - // Are there any datasources we need to be subscribed to? - var matches; + // Are there any datasources we need to be subscribed to? + var matches; - while (matches = datasourceRegex.exec(script)) { - var dsName = (matches[1] || matches[2]); - var refreshSettingNames = self.datasourceRefreshNotifications[dsName]; + while (matches = datasourceRegex.exec(script)) { + var dsName = (matches[1] || matches[2]); + var refreshSettingNames = self.datasourceRefreshNotifications[dsName]; - if (_.isUndefined(refreshSettingNames)) { - refreshSettingNames = []; - self.datasourceRefreshNotifications[dsName] = refreshSettingNames; - } + if (_.isUndefined(refreshSettingNames)) { + refreshSettingNames = []; + self.datasourceRefreshNotifications[dsName] = refreshSettingNames; + } - if(_.indexOf(refreshSettingNames, settingDef.name) == -1) // Only subscribe to this notification once. - { - refreshSettingNames.push(settingDef.name); - } - } - } - } - }); - } + if(_.indexOf(refreshSettingNames, settingDef.name) == -1) // Only subscribe to this notification once. + { + refreshSettingNames.push(settingDef.name); + } + } + } + } + }); + } - this._heightUpdate = ko.observable(); - this.height = ko.computed({ - read: function () { - self._heightUpdate(); + this._heightUpdate = ko.observable(); + this.height = ko.computed({ + read: function () { + self._heightUpdate(); - if (!_.isUndefined(self.widgetInstance) && _.isFunction(self.widgetInstance.getHeight)) { - return self.widgetInstance.getHeight(); - } + if (!_.isUndefined(self.widgetInstance) && _.isFunction(self.widgetInstance.getHeight)) { + return self.widgetInstance.getHeight(); + } - return 1; - } - }); + return 1; + } + }); - this.shouldRender = ko.observable(false); - this.render = function (element) { - self.shouldRender(false); - if (!_.isUndefined(self.widgetInstance) && _.isFunction(self.widgetInstance.render)) { - self.widgetInstance.render(element); - self.updateCalculatedSettings(); - } - } + this.shouldRender = ko.observable(false); + this.render = function (element) { + self.shouldRender(false); + if (!_.isUndefined(self.widgetInstance) && _.isFunction(self.widgetInstance.render)) { + self.widgetInstance.render(element); + self.updateCalculatedSettings(); + } + } - this.dispose = function () { + this.dispose = function () { - } + } - this.serialize = function () { - return { - title: self.title(), - type: self.type(), - settings: self.settings() - }; - } + this.serialize = function () { + return { + title: self.title(), + type: self.type(), + settings: self.settings() + }; + } - this.deserialize = function (object) { - self.title(object.title); - self.settings(object.settings); - self.type(object.type); - } + this.deserialize = function (object) { + self.title(object.title); + self.settings(object.settings); + self.type(object.type); + } } // ┌────────────────────────────────────────────────────────────────────┐ \\ @@ -2378,165 +2378,165 @@ function WidgetModel(theFreeboardModel, widgetPlugins) { // Jquery plugin to watch for attribute changes (function($) { - function isDOMAttrModifiedSupported() - { - var p = document.createElement('p'); - var flag = false; + function isDOMAttrModifiedSupported() + { + var p = document.createElement('p'); + var flag = false; - if(p.addEventListener) - { - p.addEventListener('DOMAttrModified', function() - { - flag = true - }, false); - } - else if(p.attachEvent) - { - p.attachEvent('onDOMAttrModified', function() - { - flag = true - }); - } - else - { - return false; - } + if(p.addEventListener) + { + p.addEventListener('DOMAttrModified', function() + { + flag = true + }, false); + } + else if(p.attachEvent) + { + p.attachEvent('onDOMAttrModified', function() + { + flag = true + }); + } + else + { + return false; + } - p.setAttribute('id', 'target'); + p.setAttribute('id', 'target'); - return flag; - } + return flag; + } - function checkAttributes(chkAttr, e) - { - if(chkAttr) - { - var attributes = this.data('attr-old-value'); + function checkAttributes(chkAttr, e) + { + if(chkAttr) + { + var attributes = this.data('attr-old-value'); - if(e.attributeName.indexOf('style') >= 0) - { - if(!attributes['style']) - { - attributes['style'] = {}; - } //initialize - var keys = e.attributeName.split('.'); - e.attributeName = keys[0]; - e.oldValue = attributes['style'][keys[1]]; //old value - e.newValue = keys[1] + ':' + this.prop("style")[$.camelCase(keys[1])]; //new value - attributes['style'][keys[1]] = e.newValue; - } - else - { - e.oldValue = attributes[e.attributeName]; - e.newValue = this.attr(e.attributeName); - attributes[e.attributeName] = e.newValue; - } + if(e.attributeName.indexOf('style') >= 0) + { + if(!attributes['style']) + { + attributes['style'] = {}; + } //initialize + var keys = e.attributeName.split('.'); + e.attributeName = keys[0]; + e.oldValue = attributes['style'][keys[1]]; //old value + e.newValue = keys[1] + ':' + this.prop("style")[$.camelCase(keys[1])]; //new value + attributes['style'][keys[1]] = e.newValue; + } + else + { + e.oldValue = attributes[e.attributeName]; + e.newValue = this.attr(e.attributeName); + attributes[e.attributeName] = e.newValue; + } - this.data('attr-old-value', attributes); //update the old value object - } - } + this.data('attr-old-value', attributes); //update the old value object + } + } - //initialize Mutation Observer - var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; + //initialize Mutation Observer + var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; - $.fn.attrchange = function(o) - { + $.fn.attrchange = function(o) + { - var cfg = { - trackValues: false, - callback : $.noop - }; + var cfg = { + trackValues: false, + callback : $.noop + }; - //for backward compatibility - if(typeof o === "function") - { - cfg.callback = o; - } - else - { - $.extend(cfg, o); - } + //for backward compatibility + if(typeof o === "function") + { + cfg.callback = o; + } + else + { + $.extend(cfg, o); + } - if(cfg.trackValues) - { //get attributes old value - $(this).each(function(i, el) - { - var attributes = {}; - for(var attr, i = 0, attrs = el.attributes, l = attrs.length; i < l; i++) - { - attr = attrs.item(i); - attributes[attr.nodeName] = attr.value; - } + if(cfg.trackValues) + { //get attributes old value + $(this).each(function(i, el) + { + var attributes = {}; + for(var attr, i = 0, attrs = el.attributes, l = attrs.length; i < l; i++) + { + attr = attrs.item(i); + attributes[attr.nodeName] = attr.value; + } - $(this).data('attr-old-value', attributes); - }); - } + $(this).data('attr-old-value', attributes); + }); + } - if(MutationObserver) - { //Modern Browsers supporting MutationObserver - /* - Mutation Observer is still new and not supported by all browsers. - http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/1622.html - */ - var mOptions = { - subtree : false, - attributes : true, - attributeOldValue: cfg.trackValues - }; + if(MutationObserver) + { //Modern Browsers supporting MutationObserver + /* + Mutation Observer is still new and not supported by all browsers. + http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/1622.html + */ + var mOptions = { + subtree : false, + attributes : true, + attributeOldValue: cfg.trackValues + }; - var observer = new MutationObserver(function(mutations) - { - mutations.forEach(function(e) - { - var _this = e.target; + var observer = new MutationObserver(function(mutations) + { + mutations.forEach(function(e) + { + var _this = e.target; - //get new value if trackValues is true - if(cfg.trackValues) - { - /** - * @KNOWN_ISSUE: The new value is buggy for STYLE attribute as we don't have - * any additional information on which style is getting updated. - * */ - e.newValue = $(_this).attr(e.attributeName); - } + //get new value if trackValues is true + if(cfg.trackValues) + { + /** + * @KNOWN_ISSUE: The new value is buggy for STYLE attribute as we don't have + * any additional information on which style is getting updated. + * */ + e.newValue = $(_this).attr(e.attributeName); + } - cfg.callback.call(_this, e); - }); - }); + cfg.callback.call(_this, e); + }); + }); - return this.each(function() - { - observer.observe(this, mOptions); - }); - } - else if(isDOMAttrModifiedSupported()) - { //Opera - //Good old Mutation Events but the performance is no good - //http://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/ - return this.on('DOMAttrModified', function(event) - { - if(event.originalEvent) - { - event = event.originalEvent; - } //jQuery normalization is not required for us - event.attributeName = event.attrName; //property names to be consistent with MutationObserver - event.oldValue = event.prevValue; //property names to be consistent with MutationObserver - cfg.callback.call(this, event); - }); - } - else if('onpropertychange' in document.body) - { //works only in IE - return this.on('propertychange', function(e) - { - e.attributeName = window.event.propertyName; - //to set the attr old value - checkAttributes.call($(this), cfg.trackValues, e); - cfg.callback.call(this, e); - }); - } + return this.each(function() + { + observer.observe(this, mOptions); + }); + } + else if(isDOMAttrModifiedSupported()) + { //Opera + //Good old Mutation Events but the performance is no good + //http://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/ + return this.on('DOMAttrModified', function(event) + { + if(event.originalEvent) + { + event = event.originalEvent; + } //jQuery normalization is not required for us + event.attributeName = event.attrName; //property names to be consistent with MutationObserver + event.oldValue = event.prevValue; //property names to be consistent with MutationObserver + cfg.callback.call(this, event); + }); + } + else if('onpropertychange' in document.body) + { //works only in IE + return this.on('propertychange', function(e) + { + e.attributeName = window.event.propertyName; + //to set the attr old value + checkAttributes.call($(this), cfg.trackValues, e); + cfg.callback.call(this, e); + }); + } - return this; - } + return this; + } })(jQuery); (function(jQuery) { @@ -2567,242 +2567,242 @@ function WidgetModel(theFreeboardModel, widgetPlugins) { var freeboard = (function() { - var datasourcePlugins = {}; - var widgetPlugins = {}; + var datasourcePlugins = {}; + var widgetPlugins = {}; - var freeboardUI = new FreeboardUI(); - var theFreeboardModel = new FreeboardModel(datasourcePlugins, widgetPlugins, freeboardUI); + var freeboardUI = new FreeboardUI(); + var theFreeboardModel = new FreeboardModel(datasourcePlugins, widgetPlugins, freeboardUI); - var jsEditor = new JSEditor(); - var valueEditor = new ValueEditor(theFreeboardModel); - var pluginEditor = new PluginEditor(jsEditor, valueEditor); + var jsEditor = new JSEditor(); + var valueEditor = new ValueEditor(theFreeboardModel); + var pluginEditor = new PluginEditor(jsEditor, valueEditor); - var developerConsole = new DeveloperConsole(theFreeboardModel); + var developerConsole = new DeveloperConsole(theFreeboardModel); - var currentStyle = { - values: { - "font-family": '"HelveticaNeue-UltraLight", "Helvetica Neue Ultra Light", "Helvetica Neue", sans-serif', - "color" : "#d3d4d4", - "font-weight": 100 - } - }; + var currentStyle = { + values: { + "font-family": '"HelveticaNeue-UltraLight", "Helvetica Neue Ultra Light", "Helvetica Neue", sans-serif', + "color" : "#d3d4d4", + "font-weight": 100 + } + }; - ko.bindingHandlers.pluginEditor = { - init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) - { - var options = ko.unwrap(valueAccessor()); + ko.bindingHandlers.pluginEditor = { + init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) + { + var options = ko.unwrap(valueAccessor()); - var types = {}; - var settings = undefined; - var title = ""; + var types = {}; + var settings = undefined; + var title = ""; - if(options.type == 'datasource') - { - types = datasourcePlugins; - title = "Datasource"; - } - else if(options.type == 'widget') - { - types = widgetPlugins; - title = "Widget"; - } - else if(options.type == 'pane') - { - title = "Pane"; - } + if(options.type == 'datasource') + { + types = datasourcePlugins; + title = "Datasource"; + } + else if(options.type == 'widget') + { + types = widgetPlugins; + title = "Widget"; + } + else if(options.type == 'pane') + { + title = "Pane"; + } - $(element).click(function(event) - { - if(options.operation == 'delete') - { - var phraseElement = $('

                      Are you sure you want to delete this ' + title + '?

                      '); - new DialogBox(phraseElement, "Confirm Delete", "Yes", "No", function() - { + $(element).click(function(event) + { + if(options.operation == 'delete') + { + var phraseElement = $('

                      Are you sure you want to delete this ' + title + '?

                      '); + new DialogBox(phraseElement, "Confirm Delete", "Yes", "No", function() + { - if(options.type == 'datasource') - { - theFreeboardModel.deleteDatasource(viewModel); - } - else if(options.type == 'widget') - { - theFreeboardModel.deleteWidget(viewModel); - } - else if(options.type == 'pane') - { - theFreeboardModel.deletePane(viewModel); - } + if(options.type == 'datasource') + { + theFreeboardModel.deleteDatasource(viewModel); + } + else if(options.type == 'widget') + { + theFreeboardModel.deleteWidget(viewModel); + } + else if(options.type == 'pane') + { + theFreeboardModel.deletePane(viewModel); + } - }); - } - else - { - var instanceType = undefined; + }); + } + else + { + var instanceType = undefined; - if(options.type == 'datasource') - { - if(options.operation == 'add') - { - settings = {}; - } - else - { - instanceType = viewModel.type(); - settings = viewModel.settings(); - settings.name = viewModel.name(); - } - } - else if(options.type == 'widget') - { - if(options.operation == 'add') - { - settings = {}; - } - else - { - instanceType = viewModel.type(); - settings = viewModel.settings(); - } - } - else if(options.type == 'pane') - { - settings = {}; + if(options.type == 'datasource') + { + if(options.operation == 'add') + { + settings = {}; + } + else + { + instanceType = viewModel.type(); + settings = viewModel.settings(); + settings.name = viewModel.name(); + } + } + else if(options.type == 'widget') + { + if(options.operation == 'add') + { + settings = {}; + } + else + { + instanceType = viewModel.type(); + settings = viewModel.settings(); + } + } + else if(options.type == 'pane') + { + settings = {}; - if(options.operation == 'edit') - { - settings.title = viewModel.title(); - settings.col_width = viewModel.col_width(); - } + if(options.operation == 'edit') + { + settings.title = viewModel.title(); + settings.col_width = viewModel.col_width(); + } - types = { - settings: { - settings: [ - { - name : "title", - display_name: "Title", - type : "text" - }, - { - name : "col_width", - display_name : "Columns", - type : "number", - default_value : 1, - required : true - } - ] - } - } - } + types = { + settings: { + settings: [ + { + name : "title", + display_name: "Title", + type : "text" + }, + { + name : "col_width", + display_name : "Columns", + type : "number", + default_value : 1, + required : true + } + ] + } + } + } - pluginEditor.createPluginEditor(title, types, instanceType, settings, function(newSettings) - { - if(options.operation == 'add') - { - if(options.type == 'datasource') - { - var newViewModel = new DatasourceModel(theFreeboardModel, datasourcePlugins); - theFreeboardModel.addDatasource(newViewModel); + pluginEditor.createPluginEditor(title, types, instanceType, settings, function(newSettings) + { + if(options.operation == 'add') + { + if(options.type == 'datasource') + { + var newViewModel = new DatasourceModel(theFreeboardModel, datasourcePlugins); + theFreeboardModel.addDatasource(newViewModel); - newViewModel.name(newSettings.settings.name); - delete newSettings.settings.name; + newViewModel.name(newSettings.settings.name); + delete newSettings.settings.name; - newViewModel.settings(newSettings.settings); - newViewModel.type(newSettings.type); - } - else if(options.type == 'widget') - { - var newViewModel = new WidgetModel(theFreeboardModel, widgetPlugins); - newViewModel.settings(newSettings.settings); - newViewModel.type(newSettings.type); + newViewModel.settings(newSettings.settings); + newViewModel.type(newSettings.type); + } + else if(options.type == 'widget') + { + var newViewModel = new WidgetModel(theFreeboardModel, widgetPlugins); + newViewModel.settings(newSettings.settings); + newViewModel.type(newSettings.type); - viewModel.widgets.push(newViewModel); + viewModel.widgets.push(newViewModel); - freeboardUI.attachWidgetEditIcons(element); - } - } - else if(options.operation == 'edit') - { - if(options.type == 'pane') - { - viewModel.title(newSettings.settings.title); - viewModel.col_width(newSettings.settings.col_width); - freeboardUI.processResize(false); - } - else - { - if(options.type == 'datasource') - { - viewModel.name(newSettings.settings.name); - delete newSettings.settings.name; - } + freeboardUI.attachWidgetEditIcons(element); + } + } + else if(options.operation == 'edit') + { + if(options.type == 'pane') + { + viewModel.title(newSettings.settings.title); + viewModel.col_width(newSettings.settings.col_width); + freeboardUI.processResize(false); + } + else + { + if(options.type == 'datasource') + { + viewModel.name(newSettings.settings.name); + delete newSettings.settings.name; + } - viewModel.type(newSettings.type); - viewModel.settings(newSettings.settings); - } - } - }); - } - }); - } - } + viewModel.type(newSettings.type); + viewModel.settings(newSettings.settings); + } + } + }); + } + }); + } + } - ko.virtualElements.allowedBindings.datasourceTypeSettings = true; - ko.bindingHandlers.datasourceTypeSettings = { - update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) - { - processPluginSettings(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext); - } - } + ko.virtualElements.allowedBindings.datasourceTypeSettings = true; + ko.bindingHandlers.datasourceTypeSettings = { + update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) + { + processPluginSettings(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext); + } + } - ko.bindingHandlers.pane = { - init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) - { - if(theFreeboardModel.isEditing()) - { - $(element).css({cursor: "pointer"}); - } + ko.bindingHandlers.pane = { + init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) + { + if(theFreeboardModel.isEditing()) + { + $(element).css({cursor: "pointer"}); + } - freeboardUI.addPane(element, viewModel, bindingContext.$root.isEditing()); - }, - update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) - { - // If pane has been removed - if(theFreeboardModel.panes.indexOf(viewModel) == -1) - { - freeboardUI.removePane(element); - } - freeboardUI.updatePane(element, viewModel); - } - } + freeboardUI.addPane(element, viewModel, bindingContext.$root.isEditing()); + }, + update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) + { + // If pane has been removed + if(theFreeboardModel.panes.indexOf(viewModel) == -1) + { + freeboardUI.removePane(element); + } + freeboardUI.updatePane(element, viewModel); + } + } - ko.bindingHandlers.widget = { - init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) - { - if(theFreeboardModel.isEditing()) - { - freeboardUI.attachWidgetEditIcons($(element).parent()); - } - }, - update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) - { - if(viewModel.shouldRender()) - { - $(element).empty(); - viewModel.render(element); - } - } - } + ko.bindingHandlers.widget = { + init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) + { + if(theFreeboardModel.isEditing()) + { + freeboardUI.attachWidgetEditIcons($(element).parent()); + } + }, + update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) + { + if(viewModel.shouldRender()) + { + $(element).empty(); + viewModel.render(element); + } + } + } - function getParameterByName(name) - { - name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); - return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); - } + function getParameterByName(name) + { + name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); + return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); + } - $(function() - { //DOM Ready - // Show the loading indicator when we first load - freeboardUI.showLoadingIndicator(true); + $(function() + { //DOM Ready + // Show the loading indicator when we first load + freeboardUI.showLoadingIndicator(true); var resizeTimer; @@ -2816,72 +2816,72 @@ var freeboard = (function() resizeTimer = setTimeout(resizeEnd, 500); }); - }); + }); - // PUBLIC FUNCTIONS - return { - initialize : function(allowEdit, finishedCallback) - { - ko.applyBindings(theFreeboardModel); + // PUBLIC FUNCTIONS + return { + initialize : function(allowEdit, finishedCallback) + { + ko.applyBindings(theFreeboardModel); - // Check to see if we have a query param called load. If so, we should load that dashboard initially - var freeboardLocation = getParameterByName("load"); + // Check to see if we have a query param called load. If so, we should load that dashboard initially + var freeboardLocation = getParameterByName("load"); - if(freeboardLocation != "") - { - $.ajax({ - url : freeboardLocation, - success: function(data) - { - theFreeboardModel.loadDashboard(data); + if(freeboardLocation != "") + { + $.ajax({ + url : freeboardLocation, + success: function(data) + { + theFreeboardModel.loadDashboard(data); - if(_.isFunction(finishedCallback)) - { - finishedCallback(); - } - } - }); - } - else - { - theFreeboardModel.allow_edit(allowEdit); - theFreeboardModel.setEditing(allowEdit); + if(_.isFunction(finishedCallback)) + { + finishedCallback(); + } + } + }); + } + else + { + theFreeboardModel.allow_edit(allowEdit); + theFreeboardModel.setEditing(allowEdit); - freeboardUI.showLoadingIndicator(false); - if(_.isFunction(finishedCallback)) - { - finishedCallback(); - } + freeboardUI.showLoadingIndicator(false); + if(_.isFunction(finishedCallback)) + { + finishedCallback(); + } freeboard.emit("initialized"); - } - }, - newDashboard : function() - { - theFreeboardModel.loadDashboard({allow_edit: true}); - }, - loadDashboard : function(configuration, callback) - { - theFreeboardModel.loadDashboard(configuration, callback); - }, - serialize : function() - { - return theFreeboardModel.serialize(); - }, - setEditing : function(editing, animate) - { - theFreeboardModel.setEditing(editing, animate); - }, - isEditing : function() - { - return theFreeboardModel.isEditing(); - }, - loadDatasourcePlugin: function(plugin) - { - if(_.isUndefined(plugin.display_name)) - { - plugin.display_name = plugin.type_name; - } + } + }, + newDashboard : function() + { + theFreeboardModel.loadDashboard({allow_edit: true}); + }, + loadDashboard : function(configuration, callback) + { + theFreeboardModel.loadDashboard(configuration, callback); + }, + serialize : function() + { + return theFreeboardModel.serialize(); + }, + setEditing : function(editing, animate) + { + theFreeboardModel.setEditing(editing, animate); + }, + isEditing : function() + { + return theFreeboardModel.isEditing(); + }, + loadDatasourcePlugin: function(plugin) + { + if(_.isUndefined(plugin.display_name)) + { + plugin.display_name = plugin.type_name; + } // Add a required setting called name to the beginning plugin.settings.unshift({ @@ -2892,59 +2892,59 @@ var freeboard = (function() }); - theFreeboardModel.addPluginSource(plugin.source); - datasourcePlugins[plugin.type_name] = plugin; - theFreeboardModel._datasourceTypes.valueHasMutated(); - }, + theFreeboardModel.addPluginSource(plugin.source); + datasourcePlugins[plugin.type_name] = plugin; + theFreeboardModel._datasourceTypes.valueHasMutated(); + }, resize : function() { freeboardUI.processResize(true); }, - loadWidgetPlugin : function(plugin) - { - if(_.isUndefined(plugin.display_name)) - { - plugin.display_name = plugin.type_name; - } + loadWidgetPlugin : function(plugin) + { + if(_.isUndefined(plugin.display_name)) + { + plugin.display_name = plugin.type_name; + } - theFreeboardModel.addPluginSource(plugin.source); - widgetPlugins[plugin.type_name] = plugin; - theFreeboardModel._widgetTypes.valueHasMutated(); - }, - // To be used if freeboard is going to load dynamic assets from a different root URL - setAssetRoot : function(assetRoot) - { - jsEditor.setAssetRoot(assetRoot); - }, - addStyle : function(selector, rules) - { - var styleString = selector + "{" + rules + "}"; + theFreeboardModel.addPluginSource(plugin.source); + widgetPlugins[plugin.type_name] = plugin; + theFreeboardModel._widgetTypes.valueHasMutated(); + }, + // To be used if freeboard is going to load dynamic assets from a different root URL + setAssetRoot : function(assetRoot) + { + jsEditor.setAssetRoot(assetRoot); + }, + addStyle : function(selector, rules) + { + var styleString = selector + "{" + rules + "}"; - var styleElement = $("style#fb-styles"); + var styleElement = $("style#fb-styles"); - if(styleElement.length == 0) - { - styleElement = $(''); - $("head").append(styleElement); - } + if(styleElement.length == 0) + { + styleElement = $(''); + $("head").append(styleElement); + } - if(styleElement[0].styleSheet) - { - styleElement[0].styleSheet.cssText += styleString; - } - else - { - styleElement.text(styleElement.text() + styleString); - } - }, - showLoadingIndicator: function(show) - { - freeboardUI.showLoadingIndicator(show); - }, - showDialog : function(contentElement, title, okTitle, cancelTitle, okCallback) - { - new DialogBox(contentElement, title, okTitle, cancelTitle, okCallback); - }, + if(styleElement[0].styleSheet) + { + styleElement[0].styleSheet.cssText += styleString; + } + else + { + styleElement.text(styleElement.text() + styleString); + } + }, + showLoadingIndicator: function(show) + { + freeboardUI.showLoadingIndicator(show); + }, + showDialog : function(contentElement, title, okTitle, cancelTitle, okCallback) + { + new DialogBox(contentElement, title, okTitle, cancelTitle, okCallback); + }, getDatasourceSettings : function(datasourceName) { var datasources = theFreeboardModel.datasources(); @@ -2981,26 +2981,26 @@ var freeboard = (function() var combinedSettings = _.defaults(settings, datasource.settings()); datasource.settings(combinedSettings); }, - getStyleString : function(name) - { - var returnString = ""; + getStyleString : function(name) + { + var returnString = ""; - _.each(currentStyle[name], function(value, name) - { - returnString = returnString + name + ":" + value + ";"; - }); + _.each(currentStyle[name], function(value, name) + { + returnString = returnString + name + ":" + value + ";"; + }); - return returnString; - }, - getStyleObject : function(name) - { - return currentStyle[name]; - }, - showDeveloperConsole : function() - { - developerConsole.showDeveloperConsole(); - } - }; + return returnString; + }, + getStyleObject : function(name) + { + return currentStyle[name]; + }, + showDeveloperConsole : function() + { + developerConsole.showDeveloperConsole(); + } + }; }()); $.extend(freeboard, jQuery.eventEmitter); diff --git a/js/freeboard.min.js b/js/freeboard.min.js index 34e0174..13c69cc 100644 --- a/js/freeboard.min.js +++ b/js/freeboard.min.js @@ -1,2 +1,3 @@ -function DialogBox(e,t,n,i,a){function s(){o.fadeOut(200,function(){$(this).remove()})}var o=$(''),r=$('');r.append('

                      '+t+"

                      "),$("
                      ").appendTo(r).append(e);var d=$("
                      ").appendTo(r);n&&$(''+n+"").appendTo(d).click(function(){var e=!1;_.isFunction(a)&&(e=a()),e||s()}),i&&$(''+i+"").appendTo(d).click(function(){s()}),o.append(r),$("body").append(o),o.fadeIn(200)}function FreeboardModel(e,t,n){var i=this,a=1;this.version=0,this.isEditing=ko.observable(!1),this.allow_edit=ko.observable(!1),this.allow_edit.subscribe(function(e){e?$("#main-header").show():$("#main-header").hide()}),this.header_image=ko.observable(),this.plugins=ko.observableArray(),this.datasources=ko.observableArray(),this.panes=ko.observableArray(),this.datasourceData={},this.processDatasourceUpdate=function(e,t){var n=e.name();i.datasourceData[n]=t,_.each(i.panes(),function(e){_.each(e.widgets(),function(e){e.processDatasourceUpdate(n)})})},this._datasourceTypes=ko.observable(),this.datasourceTypes=ko.computed({read:function(){i._datasourceTypes();var t=[];return _.each(e,function(e){var n=e.type_name,i=n;_.isUndefined(e.display_name)||(i=e.display_name),t.push({name:n,display_name:i})}),t}}),this._widgetTypes=ko.observable(),this.widgetTypes=ko.computed({read:function(){i._widgetTypes();var e=[];return _.each(t,function(t){var n=t.type_name,i=n;_.isUndefined(t.display_name)||(i=t.display_name),e.push({name:n,display_name:i})}),e}}),this.addPluginSource=function(e){e&&-1==i.plugins.indexOf(e)&&i.plugins.push(e)},this.serialize=function(){var e=[];_.each(i.panes(),function(t){e.push(t.serialize())});var t=[];return _.each(i.datasources(),function(e){t.push(e.serialize())}),{version:a,header_image:i.header_image(),allow_edit:i.allow_edit(),plugins:i.plugins(),panes:e,datasources:t,columns:n.getUserColumns()}},this.deserialize=function(a,s){function o(){n.setUserColumns(a.columns),_.isUndefined(a.allow_edit)?i.allow_edit(!0):i.allow_edit(a.allow_edit),i.version=a.version||0,i.header_image(a.header_image),_.each(a.datasources,function(t){var n=new DatasourceModel(i,e);n.deserialize(t),i.addDatasource(n)});var o=_.sortBy(a.panes,function(e){return n.getPositionForScreenSize(e).row});_.each(o,function(e){var n=new PaneModel(i,t);n.deserialize(e),i.panes.push(n)}),i.allow_edit()&&0==i.panes().length&&i.setEditing(!0),_.isFunction(s)&&s(),n.processResize(!0)}i.clearDashboard(),_.each(a.plugins,function(e){i.addPluginSource(e)}),_.isArray(a.plugins)&&a.plugins.length>0?head.js(a.plugins,function(){o()}):o()},this.clearDashboard=function(){n.removeAllPanes(),_.each(i.datasources(),function(e){e.dispose()}),_.each(i.panes(),function(e){e.dispose()}),i.plugins.removeAll(),i.datasources.removeAll(),i.panes.removeAll()},this.loadDashboard=function(e,t){n.showLoadingIndicator(!0),i.deserialize(e,function(){n.showLoadingIndicator(!1),_.isFunction(t)&&t(),freeboard.emit("dashboard_loaded")})},this.loadDashboardFromLocalFile=function(){if(window.File&&window.FileReader&&window.FileList&&window.Blob){var e=document.createElement("input");e.type="file",$(e).on("change",function(e){var t=e.target.files;if(t&&t.length>0){var n=t[0],a=new FileReader;a.addEventListener("load",function(e){var t=e.target,n=JSON.parse(t.result);i.loadDashboard(n),i.setEditing(!1)}),a.readAsText(n)}}),$(e).trigger("click")}else alert("Unable to load a file in this browser.")},this.saveDashboard=function(){var e="application/octet-stream",t=document.createElement("a"),n=new Blob([JSON.stringify(i.serialize())],{type:e});document.body.appendChild(t),t.href=window.URL.createObjectURL(n),t.download="dashboard.json",t.target="_self",t.click()},this.addDatasource=function(e){i.datasources.push(e)},this.deleteDatasource=function(e){delete i.datasourceData[e.name()],e.dispose(),i.datasources.remove(e)},this.createPane=function(){var e=new PaneModel(i,t);i.addPane(e)},this.addGridColumnLeft=function(){n.addGridColumnLeft()},this.addGridColumnRight=function(){n.addGridColumnRight()},this.subGridColumnLeft=function(){n.subGridColumnLeft()},this.subGridColumnRight=function(){n.subGridColumnRight()},this.addPane=function(e){i.panes.push(e)},this.deletePane=function(e){e.dispose(),i.panes.remove(e)},this.deleteWidget=function(e){ko.utils.arrayForEach(i.panes(),function(t){t.widgets.remove(e)}),e.dispose()},this.setEditing=function(e,t){if(i.allow_edit()||!e){i.isEditing(e),_.isUndefined(t)&&(t=!0);var a=t?250:0,s=$("#admin-bar").outerHeight();e?($("#toggle-header-icon").addClass("icon-chevron-up").removeClass("icon-wrench"),$(".gridster .gs_w").css({cursor:"pointer"}),$("#main-header").animate({top:"0px"},a),$("#board-content").animate({top:s+20+"px"},a),$("#main-header").data().shown=!0,n.attachWidgetEditIcons($(".sub-section")),n.enableGrid()):($("#toggle-header-icon").addClass("icon-wrench").removeClass("icon-chevron-up"),$(".gridster .gs_w").css({cursor:"default"}),$("#main-header").animate({top:"-"+s+"px"},a),$("#board-content").animate({top:"20"},a),$("#main-header").data().shown=!1,$(".sub-section").unbind(),n.disableGrid()),n.showPaneEditIcons(e,t)}},this.toggleEditing=function(){var e=!i.isEditing();i.setEditing(e)}}function FreeboardUI(){function e(e){var t=a(),n=function(){};e&&(n=function(){var e=this,n=ko.dataFor(e),i=v(n);$(e).attr("data-sizex",Math.min(n.col_width(),t,m.cols)).attr("data-row",i.row).attr("data-col",i.col),n.processSizeChange()}),s(Math.min(t,k)),o(n),i()}function t(e){var t=m.cols+1;s(t)&&o(function(){var t,n=this,i=ko.dataFor(n),a=m.cols>1?m.cols-1:1,s=i.col[a],o=i.row[a];if(e){leftPreviewCol=!0;var r=m.cols>s?s+1:m.cols;t={row:o,col:r}}else rightPreviewCol=!0,t={row:o,col:s};$(n).attr("data-sizex",Math.min(i.col_width(),m.cols)).attr("data-row",t.row).attr("data-col",t.col)}),i(),k=m.cols}function n(e){var t=m.cols-1;s(t)&&o(function(){var t,n=this,i=ko.dataFor(n),a=m.cols+1,s=i.col[a],o=i.row[a];if(e){var r=s>1?s-1:1;t={row:o,col:r}}else{var r=m.cols>=s?s:m.cols;t={row:o,col:r}}$(n).attr("data-sizex",Math.min(i.col_width(),m.cols)).attr("data-row",t.row).attr("data-col",t.col)}),i(),k=m.cols}function i(){var e=$(".column-tool"),t=$("#board-content").width(),n=Math.floor(t/C);y>=m.cols?e.addClass("min"):e.removeClass("min"),m.cols>=n?e.addClass("max"):e.removeClass("max")}function a(){var e=$("#board-content").width();return Math.floor(e/C)}function s(e){(void 0===e||y>e)&&(e=y);var t=a();e>t&&(e=t);var n=C*e+e;return $(".responsive-column-width").css("max-width",n),e===m.cols?!1:!0}function o(e){var t=m.$el;t.find("> li").unbind().removeData(),$(".responsive-column-width").css("width",""),m.generate_grid_and_stylesheet(),t.find("> li").each(e),m.init(),$(".responsive-column-width").css("width",m.cols*b+2*m.cols*w)}function r(){return k}function d(e){k=Math.max(y,e)}function c(e,t,n){var i=v(t),a=i.col,s=i.row,o=Number(t.width()),r=Number(t.getCalculatedHeight());m.add_widget(e,o,r,a,s),n&&f(!0),u(t,s,a),$(e).attrchange({trackValues:!0,callback:function(e){"data-row"==e.attributeName?u(t,Number(e.newValue),void 0):"data-col"==e.attributeName&&u(t,void 0,Number(e.newValue))}})}function l(e,t){var n=t.getCalculatedHeight(),i=Number($(e).attr("data-sizey")),a=Number($(e).attr("data-sizex"));(n!=i||t.col_width()!=a)&&m.resize_widget($(e),t.col_width(),n,function(){m.set_dom_grid_height()})}function u(e,t,n){var i=m.cols;_.isUndefined(t)||(e.row[i]=t),_.isUndefined(n)||(e.col[i]=n)}function p(e){e?x.fadeOut(0).appendTo("body").fadeIn(500):x.fadeOut(500).remove()}function f(e,t){_.isUndefined(t)&&(t=!0);var n=t?250:0;e?($(".pane-tools").fadeIn(n),$("#column-tools").fadeIn(n)):($(".pane-tools").fadeOut(n),$("#column-tools").fadeOut(n))}function h(e){$(e).hover(function(){g(this,!0)},function(){g(this,!1)})}function g(e,t){t?$(e).find(".sub-section-tools").fadeIn(250):$(e).find(".sub-section-tools").fadeOut(250)}function v(e){var t=m.cols;if(_.isNumber(e.row)&&_.isNumber(e.col)){var n={};n[t]=e.row,e.row=n,n={},n[t]=e.col,e.col=n}var i=1,a=1e3;for(var s in e.col){if(s==t)return{row:e.row[s],col:e.col[s]};if(e.col[s]>t)i=t;else{var o=t-s;a>o&&(i=s,a=o)}}return i in e.col&&i in e.row?{row:e.row[i],col:e.col[i]}:{row:1,col:i}}var m,w=10,b=300,y=3,C=w+b+w,k=y,x=$('
                      ');return ko.bindingHandlers.grid={init:function(t){m=$(t).gridster({widget_margins:[w,w],widget_base_dimensions:[b,10],resize:{enabled:!1,axes:"x"}}).data("gridster"),e(!1),m.disable()}},{showLoadingIndicator:function(e){p(e)},showPaneEditIcons:function(e,t){f(e,t)},attachWidgetEditIcons:function(e){h(e)},getPositionForScreenSize:function(e){return v(e)},processResize:function(t){e(t)},disableGrid:function(){m.disable()},enableGrid:function(){m.enable()},addPane:function(e,t,n){c(e,t,n)},updatePane:function(e,t){l(e,t)},removePane:function(e){m.remove_widget(e)},removeAllPanes:function(){m.remove_all_widgets()},addGridColumnLeft:function(){t(!0)},addGridColumnRight:function(){t(!1)},subGridColumnLeft:function(){n(!0)},subGridColumnRight:function(){n(!1)},getUserColumns:function(){return r()},setUserColumns:function(e){d(e)}}}function PaneModel(e,t){var n=this;this.title=ko.observable(),this.width=ko.observable(1),this.row={},this.col={},this.col_width=ko.observable(1),this.col_width.subscribe(function(){n.processSizeChange()}),this.widgets=ko.observableArray(),this.addWidget=function(e){this.widgets.push(e)},this.widgetCanMoveUp=function(e){return n.widgets.indexOf(e)>=1},this.widgetCanMoveDown=function(e){var t=n.widgets.indexOf(e);return n.widgets().length-1>t},this.moveWidgetUp=function(e){if(n.widgetCanMoveUp(e)){var t=n.widgets.indexOf(e),i=n.widgets();n.widgets.splice(t-1,2,i[t],i[t-1])}},this.moveWidgetDown=function(e){if(n.widgetCanMoveDown(e)){var t=n.widgets.indexOf(e),i=n.widgets();n.widgets.splice(t,2,i[t+1],i[t])}},this.processSizeChange=function(){setTimeout(function(){_.each(n.widgets(),function(e){e.processSizeChange()})},1e3)},this.getCalculatedHeight=function(){var e=_.reduce(n.widgets(),function(e,t){return e+t.height()},0);e*=6,e+=3,e*=10;var t=Math.ceil((e+20)/30);return Math.max(4,t)},this.serialize=function(){var e=[];return _.each(n.widgets(),function(t){e.push(t.serialize())}),{title:n.title(),width:n.width(),row:n.row,col:n.col,col_width:n.col_width(),widgets:e}},this.deserialize=function(i){n.title(i.title),n.width(i.width),n.row=i.row,n.col=i.col,n.col_width(i.col_width||1),_.each(i.widgets,function(i){var a=new WidgetModel(e,t);a.deserialize(i),n.widgets.push(a)})},this.dispose=function(){_.each(n.widgets(),function(e){e.dispose()})}}function WidgetModel(e,t){function n(){_.isUndefined(i.widgetInstance)||(_.isFunction(i.widgetInstance.onDispose)&&i.widgetInstance.onDispose(),i.widgetInstance=void 0)}var i=this;this.datasourceRefreshNotifications={},this.calculatedSettingScripts={},this.title=ko.observable(),this.fillSize=ko.observable(!1),this.type=ko.observable(),this.type.subscribe(function(e){function a(){s.newInstance(i.settings(),function(e){i.fillSize(s.fill_size===!0),i.widgetInstance=e,i.shouldRender(!0),i._heightUpdate.valueHasMutated()})}if(n(),e in t&&_.isFunction(t[e].newInstance)){var s=t[e];s.external_scripts?head.js(s.external_scripts.slice(0),a):a()}}),this.settings=ko.observable({}),this.settings.subscribe(function(e){!_.isUndefined(i.widgetInstance)&&_.isFunction(i.widgetInstance.onSettingsChanged)&&i.widgetInstance.onSettingsChanged(e),i.updateCalculatedSettings(),i._heightUpdate.valueHasMutated()}),this.processDatasourceUpdate=function(e){var t=i.datasourceRefreshNotifications[e];_.isArray(t)&&_.each(t,function(e){i.processCalculatedSetting(e)})},this.callValueFunction=function(t){return t.call(void 0,e.datasourceData)},this.processSizeChange=function(){!_.isUndefined(i.widgetInstance)&&_.isFunction(i.widgetInstance.onSizeChanged)&&i.widgetInstance.onSizeChanged()},this.processCalculatedSetting=function(e){if(_.isFunction(i.calculatedSettingScripts[e])){var t=void 0;try{t=i.callValueFunction(i.calculatedSettingScripts[e])}catch(n){var a=i.settings()[e];n instanceof ReferenceError&&/^\w+$/.test(a)&&(t=a)}if(!_.isUndefined(i.widgetInstance)&&_.isFunction(i.widgetInstance.onCalculatedValueChanged)&&!_.isUndefined(t))try{i.widgetInstance.onCalculatedValueChanged(e,t)}catch(n){console.log(""+n)}}},this.updateCalculatedSettings=function(){if(i.datasourceRefreshNotifications={},i.calculatedSettingScripts={},!_.isUndefined(i.type())){var e=t[i.type()].settings,n=RegExp("datasources.([\\w_-]+)|datasources\\[['\"]([^'\"]+)","g"),a=i.settings();_.each(e,function(e){if("calculated"==e.type){var t=a[e.name];if(!_.isUndefined(t)){_.isArray(t)&&(t="["+t.join(",")+"]"),1>=(t.match(/;/g)||[]).length&&-1==t.indexOf("return")&&(t="return "+t);var s;try{s=Function("datasources",t)}catch(o){var r=a[e.name].replace(/"/g,'\\"').replace(/[\r\n]/g," \\\n");s=Function("datasources",'return "'+r+'";')}i.calculatedSettingScripts[e.name]=s,i.processCalculatedSetting(e.name);for(var d;d=n.exec(t);){var c=d[1]||d[2],l=i.datasourceRefreshNotifications[c];_.isUndefined(l)&&(l=[],i.datasourceRefreshNotifications[c]=l),-1==_.indexOf(l,e.name)&&l.push(e.name)}}}})}},this._heightUpdate=ko.observable(),this.height=ko.computed({read:function(){return i._heightUpdate(),!_.isUndefined(i.widgetInstance)&&_.isFunction(i.widgetInstance.getHeight)?i.widgetInstance.getHeight():1}}),this.shouldRender=ko.observable(!1),this.render=function(e){i.shouldRender(!1),!_.isUndefined(i.widgetInstance)&&_.isFunction(i.widgetInstance.render)&&(i.widgetInstance.render(e),i.updateCalculatedSettings())},this.dispose=function(){},this.serialize=function(){return{title:i.title(),type:i.type(),settings:i.settings()}},this.deserialize=function(e){i.title(e.title),i.settings(e.settings),i.type(e.type)}}DatasourceModel=function(e,t){function n(){_.isUndefined(i.datasourceInstance)||(_.isFunction(i.datasourceInstance.onDispose)&&i.datasourceInstance.onDispose(),i.datasourceInstance=void 0)}var i=this;this.name=ko.observable(),this.latestData=ko.observable(),this.settings=ko.observable({}),this.settings.subscribe(function(e){!_.isUndefined(i.datasourceInstance)&&_.isFunction(i.datasourceInstance.onSettingsChanged)&&i.datasourceInstance.onSettingsChanged(e)}),this.updateCallback=function(t){e.processDatasourceUpdate(i,t),i.latestData(t);var n=new Date;i.last_updated(n.toLocaleTimeString())},this.type=ko.observable(),this.type.subscribe(function(e){function a(){s.newInstance(i.settings(),function(e){i.datasourceInstance=e,e.updateNow()},i.updateCallback)}if(n(),e in t&&_.isFunction(t[e].newInstance)){var s=t[e];s.external_scripts?head.js(s.external_scripts.slice(0),a):a()}}),this.last_updated=ko.observable("never"),this.last_error=ko.observable(),this.serialize=function(){return{name:i.name(),type:i.type(),settings:i.settings()}},this.deserialize=function(e){i.settings(e.settings),i.name(e.name),i.type(e.type)},this.getDataRepresentation=function(e){var t=Function("data","return "+e+";");return t.call(void 0,i.latestData())},this.updateNow=function(){!_.isUndefined(i.datasourceInstance)&&_.isFunction(i.datasourceInstance.updateNow)&&i.datasourceInstance.updateNow()},this.dispose=function(){n()}},DeveloperConsole=function(e){function t(){function t(e){var t=$(""),i=$('
                        '),a=$(''),s=$('
                      • ').click(function(){n=_.without(n,a),t.remove()});n.push(a),e&&a.val(e),i.append(s),o.append(t.append($("").append(a)).append($('').append(i)))}var n=[],i=$("
                        "),a=$('
                        ADD
                        '),s=$('
                        ');s.append($('Plugin Script URL'));var o=$("");s.append(o),i.append($("

                        Here you can add references to other scripts to load datasource or widget plugins.

                        ")).append(s).append(a).append('

                        To learn how to build plugins for freeboard, please visit http://freeboard.github.io/freeboard/docs/plugin_example.html

                        '),_.each(e.plugins(),function(e){t(e)}),a.click(function(){t()}),new DialogBox(i,"Developer Console","OK",null,function(){_.each(e.plugins(),function(e){$('script[src^="'+e+'"]').remove()}),e.plugins.removeAll(),_.each(n,function(t){var n=t.val();n&&n.length>0&&(e.addPluginSource(n),head.js(n+"?"+Date.now()))})})}return{showDeveloperConsole:function(){t()}}},JSEditor=function(){function e(e){n=e}function t(e,t){var n='// Example: Convert temp from C to F and truncate to 2 decimal places.\n// return (datasources["MyDatasource"].sensor.tempInF * 1.8 + 32).toFixed(2);';e||(e=n);var i=$('
                        '),a=$('
                        '),s=$(''),o=$('
                        This javascript will be re-evaluated any time a datasource referenced here is updated, and the value you return will be displayed in the widget. You can assume this javascript is wrapped in a function of the form function(datasources) where datasources is a collection of javascript objects (keyed by their name) corresponding to the most current data in a datasource.
                        ');i.append([o,a,s]),$("body").append(i);var r=CodeMirror(a.get(0),{value:e,mode:"javascript",theme:"ambiance",indentUnit:4,lineNumbers:!0,matchBrackets:!0,autoCloseBrackets:!0}),d=$('Close').click(function(){if(t){var e=r.getValue();e===n&&(e=""),t(e),i.remove()}});s.append(d)}var n="";return{displayJSEditor:function(e,n){t(e,n)},setAssetRoot:function(t){e(t)}}},PluginEditor=function(e,t){function n(e,t){var n=$('
                        ').html(t);$("#setting-value-container-"+e).append(n)}function i(){$("#setting-row-instance-name").length?$("#setting-row-instance-name").nextAll().remove():$("#setting-row-plugin-types").nextAll().remove()}function a(e){return!isNaN(parseFloat(e))&&isFinite(e)}function s(n,i,a,s,o){var r=$("");a.multi_input?r.change(function(){var e=[];$(n).find("textarea").each(function(){var t=$(this).val();t&&(e=e.concat(t))}),i.settings[a.name]=e}):r.change(function(){i.settings[a.name]=$(this).val()}),s&&r.val(s),t.createValueEditor(r);var d=$('
                          '),c=$('
                          ');c.append(r).append(d);var l=$('
                        • ').mousedown(function(e){e.preventDefault(),$(r).val("").focus().insertAtCaret('datasources["').trigger("freeboard-eval")});d.append(l);var u=$('
                        • ').mousedown(function(t){t.preventDefault(),e.displayJSEditor(r.val(),function(e){r.val(e),r.change()})});if(d.append(u),o){var p=$('
                        • ').mousedown(function(e){e.preventDefault(),c.remove(),$(n).find("textarea:first").change()});d.prepend(p)}$(n).append(c)}function o(e,t,o,r,d){function c(e,t){var n=$('
                          ').appendTo(f);return n.append('
                          "),$('
                          ').appendTo(n)}function l(e){_.each(e,function(e){function t(){p.settings[e.name].length>0?l.show():l.hide()}function n(n){var i=$("").appendTo(f),a={};_.isArray(p.settings[e.name])||(p.settings[e.name]=[]),p.settings[e.name].push(a),_.each(e.settings,function(e){var t=$("").appendTo(i),s="";_.isUndefined(n[e.name])||(s=n[e.name]),a[e.name]=s,$('').appendTo(t).val(s).change(function(){a[e.name]=$(this).val()})}),i.append($('').append($('
                            ').append($("
                          • ").append($('').click(function(){var n=p.settings[e.name].indexOf(a);-1!=n&&(p.settings[e.name].splice(n,1),i.remove(),t())}))))),o.scrollTop(o[0].scrollHeight),t()}!_.isUndefined(e.default_value)&&_.isUndefined(r[e.name])&&(r[e.name]=e.default_value);var i=e.name;_.isUndefined(e.display_name)||(i=e.display_name);var a=c(e.name,i);switch(e.type){case"array":var o=$('
                            ').appendTo(a),d=$('
                            ').appendTo(o),l=$("").hide().appendTo(d),u=$("").appendTo(l),f=$("").appendTo(d),h=[];_.each(e.settings,function(e){var t=e.name;_.isUndefined(e.display_name)||(t=e.display_name),$(""+t+"").appendTo(u)}),e.name in r&&(h=r[e.name]),$('
                            ADD
                            ').appendTo(a).click(function(){var t={};_.each(e.settings,function(e){t[e.name]=""}),n(t)}),_.each(h,function(e){n(e)});break;case"boolean":p.settings[e.name]=r[e.name];var g=$('
                            ').appendTo(a),v=$('').prependTo(g).change(function(){p.settings[e.name]=this.checked});e.name in r&&v.prop("checked",r[e.name]);break;case"option":var m=r[e.name],v=$("").appendTo($('
                            ').appendTo(a)).change(function(){p.settings[e.name]=$(this).val()});_.each(e.options,function(e){var t,n;_.isObject(e)?(t=e.name,n=e.value):t=e,_.isUndefined(n)&&(n=t),_.isUndefined(m)&&(m=n),$("").text(t).attr("value",n).appendTo(v)}),p.settings[e.name]=m,e.name in r&&v.val(r[e.name]);break;default:if(p.settings[e.name]=r[e.name],"calculated"==e.type){if(e.name in r){var w=r[e.name];if(e.multi_input&&_.isArray(w))for(var b=!1,y=0;w.length>y;y++)s(a,p,e,w[y],b),b=!0;else s(a,p,e,w,!1)}else s(a,p,e,null,!1);if(e.multi_input){var C=$('
                            ').mousedown(function(t){t.preventDefault(),s(a,p,e,null,!0)});$(a).siblings(".form-label").append(C)}}else{var v=$('').appendTo(a).change(function(){p.settings[e.name]="number"==e.type?Number($(this).val()):$(this).val()});e.name in r&&v.val(r[e.name])}}_.isUndefined(e.suffix)||a.append($('
                            '+e.suffix+"
                            ")),_.isUndefined(e.description)||a.append($('
                            '+e.description+"
                            "))})}var u,p={type:o,settings:{}},f=$("
                            "),h=$('
                            ').hide();f.append(h),new DialogBox(f,e,"Save","Cancel",function(){$(".validation-error").remove();for(var e=0;u.settings.length>e;e++){var t=u.settings[e];if(t.required&&(_.isUndefined(p.settings[t.name])||""==p.settings[t.name]))return n(t.name,"This is required."),!0;if("number"==t.type&&!a(p.settings[t.name]))return n(t.name,"Must be a number."),!0}_.isFunction(d)&&d(p)});var g,v=_.keys(t);if(v.length>1){var m=c("plugin-types","Type");g=$("").appendTo($('
                            ').appendTo(m)),g.append($("").attr("value","undefined")),_.each(t,function(e){g.append($("").text(e.display_name).attr("value",e.type_name))}),g.change(function(){p.type=$(this).val(),p.settings={},i(),u=t[g.val()],_.isUndefined(u)?($("#setting-row-instance-name").hide(),$("#dialog-ok").hide()):($("#setting-row-instance-name").show(),u.description&&u.description.length>0?h.html(u.description).show():h.hide(),$("#dialog-ok").show(),l(u.settings))})}else 1==v.length&&(u=t[v[0]],p.type=u.type_name,p.settings={},l(u.settings));g&&(_.isUndefined(o)?($("#setting-row-instance-name").hide(),$("#dialog-ok").hide()):($("#dialog-ok").show(),g.val(o).trigger("change")))}return{createPluginEditor:function(e,t,n,i,a,s){o(e,t,n,i,a,s)}}},ValueEditor=function(e){function t(e,t){return _.isArray(e)||_.isObject(e)?!0:n(e,t)}function n(e,t){switch(t){case h.ANY:return!0;case h.ARRAY:return _.isArray(e);case h.OBJECT:return _.isObject(e);case h.STRING:return _.isString(e);case h.NUMBER:return _.isNumber(e);case h.BOOLEAN:return _.isBoolean(e)}}function i(e,t){$(e).parent().find(".validation-error").remove(),n(f,t)||$(e).parent().append("
                            This field expects an expression that evaluates to type "+t+".
                            ")}function a(e){var t=($(e).val().match(/\n/g)||[]).length,n=Math.min(200,20*(t+1));$(e).css({height:n+"px"})}function s(e,n,i){var a=c.exec(e),s=[];if(a)if(""==a[1])_.each(n,function(e){s.push({value:e.name(),entity:void 0,precede_char:"",follow_char:'"]'})});else if(""!=a[1]&&_.isUndefined(a[2])){var o=a[1];_.each(n,function(e){var t=e.name();t!=o&&0==t.indexOf(o)&&s.push({value:t,entity:void 0,precede_char:"",follow_char:'"]'})})}else{var r=_.find(n,function(e){return e.name()===a[1]});if(!_.isUndefined(r)){var d="data",l="";if(!_.isUndefined(a[2])){var u=a[3].lastIndexOf("]")+1;d+=a[3].substring(0,u),l=a[3].substring(u,a[3].length),l=l.replace(/^[\[\"]*/,""),l=l.replace(/[\"\]]*$/,"")}var h=r.getDataRepresentation(d);if(f=h,_.isArray(h)){for(var g=0;h.length>g;g++)if(0==(""+g).indexOf(l)){var v=h[g];t(v,i)&&s.push({value:g,entity:v,precede_char:"[",follow_char:"]",preview:""+v})}}else _.isObject(h)&&_.each(h,function(e,n){0==n.indexOf(l)&&t(e,i)&&s.push({value:n,entity:e,precede_char:'["',follow_char:'"]'})})}}p=s}function o(t,n){var a=$(t).val().substring(0,$(t).getCaretPosition());if(a=a.replace(String.fromCharCode(160)," "),s(a,e.datasources(),n),p.length>0){l||(l=$('
                              ').insertAfter(t).width($(t).outerWidth()-2).css("left",$(t).position().left).css("top",$(t).position().top+$(t).outerHeight()-1)),l.empty(),l.scrollTop(0);var o=!0;u=0,_.each(p,function(e,n){var i=r(t,a,e,n);o&&($(i).addClass("selected"),o=!1)})}else i(t,n),$(t).next("ul#value-selector").remove(),l=null,u=-1}function r(e,t,n,i){var a=n.value;n.preview&&(a=a+""+n.preview+"");var s=$("
                            • "+a+"
                            • ").appendTo(l).mouseenter(function(){$(this).trigger("freeboard-select")}).mousedown(function(e){$(this).trigger("freeboard-insertValue"),e.preventDefault()}).data("freeboard-optionIndex",i).data("freeboard-optionValue",n.value).bind("freeboard-insertValue",function(){var i=n.value;i=n.precede_char+i+n.follow_char;var a=t.lastIndexOf("]");-1!=a?$(e).replaceTextAt(a+1,$(e).val().length,i):$(e).insertAtCaret(i),f=n.entity,$(e).triggerHandler("mouseup")}).bind("freeboard-select",function(){$(this).parent().find("li.selected").removeClass("selected"),$(this).addClass("selected"),u=$(this).data("freeboard-optionIndex")});return s}function d(e,t){$(e).addClass("calculated-value-input").bind("keyup mouseup freeboard-eval",function(n){return!l||"keyup"!=n.type||38!=n.keyCode&&40!=n.keyCode&&13!=n.keyCode?(o(e,t),void 0):(n.preventDefault(),void 0)}).focus(function(){$(e).css({"z-index":3001}),a(e)}).focusout(function(){i(e,t),$(e).css({height:"","z-index":3e3}),$(e).next("ul#value-selector").remove(),l=null,u=-1}).bind("keydown",function(e){if(l)if(38==e.keyCode||40==e.keyCode){e.preventDefault();var t=$(l).find("li");38==e.keyCode?u--:40==e.keyCode&&u++,0>u?u=t.size()-1:u>=t.size()&&(u=0);var n=$(t).eq(u);n.trigger("freeboard-select"),$(l).scrollTop($(n).position().top)}else 13==e.keyCode&&(e.preventDefault(),-1!=u&&$(l).find("li").eq(u).trigger("freeboard-insertValue"))})}var c=RegExp('.*datasources\\["([^"]*)("\\])?(.*)$'),l=null,u=0,p=[],f=null,h={ANY:"any",ARRAY:"array",OBJECT:"object",STRING:"string",NUMBER:"number",BOOLEAN:"boolean"};return{createValueEditor:function(e,t){t?d(e,t):d(e,h.ANY)},EXPECTED_TYPE:h}},function(e){function t(){var e=document.createElement("p"),t=!1;if(e.addEventListener)e.addEventListener("DOMAttrModified",function(){t=!0},!1);else{if(!e.attachEvent)return!1;e.attachEvent("onDOMAttrModified",function(){t=!0})}return e.setAttribute("id","target"),t}function n(t,n){if(t){var i=this.data("attr-old-value");if(n.attributeName.indexOf("style")>=0){i.style||(i.style={});var a=n.attributeName.split(".");n.attributeName=a[0],n.oldValue=i.style[a[1]],n.newValue=a[1]+":"+this.prop("style")[e.camelCase(a[1])],i.style[a[1]]=n.newValue}else n.oldValue=i[n.attributeName],n.newValue=this.attr(n.attributeName),i[n.attributeName]=n.newValue;this.data("attr-old-value",i)}}var i=window.MutationObserver||window.WebKitMutationObserver;e.fn.attrchange=function(a){var s={trackValues:!1,callback:e.noop};if("function"==typeof a?s.callback=a:e.extend(s,a),s.trackValues&&e(this).each(function(t,n){for(var i,a={},t=0,s=n.attributes,o=s.length;o>t;t++)i=s.item(t),a[i.nodeName]=i.value;e(this).data("attr-old-value",a)}),i){var o={subtree:!1,attributes:!0,attributeOldValue:s.trackValues},r=new i(function(t){t.forEach(function(t){var n=t.target;s.trackValues&&(t.newValue=e(n).attr(t.attributeName)),s.callback.call(n,t)})});return this.each(function(){r.observe(this,o)})}return t()?this.on("DOMAttrModified",function(e){e.originalEvent&&(e=e.originalEvent),e.attributeName=e.attrName,e.oldValue=e.prevValue,s.callback.call(this,e)}):"onpropertychange"in document.body?this.on("propertychange",function(t){t.attributeName=window.event.propertyName,n.call(e(this),s.trackValues,t),s.callback.call(this,t)}):this}}(jQuery),function(e){e.eventEmitter={_JQInit:function(){this._JQ=e(this)},emit:function(e,t){!this._JQ&&this._JQInit(),this._JQ.trigger(e,t)},once:function(e,t){!this._JQ&&this._JQInit(),this._JQ.one(e,t)},on:function(e,t){!this._JQ&&this._JQInit(),this._JQ.bind(e,t)},off:function(e,t){!this._JQ&&this._JQInit(),this._JQ.unbind(e,t)}}}(jQuery);var freeboard=function(){function e(e){e=e.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");var t=RegExp("[\\?&]"+e+"=([^&#]*)"),n=t.exec(location.search);return null==n?"":decodeURIComponent(n[1].replace(/\+/g," "))}var t={},n={},i=new FreeboardUI,a=new FreeboardModel(t,n,i),s=new JSEditor,o=new ValueEditor(a),r=new PluginEditor(s,o),d=new DeveloperConsole(a),c={values:{"font-family":'"HelveticaNeue-UltraLight", "Helvetica Neue Ultra Light", "Helvetica Neue", sans-serif',color:"#d3d4d4","font-weight":100}};return ko.bindingHandlers.pluginEditor={init:function(e,s,o,d){var c=ko.unwrap(s()),l={},u=void 0,p="";"datasource"==c.type?(l=t,p="Datasource"):"widget"==c.type?(l=n,p="Widget"):"pane"==c.type&&(p="Pane"),$(e).click(function(){if("delete"==c.operation){var s=$("

                              Are you sure you want to delete this "+p+"?

                              ");new DialogBox(s,"Confirm Delete","Yes","No",function(){"datasource"==c.type?a.deleteDatasource(d):"widget"==c.type?a.deleteWidget(d):"pane"==c.type&&a.deletePane(d)})}else{var o=void 0;"datasource"==c.type?"add"==c.operation?u={}:(o=d.type(),u=d.settings(),u.name=d.name()):"widget"==c.type?"add"==c.operation?u={}:(o=d.type(),u=d.settings()):"pane"==c.type&&(u={},"edit"==c.operation&&(u.title=d.title(),u.col_width=d.col_width()),l={settings:{settings:[{name:"title",display_name:"Title",type:"text"},{name:"col_width",display_name:"Columns",type:"number",default_value:1,required:!0}]}}),r.createPluginEditor(p,l,o,u,function(s){if("add"==c.operation){if("datasource"==c.type){var o=new DatasourceModel(a,t);a.addDatasource(o),o.name(s.settings.name),delete s.settings.name,o.settings(s.settings),o.type(s.type)}else if("widget"==c.type){var o=new WidgetModel(a,n);o.settings(s.settings),o.type(s.type),d.widgets.push(o),i.attachWidgetEditIcons(e) -}}else"edit"==c.operation&&("pane"==c.type?(d.title(s.settings.title),d.col_width(s.settings.col_width),i.processResize(!1)):("datasource"==c.type&&(d.name(s.settings.name),delete s.settings.name),d.type(s.type),d.settings(s.settings)))})}})}},ko.virtualElements.allowedBindings.datasourceTypeSettings=!0,ko.bindingHandlers.datasourceTypeSettings={update:function(e,t,n,i,a){processPluginSettings(e,t,n,i,a)}},ko.bindingHandlers.pane={init:function(e,t,n,s,o){a.isEditing()&&$(e).css({cursor:"pointer"}),i.addPane(e,s,o.$root.isEditing())},update:function(e,t,n,s){-1==a.panes.indexOf(s)&&i.removePane(e),i.updatePane(e,s)}},ko.bindingHandlers.widget={init:function(e){a.isEditing()&&i.attachWidgetEditIcons($(e).parent())},update:function(e,t,n,i){i.shouldRender()&&($(e).empty(),i.render(e))}},$(function(){function e(){i.processResize(!0)}i.showLoadingIndicator(!0);var t;$(window).resize(function(){clearTimeout(t),t=setTimeout(e,500)})}),{initialize:function(t,n){ko.applyBindings(a);var s=e("load");""!=s?$.ajax({url:s,success:function(e){a.loadDashboard(e),_.isFunction(n)&&n()}}):(a.allow_edit(t),a.setEditing(t),i.showLoadingIndicator(!1),_.isFunction(n)&&n(),freeboard.emit("initialized"))},newDashboard:function(){a.loadDashboard({allow_edit:!0})},loadDashboard:function(e,t){a.loadDashboard(e,t)},serialize:function(){return a.serialize()},setEditing:function(e,t){a.setEditing(e,t)},isEditing:function(){return a.isEditing()},loadDatasourcePlugin:function(e){_.isUndefined(e.display_name)&&(e.display_name=e.type_name),e.settings.unshift({name:"name",display_name:"Name",type:"text",required:!0}),a.addPluginSource(e.source),t[e.type_name]=e,a._datasourceTypes.valueHasMutated()},resize:function(){i.processResize(!0)},loadWidgetPlugin:function(e){_.isUndefined(e.display_name)&&(e.display_name=e.type_name),a.addPluginSource(e.source),n[e.type_name]=e,a._widgetTypes.valueHasMutated()},setAssetRoot:function(e){s.setAssetRoot(e)},addStyle:function(e,t){var n=e+"{"+t+"}",i=$("style#fb-styles");0==i.length&&(i=$(''),$("head").append(i)),i[0].styleSheet?i[0].styleSheet.cssText+=n:i.text(i.text()+n)},showLoadingIndicator:function(e){i.showLoadingIndicator(e)},showDialog:function(e,t,n,i,a){new DialogBox(e,t,n,i,a)},getDatasourceSettings:function(e){var t=a.datasources(),n=_.find(t,function(t){return t.name()===e});return n?n.settings():null},setDatasourceSettings:function(e,t){var n=a.datasources(),i=_.find(n,function(t){return t.name()===e});if(!i)return console.log("Datasource not found"),void 0;var s=_.defaults(t,i.settings());i.settings(s)},getStyleString:function(e){var t="";return _.each(c[e],function(e,n){t=t+n+":"+e+";"}),t},getStyleObject:function(e){return c[e]},showDeveloperConsole:function(){d.showDeveloperConsole()}}}();$.extend(freeboard,jQuery.eventEmitter); \ No newline at end of file +function DialogBox(a,b,c,d,e){function f(){g.fadeOut(200,function(){$(this).remove()})}var g=$(''),h=$('');h.append('

                              '+b+"

                              "),$("
                              ").appendTo(h).append(a);var i=$("
                              ").appendTo(h);c&&$(''+c+"").appendTo(i).click(function(){var a=!1;_.isFunction(e)&&(a=e()),a||f()}),d&&$(''+d+"").appendTo(i).click(function(){f()}),g.append(h),$("body").append(g),g.fadeIn(200)}function FreeboardModel(a,b,c){var d=this,e=1;this.version=0,this.isEditing=ko.observable(!1),this.allow_edit=ko.observable(!1),this.allow_edit.subscribe(function(a){a?$("#main-header").show():$("#main-header").hide()}),this.header_image=ko.observable(),this.plugins=ko.observableArray(),this.datasources=ko.observableArray(),this.panes=ko.observableArray(),this.datasourceData={},this.processDatasourceUpdate=function(a,b){var c=a.name();d.datasourceData[c]=b,_.each(d.panes(),function(a){_.each(a.widgets(),function(a){a.processDatasourceUpdate(c)})})},this._datasourceTypes=ko.observable(),this.datasourceTypes=ko.computed({read:function(){d._datasourceTypes();var b=[];return _.each(a,function(a){var c=a.type_name,d=c;_.isUndefined(a.display_name)||(d=a.display_name),b.push({name:c,display_name:d})}),b}}),this._widgetTypes=ko.observable(),this.widgetTypes=ko.computed({read:function(){d._widgetTypes();var a=[];return _.each(b,function(b){var c=b.type_name,d=c;_.isUndefined(b.display_name)||(d=b.display_name),a.push({name:c,display_name:d})}),a}}),this.addPluginSource=function(a){a&&-1==d.plugins.indexOf(a)&&d.plugins.push(a)},this.serialize=function(){var a=[];_.each(d.panes(),function(b){a.push(b.serialize())});var b=[];return _.each(d.datasources(),function(a){b.push(a.serialize())}),{version:e,header_image:d.header_image(),allow_edit:d.allow_edit(),plugins:d.plugins(),panes:a,datasources:b,columns:c.getUserColumns()}},this.deserialize=function(e,f){function g(){c.setUserColumns(e.columns),d.allow_edit(_.isUndefined(e.allow_edit)?!0:e.allow_edit),d.version=e.version||0,d.header_image(e.header_image),_.each(e.datasources,function(b){var c=new DatasourceModel(d,a);c.deserialize(b),d.addDatasource(c)});var g=_.sortBy(e.panes,function(a){return c.getPositionForScreenSize(a).row});_.each(g,function(a){var c=new PaneModel(d,b);c.deserialize(a),d.panes.push(c)}),d.allow_edit()&&0==d.panes().length&&d.setEditing(!0),_.isFunction(f)&&f(),c.processResize(!0)}d.clearDashboard(),_.each(e.plugins,function(a){d.addPluginSource(a)}),_.isArray(e.plugins)&&e.plugins.length>0?head.js(e.plugins,function(){g()}):g()},this.clearDashboard=function(){c.removeAllPanes(),_.each(d.datasources(),function(a){a.dispose()}),_.each(d.panes(),function(a){a.dispose()}),d.plugins.removeAll(),d.datasources.removeAll(),d.panes.removeAll()},this.loadDashboard=function(a,b){c.showLoadingIndicator(!0),d.deserialize(a,function(){c.showLoadingIndicator(!1),_.isFunction(b)&&b(),freeboard.emit("dashboard_loaded")})},this.loadDashboardFromLocalFile=function(){if(window.File&&window.FileReader&&window.FileList&&window.Blob){var a=document.createElement("input");a.type="file",$(a).on("change",function(a){var b=a.target.files;if(b&&b.length>0){var c=b[0],e=new FileReader;e.addEventListener("load",function(a){var b=a.target,c=JSON.parse(b.result);d.loadDashboard(c),d.setEditing(!1)}),e.readAsText(c)}}),$(a).trigger("click")}else alert("Unable to load a file in this browser.")},this.saveDashboard=function(){var a="application/octet-stream",b=document.createElement("a"),c=new Blob([JSON.stringify(d.serialize())],{type:a});document.body.appendChild(b),b.href=window.URL.createObjectURL(c),b.download="dashboard.json",b.target="_self",b.click()},this.addDatasource=function(a){d.datasources.push(a)},this.deleteDatasource=function(a){delete d.datasourceData[a.name()],a.dispose(),d.datasources.remove(a)},this.createPane=function(){var a=new PaneModel(d,b);d.addPane(a)},this.addGridColumnLeft=function(){c.addGridColumnLeft()},this.addGridColumnRight=function(){c.addGridColumnRight()},this.subGridColumnLeft=function(){c.subGridColumnLeft()},this.subGridColumnRight=function(){c.subGridColumnRight()},this.addPane=function(a){d.panes.push(a)},this.deletePane=function(a){a.dispose(),d.panes.remove(a)},this.deleteWidget=function(a){ko.utils.arrayForEach(d.panes(),function(b){b.widgets.remove(a)}),a.dispose()},this.setEditing=function(a,b){if(d.allow_edit()||!a){d.isEditing(a),_.isUndefined(b)&&(b=!0);var e=b?250:0,f=$("#admin-bar").outerHeight();a?($("#toggle-header-icon").addClass("icon-chevron-up").removeClass("icon-wrench"),$(".gridster .gs_w").css({cursor:"pointer"}),$("#main-header").animate({top:"0px"},e),$("#board-content").animate({top:f+20+"px"},e),$("#main-header").data().shown=!0,c.attachWidgetEditIcons($(".sub-section")),c.enableGrid()):($("#toggle-header-icon").addClass("icon-wrench").removeClass("icon-chevron-up"),$(".gridster .gs_w").css({cursor:"default"}),$("#main-header").animate({top:"-"+f+"px"},e),$("#board-content").animate({top:"20"},e),$("#main-header").data().shown=!1,$(".sub-section").unbind(),c.disableGrid()),c.showPaneEditIcons(a,b)}},this.toggleEditing=function(){var a=!d.isEditing();d.setEditing(a)}}function FreeboardUI(){function a(a){var b=e(),c=function(){};a&&(c=function(){var a=this,c=ko.dataFor(a),d=q(c);$(a).attr("data-sizex",Math.min(c.col_width(),b,r.cols)).attr("data-row",d.row).attr("data-col",d.col),c.processSizeChange()}),f(Math.min(b,w)),g(c),d()}function b(a){var b=r.cols+1;f(b)&&g(function(){var b,c=this,d=ko.dataFor(c),e=r.cols>1?r.cols-1:1,f=d.col[e],g=d.row[e];if(a){leftPreviewCol=!0;var h=f1?f-1:1;b={row:g,col:h}}else{var h=f<=r.cols?f:r.cols;b={row:g,col:h}}$(c).attr("data-sizex",Math.min(d.col_width(),r.cols)).attr("data-row",b.row).attr("data-col",b.col)}),d(),w=r.cols}function d(){var a=$(".column-tool"),b=$("#board-content").width(),c=Math.floor(b/v);r.cols<=u?a.addClass("min"):a.removeClass("min"),r.cols>=c?a.addClass("max"):a.removeClass("max")}function e(){var a=$("#board-content").width();return Math.floor(a/v)}function f(a){(void 0===a||u>a)&&(a=u);var b=e();a>b&&(a=b);var c=v*a+a;return $(".responsive-column-width").css("max-width",c),a===r.cols?!1:!0}function g(a){var b=r.$el;b.find("> li").unbind().removeData(),$(".responsive-column-width").css("width",""),r.generate_grid_and_stylesheet(),b.find("> li").each(a),r.init(),$(".responsive-column-width").css("width",r.cols*t+r.cols*s*2)}function h(){return w}function i(a){w=Math.max(u,a)}function j(a,b,c){var d=q(b),e=d.col,f=d.row,g=Number(b.width()),h=Number(b.getCalculatedHeight());r.add_widget(a,g,h,e,f),c&&n(!0),l(b,f,e),$(a).attrchange({trackValues:!0,callback:function(a){"data-row"==a.attributeName?l(b,Number(a.newValue),void 0):"data-col"==a.attributeName&&l(b,void 0,Number(a.newValue))}})}function k(a,b){var c=b.getCalculatedHeight(),d=Number($(a).attr("data-sizey")),e=Number($(a).attr("data-sizex"));(c!=d||b.col_width()!=e)&&r.resize_widget($(a),b.col_width(),c,function(){r.set_dom_grid_height()})}function l(a,b,c){var d=r.cols;_.isUndefined(b)||(a.row[d]=b),_.isUndefined(c)||(a.col[d]=c)}function m(a){a?x.fadeOut(0).appendTo("body").fadeIn(500):x.fadeOut(500).remove()}function n(a,b){_.isUndefined(b)&&(b=!0);var c=b?250:0;a?($(".pane-tools").fadeIn(c),$("#column-tools").fadeIn(c)):($(".pane-tools").fadeOut(c),$("#column-tools").fadeOut(c))}function o(a){$(a).hover(function(){p(this,!0)},function(){p(this,!1)})}function p(a,b){b?$(a).find(".sub-section-tools").fadeIn(250):$(a).find(".sub-section-tools").fadeOut(250)}function q(a){var b=r.cols;if(_.isNumber(a.row)&&_.isNumber(a.col)){var c={};c[b]=a.row,a.row=c,c={},c[b]=a.col,a.col=c}var d=1,e=1e3;for(var f in a.col){if(f==b)return{row:a.row[f],col:a.col[f]};if(a.col[f]>b)d=b;else{var g=b-f;e>g&&(d=f,e=g)}}return d in a.col&&d in a.row?{row:a.row[d],col:a.col[d]}:{row:1,col:d}}var r,s=10,t=300,u=3,v=s+t+s,w=u,x=$('
                              ');return ko.bindingHandlers.grid={init:function(b){r=$(b).gridster({widget_margins:[s,s],widget_base_dimensions:[t,10],resize:{enabled:!1,axes:"x"}}).data("gridster"),a(!1),r.disable()}},{showLoadingIndicator:function(a){m(a)},showPaneEditIcons:function(a,b){n(a,b)},attachWidgetEditIcons:function(a){o(a)},getPositionForScreenSize:function(a){return q(a)},processResize:function(b){a(b)},disableGrid:function(){r.disable()},enableGrid:function(){r.enable()},addPane:function(a,b,c){j(a,b,c)},updatePane:function(a,b){k(a,b)},removePane:function(a){r.remove_widget(a)},removeAllPanes:function(){r.remove_all_widgets()},addGridColumnLeft:function(){b(!0)},addGridColumnRight:function(){b(!1)},subGridColumnLeft:function(){c(!0)},subGridColumnRight:function(){c(!1)},getUserColumns:function(){return h()},setUserColumns:function(a){i(a)}}}function PaneModel(a,b){var c=this;this.title=ko.observable(),this.width=ko.observable(1),this.row={},this.col={},this.col_width=ko.observable(1),this.col_width.subscribe(function(){c.processSizeChange()}),this.widgets=ko.observableArray(),this.addWidget=function(a){this.widgets.push(a)},this.widgetCanMoveUp=function(a){return c.widgets.indexOf(a)>=1},this.widgetCanMoveDown=function(a){var b=c.widgets.indexOf(a);return b"),d=$('
                                '),e=$(''),f=$('
                              • ').click(function(){c=_.without(c,e),b.remove()});c.push(e),a&&e.val(a),d.append(f),g.append(b.append($("").append(e)).append($('').append(d)))}var c=[],d=$("
                                "),e=$('
                                ADD
                                '),f=$('
                                ');f.append($('Plugin Script URL'));var g=$("");f.append(g),d.append($("

                                Here you can add references to other scripts to load datasource or widget plugins.

                                ")).append(f).append(e).append('

                                To learn how to build plugins for freeboard, please visit http://freeboard.github.io/freeboard/docs/plugin_example.html

                                '),_.each(a.plugins(),function(a){b(a)}),e.click(function(){b()}),new DialogBox(d,"Developer Console","OK",null,function(){_.each(a.plugins(),function(a){$('script[src^="'+a+'"]').remove()}),a.plugins.removeAll(),_.each(c,function(b){var c=b.val();c&&c.length>0&&(a.addPluginSource(c),head.js(c+"?"+Date.now()))})})}return{showDeveloperConsole:function(){b()}}},JSEditor=function(){function a(a){c=a}function b(a,b){var c='// Example: Convert temp from C to F and truncate to 2 decimal places.\n// return (datasources["MyDatasource"].sensor.tempInF * 1.8 + 32).toFixed(2);';a||(a=c);var d=$('
                                '),e=$('
                                '),f=$(''),g=$('
                                This javascript will be re-evaluated any time a datasource referenced here is updated, and the value you return will be displayed in the widget. You can assume this javascript is wrapped in a function of the form function(datasources) where datasources is a collection of javascript objects (keyed by their name) corresponding to the most current data in a datasource.
                                ');d.append([g,e,f]),$("body").append(d);var h=CodeMirror(e.get(0),{value:a,mode:"javascript",theme:"ambiance",indentUnit:4,lineNumbers:!0,matchBrackets:!0,autoCloseBrackets:!0}),i=$('Close').click(function(){if(b){var a=h.getValue();a===c&&(a=""),b(a),d.remove()}});f.append(i)}var c="";return{displayJSEditor:function(a,c){b(a,c)},setAssetRoot:function(b){a(b)}}},PluginEditor=function(a,b){function c(a,b){var c=$('
                                ').html(b);$("#setting-value-container-"+a).append(c)}function d(){$("#setting-row-instance-name").length?$("#setting-row-instance-name").nextAll().remove():$("#setting-row-plugin-types").nextAll().remove()}function e(a){return!isNaN(parseFloat(a))&&isFinite(a)}function f(c,d,e,f,g){var h=$("");h.change(e.multi_input?function(){var a=[];$(c).find("textarea").each(function(){var b=$(this).val();b&&(a=a.concat(b))}),d.settings[e.name]=a}:function(){d.settings[e.name]=$(this).val()}),f&&h.val(f),b.createValueEditor(h);var i=$('
                                  '),j=$('
                                  ');j.append(h).append(i);var k=$('
                                • ').mousedown(function(a){a.preventDefault(),$(h).val("").focus().insertAtCaret('datasources["').trigger("freeboard-eval")});i.append(k);var l=$('
                                • ').mousedown(function(b){b.preventDefault(),a.displayJSEditor(h.val(),function(a){h.val(a),h.change()})});if(i.append(l),g){var m=$('
                                • ').mousedown(function(a){a.preventDefault(),j.remove(),$(c).find("textarea:first").change()});i.prepend(m)}$(c).append(j)}function g(a,b,g,h,i){function j(a,b){var c=$('
                                  ').appendTo(n);return c.append('
                                  "),$('
                                  ').appendTo(c)}function k(a,b,c){_.each(a,function(a){function d(){m.settings[a.name].length>0?n.show():n.hide()}function e(b){var c=$("").appendTo(p),e={};_.isArray(m.settings[a.name])||(m.settings[a.name]=[]),m.settings[a.name].push(e),_.each(a.settings,function(a){var d=$("").appendTo(c),f="";_.isUndefined(b[a.name])||(f=b[a.name]),e[a.name]=f,$('').appendTo(d).val(f).change(function(){e[a.name]=$(this).val()})}),c.append($('').append($('
                                    ').append($("
                                  • ").append($('').click(function(){var b=m.settings[a.name].indexOf(e);-1!=b&&(m.settings[a.name].splice(b,1),c.remove(),d())}))))),k.scrollTop(k[0].scrollHeight),d()}!_.isUndefined(a.default_value)&&_.isUndefined(h[a.name])&&(h[a.name]=a.default_value);var g=a.name;_.isUndefined(a.display_name)||(g=a.display_name);var i=j(a.name,g);switch(a.type){case"array":var k=$('
                                    ').appendTo(i),l=$('
                                    ').appendTo(k),n=$("").hide().appendTo(l),o=$("").appendTo(n),p=$("").appendTo(l),q=[];_.each(a.settings,function(a){var b=a.name;_.isUndefined(a.display_name)||(b=a.display_name),$(""+b+"").appendTo(o)}),a.name in h&&(q=h[a.name]),$('
                                    ADD
                                    ').appendTo(i).click(function(){var b={};_.each(a.settings,function(a){b[a.name]=""}),e(b)}),_.each(q,function(a){e(a)});break;case"boolean":m.settings[a.name]=h[a.name];var r=$('
                                    ').appendTo(i),s=$('').prependTo(r).change(function(){m.settings[a.name]=this.checked});a.name in h&&s.prop("checked",h[a.name]);break;case"option":var t=h[a.name],s=$("").appendTo($('
                                    ').appendTo(i)).change(function(){m.settings[a.name]=$(this).val()});_.each(a.options,function(a){var b,c;_.isObject(a)?(b=a.name,c=a.value):b=a,_.isUndefined(c)&&(c=b),_.isUndefined(t)&&(t=c),$("").text(b).attr("value",c).appendTo(s)}),m.settings[a.name]=t,a.name in h&&s.val(h[a.name]);break;default:if(m.settings[a.name]=h[a.name],"calculated"==a.type){if(a.name in h){var u=h[a.name];if(a.multi_input&&_.isArray(u))for(var v=!1,w=0;w
                                  • ').mousedown(function(b){b.preventDefault(),f(i,m,a,null,!0)});$(i).siblings(".form-label").append(x)}}else{var s=$('').appendTo(i).change(function(){m.settings[a.name]="number"==a.type?Number($(this).val()):$(this).val()});if(a.name in h&&s.val(h[a.name]),b&&a.typeahead_data_field&&s.addClass("typeahead_data_field-"+a.typeahead_data_field),b&&a.typeahead_field){var y=[];s.keyup(function(a){a.which>=65&&a.which<=91&&s.trigger("change")}),$(s).autocomplete({source:y,select:function(a,b){s.val(b.item.value),s.trigger("change")}}),s.change(function(){var d=s.val(),e=_.template(b)({input:d});$.get(e,function(b){if(c&&(b=b[c]),b=_.select(b,function(b){return b[a.typeahead_field][0]==d[0]}),y=_.map(b,function(b){return b[a.typeahead_field]}),$(s).autocomplete("option","source",y),1==b.length){b=b[0];for(var e in b)if(b.hasOwnProperty(e)){var f=$(_.template("input.typeahead_data_field-<%= field %>")({field:e}));f&&(f.val(b[e]),f.val()!=s.val()&&f.trigger("change"))}}})})}}}_.isUndefined(a.suffix)||i.append($('
                                    '+a.suffix+"
                                    ")),_.isUndefined(a.description)||i.append($('
                                    '+a.description+"
                                    "))})}var l,m={type:g,settings:{}},n=$("
                                    "),o=$('
                                    ').hide();n.append(o),new DialogBox(n,a,"Save","Cancel",function(){$(".validation-error").remove();for(var a=0;a1){var r=j("plugin-types","Type");p=$("").appendTo($('
                                    ').appendTo(r)),p.append($("").attr("value","undefined")),_.each(b,function(a){p.append($("").text(a.display_name).attr("value",a.type_name))}),p.change(function(){m.type=$(this).val(),m.settings={},d(),l=b[p.val()],_.isUndefined(l)?($("#setting-row-instance-name").hide(),$("#dialog-ok").hide()):($("#setting-row-instance-name").show(),l.description&&l.description.length>0?o.html(l.description).show():o.hide(),$("#dialog-ok").show(),k(l.settings,l.typeahead_source,l.typeahead_data_segment))})}else 1==q.length&&(l=b[q[0]],m.type=l.type_name,m.settings={},k(l.settings));p&&(_.isUndefined(g)?($("#setting-row-instance-name").hide(),$("#dialog-ok").hide()):($("#dialog-ok").show(),p.val(g).trigger("change")))}return{createPluginEditor:function(a,b,c,d,e,f){g(a,b,c,d,e,f)}}},ValueEditor=function(a){function b(a,b){return _.isArray(a)||_.isObject(a)?!0:c(a,b)}function c(a,b){switch(b){case o.ANY:return!0;case o.ARRAY:return _.isArray(a);case o.OBJECT:return _.isObject(a);case o.STRING:return _.isString(a);case o.NUMBER:return _.isNumber(a);case o.BOOLEAN:return _.isBoolean(a)}}function d(a,b){$(a).parent().find(".validation-error").remove(),c(n,b)||$(a).parent().append("
                                    This field expects an expression that evaluates to type "+b+".
                                    ")}function e(a){var b=($(a).val().match(/\n/g)||[]).length,c=Math.min(200,20*(b+1));$(a).css({height:c+"px"})}function f(a,c,d){var e=j.exec(a),f=[];if(e)if(""==e[1])_.each(c,function(a){f.push({value:a.name(),entity:void 0,precede_char:"",follow_char:'"]'})});else if(""!=e[1]&&_.isUndefined(e[2])){var g=e[1];_.each(c,function(a){var b=a.name();b!=g&&0==b.indexOf(g)&&f.push({value:b,entity:void 0,precede_char:"",follow_char:'"]'})})}else{var h=_.find(c,function(a){return a.name()===e[1]});if(!_.isUndefined(h)){var i="data",k="";if(!_.isUndefined(e[2])){var l=e[3].lastIndexOf("]")+1;i+=e[3].substring(0,l),k=e[3].substring(l,e[3].length),k=k.replace(/^[\[\"]*/,""),k=k.replace(/[\"\]]*$/,"")}var o=h.getDataRepresentation(i);if(n=o,_.isArray(o)){for(var p=0;p0){k||(k=$('
                                      ').insertAfter(b).width($(b).outerWidth()-2).css("left",$(b).position().left).css("top",$(b).position().top+$(b).outerHeight()-1)),k.empty(),k.scrollTop(0);var g=!0;l=0,_.each(m,function(a,c){var d=h(b,e,a,c);g&&($(d).addClass("selected"),g=!1)})}else d(b,c),$(b).next("ul#value-selector").remove(),k=null,l=-1}function h(a,b,c,d){var e=c.value;c.preview&&(e=e+""+c.preview+"");var f=$("
                                    • "+e+"
                                    • ").appendTo(k).mouseenter(function(){$(this).trigger("freeboard-select")}).mousedown(function(a){$(this).trigger("freeboard-insertValue"),a.preventDefault()}).data("freeboard-optionIndex",d).data("freeboard-optionValue",c.value).bind("freeboard-insertValue",function(){var d=c.value;d=c.precede_char+d+c.follow_char;var e=b.lastIndexOf("]");-1!=e?$(a).replaceTextAt(e+1,$(a).val().length,d):$(a).insertAtCaret(d),n=c.entity,$(a).triggerHandler("mouseup")}).bind("freeboard-select",function(){$(this).parent().find("li.selected").removeClass("selected"),$(this).addClass("selected"),l=$(this).data("freeboard-optionIndex")});return f}function i(a,b){$(a).addClass("calculated-value-input").bind("keyup mouseup freeboard-eval",function(c){return!k||"keyup"!=c.type||38!=c.keyCode&&40!=c.keyCode&&13!=c.keyCode?void g(a,b):void c.preventDefault()}).focus(function(){$(a).css({"z-index":3001}),e(a)}).focusout(function(){d(a,b),$(a).css({height:"","z-index":3e3}),$(a).next("ul#value-selector").remove(),k=null,l=-1}).bind("keydown",function(a){if(k)if(38==a.keyCode||40==a.keyCode){a.preventDefault();var b=$(k).find("li");38==a.keyCode?l--:40==a.keyCode&&l++,0>l?l=b.size()-1:l>=b.size()&&(l=0);var c=$(b).eq(l);c.trigger("freeboard-select"),$(k).scrollTop($(c).position().top)}else 13==a.keyCode&&(a.preventDefault(),-1!=l&&$(k).find("li").eq(l).trigger("freeboard-insertValue"))})}var j=new RegExp('.*datasources\\["([^"]*)("\\])?(.*)$'),k=null,l=0,m=[],n=null,o={ANY:"any",ARRAY:"array",OBJECT:"object",STRING:"string",NUMBER:"number",BOOLEAN:"boolean"};return{createValueEditor:function(a,b){b?i(a,b):i(a,o.ANY)},EXPECTED_TYPE:o}},function(a){function b(){var a=document.createElement("p"),b=!1;if(a.addEventListener)a.addEventListener("DOMAttrModified",function(){b=!0},!1);else{if(!a.attachEvent)return!1;a.attachEvent("onDOMAttrModified",function(){b=!0})}return a.setAttribute("id","target"),b}function c(b,c){if(b){var d=this.data("attr-old-value");if(c.attributeName.indexOf("style")>=0){d.style||(d.style={});var e=c.attributeName.split(".");c.attributeName=e[0],c.oldValue=d.style[e[1]],c.newValue=e[1]+":"+this.prop("style")[a.camelCase(e[1])],d.style[e[1]]=c.newValue}else c.oldValue=d[c.attributeName],c.newValue=this.attr(c.attributeName),d[c.attributeName]=c.newValue;this.data("attr-old-value",d)}}var d=window.MutationObserver||window.WebKitMutationObserver;a.fn.attrchange=function(e){var f={trackValues:!1,callback:a.noop};if("function"==typeof e?f.callback=e:a.extend(f,e),f.trackValues&&a(this).each(function(b,c){for(var d,e={},b=0,f=c.attributes,g=f.length;g>b;b++)d=f.item(b),e[d.nodeName]=d.value;a(this).data("attr-old-value",e)}),d){var g={subtree:!1,attributes:!0,attributeOldValue:f.trackValues},h=new d(function(b){b.forEach(function(b){var c=b.target;f.trackValues&&(b.newValue=a(c).attr(b.attributeName)),f.callback.call(c,b)})});return this.each(function(){h.observe(this,g)})}return b()?this.on("DOMAttrModified",function(a){a.originalEvent&&(a=a.originalEvent),a.attributeName=a.attrName,a.oldValue=a.prevValue,f.callback.call(this,a)}):"onpropertychange"in document.body?this.on("propertychange",function(b){b.attributeName=window.event.propertyName,c.call(a(this),f.trackValues,b),f.callback.call(this,b)}):this}}(jQuery),function(a){a.eventEmitter={_JQInit:function(){this._JQ=a(this)},emit:function(a,b){!this._JQ&&this._JQInit(),this._JQ.trigger(a,b)},once:function(a,b){!this._JQ&&this._JQInit(),this._JQ.one(a,b)},on:function(a,b){!this._JQ&&this._JQInit(),this._JQ.bind(a,b)},off:function(a,b){!this._JQ&&this._JQInit(),this._JQ.unbind(a,b)}}}(jQuery);var freeboard=function(){function a(a){a=a.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");var b=new RegExp("[\\?&]"+a+"=([^&#]*)"),c=b.exec(location.search);return null==c?"":decodeURIComponent(c[1].replace(/\+/g," "))}var b={},c={},d=new FreeboardUI,e=new FreeboardModel(b,c,d),f=new JSEditor,g=new ValueEditor(e),h=new PluginEditor(f,g),i=new DeveloperConsole(e),j={values:{"font-family":'"HelveticaNeue-UltraLight", "Helvetica Neue Ultra Light", "Helvetica Neue", sans-serif',color:"#d3d4d4","font-weight":100}};return ko.bindingHandlers.pluginEditor={init:function(a,f,g,i){var j=ko.unwrap(f()),k={},l=void 0,m="";"datasource"==j.type?(k=b,m="Datasource"):"widget"==j.type?(k=c,m="Widget"):"pane"==j.type&&(m="Pane"),$(a).click(function(){if("delete"==j.operation){var f=$("

                                      Are you sure you want to delete this "+m+"?

                                      ");new DialogBox(f,"Confirm Delete","Yes","No",function(){"datasource"==j.type?e.deleteDatasource(i):"widget"==j.type?e.deleteWidget(i):"pane"==j.type&&e.deletePane(i); + +})}else{var g=void 0;"datasource"==j.type?"add"==j.operation?l={}:(g=i.type(),l=i.settings(),l.name=i.name()):"widget"==j.type?"add"==j.operation?l={}:(g=i.type(),l=i.settings()):"pane"==j.type&&(l={},"edit"==j.operation&&(l.title=i.title(),l.col_width=i.col_width()),k={settings:{settings:[{name:"title",display_name:"Title",type:"text"},{name:"col_width",display_name:"Columns",type:"number",default_value:1,required:!0}]}}),h.createPluginEditor(m,k,g,l,function(f){if("add"==j.operation){if("datasource"==j.type){var g=new DatasourceModel(e,b);e.addDatasource(g),g.name(f.settings.name),delete f.settings.name,g.settings(f.settings),g.type(f.type)}else if("widget"==j.type){var g=new WidgetModel(e,c);g.settings(f.settings),g.type(f.type),i.widgets.push(g),d.attachWidgetEditIcons(a)}}else"edit"==j.operation&&("pane"==j.type?(i.title(f.settings.title),i.col_width(f.settings.col_width),d.processResize(!1)):("datasource"==j.type&&(i.name(f.settings.name),delete f.settings.name),i.type(f.type),i.settings(f.settings)))})}})}},ko.virtualElements.allowedBindings.datasourceTypeSettings=!0,ko.bindingHandlers.datasourceTypeSettings={update:function(a,b,c,d,e){processPluginSettings(a,b,c,d,e)}},ko.bindingHandlers.pane={init:function(a,b,c,f,g){e.isEditing()&&$(a).css({cursor:"pointer"}),d.addPane(a,f,g.$root.isEditing())},update:function(a,b,c,f){-1==e.panes.indexOf(f)&&d.removePane(a),d.updatePane(a,f)}},ko.bindingHandlers.widget={init:function(a){e.isEditing()&&d.attachWidgetEditIcons($(a).parent())},update:function(a,b,c,d){d.shouldRender()&&($(a).empty(),d.render(a))}},$(function(){function a(){d.processResize(!0)}d.showLoadingIndicator(!0);var b;$(window).resize(function(){clearTimeout(b),b=setTimeout(a,500)})}),{initialize:function(b,c){ko.applyBindings(e);var f=a("load");""!=f?$.ajax({url:f,success:function(a){e.loadDashboard(a),_.isFunction(c)&&c()}}):(e.allow_edit(b),e.setEditing(b),d.showLoadingIndicator(!1),_.isFunction(c)&&c(),freeboard.emit("initialized"))},newDashboard:function(){e.loadDashboard({allow_edit:!0})},loadDashboard:function(a,b){e.loadDashboard(a,b)},serialize:function(){return e.serialize()},setEditing:function(a,b){e.setEditing(a,b)},isEditing:function(){return e.isEditing()},loadDatasourcePlugin:function(a){_.isUndefined(a.display_name)&&(a.display_name=a.type_name),a.settings.unshift({name:"name",display_name:"Name",type:"text",required:!0}),e.addPluginSource(a.source),b[a.type_name]=a,e._datasourceTypes.valueHasMutated()},resize:function(){d.processResize(!0)},loadWidgetPlugin:function(a){_.isUndefined(a.display_name)&&(a.display_name=a.type_name),e.addPluginSource(a.source),c[a.type_name]=a,e._widgetTypes.valueHasMutated()},setAssetRoot:function(a){f.setAssetRoot(a)},addStyle:function(a,b){var c=a+"{"+b+"}",d=$("style#fb-styles");0==d.length&&(d=$(''),$("head").append(d)),d[0].styleSheet?d[0].styleSheet.cssText+=c:d.text(d.text()+c)},showLoadingIndicator:function(a){d.showLoadingIndicator(a)},showDialog:function(a,b,c,d,e){new DialogBox(a,b,c,d,e)},getDatasourceSettings:function(a){var b=e.datasources(),c=_.find(b,function(b){return b.name()===a});return c?c.settings():null},setDatasourceSettings:function(a,b){var c=e.datasources(),d=_.find(c,function(b){return b.name()===a});if(!d)return void console.log("Datasource not found");var f=_.defaults(b,d.settings());d.settings(f)},getStyleString:function(a){var b="";return _.each(j[a],function(a,c){b=b+c+":"+a+";"}),b},getStyleObject:function(a){return j[a]},showDeveloperConsole:function(){i.showDeveloperConsole()}}}();$.extend(freeboard,jQuery.eventEmitter); \ No newline at end of file diff --git a/js/freeboard.plugins.js b/js/freeboard.plugins.js index 8bf9f88..f625d27 100644 --- a/js/freeboard.plugins.js +++ b/js/freeboard.plugins.js @@ -493,8 +493,159 @@ newInstanceCallback(new clockDatasource(settings, updateCallback)); } }); +freeboard.loadDatasourcePlugin({ + // **type_name** (required) : A unique name for this plugin. This name should be as unique as possible to avoid collisions with other plugins, and should follow naming conventions for javascript variable and function declarations. + "type_name" : "meshblu", + // **display_name** : The pretty name that will be used for display purposes for this plugin. If the name is not defined, type_name will be used instead. + "display_name": "Octoblu", + // **description** : A description of the plugin. This description will be displayed when the plugin is selected or within search results (in the future). The description may contain HTML if needed. + "description" : "app.octoblu.com", + // **external_scripts** : Any external scripts that should be loaded before the plugin instance is created. + "external_scripts" : [ + "http://meshblu.octoblu.com/js/meshblu.js" + ], + // **settings** : An array of settings that will be displayed for this plugin when the user adds it. + "settings" : [ + { + // **name** (required) : The name of the setting. This value will be used in your code to retrieve the value specified by the user. This should follow naming conventions for javascript variable and function declarations. + "name" : "uuid", + // **display_name** : The pretty name that will be shown to the user when they adjust this setting. + "display_name" : "UUID", + // **type** (required) : The type of input expected for this setting. "text" will display a single text box input. Examples of other types will follow in this documentation. + "type" : "text", + // **default_value** : A default value for this setting. + "default_value": "device uuid", + // **description** : Text that will be displayed below the setting to give the user any extra information. + "description" : "your device UUID", + // **required** : Set to true if this setting is required for the datasource to be created. + "required" : true + }, + { + // **name** (required) : The name of the setting. This value will be used in your code to retrieve the value specified by the user. This should follow naming conventions for javascript variable and function declarations. + "name" : "token", + // **display_name** : The pretty name that will be shown to the user when they adjust this setting. + "display_name" : "Token", + // **type** (required) : The type of input expected for this setting. "text" will display a single text box input. Examples of other types will follow in this documentation. + "type" : "text", + // **default_value** : A default value for this setting. + "default_value": "device token", + // **description** : Text that will be displayed below the setting to give the user any extra information. + "description" : "your device TOKEN", + // **required** : Set to true if this setting is required for the datasource to be created. + "required" : true + }, + { + // **name** (required) : The name of the setting. This value will be used in your code to retrieve the value specified by the user. This should follow naming conventions for javascript variable and function declarations. + "name" : "server", + // **display_name** : The pretty name that will be shown to the user when they adjust this setting. + "display_name" : "Server", + // **type** (required) : The type of input expected for this setting. "text" will display a single text box input. Examples of other types will follow in this documentation. + "type" : "text", + // **default_value** : A default value for this setting. + "default_value": "meshblu.octoblu.com", + // **description** : Text that will be displayed below the setting to give the user any extra information. + "description" : "your server", + // **required** : Set to true if this setting is required for the datasource to be created. + "required" : true + }, + { + // **name** (required) : The name of the setting. This value will be used in your code to retrieve the value specified by the user. This should follow naming conventions for javascript variable and function declarations. + "name" : "port", + // **display_name** : The pretty name that will be shown to the user when they adjust this setting. + "display_name" : "Port", + // **type** (required) : The type of input expected for this setting. "text" will display a single text box input. Examples of other types will follow in this documentation. + "type" : "number", + // **default_value** : A default value for this setting. + "default_value": 80, + // **description** : Text that will be displayed below the setting to give the user any extra information. + "description" : "server port", + // **required** : Set to true if this setting is required for the datasource to be created. + "required" : true + } + + ], + // **newInstance(settings, newInstanceCallback, updateCallback)** (required) : A function that will be called when a new instance of this plugin is requested. + // * **settings** : A javascript object with the initial settings set by the user. The names of the properties in the object will correspond to the setting names defined above. + // * **newInstanceCallback** : A callback function that you'll call when the new instance of the plugin is ready. This function expects a single argument, which is the new instance of your plugin object. + // * **updateCallback** : A callback function that you'll call if and when your datasource has an update for freeboard to recalculate. This function expects a single parameter which is a javascript object with the new, updated data. You should hold on to this reference and call it when needed. + newInstance : function(settings, newInstanceCallback, updateCallback) + { + // myDatasourcePlugin is defined below. + newInstanceCallback(new meshbluSource(settings, updateCallback)); + } + }); + + + // ### Datasource Implementation + // + // ------------------- + // Here we implement the actual datasource plugin. We pass in the settings and updateCallback. + var meshbluSource = function(settings, updateCallback) + { + // Always a good idea... + var self = this; + + // Good idea to create a variable to hold on to our settings, because they might change in the future. See below. + var currentSettings = settings; + + + + /* This is some function where I'll get my data from somewhere */ + + + function getData() + { + + + var conn = skynet.createConnection({ + "uuid": currentSettings.uuid, + "token": currentSettings.token, + "server": currentSettings.server, + "port": currentSettings.port + }); + + conn.on('ready', function(data){ + + conn.on('message', function(message){ + + var newData = message; + updateCallback(newData); + + }); + + }); + } + + + + // **onSettingsChanged(newSettings)** (required) : A public function we must implement that will be called when a user makes a change to the settings. + self.onSettingsChanged = function(newSettings) + { + // Here we update our current settings with the variable that is passed in. + currentSettings = newSettings; + } + + // **updateNow()** (required) : A public function we must implement that will be called when the user wants to manually refresh the datasource + self.updateNow = function() + { + // Most likely I'll just call getData() here. + getData(); + } + + // **onDispose()** (required) : A public function we must implement that will be called when this instance of this plugin is no longer needed. Do anything you need to cleanup after yourself here. + self.onDispose = function() + { + + //conn.close(); + } + + // Here we call createRefreshTimer with our current settings, to kick things off, initially. Notice how we make use of one of the user defined settings that we setup earlier. + // createRefreshTimer(currentSettings.refresh_time); + } + }()); + // ┌────────────────────────────────────────────────────────────────────┐ \\ // │ F R E E B O A R D │ \\ // ├────────────────────────────────────────────────────────────────────┤ \\ diff --git a/js/freeboard.plugins.min.js b/js/freeboard.plugins.min.js index f9d9a44..de27cc7 100644 --- a/js/freeboard.plugins.min.js +++ b/js/freeboard.plugins.min.js @@ -1 +1 @@ -(function(){var e=function(e,t){function n(e){a&&clearInterval(a),a=setInterval(function(){i.updateNow()},e)}var i=this,a=null,s=e,o=0,r=!1;n(1e3*s.refresh),this.updateNow=function(){if(!(o>1&&!s.use_thingproxy||o>2)){var e=s.url;2==o&&s.use_thingproxy&&(e=("https:"==location.protocol?"https:":"http:")+"//thingproxy.freeboard.io/fetch/"+encodeURI(s.url));var n=s.body;if(n)try{n=JSON.parse(n)}catch(a){}$.ajax({url:e,dataType:1==o?"JSONP":"JSON",type:s.method||"GET",data:n,beforeSend:function(e){try{_.each(s.headers,function(t){var n=t.name,i=t.value;_.isUndefined(n)||_.isUndefined(i)||e.setRequestHeader(n,i)})}catch(t){}},success:function(e){r=!0,t(e)},error:function(){r||(o++,i.updateNow())}})}},this.onDispose=function(){clearInterval(a),a=null},this.onSettingsChanged=function(e){r=!1,o=0,s=e,n(1e3*s.refresh),i.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:!0},{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(t,n,i){n(new e(t,i))}});var t=function(e,t){function n(e){s&&clearInterval(s),s=setInterval(function(){a.updateNow()},e)}function i(e){return e.replace(/\w\S*/g,function(e){return e.charAt(0).toUpperCase()+e.substr(1).toLowerCase()})}var a=this,s=null,o=e;n(1e3*o.refresh),this.updateNow=function(){$.ajax({url:"http://api.openweathermap.org/data/2.5/weather?q="+encodeURIComponent(o.location)+"&units="+o.units,dataType:"JSONP",success:function(e){var n={place_name:e.name,sunrise:new Date(1e3*e.sys.sunrise).toLocaleTimeString(),sunset:new Date(1e3*e.sys.sunset).toLocaleTimeString(),conditions:i(e.weather[0].description),current_temp:e.main.temp,high_temp:e.main.temp_max,low_temp:e.main.temp_min,pressure:e.main.pressure,humidity:e.main.humidity,wind_speed:e.wind.speed,wind_direction:e.wind.deg};t(n)},error:function(){}})},this.onDispose=function(){clearInterval(s),s=null},this.onSettingsChanged=function(e){o=e,a.updateNow(),n(1e3*o.refresh)}};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(e,n,i){n(new t(e,i))}});var n=function(e,t){function n(e){t(e)}var i=this,a=e;this.updateNow=function(){dweetio.get_latest_dweet_for(a.thing_id,function(e,t){e||n(t[0].content)})},this.onDispose=function(){},this.onSettingsChanged=function(e){dweetio.stop_listening(),a=e,dweetio.listen_for(a.thing_id,function(e){n(e.content)})},i.onSettingsChanged(e)};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(e,t,i){t(new n(e,i))}});var i=function(e,t){function n(){r.length>0?(r.length>l&&(t(r[l]),l++),l>=r.length&&o.loop&&(l=0),r.length>l&&(a=setTimeout(n,1e3*o.refresh))):t({})}function i(){r=[],l=0,a&&(clearTimeout(a),a=null)}var a,s=this,o=e,r=[],l=0;this.updateNow=function(){i(),$.ajax({url:o.datafile,dataType:o.is_jsonp?"JSONP":"JSON",success:function(e){r=_.isArray(e)?e:[],l=0,n()},error:function(){}})},this.onDispose=function(){i()},this.onSettingsChanged=function(e){o=e,s.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(e,t,n){t(new i(e,n))}});var a=function(e,t){function n(){a&&(clearTimeout(a),a=null)}function i(){n(),a=setInterval(s.updateNow,1e3*o.refresh)}var a,s=this,o=e;this.updateNow=function(){var e=new Date,n={numeric_value:e.getTime(),full_string_value:e.toLocaleString(),date_string_value:e.toLocaleDateString(),time_string_value:e.toLocaleTimeString(),date_object:e};t(n)},this.onDispose=function(){n()},this.onSettingsChanged=function(e){o=e,i()},i()};freeboard.loadDatasourcePlugin({type_name:"clock",display_name:"Clock",settings:[{name:"refresh",display_name:"Refresh Every",type:"number",suffix:"seconds",default_value:1}],newInstance:function(e,t,n){t(new a(e,n))}})})(),function(){function e(e,t,n){var i=$(t).text();if(i!=e)if($.isNumeric(e)&&$.isNumeric(i)){var a=(""+e).split("."),s=0;a.length>1&&(s=a[1].length),a=(""+i).split(".");var o=0;a.length>1&&(o=a[1].length),jQuery({transitionValue:Number(i),precisionValue:o}).animate({transitionValue:Number(e),precisionValue:s},{duration:n,step:function(){$(t).text(this.transitionValue.toFixed(this.precisionValue))},done:function(){$(t).text(e)}})}else $(t).text(e)}function t(e,t){for(var n=$("
                                      "),i=0;t.length>i;i++){var s=a[i%a.length],o=t[i];n.append("
                                      "+o+"
                                      ")}e.empty().append(n),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 n(e,t,n){var s=$(e).data().values,o=$(e).data().valueMin,r=$(e).data().valueMax;s||(s=[],o=void 0,r=void 0);var l=function(e,t){s[t]||(s[t]=[]),s[t].length>=i&&s[t].shift(),s[t].push(Number(e)),(void 0===o||o>e)&&(o=e),(void 0===r||e>r)&&(r=e)};_.isArray(t)?_.each(t,l):l(t,0),$(e).data().values=s,$(e).data().valueMin=o,$(e).data().valueMax=r;var d=' {{y}}',c=!1;_.each(s,function(t,i){$(e).sparkline(t,{type:"line",composite:c,height:"100%",width:"100%",fillColor:!1,lineColor:a[i%a.length],lineWidth:2,spotRadius:3,spotColor:!1,minSpotColor:"#78AB49",maxSpotColor:"#78AB49",highlightSpotColor:"#9D3926",highlightLineColor:"#9D3926",chartRangeMin:o,chartRangeMax:r,tooltipFormat:n&&n[i]?d+" ("+n[i]+")":d}),c=!0})}var i=100,a=["#FF9900","#FFFFFF","#B3B4B4","#6B6B6B","#28DE28","#13F7F9","#E6EE18","#C41204","#CA3CB8","#0B1CFB"],s=freeboard.getStyleString("values");freeboard.addStyle(".widget-big-text",s+"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",s+"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 o=function(t){function i(){_.isUndefined(a.units)||""==a.units?r.css("max-width","100%"):r.css("max-width",s.innerWidth()-l.outerWidth(!0)+"px")}var a=t,s=$('
                                      '),o=$('

                                      '),r=$('
                                      '),l=$('
                                      '),d=$('
                                      ');this.render=function(e){$(e).empty(),$(s).append($('
                                      ').append(o)).append($('
                                      ').append($('
                                      ').append(r).append(l))).append($('
                                      ').append(d)),$(e).append(s),i()},this.onSettingsChanged=function(e){a=e;var t=!_.isUndefined(e.title)&&""!=e.title,n=!_.isUndefined(e.units)&&""!=e.units;e.sparkline?d.attr("style",null):(delete d.data().values,d.empty(),d.hide()),t?(o.html(_.isUndefined(e.title)?"":e.title),o.attr("style",null)):(o.empty(),o.hide()),n?(l.html(_.isUndefined(e.units)?"":e.units),l.attr("style",null)):(l.empty(),l.hide());var s=30;"big"==e.size&&(s=75,e.sparkline&&(s=60)),r.css({"font-size":s+"px"}),i()},this.onSizeChanged=function(){i()},this.onCalculatedValueChanged=function(t,i){"value"==t&&(a.animate?e(i,r,500):r.text(i),a.sparkline&&n(d,i))},this.onDispose=function(){},this.getHeight=function(){return"big"==a.size||a.sparkline?2:1},this.onSettingsChanged(t)};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:!0},{name:"units",display_name:"Units",type:"text"}],newInstance:function(e,t){t(new o(e))}});var r=0;freeboard.addStyle(".gauge-widget-wrapper","width: 100%;text-align: center;"),freeboard.addStyle(".gauge-widget","width:200px;height:160px;display:inline-block;");var l=function(e){function t(){o&&(s.empty(),n=new JustGage({id:i,value:_.isUndefined(l.min_value)?0:l.min_value,min:_.isUndefined(l.min_value)?0:l.min_value,max:_.isUndefined(l.max_value)?0:l.max_value,label:l.units,showInnerShadow:!1,valueFontColor:"#d3d4d4"}))}var n,i="gauge-"+r++,a=$('

                                      '),s=$('
                                      '),o=!1,l=e;this.render=function(e){o=!0,$(e).append(a).append($('
                                      ').append(s)),t()},this.onSettingsChanged=function(e){e.min_value!=l.min_value||e.max_value!=l.max_value||e.units!=l.units?(l=e,t()):l=e,a.html(e.title)},this.onCalculatedValueChanged=function(e,t){_.isUndefined(n)||n.refresh(Number(t))},this.onDispose=function(){},this.getHeight=function(){return 3},this.onSettingsChanged(e)};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(e,t){t(new l(e))}}),freeboard.addStyle(".sparkline","width:100%;height: 75px;");var d=function(e){var i=$('

                                      '),a=$('
                                      '),s=$("
                                      "),o=e;this.render=function(e){$(e).append(i).append(a).append(s)},this.onSettingsChanged=function(e){o=e,i.html(_.isUndefined(e.title)?"":e.title),e.include_legend&&t(s,e.legend.split(","))},this.onCalculatedValueChanged=function(e,t){o.legend?n(a,t,o.legend.split(",")):n(a,t)},this.onDispose=function(){},this.getHeight=function(){var e=0;if(o.include_legend&&o.legend){var t=o.legend.split(",").length;t>4?e=.5*Math.floor((t-1)/4):t&&(e=.5)}return 2+e},this.onSettingsChanged(e)};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(e,t){t(new d(e))}}),freeboard.addStyle("div.pointer-value","position:absolute;height:95px;margin: auto;top: 0px;bottom: 0px;width: 100%;text-align:center;");var c=function(e){function t(e){if(!e||2>e.length)return[];var t=[];t.push(["m",e[0],e[1]]);for(var n=2;e.length>n;n+=2)t.push(["l",e[n],e[n+1]]);return t.push(["z"]),t}var n,i,a,s,o=3,r=0,l=$('
                                      '),d=$("
                                      ");this.render=function(e){a=$(e).width(),s=$(e).height();var r=Math.min(a,s)/2-2*o;n=Raphael($(e).get()[0],a,s);var c=n.circle(a/2,s/2,r);c.attr("stroke","#FF9900"),c.attr("stroke-width",o),i=n.path(t([a/2,s/2-r+o,15,20,-30,0])),i.attr("stroke-width",0),i.attr("fill","#fff"),$(e).append($('
                                      ').append(l).append(d))},this.onSettingsChanged=function(e){d.html(e.units)},this.onCalculatedValueChanged=function(e,t){if("direction"==e){if(!_.isUndefined(i)){i.animate({transform:"r"+t+","+a/2+","+s/2},250,"bounce")}r=t}else"value_text"==e&&l.html(t)},this.onDispose=function(){},this.getHeight=function(){return 4},this.onSettingsChanged(e)};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(e,t){t(new c(e))}});var u=function(e){function t(){a&&(clearInterval(a),a=null)}function n(){if(i&&s){var e=s+(-1==s.indexOf("?")?"?":"&")+Date.now();$(i).css({"background-image":"url("+e+")"})}}var i,a,s;this.render=function(e){$(e).css({width:"100%",height:"100%","background-size":"cover","background-position":"center"}),i=e},this.onSettingsChanged=function(e){t(),e.refresh&&e.refresh>0&&(a=setInterval(n,1e3*Number(e.refresh)))},this.onCalculatedValueChanged=function(e,t){"src"==e&&(s=t),n()},this.onDispose=function(){t()},this.getHeight=function(){return 4},this.onSettingsChanged(e)};freeboard.loadWidgetPlugin({type_name:"picture",display_name:"Picture",fill_size:!0,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(e,t){t(new u(e))}}),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 p=function(e){function t(){a.toggleClass("on",o),o?i.text(_.isUndefined(s.on_text)?"":s.on_text):i.text(_.isUndefined(s.off_text)?"":s.off_text)}var n=$('

                                      '),i=$('
                                      '),a=$('
                                      '),s=e,o=!1;this.render=function(e){$(e).append(n).append(a).append(i)},this.onSettingsChanged=function(e){s=e,n.html(_.isUndefined(e.title)?"":e.title),t()},this.onCalculatedValueChanged=function(e,n){"value"==e&&(o=Boolean(n)),t()},this.onDispose=function(){},this.getHeight=function(){return 1},this.onSettingsChanged(e)};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(e,t){t(new p(e))}}),freeboard.addStyle(".gm-style-cc a","text-shadow:none;");var f=function(e){function t(){if(n&&i&&s.lat&&s.lon){var e=new google.maps.LatLng(s.lat,s.lon);i.setPosition(e),n.panTo(e)}}var n,i,a=e,s={};this.render=function(e){function a(){var a={zoom:13,center:new google.maps.LatLng(37.235,-115.811111),disableDefaultUI:!0,draggable:!1,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:.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}]}]};n=new google.maps.Map(e,a),google.maps.event.addDomListener(e,"mouseenter",function(e){e.cancelBubble=!0,n.hover||(n.hover=!0,n.setOptions({zoomControl:!0}))}),google.maps.event.addDomListener(e,"mouseleave",function(){n.hover&&(n.setOptions({zoomControl:!1}),n.hover=!1)}),i=new google.maps.Marker({map:n}),t()}window.google&&window.google.maps?a():(window.gmap_initialize=a,head.js("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=gmap_initialize"))},this.onSettingsChanged=function(e){a=e},this.onCalculatedValueChanged=function(e,n){"lat"==e?s.lat=n:"lon"==e&&(s.lon=n),t()},this.onDispose=function(){},this.getHeight=function(){return 4},this.onSettingsChanged(e)};freeboard.loadWidgetPlugin({type_name:"google_map",display_name:"Google Map",fill_size:!0,settings:[{name:"lat",display_name:"Latitude",type:"calculated"},{name:"lon",display_name:"Longitude",type:"calculated"}],newInstance:function(e,t){t(new f(e))}}),freeboard.addStyle(".html-widget","white-space:normal;width:100%;height:100%");var h=function(e){var t=$('
                                      '),n=e;this.render=function(e){$(e).append(t)},this.onSettingsChanged=function(e){n=e},this.onCalculatedValueChanged=function(e,n){"html"==e&&t.html(n)},this.onDispose=function(){},this.getHeight=function(){return Number(n.height)},this.onSettingsChanged(e)};freeboard.loadWidgetPlugin({type_name:"html",display_name:"HTML",fill_size:!0,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(e,t){t(new h(e))}})}(); \ No newline at end of file +!function(){var a=function(a,b){function c(a){e&&clearInterval(e),e=setInterval(function(){d.updateNow()},a)}var d=this,e=null,f=a,g=0,h=!1;c(1e3*f.refresh),this.updateNow=function(){if(!(g>1&&!f.use_thingproxy||g>2)){var a=f.url;2==g&&f.use_thingproxy&&(a=("https:"==location.protocol?"https:":"http:")+"//thingproxy.freeboard.io/fetch/"+encodeURI(f.url));var c=f.body;if(c)try{c=JSON.parse(c)}catch(e){}$.ajax({url:a,dataType:1==g?"JSONP":"JSON",type:f.method||"GET",data:c,beforeSend:function(a){try{_.each(f.headers,function(b){var c=b.name,d=b.value;_.isUndefined(c)||_.isUndefined(d)||a.setRequestHeader(c,d)})}catch(b){}},success:function(a){h=!0,b(a)},error:function(){h||(g++,d.updateNow())}})}},this.onDispose=function(){clearInterval(e),e=null},this.onSettingsChanged=function(a){h=!1,g=0,f=a,c(1e3*f.refresh),d.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:!0},{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(b,c,d){c(new a(b,d))}});var b=function(a,b){function c(a){f&&clearInterval(f),f=setInterval(function(){e.updateNow()},a)}function d(a){return a.replace(/\w\S*/g,function(a){return a.charAt(0).toUpperCase()+a.substr(1).toLowerCase()})}var e=this,f=null,g=a;c(1e3*g.refresh),this.updateNow=function(){$.ajax({url:"http://api.openweathermap.org/data/2.5/weather?q="+encodeURIComponent(g.location)+"&units="+g.units,dataType:"JSONP",success:function(a){var c={place_name:a.name,sunrise:new Date(1e3*a.sys.sunrise).toLocaleTimeString(),sunset:new Date(1e3*a.sys.sunset).toLocaleTimeString(),conditions:d(a.weather[0].description),current_temp:a.main.temp,high_temp:a.main.temp_max,low_temp:a.main.temp_min,pressure:a.main.pressure,humidity:a.main.humidity,wind_speed:a.wind.speed,wind_direction:a.wind.deg};b(c)},error:function(){}})},this.onDispose=function(){clearInterval(f),f=null},this.onSettingsChanged=function(a){g=a,e.updateNow(),c(1e3*g.refresh)}};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(a,c,d){c(new b(a,d))}});var c=function(a,b){function c(a){b(a)}var d=this,e=a;this.updateNow=function(){dweetio.get_latest_dweet_for(e.thing_id,function(a,b){a||c(b[0].content)})},this.onDispose=function(){},this.onSettingsChanged=function(a){dweetio.stop_listening(),e=a,dweetio.listen_for(e.thing_id,function(a){c(a.content)})},d.onSettingsChanged(a)};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(a,b,d){b(new c(a,d))}});var d=function(a,b){function c(){h.length>0?(i=h.length&&g.loop&&(i=0),i1&&(f=e[1].length),e=d.toString().split(".");var g=0;e.length>1&&(g=e[1].length),jQuery({transitionValue:Number(d),precisionValue:g}).animate({transitionValue:Number(a),precisionValue:f},{duration:c,step:function(){$(b).text(this.transitionValue.toFixed(this.precisionValue))},done:function(){$(b).text(a)}})}else $(b).text(a)}function b(a,b){for(var c=$("
                                      "),d=0;d"+g+"")}a.empty().append(c),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 c(a,b,c){var f=$(a).data().values,g=$(a).data().valueMin,h=$(a).data().valueMax;f||(f=[],g=void 0,h=void 0);var i=function(a,b){f[b]||(f[b]=[]),f[b].length>=d&&f[b].shift(),f[b].push(Number(a)),(void 0===g||g>a)&&(g=a),(void 0===h||a>h)&&(h=a)};_.isArray(b)?_.each(b,i):i(b,0),$(a).data().values=f,$(a).data().valueMin=g,$(a).data().valueMax=h;var j=' {{y}}',k=!1;_.each(f,function(b,d){$(a).sparkline(b,{type:"line",composite:k,height:"100%",width:"100%",fillColor:!1,lineColor:e[d%e.length],lineWidth:2,spotRadius:3,spotColor:!1,minSpotColor:"#78AB49",maxSpotColor:"#78AB49",highlightSpotColor:"#9D3926",highlightLineColor:"#9D3926",chartRangeMin:g,chartRangeMax:h,tooltipFormat:c&&c[d]?j+" ("+c[d]+")":j}),k=!0})}var d=100,e=["#FF9900","#FFFFFF","#B3B4B4","#6B6B6B","#28DE28","#13F7F9","#E6EE18","#C41204","#CA3CB8","#0B1CFB"],f=freeboard.getStyleString("values");freeboard.addStyle(".widget-big-text",f+"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",f+"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 g=function(b){function d(){_.isUndefined(e.units)||""==e.units?h.css("max-width","100%"):h.css("max-width",f.innerWidth()-i.outerWidth(!0)+"px")}var e=b,f=$('
                                      '),g=$('

                                      '),h=$('
                                      '),i=$('
                                      '),j=$('
                                      ');this.render=function(a){$(a).empty(),$(f).append($('
                                      ').append(g)).append($('
                                      ').append($('
                                      ').append(h).append(i))).append($('
                                      ').append(j)),$(a).append(f),d()},this.onSettingsChanged=function(a){e=a;var b=!_.isUndefined(a.title)&&""!=a.title,c=!_.isUndefined(a.units)&&""!=a.units;a.sparkline?j.attr("style",null):(delete j.data().values,j.empty(),j.hide()),b?(g.html(_.isUndefined(a.title)?"":a.title),g.attr("style",null)):(g.empty(),g.hide()),c?(i.html(_.isUndefined(a.units)?"":a.units),i.attr("style",null)):(i.empty(),i.hide());var f=30;"big"==a.size&&(f=75,a.sparkline&&(f=60)),h.css({"font-size":f+"px"}),d()},this.onSizeChanged=function(){d()},this.onCalculatedValueChanged=function(b,d){"value"==b&&(e.animate?a(d,h,500):h.text(d),e.sparkline&&c(j,d))},this.onDispose=function(){},this.getHeight=function(){return"big"==e.size||e.sparkline?2:1},this.onSettingsChanged(b)};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:!0},{name:"units",display_name:"Units",type:"text"}],newInstance:function(a,b){b(new g(a))}});var h=0;freeboard.addStyle(".gauge-widget-wrapper","width: 100%;text-align: center;"),freeboard.addStyle(".gauge-widget","width:200px;height:160px;display:inline-block;");var i=function(a){function b(){g&&(f.empty(),c=new JustGage({id:d,value:_.isUndefined(i.min_value)?0:i.min_value,min:_.isUndefined(i.min_value)?0:i.min_value,max:_.isUndefined(i.max_value)?0:i.max_value,label:i.units,showInnerShadow:!1,valueFontColor:"#d3d4d4"}))}var c,d="gauge-"+h++,e=$('

                                      '),f=$('
                                      '),g=!1,i=a;this.render=function(a){g=!0,$(a).append(e).append($('
                                      ').append(f)),b()},this.onSettingsChanged=function(a){a.min_value!=i.min_value||a.max_value!=i.max_value||a.units!=i.units?(i=a,b()):i=a,e.html(a.title)},this.onCalculatedValueChanged=function(a,b){_.isUndefined(c)||c.refresh(Number(b))},this.onDispose=function(){},this.getHeight=function(){return 3},this.onSettingsChanged(a)};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(a,b){b(new i(a))}}),freeboard.addStyle(".sparkline","width:100%;height: 75px;");var j=function(a){var d=$('

                                      '),e=$('
                                      '),f=$("
                                      "),g=a;this.render=function(a){$(a).append(d).append(e).append(f)},this.onSettingsChanged=function(a){g=a,d.html(_.isUndefined(a.title)?"":a.title),a.include_legend&&b(f,a.legend.split(","))},this.onCalculatedValueChanged=function(a,b){g.legend?c(e,b,g.legend.split(",")):c(e,b)},this.onDispose=function(){},this.getHeight=function(){var a=0;if(g.include_legend&&g.legend){var b=g.legend.split(",").length;b>4?a=.5*Math.floor((b-1)/4):b&&(a=.5)}return 2+a},this.onSettingsChanged(a)};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(a,b){b(new j(a))}}),freeboard.addStyle("div.pointer-value","position:absolute;height:95px;margin: auto;top: 0px;bottom: 0px;width: 100%;text-align:center;");var k=function(a){function b(a){if(!a||a.length<2)return[];var b=[];b.push(["m",a[0],a[1]]);for(var c=2;c'),j=$("
                                      ");this.render=function(a){e=$(a).width(),f=$(a).height();var h=Math.min(e,f)/2-2*g;c=Raphael($(a).get()[0],e,f);var k=c.circle(e/2,f/2,h);k.attr("stroke","#FF9900"),k.attr("stroke-width",g),d=c.path(b([e/2,f/2-h+g,15,20,-30,0])),d.attr("stroke-width",0),d.attr("fill","#fff"),$(a).append($('
                                      ').append(i).append(j))},this.onSettingsChanged=function(a){j.html(a.units)},this.onCalculatedValueChanged=function(a,b){if("direction"==a){if(!_.isUndefined(d)){d.animate({transform:"r"+b+","+e/2+","+f/2},250,"bounce")}h=b}else"value_text"==a&&i.html(b)},this.onDispose=function(){},this.getHeight=function(){return 4},this.onSettingsChanged(a)};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(a,b){b(new k(a))}});var l=function(a){function b(){e&&(clearInterval(e),e=null)}function c(){if(d&&f){var a=f+(-1==f.indexOf("?")?"?":"&")+Date.now();$(d).css({"background-image":"url("+a+")"})}}var d,e,f;this.render=function(a){$(a).css({width:"100%",height:"100%","background-size":"cover","background-position":"center"}),d=a},this.onSettingsChanged=function(a){b(),a.refresh&&a.refresh>0&&(e=setInterval(c,1e3*Number(a.refresh)))},this.onCalculatedValueChanged=function(a,b){"src"==a&&(f=b),c()},this.onDispose=function(){b()},this.getHeight=function(){return 4},this.onSettingsChanged(a)};freeboard.loadWidgetPlugin({type_name:"picture",display_name:"Picture",fill_size:!0,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(a,b){b(new l(a))}}),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 m=function(a){function b(){e.toggleClass("on",g),d.text(g?_.isUndefined(f.on_text)?"":f.on_text:_.isUndefined(f.off_text)?"":f.off_text)}var c=$('

                                      '),d=$('
                                      '),e=$('
                                      '),f=a,g=!1;this.render=function(a){$(a).append(c).append(e).append(d)},this.onSettingsChanged=function(a){f=a,c.html(_.isUndefined(a.title)?"":a.title),b()},this.onCalculatedValueChanged=function(a,c){"value"==a&&(g=Boolean(c)),b()},this.onDispose=function(){},this.getHeight=function(){return 1},this.onSettingsChanged(a)};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(a,b){b(new m(a))}}),freeboard.addStyle(".gm-style-cc a","text-shadow:none;");var n=function(a){function b(){if(c&&d&&f.lat&&f.lon){var a=new google.maps.LatLng(f.lat,f.lon);d.setPosition(a),c.panTo(a)}}var c,d,e=a,f={};this.render=function(a){function e(){var e={zoom:13,center:new google.maps.LatLng(37.235,-115.811111),disableDefaultUI:!0,draggable:!1,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:.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}]}]};c=new google.maps.Map(a,e),google.maps.event.addDomListener(a,"mouseenter",function(a){a.cancelBubble=!0,c.hover||(c.hover=!0,c.setOptions({zoomControl:!0}))}),google.maps.event.addDomListener(a,"mouseleave",function(){c.hover&&(c.setOptions({zoomControl:!1}),c.hover=!1)}),d=new google.maps.Marker({map:c}),b()}window.google&&window.google.maps?e():(window.gmap_initialize=e,head.js("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=gmap_initialize"))},this.onSettingsChanged=function(a){e=a},this.onCalculatedValueChanged=function(a,c){"lat"==a?f.lat=c:"lon"==a&&(f.lon=c),b()},this.onDispose=function(){},this.getHeight=function(){return 4},this.onSettingsChanged(a)};freeboard.loadWidgetPlugin({type_name:"google_map",display_name:"Google Map",fill_size:!0,settings:[{name:"lat",display_name:"Latitude",type:"calculated"},{name:"lon",display_name:"Longitude",type:"calculated"}],newInstance:function(a,b){b(new n(a))}}),freeboard.addStyle(".html-widget","white-space:normal;width:100%;height:100%");var o=function(a){var b=$('
                                      '),c=a;this.render=function(a){$(a).append(b)},this.onSettingsChanged=function(a){c=a},this.onCalculatedValueChanged=function(a,c){"html"==a&&b.html(c)},this.onDispose=function(){},this.getHeight=function(){return Number(c.height)},this.onSettingsChanged(a)};freeboard.loadWidgetPlugin({type_name:"html",display_name:"HTML",fill_size:!0,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(a,b){b(new o(a))}})}(); \ No newline at end of file diff --git a/js/freeboard.thirdparty.min.js b/js/freeboard.thirdparty.min.js index 616bd0b..58b8298 100644 --- a/js/freeboard.thirdparty.min.js +++ b/js/freeboard.thirdparty.min.js @@ -1,20 +1,24 @@ -(function(n,t){"use strict";function r(n){a[a.length]=n}function k(n){var t=RegExp(" ?\\b"+n+"\\b");c.className=c.className.replace(t,"")}function p(n,t){for(var i=0,r=n.length;r>i;i++)t.call(n,n[i],i)}function tt(){var t,e,f,o;c.className=c.className.replace(/ (w-|eq-|gt-|gte-|lt-|lte-|portrait|no-portrait|landscape|no-landscape)\d+/g,""),t=n.innerWidth||c.clientWidth,e=n.outerWidth||n.screen.width,u.screen.innerWidth=t,u.screen.outerWidth=e,r("w-"+t),p(i.screens,function(n){t>n?(i.screensCss.gt&&r("gt-"+n),i.screensCss.gte&&r("gte-"+n)):n>t?(i.screensCss.lt&&r("lt-"+n),i.screensCss.lte&&r("lte-"+n)):t===n&&(i.screensCss.lte&&r("lte-"+n),i.screensCss.eq&&r("e-q"+n),i.screensCss.gte&&r("gte-"+n))}),f=n.innerHeight||c.clientHeight,o=n.outerHeight||n.screen.height,u.screen.innerHeight=f,u.screen.outerHeight=o,u.feature("portrait",f>t),u.feature("landscape",t>f)}function it(){n.clearTimeout(b),b=n.setTimeout(tt,50)}var v,u,s,w,o,h,l,d,f,g,nt,e,b,y=n.document,rt=n.navigator,ut=n.location,c=y.documentElement,a=[],i={screens:[240,320,480,640,768,800,1024,1280,1440,1680,1920],screensCss:{gt:!0,gte:!1,lt:!0,lte:!1,eq:!1},browsers:[{ie:{min:6,max:11}}],browserCss:{gt:!0,gte:!1,lt:!0,lte:!1,eq:!0},html5:!0,page:"-page",section:"-section",head:"head"};if(n.head_conf)for(v in n.head_conf)n.head_conf[v]!==t&&(i[v]=n.head_conf[v]);switch(u=n[i.head]=function(){u.ready.apply(null,arguments)},u.feature=function(n,t,i){return n?("[object Function]"===Object.prototype.toString.call(t)&&(t=t.call()),r((t?"":"no-")+n),u[n]=!!t,i||(k("no-"+n),k(n),u.feature()),u):(c.className+=" "+a.join(" "),a=[],u)},u.feature("js",!0),s=rt.userAgent.toLowerCase(),w=/mobile|android|kindle|silk|midp|phone|(windows .+arm|touch)/.test(s),u.feature("mobile",w,!0),u.feature("desktop",!w,!0),s=/(chrome|firefox)[ \/]([\w.]+)/.exec(s)||/(iphone|ipad|ipod)(?:.*version)?[ \/]([\w.]+)/.exec(s)||/(android)(?:.*version)?[ \/]([\w.]+)/.exec(s)||/(webkit|opera)(?:.*version)?[ \/]([\w.]+)/.exec(s)||/(msie) ([\w.]+)/.exec(s)||/(trident).+rv:(\w.)+/.exec(s)||[],o=s[1],h=parseFloat(s[2]),o){case"msie":case"trident":o="ie",h=y.documentMode||h;break;case"firefox":o="ff";break;case"ipod":case"ipad":case"iphone":o="ios";break;case"webkit":o="safari"}for(u.browser={name:o,version:h},u.browser[o]=!0,l=0,d=i.browsers.length;d>l;l++)for(f in i.browsers[l])if(o===f)for(r(f),g=i.browsers[l][f].min,nt=i.browsers[l][f].max,e=g;nt>=e;e++)h>e?(i.browserCss.gt&&r("gt-"+f+e),i.browserCss.gte&&r("gte-"+f+e)):e>h?(i.browserCss.lt&&r("lt-"+f+e),i.browserCss.lte&&r("lte-"+f+e)):h===e&&(i.browserCss.lte&&r("lte-"+f+e),i.browserCss.eq&&r("eq-"+f+e),i.browserCss.gte&&r("gte-"+f+e));else r("no-"+f);r(o),r(o+parseInt(h,10)),i.html5&&"ie"===o&&9>h&&p("abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|progress|section|summary|time|video".split("|"),function(n){y.createElement(n)}),p(ut.pathname.split("/"),function(n,u){if(this.length>2&&this[u+1]!==t)u&&r(this.slice(u,u+1).join("-").toLowerCase()+i.section);else{var f=n||"index",e=f.indexOf(".");e>0&&(f=f.substring(0,e)),c.id=f.toLowerCase()+i.page,u||r("root"+i.section)}}),u.screen={height:n.screen.height,width:n.screen.width},tt(),b=0,n.addEventListener?n.addEventListener("resize",it,!1):n.attachEvent("onresize",it)})(window),function(n,t){"use strict";function a(n){for(var r in n)if(i[n[r]]!==t)return!0;return!1}function r(n){var t=n.charAt(0).toUpperCase()+n.substr(1),i=(n+" "+c.join(t+" ")+t).split(" ");return!!a(i)}var h=n.document,o=h.createElement("i"),i=o.style,s=" -o- -moz- -ms- -webkit- -khtml- ".split(" "),c="Webkit Moz O ms Khtml".split(" "),l=n.head_conf&&n.head_conf.head||"head",u=n[l],f={gradient:function(){var n="background-image:";return i.cssText=(n+s.join("gradient(linear,left top,right bottom,from(#9f9),to(#fff));"+n)+s.join("linear-gradient(left top,#eee,#fff);"+n)).slice(0,-n.length),!!i.backgroundImage},rgba:function(){return i.cssText="background-color:rgba(0,0,0,0.5)",!!i.backgroundColor},opacity:function(){return""===o.style.opacity},textshadow:function(){return""===i.textShadow},multiplebgs:function(){i.cssText="background:url(https://),url(https://),red url(https://)";var n=(i.background||"").match(/url/g);return"[object Array]"===Object.prototype.toString.call(n)&&3===n.length},boxshadow:function(){return r("boxShadow")},borderimage:function(){return r("borderImage")},borderradius:function(){return r("borderRadius")},cssreflections:function(){return r("boxReflect")},csstransforms:function(){return r("transform")},csstransitions:function(){return r("transition")},touch:function(){return"ontouchstart"in n},retina:function(){return n.devicePixelRatio>1},fontface:function(){var t=u.browser.name,n=u.browser.version;switch(t){case"ie":return n>=9;case"chrome":return n>=13;case"ff":return n>=6;case"ios":return n>=5;case"android":return!1;case"webkit":return n>=5.1;case"opera":return n>=10;default:return!1}}};for(var e in f)f[e]&&u.feature(e,f[e].call(),!0);u.feature()}(window),function(n,t){"use strict";function w(){}function u(n,t){if(n){"object"==typeof n&&(n=[].slice.call(n));for(var i=0,r=n.length;r>i;i++)t.call(n,n[i],i)}}function it(n,i){var r=Object.prototype.toString.call(i).slice(8,-1);return i!==t&&null!==i&&r===n}function s(n){return it("Function",n)}function a(n){return it("Array",n)}function et(n){var i=n.split("/"),t=i[i.length-1],r=t.indexOf("?");return-1!==r?t.substring(0,r):t}function f(n){n=n||w,n._done||(n(),n._done=1)}function ot(n,t,r,u){var f="object"==typeof n?n:{test:n,success:t?a(t)?t:[t]:!1,failure:r?a(r)?r:[r]:!1,callback:u||w},e=!!f.test;return e&&f.success?(f.success.push(f.callback),i.load.apply(null,f.success)):e||!f.failure?u():(f.failure.push(f.callback),i.load.apply(null,f.failure)),i}function v(n){var i,r,t={};if("object"==typeof n)for(i in n)!n[i]||(t={name:i,url:n[i]});else t={name:et(n),url:n};return r=c[t.name],r&&r.url===t.url?r:(c[t.name]=t,t)}function y(n){n=n||c;for(var t in n)if(n.hasOwnProperty(t)&&n[t].state!==l)return!1;return!0}function st(n){n.state=ft,u(n.onpreload,function(n){n.call()})}function ht(n){n.state===t&&(n.state=nt,n.onpreload=[],rt({url:n.url,type:"cache"},function(){st(n)}))}function ct(){var n=arguments,t=n[n.length-1],r=[].slice.call(n,1),f=r[0];return s(t)||(t=null),a(n[0])?(n[0].push(t),i.load.apply(null,n[0]),i):(f?(u(r,function(n){s(n)||!n||ht(v(n))}),b(v(n[0]),s(f)?f:function(){i.load.apply(null,r)})):b(v(n[0])),i)}function lt(){var n=arguments,t=n[n.length-1],r={};return s(t)||(t=null),a(n[0])?(n[0].push(t),i.load.apply(null,n[0]),i):(u(n,function(n){n!==t&&(n=v(n),r[n.name]=n)}),u(n,function(n){n!==t&&(n=v(n),b(n,function(){y(r)&&f(t)}))}),i)}function b(n,t){return t=t||w,n.state===l?(t(),void 0):n.state===tt?(i.ready(n.name,t),void 0):n.state===nt?(n.onpreload.push(function(){b(n,t)}),void 0):(n.state=tt,rt(n,function(){n.state=l,t(),u(h[n.name],function(n){f(n)}),o&&y()&&u(h.ALL,function(n){f(n)})}),void 0)}function at(n){n=n||"";var t=n.split("?")[0].split(".");return t[t.length-1].toLowerCase()}function rt(t,i){function e(t){t=t||n.event,u.onload=u.onreadystatechange=u.onerror=null,i()}function o(f){f=f||n.event,("load"===f.type||/loaded|complete/.test(u.readyState)&&(!r.documentMode||9>r.documentMode))&&(n.clearTimeout(t.errorTimeout),n.clearTimeout(t.cssTimeout),u.onload=u.onreadystatechange=u.onerror=null,i())}function s(){if(t.state!==l&&20>=t.cssRetries){for(var i=0,f=r.styleSheets.length;f>i;i++)if(r.styleSheets[i].href===u.href)return o({type:"load"}),void 0;t.cssRetries++,t.cssTimeout=n.setTimeout(s,250)}}var u,h,f;i=i||w,h=at(t.url),"css"===h?(u=r.createElement("link"),u.type="text/"+(t.type||"css"),u.rel="stylesheet",u.href=t.url,t.cssRetries=0,t.cssTimeout=n.setTimeout(s,500)):(u=r.createElement("script"),u.type="text/"+(t.type||"javascript"),u.src=t.url),u.onload=u.onreadystatechange=o,u.onerror=e,u.async=!1,u.defer=!1,t.errorTimeout=n.setTimeout(function(){e({type:"timeout"})},7e3),f=r.head||r.getElementsByTagName("head")[0],f.insertBefore(u,f.lastChild)}function vt(){for(var t,u=r.getElementsByTagName("script"),n=0,f=u.length;f>n;n++)if(t=u[n].getAttribute("data-headjs-load"),!!t)return i.load(t),void 0}function yt(n,t){var v,p,e;return n===r?(o?f(t):d.push(t),i):(s(n)&&(t=n,n="ALL"),a(n)?(v={},u(n,function(n){v[n]=c[n],i.ready(n,function(){y(v)&&f(t)})}),i):"string"==typeof n&&s(t)?(p=c[n],p&&p.state===l||"ALL"===n&&y()&&o?(f(t),i):(e=h[n],e?e.push(t):e=h[n]=[t],i)):i)}function e(){return r.body?(o||(o=!0,vt(),u(d,function(n){f(n)})),void 0):(n.clearTimeout(i.readyTimeout),i.readyTimeout=n.setTimeout(e,50),void 0)}function k(){r.addEventListener?(r.removeEventListener("DOMContentLoaded",k,!1),e()):"complete"===r.readyState&&(r.detachEvent("onreadystatechange",k),e())}var o,p,r=n.document,d=[],h={},c={},ut="async"in r.createElement("script")||"MozAppearance"in r.documentElement.style||n.opera,g=n.head_conf&&n.head_conf.head||"head",i=n[g]=n[g]||function(){i.ready.apply(null,arguments)},nt=1,ft=2,tt=3,l=4;if("complete"===r.readyState)e();else if(r.addEventListener)r.addEventListener("DOMContentLoaded",k,!1),n.addEventListener("load",e,!1);else{r.attachEvent("onreadystatechange",k),n.attachEvent("onload",e),p=!1;try{p=!n.frameElement&&r.documentElement}catch(wt){}p&&p.doScroll&&function pt(){if(!o){try{p.doScroll("left")}catch(t){return n.clearTimeout(i.readyTimeout),i.readyTimeout=n.setTimeout(pt,50),void 0}e()}}()}i.load=i.js=ut?lt:ct,i.test=ot,i.ready=yt,i.ready(r,function(){y()&&u(h.ALL,function(n){f(n)}),i.feature&&i.feature("domloaded",!0)})}(window),function(e,undefined){function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t[n]=!0}),t}function F(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=x.expando+Math.random()}function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t.replace(O,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:H.test(n)?JSON.parse(n):n}catch(i){}L.set(e,t,n)}else n=undefined;return n}function U(){return!0}function Y(){return!1}function V(){try{return o.activeElement}catch(e){}}function Z(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function et(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(G.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return g.call(t,e)>=0!==n})}function pt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function ft(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function ht(e){var t=ut.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function dt(e,t){for(var n=e.length,r=0;n>r;r++)q.set(e[r],"globalEval",!t||q.get(t[r],"globalEval"))}function gt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(q.hasData(e)&&(o=q.access(e),s=q.set(t,o),l=o.events)){delete s.handle,s.events={};for(i in l)for(n=0,r=l[i].length;r>n;n++)x.event.add(t,i,l[i][n])}L.hasData(e)&&(a=L.access(e),u=x.extend({},a),L.set(t,u))}}function mt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return t===undefined||t&&x.nodeName(e,t)?x.merge([e],n):n}function yt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&ot.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}function At(e,t){if(t in e)return t;for(var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Dt.length;i--;)if(t=Dt[i]+n,t in e)return t;return r}function Lt(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function qt(t){return e.getComputedStyle(t,null)}function Ht(e,t){for(var n,r,i,o=[],s=0,a=e.length;a>s;s++)r=e[s],r.style&&(o[s]=q.get(r,"olddisplay"),n=r.style.display,t?(o[s]||"none"!==n||(r.style.display=""),""===r.style.display&&Lt(r)&&(o[s]=q.access(r,"olddisplay",Rt(r.nodeName)))):o[s]||(i=Lt(r),(n&&"none"!==n||!i)&&q.set(r,"olddisplay",i?n:x.css(r,"display"))));for(s=0;a>s;s++)r=e[s],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[s]||"":"none"));return e}function Ot(e,t,n){var r=Tt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function Ft(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,s=0;4>o;o+=2)"margin"===n&&(s+=x.css(e,n+jt[o],!0,i)),r?("content"===n&&(s-=x.css(e,"padding"+jt[o],!0,i)),"margin"!==n&&(s-=x.css(e,"border"+jt[o]+"Width",!0,i))):(s+=x.css(e,"padding"+jt[o],!0,i),"padding"!==n&&(s+=x.css(e,"border"+jt[o]+"Width",!0,i)));return s}function Pt(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=qt(e),s=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=vt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Ct.test(i))return i;r=s&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+Ft(e,t,n||(s?"border":"content"),r,o)+"px"}function Rt(e){var t=o,n=Nt[e];return n||(n=Mt(e,t),"none"!==n&&n||(xt=(xt||x("