Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions assets-public/js/Framework/Clipboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { addFlash } from './Flash.js'

const Clipboard = function (element) {
element.addEventListener('click', (event) => {
const elementId = event.currentTarget.getAttribute('data-clipboard-target')

if (elementId === null) {
console.error(
'data-clipboard-target attribute was not provided. This is the element we will be copying from.'
)
return
}

const textSource = document.querySelector(elementId)

if (textSource === null) {
console.error('Source element was not found "' + elementId + '".')
return
}

const text = textSource.value || textSource.innerText || textSource.textSource
const successMessage = event.currentTarget.getAttribute('data-success-message')

if (!navigator.clipboard) {
document.execCommand('copy')

if (successMessage !== null) {
addFlash(successMessage, 'success')
}
} else {
navigator.clipboard.writeText(text).then(
function () {
if (successMessage !== null) {
addFlash(successMessage, 'success')
}
}
)
}
})
}

export default Clipboard
33 changes: 33 additions & 0 deletions assets-public/js/Framework/Cookies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export function isEnabled () {
// try to grab the property
let cookiesEnabled = !!(navigator.cookieEnabled)

// unknown property?
if (typeof navigator.cookieEnabled === 'undefined' && !cookiesEnabled) {
// try to set a cookie
document.cookie = 'testcookie'
cookiesEnabled = document.cookie.includes('testcookie')
}

return cookiesEnabled
}

export function readCookie (name) {
const cookies = document.cookie.split(';')
name = name + '='

for (let cookie of cookies) {
cookie = cookie.trim()
if (cookie.indexOf(name) === 0) {
return cookie.substring(name.length, cookie.length)
}
}

return null
}

export function setCookie (name, value, days = 7) {
const expireDate = new Date()
expireDate.setDate(expireDate.getDate() + days)
document.cookie = name + '=' + value + ';expires=' + expireDate.toUTCString() + ';path=/'
}
42 changes: 42 additions & 0 deletions assets-public/js/Framework/DateTimePicker/DatePicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import flatpickr from 'flatpickr'
// import the most common languages
import { Austria } from 'flatpickr/dist/l10n/at.js'
import { Czech } from 'flatpickr/dist/l10n/cs.js'
import { Danish } from 'flatpickr/dist/l10n/da.js'
import { Dutch } from 'flatpickr/dist/l10n/nl.js'
import { Estonian } from 'flatpickr/dist/l10n/et.js'
import { Finnish } from 'flatpickr/dist/l10n/fi.js'
import { French } from 'flatpickr/dist/l10n/fr.js'
import { German } from 'flatpickr/dist/l10n/de.js'
import { Greek } from 'flatpickr/dist/l10n/gr.js'
import { Latvian } from 'flatpickr/dist/l10n/lv.js'
import { Lithuanian } from 'flatpickr/dist/l10n/lt.js'
import { Italian } from 'flatpickr/dist/l10n/it.js'
import { Norwegian } from 'flatpickr/dist/l10n/no.js'
import { Polish } from 'flatpickr/dist/l10n/pl.js'
import { Portuguese } from 'flatpickr/dist/l10n/pt.js'
import { Slovak } from 'flatpickr/dist/l10n/sk.js'
import { Swedish } from 'flatpickr/dist/l10n/sv.js'
import { Spanish } from 'flatpickr/dist/l10n/es.js'
import { Slovenian } from 'flatpickr/dist/l10n/sl.js'

export class DatePicker {
constructor (element, enableTime = false, noCalendar = false) {
this.element = element

let locale = document.documentElement.lang
if (locale === 'en') {
locale = 'default'
}

try {
this.element._flatpickr = flatpickr(this.element, {
locale: locale,
enableTime: enableTime,
noCalendar: noCalendar
})
} catch (ex) {
console.log('No translation found for ' + locale)
}
}
}
7 changes: 7 additions & 0 deletions assets-public/js/Framework/DateTimePicker/DateTimePicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { DatePicker } from './DatePicker.js'

