Fix most glaring bugs and vulnerabilities

This commit is contained in:
Luka Romih
2023-05-08 23:25:25 +02:00
parent 9867875ef2
commit d0e53fee3b
144 changed files with 2231 additions and 2981 deletions
@@ -0,0 +1,5 @@
<svg width="98" height="98" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 1C18.1 1 23 5.9 23 12C23 18.1 18.1 23 12 23C5.9 23 1 18.1 1 12C1 5.9 5.9 1 12 1ZM12 21C17 21 21 17 21 12C21 7 17 3 12 3C7 3 3 7 3 12C3 17 7 21 12 21Z" fill="#057ED1"/>
<path d="M12 11C12.6 11 13 11.4 13 12L13 16C13 16.6 12.6 17 12 17C11.4 17 11 16.6 11 16L11 12C11 11.4 11.4 11 12 11Z" fill="#057ED1"/>
<path d="M12 7C12.3 7 12.5 7.1 12.7 7.3C12.9 7.5 13 7.7 13 8C13 8.1 13 8.3 12.9 8.4C12.8 8.5 12.8 8.6 12.7 8.7C12.4 9 12 9.1 11.6 8.9C11.5 8.9 11.5 8.9 11.4 8.8C11.4 8.8 11.3 8.7 11.2 8.7C11.1 8.6 11 8.5 11 8.4C11 8.3 11 8.1 11 8C11 7.9 11 7.7 11.1 7.6C11.2 7.5 11.2 7.4 11.3 7.3C11.5 7.1 11.7 7 12 7Z" fill="#057ED1"/>
</svg>

After

Width:  |  Height:  |  Size: 739 B

