// Note: We keep the data and the states in this component class ChoiceSituationWrapper extends React.Component { constructor(props) { super(props); this.state = { // Check whether all nodes are clicked isClicked: [ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false ], // Save continuation decision in percent continuation: [ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0, 0, 0, 0, 0, 0, // 1, 1, 1, 1, 1, 1, 1 // 1, 1, 1, 1, 1, 1, 1, 1 ], // Save outcome path drawn: [-1], indArray: [ [0], [1, 2], [3, 4, 5], [6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20] ], path: {}, pdStopDecision : {}, clickCounter: 0, // Keep track of clicks currentNode: -1, // Keep track of currently clicked node nodeChoiceShow: false, // Show Choice Section showHelp: false, // Show help section nonStandardBalls: 0, // Number of non-standard balls (numbered or White-black-gradient) } this.submitPage = this.submitPage.bind(this); this.showHelp = this.showHelp.bind(this); this.skip = this.skip.bind(this); this.pdRadioClick = this.pdRadioClick.bind(this); this.updatePDStop = this.updatePDStop.bind(this); this.updateCostly = this.updateCostly.bind(this); } componentDidMount() { this.allButtonsClicked() } NodeClickHandler = (currentNode) => { this.setState({ nodeChoiceShow: true, currentNode: currentNode, }) this.hidePointer(); } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Functions to block tree coloring hidePointer() { var balls = document.getElementsByClassName('ball'); for (var i = 0; i < balls.length; i++) { balls[i].style.cursor = 'not-allowed'; balls[i].disabled = true; } document.getElementById("input-widget").style.cursor = 'not-allowed'; } allowPointer() { var balls = document.getElementsByClassName('ball'); for (var i = 0; i < balls.length; i++) { balls[i].style.cursor = 'pointer'; balls[i].disabled = false; } document.getElementById("input-widget").style.cursor = 'auto'; } showHelp() { const current = this.state.showHelp; if (current) { this.setState({showHelp: false}) document.getElementById("help").classList.add("d-none") } else { this.setState({showHelp: true}) document.getElementById("help").classList.remove("d-none") } } allButtonsClicked = () => { const {isClicked} = this.state; if (!isClicked.includes(false)) { document.getElementById("confirmButton").classList.remove("d-none") document.getElementById("helpBtn").classList.add("d-none") } } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Functions to submit choice pop submitNodeChoice = (probContinue) =>{ const { isClicked, continuation, currentNode, clickCounter} = this.state; isClicked[currentNode] = true, continuation[currentNode] = probContinue, this.setState({ nodeChoiceShow: false, isClicked, continuation, clickCounter: clickCounter +1, currentNode: -1, }, () => { this.allowPointer() this.allButtonsClicked() // test if all buttons have been clicked if (this.props.costly) { this.updateCostly(); } }) } pdRadioClick() { const {currentNode} = this.state const allPaths = js_vars.allPaths[currentNode] const clickedRadios = document .getElementById("pdInstructCol") .querySelectorAll('input[type=radio]:checked'); if (allPaths.length == clickedRadios.length) { setTimeout(() => { this.updatePDStop(clickedRadios); }, 200); } } updatePDStop(clickedRadios) { const { isClicked, continuation, currentNode} = this.state; let {pdStopDecision} = this.state let allStop = true let allContinue = true for (let i=0; i< clickedRadios.length; i++){ let thisRadio = clickedRadios[i]; let path = thisRadio.name let decision = thisRadio.value if (decision == "Stop") { allContinue = false } else if (decision == "Continue") { allStop = false } pdStopDecision[path] = decision } let probContinue = -100 if (allStop) { probContinue = 0 } else if (allContinue) { probContinue = 100 } isClicked[currentNode] = true continuation[currentNode] = probContinue this.setState({ isClicked, continuation, clickCounter: this.state.clickCounter +1, currentNode: -1, pdStopDecision:pdStopDecision, nodeChoiceShow: false, currentNode: -1, }, () => { this.allButtonsClicked() this.allowPointer(); if (this.props.costly) { this.updateCostly(); } }) } closeNodeChoice = () => { this.setState({ nodeChoiceShow: false, currentNode: -1, }) this.allowPointer(); } updateCostly() { const numCurrentBalls = this.state.nonStandardBalls; const {isClicked, continuation} = this.state // Determine new number of non-standard balls let newnNonStandardBalls = 0; for (const [index, element] of isClicked.entries()) { if (element && continuation[index] != 0 && continuation[index] != 100) { newnNonStandardBalls = newnNonStandardBalls +1; } } if (newnNonStandardBalls != numCurrentBalls) { this.setState({ nonStandardBalls: newnNonStandardBalls, }) document.getElementById("decoloringCost").classList.add("alert-info") setTimeout( () => { document.getElementById("decoloringCost").classList.remove("alert-info") }, 1000) } } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Functions to make buttons work submitPage = () =>{ liveSend(this.state) // Sends state to oTree backend } // Skip to next page skip() { if (this.props.treatment == 2 || this.props.treatment == 4) { this.setState({ isClicked: [ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true ], // Save continuation decision in percent continuation: [ 100, 100, 100, 100, 50, 40, 100, 60, 40, 20, 90, 80, 40, 20, 0, 0, 0, 0, 0, 0, 0, // 1, 1, 1, 1, 1, 1, 1 // 1, 1, 1, 1, 1, 1, 1, 1 ], }, () => {document.getElementById("confirmButton").click()}) } else { this.setState({ isClicked: [ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true ], // Save continuation decision in percent continuation: [ 100, 100, 100, 100, 100, 0, 100, 100, 0, 0, 100, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1, 1, 1, 1, 1, 1, 1 // 1, 1, 1, 1, 1, 1, 1, 1 ], }, () => {document.getElementById("confirmButton").click()}) } document.getElementById("helpBtn").classList.add("d-none") document.getElementById("skipBtn").classList.add("d-none") } voidFunction() { return } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ render() { let upTickSub = js_vars.upTick let upTick = upTickSub.toFixed(2); let downTick; if (js_vars.downTick) { let downTickSub = js_vars.downTick downTickSub = - downTickSub downTick = (downTickSub.toFixed(2)); } else { upTickSub = -upTickSub downTick = (upTickSub.toFixed(2)); } // Determine costs for non-standard balls in respective treatments let costsPerBall = parseFloat(js_vars.costPerBall.replace('£', '')) const nonStandardCosts = (this.state.nonStandardBalls * costsPerBall).toFixed(2); return ( <>
Your endowment is {this.props.endowment || "£1.50"}.
{/* Left column showing individual risk */} {this.state.nodeChoiceShow ? <> {this.props.treatment == 0 ? : this.props.treatment == 1 || this.props.treatment == 3 ? : this.props.treatment == 2 || this.props.treatment == 4 ? : <> } :
Single risk
The winning side Coin toss outcome
Win the single risk: +£{upTick}
Lose the single risk: -£{upTick}
} {/* Right column showing repeated gamble as tree */}
My risk-taking instructions
{ this.props.costly && this.props.treatment == 3 ? <> You instruct the computer to use {this.state.nonStandardBalls} white-black-gradient {(this.state.nonStandardBalls == 1) ? <> ball : <> balls} , costing you £{nonStandardCosts}.

Decoloring a ball to become white-black-gradient costs {this.props.costPerBall}. White and black balls are free. : this.props.costly && this.props.treatment == 4 ? <> You instruct the computer to use {this.state.nonStandardBalls} numbered {(this.state.nonStandardBalls == 1) ? <> ball : <> balls} , costing you £{nonStandardCosts}.

Decoloring a ball to become numbered costs {this.props.costPerBall}. White and black balls are free. : <> }
Please give risk-taking instructions by decoloring the {js_vars.colorName} ball triangle.

Note: You can change the decoloring of any ball by clicking it.
{/* Left column showing individual risk help */}

Please be reminded that the single risks work as follows:



You enter the outcome of your coin toss (or choose a coin side you like). We – the experimenters – already chose a winning side, stated in Verification.pdf. If your coin toss outcome equals the winning side, you gain £{upTick}. Otherwise, you lose £{upTick}.

{/* Right column showing decoloring help */}

Please be reminded of the following decoloring scheme:

  • White ball: If a white ball is reached, the computer continues risk-taking.
  • Black ball: If a black ball is reached, the computer stops risk-taking and gives you the accumulated earnings at this ball.
  • { (this.props.treatment == 1 || this.props.treatment == 3) ?
  • white-black-gradient ball: If a white-black-gradient ball is reached, the computer stops or continues risk-taking depending on the path taken to the ball.
  • : (this.props.treatment == 2 || this.props.treatment == 4) ?
  • Numbered ball: If a numbered ball is reached, the computer stops or continues risk-taking with the probability shown on the ball.
  • : <>}
{js_vars.testing?
: <> } ) } } function SubmitModal(props) { return(
{/* Toggle Button */} {/* Modal content */}
) }