draggable.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
  2. 'use strict';
  3. const init_draggable_list = function () {
  4. if (!window.context) {
  5. if (window.console) {
  6. console.log('FreshRSS draggable list waiting for JS…');
  7. }
  8. setTimeout(init_draggable_list, 50);
  9. return;
  10. }
  11. let source;
  12. const draggableList = document.querySelector('.draggableList');
  13. const addMarker = (position, element) => {
  14. const hr = draggableList.querySelector('hr.drag-drop-marker');
  15. if (null === hr) {
  16. element.insertAdjacentHTML(position, '<hr class="drag-drop-marker" />');
  17. }
  18. };
  19. const removeMarker = () => {
  20. const hr = draggableList.querySelector('hr.drag-drop-marker');
  21. if (null !== hr) {
  22. hr.remove();
  23. }
  24. };
  25. draggableList.addEventListener('dragstart', event => {
  26. source = event.target.closest('[draggable="true"]');
  27. event.dataTransfer.setData('text/html', source.outerHTML);
  28. event.dataTransfer.effectAllowed = 'move';
  29. });
  30. draggableList.addEventListener('dragover', event => {
  31. event.preventDefault();
  32. if (!event.target || !event.target.closest) {
  33. return;
  34. }
  35. const draggableItem = event.target.closest('[draggable="true"]');
  36. if (null === draggableItem || source === draggableItem) {
  37. return;
  38. }
  39. const rect = draggableItem.getBoundingClientRect();
  40. if (event.clientY < (rect.top + rect.height / 2)) {
  41. addMarker('beforebegin', draggableItem);
  42. } else {
  43. addMarker('afterend', draggableItem);
  44. }
  45. });
  46. draggableList.addEventListener('dragleave', event => {
  47. event.preventDefault();
  48. removeMarker();
  49. });
  50. draggableList.addEventListener('drop', event => {
  51. event.preventDefault();
  52. event.stopPropagation();
  53. if (!event.target || !event.target.closest) {
  54. return;
  55. }
  56. const draggableItem = event.target.closest('[draggable="true"]');
  57. if (null === draggableItem || source === draggableItem) {
  58. return;
  59. }
  60. const rect = draggableItem.getBoundingClientRect();
  61. if (event.clientY < (rect.top + rect.height / 2)) {
  62. draggableItem.insertAdjacentHTML('beforebegin', event.dataTransfer.getData('text/html'));
  63. } else {
  64. draggableItem.insertAdjacentHTML('afterend', event.dataTransfer.getData('text/html'));
  65. }
  66. source.remove();
  67. removeMarker();
  68. draggableList.submit();
  69. });
  70. // This is needed to work around a Firefox bug → https://bugzilla.mozilla.org/show_bug.cgi?id=800050
  71. draggableList.addEventListener('focusin', event => {
  72. if (!event.target || !event.target.closest) {
  73. return;
  74. }
  75. const itemName = event.target.closest('input[type="text"]');
  76. if (null !== itemName) {
  77. itemName.select();
  78. }
  79. });
  80. };
  81. if (document.readyState && document.readyState !== 'loading') {
  82. init_draggable_list();
  83. } else if (document.addEventListener) {
  84. document.addEventListener('DOMContentLoaded', event => init_draggable_list(), false);
  85. }
  86. // @license-end