/*! * Nestedset drag & drop for Symfony2Admingenerator * ================================================ * * Created by loostro, 2013 * Released under MIT license. * * Uses: * > jquery-ui draggable * > jquery-ui droppable * > twitter bootstrap modal * > jquery treeTable (modified version) * * Requires setting (in template) variables: * admingeneratorNestedUrl >> url used for ajax call * nestedsetStringExpand >> collapsed branch title translation * nestedsetStringCollapse >> expanded branch title translation */ $(document).ready(function(){ nestedsetTreeTable(); nestedsetDragAndDrop(); }); // Init treeTable function nestedsetTreeTable() { $('#tree-table').treeTable({ stringExpand: nestedsetStringExpand, stringCollapse: nestedsetStringCollapse, doubleclickMode: true, initialRootState: "expanded", initialIndent: -19, onNodeInit: function(node, expandable, isRootNode) { (isRootNode) ? node.addClass('ui-helper-hidden') : ''; var cell = $(node).children().first(); var expander = $('').addClass('icon-expander'); cell.prepend(expander); }, onNodeReinit: function(node, expandable, isRootNode) { $(node).find('.icon-expander').remove(); } }); } // Init drag and drop function nestedsetDragAndDrop() { $("#tree-table tbody tr").draggable({ helper: function(e) { var row = $(e.target).closest('tr'); var branch = row.selectBranch(); return $('
').addClass('nested-helper').css({ 'width': branch.closest('table').width()+'px', 'height': row.outerHeight() }).append($('').css('width', '100%').append(branch.clone())); }, drag: function(e, ui) { var $draggable = $(this); var $droppable = $draggable.data('current-droppable'); if($droppable && isAccepted($droppable, $draggable, ui)) { highlightAction(ui, $droppable); } }, refreshPositions: true, // Performance? revert: "invalid", revertDuration: 300, scroll: true }); $("#tree-table tbody tr").droppable({ accept: "#tree-table tbody tr", over: function(e, ui) { var $droppable = $(this); var $draggable = ui.draggable; if(isAccepted($droppable, $draggable, ui)) { highlightAction(ui, $droppable); $draggable.data('current-droppable', $droppable); } }, out: function(e, ui) { var $droppable = $(this); var $draggable = ui.draggable; if(isAccepted($droppable, $draggable, ui)) { unhighlightAction(ui, $droppable); } }, drop: function(e, ui) { var $droppable = $(this); var $draggable = ui.draggable; if(isAccepted($droppable, $draggable, ui)) { unhighlightAction(ui, $droppable); var action = dropActionName(ui, $droppable); var url = admingeneratorNestedUrl; url = url.replace('|dragged|', ($draggable[0].id).replace('node-','')); url = url.replace('|dropped|', ($droppable[0].id).replace('node-','')); url = url.replace('|action|', action); $('#nestedset_loading').modal('show'); $.ajax({ url: url, success: function() { // reorganize tree var $parent = $draggable.nodeParent(); $draggable.moveBranch(action, $droppable); if($parent) { $parent.nodeReinitialize(); } if(action == 'in') { $droppable.nodeReinitialize(); } // hide modal, display success alert var alert = $('#nestedset_success').clone(); $('#flashes').append(alert); $('#nestedset_loading').modal('hide'); }, error: function() { // hide modal, display error alert var alert = $('#nestedset_error').clone(); $('#flashes').append(alert); $('#nestedset_loading').modal('hide'); } }); } } }); } // Private functions function isAccepted($droppable, $draggable, ui) { if($draggable.nodeParent() && $draggable.nodeParent()[0].id == $droppable[0].id && dropActionName(ui, $droppable) == 'after') { return false; } return ($.inArray($droppable, $draggable.selectBranch()) == -1); } function dropActionName(ui, $droppable) { var $draggable = ui.draggable || ui.helper.find('table tbody tr:first'); var draggableX = ui.offset.top + $draggable.outerHeight() / 2; var droppableOffset = $droppable.offset().top; var droppableHeight = $droppable.outerHeight(); if (droppableOffset <= draggableX && draggableX < droppableOffset + droppableHeight * 1/3 ) { return 'before'; } else if (droppableOffset + droppableHeight * 1/3 <= draggableX && draggableX <= droppableOffset + droppableHeight * 2/3 ) { return 'in'; } else if (droppableOffset + droppableHeight * 2/3 < draggableX && draggableX <= droppableOffset + droppableHeight ) { return 'after'; } } function highlightAction(ui, $droppable) { var className = 'droppable-'+dropActionName(ui, $droppable); $droppable.removeClass('droppable-before').removeClass('droppable-in').removeClass('droppable-after').addClass(className); } function unhighlightAction(ui, $droppable) { ui.draggable.removeData('current-droppable'); $droppable.removeClass('droppable-before').removeClass('droppable-in').removeClass('droppable-after'); }