All Downloads are FREE. Search and download functionalities are using the official Maven repository.

js.models.charting.Model.js Maven / Gradle / Ivy

define(function(require){
	
	require("jquery.flot.crosshair");
	require("jquery.flot.resize");
	require("jquery.flot.time");
	require("jquery.flot.fillbetween");
	
	var View = require("text!./View.html"),
		$ = require("jquery"),
		MV = require("mv"),
		Plot = require("jquery.flot"),//https://github.com/flot/flot/blob/master/API.md
		
		Markings = require("./Markings"),
		RangeSelectorModel = require("./rangeselector/Model"),
		
		StatModel = require("./stat/Model"),
		ContextModel = require("./context/Model");
	
	return function(cfg){
		
		var self = this;
		
		/* 
		 * Submodels
		 */
		this.stat = new StatModel();
		
		/**
		 * Date range selector
		 */
		this.rangeselector = new RangeSelectorModel();

		/**
		 * Context (stack) model
		 */
		this.context = new ContextModel({
			setFromDate: function(date){
				//update date range
				self.rangeselector.from.date(date);
			},
			setToDate: function(date){
				//update date range
				self.rangeselector.to.date(date);
			}
		});

		/**
		 * Default config
		 */
		cfg = MV.extend({
			url: "",
			plotConfig: {
				grid: {
					hoverable: true,
					clickable: true,
					markings: Markings
				},
				crosshair: {
					mode: "x"
				},
				xaxis: {
					mode: "time",
					//timeformat: "%Y/%m/%d"
					//timezone: "",
				},
				legend: {
					show: true,
					labelFormatter: function(label, series){
						if (series.computed)
							//hide computed plots in the legend
							return null;
						if (series.unit)
							//Add units if any
							return series.label + " ["+series.unit+"]";
						else
							//Plain label
							return series.label;
					},
					position: "sw"
				}
			},
			hover: function(event, pos, item){},
			select: function(event, pos, item){},
			
			/**
			 * Retrieve metadata
			 * format:
			 * [{
			 * 	fields: [{
			 * 		label: "",
			 * 		unit: ""
			 * 	}],
			 * 	name: ""
			 * },{
			 * 	...
			 * }]
			 */
			getMetadata: function(callback){
				$.getJSON(cfg.url + "api/charting", function(data){
					callback(data.sources);
				});
			},
			
			/**
			 * Retrieve actual data
			 * format:
			 * [{
			 * 	label: "",
			 * 	unit: "",
			 * 	data: [ {time: 0, value: 1, min: -1, max: 2}, {}, {} ],
			 * },{
			 * 	...
			 * }]
			 */
			getData: function(sourceName, from, to, maxItems, callback){
				$.getJSON(cfg.url + "api/charting/" + sourceName + "/" + from + "/" + to + "?&maxItems=" + maxItems, callback);
			}
		}, cfg);
		
		/**
		 * Enhances the metadata with ui observables
		 */
		var enhanceMetadata = function(metadata){
			metadata.forEach(function(source){
				source.fields.forEach(function(field){
					field.enabled = MV.observable(false);
					field.toggle = function(){
						field.enabled( !field.enabled() );
					};
				});
			});
		}
		
		this.html = View;
		
		/**
		 * Loading icon in update button
		 */
		this.loading = MV.observable(false);
		
		/**
		 * Available data sources
		 */
		this.sources = MV.observableArray();

		/**
		 * Plot target element
		 */
		this.plotEl = undefined;
		
		/**
		 * flot reference
		 */
		this.plot = undefined;
		
		var hoverBinding = function(event, pos, item){
			if (item){
				var x = item.datapoint[0],
					y = item.datapoint[1],
					series = item.series;
				
				self.context.hover(x, y, series);
				cfg.hover(x, y, series);
			}
		};
		
		var clickBinding = function(event, pos, item){
			if (item){
				var x = item.datapoint[0],
					y = item.datapoint[1],
					series = item.series;
				
				self.context.select(x, y, series);
				cfg.select(x, y, series);
			}
		};
		
		/**
		 * Creates the plot with given data
		 */
		var createPlot = function(data){
			if (self.plot){
				//unsubscribe from events
				self.plotEl.unbind("plothover", hoverBinding);
				self.plotEl.unbind("plotclick", clickBinding);		
				self.plot.shutdown();
				self.plotEl.html("");
			}
			
			self.plot = Plot(self.plotEl, data, cfg.plotConfig);
			
			//Subscribe to events
			self.plotEl.bind("plothover", hoverBinding);
			self.plotEl.bind("plotclick", clickBinding);		
		};
		
		this.afterRender = function($el){

			//Retrieve metadata
			cfg.getMetadata(function(md){
				//Enhance
				enhanceMetadata(md);
				
				//Push to local sources
				md.forEach(function(source){
					self.sources.push(source);
				});
			});

			//Plot element
			self.plotEl = $el.find(".plot");
			
			//Empty plot
			createPlot([]);
		
		};
		
		this.update = function(){
			
			self.loading(true);
			
			var stations = [],
				//done count
				count = 0,
				items = 0,
				results = [];
		
			var processResults = function(){

				self.loading(false);
				
				var axisMap = {},
					axisIndex = 2,
					colorIndex = 0,
					computedPlots = [];

				//Filter out disabled fields
				results = results.filter(function(plot){
					var found = false;
					self.sources().forEach(function(source){
						source.fields.forEach(function(field){
							//Check starting string
							if (field.label == plot.label && source.name == plot.source)
								found = field.enabled();
						});
					});
					return found;
				});
				

				//Preprocess plots
				results.forEach(function(plot){
					
					//Set yaxis for same units
					if (plot.unit == undefined)
						plot.yaxis = 1; //default to 1
					else {
						if (axisMap[plot.unit] == undefined)
							axisMap[plot.unit] = axisIndex++;
						plot.yaxis = axisMap[plot.unit];
					}
					
					//Add color and increment
					plot.color = colorIndex++;

					//Prefix source id
					plot.label = plot.sourceId + " " + plot.label;
					
					//Assign id
					plot.id = plot.label;
					
					//line stub
					plot.lines = {
							show: true,
							lineWidth: 1
					};
					
					if (plot.data.some(function(e){ return e.max != undefined; })){
						//Max plot
						computedPlots.push({
							color: plot.color,
							label: plot.label + " Max",
							unit: plot.unit,
							yaxis: plot.yaxis,
							id: plot.label + "-max",
							data: plot.data.map(function(e){ return [e.time, e.max]; }),
							
							//fill to average plot
							fillBetween: plot.id,
							lines: {
								show: true,
								lineWidth: 0,
								fill: 0.4
							},
							computed: {
								type: "max"
							}
						});
						
					}
					
					if (plot.data.some(function(e){ return e.min != undefined; })){
						//Min plot
						computedPlots.push({
							color: plot.color,
							label: plot.label + " Min",
							yaxis: plot.yaxis,
							unit: plot.unit,
							id: plot.label + "-min",
							data: plot.data.map(function(e){ return [e.time, e.min]; }),
							lines: {
								show: true,
								lineWidth: 0
							},
							computed: {
								type: "min"
							}
						});
						
						//Fill from average to min
						plot.fillBetween = plot.label + "-min";
						plot.lines.fill = 0.4;
					}

					//Rewrite plot data
					plot.data = plot.data.map(function(e){ return [e.time, e.value]; });
					
				});
				
				//Append computed plots
				computedPlots.forEach(function(cp){ results.push(cp); });
								
				//Update stats
				self.stat.update(results);
				
				createPlot(results);

			};
		
			//map station/source list
			self.sources().forEach(function(source){
				if (source.fields.some(function(f){ return f.enabled(); }))
						stations.push(source.name);
			});
			
			if (stations.length == 0)
				self.loading(false);
			
			stations.forEach(function(station){
				//sourceName, from, to, maxItems, callback
				cfg.getData(station, self.rangeselector.from.date().getTime(), self.rangeselector.to.date().getTime(), 1000, function(data){
					data.forEach(function(plot){
						//Assign sourceId
						plot.source = station;
						
						//Add to results
						results.push(plot);
					});
					
					//Increment and check source count
					if (++count == stations.length)
						//All sources retrieved
						processResults();
				});
			});

		};
		
	};
	
});




© 2015 - 2025 Weber Informatics LLC | Privacy Policy