import _ from "lodash" ; import { IColorValueMap } from "../ui/react-pathway-mapper" ; import GraphUtilities from "../utils/GraphUtilities" ; import EditorActionsManager from "./EditorActionsManager" ; let sharedb ; let socket ; let connection ; export default class ShareDBManager { readonly NODEMAP_NAME = 'nodes' ; readonly EDGEMAP_NAME = 'edges' ; readonly LAYOUT_PROPS_NAME = 'layoutProperties' ; readonly GLOBAL_OPTS_NAME = 'globalOptions' ; readonly GENOMIC_DATA_MAP_NAME = 'genomicDataMap' ; readonly VISIBLE_GENOMIC_DATA_MAP_NAME = 'visibleGenomicDataMapByType' ; readonly GENOMIC_DATA_GROUP_NAME = 'genomicDataGroupList' ; readonly GENOMIC_DATA_GROUP_COUNT = 'genomicDataGroupCount' ; readonly GENOMIC_DATA_COLOR_SCHEME_NAME = 'genomicDataColorScheme' ; doc : any ; postFileLoad : any ; editor : EditorActionsManager ; constructor ( postFileLoadCallback ) { this . postFileLoad = postFileLoadCallback ; } ; getDoc ( ) { return this . doc ; } setEditor ( editor ) { this . editor = editor ; } applyShareDBOperation ( op ) { this . doc . submitOp ( op , this . shareDBError ) ; } ; clearShareDBGenomicData ( ) { var ops = [ ] ; var genomicMap = this . doc . data [ this . GENOMIC_DATA_MAP_NAME ] ; var visMap = this . doc . data [ this . VISIBLE_GENOMIC_DATA_MAP_NAME ] ; var genomicDataGroupMap = this . doc . data [ this . GENOMIC_DATA_GROUP_NAME ] ; var genomicDataGroupCount = this . doc . data [ this . GENOMIC_DATA_GROUP_COUNT ] ; for ( const key of Object . keys ( genomicMap ) ) { ops . push ( { p : [ this . GENOMIC_DATA_MAP_NAME , key ] , od : genomicMap [ key ] } ) ; } for ( const key of Object . keys ( visMap ) ) { ops . push ( { p : [ this . VISIBLE_GENOMIC_DATA_MAP_NAME , key ] , od : visMap [ key ] } ) ; } for ( const key of Object . keys ( genomicDataGroupMap ) ) { ops . push ( { p : [ this . GENOMIC_DATA_GROUP_NAME , key ] , od : genomicDataGroupMap [ key ] } ) ; } ops . push ( { p : [ this . GENOMIC_DATA_GROUP_COUNT ] , na : - genomicDataGroupCount } ) ; this . doc . submitOp ( ops , this . shareDBError ) ; } ; updateShareDBObject ( mapName , objectKey , object ) { this . doc . submitOp ( [ { p : [ mapName , objectKey ] , od : this . doc . data [ mapName ] [ objectKey ] , oi : object } ] , this . shareDBError ) ; } ; insertShareDBObject ( mapName , objectKey , object ) { this . doc . submitOp ( [ { p : [ mapName , objectKey ] , oi : object } ] , this . shareDBError ) ; } ; deleteShareDBObject ( mapName , objectKey ) { this . doc . submitOp ( [ { p : [ mapName , objectKey ] , od : this . doc . data [ mapName ] [ objectKey ] } ] , this . shareDBError ) ; } ; initializeShareDBLayoutProperties ( ) { this . doc . submitOp ( [ { p : [ this . LAYOUT_PROPS_NAME , 0 ] , li : [ this . editor . layoutProperties ] } ] , this . shareDBError ) ; } ; initializeShareDBGlobalOptions ( ) { this . doc . submitOp ( [ { p : [ this . GLOBAL_OPTS_NAME , 0 ] , li : [ this . editor . getGlobalOptions ( ) ] } ] , this . shareDBError ) ; } ; updateShareDBLayoutProperties ( object ) { this . doc . submitOp ( [ { p : [ this . LAYOUT_PROPS_NAME , 0 ] , ld : this . doc . data [ this . LAYOUT_PROPS_NAME ] [ 0 ] , li : object } ] , this . shareDBError ) ; } ; updateShareDBGlobalOptions ( object ) { this . doc . submitOp ( [ { p : [ this . GLOBAL_OPTS_NAME , 0 ] , ld : this . doc . data [ this . GLOBAL_OPTS_NAME ] [ 0 ] , li : object } ] , this . shareDBError ) ; } ; updateShareDBGenomicDataOverlayColorScheme ( object ) { this . doc . submitOp ( [ { p : [ this . GENOMIC_DATA_COLOR_SCHEME_NAME , 0 ] , ld : this . doc . data [ this . GENOMIC_DATA_COLOR_SCHEME_NAME ] [ 0 ] , li : object } ] , this . shareDBError ) ; } ; incrementShareDBGroupCount ( ) { this . doc . submitOp ( [ { p : [ this . GENOMIC_DATA_GROUP_COUNT ] , na : 1 } ] , this . shareDBError ) ; } ; isShareDBReplaceEvent ( op ) { return ( op . hasOwnProperty ( "oi" ) && op . hasOwnProperty ( "od" ) ) || ( op . hasOwnProperty ( "li" ) && op . hasOwnProperty ( "ld" ) ) ; } ; initializeSharedDBDoc ( ) { this . doc . subscribe ( ) ; } ; shareDBError ( err ) { if ( err ) { console . error ( err ) ; } } ; initShareDB ( ) { sharedb = require ( 'sharedb/lib/client' ) ; socket = new WebSocket ( 'ws://' + window . location . host ) ; connection = new sharedb . Connection ( socket ) ; var self = this ; var id = this . getParam ( 'id' ) ; var loadFileCallback = function ( ) { self . onFileLoaded ( ) ; } ; var createNewDocument = ( ) => { var new_id = self . getCustomObjId ( ) ; var data = { nodes : { } , edges : { } , layoutProperties : [ this . editor . layoutProperties ] , globalOptions : [ this . editor . getGlobalOptions ( ) ] , genomicDataMap : { } , visibleGenomicDataMapByType : { } , genomicDataGroupList : { } , genomicDataGroupCount : 0 , genomicDataColorScheme : [ { '-100' : "#0000ff" , '0' : "#ffffff" , '100' : "#ff0000" } ] } ; window . history . pushState ( null , null , '?id=' + new_id ) ; self . doc = connection . get ( 'cy' , new_id ) ; self . doc . create ( data , loadFileCallback ) ; } ; if ( id ) { this . doc = connection . get ( 'cy' , id ) ; this . doc . fetch ( function ( err ) { if ( err ) throw err ; if ( self . doc . type === null ) { createNewDocument ( ) ; return ; } self . doc . subscribe ( loadFileCallback ) ; } ) ; } else { createNewDocument ( ) ; } } ; onFileLoaded ( ) { this . initializeSharedDBDoc ( ) ; this . syncInitialSharedDocData ( ) ; this . initSharedDocEventHandlers ( ) ; this . postFileLoad ( ) ; } ; syncInitialSharedDocData ( ) { var self = this ; var nodeMap = self . doc . data [ this . NODEMAP_NAME ] ; var edgeMap = self . doc . data [ this . EDGEMAP_NAME ] ; var shareDBLayoutProperties = self . doc . data [ this . LAYOUT_PROPS_NAME ] [ 0 ] ; var globalOptions = self . doc . data [ this . GLOBAL_OPTS_NAME ] [ 0 ] ; var genomicDataMap = self . doc . data [ this . GENOMIC_DATA_MAP_NAME ] ; var visDataMap = self . doc . data [ this . VISIBLE_GENOMIC_DATA_MAP_NAME ] ; var groupedGenomicDataMap = self . doc . data [ this . GENOMIC_DATA_GROUP_NAME ] ; var groupedGenomicDataCount = self . doc . data [ this . GENOMIC_DATA_GROUP_COUNT ] ; let genomicDataColorScheme ; if ( self . doc . data [ this . GENOMIC_DATA_COLOR_SCHEME_NAME ] ) { genomicDataColorScheme = self . doc . data [ this . GENOMIC_DATA_COLOR_SCHEME_NAME ] [ 0 ] ; } var invalidGenes = [ ] ; var highlightedGenes = [ ] ; var invalidHighlightedGenes = [ ] ; var hiddenGenes = [ ] ; for ( const key of Object . keys ( nodeMap ) ) { var tmpNode = nodeMap [ key ] ; var tmpNodeId = tmpNode . id ; if ( tmpNode . isInvalidGene && tmpNode . isHighlighted ) { invalidHighlightedGenes . push ( tmpNodeId ) ; } else if ( tmpNode . isInvalidGene ) { invalidGenes . push ( tmpNodeId ) ; } else if ( tmpNode . isHighlighted ) { highlightedGenes . push ( tmpNodeId ) ; } if ( tmpNode . isHidden ) { hiddenGenes . push ( tmpNodeId ) ; } } var highlightedEdges = [ ] ; for ( const key of Object . keys ( edgeMap ) ) { var tmpEdge = edgeMap [ key ] ; var tmpEdgeId = tmpEdge . id ; if ( tmpEdge . isHighlighted ) { highlightedEdges . push ( tmpEdgeId ) ; } } for ( const key of Object . keys ( edgeMap ) ) { const tmpEdge = edgeMap [ key ] ; if ( tmpEdge . pubmedIDs == undefined || tmpEdge . name == undefined || tmpEdge . bendPoint == undefined || tmpEdge . anchorPoints == undefined || tmpEdge . edgeCurveStyle == undefined ) { const pubmedIDs = ( tmpEdge . pubmedIDs == undefined ) ? [ ] : tmpEdge . pubmedID ; const edgeLabel = ( tmpEdge . name == undefined ) ? "" : tmpEdge . name ; let anchorPoints = [ ] ; if ( tmpEdge . bendPoint ) { anchorPoints = tmpEdge . bendPoint ; } else if ( tmpEdge . anchorPoints ) { anchorPoints = tmpEdge . anchorPoints ; } const edgeCurveStyle = tmpEdge . edgeCurveStyle ? tmpEdge . edgeCurveStyle : "bezier" ; const param = { type : tmpEdge . type , source : tmpEdge . source , id : self . getCustomObjId ( ) , target : tmpEdge . target , pubmedID : pubmedIDs , name : edgeLabel , anchorPoints : anchorPoints , edgeCurveStyle : edgeCurveStyle } ; const newEdge = self . edgeInitializer ( param ) ; const tmpEdgeID = tmpEdge . id ; const newEdgeID = newEdge . id ; const ops = [ { p : [ self . EDGEMAP_NAME , tmpEdgeID ] , od : tmpEdge } , { p : [ self . EDGEMAP_NAME , newEdgeID ] , oi : newEdge } ] ; self . applyShareDBOperation ( ops ) ; } } this . editor . addNewElementsLocally ( nodeMap , edgeMap ) ; this . editor . highlightElementsInitially ( invalidHighlightedGenes , invalidGenes , highlightedGenes , highlightedEdges , hiddenGenes ) ; if ( shareDBLayoutProperties . name == "cose-bilkent" ) { shareDBLayoutProperties . name = "fcose" ; this . updateLayoutProperties ( shareDBLayoutProperties ) ; } this . editor . updateLayoutPropertiesCallback ( { li : shareDBLayoutProperties } ) ; this . editor . changeGlobalOptions ( { li : globalOptions } ) ; if ( ! groupedGenomicDataMap ) { self . insertShareDBObject ( self . GENOMIC_DATA_MAP_NAME , '0' , [ ] ) ; for ( const key of Object . keys ( visDataMap ) ) { var currentMap = _ . clone ( groupedGenomicDataMap [ '0' ] ) ; self . updateShareDBObject ( self . GENOMIC_DATA_MAP_NAME , '0' , currentMap ) ; } } if ( ! groupedGenomicDataCount ) { var count = self . doc . data [ self . GENOMIC_DATA_GROUP_COUNT ] ; var op = [ { p : [ self . GENOMIC_DATA_GROUP_COUNT ] , na : - count } ] ; self . applyShareDBOperation ( op ) ; groupedGenomicDataCount = self . doc . data [ self . GENOMIC_DATA_GROUP_COUNT ] ; } if ( ! genomicDataColorScheme ) { genomicDataColorScheme = this . editor . getGenomicDataOverlayColorScheme ( ) ; const op = [ { p : [ self . GENOMIC_DATA_COLOR_SCHEME_NAME ] , oi : [ genomicDataColorScheme ] } ] ; this . applyShareDBOperation ( op ) ; } this . editor . updateGenomicDataColorSchemeHandler ( { li : genomicDataColorScheme } ) ; for ( const key_g of Object . keys ( genomicDataMap ) ) { this . editor . genomicDataOverlayManager . genomicDataMap [ key_g ] = genomicDataMap [ key_g ] ; } for ( const key_g of Object . keys ( groupedGenomicDataMap ) ) { this . editor . genomicDataOverlayManager . groupedGenomicDataMap [ key_g ] = groupedGenomicDataMap [ key_g ] ; const data = groupedGenomicDataMap [ key_g ] ; if ( data . length !== 1 ) { console . log ( "Grouped genomic data expected to be of length 1 (from sync)" ) ; } data . forEach ( ( profileId ) => { this . editor . addToProfiles ( profileId ) ; } ) ; } for ( const key_g of Object . keys ( visDataMap ) ) { this . editor . genomicDataOverlayManager . visibleGenomicDataMapByType [ key_g ] = visDataMap [ key_g ] ; this . editor . adjustVisibilityShareDB ( key_g , visDataMap [ key_g ] ) ; } this . editor . genomicDataOverlayManager . groupedGenomicDataCount = groupedGenomicDataCount ; this . editor . genomicDataOverlayManager . showGenomicData ( ( node ) => { this . editor . resizeElements ( node ) ; } ) ; this . editor . genomicDataOverlayManager . notifyObservers ( ) ; this . editor . cy . style ( ) . update ( ) ; this . editor . cy . fit ( 50 ) ; } ; initSharedDocEventHandlers ( ) { var self = this ; var nodeAddRemoveHandler = function ( op ) { self . editor . shareDBNodeAddRemoveEventCallBack ( op ) ; } ; var edgeAddRemoveHandler = function ( op ) { self . editor . shareDBEdgeAddRemoveEventCallBack ( op ) ; } ; var genomicDataAddRemoveHandler = function ( op ) { self . editor . shareDBGenomicDataHandler ( op ) ; } ; var genomicDataVisibilityChangeHandler = function ( op ) { self . editor . shareDBGenomicDataVisibilityHandler ( op ) ; self . checkShareDBGenomicData ( ) ; } ; var genomicDataGroupChangeHandler = function ( op ) { self . editor . shareDBGenomicDataGroupChangeHandler ( op ) ; } ; var updateElementHandler = function ( op ) { self . editor . updateElementCallback ( op ) ; } ; var updateLayoutPropsHandler = function ( op ) { self . editor . updateLayoutPropertiesCallback ( op ) ; } ; var updateGlobalOptionsHandler = function ( op ) { self . editor . changeGlobalOptions ( op ) ; } ; var updateGenomicDataColorSchemeHandler = function ( op ) { self . editor . updateGenomicDataColorSchemeHandler ( op ) ; } ; this . doc . on ( 'op' , function ( op , source ) { for ( var i = 0 ; i < op . length ; i ++ ) { var handleOp = op [ i ] ; var path = handleOp . p [ 0 ] ; var isReplaceEvent = self . isShareDBReplaceEvent ( handleOp ) ; if ( ! isReplaceEvent ) { if ( path === self . NODEMAP_NAME ) { nodeAddRemoveHandler ( handleOp ) ; } else if ( path === self . EDGEMAP_NAME ) { edgeAddRemoveHandler ( handleOp ) ; } else if ( path === self . GENOMIC_DATA_MAP_NAME ) { genomicDataAddRemoveHandler ( handleOp ) ; } else if ( path === self . VISIBLE_GENOMIC_DATA_MAP_NAME ) { genomicDataVisibilityChangeHandler ( handleOp ) ; } else if ( path === self . GENOMIC_DATA_GROUP_NAME ) { genomicDataGroupChangeHandler ( handleOp ) ; } } else { if ( path === self . NODEMAP_NAME ) { updateElementHandler ( handleOp ) ; } else if ( path === self . EDGEMAP_NAME ) { updateElementHandler ( handleOp ) ; } else if ( path === self . LAYOUT_PROPS_NAME ) { updateLayoutPropsHandler ( handleOp ) ; } else if ( path === self . GLOBAL_OPTS_NAME ) { updateGlobalOptionsHandler ( handleOp ) ; } else if ( path === self . GENOMIC_DATA_COLOR_SCHEME_NAME ) { updateGenomicDataColorSchemeHandler ( handleOp ) ; } } } } ) ; } ; checkShareDBGenomicData ( ) { var self = this ; var genomicDataMap = self . doc . data [ this . GENOMIC_DATA_MAP_NAME ] ; var visDataMap = self . doc . data [ this . VISIBLE_GENOMIC_DATA_MAP_NAME ] ; var groupedGenomicDataMap = self . doc . data [ this . GENOMIC_DATA_GROUP_NAME ] ; var groupedGenomicDataCount = self . doc . data [ this . GENOMIC_DATA_GROUP_COUNT ] ; for ( var key in genomicDataMap ) { this . editor . genomicDataOverlayManager . genomicDataMap [ key ] = genomicDataMap [ key ] ; } for ( var key in visDataMap ) { this . editor . genomicDataOverlayManager . visibleGenomicDataMapByType [ key ] = visDataMap [ key ] ; } for ( var key in groupedGenomicDataMap ) { this . editor . genomicDataOverlayManager . groupedGenomicDataMap [ key ] = groupedGenomicDataMap [ key ] ; } this . editor . genomicDataOverlayManager . groupedGenomicDataCount = groupedGenomicDataCount ; this . editor . genomicDataOverlayManager . showGenomicData ( ( node ) => { this . editor . resizeElements ( node ) ; } ) ; this . editor . genomicDataOverlayManager . notifyObservers ( ) ; } ; getEmptyGroupID ( ) { var returnCount = this . doc . data [ this . GENOMIC_DATA_GROUP_COUNT ] ; this . incrementShareDBGroupCount ( ) ; return returnCount ; } ; groupGenomicData ( cancerNames , inGroupId ) { var genomicGroupMap = this . doc . data [ this . GENOMIC_DATA_GROUP_NAME ] ; var genomicVisMap = this . doc . data [ this . VISIBLE_GENOMIC_DATA_MAP_NAME ] ; var groupID = "" + inGroupId ; var currentGroup = [ ] ; if ( genomicGroupMap . hasOwnProperty ( groupID ) ) currentGroup = _ . clone ( genomicGroupMap [ groupID ] ) ; for ( var i in cancerNames ) { if ( ! genomicVisMap . hasOwnProperty ( cancerNames [ i ] ) ) currentGroup . push ( cancerNames [ i ] ) ; } if ( genomicGroupMap . hasOwnProperty ( groupID ) ) { this . updateShareDBObject ( this . GENOMIC_DATA_GROUP_NAME , groupID , currentGroup ) ; } else { this . insertShareDBObject ( this . GENOMIC_DATA_GROUP_NAME , groupID , currentGroup ) ; } } ; clearGenomicData ( ) { this . clearShareDBGenomicData ( ) ; } ; addGenomicData ( geneData ) { var genomicMap = this . doc . data [ this . GENOMIC_DATA_MAP_NAME ] ; var ops = [ ] ; for ( var geneSymbol in geneData ) { var genomicMapEntry = { } ; if ( genomicMap . hasOwnProperty ( geneSymbol ) ) genomicMapEntry = _ . clone ( genomicMap [ geneSymbol ] ) ; for ( var cancerType in geneData [ geneSymbol ] ) { if ( ! ( cancerType in genomicMapEntry ) ) genomicMapEntry [ cancerType ] = parseInt ( geneData [ geneSymbol ] [ cancerType ] ) . toFixed ( 2 ) ; } ops . push ( { p : [ this . GENOMIC_DATA_MAP_NAME , geneSymbol ] , oi : genomicMapEntry } ) ; } this . applyShareDBOperation ( ops ) ; } ; addGenomicVisibilityData ( visMap ) { var ops = [ ] ; for ( var cancerStudy in visMap ) { ops . push ( { p : [ this . VISIBLE_GENOMIC_DATA_MAP_NAME , cancerStudy ] , oi : visMap [ cancerStudy ] } ) ; } this . applyShareDBOperation ( ops ) ; } ; changeVisibility ( nodesToHide , isHidden ) { var self = this ; var nodeMap = self . doc . data [ this . NODEMAP_NAME ] ; nodesToHide . forEach ( function ( ele ) { var nodeID = ele . id ( ) ; if ( nodeMap . hasOwnProperty ( nodeID ) ) { var realTimeNode = nodeMap [ nodeID ] ; realTimeNode . isHidden = isHidden ; self . updateShareDBObject ( self . NODEMAP_NAME , nodeID , realTimeNode ) ; } } ) ; } ; changeHighlight ( elementsToHighlight , isHighlighted ) { var self = this ; var nodeMap = self . doc . data [ this . NODEMAP_NAME ] ; var edgeMap = self . doc . data [ this . EDGEMAP_NAME ] ; elementsToHighlight . forEach ( function ( ele ) { var elementID = ele . id ( ) ; if ( nodeMap . hasOwnProperty ( elementID ) ) { var realTimeNode = nodeMap [ elementID ] ; realTimeNode . isHighlighted = isHighlighted ; self . updateShareDBObject ( self . NODEMAP_NAME , elementID , realTimeNode ) ; } if ( edgeMap . hasOwnProperty ( elementID ) ) { var realTimeEdge = edgeMap [ elementID ] ; realTimeEdge . isHighlighted = isHighlighted ; self . updateShareDBObject ( self . EDGEMAP_NAME , elementID , realTimeEdge ) ; } } ) ; } ; addNewNode ( nodeData , posData ) { var realTimeGeneratedID = this . getCustomObjId ( ) ; var params = { name : nodeData . name , type : nodeData . type , id : realTimeGeneratedID , parent : nodeData . parent , w : nodeData . w , h : nodeData . h , minWidth : nodeData . minWidth , minWidthBiasLeft : nodeData . minWidthBiasLeft , minWidthBiasRight : nodeData . minWidthBiasRight , minHeight : nodeData . minHeight , minHeightBiasTop : nodeData . minHeightBiasTop , minHeightBiasBottom : nodeData . minHeightBiasBottom } ; var newNode = this . nodeInitializer ( params ) ; if ( posData ) { newNode . x = posData . x ; newNode . y = posData . y ; } this . insertShareDBObject ( this . NODEMAP_NAME , realTimeGeneratedID , newNode ) ; } ; addNewEdge ( edgeData ) { var realTimeGeneratedID = this . getCustomObjId ( ) ; var params = { type : edgeData . type , id : realTimeGeneratedID , source : edgeData . source , target : edgeData . target , pubmedIDs : edgeData . pubmedIDs , name : edgeData . name , anchorPoints : edgeData . anchorPoints } ; var newEdge = this . edgeInitializer ( params ) ; this . insertShareDBObject ( this . EDGEMAP_NAME , realTimeGeneratedID , newEdge ) ; } ; reconnectEdge = function ( sourceID , targetID , edgeData ) { var edgeMap = this . doc . data [ this . EDGEMAP_NAME ] ; var edge = edgeMap [ edgeData . id ] ; var edgeID = edge . id ; edge . source = sourceID ; edge . target = targetID ; if ( edgeMap . hasOwnProperty ( edgeID ) ) { this . updateShareDBObject ( this . EDGEMAP_NAME , edgeID , edge ) ; } else { throw new Error ( 'Element does not exist in Real Time' ) ; } } ; removeElement ( elementID ) { var edgeMap = this . doc . data [ this . EDGEMAP_NAME ] ; var nodeMap = this . doc . data [ this . NODEMAP_NAME ] ; if ( nodeMap . hasOwnProperty ( elementID ) ) { this . deleteShareDBObject ( this . NODEMAP_NAME , elementID ) ; } else if ( edgeMap . hasOwnProperty ( elementID ) ) { this . deleteShareDBObject ( this . EDGEMAP_NAME , elementID ) ; } else { throw new Error ( 'Element does not exist in Real Time' ) ; } } ; moveElement ( ele ) { var nodeMap = this . doc . data [ this . NODEMAP_NAME ] ; var elementID = ele . id ( ) ; var newPos = ele . position ( ) ; if ( nodeMap . hasOwnProperty ( elementID ) ) { var tmpNode = nodeMap [ elementID ] ; tmpNode . x = newPos . x ; tmpNode . y = newPos . y ; this . updateShareDBObject ( this . NODEMAP_NAME , elementID , tmpNode ) ; } else { throw new Error ( 'Element does not exist in nodes !!! ' ) ; } } ; changeElementsPositionByAlignment ( coll ) { var self = this ; var nodeMap = self . doc . data [ this . NODEMAP_NAME ] ; coll . forEach ( function ( ele ) { var elementID = ele . node . id ( ) ; if ( nodeMap . hasOwnProperty ( elementID ) ) { var tmpNode = nodeMap [ elementID ] ; tmpNode . x = ele . nextPosition . x ; tmpNode . y = ele . nextPosition . y ; self . updateShareDBObject ( self . NODEMAP_NAME , elementID , tmpNode ) ; } else { throw new Error ( 'Element does not exist in nodes !!! ' ) ; } } ) ; } ; resizeElement ( ele , previousWidth , previousHeight ) { var nodeMap = this . doc . data [ this . NODEMAP_NAME ] ; var elementID = ele . id ( ) ; var newWidth = ele . width ( ) ; var newHeight = ele . height ( ) ; var currentX = ele . position ( 'x' ) ; var currentY = ele . position ( 'y' ) ; if ( nodeMap . hasOwnProperty ( elementID ) ) { var tmpNode = nodeMap [ elementID ] ; tmpNode . x = currentX + newWidth - previousWidth ; tmpNode . y = currentY + newHeight - previousHeight ; tmpNode . w = newWidth ; tmpNode . h = newHeight ; this . updateShareDBObject ( this . NODEMAP_NAME , elementID , tmpNode ) ; } else { throw new Error ( 'Element does not exist in nodes !!! ' ) ; } } ; setSizeOfElement ( ele , newWidth , newHeight ) { var nodeMap = this . doc . data [ this . NODEMAP_NAME ] ; var elementID = ele . id ( ) ; if ( nodeMap . hasOwnProperty ( elementID ) ) { var tmpNode = nodeMap [ elementID ] ; tmpNode . w = newWidth ; tmpNode . h = newHeight ; this . updateShareDBObject ( this . NODEMAP_NAME , elementID , tmpNode ) ; } else { throw new Error ( 'Element does not exist in nodes !!! ' ) ; } } ; resizeCompound ( ele , minWidth , minWidthBiasLeft , minWidthBiasRight , minHeight , minHeightBiasTop , minHeightBiasBottom ) { var nodeMap = this . doc . data [ this . NODEMAP_NAME ] ; var elementID = ele . id ( ) ; var currentX = ele . position ( 'x' ) ; var currentY = ele . position ( 'y' ) ; if ( nodeMap . hasOwnProperty ( elementID ) ) { var tmpNode = nodeMap [ elementID ] ; tmpNode . x = currentX ; tmpNode . y = currentY ; tmpNode . minWidth = minWidth ; tmpNode . minWidthBiasLeft = minWidthBiasLeft ; tmpNode . minWidthBiasRight = minWidthBiasRight ; tmpNode . minHeight = minHeight ; tmpNode . minHeightBiasTop = minHeightBiasTop ; tmpNode . minHeightBiasBottom = minHeightBiasBottom ; this . updateShareDBObject ( this . NODEMAP_NAME , elementID , tmpNode ) ; } else { throw new Error ( 'Element does not exist in nodes !!! ' ) ; } } ; changeNodePositionsShareDB ( nodes ) { var self = this ; var nodeMap = self . doc . data [ self . NODEMAP_NAME ] ; nodes . forEach ( function ( ele ) { var nodeID = ele . id ( ) ; if ( nodeMap . hasOwnProperty ( nodeID ) ) { var realTimeNode = nodeMap [ nodeID ] ; realTimeNode . x = ele . position ( 'x' ) ; realTimeNode . y = ele . position ( 'y' ) ; self . updateShareDBObject ( self . NODEMAP_NAME , nodeID , realTimeNode ) ; } else { throw new Error ( 'Element does not exist in nodes !!! ' ) ; } } ) ; } ; changeHighlightInvalidGenes ( nodeIDs , isInvalid ) { var nodeMap = this . doc . data [ this . NODEMAP_NAME ] ; for ( var i in nodeIDs ) { var nodeID = nodeIDs [ i ] ; if ( nodeMap . hasOwnProperty ( nodeID ) ) { var collaborativeNode = nodeMap [ nodeID ] ; collaborativeNode . isInvalidGene = isInvalid ; this . updateShareDBObject ( this . NODEMAP_NAME , nodeID , collaborativeNode ) ; } } } ; addPubmedIDs ( edgeID , pubmedIDs ) { var edgeMap = this . doc . data [ this . EDGEMAP_NAME ] ; if ( edgeMap . hasOwnProperty ( edgeID ) ) { var tmpEdge = edgeMap [ edgeID ] ; var nonDuplicateArray = [ ] ; for ( var i = 0 ; i < pubmedIDs . length ; i ++ ) { if ( tmpEdge . pubmedIDs . indexOf ( pubmedIDs [ i ] ) < 0 ) { nonDuplicateArray . push ( pubmedIDs [ i ] ) ; } } tmpEdge . pubmedIDs = tmpEdge . pubmedIDs . concat ( nonDuplicateArray ) ; this . updateShareDBObject ( this . EDGEMAP_NAME , edgeID , tmpEdge ) ; } else { throw new Error ( 'Edge does not exist in real time !!! ' ) ; } } ; removePubmedID ( edgeID , pubmedIDs ) { var edgeMap = this . doc . data [ this . EDGEMAP_NAME ] ; if ( edgeMap . hasOwnProperty ( edgeID ) ) { var tmpEdge = edgeMap [ edgeID ] ; var removedIndices = [ ] ; for ( var i = 0 ; i < pubmedIDs . length ; i ++ ) { var tmpID = pubmedIDs [ i ] ; var index = tmpEdge . pubmedIDs . indexOf ( tmpID ) ; if ( index >= 0 ) { removedIndices . push ( index ) ; } } for ( var i = 0 ; i < removedIndices . length ; i ++ ) { tmpEdge . pubmedIDs . remove ( removedIndices [ i ] ) ; } this . updateShareDBObject ( this . EDGEMAP_NAME , edgeID , tmpEdge ) ; } else { throw new Error ( 'Edge does not exist in real time !!! ' ) ; } } ; updateEdgeAnchorPoints ( edgeID , anchorPointsArray , edgeCurveStyle ) { var edgeMap = this . doc . data [ this . EDGEMAP_NAME ] ; if ( edgeMap . hasOwnProperty ( edgeID ) ) { var tmpEdge = edgeMap [ edgeID ] ; tmpEdge . anchorPoints = anchorPointsArray ; tmpEdge . edgeCurveStyle = edgeCurveStyle ; this . updateShareDBObject ( this . EDGEMAP_NAME , edgeID , tmpEdge ) ; } else { throw new Error ( 'Edge does not exist in real time !!! ' ) ; } } ; changeName ( ele , newName ) { var nodeMap = this . doc . data [ this . NODEMAP_NAME ] ; var edgeMap = this . doc . data [ this . EDGEMAP_NAME ] ; var elementID = ele . id ( ) ; if ( ele . isNode ( ) ) { if ( nodeMap . hasOwnProperty ( elementID ) ) { var tmpNode = nodeMap [ elementID ] ; tmpNode . name = newName ; this . updateShareDBObject ( this . NODEMAP_NAME , elementID , tmpNode ) ; } else { throw new Error ( 'Element does not exist in nodes !!! ' ) ; } } else { if ( edgeMap . hasOwnProperty ( elementID ) ) { var tmpEdge = edgeMap [ elementID ] ; tmpEdge . name = newName ; this . updateShareDBObject ( this . EDGEMAP_NAME , elementID , tmpEdge ) ; } else { throw new Error ( 'Element does not exist in edges !!! ' ) ; } } } ; changeParent ( rootNode , newParentId , connectedEdges ) { var self = this ; var nodeLookupTable = { } ; function traverseFromRoot ( rootNode , parId ) { var refNode = rootNode . nodeRef ; var children = rootNode . children ; var newParentId = parId ; if ( refNode ) { var refNodeId = refNode . id ( ) ; var nodeData = refNode . data ( ) ; var posData = refNode . position ( ) ; var currentWidth = refNode . data ( 'w' ) ; var currentHeight = refNode . data ( 'h' ) ; var newNodeData = { name : nodeData . name , type : nodeData . type , x : posData . x , y : posData . y , w : currentWidth , h : currentHeight , parent : - 1 } ; if ( parId ) { newNodeData . parent = parId ; } self . removeElement ( refNodeId ) ; var newNode = self . nodeInitializer ( newNodeData ) ; var newNodeId = newNode . id ; self . insertShareDBObject ( self . NODEMAP_NAME , newNodeId , newNode ) ; newParentId = newNodeId ; nodeLookupTable [ refNodeId ] = newNodeId ; } for ( const childNode of children ) { traverseFromRoot ( childNode , newParentId ) ; } } traverseFromRoot ( rootNode , newParentId ) ; connectedEdges . forEach ( function ( edge , index ) { var edgeData = edge . data ( ) ; self . removeElement ( edge . id ( ) ) ; var newSource = nodeLookupTable [ edgeData . source ] ; var newTarget = nodeLookupTable [ edgeData . target ] ; if ( newSource ) { edgeData . source = newSource ; } if ( newTarget ) { edgeData . target = newTarget ; } self . addNewEdge ( edgeData ) ; } ) ; } ; removeAllElements ( ) { var nodeMap = this . doc . data [ this . NODEMAP_NAME ] ; var edgeMap = this . doc . data [ this . EDGEMAP_NAME ] ; var ops = [ ] ; for ( const key of Object . keys ( nodeMap ) ) { ops . push ( { p : [ this . NODEMAP_NAME , key ] , od : nodeMap [ key ] } ) ; } for ( const key of Object . keys ( edgeMap ) ) { ops . push ( { p : [ this . EDGEMAP_NAME , key ] , od : edgeMap [ key ] } ) ; } this . applyShareDBOperation ( ops ) ; } ; loadGraph ( nodes , edges ) { var self = this ; this . removeAllElements ( ) ; var oldIdNewIdMap = { } ; function traverseTree ( node , newParentId ) { node . data . x = node . position . x ; node . data . y = node . position . y ; if ( newParentId ) { var parent = node . data . parent ; if ( parent ) { node . data . parent = newParentId ; } } var newNodeId = self . getCustomObjId ( ) ; var params = node . data ; oldIdNewIdMap [ params . id ] = newNodeId ; var newNode = self . nodeInitializer ( params ) ; newNode . id = newNodeId ; self . insertShareDBObject ( self . NODEMAP_NAME , newNodeId , newNode ) ; if ( node . children . length > 0 ) { for ( const tmpNode of node . children ) { traverseTree ( tmpNode , newNodeId ) ; } } } var tree = GraphUtilities . createGraphHierarchyRealTime ( nodes ) ; for ( const rootLevelNode of tree ) { traverseTree ( rootLevelNode , undefined ) ; } for ( const edge of edges ) { edge . data . source = oldIdNewIdMap [ edge . data . source ] ; edge . data . target = oldIdNewIdMap [ edge . data . target ] ; var edgeCurveStyle = "bezier" ; if ( edge . data . bendPointPositions && edge . data . bendPointPositions . length > 0 ) { edge . data [ 'anchorPoints' ] = edge . data [ 'bendPointPositions' ] ; delete edge . data [ 'bendPointPositions' ] ; edgeCurveStyle = "segments" ; } else if ( edge . data . controlPointPositions && edge . data . controlPointPositions . length > 0 ) { edge . data [ 'anchorPoints' ] = edge . data [ 'controlPointPositions' ] ; delete edge . data [ 'controlPointPositions' ] ; edgeCurveStyle = "unbundled-bezier" ; } var params = edge . data ; params . edgeCurveStyle = edgeCurveStyle ; var newEdgeID = self . getCustomObjId ( ) ; params . id = newEdgeID ; var newEdge = self . edgeInitializer ( params ) ; self . insertShareDBObject ( self . EDGEMAP_NAME , newEdgeID , newEdge ) ; } } ; mergeGraph ( nodes , edges ) { var self = this ; var nodeMap = self . doc . data [ this . NODEMAP_NAME ] ; var realTimeNodeMap = nodeMap ; var realTimeNodeLookupTable = { } ; var realTimeNodeNameLookupTable = { } ; var oldIdNewIdMap = { } ; for ( var i in realTimeNodeMap ) { var nodeMapItem = realTimeNodeMap [ i ] ; realTimeNodeLookupTable [ nodeMapItem . id ] = nodeMapItem ; realTimeNodeNameLookupTable [ nodeMapItem . name ] = nodeMapItem ; } function traverseTree ( node , newParentId ) { if ( ! ( node . data . name in realTimeNodeNameLookupTable ) ) { node . data . x = node . position . x ; node . data . y = node . position . y ; if ( newParentId ) { var parent = node . data . parent ; if ( parent ) { node . data . parent = newParentId ; } } var newNodeId = self . getCustomObjId ( ) ; oldIdNewIdMap [ node . data . id ] = newNodeId ; var newNode = self . nodeInitializer ( node . data ) ; newNode . id = newNodeId ; self . insertShareDBObject ( self . NODEMAP_NAME , newNodeId , newNode ) ; if ( node . children . length > 0 ) { for ( const tmpNode of node . children ) { traverseTree ( tmpNode , newNodeId ) ; } } } else { var sameNameNode = realTimeNodeNameLookupTable [ node . data . name ] ; var sameNodeId = sameNameNode . id ; oldIdNewIdMap [ node . data . id ] = sameNodeId ; if ( node . children . length > 0 ) { for ( const tmpNode of node . children ) { traverseTree ( tmpNode , sameNodeId ) ; } } } } var tree = GraphUtilities . createGraphHierarchyRealTime ( nodes ) ; for ( const rootLevelNode of tree ) { traverseTree ( rootLevelNode , rootLevelNode . data . id ) ; } var ops = [ ] ; for ( const edge of edges ) { edge . data . source = oldIdNewIdMap [ edge . data . source ] ; edge . data . target = oldIdNewIdMap [ edge . data . target ] ; var newEdge = self . edgeInitializer ( edge . data ) ; var newEdgeID = this . getCustomObjId ( ) ; newEdge . id = newEdgeID ; ops . push ( { p : [ self . EDGEMAP_NAME , newEdgeID ] , oi : newEdge } ) ; } self . applyShareDBOperation ( ops ) ; } ; updateLayoutProperties ( newLayoutProperties ) { this . updateShareDBLayoutProperties ( newLayoutProperties ) ; } ; updateGlobalOptions ( newOptions ) { this . updateShareDBGlobalOptions ( newOptions ) ; } ; updateGenomicDataOverlayColorScheme ( newColorScheme ) { this . updateShareDBGenomicDataOverlayColorScheme ( newColorScheme ) ; } createGraphHierarchy ( nodes ) { var tree = [ ] ; var mappedArr = { } ; for ( var i = 0 , len = nodes . length ; i < len ; i ++ ) { var arrElem = nodes [ i ] ; mappedArr [ arrElem . data . id ] = arrElem ; mappedArr [ arrElem . data . id ] . children = [ ] ; } for ( const id of Object . keys ( mappedArr ) ) { var mappedElem = mappedArr [ id ] ; if ( mappedElem . data . parent ) { mappedArr [ mappedElem . data . parent ] . children . push ( mappedElem ) ; } else { tree . push ( mappedElem ) ; } } return tree ; } ; edgeInitializer ( params ) { var edge = { } ; edge . id = params . id || this . getCustomObjId ( ) ; edge . type = params . type || "undefined" ; edge . source = params . source || "undefined" ; edge . target = params . target || "undefined" ; edge . name = params . name || "" ; edge . isHighlighted = params . isHighlighted || false ; edge . anchorPoints = params . anchorPoints || [ ] ; edge . edgeCurveStyle = params . edgeCurveStyle || "bezier" ; if ( params . pubmedIDs ) { if ( edge . pubmedIDs == undefined ) { edge . pubmedIDs = [ ] ; } if ( params . pubmedIDs . length > 0 ) edge . pubmedIDs . push ( params . pubmedIDs ) ; } else { edge . pubmedIDs = [ ] ; } if ( params . anchorPoints ) { if ( edge . anchorPoints == undefined ) { edge . anchorPoints = [ ] ; } if ( Array . isArray ( params . anchorPoints [ 0 ] ) ) { edge . anchorPoints = params . anchorPoints [ 0 ] ; } else { edge . anchorPoints = params . anchorPoints ; } } else { edge . anchorPoints = [ ] ; } return edge ; } ; nodeInitializer ( params ) { var node = { } ; node . id = params . id || this . getCustomObjId ( ) ; node . name = params . name || "undefined" ; node . type = params . type || "undefined" ; node . parent = params . parent || "undefined" ; node . x = params . x || "undefined" ; node . y = params . y || "undefined" ; node . w = params . w || "undefined" ; node . h = params . h || "undefined" ; node . minWidth = params . minWidth || 0 ; node . minWidthBiasLeft = params . minWidth || 0 ; node . minWidthBiasRight = params . minWidth || 0 ; node . minHeight = params . minWidth || 0 ; node . minHeightBiasTop = params . minWidth || 0 ; node . minHeightBiasBottom = params . minWidth || 0 ; node . isHidden = params . isHidden || false ; node . isInvalidGene = params . isInvalidGene || false ; node . isHighlighted = params . isHighlighted || false ; return node ; } ; layoutPropertiesInitializer ( params ) { var layoutProperties = { } ; layoutProperties . name = params . name || 'undefined' ; layoutProperties . nodeRepulsion = params . nodeRepulsion || 'undefined' ; layoutProperties . idealEdgeLength = params . idealEdgeLength || 'undefined' ; layoutProperties . edgeElasticity = params . edgeElasticity || 'undefined' ; layoutProperties . nestingFactor = params . nestingFactor || 'undefined' ; layoutProperties . gravity = params . gravity || 'undefined' ; layoutProperties . numIter = params . numIter || 'undefined' ; layoutProperties . tile = params . tile || 'undefined' ; layoutProperties . animate = params . animate || 'undefined' ; layoutProperties . randomize = params . randomize || false ; layoutProperties . gravityRangeCompound = params . gravityRangeCompound || 'undefined' ; layoutProperties . gravityCompound = params . gravityCompound || 'undefined' ; layoutProperties . gravityRange = params . gravityRange || 'undefined' ; layoutProperties . tilingPaddingVertical = params . tilingPaddingVertical || 'undefined' ; layoutProperties . tilingPaddingHorizontal = params . tilingPaddingHorizontal || 'undefined' ; layoutProperties . initialEnergyOnIncremental = params . initialEnergyOnIncremental || 'undefined' ; return layoutProperties ; } ; globalOptionsInitializer ( params ) { var globalOptions = { } ; globalOptions . zoomLevel = params . zoomLevel || 'undefined' ; globalOptions . panLevel = params . panLevel || 'undefined' ; return globalOptions ; } ; getCustomObjId ( ) { var d = Date . now ( ) ; if ( typeof performance !== 'undefined' && typeof performance . now === 'function' ) { d += performance . now ( ) ; } return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' . replace ( /[xy]/g , function ( c ) { var r = ( d + Math . random ( ) * 16 ) % 16 | 0 ; d = Math . floor ( d / 16 ) ; return ( c === 'x' ? r : ( r & 0x3 | 0x8 ) ) . toString ( 16 ) ; } ) ; } ; getParam ( urlParam ) { const regExp = new RegExp ( urlParam + '=(.*?)($|&)' , 'g' ) ; let match = window . location . search . match ( regExp ) ; if ( match && match . length ) { match = match [ 0 ] ; match = match . replace ( urlParam + '=' , '' ) . replace ( '&' , '' ) ; } else { match = null ; } return match ; } ; }