1/** 2 * Relaciona activity (Export) 3 * 4 * Released under Attribution-ShareAlike 4.0 International License. 5 * Author: Manuel Narváez MartÃnez 6 * License: http://creativecommons.org/licenses/by-sa/4.0/ 7 * 8 */ 9var $eXeRelaciona = { 10 idevicePath: '', 11 borderColors: $exeDevices.iDevice.gamification.colors.borderColors, 12 colors: $exeDevices.iDevice.gamification.colors.backColor, 13 options: [], 14 hasSCORMbutton: false, 15 isInExe: false, 16 userName: '', 17 previousScore: '', 18 initialScore: '', 19 version: 3, 20 scormAPIwrapper: 'libs/SCORM_API_wrapper.js', 21 scormFunctions: 'libs/SCOFunctions.js', 22 mScorm: null, 23 jqueryui: 1, 24 25 init: function () { 26 $exeDevices.iDevice.gamification.initGame( 27 this, 28 'Relate', 29 'relate', 30 'relaciona-IDevice' 31 ); 32 }, 33 34 enable: function () { 35 $eXeRelaciona.loadGame(); 36 }, 37 38 loadGame: function () { 39 $eXeRelaciona.options = []; 40 41 $eXeRelaciona.activities.each(function (i) { 42 const dl = $('.relaciona-DataGame', this); 43 const mOption = $eXeRelaciona.loadDataGame(dl, this); 44 45 mOption.scorerp = 0; 46 mOption.idevicePath = $eXeRelaciona.idevicePath; 47 mOption.main = 'rlcMainContainer-' + i; 48 mOption.idevice = 'relaciona-IDevice'; 49 50 $eXeRelaciona.options.push(mOption); 51 52 const rlc = $eXeRelaciona.createInterfaceCards(i); 53 54 dl.before(rlc).remove(); 55 $('#rlcGameMinimize-' + i).hide(); 56 $('#rlcGameContainer-' + i).hide(); 57 $('#rlcCubierta-' + i).hide(); 58 if (mOption.showMinimize) { 59 $('#rlcGameMinimize-' + i) 60 .css({ cursor: 'pointer' }) 61 .show(); 62 } else { 63 $('#rlcGameContainer-' + i).show(); 64 } 65 66 $eXeRelaciona.createCards(i); 67 68 $eXeRelaciona.addEvents(i); 69 if (mOption.type == 2 && mOption.time > 0) { 70 $('#rlcImgTime-' + i).show(); 71 $('#rlcPTime-' + i).show(); 72 $('#rlcStartGame-' + i).show(); 73 $('#rlcMessage-' + i).hide(); 74 $eXeRelaciona.updateTime(mOption.time * 60, i); 75 } 76 }); 77 78 let node = document.querySelector('.page-content'); 79 if (this.isInExe) { 80 node = document.getElementById('node-content'); 81 } 82 if (node) 83 $exeDevices.iDevice.gamification.observers.observeResize( 84 $eXeRelaciona, 85 node 86 ); 87 88 const relateHtml = $('.relaciona-IDevice').html(); 89 if ($exeDevices.iDevice.gamification.math.hasLatex(relateHtml)) { 90 $exeDevices.iDevice.gamification.math.updateLatex( 91 '.relaciona-IDevice' 92 ); 93 } 94 }, 95 96 loadDataGame: function (data, sthis) { 97 const json = data.text(), 98 mOptions = 99 $exeDevices.iDevice.gamification.helpers.isJsonString(json), 100 $imagesLink = $('.relaciona-LinkImages', sthis), 101 $audiosLink = $('.relaciona-LinkAudios', sthis), 102 $imagesLinkBack = $('.relaciona-LinkImagesBack', sthis), 103 $audiosLinkBack = $('.relaciona-LinkAudiosBack', sthis); 104 105 mOptions.playerAudio = ''; 106 mOptions.gameStarted = false; 107 mOptions.typeDrag = 0; 108 109 $imagesLink.each(function () { 110 const iq = parseInt($(this).text()); 111 if (!isNaN(iq) && iq < mOptions.cardsGame.length) { 112 const flipcard = mOptions.cardsGame[iq]; 113 flipcard.url = $(this).attr('href'); 114 if (flipcard.url.length < 4) { 115 flipcard.url = ''; 116 } 117 } 118 }); 119 120 $audiosLink.each(function () { 121 const iqa = parseInt($(this).text()); 122 if (!isNaN(iqa) && iqa < mOptions.cardsGame.length) { 123 const flipcard = mOptions.cardsGame[iqa]; 124 flipcard.audio = $(this).attr('href'); 125 if (flipcard.audio.length < 4) { 126 flipcard.audio = ''; 127 } 128 } 129 }); 130 131 $imagesLinkBack.each(function () { 132 const iq = parseInt($(this).text()); 133 if (!isNaN(iq) && iq < mOptions.cardsGame.length) { 134 const flipcard = mOptions.cardsGame[iq]; 135 flipcard.urlBk = $(this).attr('href'); 136 if (flipcard.urlBk.length < 4) { 137 flipcard.urlBk = ''; 138 } 139 } 140 }); 141 142 $audiosLinkBack.each(function () { 143 const iqa = parseInt($(this).text()); 144 if (!isNaN(iqa) && iqa < mOptions.cardsGame.length) { 145 const flipcard = mOptions.cardsGame[iqa]; 146 flipcard.audioBk = $(this).attr('href'); 147 if (flipcard.audioBk.length < 4) { 148 flipcard.audioBk = ''; 149 } 150 } 151 }); 152 mOptions.currentWordDiv = null; 153 mOptions.linesMap = new Map(); 154 mOptions.lines = []; 155 mOptions.permitirErrores = mOptions.type > 0; 156 mOptions.time = 157 typeof mOptions.time === 'undefined' ? 0 : mOptions.time; 158 mOptions.evaluation = 159 typeof mOptions.evaluation === 'undefined' 160 ? false 161 : mOptions.evaluation; 162 mOptions.evaluationID = 163 typeof mOptions.evaluationID === 'undefined' 164 ? '' 165 : mOptions.evaluationID; 166 mOptions.hits = 0; 167 mOptions.errors = 0; 168 mOptions.score = 0; 169 mOptions.active = 0; 170 mOptions.obtainedClue = false;
171 172 mOptions.cardsGame = 173 $exeDevices.iDevice.gamification.helpers.getQuestions( 174 mOptions.cardsGame, 175 mOptions.percentajeCards 176 ); 177 for (let i = 0; i < mOptions.cardsGame.length; i++) { 178 mOptions.cardsGame[i].id = i; 179 mOptions.cardsGame[i].eText = $eXeRelaciona.decodeURIComponentSafe( 180 mOptions.cardsGame[i].eText 181 ); 182 mOptions.cardsGame[i].eTextBk = 183 $eXeRelaciona.decodeURIComponentSafe( 184 mOptions.cardsGame[i].eTextBk 185 ); 186 const id = $eXeRelaciona.getID(); 187 mOptions.cardsGame[i].lineindex = id; 188 } 189 190 mOptions.numberCards = mOptions.cardsGame.length; 191 mOptions.realNumberCards = mOptions.numberCards; 192 mOptions.fullscreen = false; 193 return mOptions; 194 }, 195 196 decodeURIComponentSafe: function (s) { 197 if (!s) return s; 198 return decodeURIComponent(s).replace('%', '%'); 199 }, 200 201 updateTime: function (tiempo, instance) { 202 const mOptions = $eXeRelaciona.options[instance], 203 mTime = 204 $exeDevices.iDevice.gamification.helpers.getTimeToString( 205 tiempo 206 ); 207 if (mOptions.time < 0) return; 208 $(`#rlcPTime-${instance}`).text(mTime); 209 }, 210 211 clearHtml: function (htmlString) { 212 const tempDiv = $('<div>').html(htmlString); 213 return tempDiv.text(); 214 }, 215 216 createCards: function (instance) { 217 const mOptions = $eXeRelaciona.options[instance], 218 shuffledWords = $exeDevices.iDevice.gamification.helpers.shuffleAds( 219 [...mOptions.cardsGame] 220 ), 221 shuffledDefinitions = 222 $exeDevices.iDevice.gamification.helpers.shuffleAds([ 223 ...mOptions.cardsGame, 224 ]); 225 226 $('#rlcContainerWords-' + instance).empty(); 227 $('#rlcContainerDefinitions-' + instance).empty(); 228 229 shuffledWords.forEach((card, index) => { 230 let imgV = card.url.length > 3 ? 'block' : 'none'; 231 let txtV = card.eText.length > 0 ? 'flex' : 'none'; 232 let imgW = card.eText.length == 0 ? '100%' : '30%'; 233 234 imgW = card.url.length < 3 ? '0%' : imgW; 235 236 let txtW = card.url.length < 3 ? '100%' : '70%'; 237 txtW = card.eText.length == 0 ? '0%' : txtW; 238 let color = card.color.length > 2 ? card.color : 'inherit'; 239 let bkcolor = 240 card.backcolor.length > 2 ? card.backcolor : 'trasparent'; 241 let audio = card.audio.length > 3 ? 'block' : 'none'; 242 let author = card.author.length > 3 ? 'block' : 'none'; 243 let audioCls = 244 card.url.length < 3 && card.eText.length == 0 245 ? 'RLCP-LinkAudioBig' 246 : 'RLCP-LinkAudio'; 247 248 const fullimage = 249 card.url.length > 3 250 ? `<a href="#" class="RLCP-FullLinkImage" data-url="${card.url}"" title="${mOptions.msgs.msgFullScreen}"> 251 <strong><span class="sr-av">${mOptions.msgs.msgFullScreen}:</span></strong> 252 <div class="exeQuextIcons exeQuextIcons-FullImage RLCP-Activo"></div> 253 </a>` 254 : ''; 255 256 const wordDiv = 257 $(`<div class="RLCP-Word RLCP-NoSelect" data-id="${card.id}" data-lineindex="${card.lineindex}"> 258 <div class="RLCP-ContainerData"> 259 <div class="RLCP-EText" style="display:${txtV}; width:${txtW}; color:${color}; background-color:${bkcolor};"><div class="RLCP-ETextDinamyc">${card.eText}</div></div> 260 <div class="RLCP-ImageContain" style="display:${imgV}; width:${imgW};"> 261 <img src="${card.url}" class="RLCP-Image" data-url="${card.url}" data-x="${card.x}" data-y="${card.y}" alt="${card.alt}" /> 262 <div class="RLCP-Author RLCP-AuthorWord" data-author="${card.author}" style="display: ${author};" alt="${card.author}" title="${mOptions.msgs.msgAuthor + ': ' + $eXeRelaciona.clearHtml(card.author)}"><img src="${$eXeRelaciona.idevicePath}exequextcopyright.png" /></div> 263 ${fullimage} 264 </div> 265 </div> 266 <div style="display:${audio}" data-audio="${card.audio}" class="RLCP-TAudio ${audioCls}" title="Audio"><img src="${$eXeRelaciona.idevicePath}exequextplayaudio.svg" class="RLCP-Audio" alt="Audio"></div> 267 </div>`); 268 $('#rlcContainerWords-' + instance).append(wordDiv); 269 }); 270 271 shuffledDefinitions.forEach((card, index) => { 272 let imgV = card.urlBk.length > 3 ? 'block' : 'none'; 273 let txtV = card.eTextBk.length > 0 ? 'flex' : 'none'; 274 let imgW = card.eTextBk.length == 0 ? '100%' : '30%'; 275 imgW = card.urlBk.length < 3 ? '0%' : imgW; 276 let txtW = card.urlBk.length < 3 ? '100%' : '70%'; 277 txtW = card.eTextBk.length == 0 ? '0%' : txtW; 278 let color = card.colorBk.length > 2 ? card.colorBk : 'inherit'; 279 let bkcolor = 280 card.backcolorBk.length > 2 ? card.backcolorBk : 'trasparent'; 281 let audio = card.audioBk.length > 3 ? 'block' : 'none'; 282 let author = card.authorBk.length > 0 ? 'block' : 'none'; 283 let audioCls = 284 card.urlBk.length < 3 && card.eTextBk.length == 0 285 ? 'RLCP-LinkAudioBig' 286 : 'RLCP-LinkAudio'; 287 const fullimage = 288 card.urlBk.length > 3 289 ? `<a href="#" class="RLCP-FullLinkImage" data-url="${card.urlBk}"" title="${mOptions.msgs.msgFullScreen}"> 290 <strong><span class="sr-av">${mOptions.msgs.msgFullScreen}:</span></strong> 291 <div class="exeQuextIcons exeQuextIcons-FullImage RLCP-Activo"></div> 292 </a>` 293 : ''; 294 const definitionDiv = 295 $(`<div class="RLCP-Definition RLCP-NoSelect" data-id="${card.id}" data-lineindex="0" > 296 <div class="RLCP-ContainerData"> 297 <div class="RLCP-ImageContain" style="display:${imgV}; width:${imgW};"> 298 <img src="${card.urlBk}" class="RLCP-Image" data-url="${card.urlBk}" data-x="${card.x}" data-y="${card.y}" alt="${card.altBk}" /> 299 <div class="RLCP-Author RLCP-AuthorDef" style="display: ${author};" data-author="${card.authorBk}" alt="${card.authorBk}" title="${mOptions.msgs.msgAuthor + ': ' + $eXeRelaciona.clearHtml(card.authorBk)}"><img src="${$eXeRelaciona.idevicePath}exequextcopyright.png" /></div> 300 ${fullimage} 301 </div> 302 <div class="RLCP-EText" style="display:${txtV}; width:${txtW}; color:${color}; background-color:${bkcolor};"><div class="RLCP-ETextDinamyc">${card.eTextBk}</div></div> 303 </div> 304 <div data-audio="${card.audioBk}" style="display:${audio}" class="RLCP-TAudio ${audioCls}" title="Audio"><img src="${$eXeRelaciona.idevicePath}exequextplayaudio.svg" class="FLCDSP-RLCP" alt="Audio"></div> 305 </div>`); 306 $('#rlcContainerDefinitions-' + instance).append(definitionDiv); 307 }); 308 }, 309 310 getID: function () { 311 const randomNumber1 = Math.floor(1000 + Math.random() * 9000), 312 randomNumber2 = Math.floor(1000 + Math.random() * 9000), 313 timestamp = Date.now(); 314 return `${randomNumber1}${timestamp}${randomNumber2}`; 315 }, 316 317 startGame: function (instance) { 318 let mOptions = $eXeRelaciona.options[instance]; 319 320 if (mOptions.gameStarted) return; 321 322 $('#rlcContainerGame-' + instance).show(); 323 $(`#rlcImgTime-${instance}`).hide(); 324 $(`#rlcPTime-${instance}`).hide(); 325 $(`#rlcButtons-${instance}`).hide(); 326 $(`#rlcResetButton-${instance}`).hide(); 327 328 if (mOptions.type > 0) { 329 $(`#rlcButtons-${instance}`).css('display', 'flex'); 330 $(`#rlcCheckButton-${instance}`).show(); 331 } 332 mOptions.gameStarted = true; 333 mOptions.solveds = []; 334 mOptions.selecteds = []; 335 mOptions.hits = 0; 336 mOptions.errors = 0; 337 mOptions.score = 0; 338 mOptions.counter = mOptions.time * 60; 339 mOptions.gameOver = false;
340 mOptions.gameStarted = false; 341 mOptions.obtainedClue = false; 342 343 $('#rlcPShowClue-' + instance).text(''); 344 $('#rlcShowClue-' + instance).hide(); 345 $('#rlcPHits-' + instance).text(mOptions.hits); 346 $('#rlcPErrors-' + instance).text(mOptions.errors); 347 $('#rlcCubierta-' + instance).hide(); 348 $('#rlcStartGame-' + instance).hide(); 349 $('#rlcMessage-' + instance).hide(); 350 351 if ( 352 typeof mOptions != 'undefined' && 353 mOptions.type == 2 && 354 mOptions.time > 0 355 ) { 356 $(`#rlcPTime-${instance}`).show(); 357 $(`#rlcImgTime-${instance}`).show(); 358 let $node = $('#rlcMainContainer-' + instance); 359 let $content = $('#node-content'); 360 mOptions.counterClock = setInterval(function () { 361 if ( 362 !$node.length || 363 ($content.length && $content.attr('mode') === 'edition') 364 ) { 365 clearInterval(mOptions.counterClock); 366 return; 367 } 368 if (typeof mOptions != 'undefined' && mOptions.gameStarted) { 369 mOptions.counter--; 370 $eXeRelaciona.updateTime(mOptions.counter, instance); 371 if (mOptions.counter <= 0) { 372 $eXeRelaciona.gameOver(instance); 373 return; 374 } 375 } 376 }, 1000); 377 $eXeRelaciona.updateTime(mOptions.time * 60, instance); 378 } 379 380 mOptions.gameStarted = true; 381 }, 382 redibujarLineas: function (instance, isMoving) { 383 const mOptions = $eXeRelaciona.options[instance]; 384 if (!mOptions) return; 385 mOptions._moving = !!isMoving; 386 $eXeRelaciona.requestRedraw(instance); 387 }, 388 389 requestRedraw: function (instance) { 390 const mOptions = $eXeRelaciona.options[instance]; 391 if (!mOptions || !mOptions.canvas || !mOptions.contexto) return; 392 if (mOptions._rafPending) return; 393 mOptions._rafPending = true; 394 const raf = 395 typeof requestAnimationFrame !== 'undefined' 396 ? requestAnimationFrame 397 : function (cb) { 398 return setTimeout(cb, 16); 399 }; 400 raf(() => { 401 mOptions._rafPending = false; 402 $eXeRelaciona._drawLines(instance); 403 }); 404 }, 405 406 _drawLines: function (instance) { 407 const mOptions = $eXeRelaciona.options[instance]; 408 if (!mOptions || !mOptions.canvas || !mOptions.contexto) return; 409 const ctx = mOptions.contexto; 410 const canvasRect = mOptions.canvas.getBoundingClientRect(); 411 const dpr = mOptions._dpr || Math.max(window.devicePixelRatio || 1, 1); 412 const cssW = mOptions._cssW 413 ? mOptions._cssW 414 : mOptions.canvas.width / dpr; 415 const cssH = mOptions._cssH 416 ? mOptions._cssH 417 : mOptions.canvas.height / dpr; 418 ctx.clearRect(0, 0, cssW, cssH); 419 420 ctx.lineWidth = $eXeRelaciona.isMobile() ? 3 : 5; 421 422 mOptions.linesMap.forEach((line) => { 423 const startRect = line.start[0].getBoundingClientRect(); 424 const endRect = line.end[0].getBoundingClientRect(); 425 const x1 = startRect.right - canvasRect.left; 426 const y1 = startRect.top + startRect.height / 2 - canvasRect.top; 427 const x2 = endRect.left - canvasRect.left; 428 const y2 = endRect.top + endRect.height / 2 - canvasRect.top; 429 $eXeRelaciona.dibujaLineaCurva(ctx, x1, y1, x2, y2, line.color); 430 }); 431 432 if (mOptions._moving && mOptions.currentWordDiv) { 433 const startRect = 434 mOptions.currentWordDiv[0].getBoundingClientRect(); 435 const x1 = startRect.right - canvasRect.left; 436 const y1 = startRect.top + startRect.height / 2 - canvasRect.top; 437 const x2 = 438 typeof mOptions._tempX === 'number' 439 ? mOptions._tempX - canvasRect.left 440 : x1; 441 const y2 = 442 typeof mOptions._tempY === 'number' 443 ? mOptions._tempY - canvasRect.top 444 : y1; 445 $eXeRelaciona.dibujaLineaCurva( 446 ctx, 447 x1, 448 y1, 449 x2, 450 y2, 451 $eXeRelaciona.borderColors.blue 452 ); 453 } 454 }, 455 456 createInterfaceCards: function (instance) { 457 const path = $eXeRelaciona.idevicePath, 458 msgs = $eXeRelaciona.options[instance].msgs, 459 mOptions = $eXeRelaciona.options[instance], 460 html = ` 461 <div class="RLCP-MainContainer" id="rlcMainContainer-${instance}"> 462 <div class="RLCP-GameMinimize" id="rlcGameMinimize-${instance}"> 463 <a href="#" class="RLCP-LinkMaximize" id="rlcLinkMaximize-${instance}" title="${msgs.msgMaximize}"> 464 <img src="${path}relacionaIcon.png" class="RLCP-IconMinimize RLCP-Activo" alt=""> 465 <div class="RLCP-MessageMaximize" id="rlcMessageMaximize-${instance}">${msgs.msgPlayStart}</div> 466 </a> 467 </div> 468 <div class="RLCP-GameContainer" id="rlcGameContainer-${instance}"> 469 <div class="RLCP-GameScoreBoard" id="rlcGameScoreBoard-${instance}"> 470 <div class="RLCP-GameScores" id="rlcGameScores-${instance}"> 471 <div class="exeQuextIcons exeQuextIcons-Number" id="rlcPNumberIcon-${instance}" title="${msgs.msgNumQuestions}"></div> 472 <p><span class="sr-av">${msgs.msgNumQuestions}: </span><span id="rlcPNumber-${instance}">0</span></p> 473 <div class="exeQuextIcons exeQuextIcons-Hit" title="${msgs.msgHits}"></div> 474 <p><span class="sr-av">${msgs.msgHits}: </span><span id="rlcPHits-${instance}">0</span></p> 475 <div class="exeQuextIcons exeQuextIcons-Error" title="${msgs.msgErrors}"></div> 476 <p><span class="sr-av">${msgs.msgErrors}: </span><span id="rlcPErrors-${instance}">0</span></p> 477 <div class="exeQuextIcons exeQuextIcons-Score" title="${msgs.msgScore}"></div> 478 <p><span class="sr-av">${msgs.msgScore}: </span><span id="rlcPScore-${instance}">0</span></p> 479 </div> 480 <div class="RLCP-Info" id="rlcInfo-${instance}"></div> 481 <div class="RLCP-TimeNumber"> 482 <strong><span class="sr-av">${msgs.msgTime}:</span></strong> 483 <div class="exeQuextIcons exeQuextIcons-Time" style="display:none" id="rlcImgTime-${instance}" title="${msgs.msgTime}"></div> 484 <p id="rlcPTime-${instance}" style="display:none" class="RLCP-PTime">00:00</p> 485 <a href="#" class="RLCP-LinkMinimize" id="rlcLinkMinimize-${instance}" title="${msgs.msgMinimize}"> 486 <strong><span class="sr-av">${msgs.msgMinimize}:</span></strong> 487 <div class="exeQuextIcons exeQuextIcons-Minimize RLCP-Activo"></div> 488 </a> 489 <a href="#" class="RLCP-LinkFullScreen" id="rlcLinkFullScreen-${instance}" title="${msgs.msgFullScreen}"> 490 <strong><span class="sr-av">${msgs.msgFullScreen}:</span></strong> 491 <div class="exeQuextIcons exeQuextIcons-FullScreen RLCP-Activo" id="rlcFullScreen-${instance}"></div> 492 </a> 493 </div> 494 </div> 495 <div class="RLCP-Information"> 496 <a href="#" style="display:none" id="rlcStartGame-${instance}">${msgs.msgPlayStart}</a> 497 <p class="RLCP-Message" id="rlcMessage-${instance}"></p> 498 </div> 499 <div id="rlcButtons-${instance}" class="RLCP-Buttons"> 500 <a href="#" class="RLCP-ResetButton" id="rlcResetButton-${instance}">${msgs.msgRestart}</a> 501 <a href="#" class="RLCP-CheckButton" id="rlcCheckButton-${instance}">${msgs.msgCheck}</a> 502 </div> 503 <div class="RLCP-Multimedia" id="rlcMultimedia-${instance}"> 504 ${$eXeRelaciona.getMainHtml(instance)} 505 </div> 506 <div class="RLCP-AuthorGame" id="rlcAuthorGame-${instance}"></div> 507 </div> 508 <div class="RLCP-Cover" id="rlcCubierta-${instance}"> 509 <div class="RLCP-CodeAccessDiv" id="rlcCodeAccessDiv-${instance}"> 510 <div class="RLCP-MessageCodeAccessE" id="rlcMesajeAccesCodeE-${instance}"></div> 511 <div class="RLCP-DataCodeAccessE"> 512 <label class="sr-av">${msgs.msgCodeAccess}:</label> 513 <input type="text" class="RLCP-CodeAccessE form-control" id="rlcCodeAccessE-${instance}" placeholder="${msgs.msgCodeAccess}"> 514 <a href="#" id="rlcCodeAccessButton-${instance}" title="${msgs.msgSubmit}"> 515 <strong><span class="sr-av">${msgs.msgSubmit}</span></strong> 516 <div class="exeQuextIcons-Submit RLCP-Activo"></div> 517 </a> 518 </div> 519 </div> 520 <div class="RLCP-ShowClue" id="rlcShowClue-${instance}"> 521 <p class="sr-av">${msgs.msgClue}</p> 522 <p class="RLCP-PShowClue" id="rlcPShowClue-${instance}"></p> 523 <a href="#" class="RLCP-ClueBotton" id="rlcClueButton-${instance}" title="${msgs.msgClose}">${msgs.msgClose}</a> 524 </div> 525 </div> 526 </div> 527 ${$exeDevices.iDevice.gamification.scorm.addButtonScoreNew(mOptions, this.isInExe)} 528 `; 529 return html; 530 }, 531 532 getMainHtml: function (instance) { 533 let html = `<div class="RLCP-Main"> 534 <div id="rlcContainerGame-${instance}" class="RLCP-ContainerGame"> 535 <div id="rlcContainerWords-${instance}" class="RLCP-ContainerWords"></div> 536 <div id="rlcContainerDefinitions-${instance}" class="RLCP-ContainerDefinitions"></div> 537 </div>
538 <canvas id="rlcCanvas-${instance}" width="800" height="600" class="RLCP-Canvas"></canvas> 539 </div>`; 540 return html; 541 }, 542 543 hexToRgba: function (hex, opacity) { 544 const r = parseInt(hex.slice(1, 3), 16), 545 g = parseInt(hex.slice(3, 5), 16), 546 b = parseInt(hex.slice(5, 7), 16), 547 opacity1 = Math.min(Math.max(opacity, 0), 1); 548 return `rgba(${r}, ${g}, ${b}, ${opacity1})`; 549 }, 550 551 shuffleElements: function (parentElement) { 552 const children = parentElement.children().get(), 553 shuffledChildren = 554 $exeDevices.iDevice.gamification.helpers.shuffleAds(children); 555 parentElement.empty().append(shuffledChildren); 556 }, 557 558 gameOver: function (instance) { 559 $eXeRelaciona.checkState(instance); 560 }, 561 562 showScoreFooter: function (instance) { 563 const mOptions = $eXeRelaciona.options[instance], 564 score = (mOptions.hits * 10) / mOptions.realNumberCards, 565 formattedScore = Number(score).toFixed(2); 566 567 $(`#rlcRepeatActivity-${instance}`).text( 568 `${mOptions.msgs.msgYouScore}: ${formattedScore}` 569 ); 570 571 return formattedScore; 572 }, 573 574 showScoreGame: function (instance) { 575 const mOptions = $eXeRelaciona.options[instance], 576 msgs = mOptions.msgs, 577 score = ((mOptions.hits * 10) / mOptions.numberCards).toFixed(2); 578 let message = msgs.msgEndGameM.replace('%s', score), 579 messageColor = 0, 580 clueMessage = ''; 581 582 $eXeRelaciona.showMessage(messageColor, message, instance, true); 583 584 if (mOptions.itinerary.showClue) { 585 if (score * 100 > mOptions.itinerary.percentageClue) { 586 clueMessage = mOptions.itinerary.clueGame; 587 } else { 588 clueMessage = msgs.msgTryAgain.replace( 589 '%s', 590 mOptions.itinerary.percentageClue 591 ); 592 } 593 $eXeRelaciona.showMessage(3, clueMessage, instance, true); 594 } 595 596 const sscore = ( 597 (mOptions.hits * 10) / 598 mOptions.cardsGame.length 599 ).toFixed(2); 600 $(`#rlcPScore-${instance}`).text(sscore); 601 $(`#rlcPHits-${instance}`).text(mOptions.hits); 602 $(`#rlcPErrors-${instance}`).text(mOptions.errors); 603 $(`#rlcPNumber-${instance}`).text( 604 mOptions.realNumberCards - mOptions.hits - mOptions.errors 605 ); 606 }, 607 608 showClue: function (instance) { 609 const mOptions = $eXeRelaciona.options[instance], 610 percentageHits = (mOptions.hits * 10) / mOptions.cardsGame.length; 611 if (mOptions.itinerary.showClue) { 612 if (percentageHits >= mOptions.itinerary.percentageClue) { 613 if (!mOptions.obtainedClue) { 614 mOptions.obtainedClue = true; 615 const msg = `${mOptions.msgs.msgInformation}: ${mOptions.itinerary.clueGame}`; 616 $(`#rlcPShowClue-${instance}`).text(msg); 617 $(`#rlcShowClue-${instance}`).show(); 618 $(`#rlcCubierta-${instance}`).show(); 619 } 620 } 621 } 622 }, 623 624 reboot: function (instance) { 625 const mOptions = $eXeRelaciona.options[instance]; 626 mOptions.hits = 0; 627 mOptions.errors = 0; 628 mOptions.score = 0; 629 mOptions.active = 0; 630 mOptions.obtainedClue = false; 631 $('#rlcButtons-' + instance).hide(); 632 $eXeRelaciona.rebootCards(instance); 633 $eXeRelaciona.showScoreGame(instance); 634 mOptions.gameStarted = true; 635 mOptions.gameOver = false; 636 637 $('#rlcMessage-' + instance).hide(); 638 }, 639 640 rebootCards: function (instance) { 641 const mOptions = $eXeRelaciona.options[instance]; 642 643 const $container = $(`#rlcGameContainer-${instance}`); 644 $container 645 .find('.RLCP-Word, .RLCP-Definition') 646 .removeClass('RLCP-Connected') 647 .removeData('lineindex'); 648 649 mOptions.linesMap.clear(); 650 mOptions.currentWordDiv = null; 651 if (mOptions.tempEnd) { 652 mOptions.tempEnd.remove(); 653 mOptions.tempEnd = null; 654 } 655 656 $eXeRelaciona.ajustarCanvas(instance); 657 $eXeRelaciona.redibujarLineas(instance, false); 658 659 $eXeRelaciona.createCards(instance); 660 $eXeRelaciona.showScoreGame(instance); 661 662 if (mOptions.type == 2) { 663 mOptions.counter = mOptions.time * 60; 664 $eXeRelaciona.startGame(instance); 665 } 666 }, 667 668 checkState: function (instance) { 669 const mOptions = $eXeRelaciona.options[instance]; 670 671 if (typeof mOptions === 'undefined') return; 672 673 clearInterval(mOptions.counterClock); 674 mOptions.gameOver = true; 675 mOptions.gameStarted = false;
676 $eXeRelaciona.checkStateArrows(instance); 677 678 $(`#rlcCheckButton-${instance}`).hide(); 679 $(`#rlcButtons-${instance}`).css('display', 'flex'); 680 $(`#rlcResetButton-${instance}`).show(); 681 682 $exeDevices.iDevice.gamification.media.stopSound(mOptions); 683 $eXeRelaciona.showScoreGame(instance); 684 $eXeRelaciona.saveEvaluation(instance, true); 685 686 if (mOptions.isScorm === 1) { 687 $eXeRelaciona.sendScore(true, instance); 688 $eXeRelaciona.initialScore = 689 $eXeRelaciona.showScoreFooter(instance); 690 } 691 }, 692 693 checkStateArrows: function (instance) { 694 const mOptions = $eXeRelaciona.options[instance]; 695 mOptions.hits = 0; 696 mOptions.errors = 0; 697 698 mOptions.linesMap.forEach((value, key) => { 699 if (value.correct) { 700 mOptions.hits++; 701 value.color = $eXeRelaciona.borderColors.green; 702 } else { 703 value.color = $eXeRelaciona.borderColors.red; 704 mOptions.errors++; 705 } 706 }); 707 $eXeRelaciona.redibujarLineas(instance, false); 708 }, 709 710 removeEvents: function (instance) { 711 $(`#rlcLinkMaximize-${instance}`).off('click touchstart'); 712 $(`#rlcLinkMinimize-${instance}`).off('click touchstart'); 713 $(`#rlcCodeAccessButton-${instance}`).off('click touchstart'); 714 $(`#rlcCodeAccessE-${instance}`).off('keydown'); 715 $(`#rlcSendScore-${instance}`).off('click'); 716 $('#rlcMainContainer-' + instance) 717 .closest('.idevice_node') 718 .off('click', '.Games-SendScore'); 719 720 $(window).off('unload.eXeRelaciona beforeunload.eXeRelaciona'); 721 722 $(document).off('mousemove.eXeRlc' + instance); 723 $(document).off('mouseup.eXeRlc' + instance); 724 725 $(`#rlcClueButton-${instance}`).off('click'); 726 $(`#rlcStartGame-${instance}`).off('click'); 727 $(`#rlcLinkFullScreen-${instance}`).off('click touchstart'); 728 $(`#rlcResetButton-${instance}`).off('click'); 729 $(`#rlcCheckButton-${instance}`).off('click'); 730 $(`#rlcGameContainer-${instance}`).off( 731 'mouseenter touchstart', 732 '.RLCP-Author' 733 ); 734 $(`#rlcGameContainer-${instance}`).off( 735 'mouseleave touchend touchcancel', 736 '.RLCP-Author' 737 ); 738 $(`#rlcGameContainer-${instance}`).off( 739 'click touchstart', 740 '.RLCP-TAudio' 741 ); 742 }, 743 744 addEvents: function (instance) { 745 const mOptions = $eXeRelaciona.options[instance]; 746 const $rlcGameContainer = $('#rlcGameContainer-' + instance); 747 748 $eXeRelaciona.removeEvents(instance); 749 750 $('#rlcMainContainer-' + instance) 751 .closest('.idevice_node') 752 .on('click', '.Games-SendScore', function (e) { 753 e.preventDefault(); 754 $eXeRelaciona.sendScore(false, instance); 755 $eXeRelaciona.saveEvaluation(instance); 756 }); 757 758 $('#rlcLinkMaximize-' + instance).on('click touchstart', function (e) { 759 e.preventDefault(); 760 $rlcGameContainer.show(); 761 $('#rlcGameMinimize-' + instance).hide(); 762 }); 763 764 $('#rlcLinkMinimize-' + instance).on('click touchstart', function (e) { 765 e.preventDefault(); 766 $rlcGameContainer.hide(); 767 $('#rlcGameMinimize-' + instance) 768 .css('visibility', 'visible') 769 .show(); 770 }); 771 772 $('#rlcCubierta-' + instance).hide(); 773 $('#rlcCodeAccessDiv-' + instance).hide(); 774 775 $('#rlcCodeAccessButton-' + instance).on( 776 'click touchstart', 777 function (e) { 778 e.preventDefault(); 779 $eXeRelaciona.enterCodeAccess(instance); 780 } 781 ); 782 783 $('#rlcCodeAccessE-' + instance).on('keydown', function (event) { 784 if (event.which == 13 || event.keyCode == 13) { 785 $eXeRelaciona.enterCodeAccess(instance); 786 return false; 787 } 788 return true; 789 }); 790 791 $('#rlcPNumber-' + instance).text(mOptions.realNumberCards); 792 793 $(window).on( 794 'unload.eXeRelaciona beforeunload.eXeRelaciona', 795 function () { 796 if ($eXeRelaciona.mScorm) { 797 $exeDevices.iDevice.gamification.scorm.endScorm( 798 $eXeRelaciona.mScorm 799 ); 800 } 801 } 802 ); 803 804 $('#rlcSendScore-' + instance).click(function (e) { 805 e.preventDefault(); 806 $eXeRelaciona.sendScore(false, instance); 807 $eXeRelaciona.saveEvaluation(instance, true); 808 }); 809 810 $('#rlcClueButton-' + instance).on('click', function (e) { 811 e.preventDefault(); 812 $('#rlcShowClue-' + instance).hide(); 813 $('#rlcCubierta-' + instance).fadeOut(); 814 }); 815
816 $('#rlcPErrors-' + instance).text(mOptions.errors); 817 if (mOptions.author.trim().length > 0 && !mOptions.fullscreen) { 818 $('#rlcAuthorGame-' + instance).html( 819 mOptions.msgs.msgAuthor + ': ' + mOptions.author 820 ); 821 $('#rlcAuthorGame-' + instance).show(); 822 } 823 824 if (mOptions.isScorm > 0) { 825 $exeDevices.iDevice.gamification.scorm.registerActivity(mOptions); 826 } 827 828 $('#rlcStartGame-' + instance).on('click', function (e) { 829 e.preventDefault(); 830 $eXeRelaciona.startGame(instance); 831 }); 832 833 $('#rlcLinkFullScreen-' + instance).on( 834 'click touchstart', 835 function (e) { 836 e.preventDefault(); 837 const element = document.getElementById( 838 'rlcGameContainer-' + instance 839 ); 840 $exeDevices.iDevice.gamification.helpers.toggleFullscreen( 841 element 842 ); 843 } 844 ); 845 846 $exeDevices.iDevice.gamification.report.updateEvaluationIcon( 847 mOptions, 848 this.isInExe 849 ); 850 851 $('#rlcResetButton-' + instance).on('click', function (e) { 852 e.preventDefault(); 853 $eXeRelaciona.reboot(instance); 854 }); 855 856 $('#rlcCheckButton-' + instance).on('click', function (e) { 857 e.preventDefault(); 858 $eXeRelaciona.checkState(instance); 859 }); 860 861 $eXeRelaciona.ajustarCanvas(instance); 862 863 $rlcGameContainer.on( 864 'mouseenter touchstart', 865 '.RLCP-Author', 866 function () { 867 $eXeRelaciona.showAutorImage($(this), instance); 868 } 869 ); 870 871 $rlcGameContainer.on( 872 'mouseleave touchend touchcancel', 873 '.RLCP-Author', 874 function () { 875 $eXeRelaciona.hideAutorImage($(this), instance); 876 } 877 ); 878 879 $rlcGameContainer.on('click touchstart', '.RLCP-TAudio', function () { 880 if (!mOptions.gameStarted || mOptions.gameOver) return; 881 const audio = $(this).data('audio'); 882 if (audio && audio.length > 3) 883 $exeDevices.iDevice.gamification.media.playSound( 884 audio, 885 mOptions 886 ); 887 }); 888 889 $eXeRelaciona.setupEventHandlers(instance); 890 $eXeRelaciona.setupEventHandlersMovil(instance); 891 892 $rlcGameContainer.on('click', '.RLCP-FullLinkImage', function (e) { 893 e.preventDefault(); 894 e.stopPropagation(); 895 const largeImageSrc = $(this).data('url'); 896 if (largeImageSrc && largeImageSrc.length > 3) { 897 $exeDevices.iDevice.gamification.helpers.showFullscreenImage( 898 largeImageSrc, 899 $rlcGameContainer 900 ); 901 } 902 }); 903 $eXeRelaciona.refreshGame(instance); 904 $(`#rlcContainerGame-${instance}`).hide(); 905 if (mOptions.itinerary.showCodeAccess) { 906 $('#rlcMesajeAccesCodeE-' + instance).text( 907 mOptions.itinerary.messageCodeAccess 908 ); 909 $('#rlcCodeAccessDiv-' + instance).show(); 910 $('#rlcShowClue-' + instance).hide(); 911 $('#rlcCubierta-' + instance).show(); 912 } 913 mOptions.gameOver = false; 914 if (mOptions.type < 2 && !mOptions.itinerary.showCodeAccess) { 915 $eXeRelaciona.startGame(instance); 916 } 917 }, 918 919 setFontSize: function (instance) { 920 const $words = $('#rlcGameContainer-' + instance).find('.RLCP-Word'); 921 $words.each(function () { 922 const $card = $(this), 923 $text = $card.find('.RLCP-EText'); 924 $eXeRelaciona.adjustFontSize($text); 925 }); 926 927 const $definition = $('#rlcGameContainer-' + instance).find( 928 '.RLCP-Definition' 929 ); 930 $definition.each(function () { 931 const $card = $(this), 932 $text = $card.find('.RLCP-EText'); 933 $eXeRelaciona.adjustFontSize($text); 934 }); 935 }, 936 937 refreshGame: function (instance) { 938 const mOptions = $eXeRelaciona.options[instance]; 939 if (!mOptions) return; 940 if (mOptions.refreshGame) return; 941 mOptions.refreshGame = true; 942 let element = document.getElementById('rlcGameContainer-' + instance); 943 element = element || document.documentElement; 944 mOptions.fullscreen = !( 945 !document.fullscreenElement && 946 !document.mozFullScreenElement && 947 !document.webkitFullscreenElement && 948 !document.msFullscreenElement 949 ); 950 $eXeRelaciona.setFontSize(instance); 951 $eXeRelaciona.ajustarCanvas(instance); 952 mOptions.refreshGame = false;
953 }, 954 955 removeEventHandlers: function (instance) { 956 const $gameContainer = $(`#rlcGameContainer-${instance}`); 957 $gameContainer.off('mousedown', '.RLCP-Word'); 958 }, 959 960 setupEventHandlers: function (instance) { 961 this.removeEventHandlers(instance); 962 963 const mOptions = $eXeRelaciona.options[instance], 964 dragThreshold = 5, 965 $gameContainer = $(`#rlcContainerGame-${instance}`); 966 let isDragging = false; 967 968 $gameContainer.on('mousedown', '.RLCP-Word', function (e) { 969 e.preventDefault(); 970 if ( 971 typeof mOptions == 'undefined' || 972 !mOptions.gameStarted || 973 mOptions.gameOver 974 ) 975 return; 976 977 const targetWord = $(e.target).closest('.RLCP-Word'), 978 sound = $(this).find('.RLCP-TAudio').data('audio'); 979 980 if (targetWord.hasClass('RLCP-Connected')) { 981 $eXeRelaciona.removeConnection(targetWord, instance); 982 } 983 984 $gameContainer.find('.RLCP-Word').removeClass('RLCP-Selected'); 985 targetWord.addClass('RLCP-Selected'); 986 mOptions.currentWordDiv = targetWord; 987 mOptions.canvas = $(`#rlcCanvas-${instance}`)[0]; 988 mOptions.contexto = mOptions.canvas.getContext('2d'); 989 mOptions.canvasRect = mOptions.canvas.getBoundingClientRect(); 990 991 mOptions.tempEnd = $('<div>') 992 .css({ 993 position: 'absolute', 994 top: e.clientY - mOptions.canvasRect.top, 995 left: e.clientX - mOptions.canvasRect.left, 996 width: 0, 997 height: 0, 998 padding: 0, 999 margin: 0, 1000 'border-radius': '50%', 1001 'background-color': 'red', 1002 'z-index': 9, 1003 }) 1004 .appendTo(`#rlcContainerGame-${instance}`); 1005 1006 mOptions.lineindex = mOptions.currentWordDiv.data('lineindex'); 1007 mOptions.startX = e.clientX - mOptions.canvasRect.left; 1008 mOptions.startY = e.clientY - mOptions.canvasRect.top; 1009 isDragging = false; 1010 1011 if (sound && sound.length > 4) { 1012 $exeDevices.iDevice.gamification.media.playSound( 1013 sound, 1014 mOptions 1015 ); 1016 } 1017 }); 1018 1019 $(document).on('mousemove.eXeRlc' + instance, (e) => { 1020 if ( 1021 typeof mOptions == 'undefined' || 1022 !mOptions.gameStarted || 1023 mOptions.gameOver || 1024 !mOptions.currentWordDiv || 1025 !mOptions.tempEnd 1026 ) 1027 return; 1028 1029 const dx = Math.abs(e.clientX - mOptions.startX), 1030 dy = Math.abs(e.clientY - mOptions.startY); 1031 1032 if (dx > dragThreshold || dy > dragThreshold) { 1033 mOptions.canvasRect = mOptions.canvas.getBoundingClientRect(); 1034 mOptions.tempEnd.css({ 1035 top: e.clientY - mOptions.canvasRect.top, 1036 left: e.clientX - mOptions.canvasRect.left, 1037 }); 1038 // Guardar coords absolutas para el repintado 1039 mOptions._tempX = e.clientX; 1040 mOptions._tempY = e.clientY; 1041 1042 const buffer = 40, 1043 scrollSpeed = 10; 1044 1045 if (e.clientY < buffer) { 1046 window.scrollBy(0, -scrollSpeed); 1047 } else if (window.innerHeight - e.clientY < buffer) { 1048 window.scrollBy(0, scrollSpeed); 1049 } 1050 isDragging = true; 1051 $eXeRelaciona.redibujarLineas(instance, isDragging); 1052 } 1053 }); 1054 1055 $(document).on('mouseup.eXeRlc' + instance, (e) => { 1056 e.preventDefault(); 1057 if ( 1058 typeof mOptions == 'undefined' || 1059 !mOptions.gameStarted || 1060 mOptions.gameOver || 1061 !mOptions.currentWordDiv 1062 ) 1063 return; 1064 1065 const definitionDiv = $(e.target).closest('.RLCP-Definition'), 1066 sound = definitionDiv.find('.RLCP-TAudio').data('audio'); 1067 1068 if ( 1069 definitionDiv.length > 0 && 1070 !definitionDiv.hasClass('RLCP-Connected') 1071 ) { 1072 if ( 1073 !( 1074 mOptions.type === 0 && 1075 mOptions.currentWordDiv.data('id') !== 1076 definitionDiv.data('id') 1077 ) 1078 ) { 1079 if (isDragging || mOptions.currentWordDiv) { 1080 $eXeRelaciona.addOrUpdateLine( 1081 mOptions.lineindex, 1082 mOptions.currentWordDiv, 1083 definitionDiv, 1084 $eXeRelaciona.borderColors.blue, 1085 instance 1086 ); 1087 $eXeRelaciona.checkAllConnected(instance); 1088 mOptions.currentWordDiv = null; 1089 } 1090 } 1091 if (sound && sound.length > 4) { 1092 $exeDevices.iDevice.gamification.media.playSound( 1093 sound, 1094 mOptions 1095 ); 1096 } 1097 } 1098 mOptions._moving = false;
1099 $eXeRelaciona.requestRedraw(instance); 1100 isDragging = false; 1101 if (mOptions.tempEnd) { 1102 mOptions.tempEnd.remove(); 1103 mOptions.tempEnd = null; 1104 } 1105 mOptions._tempX = undefined; 1106 mOptions._tempY = undefined; 1107 }); 1108 }, 1109 1110 showAutorImage: function ($this, instance) { 1111 const mOptions = $eXeRelaciona.options[instance], 1112 author = $this.data('author'), 1113 $rlcAuthorGameSelector = `#rlcAuthorGame-${instance}`; 1114 if (author && author.length > 0) { 1115 $($rlcAuthorGameSelector) 1116 .html(`${mOptions.msgs.msgAuthor}: ${author}`) 1117 .show(); 1118 } 1119 }, 1120 1121 hideAutorImage: function ($this, instance) { 1122 const mOptions = $eXeRelaciona.options[instance], 1123 author = $this.data('author'), 1124 $rlcAuthorGameSelector = `#rlcAuthorGame-${instance}`; 1125 if (mOptions.author && mOptions.author.length > 0) { 1126 $($rlcAuthorGameSelector) 1127 .html(`${mOptions.msgs.msgAuthor}: ${author}`) 1128 .show(); 1129 } else { 1130 $($rlcAuthorGameSelector).hide(); 1131 } 1132 }, 1133 1134 removeEventHandlersMovil: function (instance) { 1135 const containerGame = document.querySelector( 1136 `#rlcGameContainer-${instance}` 1137 ); 1138 1139 if (containerGame) { 1140 containerGame.removeEventListener( 1141 'touchstart', 1142 this.touchStartHandler, 1143 { passive: false } 1144 ); 1145 containerGame.removeEventListener( 1146 'touchmove', 1147 this.touchMoveHandler, 1148 { passive: false } 1149 ); 1150 containerGame.removeEventListener( 1151 'touchend', 1152 this.touchEndHandler, 1153 { passive: false } 1154 ); 1155 } 1156 }, 1157 1158 setupEventHandlersMovil: function (instance) { 1159 $eXeRelaciona.removeEventHandlersMovil(instance); 1160 1161 const mOptions = $eXeRelaciona.options[instance], 1162 $gameContainer = $(`#rlcGameContainer-${instance}`); 1163 1164 let isDragging = false, 1165 dragThreshold = 5; 1166 1167 this.touchStartHandler = function (e) { 1168 e.preventDefault(); 1169 if ( 1170 !mOptions.gameStarted || 1171 mOptions.gameOver || 1172 !e.touches || 1173 e.touches.length === 0 1174 ) { 1175 return; 1176 } 1177 const touch = e.touches[0], 1178 target = touch.target, 1179 touchedWord = $(target).closest('.RLCP-Word'); 1180 1181 if (touchedWord.length === 0) return; 1182 1183 mOptions.targetWord = touchedWord; 1184 1185 if (mOptions.targetWord.hasClass('RLCP-Connected')) { 1186 $eXeRelaciona.removeConnection(mOptions.targetWord, instance); 1187 } 1188 1189 $gameContainer.find('.RLCP-Word').removeClass('RLCP-Selected'); 1190 mOptions.targetWord.addClass('RLCP-Selected'); 1191 mOptions.currentWordDiv = mOptions.targetWord; 1192 mOptions.canvas = document.getElementById(`rlcCanvas-${instance}`); 1193 mOptions.contexto = mOptions.canvas.getContext('2d'); 1194 mOptions.canvasRect = mOptions.canvas.getBoundingClientRect(); 1195 mOptions.tempEnd = $('<div>') 1196 .css({ 1197 position: 'absolute', 1198 top: touch.clientY - mOptions.canvasRect.top, 1199 left: touch.clientX - mOptions.canvasRect.left, 1200 width: 10, 1201 height: 10, 1202 padding: 0, 1203 margin: 0, 1204 'border-radius': '50%', 1205 'background-color': 'red', 1206 'z-index': 9, 1207 }) 1208 .appendTo(`#rlcContainerGame-${instance}`); 1209 mOptions.lineindex = mOptions.currentWordDiv.data('lineindex'); 1210 mOptions.startX = touch.clientX; 1211 mOptions.startY = touch.clientY; 1212 isDragging = false; 1213 }; 1214 1215 this.touchMoveHandler = function (e) { 1216 if ( 1217 !mOptions.gameStarted || 1218 mOptions.gameOver || 1219 !mOptions.currentWordDiv || 1220 !mOptions.tempEnd || 1221 !e.touches || 1222 e.touches.length === 0 1223 ) { 1224 return; 1225 } 1226 const touch = e.touches[0], 1227 dx = Math.abs(touch.clientX - mOptions.startX), 1228 dy = Math.abs(touch.clientY - mOptions.startY); 1229 1230 if (dx > dragThreshold || dy > dragThreshold) { 1231 mOptions.tempEnd.css({ 1232 top: touch.clientY - mOptions.canvasRect.top, 1233 left: touch.clientX - mOptions.canvasRect.left, 1234 }); 1235 mOptions._tempX = touch.clientX; 1236 mOptions._tempY = touch.clientY; 1237 const buffer = 40, 1238 scrollSpeed = 10; 1239 if (touch.clientY < buffer) { 1240 window.scrollBy(0, -scrollSpeed); 1241 } else if (window.innerHeight - touch.clientY < buffer) { 1242 window.scrollBy(0, scrollSpeed); 1243 } 1244 isDragging = true; 1245 $eXeRelaciona.redibujarLineas(instance, isDragging); 1246 } 1247 }; 1248 1249 this.touchEndHandler = function (e) { 1250 e.preventDefault(); 1251 if ( 1252 !mOptions.gameStarted || 1253 mOptions.gameOver || 1254 !e.changedTouches || 1255 e.changedTouches.length === 0 || 1256 !mOptions.currentWordDiv 1257 ) { 1258 return; 1259 } 1260 const touch = e.changedTouches[0], 1261 definitionDiv = $( 1262 document.elementFromPoint(touch.clientX, touch.clientY) 1263 ).closest('.RLCP-Definition'); 1264 1265 if ( 1266 definitionDiv.length > 0 && 1267 !definitionDiv.hasClass('RLCP-Connected') 1268 ) { 1269 if ( 1270 !( 1271 mOptions.type === 0 && 1272 mOptions.currentWordDiv.data('id') !== 1273 definitionDiv.data('id') 1274 ) 1275 ) { 1276 if (isDragging || mOptions.currentWordDiv) { 1277 $eXeRelaciona.addOrUpdateLine( 1278 mOptions.lineindex, 1279 mOptions.currentWordDiv, 1280 definitionDiv, 1281 $eXeRelaciona.borderColors.blue, 1282 instance 1283 ); 1284 $eXeRelaciona.checkAllConnected(instance); 1285 mOptions.currentWordDiv = null; 1286 } 1287 } 1288 } 1289 mOptions._moving = false;
1290 $eXeRelaciona.requestRedraw(instance); 1291 isDragging = false; 1292 if (mOptions.tempEnd) { 1293 mOptions.tempEnd.remove(); 1294 mOptions.tempEnd = null; 1295 } 1296 mOptions._tempX = undefined; 1297 mOptions._tempY = undefined; 1298 }; 1299 1300 const containerGame = document.querySelector( 1301 `#rlcGameContainer-${instance}` 1302 ); 1303 if (containerGame) { 1304 containerGame.addEventListener( 1305 'touchstart', 1306 this.touchStartHandler, 1307 { passive: false } 1308 ); 1309 containerGame.addEventListener('touchmove', this.touchMoveHandler, { 1310 passive: false, 1311 }); 1312 containerGame.addEventListener('touchend', this.touchEndHandler, { 1313 passive: false, 1314 }); 1315 } 1316 }, 1317 1318 removeLine: function (lineindex, instance) { 1319 const mOptions = $eXeRelaciona.options[instance]; 1320 mOptions.linesMap.delete(lineindex); 1321 }, 1322 1323 updateColorLine: function (lineindex, color, instance) { 1324 const mOptions = $eXeRelaciona.options[instance]; 1325 let line; 1326 if (mOptions.linesMap.has(lineindex)) { 1327 line = mOptions.linesMap.get(lineindex); 1328 line.color = color; 1329 } 1330 }, 1331 1332 dibujaLineaTemporal: function (instance) { 1333 const mOptions = $eXeRelaciona.options[instance], 1334 startRect = mOptions.currentWordDiv[0].getBoundingClientRect(), 1335 endRect = mOptions.tempEnd[0].getBoundingClientRect(), 1336 x1 = startRect.right - mOptions.canvasRect.left, 1337 y1 = startRect.top + startRect.height / 2 - mOptions.canvasRect.top, 1338 x2 = endRect.left - mOptions.canvasRect.left, 1339 y2 = endRect.top + endRect.height / 2 - mOptions.canvasRect.top; 1340 1341 $eXeRelaciona.dibujaLineaCurva( 1342 mOptions.contexto, 1343 x1, 1344 y1, 1345 x2, 1346 y2, 1347 $eXeRelaciona.borderColors.blue 1348 ); 1349 }, 1350 1351 addOrUpdateLine: function (lineindex, $word, $definition, color, instance) { 1352 const mOptions = $eXeRelaciona.options[instance]; 1353 let line, 1354 correct = false; 1355 1356 if ($word && $definition) { 1357 correct = $word.data('id') === $definition.data('id'); 1358 } 1359 1360 $word.removeClass('RLCP-Selected'); 1361 $definition.data('lineindex', lineindex).addClass('RLCP-Connected'); 1362 $word.addClass('RLCP-Connected'); 1363 1364 if (mOptions.linesMap.has(lineindex)) { 1365 line = mOptions.linesMap.get(lineindex); 1366 Object.assign(line, { 1367 start: $word, 1368 end: $definition, 1369 correct: correct, 1370 color: color, 1371 }); 1372 } else { 1373 line = { 1374 start: $word, 1375 end: $definition, 1376 color: color, 1377 correct: correct, 1378 }; 1379 mOptions.linesMap.set(lineindex, line); 1380 } 1381 return line; 1382 }, 1383 1384 adjustFontSize: function ($container) { 1385 const $text = $container.find('.RLCP-ETextDinamyc').eq(0), 1386 minFontSize = 10, 1387 maxFontSize = 22, 1388 widthc = $container.innerWidth(), 1389 heightc = $container.innerHeight(); 1390 1391 let fontSize = maxFontSize; 1392 1393 $text.css('font-size', fontSize + 'px'); 1394 1395 while ( 1396 ($text.outerWidth() > widthc || $text.outerHeight() > heightc) && 1397 fontSize > minFontSize 1398 ) { 1399 fontSize--; 1400 $text.css('font-size', fontSize + 'px'); 1401 } 1402 1403 while ( 1404 $text.outerWidth() < widthc && 1405 $text.outerHeight() < heightc && 1406 fontSize < maxFontSize 1407 ) { 1408 fontSize++; 1409 $text.css('font-size', fontSize + 'px'); 1410 1411 if ($text.outerWidth() > widthc || $text.outerHeight() > heightc) { 1412 fontSize--; 1413 $text.css('font-size', fontSize + 'px'); 1414 break; 1415 } 1416 } 1417 }, 1418 1419 getNumberCards: function (instance) { 1420 const mOptions = $eXeOrdena.options[instance]; 1421 return mOptions.cardsGame[mOptions.active].cards.length; 1422 }, 1423 1424 ajustarCanvas: function (instance) { 1425 const mOptions = $eXeRelaciona.options[instance], 1426 $contenedor = $(`#rlcContainerGame-${instance}`), 1427 $canvas = $(`#rlcCanvas-${instance}`); 1428 1429 const cssW = $contenedor.width(); 1430 const cssH = $contenedor.height(); 1431 const dpr = Math.max(window.devicePixelRatio || 1, 1); 1432 mOptions._dpr = dpr; 1433 mOptions._cssW = cssW;
1434 mOptions._cssH = cssH; 1435 1436 $canvas.attr('width', Math.round(cssW * dpr)); 1437 $canvas.attr('height', Math.round(cssH * dpr)); 1438 $canvas.css({ width: cssW + 'px', height: cssH + 'px' }); 1439 1440 mOptions.canvas = $canvas[0]; 1441 1442 if (mOptions.canvas) { 1443 mOptions.contexto = mOptions.canvas.getContext('2d'); 1444 mOptions.contexto.setTransform(dpr, 0, 0, dpr, 0, 0); 1445 mOptions.canvasRect = mOptions.canvas.getBoundingClientRect(); 1446 $eXeRelaciona.requestRedraw(instance); 1447 } 1448 }, 1449 1450 redibujarLineas: function (instance, isMoving) { 1451 const mOptions = $eXeRelaciona.options[instance]; 1452 if (!mOptions) return; 1453 mOptions._moving = !!isMoving; 1454 $eXeRelaciona.requestRedraw(instance); 1455 }, 1456 1457 dibujaLineaCurva: function (contexto, x0, y0, x1, y1, color) { 1458 const p0 = { x: x0, y: y0 }, 1459 p1 = { x: x1, y: y1 }, 1460 dx = p1.x - p0.x, 1461 dy = p1.y - p0.y, 1462 desplazamiento = Math.min(100, Math.abs(dx) / 2), 1463 pc1 = { x: p0.x + desplazamiento, y: p0.y }, 1464 pc2 = { x: p1.x - desplazamiento, y: p1.y }, 1465 tangente = { x: 3 * (p1.x - pc2.x), y: 3 * (p1.y - pc2.y) }, 1466 angulo = Math.atan2(tangente.y, tangente.x); 1467 1468 contexto.beginPath(); 1469 contexto.strokeStyle = color; 1470 contexto.moveTo(p0.x, p0.y); 1471 contexto.bezierCurveTo(pc1.x, pc1.y, pc2.x, pc2.y, p1.x, p1.y); 1472 contexto.stroke(); 1473 $eXeRelaciona.dibujaPuntaFlecha(contexto, p1, angulo, color); 1474 }, 1475 1476 dibujaPuntaFlecha: function (contexto, punto, angulo, color) { 1477 const tamañoFlecha = $eXeRelaciona.isMobile() ? 10 : 12, 1478 anguloFlecha = Math.PI / 6; 1479 1480 contexto.fillStyle = color; 1481 contexto.beginPath(); 1482 contexto.moveTo(punto.x, punto.y); 1483 1484 contexto.lineTo( 1485 punto.x - tamañoFlecha * Math.cos(angulo - anguloFlecha), 1486 punto.y - tamañoFlecha * Math.sin(angulo - anguloFlecha) 1487 ); 1488 1489 contexto.lineTo( 1490 punto.x - tamañoFlecha * Math.cos(angulo + anguloFlecha), 1491 punto.y - tamañoFlecha * Math.sin(angulo + anguloFlecha) 1492 ); 1493 1494 contexto.closePath(); 1495 contexto.fill(); 1496 }, 1497 1498 removeConnection: function (element, instance) {
1499 const lineId = element.data('lineindex'); 1500 1501 if (lineId !== undefined) { 1502 $eXeRelaciona.removeLine(lineId, instance); 1503 $(`#rlcContainerWords-${instance} .RLCP-Word`).each(function () { 1504 if ($(this).data('lineindex') === lineId) { 1505 $(this).removeClass('RLCP-Connected'); 1506 } 1507 }); 1508 1509 $(`#rlcContainerDefinitions-${instance} .RLCP-Definition`).each( 1510 function () { 1511 if ($(this).data('lineindex') === lineId) { 1512 $(this) 1513 .removeClass('RLCP-Connected') 1514 .data('lineindex', 0); 1515 } 1516 } 1517 ); 1518 } 1519 $eXeRelaciona.redibujarLineas(instance, false); 1520 }, 1521 1522 checkAllConnected: function (instance) { 1523 const mOptions = $eXeRelaciona.options[instance], 1524 $constainerGame = $(`#rlcGameContainer-${instance}`); 1525 1526 $(`#rlcCheckButton-${instance}`).hide(); 1527 $(`#rlcResetButton-${instance}`).hide(); 1528 $(`#rlcButtons-${instance}`).css('display', 'flex'); 1529 1530 if ( 1531 $constainerGame.find('.RLCP-Word.RLCP-Connected').length === 1532 $constainerGame.find('.RLCP-Word').length 1533 ) { 1534 if (mOptions.permitirErrores) { 1535 $(`#rlcCheckButton-${instance}`).show(); 1536 } else { 1537 $eXeRelaciona.checkState(instance); 1538 } 1539 } 1540 }, 1541 1542 isMobile: function () { 1543 return /Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i.test( 1544 navigator.userAgent 1545 ); 1546 }, 1547 1548 enterCodeAccess: function (instance) { 1549 const mOptions = $eXeRelaciona.options[instance], 1550 codeInput = $(`#rlcCodeAccessE-${instance}`).val().toLowerCase(), 1551 codeAccess = mOptions.itinerary.codeAccess.toLowerCase(); 1552 1553 if (codeAccess === codeInput) { 1554 $(`#rlcCodeAccessDiv-${instance}, #rlcCubierta-${instance}`).hide(); 1555 $(`#rlcContainerGame-${instance}`).show(); 1556 $eXeRelaciona.refreshGame(instance); 1557 $eXeRelaciona.startGame(instance); 1558 } else { 1559 $(`#rlcMesajeAccesCodeE-${instance}`) 1560 .fadeOut(300) 1561 .fadeIn(200) 1562 .fadeOut(300) 1563 .fadeIn(200); 1564 $(`#rlcCodeAccessE-${instance}`).val(''); 1565 } 1566 }, 1567 1568 updateScore: function (correctAnswer, instance) { 1569 const mOptions = $eXeRelaciona.options[instance]; 1570 let obtainedPoints = 0; 1571 1572 if (correctAnswer) { 1573 mOptions.hits++; 1574 obtainedPoints = 10 / mOptions.realNumberCards; 1575 } else { 1576 mOptions.errors++; 1577 } 1578 mOptions.score = Math.max(mOptions.score + obtainedPoints, 0); 1579 const sscore = mOptions.score.toFixed(2); 1580 1581 $(`#rlcPScore-${instance}`).text(sscore); 1582 $(`#rlcPHits-${instance}`).text(mOptions.hits); 1583 $(`#rlcPErrors-${instance}`).text(mOptions.errors); 1584 $(`#rlcPNumber-${instance}`).text( 1585 mOptions.realNumberCards - mOptions.hits - mOptions.errors 1586 ); 1587 }, 1588 1589 showMessage: function (type, message, instance) { 1590 const colors = [ 1591 '#555555', 1592 $eXeRelaciona.borderColors.red, 1593 $eXeRelaciona.borderColors.green, 1594 $eXeRelaciona.borderColors.blue, 1595 $eXeRelaciona.borderColors.yellow, 1596 ], 1597 color = colors[type], 1598 $rlcMessage = $(`#rlcMessage-${instance}`); 1599 $rlcMessage 1600 .html(message) 1601 .css({ color: color, 'font-style': 'bold' }) 1602 .show(); 1603 }, 1604 1605 saveEvaluation: function (instance) { 1606 const mOptions = $eXeRelaciona.options[instance]; 1607 ((mOptions.scorerp = (mOptions.hits * 10) / mOptions.realNumberCards), 1608 $exeDevices.iDevice.gamification.report.saveEvaluation( 1609 mOptions, 1610 $eXeRelaciona.isInExe 1611 )); 1612 }, 1613 1614 sendScore: function (auto, instance) { 1615 const mOptions = $eXeRelaciona.options[instance]; 1616 1617 ((mOptions.scorerp = score = 1618 (mOptions.hits * 10) / mOptions.realNumberCards), 1619 (mOptions.previousScore = $eXeRelaciona.previousScore)); 1620 mOptions.userName = $eXeRelaciona.userName; 1621 1622 $exeDevices.iDevice.gamification.scorm.sendScoreNew(auto, mOptions); 1623 1624 $eXeRelaciona.previousScore = mOptions.previousScore; 1625 }, 1626}; 1627$(function () { 1628 $eXeRelaciona.init(); 1629});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.