mar10/fancytree

View on GitHub
demo/sample-ext-dnd5.html

Summary

Maintainability
Test Coverage
<!DOCTYPE html>
<html>
    <head>
        <meta
            http-equiv="content-type"
            content="text/html; charset=ISO-8859-1"
        />
        <title>Fancytree - Example: Drag'n'drop</title>
        <script src="../lib/jquery.js"></script>

        <link href="../src/skin-win8/ui.fancytree.css" rel="stylesheet" />
        <script src="../src/jquery-ui-dependencies/jquery.fancytree.ui-deps.js"></script>
        <script src="../src/jquery.fancytree.js"></script>
        <script src="../src/jquery.fancytree.dnd5.js"></script>

        <!-- Start_Exclude: This block is not part of the sample code -->
        <link href="../lib/prettify.css" rel="stylesheet" />
        <script src="../lib/prettify.js"></script>
        <link href="sample.css" rel="stylesheet" />
        <script src="sample.js"></script>
        <!-- End_Exclude -->

        <style type="text/css">
            span.drag-source {
                border: 1px solid grey;
                border-radius: 3px;
                padding: 2px;
                background-color: silver;
            }

            ul.fancytree-container {
                max-height: 200px;
                overflow-y: scroll;
                width: 48%;
                display: inline-block;
            }
            #tree ul.fancytree-container {
                float: left;
            }

            span.fancytree-node.fancytree-drag-source {
                outline: 1px dotted grey;
            }
            span.fancytree-node.fancytree-drop-accept {
                outline: 1px dotted green;
            }
            span.fancytree-node.fancytree-drop-reject {
                outline: 1px dotted red;
            }

            span.trashcan {
                border: 1px solid #f5c6cb;
        background-color: #f8d7da;
        color: #721c24;
        padding: 1px 3px;
            }

        </style>
        <!-- Add code to initialize the tree when the document is loaded: -->
        <script type="text/javascript">