+136 -73
View File
@@ -1,4 +1,4 @@
/* global $, axios, bootstrap, currentPagePath, initPagination, removeAllChildNodes, unsavedData, replaceContainer, i18next */
/* global $, axios, bootstrap, currentPagePath, initPagination, removeAllChildNodes, unsavedData, replaceContainer, isI18nReady, i18next, validator */
// const currentPagePath = location.pathname
@@ -144,62 +144,64 @@ function initAdmin() {
}
if (currentPagePath === '/admin/uporabniki/seznam') {
const resultsListEl = document.getElementById('page-results')
isI18nReady.then(t => {
const resultsListEl = document.getElementById('page-results')
const updatePager = initPagination('pagination', onPageChange)
const updatePager = initPagination('pagination', onPageChange)
async function onPageChange(newPage) {
try {
const { page, numberOfAllPages, results } = await getDataForPage(
newPage
)
removeAllChildNodes(resultsListEl)
renderResults(results)
updatePager(page, numberOfAllPages)
} catch (error) {
let message = i18next.t('Prišlo je do napake.')
if (error.response?.data) {
message = error.response.data
} else if (error.request) {
message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.')
async function onPageChange(newPage) {
try {
const { page, numberOfAllPages, results } = await getDataForPage(
newPage
)
removeAllChildNodes(resultsListEl)
renderResults(results)
updatePager(page, numberOfAllPages)
} catch (error) {
let message = i18next.t('Prišlo je do napake.')
if (error.response?.data) {
message = error.response.data
} else if (error.request) {
message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.')
}
alert(message)
updatePager()
}
alert(message)
updatePager()
}
}
async function getDataForPage(page) {
const url = `/api/v1/users/listAllUsers?p=${page}`
const { data } = await axios.get(url)
return data
}
async function getDataForPage(page) {
const url = `/api/v1/users/listAllUsers?p=${page}`
const { data } = await axios.get(url)
return data
}
function renderResults(results) {
results.forEach(result => {
const rowEl = document.createElement('tr')
const td1 = document.createElement('td')
const td2 = document.createElement('td')
const td3 = document.createElement('td')
const td4 = document.createElement('td')
const aEl = document.createElement('a')
const imgEl = document.createElement('img')
const spanEl = document.createElement('span')
td1.textContent = result.userName
td2.textContent = result.email
td3.textContent = result.status
aEl.classList.add('image-link')
aEl.type = 'link'
aEl.href = `/admin/uporabniki/${result.id}/urejanje`
imgEl.src = '/images/u_edit-alt.svg'
imgEl.alt = i18next.t('Uredi')
spanEl.className = 'normal-gray ms-1'
spanEl.textContent = i18next.t('Uredi')
td4.append(aEl)
aEl.append(imgEl, spanEl)
rowEl.append(td1, td2, td3, td4)
resultsListEl.appendChild(rowEl)
})
}
function renderResults(results) {
results.forEach(result => {
const rowEl = document.createElement('tr')
const td1 = document.createElement('td')
const td2 = document.createElement('td')
const td3 = document.createElement('td')
const td4 = document.createElement('td')
const aEl = document.createElement('a')
const imgEl = document.createElement('img')
const spanEl = document.createElement('span')
td1.textContent = result.userName
td2.textContent = result.email
td3.textContent = t(`userStatus${result.status}`)
aEl.classList.add('image-link')
aEl.type = 'link'
aEl.href = `/admin/uporabniki/${result.id}/urejanje`
imgEl.src = '/images/u_edit-alt.svg'
imgEl.alt = i18next.t('Uredi')
spanEl.className = 'normal-gray ms-1'
spanEl.textContent = i18next.t('Uredi')
td4.append(aEl)
aEl.append(imgEl, spanEl)
rowEl.append(td1, td2, td3, td4)
resultsListEl.appendChild(rowEl)
})
}
})
}
if (currentPagePath === '/admin/slovarji') {
@@ -280,12 +282,16 @@ function initAdmin() {
}
}
if (/\/admin\/slovarji\/\d+\/podatki/.test(currentPagePath)) {
if (
/\/admin\/slovarji\/\d+\/podatki/.test(currentPagePath) ||
/\/slovarji\/\d+\/podatki/.test(currentPagePath)
) {
const formEl = document.getElementById('admin-description')
const imgTrashIcon = document.querySelectorAll('.delete-author-btn')
const inputNewAuthorEl = document.getElementById('input-new-author')
const inputNewAreaEl = document.getElementById('input-new-area')
const dictSideMenu = document.querySelector('.admin-nav-content')
$('.without-addition').on('change', enableButton)
unsavedData(formEl, dictSideMenu)
formEl.addEventListener('input', enableButton)
if (imgTrashIcon !== null) {
@@ -946,7 +952,7 @@ function mobileMoveContent() {
// primaryButton.style.marginRight = '10px'
primaryButton.style.whiteSpace = 'nowrap'
}
navTitle.textContent = siteHeadingTextContent
if (navTitle) navTitle.textContent = siteHeadingTextContent
siteHeading.style.display = 'none'
}
if (document.body.clientWidth > 1200) {
@@ -966,12 +972,14 @@ function mobileMoveContent() {
primaryButton.style.whiteSpace = ''
}
if (
currentPagePath.includes('slovarji') &&
!currentPagePath.includes('admin')
)
navTitle.textContent = i18next.t('Urejanje')
else navTitle.textContent = i18next.t('Administracija')
if (navTitle) {
if (
currentPagePath.includes('slovarji') &&
!currentPagePath.includes('admin')
)
navTitle.textContent = i18next.t('Urejanje')
else navTitle.textContent = i18next.t('Administracija')
}
siteHeading.style.display = 'block'
}
}
@@ -1180,7 +1188,7 @@ $('.summernote').summernote({
const profileForm = document.getElementById('profileForm')
if (profileForm) {
profileForm.addEventListener('change', e => {
profileForm.addEventListener('input', e => {
enableButton()
})
@@ -1189,12 +1197,30 @@ $('.summernote').summernote({
const data = Object.fromEntries(new FormData(e.target))
const notifyAction = () => {
document.getElementById('fpi-text').innerHTML = i18next.t(
'Izpolnite vsa prazna polja.'
)
$('#reset-pass-info').modal('show')
}
if (data.numberOfHits) {
await handleUpdateHitsPerPage(data.numberOfHits)
}
if (data.name && data.surname) {
await handleUpdateUsersName(data.name, data.surname)
if (data.firstName && data.lastName && data.email) {
await handleUpdateBasicData(data)
} else if (location.pathname === '/moj-racun') {
// if missing the required data on endpoint /moj-racun, notify!
notifyAction()
return
}
if (data.passwordOld && data.passwordNew && data.passwordNewRepeat) {
await handleUpdatePassword(data)
} else if (location.pathname === '/spremeni-geslo') {
// if missing the required data on endpoint /spremeni-geslo, notify!
notifyAction()
}
// console.log(data)
@@ -1204,23 +1230,60 @@ $('.summernote').summernote({
async function handleUpdateHitsPerPage(hitAmount) {
try {
await axios.post('/api/v1/users/hitsPerPage', { hitAmount })
} catch (error) {
// console.log(error)
} finally {
location.reload(true)
} catch (error) {
displayError(error)
}
}
async function handleUpdateUsersName(name, surname) {
async function handleUpdateBasicData(payload) {
try {
await axios.post('/api/v1/users/nameAndSurname', {
name,
surname
})
} catch (error) {
// console.log(error)
} finally {
if (!validator.isEmail(payload.email)) {
document.getElementById('fpi-text').innerHTML =
i18next.t('Neveljavna e-pošta')
$('#reset-pass-info').modal('show')
return
}
await axios.post('/api/v1/users/basic-data', payload)
location.reload(true)
} catch (error) {
displayError(error)
}
}
async function handleUpdatePassword(payload) {
try {
if (payload.passwordNew !== payload.passwordNewRepeat) {
document.getElementById('fpi-text').innerHTML = i18next.t(
'Gesli se ne ujemata'
)
$('#reset-pass-info').modal('show')
return
}
if (!validator.isLength(payload.passwordNew, { min: 8 })) {
document.getElementById('fpi-text').innerHTML =
i18next.t('Geslo je prekratko')
$('#reset-pass-info').modal('show')
return
}
await axios.post('/api/v1/users/password', payload)
location.reload(true)
} catch (error) {
displayError(error)
}
}
function displayError(error) {
let message = i18next.t('Prišlo je do napake.')
if (error.response) {
message = error.response.data
} else if (error.request) {
message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.')
}
document.getElementById('fpi-text').textContent = message
$('#reset-pass-info').modal('show')
}
}
@@ -1,188 +0,0 @@
/* global axios */
// Priporočam uporabo block scopa okoli paginacijske logike za čimvečjo izolacijo.
{
// Referenca na element, kamor se izrisuje seznam rezultatov.
const resultsListEl = document.getElementById('page-results')
// Paginacijo inicializiraš s klicem funkcije initPagination:
// 1. parameter: id pager elementa. V demo-paginacija.pug je to #pagination.
// UPDATE: Sedaj sprejme tudi array idjev, če je pager kontrolerjev več (npr. en zgoraj (#pagination-top), en spodaj (#pagination-bottom)).
// 2. parameter: callback funkcijo, ki jo pager pokliče vsakič, ko uporabnik zahteva novo stran. Poliče jo s številko zahtevane strani.
// vrnjena vrednost: funkcija, ki jo (kasneje) kličeš za posodobitev pagerja. Tu jo poimenujem updateDemoPager.
const updateDemoPager = initPagination(
['pagination-top', 'pagination-bottom'],
onPageChange
)
// Za samo en kontroler je bilo:
// const updateDemoPager = initPagination('pagination', onPageChange)
// Fukncija, prejme številko nove strani in naj:
// 1. Pridobi podatke nove strani.
// 2. Izriše seznam elementov te strani.
// 3. Posodobi pager, tako, da kliče funkcijo, ki jo je vrnil klic initPagination (updateDemoPager) z novo stranjo in številom vseh strani.
async function onPageChange(newPage) {
try {
const { page, numberOfAllPages, results } = await getDataForPage(newPage)
removeAllChildNodes(resultsListEl)
renderResults(results)
updateDemoPager(page, numberOfAllPages)
} catch (error) {
let message = 'Prišlo je do napake.'
if (error.response?.data) {
message = error.response.data
} else if (error.request) {
message = 'Strežnik ni dosegljiv. Poskusite kasneje.'
}
alert(message)
updateDemoPager()
}
}
// Primer helper funkcije za pridobitev podatkov želene strani.
async function getDataForPage(page) {
const url = `/api/v1/demo-paginacija/list?p=${page}`
const { data } = await axios.get(url)
return data
}
// Primer helper funkcije za izris seznama novih podatkov.
function renderResults(results) {
results.forEach(result => {
const newListEl = document.createElement('li')
const textNode1 = document.createTextNode('Zanimiva vrednost: ')
const boldedEl = document.createElement('b')
boldedEl.textContent = result.zanimivo
const textNode2 = document.createTextNode(
`. Totalno nezanimivo: ${result.nezanimivo1} in ${result.nezanimivo2}`
)
newListEl.append(textNode1, boldedEl, textNode2)
resultsListEl.appendChild(newListEl)
})
}
}
/** ****************************************************************************************************************************************** **\
* Koda od tu navzdol za vaju ni relevantna. Tu je, da dela zgornja koda. Za realno uporabo sem je skopiral tudi v public/javascripts/scripts.js *
* Na vsaki strani, kjer bo paginacija, jo inicializiraj in uporabljaj po zgledu zgornje kode. *
\** ****************************************************************************************************************************************** **/
function initPagination(paginationRootElIds, onPageChange, currentPage = 1) {
let reqLock = false
let numOfAllPages
const backwardBtnEls = []
const forwardBtnEls = []
const pageInputEls = []
const pagesCountDisplayEls = []
if (Array.isArray(paginationRootElIds)) {
paginationRootElIds.forEach(id => initControls(id))
} else {
initControls(paginationRootElIds)
}
const allControlEls = [...backwardBtnEls, ...forwardBtnEls, ...pageInputEls]
function initControls(paginationRootElId) {
const rootEl = document.getElementById(paginationRootElId)
const btnFirstPage = rootEl.querySelector('.first-page')
const btnPreviousPage = rootEl.querySelector('.previous-page')
const btnNextPage = rootEl.querySelector('.next-page')
const btnLastPage = rootEl.querySelector('.last-page')
const formEl = rootEl.querySelector('form')
const pageInputEl = formEl.querySelector('input')
const pagesCountDisplayEl = formEl.querySelector('.pages-total')
numOfAllPages = +pagesCountDisplayEl.textContent
backwardBtnEls.push(btnFirstPage, btnPreviousPage)
forwardBtnEls.push(btnNextPage, btnLastPage)
pageInputEls.push(pageInputEl)
pagesCountDisplayEls.push(pagesCountDisplayEl)
rootEl.addEventListener('click', e => {
handleButtonClick(e, paginationRootElId)
})
formEl.addEventListener('submit', e => handleFormSubmit(e, pageInputEl))
}
function handleButtonClick({ target }, paginationRootElId) {
if (reqLock) return
const buttonEl = target.closest(`#${paginationRootElId} button`)
if (!buttonEl) return
if (buttonEl.classList.contains('first-page')) {
if (currentPage === 1) return
enableLock()
onPageChange(1)
} else if (buttonEl.classList.contains('previous-page')) {
if (currentPage === 1) return
enableLock()
onPageChange(currentPage - 1)
} else if (buttonEl.classList.contains('next-page')) {
if (currentPage === numOfAllPages) return
enableLock()
onPageChange(currentPage + 1)
} else if (buttonEl.classList.contains('last-page')) {
if (currentPage === numOfAllPages) return
enableLock()
onPageChange(numOfAllPages)
}
}
function handleFormSubmit(e, pageInputEl) {
e.preventDefault()
if (reqLock) return
const inputValue = +pageInputEl.value
if (!(inputValue > 0 && inputValue <= numOfAllPages)) {
alert('Nepravilna vrednost strani')
pageInputEl.value = currentPage
return
}
enableLock()
onPageChange(inputValue)
}
function enableLock() {
reqLock = true
allControlEls.forEach(el => (el.disabled = true))
}
function disableLock() {
reqLock = false
allControlEls.forEach(el => (el.disabled = false))
}
function updatePagerUi(newCurrentPage, newNumOfAllPages) {
disableLock()
if (!newCurrentPage) return
currentPage = newCurrentPage
pageInputEls.forEach(el => (el.value = newCurrentPage))
pagesCountDisplayEls.forEach(el => (el.textContent = newNumOfAllPages))
if (newCurrentPage === 1) {
backwardBtnEls.forEach(el => (el.disabled = true))
} else {
backwardBtnEls.forEach(el => (el.disabled = false))
}
if (newCurrentPage === newNumOfAllPages) {
forwardBtnEls.forEach(el => (el.disabled = true))
} else {
forwardBtnEls.forEach(el => (el.disabled = false))
}
}
return updatePagerUi
}
// Helper function to easily remove all child nodes. Useful for pagination.
function removeAllChildNodes(parent) {
while (parent.firstChild) {
parent.removeChild(parent.firstChild)
}
}
+21 -12
View File
@@ -845,7 +845,7 @@ function initDictionaries() {
}
}
if (listForeignSynonyms.length) {
if (el.synonym != null)
if (el.synonym != null) {
el.synonym.forEach(element => {
if (element.length) {
// eslint-disable-next-line
@@ -861,6 +861,7 @@ function initDictionaries() {
selectSyn.append(newOption).trigger('change')
}
})
}
}
}
})
@@ -2035,19 +2036,21 @@ function loadPreview(info) {
}
const languageContainers = document.querySelectorAll('.preview-one-language')
const langLine = document.querySelector('.language-line')
const langLine = document.querySelector('.start-line')
if (languageContainers) {
const arrLang = Array.from(languageContainers)
arrLang.forEach(el => {
const arrChildren = Array.from(el.children)
if (arrChildren.filter(e => e.classList.contains('d-none')).length > 2) {
el.classList.add('d-none')
langLine.classList.add('d-none')
} else {
el.classList.remove('d-none')
langLine.classList.remove('d-none')
}
} else el.classList.remove('d-none')
})
if (
arrLang.filter(el => el.classList.contains('d-none')).length >=
arrLang.length
) {
langLine.classList.add('d-none')
} else langLine.classList.remove('d-none')
}
changeCollapsedContent()
}
@@ -2110,10 +2113,10 @@ function changeVersionList(versions) {
// eslint-disable-next-line
new bootstrap.Tooltip(dateLabel, {
title:
i18next.t('Verzija') +
`${el.version} <br>` +
i18next.t('Verzija:') +
` ${el.version} <br>` +
i18next.t('Avtor:') +
`${el.version_author}`
` ${el.version_author}`
})
allDatesEl.append(dateRadio)
allDatesEl.appendChild(dateLabel)
@@ -2133,9 +2136,9 @@ function setLatestVersion(data) {
const tooltip = new bootstrap.Tooltip(latestVersionLabel, {
title:
i18next.t('Verzija:') +
`${data.version} <br>` +
` ${data.version} <br>` +
i18next.t('Avtor:') +
`${data.version_author}`,
` ${data.version_author}`,
customClass: 'dark-gray-tooltip',
html: true,
placement: 'bottom'
@@ -2823,6 +2826,12 @@ function deleteContentData(
linkType.value = 'related'
linkText.value = ''
}
if (listForeignDefinitions) {
const foreignDefinitionsFields = document.querySelectorAll(
'.foreign-definition-el'
)
foreignDefinitionsFields.forEach(el => (el.value = ''))
}
}
function activateMe(term) {
@@ -62,6 +62,10 @@
}
function renderResults(results) {
const tableContainer = document.querySelector(
'.list-terminology-candidates'
)
tableContainer.classList.remove('d-none')
results.forEach(([sequentialCount, candidate]) => {
const rowEl = document.createElement('tr')
const tdId = document.createElement('td')
@@ -197,7 +197,7 @@ function updateFileListEl(fileListEl, { status, fileStats, index }) {
deleteImgEl.alt = ''
const deleteSpanEl = document.createElement('span')
deleteSpanEl.className = 'ms-2'
deleteSpanEl.textContent = 'Briši'
deleteSpanEl.textContent = i18next.t('Briši')
const deleteButtonEl = document.createElement('button')
deleteButtonEl.className = 'p-0 delete-file delete-btn-table'
deleteButtonEl.append(deleteImgEl, deleteSpanEl)
@@ -1,128 +0,0 @@
/* global axios */
const fileUploadForm = document.forms['upload-files']
const fileInputEl = fileUploadForm.querySelector('input[type="file"]')
const messageContainerEl = document.getElementById('messages')
const filesListEl = document.getElementById('files-list')
const extractionId = +fileUploadForm.extractionId.value
let apiEndpointBase
switch (location.pathname.split('/').at(-1)) {
case 'besedila':
apiEndpointBase = `/api/v1/extraction/${extractionId}/documents`
break
case 'stop-termini':
apiEndpointBase = `/api/v1/extraction/${extractionId}/stop-terms`
break
default:
throw Error("apiEndpointBase couldn't be determined")
}
fileInputEl.addEventListener('change', submitFiles)
filesListEl.addEventListener('click', handleFileClick)
async function submitFiles() {
// TODO Lock additional submits for the duration of this function execution?
const MAX_FILE_SIZE = 10 ** 9 // 1 GB
const failedUploads = []
displaySpinner()
for (const file of fileInputEl.files) {
if (file.size > MAX_FILE_SIZE) {
const failedUpload = {
filename: file.name,
message: 'File too large. Must not be over 1 GB.'
}
failedUploads.push(failedUpload)
continue
}
const payload = new FormData()
payload.set(fileInputEl.name, file)
try {
await axios.put(apiEndpointBase, payload)
} catch (error) {
const failedUpload = {
filename: file.name,
message: error.response.data
}
failedUploads.push(failedUpload)
}
}
try {
const { data: files } = await axios.get(apiEndpointBase)
updateFilesList(files)
} catch {
alert('Pri posodobljanju seznama naloženih datotek je prišlo do napake.')
}
fileInputEl.value = ''
displayFailedUploads(failedUploads)
hideSpinner()
}
function displaySpinner() {
const messageEl = document.createElement('li')
messageEl.textContent = 'Spinner on'
messageContainerEl.appendChild(messageEl)
}
function hideSpinner() {
const messageEl = document.createElement('li')
messageEl.textContent = 'Spinner off'
messageContainerEl.appendChild(messageEl)
}
function updateFilesList(files) {
removeAllChildNodes(filesListEl)
files.forEach(({ filename, size, timeModified }) => {
const fileEl = document.createElement('li')
const filenameSpanEl = document.createElement('span')
filenameSpanEl.className = 'filename'
filenameSpanEl.textContent = filename
const formattedDate = new Date(timeModified).toLocaleDateString('sl-SL')
const deleteButtonEl = document.createElement('a')
deleteButtonEl.className = 'delete-file'
deleteButtonEl.href = '#'
deleteButtonEl.textContent = 'BRIŠI'
fileEl.append(
'DATOTEKA - Ime: ',
filenameSpanEl,
`, velikost: ${size}, datum: ${formattedDate} `,
deleteButtonEl
)
filesListEl.appendChild(fileEl)
})
}
function displayFailedUploads(failedUploads) {
failedUploads.forEach(({ filename, message }) => {
const messageEl = document.createElement('li')
messageEl.textContent = `NAPAKA - Ime datoteke: ${filename}, razlog: ${message}`
messageContainerEl.appendChild(messageEl)
})
}
async function handleFileClick(e) {
if (e.target.closest('.delete-file')) {
const fileEl = e.target.closest('li')
const filename = fileEl.querySelector('.filename').textContent
try {
await axios.delete(`${apiEndpointBase}/${filename}`)
fileEl.remove()
} catch {
alert('Pri brisanju datoteke je prišlo do napake.')
}
}
}
// Don't copy this one into final JS. It's already defined in scripts.js
function removeAllChildNodes(parent) {
while (parent.firstChild) {
parent.removeChild(parent.firstChild)
}
}
@@ -1,22 +0,0 @@
/* global axios */
const extractionListEl = document.getElementById('extraction-list')
extractionListEl.addEventListener('click', onListClick)
async function onListClick({ target }) {
if (target.classList.contains('btn-delete')) {
const extractionEl = target.closest('li')
const extractionId = extractionEl.dataset.id
await axios.delete(`/api/v1/extraction/${extractionId}`)
extractionEl.remove()
} else if (target.classList.contains('btn-begin')) {
const extractionEl = target.closest('li')
const extractionId = extractionEl.dataset.id
await axios.put(`/api/v1/extraction/${extractionId}/begin`)
} else if (target.classList.contains('btn-duplicate')) {
const extractionEl = target.closest('li')
const extractionId = extractionEl.dataset.id
await axios.post(`/api/v1/extraction/${extractionId}/duplicate`)
}
}
@@ -1,95 +0,0 @@
/* global $, axios */
$('.pick-multiple').select2()
$('.enter-multiple').select2({
tags: true
})
const editStopTermsLink = document.getElementById('edit-stop-terms')
const searchButton = document.getElementById('search-btn')
const searchResultEl = document.getElementById('search-result')
const messageContainerEl = document.getElementById('messages')
const formEl = document.forms[0]
const extractionId = +location.pathname.split('/').at(-1)
editStopTermsLink.addEventListener('click', saveOssParamsFirst)
searchButton.addEventListener('click', handleSearch)
searchResultEl.addEventListener('click', handleSearchResultsClick)
async function saveOssParamsFirst() {
const payload = new URLSearchParams(new FormData(formEl))
navigator.sendBeacon(
`/api/v1/extraction/${extractionId}/oss-save-params`,
payload
)
}
async function handleSearch() {
displaySpinner()
try {
const { data } = await submitSearch()
displaySearchResults(data)
} catch {
handleSearchError()
}
hideSpinner()
}
function displaySpinner() {
const messageEl = document.createElement('li')
messageEl.textContent = 'Spinner on'
messageContainerEl.appendChild(messageEl)
}
function hideSpinner() {
const messageEl = document.createElement('li')
messageEl.textContent = 'Spinner off'
messageContainerEl.appendChild(messageEl)
}
function handleSearchError() {
const messageEl = document.createElement('li')
messageEl.textContent = 'Notify the user of error that occured during search'
messageContainerEl.appendChild(messageEl)
}
async function submitSearch() {
const payload = new URLSearchParams(new FormData(formEl))
return await axios.put(
`/api/v1/extraction/${extractionId}/oss-search`,
payload
)
}
function displaySearchResults({ documentCount, canSave }) {
removeAllChildNodes(searchResultEl)
searchResultEl.textContent = `Število dokumentov: ${documentCount}`
if (canSave) {
const saveButton = document.createElement('button')
saveButton.id = 'save-params'
saveButton.textContent = 'Shrani'
searchResultEl.append(saveButton)
}
}
function handleSearchResultsClick({ target }) {
if (target.closest('#save-params')) confirmParams()
}
async function confirmParams() {
displaySpinner()
try {
await axios.put(`/api/v1/extraction/${extractionId}/oss-confirm-params`)
location = '../poc'
} catch {
alert('Error saving params')
hideSpinner()
}
}
// Don't copy this one into final JS. It's already defined in scripts.js
function removeAllChildNodes(parent) {
while (parent.firstChild) {
parent.removeChild(parent.firstChild)
}
}
@@ -1,144 +0,0 @@
/* global termCandidates, hitsPerPage, numberOfAllPages */
{
const resultsListEl = document.getElementById('page-results')
const updateDemoPager = initPagination('pagination', onPageChange)
function onPageChange(newPage) {
const results = getDataForPage(newPage)
removeAllChildNodes(resultsListEl)
renderResults(results)
updateDemoPager(newPage, numberOfAllPages)
}
function getDataForPage(page) {
const sliceStart = (page - 1) * hitsPerPage
const sliceEnd = page * hitsPerPage
const onePageOfTermCandidates = termCandidates.slice(sliceStart, sliceEnd)
const data = onePageOfTermCandidates.map((candidate, index) => {
const sequentialCount = sliceStart + index + 1
return [sequentialCount, candidate]
})
return data
}
function renderResults(results) {
results.forEach(([sequentialCount, candidate]) => {
const newListEl = document.createElement('li')
newListEl.textContent = `[${sequentialCount}] ${JSON.stringify(
candidate
)}`
resultsListEl.appendChild(newListEl)
})
}
}
// Below code is copied from scripts.js, so it will already be available on the real page. No need to copy it there also.
function initPagination(paginationRootElId, onPageChange, currentPage = 1) {
const rootEl = document.getElementById(paginationRootElId)
const btnFirstPage = rootEl.querySelector('.first-page')
const btnPreviousPage = rootEl.querySelector('.previous-page')
const btnNextPage = rootEl.querySelector('.next-page')
const btnLastPage = rootEl.querySelector('.last-page')
const formEl = rootEl.querySelector('form')
const pageInputEl = formEl.querySelector('input')
const pagesCountDisplayEl = formEl.querySelector('.pages-total')
let reqLock = false
rootEl.addEventListener('click', handleButtonClick)
formEl.addEventListener('submit', handleFormSubmit)
function handleButtonClick({ target }) {
if (reqLock) return
const buttonEl = target.closest(`#${paginationRootElId} button`)
if (!buttonEl) return
const numOfAllPages = +pagesCountDisplayEl.textContent
if (buttonEl.classList.contains('first-page')) {
if (currentPage === 1) return
enableLock()
onPageChange(1)
} else if (buttonEl.classList.contains('previous-page')) {
if (currentPage === 1) return
enableLock()
onPageChange(currentPage - 1)
} else if (buttonEl.classList.contains('next-page')) {
if (currentPage === numOfAllPages) return
enableLock()
onPageChange(currentPage + 1)
} else if (buttonEl.classList.contains('last-page')) {
if (currentPage === numOfAllPages) return
enableLock()
onPageChange(numOfAllPages)
}
}
function handleFormSubmit(e) {
e.preventDefault()
if (reqLock) return
const inputValue = +pageInputEl.value
if (!(inputValue > 0 && inputValue <= pagesCountDisplayEl.textContent)) {
alert('Nepravilna vrednost strani')
pageInputEl.value = currentPage
return
}
enableLock()
onPageChange(inputValue)
}
function enableLock() {
reqLock = true
btnFirstPage.disabled = true
btnPreviousPage.disabled = true
btnNextPage.disabled = true
btnLastPage.disabled = true
pageInputEl.disabled = true
}
function disableLock() {
reqLock = false
btnFirstPage.disabled = false
btnPreviousPage.disabled = false
btnNextPage.disabled = false
btnLastPage.disabled = false
pageInputEl.disabled = false
}
function updatePagerUi(newCurrentPage, newNumOfAllPages) {
disableLock()
if (!newCurrentPage) return
currentPage = newCurrentPage
pageInputEl.value = newCurrentPage
pagesCountDisplayEl.textContent = newNumOfAllPages
if (newCurrentPage === 1) {
btnFirstPage.disabled = true
btnPreviousPage.disabled = true
} else {
btnFirstPage.disabled = false
btnPreviousPage.disabled = false
}
if (newCurrentPage === newNumOfAllPages) {
btnNextPage.disabled = true
btnLastPage.disabled = true
} else {
btnNextPage.disabled = false
btnLastPage.disabled = false
}
}
return updatePagerUi
}
// Helper function to easily remove all child nodes. Useful for pagination.
function removeAllChildNodes(parent) {
while (parent.firstChild) {
parent.removeChild(parent.firstChild)
}
}
+25 -2
View File
@@ -1,11 +1,34 @@
/* global isI18nReady */
/* global isI18nReady, i18next, $, axios */
isI18nReady.then(t => {
const alertText = document.querySelector('#alert-text')
alertText.textContent = t(
'Ali želite zbrisati ta profil. S tem bodo izbrisani vsi podatki, ki ste jih ustvarili'
'Ali res želite izbrisati svoj račun? S tem boste trajno izgubili dostop do podatkov, ki ste jih ustvarili.'
)
})
const redConfirm = document.querySelector('#modal-use-btn')
redConfirm.style.backgroundColor = '#AC7171'
document.querySelector('#modal-alert-label').style.color = '#AC7171'
redConfirm.addEventListener('click', async () => {
// TODO: Optimize, create a common helper function for example
function displayError(error) {
let message = i18next.t('Prišlo je do napake.')
if (error.response) {
message = error.response.data
} else if (error.request) {
message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.')
}
document.querySelector('#info-text').textContent = message
$('#info-modal').modal('show')
}
try {
await axios.delete('/api/v1/users/current')
window.location = '/'
} catch (error) {
displayError(error)
}
})
+39 -32
View File
@@ -1,59 +1,66 @@
/* global $, axios, i18next */
/* global $, axios, validator, i18next */
// Function to verify password and repeat password
function verifyPassword() {
const password = document.getElementById('reset-password').value
const repeatPassword = document.getElementById('reset-password-repeat').value
if (password !== repeatPassword) {
alert(i18next.t('Gesli se ne ujemata')) // 'Passwords do not match')
// alert(i18next.t('Gesli se ne ujemata')) // 'Passwords do not match')
document.querySelector('#reset-password-error').textContent = i18next.t(
'Gesli se ne ujemata'
)
document.querySelector('#reset-password-error').style.visibility = 'visible'
return false
}
if (!validator.isLength(password, { min: 8 })) {
// alert(i18next.t('Geslo je prekratko')) // 'Passwords do not match')
document.querySelector('#reset-password-error').textContent =
i18next.t('Geslo je prekratko')
document.querySelector('#reset-password-error').style.visibility = 'visible'
return false
}
document.querySelector('#reset-password-error').style.visibility = 'invisible'
return true
}
// Handle submit event
document
.querySelector('#reset-and-redirect')
.addEventListener('submit', event => {
.addEventListener('submit', async event => {
event.preventDefault()
if (verifyPassword()) {
const token = document.getElementById('token').value
const password = document.getElementById('reset-password').value
const repeatPassword = document.getElementById(
const passwordRepeat = document.getElementById(
'reset-password-repeat'
).value
const token = document.getElementById('token').value
// const email = document.getElementById('email').value
// const data = { password, repeatPassword, token, email }
axios
.post('/api/v1/users/reset-passwordPLACEHOLDER_URL', {
try {
await axios.post('/api/v1/users/reset-password-submit', {
token,
password,
repeatPassword,
token
})
.then(response => {
if (response.data.success) {
$('#reset-pass-info').modal('show')
// window.location = '/'
} else {
alert(response.data.message)
}
})
.catch(error => {
// Very unlikely, but can happen
alert(i18next.t('Napaka na strežniku'))
console.log(error)
// DEBUG SUCCESS DUE TO NO ENDPOINT $('#reset-pass-info').modal('show') <<- REMOVE
passwordRepeat
})
window.location = '/'
} catch (error) {
displayError(error)
}
}
})
// Handle cancel event
document.querySelector('#cancel-btn').addEventListener('click', event => {
event.preventDefault()
window.location = '/'
})
function displayError(error) {
let message = i18next.t('Prišlo je do napake.')
if (error.response) {
message = error.response.data
} else if (error.request) {
message = i18next.t('Strežnik ni dosegljiv. Poskusite kasneje.')
}
// Handle redirect on success
document.querySelector('#modal-fp-info-close').addEventListener('click', () => {
window.location = '/'
})
document.querySelector('#fpi-text.normal-gray').textContent = message
$('#reset-pass-info').modal('show')
}
+59 -79
View File
@@ -953,85 +953,45 @@ if (registerSwithcButton) {
$(document).ready(function () {
// TODO refactor wth specific select2
$('.select-search-field').select2({})
initSelect2('.select-domain-field', 'Področje')
initSelect2('.select-src-lang-field', 'Jezik iskanja')
initSelect2('.select-dest-lang-field', 'Ciljni jezik')
initSelect2('.select-dict-field', 'Slovar')
initSelect2('.select-source-field', 'Vir')
isI18nReady.then(t => {
$('.select-search-field').select2({})
$('b[role="presentation"]').hide()
$('.select2-selection__arrow').append(
'<img src="/images/chevron-down-darker.svg" alt="V"></img>'
)
initSelect2('.select-domain-field', t('Področje'))
initSelect2('.select-src-lang-field', t('Jezik iskanja'))
initSelect2('.select-dest-lang-field', t('Ciljni jezik'))
initSelect2('.select-dict-field', t('Slovar'))
initSelect2('.select-source-field', t('Vir'))
$('.select-search-field').on('select2:select', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(e.target.parentNode.children[2])
// const label = e.target.parentNode.children[2]
$('b[role="presentation"]').hide()
$('.select2-selection__arrow').append(
'<img src="/images/chevron-down-darker.svg" alt="V"></img>'
)
// console.log(activeInput)
// console.log(activeInput)
activeInput.addTag(e.params.data._resultId)
activeInput.labelJump()
})
$('.select-search-field').on('select2:select', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(e.target.parentNode.children[2])
// const label = e.target.parentNode.children[2]
$('.select-search-field').on('select2:unselect', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(activeInput)
activeInput.removeTag(e.params.data._resultId)
activeInput.labelJump()
// console.log('DELETED ' + e)
})
// console.log(activeInput)
// console.log(activeInput)
activeInput.addTag(e.params.data._resultId)
activeInput.labelJump()
})
inputs.forEach(e => {
e.input = e.domElement.querySelector('.select2-search__field')
})
})
$('.select-search-field').on('select2:unselect', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(activeInput)
activeInput.removeTag(e.params.data._resultId)
activeInput.labelJump()
// console.log('DELETED ' + e)
})
/* TODO REMOVE OTHER SCRIPTS WHEN YOU FINISH MODULARIZING THINGS THAT COULD BE MODULARIZED */
$(document).ready(function () {
// TODO refactor wth specific select2
$('.select-search-field').select2({})
initSelect2('.select-domain-field', 'Področje')
initSelect2('.select-src-lang-field', 'Jezik iskanja')
initSelect2('.select-dest-lang-field', 'Ciljni jezik')
initSelect2('.select-dict-field', 'Slovar')
initSelect2('.select-source-field', 'Vir')
$('b[role="presentation"]').hide()
$('.select2-selection__arrow').append(
'<img src="/images/chevron-down-darker.svg" alt="V"></img>'
)
$('.select-search-field').on('select2:select', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(e.target.parentNode.children[2])
// const label = e.target.parentNode.children[2]
// console.log(activeInput)
// console.log(activeInput)
activeInput.addTag(e.params.data._resultId)
activeInput.labelJump()
})
$('.select-search-field').on('select2:unselect', function (e) {
selectActiveElementFromInput(e)
// console.log(activeInput)
// console.log(activeInput)
activeInput.removeTag(e.params.data._resultId)
activeInput.labelJump()
// console.log('DELETED ' + e)
})
inputs.forEach(e => {
e.input = e.domElement.querySelector('.select2-search__field')
inputs.forEach(e => {
e.input = e.domElement.querySelector('.select2-search__field')
})
})
})
@@ -1192,7 +1152,7 @@ const forgottenPasswordInvoker = new StateManagerInvoker(stateManager, () => {
}
function apllyDescriptionAccordingToState() {
$('#alert-text').text(stateManager.getState().fpassWindowDescription)
$('#fpi-text').text(stateManager.getState().fpassWindowDescription)
}
closeAllFPRelatedModals()
@@ -1233,8 +1193,21 @@ function onCancelForgotPassword() {
}
function onSendForgottenEmailRequest() {
if (document.getElementById('forgot-pass-input').value === '') {
stateManager.setState({
fpassWindowDescription: i18next.t(
'Prosimo vnesite vaš elektronski naslov.'
)
})
forgottenPasswordInvoker.setState({
fpassWindowState: ForgotPasswordState.FORGOT_PASSWORD_ERROR
})
return
}
axios
.post('/REPLACETHISDUMMYURL', {
.post('/api/v1/users/reset-password-init', {
usernameOrEmail: document.getElementById('forgot-pass-input').value
})
.then(res => {
@@ -1253,11 +1226,18 @@ function onSendForgottenEmailRequest() {
})
})
.catch(err => {
stateManager.setState({
fpassWindowDescription: i18next.t(
'Prišlo je do napake pri pošiljanju sporočila na vaš elektronski naslov. Poskusite ponovno.'
)
})
if (err.response && err.response.data) {
stateManager.setState({
fpassWindowDescription: err.response.data
})
} else {
stateManager.setState({
fpassWindowDescription: i18next.t(
'Prišlo je do napake pri pošiljanju sporočila na vaš elektronski naslov. Poskusite ponovno.'
)
})
}
forgottenPasswordInvoker.setState({
fpassWindowState: ForgotPasswordState.FORGOT_PASSWORD_ERROR
})
+16 -14
View File
@@ -1,7 +1,7 @@
/* global $, axios, currentPagePath, initPagination,
removeAllChildNodes, transferText, tooltipTriggerList,
tooltipList, createTooltip, resetTooltipTriggerList,
transferTextExtended, i18next */
transferTextExtended, isI18nReady, i18next */
// position correction functions
@@ -278,20 +278,22 @@ window.addEventListener('load', () => {
$('[data-toggle="tooltip"]').tooltip()
})
function handleProperTextDisplay() {
// const BROWSER_UNUSUAL_OFFSET = 17
if (/\/iskanje/.test(currentPagePath)) {
transferText('Iskanje po slovarjih', true, 'site-heading') //,
// BROWSER_UNUSUAL_OFFSET
// )
} else if (/\/termin/.test(currentPagePath)) {
transferText('', true, 'site-heading') // , BROWSER_UNUSUAL_OFFSET)
isI18nReady.then(t => {
function handleProperTextDisplay() {
// const BROWSER_UNUSUAL_OFFSET = 17
if (/\/iskanje/.test(currentPagePath)) {
transferText(t('Iskanje po slovarjih'), true, 'site-heading') //,
// BROWSER_UNUSUAL_OFFSET
// )
} else if (/\/termin/.test(currentPagePath)) {
transferText('', true, 'site-heading') // , BROWSER_UNUSUAL_OFFSET)
}
}
}
window.addEventListener('resize', () => {
window.addEventListener('resize', () => {
handleProperTextDisplay()
adjustOffsetBy()
})
handleProperTextDisplay()
adjustOffsetBy()
})
handleProperTextDisplay()
+35 -5
View File
@@ -222,7 +222,7 @@
"Modul za svetovanje pri terminoloških zagatah.": "Terminology consulting module.",
"Modul za urejanje terminoloških slovarjev.": "Module for editing terminology dictionaries.",
"MODULI": "MODULES",
"Moj Profil": "My Profile",
"Moj Račun": "My Account",
"Moji slovarji": "My dictionaries",
"Morebitne že obstoječe poimenovalne rešitve, če obstajajo.": "Any already existing naming solutions.",
"Morebitni primeri rabe termina v besedilih ali povezave do njih, če obstajajo.": "Any examples of term use in texts or links to text where the term is used.",
@@ -234,7 +234,7 @@
"Na tem mestu lahko določite področne oznake, če želite posamezne termine v svojem terminološkem slovarju razvrstiti še podrobneje.": "Here you can define domain labels, if you want to classify individual terms in your terminology dictionary in more detail.",
"Na tem mestu so zbrani vsi komentarji, povezani s terminološkim portalom.": "All comments related to the terminology portal can be found here.",
"Na tem mestu so zbrani vsi komentarji, povezani z izbranim terminološkim slovarjem.": "All comments related to the selected terminology dictionary can be found here.",
"na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili svoj uporabniški račun na Terminološkem portalu. Povezava za potrditev je veljavna ": "we a message with a link to verify your Termonology Portal user account to your e-mail address. The verification link is valid.",
"na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili svoj uporabniški račun na Terminološkem portalu. Povezava za potrditev je veljavna ": "we have sent a message with a link to verify your Termonology Portal user account to your e-mail address. The verification link will be valid ",
"Na vaš elektronski naslov smo vam posredovali povezavo za ponastavitev gesla. Prosimo preverite svoj elektronski predal.": "We sent a password reset link to your e-mail address. Please, check your inbox.",
"Nabor vseh luščenj, s katerimi lahko uporabnik iz izbranih besedil pridobi sezname terminoloških kandidatov. Posamezne terminološke kandidate lahko preveri tudi v konkordančniku.": "All extractions the users can use to obtain a list of term candidates from the selected texts. The user can also check individual term candidates using the concordance tool.",
"Nabor vseh luščenj, s pomočjo katerih uporabnik iz vhodnih besedil pridobi sezname terminoloških kandidatov in dostop do konkordančnika po teh besedilih.": "All extractions the users can use to obtain a list of term candidates from the selected texts and access to the concordance tools for these texts.",
@@ -407,7 +407,7 @@
"POVEZANI TERMIN": "RELATED TERM",
"POVEZANI TERMIN:": "RELATED TERM:",
"POVEZAVA": "LINK",
"Povezava ni (več) veljavna. Prosimo, da ponovno zahtevate ponastavitev gesla.": "The link is (no longer) valid. Please, request new password reset link.",
"Povezava ni (več) veljavna. Prosimo, da ponovno zahtevate ponastavitev gesla.": "This link is invalid or expired. Please, request new password reset link.",
"Povezave": "Links",
"Povezave s portali": "Links to other portals",
"Pozabljeno geslo": "Forgoten password",
@@ -540,7 +540,7 @@
"Strokovni pregled": "Terminology review",
"Strokovno pregledano": "Terminologically reviewed",
"Struktura": "Structure",
"STRUKTURA - ELEMENTI SLOVARSKEGA SESTAVKA:": "STRUCTURE - ELEMENTS OF THA DICTIONARY ENTRY:",
"STRUKTURA - ELEMENTI SLOVARSKEGA SESTAVKA:": "STRUCTURE - ELEMENTS OF THE DICTIONARY ENTRY:",
"Struktura slovarskega sestavka": "The structure of the dictionary entry",
"Svetovalci": "Consultants",
"Svetovalec": "Consultant",
@@ -706,5 +706,35 @@
"ZVOK": "AUDIO",
"ZVOK:": "AUDIO:",
"titleTermsOfUse": "Terms of Use",
"titlePrivacyPolicy": "Privacy Policy"
"titlePrivacyPolicy": "Privacy Policy",
"userStatusregistered": "registered",
"userStatusactive": "active",
"userStatusinactive": "inactive",
"userStatusclosed": "closed",
"Nepravilno uporabniško ime ali elektronski naslov.": "Invalid user name or e-mail address.",
"Ponastavitev gesla": "Password reset",
"Uspešna ponastavitev gesla": "Password successfully reset",
"Vaše geslo je bilo uspešno ponastavljeno.": "Your password has been successfully reset.",
"Prosimo vnesite vaš elektronski naslov.": "Please, enter your e-mail address.",
"Sprememba elektronskega naslova": "Change e-mail address",
"Na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili menjavo elektronskega naslova. Povezava za potrditev je veljavna ": "We have sent a message with a link to confirm the change of your e-mail address to your new e-mail adress. The verification link will be valid ",
"Sprememba elektronskega naslova - uspeh": "E-mail address change successful",
"Uspešno ste spremenili svoj elektronski naslov.": "You have successfully changed your e-mail address.",
"Elektronski naslov uporablja že drug uporabnik.": "This e-mail address is already used by another user.",
"Povezava ni (več) veljavna. Elektronski naslov ni bil spremenjen.": "This link is invalid or expired. The e-mail address has not been changed.",
"Vaš uporabniški račun je bil uspešno izbrisan.": "Your user account has been successfully deleted.",
"Ali res želite izbrisati svoj račun? S tem boste trajno izgubili dostop do podatkov, ki ste jih ustvarili.": "Do you really want to delete your account? If you delete your account, you will permanently lose access to all data you created.",
"Uspešno ste aktivirali svoj uporabniški račun in se prijavili.": "You have successfully activated your user account and signed in.",
"Povezava ni (več) veljavna. Prosimo, da se ponovno registrirate.": "This link is invalid or expired. Please, register again.",
"Nepravilno staro geslo.": "Invalid current password.",
"Sprememba gesla": "Change password",
"Izbrano uporabniško ime uporablja že drug uporabnik.": "This user name is already taken.",
"Izpolnite vsa prazna polja.": "Fill-in all empty fields.",
"Jezik iskanja": "Search language",
"Ciljni jezik": "Target language",
"Spremenjen": "Changed",
"Število slovarskih sestavkov": "Number of dictionary entries",
"VPRAŠANJE": "QUESTION",
"MNENJE": "OPINION",
"ANGLEŠKI PREVOD": "ENGLISH TRANSLATION"
}
+32 -2
View File
@@ -222,7 +222,7 @@
"Modul za svetovanje pri terminoloških zagatah.": "Modul za svetovanje pri terminoloških zagatah.",
"Modul za urejanje terminoloških slovarjev.": "Modul za urejanje terminoloških slovarjev.",
"MODULI": "MODULI",
"Moj Profil": "Moj Profil",
"Moj Račun": "Moj Račun",
"Moji slovarji": "Moji slovarji",
"Morebitne že obstoječe poimenovalne rešitve, če obstajajo.": "Morebitne že obstoječe poimenovalne rešitve, če obstajajo.",
"Morebitni primeri rabe termina v besedilih ali povezave do njih, če obstajajo.": "Morebitni primeri rabe termina v besedilih ali povezave do njih, če obstajajo.",
@@ -706,5 +706,35 @@
"ZVOK": "ZVOK",
"ZVOK:": "ZVOK:",
"titleTermsOfUse": "Pogoji uporabe",
"titlePrivacyPolicy": "Politika zasebnosti"
"titlePrivacyPolicy": "Politika zasebnosti",
"userStatusregistered": "registriran",
"userStatusactive": "aktiven",
"userStatusinactive": "neaktiven",
"userStatusclosed": "zaprt",
"Nepravilno uporabniško ime ali elektronski naslov.": "Nepravilno uporabniško ime ali elektronski naslov.",
"Ponastavitev gesla": "Ponastavitev gesla",
"Uspešna ponastavitev gesla": "Uspešna ponastavitev gesla",
"Vaše geslo je bilo uspešno ponastavljeno.": "Vaše geslo je bilo uspešno ponastavljeno.",
"Prosimo vnesite vaš elektronski naslov.": "Prosimo vnesite vaš elektronski naslov.",
"Sprememba elektronskega naslova": "Sprememba elektronskega naslova",
"Na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili menjavo elektronskega naslova. Povezava za potrditev je veljavna ": "Na vaš elektronski naslov smo vam poslali sporočilo s povezavo, s katero boste potrdili menjavo elektronskega naslova. Povezava za potrditev je veljavna ",
"Sprememba elektronskega naslova - uspeh": "Sprememba elektronskega naslova - uspeh",
"Uspešno ste spremenili svoj elektronski naslov.": "Uspešno ste spremenili svoj elektronski naslov.",
"Elektronski naslov uporablja že drug uporabnik.": "Elektronski naslov uporablja že drug uporabnik.",
"Povezava ni (več) veljavna. Elektronski naslov ni bil spremenjen.": "Povezava ni (več) veljavna. Elektronski naslov ni bil spremenjen.",
"Vaš uporabniški račun je bil uspešno izbrisan.": "Vaš uporabniški račun je bil uspešno izbrisan.",
"Ali res želite izbrisati svoj račun? S tem boste trajno izgubili dostop do podatkov, ki ste jih ustvarili.": "Ali res želite izbrisati svoj račun? S tem boste trajno izgubili dostop do podatkov, ki ste jih ustvarili.",
"Uspešno ste aktivirali svoj uporabniški račun in se prijavili.": "Uspešno ste aktivirali svoj uporabniški račun in se prijavili.",
"Povezava ni (več) veljavna. Prosimo, da se ponovno registrirate.": "Povezava ni (več) veljavna. Prosimo, da se ponovno registrirate.",
"Nepravilno staro geslo.": "Nepravilno staro geslo.",
"Sprememba gesla": "Sprememba gesla",
"Izbrano uporabniško ime uporablja že drug uporabnik.": "Izbrano uporabniško ime uporablja že drug uporabnik.",
"Izpolnite vsa prazna polja.": "Izpolnite vsa prazna polja.",
"Jezik iskanja": "Jezik iskanja",
"Ciljni jezik": "Ciljni jezik",
"Spremenjen": "Spremenjen",
"Število slovarskih sestavkov": "Število slovarskih sestavkov",
"VPRAŠANJE": "VPRAŠANJE",
"MNENJE": "MNENJE",
"ANGLEŠKI PREVOD": "ANGLEŠKI PREVOD"
}