1 
  2 
  3 //intialize with var data ={DATATYPE : "vq.models.FlexPlotData", CONTENTS : {DATAARRAY : data_array} };
  4 // notifier = function(x,dx) where x = position within scroll bar range,  dx = total length of window in scale of scroll bar
  5 
  6 //draw with draw (data ={DATATYPE : "vq.models.FlexPlotData", {DATAARRAY : data_array} }
  7 //				and options = {plotHeight: xx, plotWidth : xx, verticalPadding: xx, 
  8 //				horizontalPadding : xx , max_x_axis_value: xx, min_x_axis_value: xx,
  9 //				maxRange: xx, minRange: xx, dblclick_notifier : function(x,dx),
 10 //				fixedWindowWidth: xx, scaleMultiplier : xx, interval : xx, font : "fontname"}
 11 //note dblclick_notifer is the last listed option.  This can be used to create a "zoom" effect by re-instanstiating the scroll bar with new parameters.
 12 
 13 
 14 vq.ChromaVis = function() {
 15     vq.Vis.call(this);
 16 
 17     //set option variables to useful values before options are set.
 18     this.height(500);     // defaults
 19     this.width(500);     // defaults
 20     this.vertical_padding(30);
 21     this.horizontal_padding(30);
 22     this.max_x_axis_value(11);
 23     this.min_x_axis_value(0);
 24     this.context_height(50);
 25     this.max_y_axis_value(1);
 26     this.min_y_axis_value(1);
 27     this.max_y(1);
 28     this.min_y(1);
 29     this.uuid(vq.utils.VisUtils.guid());
 30 
 31 };
 32 
 33 vq.ChromaVis.prototype = pv.extend(vq.Vis);
 34 
 35 vq.ChromaVis.prototype
 36         .property('uuid',String)
 37         .property('max_x_axis_value',Number)
 38         .property('min_x_axis_value',Number)
 39         .property('max_y_axis_value',Number)
 40         .property('min_y_axis_value',Number)
 41         .property('max_y',Number)
 42         .property('min_y',Number)
 43         .property('auto_scale_y',Boolean)
 44         .property('auto_scale_x',Boolean)
 45         .property('auto_update_scale_y',Boolean)
 46         .property('context_height',Number);
 47 
 48 vq.ChromaVis.prototype.slaveTo = function(master_id) {
 49     var that = this;
 50     var slave_x = true, slave_y = true;
 51     if (arguments.length == 3) { slave_x = arguments[1]; slave_y=arguments[2];}
 52     this.slaveX = slave_x;
 53     this.slaveY = slave_y;
 54     if (!this.windowChangeHandler)  this.windowChangeHandler = {};
 55     this.windowChangeHandler[master_id] = function(obj) {that.windowchange(obj);};
 56     vq.events.Dispatcher.addListener('chromavis_windowchange',master_id,that.windowChangeHandler[master_id]);
 57 };
 58 
 59 vq.ChromaVis.prototype.unSlaveFrom =function(master_id) {
 60     var that =this;
 61     if (!that.windowChangeHandler || !that.windowChangeHandler[master_id]) { return;}
 62     vq.events.Dispatcher.removeListener('chromavis_windowchange',master_id,that.windowChangeHandler[master_id]);
 63 };
 64 
 65 vq.ChromaVis.prototype.windowchange = function(window_obj) {
 66     this.slaveRenderX = this.slaveX;
 67     this.slaveRenderY = this.slaveY;
 68     if (this.slaveRenderX) this.posX.domain(window_obj.pos.x.min,window_obj.pos.x.max);
 69     if (this.slaveRenderY) this._bl_data.yScale.domain(window_obj.pos.y.min,window_obj.pos.y.max);
 70     this.drawPanel.render();
 71     this.slaveRenderX = false;
 72     this.slaveRenderY = false;
 73 };
 74 
 75 vq.ChromaVis.prototype._setOptionDefaults =  function(options) {
 76 
 77     if (options.max_x_axis_value != null) { this.max_x_axis_value(options.max_x_axis_value); }
 78 
 79     if (options.min_x_axis_value != null) { this.min_x_axis_value(options.min_x_axis_value); }
 80 
 81     if (options.context_height != null) { this.context_height(options.context_height); }
 82 
 83     if (options.auto_scale_y != null) { this.auto_scale_y(options.auto_scale_y); }
 84     if (options.auto_scale_x != null) { this.auto_scale_x(options.auto_scale_x); }
 85 
 86     if (options.auto_update_scale_y != null) { this.auto_update_scale_y(options.auto_update_scale_y); }
 87 
 88     if (options.max_y_axis_value != null) { this.max_y_axis_value(options.max_y_axis_value); }
 89     if (options.min_y_axis_value != null) { this.min_y_axis_value(options.min_y_axis_value); }
 90 
 91     if (options.height != null) { this.height(options.height); }
 92 
 93     if (options.width != null) { this.width(options.width); }
 94 
 95     if (options.container) { this.container(options.container); }
 96 
 97     if (options.vertical_padding != null) { this.vertical_padding(options.vertical_padding); }
 98 
 99     if (options.horizontal_padding != null) { this.horizontal_padding(options.horizontal_padding); }
100 
101 };
102 
103 vq.ChromaVis.prototype.draw = function(data) {
104     var that = this;
105     this._bl_data = new vq.models.ChromaVisData(data);
106     if (this._bl_data.isDataReady()) {
107         this._setOptionDefaults(that._bl_data);
108         this._render();
109     }
110 };
111 
112 vq.ChromaVis.prototype._render = function() {
113 
114     var that = this;
115     var dataObj = this._bl_data;
116 
117     var x_label;
118     var left_margin = 55;
119 
120     this.show_vals = false;
121     this.current_vals = {};
122     that.mouse_x = null;
123     that.slaveRenderX = false;
124     that.slaveRenderY = false;
125 
126     this.visibleWidth = (this.width() - 2 * this.horizontal_padding() - dataObj.legend_width) - left_margin;  //40 is for y-axis label
127     this.visibleHeight = (this.height() - this.vertical_padding() * 2);
128     this.focus_height = this.visibleHeight - this.context_height();
129     this.posX =  pv.Scale.linear().range(0,that.visibleWidth);
130 	this.min_x = pv.min(dataObj.data_array,function(a) { return pv.min(a[dataObj.data_contents_id],function(b) {return b[dataObj.x_column_id];});});
131 	this.max_x = pv.max(dataObj.data_array,function(a) { return pv.max(a[dataObj.data_contents_id],function(b) {return b[dataObj.x_column_id];});});
132 
133         this.max_y(pv.max(dataObj.data_array,function(a) { return pv.max(a[dataObj.data_contents_id],function(b) {return b[dataObj.y_column_id];});}));
134 
135     var  min_x =  that.auto_scale_x()  ? this.min_x :
136             that.min_x_axis_value(),
137          max_x =  that.auto_scale_x()  ? this.max_x :
138             that.max_x_axis_value();
139 
140     that.min_x_axis_value(min_x);
141     that.max_x_axis_value(max_x);
142 
143 
144     this.context_posX = pv.Scale.linear(that.min_x_axis_value(), that.max_x_axis_value()).range(0,that.visibleWidth);
145     that.window = {x:that.context_posX.range()[0],dx:(that.context_posX.range()[1] - that.context_posX.range()[0]) *.6};
146     that.posX.domain(that.context_posX.invert(that.window.x),that.context_posX.invert(that.window.x + that.window.dx));
147     that.focus_window = {x:0,dx:0};
148 
149     function update_vals() {
150         var index = -1;
151          that.show_vals = true;
152          that.mouse_x = that.posX.invert(tracks_panel.mouse().x);
153          dataObj.data_array.forEach(function(line) {
154             index = pv.search(line[dataObj.data_contents_id].map(function(point) { return point[dataObj.x_column_id];}),that.mouse_x);
155              index = index < 0 ? (-index -2) : index;
156              if (index < 0) { that.show_vals = false;}
157             that.current_vals[line[dataObj.data_label]]= index < 0 ? 0 : line[dataObj.data_contents_id][index][dataObj.y_column_id];
158          });
159         x_label.render();
160         if (bubble) {
161             bubble.render();
162         }
163         return legend;
164     }
165 
166     function remove_vals() { that.show_vals = false; that.mouse_x = null; x_label.render(); return legend;}
167 
168    
169  var     x = pv.Scale.linear(that.min_x_axis_value(),that.max_x_axis_value())
170             .range(0,that.visibleWidth),
171             scale_height = this.focus_height - 4,
172              min_val = that.min_y_axis_value(),
173             max_val  = that.auto_scale_y()  ? this.max_y() :
174                      this.max_y_axis_value(),
175             yScale = pv.Scale.linear(min_val,max_val ).range(0,scale_height);
176     dataObj.context_yScale = pv.Scale.linear(min_val,max_val ).range(0,that.context_height()-1);
177 
178     this.max_y_axis_value(max_val);
179     this.min_y_axis_value(min_val);
180 
181     if (dataObj.yaxis_scale_type != undefined && dataObj.yaxis_scale_type != null &&
182             dataObj.yaxis_scale_type == 'log') {
183         min_val = min_val > 0 ? min_val : 1;
184         dataObj.base_value = dataObj.base_value > 0
185                 ? dataObj.base_value : min_val;
186         yScale = pv.Scale.log(min_val,max_val ).range(0,scale_height).nice();
187         dataObj.context_yScale =  pv.Scale.log(min_val,max_val ).range(0,that.context_height()-1).nice();
188         yScale.ticks = log_ticks(yScale.domain());
189     }
190 
191     dataObj.yScale = yScale;
192 
193     var log_ticks = function(domain) {
194         var b = 10,
195                 p = Math.log(b),
196                 log = function(x) { return Math.log(x) / p; },
197                 pow = function(y) { return Math.pow(b, y); };
198         n = domain[0] < 0,
199                 i = Math.floor(n ? -log(-domain[0]) : log(domain[0])),
200                 j = Math.ceil(n ? -log(-domain[1]) : log(domain[1]));
201         return function() { return pv.range(i,j+1,1).map(pow);};};
202 
203     var dd =   vq.utils.VisUtils.clone(dataObj.data_array);
204     //array of array of  timestamps
205     var contents = dd.map(function(a) { return a[dataObj.data_contents_id].map(function(b) { return b[dataObj.x_column_id];});});
206 
207     var init =  function() {
208 
209         var d1,d2;
210           if (!that.slaveRenderX) {
211             d1 = that.context_posX.invert(that.window.x),
212                 d2 =  that.context_posX.invert(that.window.x + that.window.dx),
213                 that.posX.domain(d1, d2);
214         }
215         else {
216             d1 =that.posX.domain()[0];
217             d2 =that.posX.domain()[1];
218             that.window.x =that.context_posX(d1);
219             that.window.dx = that.context_posX(d2) - that.window.x;
220         }
221         //find the values just less than the minimum displayed x-axis value
222         //find the data line with the smallest of these values (how low to plot from)
223         var min_val = pv.min(
224             pv.blend(
225                 contents.map(function(a) {
226                     return pv.max(
227                         a.filter(function(d) {
228                             return d < d1;}));})));
229 
230         var max_val = pv.max(
231             pv.blend(
232                 contents.map(function(a) {
233                     return pv.min(
234                         a.filter(function(d) {
235                             return d > d2;}));})));
236 
237         var data = dd.map(function(line) {
238             var obj = {};
239             obj[dataObj.data_label] = line[dataObj.data_label];
240             obj[dataObj.data_contents_id] =
241                 line[dataObj.data_contents_id].filter(function(d){
242                     return (d[dataObj.x_column_id] <= max_val && d[dataObj.x_column_id] >= min_val);
243                 });
244             return obj;
245         });
246         if(!that.slaveRenderY) {
247             if (that.auto_update_scale_y()) {
248                 var y_max_val  =
249                     pv.max(data,function(a) { return pv.max(a[dataObj.data_contents_id],function(b) {return b[dataObj.y_column_id];});});
250                 var domain = dataObj.yScale.domain();
251                 dataObj.yScale.domain(domain[0],y_max_val);
252             } else {
253                 dataObj.yScale.domain(that.min_y_axis_value(),that.max_y_axis_value());
254             }
255         }
256 
257        return data;
258     };
259 
260     var vis = new pv.Panel()
261             .top(that.vertical_padding())
262             .bottom(that.vertical_padding())
263             .left(that.horizontal_padding())
264             .right(that.horizontal_padding())
265             .width(that.width())
266             .height(that.height())
267             .fillStyle(null)
268             .canvas(that.container());
269 
270     var wholePanel = vis.add(pv.Panel)
271             .left(0)
272             .top(0);
273 
274     var drawPanel = wholePanel.add(pv.Panel)
275                             .width(that.visibleWidth)
276                            .left(left_margin);
277     this.drawPanel = drawPanel;
278 
279     var   y_axis_label=wholePanel.add(pv.Panel)
280        .top(that.focus_height/3)
281        .height(that.focus_height/3)
282        .left(0)
283        .width(10);
284 
285     var focus = drawPanel.add(pv.Panel)
286             .top(0)
287             .left(0)
288             .height(that.focus_height);
289     var focus_click_panel = focus.add(pv.Panel);
290 
291     var plot = focus.add(pv.Panel)
292             .bottom(0)
293             .overflow("hidden");
294 
295     focus.add(pv.Rule)
296             .left(0)
297             .add(pv.Rule)
298             .data(function() { return that.posX.ticks();} )
299             .left(that.posX)
300             .strokeStyle("#888")
301             .events('none')
302             .anchor("bottom").add(pv.Label)
303             .text(that.posX.tickFormat);
304 
305      x_label = focus.add(pv.Panel);
306     var x_rule = x_label.add(pv.Rule)
307             .visible(function() { return that.mouse_x;})
308             .bottom(-10)
309             .height(10)
310             .strokeStyle("#888")
311             .events('none')
312             .left(function() { return that.posX(that.mouse_x);});
313     x_rule.anchor("bottom").add(pv.Label)
314             .text(function() { return that.mouse_x.toFixed(2);})
315             .font('4pt');
316 
317     if (dataObj.vertical_marker_array.length > 0) {
318           var marker_panel = focus.add(pv.Panel)
319                   .overflow("hidden")
320                   .data(dataObj.vertical_marker_array);
321        marker_panel.add(pv.Bar)
322                        .lineWidth(2)
323                        .left(function(marker) { return that.posX(marker.value);})
324                        .fillStyle('rgba(240,0,0,0.6)')
325                        .strokeStyle('rgba(240,0,0,0.6)')
326                        .width(1)
327                   .anchor('left').add(pv.Label)
328                       .font('10px sans-serif')
329                       .textAngle(-Math.PI/2)
330                       .textAlign('center')
331                       .textBaseline('bottom')
332                       .textStyle('black')
333                       .text(function(marker){return marker.id;});
334       }
335 
336     var tracks_panel = plot.add(pv.Panel)
337             .data(function() { return init();})
338             .left(0)
339             .width(that.visibleWidth);
340 
341 
342     var strokeStyle = dataObj.strokeStyle,
343             lineWidth  = dataObj.lineWidth,
344             item = tracks_panel.add(pv.Line)
345                     .data(function(d){ return d[dataObj.data_contents_id];})
346                     .left(function(c) { return that.posX(c[dataObj.x_column_id]);})
347                     .lineWidth(lineWidth)
348                     .bottom(function(c){ return dataObj.yScale(c[dataObj.y_column_id]);})
349                     .strokeStyle(function() {return strokeStyle.call(this.parent);});
350 
351 
352     if (dataObj.notifier != undefined){
353         item.cursor('pointer')
354                 .event('click',function(c,d){ dataObj.notifier(c,d); });
355     }
356 
357     if (dataObj.legend_width){
358            item
359                    .event('mousemove',update_vals)
360                    .event('mouseout',function() { that.show_vals = false; that.mouse_x = null; return legend;});
361     }
362 
363     //only plot base_value ruler line if it falls in the current range of y-axis values
364     if (dataObj.yScale.domain()[0] <= dataObj.base_value &&
365             dataObj.yScale.domain()[1] >= dataObj.base_value) {
366         focus.add(pv.Rule)
367                 .bottom(function() { return dataObj.yScale(dataObj.base_value);});
368     } else { // otherwise put the ruler along the bottom.
369         focus.add(pv.Rule)
370                 .bottom(0);
371     }
372     focus.add(pv.Rule)
373             .data(function() { return dataObj.yScale.ticks(5);})
374             .bottom(function(c) {return dataObj.yScale(c);})
375             .strokeStyle('#222')
376             .width(10)
377             .left(0)
378             .anchor('left').add(pv.Label)
379             .font('4pt')
380             .text(function(c,d) {return dataObj.yScale.tickFormat(c);});
381 
382     y_axis_label.add(pv.Label)
383     .textAngle(-Math.PI/2)
384     .font('4pt')
385     .textAlign('center')
386     .textBaseline('middle')
387     .text(dataObj.y_axis_label);
388 
389     var x_axis_label = drawPanel.add(pv.Panel)
390         .bottom(that.context_height())
391         .height(15)
392         .left(0)
393         .width(that.visibleWidth)
394     .anchor('center').add(pv.Label)
395         .text(dataObj.x_axis_label)
396         .font('4pt')
397         .textBaseline('middle');
398 
399 
400     /* Context panel (zoomed out). */
401     var context = drawPanel.add(pv.Panel)
402             .bottom(0)
403             .width(that.visibleWidth)
404             .left(0)
405             .height(that.context_height());
406     var context_panel = context.add(pv.Panel)
407             .bottom(0);
408 
409     if (dataObj.vertical_marker_array.length > 0) {
410           var context_marker_panel = context_panel.add(pv.Panel)
411                       .data(dataObj.vertical_marker_array);
412           context_marker_panel.add(pv.Bar)
413                       .lineWidth(2)
414                       .fillStyle('rgba(240,0,0,0.5)')
415                       .left(function(marker) { return x(marker.value);})
416                       .fillStyle('rgba(240,0,0,0.5)')
417                       .width(1)
418 
419       }
420 
421     var context_track = context_panel.add(pv.Panel)
422             .data(function(c) { return dataObj.data_array;})
423             .overflow('hidden')
424             .left(0);
425     context_track.add(pv.Line)
426             .data(function(d){ return d[dataObj.data_contents_id];})
427             .left(function(c) { return x(c[dataObj.x_column_id]);})
428             .bottom(function(c){ return dataObj.context_yScale(c[dataObj.y_column_id]);})
429             .lineWidth(.5)
430             .antialias(true)
431             .strokeStyle(function() {return strokeStyle.call(this.parent);});
432 
433     context.add(pv.Rule)
434             .left(0)
435         /* X-axis ticks. */
436             .add(pv.Rule)
437             .data(that.context_posX.ticks())
438             .left(that.context_posX)
439             .strokeStyle("#888")
440             .anchor("bottom").add(pv.Label)
441             .text(that.context_posX.tickFormat);
442 
443     /* Y-axis ticks. */
444     context.add(pv.Rule)
445             .bottom(0);
446 
447 function render() {
448                 drawPanel.render();
449 }
450 
451     function renderAndDispatch() {
452         if (that.window.dx < 2) { return; }
453         render();
454         if(!dataObj.dispatch_events) { return; }
455          vq.events.Dispatcher.dispatch(
456                 new vq.events.Event('chromavis_windowchange',that.uuid(),{
457                     pos: {
458                         x:{min:that.context_posX.invert(that.window.x),max:that.context_posX.invert(that.window.dx +that.window.x) },
459                         y:{min: dataObj.yScale.domain()[0], max : dataObj.yScale.domain()[1]}        }
460         }));
461     }
462 
463     /* The selectable, draggable focus region. */
464     context.add(pv.Panel)
465             .data(function() {return [that.window];})
466             .cursor("crosshair")
467             .events("all")
468             .event("mousedown", pv.Behavior.select())
469             .event("select", function() {
470                     render();
471                 })
472             .event("selectend", function() {
473             renderAndDispatch();
474         })
475         .add(pv.Bar)
476         .left(function(d) { return d.x;})
477          .width(function(d) {return d.dx;})
478          .fillStyle("rgba(255, 128, 128, .4)")
479          .cursor("move")
480          .event("mousedown", pv.Behavior.drag())
481          .event("drag", function() {
482                 render();
483              })
484          .event("dragend", function() {
485                 renderAndDispatch();
486              });
487 
488     /* The selectable, draggable focus region. */
489     focus_click_panel
490             .data(function() { return [that.focus_window];})
491             .cursor("crosshair")
492             .events("all")
493             .event("mousedown", pv.Behavior.select())
494             .event("selectend", function() {
495                     if (that.focus_window.dx < 2) { that.focus_window = {x:0,dx:0}; focus.render();
496                         context.render();return; }
497                         that.window ={x: that.context_posX(that.posX.invert(that.focus_window.x)),
498                             dx: that.context_posX(that.posX.invert(that.focus_window.dx)) -
499                                     that.context_posX(that.posX.invert(0))};
500                             if (dataObj.dispatch_events) {
501                                 vq.events.Dispatcher.dispatch(
502                                 new vq.events.Event('chromavis_windowchange',that.uuid(),{
503                                     pos: {
504                                         x:{min:that.posX.invert(that.focus_window.x),max:that.posX.invert(that.focus_window.dx+that.focus_window.x)},
505                                         y:{min: dataObj.yScale.domain()[0], max : dataObj.yScale.domain()[1]}        }
506 
507                                 }));
508                             }
509                         that.focus_window = {x:0,dx:0};
510                     drawPanel.render();
511         })
512             .add(pv.Bar)
513             .left(function(d) { return d.x;})
514             .width(function(d) {return d.dx;})
515             .fillStyle("rgba(128, 128, 255, .4)");
516 
517     var legend_height = 0;
518 
519     if (dataObj.legend_width){
520         legend_height = 12 * dataObj.data_array.length;
521 
522         focus_click_panel
523                 .event('mousemove',update_vals)
524                 .event('mouseout',remove_vals);
525 
526         var legend = drawPanel.add(pv.Panel)
527                 .left(that.visibleWidth)
528                 .width(dataObj.legend_width)
529                 .top(10)
530                 .height(legend_height);
531         var legend_item = legend.add(pv.Panel)
532                 .data(dataObj.data_array)
533                 .top(function(c) { return this.index * 12;})
534                 .height(10);
535 
536         legend_item.add(pv.Bar)
537                 .left(2)
538                 .width(10)
539                 .height(2)
540                 .top(2)
541                 .strokeStyle(function() {return strokeStyle.call(this.parent);})
542        .anchor('right').add(pv.Label)
543                 .text(function(c) { return c[dataObj.data_label] + (that.show_vals ? ' : ' + (that.current_vals[c[dataObj.data_label]]).toFixed(3) : '');})
544                 .textAlign('left')
545                 .font("10px monospace");
546 
547     }
548 
549 if (dataObj.data_array[0][dataObj.eri_id] != undefined && !isNaN(dataObj.data_array[0][dataObj.eri_id])) {
550 
551     var new_vals = pv.normalize(dataObj.data_array, function(val){ return val.eri;});
552 
553     var norm_eri_ordered = dataObj.data_array.map(function(data,index) { return {eri:new_vals[index],label:data[dataObj.data_label]};})
554                    .sort(function(a,b) {
555                            if(a.eri > b.eri) { return 1;}
556                             else if(a.eri < b.eri) { return 1;}
557                             else { return 0;}});
558 
559     function update_bubble() {
560         if(!that.show_vals) { return [50];}
561         var mean_square = pv.normalize(norm_eri_ordered.map(function(val) { return that.current_vals[val.label];}))
562                         .reduce(function(prev,curr,index,arr){
563                             return prev +
564                                             (norm_eri_ordered[index][dataObj.eri_id]  - curr) *
565                                             (norm_eri_ordered[index][dataObj.eri_id] - curr);},0);
566         return [Math.sqrt(mean_square) || 0];
567     }
568     var bubble;
569     var eri_gauge_height = 0;
570     if(dataObj.eri_gauge) {
571         eri_gauge_height = 20;
572         var eri_gauge_width = 70;
573         var gauge_color_scale = pv.Scale.quantitative(0,dataObj.eri_bubble_max).range('#2f2','#f22');
574         var gauge_xscale = pv.Scale.linear(0,dataObj.eri_bubble_max).range(0,eri_gauge_width);
575          var eri_gauge = drawPanel.add(pv.Panel)
576                     .left(that.visibleWidth + 5)
577                     .width(80)
578                     .top(legend_height + 5)
579                     .height(eri_gauge_height);
580 
581         var gauge_line = eri_gauge.add(pv.Bar)
582                     .bottom(9)
583                     .width(eri_gauge_width)
584                     .height(2)
585                     .left(10)
586                     .strokeStyle('#333')
587                     .lineWidth(1);
588            eri_gauge.add(pv.Label)
589                 .bottom(-10)
590                 .left(3)
591               .text('Prediction Match');
592 
593         eri_gauge.add(pv.Rule)
594                             .left(10)
595                             .height(8)
596                             .bottom(6)
597                             .strokeStyle('#333')
598                             .lineWidth(2);
599 
600         eri_gauge.add(pv.Rule)
601                       .left(eri_gauge_width+10)
602                       .height(8)
603                       .bottom(6)
604                       .strokeStyle('#333')
605                       .lineWidth(2);
606 
607         var line_panel = eri_gauge.add(pv.Panel)
608                           .bottom(10)
609                           .width(eri_gauge_width)
610                           .height(2)
611                           .left(10)
612                           .strokeStyle(null);
613 
614         bubble = line_panel.add(pv.Dot)
615                     .data(function() {return update_bubble();})
616                     .shape('circle')
617                     .radius(4)
618                     .strokeStyle('#333')
619                     .lineWidth(1)
620                 .fillStyle(function(c) { return gauge_color_scale(Math.min(dataObj.eri_bubble_max,c));})
621                 .bottom(0)
622                 .left(function(c) { return gauge_xscale(Math.min(dataObj.eri_bubble_max,c));});
623 }
624 
625         var top_height =  (legend_height + 15 + eri_gauge_height);
626         var height = that.focus_height - top_height;
627         function gaussian(val,eri) {
628             var exp = -1* Math.pow((val),2) / 2;
629             return  eri * Math.exp(exp);
630         }
631 
632         var line = pv.range(-1,1.01,0.01);
633         var x_axis = pv.Scale.linear(-1,1).range(0,80);
634 
635           var expected = dataObj.data_array.map(function(line) {
636             return isNaN(line[dataObj.eri_id]) ? 0 : line[dataObj.eri_id];   });
637         var max_eri = pv.max(expected);
638         var y_axis = pv.Scale.linear(0,max_eri).range(0,height -5);
639 
640         var eri = drawPanel.add(pv.Panel)
641                     .left(that.visibleWidth + 5)
642                     .width(80)
643                     .top(top_height)
644                     .height(height);
645 
646         eri.add(pv.Panel)
647                 .data(expected)
648                 .top(1)
649                 .strokeStyle('black')
650                 .lineWidth(1)
651             .add(pv.Line)
652                 .data(line)
653                 .bottom(function(c,d) { return y_axis(gaussian(c,d));})
654                 .left(x_axis)
655                 .strokeStyle(function() { return strokeStyle.call(this.parent);})
656                 .lineWidth(dataObj.lineWidth);
657     }
658 
659     vis.render();
660 
661 };
662 
663 
664 vq.models.ChromaVisData = function(data) {
665     vq.models.VisData.call(this,data);
666     this._setDataModel();
667     if (this.getDataType() == 'vq.models.ChromaVisData') {
668         this._build_data(this.getContents());
669     } else {
670         console.warn('Unrecognized JSON object.  Expected vq.models.ChromaVisData object.');
671     }
672 };
673 vq.models.ChromaVisData.prototype = pv.extend(vq.models.VisData);
674 
675 vq.models.ChromaVisData.prototype._build_data = function(data) {
676 
677     this._processData(data);
678     this.setDataReady(true);
679 };
680 
681 
682 vq.models.ChromaVisData.prototype._setDataModel = function() {
683     this._dataModel = [
684         {label: 'width', id: 'PLOT.width', cast : Number, defaultValue: 500},
685         {label: 'height', id: 'PLOT.height', cast : Number, defaultValue: 500},
686         {label : 'container', id:'PLOT.container', optional : true},
687         {label: 'vertical_padding', id: 'PLOT.vertical_padding', cast : Number, defaultValue: 30},
688         {label: 'horizontal_padding', id: 'PLOT.horizontal_padding',cast : Number,  defaultValue: 30},
689         {label: 'min_x_axis_value', id: 'PLOT.min_x_axis_value', cast : Number, defaultValue: 0},
690         {label: 'max_x_axis_value', id: 'PLOT.max_x_axis_value',cast : Number,  defaultValue: 100},
691         {label: 'max_y_axis_value', id: 'PLOT.max_y_axis_value',cast : Number,  defaultValue : 1},
692         {label: 'min_y_axis_value', id: 'PLOT.min_y_axis_value',cast : Number,  defaultValue : 0},
693         {label: 'context_height', id: 'PLOT.context_height',cast : Number,  defaultValue: 50},
694         {label: 'legend_width', id: 'PLOT.legend_width',cast : Number,  defaultValue : 0},
695         {label: 'auto_scale_y', id: 'PLOT.auto_scale_y',cast : Boolean,  defaultValue: false},
696         {label: 'auto_update_scale_y', id: 'PLOT.auto_update_scale_y',cast : Boolean,  defaultValue: false},
697         {label : 'auto_scale_x', id: 'PLOT.auto_scale_x',cast : Boolean,  defaultValue: false},
698         {label : 'label', id: 'label', cast: String, defaultValue : '' },
699         {label : 'type', id: 'type', cast: String, defaultValue : 'line' },
700         {label : 'x_column_id' , id: 'x_column_id', cast : String, defaultValue : 'x'},
701         {label : 'y_column_id' , id: 'y_column_id', cast : String, defaultValue : 'y'},
702         {label : 'eri_id' , id: 'eri_id', cast : String, defaultValue : 'eri'},
703          {label : 'eri_gauge' , id: 'PLOT.eri_gauge', cast : Boolean, defaultValue : false},
704         {label : 'y_axis_label' , id: 'y_axis_label', cast : String, defaultValue : 'Intensity'},
705         {label : 'x_axis_label' , id: 'x_axis_label', cast : String, defaultValue : 'Time(ms)'},
706         {label : 'strokeStyle', id: 'stroke_style', cast: vq.utils.VisUtils.wrapProperty, defaultValue : pv.Colors.category10() },
707         {label : 'lineWidth', id: 'line_width', cast: vq.utils.VisUtils.wrapProperty, defaultValue : vq.utils.VisUtils.wrapProperty(1) },
708         {label : 'base_value', id: 'base_value', cast: Number, defaultValue : 0 },
709         {label : 'yaxis_scale_type', id: 'yaxis_scale_type', cast: String, defaultValue : 'linear' },
710         {label : 'eri_bubble_max', id:'eri_bubble_max', cast : Number, defaultValue : 0.1},
711         {label : 'notifier', id: 'notifier', cast: Function, optional : true },
712         {label : 'tooltipItems', id: 'tooltip_items', defaultValue :
713         {X : 'x' , Value : 'y'} },
714         {label : 'dispatch_events', id:'dispatch_events',cast: Boolean, defaultValue: true},
715         {label : 'data_array', id: 'data_array', defaultValue : [] },
716         {label : 'vertical_marker_array', id:'vertical_marker_array', cast: Object, defaulValue: [] },
717         {label : 'data_label', id: 'data_label', defaultValue : 'label'},
718         {label : 'data_contents_id', id : 'data_contents_id', defaultValue : 'data'}
719     ];
720 };
721 
722