UNPKG

13.3 kBJavaScriptView Raw
1import * as is from '../is';
2import window from '../window';
3import * as math from '../math';
4
5let defaultSelectionType = 'single';
6
7let corefn = ({
8
9 autolock: function( bool ){
10 if( bool !== undefined ){
11 this._private.autolock = bool ? true : false;
12 } else {
13 return this._private.autolock;
14 }
15
16 return this; // chaining
17 },
18
19 autoungrabify: function( bool ){
20 if( bool !== undefined ){
21 this._private.autoungrabify = bool ? true : false;
22 } else {
23 return this._private.autoungrabify;
24 }
25
26 return this; // chaining
27 },
28
29 autounselectify: function( bool ){
30 if( bool !== undefined ){
31 this._private.autounselectify = bool ? true : false;
32 } else {
33 return this._private.autounselectify;
34 }
35
36 return this; // chaining
37 },
38
39 selectionType: function( selType ){
40 let _p = this._private;
41
42 if( _p.selectionType == null ){
43 _p.selectionType = defaultSelectionType;
44 }
45
46 if( selType !== undefined ){
47 if( selType === 'additive' || selType === 'single' ){
48 _p.selectionType = selType;
49 }
50 } else {
51 return _p.selectionType;
52 }
53
54 return this;
55 },
56
57 panningEnabled: function( bool ){
58 if( bool !== undefined ){
59 this._private.panningEnabled = bool ? true : false;
60 } else {
61 return this._private.panningEnabled;
62 }
63
64 return this; // chaining
65 },
66
67 userPanningEnabled: function( bool ){
68 if( bool !== undefined ){
69 this._private.userPanningEnabled = bool ? true : false;
70 } else {
71 return this._private.userPanningEnabled;
72 }
73
74 return this; // chaining
75 },
76
77 zoomingEnabled: function( bool ){
78 if( bool !== undefined ){
79 this._private.zoomingEnabled = bool ? true : false;
80 } else {
81 return this._private.zoomingEnabled;
82 }
83
84 return this; // chaining
85 },
86
87 userZoomingEnabled: function( bool ){
88 if( bool !== undefined ){
89 this._private.userZoomingEnabled = bool ? true : false;
90 } else {
91 return this._private.userZoomingEnabled;
92 }
93
94 return this; // chaining
95 },
96
97 boxSelectionEnabled: function( bool ){
98 if( bool !== undefined ){
99 this._private.boxSelectionEnabled = bool ? true : false;
100 } else {
101 return this._private.boxSelectionEnabled;
102 }
103
104 return this; // chaining
105 },
106
107 pan: function(){
108 let args = arguments;
109 let pan = this._private.pan;
110 let dim, val, dims, x, y;
111
112 switch( args.length ){
113 case 0: // .pan()
114 return pan;
115
116 case 1:
117
118 if( is.string( args[0] ) ){ // .pan('x')
119 dim = args[0];
120 return pan[ dim ];
121
122 } else if( is.plainObject( args[0] ) ){ // .pan({ x: 0, y: 100 })
123 if( !this._private.panningEnabled ){
124 return this;
125 }
126
127 dims = args[0];
128 x = dims.x;
129 y = dims.y;
130
131 if( is.number( x ) ){
132 pan.x = x;
133 }
134
135 if( is.number( y ) ){
136 pan.y = y;
137 }
138
139 this.emit( 'pan viewport' );
140 }
141 break;
142
143 case 2: // .pan('x', 100)
144 if( !this._private.panningEnabled ){
145 return this;
146 }
147
148 dim = args[0];
149 val = args[1];
150
151 if( (dim === 'x' || dim === 'y') && is.number( val ) ){
152 pan[ dim ] = val;
153 }
154
155 this.emit( 'pan viewport' );
156 break;
157
158 default:
159 break; // invalid
160 }
161
162 this.notify('viewport');
163
164 return this; // chaining
165 },
166
167 panBy: function( arg0, arg1 ){
168 let args = arguments;
169 let pan = this._private.pan;
170 let dim, val, dims, x, y;
171
172 if( !this._private.panningEnabled ){
173 return this;
174 }
175
176 switch( args.length ){
177 case 1:
178
179 if( is.plainObject( arg0 ) ){ // .panBy({ x: 0, y: 100 })
180 dims = args[0];
181 x = dims.x;
182 y = dims.y;
183
184 if( is.number( x ) ){
185 pan.x += x;
186 }
187
188 if( is.number( y ) ){
189 pan.y += y;
190 }
191
192 this.emit( 'pan viewport' );
193 }
194 break;
195
196 case 2: // .panBy('x', 100)
197 dim = arg0;
198 val = arg1;
199
200 if( (dim === 'x' || dim === 'y') && is.number( val ) ){
201 pan[ dim ] += val;
202 }
203
204 this.emit( 'pan viewport' );
205 break;
206
207 default:
208 break; // invalid
209 }
210
211 this.notify('viewport');
212
213 return this; // chaining
214 },
215
216 fit: function( elements, padding ){
217 let viewportState = this.getFitViewport( elements, padding );
218
219 if( viewportState ){
220 let _p = this._private;
221 _p.zoom = viewportState.zoom;
222 _p.pan = viewportState.pan;
223
224 this.emit( 'pan zoom viewport' );
225
226 this.notify('viewport');
227 }
228
229 return this; // chaining
230 },
231
232 getFitViewport: function( elements, padding ){
233 if( is.number( elements ) && padding === undefined ){ // elements is optional
234 padding = elements;
235 elements = undefined;
236 }
237
238 if( !this._private.panningEnabled || !this._private.zoomingEnabled ){
239 return;
240 }
241
242 let bb;
243
244 if( is.string( elements ) ){
245 let sel = elements;
246 elements = this.$( sel );
247
248 } else if( is.boundingBox( elements ) ){ // assume bb
249 let bbe = elements;
250 bb = {
251 x1: bbe.x1,
252 y1: bbe.y1,
253 x2: bbe.x2,
254 y2: bbe.y2
255 };
256
257 bb.w = bb.x2 - bb.x1;
258 bb.h = bb.y2 - bb.y1;
259
260 } else if( !is.elementOrCollection( elements ) ){
261 elements = this.mutableElements();
262 }
263
264 if( is.elementOrCollection( elements ) && elements.empty() ){ return; } // can't fit to nothing
265
266 bb = bb || elements.boundingBox();
267
268 let w = this.width();
269 let h = this.height();
270 let zoom;
271 padding = is.number( padding ) ? padding : 0;
272
273 if( !isNaN( w ) && !isNaN( h ) && w > 0 && h > 0 && !isNaN( bb.w ) && !isNaN( bb.h ) && bb.w > 0 && bb.h > 0 ){
274 zoom = Math.min( (w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h );
275
276 // crop zoom
277 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
278 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
279
280 let pan = { // now pan to middle
281 x: (w - zoom * ( bb.x1 + bb.x2 )) / 2,
282 y: (h - zoom * ( bb.y1 + bb.y2 )) / 2
283 };
284
285 return {
286 zoom: zoom,
287 pan: pan
288 };
289 }
290
291 return;
292 },
293
294 zoomRange: function( min, max ){
295 let _p = this._private;
296
297 if( max == null ){
298 let opts = min;
299
300 min = opts.min;
301 max = opts.max;
302 }
303
304 if( is.number( min ) && is.number( max ) && min <= max ){
305 _p.minZoom = min;
306 _p.maxZoom = max;
307 } else if( is.number( min ) && max === undefined && min <= _p.maxZoom ){
308 _p.minZoom = min;
309 } else if( is.number( max ) && min === undefined && max >= _p.minZoom ){
310 _p.maxZoom = max;
311 }
312
313 return this;
314 },
315
316 minZoom: function( zoom ){
317 if( zoom === undefined ){
318 return this._private.minZoom;
319 } else {
320 return this.zoomRange({ min: zoom });
321 }
322 },
323
324 maxZoom: function( zoom ){
325 if( zoom === undefined ){
326 return this._private.maxZoom;
327 } else {
328 return this.zoomRange({ max: zoom });
329 }
330 },
331
332 getZoomedViewport: function( params ){
333 let _p = this._private;
334 let currentPan = _p.pan;
335 let currentZoom = _p.zoom;
336 let pos; // in rendered px
337 let zoom;
338 let bail = false;
339
340 if( !_p.zoomingEnabled ){ // zooming disabled
341 bail = true;
342 }
343
344 if( is.number( params ) ){ // then set the zoom
345 zoom = params;
346
347 } else if( is.plainObject( params ) ){ // then zoom about a point
348 zoom = params.level;
349
350 if( params.position != null ){
351 pos = math.modelToRenderedPosition( params.position, currentZoom, currentPan );
352 } else if( params.renderedPosition != null ){
353 pos = params.renderedPosition;
354 }
355
356 if( pos != null && !_p.panningEnabled ){ // panning disabled
357 bail = true;
358 }
359 }
360
361 // crop zoom
362 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
363 zoom = zoom < _p.minZoom ? _p.minZoom : zoom;
364
365 // can't zoom with invalid params
366 if( bail || !is.number( zoom ) || zoom === currentZoom || ( pos != null && (!is.number( pos.x ) || !is.number( pos.y )) ) ){
367 return null;
368 }
369
370 if( pos != null ){ // set zoom about position
371 let pan1 = currentPan;
372 let zoom1 = currentZoom;
373 let zoom2 = zoom;
374
375 let pan2 = {
376 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
377 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
378 };
379
380 return {
381 zoomed: true,
382 panned: true,
383 zoom: zoom2,
384 pan: pan2
385 };
386
387 } else { // just set the zoom
388 return {
389 zoomed: true,
390 panned: false,
391 zoom: zoom,
392 pan: currentPan
393 };
394 }
395 },
396
397 zoom: function( params ){
398 if( params === undefined ){ // get
399 return this._private.zoom;
400 } else { // set
401 let vp = this.getZoomedViewport( params );
402 let _p = this._private;
403
404 if( vp == null || !vp.zoomed ){ return this; }
405
406 _p.zoom = vp.zoom;
407
408 if( vp.panned ){
409 _p.pan.x = vp.pan.x;
410 _p.pan.y = vp.pan.y;
411 }
412
413 this.emit( 'zoom' + ( vp.panned ? ' pan' : '' ) + ' viewport' );
414
415 this.notify('viewport');
416
417 return this; // chaining
418 }
419 },
420
421 viewport: function( opts ){
422 let _p = this._private;
423 let zoomDefd = true;
424 let panDefd = true;
425 let events = []; // to trigger
426 let zoomFailed = false;
427 let panFailed = false;
428
429 if( !opts ){ return this; }
430 if( !is.number( opts.zoom ) ){ zoomDefd = false; }
431 if( !is.plainObject( opts.pan ) ){ panDefd = false; }
432 if( !zoomDefd && !panDefd ){ return this; }
433
434 if( zoomDefd ){
435 let z = opts.zoom;
436
437 if( z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled ){
438 zoomFailed = true;
439
440 } else {
441 _p.zoom = z;
442
443 events.push( 'zoom' );
444 }
445 }
446
447 if( panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled ){
448 let p = opts.pan;
449
450 if( is.number( p.x ) ){
451 _p.pan.x = p.x;
452 panFailed = false;
453 }
454
455 if( is.number( p.y ) ){
456 _p.pan.y = p.y;
457 panFailed = false;
458 }
459
460 if( !panFailed ){
461 events.push( 'pan' );
462 }
463 }
464
465 if( events.length > 0 ){
466 events.push( 'viewport' );
467 this.emit( events.join( ' ' ) );
468
469 this.notify('viewport');
470 }
471
472 return this; // chaining
473 },
474
475 center: function( elements ){
476 let pan = this.getCenterPan( elements );
477
478 if( pan ){
479 this._private.pan = pan;
480
481 this.emit( 'pan viewport' );
482
483 this.notify('viewport');
484 }
485
486 return this; // chaining
487 },
488
489 getCenterPan: function( elements, zoom ){
490 if( !this._private.panningEnabled ){
491 return;
492 }
493
494 if( is.string( elements ) ){
495 let selector = elements;
496 elements = this.mutableElements().filter( selector );
497 } else if( !is.elementOrCollection( elements ) ){
498 elements = this.mutableElements();
499 }
500
501 if( elements.length === 0 ){ return; } // can't centre pan to nothing
502
503 let bb = elements.boundingBox();
504 let w = this.width();
505 let h = this.height();
506 zoom = zoom === undefined ? this._private.zoom : zoom;
507
508 let pan = { // middle
509 x: (w - zoom * ( bb.x1 + bb.x2 )) / 2,
510 y: (h - zoom * ( bb.y1 + bb.y2 )) / 2
511 };
512
513 return pan;
514 },
515
516 reset: function(){
517 if( !this._private.panningEnabled || !this._private.zoomingEnabled ){
518 return this;
519 }
520
521 this.viewport( {
522 pan: { x: 0, y: 0 },
523 zoom: 1
524 } );
525
526 return this; // chaining
527 },
528
529 invalidateSize: function(){
530 this._private.sizeCache = null;
531 },
532
533 size: function(){
534 let _p = this._private;
535 let container = _p.container;
536
537 return ( _p.sizeCache = _p.sizeCache || ( container ? (function(){
538 let style = window.getComputedStyle( container );
539 let val = function( name ){ return parseFloat( style.getPropertyValue( name ) ); };
540
541 return {
542 width: container.clientWidth - val('padding-left') - val('padding-right'),
543 height: container.clientHeight - val('padding-top') - val('padding-bottom')
544 };
545 })() : { // fallback if no container (not 0 b/c can be used for dividing etc)
546 width: 1,
547 height: 1
548 } ) );
549 },
550
551 width: function(){
552 return this.size().width;
553 },
554
555 height: function(){
556 return this.size().height;
557 },
558
559 extent: function(){
560 let pan = this._private.pan;
561 let zoom = this._private.zoom;
562 let rb = this.renderedExtent();
563
564 let b = {
565 x1: ( rb.x1 - pan.x ) / zoom,
566 x2: ( rb.x2 - pan.x ) / zoom,
567 y1: ( rb.y1 - pan.y ) / zoom,
568 y2: ( rb.y2 - pan.y ) / zoom
569 };
570
571 b.w = b.x2 - b.x1;
572 b.h = b.y2 - b.y1;
573
574 return b;
575 },
576
577 renderedExtent: function(){
578 let width = this.width();
579 let height = this.height();
580
581 return {
582 x1: 0,
583 y1: 0,
584 x2: width,
585 y2: height,
586 w: width,
587 h: height
588 };
589 },
590
591 multiClickDebounceTime: function ( int ){
592 if( int ) (this._private.multiClickDebounceTime = int);
593 else return this._private.multiClickDebounceTime;
594 return this; // chaining
595 }
596});
597
598// aliases
599corefn.centre = corefn.center;
600
601// backwards compatibility
602corefn.autolockNodes = corefn.autolock;
603corefn.autoungrabifyNodes = corefn.autoungrabify;
604
605export default corefn;