nestedset.js 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*!
  2. * Nestedset drag & drop for Symfony2Admingenerator
  3. * ================================================
  4. *
  5. * Created by loostro, 2013
  6. * Released under MIT license.
  7. *
  8. * Uses:
  9. * > jquery-ui draggable
  10. * > jquery-ui droppable
  11. * > twitter bootstrap modal
  12. * > jquery treeTable (modified version)
  13. *
  14. * Requires setting (in template) variables:
  15. * admingeneratorNestedUrl >> url used for ajax call
  16. * nestedsetStringExpand >> collapsed branch title translation
  17. * nestedsetStringCollapse >> expanded branch title translation
  18. */
  19. $(document).ready(function(){
  20. nestedsetTreeTable();
  21. nestedsetDragAndDrop();
  22. });
  23. // Init treeTable
  24. function nestedsetTreeTable() {
  25. $('#tree-table').treeTable({
  26. stringExpand: nestedsetStringExpand,
  27. stringCollapse: nestedsetStringCollapse,
  28. doubleclickMode: true,
  29. initialRootState: "expanded",
  30. initialIndent: -19,
  31. onNodeInit: function(node, expandable, isRootNode) {
  32. (isRootNode) ? node.addClass('ui-helper-hidden') : '';
  33. var cell = $(node).children().first();
  34. var expander = $('<i />').addClass('icon-expander');
  35. cell.prepend(expander);
  36. },
  37. onNodeReinit: function(node, expandable, isRootNode) {
  38. $(node).find('.icon-expander').remove();
  39. }
  40. });
  41. }
  42. // Init drag and drop
  43. function nestedsetDragAndDrop() {
  44. $("#tree-table tbody tr").draggable({
  45. helper: function(e) {
  46. var row = $(e.target).closest('tr');
  47. var branch = row.selectBranch();
  48. return $('<div />').addClass('nested-helper').css({
  49. 'width': branch.closest('table').width()+'px',
  50. 'height': row.outerHeight()
  51. }).append($('<table />').css('width', '100%').append(branch.clone()));
  52. },
  53. drag: function(e, ui) {
  54. var $draggable = $(this);
  55. var $droppable = $draggable.data('current-droppable');
  56. if($droppable && isAccepted($droppable, $draggable, ui)) { highlightAction(ui, $droppable); }
  57. },
  58. refreshPositions: true, // Performance?
  59. revert: "invalid",
  60. revertDuration: 300,
  61. scroll: true
  62. });
  63. $("#tree-table tbody tr").droppable({
  64. accept: "#tree-table tbody tr",
  65. over: function(e, ui) {
  66. var $droppable = $(this);
  67. var $draggable = ui.draggable;
  68. if(isAccepted($droppable, $draggable, ui)) {
  69. highlightAction(ui, $droppable);
  70. $draggable.data('current-droppable', $droppable);
  71. }
  72. },
  73. out: function(e, ui) {
  74. var $droppable = $(this);
  75. var $draggable = ui.draggable;
  76. if(isAccepted($droppable, $draggable, ui)) { unhighlightAction(ui, $droppable); }
  77. },
  78. drop: function(e, ui) {
  79. var $droppable = $(this);
  80. var $draggable = ui.draggable;
  81. if(isAccepted($droppable, $draggable, ui)) {
  82. unhighlightAction(ui, $droppable);
  83. var action = dropActionName(ui, $droppable);
  84. var url = admingeneratorNestedUrl;
  85. url = url.replace('|dragged|', ($draggable[0].id).replace('node-',''));
  86. url = url.replace('|dropped|', ($droppable[0].id).replace('node-',''));
  87. url = url.replace('|action|', action);
  88. $('#nestedset_loading').modal('show');
  89. $.ajax({
  90. url: url,
  91. success: function() {
  92. // reorganize tree
  93. var $parent = $draggable.nodeParent();
  94. $draggable.moveBranch(action, $droppable);
  95. if($parent) { $parent.nodeReinitialize(); }
  96. if(action == 'in') { $droppable.nodeReinitialize(); }
  97. // hide modal, display success alert
  98. var alert = $('#nestedset_success').clone();
  99. $('#flashes').append(alert);
  100. $('#nestedset_loading').modal('hide');
  101. },
  102. error: function() {
  103. // hide modal, display error alert
  104. var alert = $('#nestedset_error').clone();
  105. $('#flashes').append(alert);
  106. $('#nestedset_loading').modal('hide');
  107. }
  108. });
  109. }
  110. }
  111. });
  112. }
  113. // Private functions
  114. function isAccepted($droppable, $draggable, ui) {
  115. if($draggable.nodeParent() && $draggable.nodeParent()[0].id == $droppable[0].id && dropActionName(ui, $droppable) == 'after') { return false; }
  116. return ($.inArray($droppable, $draggable.selectBranch()) == -1);
  117. }
  118. function dropActionName(ui, $droppable)
  119. {
  120. var $draggable = ui.draggable || ui.helper.find('table tbody tr:first');
  121. var draggableX = ui.offset.top + $draggable.outerHeight() / 2;
  122. var droppableOffset = $droppable.offset().top;
  123. var droppableHeight = $droppable.outerHeight();
  124. if (droppableOffset <= draggableX && draggableX < droppableOffset + droppableHeight * 1/3 )
  125. { return 'before'; } else
  126. if (droppableOffset + droppableHeight * 1/3 <= draggableX && draggableX <= droppableOffset + droppableHeight * 2/3 )
  127. { return 'in'; } else
  128. if (droppableOffset + droppableHeight * 2/3 < draggableX && draggableX <= droppableOffset + droppableHeight )
  129. { return 'after'; }
  130. }
  131. function highlightAction(ui, $droppable) {
  132. var className = 'droppable-'+dropActionName(ui, $droppable);
  133. $droppable.removeClass('droppable-before').removeClass('droppable-in').removeClass('droppable-after').addClass(className);
  134. }
  135. function unhighlightAction(ui, $droppable) {
  136. ui.draggable.removeData('current-droppable');
  137. $droppable.removeClass('droppable-before').removeClass('droppable-in').removeClass('droppable-after');
  138. }