import { GrandFinalType , InputStage , RoundRobinMode , SeedOrdering , StageSettings , StageType } from 'brackets-model' ; import { t } from './lang' ; const stageTypes = [ 'round_robin' , 'single_elimination' , 'double_elimination' ] ; const roundRobinMode = [ 'simple' , 'double' ] ; const roundRobinSeeds = [ 'groups.effort_balanced' , 'groups.seed_optimized' , 'groups.bracket_optimized' ] ; const eliminationOrderings = [ 'natural' , 'reverse' , 'half_shift' , 'reverse_half_shift' , 'pair_flip' , 'inner_outer' ] ; const grandFinalTypes = [ 'none' , 'simple' , 'double' ] ; export type CallbackFunction = ( config : InputStage ) => void ; export type FormConfiguration = { parent_id : string ; html_name_id : string ; html_stage_type_selector_id : string ; html_team_input_id : string ; html_group_id : string ; html_seed_order_id : string ; html_round_robin_mode_id : string ; html_consolation_final_checkbox_id : string ; html_skip_first_round_checkbox_id : string ; html_grand_final_type_id : string ; html_double_elimination_seed_textarea_id : string ; group_default_size : number ; } ; param configuration param submitCallable export default function stageFormCreator ( configuration , submitCallable ) { const parent = document . getElementById ( configuration . parent_id ) ; if ( null === parent ) throw new DOMException ( 'parent with ID: ' + configuration . parent_id + ' was not found!' ) ; createBaseMask ( parent , configuration ) ; const stageSelector = document . getElementById ( configuration . html_stage_type_selector_id ) ; if ( null === stageSelector ) throw new DOMException ( 'somehow we could not create a select!' ) ; stageSelector . onchange = ( ) : void => { removeMaskFields ( parent ) ; let stage ; switch ( ( < HTMLInputElement > stageSelector ) . value ) { case 'single_elimination' : stage = 'single_elimination' ; break ; case 'double_elimination' : stage = 'double_elimination' ; break ; case 'round_robin' : stage = 'round_robin' ; break ; default : throw new DOMException ( 'stage ' + ( < HTMLInputElement > stageSelector ) . value + ' seems to be not implemented yet.' ) ; } createMaskFields ( configuration , stage , parent , submitCallable ) ; } ; createMaskFields ( configuration , 'round_robin' , parent , submitCallable ) ; } param parent function removeMaskFields ( parent ) { if ( parent . children . length <= 3 ) return ; for ( let i = 3 ; i < parent . children . length ; i ++ ) { parent . children [ i ] . remove ( ) ; i -- ; } } param parent param configuration function createBaseMask ( parent , configuration ) { createInput ( parent , 'text' , configuration . html_name_id , t ( 'form-creator.stage-name-label' ) , t ( 'form-creator.stage-name-placeholder' ) , undefined , undefined , 1 ) ; createTextarea ( parent , configuration . html_team_input_id , t ( 'form-creator.team-label' ) , t ( 'form-creator.team-placeholder' ) ) ; createSelect ( parent , configuration . html_stage_type_selector_id , t ( 'form-creator.stage-selector-label' ) , stageTypes ) ; } param config param stage param parent param submitCallback function createMaskFields ( config , stage , parent , submitCallback ) { switch ( stage ) { case 'round_robin' : createInput ( parent , 'number' , config . html_group_id , t ( 'form-creator.group-label' ) , t ( 'form-creator.group-placeholder' ) , config . group_default_size . toString ( ) , '1' ) ; createSelect ( parent , config . html_seed_order_id , t ( 'form-creator.seed-order-label' ) , roundRobinSeeds ) ; createSelect ( parent , config . html_round_robin_mode_id , t ( 'form-creator.round-robin-mode-label' ) , roundRobinMode ) ; break ; case 'double_elimination' : createInput ( parent , 'checkbox' , config . html_consolation_final_checkbox_id , t ( 'form-creator.consolation-final-label' ) ) ; createInput ( parent , 'checkbox' , config . html_skip_first_round_checkbox_id , t ( 'form-creator.skip-first-round-label' ) ) ; createSelect ( parent , config . html_grand_final_type_id , t ( 'form-creator.grand-final-type-label' ) , grandFinalTypes ) ; createTextarea ( parent , config . html_double_elimination_seed_textarea_id , t ( 'form-creator.seed-order-label' ) , t ( 'form-creator.double-elimination-seed-order-placeholder' ) ) ; break ; case 'single_elimination' : createSelect ( parent , config . html_seed_order_id , t ( 'form-creator.seed-order-label' ) , eliminationOrderings ) ; createInput ( parent , 'checkbox' , config . html_consolation_final_checkbox_id , t ( 'form-creator.consolation-final-label' ) ) ; break ; default : throw new DOMException ( 'stage ' + stage + ' seems to be not implemented yet.' ) ; } const submitBtnWrapper = document . createElement ( 'div' ) ; const submitBtn = document . createElement ( 'button' ) ; submitBtn . innerText = t ( 'form-creator.submit' ) ; submitBtn . type = 'submit' ; submitBtnWrapper . appendChild ( submitBtn ) ; submitBtn . onclick = ( ) : void => { let query ; switch ( stage ) { case 'round_robin' : try { validateRoundRobin ( config ) ; } catch ( e ) { alert ( ( < DOMException > e ) . message ) ; return ; } const roundRobinSettings = { seedOrdering : [ ( < SeedOrdering > ( < HTMLInputElement > document . getElementById ( config . html_seed_order_id ) ) . value ?? 'groups.effort_balanced' ) , ] , roundRobinMode : ( < RoundRobinMode > ( < HTMLSelectElement > document . getElementById ( config . html_round_robin_mode_id ) ) . value ?? 'simple' ) , groupCount : parseInt ( ( < HTMLInputElement > document . getElementById ( config . html_group_id ) ) . value ?? '0' ) , } ; query = { name : ( < HTMLInputElement > document . getElementById ( config . html_name_id ) ) . value ?? '' , seeding : ( < HTMLTextAreaElement > document . getElementById ( config . html_team_input_id ) ) . value . split ( ',' ) , settings : roundRobinSettings , tournamentId : 0 , type : stage , } ; break ; case 'double_elimination' : validateDoubleElimination ( config ) ; const rawSeedOrder = ( < HTMLTextAreaElement > document . getElementById ( config . html_double_elimination_seed_textarea_id ) ) . value . split ( ',' ) ; const seedOrder = [ ] ; for ( const i in rawSeedOrder ) seedOrder . push ( < SeedOrdering > rawSeedOrder [ i ] . trim ( ) ) ; const doubleEliminationSettings = { seedOrdering : seedOrder , consolationFinal : ( < HTMLInputElement > document . getElementById ( config . html_consolation_final_checkbox_id ) ) . checked , skipFirstRound : ( < HTMLInputElement > document . getElementById ( config . html_skip_first_round_checkbox_id ) ) . checked , grandFinal : < GrandFinalType > ( < HTMLSelectElement > document . getElementById ( config . html_grand_final_type_id ) ) . value , } ; query = { name : ( < HTMLInputElement > document . getElementById ( config . html_name_id ) ) . value ?? '' , seeding : ( < HTMLTextAreaElement > document . getElementById ( config . html_team_input_id ) ) . value . split ( ',' ) , settings : doubleEliminationSettings , tournamentId : 0 , type : stage , } ; break ; case 'single_elimination' : validateSingleElimination ( config ) ; const singleEliminationSettings = { seedOrdering : [ ( < SeedOrdering > ( < HTMLInputElement > document . getElementById ( config . html_seed_order_id ) ) . value ?? 'natural' ) , ] , consolationFinal : ( < HTMLInputElement > document . getElementById ( config . html_consolation_final_checkbox_id ) ) . checked , } ; query = { name : ( < HTMLInputElement > document . getElementById ( config . html_name_id ) ) . value ?? '' , seeding : ( < HTMLTextAreaElement > document . getElementById ( config . html_team_input_id ) ) . value . split ( ',' ) , settings : singleEliminationSettings , tournamentId : 0 , type : stage , } ; break ; default : throw new DOMException ( 'stage ' + stage + ' seems to be not implemented yet.' ) ; } submitCallback ( query ) ; } ; parent . appendChild ( submitBtnWrapper ) ; } param config function validateDoubleElimination ( config ) { baseValidation ( config ) ; const grandFinalType = ( < HTMLSelectElement > document . getElementById ( config . html_grand_final_type_id ) ) . value as GrandFinalType ; if ( ! grandFinalType || ! grandFinalTypes . includes ( grandFinalType ) ) throw new DOMException ( 'grand_final_type must be one of: ' + grandFinalTypes . toString ( ) ) ; const orderings = ( < HTMLTextAreaElement > document . getElementById ( config . html_double_elimination_seed_textarea_id ) ) . value . split ( ',' ) ; for ( const i in orderings ) { if ( orderings [ i ] === '' ) { delete orderings [ i ] ; continue ; } const ordering = orderings [ i ] . trim ( ) as SeedOrdering ; if ( ! eliminationOrderings . includes ( ordering ) ) throw new DOMException ( 'elimination seed_ordering wrong found: ' + ordering + 'must be one of: ' + eliminationOrderings . toString ( ) ) ; } } param config function validateSingleElimination ( config ) { baseValidation ( config ) ; const ordering = ( < HTMLSelectElement > document . getElementById ( config . html_seed_order_id ) ) . value as SeedOrdering ; if ( ! ordering || ! eliminationOrderings . includes ( ordering ) ) throw new DOMException ( 'seed_ordering must be one of: ' + eliminationOrderings . toString ( ) ) ; } param config function validateRoundRobin ( config ) { baseValidation ( config ) ; const groupAmount = parseInt ( ( < HTMLInputElement > document . getElementById ( config . html_group_id ) ) . value ) ; if ( groupAmount <= 0 ) throw new DOMException ( 'group_amount must be equal or bigger than 1' ) ; const ordering = ( < HTMLSelectElement > document . getElementById ( config . html_seed_order_id ) ) . value as SeedOrdering ; if ( ! roundRobinSeeds . includes ( ordering ) ) throw new DOMException ( 'seed_ordering must be one of ' + roundRobinSeeds . toString ( ) ) ; const roundRobinMode = ( < HTMLSelectElement > document . getElementById ( config . html_round_robin_mode_id ) ) . value ; if ( ! roundRobinMode . includes ( roundRobinMode ) ) throw new DOMException ( 'round_robin_mode must be one of ' + roundRobinMode . toString ( ) ) ; } param config function baseValidation ( config ) { const name = ( < HTMLInputElement > document . getElementById ( config . html_name_id ) ) . value ; if ( ! name || name === '' ) throw new DOMException ( 'No name provided.' ) ; const teams = ( < HTMLInputElement > document . getElementById ( config . html_team_input_id ) ) . value . split ( ',' ) ; if ( teams . length < 2 || ! Number . isInteger ( Math . log2 ( teams . length ) ) ) throw new DOMException ( 'Invalid team amount provided.' ) ; const stageType = ( < HTMLInputElement > document . getElementById ( config . html_stage_type_selector_id ) ) . value as StageType ; if ( ! stageType && stageTypes . includes ( stageType ) ) throw new DOMException ( 'Invalid stage.' ) ; } param parent param textareaId param labelText param textareaPlaceholder param textareaDefaultValue function createTextarea ( parent , textareaId , labelText , textareaPlaceholder , textareaDefaultValue ? ) { const wrapper = document . createElement ( 'div' ) ; const label = document . createElement ( 'label' ) ; label . setAttribute ( 'for' , textareaId ) ; label . innerText = labelText ; const textarea = document . createElement ( 'textarea' ) ; textarea . placeholder = textareaPlaceholder ; textarea . id = textareaId ; if ( null !== textareaDefaultValue && undefined !== textareaDefaultValue ) textarea . value = textareaDefaultValue ; wrapper . appendChild ( label ) ; wrapper . appendChild ( textarea ) ; parent . appendChild ( wrapper ) ; } param parent param inputType param inputId param labelText param inputPlaceholder param inputDefaultValue param inputMinValue param inputMinLengthValue function createInput ( parent , inputType , inputId , labelText , inputPlaceholder ? , inputDefaultValue ? , inputMinValue ? , inputMinLengthValue ? ) { const wrapper = document . createElement ( 'div' ) ; const label = document . createElement ( 'label' ) ; label . setAttribute ( 'for' , inputId ) ; label . innerText = labelText ; const input = document . createElement ( 'input' ) ; input . type = inputType ; input . id = inputId ; if ( null !== inputPlaceholder && undefined !== inputPlaceholder ) input . placeholder = inputPlaceholder ; if ( null !== inputDefaultValue && undefined !== inputDefaultValue ) input . value = inputDefaultValue ; if ( null !== inputMinValue && undefined !== inputMinValue ) input . min = inputMinValue ; if ( null !== inputMinLengthValue && undefined !== inputMinLengthValue ) input . minLength = inputMinLengthValue ; wrapper . appendChild ( label ) ; wrapper . appendChild ( input ) ; parent . appendChild ( wrapper ) ; } param parent param selectId param labelText param options function createSelect ( parent , selectId , labelText , options ) { const wrapper = document . createElement ( 'div' ) ; const label = document . createElement ( 'label' ) ; label . setAttribute ( 'for' , selectId ) ; label . innerText = labelText ; const select = document . createElement ( 'select' ) ; select . id = selectId ; createOptions ( select , options ) ; wrapper . appendChild ( label ) ; wrapper . appendChild ( select ) ; parent . appendChild ( wrapper ) ; } param optionSwitch param options function createOptions ( optionSwitch , options ) { for ( const i in options ) { const option = document . createElement ( 'option' ) ; option . innerText = options [ i ] ; optionSwitch . appendChild ( option ) ; } } window . stageFormCreator = stageFormCreator ;