Ir para o conteúdo principal
Universidade Nilton Lins

Acesso a Universidade Nilton Lins

Perdeu a senha?

Esta é a sua primeira vez aqui?

Olá!

Para o acesso aos cursos você precisa ser cadastrado como usuário do site.

Para fazer isto, basta completar o formulário desta página escolhendo um usuário e uma senha que você queira usar.

Se alguém já tiver escolhido o mesmo usuário, você vai tentar novamente escolhendo um usuário diferente.

Alguns cursos podem permitir o acesso a visitantes

Português - Brasil ‎(pt_br)‎
English ‎(en)‎ Português - Brasil ‎(pt_br)‎
Você ainda não se identificou.
Resumo de retenção de dados
Baixar o aplicativo móvel.
* * Novidades v2: * - Detecta páginas de módulo (diary, page, forum, quiz, wiki, assign, etc.) * - Registra o tempo ativo em cada ferramenta/recurso com duration * - YouTube: rastreia tempo de interação com iframes embarcados * - sendBeacon no beforeunload para garantir envio ao sair da página */ (function () { // ── Configuração ──────────────────────────────────────────────────────────── const API_URL = 'https://gestao-unl.manuskrypton.com.br/api/tracking/student'; const SAVE_INTERVAL_MS = 15000; // Sync a cada 15 s const IDLE_TIMEOUT_MS = 2 * 60 * 1000; // Inativo após 2 min sem interação // Labels amigáveis por tipo de módulo Moodle const MOD_LABELS = { diary: 'Diário', page: 'Página Web', forum: 'Fórum', quiz: 'Quiz/Questionário', wiki: 'Wiki', assign: 'Tarefa', resource: 'Arquivo/PDF', book: 'Livro', choice: 'Enquete', workshop: 'Workshop', chat: 'Chat', glossary: 'Glossário', lesson: 'Lição', scorm: 'SCORM', url: 'URL Externa', lti: 'LTI', h5pactivity: 'H5P', feedback: 'Feedback', survey: 'Questionário', data: 'Banco de Dados', label: 'Rótulo', folder: 'Pasta', imscp: 'IMS CP' }; // ── Estado Global ─────────────────────────────────────────────────────────── var sessionTimeSecs = 0; var isActive = true; var lastActivityTime = Date.now(); var maxScrollDepth = 0; var eventsLog = []; // eventos de clique (sem duration) var moodleUser = null; var courseId = null; var isTracking = false; // Recurso atual (se estamos numa página de módulo) var currentResource = null; // { type, title, cmid, url } var resourceTimeSecs = 0; // segundos ativos neste recurso desde o último sync // Acumulador de recursos do YouTube (por iframe) var youtubeTimers = {}; // { iframeTitle: { seconds, title } } // ── 1. Inicialização ──────────────────────────────────────────────────────── function initTracker() { try { courseId = detectCourseId(); if (!courseId || parseInt(courseId) <= 1) { console.warn('[Manuskrypton Tracker] Ignorado: Curso não detectado.'); return; } // Usuário logado var userName = getUserName(); if (!userName) { console.warn('[Manuskrypton Tracker] Ignorado: Usuário não identificado.'); return; } moodleUser = { name: userName, email: userName }; isTracking = true; // Detecta se estamos numa página de módulo específico currentResource = detectModulePage(); resourceTimeSecs = 0; bindEvents(); startTimers(); var ctx = currentResource ? ('módulo ' + currentResource.type + ' "' + currentResource.title + '"') : 'página do curso'; console.log('[Manuskrypton v2] Inicializado com sucesso! Usuário:', userName, '| Curso:', courseId, '|', ctx); } catch (e) { console.error('[Manuskrypton Tracker] Erro init:', e); } } function detectCourseId() { // 1. M.cfg.courseId ou M.cfg.courseid if (window.M && window.M.cfg) { var cid = window.M.cfg.courseId || window.M.cfg.courseid; if (cid && parseInt(cid) > 1) return String(cid); } // 2. URL parameters (?id= ou ?course= ou ?c=) var p = new URLSearchParams(window.location.search); var raw = p.get('id') || p.get('course') || p.get('c'); if (raw && parseInt(raw) > 1) { // Só usa se não estivermos na página do módulo (pois na página do módulo, id é o cmid) if (!window.location.pathname.match(/\/mod\//)) { return String(raw); } } // 3. Classe do body (Moodle costuma adicionar 'course-XXX' ao body) if (document.body) { var bodyClass = document.body.className; var bodyMatch = bodyClass.match(/course-(\d+)/); if (bodyMatch && parseInt(bodyMatch[1]) > 1) { return bodyMatch[1]; } } // 4. Links do curso nas migalhas de pão ou menu de navegação var courseLinks = document.querySelectorAll('a[href*="/course/view.php?id="], a[href*="/course/view.php?course="]'); for (var i = 0; i < courseLinks.length; i++) { var href = courseLinks[i].href; var match = href.match(/id=(\d+)/) || href.match(/course=(\d+)/); if (match && parseInt(match[1]) > 1) { return match[1]; } } return null; } // ── 2. Detecção de Página de Módulo ───────────────────────────────────────── function detectModulePage() { var path = window.location.pathname; var match = path.match(/\/mod\/([a-z_]+)\//); if (!match) return null; var modType = match[1]; var params = new URLSearchParams(window.location.search); var cmid = params.get('id') || params.get('cmid'); var title = getPageTitle(); return { type: modType, label: MOD_LABELS[modType] || (modType.charAt(0).toUpperCase() + modType.slice(1)), title: title, cmid: cmid, url: window.location.href }; } function getPageTitle() { // Tenta várias formas de obter o título da página/módulo var candidates = [ document.querySelector('.page-header-headings h1'), document.querySelector('.activity-name h1'), document.querySelector('h1.h2'), document.querySelector('#region-main h1'), document.querySelector('h1') ]; for (var i = 0; i < candidates.length; i++) { if (candidates[i]) { return candidates[i].textContent.trim().substring(0, 80); } } return document.title.replace(/\|.*$/, '').trim().substring(0, 80) || 'Recurso'; } function getUserName() { // Ordem de preferência para obter o nome do usuário var selectors = [ '.usertext', '.userbutton .usertext', '.logininfo a', '[data-region="user-menu"] .username', '.usermenu .usertext', '[data-login] .usertext', 'nav .usertext', // Temas Boost e variantes '.navbar .usertext', '[data-region="action-menu-trigger"] .usertext', '.user-menu .usertext', '.nav-link.dropdown-toggle .usertext', // Fullname em qualquer elemento visível '[data-fullname]', '.profile-user-box h4', '.userfullname', // Avatar title 'a[title][href*="profile"]' ]; for (var i = 0; i < selectors.length; i++) { var el = document.querySelector(selectors[i]); if (el) { var name = (el.getAttribute('data-fullname') || el.getAttribute('title') || el.textContent || '').trim(); if (name && !isGuestUser(name)) return name; } } // Fallback via M.cfg (userid é mais estável que sesskey) if (window.M && window.M.cfg) { if (window.M.cfg.userid && window.M.cfg.userid > 0) { return 'Usuário_' + window.M.cfg.userid; } if (window.M.cfg.sesskey) { return 'Aluno_' + window.M.cfg.sesskey.substring(0, 6); } } return null; } function isGuestUser(name) { var lower = name.toLowerCase(); return lower.includes('guest') || lower.includes('visitante') || lower === 'você não acessou' || lower.includes('login'); } // ── 3. Eventos de Interatividade ───────────────────────────────────────────── function bindEvents() { var activityEvents = ['mousemove', 'mousedown', 'keydown', 'touchstart', 'scroll', 'click']; activityEvents.forEach(function (evt) { document.addEventListener(evt, handleActivity, { passive: true }); }); document.addEventListener('visibilitychange', function () { if (document.hidden) { isActive = false; } else { resumeTracking(); } }); // Scroll depth window.addEventListener('scroll', function () { var scrollable = document.body.scrollHeight - window.innerHeight; if (scrollable <= 0) return; var pct = Math.min(100, Math.max(0, Math.round((window.scrollY / scrollable) * 100))); if (pct > maxScrollDepth) maxScrollDepth = pct; }, { passive: true }); // Cliques em links (PDFs, YouTube, Sagah, módulos Moodle) document.body.addEventListener('click', handleLinkClick); // Rastrear iframes YouTube na página setupYouTubeTracking(); // Sincronizar ao sair da página (mais confiável) window.addEventListener('beforeunload', function () { syncWithServer(true); }); window.addEventListener('pagehide', function () { syncWithServer(true); }); } function handleLinkClick(e) { var target = e.target.closest('a[href]'); if (!target) return; var href = target.href || ''; var text = (target.textContent || target.title || '').trim().substring(0, 80); // PDF if (/\.pdf(\?|$)/i.test(href)) { logClickEvent('pdf', text || 'Arquivo PDF', null); return; } // YouTube externo if (/youtube\.com|youtu\.be/i.test(href)) { logClickEvent('youtube', text || 'Vídeo YouTube', null); return; } // Sagah / LTI if (/sagah/i.test(href)) { logClickEvent('sagah', text || 'Sagah', null); return; } // Link para módulo Moodle (clique na página do curso) var modMatch = href.match(/\/mod\/([a-z_]+)\/view\.php\?(?:.*&)?id=(\d+)/); if (modMatch) { var modType = modMatch[1]; var cmid = modMatch[2]; logClickEvent(modType, text || MOD_LABELS[modType] || modType, cmid); } } function logClickEvent(type, title, cmid) { // Evita duplicar o mesmo evento em < 5 s var last = eventsLog[eventsLog.length - 1]; if (last && last.type === type && last.cmid === cmid && (Date.now() - last._ts < 5000)) return; var ev = { type: type, title: title, timestamp: Date.now() }; if (cmid) ev.cmid = cmid; ev._ts = Date.now(); // campo interno, removido antes de enviar eventsLog.push(ev); } // ── 4. YouTube Iframes ────────────────────────────────────────────────────── function setupYouTubeTracking() { var iframes = document.querySelectorAll('iframe'); iframes.forEach(function (iframe) { var src = iframe.src || iframe.getAttribute('data-src') || ''; if (!/youtube/i.test(src)) return; var title = getYouTubeTitle(iframe); var key = title; youtubeTimers[key] = { seconds: 0, title: title, active: false }; // Rastrear hover sobre o iframe como proxy de visualização var hoverInterval = null; iframe.addEventListener('mouseenter', function () { youtubeTimers[key].active = true; hoverInterval = setInterval(function () { if (isActive && youtubeTimers[key].active) { youtubeTimers[key].seconds++; } }, 1000); }); iframe.addEventListener('mouseleave', function () { youtubeTimers[key].active = false; if (hoverInterval) { clearInterval(hoverInterval); hoverInterval = null; } }); }); } function getYouTubeTitle(iframe) { // Tenta pegar o título da atividade mais próxima var activity = iframe.closest('.activity, [data-instance-id], .mod-indent'); if (activity) { var name = activity.querySelector('.instancename, .activity-name, h3, h4'); if (name) return name.textContent.trim().replace(/\s+/g, ' ').substring(0, 80); } // Fallback: texto próximo var p = iframe.parentElement; if (p) { var sibling = p.previousElementSibling || p.nextElementSibling; if (sibling) return sibling.textContent.trim().substring(0, 80); } return 'Vídeo YouTube'; } // ── 5. Atividade e Timers ─────────────────────────────────────────────────── function handleActivity() { lastActivityTime = Date.now(); if (!isActive && !document.hidden) resumeTracking(); } function resumeTracking() { isActive = true; lastActivityTime = Date.now(); } function startTimers() { // Relógio de segundos (tempo total + tempo do recurso atual) setInterval(function () { if (!isTracking) return; var now = Date.now(); if (now - lastActivityTime > IDLE_TIMEOUT_MS) isActive = false; if (isActive) { sessionTimeSecs++; if (currentResource) resourceTimeSecs++; } }, 1000); // Sincronização periódica setInterval(function () { syncWithServer(false); }, SAVE_INTERVAL_MS); } // ── 6. Sincronização com Servidor ─────────────────────────────────────────── function syncWithServer(isUnload) { if (!isTracking || sessionTimeSecs === 0) return; var timeToSync = sessionTimeSecs; var resourceTime = resourceTimeSecs; var currentScroll = maxScrollDepth; var eventsToSend = []; // Copia e limpa eventsLog.forEach(function (e) { var clean = { type: e.type, title: e.title, timestamp: e.timestamp }; if (e.cmid) clean.cmid = e.cmid; if (e.duration) clean.duration = e.duration; eventsToSend.push(clean); }); sessionTimeSecs = 0; resourceTimeSecs = 0; eventsLog = []; // Adiciona o tempo acumulado no recurso atual (se estivermos num módulo) if (currentResource && resourceTime > 0) { eventsToSend.push({ type: currentResource.type, title: currentResource.title, cmid: currentResource.cmid, duration: resourceTime, timestamp: Date.now() }); } // Adiciona tempo de vídeos YouTube (hover) Object.keys(youtubeTimers).forEach(function (key) { var yt = youtubeTimers[key]; if (yt.seconds > 0) { eventsToSend.push({ type: 'youtube', title: yt.title, duration: yt.seconds, timestamp: Date.now() }); yt.seconds = 0; } }); var payload = { user: moodleUser, courseId: courseId, timeSeconds: timeToSync, scrollDepth: currentScroll, events: eventsToSend, userAgent: navigator.userAgent, timestamp: new Date().toISOString() }; if (isUnload && navigator.sendBeacon) { // Mais confiável ao fechar/navegar a página try { var blob = new Blob([JSON.stringify(payload)], { type: 'application/json' }); navigator.sendBeacon(API_URL, blob); } catch (_) { // Ignora erros no beacon } return; } // Envio normal async fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }).then(function (res) { if (res.ok) { console.log('[Manuskrypton v2] Sincronização realizada com sucesso. ' + payload.timeSeconds + 's enviados.'); } else { console.warn('[Manuskrypton v2] Servidor retornou erro na sincronização: ' + res.status); } }).catch(function (err) { console.warn('[Manuskrypton] Falha ao enviar. Dados devolvidos ao buffer.', err); sessionTimeSecs += timeToSync; resourceTimeSecs += resourceTime; eventsLog = eventsToSend.concat(eventsLog); }); } // ── Inicializa ────────────────────────────────────────────────────────────── if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initTracker); } else { initTracker(); } })();
Fornecido por Moodle