1 
  2 
  3 vq.LinearBrowser = function() {
  4       vq.Vis.call(this);
  5         //set option variables to useful values before options are set.
  6     this.height(40);     // defaults
  7     this.width(600);     // defaults
  8     this.vertical_padding(10);
  9     this.horizontal_padding(10);
 10     this.max_position(100);
 11     this.min_position(0);
 12     this.context_height(50);
 13 };
 14 
 15 vq.LinearBrowser.prototype = pv.extend(vq.Vis);
 16 
 17 vq.LinearBrowser.prototype
 18     .property('max_position',Number)
 19     .property('min_position',Number)
 20     .property('context_height',Number);
 21 
 22 vq.LinearBrowser.prototype._setOptionDefaults = function(options) {
 23 
 24     if (options.max_position != null) { this.max_position(options.max_position); }
 25 
 26     if (options.min_position != null) { this.min_position(options.min_position); }
 27 
 28     if (options.context_height != null) { this.context_height(options.context_height); }
 29 
 30     if (options.height != null) { this.height(options.height); }
 31 
 32     if (options.width != null) { this.width(options.width); }
 33 
 34     if (options.container) { this.container(options.container); }
 35 
 36     if (options.vertical_padding != null) { this.vertical_padding(options.vertical_padding); }
 37 
 38     if (options.horizontal_padding != null) { this.horizontal_padding(options.horizontal_padding); }
 39 
 40 };
 41 
 42 vq.LinearBrowser.prototype.setFocusRange = function(start,dx) {
 43         var range_start = Math.max(this.min_position(),start);
 44         var range_size = Math.min(this.max_position() - range_start,dx);
 45         this.window ={x: this.context_posX(range_start),
 46             dx: this.context_posX(range_size) - this.context_posX(0)};
 47         this.focus_window = {x:0,dx:0};
 48         this.drawPanel.render();
 49     };
 50 
 51 vq.LinearBrowser.prototype.getFocusRange = function() {
 52         var focal_range = this.posX.domain();
 53         return {x:focal_range[0],dx:focal_range[1]-focal_range[0]};
 54     };
 55 
 56 vq.LinearBrowser.prototype.draw = function(data) {
 57 
 58     this._li_br_data = new vq.models.LinearBrowserData(data);
 59 
 60         if (this._li_br_data.isDataReady()) {
 61             this._setOptionDefaults(this._li_br_data);
 62             this._render();
 63         }
 64     };
 65 
 66 vq.LinearBrowser.prototype._render = function() {
 67         var that = this;
 68         var dataObj = this._li_br_data;
 69 
 70         this.visibleWidth = (this.width() - 2 * this.horizontal_padding());
 71         this.visibleHeight = (this.height() - this.vertical_padding() * 2);
 72         this.focus_height = this.visibleHeight - this.context_height();
 73         this.posX =  pv.Scale.linear().range(0,that.visibleWidth);
 74             this.context_posX = pv.Scale.linear(that.min_position(), that.max_position()).range(0,that.visibleWidth);
 75 
 76         that.window = {x:that.context_posX.range()[0]*.2,dx:(that.context_posX.range()[1] - that.context_posX.range()[0]) *.6};
 77         that.posX.domain(that.context_posX.invert(that.window.x),that.context_posX.invert(that.window.x + that.window.dx));
 78         that.focus_window = {x:0,dx:0};
 79 
 80         var x = pv.Scale.linear(that.min_position(),that.max_position())
 81                 .range(0,that.visibleWidth);
 82 
 83         var context_scale = that.context_height() / that.focus_height;
 84 
 85         var log_ticks = function(domain) {
 86                            var b = 10,
 87                            p = Math.log(b),
 88                         log = function(x) { return Math.log(x) / p; },
 89                         pow = function(y) { return Math.pow(b, y); };
 90                         n = domain[0] < 0,
 91                         i = Math.floor(n ? -log(-domain[0]) : log(domain[0])),
 92                         j = Math.ceil(n ? -log(-domain[1]) : log(domain[1]));
 93                            return function() { return pv.range(i,j+1,1).map(pow);};};
 94 
 95         var init =  function(index) {
 96 
 97             var d1 = that.context_posX.invert(that.window.x),
 98                     d2 = that.context_posX.invert(that.window.x + that.window.dx),
 99                     dd =   dataObj.tracks[index].data_array.map(function(datum) { return datum;});
100                        var min_val = pv.max(dd.filter(function(d) { return d.start < d1;}),function(e) { return e.start;});
101             var max_val = pv.min(dd.filter(function(d) { return d.start > d2;}),function(e) { return e.start;});
102             that.posX.domain(d1, d2);
103             dd = dataObj.tracks[index].type =='tile' ? dd.filter(function(d) { return d.start <= d2 || d.end >= d1;})
104             : dd.filter(function(d){ return (d.start <= max_val && d.start >= min_val); } );
105             return dd;
106         };
107 
108         var vis = new pv.Panel()
109                 .top(that.vertical_padding())
110                 .bottom(that.vertical_padding())
111                 .left(that.horizontal_padding())
112                 .right(that.horizontal_padding())
113                 .width(that.width())
114                 .height(that.height())
115                 .fillStyle(null)
116                 .canvas(that.container());
117 
118         var drawPanel = vis.add(pv.Panel)
119                 .width(that.visibleWidth);
120 
121   	this.drawPanel = drawPanel;
122         
123         var focus = drawPanel.add(pv.Panel)
124                 .top(0)
125                 .height(that.focus_height);
126                 
127         var bg_plot = focus.add(pv.Panel)
128                 .bottom(0);  
129         
130         var focus_click_panel = focus.add(pv.Panel);
131                 
132         var plot = focus.add(pv.Panel)
133                 .bottom(0);
134                         
135             focus.add(pv.Rule)
136                 .left(0)
137               .add(pv.Rule)
138                 .data(function() { return that.posX.ticks();} )
139                 .left(that.posX)
140                 .strokeStyle("#888")
141               .anchor("bottom").add(pv.Label)
142                 .text(that.posX.tickFormat);
143             
144         var bg_panel = bg_plot.add(pv.Panel)
145                 .left(0)
146                 .width(that.visibleWidth);
147                         
148         var tracks_panel = plot.add(pv.Panel)
149                 .left(0)
150                 .fillStyle(null)
151                 .strokeStyle(null)
152 	        .width(that.visibleWidth);
153         var track_height;
154 
155     for(var index = 0; index < dataObj.tracks.length; index++) {
156 
157         var fillStyle = dataObj.tracks[index].fillStyle,
158                 strokeStyle = dataObj.tracks[index].strokeStyle,
159                 lineWidth  = dataObj.tracks[index].lineWidth;
160         track_height = dataObj.tracks[index].track_height;
161 
162     var bg_index = bg_panel.add(pv.Panel)
163                 .data([index]);
164 
165         var bg_track = bg_index.add(pv.Panel)
166                 .data(function(c) { return [dataObj.tracks[c]];})
167                 .bottom(function(c,index){
168             return pv.sum(  dataObj.tracks.slice(0,index),function(d) {
169                 return d.track_height+d.track_padding; })})
170                         .fillStyle(function(c) { return c.trackFillStyle();})
171                         .strokeStyle(function(c) { return c.trackStrokeStyle();})
172                         .lineWidth(function(c) { return c.trackLineWidth();})
173                 .height(function(c) { return c.track_height+c.track_padding;});
174                 
175         var track_index = tracks_panel.add(pv.Panel)
176                 .data([index]);
177         var track = track_index.add(pv.Panel)
178                 .data(function(c) { return [dataObj.tracks[c]];})
179                 .bottom(function(c,index){
180             return pv.sum(  dataObj.tracks.slice(0,index),function(d) {
181                 return d.track_height+d.track_padding; })})
182                 .height(function(c) { return c.track_height+c.track_padding;});
183 
184         var yScale, min_val,max_val,plot_track,item,shape,radius;
185 	var num_y_rule_lines = dataObj.tracks[index].num_y_rule_lines;
186 
187                 plot_track = track.add(pv.Panel)
188                         .height(track_height)
189 			.bottom(2)
190                         .events('painted')
191 			.overflow('hidden');
192 
193         var value_key = dataObj.tracks[index].value_key;
194 			
195 
196         switch (dataObj.tracks[index].type){
197 
198             case 'scatter':
199             case 'scatterplot':
200                  shape = dataObj.tracks[index].shape;
201                 radius = dataObj.tracks[index].radius;
202                 min_val = dataObj.tracks[index].min_value != undefined ? dataObj.tracks[index].min_value :
203                         pv.min(dataObj.tracks[index].data_array,function(a) { return a[value_key];});
204                 max_val  = dataObj.tracks[index].max_value != undefined ? dataObj.tracks[index].max_value :
205                         pv.max(dataObj.tracks[index].data_array,function(a) { return a[value_key];});
206                 yScale = pv.Scale.linear(min_val,max_val ).range(0,track_height);
207                 if (dataObj.tracks[index].yaxis_scale_type != undefined && dataObj.tracks[index].yaxis_scale_type != null &&
208                         dataObj.tracks[index].yaxis_scale_type == 'log') {
209                     min_val = min_val > 0 ? min_val : 1;
210                     dataObj.tracks[index].base_value = dataObj.tracks[index].base_value > 0
211                             ? dataObj.tracks[index].base_value : min_val;
212                     yScale = pv.Scale.log(min_val,max_val ).range(0,track_height).nice();
213 
214                     yScale.ticks = log_ticks(yScale.domain());
215                 }
216 
217                 dataObj.tracks[index].yScale = yScale;
218 
219                 item = plot_track.add(pv.Dot)
220                         .data(function(d,obj,index){ return init(index);})
221                         .left(function(c) { return that.posX(c.start);})
222                         .lineWidth(lineWidth)
223                         .bottom(function(c,d){ return d.yScale(c[value_key]);})
224                         .fillStyle(fillStyle)
225                         .shape(shape)
226                         .radius(radius)
227                         .strokeStyle(strokeStyle);
228 
229                     item.event('mouseover',
230                         pv.Behavior.hovercard(
231                             {
232                                 include_header : false,
233                                 include_footer : true,
234                                 self_hover : true,
235                                 timeout : dataObj.tracks[index]._tooltip_timeout,
236                                 data_config :
237                                     dataObj.tracks[index]._tooltipItems,
238                                 tool_config : dataObj.tracks[index]._tooltipLinks
239                             }
240                         ));
241 
242                 if (dataObj.tracks[index].notifier != undefined){
243                     item
244                             .cursor('pointer')
245                             .event('click',function(c,d){ d.notifier(c,d); });
246                 }
247                 track.add(pv.Rule)
248                         .bottom(function(c) { return c.yScale(c.base_value);})
249                         .add(pv.Rule)
250                         .data(function(c) { return c.yScale.ticks(num_y_rule_lines);})
251                         .bottom(function(c,d) {return d.yScale(c);})
252                         .strokeStyle('#222')
253                         .anchor('left').add(pv.Label)
254                         .font('4pt')
255                         .text(function(c,d) {return d.yScale.tickFormat(c);});
256                 break;
257 
258             case  'line' :
259 
260                 min_val = dataObj.tracks[index].min_value != undefined ? dataObj.tracks[index].min_value :
261                         pv.min(dataObj.tracks[index].data_array,function(a) { return a[value_key];});
262 
263                 max_val  = dataObj.tracks[index].max_value != undefined ? dataObj.tracks[index].max_value :
264                         pv.max(dataObj.tracks[index].data_array,function(a) { return a[value_key];});
265                 yScale = pv.Scale.linear(min_val,max_val ).range(0,track_height);
266                 if (dataObj.tracks[index].yaxis_scale_type != undefined && dataObj.tracks[index].yaxis_scale_type != null &&
267                         dataObj.tracks[index].yaxis_scale_type == 'log') {
268                     min_val = min_val > 0 ? min_val : 1;
269                     dataObj.tracks[index].base_value = dataObj.tracks[index].base_value > 0
270                             ? dataObj.tracks[index].base_value : min_val;
271                     yScale = pv.Scale.log(min_val,max_val ).range(0,track_height).nice();
272 
273                     yScale.ticks = log_ticks(yScale.domain());
274                 }
275 
276                 dataObj.tracks[index].yScale = yScale;
277 
278                 item = plot_track.add(pv.Line)
279                         .data(function(d,obj,index){ return init(index);})
280                         .left(function(c) { return that.posX(c.start);})
281                         .lineWidth(lineWidth)
282                         .bottom(function(c,d){ return d.yScale(c[value_key]);})
283                         .fillStyle(fillStyle)
284                         .strokeStyle(strokeStyle);
285                 item.event('mouseover',
286                     pv.Behavior.hovercard(
287                         {
288                             include_header : false,
289                             include_footer : true,
290                             self_hover : true,
291                             timeout : dataObj.tracks[index]._tooltip_timeout,
292                             data_config :
293                                 dataObj.tracks[index]._tooltipItems,
294                             tool_config : dataObj.tracks[index]._tooltipLinks
295                         }
296                     ));
297 
298 
299                 if (dataObj.tracks[index].notifier != undefined){
300                             item.cursor('pointer')
301                             .event('click',function(c,d){ d.notifier(c,d); });
302                 }
303               track.add(pv.Rule)
304                         .bottom(function(c) { return c.yScale(c.base_value);})
305                         .add(pv.Rule)
306                         .data(function(c) { return c.yScale.ticks(num_y_rule_lines);})
307                         .bottom(function(c,d) {return d.yScale(c);})
308                         .strokeStyle('#222')
309                         .anchor('left').add(pv.Label)
310                         .font('4pt')
311                         .text(function(c,d) {return d.yScale.tickFormat(c);});
312             break;
313 
314             case 'bar':
315 
316                 min_val = dataObj.tracks[index].min_value != undefined ? dataObj.tracks[index].min_value :
317                         pv.min(dataObj.tracks[index].data_array,function(a) { return a[value_key];});
318 
319                 max_val  = dataObj.tracks[index].max_value != undefined ? dataObj.tracks[index].max_value :
320                         pv.max(dataObj.tracks[index].data_array,function(a) { return a[value_key];});
321                 yScale = pv.Scale.linear(min_val,max_val ).range(0,track_height);
322                 if (dataObj.tracks[index].yaxis_scale_type != undefined && dataObj.tracks[index].yaxis_scale_type != null &&
323                         dataObj.tracks[index].yaxis_scale_type == 'log') {
324                     min_val = min_val > 0 ? min_val : 1;
325                     dataObj.tracks[index].base_value = dataObj.tracks[index].base_value > 0
326                             ? dataObj.tracks[index].base_value : min_val;
327                     yScale = pv.Scale.log(min_val,max_val ).range(0,track_height).nice();
328                     yScale.ticks = log_ticks(yScale.domain());
329                 }
330 
331                 dataObj.tracks[index].yScale = yScale;
332 
333                 item = plot_track.add(pv.Bar)
334                         .data(function(d,obj,index){ return init(index);})
335                         .left(function(c) { return that.posX(c.start);})
336                         .lineWidth(lineWidth)
337                         .width(function(c) { return that.posX(c.end)-that.posX(c.start);})
338                         .bottom(function(c,d){ return d.yScale(Math.min(d.base_value,c[value_key]));})
339                         .height(function(c,d) { return d.yScale(Math.max(d.base_value,c[value_key])) - this.bottom();})
340                         .fillStyle(fillStyle)
341                         .strokeStyle(strokeStyle);
342                 item.event('mouseover',
343                     pv.Behavior.hovercard(
344                         {
345                             include_header : false,
346                             include_footer : true,
347                             self_hover : true,
348                             timeout : dataObj.tracks[index]._tooltip_timeout,
349                             data_config :
350                                 dataObj.tracks[index]._tooltipItems,
351                             tool_config : dataObj.tracks[index]._tooltipLinks
352                         }
353                     ));
354 
355                 if (dataObj.tracks[index].notifier != undefined){
356                             item.cursor('pointer')
357                             .event('click',function(c,d){ d.notifier(c,d); });
358                 }
359                 track.add(pv.Rule)
360                         .bottom(function(c) { return c.yScale(c.base_value);})
361                         .add(pv.Rule)
362                         .data(function(c) { return c.yScale.ticks(num_y_rule_lines);})
363                         .bottom(function(c,d) {return d.yScale(c);})
364                         .strokeStyle('#222')
365                         .anchor('left').add(pv.Label)
366                         .font('4pt')
367                         .text(function(c,d) {return d.yScale.tickFormat(c);});
368                 break;
369             case 'glyph' :
370               shape = dataObj.tracks[index].shape;
371               radius = dataObj.tracks[index].radius;
372 
373                 item = plot_track.add(pv.Dot)
374                         .data(function(d,obj,index){ return init(index);})
375                         .left(function(c) { return that.posX(c.start);})
376                         .bottom(function(c,d) { return c.level*(d.tile_height + d.tile_padding) + (radius*2);})
377                         .shape(shape)
378                         .radius(radius)
379                         .fillStyle(fillStyle)
380                         .strokeStyle(strokeStyle);
381 
382                  if (dataObj.tracks[index]._tooltipItems != undefined){
383                      item.event('mouseover',
384                          pv.Behavior.hovercard(
385                              {
386                                  include_header : false,
387                                  include_footer : true,
388                                  self_hover : true,
389                                  timeout : dataObj.tracks[index]._tooltip_timeout,
390                                  data_config :
391                                      dataObj.tracks[index]._tooltipItems,
392                                  tool_config : dataObj.tracks[index]._tooltipLinks
393                              }
394                          ));
395 
396                 }
397                 if (dataObj.tracks[index].notifier != undefined){
398                     item
399                             .cursor('pointer')
400                             .event('click',function(c,d){ d.notifier(c,d); });
401                 }
402                 break;
403             case 'tile':
404             default :
405 
406                 item = plot_track.add(pv.Bar)
407                         .data(function(d,obj,index){ return init(index);})
408                         .left(function(c) { return that.posX(c.start);})
409                         .width(function(c) { return that.posX(c.end)-that.posX(c.start)})
410                         .bottom(function(c,d) { return c.level*(d.tile_height + d.tile_padding);})
411                         .height(function(c,d) { return d.tile_height;})
412                         .lineWidth(1)
413                         .fillStyle(fillStyle)
414                         .strokeStyle(strokeStyle);
415 
416                  if (dataObj.tracks[index]._tooltipItems != undefined){
417                      item.event('mouseover',
418                          pv.Behavior.hovercard(
419                              {
420                                  include_header : false,
421                                  include_footer : true,
422                                  self_hover : true,
423                                  timeout : dataObj.tracks[index]._tooltip_timeout,
424                                  data_config :
425                                      dataObj.tracks[index]._tooltipItems,
426                                  tool_config : dataObj.tracks[index]._tooltipLinks
427                              }
428                          ));
429 
430                 }
431                 if (dataObj.tracks[index].notifier != undefined){
432                     item
433                             .cursor('pointer')
434                             .event('click',function(c,d){ d.notifier(c,d); });
435                 }
436                 break;
437 
438         }
439 
440         track.add(pv.Label)
441                 .font('12pt helvetica')
442                 .title(function(c) { return c.description;})
443                 .text(function(c) { return c.label;})
444                 .left(0)
445                 .top(0)
446                 .textAlign('left')
447                 .textStyle("#333")
448                 .textBaseline('top');
449 
450     }
451 
452         /* Context panel (zoomed out). */
453         var context = drawPanel.add(pv.Panel)
454                 .bottom(0)
455                 .width(that.visibleWidth)
456                 .left(0)
457                 .height(that.context_height());
458         var context_panel = context.add(pv.Panel)
459                 .bottom(0);
460 
461         for(index = 0; index < dataObj.tracks.length; index++) {
462             var context_index = context_panel.add(pv.Panel)
463                     .data([index]);
464             var context_track = context_index.add(pv.Panel)
465                     .data(function(c) { return [dataObj.tracks[c]];})
466                     .bottom(function(c,index){return pv.sum(  dataObj.tracks.slice(0,index),function(d) {
467                 return d.track_height+d.track_padding; }) * context_scale;  })
468                     .height(function(c) { return (c.track_height + c.track_padding) * context_scale;})
469                     .width(that.visibleWidth)
470                     .overflow('hidden')
471                     .fillStyle(function(c) { return c.trackFillStyle();})
472                     .strokeStyle(function(c) { return c.trackStrokeStyle();})
473                     .lineWidth(function(c) { return c.trackLineWidth();})
474                     .left(0);
475 
476             switch (dataObj.tracks[index].type){
477                  case 'scatter':
478                  case 'scatterplot':
479                      context_track.add(pv.Dot)
480                             .data(function(d){ return d.data_array;})
481                             .left(function(c) { return x(c.start);})
482                             .bottom(function(c,d){ return d.yScale(c[value_key])* context_scale;})
483                             .radius(1)
484                             .shape('circle')
485                             .fillStyle(function() { return "#00c";} )
486                             .strokeStyle(function() { return "#009";} );
487                     break;
488                  case 'line':
489                      context_track.add(pv.Line)
490                             .data(function(d){ return d.data_array;})
491                             .left(function(c) { return x(c.start);})
492                             .bottom(function(c,d){ return d.yScale(c[value_key])* context_scale;})
493                             .fillStyle(function() { return null;} )
494                             .lineWidth(.5)
495                             .strokeStyle(function() { return "#009";} );
496                     break;
497                 case 'bar':
498                      context_track.add(pv.Bar)
499                             .data(function(d){ return d.data_array;})
500                             .left(function(c) { return x(c.start);})
501                             .width(function(c) { return x(c.end-c.start);})
502                             .bottom(function(c,d){ return d.yScale(Math.min(d.base_value,c[value_key]))* context_scale;})
503                             .height(function(c,d) { return (d.yScale(Math.max(d.base_value,c[value_key]))* context_scale - this.bottom());})
504                             .lineWidth(1)
505                             .fillStyle(function() { return "#00c";} )
506                             .strokeStyle(function() { return "#009";} );
507                     break;
508                 case 'glyph':
509                         context_track.add(pv.Dot)
510                             .data(function(d){ return d.data_array;})
511                             .left(function(c) { return x(c.start);})
512                             .shape('circle')
513                             .radius(.5)
514                             .bottom(function(c,d) { return (c.level*(d.tile_padding + d.tile_height) + (d.radius*2)) * context_scale;})
515                             .fillStyle(function() { return "#00c";} )
516                             .strokeStyle(function() { return "#009";} );
517                     break;
518                     case 'tile':
519                     default :
520                         context_track.add(pv.Bar)
521                             .data(function(d){ return d.data_array;})
522                             .left(function(c) { return x(c.start);})
523                             .width(function(c) { return x(c.end-c.start);})
524                             .bottom(function(c,d) { return c.level*(d.tile_padding + d.tile_height) * context_scale;})
525                             .height(function(c,d) { return d.tile_height * context_scale;})
526                             .lineWidth(1)
527                             .fillStyle(function() { return "#00c";} )
528                             .strokeStyle(function() { return "#009";} );
529                     break;
530 
531             }
532         }
533         context.add(pv.Rule)
534                 .left(0)
535           /* X-axis ticks. */
536               .add(pv.Rule)		
537                 .data(that.context_posX.ticks())
538                 .left(that.context_posX)
539                 .strokeStyle("#888")
540               .anchor("bottom").add(pv.Label)
541                 .text(that.context_posX.tickFormat);
542 
543         /* Y-axis ticks. */
544         context.add(pv.Rule)
545                 .bottom(0);
546 
547         /* The selectable, draggable focus region. */
548         context.add(pv.Panel)
549                 .data(function() {return [that.window];})
550                 .cursor("crosshair")
551                 .events("all")
552                 .event("mousedown", pv.Behavior.select())
553                 .event("select", function() {
554                                 if (that.window.dx < 2) { return; }
555                                 focus.render();})
556                 .add(pv.Bar)
557                 .left(function(d) { return d.x;})
558                 .width(function(d) {return d.dx;})
559                 .fillStyle("rgba(255, 128, 128, .4)")
560                 .cursor("move")
561                 .event("mousedown", pv.Behavior.drag())
562                 .event("drag", focus);
563 
564         /* The selectable, draggable focus region. */
565         focus_click_panel
566                 .data(function() { return [that.focus_window];})
567                 .cursor("crosshair")
568                 .events("all")
569                 .event("mousedown", pv.Behavior.select())
570                 .event("selectend", function() {
571                                 if (that.focus_window.dx < 2) { that.focus_window = {x:0,dx:0}; focus.render();
572                                 context.render();return; }
573                                 that.window ={x: that.context_posX(that.posX.invert(that.focus_window.x)),
574                                     dx: that.context_posX(that.posX.invert(that.focus_window.dx)) -
575                                             that.context_posX(that.posX.invert(0))};
576                                 that.focus_window = {x:0,dx:0};
577                                 focus.render();
578                                 context.render();})
579                 .add(pv.Bar)
580                 .left(function(d) { return d.x;})
581                 .width(function(d) {return d.dx;})
582                 .fillStyle("rgba(128, 128, 255, .4)");
583 
584         vis.render();
585 };
586 
587 
588 vq.models.LinearBrowserData = function(data) {
589 
590     vq.models.VisData.call(this,data);
591     this._setDataModel();
592 
593             if (this.getDataType() == 'vq.models.LinearBrowserData') {
594                 this._build_data(this.getContents())
595             } else {
596                 console.warn('Unrecognized JSON object.  Expected vq.models.LinearBrowserData object.');
597             }
598         };
599 vq.models.LinearBrowserData.prototype = pv.extend(vq.models.VisData);
600 
601 vq.models.LinearBrowserData.prototype._setDataModel = function() {
602          this._dataModel = [
603            {label: 'width', id: 'PLOT.width', cast : Number, defaultValue: 400},
604            {label: 'height', id: 'PLOT.height', cast : Number, defaultValue: 400},
605            {label : 'container', id:'PLOT.container', optional : true},
606            {label: 'vertical_padding', id: 'PLOT.vertical_padding', cast : Number, defaultValue: 0},
607            {label: 'horizontal_padding', id: 'PLOT.horizontal_padding',cast : Number,  defaultValue: 0},
608            {label: 'min_position', id: 'PLOT.min_position', cast : Number, defaultValue: 0},
609             {label: 'max_position', id: 'PLOT.max_position',cast : Number,  defaultValue: 100},
610             {label: 'context_height', id: 'PLOT.context_height',cast : Number,  defaultValue: 50},
611             {label : 'tracks', id: 'TRACKS', defaultValue : [] }
612     ];
613 };
614 
615 vq.models.LinearBrowserData.prototype._build_data = function(data_struct) {
616 
617     this._processData(data_struct);
618 
619             this.tracks = this.tracks.map(function(b) {
620                return new vq.models.LinearBrowserData.TrackData(b);
621             });
622 
623             this.tracks.forEach(function(b){
624                 if (b.type == 'tile' || b.type == 'glyph') {
625                     var  max_tile_level = b.tile_show_all_tiles ?
626                                Math.floor((b.track_height - (b.radius * 4)) / (b.tile_height + b.tile_padding)) :
627                                undefined;
628                     b.data_array = (b.type =='tile' ? vq.utils.VisUtils.layoutChrTiles(b.data_array,b.tile_overlap_distance,max_tile_level) :
629                             vq.utils.VisUtils.layoutChrTicks(b.data_array,b.tile_overlap_distance,max_tile_level));
630                 }
631             });
632 
633             this.setDataReady(true);
634         };
635 
636 vq.models.LinearBrowserData.TrackData = function(data) {
637 
638     vq.models.VisData.call(this,{CONTENTS:data});
639     this._setDataModel();
640 
641     this._build_data(this.getContents());
642 };
643 
644 
645 vq.models.LinearBrowserData.TrackData.prototype = pv.extend(vq.models.VisData);
646 
647 vq.models.LinearBrowserData.TrackData.prototype._setDataModel = function() {
648     this._dataModel = [
649         {label : 'label', id: 'label', cast: String, defaultValue : '' },
650             {label : 'type', id: 'type', cast: String, defaultValue : 'scatter' },
651             {label : 'fillStyle', id: 'CONFIGURATION.fill_style', cast: vq.utils.VisUtils.wrapProperty, defaultValue : null },
652             {label : 'strokeStyle', id: 'CONFIGURATION.stroke_style', cast: vq.utils.VisUtils.wrapProperty, defaultValue : null },
653             {label : 'lineWidth', id: 'CONFIGURATION.line_width', cast: vq.utils.VisUtils.wrapProperty, defaultValue : vq.utils.VisUtils.wrapProperty(1) },
654             {label : 'trackFillStyle', id: 'CONFIGURATION.track_fill_style', cast: vq.utils.VisUtils.wrapProperty, defaultValue : function() { return pv.color('#FFFFFF');} },
655             {label : 'trackStrokeStyle', id: 'CONFIGURATION.track_stroke_style', cast: vq.utils.VisUtils.wrapProperty, defaultValue : null },
656             {label : 'trackLineWidth', id: 'CONFIGURATION.track_line_width', cast: vq.utils.VisUtils.wrapProperty, defaultValue : vq.utils.VisUtils.wrapProperty(1) },
657             {label : 'track_height', id: 'CONFIGURATION.track_height', cast: Number, defaultValue : 80 },
658             {label : 'track_padding', id: 'CONFIGURATION.track_padding', cast: Number, defaultValue : 20 },
659             {label : 'base_value', id: 'CONFIGURATION.base_value', cast: Number, defaultValue : 0 },
660             {label : 'max_value', id: 'CONFIGURATION.max_value', cast: Number, defaultValue : null, optional : true },
661             {label : 'min_value', id: 'CONFIGURATION.min_value', cast: Number, defaultValue : null, optional : true },
662             {label : 'num_y_rule_lines', id: 'CONFIGURATION.num_y_rule_lines', cast: Number, defaultValue : 3 },
663             {label : 'shape', id: 'CONFIGURATION.shape', cast: String, defaultValue : 'circle' },
664             {label : 'radius', id: 'CONFIGURATION.radius', cast: Number, defaultValue : 5 },
665             {label : 'yaxis_scale_type', id: 'CONFIGURATION.yaxis_scale_type', cast: String, defaultValue : 'linear' },
666             {label : 'notifier', id: 'CONFIGURATION.notifier', cast: Function, optional : true },
667             {label : 'tile_padding', id: 'CONFIGURATION.tile_padding', cast: Number, defaultValue : 5 },
668             {label : 'tile_overlap_distance', id: 'CONFIGURATION.tile_overlap_distance', cast: Number, defaultValue : 0.1 },
669             {label : 'tile_height', id: 'CONFIGURATION.tile_height', cast: Number, defaultValue : 5 },
670             {label : 'tile_show_all_tiles', id: 'CONFIGURATION.tile_show_all_tiles', cast: Boolean, defaultValue : false },
671            {label : '_tooltipItems', id: 'OPTIONS.tooltip_items',  defaultValue :{Start : 'start' , End : 'end', Value : 'value'} },
672          {label : '_tooltipLinks', id: 'OPTIONS.tooltip_links',  defaultValue : {} },
673         {label : '_tooltip_timeout', id: 'OPTIONS.tooltip_timeout',  defaultValue : 200 },
674             {label : 'data_array', id: 'data_array', defaultValue : [] },
675         {label : 'value_key', id: 'value_key', defaultValue : 'value' }
676     ];
677 };
678 
679 vq.models.LinearBrowserData.TrackData.prototype._build_data = function(data) {
680             this._processData(data);
681 };
682