/**
	 * Applies interpretation script category error messages
	 * to the current proposal line.
	 * @param   $prop_desc  array
	 *          keys are category numbers (indexes)
	 *          values are interpretation script-generated error messages
	 * @return  boolean true when at least one category was found in the list
	 *          false otherwise
	 */
	function applyInterpErrors( array $prop_desc ) {
		$foundCats = false;
		# scan the category views row to highlight erroneous categories
		foreach ( $this->row as $cat_id => &$cat_tag ) {
			# only integer keys are the category views
			if ( is_int( $cat_id ) && isset( $prop_desc[$cat_id] ) ) {
				# found a category which has script-generated error
				$foundCats = true;
				# whether to use custom or standard error message
				if ( !is_string( $cat_desc = $prop_desc[$cat_id] ) ) {
					$cat_desc = wfMsg( 'qp_interpetation_wrong_answer' );
				}
				# highlight the input
				qp_Renderer::addClass( $cat_tag, 'cat_error' );
				array_unshift( $cat_tag, $this->ctrl->view->bodyErrorMessage( $cat_desc, '', false ) . '<br />' );
			}
		}
		return $foundCats;
	}
	/**
	 * Creates question view which should be renreded and
	 * also may be altered during the poll generation
	 */
	function questionParseBody( $inputType ) {
		$proposalId = -1;
		# set static view state for the future qp_TabularQuestionProposalView instances
		qp_TabularQuestionProposalView::applyViewState( $this->view );
		$prop_attrs = qp_Setup::$propAttrs;
		$prop_attrs->setQuestion( $this );
		while ( $prop_attrs->iterate() ) {
			# new proposal view
			$pview = new qp_TabularQuestionProposalView( $proposalId + 1, $this );
			$proposalId++;
			$prop_attrs->dbText = $pview->text = $prop_attrs->cpdef;
			if ( is_string( $prop_attrs->error ) ) {
				$pview->prependErrorMessage( wfMsg( $prop_attrs->error ), 'error' );
			} elseif ( $prop_attrs->name !== '' ) {
				$this->mProposalNames[$proposalId] = $prop_attrs->name;
			}
			$this->mProposalText[$proposalId] = strval( $prop_attrs );
			foreach ( $this->mCategories as $catId => $catDesc ) {
				# start new input field tag (category)
				$pview->addNewCategory( $catId );
				$inp = array( '__tag' => 'input' );
				$pview->resetSpanState();
				# Determine the input's name and value.
				switch( $this->mType ) {
				case 'multipleChoice':
					$name = "q{$this->mQuestionId}p{$proposalId}s{$catId}";
					$value = "s{$catId}";
					break;
				case 'singleChoice':
					$name = "q{$this->mQuestionId}p{$proposalId}";
					$value = "s{$catId}";
					# category spans have sense only with single choice proposals
					$pview->renderSpan( $name, $value, $catDesc );
					break;
				}
				# Determine if the input had to be checked.
				if ( $this->poll->mBeingCorrected && qp_Setup::$request->getVal( $name ) == $value ) {
					$inp[ 'checked' ] = 'checked';
				}
				if ( $this->answerExists( $inputType, $proposalId, $catId ) !== false ) {
					$inp[ 'checked' ] = 'checked';
				}
				if ( array_key_exists( 'checked', $inp ) ) {
					if ( $this->mSubType == 'unique' ) {
						if ( $this->poll->mBeingCorrected && !$this->isUniqueProposalCategoryId( $proposalId, $catId ) ) {
							$pview->prependErrorMessage( wfMsg( 'qp_error_non_unique_choice' ), 'NA' );
							unset( $inp[ 'checked' ] );
							qp_Renderer::addClass( $row[ $catId ], 'error' );
						}
					} else {
						$pview->spanWasChecked( true );
					}
				}
				if ( array_key_exists( 'checked', $inp ) ) {
					# add category to the list of user answers for current proposal (row)
					$this->mProposalCategoryId[ $proposalId ][] = $catId;
					$this->mProposalCategoryText[ $proposalId ][] = '';
				}
				$pview->setCategorySpan();
				if ( $this->mSubType == 'unique' ) {
					# unique (orderid,question,proposal,category) "coordinate" for javascript
					$inp['id'] = "uq{$this->poll->mOrderId}q{$this->mQuestionId}p{$proposalId}c{$catId}";
					# If type='unique()' question has more proposals than categories, such question is impossible to complete
					if ( count( $this->mProposalText ) > count( $this->mCategories ) ) {
						# if there was no previous errors, hightlight the whole row
						if ( $this->getState() == '' ) {
							$pview->addCellsClass( 'error' );
						}
						$pview->prependErrorMessage( wfMsg( 'qp_error_unique' ), 'error' );
					}
				}
				$inp['class'] = 'check';
				$inp['type'] = $inputType;
				$inp['name'] = $name;
				$inp['value'] = $value;
				$pview->setCat( $inp );
			}
			# If the proposal text is empty, the question has a syntax error.
			if ( $pview->text !== null && trim( $pview->text ) == '' ) {
				$pview->setErrorMessage( wfMsg( 'qp_error_proposal_text_empty' ), 'error' );
				$pview->addCellsClass( 'error' );
			}
			if ( $inputType === 'radio' && $prop_attrs->catreq > 1 ) {
				# radio buttons row always require not more than one category,
				# otherwise the poll will be impossible to submit sucessfully.
				$prop_attrs->catreq = 1;
			}
			# If the proposal was submitted but unanswered
			if ( $this->poll->mBeingCorrected &&
						$prop_attrs->hasMissingCategories(
							$answered_cats_count = $this->getAnsweredCatCount( $proposalId ),
							count( $this->mCategories )
						) ) {
				# if there was no previous errors, hightlight the whole row
				if ( $this->getState() == '' ) {
					$pview->addCellsClass( 'error' );
				}
				# the proposal was submitted but has not enough categories answered
				$pview->prependErrorMessage(
					($answered_cats_count > 0) ?
						wfMsg( 'qp_error_not_enough_categories_answered' ) :
						wfMsg( 'qp_error_no_answer' )
					, 'NA'
				);
			}
			if ( $pview->text !== null ) {
				$this->view->addProposal( $proposalId, $pview );
			}
		}
	}