import clone from 'clone' ; import Constants from './Constants' ; import { Model } from './Model' ; import { PathUtils } from './PathUtils' ; private interface ItemWrapper { parent ? : Model ; parentPath ? : string ; data ? : Model ; key ? : string ; } /**
 * The ModelStore is in charge of providing access to the data model. It provides the CRUD operations over the model.
 * To protect the integrity of the data it initially returns immutable data. If needed, you can request a mutable object.
 */ export class ModelStore { private _pageContentDelimiter : string [ ] | null ; private _data : Model | null = null ; private _rootPath : string | null = null ; param rootPath param data private constructor ( rootPath ? , data ? ) { this . _data = { } ; if ( rootPath ) { this . initialize ( rootPath , data ? data : { } ) ; } this . _pageContentDelimiter = [ Constants . JCR_CONTENT ] ; } param rootPath param data private public initialize ( rootPath , data ) { if ( data ) { this . _data = data ; } this . _rootPath = rootPath ; } returns public get rootPath ( ) : string { return this . _rootPath || '' ; } returns public get dataMap ( ) : any { return this . _data ; } param path param newData private public setData ( path , newData = { } ) { const itemKey = PathUtils . getNodeName ( path ) ; if ( itemKey ) { const data = this . getData ( PathUtils . getParentNodePath ( path ) , false ) ; if ( data && data [ Constants . ITEMS_PROP ] ) { const localData = clone ( newData ) ; const items = data [ Constants . ITEMS_PROP ] || { } ; if ( items [ itemKey ] ) { Object . keys ( items [ itemKey ] ) . forEach ( x => localData . value [ x ] = localData . value [ x ] || '' ) ; } items [ itemKey ] = localData . value ; data [ Constants . ITEMS_PROP ] = items ; } } } param path param immutable return public getData < M extends Model > ( path ? , immutable = true ) { if ( ! path && ( typeof path !== 'string' ) ) { return ( immutable ? clone ( this . _data ) : this . _data ) as M ; } if ( ( path === this . _rootPath ) || ( path === `${ this . _rootPath }/${ Constants . JCR_CONTENT }` ) ) { return ( immutable ? clone ( this . _data ) : this . _data ) as M ; } const dataPaths = PathUtils . splitPageContentPaths ( path ) ; if ( dataPaths ) { const pageData = this . _getPageData ( dataPaths . pagePath ) ; if ( ! pageData || ! dataPaths . itemPath ) { return ( immutable ? clone ( pageData ) : pageData ) as M ; } const result = this . _findItemData ( dataPaths . itemPath , pageData ) ; if ( result ) { return ( immutable ? clone ( result . data ) : result . data ) as M ; } } } param path param data param siblingName param insertBefore private public insertData ( path , data , siblingName ? , insertBefore = false ) { data = clone ( data ) ; if ( ! path ) { console . warn ( `No path provided for data: ${ data }` ) ; return ; } const isItem = PathUtils . isItem ( path ) ; if ( ! isItem && this . _data ) { if ( ! this . _data [ Constants . CHILDREN_PROP ] ) { this . _data [ Constants . CHILDREN_PROP ] = { } ; } this . _data [ Constants . CHILDREN_PROP ] [ path ] = data ; return ; } const dataPaths = PathUtils . splitPageContentPaths ( path ) ; if ( dataPaths && dataPaths . itemPath ) { const pageData = this . _getPageData ( dataPaths . pagePath ) ; const result = this . _findItemData ( dataPaths . itemPath , pageData ) ; const parent = result . parent || pageData || this . _data ; const itemName = PathUtils . getNodeName ( dataPaths . itemPath ) ; if ( ( itemName != null ) && parent && Object . prototype . hasOwnProperty . call ( parent , Constants . ITEMS_PROP ) ) { const items = parent [ Constants . ITEMS_PROP ] ; if ( items ) { items [ itemName ] = data ; const itemsOrder = parent [ Constants . ITEMS_ORDER_PROP ] ; if ( ( itemsOrder != null ) && ( itemsOrder . length > 0 ) && ( siblingName != null ) ) { const index = itemsOrder . indexOf ( siblingName ) ; if ( index > - 1 ) { itemsOrder . splice ( insertBefore ? index : ( index + 1 ) , 0 , itemName ) ; } else { itemsOrder . push ( itemName ) ; } } } } } } param path private return { string | null } public removeData ( path ) { if ( ! path ) { return null ; } const isItem = PathUtils . isItem ( path ) ; if ( ! isItem && this . _data && this . _data [ Constants . CHILDREN_PROP ] ) { delete this . _data [ Constants . CHILDREN_PROP ] [ path ] ; return null ; } const dataPaths = PathUtils . splitPageContentPaths ( path ) ; if ( dataPaths && dataPaths . itemPath ) { const pageData = this . _getPageData ( dataPaths . pagePath ) ; const result = this . _findItemData ( dataPaths . itemPath , pageData ) ; if ( result . data ) { if ( result && result . parent && Object . prototype . hasOwnProperty . call ( result . parent , Constants . ITEMS_PROP ) ) { const { parent } = result ; const items = parent [ Constants . ITEMS_PROP ] ; const itemName = PathUtils . getNodeName ( dataPaths . itemPath ) ; if ( itemName ) { if ( items ) { delete items [ itemName ] ; } delete result . data ; delete result . parent ; const itemsOrder = parent [ Constants . ITEMS_ORDER_PROP ] ; if ( itemsOrder && ( itemsOrder . length > 0 ) ) { const index = itemsOrder . indexOf ( itemName ) ; itemsOrder . splice ( index , 1 ) ; } return result . parentPath ? result . parentPath : null ; } } } } console . warn ( `Item for path ${ path } was not found! Nothing to remove then.` ) ; return null ; } private public destroy ( ) { this . _data = null ; this . _rootPath = null ; this . _pageContentDelimiter = null ; } param path param data param parent param parentPath private return private _findItemData ( path , data = this . _data , parent = null , parentPath = '' ) { const answer = { parent , parentPath } ; if ( ! data ) { throw new Error ( 'Assertion error: No data provided. This should never happen.' ) ; } const items = data [ Constants . ITEMS_PROP ] ; if ( ! items ) { return answer ; } for ( const pathKey in items ) { if ( ! Object . prototype . hasOwnProperty . call ( items , pathKey ) ) { continue ; } const childItem = items [ pathKey ] ; if ( pathKey === path ) { answer . data = items [ pathKey ] ; answer . key = pathKey ; return answer ; } else { let subPath = PathUtils . subpath ( path , pathKey ) ; if ( this . _pageContentDelimiter ) { const pageDelimiter = PathUtils . _getStartStrings ( subPath , this . _pageContentDelimiter ) ; const childParentPath = PathUtils . join ( [ parentPath , pathKey , pageDelimiter ] ) ; subPath = PathUtils . trimStrings ( subPath , this . _pageContentDelimiter ) ; if ( subPath !== path ) { const childItemWrapped = this . _findItemData ( subPath , childItem , childItem , childParentPath ) ; if ( childItemWrapped ) { return childItemWrapped ; } } } else { throw new Error ( '_pageContentDelimiter not set. this should never happen as its set in constructor.' ) ; } } } return answer ; } param pagePath private return private _getPageData ( pagePath ) { if ( ! this . _data ) { return ; } if ( ( pagePath === '' ) || ( pagePath === this . _data [ Constants . PATH_PROP ] ) || ( pagePath === this . rootPath ) ) { return this . _data ; } const children = this . _data [ Constants . CHILDREN_PROP ] ; return children && children [ pagePath ] ; } }