import autobind from "autobind-decorator" ; import { action , computed , makeObservable , observable } from "mobx" ; import EditorActionsManager from "../managers/EditorActionsManager" ; import FileOperationsManager , { IPathwayInfo } from "../managers/FileOperationsManager" ; import GridOptionsManager from "../managers/GridOptionsManager" ; import ViewOperationsManager from "../managers/ViewOperationsManager" ; import ConfirmationModal from "../modals/ConfirmationModal" ; import { EGridType } from "../modals/GridSettings" ; import { ILayoutProperties } from "../modals/LayoutProperties" ; import { EModalType , IPathwayData , IProfileMetaData } from "../ui/react-pathway-mapper" ; import SaveLoadUtility from "./SaveLoadUtility" ; export default class PathwayActions { @ observable selectedPathway : string ; fileManager : FileOperationsManager ; editor : EditorActionsManager ; undoRedoManager : any ; pathwayHandler : ( pathwayName : string ) => void ; handleOpen : ( modalId : EModalType ) => void ; eh : any ; profiles : IProfileMetaData [ ] ; gridOptionsManager : GridOptionsManager ; uploader : any ; merger : any ; isCBioPortal : boolean ; isCollaborative : boolean ; viewOperationsManager : ViewOperationsManager ; overlayUploader : any ; @ observable enabledType : EGridType ; constructor ( pathwayHandler , profiles , fileManager , handleOpen , isCBioPortal , isCollaborative ) { makeObservable ( this ) ; this . pathwayHandler = pathwayHandler ; this . profiles = profiles ; this . fileManager = fileManager ; this . handleOpen = handleOpen ; this . isCBioPortal = isCBioPortal ; this . isCollaborative = isCollaborative ; this . enabledType = EGridType . NONE ; } @ action . bound addProfile ( profile ) { this . profiles . push ( profile ) ; } @ action . bound clearProfiles ( ) { this . profiles . length = 0 ; } emphasizeQueryGenes ( queryGenes ) { if ( this . editor ) this . editor . cy . nodes ( ) . forEach ( ( node ) => { const nodeName = node . data ( ) . name ; const nodeType = node . data ( ) . type ; if ( queryGenes . includes ( nodeName ) && nodeType === "GENE" ) { node . style ( { "border-width" : "4px" , "font-weight" : "bold" } ) ; } } ) ; } getSelectedNodes ( ) { return this . editor . cy . nodes ( ":selected" ) ; } setLayoutProperties ( layoutProperties ) { this . editor . saveLayoutProperties ( layoutProperties ) ; } doesCyHaveElements ( ) { return this . editor . cy . elements ( ) . length > 0 ; } @ autobind toggleGrid ( isEnabled ) { this . gridOptionsManager . setSnapToGuidelines ( false ) ; this . gridOptionsManager . setShowGrid ( isEnabled ) ; } @ autobind toggleGuide ( isEnabled ) { this . gridOptionsManager . setSnapToGuidelines ( isEnabled ) ; this . gridOptionsManager . setShowGrid ( false ) ; } adjustGridSettings ( gridSize , color ) { this . gridOptionsManager . currentProperties . gridSpacing = gridSize ; this . gridOptionsManager . currentProperties . guidelinesStyle . strokeStyle = color ; this . gridOptionsManager . currentProperties . guidelinesStyle . horizontalDistColor = color ; this . gridOptionsManager . currentProperties . guidelinesStyle . verticalDistColor = color ; } @ autobind resizeToContent ( ) { this . editor . resizeNodesToContent ( this . editor . cy . nodes ( ) ) ; } @ autobind align ( param ) { this . viewOperationsManager . handleNodeAlignment ( param ) ; } @ autobind onChangeFile ( e , isMerge ) { const file = e . target . files [ 0 ] as File ; this . processFile ( file , isMerge ) ; e . target . value = "" ; } uploadOverlay ( ) { this . overlayUploader . click ( ) ; } overlayFromText ( file ) { const formData = new FormData ( ) ; formData . append ( "graphFile" , file ) ; const request = new XMLHttpRequest ( ) ; request . onreadystatechange = ( ) => { if ( request . readyState === XMLHttpRequest . DONE && request . status === 200 ) { const linesOfData = request . responseText . split ( "\n" ) ; if ( linesOfData . length > 0 ) { const profileIdsFromFile = linesOfData [ 0 ] . split ( "\t" ) . slice ( 1 ) ; profileIdsFromFile . forEach ( id => this . addProfile ( { profileId : id , enabled : true } ) ) ; } else { console . log ( "Error: No valid data" ) ; } this . editor . addGenomicData ( request . responseText ) ; } } ; request . open ( "POST" , "/loadGraph" ) ; request . send ( formData ) ; } @ autobind upload ( ) { if ( this . editor . cy . elements ( ) . length > 0 ) { this . handleOpen ( EModalType . CONFIRMATION ) ; ConfirmationModal . pendingFunction = ( ) => { this . uploader . click ( ) ; } ; } else { this . uploader . click ( ) ; } } @ autobind merge ( ) { this . merger . click ( ) ; } setOverlayUploader ( inputRef ) { this . overlayUploader = inputRef ; } @ autobind setUploaders ( inputRef , isMerge ) { if ( isMerge ) this . merger = inputRef ; else this . uploader = inputRef ; } @ computed get getPathwayInfo ( ) { return this . fileManager . getPathwayInfo ; } @ autobind setPathwayInfo ( other ) { this . fileManager . setPathwayInfo ( other ) ; } @ autobind undo ( ) { this . undoRedoManager . undo ( ) ; } @ autobind redo ( ) { this . undoRedoManager . redo ( ) ; } @ autobind export ( isSIFNX ) { this . eh . hide ( ) ; this . fileManager . saveGraph ( isSIFNX , this . editor ) ; } @ autobind resetUndoStack ( ) { this . undoRedoManager . reset ( ) ; } @ autobind newPathway ( ) { const commitNewPathway = ( ) => { this . editor . removeAllElements ( ) ; this . fileManager . setPathwayInfo ( { pathwayTitle : "New Pathway" , pathwayDetails : "" } ) ; this . resetUndoStack ( ) ; this . pathwayHandler ( "Dummy" ) ; } ; if ( this . editor . cy . elements ( ) . length > 0 ) { this . handleOpen ( EModalType . CONFIRMATION ) ; ConfirmationModal . pendingFunction = commitNewPathway ; } else { commitNewPathway ( ) ; } } @ autobind changePathway ( pathwayName ) { this . pathwayHandler ( pathwayName ) ; if ( ! this . isCBioPortal ) { this . fileManager . setPathwayInfo ( { pathwayTitle : pathwayName , pathwayDetails : "" , } ) ; if ( this . editor ) { this . resetUndoStack ( ) ; } } } @ autobind highlightNeighbours ( ) { this . editor . highlightNeighbors ( ) ; } @ autobind highlightSelected ( ) { this . editor . highlightSelected ( ) ; } @ autobind validateGenes ( ) { this . editor . validateGenes ( ) ; } @ autobind showAll ( ) { this . editor . showAllNodes ( ) ; } @ autobind hideSelected ( ) { this . editor . hideSelectedNodes ( ) ; } @ autobind deleteSelected ( ) { const selectedEles = this . editor . cy . elements ( ":selected" ) ; this . editor . removeElement ( selectedEles ) ; } @ autobind addEdge ( edgeTypeIndex ) { window . edgeAddingMode = edgeTypeIndex + 1 ; if ( edgeTypeIndex === - 1 ) { this . eh . disable ( ) ; this . eh . hide ( ) ; return ; } else { this . eh . enable ( ) ; } } @ autobind changeNodeName ( oldName , newName ) { const cyNode = this . editor . cy . $ ( '[name="' + oldName + '"]' ) [ 0 ] ; this . editor . changeName ( cyNode , newName ) ; } @ autobind addNode ( nodeType ) { const nodeData = { type : nodeType . toUpperCase ( ) , name : "New " + nodeType , w : "150" , h : "52" } ; const extent = this . editor . cy . extent ( ) ; const posData = { x : ( extent . x1 + extent . x2 ) / 2 , y : ( extent . y1 + extent . y2 ) / 2 } ; this . editor . addNode ( nodeData , posData ) ; this . pathwayHandler ( "Additional Pathway" ) ; } @ autobind searchGene ( geneName ) { const selector = "node[name @*= '" + geneName + "']" ; const nodesContainingSearchedGene = this . editor . cy . filter ( selector ) ; let nodesToSelect = this . editor . cy . collection ( ) ; nodesContainingSearchedGene . forEach ( function ( ele , index ) { if ( ! ele . hasClass ( "highlightedNode" ) && ! ele . hasClass ( "invalidGeneHighlight" ) ) nodesToSelect = nodesToSelect . union ( ele ) ; } ) ; this . editor . highlightBySearch ( nodesToSelect ) ; } @ autobind removeAllData ( ) { this . editor . removeGenomicData ( ) ; this . clearProfiles ( ) ; } @ autobind removeAllHighlight ( ) { this . editor . removeAllHighlight ( ) ; } @ autobind processFile ( file , isMerge ) { const formData = new FormData ( ) ; formData . append ( "graphFile" , file ) ; const request = new XMLHttpRequest ( ) ; request . onreadystatechange = ( ) => { if ( request . readyState === XMLHttpRequest . DONE ) { if ( request . status === 200 ) { const pathwayData = SaveLoadUtility . parseGraph ( request . responseText , false ) ; if ( isMerge ) { this . editor . mergeGraph ( pathwayData . nodes , pathwayData . edges ) ; const graphJSON = this . editor . cy . json ( ) ; pathwayData . nodes = graphJSON . elements . nodes ; pathwayData . edges = graphJSON . elements . edges ; pathwayData . title = "Additional Pathway" ; } else { this . editor . loadFile ( pathwayData . nodes , pathwayData . edges ) ; this . fileManager . setPathwayInfo ( { pathwayTitle : pathwayData . title , pathwayDetails : pathwayData . description } ) ; } this . pathwayHandler ( pathwayData . title + "_imported" ) ; this . resetUndoStack ( ) ; } else { console . error ( "Error processing file: " + request . readyState + request . responseText ) ; } } } ; request . open ( "POST" , "/loadGraph" ) ; request . send ( formData ) ; } @ autobind saveAs ( type ) { if ( type === "SVG" ) { this . fileManager . saveAsSVG ( this . editor ) ; } else if ( type === "PNG" ) { this . fileManager . saveAsPNG ( this . editor . cy ) ; } else if ( type === "JPEG" ) { this . fileManager . saveAsJPEG ( this . editor . cy ) ; } } @ autobind editorHandler ( editor , eh , undoRedoManager , viewOperationsManager , gridOptionsManager ) { this . editor = editor ; this . eh = eh ; this . undoRedoManager = undoRedoManager ; this . viewOperationsManager = viewOperationsManager ; this . gridOptionsManager = gridOptionsManager ; } @ autobind exists ( profileId ) { let exists = false ; this . profiles . forEach ( ( profile ) => { if ( profile . profileId === profileId ) { exists = true ; } } ) ; return exists ; } @ autobind loadSampleData ( ) { const data = "gene\tlung\tovarian\tbreast\n" + "PTEN\t-7\t-20\t10\n" + "NF1\t-12\t-4\t30\n" + "PIK3CA\t18\t40\t-50\n" + "KRAS\t11\t-5\t0\n" + "ZIYA\t0\t-2\t0\n" + "AKT1\t3\t30\t-10\n" + "AKT2\t6\t-3\t20\n" + "AKT3\t6\t-3\t20\n" + "\n" ; if ( this . exists ( "lung" ) || this . exists ( "ovarian" ) || this . exists ( "breast" ) ) { return ; } this . editor . addGenomicData ( data ) ; if ( ! this . isCollaborative ) { this . addProfile ( { profileId : "lung" , enabled : this . profiles . length < 6 ? true : false } ) ; this . addProfile ( { profileId : "ovarian" , enabled : this . profiles . length < 6 ? true : false } ) ; this . addProfile ( { profileId : "breast" , enabled : this . profiles . length < 6 ? true : false } ) ; } } @ autobind performLayout ( ) { this . editor . performLayout ( ) ; } }