1 | import * as is from '../is';
|
2 | import window from '../window';
|
3 | import * as math from '../math';
|
4 |
|
5 | let defaultSelectionType = 'single';
|
6 |
|
7 | let 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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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:
|
114 | return pan;
|
115 |
|
116 | case 1:
|
117 |
|
118 | if( is.string( args[0] ) ){
|
119 | dim = args[0];
|
120 | return pan[ dim ];
|
121 |
|
122 | } else if( is.plainObject( args[0] ) ){
|
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:
|
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;
|
160 | }
|
161 |
|
162 | this.notify('viewport');
|
163 |
|
164 | return this;
|
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 ) ){
|
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:
|
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;
|
209 | }
|
210 |
|
211 | this.notify('viewport');
|
212 |
|
213 | return this;
|
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;
|
230 | },
|
231 |
|
232 | getFitViewport: function( elements, padding ){
|
233 | if( is.number( elements ) && padding === undefined ){
|
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 ) ){
|
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; }
|
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 |
|
277 | zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
|
278 | zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
|
279 |
|
280 | let pan = {
|
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;
|
337 | let zoom;
|
338 | let bail = false;
|
339 |
|
340 | if( !_p.zoomingEnabled ){
|
341 | bail = true;
|
342 | }
|
343 |
|
344 | if( is.number( params ) ){
|
345 | zoom = params;
|
346 |
|
347 | } else if( is.plainObject( params ) ){
|
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 ){
|
357 | bail = true;
|
358 | }
|
359 | }
|
360 |
|
361 |
|
362 | zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
|
363 | zoom = zoom < _p.minZoom ? _p.minZoom : zoom;
|
364 |
|
365 |
|
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 ){
|
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 {
|
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 ){
|
399 | return this._private.zoom;
|
400 | } else {
|
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;
|
418 | }
|
419 | },
|
420 |
|
421 | viewport: function( opts ){
|
422 | let _p = this._private;
|
423 | let zoomDefd = true;
|
424 | let panDefd = true;
|
425 | let events = [];
|
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;
|
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;
|
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; }
|
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 = {
|
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;
|
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 | })() : {
|
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;
|
595 | }
|
596 | });
|
597 |
|
598 |
|
599 | corefn.centre = corefn.center;
|
600 |
|
601 |
|
602 | corefn.autolockNodes = corefn.autolock;
|
603 | corefn.autoungrabifyNodes = corefn.autoungrabify;
|
604 |
|
605 | export default corefn;
|