import { TEMPLATE_TYPES } from '../schema/templates/Templates' ; export function isEmptyObject ( obj ) { return Object . keys ( obj ) . length === 0 && JSON . stringify ( obj ) === JSON . stringify ( { } ) ; } export function validate ( root ) { if ( root === undefined ) { throw new Error ( 'Quest has invalid root node' ) ; } const badEntries = getInvalidNodesAndAttributes ( root ) ; if ( ! isEmptyObject ( badEntries ) ) { throw new Error ( 'Found invalid nodes and attributes: ' + JSON . stringify ( badEntries ) ) ; } const duplicateIDs = getDuplicateIds ( root ) ; if ( ! isEmptyObject ( duplicateIDs ) ) { throw new Error ( 'Found nodes with duplicate ids: ' + JSON . stringify ( duplicateIDs ) ) ; } } function getInvalidNodesAndAttributes ( node ) { const results = { } ; const validTags = [ 'op' , 'quest' , 'div' , 'span' , 'b' , 'i' , 'choice' , 'event' , 'p' , 'e' , 'em' , 'trigger' , 'instruction' ] ; Array . prototype . push . apply ( TEMPLATE_TYPES as string [ ] ) ; if ( validTags . indexOf ( node . get ( 0 ) . tagName . toLowerCase ( ) ) === - 1 ) { results [ node . get ( 0 ) . tagName . toLowerCase ( ) ] = ( results [ node . get ( 0 ) . tagName . toLowerCase ( ) ] || 0 ) + 1 ; } const attribNames = Object . keys ( node . get ( 0 ) . attribs ) ; for ( const attribName of attribNames ) { if ( attribName . indexOf ( 'on' ) === 0 && attribName !== 'on' ) { const k = node . get ( 0 ) . tagName . toLowerCase ( ) + '.' + attribName ; results [ k ] = ( results [ k ] || 0 ) + 1 ; } } for ( let i = 0 ; i < node . children ( ) . length ; i ++ ) { const v = getInvalidNodesAndAttributes ( node . children ( ) . eq ( i ) ) ; Object . keys ( v ) . forEach ( ( k ) : void => { results [ k ] = ( results [ k ] || 0 ) + this [ k ] ; } ) ; } return results ; } function getDuplicateIds ( node ) { const map = generateIdMapping ( node ) ; const results = { } ; Object . keys ( map ) . forEach ( ( k ) => { if ( map [ k ] . length > 1 ) { results [ k ] = map [ k ] ; } } ) ; return results ; } function generateIdMapping ( node ) { const map = { } ; if ( node . attr ( 'id' ) ) { const id = node . attr ( 'id' ) ; map [ id ] = ( map [ id ] || [ ] ) . concat ( [ node . get ( 0 ) . tagName . toLowerCase ( ) ] ) ; } for ( let i = 0 ; i < node . children ( ) . length ; i ++ ) { const m = generateIdMapping ( node . children ( ) . eq ( i ) ) ; Object . keys ( m ) . forEach ( ( k ) : void => { map [ k ] = ( map [ k ] || [ ] ) . concat ( this [ k ] ) ; } ) ; } return map ; }