import Map from "../map" ; import Node , { Colors , Coordinates , ExportNodeProperties , Font , Image , NodeProperties } from "../models/node" ; import { Event } from "./events" ; import Log from "../../utils/log" ; import Utils from "../../utils/utils" ; /**
 * Manage map history, for each change save a snapshot.
 */ export default class History { private map : Map ; private index : number ; private snapshots : MapSnapshot [ ] ; param { Map } map constructor ( map ) { this . map = map ; this . index = - 1 ; this . snapshots = [ ] ; } return { MapSnapshot } public current = ( ) : MapSnapshot => { return this . snapshots [ this . index ] ; } ; param { MapSnapshot } snapshot public new = ( snapshot ? ) => { if ( snapshot === undefined ) { let oldRootCoordinates = Utils . cloneObject ( this . map . nodes . getRoot ( ) . coordinates ) ; this . map . nodes . setCounter ( 0 ) ; this . map . nodes . clear ( ) ; this . map . draw . clear ( ) ; this . map . draw . update ( ) ; this . map . nodes . addRootNode ( oldRootCoordinates ) ; this . map . zoom . center ( null , 0 ) ; this . save ( ) ; this . map . events . call ( Event . create ) ; } else if ( this . checkSnapshotStructure ( snapshot ) ) { this . redraw ( snapshot ) ; this . map . zoom . center ( "position" , 0 ) ; this . save ( ) ; this . map . events . call ( Event . create ) ; } else { Log . error ( "The snapshot is not correct" ) ; } } ; /**
     * Undo last changes.
     */ public undo = ( ) => { if ( this . index > 0 ) { this . redraw ( this . snapshots [ -- this . index ] ) ; this . map . events . call ( Event . undo ) ; } } ; /**
     * Redo one change which was undone.
     */ public redo = ( ) => { if ( this . index < this . snapshots . length - 1 ) { this . redraw ( this . snapshots [ ++ this . index ] ) ; this . map . events . call ( Event . redo ) ; } } ; /**
     * Save the current snapshot of the mind map.
     */ public save ( ) { if ( this . index < this . snapshots . length - 1 ) { this . snapshots . splice ( this . index + 1 ) ; } this . snapshots . push ( this . getSnapshot ( ) ) ; this . index ++ ; } returns { MapSnapshot [ ] } public getHistory = ( ) : ExportHistory => { return { snapshots : this . snapshots . slice ( 0 ) , index : this . index } ; } ; param { MapSnapshot } snapshot private redraw ( snapshot ) { this . map . nodes . clear ( ) ; snapshot . forEach ( ( property ) => { let properties = { id : this . sanitizeNodeId ( property . id ) , parent : this . map . nodes . getNode ( this . sanitizeNodeId ( property . parent ) ) , k : property . k , name : property . name , coordinates : Utils . cloneObject ( property . coordinates ) as Coordinates , image : Utils . cloneObject ( property . image ) as Image , colors : Utils . cloneObject ( property . colors ) as Colors , font : Utils . cloneObject ( property . font ) as Font , locked : property . locked } ; let node = new Node ( properties ) ; this . map . nodes . setNode ( node . id , node ) ; } ) ; this . map . draw . clear ( ) ; this . map . draw . update ( ) ; this . map . nodes . selectRootNode ( ) ; this . setCounter ( ) ; } return { MapSnapshot } private getSnapshot ( ) { return this . map . nodes . getNodes ( ) . map ( ( node ) => { return this . map . nodes . getNodeProperties ( node , false ) ; } ) . slice ( ) ; } /**
     * Set the right counter value of the nodes.
     */ private setCounter ( ) { let id = this . map . nodes . getNodes ( ) . map ( ( node ) => { let words = node . id . split ( "_" ) ; return parseInt ( words [ words . length - 1 ] ) ; } ) ; this . map . nodes . setCounter ( Math . max ( ... id ) + 1 ) ; } param { string } oldId returns { string } private sanitizeNodeId ( oldId ) { if ( typeof oldId === "string" ) { let words = oldId . split ( "_" ) ; return this . map . id + "_" + words [ words . length - 2 ] + "_" + words [ words . length - 1 ] ; } } param { MapSnapshot } snapshot return { boolean } private checkSnapshotStructure ( snapshot ) { if ( ! Array . isArray ( snapshot ) ) { return false ; } if ( ( ( < any > snapshot [ 0 ] ) . key && ( < any > snapshot [ 0 ] ) . value ) ) { this . convertOldMmp ( snapshot ) ; } for ( let node of snapshot ) { if ( ! this . checkNodeProperties ( node ) ) { return false ; } } this . translateNodePositions ( snapshot ) ; return true ; } param { ExportNodeProperties } node return { boolean } private checkNodeProperties ( node ) { let conditions = [ typeof node . id === "string" , typeof node . parent === "string" , typeof node . k === "number" , typeof node . name === "string" , typeof node . locked === "boolean" , node . coordinates && typeof node . coordinates . x === "number" && typeof node . coordinates . y === "number" , node . image && typeof node . image . size === "number" && typeof node . image . src === "string" , node . colors && typeof node . colors . background === "string" && typeof node . colors . branch === "string" && typeof node . colors . name === "string" , node . font && typeof node . font . size === "number" && typeof node . font . weight === "string" && typeof node . font . style === "string" ] ; return conditions . every ( condition => condition ) ; } param { Array } snapshot private convertOldMmp ( snapshot ) { for ( let node of snapshot ) { let oldNode = Utils . cloneObject ( node ) ; Utils . clearObject ( node ) ; node . id = "map_node_" + oldNode . key . substr ( 4 ) ; node . parent = oldNode . value . parent ? "map_node_" + oldNode . value . parent . substr ( 4 ) : "" ; node . k = oldNode . value . k ; node . name = oldNode . value . name ; node . locked = oldNode . value . fixed ; node . coordinates = { x : oldNode . value . x , y : oldNode . value . y } ; node . image = { size : parseInt ( oldNode . value [ "image-size" ] ) , src : oldNode . value [ "image-src" ] } ; node . colors = { background : oldNode . value [ "background-color" ] , branch : oldNode . value [ "branch-color" ] || "" , name : oldNode . value [ "text-color" ] } ; node . font = { size : parseInt ( oldNode . value [ "font-size" ] ) , weight : oldNode . value . bold ? "bold" : "normal" , style : oldNode . value . italic ? "italic" : "normal" } ; } } param { MapSnapshot } snapshot private translateNodePositions ( snapshot ) { let oldRootNode = this . map . nodes . getRoot ( ) , newRootNode = ( < any > snapshot ) . find ( ( node ) => { let words = node . id . split ( "_" ) ; return words [ words . length - 1 ] === "0" ; } ) , dx = newRootNode . coordinates . x - oldRootNode . coordinates . x , dy = newRootNode . coordinates . y - oldRootNode . coordinates . y ; for ( let node of snapshot ) { node . coordinates . x -= dx ; node . coordinates . y -= dy ; } } } export interface ExportHistory { snapshots : Array < MapSnapshot > ; index : number ; } export interface MapSnapshot extends Array < ExportNodeProperties > { }