public static function impl_formulation_and_controls(qtype_multichoice_renderer_base $renderer, question_attempt $qa, question_display_options $options) { global $CFG, $PAGE, $OUTPUT; // get the current question $question = $qa->get_question(); // get the response $response = $question->get_response($qa); // answer prefix $answer_input_name = $qa->get_qt_field_name('answer'); // set the ID of the OmeroImageViewer $omero_frame_id = self::to_unique_identifier($qa, "omero-image-viewer"); // set the ID the ModalImagePanel $modal_image_panel_id = $omero_frame_id . "-" . qtype_omerocommon_renderer_helper::MODAL_VIEWER_ELEMENT_ID; // set the name of the feedback image class $feedback_image_class = $omero_frame_id . "-feedbackimage"; // ID of the question answer container $question_answer_container = self::to_unique_identifier($qa, "omero-interactive-question-container"); // the OMERO image URL $omero_image_url = $question->omeroimageurl; // extract the omero server $OMERO_SERVER = get_config('omero', 'omero_restendpoint'); // parse the URL to get the image ID and its related params $matches = array(); $pattern = '/\\/([0123456789]+)(\\?.*)?/'; if (preg_match($pattern, $omero_image_url, $matches)) { $omero_image = $matches[1]; $omero_image_params = count($matches) === 3 ? $matches[2] : ""; } // check $multi_correct_answer = $question instanceof qtype_omerointeractive_multi_question; $no_max_markers = 0; $answer_shape_map = array(); $shape_grade_map = new stdClass(); $available_shapes = array(); $shape_groups = array(); foreach ($question->get_order($qa) as $ans_idx => $ansid) { $ans = $question->answers[$ansid]; $value = $ans->answer; $shape_group = array_map("intval", explode(",", $ans->answer)); $shape_group_cardinality = count($shape_group); // update the max number of markers allowed if ($ans->fraction > 0 && !empty($value)) { $no_max_markers += $shape_group_cardinality; } // compute the shape grade $shape_group_fraction = 0; if ($shape_group_cardinality > 0) { $shape_group_fraction = $multi_correct_answer ? $ans->fraction / $shape_group_cardinality : $ans->fraction; } foreach ($shape_group as $shape_id) { $shape_grade_map->{$shape_id} = $shape_group_fraction; array_push($available_shapes, $shape_id); $answer_shape_map[$shape_id] = $ans; } array_push($shape_groups, array("shapes" => $shape_group, "shape_grade" => $shape_group_fraction)); } // Fix the max number of markers if (!$multi_correct_answer) { $no_max_markers = 1; } $answer_order = ""; $answer_options = array(); $feedbackimages = array(); foreach ($ans->feedbackimages as $image_id => $image) { array_push($feedbackimages, $image_id); } $feedbackimages_html = ""; if (count($ans->feedbackimages) > 0) { $feedbackimages_html = '<div style="display: block; float: right;">[ ' . get_string("see", "qtype_omerocommon") . " "; $current_language = current_language(); foreach ($ans->feedbackimages as $image) { $feedbackimages_html .= '<span class="' . $feedback_image_class . '" imageid="' . $image->id . '"' . ' imagename="' . $image->name . '"' . ' imagedescription="' . $image->description_locale_map->{$current_language} . '"' . ' imagelock="' . $image->lock . '"' . ' imageproperties="' . htmlspecialchars(json_encode($image->properties)) . '"' . ' visiblerois="' . implode(",", $image->visiblerois) . '"' . ' focusablerois="' . implode(",", $image->focusablerois) . '"' . '>' . '<i class="glyphicon glyphicon-book" style="margin-left: 2px; margin-right: 5px;"></i>' . '"' . $image->name . '"</span>'; } $feedbackimages_html .= ' ]</div>'; } $feedbackimg = array(); $classes = array(); // Show correct/wrong markers if ($options->correctness) { foreach ($response->markers as $index => $marker) { $shape = $response->shapes[$index]; $isselected = true; $hidden = ''; $answer_options_attributes = array(); $marker_correction = $hidden; $marker_correction .= html_writer::empty_tag('li', $answer_options_attributes); $marker_correction_text = get_string("marker", "qtype_omerointeractive") . " " . html_writer::tag("i", " ", array("class" => "glyphicon glyphicon-map-marker roi-shape-info", "roi-shape-id" => $marker->shape_id)) . " "; if ($shape !== "none") { $marker_correction_text .= get_string("your_marker_inside", "qtype_omerointeractive") . " " . html_writer::tag("i", " ", array("class" => "glyphicon glyphicon-map-marker roi-shape-info", "roi-shape-id" => $shape->shape_id)) . " [" . $shape->shape_id . "] "; } else { $marker_correction_text .= get_string("your_marker_outside", "qtype_omerointeractive"); } $marker_correction_text .= '<span class="pull-right">' . $renderer->feedback_image($renderer->is_right_marker($shape_grade_map, $response, $index)) . html_writer::tag("i", " ", array("class" => "glyphicon glyphicon-eye-open roi-shape-visibility", "roi-shape-id" => $marker->shape_id, "style" => "margin-right: 5px")) . '</span>'; if ($shape !== "none" && !empty(strip_tags($answer_shape_map[$shape->shape_id]->feedback))) { $shape_answer = $answer_shape_map[$shape->shape_id]; $marker_correction_text .= html_writer::tag("div", html_writer::tag("i", " ", array("class" => "pull-left glyphicon glyphicon-record", "style" => "margin-right: 5px")) . format_text($shape_answer->feedback) . $feedbackimages_html, array("class" => "outcome", "style" => "padding: 20px 30px 20px;")); } $marker_correction .= html_writer::tag('label', $marker_correction_text); $answer_options[] = $marker_correction; $class = 'r' . $value % 2; $answer_options_attributes['checked'] = 'checked'; $feedback_class = $renderer->feedback_image($renderer->is_right_marker($shape_grade_map, $response, $index)); $feedbackimg[] = $renderer->feedback_image($renderer->is_right_marker($shape_grade_map, $response, $index)); $class .= ' ' . $renderer->feedback_class($renderer->is_right_marker($shape_grade_map, $response, $index)); $classes[] = $class; } } foreach ($question->get_order($qa) as $value => $ansid) { $ans = $question->answers[$ansid]; $answer_order .= $ans->answer; $answer_options_attributes['name'] = $renderer->get_input_name($qa, $value); $answer_options_attributes['value'] = $renderer->get_input_value($value); $answer_options_attributes['id'] = $renderer->get_input_id($qa, $value); $hidden = ''; if (!$options->readonly && $renderer->get_input_type() == 'checkbox') { $hidden .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $answer_options_attributes['name'], 'value' => 0)); } } /** * Render the question */ $result = ''; // add the ModalImagePanel template $result .= qtype_omerocommon_renderer_helper::modal_viewer(true, true, true, $modal_image_panel_id); // main question_answer_container $result .= html_writer::start_tag('div', array('id' => $question_answer_container, 'class' => 'ablock')); // question text $result .= html_writer::tag('div', $question->format_questiontext($qa), array('class' => 'qtext')); // viewer of the question image $result .= '<div class="image-viewer-with-controls-container">'; $result .= '<!-- TOOLBAR --> <div class="btn-group interactive-player-toolbar pull-right" style="margin-left: 5px;" data-toggle="buttons" aria-pressed="false" autocomplete="true"> <a href="#" id="' . self::to_unique_identifier($qa, self::IMAGE_CLEAR_MARKER_CTRL) . '" class="btn btn-default disabled" aria-label="Left Align"> <i class="glyphicon glyphicon-remove"></i> ' . get_string('clear_markers', 'qtype_omerointeractive') . '</a> </div> <div class="btn-group interactive-player-toolbar pull-right" data-toggle="buttons" aria-pressed="false" autocomplete="off"> <a href="#" id="' . self::to_unique_identifier($qa, self::IMAGE_ADD_MARKER_CTRL) . '" class="btn btn-default disabled" aria-label="Left Align"> <i class="glyphicon glyphicon-plus"></i> ' . get_string('add_marker', 'qtype_omerointeractive') . '</a> <a href="#" id="' . self::to_unique_identifier($qa, self::IMAGE_EDIT_MARKER_CTRL) . '" class="btn btn-default disabled" aria-label="Left Align"> <i class="glyphicon glyphicon-edit"></i> ' . get_string('edit_marker', 'qtype_omerointeractive') . '</a> </div>'; $result .= '<div id="' . self::to_unique_identifier($qa, "graphics_container") . '" class="image-viewer-container" style="position: relative;" > <div id="' . self::to_unique_identifier($qa, self::IMAGE_VIEWER_CONTAINER) . '" style="position: absolute; width: 100%; height: 500px; margin: auto; z-index: 0;"></div> <canvas id="' . self::to_unique_identifier($qa, 'annotations_canvas') . '" style="position: absolute; width: 100%; height: 500px; margin: auto; z-index: 1;"></canvas> <div id="' . self::to_unique_identifier($qa, self::IMAGE_VIEWER_CONTAINER) . '-loading-dialog" class="image-viewer-loading-dialog"></div> </div>'; $image_properties = null; $image_properties = json_decode($question->omeroimageproperties); $result .= '<div class="image_position_button">' . '<span class="sm">' . ($question->omeroimageproperties ? '<b>(x,y):</b> ' . $image_properties->center->x . ", " . $image_properties->center->y . '<i class="restore-image-center-btn glyphicon glyphicon-screenshot" style="margin-left: 10px;"></i>' : "") . '</span></div>'; if (!empty($question->focusablerois)) { $result .= '<div id="' . self::to_unique_identifier($qa, self::FOCUS_AREAS_CONTAINER) . '" ' . ' class="focus_areas_container">' . '<span class="focus-areas-text">* ' . get_string("focusareas", "qtype_omerointeractive") . '</span> ' . '</div>'; } $result .= '<div id="' . self::to_unique_identifier($qa, self::MARKER_REMOVERS_CONTAINER) . '" ' . ' class="remove_marker_button_group">' . '<span class="yourmarkers-text">* ' . get_string("yourmarkers", "qtype_omerointeractive") . '</span> ' . '</div>'; $result .= '</div>'; $answer_input_name = $qa->get_qt_field_name('answer'); $answer_options_attributes = array('type' => $renderer->get_input_type(), 'name' => $answer_input_name); if ($options->readonly) { $answer_options_attributes['disabled'] = 'disabled'; } if ($options->correctness && count($response->markers) > 0) { $result .= html_writer::start_tag('div', array('class' => 'question-summary hidden')); $result .= html_writer::tag('div', count($response->markers) === 1 ? get_string("notice_your_answer", "qtype_omerocommon") : get_string("notice_your_answers", "qtype_omerocommon"), array("class" => "answer-summary-fixed-text")); $result .= html_writer::start_tag('ul', array('class' => 'answer')); foreach ($answer_options as $key => $answer_option) { $result .= html_writer::tag('div', $answer_option, array('class' => $classes[$key])) . "\n"; } $result .= html_writer::end_tag('ul'); // Answer. $result .= html_writer::end_tag('div'); // Answer. } $result .= html_writer::tag('div', '', array('id' => $question_answer_container . '-invalidator-panel', 'class' => "invalidator-panel")); $result .= html_writer::end_tag('div'); // Ablock. // support for dialog message $result .= html_writer::tag('div', ' <div class="modal fade" id="modal-frame-' . $omero_frame_id . '" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title text-warning" id="modal-frame-label-' . $omero_frame_id . '"> <i class="glyphicon glyphicon-warning-sign"></i> ' . get_string('validate_warning', 'qtype_omerocommon') . '</h4> </div> <div class="modal-body text-left"> <span id="modal-frame-text-' . $omero_frame_id . '"></span> </div> <div class="modal-footer text-center"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> </div> </div> </div>'); $player_config = array("image_id" => $omero_image, "image_properties" => json_decode($question->omeroimageproperties), "image_frame_id" => $omero_frame_id, "image_annotations_canvas_id" => self::to_unique_identifier($qa, "annotations_canvas"), "modal_image_panel_id" => $modal_image_panel_id, "feedback_image_class" => $feedback_image_class, "image_server" => $OMERO_SERVER, "image_viewer_container" => self::to_unique_identifier($qa, self::IMAGE_VIEWER_CONTAINER), "image_navigation_locked" => (bool) $question->omeroimagelocked, "viewer_model_server" => $CFG->omero_image_server, "qname" => $question->name, "question_answer_container" => $question_answer_container, "enable_add_makers_ctrl_id" => self::to_unique_identifier($qa, self::IMAGE_ADD_MARKER_CTRL), "enable_edit_markers_ctrl_id" => self::to_unique_identifier($qa, self::IMAGE_EDIT_MARKER_CTRL), "remove_marker_ctrl_id" => self::to_unique_identifier($qa, self::IMAGE_DEL_MARKER_CTRL), "clear_marker_ctrl_id" => self::to_unique_identifier($qa, self::IMAGE_CLEAR_MARKER_CTRL), "marker_removers_container" => self::to_unique_identifier($qa, self::MARKER_REMOVERS_CONTAINER), "focus_areas_container" => self::to_unique_identifier($qa, self::FOCUS_AREAS_CONTAINER), "answer_input_name" => $answer_input_name, "available_shapes" => $available_shapes, "shape_groups" => $shape_groups, "visible_rois" => empty($question->visiblerois) ? [] : explode(",", $question->visiblerois), "focusable_rois" => empty($question->focusablerois) ? [] : explode(",", $question->focusablerois), "correction_mode" => (bool) $options->correctness, "response" => $response, "answers" => $response, "answer_fraction" => $shape_grade_map, "max_markers" => $no_max_markers); $player_config_element_id = self::to_unique_identifier($qa, "viewer-config"); $result .= html_writer::empty_tag("input", array("id" => $player_config_element_id, "type" => "hidden", "value" => json_encode($player_config))); if ($qa->get_state() == question_state::$invalid) { $result .= html_writer::nonempty_tag('div', $question->get_validation_error($qa->get_last_qt_data()), array('class' => 'validationerror')); } $result .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => "answer_input_name", 'value' => $answer_input_name)); global $PAGE; $PAGE->requires->string_for_js('marker', 'qtype_omerointeractive'); $PAGE->requires->js_call_amd("qtype_omerointeractive/question-player-interactive", "start", array($player_config_element_id)); return $result; }
protected function num_parts_correct(question_attempt $qa) { if ($qa->get_question()->get_num_selected_choices($qa->get_last_qt_data()) > $qa->get_question()->get_num_correct_choices()) { return get_string('toomanyselected', 'qtype_multichoice'); } return parent::num_parts_correct($qa); }
public static function impl_formulation_and_controls(qtype_multichoice_renderer_base $renderer, question_attempt $qa, question_display_options $options) { global $CFG, $PAGE; // get the current question $question = $qa->get_question(); // get the response $response = $question->get_response($qa); // answer prefix $answer_input_name = $qa->get_qt_field_name('answer'); // set the ID of the OmeroImageViewer $omero_frame_id = self::to_unique_identifier($qa, "omero-image-viewer"); // set the ID of the ModalImagePanel $modal_image_panel_id = $omero_frame_id . "-" . qtype_omerocommon_renderer_helper::MODAL_VIEWER_ELEMENT_ID; // set the name of feedback image class $feedback_image_class = $omero_frame_id . "-feedbackimage"; // set the ID of the answer container $question_answer_container = self::to_unique_identifier($qa, "omero-multichoice-question-container"); // the OMERO image URL $omero_image_url = $question->omeroimageurl; // extract the omero server $OMERO_SERVER = get_config('omero', 'omero_restendpoint'); // parse the URL to get the image ID and its related params $matches = array(); $pattern = '/\\/([0123456789]+)(\\?.*)?/'; if (preg_match($pattern, $omero_image_url, $matches)) { $omero_image = $matches[1]; $omero_image_params = count($matches) === 3 ? $matches[2] : ""; } $no_max_markers = 0; $available_answers = array(); foreach ($question->get_order($qa) as $ans_idx => $ansid) { $ans = $question->answers[$ansid]; $value = $ans->answer; array_push($available_answers, $value); if ($ans->fraction > 0 && !empty($value)) { $shape_group = explode(",", $ans->answer); $no_max_markers += count($shape_group); } } $multi_correct_answer = $question instanceof qtype_omeromultichoice_multi_renderer; if (!$multi_correct_answer) { $no_max_markers = 1; } $inputname = $qa->get_qt_field_name('answer'); $inputattributes = array('type' => $renderer->get_input_type(), 'name' => $inputname); if ($options->readonly) { $inputattributes['disabled'] = 'disabled'; } $radiobuttons = array(); $feedbackimg = array(); $feedback = array(); $classes = array(); $num_of_response = 0; foreach ($question->get_order($qa) as $value => $ansid) { $ans = $question->answers[$ansid]; $inputattributes['name'] = $renderer->get_input_name($qa, $value); $inputattributes['value'] = $renderer->get_input_value($value); $inputattributes['id'] = $renderer->get_input_id($qa, $value . "XXX"); $isselected = $question->is_choice_selected($response, $value); if ($isselected) { $inputattributes['checked'] = 'checked'; $num_of_response++; } else { unset($inputattributes['checked']); } $hidden = ''; if (!$options->readonly && $renderer->get_input_type() == 'checkbox') { $hidden = html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $inputattributes['name'], 'value' => 0)); } $feedbackimages = array(); foreach ($ans->feedbackimages as $image_id => $image) { array_push($feedbackimages, $image_id); } $feedbackimages_html = ""; if (count($ans->feedbackimages) > 0) { $feedbackimages_html = '<div style="display: block; float: right;">[ ' . get_string("see", "qtype_omerocommon") . " "; $current_language = current_language(); foreach ($ans->feedbackimages as $image) { $feedbackimages_html .= '<span class="' . $feedback_image_class . '" imageid="' . $image->id . '"' . ' currentlanguage="' . $current_language . '"' . ' imagename="' . $image->name . '"' . ' imagedescription="' . htmlspecialchars($image->description_locale_map->{$current_language}) . '"' . ' imagelock="' . ($image->lock ? "true" : "false") . '"' . ' imageproperties="' . htmlspecialchars(json_encode($image->properties)) . '"' . ' visiblerois="' . implode(",", $image->visiblerois) . '"' . ' focusablerois="' . implode(",", $image->focusablerois) . '"' . '>' . '<i class="glyphicon glyphicon-book" style="margin-left: 2px; margin-right: 5px;"></i>' . '"' . $image->name . '"</span>'; } $feedbackimages_html .= ' ]</div>'; } // Param $options->suppresschoicefeedback is a hack specific to the // oumultiresponse question type. It would be good to refactor to // avoid refering to it here. $feedback_content = null; if ($options->feedback && empty($options->suppresschoicefeedback) && $isselected) { $feedback_content = $isselected ? '<span class="pull-right">' . $renderer->feedback_image($renderer->is_right($ans)) . '</span>' : ""; $feedback_text = trim($ans->feedback); if (!empty(strip_tags($feedback_text))) { $feedback_content .= html_writer::tag("div", html_writer::tag("i", " ", array("class" => "pull-left glyphicon glyphicon-record", "style" => "margin-left: 10px; margin-right: 5px")) . format_text($feedback_text) . $feedbackimages_html, array("class" => "outcome", "style" => "padding: 20px 15px;")); } $feedback[] = $feedback_content; } else { $feedback[] = ''; } $class = 'r' . $value % 2; if ($options->correctness && $isselected) { $feedbackimg[] = $renderer->feedback_image($renderer->is_right($ans)); $class .= ' ' . $renderer->feedback_class($renderer->is_right($ans)); } else { $feedbackimg[] = ''; } $classes[] = $class; $radiobutton_label = html_writer::tag('label', $question->format_text($renderer->number_in_style($value, $question->answernumbering) . preg_replace('/<p[^>]*>(.*)<\\/p[^>]*>/i', '$1', $ans->answer), $ans->answerformat, $qa, 'question', 'answer', $ansid), array('for' => $inputattributes['id'])); $radiobuttons[] = $hidden . html_writer::empty_tag('input', $inputattributes) . qtype_omeromultichoice_base_renderer::number_answer($value, $question->answernumbering) . $radiobutton_label; } /** * Render the question */ $result = ''; // add the ModalImagePanel $result .= qtype_omerocommon_renderer_helper::modal_viewer(true, true, true, $modal_image_panel_id); // main question_answer_container $result .= html_writer::start_tag('div', array('id' => $question_answer_container, 'class' => 'ablock')); // question text $result .= html_writer::tag('div', $question->format_questiontext($qa), array('class' => 'qtext')); // viewer of the question image $result .= '<div class="image-viewer-with-controls-container">'; $result .= '<div id="' . self::to_unique_identifier($qa, "graphics_container") . '" class="image-viewer-container" style="position: relative;" > <div id="' . self::to_unique_identifier($qa, self::IMAGE_VIEWER_CONTAINER) . '" style="position: absolute; width: 100%; height: 500px; margin: auto;"></div> <canvas id="' . self::to_unique_identifier($qa, 'annotations_canvas') . '" style="position: absolute; width: 100%; height: 500px; margin: auto;"></canvas> <div id="' . self::to_unique_identifier($qa, self::IMAGE_VIEWER_CONTAINER) . '-loading-dialog" class="image-viewer-loading-dialog"></div> </div>'; $image_properties = null; if ($question->omeroimageproperties) { $image_properties = json_decode($question->omeroimageproperties); $result .= '<div class="image_position_button">' . '<span class="sm">' . '<b>(x,y):</b> ' . $image_properties->center->x . ", " . $image_properties->center->y . '<i class="restore-image-center-btn glyphicon glyphicon-screenshot" style="margin-left: 10px;">' . '</i></span></div>'; } $result .= '</div>'; if (!empty($question->focusablerois)) { $result .= '<div id="' . self::to_unique_identifier($qa, self::FOCUS_AREAS_CONTAINER) . '" ' . ' class="focus_areas_container">' . '<span class="focus-areas-text">* ' . get_string("focusareas", "qtype_omerointeractive") . '</span> ' . '</div>'; } $result .= html_writer::start_tag('div', array('class' => 'multichoice-options-container')); $result .= html_writer::tag('div', !$options->correctness ? $renderer->prompt() : ($num_of_response === 1 ? get_string("notice_your_answer", "qtype_omerocommon") : get_string("notice_your_answers", "qtype_omerocommon")), array('class' => 'prompt')); $result .= html_writer::start_tag('div', array('class' => 'answer')); foreach ($radiobuttons as $key => $radio) { $result .= html_writer::tag('div', $radio . ' ' . $feedback[$key], array('class' => $classes[$key])); } $result .= html_writer::end_tag('div'); // Answer. $result .= html_writer::tag('div', '', array('id' => $question_answer_container . '-invalidator-panel', 'class' => "invalidator-panel")); $result .= html_writer::end_tag('div'); // Ablock. // support for dialog message $result .= html_writer::tag('div', ' <div class="modal fade" id="modal-frame-' . $omero_frame_id . '" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title text-warning" id="modal-frame-label-' . $omero_frame_id . '"> <i class="glyphicon glyphicon-warning-sign"></i> ' . get_string('validate_warning', 'qtype_omerocommon') . '</h4> </div> <div class="modal-body text-left"> <span id="modal-frame-text-' . $omero_frame_id . '"></span> </div> <div class="modal-footer text-center"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> </div> </div> </div> </div>'); if ($qa->get_state() == question_state::$invalid) { $result .= html_writer::nonempty_tag('div', $question->get_validation_error($qa->get_last_qt_data()), array('class' => 'validationerror')); } // set the player configuration $player_config = array("image_id" => $omero_image, "image_properties" => json_decode($question->omeroimageproperties), "image_frame_id" => $omero_frame_id, "image_annotations_canvas_id" => self::to_unique_identifier($qa, "annotations_canvas"), "modal_image_panel_id" => $modal_image_panel_id, "feedback_image_class" => $feedback_image_class, "image_server" => $OMERO_SERVER, "viewer_model_server" => $CFG->omero_image_server, "image_viewer_container" => self::to_unique_identifier($qa, self::IMAGE_VIEWER_CONTAINER), "image_navigation_locked" => (bool) $question->omeroimagelocked, "qname" => $question->name, "question_answer_container" => $question_answer_container, "focus_areas_container" => self::to_unique_identifier($qa, self::FOCUS_AREAS_CONTAINER), "visible_rois" => empty($question->visiblerois) ? [] : explode(",", $question->visiblerois), "focusable_rois" => empty($question->focusablerois) ? [] : explode(",", $question->focusablerois), "answer_input_name" => $answer_input_name); // embed the player configuration within an hidden input element $player_config_element_id = self::to_unique_identifier($qa, "player-config"); $result .= html_writer::empty_tag("input", array("id" => $player_config_element_id, "type" => "hidden", "value" => json_encode($player_config))); // start the pplayer $PAGE->requires->js_call_amd("qtype_omeromultichoice/question-player-multichoice", "start", array($player_config_element_id)); return $result; }