1 
  2 
  3 //intialize with var data ={DATATYPE : "vq.models.FlexScrollData", CONTENTS : "test"};
  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.FlexScrollData", CONTENTS : "test"}
  7 //				and options = {plotHeight: xx, plotWidth : xx, vertical_padding: xx,
  8 //				horizontal_padding : xx , max_position: xx, min_position: xx,
  9 //				maxRange: xx, minRange: xx, dblclick_notifier : function(x,dx),
 10 //				fixed_window_width: xx, scale_multiplier : 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.FlexScroll = function(){
 15     vq.Vis.call(this);
 16 
 17     //set option variables to useful values before options are set.
 18     this.height(40);     // defaults
 19     this.width(600);     // defaults
 20     this.vertical_padding(10);
 21     this.horizontal_padding(10);
 22     this.max_position(100);
 23     this.min_position(0);
 24     this.interval(5);
 25     this.scale_multiplier(1);
 26     this.fixed_window_width(-1);
 27     this.callback_always(false);
 28 
 29 };
 30 
 31 vq.FlexScroll.prototype = pv.extend(vq.Vis);
 32 vq.FlexScroll.prototype
 33         .property('max_position',Number)
 34         .property('min_position',Number)
 35         .property('interval', Number)
 36         .property('scale_multiplier',Number)
 37         .property('callback_always', Boolean)
 38         .property('fixed_window_width',Number);
 39 
 40 
 41 vq.FlexScroll.prototype._setOptionDefaults = function(options) {
 42 
 43     if (options.max_position != null) { this.max_position(options.max_position); }
 44 
 45     if (options.min_position != null) { this.min_position(options.min_position); }
 46 
 47     if (options.plotHeight != null) { this.height(options.plotHeight); }
 48 
 49     if (options.plotWidth != null) { this.width(options.plotWidth); }
 50 
 51     if (options.vertical_padding != null) { this.vertical_padding(options.vertical_padding); }
 52 
 53     if (options.horizontal_padding != null) { this.horizontal_padding(options.horizontal_padding); }
 54 
 55     if (options.interval != null) { this.interval(options.interval); }
 56 
 57     if (options.callback_always != null) this.callback_always = options.callbackAlways;
 58 
 59     if (options.scale_multiplier != null) { this.scale_multiplier(options.scale_multiplier); }
 60 
 61     if (options.fixed_window_width != null) {this.fixed_window_width(options.fixed_window_width); }
 62 
 63     if (options.container) { this.container(options.container); }
 64     if (options.notifier) { this.notifier= options.notifier; }
 65     if (options.dblclick_notifier) { this.dblclick_notifier = options.dblclick_notifier; }
 66 
 67 };
 68 
 69 vq.FlexScroll.prototype.set_position = function(x_var,dx_var) {
 70     this.window = {x: this.xScale(x_var), dx : this.xScale(dx_var)};
 71     this.render();
 72 };
 73 
 74 
 75 vq.FlexScroll.prototype.draw = function(data) {
 76     var data_obj = new vq.models.FlexScrollData(data);
 77     if(data_obj.isDataReady()) {
 78         this._setOptionDefaults(data_obj);
 79         this.data = data_obj;
 80         this.render();
 81     }
 82 };
 83 
 84 vq.FlexScroll.prototype.render = function() {
 85     var that = this;
 86 
 87     var w = this.width(),
 88             h2 = this.height(),
 89             scrollWidth = this.width() - (2 * this.horizontal_padding()) ,
 90             focusMap = pv.Scale.linear(0, w).range(this.min_position(),this.max_position()),
 91             x=pv.Scale.linear(this.min_position(), this.max_position()).range(0, scrollWidth),
 92             isWindowWidthFixed = (this.fixed_window_width() > 0),
 93             windowWidth = (isWindowWidthFixed) ? this.fixed_window_width() : this.interval(),
 94             maxX = this.max_position(),
 95             minX = this.min_position(),
 96             halfX = (maxX - minX) / 2,
 97             formatter = pv.Format.number(),
 98             scale_multiplier = this.scale_multiplier(),
 99             scaleX = 2 * halfX / scrollWidth;
100 
101     if (this.window == undefined){
102         this.window = {x:x(this.min_position()), dx:x(this.min_position() + windowWidth) - x(this.min_position()) };
103 
104     }
105     this.xScale = x;
106 
107     var notify_dblclick = function(d) {
108         var window = translateAndScale(d);
109         that.dblclick_notifier(window.x, window.dx);
110     };
111 
112     var zoom = function() {
113         var t = this.transform().invert();
114         var halfX = (maxX - minX) / 2,
115                 centerX = maxX - halfX,
116                 scaleX = 2 * halfX / (scrollWidth);
117         var start = t.x * scaleX - (halfX) + centerX,
118                 end = centerX + (scrollWidth * t.k + t.x) * scaleX - halfX;
119         x.domain(start, end).nice();
120         focusMap.range(start, end);
121         that.notifier(i.x * scaleX + centerX - halfX, (i.dx) * scaleX);
122         vis.render();
123     };
124 
125     var translateFocus = function(d) {
126         var endX = pv.min( [ pv.max(x.domain()), maxX ] );
127         var startX = pv.max( [ pv.min(x.domain()), minX] );
128         var halfX = (endX - startX) / 2;
129         var scaleX = 2 * halfX / scrollWidth;
130         return { x: Math.round((d.x *scaleX + startX))  , dx : (isWindowWidthFixed) ? Math.round(windowWidth * scaleX) : Math.round((d.dx * scaleX))   };
131     };
132 
133     var translateAndScale = function(d) {
134         var translated = translateFocus(d);
135         return {x : translated.x * scale_multiplier, dx : translated.dx * scale_multiplier };
136     };
137 
138     var dragStart = function(d) {
139         this.parent.active(true) ;
140         this.fillStyle("steelblue");
141         if (that.callbackAlways) { dispatchEvent(d);}
142         vis.render();
143     };
144 
145     var dragEnd = function(d) {
146 //	this.parent.active(false) ;
147         this.fillStyle(undefined);
148         dispatchEvent(d);
149         vis.render();
150     };
151 
152     var selectStart = function(d) {
153         this.parent.active(true) ;
154         if (that.callbackAlways) { dispatchEvent(d);}
155         vis.render();
156     };
157 
158     var selectEnd = function(d) {
159 //	this.parent.active(false) ;
160         dispatchEvent(d);
161         vis.render();
162     };
163 
164     var isActive = function(active) {
165         return active;
166     };
167 
168     var dispatchEvent = function(d) {
169 //if we're at the same place as last time, do nothing
170         var window = translateAndScale(d);
171         that.notifier(window.x ,window.dx );
172     };
173 
174     var vis = new pv.Panel()
175             .width(w)
176             .height(h2)
177             .strokeStyle("#aaa")
178             .canvas(that.container());
179 
180     var	scroll = vis.add(pv.Panel)
181             .left(this.horizontal_padding())
182             .width(scrollWidth )
183             .top(this.vertical_padding())
184             .bottom(12)
185             .events("all")
186             .event("mousemove",pv.Behavior.point(Infinity).collapse("y"))
187             .def ("active", false);
188 
189     var panel = scroll.add(pv.Panel)
190             .data([that.window])
191             .fillStyle("white")
192             .events("all")
193             .cursor("crosshair");
194     var bar = scroll.add(pv.Bar)
195             .data([that.window])
196             .left(function(d) {return d.x;})
197             .width(function(d) {return isWindowWidthFixed ==  true ? x(windowWidth) : d.dx;})
198             .def ("fillStyle", 'rgba(255, 128, 128, .4)')
199             .cursor("move")
200             .events("painted")
201             .event("mousedown", pv.Behavior.drag())
202             .event("drag", function(d) { if(that.callbackAlways) {dispatchEvent(d);}vis.render();})
203             .event("dragstart",dragStart )
204             .event("dragend", dragEnd )
205             .event("point", function() { this.parent.active(true); return vis.render(); } )
206             .event("unpoint", function() { this.parent.active(false); return vis.render(); } )
207             .event("dblclick", function(d) { notify_dblclick(d) } );
208     panel.add(pv.Label)
209             .top(2)
210             .left(function(d) { return d.x + (d.dx/2) - 10;} )
211             .text(function(d) { return translateFocus(d).dx});
212 
213     scroll.add(pv.Rule)
214             .data(function() { return x.ticks(); })
215             .left(x)
216             .strokeStyle("#eee")
217             .fillStyle("white")
218             .anchor("bottom").add(pv.Label)
219             .text( x.tickFormat );
220 
221     if(!isWindowWidthFixed) {
222         scroll.add(pv.Bar)
223                 .data([this.window])
224                 .fillStyle("steelblue")
225                 .left(function(d) {return d.x+ d.dx;})
226                 .width(function(d) {return  4;})
227                 .events("painted")
228                 .event("mousedown",pv.Behavior.resize("right"))
229                 .event("resize",function(d) { if(that.callbackAlways) {dispatchEvent(d);}vis.render();})
230                 .event("resizestart", selectStart)
231                 .event("resizeend",selectEnd);
232         var labels = vis.add(pv.Panel)
233                 .data([this.window]);
234         labels.anchor("left").add(pv.Label)
235                 .visible(function() { return isActive(scroll.active());  })
236                 .text(function(d) { return translateFocus({x: d.x , dx: 0}).x; } );
237         labels.anchor("right").add(pv.Label)
238                 .visible(function() { return isActive(scroll.active());  })
239                 .text(function(d) { return translateFocus({x: d.x+d.dx , dx: 0}).x; } );
240 
241         scroll.add(pv.Bar)
242                 .data([this.window])
243                 .fillStyle("steelblue")
244                 .left(function(d) {return d.x-4;})
245                 .width(function(d) {return  4;})
246                 .events("painted")
247                 .event("mousedown",pv.Behavior.resize("left"))
248                 .event("resize",function(d) { if(that.callbackAlways) {dispatchEvent(d);}vis.render(); })
249                 .event("resizeend", selectEnd)
250                 .event("resizestart", selectStart);
251 
252     }
253 
254     vis.render();
255 
256     dispatchEvent(this.window);
257 };
258 
259 
260 vq.models.FlexScrollData = function(data) {
261     vq.models.VisData.call(this,data);
262 
263     this.setDataModel();
264 
265             if (this.getDataType() == 'vq.models.FlexScrollData') {
266                 this._build_data(this.getContents());
267             } else {
268                 console.warn('Unrecognized JSON object.  Expected vq.models.FlexPlotData object.');
269             }
270         };
271 vq.models.FlexScrollData.prototype = pv.extend(vq.models.VisData);
272 
273 vq.models.FlexScrollData.prototype._build_data = function(data) {
274         this._processData(data);
275 
276         if (this.interval === undefined) {this.interval = (this.max_position - this.min_position) / 20; }
277 
278                 this.setDataReady(true);
279 };
280 
281 
282 vq.models.FlexScrollData.prototype.setDataModel = function () {
283     this._dataModel = [
284         {label: 'width', id: 'PLOT.width', cast : Number, defaultValue: 600},
285         {label: 'height', id: 'PLOT.height', cast : Number, defaultValue: 40},
286         {label :'container', id:'PLOT.container', optional : true},
287         {label: 'vertical_padding', id: 'PLOT.vertical_padding', cast : Number, defaultValue: 10},
288         {label: 'horizontal_padding', id: 'PLOT.horizontal_padding',cast : Number,  defaultValue: 10},
289         {label: 'min_position', id: 'PLOT.min_position', cast : Number, defaultValue: 0},
290         {label: 'max_position', id: 'PLOT.max_position',cast : Number,  defaultValue: 100},
291         {label: 'scale_multiplier', id: 'PLOT.scale_multiplier',cast : Number, defaultValue : 1},
292         {label: 'fixed_window_width', id: 'PLOT.fixed_window_width',cast : Number, defaultValue : -1},
293         {label: 'interval', id: 'PLOT.interval', cast : Number, optional : true},
294         {label: 'notifier', id: 'notifier', defaultValue : function(a){return null;}},
295         {label :'callback_always', id:'callback_always', cast : Boolean, defaultValue : true},
296         {label: 'dblclick_notifier', id: 'dblclick_notifier', defaultValue : function(a){return null;}}
297 
298     ];
299 };
300 
301