export class DateTimePicker extends DatePicker {
constructor (element) {
super(element, true)
}
}
9 changes: 9 additions & 0 deletions assets-public/js/Framework/DateTimePicker/TimePicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { DatePicker } from './DatePicker.js'

export class TimePicker extends DatePicker {
constructor (element) {
super(element, true, true)

element.parentNode.querySelector('[data-flatpicker-clear]').addEventListener('click', event => this.element._flatpickr.clear())
}
}
7 changes: 7 additions & 0 deletions assets-public/js/Framework/Flash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function addFlash (message, type, delay = 10000) {
const toastId = 'toast' + Date.now()

// TODO when integrating stimulus controllers

return toastId
}
10 changes: 10 additions & 0 deletions assets-public/js/Framework/Form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function Form (forms) {
forms.forEach((form) => {
form.addEventListener('submit', () => { hijackSubmit() })
})

const hijackSubmit = () => {
const event = new Event('form_submitting')
document.dispatchEvent(event)
}
}
104 changes: 104 additions & 0 deletions assets-public/js/Framework/FormCollection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import Sortable from 'sortablejs'

const FormCollection = function (element) {
const addButton = element.querySelector('[data-role="collection-add-button"]')
// set index if not defined
const index = element.dataset.index

const items = element.querySelectorAll('[data-role="collection-item"]')
let numberOfItems = items.length
const minimumItems = element.dataset.min !== undefined && element.dataset.min !== 'null' ? parseInt(element.dataset.min) : 0
const maximumItems = element.dataset.max !== undefined && element.dataset.max !== 'null' ? parseInt(element.dataset.max) : null

addButton.addEventListener('click', event => { addItem(event, element) })
element.querySelectorAll('[data-role="collection-remove-button"]').forEach(button => {
button.addEventListener('click', event => { removeItem(event) })
})

if (index === undefined || index === null) {
element.dataset.index = numberOfItems
}

if (element.dataset.allowDragAndDrop === '1') {
Sortable.create(element.querySelector('ul'), {
handler: '[data-role="collection-item-change-order"]',
animation: 150,
ghostClass: 'collection-item-selected',
chosenClass: 'collection-item-selected',
dragClass: 'collection-item-selected'
})
}

// Add minimum items
while (numberOfItems < minimumItems) {
addItem(new Event('click'), element)
numberOfItems++
}
}

const addItem = function (event, element) {
event.preventDefault()

const addButton = element.querySelector('[data-role="collection-add-button"]')
let numberOfItems = element.querySelectorAll('[data-role="collection-item"]').length
const minimumItems = element.dataset.min !== undefined && element.dataset.min !== 'null' ? parseInt(element.dataset.min) : 0
const maximumItems = element.dataset.max !== undefined && element.dataset.max !== 'null' ? parseInt(element.dataset.max) : null

// Check if the button should have been disabled
if (maximumItems && numberOfItems >= maximumItems) {
addButton.setAttribute('disabled', 'disabled')

return
}

numberOfItems++

document.dispatchEvent(new Event('add.collection.item'))

let prototype = element.dataset.prototype
// get the new index
const index = parseInt(element.dataset.index)
// Replace '__name__' in the prototype's HTML to
// instead be a number based on how many items we have
prototype = prototype.replace(/__name__/g, index)
// increase the index with one for the next item
element.dataset.index = index + 1
// Display the form in the page before the "new" link

const container = element.querySelector('[data-role="collection-item-container"]')

container.insertAdjacentHTML('beforeend', prototype)

container.lastElementChild.querySelector('[data-role="collection-remove-button"]').addEventListener('click', event => {
removeItem(event)
})

if (maximumItems && numberOfItems >= maximumItems) {
addButton.setAttribute('disabled', 'disabled')
}

document.dispatchEvent(new Event('added.collection.item'))
}

