basant307/AI_Governance_Project
048
1import window from '../window.mjs';2import * as util from '../util/index.mjs';3import Collection from '../collection/index.mjs';4import * as is from '../is.mjs';5import Promise from '../promise.mjs';6 7import addRemove from './add-remove.mjs';8import animation from './animation/index.mjs';9import events from './events.mjs';10import exportFormat from './export.mjs';11import layout from './layout.mjs';12import notification from './notification.mjs';13import renderer from './renderer.mjs';14import search from './search.mjs';15import style from './style.mjs';16import viewport from './viewport.mjs';17import data from './data.mjs';18 19let Core = function( opts ){20 let cy = this;21 22 opts = util.extend( {}, opts );23 24 let container = opts.container;25 26 // allow for passing a wrapped jquery object27 // e.g. cytoscape({ container: $('#cy') })28 if( container && !is.htmlElement( container ) && is.htmlElement( container[0] ) ){29 container = container[0];30 }31 32 let reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery33 reg = reg || {};34 35 if( reg && reg.cy ){36 reg.cy.destroy();37 38 reg = {}; // old instance => replace reg completely39 }40 41 let readies = reg.readies = reg.readies || [];42 43 if( container ){ container._cyreg = reg; } // make sure container assoc'd reg points to this cy44 reg.cy = cy;45 46 let head = window !== undefined && container !== undefined && !opts.headless;47 let options = opts;48 options.layout = util.extend( { name: head ? 'grid' : 'null' }, options.layout );49 options.renderer = util.extend( { name: head ? 'canvas' : 'null' }, options.renderer );50 51 let defVal = function( def, val, altVal ){52 if( val !== undefined ){53 return val;54 } else if( altVal !== undefined ){55 return altVal;56 } else {57 return def;58 }59 };60 61 let _p = this._private = {62 container: container, // html dom ele container63 ready: false, // whether ready has been triggered64 options: options, // cached options65 elements: new Collection( this ), // elements in the graph66 listeners: [], // list of listeners67 aniEles: new Collection( this ), // elements being animated68 data: options.data || {}, // data for the core69 scratch: {}, // scratch object for core70 layout: null,71 renderer: null,72 destroyed: false, // whether destroy was called73 notificationsEnabled: true, // whether notifications are sent to the renderer74 minZoom: 1e-50,75 maxZoom: 1e50,76 zoomingEnabled: defVal( true, options.zoomingEnabled ),77 userZoomingEnabled: defVal( true, options.userZoomingEnabled ),78 panningEnabled: defVal( true, options.panningEnabled ),79 userPanningEnabled: defVal( true, options.userPanningEnabled ),80 boxSelectionEnabled: defVal( true, options.boxSelectionEnabled ),81 autolock: defVal( false, options.autolock, options.autolockNodes ),82 autoungrabify: defVal( false, options.autoungrabify, options.autoungrabifyNodes ),83 autounselectify: defVal( false, options.autounselectify ),84 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,85 zoom: is.number( options.zoom ) ? options.zoom : 1,86 pan: {87 x: is.plainObject( options.pan ) && is.number( options.pan.x ) ? options.pan.x : 0,88 y: is.plainObject( options.pan ) && is.number( options.pan.y ) ? options.pan.y : 089 },90 animation: { // object for currently-running animations91 current: [],92 queue: []93 },94 hasCompoundNodes: false,95 multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)96 };97 98 this.createEmitter();99 100 // set selection type101 this.selectionType( options.selectionType );102 103 // init zoom bounds104 this.zoomRange({ min: options.minZoom, max: options.maxZoom });105 106 let loadExtData = function( extData, next ){107 let anyIsPromise = extData.some( is.promise );108 109 if( anyIsPromise ){110 return Promise.all( extData ).then( next ); // load all data asynchronously, then exec rest of init111 } else {112 next( extData ); // exec synchronously for convenience113 }114 };115 116 // start with the default stylesheet so we have something before loading an external stylesheet117 if( _p.styleEnabled ){118 cy.setStyle([]);119 }120 121 // create the renderer122 let rendererOptions = util.assign({}, options, options.renderer); // allow rendering hints in top level options123 cy.initRenderer( rendererOptions );124 125 let setElesAndLayout = function( elements, onload, ondone ){126 cy.notifications( false );127 128 // remove old elements129 let oldEles = cy.mutableElements();130 if( oldEles.length > 0 ){131 oldEles.remove();132 }133 134 if( elements != null ){135 if( is.plainObject( elements ) || is.array( elements ) ){136 cy.add( elements );137 }138 }139 140 cy.one( 'layoutready', function( e ){141 cy.notifications( true );142 cy.emit( e ); // we missed this event by turning notifications off, so pass it on143 144 cy.one( 'load', onload );145 cy.emitAndNotify( 'load' );146 } ).one( 'layoutstop', function(){147 cy.one( 'done', ondone );148 cy.emit( 'done' );149 } );150 151 let layoutOpts = util.extend( {}, cy._private.options.layout );152 layoutOpts.eles = cy.elements();153 154 cy.layout( layoutOpts ).run();155 };156 157 loadExtData([ options.style, options.elements ], function( thens ){158 let initStyle = thens[0];159 let initEles = thens[1];160 161 // init style162 if( _p.styleEnabled ){163 cy.style().append( initStyle );164 }165 166 // initial load167 setElesAndLayout( initEles, function(){ // onready168 cy.startAnimationLoop();169 _p.ready = true;170 171 // if a ready callback is specified as an option, the bind it172 if( is.fn( options.ready ) ){173 cy.on( 'ready', options.ready );174 }175 176 // bind all the ready handlers registered before creating this instance177 for( let i = 0; i < readies.length; i++ ){178 let fn = readies[ i ];179 cy.on( 'ready', fn );180 }181 if( reg ){ reg.readies = []; } // clear b/c we've bound them all and don't want to keep it around in case a new core uses the same div etc182 183 cy.emit( 'ready' );184 }, options.done );185 186 } );187};188 189let corefn = Core.prototype; // short alias190 191util.extend( corefn, {192 instanceString: function(){193 return 'core';194 },195 196 isReady: function(){197 return this._private.ready;198 },199 200 destroyed: function(){201 return this._private.destroyed;202 },203 204 ready: function( fn ){205 if( this.isReady() ){206 this.emitter().emit( 'ready', [], fn ); // just calls fn as though triggered via ready event207 } else {208 this.on( 'ready', fn );209 }210 211 return this;212 },213 214 destroy: function(){215 let cy = this;216 if( cy.destroyed() ) return;217 218 cy.stopAnimationLoop();219 220 cy.destroyRenderer();221 222 this.emit( 'destroy' );223 224 cy._private.destroyed = true;225 226 return cy;227 },228 229 hasElementWithId: function( id ){230 return this._private.elements.hasElementWithId( id );231 },232 233 getElementById: function( id ){234 return this._private.elements.getElementById( id );235 },236 237 hasCompoundNodes: function(){238 return this._private.hasCompoundNodes;239 },240 241 headless: function(){242 return this._private.renderer.isHeadless();243 },244 245 styleEnabled: function(){246 return this._private.styleEnabled;247 },248 249 addToPool: function( eles ){250 this._private.elements.merge( eles );251 252 return this; // chaining253 },254 255 removeFromPool: function( eles ){256 this._private.elements.unmerge( eles );257 258 return this;259 },260 261 container: function(){262 return this._private.container || null;263 },264 265 window: function() {266 let container = this._private.container;267 if (container == null) return window;268 269 let ownerDocument = this._private.container.ownerDocument;270 271 if (ownerDocument === undefined || ownerDocument == null) {272 return window;273 }274 275 return ownerDocument.defaultView || window;276 },277 278 mount: function( container ){279 if( container == null ){ return; }280 281 let cy = this;282 let _p = cy._private;283 let options = _p.options;284 285 if( !is.htmlElement( container ) && is.htmlElement( container[0] ) ){286 container = container[0];287 }288 289 cy.stopAnimationLoop();290 291 cy.destroyRenderer();292 293 _p.container = container;294 _p.styleEnabled = true;295 296 cy.invalidateSize();297 298 cy.initRenderer( util.assign({}, options, options.renderer, {299 // allow custom renderer name to be re-used, otherwise use canvas300 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name301 }) );302 303 cy.startAnimationLoop();304 305 cy.style( options.style );306 307 cy.emit( 'mount' );308 309 return cy;310 },311 312 unmount: function(){313 let cy = this;314 315 cy.stopAnimationLoop();316 317 cy.destroyRenderer();318 319 cy.initRenderer( { name: 'null' } );320 321 cy.emit( 'unmount' );322 323 return cy;324 },325 326 options: function(){327 return util.copy( this._private.options );328 },329 330 json: function( obj ){331 let cy = this;332 let _p = cy._private;333 let eles = cy.mutableElements();334 let getFreshRef = ele => cy.getElementById(ele.id());335 336 if( is.plainObject( obj ) ){ // set337 338 cy.startBatch();339 340 if( obj.elements ){341 let idInJson = {};342 343 let updateEles = function( jsons, gr ){344 let toAdd = [];345 let toMod = [];346 347 for( let i = 0; i < jsons.length; i++ ){348 let json = jsons[ i ];349 350 if( !json.data.id ){351 util.warn( 'cy.json() cannot handle elements without an ID attribute' );352 continue;353 }354 355 let id = '' + json.data.id; // id must be string356 let ele = cy.getElementById( id );357 358 idInJson[ id ] = true;359 360 if( ele.length !== 0 ){ // existing element should be updated361 toMod.push({ ele, json });362 } else { // otherwise should be added363 if( gr ){364 json.group = gr;365 366 toAdd.push( json );367 } else {368 toAdd.push( json );369 }370 }371 }372 373 cy.add( toAdd );374 375 for( let i = 0; i < toMod.length; i++ ){376 let { ele, json } = toMod[i];377 378 ele.json(json);379 }380 };381 382 if( is.array( obj.elements ) ){ // elements: []383 updateEles( obj.elements );384 385 } else { // elements: { nodes: [], edges: [] }386 let grs = [ 'nodes', 'edges' ];387 for( let i = 0; i < grs.length; i++ ){388 let gr = grs[ i ];389 let elements = obj.elements[ gr ];390 391 if( is.array( elements ) ){392 updateEles( elements, gr );393 }394 }395 }396 397 let parentsToRemove = cy.collection();398 399 (eles400 .filter(ele => !idInJson[ ele.id() ])401 .forEach(ele => {402 if ( ele.isParent() ) {403 parentsToRemove.merge(ele);404 } else {405 ele.remove();406 }407 })408 );409 410 // so that children are not removed w/parent411 parentsToRemove.forEach(ele => ele.children().move({ parent: null }));412 413 // intermediate parents may be moved by prior line, so make sure we remove by fresh refs414 parentsToRemove.forEach(ele => getFreshRef(ele).remove());415 }416 417 if( obj.style ){418 cy.style( obj.style );419 }420 421 if( obj.zoom != null && obj.zoom !== _p.zoom ){422 cy.zoom( obj.zoom );423 }424 425 if( obj.pan ){426 if( obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y ){427 cy.pan( obj.pan );428 }429 }430 431 if( obj.data ){432 cy.data( obj.data );433 }434 435 let fields = [436 'minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled',437 'panningEnabled', 'userPanningEnabled',438 'boxSelectionEnabled',439 'autolock', 'autoungrabify', 'autounselectify',440 'multiClickDebounceTime'441 ];442 443 for( let i = 0; i < fields.length; i++ ){444 let f = fields[ i ];445 446 if( obj[ f ] != null ){447 cy[ f ]( obj[ f ] );448 }449 }450 451 cy.endBatch();452 453 return this; // chaining454 } else { // get455 let flat = !!obj;456 let json = {};457 458 if( flat ){459 json.elements = this.elements().map( ele => ele.json() );460 } else {461 json.elements = {};462 463 eles.forEach( function( ele ){464 let group = ele.group();465 466 if( !json.elements[ group ] ){467 json.elements[ group ] = [];468 }469 470 json.elements[ group ].push( ele.json() );471 } );472 }473 474 if( this._private.styleEnabled ){475 json.style = cy.style().json();476 }477 478 json.data = util.copy( cy.data() );479 480 let options = _p.options;481 482 json.zoomingEnabled = _p.zoomingEnabled;483 json.userZoomingEnabled = _p.userZoomingEnabled;484 json.zoom = _p.zoom;485 json.minZoom = _p.minZoom;486 json.maxZoom = _p.maxZoom;487 json.panningEnabled = _p.panningEnabled;488 json.userPanningEnabled = _p.userPanningEnabled;489 json.pan = util.copy( _p.pan );490 json.boxSelectionEnabled = _p.boxSelectionEnabled;491 json.renderer = util.copy( options.renderer );492 json.hideEdgesOnViewport = options.hideEdgesOnViewport;493 json.textureOnViewport = options.textureOnViewport;494 json.wheelSensitivity = options.wheelSensitivity;495 json.motionBlur = options.motionBlur;496 json.multiClickDebounceTime = options.multiClickDebounceTime;497 498 return json;499 }500 }501 502} );503 504corefn.$id = corefn.getElementById;505 506[507 addRemove,508 animation,509 events,510 exportFormat,511 layout,512 notification,513 renderer,514 search,515 style,516 viewport,517 data518].forEach( function( props ){519 util.extend( corefn, props );520} );521 522export default Core;523 