Эх сурвалжийг харах

Merge branch '1.10.x' of github.com:chamilo/chamilo-lms into 1.10.x

aragonc 9 жил өмнө
parent
commit
0bdc95ba72

+ 1 - 0
main/exercice/admin.php

@@ -344,6 +344,7 @@ $template = new Template();
 $htmlHeadXtra[] = $template->fetch('default/exercise/submit.js.tpl');
 $htmlHeadXtra[] = api_get_js('d3/jquery.xcolor.js');
 
+$htmlHeadXtra[] = '<link rel="stylesheet" href="../plugin/hotspot2/css/hotspot.css">';
 $htmlHeadXtra[] = '<script src="../plugin/hotspot2/js/hotspot.js"></script>';
 
 $htmlHeadXtra[] = "<script type=\"text/javascript\" src=\"../plugin/hotspot/JavaScriptFlashGateway.js\"></script>

+ 3 - 2
main/exercice/exercise.class.php

@@ -4044,10 +4044,11 @@ class Exercise
 
                                 <script>
                                     $(document).on('ready', function () {
-                                        HotSpotSolution.init({
+                                        HotspotQuestion.init({
                                             questionId: $questionId,
                                             exerciseId: $exeId,
-                                            selector: '#hotspot-solution-$questionId'
+                                            selector: '#hotspot-solution-$questionId',
+                                            for: 'solution'
                                         });
                                     });
                                 </script>

+ 2 - 1
main/exercice/exercise_result.php

@@ -75,7 +75,8 @@ $interbreadcrumb[] = array(
     "name" => get_lang('Exercises'),
 );
 
-$htmlHeadXtra[] = '<script src="' . api_get_path(WEB_CODE_PATH) . 'plugin/hotspot2/js/hotspot_solution.js"></script>';
+$htmlHeadXtra[] = '<script src="' . api_get_path(WEB_CODE_PATH) . 'plugin/hotspot2/js/hotspot.js"></script>';
+$htmlHeadXtra[] = '<link rel="stylesheet" href="' . api_get_path(WEB_CODE_PATH) . 'plugin/hotspot2/css/hotspot.css">';
 
 if ($origin != 'learnpath') {
 	// So we are not in learnpath tool

+ 8 - 5
main/exercice/exercise_show.php

@@ -120,7 +120,8 @@ $interbreadcrumb[]= array("url" => "#","name" => get_lang('Result'));
 
 $this_section = SECTION_COURSES;
 
-$htmlHeadXtra[] = '<script src="' . api_get_path(WEB_CODE_PATH) . 'plugin/hotspot2/js/hotspot_solution.js"></script>';
+$htmlHeadXtra[] = '<link rel="stylesheet" href="' . api_get_path(WEB_CODE_PATH) . 'plugin/hotspot2/css/hotspot.css">';
+$htmlHeadXtra[] = '<script src="' . api_get_path(WEB_CODE_PATH) . 'plugin/hotspot2/js/hotspot.js"></script>';
 
 if ($origin != 'learnpath') {
 	Display::display_header('');
@@ -376,10 +377,11 @@ foreach ($questionList as $questionId) {
                             <div id=\"hotspot-solution\"></div>
                             <script>
                                 $(document).on('ready', function () {
-                                    HotSpotSolution.init({
+                                    HotspotQuestion.init({
                                         questionId: $questionId,
                                         exerciseId: $id,
-                                        selector: '#hotspot-solution'
+                                        selector: '#hotspot-solution',
+                                        for: 'solution'
                                     });
                                 });
                             </script>
@@ -517,10 +519,11 @@ foreach ($questionList as $questionId) {
                             <div id=\"hotspot-solution\"></div>
                             <script>
                                 $(document).on('ready', function () {
-                                    HotSpotSolution.init({
+                                    HotspotQuestion.init({
                                         questionId: $questionId,
                                         exerciseId: $id,
-                                        selector: '#hotspot-solution'
+                                        selector: '#hotspot-solution',
+                                        for: 'solution'
                                     });
                                 });
                             </script>

+ 2 - 1
main/exercice/exercise_submit.php

@@ -60,7 +60,8 @@ $htmlHeadXtra[] = api_get_css(api_get_path(WEB_LIBRARY_PATH).'javascript/epicloc
 $htmlHeadXtra[] = api_get_js('epiclock/javascript/jquery.dateformat.min.js');
 $htmlHeadXtra[] = api_get_js('epiclock/javascript/jquery.epiclock.min.js');
 $htmlHeadXtra[] = api_get_js('epiclock/renderers/minute/epiclock.minute.js');
-$htmlHeadXtra[] = '<script src="../plugin/hotspot2/js/hotspot_user.js"></script>';
+$htmlHeadXtra[] = '<link rel="stylesheet" href="../plugin/hotspot2/css/hotspot.css">';
+$htmlHeadXtra[] = '<script src="../plugin/hotspot2/js/hotspot.js"></script>';
 
 $template = new Template();
 

+ 21 - 241
main/exercice/export/scorm/scorm_classes.php

@@ -569,118 +569,8 @@ class ScormAnswerHotspot extends Answer
 			$header .= file_get_contents('../plugin/hotspot/JavaScriptFlashGateway.js');
 			$header .= '</script>';
 			$header .= '<script type="text/javascript" language="javascript">';
-			$header .= file_get_contents('../plugin/hotspot/hotspot.js');
+			$header .= file_get_contents('../plugin/hotspot2/hotspot.js');
 			$header .= '</script>';
-			$header .= '<script language="javascript" type="text/javascript">'.
-					"<!--
-					// -----------------------------------------------------------------------------
-					// Globals
-					// Major version of Flash required
-					var requiredMajorVersion = 7;
-					// Minor version of Flash required
-					var requiredMinorVersion = 0;
-					// Minor version of Flash required
-					var requiredRevision = 0;
-					// the version of javascript supported
-					var jsVersion = 1.0;
-					// -----------------------------------------------------------------------------
-					// -->
-					</script>
-					<script language=\"VBScript\" type=\"text/vbscript\">
-					<!-- // Visual basic helper required to detect Flash Player ActiveX control version information
-					Function VBGetSwfVer(i)
-					  on error resume next
-					  Dim swControl, swVersion
-					  swVersion = 0
-
-					  set swControl = CreateObject(\"ShockwaveFlash.ShockwaveFlash.\" + CStr(i))
-					  if (IsObject(swControl)) then
-					    swVersion = swControl.GetVariable(\"\$version\")
-					  end if
-					  VBGetSwfVer = swVersion
-					End Function
-					// -->
-					</script>
-
-					<script language=\"JavaScript1.1\" type=\"text/javascript\">
-					<!-- // Detect Client Browser type
-					var isIE  = (navigator.appVersion.indexOf(\"MSIE\") != -1) ? true : false;
-					var isWin = (navigator.appVersion.toLowerCase().indexOf(\"win\") != -1) ? true : false;
-					var isOpera = (navigator.userAgent.indexOf(\"Opera\") != -1) ? true : false;
-					jsVersion = 1.1;
-					// JavaScript helper required to detect Flash Player PlugIn version information
-					function JSGetSwfVer(i){
-						// NS/Opera version >= 3 check for Flash plugin in plugin array
-						if (navigator.plugins != null && navigator.plugins.length > 0) {
-							if (navigator.plugins[\"Shockwave Flash 2.0\"] || navigator.plugins[\"Shockwave Flash\"]) {
-								var swVer2 = navigator.plugins[\"Shockwave Flash 2.0\"] ? \" 2.0\" : \"\";
-					      		var flashDescription = navigator.plugins[\"Shockwave Flash\" + swVer2].description;
-								descArray = flashDescription.split(\" \");
-								tempArrayMajor = descArray[2].split(\".\");
-								versionMajor = tempArrayMajor[0];
-								versionMinor = tempArrayMajor[1];
-								if ( descArray[3] != \"\" ) {
-									tempArrayMinor = descArray[3].split(\"r\");
-								} else {
-									tempArrayMinor = descArray[4].split(\"r\");
-								}
-					      		versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
-					            flashVer = versionMajor + \".\" + versionMinor + \".\" + versionRevision;
-					      	} else {
-								flashVer = -1;
-							}
-						}
-						// MSN/WebTV 2.6 supports Flash 4
-						else if (navigator.userAgent.toLowerCase().indexOf(\"webtv/2.6\") != -1) flashVer = 4;
-						// WebTV 2.5 supports Flash 3
-						else if (navigator.userAgent.toLowerCase().indexOf(\"webtv/2.5\") != -1) flashVer = 3;
-						// older WebTV supports Flash 2
-						else if (navigator.userAgent.toLowerCase().indexOf(\"webtv\") != -1) flashVer = 2;
-						// Can't detect in all other cases
-						else {
-
-							flashVer = -1;
-						}
-						return flashVer;
-					}
-					// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
-					function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
-					{
-					 	reqVer = parseFloat(reqMajorVer + \".\" + reqRevision);
-					   	// loop backwards through the versions until we find the newest version
-						for (i=25;i>0;i--) {
-							if (isIE && isWin && !isOpera) {
-								versionStr = VBGetSwfVer(i);
-							} else {
-								versionStr = JSGetSwfVer(i);
-							}
-							if (versionStr == -1 ) {
-								return false;
-							} else if (versionStr != 0) {
-								if(isIE && isWin && !isOpera) {
-									tempArray         = versionStr.split(\" \");
-									tempString        = tempArray[1];
-									versionArray      = tempString .split(\",\");
-								} else {
-									versionArray      = versionStr.split(\".\");
-								}
-								versionMajor      = versionArray[0];
-								versionMinor      = versionArray[1];
-								versionRevision   = versionArray[2];
-
-								versionString     = versionMajor + \".\" + versionRevision;   // 7.0r24 == 7.24
-								versionNum        = parseFloat(versionString);
-					        	// is the major.revision >= requested major.revision AND the minor version >= requested minor
-								if ( (versionMajor > reqMajorVer) && (versionNum >= reqVer) ) {
-									return true;
-								} else {
-									return ((versionNum >= reqVer && versionMinor >= reqMinorVer) ? true : false );
-								}
-							}
-						}
-					}
-					// -->
-					</script>";
 			//because this header closes so many times the <script> tag, we have to reopen our own
 			$header .= '<script type="text/javascript" language="javascript">'."\n";
 			$header .= 'questions_answers['.$this->questionJSId.'] = new Array();'."\n";
@@ -750,28 +640,25 @@ class ScormAnswerHotspot extends Answer
 		$canClick = true;
 		//$tes = isset($_GET['modifyAnswers']) ? '0' : '1';
 		//echo $tes;
-		$html .= '<tr><td>'."
-					<script language=\"JavaScript\" type=\"text/javascript\">
-						<!--
-						// Version check based upon the values entered above in \"Globals\"
-						var hasReqestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision);
-
-
-						// Check to see if the version meets the requirements for playback
-						if (hasReqestedVersion) {  // if we've detected an acceptable version
-						    var oeTags = '<object type=\"application/x-shockwave-flash\"".' data="'.api_get_path(WEB_CODE_PATH).'plugin/hotspot/hotspot_user.swf?modifyAnswers='.$this->questionJSId."&canClick:".$canClick."\" width=\"380\" height=\"470\">'
-										+ '<param name=\"movie\"".' value="'.api_get_path(WEB_CODE_PATH).'plugin/hotspot/hotspot_user.swf?modifyAnswers='.$this->questionJSId."&canClick:".$canClick."\" \/>'
-										+ '<\/object>';
-						    document.write(oeTags);   // embed the Flash Content SWF when all tests are passed
-						} else {  // flash is too old or we can't detect the plugin
-							var alternateContent = 'Error<br \/>'
-								+ 'Hotspots requires Macromedia Flash 7.<br \/>'
-								+ '<a href=http://www.macromedia.com/go/getflash/>Get Flash<\/a>';
-							document.write(alternateContent);  // insert non-flash content
-						}
-						// -->
-					</script></td>
-					<td valign='top'>$answer_list</td></tr>";
+        $html .= <<<HTML
+            <tr>
+                <td>
+                    <div id="hotspot-{$this->questionJSId}"></div>
+                    <script>
+                        document.addEventListener('DOMContentListener', function () {
+                            HotspotQuestion.init({
+                                questionId: {$this->questionJSId},
+                                selector: '#hotspot-{$this->questionJSId}',
+                                for: 'user'
+                            });
+                        });
+                    </script>
+                </td>
+                <td>
+                    $answer_list
+                </td>
+            <tr>
+HTML;
 		$html .= '</table></td></tr>' . "\n";
 
 		// currently the free answers cannot be displayed, so ignore the textarea
@@ -1114,114 +1001,7 @@ class ScormSection
      */
     function common_js() {
         $js = "\n";
-        $js .= file_get_contents('../plugin/hotspot/JavaScriptFlashGateway.js');
-        $js .= file_get_contents('../plugin/hotspot/hotspot.js');
-        $js .=	"<!--
-					// Globals
-					// Major version of Flash required
-					var requiredMajorVersion = 7;
-					// Minor version of Flash required
-					var requiredMinorVersion = 0;
-					// Minor version of Flash required
-					var requiredRevision = 0;
-					// the version of javascript supported
-					var jsVersion = 1.0;
-					// -->
-					</script>
-					<script language=\"VBScript\" type=\"text/vbscript\">
-					<!-- // Visual basic helper required to detect Flash Player ActiveX control version information
-					Function VBGetSwfVer(i)
-					  on error resume next
-					  Dim swControl, swVersion
-					  swVersion = 0
-
-					  set swControl = CreateObject(\"ShockwaveFlash.ShockwaveFlash.\" + CStr(i))
-					  if (IsObject(swControl)) then
-					    swVersion = swControl.GetVariable(\"\$version\")
-					  end if
-					  VBGetSwfVer = swVersion
-					End Function
-					// -->
-					</script>
-
-					<script language=\"JavaScript1.1\" type=\"text/javascript\">
-					<!-- // Detect Client Browser type
-					var isIE  = (navigator.appVersion.indexOf(\"MSIE\") != -1) ? true : false;
-					var isWin = (navigator.appVersion.toLowerCase().indexOf(\"win\") != -1) ? true : false;
-					var isOpera = (navigator.userAgent.indexOf(\"Opera\") != -1) ? true : false;
-					jsVersion = 1.1;
-					// JavaScript helper required to detect Flash Player PlugIn version information
-					function JSGetSwfVer(i){
-						// NS/Opera version >= 3 check for Flash plugin in plugin array
-						if (navigator.plugins != null && navigator.plugins.length > 0) {
-							if (navigator.plugins[\"Shockwave Flash 2.0\"] || navigator.plugins[\"Shockwave Flash\"]) {
-								var swVer2 = navigator.plugins[\"Shockwave Flash 2.0\"] ? \" 2.0\" : \"\";
-					      		var flashDescription = navigator.plugins[\"Shockwave Flash\" + swVer2].description;
-								descArray = flashDescription.split(\" \");
-								tempArrayMajor = descArray[2].split(\".\");
-								versionMajor = tempArrayMajor[0];
-								versionMinor = tempArrayMajor[1];
-								if ( descArray[3] != \"\" ) {
-									tempArrayMinor = descArray[3].split(\"r\");
-								} else {
-									tempArrayMinor = descArray[4].split(\"r\");
-								}
-					      		versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
-					            flashVer = versionMajor + \".\" + versionMinor + \".\" + versionRevision;
-					      	} else {
-								flashVer = -1;
-							}
-						}
-						// MSN/WebTV 2.6 supports Flash 4
-						else if (navigator.userAgent.toLowerCase().indexOf(\"webtv/2.6\") != -1) flashVer = 4;
-						// WebTV 2.5 supports Flash 3
-						else if (navigator.userAgent.toLowerCase().indexOf(\"webtv/2.5\") != -1) flashVer = 3;
-						// older WebTV supports Flash 2
-						else if (navigator.userAgent.toLowerCase().indexOf(\"webtv\") != -1) flashVer = 2;
-						// Can't detect in all other cases
-						else {
-
-							flashVer = -1;
-						}
-						return flashVer;
-					}
-					// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
-					function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
-					{
-					 	reqVer = parseFloat(reqMajorVer + \".\" + reqRevision);
-					   	// loop backwards through the versions until we find the newest version
-						for (i=25;i>0;i--) {
-							if (isIE && isWin && !isOpera) {
-								versionStr = VBGetSwfVer(i);
-							} else {
-								versionStr = JSGetSwfVer(i);
-							}
-							if (versionStr == -1 ) {
-								return false;
-							} else if (versionStr != 0) {
-								if(isIE && isWin && !isOpera) {
-									tempArray         = versionStr.split(\" \");
-									tempString        = tempArray[1];
-									versionArray      = tempString .split(\",\");
-								} else {
-									versionArray      = versionStr.split(\".\");
-								}
-								versionMajor      = versionArray[0];
-								versionMinor      = versionArray[1];
-								versionRevision   = versionArray[2];
-
-								versionString     = versionMajor + \".\" + versionRevision;   // 7.0r24 == 7.24
-								versionNum        = parseFloat(versionString);
-					        	// is the major.revision >= requested major.revision AND the minor version >= requested minor
-								if ( (versionMajor > reqMajorVer) && (versionNum >= reqVer) ) {
-									return true;
-								} else {
-									return ((versionNum >= reqVer && versionMinor >= reqMinorVer) ? true : false );
-								}
-							}
-						}
-					}
-					// -->\n\n";
+        $js .= file_get_contents('../plugin/hotspot2/hotspot.js');
         $js .= file_get_contents('../newscorm/js/api_wrapper.js');
         $js .= 'var questions = new Array();' . "\n";
         $js .= 'var questions_answers = new Array();' . "\n";

+ 1 - 6
main/exercice/hotspot.class.php

@@ -67,12 +67,7 @@ class HotSpot extends Question
 
 			// fixed width ang height
 			if (file_exists($picturePath.'/'.$this->picture)) {
-				list($width,$height) = @getimagesize($picturePath.'/'.$this->picture);
-				if ($width > $height) {
-					$this->resizePicture('width', 545);
-				} else {
-					$this->resizePicture('height', 350);
-				}
+				$this->resizePicture('width', 800);
 				$this->save();
 			} else {
 				return false;

+ 1 - 0
main/exercice/hotspot_actionscript.as.php

@@ -37,6 +37,7 @@ if ($answer_type==HOT_SPOT_DELINEATION) {
 $result = Database::query($sql);
 
 $data = [];
+$data['type'] = 'user';
 $data['lang'] = [
     'Square' => get_lang('Square'),
     'Circle' => get_lang('Circle'),

+ 22 - 4
main/exercice/hotspot_actionscript_admin.as.php

@@ -22,10 +22,27 @@ $pictureWidth = $pictureSize[0];
 $pictureHeight = $pictureSize[1];
 
 $data = [];
-$data['hotspot_lang'] = $_course['language'];
-$data['hotspot_image'] = $pictureName;
-$data['hotspot_image_width'] = $pictureWidth;
-$data['hotspot_image_height'] = $pictureHeight;
+$data['type'] = 'admin';
+$data['lang'] = [
+    'Square' => get_lang('Square'),
+    'Circle' => get_lang('Circle'),
+    'Polygon' => get_lang('Polygon'),
+    'HotspotStatus1' => get_lang('HotspotStatus1'),
+    'HotspotStatus2Polygon' => get_lang('HotspotStatus2Polygon'),
+    'HotspotStatus2Other' => get_lang('HotspotStatus2Other'),
+    'HotspotStatus3' => get_lang('HotspotStatus3'),
+    'HotspotShowUserPoints' => get_lang('HotspotShowUserPoints'),
+    'ShowHotspots' => get_lang('ShowHotspots'),
+    'Triesleft' => get_lang('Triesleft'),
+    'HotspotExerciseFinished' => get_lang('HotspotExerciseFinished'),
+    'NextAnswer' => get_lang('NextAnswer'),
+    'Delineation' => get_lang('Delineation'),
+    'CloseDelineation' => get_lang('CloseDelineation'),
+    'Oar' => get_lang('oar')
+];
+$data['image'] = $objQuestion->selectPicturePath();
+$data['image_width'] = $pictureWidth;
+$data['image_height'] = $pictureHeight;
 $data['courseCode'] = $_course['path'];
 $data['hotspots'] = [];
 
@@ -39,6 +56,7 @@ $nbrAnswers = count($answers['answer']);
 
 for ($i=1;$i <= $nbrAnswers; $i++) {
     $hotSpot = [];
+    $hotSpot['id'] = null;
     $hotSpot['answer']= $answers['answer'][$i];
 
     if ($answer_type == HOT_SPOT_DELINEATION) {

+ 5 - 7
main/exercice/hotspot_admin.inc.php

@@ -1008,9 +1008,6 @@ if ($modifyAnswers) {
                         </tbody>
                     </table>
         </div>
-        <div class="row">
-            <div class="col-xs-12" id="hotspot-selectors"></div>
-        </div>
         <div class="row">
             <div class="col-xs-12">
     <?php
@@ -1018,16 +1015,17 @@ if ($modifyAnswers) {
     $height = 450;
     ?>
                 <div id="hotspot-container" class="center-block">
-                    <div id="hotspot-alert"></div>
-                    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 0 0">
-                    </svg>
                 </div>
             </div>
         </div>
     </form>
     <script>
         $(document).on('ready', function () {
-            HotSpotAdmin.init(<?php echo $modifyAnswers ?>, '<?php echo $objQuestion->selectPicturePath(); ?>');
+            HotspotQuestion.init({
+                questionId: <?php echo $modifyAnswers ?>,
+                selector: '#hotspot-container',
+                for: 'admin'
+            });
         });
     </script>
     <?php

+ 3 - 0
main/exercice/hotspot_answers.as.php

@@ -37,6 +37,8 @@ if ($answer_type == HOT_SPOT_DELINEATION) {
 }
 $result = Database::query($sql);
 // Init
+$data = [];
+$data['type'] = 'solution';
 $data['lang'] = [
     'Square' => get_lang('Square'),
     'Circle' => get_lang('Circle'),
@@ -63,6 +65,7 @@ $data['hotspots'] = [];
 while ($hotspot = Database::fetch_array($result)) {
     $hotSpot = [];
     $hotSpot['id'] = $hotspot['id'];
+    $hotSpot['answer'] = $hotspot['answer'];
 
 	// Square or rectancle
 	if ($hotspot['hotspot_type'] == 'square' ) {

+ 2 - 1
main/exercice/result.php

@@ -53,7 +53,8 @@ if (!$is_allowedToEdit) {
     }
 }
 
-$htmlHeadXtra[] = '<script src="' . api_get_path(WEB_CODE_PATH) . 'plugin/hotspot2/js/hotspot_solution.js"></script>';
+$htmlHeadXtra[] = '<link rel="stylesheet" href="' . api_get_path(WEB_CODE_PATH) . 'plugin/hotspot2/css/hotspot.css">';
+$htmlHeadXtra[] = '<script src="' . api_get_path(WEB_CODE_PATH) . 'plugin/hotspot2/js/hotspot.js"></script>';
 
 if ($show_headers) {
     $interbreadcrumb[] = array(

+ 3 - 2
main/inc/lib/exercise.lib.php

@@ -1177,9 +1177,10 @@ HOTSPOT;
                                 <div class="hotspot-image"></div>
                                 <script>
                                     $(document).on('ready', function () {
-                                        HotSpotUser.init({
+                                        HotspotQuestion.init({
                                             questionId: $questionId,
-                                            selector: '#question_div_' + $questionId + ' .hotspot-image'
+                                            selector: '#question_div_' + $questionId + ' .hotspot-image',
+                                            for: 'user'
                                         });
                                     });
                                 </script>

+ 184 - 10
main/plugin/hotspot2/css/hotspot.css

@@ -1,15 +1,189 @@
 #hotspot-container {
-    line-height: 0;
-    /*position: relative;
-    width: 100%;
-    vertical-align: middle;
-    padding-bottom: 408px;
-    margin: 0;
-    overflow: hidden;*/
+    position: relative;
 }
 #hotspot-container svg {
-    /*display: inline-block;
+    
+}
+#hotspot-container #hotspot-context-menu {
     position: absolute;
-    top: 0;
-    left: 0;*/
+}
+
+.input-group.hotspot-0 .fa-square {
+   color: rgb(66, 113, 181);
+}
+.input-group.hotspot-1 .fa-square {
+   color: rgb(254, 142, 22);
+}
+.input-group.hotspot-2 .fa-square {
+   color: rgb(69, 199, 240);
+}
+.input-group.hotspot-3 .fa-square {
+   color: rgb(188, 214, 49);
+}
+.input-group.hotspot-4 .fa-square {
+   color: rgb(214, 49, 115);
+}
+.input-group.hotspot-5 .fa-square {
+   color: rgb(215, 215, 215);
+}
+.input-group.hotspot-6 .fa-square {
+   color: rgb(144, 175, 221);
+}
+.input-group.hotspot-7 .fa-square {
+   color: rgb(175, 134, 64);
+}
+.input-group.hotspot-8 .fa-square {
+   color: rgb(79, 146, 66);
+}
+.input-group.hotspot-9 .fa-square {
+   color: rgb(244, 235, 36);
+}
+.input-group.hotspot-10 .fa-square {
+   color: rgb(237, 32, 36);
+}
+.input-group.hotspot-11 .fa-square {
+   color: rgb(59, 59, 59);
+}
+
+.input-group.hotspot-0.active {
+    border-color: rgb(66, 113, 181);
+    box-shadow: 1px 1px 10px rgb(66, 113, 181);
+    border-radius: 4px;
+}
+.input-group.hotspot-0.active .input-group-addon {
+    border-color: rgb(66, 113, 181);
+}
+.input-group.hotspot-0.active select {
+    border-color: rgb(66, 113, 181);
+    border-left-color: #CCC;
+}
+.input-group.hotspot-1.active {
+    border-color: rgb(254, 142, 22);
+    box-shadow: 1px 1px 10px rgb(254, 142, 22);
+    border-radius: 4px;
+}
+.input-group.hotspot-1.active .input-group-addon {
+    border-color: rgb(254, 142, 22);
+}
+.input-group.hotspot-1.active select {
+    border-color: rgb(254, 142, 22);
+    border-left-color: #CCC;
+}
+.input-group.hotspot-2.active {
+    border-color: rgb(69, 199, 240);
+    box-shadow: 1px 1px 10px rgb(69, 199, 240);
+    border-radius: 4px;
+}
+.input-group.hotspot-2.active .input-group-addon {
+    border-color: rgb(69, 199, 240);
+}
+.input-group.hotspot-2.active select {
+    border-color: rgb(69, 199, 240);
+    border-left-color: #CCC;
+}
+.input-group.hotspot-3.active {
+    border-color: rgb(188, 214, 49);
+    box-shadow: 1px 1px 10px rgb(188, 214, 49);
+    border-radius: 4px;
+}
+.input-group.hotspot-3.active .input-group-addon {
+    border-color: rgb(188, 214, 49);
+}
+.input-group.hotspot-3.active select {
+    border-color: rgb(188, 214, 49);
+    border-left-color: #CCC;
+}
+
+.input-group.hotspot-0 select:focus,
+.input-group.hotspot-1 select:focus,
+.input-group.hotspot-2 select:focus,
+.input-group.hotspot-3 select:focus,
+.input-group.hotspot-4 select:focus,
+.input-group.hotspot-5 select:focus,
+.input-group.hotspot-6 select:focus,
+.input-group.hotspot-7 select:focus,
+.input-group.hotspot-8 select:focus,
+.input-group.hotspot-9 select:focus,
+.input-group.hotspot-10 select:focus,
+.input-group.hotspot-11 select:focus {
+    box-shadow: none;
+}
+.input-group.hotspot-0 select,
+.input-group.hotspot-1 select,
+.input-group.hotspot-2 select,
+.input-group.hotspot-3 select,
+.input-group.hotspot-4 select,
+.input-group.hotspot-5 select,
+.input-group.hotspot-6 select,
+.input-group.hotspot-7 select,
+.input-group.hotspot-8 select,
+.input-group.hotspot-9 select,
+.input-group.hotspot-10 select,
+.input-group.hotspot-11 select {
+    transition: none;
+    border-color: #CCC;
+}
+
+svg .hotspot-0,
+svg .hotspot-1,
+svg .hotspot-2,
+svg .hotspot-3,
+svg .hotspot-4,
+svg .hotspot-5,
+svg .hotspot-6,
+svg .hotspot-7,
+svg .hotspot-8,
+svg .hotspot-9,
+svg .hotspot-10,
+svg .hotspot-11 {
+    stroke-width: 2;
+}
+
+svg .hotspot-0 {
+    stroke: rgb(66, 113, 181);
+    fill: rgba(66, 113, 181, 0.75);
+}
+svg .hotspot-1 {
+    stroke: rgb(254, 142, 22);
+    fill: rgba(254, 142, 22, 0.75);
+}
+svg .hotspot-2 {
+    stroke: rgb(69, 199, 240);
+    fill: rgba(69, 199, 240, 0.75);
+}
+svg .hotspot-3 {
+    stroke: rgb(188, 214, 49);
+    fill: rgba(188, 214, 49, 0.75);
+}
+svg .hotspot-4 {
+    stroke: rgb(214, 49, 115);
+    fill: rgba(214, 49, 115, 0.75);
+}
+svg .hotspot-5 {
+    stroke: rgb(215, 215, 21);
+    fill: rgba(215, 215, 21, 0.75);
+}
+svg .hotspot-6 {
+    stroke: rgb(144, 175, 221);
+    fill: rgba(144, 175, 221, 0.75);
+}
+svg .hotspot-7 {
+    stroke: rgb(175, 134, 64);
+    fill: rgba(175, 134, 64, 0.75);
+}
+svg .hotspot-8 {
+    stroke: rgb(79, 146, 66);
+    fill: rgba(79, 146, 66, 0.75);
+}
+svg .hotspot-9 {
+    stroke: rgb(244, 235, 36);
+    fill: rgba(244, 235, 36, 0.75);
+}
+svg .hotspot-10 {
+    stroke: rgb(237, 32, 36);
+    fill: rgba(237, 32, 36, 0.75);
+}
+svg .hotspot-11 {
+    stroke: rgb(59, 59, 59);
+    fill: rgba(59, 59, 59, 0.75);
 }

+ 951 - 372
main/plugin/hotspot2/js/hotspot.js

@@ -1,505 +1,1084 @@
-var HotSpotAdmin = (function () {
-    var HotSpotSquare = function () {
-        this.x = 0;
-        this.y = 0;
-        this.width = 0;
-        this.height = 0;
+var HotspotQuestion = (function () {
+    var HotspotModel = function (attributes) {
+        this.attributes = attributes;
+        this.id = 0;
+        this.name = '';
+
+        this.changeEvent = null;
+    };
+    HotspotModel.prototype.set = function (key, value) {
+        this.attributes[key] = value;
+
+        if (this.changeEvent) {
+            this.changeEvent(this);
+        }
+    };
+    HotspotModel.prototype.get = function (key) {
+        return this.attributes[key];
     };
-    HotSpotSquare.prototype.setStartPoint = function (x, y) {
-        this.x = parseInt(x);
-        this.y = parseInt(y);
+    HotspotModel.prototype.onChange = function (callback) {
+        this.changeEvent = callback;
+    };
+    HotspotModel.prototype.checkPoint = function (x, y) {
+        return false;
+    };
+    HotspotModel.decode = function () {
+        return new this;
+    };
+    HotspotModel.prototype.encode = function () {
+        return '';
     };
-    HotSpotSquare.prototype.setEndPoint = function (x, y) {
-        var x2, y2;
 
+    var SquareModel = function (attributes) {
+        HotspotModel.call(this, attributes);
+    };
+    SquareModel.prototype = Object.create(HotspotModel.prototype);
+    SquareModel.prototype.setStartPoint = function (x, y) {
         x = parseInt(x);
         y = parseInt(y);
 
-        if (x < this.x) {
-            x2 = this.x;
-            this.x = x;
-        } else {
-            x2 = x;
+        this.set('x', x);
+        this.set('y', y);
+    };
+    SquareModel.prototype.setEndPoint = function (x, y) {
+        var startX = this.get('x'),
+            startY = this.get('y');
+
+        x = parseInt(x);
+        y = parseInt(y);
+
+        if (x >= startX) {
+            this.set('width', x - startX);
         }
 
-        if (y < this.y) {
-            y2 = this.y;
-            this.y = y;
-        } else {
-            y2 = y;
+        if (y >= startY) {
+            this.set('height', y - startY);
         }
+    };
+    SquareModel.prototype.checkPoint = function (x, y) {
+        var left = this.get('x'),
+            right = this.get('x') + this.get('width'),
+            top = this.get('y'),
+            bottom = this.get('y') + this.get('height');
 
-        this.width = Math.round(x2 - this.x);
-        this.height = Math.round(y2 - this.y);
+        var xIsValid = x >= left && x <= right,
+            yIsValid = y >= top && y <= bottom;
+
+        return xIsValid && yIsValid;
     };
-    HotSpotSquare.prototype.encode = function () {
-        var encodedPosition = [this.x, this.y].join(';');
+    SquareModel.decode = function (hotspotInfo) {
+        var coords = hotspotInfo.coord.split('|'),
+            position = coords[0].split(';'),
+            hotspot = new SquareModel({
+                x: parseInt(position[0]),
+                y: parseInt(position[1]),
+                width: parseInt(coords[1]),
+                height: parseInt(coords[2])
+            });
 
+        hotspot.id = hotspotInfo.id;
+        hotspot.name = hotspotInfo.answer;
+
+        return hotspot;
+    };
+    SquareModel.prototype.encode = function () {
         return [
-            encodedPosition,
-            this.width,
-            this.height
+            [
+                this.get('x'),
+                this.get('y')
+            ].join(';'),
+            this.get('width'),
+            this.get('height')
         ].join('|');
-    }
+    };
 
-    var HotSpotEllipse = function () {
-        this.centerX = 0;
-        this.centerY = 0;
-        this.radiusX = 0;
-        this.radiusY = 0;
+    var EllipseModel = function (attributes) {
+        HotspotModel.call(this, attributes);
     };
-    HotSpotEllipse.prototype.setStartPoint = function (x, y) {
-        this.centerX = parseInt(x);
-        this.centerY = parseInt(y);
+    EllipseModel.prototype = Object.create(HotspotModel.prototype);
+    EllipseModel.prototype.setStartPoint = function (x, y) {
+        x = parseInt(x);
+        y = parseInt(y);
+
+        this.set('centerX', x);
+        this.set('centerY', y);
     };
-    HotSpotEllipse.prototype.setEndPoint = function (x, y) {
-        var startX = this.centerX,
-            startY = this.centerY,
-            endX = 0,
-            endY = 0;
+    EllipseModel.prototype.setEndPoint = function (x, y) {
+        var startX = this.get('centerX'),
+            startY = this.get('centerY'),
+            width = 0,
+            height = 0;
 
         x = parseInt(x);
         y = parseInt(y);
 
-        if (x < startX) {
-            endX = startX;
-            startX = x;
-        } else {
-            endX = x;
+        if (x >= startX) {
+            width = x - startX;
+
+            this.set('radiusX', Math.round(width / 2));
+            this.set('centerX', startX + this.get('radiusX'));
         }
 
-        if (y < startY) {
-            endY = startY;
-            startY = y;
-        } else {
-            endY = y;
+        if (y >= startY) {
+            height = y - startY;
+
+            this.set('radiusY', Math.round(height / 2));
+            this.set('centerY', startY + this.get('radiusY'));
         }
+    };
+    EllipseModel.prototype.checkPoint = function (x, y) {
+        var dX = x - this.get('centerX'),
+            dY = y - this.get('centerY');
 
-        var width = Math.round(endX - startX);
-        var height = Math.round(endY - startY);
+        var dividend = Math.pow(dX, 2) / Math.pow(this.get('radiusX'), 2),
+            divider = Math.pow(dY, 2) / Math.pow(this.get('radiusY'), 2);
 
-        this.radiusX = Math.round(width / 2);
-        this.radiusY = Math.round(height / 2);
-        this.centerX = startX + this.radiusX;
-        this.centerY = startY + this.radiusY;
+        return dividend + divider <= 1;
     };
-    HotSpotEllipse.prototype.encode = function () {
-        var encodedCenter = [this.centerX, this.centerY].join(';');
+    EllipseModel.decode = function (hotspotInfo) {
+        var coords = hotspotInfo.coord.split('|'),
+            center = coords[0].split(';'),
+            hotspot = new EllipseModel({
+                centerX: parseInt(center[0]),
+                centerY: parseInt(center[1]),
+                radiusX: parseInt(coords[1]),
+                radiusY: parseInt(coords[2])
+            });
+
+        hotspot.id = hotspotInfo.id;
+        hotspot.name = hotspotInfo.answer;
 
+        return hotspot;
+    };
+    EllipseModel.prototype.encode = function () {
         return [
-            encodedCenter,
-            this.radiusX,
-            this.radiusY
+            [
+                this.get('centerX'),
+                this.get('centerY')
+            ].join(';'),
+            this.get('radiusX'),
+            this.get('radiusY')
         ].join('|');
     };
 
-    var HotSpotPolygon = function () {
-        this.points = [];
-    };
-    HotSpotPolygon.prototype.addPoint = function (x, y) {
-        this.points.push([
-            parseInt(x),
-            parseInt(y)
-        ]);
+    var PolygonModel = function (attributes) {
+        HotspotModel.call(this, attributes);
     };
-    HotSpotPolygon.prototype.encode = function () {
-        var encodedPoints = [];
+    PolygonModel.prototype = Object.create(HotspotModel.prototype);
+    PolygonModel.prototype.addPoint = function (x, y) {
+        var points = this.get('points');
 
-        this.points.forEach(function (point) {
-            encodedPoints.push(point.join(';'));
-        });
+        x = parseInt(x);
+        y = parseInt(y);
 
-        return encodedPoints.join('|');
-    };
+        points.push([x, y]);
 
-    var HotSpotEl = function (hotspot, color) {
-        this.hotspot = hotspot;
-        this.element = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
-        this.elStroke = 'rgb(' + color + ')';
-        this.elFill = 'rgba(' + color + ', 0.5)';
-    };
-    HotSpotEl.prototype.render = function () {
-        return this;
+        this.set('points', points);
     };
-    HotSpotEl.prototype.remove = function () {
-        if (!this.element) {
-            return;
+    PolygonModel.prototype.checkPoint = function (x, y) {
+        var points = this.get('points'),
+            isInside = false;
+
+        for (var i = 0, j = points.length - 1; i < points.length; j = i++) {
+            var xi = points[i][0],
+                yi = points[i][1],
+                xj = points[j][0],
+                yj = points[j][1];
+
+            var intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
+
+            if (intersect) {
+                isInside = !isInside;
+            }
         }
 
-        this.element.parentNode.removeChild(this.element);
-        this.hotspot = null;
+        return isInside;
     };
+    PolygonModel.decode = function (hotspotInfo) {
+        var pairedPoints = hotspotInfo.coord.split('|'),
+            points = [];
 
-    var SquareEl = function (hotspot, color) {
-        HotSpotEl.call(this, hotspot, color);
+        $.each(pairedPoints, function (index, pair) {
+            var point = pair.split(';');
+
+            points.push([
+                parseInt(point[0]),
+                parseInt(point[1])
+            ]);
+        });
 
-        this.element = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
-        this.element.setAttribute('stroke-width', 2);
-        this.element.setAttribute('stroke', this.elStroke);
-        this.element.setAttribute('fill', this.elFill);
+        var hotspot = new PolygonModel({
+            points: points
+        });
+        hotspot.id = hotspotInfo.id;
+        hotspot.name = hotspotInfo.answer;
+
+        return hotspot;
     };
-    SquareEl.prototype = Object.create(HotSpotEl.prototype);
-    SquareEl.prototype.render = function () {
-        this.element.setAttribute('x', this.hotspot.x);
-        this.element.setAttribute('y', this.hotspot.y);
-        this.element.setAttribute('width', this.hotspot.width);
-        this.element.setAttribute('height', this.hotspot.height);
+    PolygonModel.prototype.encode = function () {
+        var pairedPoints = [];
+
+        this.get('points').forEach(function (point) {
+            pairedPoints.push(
+                point.join(';')
+            );
+        });
 
-        return this.element;
+        return pairedPoints.join('|');
     };
 
-    var EllipseEl = function (hotspot, color) {
-        HotSpotEl.call(this, hotspot, color);
+    var AnswerModel = function (attributes) {
+        this.attributes = attributes;
+        this.changeEvent = null;
+    };
+    AnswerModel.prototype.set = function (key, value) {
+        this.attributes[key] = value;
 
-        this.element = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
-        this.element.setAttribute('stroke-width', 2);
-        this.element.setAttribute('stroke', this.elStroke);
-        this.element.setAttribute('fill', this.elFill);
+        if (this.changeEvent) {
+            this.changeEvent(this);
+        }
+    };
+    AnswerModel.prototype.get = function (key) {
+        return this.attributes[key];
     };
-    EllipseEl.prototype = Object.create(HotSpotEl.prototype);
-    EllipseEl.prototype.render = function () {
-        this.element.setAttribute('cx', this.hotspot.centerX);
-        this.element.setAttribute('cy', this.hotspot.centerY);
-        this.element.setAttribute('rx', this.hotspot.radiusX);
-        this.element.setAttribute('ry', this.hotspot.radiusY);
+    AnswerModel.prototype.onChange = function (callback) {
+        this.changeEvent = callback;
+    };
+    AnswerModel.decode = function (answerInfo) {
+        var coords = answerInfo.split(';');
 
-        return this.element;
+        return new AnswerModel({
+            x: coords[0],
+            y: coords[1]
+        });
     };
 
-    var PolygonEl = function (hotspot, color) {
-        HotSpotEl.call(this, hotspot, color);
+    var AnswersCollection = function () {
+        this.models = [];
+        this.length = 0;
+        this.addEvent = null;
+    };
+    AnswersCollection.prototype.add = function (answerModel) {
+        this.models.push(answerModel);
+        this.length++;
 
-        this.element = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
-        this.element.setAttribute('stroke-width', 2);
-        this.element.setAttribute('stroke', this.elStroke);
-        this.element.setAttribute('fill', this.elFill);
+        if (this.addEvent) {
+            this.addEvent(answerModel);
+        }
+    };
+    AnswersCollection.prototype.onAdd = function (callback) {
+        this.addEvent = callback; 
     };
-    PolygonEl.prototype = Object.create(HotSpotEl.prototype);
-    PolygonEl.prototype.render = function () {
-        var pointsPaired = [];
 
-        this.hotspot.points.forEach(function (point) {
-            pointsPaired.push(point.join(','));
-        });
+    var HotspotsCollection = function () {
+        this.hotspots = [];
+        this.length = 0;
 
-        this.element.setAttribute(
-            'points',
-            pointsPaired.join(' ')
-        );
+        this.addEvent = null;
+    };
+    HotspotsCollection.prototype.add = function (hotspot) {
+        this.hotspots.push(hotspot);
+        this.length++;
 
-        return this.element;
+        if (this.addEvent) {
+            this.addEvent(hotspot);
+        }
+    };
+    HotspotsCollection.prototype.get = function (index) {
+        return this.hotspots[index];
+    };
+    HotspotsCollection.prototype.set = function (index, newHotspot) {
+        this.hotspots[index] = newHotspot;
     };
+    HotspotsCollection.prototype.onAdd = function (callback) {
+        this.addEvent = callback;
+    };
+
+    var HotspotSVG = function (modelModel, index) {
+        var self = this;
+
+        this.model = modelModel;
+        this.hotspotIndex = index;
 
-    var HotSpotSelectorEl = function (color, index, selectedValue) {
-        this.hotSpotIndex = parseInt(index);
-        this.elStroke = 'rgb(' + color + ')';
-        this.elFill = 'rgba(' + color + ', 0.5)';
+        this.el = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
 
-        switch (selectedValue) {
-            case 'square':
-            default:
-                this.selectedValue = 'square';
-                break;
+        this.model.onChange(function (hotspotModel) {
+            self.render();
+        });
+    };
+    HotspotSVG.prototype.render = function () {
+        var newEl = null;
+
+        if (this.model instanceof SquareModel) {
+            newEl = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
+            newEl.setAttribute('x', this.model.get('x'));
+            newEl.setAttribute('y', this.model.get('y'));
+            newEl.setAttribute('width', this.model.get('width'));
+            newEl.setAttribute('height', this.model.get('height'));
+        } else if (this.model instanceof EllipseModel) {
+            newEl = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
+            newEl.setAttribute('cx', this.model.get('centerX'));
+            newEl.setAttribute('cy', this.model.get('centerY'));
+            newEl.setAttribute('rx', this.model.get('radiusX'));
+            newEl.setAttribute('ry', this.model.get('radiusY'));
+        } else if (this.model instanceof PolygonModel) {
+            var pointsPaired = [];
+
+            this.model.get('points').forEach(function (point) {
+                pointsPaired.push(point.join(','));
+            });
+
+            newEl = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
+            newEl.setAttribute(
+                'points',
+                pointsPaired.join(' ')
+            );
+        }
 
-            case 'circle':
-                this.selectedValue = 'ellipse';
-                break;
+        newEl.setAttribute('class', 'hotspot-' + this.hotspotIndex);
 
-            case 'poly':
-                this.selectedValue = 'polygon';
-                break;
+        if (this.el.parentNode) {
+            this.el.parentNode.replaceChild(newEl, this.el);
         }
+
+        this.el = newEl;
+
+        return this;
     };
-    HotSpotSelectorEl.prototype.render = function () {
-        var self = this;
+
+    var HotspotSelect = function (index, hotspotsCollection, hotspotSVG) {
+        this.hotspotIndex = index;
+        this.hotspotsCollection = hotspotsCollection;
+        this.hotspotSVG = hotspotSVG;
 
         this.el = document.createElement('div');
-        this.el.className = 'col-xs-6 col-sm-3 col-md-2';
-        this.el.innerHTML = '\n\
-            <div class="input-group">\n\
-                <span class="input-group-addon" id="hotspot-' + this.hotSpotIndex + '">\n\
-                    <span class="fa fa-square fa-fw" data-hidden="true" style="color: ' + this.elStroke + '"></span>\n\
+        this.el.className = 'col-xs-6 col-sm-4 col-md-3 col-lg-2';
+
+        selectedHotspotIndex = this.hotspotIndex;
+
+        $('.input-group').removeClass('active');
+    };
+    HotspotSelect.prototype.render = function () {
+        var self = this,
+            $el = $(this.el);
+
+        var template = '\n\
+            <div class="input-group hotspot-'  + this.hotspotIndex + ' active">\n\
+                <span class="input-group-addon" id="hotspot-' + this.hotspotIndex + '">\n\
+                    <span class="fa fa-square fa-fw" data-hidden="true"></span>\n\
+                    <span class="sr-only">' + (this.hotspotSVG.model.get('name') ? this.hotspotSVG.model.get('name') : 'hotspot ' + this.hotspotIndex) + '</span>\n\
                 </span>\n\
-                <select class="form-control" aria-describedby="hotspot-' + this.hotSpotIndex + '">\n\
-                    <option value="">Select</option>\n\
-                    <option value="square">Square</option>\n\
-                    <option value="ellipse">Ellipse</option>\n\
-                    <option value="polygon">Polygon</option>\n\
+                <select class="form-control" aria-describedby="hotspot-' + this.hotspotIndex + '">\n\
+                    <option value="square">' + lang.Square + '</option>\n\
+                    <option value="ellipse">' + lang.Circle + '</option>\n\
+                    <option value="polygon">' + lang.Polygon + '</option>\n\
                 </select>\n\
             </div>\n\
         ';
+        $el.html(template);
+
+        $el.find('select')
+            .on('change', function () {
+                selectedHotspotIndex = self.hotspotIndex;
+
+                var newHotspot = null,
+                    changeEvent = self.hotspotSVG.model.changeEvent;
+
+                switch (this.value) {
+                    case 'square':
+                        newHotspot = new SquareModel({
+                            x: 0,
+                            y: 0,
+                            width: 0,
+                            height: 0
+                        });
+                        break;
+
+                    case 'ellipse':
+                        newHotspot = new EllipseModel({
+                            centerX: 0,
+                            centerY: 0,
+                            radiusX: 0,
+                            radiusY: 0
+                        });
+                        break;
+
+                    case 'polygon':
+                        newHotspot = new PolygonModel({
+                            points: []
+                        });
+                        break;
+                }
 
-        $(this.el).find('select').val(this.selectedValue);
-
-        var selectShapeEvent = function (e) {
-            switch (this.value) {
-                case 'square':
-                    //no break
-                case 'ellipse':
-                    //no break
-                case 'polygon':
-                    currentShapeType = this.value;
-                    currentHotSpotIndex = self.hotSpotIndex;
-                    break;
+                newHotspot.onChange(changeEvent);
 
-                default:
-                    break;
-            }
-        };
+                self.hotspotsCollection.set(self.hotspotIndex, newHotspot);
+                self.hotspotSVG.model = newHotspot;
+            })
+            .on('focus', function () {
+                $('.input-group').removeClass('active');
 
-        $(this.el).on('click', selectShapeEvent);
-        $(this.el).find('select').on('change', selectShapeEvent);
-
-        return this.el;
-    };
-
-    var colors = [
-        '66, 113, 181',
-        '254, 142, 22',
-        '69, 199, 240',
-        '188, 214, 49',
-        '214, 49, 115',
-        '215, 215, 215',
-        '144, 175, 221',
-        '174, 134, 64',
-        '79, 146, 66',
-        '244, 235, 36',
-        '237, 32, 36',
-        '59, 59, 59',
-        '247, 189, 226'
-    ];
-
-    var getPointOnImage = function (x, y) {
-        var pointerPosition = {
-            left: x + window.scrollX,
-            top: y + window.scrollY
-        },
-            canvasOffset = {
-                x: canvas.getBoundingClientRect().x + window.scrollX,
-                y: canvas.getBoundingClientRect().y + window.scrollY
-            };
+                $el.find('.input-group').addClass('active');
 
-        return {
-            x: Math.round(pointerPosition.left - canvasOffset.x),
-            y: Math.round(pointerPosition.top - canvasOffset.y)
-        };
+                selectedHotspotIndex = self.hotspotIndex;
+            })
+            .val(function () {
+                if (self.hotspotSVG.model instanceof SquareModel) {
+                    return 'square';
+                }
+
+                if (self.hotspotSVG.model instanceof EllipseModel) {
+                    return 'ellipse';
+                }
+
+                if (self.hotspotSVG.model instanceof PolygonModel) {
+                    return 'polygon';
+                }
+            });
+
+        return this;
     };
 
-    var startCanvas = function () {
-        var newHotSpotEl = null,
-            pressingShift = false;
+    var ContextMenu = function () {
+        this.el = document.createElement('ul');
 
-        document.addEventListener('keydown', function (e) {
-            if (e.keyCode === 16) {
-                pressingShift = true;
-            }
-        }, false);
-        document.addEventListener('keyup', function (e) {
-            if (e.keyCode === 16) {
-                pressingShift = false;
-            }
-        }, false);
+        $(this.el).addClass('dropdown-menu').attr('id', "hotspot-context-menu");
 
-        canvas.addEventListener('click', function (e) {
+        this.hideEvent = null;
+    };
+    ContextMenu.prototype.onHide = function (callback) {
+        this.hideEvent = callback;
+    };
+    ContextMenu.prototype.render = function () {
+        var self = this,
+            template = '\n\
+                <li>\n\
+                    <a href="#">' + 'ClosePolygon' + '</a>\n\
+                </li>\n\
+            ';
+
+        $(this.el).html(template);
+        
+        $(this.el).find('a').on('click', function (e) {
             e.preventDefault();
-        }, false);
 
-        container.addEventListener('dragstart', function (e) {
-            e.preventDefault();
-        }, false);
-        container.addEventListener('click', function (e) {
-            if (shapes.length >= colors.length) {
-                return;
+            if (self.hideEvent) {
+                self.hideEvent(e);
             }
 
-            newHotSpotEl = draw(newHotSpotEl, e.clientX, e.clientY, pressingShift);
+            $(self.el).hide();
+        });
 
-            if (!newHotSpotEl) {
-                updateValues();
-            }
-        }, false);
+        return this;
     };
+    ContextMenu.prototype.show = function (x, y) {
+        $(this.el).css({left: x, top: y}).show();
+    };
+
+    var AdminHotspotsSVG = function (hotspotsCollection, image) {
+        var self = this;
+
+        this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+        this.collection = hotspotsCollection;
+        this.image = image;
+
+        this.collection.onAdd(function (hotspotModel) {
+            self.renderHotspot(hotspotModel);
+        });
+    };
+    AdminHotspotsSVG.prototype.render = function () {
+        this.el.setAttribute('version', '1.1');
+        this.el.setAttribute('viewBox', '0 0 ' + this.image.width + ' ' + this.image.height);
+
+        var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+        imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.image.src);
+        imageSvg.setAttribute('width', this.image.width);
+        imageSvg.setAttribute('height', this.image.height);
+
+        this.el.appendChild(imageSvg);
+
+        this.setEvents();
+
+        return this;
+    };
+    AdminHotspotsSVG.prototype.renderHotspot = function (hotspot) {
+        var hotspotIndex = this.collection.length - 1,
+            hotspotSVG = new HotspotSVG(hotspot, hotspotIndex);
+
+        this.el.appendChild(
+            hotspotSVG.render().el
+        );
+
+        var hotspotSelect = new HotspotSelect(hotspotIndex, this.collection, hotspotSVG);
+
+        $(config.selector).parent().find('.row').append(
+            hotspotSelect.render().el
+        );
+    };
+    AdminHotspotsSVG.prototype.setEvents = function () {
+        var self = this,
+            $el = $(this.el);
+            isDrawing = false;
+
+        var startPoint = {
+                x: 0,
+                y: 0
+            };
+
+        $el.on('dragstart', function (e) {
+                e.preventDefault();
+            })
+            .on('mousedown', function (e) {
+                e.preventDefault();
+
+                if (e.button > 0) {
+                    return;
+                }
+
+                if (self.collection.length <= 0) {
+                    return;
+                }
+
+                var currentHotspot = self.collection.get(selectedHotspotIndex);
+
+                if (!currentHotspot) {
+                    return;
+                }
+
+                startPoint = getPointOnImage(self.el, e.clientX, e.clientY);
+
+                if (currentHotspot instanceof SquareModel) {
+                    isDrawing = true;
+
+                    currentHotspot.set('x', startPoint.x);
+                    currentHotspot.set('y', startPoint.y);
+                    currentHotspot.set('width', 0);
+                    currentHotspot.set('height', 0);
+
+                    return;
+                }
+
+                if (currentHotspot instanceof EllipseModel) {
+                    isDrawing = true;
+
+                    currentHotspot.set('centerX', 0);
+                    currentHotspot.set('centerY', 0);
+                    currentHotspot.set('radiusX', 0);
+                    currentHotspot.set('radiusY', 0);
+                    return;
+                }
+            })
+            .on('mousemove', function (e) {
+                e.preventDefault();
+
+                if (self.collection.length <= 0) {
+                    return;
+                }
+
+                if (!isDrawing) {
+                    return;
+                }
 
-    var draw = function (hotSpotEl, x, y, isPressingShift) {
-        var pointerPosition = getPointOnImage(x, y),
-            hotSpot = null;
+                var currentHotspot = self.collection.get(selectedHotspotIndex),
+                    currentPoint = getPointOnImage(self.el, e.clientX, e.clientY);
 
-        if (!hotSpotEl) {
-            switch (currentShapeType) {
-                case 'square':
-                    //no break
-                case 'ellipse':
-                    if (currentShapeType === 'ellipse') {
-                        hotSpot = new HotSpotEllipse();
-                        hotSpotEl = new EllipseEl(hotSpot, colors[currentHotSpotIndex]);
+                if (!currentHotspot) {
+                    return;
+                }
+
+                if (currentHotspot instanceof SquareModel) {
+                    if (startPoint.x < currentPoint.x) {
+                        currentHotspot.set('width', currentPoint.x - startPoint.x);
                     } else {
-                        hotSpot = new HotSpotSquare();
-                        hotSpotEl = new SquareEl(hotSpot, colors[currentHotSpotIndex]);
+                        currentHotspot.set('x', currentPoint.x);
+                        currentHotspot.set('width', startPoint.x - currentPoint.x);
                     }
 
-                    hotSpot.setStartPoint(pointerPosition.x, pointerPosition.y);
-                    break;
+                    if (startPoint.y < currentPoint.y) {
+                        currentHotspot.set('height', currentPoint.y - startPoint.y);
+                    } else {
+                        currentHotspot.set('y', currentPoint.y);
+                        currentHotspot.set('height', startPoint.y - currentPoint.y);
+                    }
 
-                case 'polygon':
-                    hotSpot = new HotSpotPolygon();
-                    hotSpotEl = new PolygonEl(hotSpot, colors[currentHotSpotIndex]);
+                    return;
+                }
 
-                    hotSpot.addPoint(pointerPosition.x, pointerPosition.y);
-                    break;
-            }
+                if (currentHotspot instanceof EllipseModel) {
+                    var width = 0,
+                        height = 0;
 
-            shapes[currentHotSpotIndex].remove();
-            shapes.splice(currentHotSpotIndex, 1, hotSpotEl);
+                    if (startPoint.x < currentPoint.x) {
+                        width = currentPoint.x - startPoint.x;
 
-            canvas.appendChild(hotSpotEl.render());
+                        currentHotspot.set('radiusX', Math.round(width / 2));
+                        currentHotspot.set('centerX', startPoint.x + currentHotspot.get('radiusX'));
+                    } else {
+                        width = startPoint.x - currentPoint.x;
 
-            return hotSpotEl;
-        }
+                        currentHotspot.set('radiusX', Math.round(width / 2));
+                        currentHotspot.set('centerX', currentPoint.x + currentHotspot.get('radiusX'))
+                    }
 
-        switch (currentShapeType) {
-            case 'square':
-                //no break
-            case 'ellipse':
-                hotSpotEl.hotspot.setEndPoint(pointerPosition.x, pointerPosition.y);
-                hotSpotEl.render();
+                    if (startPoint.y < currentPoint.y) {
+                        height = currentPoint.y - startPoint.y;
 
-                hotSpotEl = null;
-                break;
+                        currentHotspot.set('radiusY', Math.round(height / 2));
+                        currentHotspot.set('centerY', startPoint.y + currentHotspot.get('radiusY'));
+                    } else {
+                        height = startPoint.y - currentPoint.y;
 
-            case 'polygon':
-                $(container).find('#hotspot-alert').text('Keed pressed the SHIFT key and click the image to close the polygon');
+                        currentHotspot.set('radiusY', Math.round(height / 2));
+                        currentHotspot.set('centerY', currentPoint.y + currentHotspot.get('radiusY'));
+                    }
 
-                hotSpotEl.hotspot.addPoint(pointerPosition.x, pointerPosition.y);
-                hotSpotEl.render();
+                    return;
+                }
+            })
+            .on('mouseup', function (e) {
+                e.preventDefault();
 
-                if (isPressingShift) {
-                    hotSpotEl = null;
-                    $(container).find('#hotspot-alert').text('');
+                if (e.button > 0) {
+                    return;
                 }
-                break;
-        }
 
-        return hotSpotEl;
+                if (self.collection.length <= 0) {
+                    return;
+                }
+
+                if (!isDrawing) {
+                    return;
+                }
+
+                var currentHotspot = self.collection.get(selectedHotspotIndex),
+                    hotspotTypeSelector = '[name="hotspot_type[' + (selectedHotspotIndex + 1) + ']"]',
+                    hotspotCoordSelector = '[name="hotspot_coordinates[' + (selectedHotspotIndex + 1) + ']"]';
+
+                if (!currentHotspot) {
+                    return;
+                }
+
+                if (currentHotspot instanceof SquareModel) {
+                    $(hotspotTypeSelector).val('square');
+                    $(hotspotCoordSelector).val(currentHotspot.encode());
+
+                    isDrawing = false;
+                } else if (currentHotspot instanceof EllipseModel) {
+                    $(hotspotTypeSelector).val('circle');
+                    $(hotspotCoordSelector).val(currentHotspot.encode());
+
+                    isDrawing = false;
+                }
+            })
+            .on('click', function (e) {
+                e.preventDefault();
+
+                var currentHotspot = self.collection.get(selectedHotspotIndex),
+                    currentPoint = getPointOnImage(self.el, e.clientX, e.clientY);
+
+                if (!currentHotspot) {
+                    return;
+                }
+
+                if (currentHotspot instanceof PolygonModel) {
+                    var points = [];
+
+                    if (!isDrawing) {
+                        isDrawing = true;
+                    } else {
+                        points = currentHotspot.get('points');
+                    }
+
+                    points.push([currentPoint.x, currentPoint.y]);
+
+                    currentHotspot.set('points', points);
+
+                    return;
+                }
+            })
+            .on('contextmenu', function (e) {
+                e.preventDefault();
+
+                var currentPoint = getPointOnImage(self.el, e.clientX, e.clientY),
+                    currentHotspot = self.collection.get(selectedHotspotIndex),
+                    hotspotTypeSelector = '[name="hotspot_type[' + (selectedHotspotIndex + 1) + ']"]',
+                    hotspotCoordSelector = '[name="hotspot_coordinates[' + (selectedHotspotIndex + 1) + ']"]';
+
+                if (!currentHotspot) {
+                    return;
+                }
+
+                if (currentHotspot instanceof PolygonModel) {
+                    contextMenu.show(currentPoint.x, currentPoint.y);
+                    contextMenu.onHide(function () {
+                        $(hotspotTypeSelector).val('poly');
+                        $(hotspotCoordSelector).val(currentHotspot.encode());
+
+                        isDrawing = false;
+                    });
+                }
+            });
     };
 
-    var updateValues = function () {
-        var currentHotSpotEl = shapes[currentHotSpotIndex];
+    var startHotspotsAdmin = function (questionInfo) {
+        var image = new Image();
+        image.onload = function () {
+            var hotspotsCollection = new HotspotsCollection(),
+                hotspotsSVG = new AdminHotspotsSVG(hotspotsCollection, this);
 
-        if (currentHotSpotIndex === undefined) {
-            return;
-        }
+            $(config.selector).css('width', this.width).append(hotspotsSVG.render().el);
 
-        if (currentHotSpotEl.hotspot instanceof HotSpotSquare) {
-            $('[name="hotspot_type[' + (currentHotSpotIndex + 1) + ']"]').val('square');
-        } else if (currentHotSpotEl.hotspot instanceof HotSpotEllipse) {
-            $('[name="hotspot_type[' + (currentHotSpotIndex + 1) + ']"]').val('circle');
-        } else if (currentHotSpotEl.hotspot instanceof HotSpotPolygon) {
-            $('[name="hotspot_type[' + (currentHotSpotIndex + 1) + ']"]').val('poly');
-        }
+            $(config.selector).parent().prepend('<div class="row"></div>');
 
-        $('[name="hotspot_coordinates[' + (currentHotSpotIndex + 1) + ']"]').val(
-            currentHotSpotEl.hotspot.encode()
-        );
+            contextMenu = new ContextMenu();
+
+            $(config.selector).append(
+                contextMenu.render().el
+            );
+
+            $.each(questionInfo.hotspots, function (index, hotspotInfo) {
+                var hotspot = null;
+
+                switch (hotspotInfo.type) {
+                    case 'square':
+                    default:
+                        hotspot = SquareModel.decode(hotspotInfo);
+                        break;
+
+                    case 'circle':
+                        hotspot = EllipseModel.decode(hotspotInfo);
+                        break;
+
+                    case 'poly':
+                        hotspot = PolygonModel.decode(hotspotInfo);
+                        break;
+                }
+
+                hotspotsCollection.add(hotspot);
+            });
+        };
+        image.src = questionInfo.image;
+
+        lang = questionInfo.lang;
     };
 
-    var loadHotSpots = function (hotSpotList) {
-        hotSpotList.forEach(function (hotSpotData, index) {
-            var hotSpot = null,
-                hotSpotEl = null,
-                color = colors[shapes.length];
+    var UserHotspotsSVG = function (hotspotsCollection, image) {
+        var self = this;
 
-            switch (hotSpotData.type) {
-                case 'square':
-                    hotSpot = new HotSpotSquare();
-                    hotSpotEl = new SquareEl(hotSpot, color);
+        this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+        this.hotspotsCollection = hotspotsCollection;
+        this.answersCollection = new AnswersCollection();
+        this.image = image;
 
-                    var coords = hotSpotData.coord.split('|'),
-                        position = coords[0].split(';'),
-                        x = parseInt(position[0]),
-                        y = parseInt(position[1]),
-                        width = parseInt(coords[1]),
-                        height = parseInt(coords[2]);
+        this.answersCollection.onAdd(function (answerModel) {
+            self.renderAnswer(answerModel);
+        });
+    };
+    UserHotspotsSVG.prototype.render = function () {
+        this.el.setAttribute('version', '1.1');
+        this.el.setAttribute('viewBox', '0 0 ' + this.image.width + ' ' + this.image.height);
 
-                    hotSpot.setStartPoint(x, y);
-                    hotSpot.setEndPoint(x + width, y + height);
-                    break;
-                case 'circle':
-                    hotSpot = new HotSpotEllipse();
-                    hotSpotEl = new EllipseEl(hotSpot, color);
-
-                    var coords = hotSpotData.coord.split('|'),
-                        position = coords[0].split(';'),
-                        x = parseInt(position[0] - coords[1]),
-                        y = parseInt(position[1] - coords[2]),
-                        width = parseInt(coords[1]) * 2,
-                        height = parseInt(coords[2]) * 2;
-
-                    hotSpot.setStartPoint(x, y);
-                    hotSpot.setEndPoint(x + width, y + height);
-                    break;
+        var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+        imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.image.src);
+        imageSvg.setAttribute('width', this.image.width);
+        imageSvg.setAttribute('height', this.image.height);
 
-                case 'poly':
-                    hotSpot = new HotSpotPolygon();
-                    hotSpotEl = new PolygonEl(hotSpot, color);
+        this.el.appendChild(imageSvg);
 
-                    hotSpotData.coord.split('|').forEach(function (point) {
-                        var exis = point.split(';');
+        this.setEvents();
 
-                        hotSpot.addPoint(exis[0], exis[1]);
-                    });
-                    break;
+        return this;
+    };
+    UserHotspotsSVG.prototype.renderAnswer = function (answerModel) {
+        var pointSVG = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
+        pointSVG.setAttribute('cx', answerModel.get('x'));
+        pointSVG.setAttribute('cy', answerModel.get('y'));
+        pointSVG.setAttribute('r', 15);
+        pointSVG.setAttribute('fill', '#00677C');
+
+        var textSVG = document.createElementNS('http://www.w3.org/2000/svg', 'text');
+        textSVG.setAttribute('x', answerModel.get('x'));
+        textSVG.setAttribute('y', answerModel.get('y'));
+        textSVG.setAttribute('dy', 5);
+        textSVG.setAttribute('font-family', 'sans-serif');
+        textSVG.setAttribute('text-anchor', 'middle');
+        textSVG.setAttribute('fill', 'white');
+        textSVG.textContent = this.answersCollection.length;
+
+        this.el.appendChild(pointSVG);
+        this.el.appendChild(textSVG);
+
+        var hotspot = this.hotspotsCollection.get(this.answersCollection.length - 1),
+            x = answerModel.get('x'),
+            y = answerModel.get('y');
+
+        $('<input>', {
+            type: 'hidden',
+            name: 'hotspot[' + config.questionId + '][' + hotspot.id + ']'
+        }).val(function () {
+            return [x, y].join(';');
+        }).appendTo(this.el.parentNode);
+
+        $('<input>', {
+            type: 'hidden',
+            name: 'choice[' + config.questionId + '][' + hotspot.id + ']'
+        }).val(function () {
+            if (hotspot.checkPoint(x, y)) {
+                return 1;
             }
 
-            if (hotSpotEl) {
-                var hotSpotSelector = new HotSpotSelectorEl(color, index, hotSpotData.type);
+            return 0;
+        }).appendTo(this.el.parentNode);
+    };
+    UserHotspotsSVG.prototype.setEvents = function () {
+        var self = this,
+            $el = $(this.el);
+
+        $el
+            .on('dragstart', function (e) {
+                e.preventDefault();
+            })
+            .on('click', function (e) {
+                e.preventDefault();
+
+                if (self.answersCollection.length >= self.hotspotsCollection.length) {
+                    return;
+                }
 
-                selectors.appendChild(hotSpotSelector.render());
-                canvas.appendChild(hotSpotEl.render());
-                shapes.push(hotSpotEl);
-            }
+                var point = getPointOnImage(self.el, e.clientX, e.clientY);
+
+                var answerModel = new AnswerModel({
+                    x: point.x,
+                    y: point.y
+                });
+
+                self.answersCollection.add(answerModel);
+
+                if (self.answersCollection.length === self.hotspotsCollection.length) {
+                    $(config.selector).parent()
+                        .find('#hotspot-messages-' + config.questionId).text(
+                            lang.HotspotExerciseFinished
+                        );
+
+                    return;
+               }
+
+               $(config.selector).parent()
+                    .find('#hotspot-messages-' + config.questionId).text(
+                        lang.NextAnswer + ' ' + self.hotspotsCollection.get(
+                            self.answersCollection.length
+                        ).name
+                    );
+            });
+    };
+
+    var startHotspotsUser = function (questionInfo) {
+        var image = new Image();
+        image.onload = function () {
+            var hotspotsCollection = new HotspotsCollection(),
+                hotspotsSVG = new UserHotspotsSVG(hotspotsCollection, this);
+
+            $(config.selector).css('width', this.width).append(hotspotsSVG.render().el);
+
+            $(config.selector).parent().prepend('<div id="hotspot-messages-' + config.questionId + '"></div>');
+
+            $.each(questionInfo.hotspots, function (index, hotspotInfo) {
+                var hotspot = null;
+
+                switch (hotspotInfo.type) {
+                    case 'square':
+                    default:
+                        hotspot = SquareModel.decode(hotspotInfo);
+                        break;
+
+                    case 'circle':
+                        hotspot = EllipseModel.decode(hotspotInfo);
+                        break;
+
+                    case 'poly':
+                        hotspot = PolygonModel.decode(hotspotInfo);
+                        break;
+                }
+
+                hotspotsCollection.add(hotspot);
+            });
+
+            $(config.selector).parent().find('#hotspot-messages-' + config.questionId)
+                .text(
+                    lang.NextAnswer + ' ' + hotspotsCollection.get(0).name
+                );
+        };
+        image.src = questionInfo.image;
+
+        lang = questionInfo.lang;
+    };
+
+    var SolutionHotspotsSVG = function (hotspotsCollection, answersCollection, image) {
+        this.hotspotsCollection = hotspotsCollection;
+        this.answersCollection = answersCollection;
+        this.image = image;
+        this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+
+        var self = this;
+
+        this.hotspotsCollection.onAdd(function (hotspotModel) {
+            self.renderHotspot(hotspotModel);
+        });
+
+        this.answersCollection.onAdd(function (answerModel) {
+            self.renderAnswer(answerModel);
         });
     };
+    SolutionHotspotsSVG.prototype.render = function () {
+        this.el.setAttribute('version', '1.1');
+        this.el.setAttribute('viewBox', '0 0 ' + this.image.width + ' ' + this.image.height);
+
+        var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+        imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.image.src);
+        imageSvg.setAttribute('width', this.image.width);
+        imageSvg.setAttribute('height', this.image.height);
+
+        this.el.appendChild(imageSvg);
+
+        return this;
+    };
+    SolutionHotspotsSVG.prototype.renderHotspot = function (hotspotModel) {
+        var hotspotIndex = this.hotspotsCollection.length,
+            hotspotSVG = new HotspotSVG(hotspotModel, hotspotIndex);
+
+        this.el.appendChild(
+            hotspotSVG.render().el
+        );
+
+        return this;
+    };
+    SolutionHotspotsSVG.prototype.renderAnswer = function (answerModel) {
+        var pointSVG = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
+        pointSVG.setAttribute('cx', answerModel.get('x'));
+        pointSVG.setAttribute('cy', answerModel.get('y'));
+        pointSVG.setAttribute('r', 15);
+        pointSVG.setAttribute('fill', '#00677C');
+
+        var textSVG = document.createElementNS('http://www.w3.org/2000/svg', 'text');
+        textSVG.setAttribute('x', answerModel.get('x'));
+        textSVG.setAttribute('y', answerModel.get('y'));
+        textSVG.setAttribute('dy', 5);
+        textSVG.setAttribute('font-family', 'sans-serif');
+        textSVG.setAttribute('text-anchor', 'middle');
+        textSVG.setAttribute('fill', 'white');
+        textSVG.textContent = this.answersCollection.length;
+
+        this.el.appendChild(pointSVG);
+        this.el.appendChild(textSVG);
+
+        return this;
+    };
 
-    var container, canvas, selectors, currentShapeType, currentHotSpotIndex, shapes = [];
+    var startHotspotsSolution = function (questionInfo) {
+        var image = new Image();
+        image.onload = function () {
+            var hotspotsCollection = new HotspotsCollection(),
+                answersCollection = new AnswersCollection(),
+                hotspotsSVG = new SolutionHotspotsSVG(hotspotsCollection, answersCollection, this);
+
+            $(config.selector).css('width', this.width).append(hotspotsSVG.render().el);
+
+            $.each(questionInfo.hotspots, function (index, hotspotInfo) {
+                var hotspot = null;
+
+                switch (hotspotInfo.type) {
+                    case 'square':
+                    default:
+                        hotspot = SquareModel.decode(hotspotInfo);
+                        break;
+
+                    case 'circle':
+                        hotspot = EllipseModel.decode(hotspotInfo);
+                        break;
+
+                    case 'poly':
+                        hotspot = PolygonModel.decode(hotspotInfo);
+                        break;
+                }
+
+                hotspotsCollection.add(hotspot);
+            });
+
+            $.each(questionInfo.answers, function (index, answerInfo) {
+                var answer = AnswerModel.decode(answerInfo);
+
+                answersCollection.add(answer);
+            });
+        };
+        image.src = questionInfo.image;
+
+        lang = questionInfo.lang;
+    };
+
+    var getPointOnImage = function (referenceElement, x, y) {
+        var pointerPosition = {
+                left: x + window.scrollX,
+                top: y + window.scrollY
+            },
+            canvasOffset = {
+                x: referenceElement.getBoundingClientRect().left + window.scrollX,
+                y: referenceElement.getBoundingClientRect().top + window.scrollY
+            };
+
+        return {
+            x: Math.round(pointerPosition.left - canvasOffset.x),
+            y: Math.round(pointerPosition.top - canvasOffset.y)
+        };
+    };
+
+    var config, lang, selectedHotspotIndex = 0, contextMenu;
 
     return {
-        init: function (questionId, imageSrc) {
-            if (!questionId || !imageSrc) {
+        init: function (settings) {
+            config = $.extend({
+                questionId: 0,
+                selector: ''
+            }, settings);
+
+            if (!config.questionId || !config.selector) {
                 return;
             }
 
-            selectors = document.querySelector('#hotspot-selectors');
-            container = document.querySelector('#hotspot-container');
-            canvas = document.querySelector('#hotspot-container svg');
-            currentShapeType = 'square';
+            var xhrQuestion = null;
 
-            var xhrImage = new $.Deferred();
+            switch (config.for) {
+                case 'admin':
+                    xhrQuestion = $.getJSON('/main/exercice/hotspot_actionscript_admin.as.php', {
+                        modifyAnswers: parseInt(config.questionId)
+                    });
+                    break;
 
-            var image = new Image();
-            image.onload = function () {
-                xhrImage.resolve(this);
-            };
-            image.onerror = function () {
-                xhrImage.reject();
-            };
-            image.src = imageSrc;
+                case 'user':
+                    xhrQuestion = $.getJSON('/main/exercice/hotspot_actionscript.as.php', {
+                        modifyAnswers: parseInt(config.questionId)
+                    });
+                    break;
 
-            var xhrHotSpots = $.get('/main/exercice/hotspot_actionscript_admin.as.php', {
-                modifyAnswers: parseInt(questionId)
-            });
+                case 'solution':
+                    xhrQuestion = $.getJSON('/main/exercice/hotspot_answers.as.php', {
+                        modifyAnswers: parseInt(config.questionId),
+                        exe_id: parseInt(config.exerciseId)
+                    });
+            }
 
-            $.when.apply($, [xhrImage, xhrHotSpots]).done(function (imageRequest, hotSpotsRequest) {
-                var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
-                imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', imageRequest.src);
-                imageSvg.setAttribute('width', imageRequest.width);
-                imageSvg.setAttribute('height', imageRequest.height);
+            $.when(xhrQuestion).done(function (questionInfo) {
+                switch (questionInfo.type) {
+                    case 'admin':
+                        startHotspotsAdmin(questionInfo);
+                        break;
 
-                container.style.width = imageRequest.width + 'px';
-                canvas.setAttribute('viewBox', '0 0 ' + imageRequest.width + ' ' + imageRequest.height);
-                canvas.appendChild(imageSvg);
+                    case 'user':
+                        startHotspotsUser(questionInfo);
+                        break;
 
-                loadHotSpots(hotSpotsRequest[0].hotspots);
-                startCanvas();
+                    case 'solution':
+                        startHotspotsSolution(questionInfo);
+                }
             });
         }
     };

+ 0 - 231
main/plugin/hotspot2/js/hotspot_solution.js

@@ -1,231 +0,0 @@
-var HotSpotSolution = (function () {
-    var Answer = function () {
-        this.x = 0;
-        this.y = 0;
-    };
-
-    var HotSpot = function () {
-        this.id = 0;
-        this.name = '';
-    };
-
-    var Square = function () {
-        HotSpot.call(this);
-
-        this.x = 0,
-        this.y = 0,
-        this.width = 0,
-        this.height = 0;
-    };
-    Square.prototype = Object.create(HotSpot.prototype);
-
-    var Ellipse = function () {
-        HotSpot.call(this);
-
-        this.centerX = 0;
-        this.centerY = 0;
-        this.radiusX = 0;
-        this.radiusY = 0;
-    };
-    Ellipse.prototype = Object.create(HotSpot.prototype);
-
-    var Polygon = function () {
-        HotSpot.call(this);
-
-        this.points = [];
-    };
-    Polygon.prototype = Object.create(HotSpot.prototype);
-
-    var config, lang, hotSpots = [], answers = [];
-
-    var CanvasSVG = function (image) {
-        var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
-        imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', image.src);
-        imageSvg.setAttribute('width', image.width);
-        imageSvg.setAttribute('height', image.height);
-
-        this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
-        this.el.setAttribute('version', '1.1');
-        this.el.setAttribute('viewBox', '0 0 ' + image.width + ' ' + image.height);
-        this.el.appendChild(imageSvg);
-    };
-    CanvasSVG.prototype.addHotSpot = function (hotSpot) {
-        var hotSpotSVG = null;
-
-        if (hotSpot instanceof Square) {
-            hotSpotSVG = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
-            hotSpotSVG.setAttribute('x', hotSpot.x);
-            hotSpotSVG.setAttribute('y', hotSpot.y);
-            hotSpotSVG.setAttribute('width', hotSpot.width);
-            hotSpotSVG.setAttribute('height', hotSpot.height);
-        } else if (hotSpot instanceof Ellipse) {
-            hotSpotSVG = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
-            hotSpotSVG.setAttribute('cx', hotSpot.centerX);
-            hotSpotSVG.setAttribute('cy', hotSpot.centerY);
-            hotSpotSVG.setAttribute('rx', hotSpot.radiusX);
-            hotSpotSVG.setAttribute('ry', hotSpot.radiusY);
-        } else if (hotSpot instanceof Polygon) {
-            var pointsPaired = [];
-
-            hotSpot.points.forEach(function (point) {
-                pointsPaired.push(point.join(','));
-            });
-
-            hotSpotSVG = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
-            hotSpotSVG.setAttribute(
-                'points',
-                pointsPaired.join(' ')
-            );
-        }
-
-        if (!hotSpotSVG) {
-            return;
-        }
-
-        var color = colors[hotSpots.length - 1];
-
-        hotSpotSVG.setAttribute('stroke-width', 2);
-        hotSpotSVG.setAttribute('stroke', 'rgb(' + color + ')');
-        hotSpotSVG.setAttribute('fill', 'rgba(' + color + ', 0.75)');
-
-        this.el.appendChild(hotSpotSVG);
-    };
-    CanvasSVG.prototype.addAnswer = function (answer) {
-        var pointSVG = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
-        pointSVG.setAttribute('cx', answer.x);
-        pointSVG.setAttribute('cy', answer.y);
-        pointSVG.setAttribute('r', 15);
-        pointSVG.setAttribute('fill', '#00677C');
-
-        var textSVG = document.createElementNS('http://www.w3.org/2000/svg', 'text');
-        textSVG.setAttribute('x', answer.x);
-        textSVG.setAttribute('y', answer.y);
-        textSVG.setAttribute('dy', 5);
-        textSVG.setAttribute('font-family', 'sans-serif');
-        textSVG.setAttribute('text-anchor', 'middle');
-        textSVG.setAttribute('fill', 'white');
-        textSVG.textContent = answers.length;
-
-        this.el.appendChild(pointSVG);
-        this.el.appendChild(textSVG);
-    };
-
-    var decodeHotSpot = function (hotSpotInfo) {
-        var hotSpot = null,
-            coords = hotSpotInfo.coord.split('|');
-
-        switch (hotSpotInfo.type) {
-            case 'square':
-                var position = coords[0].split(';');
-
-                hotSpot = new Square();
-                hotSpot.x = parseInt(position[0]);
-                hotSpot.y = parseInt(position[1]);
-                hotSpot.width = parseInt(coords[1]);
-                hotSpot.height = parseInt(coords[2]);
-                break;
-            case 'circle':
-                var center = coords[0].split(';');
-
-                hotSpot = new Ellipse();
-                hotSpot.centerX = parseInt(center[0]);
-                hotSpot.centerY = parseInt(center[1]);
-                hotSpot.radiusX = parseInt(coords[1]);
-                hotSpot.radiusY = parseInt(coords[2]);
-                break;
-            case 'poly':
-                hotSpot = new Polygon();
-
-                coords.forEach(function (pairedCoord) {
-                    var coord = pairedCoord.split(';');
-
-                    hotSpot.points.push([
-                        parseInt(coord[0]),
-                        parseInt(coord[1])
-                    ]);
-                });
-                break;
-        }
-
-        return hotSpot;
-    };
-
-    var decodeAnswer = function (answerInfo) {
-        var answer = null,
-            coords = answerInfo.split(';');
-
-        answer = new Answer();
-        answer.x = coords[0];
-        answer.y = coords[1];
-
-        return answer;
-    };
-
-    var colors = [
-        '66, 113, 181',
-        '254, 142, 22',
-        '69, 199, 240',
-        '188, 214, 49',
-        '214, 49, 115',
-        '215, 215, 215',
-        '144, 175, 221',
-        '174, 134, 64',
-        '79, 146, 66',
-        '244, 235, 36',
-        '237, 32, 36',
-        '59, 59, 59'
-    ];
-
-    var startAnswer = function (hotSpotAnswerInfo) {
-        var image = new Image();
-        image.onload = function () {
-            var canvasSVG = new CanvasSVG(this);
-
-            hotSpotAnswerInfo.hotspots.forEach(function (hotSpotInfo) {
-                var hotSpot = decodeHotSpot(hotSpotInfo);
-
-                if (!hotSpot) {
-                    return;
-                }
-
-                hotSpots.push(hotSpot);
-                canvasSVG.addHotSpot(hotSpot);
-            });
-
-            hotSpotAnswerInfo.answers.forEach(function (answerInfo) {
-               var answer = decodeAnswer(answerInfo);
-
-               answers.push(answer);
-               canvasSVG.addAnswer(answer);
-            });
-
-            $(config.selector)
-                .css('width', this.width)
-                .append(canvasSVG.el);
-        };
-        image.src = hotSpotAnswerInfo.image;
-
-        lang = hotSpotAnswerInfo.lang;
-    };
-
-    return {
-        init: function (settings) {
-            config = $.extend({
-                questionId: 0,
-                exerciseId: 0,
-                selector: ''
-            }, settings);
-
-            if (!config.questionId || !config.selector) {
-                return;
-            }
-
-            var xhrHotSpotAnswers = $.getJSON('/main/exercice/hotspot_answers.as.php', {
-                modifyAnswers: parseInt(config.questionId),
-                exe_id: parseInt(config.exerciseId)
-            });
-
-            $.when(xhrHotSpotAnswers).done(startAnswer);
-        }
-    };
-})();

+ 0 - 270
main/plugin/hotspot2/js/hotspot_user.js

@@ -1,270 +0,0 @@
-var HotSpotUser = (function () {
-    var Answer = function () {
-        this.x = 0;
-        this.y = 0;
-    };
-
-    var HotSpot = function () {
-        this.id = 0;
-        this.name = '';
-    };
-    HotSpot.prototype.checkPoint = function (x, y) {
-        return false;
-    };
-
-    var Square = function () {
-        HotSpot.call(this);
-
-        this.x = 0,
-        this.y = 0,
-        this.width = 0,
-        this.height = 0;
-    };
-    Square.prototype = Object.create(HotSpot.prototype);
-    Square.prototype.checkPoint = function (x, y) {
-        var left = this.x,
-            right = this.x + this.width,
-            top = this.y,
-            bottom = this.y + this.height;
-
-        var xIsValid = x >= left && x <= right,
-            yIsValid = y >= top && y <= bottom;
-
-        return xIsValid && yIsValid;
-    };
-
-    var Ellipse = function () {
-        HotSpot.call(this);
-
-        this.centerX = 0;
-        this.centerY = 0;
-        this.radiusX = 0;
-        this.radiusY = 0;
-    };
-    Ellipse.prototype = Object.create(HotSpot.prototype);
-    Ellipse.prototype.checkPoint = function (x, y) {
-        var dX = x - this.centerX,
-            dY = y - this.centerY;
-
-        return Math.pow(dX, 2) / Math.pow(this.radiusX, 2) + Math.pow(dY, 2) / Math.pow(this.radiusY, 2) <= 1;
-    };
-
-    var Polygon = function () {
-        HotSpot.call(this);
-
-        this.points = [];
-    };
-    Polygon.prototype = Object.create(HotSpot.prototype);
-    Polygon.prototype.checkPoint = function (x, y) {
-        var isInside = false;
-
-        for (var i = 0, j = this.points.length - 1; i < this.points.length; j = i++) {
-            var xi = this.points[i][0],
-                yi = this.points[i][1],
-                xj = this.points[j][0],
-                yj = this.points[j][1];
-
-            var intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
-
-            if (intersect) {
-                isInside = !isInside;
-            }
-        }
-
-        return isInside;
-    };
-
-    var CanvasSVG = function (image) {
-        var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
-        imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', image.src);
-        imageSvg.setAttribute('width', image.width);
-        imageSvg.setAttribute('height', image.height);
-
-        this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
-        this.el.setAttribute('version', '1.1');
-        this.el.setAttribute('viewBox', '0 0 ' + image.width + ' ' + image.height);
-        this.el.appendChild(imageSvg);
-
-        this.messagesEl = document.createElement('div');
-    };
-    CanvasSVG.prototype.setEvents = function () {
-        var self = this;
-
-        var getPointOnImage = function (x, y) {
-            var pointerPosition = {
-                    left: x + window.scrollX,
-                    top: y + window.scrollY
-                },
-                canvasOffset = {
-                    x: self.el.getBoundingClientRect().x + window.scrollX,
-                    y: self.el.getBoundingClientRect().y + window.scrollY
-                };
-
-            return {
-                x: Math.round(pointerPosition.left - canvasOffset.x),
-                y: Math.round(pointerPosition.top - canvasOffset.y)
-            };
-        };
-
-        this.el.addEventListener('dragstart', function (e) {
-            e.preventDefault();
-        }, false);
-        this.el.addEventListener('click', function (e) {
-            e.preventDefault();
-
-            if (answers.length >= hotSpots.length) {
-                return;
-            }
-
-            var point = getPointOnImage(e.clientX, e.clientY);
-
-            var answer = new Answer();
-            answer.x = point.x;
-            answer.y = point.y;
-
-            answers.push(answer);
-            self.addAnswer(answer);
-
-            if (answers.length === hotSpots.length) {
-                console.log(lang);
-                self.messagesEl.textContent = lang.HotspotExerciseFinished;
-
-                return;
-            }
-
-            self.messagesEl.textContent = lang.NextAnswer + ' ' + hotSpots[answers.length].name;
-        });
-    };
-    CanvasSVG.prototype.addAnswer = function (answer) {
-        var pointSVG = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
-        pointSVG.setAttribute('cx', answer.x);
-        pointSVG.setAttribute('cy', answer.y);
-        pointSVG.setAttribute('r', 15);
-        pointSVG.setAttribute('fill', '#00677C');
-
-        var textSVG = document.createElementNS('http://www.w3.org/2000/svg', 'text');
-        textSVG.setAttribute('x', answer.x);
-        textSVG.setAttribute('y', answer.y);
-        textSVG.setAttribute('dy', 5);
-        textSVG.setAttribute('font-family', 'sans-serif');
-        textSVG.setAttribute('text-anchor', 'middle');
-        textSVG.setAttribute('fill', 'white');
-        textSVG.textContent = answers.length;
-
-        this.el.appendChild(pointSVG);
-        this.el.appendChild(textSVG);
-
-        var hotSpot = hotSpots[answers.length - 1];
-
-        var hotspotTxt = document.createElement('input');
-        hotspotTxt.type = 'hidden';
-        hotspotTxt.name = 'hotspot[' + config.questionId + '][' + hotSpot.id + ']';
-        hotspotTxt.value = [answer.x, answer.y].join(';');
-
-        var choiceTxt = document.createElement('input');
-        choiceTxt.type = 'hidden';
-        choiceTxt.name = 'choice[' + config.questionId + '][' + hotSpot.id + ']';
-        choiceTxt.value = hotSpot.checkPoint(answer.x, answer.y) ? 1 : 0;
-
-        this.el.parentNode.appendChild(hotspotTxt);
-        this.el.parentNode.appendChild(choiceTxt);
-    };
-    CanvasSVG.prototype.startMessagesPanel = function () {
-        this.messagesEl.textContent = lang.NextAnswer + ' ' + hotSpots[0].name;
-
-        this.el.parentNode.parentNode.appendChild(this.messagesEl);
-    };
-
-    var decodeHotSpot = function (hotSpotInfo) {
-        var hotSpot = null,
-            coords = hotSpotInfo.coord.split('|');
-
-        switch (hotSpotInfo.type) {
-            case 'square':
-                var position = coords[0].split(';');
-
-                hotSpot = new Square();
-                hotSpot.x = parseInt(position[0]);
-                hotSpot.y = parseInt(position[1]);
-                hotSpot.width = parseInt(coords[1]);
-                hotSpot.height = parseInt(coords[2]);
-                break;
-            case 'circle':
-                var center = coords[0].split(';');
-
-                hotSpot = new Ellipse();
-                hotSpot.centerX = parseInt(center[0]);
-                hotSpot.centerY = parseInt(center[1]);
-                hotSpot.radiusX = parseInt(coords[1]);
-                hotSpot.radiusY = parseInt(coords[2]);
-                break;
-            case 'poly':
-                hotSpot = new Polygon();
-
-                coords.forEach(function (pairedCoord) {
-                    var coord = pairedCoord.split(';');
-
-                    hotSpot.points.push([
-                        parseInt(coord[0]),
-                        parseInt(coord[1])
-                    ]);
-                });
-                break;
-        }
-
-        if (hotSpot) {
-            hotSpot.id = parseInt(hotSpotInfo.id);
-            hotSpot.name = hotSpotInfo.answer;
-        }
-
-        return hotSpot;
-    };
-
-    var config, lang, hotSpots = [], answers = [];
-
-    var startQuestion = function (hotSpotQuestionInfo) {
-        var image = new Image();
-        image.onload = function () {
-            var canvasSVG = new CanvasSVG(this);
-
-            hotSpotQuestionInfo.hotspots.forEach(function (hotSpotInfo) {
-                var hotSpot = decodeHotSpot(hotSpotInfo);
-
-                if (!hotSpot) {
-                    return;
-                }
-
-                hotSpots.push(hotSpot);
-            });
-
-            $(config.selector)
-                .css('width', this.width)
-                .append(canvasSVG.el);
-
-            canvasSVG.setEvents();
-            canvasSVG.startMessagesPanel();
-        };
-        image.src = hotSpotQuestionInfo.image;
-
-        lang = hotSpotQuestionInfo.lang;
-    };
-
-    return {
-        init: function (settings) {
-            config = $.extend({
-                questionId: 0,
-                selector: ''
-            }, settings);
-
-            if (!config.questionId || !config.selector) {
-                return;
-            }
-
-            var xhrHotSpotQuestion = $.getJSON('/main/exercice/hotspot_actionscript.as.php', {
-                modifyAnswers: parseInt(config.questionId)
-            });
-
-            $.when(xhrHotSpotQuestion).done(startQuestion);
-        } 
-    };
-})();