116 lines
3.6 KiB
JavaScript
116 lines
3.6 KiB
JavaScript
'use strict';
|
|
|
|
{
|
|
function isBefore(el1, el2) {
|
|
if (el2.parentNode === el1.parentNode) {
|
|
for (let cur = el1.previousSibling; cur; cur = cur.previousSibling) {
|
|
if (cur === el2) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
let draggedItem = null;
|
|
|
|
function sortableItemDragStart(e, item) {
|
|
const box = item.getBoundingClientRect();
|
|
const oX = e.clientX - box.left;
|
|
const oY = e.clientY - box.top;
|
|
draggedItem = item;
|
|
item.classList.add('contrast-bg');
|
|
e.dataTransfer.setDragImage(item, oX, oY);
|
|
e.dataTransfer.effectAllowed = 'move';
|
|
}
|
|
|
|
function sortableItemDragEnd(e, item) {
|
|
draggedItem = null;
|
|
item.classList.remove('contrast-bg');
|
|
}
|
|
|
|
function sortableItemDragOver(e, item) {
|
|
const target = e.target.closest('.sortable-item');
|
|
if (!target || target === draggedItem) {
|
|
return;
|
|
}
|
|
if (draggedItem === null) {
|
|
return;
|
|
}
|
|
const inSameList = draggedItem.dataset.sortableListKey === target.dataset.sortableListKey;
|
|
if (!inSameList) {
|
|
return;
|
|
}
|
|
const targetList = draggedItem.closest('.sortable-list');
|
|
if (isBefore(draggedItem, target)) {
|
|
targetList.insertBefore(draggedItem, target);
|
|
} else {
|
|
targetList.insertBefore(draggedItem, target.nextSibling);
|
|
}
|
|
}
|
|
|
|
const listItemsHandled = new Map();
|
|
|
|
const getListItemsHandled = (list) => {
|
|
return listItemsHandled.get(list) || new Set();
|
|
}
|
|
|
|
function registerSortableList(list) {
|
|
list.querySelectorAll('li:not(.immovable)').forEach(item => {
|
|
const listItems = getListItemsHandled(list);
|
|
listItems.add(item);
|
|
listItemsHandled.set(list, listItems);
|
|
const dragger = item.querySelector('.dragger');
|
|
dragger.addEventListener('dragstart', e => { sortableItemDragStart(e, item) });
|
|
dragger.addEventListener('dragend', e => { sortableItemDragEnd(e, item) });
|
|
item.addEventListener('dragover', e => { sortableItemDragOver(e, item) });
|
|
});
|
|
|
|
const obs = new MutationObserver(records => {
|
|
for (const mutation of records) {
|
|
mutation.addedNodes.forEach(node => {
|
|
if (!(node instanceof HTMLElement)) return;
|
|
if (!node.classList.contains('sortable-item')) return;
|
|
const listItems = getListItemsHandled(list);
|
|
if (listItems.has(node)) return;
|
|
|
|
const dragger = node.querySelector('.dragger');
|
|
dragger.addEventListener('dragstart', e => { sortableItemDragStart(e, node) });
|
|
dragger.addEventListener('dragend', e => { sortableItemDragEnd(e, node) });
|
|
node.addEventListener('dragover', e => { sortableItemDragOver(e, node) });
|
|
listItems.add(node);
|
|
listItemsHandled.set(list, listItems);
|
|
});
|
|
}
|
|
});
|
|
obs.observe(list, { childList: true });
|
|
}
|
|
|
|
document.querySelectorAll('.sortable-list').forEach(registerSortableList);
|
|
|
|
const listsObs = new MutationObserver(records => {
|
|
for (const mutation of records) {
|
|
mutation.addedNodes.forEach(node => {
|
|
if (!(node instanceof HTMLElement)) return;
|
|
if (!node.classList.contains('sortable-list')) return;
|
|
|
|
registerSortableList(node);
|
|
});
|
|
}
|
|
});
|
|
listsObs.observe(document.body, { childList: true, subtree: true })
|
|
}
|
|
|
|
{
|
|
// babycode editor: press ctrl+enter to submit
|
|
document.querySelectorAll('.babycode-editor').forEach(ta => {
|
|
if (ta.form instanceof HTMLFormElement) {
|
|
ta.addEventListener('keydown', e => {
|
|
if (e.ctrlKey && e.key === 'Enter') {
|
|
if (ta.form.reportValidity()) {
|
|
ta.form.submit();
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|