$(function() {
  var LAST_EFFECT_DO = null,
      LAST_EFFECT_DD = null,
      lazyLogCache = {};

  /* Log if value changed, nor more than interval/sec.*/
  function logLazy(name, value, interval, msg) {
    if( !lazyLogCache[name] ) { lazyLogCache[name] = {stamp: now}};
    var now = Date.now(),
      entry = lazyLogCache[name];

    if( value && value === entry.value ) {
      return;
    }
    entry.value = value;

    if( interval > 0 && (now - entry.stamp) <= interval ) {
      return;
    }
    entry.stamp = now;
    lazyLogCache[name] = entry;
    console.log(msg);
  }

  // Attach the fancytree widget to an existing <div id="tree"> element
  // and pass the tree options as an argument to the fancytree() function:

  $("#tree").fancytree({
    extensions: ["dnd5"],
    treeId: "1",
    // titlesTabbable: true,
    source: {
      url: "ajax-tree-fs.json",
    },
    init: function(event, data) {
      data.tree.getFirstChild().setTitle("C:\\");
    },
    dnd5: {
      // autoExpandMS: 400,
      // preventForeignNodes: true,
      // preventNonNodes: true,
      preventRecursion: true, // Prevent dropping nodes on own descendants
      // preventSameParent: true,
      preventVoidMoves: true, // Prevent moving nodes 'before self', etc.
      // effectAllowed: "all",
      // dropEffectDefault: "move", // "auto",

      // --- Drag-support:

      dragStart: function(node, data) {
        /* This function MUST be defined to enable dragging for the tree.
          *
          * Return false to cancel dragging of node.
          * data.dataTransfer.setData() and .setDragImage() is available
          * here.
          */
        node.debug( "T1: dragStart: " + "data: " + data.dropEffect + "/" + data.effectAllowed +
        ", dataTransfer: " + data.dataTransfer.dropEffect + "/" + data.dataTransfer.effectAllowed, data );

        // Set the allowed effects (i.e. override the 'effectAllowed' option)
        data.effectAllowed = "all";

        // Set a drop effect (i.e. override the 'dropEffectDefault' option)
        // data.dropEffect = "link";
        data.dropEffect = "copy";

        // We could use a custom image here:
        // data.dataTransfer.setDragImage($("<div>TEST</div>").appendTo("body")[0], -10, -10);
        // data.useDefaultImage = false;

        // Return true to allow the drag operation
        return true;
      },
      // dragDrag: function(node, data) {
      //   logLazy("dragDrag", null, 2000,
      //     "T1: dragDrag: " + "data: " + data.dropEffect + "/" + data.effectAllowed +
      //     ", dataTransfer: " + data.dataTransfer.dropEffect + "/" + data.dataTransfer.effectAllowed );
      // },
      // dragEnd: function(node, data) {
      //   node.debug( "T1: dragEnd: " + "data: " + data.dropEffect + "/" + data.effectAllowed +
      //     ", dataTransfer: " + data.dataTransfer.dropEffect + "/" + data.dataTransfer.effectAllowed, data);
      //     alert("T1: dragEnd")
      // },

      // --- Drop-support:

      dragEnter: function(node, data) {
        node.debug( "T1: dragEnter: " + "data: " + data.dropEffect + "/" + data.effectAllowed +
          ", dataTransfer: " + data.dataTransfer.dropEffect + "/" + data.dataTransfer.effectAllowed, data );

        // data.dropEffect = "copy";
        return true;
      },
      dragOver: function(node, data) {
        logLazy("dragOver", null, 2000,
          "T1: dragOver: " + "data: " + data.dropEffect + "/" + data.effectAllowed +
          ", dataTransfer: " + data.dataTransfer.dropEffect + "/" + data.dataTransfer.effectAllowed );

        // Assume typical mapping for modifier keys
        data.dropEffect = data.dropEffectSuggested;
        // data.dropEffect = "move";
      },
      dragDrop: function(node, data) {
        /* This function MUST be defined to enable dropping of items on
          * the tree.
          */
        var newNode,
          transfer = data.dataTransfer,
          sourceNodes = data.otherNodeList,
          mode = data.dropEffect;

        node.debug( "T1: dragDrop: effect=" + "data: " + data.dropEffect + "/" + data.effectAllowed +
          ", dataTransfer: " + transfer.dropEffect + "/" + transfer.effectAllowed, data );

        alert("Drop on " + node + ":\n"
          + "source:" + JSON.stringify(data.otherNodeData) + "\n"
          + "hitMode:" + data.hitMode
          + ", dropEffect:" + data.dropEffect
          + ", effectAllowed:" + data.effectAllowed);

        if( data.hitMode === "after" ){
          // If node are inserted directly after tagrget node one-by-one,
          // this would reverse them. So we compensate:
          sourceNodes.reverse();
        }
        if (data.otherNode) {
          // Drop another Fancytree node from same frame (maybe a different tree however)
          var sameTree = (data.otherNode.tree === data.tree);

          if (mode === "move") {
            data.otherNode.moveTo(node, data.hitMode);
          } else {
            newNode = data.otherNode.copyTo(node, data.hitMode);
            if (mode === "link") {
              newNode.setTitle("Link to " + newNode.title);
            } else {
              newNode.setTitle("Copy of " + newNode.title);
            }
          }
        } else if (data.otherNodeData) {
          // Drop Fancytree node from different frame or window, so we only have
          // JSON representation available
          node.addChild(data.otherNodeData, data.hitMode);
        } else if (data.files.length) {
          // Drop files
          for(var i=0; i<data.files.length; i++) {
            var file = data.files[i];
            node.addNode( { title: "'" + file.name + "' (" + file.size + " bytes)" }, data.hitMode );
            // var url = "'https://example.com/upload",
            //     formData = new FormData();

            // formData.append("file", transfer.files[0])
            // fetch(url, {
            //   method: "POST",
            //   body: formData
            // }).then(function() { /* Done. Inform the user */ })
            // .catch(function() { /* Error. Inform the user */ });
          }
        } else {
          // Drop a non-node
          node.addNode( { title: transfer.getData("text") }, data.hitMode );
        }
        node.setExpanded();
      },
    },
    // activate: function(event, data) {
    //   alert("activate " + data.node);
    // },
    lazyLoad: function(event, data) {
      data.result = { url: "ajax-sub2.json" };
    },
  });



  $("#tree2").fancytree({
    extensions: ["dnd5"],
    treeId: "2",
    source: {
      url: "ajax-tree-fs.json",
    },
    init: function(event, data) {
      data.tree.getFirstChild().setTitle("D:\\");
    },
    dnd5: {
      // --- Drag-support:
      dragStart: function(node, data) {
        node.debug( "T2: dragStart: " + "data: " + data.dropEffect + "/" + data.effectAllowed +
          ", dataTransfer: " + data.dataTransfer.dropEffect + "/" + data.dataTransfer.effectAllowed, data );

          data.effectAllowed = "all";
          data.dropEffect = data.dropEffectSuggested;
          return true;
      },
      // dragEnd: function(node, data) {
      //   node.warn( "T2: dragEnd: " + "data: " + data.dropEffect + "/" + data.effectAllowed +
      //     ", dataTransfer: " + data.dataTransfer.dropEffect + "/" + data.dataTransfer.effectAllowed, data );
      //     alert("T2: dragEnd")
      // },
      // --- Drop-support:
      dragEnter: function(node, data) {
        node.debug( "T2: dragEnter: " + "data: " + data.dropEffect + "/" + data.effectAllowed +
          ", dataTransfer: " + data.dataTransfer.dropEffect + "/" + data.dataTransfer.effectAllowed, data );
        return true;
      },
      dragOver: function(node, data) {
        logLazy("dragOver", null, 2000,
          "T2: dragOver: " + "data: " + data.dropEffect + "/" + data.effectAllowed +
          ", dataTransfer: " + data.dataTransfer.dropEffect + "/" + data.dataTransfer.effectAllowed);

          data.dropEffect = data.dropEffectSuggested;
      },
      dragDrop: function(node, data) {
        var newNode,
          transfer = data.dataTransfer,
          sourceNodes = data.otherNodeList,
          mode = data.dropEffect;

        // don't open links, files, ... even if an error occurs in this handler:
        data.originalEvent.preventDefault();

        node.debug( "T2: dragDrop: effect=" + "data: " + data.dropEffect + "/" + data.effectAllowed +
          ", dataTransfer: " + data.dataTransfer.dropEffect + "/" + data.dataTransfer.effectAllowed, data );

        if( data.hitMode === "after" ){
          // If node are inserted directly after tagrget node one-by-one,
          // this would reverse them. So we compensate:
          sourceNodes.reverse();
        }
        if (data.otherNode) {
          // Drop another Fancytree node from same frame
          // (maybe from another tree however)
          var sameTree = data.otherNode.tree === data.tree;

          if (mode === "move") {
            data.otherNode.moveTo(node, data.hitMode);
          } else {
            newNode = data.otherNode.copyTo(node, data.hitMode);
            if (mode === "link") {
              newNode.setTitle("Link to " + newNode.title);
            } else {
              newNode.setTitle("Copy of " + newNode.title);
            }
          }
        } else if (data.otherNodeData) {
          // Drop Fancytree node from different frame or window, so we only have
          // JSON representation available
          node.addChild(data.otherNodeData, data.hitMode);
        } else if (data.files.length) {
          // Drop files
          for(var i=0; i<data.files.length; i++) {
            var file = data.files[i];
            node.addNode( { title: "'" + file.name + "' (" + file.size + " bytes)" }, data.hitMode );
          }

        } else {
          // Drop a non-node
          node.addNode(
            {
              title: transfer.getData("text"),
            },
            data.hitMode
          );
        }
        node.setExpanded();
      },
    },
    // activate: function(event, data) {
    // },
    lazyLoad: function(event, data) {
      data.result = { url: "ajax-sub2.json" };
    },
  });

  /*
    Implement a trashcan
  */
  $("span.trashcan").on("dragenter dragover", function(event){
    var dt = event.originalEvent.dataTransfer;

    // This would be the 'clean' way, but does not work on IE:
//  if ( $.inArray("application/x-fancytree-node", dt.types) >= 0) {
    // This works on IE, but only detects nodes from the same window:
    if ( $.ui.fancytree.getDragNode() ) {
      dt.dropEffect = "move";
      return false;  // only accept Fancytree nodes
    }
  }).on("drop", function(event){

    // prevent opening as link, etc.
    event.preventDefault();

    // Remove source node (using information from dataTransfer)

    // IE only knows 'text' data type, so we cannot use this approach:
//  var dt = event.originalEvent.dataTransfer,
//    nodeData = JSON.parse(dt.getData("application/x-fancytree-node"));
//    $.ui.fancytree.getTree(nodeData.treeId).getNodeByKey(nodeData.key).remove();

    // Remove source node (assuming same window, and single select):
    $.ui.fancytree.getDragNode().remove();
    // $.ui.fancytree.getDragNodeList()[0].remove();
  });

});
        </script>
    </head>
    <body class="example">
        <h1>Example: 'dnd5' extension</h1>
        <div class="description">
            <p>
                Native Drag-and-Drop support:
            </p>
            <ul>
                <li>
                    Support drag and drop of tree nodes (inside the same or between different trees).
                </li>
                <li>
                    Support drag and drop between different frames, browser tabs, windows, or desktop.
                </li>
                <li>Requires IE 11 or later.</li>
            </ul>
            <p>
                <b>Status:</b> production.
                <b>Details:</b>
                <a
                    href="https://github.com/mar10/fancytree/wiki/ExtDnd5"
                    target="_blank"
                    class="external"
                    >ext-dnd5</a
                >.
            </p>
        </div>

        <div>
            <label for="skinswitcher">Skin:</label>
            <select id="skinswitcher"></select>
        </div>

        <p>
            <span class="drag-source" draggable="true"
                ondragstart="event.dataTransfer.setData('text/plain', 'Drag me');" >
                Drag me</span>
            <br />
            <label>Drop here:
                <input type="text" class="" placeholder="Drop here" />
            </label>
      <span class="drop-target trashcan">Trashcan</span>
        </p>
        <!-- Add a <table> element where the tree should appear: -->
        <div id="tree"></div>
        <div id="tree2"></div>

        <!-- Start_Exclude: This block is not part of the sample code -->
        <hr />
        <p class="sample-links  no_code">
            <a class="hideInsideFS" href="https://github.com/mar10/fancytree"
                >jquery.fancytree.js project home</a
            >
            <a class="hideOutsideFS" href="#">Link to this page</a>
            <a class="hideInsideFS" href="index.html">Example Browser</a>
            <a href="#" id="codeExample">View source code</a>
        </p>
        <pre id="sourceCode" class="prettyprint" style="display:none"></pre>
        <!-- End_Exclude -->
    </body>
</html>