'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(); } } }) } }) }