UNPKG

freeboard-leaflet-realtime

Version:
639 lines (565 loc) 17.6 kB
// ┌────────────────────────────────────────────────────────────────────┐ \\ // │ F R E E B O A R D │ \\ // ├────────────────────────────────────────────────────────────────────┤ \\ // │ Copyright © 2013 Jim Heising (https://github.com/jheising) │ \\ // │ Copyright © 2013 Bug Labs, Inc. (http://buglabs.net) │ \\ // ├────────────────────────────────────────────────────────────────────┤ \\ // │ Licensed under the MIT license. │ \\ // └────────────────────────────────────────────────────────────────────┘ \\ // Jquery plugin to watch for attribute changes (function($) { 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; } p.setAttribute('id', 'target'); return flag; } 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; } this.data('attr-old-value', attributes); //update the old value object } } //initialize Mutation Observer var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; $.fn.attrchange = function(o) { var cfg = { trackValues: false, callback : $.noop }; //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; } $(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 }; 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); } 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; } })(jQuery); (function(jQuery) { jQuery.eventEmitter = { _JQInit: function() { this._JQ = jQuery(this); }, emit: function(evt, data) { !this._JQ && this._JQInit(); this._JQ.trigger(evt, data); }, once: function(evt, handler) { !this._JQ && this._JQInit(); this._JQ.one(evt, handler); }, on: function(evt, handler) { !this._JQ && this._JQInit(); this._JQ.bind(evt, handler); }, off: function(evt, handler) { !this._JQ && this._JQInit(); this._JQ.unbind(evt, handler); } }; }(jQuery)); var freeboard = (function() { var datasourcePlugins = {}; var widgetPlugins = {}; 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 developerConsole = new DeveloperConsole(theFreeboardModel); 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()); 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"; } $(element).click(function(event) { if(options.operation == 'delete') { var phraseElement = $('<p>Are you sure you want to delete this ' + title + '?</p>'); 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); } }); } 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.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 : "integer", 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); 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); 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; } 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.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); } } 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() { //DOM Ready // Show the loading indicator when we first load freeboardUI.showLoadingIndicator(true); var resizeTimer; function resizeEnd() { freeboardUI.processResize(true); } $(window).resize(function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(resizeEnd, 500); }); }); // 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"); if(freeboardLocation != "") { $.ajax({ url : freeboardLocation, success: function(data) { theFreeboardModel.loadDashboard(data); if(_.isFunction(finishedCallback)) { finishedCallback(); } } }); } else { theFreeboardModel.allow_edit(allowEdit); theFreeboardModel.setEditing(allowEdit); 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; } // Add a required setting called name to the beginning plugin.settings.unshift({ name : "name", display_name : "Name", type : "text", required : true }); 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; } 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"); if(styleElement.length == 0) { styleElement = $('<style id="fb-styles" type="text/css"></style>'); $("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); }, getDatasourceSettings : function(datasourceName) { var datasources = theFreeboardModel.datasources(); // Find the datasource with the name specified var datasource = _.find(datasources, function(datasourceModel){ return (datasourceModel.name() === datasourceName); }); if(datasource) { return datasource.settings(); } else { return null; } }, setDatasourceSettings : function(datasourceName, settings) { var datasources = theFreeboardModel.datasources(); // Find the datasource with the name specified var datasource = _.find(datasources, function(datasourceModel){ return (datasourceModel.name() === datasourceName); }); if(!datasource) { console.log("Datasource not found"); return; } var combinedSettings = _.defaults(settings, datasource.settings()); datasource.settings(combinedSettings); }, getStyleString : function(name) { var returnString = ""; _.each(currentStyle[name], function(value, name) { returnString = returnString + name + ":" + value + ";"; }); return returnString; }, getStyleObject : function(name) { return currentStyle[name]; }, showDeveloperConsole : function() { developerConsole.showDeveloperConsole(); } }; }()); $.extend(freeboard, jQuery.eventEmitter);