const removeItem = function (event) {
event.preventDefault()

document.dispatchEvent(new Event('remove.collection.item'))

const itemToRemove = event.target.closest('[data-role="collection-item"]')
const element = event.target.closest('[data-role="collection"]')
const addButton = element.querySelector('[data-role="collection-add-button"]')
const numberOfItems = element.querySelectorAll('[data-role="collection-item"]').length - 1
const minimumItems = element.dataset.min !== undefined && element.dataset.min !== 'null' ? parseInt(element.dataset.min) : 0
const maximumItems = element.dataset.max !== undefined && element.dataset.max !== 'null' ? parseInt(element.dataset.max) : null

itemToRemove.parentNode.removeChild(itemToRemove)

if (maximumItems && numberOfItems < maximumItems) {
addButton.removeAttribute('disabled')
}

document.dispatchEvent(new Event('removed.collection.item'))
}

export default FormCollection
10 changes: 10 additions & 0 deletions assets-public/js/Framework/GoBack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const GoBack = function (backButton) {
if (backButton !== null) {
backButton.addEventListener('click', (event) => {
event.preventDefault()
window.history.back()
})
}
}

export default GoBack
47 changes: 47 additions & 0 deletions assets-public/js/Framework/PasswordStrengthChecker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import axios from 'axios'

export default function PasswordStrengthChecker () {
const passwordInput = document.querySelectorAll('[data-role="check-password"] input[type="password"]')[0]
const meterSections = document.querySelectorAll('.meter-section')

const debounced = debounce(getStrength)
if (passwordInput) passwordInput.addEventListener('input', debounced)

function debounce (func, timeout = 300) {
let timer;

return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}

function getStrength () {
const password = passwordInput.value
const dataRoute = passwordInput.closest('[data-route]').dataset.route

axios.post(dataRoute, {
password: password
})
.then(function (response) {
updateMeter(response.data.strength)
})
.catch(function (error) {
console.error(error)
})
}

function updateMeter (strength) {
const classes = ['weak', 'medium', 'strong', 'very-strong', 'very-strong']

// Remove all strength classes
meterSections.forEach((section) => {
section.classList.remove(...classes)
})

// Add the appropriate strength class based on the strength value
for (let i = 0; i <= strength; i++) {
meterSections[i].classList.add(classes[strength])
}
}
}
10 changes: 10 additions & 0 deletions assets-public/js/Framework/Popover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Popover as BootstrapPopover } from 'bootstrap'

const Popover = function () {
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new BootstrapPopover(tooltipTriggerEl)
})
}

export default Popover
47 changes: 47 additions & 0 deletions assets-public/js/Framework/Scrolling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const Scrolling = function (scrollToTop) {
document.querySelectorAll('a[href*=\\#]').forEach((link) => {
link.addEventListener('click', scrollTo)
})

// On long pages, show the Back to top link
if (scrollToTop) {
window.addEventListener('scroll', () => {
if (document.body.scrollY > 1000) {
scrollToTop.classList.remove('d-none')
scrollToTop.classList.add('show')
} else {
scrollToTop.classList.add('d-none')
scrollToTop.classList.remove('show')
}
})
}
}

const scrollTo = (event) => {
const anchor = event.currentTarget
const href = anchor.href
const url = href.substr(0, href.indexOf('#'))
const hash = href.substr(href.indexOf('#'))

// If the hash is only a hash, there's nowhere to go to
if (hash === '#') {
return
}

const targetElement = document.querySelector(hash)

/* check if we have an url, and if it is on the current page and the element exists disabled for nav-tabs */
if (
(url === '' || url.indexOf(document.location.pathname) >= 0) &&
!anchor.hasAttribute('data-no-scroll') &&
targetElement != null
) {
window.scroll({
top: targetElement.scrollHeight,
left: 0,
behavior: 'smooth'
})
}
}

export default Scrolling
Loading