{"version":3,"file":"connect-CkXTWk05.js","sources":["../../../client/app/bundles/Connect/actions/concepts.js","../../../client/app/bundles/Connect/components/fillInBlank/fillInBlankForm.jsx","../../../client/app/bundles/Connect/components/fillInBlank/editFillInBlank.jsx","../../../client/app/bundles/Connect/components/renderForQuestions/cues.jsx","../../../client/app/bundles/Connect/components/renderForQuestions/feedbackStatements.jsx","../../../client/app/bundles/Connect/components/renderForQuestions/updateResponseResource.js","../../../client/app/bundles/Connect/components/fillInBlank/testFillInBlankQuestionContainer.jsx","../../../client/app/bundles/Connect/actions/display.js","../../../client/app/bundles/Connect/actions/massEdit.js","../../../client/app/bundles/Connect/components/questions/boilerplateFeedback.jsx","../../../client/app/bundles/Connect/components/questions/conceptResultList.jsx","../../../client/app/bundles/Connect/components/questions/massEditContainer.jsx","../../../client/app/bundles/Connect/actions/filters.js","../../../client/app/bundles/Connect/libs/partsOfSpeechTagging.js","../../../client/app/bundles/Connect/libs/algorithms/changeObjects.js","../../../client/app/bundles/Connect/libs/algorithms/joiningWords.js","../../../client/app/bundles/Connect/libs/algorithms/spacingBeforePunctuation.js","../../../client/app/bundles/Connect/libs/quillNormalizer.js","../../../client/app/bundles/Connect/libs/requiredWords.js","../../../client/app/bundles/Connect/libs/sharedResponseFunctions.js","../../../client/app/bundles/Connect/libs/question.js","../../../client/app/bundles/Connect/libs/validEndingPunctuation.js","../../../client/app/bundles/Connect/libs/sentenceFragment.js","../../../client/app/bundles/Connect/components/questions/POSForResponse.jsx","../../../client/app/bundles/Connect/components/questions/POSIndex.jsx","../../../client/app/bundles/Connect/components/questions/POSForResponsesList.jsx","../../../client/app/bundles/Connect/components/questions/responseList.jsx","../../../client/app/bundles/Connect/components/questions/responseComponent.jsx","../../../client/app/bundles/Connect/components/questions/responseRouteWrapper.jsx","../../../client/app/bundles/Connect/components/fillInBlank/fillInBlankQuestion.jsx","../../../client/app/bundles/Connect/components/focusPoints/editFocusPointsContainer.jsx","../../../client/app/bundles/Connect/components/focusPoints/focusPointsContainer.jsx","../../../client/app/bundles/Connect/components/focusPoints/newFocusPointsContainer.jsx","../../../client/app/bundles/Connect/components/incorrectSequence/editIncorrectSequenceContainer.jsx","../../../client/app/bundles/Connect/components/incorrectSequence/incorrectSequenceContainer.jsx","../../../client/app/bundles/Connect/components/incorrectSequence/newIncorrectSequenceContainer.jsx","../../../client/app/bundles/Connect/components/questions/chooseModelContainer.jsx","../../../client/app/bundles/Connect/components/questions/questionForm.jsx","../../../client/app/bundles/Connect/components/renderForQuestions/renderEndState.jsx","../../../client/app/bundles/Connect/libs/markupUserResponses.js","../../../client/app/bundles/Connect/components/renderForQuestions/renderTextEditor.jsx","../../../client/app/bundles/Connect/components/renderForQuestions/renderFormForAnswer.jsx","../../../client/app/bundles/Connect/components/renderForQuestions/submitResponse.js","../../../client/app/bundles/Connect/components/questions/testQuestion.jsx","../../../client/app/bundles/Connect/components/questions/question.jsx","../../../client/app/bundles/Connect/components/sentenceFragments/chooseModelContainer.jsx","../../../client/app/bundles/Connect/components/sentenceFragments/sentenceFragmentForm.jsx","../../../client/app/bundles/Connect/components/studentLessons/sentenceFragment.jsx","../../../client/app/bundles/Connect/components/sentenceFragments/testSentenceFragmentContainer.jsx","../../../client/app/bundles/Connect/components/sentenceFragments/sentenceFragment.jsx","../../../client/app/bundles/Connect/libs/flagArray.js","../../../client/app/bundles/Connect/components/lessons/chooseModelContainer.jsx","../../../client/app/bundles/Connect/components/lessons/lessonForm.jsx","../../../client/app/bundles/Connect/components/lessons/lesson.jsx","../../../client/app/bundles/Connect/components/admin/adminLessonSidebar.jsx","../../../client/app/bundles/Connect/components/admin/adminLesson.jsx","../../../client/app/bundles/Connect/components/admin/adminMainSidebar.jsx","../../../client/app/bundles/Connect/components/concepts/concepts.jsx","../../../client/app/bundles/Connect/components/feedback/concept-feedback.jsx","../../../client/app/bundles/Connect/components/feedback/concepts-feedback.jsx","../../../client/app/bundles/Connect/components/fillInBlank/fillInBlankQuestions.jsx","../../../client/app/bundles/Connect/components/lessons/lessons.jsx","../../../client/app/bundles/Connect/components/questions/questions.jsx","../../../client/app/bundles/Connect/components/sentenceFragments/sentenceFragments.jsx","../../../client/app/bundles/Connect/components/admin/admin.jsx","../../../client/app/bundles/Connect/components/studentLessons/finished.jsx","../../../client/app/bundles/Connect/actions/sessions.js","../../../client/app/bundles/Connect/libs/formattedCues.js","../../../client/app/bundles/Connect/libs/conceptResults/sharedConceptResultsFunctions.js","../../../client/app/bundles/Connect/libs/conceptResults/sentenceCombiningLessonQuestion.js","../../../client/app/bundles/Connect/libs/conceptResults/sentenceFragment.js","../../../client/app/bundles/Connect/libs/conceptResults/fillInTheBlanks.js","../../../client/app/bundles/Connect/libs/conceptResults/lesson.js","../../../client/app/bundles/Connect/components/studentLessons/lesson.jsx","../../../client/app/bundles/Connect/actions/turk.js","../../../client/app/bundles/Connect/libs/conceptResults/sentenceCombining.js","../../../client/app/bundles/Connect/libs/conceptResults/diagnostic.js","../../../client/app/bundles/Connect/components/turk/diagnosticQuestions.jsx","../../../client/app/bundles/Connect/components/turk/finishedDiagnostic.jsx","../../../client/app/bundles/Connect/components/turk/landing.jsx","../../../client/app/bundles/Connect/components/turk/sentenceFragment.jsx","../../../client/app/bundles/Connect/components/turk/turkActivity.jsx","../../../client/app/bundles/Connect/utils/backOff.js","../../../client/app/bundles/Connect/reducers/activityHealth.js","../../../client/app/bundles/Connect/reducers/classroomLesson.js","../../../client/app/bundles/Connect/reducers/classroomLessons.js","../../../client/app/bundles/Connect/reducers/classroomLessonsReviews.js","../../../client/app/bundles/Connect/reducers/classroomSessions.js","../../../client/app/bundles/Connect/reducers/concepts.js","../../../client/app/bundles/Connect/reducers/concepts-feedback.js","../../../client/app/bundles/Connect/reducers/customize.js","../../../client/app/bundles/Connect/reducers/display.js","../../../client/app/bundles/Connect/reducers/fillInBlank.js","../../../client/app/bundles/Connect/reducers/filtersReducer.js","../../../client/app/bundles/Connect/reducers/generatedIncorrectSequences.js","../../../client/app/bundles/Connect/reducers/item-levels.js","../../../client/app/bundles/Connect/reducers/lessons.js","../../../client/app/bundles/Connect/reducers/massEdit.js","../../../client/app/bundles/Connect/reducers/questionReducer.js","../../../client/app/bundles/Connect/reducers/questionReducerV2.js","../../../client/app/bundles/Connect/reducers/questions.js","../../../client/app/bundles/Connect/reducers/responsesReducer.js","../../../client/app/bundles/Connect/reducers/sentenceFragments.js","../../../client/app/bundles/Connect/reducers/sessions.js","../../../client/app/bundles/Connect/reducers/titleCards.js","../../../client/app/bundles/Connect/reducers/turk.js","../../../client/app/bundles/Connect/reducers/combined.js","../../../client/app/bundles/Connect/utils/configureStore.js","../../../client/app/bundles/Connect/clientRegistration.js"],"sourcesContent":["import _ from 'underscore';\n\nimport { requestGet, } from '../../../modules/request/index';\n\nimport C from '../constants';\n\nconst conceptsEndpoint = `${process.env.DEFAULT_URL}/api/v1/concepts.json`;\n\n\nfunction splitInLevels(concepts) {\n return _.groupBy(concepts, 'level');\n}\n\nfunction getParentName(concept, concepts) {\n const parent = _.find(concepts['1'], { id: concept.parent_id, });\n const grandParent = _.find(concepts['2'], { id: parent.parent_id, });\n return `${grandParent.name} | ${parent.name}`;\n}\n\nconst actions = {\n startListeningToConcepts() {\n return (dispatch) => {\n requestGet(conceptsEndpoint, (body) => {\n const concepts = splitInLevels(body.concepts);\n concepts['0'] = concepts['0'].map((concept) => {\n concept.displayName = `${getParentName(concept, concepts)} | ${concept.name}`;\n return concept;\n });\n dispatch({ type: C.RECEIVE_CONCEPTS_DATA, data: concepts, });\n });\n };\n },\n};\n\nexport default actions;\n","import React, { Component } from 'react';\nimport { FlagDropdown, TextEditor, ConceptSelector, } from '../../../Shared/index';\nimport C from '../../constants.js';\n\nclass FillInBlankForm extends Component {\n constructor(props) {\n super(props);\n const { blankAllowed, caseInsensitive, conceptID, cues, cuesLabel, flag, instructions, prompt} = props\n this.state = {\n blankAllowed: blankAllowed || false,\n caseInsensitive: caseInsensitive || false,\n conceptID: conceptID || '',\n cues: cues || '',\n cuesLabel: cuesLabel || '',\n flag: flag || 'alpha',\n instructions: instructions || '',\n newQuestionOptimalResponse: '',\n prompt: prompt || '',\n showDefaultInstructions: false,\n };\n }\n\n clearForm = () => {\n this.setState({\n blankAllowed: false,\n caseInsensitive: false,\n newQuestionPrompt: '',\n newQuestionOptimalResponse: '',\n instructions: '',\n conceptID: null,\n flag: 'alpha',\n cuesLabel: '',\n showDefaultInstructions: false,\n });\n }\n\n handleCuesChange = e => {\n this.setState({cues: e.target.value});\n };\n\n handleCuesLabelChange = e => {\n this.setState({ cuesLabel: e.target.value, });\n };\n\n handleFlagChange = e => {\n this.setState({ flag: e.target.value, });\n };\n\n handleInstructionsChange = e => {\n this.setState({instructions: e.target.value});\n if (e.target.value == '/') {\n this.setState({ showDefaultInstructions: true})\n } else {\n this.setState({ showDefaultInstructions: false})\n }\n };\n\n handleNewQuestionOptimalResponseChange = e => {\n this.setState({newQuestionOptimalResponse: e.target.value});\n };\n\n handlePromptChange = prompt => {\n this.setState({ prompt });\n };\n\n handleSelectorChange = e => {\n this.setState({conceptID: e.value});\n };\n\n renderDefaultInstructions = () => {\n const { showDefaultInstructions } = this.state\n const defaultInstructionsDiv = C.DEFAULT_FILL_IN_BLANKS_INSTRUCTIONS.map((item, i) =>\n ()\n )\n if (showDefaultInstructions) {\n return (\n
\n \n
\nAuthor: {author}
\nFeedback: {feedback}
\nLoading...
\n );\n }\n }\n\n}\n\nfunction select(props) {\n return {\n conceptsFeedback: props.conceptsFeedback,\n fillInBlank: props.fillInBlank,\n playLesson: props.playLesson\n };\n}\n\nexport default connect(select)(TestQuestion);\n","/* eslint-env browser*/\nimport C from '../constants';\n\nexport function clearDisplayMessageAndError() {\n return { type: C.CLEAR_DISPLAY_MESSAGE_AND_ERROR };\n}\n","import C from '../constants';\n\nexport default {\n addResponsesToMassEditArray: function(keys) {\n return { type: C.ADD_RESPONSES_TO_MASS_EDIT_ARRAY, keys };\n },\n addResponseToMassEditArray: function(key) {\n return { type: C.ADD_RESPONSE_TO_MASS_EDIT_ARRAY, responseKey: key };\n },\n removeResponseFromMassEditArray: function(key) {\n return { type: C.REMOVE_RESPONSE_FROM_MASS_EDIT_ARRAY, responseKey: key };\n },\n removeResponsesFromMassEditArray: function(keys) {\n return { type: C.REMOVE_RESPONSES_FROM_MASS_EDIT_ARRAY, keys };\n },\n clearResponsesFromMassEditArray: function() {\n return { type: C.CLEAR_RESPONSES_FROM_MASS_EDIT_ARRAY }\n }\n}\n","export default function getBoilerplateFeedback () {\n return (\n [\n {\n key: \"1\",\n name: \"Feedback 1\",\n description: \"Student writes a strong sentence and uses correct grammar.\",\n children: [\n {\n key: \"1-0\",\n name: \"Coordinating or Conjunction\",\n description: \"That’s a strong sentence! Using the word or helps show there’s a choice, and you put the comma in the right place.\"\n },\n {\n key: \"1-1\",\n name: \"Coordinating or Conjunction (no comma)\",\n description: \"That’s a strong sentence! The word or helps show there’s a choice, and you remembered this type of sentence doesn’t need a comma.\"\n },\n {\n key: \"1-2\",\n name: \"Coordinating and Conjunction\",\n description: \"That’s a strong sentence! The word and is used to add another idea, and you put the comma in the right place.\"\n },\n {\n key: \"1-3\",\n name: \"Time\",\n description: \"That’s a strong sentence! Using the word (insert conjunction) helps show the order of events, and you put the comma in the right place.\"\n },\n {\n key: \"1-4\",\n name: \"Cause (no comma)\",\n description: \"That’s a strong sentence! Using (insert conjunction) helps tell why or give reason, and you remembered this type of sentence doesn’t need a comma.\"\n },\n {\n key: \"1-5\",\n name: \"Cause\",\n description: \"That’s a strong sentence! Using the word (insert conjunction) helps tell why or give a reason, and you put the comma in the right place.\"\n },\n {\n key: \"1-6\",\n name: \"Opposition (no comma)\",\n description: \"That’s a strong sentence! You used (insert conjunction) to show that the two ideas are opposites. You remembered that this type of sentence doesn’t need a comma.\"\n },\n {\n key: \"1-7\",\n name: \"Opposition\",\n description: \"That’s a strong sentence! Using the word (insert conjunction) helps show the two ideas are opposite, and you put the comma in the right place.\"\n },\n {\n key: \"1-8\",\n name: \"Condition\",\n description: \"That’s a strong sentence! Using the word (insert conjunction) helps show that if one idea happens, the second idea will happen. You also put the comma in the right place.\"\n },\n {\n key: \"1-8\",\n name: \"Condition (no comma)\",\n description: \"That’s a strong sentence! Using the word (insert conjunction) helps show that if one idea happens, the second idea will happen. You also remembered this type of sentence doesn’t need a comma.\"\n }\n ]\n },\n {\n key: \"2\",\n name: \"Feedback 2\",\n description: \"Student uses correct punctuation and the correct joining word but makes another grammar error. The feedback will include the student response with the error underlined.\",\n children: [\n {\n key: \"2-0\",\n name: \"\",\n description: \"You used a strong joining word, and you punctuated the sentence correctly. Good work! Now check your verb tense.\"\n }\n ]\n },\n {\n key: \"3\",\n name: \"Feedback 3\",\n description: \"Student uses an inaccurate joining word in a multi-cue or uncued activity.\",\n children: [\n {\n key: \"3-0\",\n name: \"Coordinating and Conjunction\",\n description: \"Which joining word is used to add another idea?\"\n },\n {\n key: \"3-1\",\n name: \"Coordinating or Conjunction\",\n description: \"Revise your work. Which joining word is used to show there’s a choice?\"\n },\n {\n key: \"3-2\",\n name: \"Time Conjunctions\",\n description: \"Revise your work. Which joining word helps show the order of events?\"\n },\n {\n key: \"3-3\",\n name: \"Cause Conjunctions\",\n description: \" Try again! Which joining word helps tell why or give a reason?\"\n },\n {\n key: \"3-4\",\n name: \"Opposition Conjunctions\",\n description: \"Try again! Which joining word helps show that the two ideas are opposite?\"\n },\n {\n key: \"3-5\",\n name: \"Condition Conjunctions\",\n description: \"Try again! Which joining word helps show that one of the ideas must happen for the other one to happen?\"\n }\n ]\n },\n {\n key: \"4\",\n name: \"Feedback 4\",\n description: \"Sentence is grammatically correct and makes sense, but the student uses a joining word other than the word specified in the cue box.\",\n children: [\n {\n key: \"4-0\",\n name: \"\",\n description: \"Strong sentence! Now revise your sentence with the joining word in the directions.\"\n }\n ]\n },\n {\n key: \"5\",\n name: \"Feedback 5\",\n description: \"Student uses a joining word other than the word specified in the cue box, and the joining word they chose does not make sense.\",\n children: [\n {\n key: \"5-0\",\n name: \"\",\n description: \"Revise your sentence with the joining word in the directions.\"\n }\n ]\n },\n {\n key: \"6\",\n name: \"Feedback 6\",\n description: \"Student writes a sentence that uses the correct joining word but is not punctuated correctly.\",\n children: [\n {\n key: \"6-0\",\n name: \"Coordinating And Conjunction\",\n description: \"Using and helps show that another idea has been added. Nice! Now correct your punctuation.\"\n },\n {\n key: \"6-1\",\n name: \"Coordinating or Conjunction\",\n description: \"Using or helps show that there’s a choice. Nice! Now correct your punctuation.\"\n },\n {\n key: \"6-2\",\n name: \"Time Conjunction\",\n description: \"Using (insert conjunction) helps show the order of events, and you put it in the right spot. Nice! Now correct your punctuation.\"\n },\n {\n key: \"6-3\",\n name: \"Cause Conjunction\",\n description: \"Using (insert conjunction) helps tell why or give a reason, and you put it in the right spot. Nice! Now correct your punctuation.\"\n },\n {\n key: \"6-4\",\n name: \"Opposition Conjunction\",\n description: \"Using (insert conjunction) shows the ideas are opposites, and you put it in the right spot. Nice! Now correct your punctuation.\"\n },\n {\n key: \"6-5\",\n name: \"Condition Conjunction\",\n description: \" Using (insert conjunction) shows that if one thing happens, the other will happen. Now compare your sentence to the example and correct your punctuation.\"\n }\n ]\n },\n {\n key: \"7\",\n name: \"Feedback 7\",\n description: \"Student writes a sentence that uses the correct joining word but the relationship between the ideas is not accurate (i.e., the clauses are in the wrong order).\",\n children: [\n {\n key: \"7-0\",\n name: \"Time Conjunction\",\n description: \"Revise your work. (insert conjunction) is the right word to use, but you haven’t shown the correct order of events.\"\n },\n {\n key: \"7-1\",\n name: \"Cause Conjunction\",\n description: \"Revise your work. (insert conjunction) is the right word to use, but you need to re-order the ideas to show that … (tailored to the question).\"\n },\n {\n key: \"7-2\",\n name: \"Other Conjunction\",\n description: \"Revise your work. (insert conjunction) is the right word to use, but you need to re-order the ideas to show that …(tailored to the question).\"\n }\n ]\n },\n ]\n )\n}\n","import React from 'react'\nimport _ from 'underscore'\n\nimport { ConceptSelectorWithCheckbox, } from '../../../Shared/index'\n\nexport default class conceptResultList extends React.Component {\n constructor(props) {\n super(props)\n this.state = {\n conceptResults: {}\n }\n }\n\n deleteConceptResult = (key) => {\n const newConceptResults = Object.assign({}, this.state.conceptResults)\n delete newConceptResults[key]\n this.setState({conceptResults: newConceptResults}, () => this.props.updateConceptResults(this.state.conceptResults))\n }\n\n handleConceptChange = e => {\n const newConceptResults = Object.assign({}, this.state.conceptResults)\n newConceptResults[e.value] ? null : newConceptResults[e.value] = false\n this.setState({conceptResults: newConceptResults}, () => this.props.updateConceptResults(this.state.conceptResults))\n };\n\n toggleConceptResultCorrect = (key) => {\n const newConceptResults = Object.assign({}, this.state.conceptResults)\n newConceptResults[key] = !newConceptResults[key]\n this.setState({conceptResults: newConceptResults}, () => this.props.updateConceptResults(this.state.conceptResults))\n }\n\n renderConceptResults = () => {\n const mapped = Object.assign({}, this.state.conceptResults, { null: false })\n const components = _.mapObject(mapped, (val, key) => (\nthis.removeResponseFromMassEditArray(response)} style={{ marginRight: '0.5em', }} type=\"checkbox\" />{this.state.responses[response].text}
\n );\n }\n\n render() {\n let content;\n if (Object.keys(this.state.responses).length > 0) {\n content =Revise your sentence. You don't need to have a space before a ${fb}.
`;\n}\n\nexport function checkForSpacingError(userString) {\n return _.find(subStrings, subString => userString.indexOf(subString) !== -1);\n}\n\nexport function spacingBeforePunctuation(userString) {\n const match = checkForSpacingError(userString);\n return (match ? { feedback: getFeedbackForPunc(match), } : undefined);\n}\n","import { stringNormalize, } from 'quill-string-normalizer'\n\nexport default function() {\n return stringNormalize(this)\n}\n","import _ from 'underscore';\nimport {\n getPartsOfSpeechWordsWithTags\n} from './partsOfSpeechTagging';\n\nconst posTranslations = {\n JJ: 'Adjective',\n JJR: 'Adjective',\n JJS: 'Adjective',\n\n RB: 'Adverb',\n RBR: 'Adverb',\n RBS: 'Adverb',\n WRB: 'Adverb',\n\n CC: 'Conjunction',\n\n NN: 'Noun',\n NNP: 'Noun',\n NNPS: 'Noun',\n NNS: 'Noun',\n\n CD: 'Number',\n LS: 'Number',\n\n IN: 'Preposition',\n\n PP$: 'Pronoun',\n PRP: 'Pronoun',\n WP: 'Pronoun',\n WP$: 'Pronoun',\n\n VB: 'Verb',\n VBD: 'Verb',\n VBG: 'Verb',\n VBN: 'Verb',\n VBP: 'Verb',\n VBZ: 'Verb',\n};\n\nconst posConceptResults = {\n Adjective: 'placeholder',\n Adverb: 'placeholder',\n Conjunction: 'placeholder',\n Noun: 'placeholder',\n Number: 'placeholder',\n Preposition: 'placeholder',\n Pronoun: 'placeholder',\n Verb: 'placeholder',\n};\n\nexport function getCommonWords(sentences) {\n const words = _.map(sentences, (sentence) => normalizeString(sentence).split(' '));\n return _.intersection(...words);\n}\n\nexport function getCommonWordsWithImportantPOS(sentences) {\n const allCommonWords = getCommonWords(sentences);\n return _.reject(allCommonWords, (word) => {\n if (getPartsOfSpeechWordsWithTags(word) && getPartsOfSpeechWordsWithTags(word)[0]) {\n const tag = getPartsOfSpeechWordsWithTags(word)[0][1];\n return !posTranslations[tag];\n }\n return true;\n });\n}\n\nexport function getMissingWords(userString, sentences) {\n const commonWords = getCommonWordsWithImportantPOS(sentences);\n const wordsFromUser = normalizeString(userString).split(' ');\n return _.reject(commonWords, commonWord => _.contains(wordsFromUser, commonWord));\n}\n\nexport function getPOSForWord(word) {\n const tag = getPartsOfSpeechWordsWithTags(word)[0][1];\n return posTranslations[tag];\n}\n\nfunction _getCaseSensitiveWord(word, optimalSentence) {\n const normalizedString = removePunctuation(optimalSentence);\n const normalizedStringPlusLower = normalizeString(optimalSentence);\n const startIndex = normalizedStringPlusLower.indexOf(word);\n return normalizedString.substring(startIndex, word.length + startIndex);\n}\n\nexport function getFeedbackForWord(word, sentences, isSentenceFragment) {\n // const tag = getPOSForWord(word).toLowerCase();\n if (isSentenceFragment) {\n return 'Revise your work. Use all the words from the prompt, and make it complete by adding to it.
';\n }\n const caseSensitiveWord = _getCaseSensitiveWord(word, sentences[0]);\n return `Revise your sentence to include the word ${caseSensitiveWord}. You may have misspelled it.
`;\n}\n\nexport function extractSentencesFromResponses(responses) {\n return _.map(responses, response => response.text);\n}\n\nexport function getMissingWordsFromResponses(userString, sentences) {\n const missingWords = getMissingWords(userString, sentences);\n return _.sortBy(missingWords, word => word.length).reverse();\n}\n\nexport function checkForMissingWords(userString, responses, isSentenceFragment = false) {\n const sentences = extractSentencesFromResponses(responses);\n const missingWords = getMissingWordsFromResponses(userString, sentences);\n if (missingWords.length > 0) {\n return { feedback: getFeedbackForWord(missingWords[0], sentences, isSentenceFragment), };\n }\n}\n\nfunction normalizeString(string = '') {\n return string.replace(/[.,?!;]/g, '').toLowerCase();\n}\n\nfunction removePunctuation(string = '') {\n return string.replace(/[.,?!;\"()]/g, '');\n}\n","import _ from 'underscore';\n\nexport function getOptimalResponses(responses) {\n return _.where(responses, { optimal: true, });\n}\n\nexport function getSubOptimalResponses(responses) {\n return _.filter(responses, resp => resp.parentID === undefined && resp.feedback !== undefined && resp.optimal !== true);\n}\n\nexport function getTopOptimalResponse(responses) {\n return _.sortBy(getOptimalResponses(responses), r => r.count).reverse(responses)[0];\n}\n\nfunction getWeakResponses(responses) {\n return _.filter(responses, resp => resp.weak === true);\n}\n\nfunction getCommonUnmatchedResponses(responses) {\n return _.filter(responses, resp => resp.feedback === undefined && resp.count > 2);\n}\n\nfunction getSumOfWeakAndCommonUnmatchedResponses(responses) {\n return getWeakResponses(responses).length + getCommonUnmatchedResponses(responses).length;\n}\n\nexport function getPercentageWeakResponses(responses) {\n return (getSumOfWeakAndCommonUnmatchedResponses(responses) / responses.length * 100).toPrecision(4);\n}\n\nexport function getGradedResponses(responses) {\n // Returns sorted collection optimal first followed by suboptimal\n const gradedResponses = _.reject(responses, response =>\n (response.optimal === undefined) || (response.parentID)\n );\n return _.sortBy(gradedResponses, 'optimal').reverse();\n}\n","import * as jsDiff from 'diff';\nimport fuzzy from 'fuzzyset.js';\nimport _ from 'underscore';\nimport constants from '../constants';\nimport {\n checkChangeObjectMatch\n} from './algorithms/changeObjects';\nimport {\n getFeedbackForMissingWord\n} from './algorithms/joiningWords';\nimport {\n spacingBeforePunctuation\n} from './algorithms/spacingBeforePunctuation';\nimport quillNormalize from './quillNormalizer';\nimport { checkForMissingWords } from './requiredWords';\nimport { getOptimalResponses, getTopOptimalResponse } from './sharedResponseFunctions';\n\nconst ERROR_TYPES = {\n NO_ERROR: 'NO_ERROR',\n MISSING_WORD: 'MISSING_WORD',\n ADDITIONAL_WORD: 'ADDITIONAL_WORD',\n INCORRECT_WORD: 'INCORRECT_WORD',\n};\n\nconst conceptResultTemplate = (conceptUID, correct = false) => ({\n conceptUID,\n correct,\n});\n\nexport function removePunctuation(string) {\n return string.replace(/[^A-Za-z0-9\\s]/g, '');\n}\n\nconst downcasedFocusPoints = (focusPointsArr = []) => focusPointsArr.map((fp) => {\n fp.text = fp.text.toLowerCase();\n return fp;\n});\n\nconst removeSpaces = string => string.replace(/\\s+/g, '');\n\n// check number of chars added.\n\nconst getLowAdditionCount = (newString, oldString) => {\n const diff = jsDiff.diffChars(newString, oldString);\n const additions = _.where(diff, { added: true, });\n if (additions.length > 1) {\n return false;\n }\n const count = _.reduce(additions, (memo, num) => memo + num.count, 0);\n if (count < 3) {\n return true;\n }\n return false;\n};\n\nString.prototype.quillNormalize = quillNormalize\n\nexport default class Question {\n\n constructor(data) {\n this.prompt = data.prompt;\n this.sentences = data.sentences;\n this.responses = data.responses;\n this.questionUID = data.questionUID;\n this.focusPoints = downcasedFocusPoints(data.focusPoints) || [];\n this.incorrectSequences = data.incorrectSequences || [];\n }\n\n checkMatch(response) {\n // remove leading and trailing whitespace, then make sure all words are single spaced\n response = response.trim().replace(/\\s{2,}/g, ' ');\n const returnValue = {\n found: true,\n submitted: response,\n response: {\n text: response,\n questionUID: this.questionUID,\n gradeIndex: `nonhuman${this.questionUID}`,\n count: 1,\n },\n };\n\n const res = returnValue.response;\n const exactMatch = this.checkExactMatch(response);\n if (exactMatch !== undefined) {\n returnValue.response = exactMatch;\n return returnValue;\n }\n const focusPointMatch = this.checkFocusPointMatch(response);\n if (focusPointMatch !== undefined) {\n res.feedback = focusPointMatch.feedback;\n res.author = 'Focus Point Hint';\n res.parentID = getTopOptimalResponse(this.responses).key;\n if (focusPointMatch.conceptUID) {\n res.conceptResults = [\n conceptResultTemplate(focusPointMatch.conceptUID)\n ];\n }\n if (focusPointMatch.conceptResults) {\n res.conceptResults = focusPointMatch.conceptResults;\n }\n return returnValue;\n }\n const incorrectSequenceMatch = this.checkIncorrectSequenceMatch(response);\n if (incorrectSequenceMatch !== undefined) {\n res.feedback = incorrectSequenceMatch.feedback;\n res.author = 'Incorrect Sequence Hint';\n res.parentID = getTopOptimalResponse(this.responses).key;\n if (incorrectSequenceMatch.conceptResults) {\n res.conceptResults = incorrectSequenceMatch.conceptResults;\n }\n return returnValue;\n }\n const lowerCaseMatch = this.checkCaseInsensitiveMatch(response);\n if (lowerCaseMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.caseError;\n res.author = 'Capitalization Hint';\n res.parentID = lowerCaseMatch.key;\n res.conceptResults = [\n conceptResultTemplate('66upe3S5uvqxuHoHOt4PcQ')\n ];\n return returnValue;\n }\n const punctuationMatch = this.checkPunctuationInsensitiveMatch(response);\n if (punctuationMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.punctuationError;\n res.author = 'Punctuation Hint';\n res.parentID = punctuationMatch.key;\n res.conceptResults = [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ];\n return returnValue;\n }\n const punctuationAndCaseMatch = this.checkPunctuationAndCaseInsensitiveMatch(response);\n if (punctuationAndCaseMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.punctuationAndCaseError;\n res.author = 'Punctuation and Case Hint';\n res.parentID = punctuationAndCaseMatch.key;\n res.conceptResults = [\n conceptResultTemplate('66upe3S5uvqxuHoHOt4PcQ'),\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ];\n return returnValue;\n }\n const spacingBeforePunctuationMatch = this.checkSpacingBeforePunctuationMatch(response);\n if (spacingBeforePunctuationMatch !== undefined) {\n res.feedback = spacingBeforePunctuationMatch.feedback;\n res.author = 'Punctuation Hint';\n res.parentID = getTopOptimalResponse(this.responses) ? getTopOptimalResponse(this.responses).key : undefined;\n res.conceptResults = [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ];\n return returnValue;\n }\n const spacingAfterCommaMatch = this.checkSpacingAfterCommaMatch(response);\n if (spacingAfterCommaMatch !== undefined) {\n res.feedback = spacingAfterCommaMatch.feedback;\n res.author = 'Punctuation Hint';\n res.parentID = getTopOptimalResponse(this.responses) ? getTopOptimalResponse(this.responses).key : undefined;\n res.conceptResults = [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ];\n return returnValue;\n }\n const whitespaceMatch = this.checkWhiteSpaceMatch(response);\n if (whitespaceMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.whitespaceError;\n res.author = 'Whitespace Hint';\n res.parentID = whitespaceMatch.key;\n res.conceptResults = [\n conceptResultTemplate('5Yv4-kNHwwCO2p8HI90oqQ')\n ];\n return returnValue;\n }\n const changeObjectMatch = this.checkChangeObjectRigidMatch(response);\n if (changeObjectMatch !== undefined) {\n switch (changeObjectMatch.errorType) {\n case ERROR_TYPES.INCORRECT_WORD:\n const missingWord = changeObjectMatch.missingText;\n const missingTextFeedback = getFeedbackForMissingWord(missingWord);\n res.feedback = missingTextFeedback || constants.FEEDBACK_STRINGS.modifiedWordError;\n res.author = 'Modified Word Hint';\n res.parentID = changeObjectMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('H-2lrblngQAQ8_s-ctye4g')\n ];\n return returnValue;\n case ERROR_TYPES.ADDITIONAL_WORD:\n res.feedback = constants.FEEDBACK_STRINGS.additionalWordError;\n res.author = 'Additional Word Hint';\n res.parentID = changeObjectMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('QYHg1tpDghy5AHWpsIodAg')\n ];\n return returnValue;\n case ERROR_TYPES.MISSING_WORD:\n\n res.feedback = constants.FEEDBACK_STRINGS.missingWordError;\n res.author = 'Missing Word Hint';\n res.parentID = changeObjectMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('N5VXCdTAs91gP46gATuvPQ')\n ];\n return returnValue;\n default:\n return;\n }\n }\n const changeObjectFlexMatch = this.checkChangeObjectFlexibleMatch(response);\n if (changeObjectFlexMatch !== undefined) {\n switch (changeObjectFlexMatch.errorType) {\n case ERROR_TYPES.INCORRECT_WORD:\n const missingWord = changeObjectFlexMatch.missingText;\n const missingTextFeedback = getFeedbackForMissingWord(missingWord);\n res.feedback = missingTextFeedback || constants.FEEDBACK_STRINGS.modifiedWordError;\n res.author = 'Flexible Modified Word Hint';\n res.parentID = changeObjectFlexMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('H-2lrblngQAQ8_s-ctye4g')\n ];\n return returnValue;\n case ERROR_TYPES.ADDITIONAL_WORD:\n\n res.feedback = constants.FEEDBACK_STRINGS.additionalWordError;\n res.author = 'Flexible Additional Word Hint';\n res.parentID = changeObjectFlexMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('QYHg1tpDghy5AHWpsIodAg')\n ];\n return returnValue;\n case ERROR_TYPES.MISSING_WORD:\n\n res.feedback = constants.FEEDBACK_STRINGS.missingWordError;\n res.author = 'Flexible Missing Word Hint';\n res.parentID = changeObjectFlexMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('N5VXCdTAs91gP46gATuvPQ')\n ];\n return returnValue;\n default:\n return;\n }\n }\n const requiredWordsMatch = this.checkRequiredWordsMatch(response);\n if (requiredWordsMatch !== undefined) {\n res.feedback = requiredWordsMatch.feedback;\n res.author = 'Required Words Hint';\n res.parentID = getTopOptimalResponse(this.responses).key;\n res.conceptResults = [\n conceptResultTemplate('N5VXCdTAs91gP46gATuvPQ')\n ];\n return returnValue;\n }\n const minLengthMatch = this.checkMinLengthMatch(response);\n if (minLengthMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.minLengthError;\n res.author = 'Missing Details Hint';\n res.parentID = minLengthMatch.key;\n res.conceptResults = [\n conceptResultTemplate('N5VXCdTAs91gP46gATuvPQ')\n ];\n return returnValue;\n }\n const maxLengthMatch = this.checkMaxLengthMatch(response);\n if (maxLengthMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.maxLengthError;\n res.author = 'Not Concise Hint';\n res.parentID = maxLengthMatch.key;\n res.conceptResults = [\n conceptResultTemplate('QYHg1tpDghy5AHWpsIodAg')\n ];\n return returnValue;\n }\n const lowerCaseStartMatch = this.checkCaseStartMatch(response);\n if (lowerCaseStartMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.caseError;\n res.author = 'Capitalization Hint';\n res.parentID = lowerCaseStartMatch.key;\n res.conceptResults = [\n conceptResultTemplate('S76ceOpAWR-5m-k47nu6KQ')\n ];\n return returnValue;\n }\n const punctuationEndMatch = this.checkPunctuationEndMatch(response);\n if (punctuationEndMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.punctuationError;\n res.author = 'Punctuation End Hint';\n res.parentID = punctuationEndMatch.key;\n res.conceptResults = [\n conceptResultTemplate('JVJhNIHGZLbHF6LYw605XA')\n ];\n return returnValue;\n }\n returnValue.found = false;\n returnValue.response.gradeIndex = `unmarked${this.questionUID}`;\n return returnValue;\n }\n\n nonChildResponses() {\n return _.filter(this.responses,\n resp => resp.parentID === undefined && resp.feedback !== undefined\n );\n }\n\n checkExactMatch(response) {\n return _.find(this.responses,\n resp => resp.text.quillNormalize() === response.quillNormalize()\n );\n }\n\n checkCaseInsensitiveMatch(response) {\n return _.find(getOptimalResponses(this.responses),\n resp => resp.text.quillNormalize().toLowerCase() === response.quillNormalize().toLowerCase()\n );\n }\n\n checkCaseStartMatch(response) {\n if ((/^[a-z]/).test(response)) {\n return getTopOptimalResponse(this.responses);\n }\n }\n\n checkPunctuationEndMatch(response) {\n const lastChar = response[response.length - 1];\n if (lastChar && lastChar.match(/[a-z]/i)) {\n return getTopOptimalResponse(this.responses);\n }\n }\n\n checkPunctuationInsensitiveMatch(response) {\n return _.find(getOptimalResponses(this.responses),\n resp => removePunctuation(resp.text.quillNormalize()) === removePunctuation(response.quillNormalize())\n );\n }\n\n checkPunctuationAndCaseInsensitiveMatch(response) {\n return _.find(getOptimalResponses(this.responses), (resp) => {\n const supplied = removePunctuation(response.quillNormalize()).toLowerCase();\n const target = removePunctuation(resp.text.quillNormalize()).toLowerCase();\n return supplied === target;\n });\n }\n\n checkWhiteSpaceMatch(response) {\n return _.find(getOptimalResponses(this.responses),\n resp => removeSpaces(response.quillNormalize()) === removeSpaces(resp.text.quillNormalize())\n );\n }\n\n checkSmallTypoMatch(response) {\n return _.find(this.nonChildResponses(this.responses),\n resp => getLowAdditionCount(response.quillNormalize(), resp.text.quillNormalize())\n );\n }\n\n checkChangeObjectRigidMatch(response) {\n const fn = string => string.quillNormalize();\n return checkChangeObjectMatch(response, getOptimalResponses(this.responses), fn);\n }\n\n checkChangeObjectFlexibleMatch(response) {\n const fn = string => removePunctuation(string.quillNormalize()).toLowerCase();\n return checkChangeObjectMatch(response, getOptimalResponses(this.responses), fn);\n }\n\n checkFuzzyMatch(response) {\n const set = fuzzy(_.pluck(this.responses, 'text'));\n const matches = set.get(response, []);\n let foundResponse;\n let text;\n if (matches.length > 0) {\n const threshold = (matches[0][1].length - 3) / matches[0][1].length;\n text = (matches[0][0] > threshold) && (response.split(' ').length <= matches[0][1].split(' ').length) ? matches[0][1] : null;\n }\n if (text) {\n foundResponse = _.findWhere(this.responses, { text, });\n }\n return foundResponse;\n }\n\n checkRequiredWordsMatch(response) {\n return checkForMissingWords(response, getOptimalResponses(this.responses));\n }\n\n checkMinLengthMatch(response) {\n const optimalResponses = getOptimalResponses(this.responses);\n if (optimalResponses.length < 2) {\n return undefined;\n }\n const lengthsOfResponses = optimalResponses.map(resp => resp.text.quillNormalize().split(' ').length);\n const minLength = _.min(lengthsOfResponses) - 1;\n if (response.split(' ').length < minLength) {\n return _.sortBy(optimalResponses, resp => resp.text.quillNormalize().length)[0];\n }\n return undefined;\n }\n\n checkMaxLengthMatch(response) {\n const optimalResponses = getOptimalResponses(this.responses);\n if (optimalResponses.length < 2) {\n return undefined;\n }\n const lengthsOfResponses = optimalResponses.map(resp => resp.text.quillNormalize().split(' ').length);\n const maxLength = _.max(lengthsOfResponses) + 1;\n if (response.split(' ').length > maxLength) {\n return _.sortBy(optimalResponses, resp => resp.text.quillNormalize().length).reverse()[0];\n }\n return undefined;\n }\n\n checkFocusPointMatch(response) {\n return _.find(this.focusPoints, (fp) => {\n const options = fp.text.split('|||');\n const anyMatches = _.any(options, opt => response.toLowerCase().indexOf(opt) !== -1);\n return !anyMatches;\n });\n }\n\n checkIncorrectSequenceMatch(response) {\n return _.find(this.incorrectSequences, (incSeq) => {\n const options = incSeq.text.split('|||');\n const anyMatches = _.any(options, opt => new RegExp(opt).test(response));\n return anyMatches;\n });\n }\n\n checkSpacingBeforePunctuationMatch(response) {\n return spacingBeforePunctuation(response);\n }\n\n checkSpacingAfterCommaMatch(response) {\n for (let i = 0; i < response.length; i++) {\n if (response[i] === ',' && (i + 1 < response.length)) {\n if (response[i + 1] !== ' ') {\n return {\n feedback: 'Revise your work. Always put a space after a comma.
',\n };\n }\n }\n }\n return undefined;\n }\n}\n","export default ['\"', \"'\", '!', '.', '?'];\n","import _ from 'underscore';\nimport constants from '../constants';\nimport validEndingPunctuation from '../libs/validEndingPunctuation.js';\nimport {\n spacingBeforePunctuation\n} from './algorithms/spacingBeforePunctuation';\nimport * as qpos from './partsOfSpeechTagging';\nimport { checkForMissingWords } from './requiredWords';\n\nconst conceptResultTemplate = (conceptUID, correct = false) => ({\n conceptUID,\n correct,\n});\n\nexport function wordLengthCount(str) {\n const strNoPunctuation = str.replace(/[^0-9a-z\\s]/gi, '').replace(/\\s{2,}/g, ' ').split(' ');\n return strNoPunctuation.length;\n}\n\nfunction sortbyCount(responses) {\n return _.sortBy(responses, r => r.count).reverse();\n}\n\nfunction removePunctuation(string) {\n return string.replace(/[^A-Za-z0-9\\s]/g, '');\n}\n\nexport default class POSMatcher {\n\n constructor(data) {\n this.prompt = data.prompt;\n this.responses = sortbyCount(data.responses);\n this.questionUID = data.questionUID;\n this.wordCountChange = data.wordCountChange || {};\n this.ignoreCaseAndPunc = data.ignoreCaseAndPunc;\n this.incorrectSequences = data.incorrectSequences || [];\n }\n\n getOptimalResponses() {\n return _.reject(this.responses, response =>\n (response.optimal !== true) || (response.parentID)\n );\n }\n\n getTopOptimalResponse() {\n return _.sortBy(this.getOptimalResponses(), r => r.count).reverse()[0];\n }\n\n getGradedResponses() {\n // returns sorted collection optimal first followed by suboptimal\n const gradedResponses = _.reject(this.responses, response =>\n (response.optimal === undefined) || (response.parentID)\n );\n return _.sortBy(gradedResponses, 'optimal').reverse();\n }\n\n checkMatch(userSubmission) {\n const formattedResponse = userSubmission.trim().replace(/\\s{2,}/g, ' ');\n const returnValue = {\n found: true,\n submitted: formattedResponse,\n response: {\n text: formattedResponse,\n questionUID: this.questionUID,\n gradeIndex: `nonhuman${this.questionUID}`,\n count: 1,\n },\n };\n const res = returnValue.response;\n\n const exactMatch = this.checkExactMatch(userSubmission);\n if (exactMatch !== undefined) {\n returnValue.response = exactMatch;\n return returnValue;\n }\n const incorrectSequenceMatch = this.checkIncorrectSequenceMatch(userSubmission);\n if (incorrectSequenceMatch !== undefined) {\n returnValue.response = Object.assign({}, res, incorrectSequenceMatch);\n return returnValue;\n }\n const lengthMatch = this.checkLengthMatch(userSubmission);\n if (lengthMatch !== undefined) {\n returnValue.response = Object.assign({}, res, lengthMatch);\n return returnValue;\n }\n\n const endingPunctuationMatch = this.checkEndingPunctuationMatch(userSubmission);\n if (endingPunctuationMatch !== undefined) {\n returnValue.response = Object.assign({}, res, endingPunctuationMatch);\n return returnValue;\n }\n\n const startingCapitalizationMatch = this.checkStartingCapitalization(userSubmission);\n if (startingCapitalizationMatch !== undefined) {\n returnValue.response = Object.assign({}, res, startingCapitalizationMatch);\n return returnValue;\n }\n\n const optimalCapitalizationMatch = this.checkOptimalCapitalizationMatch(userSubmission);\n if (optimalCapitalizationMatch !== undefined) {\n returnValue.response = Object.assign({}, res, optimalCapitalizationMatch);\n return returnValue;\n }\n\n const optimalPunctuationMatch = this.checkOptimalPunctuationMatch(userSubmission);\n if (optimalPunctuationMatch !== undefined) {\n returnValue.response = Object.assign({}, res, optimalPunctuationMatch);\n return returnValue;\n }\n\n const punctuationAndCaseMatch = this.checkPunctuationAndCaseInsensitiveMatch(userSubmission);\n if (punctuationAndCaseMatch !== undefined) {\n returnValue.response = Object.assign({}, res, punctuationAndCaseMatch);\n return returnValue;\n }\n\n const spacingBeforePunctuationMatch = this.checkSpacingBeforePunctuationMatch(userSubmission);\n if (spacingBeforePunctuationMatch !== undefined) {\n returnValue.response = Object.assign({}, res, spacingBeforePunctuationMatch);\n return returnValue;\n }\n\n const spacingAfterCommaMatch = this.checkSpacingAfterCommaMatch(userSubmission);\n if (spacingAfterCommaMatch !== undefined) {\n returnValue.response = Object.assign({}, res, spacingAfterCommaMatch);\n return returnValue;\n }\n\n const requiredWordsMatch = this.checkRequiredWordsMatch(userSubmission);\n if (requiredWordsMatch !== undefined) {\n returnValue.response = Object.assign({}, res, requiredWordsMatch);\n return returnValue;\n }\n\n const posMatch = this.checkPOSMatch(userSubmission);\n if (posMatch !== undefined) {\n returnValue.response = Object.assign({}, res, posMatch);\n return returnValue;\n }\n\n returnValue.found = false;\n returnValue.response.gradeIndex = `unmarked${this.questionUID}`;\n return returnValue;\n }\n\n checkExactMatch(userSubmission) {\n return _.find(this.responses, response => response.text === userSubmission);\n }\n\n checkIncorrectSequenceMatch(userSubmission) {\n const match = _.find(this.incorrectSequences, (incSeq) => {\n const options = incSeq.text.split('|||');\n const anyMatches = _.any(options, opt => userSubmission.toLowerCase().indexOf(opt) !== -1);\n return anyMatches;\n });\n if (match !== undefined) {\n return {\n optimal: false,\n parentID: this.getTopOptimalResponse().key,\n author: 'Incorrect Sequence Hint',\n feedback: match.feedback,\n conceptResults: match.conceptResults ? match.conceptResults : undefined,\n };\n }\n }\n\n checkLengthMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const userWordCount = wordLengthCount(userSubmission);\n const promptWordCount = wordLengthCount(this.prompt);\n const maxWordCount = promptWordCount + this.wordCountChange.max;\n const minWordCount = promptWordCount + this.wordCountChange.min;\n const templateResponse = {\n optimal: false,\n parentID: this.getTopOptimalResponse().key,\n };\n const feedback = getMinMaxFeedback(this.wordCountChange.min, this.wordCountChange.max);\n if (this.wordCountChange.min && (userWordCount < minWordCount)) {\n if (this.wordCountChange.min === 1) {\n return Object.assign({}, templateResponse, {\n feedback,\n author: 'Too Short Hint',\n });\n } else if (this.wordCountChange.min === this.wordCountChange.max) {\n return Object.assign({}, templateResponse, {\n feedback,\n author: 'Too Short Hint',\n });\n }\n return Object.assign({}, templateResponse, {\n feedback,\n author: 'Too Short Hint',\n });\n } else if (this.wordCountChange.max && (userWordCount > maxWordCount)) {\n if (this.wordCountChange.max === 1) {\n return Object.assign({}, templateResponse, {\n feedback,\n author: 'Too Long Hint',\n });\n }\n return Object.assign({}, templateResponse, {\n feedback,\n author: 'Too Long Hint',\n });\n }\n }\n\n checkEndingPunctuationMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const lastChar = _.last(userSubmission);\n if (!_.includes(validEndingPunctuation, lastChar)) {\n return {\n optimal: false,\n parentID: this.getTopOptimalResponse().key,\n author: 'Punctuation End Hint',\n feedback: 'Proofread your sentence for missing punctuation.',\n conceptResults: [\n conceptResultTemplate('JVJhNIHGZLbHF6LYw605XA')\n ],\n };\n }\n }\n\n checkStartingCapitalization(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n // only trigger if sentence begins with a lower case letter\n if ((/^[a-z]/).test(userSubmission)) {\n return {\n optimal: false,\n parentID: this.getTopOptimalResponse().key,\n author: 'Starting Capitalization Hint',\n feedback: 'Proofread your sentence for correct capitalization.',\n conceptResults: [\n conceptResultTemplate('S76ceOpAWR-5m-k47nu6KQ')\n ],\n };\n }\n }\n\n checkOptimalCapitalizationMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const optimals = this.getOptimalResponses();\n for (let i = 0; i < optimals.length; i++) {\n const optimal = optimals[i];\n if (userSubmission.toLowerCase() === optimal.text.toLowerCase()) {\n if (userSubmission !== optimal) {\n return {\n optimal: false,\n parentID: optimal.key,\n author: 'Capitalization Hint',\n feedback: 'Proofread your sentence for correct capitalization.',\n conceptResults: [\n conceptResultTemplate('66upe3S5uvqxuHoHOt4PcQ')\n ],\n };\n }\n }\n }\n }\n\n checkOptimalPunctuationMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const optimals = this.getOptimalResponses();\n for (let i = 0; i < optimals.length; i++) {\n const optimal = optimals[i];\n if (removePunctuation(userSubmission) === removePunctuation(optimal.text)) {\n if (userSubmission !== optimal) {\n return {\n optimal: false,\n parentID: optimal.key,\n author: 'Punctuation Hint',\n feedback: 'Proofread your sentence for correct punctuation.',\n conceptResults: [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ],\n };\n }\n }\n }\n }\n\n checkPunctuationAndCaseInsensitiveMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const optimals = this.getOptimalResponses();\n for (let i = 0; i < optimals.length; i++) {\n const optimal = optimals[i];\n if (removePunctuation(userSubmission).toLowerCase() === removePunctuation(optimal.text).toLowerCase()) {\n if (userSubmission !== optimal) {\n return {\n optimal: false,\n parentID: optimal.key,\n author: 'Punctuation and Case Hint',\n feedback: 'Proofread your sentence for correct capitalization and punctuation.',\n conceptResults: [\n conceptResultTemplate('66upe3S5uvqxuHoHOt4PcQ'),\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ],\n };\n }\n }\n }\n }\n\n checkSpacingBeforePunctuationMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const spacingBeforePunctuationMatch = spacingBeforePunctuation(userSubmission);\n if (spacingBeforePunctuationMatch) {\n return {\n optimal: false,\n feedback: spacingBeforePunctuationMatch.feedback,\n author: 'Punctuation Hint',\n parentID: this.getTopOptimalResponse().key,\n conceptResults: [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ],\n };\n }\n }\n\n checkSpacingAfterCommaMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n for (let i = 0; i < userSubmission.length; i++) {\n if (userSubmission[i] === ',' && (i + 1 < userSubmission.length)) {\n if (userSubmission[i + 1] !== ' ') {\n return {\n optimal: false,\n feedback: 'Revise your work. Always put a space after a comma.
',\n author: 'Punctuation Hint',\n parentID: this.getTopOptimalResponse().key,\n conceptResults: [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ],\n };\n }\n }\n }\n }\n\n checkRequiredWordsMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n } else if (this.getOptimalResponses().length < 3) {\n return;\n }\n return checkForMissingWords(userSubmission, this.getOptimalResponses(), true);\n }\n\n checkPOSMatch(userSubmission) {\n // get graded responses and convert to POS strings\n const correctPOSTags = this.getGradedResponses().map(\n optimalResponse => qpos.getPartsOfSpeechTags(optimalResponse.text)\n );\n // convert user submission to POS string\n const userPOSTags = qpos.getPartsOfSpeechTags(userSubmission);\n // if user string could be converted to POS tags find response that has the same POS tags\n if (userPOSTags) {\n const matchedResponse = _.find(this.getGradedResponses(), (optimalResponse, index) => {\n if (optimalResponse.parentID) {\n return false;\n } else if (correctPOSTags[index]) {\n if (JSON.stringify(correctPOSTags[index]) === JSON.stringify(userPOSTags)) {\n // this will return the response object\n return true;\n }\n }\n });\n if (matchedResponse) {\n const returnValue = {\n optimal: matchedResponse.optimal,\n parentID: matchedResponse.key,\n feedback: matchedResponse.feedback,\n author: 'Parts of Speech',\n };\n if (matchedResponse.conceptResults) {\n returnValue.conceptResults = matchedResponse.conceptResults;\n }\n return returnValue;\n }\n }\n }\n}\n\nfunction getMinMaxFeedback(min, max) {\n if (min === max) {\n if (min === 1) {\n return 'Revise your work. Add one word to the prompt to make the sentence complete.';\n }\n return `Revise your work. Add ${constants.NUMBERS_AS_WORDS[min]} words to the prompt to make the sentence complete.`;\n }\n return `Revise your work. Add ${constants.NUMBERS_AS_WORDS[min]} to ${constants.NUMBERS_AS_WORDS[max]} words to the prompt to make the sentence complete.`;\n}\n","import React from 'react';\n\nexport default class extends React.Component {\n state = {\n isExpanded: false,\n };\n\n toggleExpandSinglePOS = () => {\n this.setState({\n isExpanded: !this.state.isExpanded,\n });\n };\n\n renderExpandedPOSListText = () => {\n if (this.state.isExpanded) {\n const tag = this.props.tag;\n const additionalResponses = tag.responses.slice(1); // first response has already been rendered in unexpanded view\n if (additionalResponses.length === 0) {\n return (***No more responses match this pattern***
);\n }\n return additionalResponses.map(response => (\n{response.text}
\n ));\n }\n };\n\n renderExpandedPOSListCount = () => {\n if (this.state.isExpanded) {\n const tag = this.props.tag;\n const additionalResponses = tag.responses.slice(1); // first response has already been rendered in unexpanded view\n return additionalResponses.map(response => (\n{response.count}
\n ));\n }\n };\n\n render() {\n const { headerStyle, } = this.props;\n if (this.state.isExpanded) {\n headerStyle.marginTop = '20px';\n headerStyle.marginBottom = '20px';\n } else {\n headerStyle.marginTop = '0px';\n headerStyle.marginBottom = '0px';\n }\n return (\n{this.props.tagsToRender.join('---')}
\n{this.props.tag.responses[0].text}
\n {this.renderExpandedPOSListText()}\n{this.props.icon} {this.props.tag.count === undefined ? 0 : this.props.tag.count}
\n{this.props.tag.responses[0].count === undefined ? 0 : this.props.tag.responses[0].count}
\n {this.renderExpandedPOSListCount()}\n{message}
;\n };\n\n renderExpandCollapseAll = () => {\n let text,\n handleClick;\n\n if (this.allClosed()) {\n handleClick = this.expandAllResponses;\n text = 'Expand';\n } else {\n handleClick = this.collapseAllResponses;\n text = 'Close';\n }\n return {text};\n };\n\n renderPOSStrings = () => {\n if (!this.state.viewingResponses) {\n const posTagsList = this.getResponsesForCurrentPage(hashToCollection(this.getPOSTagsList()));\n return (\nLoading...
);\n } else if (question) {\n const activeLink = massEdit.numSelectedResponses > 1\n ?{question.instructions || 'Fill in the blank with the correct option.'}
\n\n Edit Question\n
\n404: No Question Found
\n );\n }\n }\n\n}\n\nfunction select(props) {\n return {\n fillInBlank: props.fillInBlank,\n massEdit: props.massEdit\n };\n}\n\nexport default withRouter(connect(select)(FillInBlankQuestion));\n","import React, { Component } from 'react';\nimport { connect } from 'react-redux';\n\nimport questionActions from '../../actions/questions';\nimport sentenceFragmentActions from '../../actions/sentenceFragments';\nimport ResponseComponent from '../questions/responseComponent'\nimport { FocusPointsInputAndConceptSelectorForm, } from '../../../Shared/index';\n\nclass EditFocusPointsContainer extends Component {\n constructor() {\n super();\n\n const questionType = window.location.href.includes('sentence-fragments') ? 'sentenceFragments' : 'questions'\n const questionTypeLink = questionType === 'sentenceFragments' ? 'sentence-fragments' : 'questions'\n const actionFile = questionType === 'sentenceFragments' ? sentenceFragmentActions : questionActions\n\n this.state = { questionType, questionTypeLink, actionFile }\n }\n\n getFocusPoint = () => {\n const { questionType } = this.state\n const { match } = this.props\n const { params } = match\n const { focusPointID, questionID } = params\n const focusPoint = this.props[questionType].data[questionID].focusPoints[focusPointID]\n return Object.assign(focusPoint, { id: focusPointID, });\n }\n\n submitForm = (data, focusPointID) => {\n const { actionFile } = this.state\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params\n delete data.conceptResults.null;\n dispatch(actionFile.submitEditedFocusPoint(questionID, data, focusPointID));\n setTimeout(() => {window.history.back()}, 2000);\n };\n\n render() {\n const { match, questions, sentenceFragments } = this.props\n const { params } = match\n const { questionID } = params\n return (\n{val.name}\n {val.correct ? Correct\n : Incorrect }\n this.deleteConceptResult(key, focusPointKey)} style={{ cursor: 'pointer', marginLeft: 5, }}>Delete\n
\n )\n );\n return _.values(components);\n }\n }\n\n renderFocusPointsList = () => {\n const { match } = this.props\n const { params } = match\n const { questionID } = params\n const components = this.fPsortedByOrder().map((fp) => {\n return (\n\n {this.renderTextInputFields(fp.text, fp.key)}\n \n
\n\n {fp.order}\n
\n{val.name}\n {val.correct ? Correct\n : Incorrect }\n this.deleteConceptResult(key, sequenceKey)} style={{ cursor: 'pointer', marginLeft: 5, }}>Delete\n
\n )\n );\n return _.values(components);\n }\n }\n\n renderSaveButton() {\n return (\n \n );\n }\n\n renderSequenceList = () => {\n const { match } = this.props\n\n const components = this.sequencesSortedByOrder().map((sequence) => {\n return (\n\n {this.renderTextInputFields(sequence.text, sequence.key)}\n \n
\n\n {sequence.order}\n
\n\n \n \n \n
\n )\n }\n\n renderLessonModelNote = () => {\n const { lessonModelConceptUID, modelConceptUID } = this.state\n const { concepts } = this.props\n const { data } = concepts\n if (lessonModelConceptUID && lessonModelConceptUID !== modelConceptUID) {\n const concept = data['0'].find(c => c.uid === lessonModelConceptUID)\n if (concept) {\n return (\nThe activity that this question belongs to has the following Model Concept:
\n\"{concept.displayName}\"
\nAdd a concept to save this question
}\n\n \n
\n\n \n
\n {this.renderDefaultInstructions()}\n \n\n \n
\n \n\n \n
\n {this.renderOptimalResponse()}\n\nLoading...
\n );\n }\n }\n}\n\nfunction select(props) {\n return {\n questions: props.questions,\n playLesson: props.playLesson,\n conceptsFeedback: props.conceptsFeedback\n };\n}\n\nexport default connect(select)(TestQuestion);\n","import React from 'react';\nimport { connect } from 'react-redux';\nimport { NavLink, Route, Switch, withRouter } from 'react-router-dom';\nimport _ from 'underscore';\nimport { Modal, UploadOptimalResponses, } from '../../../Shared/index';\nimport questionActions from '../../actions/questions';\nimport {\n submitOptimalResponses,\n submitResponse\n} from '../../actions/responses';\nimport C from '../../constants';\nimport EditFocusPointsContainer from '../focusPoints/editFocusPointsContainer.jsx';\nimport FocusPointsContainer from '../focusPoints/focusPointsContainer.jsx';\nimport NewFocusPointsContainer from '../focusPoints/newFocusPointsContainer.jsx';\nimport EditIncorrectSequenceContainer from '../incorrectSequence/editIncorrectSequenceContainer.jsx';\nimport IncorrectSequenceContainer from '../incorrectSequence/incorrectSequenceContainer.jsx';\nimport NewIncorrectSequenceContainer from '../incorrectSequence/newIncorrectSequenceContainer.jsx';\nimport Cues from '../renderForQuestions/cues.jsx';\nimport getBoilerplateFeedback from './boilerplateFeedback.jsx';\nimport ChooseModelContainer from './chooseModelContainer.jsx';\nimport MassEditContainer from './massEditContainer.jsx';\nimport EditForm from './questionForm.jsx';\nimport ResponseComponentWrapper from './responseRouteWrapper.jsx';\nimport TestQuestionContainer from './testQuestion';\n\nconst icon = `${process.env.CDN_URL}/images/icons/direction.svg`\n\nexport class Question extends React.Component {\n constructor(props) {\n super(props)\n\n this.state = {\n selectedBoilerplateCategory: '',\n responses: [],\n loadedResponses: false,\n addingNewResponse: false,\n uploadingNewOptimalResponses: false\n }\n }\n\n onCloseNewResponseModal = () => this.setState({ addingNewResponse: false, })\n\n onCloseUploadOptimalResponsesModal = () => this.setState({ uploadingNewOptimalResponses: false })\n\n getQuestion = () => {\n const { match, questions, } = this.props\n const { params } = match\n const { data, } = questions;\n const { questionID, } = params;\n return data[questionID];\n }\n\n boilerplateCategoriesToOptions = () => {\n return getBoilerplateFeedback().map(category => (\n \n ));\n }\n\n boilerplateSpecificFeedbackToOptions = (selectedCategory) =>{\n return selectedCategory.children.map(childFeedback => (\n \n ));\n }\n\n cancelEditingQuestion = () => {\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params\n dispatch(questionActions.cancelQuestionEdit(questionID));\n }\n\n chooseBoilerplateCategory = (e) => {\n this.setState({ selectedBoilerplateCategory: e.target.value, });\n }\n\n chooseSpecificBoilerplateFeedback = (e) => {\n if (e.target.value === 'Select specific boilerplate feedback') {\n this.refs.newResponseFeedback.value = '';\n } else {\n this.refs.newResponseFeedback.value = e.target.value;\n }\n }\n\n handleClickAddNewResponse = () => {\n this.setState({ addingNewResponse: true, });\n }\n\n handleClickStartEditingQuestion = () => {\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params\n dispatch(questionActions.startQuestionEdit(questionID));\n }\n\n handleClickUploadOptimalResponses = () => {\n this.setState({ uploadingNewOptimalResponses: true, });\n }\n\n isLoading = () => {\n const { questions } = this.props\n const { hasreceiveddata } = questions\n return !hasreceiveddata;\n }\n\n saveQuestionEdits = (vals) => {\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params\n dispatch(questionActions.submitQuestionEdit(questionID, vals));\n }\n\n submitOptimalResponses = (responses) => {\n const { dispatch, match, concepts } = this.props\n const { params } = match\n const { questionID } = params\n const conceptUID = this.getQuestion().conceptID\n dispatch(\n submitOptimalResponses(questionID, conceptUID, responses, concepts)\n )\n this.setState({ uploadingNewOptimalResponses: false, })\n }\n\n submitResponse = () => {\n const { match, dispatch, } = this.props\n const { params } = match\n const { questionID } = params\n\n const newResp = {\n vals: {\n text: this.refs.newResponseText.value,\n feedback: this.refs.newResponseFeedback.value,\n optimal: this.refs.newResponseOptimal.checked,\n questionUID: questionID,\n gradeIndex: `human${questionID}`,\n count: 1,\n }\n };\n this.refs.newResponseText.value = null;\n this.refs.newResponseFeedback.value = null;\n this.refs.newResponseOptimal.checked = false;\n dispatch(submitResponse(newResp.vals));\n this.setState({ addingNewResponse: false, });\n }\n\n renderBoilerplateCategoryDropdown = (onChangeEvent) => {\n const style = { marginRight: '20px', };\n return (\n \n \n \n );\n }\n\n renderBoilerplateCategoryOptionsDropdown = (onChangeEvent, description) => {\n const selectedCategory = _.find(getBoilerplateFeedback(), { description, });\n if (selectedCategory) {\n return (\n \n \n \n );\n } else {\n return ();\n }\n }\n\n renderEditForm = () => {\n const { questions, match, concepts, itemLevels, } = this.props\n const { data, states } = questions\n const { params } = match\n const { questionID, } = params;\n const question = (data[questionID]);\n if (states[questionID] === C.EDITING_QUESTION) {\n return (\n\n \n
\n \n\n \n
\n \n\n \n
\n \nLoading...
);\n } else if (data[questionID]) {\n const activeLink = massEdit.numSelectedResponses > 1\n ?{instructionText}
\n\n Edit Question\n Add New Response\n Upload Optimal Responses\n
\n404: No Question Found
\n );\n }\n }\n}\n\nfunction select(state) {\n return {\n concepts: state.concepts,\n questions: state.questions,\n itemLevels: state.itemLevels,\n routing: state.routing,\n massEdit: state.massEdit\n };\n}\n\nexport default withRouter(connect(select)(Question));\n","import React, { Component } from 'react';\nimport { connect } from 'react-redux';\nimport { ConceptExplanation, ConceptSelector, } from '../../../Shared/index';\nimport sentenceFragmentActions from '../../actions/sentenceFragments';\n\nclass ChooseModelContainer extends Component {\n constructor(props) {\n super(props);\n const { match, sentenceFragments } = props\n const { data } = sentenceFragments\n const { params } = match\n const { questionID } = params\n this.state = {\n modelConceptUID: data[questionID].modelConceptUID\n }\n }\n\n getModelConceptUID = () => {\n const { modelConceptUID } = this.state\n const { match, sentenceFragments } = this.props\n const { data } = sentenceFragments\n const { params } = match\n const { questionID } = params\n return modelConceptUID || data[questionID].modelConceptUID;\n }\n\n removeModelConcept = () => {\n const { dispatch, match, sentenceFragments } = this.props\n const { data } = sentenceFragments\n const { params } = match\n const { questionID } = params\n let questionData = Object.assign({}, data[questionID], {modelConceptUID: null});\n dispatch(sentenceFragmentActions.submitSentenceFragmentEdit(questionID, questionData));\n window.history.back();\n };\n\n saveModelConcept = () => {\n const { modelConceptUID } = this.state\n const { dispatch, match, sentenceFragments } = this.props\n const { data } = sentenceFragments\n const { params } = match\n const { questionID } = params\n dispatch(sentenceFragmentActions.submitSentenceFragmentEdit(questionID,\n Object.assign({}, data[questionID], {modelConceptUID: modelConceptUID})));\n window.history.back();\n };\n\n selectConcept = e => {\n this.setState({modelConceptUID: e.value});\n };\n\n renderButtons = () => {\n const { modelConceptUID } = this.state\n const { match, sentenceFragments } = this.props\n const { data } = sentenceFragments\n const { params } = match\n const { questionID } = params\n return(\n\n \n \n \n
\n )\n }\n\n render() {\n const { conceptsFeedback } = this.props\n const { data } = conceptsFeedback\n return(\n\n \n
)\n ]\n );\n };\n\n renderDefaultInstructions = () => {\n const { showDefaultInstructions } = this.state\n const defaultInstructionsDiv = C.DEFAULT_SENTENCE_FRAGMENT_INSTRUCTIONS.map((item, i) =>\n ()\n )\n if (showDefaultInstructions) {\n return\n \n
\n \n\n \n
\n {this.renderDefaultInstructions()}\n\n \n
\n\n \n
\n \n
\n \n
\n {this.renderOptimalResponseTextInput()}\n\n \n
Loading...
\n );\n }\n }\n\n}\n\nfunction select(props) {\n return {\n conceptsFeedback: props.conceptsFeedback,\n sentenceFragments: props.sentenceFragments,\n playLesson: props.playLesson,\n };\n}\n\nexport default connect(select)(TestQuestion);\n","import React from 'react';\nimport { connect } from 'react-redux';\nimport { NavLink, Route, Switch, withRouter } from 'react-router-dom';\nimport { Modal, UploadOptimalResponses, } from '../../../Shared/index';\nimport {\n submitOptimalResponses\n} from '../../actions/responses';\nimport fragmentActions from '../../actions/sentenceFragments';\nimport C from '../../constants';\nimport EditFocusPointsContainer from '../focusPoints/editFocusPointsContainer.jsx';\nimport FocusPointsContainer from '../focusPoints/focusPointsContainer.jsx';\nimport NewFocusPointsContainer from '../focusPoints/newFocusPointsContainer.jsx';\nimport EditIncorrectSequenceContainer from '../incorrectSequence/editIncorrectSequenceContainer.jsx';\nimport IncorrectSequenceContainer from '../incorrectSequence/incorrectSequenceContainer.jsx';\nimport NewIncorrectSequenceContainer from '../incorrectSequence/newIncorrectSequenceContainer.jsx';\nimport MassEditContainer from '../questions/massEditContainer.jsx';\nimport ResponseComponent from '../questions/responseComponent.jsx';\nimport ResponseComponentWrapper from '../questions/responseRouteWrapper.jsx';\nimport ChooseModelContainer from './chooseModelContainer.jsx';\nimport EditForm from './sentenceFragmentForm.jsx';\nimport TestQuestionContainer from './testSentenceFragmentContainer';\n\nconst icon = `${process.env.CDN_URL}/images/icons/direction.svg`\n\nclass SentenceFragment extends React.Component {\n constructor(props) {\n super(props)\n\n this.state = {\n selectedBoilerplateCategory: '',\n responses: [],\n loadedResponses: false,\n uploadingNewOptimalResponses: false\n }\n }\n\n getQuestion = () => {\n const { sentenceFragments, match } = this.props\n const { params } = match\n const { questionID } = params\n const { data } = sentenceFragments\n return data[questionID];\n }\n\n cancelEditingSentenceFragment = () => {\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params\n dispatch(fragmentActions.cancelSentenceFragmentEdit(questionID));\n }\n\n closeModal = () => {\n this.setState({ uploadingNewOptimalResponses: false, })\n }\n\n handleEditFragmentClick = () => {\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params\n dispatch(fragmentActions.startSentenceFragmentEdit(questionID));\n }\n\n handleUploadOptimalResponsesClick = () => {\n this.setState({ uploadingNewOptimalResponses: true, });\n }\n\n saveSentenceFragmentEdits = (data) => {\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params\n dispatch(fragmentActions.submitSentenceFragmentEdit(questionID, data));\n }\n\n submitOptimalResponses(responses) {\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params\n const conceptUID = this.getQuestion().conceptID\n dispatch(submitOptimalResponses(questionID, conceptUID, responses, concepts))\n this.setState({ uploadingNewOptimalResponses: false, })\n }\n\n renderEditForm = () => {\n const { sentenceFragments, match, concepts, } = this.props\n const { data, states } = sentenceFragments\n const { params } = match\n const { questionID } = params\n if (states[questionID] === C.EDITING_SENTENCE_FRAGMENT) {\n return (\n{data[questionID].instructions || 'Combine the sentences into one sentence.'}
\n\n \n
\n )\n }\n\n render() {\n return (\n\n {promptOrTitle}\n {'\\t\\t'}\n \n
\n );\n });\n return\n \n \n
\n\n \n
\n\n \n \n \n \n
\n\n \n \n \n \n
\n\n \n
\n\n \n
\n\n \n \n \n Play Activity\n
\nLoading...
);\n }\n return (\n404: No Concept Found
\n );\n }\n};\n\nfunction select(state) {\n return {\n lessons: state.lessons,\n questions: state.questions,\n routing: state.routing,\n sentenceFragments: state.sentenceFragments,\n fillInBlank: state.fillInBlank,\n titleCards: state.titleCards\n };\n}\n\nexport default connect(select)(Lesson);\n","import React from 'react';\nimport Lesson from '../lessons/lesson.jsx';\n\nconst AdminLessonSidebar = ({ match }) => {\n return (\n\n \n \n { this.renderTranslationButton(data[conceptFeedbackID]) }\n
\nLoading...
)\n } else {\n return (\n\n \n \n
\n\n \n
\nFill In The Blank
\n\n \n \n
\n\n \n \n \n
\n\n \n
\nSentence Fragments
\n\n The activity session with this unique identifier has already been completed.
\n In order to redo this activity, you must return to your dashboard and click \"Replay Activity\".
\n If you believe that you have received this message in error, ask your teacher to contact Quill.
\n Please provide the following URL to help us solve the problem.\n
Your results could not be saved.
\n Make sure you are connected to the internet.
\n You can attempt to save again using the button below.
\n If the issue persists, ask your teacher to contact Quill.
\n Please provide the following URL to help us solve the problem.\n
\n {window.location.href}\n
{translate('Quill Connect Completed^message')}
: null\n return (\nYour results are being saved now. You'll be redirected automatically once they are saved.
\n {translatedMessage}\n\n Your unique code is ij3982rmf9.\n
\n\n You're about to answer questions about writing sentences.\n Please answer to the best of your ability.\n
\n \n