Project import generated by Copybara.

GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/static/js/tracker/tracker-dd.js b/static/js/tracker/tracker-dd.js
new file mode 100644
index 0000000..e7b4c1e
--- /dev/null
+++ b/static/js/tracker/tracker-dd.js
@@ -0,0 +1,132 @@
+/* Copyright 2016 The Chromium Authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+/**
+ * Functions used by Monorail to control drag-and-drop re-orderable lists
+ *
+ */
+
+/**
+ * Initializes the drag-and-drop functionality on the elements of a
+ * container node.
+ * TODO(lukasperaza): allow bulk drag-and-drop
+ * @param {Element} container The HTML container element to turn into
+ *    a drag-and-drop list. The items of the list must have the
+ *    class 'drag_item'
+ */
+function TKR_initDragAndDrop(container, opt_onDrop, opt_preventMultiple) {
+  let dragSrc = null;
+  let dragLocation = null;
+  let dragItems = container.getElementsByClassName('drag_item');
+  let target = null;
+
+  opt_preventMultiple = opt_preventMultiple || false;
+  opt_onDrop = opt_onDrop || function() {};
+
+  function _handleMouseDown(event) {
+    target = event.target;
+  }
+
+  function _handleDragStart(event) {
+    let el = event.currentTarget;
+    let gripper = el.getElementsByClassName('gripper');
+    if (gripper.length && !gripper[0].contains(target)) {
+      event.preventDefault();
+      return;
+    }
+    el.style.opacity = 0.4;
+    event.dataTransfer.setData('text/html', el.outerHTML);
+    event.dataTransfer.dropEffect = 'move';
+    dragSrc = el;
+  }
+
+  function inRect(rect, x, y) {
+    if (x < rect.left || x > rect.right) {
+      return '';
+    } else if (rect.top <= y && y <= rect.top + rect.height / 2) {
+      return 'top';
+    } else {
+      return 'bottom';
+    }
+  }
+
+  function _handleDragOver(event) {
+    if (dragSrc == null) {
+      return true;
+    }
+    event.preventDefault();
+    let el = event.currentTarget;
+    let rect = el.getBoundingClientRect(),
+      classes = el.classList;
+    let section = inRect(rect, event.clientX, event.clientY);
+    if (section == 'top' && !classes.contains('top')) {
+      dragLocation = 'top';
+      classes.remove('bottom');
+      classes.add('top');
+    } else if (section == 'bottom' && !classes.contains('bottom')) {
+      dragLocation = 'bottom';
+      classes.remove('top');
+      classes.add('bottom');
+    }
+    return false;
+  }
+
+  function removeClasses(el) {
+    el.classList.remove('top');
+    el.classList.remove('bottom');
+  }
+
+  function _handleDragDrop(event) {
+    let el = event.currentTarget;
+    if (dragSrc == null || el == dragSrc) {
+      return true;
+    }
+
+    if (opt_preventMultiple) {
+      let dragItems = container.getElementsByClassName('drag_item');
+      for (let i = 0; i < dragItems.length; i++) {
+        dragItems[i].setAttribute('draggable', false);
+      }
+    }
+
+    let srcID = dragSrc.getAttribute('data-id');
+    let id = el.getAttribute('data-id');
+
+    if (dragLocation == 'top') {
+      el.parentNode.insertBefore(dragSrc, el);
+      opt_onDrop(srcID, id, 'above');
+    } else if (dragLocation == 'bottom') {
+      el.parentNode.insertBefore(dragSrc, el.nextSibling);
+      opt_onDrop(srcID, id, 'below');
+    }
+    dragSrc.style.opacity = 0.4;
+    dragSrc = null;
+  }
+
+  function _handleDragEnd(event) {
+    if (dragSrc) {
+      dragSrc.style.opacity = 1;
+      dragSrc = null;
+    }
+    for (let i = 0; i < dragItems.length; i++) {
+      removeClasses(dragItems[i]);
+    }
+  }
+
+  for (let i = 0; i < dragItems.length; i++) {
+    let el = dragItems[i];
+    el.setAttribute('draggable', true);
+    el.addEventListener('mousedown', _handleMouseDown);
+    el.addEventListener('dragstart', _handleDragStart);
+    el.addEventListener('dragover', _handleDragOver);
+    el.addEventListener('drop', _handleDragDrop);
+    el.addEventListener('dragend', _handleDragEnd);
+    el.addEventListener('dragleave', function(event) {
+      removeClasses(event.currentTarget);
+    });
+  }
+}