private function getFeatureReplacementString($node)
 {
     // Process inline feature
     if (isset($node->attr['data-type']) && isset($node->attr['data-src'])) {
         $src = trim($node->attr['data-src']);
         $type = trim($node->attr['data-type']);
         if ($type === 'audioplayer' || $type === 'audioplayer') {
             return QtiMarshallerUtil::marshallValidQti(new Object($src, MimeUtil::guessMimeType(basename($src))));
         }
         // Process regular question feature
     } else {
         $nodeClassAttribute = $node->attr['class'];
         $featureReference = $this->getFeatureReferenceFromClassName($nodeClassAttribute);
         $feature = $this->widgets[$featureReference];
         $type = $feature['data']['type'];
         if ($type === 'audioplayer' || $type === 'audioplayer') {
             $src = $feature['data']['src'];
             $object = new Object($src, MimeUtil::guessMimeType(basename($src)));
             $object->setLabel($featureReference);
             return QtiMarshallerUtil::marshallValidQti($object);
         } else {
             if ($type === 'sharedpassage') {
                 $content = $feature['data']['content'];
                 $object = new Object('', 'text/html');
                 $object->setContent(ContentCollectionBuilder::buildObjectFlowCollectionContent(QtiMarshallerUtil::unmarshallElement($content)));
                 $object->setLabel($featureReference);
                 return QtiMarshallerUtil::marshallValidQti($object);
             }
         }
     }
     throw new MappingException($type . ' not supported');
 }
 public static function buildQtiImageObject($data, $type)
 {
     $object = new Object($data, $type);
     $objectFlowCollection = new ObjectFlowCollection();
     $objectFlowCollection->attach(new TextRun('qwe'));
     $object->setContent($objectFlowCollection);
     return $object;
 }
 protected function buildTemplate(QtiGraphicGapMatchInteraction $interaction, Object $object)
 {
     $associableHotspots = [];
     foreach ($interaction->getAssociableHotspots() as $associableHotspot) {
         /** @var AssociableHotspot $associableHotspot */
         $associableHotspots[$associableHotspot->getIdentifier()] = QtiCoordinateUtil::convertQtiCoordsToPercentage([$object->getWidth(), $object->getHeight()], explode(',', $associableHotspot->getCoords()), $associableHotspot->getShape());
     }
     return $associableHotspots;
 }
 public static function buildWithPolyShapesChoices($responseIdentifier)
 {
     $imageObject = new Object('http://anyurl.com', 'image/png');
     $imageObject->setWidth(131);
     $imageObject->setHeight(37);
     $collection = new HotspotChoiceCollection();
     $collection->attach(new HotspotChoice('A', QtiShape::POLY, new QtiCoords(QtiShape::POLY, [7, 12, 64, 12, 64, 28, 7, 28])));
     $collection->attach(new HotspotChoice('B', QtiShape::POLY, new QtiCoords(QtiShape::POLY, [66, 11, 125, 11, 125, 30, 66, 30])));
     return new HotspotInteraction($responseIdentifier, $imageObject, 0, $collection);
 }
 public function testMarshallSimple()
 {
     $param1 = new Param('movie', 'movie.swf', ParamType::REF);
     $param2 = new Param('quality', 'high', ParamType::DATA);
     $object = new Object('http://mywebsite.com/movie.swf', 'application/x-shockwave-flash', 'flash-movie');
     $object->setContent(new ObjectFlowCollection(array($param1, $param2)));
     $element = $this->getMarshallerFactory()->createMarshaller($object)->marshall($object);
     $dom = new DOMDocument('1.0', 'UTF-8');
     $element = $dom->importNode($element, true);
     $this->assertEquals('<object data="http://mywebsite.com/movie.swf" type="application/x-shockwave-flash" id="flash-movie"><param name="movie" value="movie.swf" valuetype="REF"/><param name="quality" value="high" valuetype="DATA"/></object>', $dom->saveXml($element));
 }
 public function testMarshall()
 {
     $interactionObject = new Object('airplane.jpg', 'image/jpeg');
     $interactionObject->setHeight(16);
     $interactionObject->setWidth(16);
     $interaction = new PositionObjectInteraction('RESPONSE', $interactionObject);
     $interaction->setCenterPoint(new Point(8, 8));
     $stageObject = new Object('country.jpg', 'image/jpeg');
     $positionObjectStage = new PositionObjectStage($stageObject, new PositionObjectInteractionCollection(array($interaction)));
     $element = $this->getMarshallerFactory('2.1.0')->createMarshaller($positionObjectStage)->marshall($positionObjectStage);
     $dom = new DOMDocument('1.0', 'UTF-8');
     $element = $dom->importNode($element, true);
     $this->assertEquals('<positionObjectStage><object data="country.jpg" type="image/jpeg"/><positionObjectInteraction responseIdentifier="RESPONSE" maxChoices="1" centerPoint="8 8"><object data="airplane.jpg" type="image/jpeg" width="16" height="16"/></positionObjectInteraction></positionObjectStage>', $dom->saveXML($element));
 }
 /**
  * @depends testMarshall21
  */
 public function testMarshall20()
 {
     // Make sure minChoices is not taken into account in a QTI 2.0 context.
     $object = new Object('myimg.jpg', 'image/jpeg');
     $object->setWidth(400);
     $object->setHeight(300);
     $positionObjectInteraction = new PositionObjectInteraction('RESPONSE', $object);
     $positionObjectInteraction->setMaxChoices(2);
     $positionObjectInteraction->setMinChoices(1);
     $element = $this->getMarshallerFactory('2.0.0')->createMarshaller($positionObjectInteraction)->marshall($positionObjectInteraction);
     $dom = new DOMDocument('1.0', 'UTF-8');
     $element = $dom->importNode($element, true);
     $this->assertEquals('<positionObjectInteraction responseIdentifier="RESPONSE" maxChoices="2"><object data="myimg.jpg" type="image/jpeg" width="400" height="300"/></positionObjectInteraction>', $dom->saveXML($element));
 }
 public function testMarshall()
 {
     $object = new Object('myimg.jpg', 'image/jpeg');
     $object->setWidth(400);
     $object->setHeight(300);
     $prompt = new Prompt();
     $prompt->setContent(new FlowStaticCollection(array(new TextRun('Prompt...'))));
     $positionObjectInteraction = new PositionObjectInteraction('RESPONSE', $object, 'my-pos');
     $positionObjectInteraction->setCenterPoint(new Point(150, 74));
     $positionObjectInteraction->setMaxChoices(2);
     $positionObjectInteraction->setMinChoices(1);
     $element = $this->getMarshallerFactory()->createMarshaller($positionObjectInteraction)->marshall($positionObjectInteraction);
     $dom = new DOMDocument('1.0', 'UTF-8');
     $element = $dom->importNode($element, true);
     $this->assertEquals('<positionObjectInteraction responseIdentifier="RESPONSE" maxChoices="2" minChoices="1" centerPoint="150 74" id="my-pos"><object data="myimg.jpg" type="image/jpeg" width="400" height="300"/></positionObjectInteraction>', $dom->saveXML($element));
 }
 public function testMatchCorrectValidation()
 {
     $bgObject = new Object('http://img.png', 'image/png');
     $bgObject->setWidth(100);
     $bgObject->setHeight(200);
     $testInteraction = GraphicGapInteractionBuilder::build('testInteraction', $bgObject, ['A' => 'img_A.png', 'B' => 'img_B.png'], ['G1' => [0, 0, 10, 10], 'G2' => [30, 40, 50, 60]]);
     $responseProcessingTemplate = ResponseProcessingTemplate::matchCorrect();
     $validResponseIdentifier = [new QtiDirectedPair('A', 'G1'), new QtiDirectedPair('A', 'G2'), new QtiDirectedPair('B', 'G1')];
     $responseDeclaration = ResponseDeclarationBuilder::buildWithCorrectResponse('testIdentifier', $validResponseIdentifier);
     $mapper = new GraphicGapMatchInteractionMapper($testInteraction, $responseDeclaration, $responseProcessingTemplate);
     /** @var imageclozeassociation $q */
     $q = $mapper->getQuestionType();
     $this->assertEquals('imageclozeassociation', $q->get_type());
     $this->assertEquals(['<img src="img_A.png"/>', '<img src="img_B.png"/>'], $q->get_possible_responses());
     $this->assertTrue($q->get_duplicate_responses());
     $this->assertEquals([['x' => 0, 'y' => 0], ['x' => 30, 'y' => 20]], $q->get_response_positions());
     $img = $q->get_image();
     $this->assertInstanceOf('LearnosityQti\\Entities\\QuestionTypes\\imageclozeassociation_image', $img);
     $this->assertEquals('http://img.png', $img->get_src());
     /** @var imageclozeassociation_validation $validation */
     $validation = $q->get_validation();
     $this->assertInstanceOf('LearnosityQti\\Entities\\QuestionTypes\\imageclozeassociation_validation', $validation);
     $this->assertEquals('exactMatch', $validation->get_scoring_type());
     /** @var imageclozeassociation_validation_valid_response $validResponse */
     $validResponse = $validation->get_valid_response();
     $this->assertInstanceOf('LearnosityQti\\Entities\\QuestionTypes\\imageclozeassociation_validation_valid_response', $validResponse);
     $this->assertEquals(1, $validResponse->get_score());
     $this->assertEquals(['<img src="img_A.png"/>', '<img src="img_A.png"/>'], $validResponse->get_value());
     $altResponses = $validation->get_alt_responses();
     $this->assertCount(1, $altResponses);
     $this->assertEquals(1, $altResponses[0]->get_score());
     $this->assertEquals(['<img src="img_B.png"/>', '<img src="img_A.png"/>'], $altResponses[0]->get_value());
 }
 public function testMarshall()
 {
     $object = new Object('my-video.mp4', 'video/mp4');
     $object->setWidth(400);
     $object->setHeight(300);
     $mediaInteraction = new MediaInteraction('RESPONSE', false, $object, 'my-media');
     $mediaInteraction->setMinPlays(1);
     $mediaInteraction->setMaxPlays(2);
     $mediaInteraction->setLoop(true);
     $prompt = new Prompt();
     $prompt->setContent(new FlowStaticCollection(array(new TextRun('Prompt...'))));
     $mediaInteraction->setPrompt($prompt);
     $element = $this->getMarshallerFactory('2.1.0')->createMarshaller($mediaInteraction)->marshall($mediaInteraction);
     $dom = new DOMDocument('1.0', 'UTF-8');
     $element = $dom->importNode($element, true);
     $this->assertEquals('<mediaInteraction id="my-media" responseIdentifier="RESPONSE" autostart="false" minPlays="1" maxPlays="2" loop="true"><prompt>Prompt...</prompt><object data="my-video.mp4" type="video/mp4" width="400" height="300"/></mediaInteraction>', $dom->saveXML($element));
 }
 private function checkObjectComponents(Object $object, $conversionTo)
 {
     if (!empty($object->getComponents())) {
         LogService::log('Converting <object> element to ' . $conversionTo . '. Any contents within it are removed');
     }
 }
 private function convertTextToObjectWithBase64ImageString($text)
 {
     $string = $text;
     $font = 3;
     $width = ImageFontWidth($font) * strlen($string);
     $height = ImageFontHeight($font);
     ob_start();
     $im = @imagecreate($width, $height);
     $background_color = imagecolorallocate($im, 255, 255, 255);
     // White background
     $text_color = imagecolorallocate($im, 0, 0, 0);
     // Black text
     imagestring($im, $font, 0, 0, $string, $text_color);
     imagepng($im);
     $imagedata = ob_get_contents();
     ob_end_clean();
     $imagedata = 'data:image/png;base64,' . base64_encode($imagedata);
     $gapImageObject = new Object($imagedata, 'image/png');
     $gapImageObject->setWidth($width);
     $gapImageObject->setHeight($height);
     return $gapImageObject;
 }
 private function transformCoordinates(QtiCoords $coords, $shape, Object $imageObject)
 {
     $width = $imageObject->getWidth();
     $height = $imageObject->getHeight();
     $coords = explode(',', $coords);
     switch ($shape) {
         // rect: left-x, top-y, right-x, bottom-y.
         case QtiShape::RECT:
             $leftX = round($coords[0] / $width * 100, 2);
             $topY = round($coords[1] / $height * 100, 2);
             $rightX = round($coords[2] / $width * 100, 2);
             $bottomY = round($coords[3] / $height * 100, 2);
             $result = [['x' => $leftX, 'y' => $topY], ['x' => $rightX, 'y' => $topY], ['x' => $rightX, 'y' => $bottomY], ['x' => $leftX, 'y' => $bottomY]];
             return $result;
         case QtiShape::CIRCLE:
             // circle: center-x, center-y, radius. Note. When the radius value is a percentage value, user agents
             // should calculate the final radius value based on the associated object's width and height. The radius should be the smaller value of the two.
             LogService::log('Unable to map QTI `circle` Shape. Mapping it as `rect` instead');
             $radius = $coords[2];
             $leftX = round(($coords[0] - $radius) / $width * 100, 2);
             $topY = round(($coords[1] + $radius) / $height * 100, 2);
             $rightX = round(($coords[0] + $radius) / $width * 100, 2);
             $bottomY = round(($coords[1] - $radius) / $height * 100, 2);
             $result = [['x' => $leftX, 'y' => $topY], ['x' => $rightX, 'y' => $topY], ['x' => $rightX, 'y' => $bottomY], ['x' => $leftX, 'y' => $bottomY]];
             return $result;
         case QtiShape::POLY:
             // poly: x1, y1, x2, y2, ..., xN, yN. The first x and y coordinate pair and the last should be the same to close the polygon.
             // When these coordinate values are not the same, user agents should infer an additional coordinate pair to close the polygon.
             $result = [];
             for ($i = 0; $i < count($coords); $i += 2) {
                 $result[] = ['x' => round($coords[$i] / $width * 100, 2), 'y' => round($coords[$i + 1] / $height * 100, 2)];
             }
             return $result;
         case QtiShape::ELLIPSE:
         default:
             LogService::log('Unsupported QTI Shape mapping conversion. Area mapping conversion is ignored');
             return [];
     }
 }