tableのtr要素をドラッグ&ドロップで任意に入れ替え その2 ギブアップ!

せっかく、解説書がありながら、ギブアップ!
テーブルの範囲を選んだときに、rowSpan を見て、関連のある行を自動で選択できるようにするも、前方に検索するのは、あきらめました。view.getSelection で得た、range の扱いもゴタゴタ。

<!DOCTYPE html>
<title></title>
<style type="text/css">
table, th {
  border:1px #c88 outset;
}
td {
  border:1px #c88 inset;
  width :4em;
  text-align: right;
}
</style>

<body>
<table id="huga">
  <thead>
    <tr>
      <th id="p">1  <th>2  <th>3  <th>4  <th>5  <th>6  <th>7
  </thead>
  
  <tbody>
    <tr>
      <td>1  <td>2  <td>3  <td>4  <td>5  <td>6  <td>7

    <tr>
      <td>1  <td>2  <td>3  <td>4  <td>5  <td>6  <td>7

    <tr>
      <td>1  <td>2  <td>3  <td>4  <td>5  <td>6  <td>7

    <tr>
      <td>2  <td rowspan="2">2  <td>3  <td>4  <td>5  <td>6  <td>7
    
    <tr>
      <td>3  <td rowspan="2">3  <td>4  <td>5  <td>6  <td>7
    
    <tr>
      <td>4  <td>2  <td rowspan="2">4  <td>5  <td>6  <td>7
    
    <tr>
      <td>5  <td>2  <td>3  <td rowspan="2">5  <td>6  <td>7
    
    <tr>
      <td>6  <td>2  <td>3  <td>4  <td rowspan="2">6  <td>7
    
    <tr>
      <td>7  <td>2  <td>3  <td>4  <td>5  <td rowspan="2">7
    
    <tr>
      <td>8  <td>2  <td>3  <td>4  <td>5  <td>6
    
    <tr>
      <td>9   <td>2  <td>3  <td>4  <td>5  <td>6  <td>7

  </tbody>
</table>
  <p>abc</p>

<script type="application/javascript; version=1.8">

(function () {

  const TableDrag = {
    table : []
  };


//__________

  // 目的の親を得る
  const getParent =
    (function  (n, t, v)
      n ? (v == n[t]) ? n: arguments.callee (n.parentNode, t, v): null);


  // ノードが登録されたテーブルに含まれているか
  const hasInsideTable =
    (function (checker)
      (function (target)
        TableDrag.table.some (checker, target)))
    
    ((function (parent)
      !! (this.compareDocumentPosition (parent) & 8))); // 外部ファイルにしないと…


  // 指定行が、rowSpan で関連がある行番号の最大値を返す
  const getRowsRange =
    (function (getMax)
      (function (rowIndex) {
        var max = 0;
        var rowSpanMax;
        var row;
        var result = { min: rowIndex };
        
        while (row = this[rowIndex]) {
          rowSpanMax = Math.max.apply (null, Array.map (row.cells, getMax));

          if (max < rowSpanMax)
            max = rowSpanMax;

          max -= 1;
          if (1 > max) break;

          rowIndex += 1;
        }
        
        result.max = rowIndex;
        return result;
      }))
    
    ((function (cell) parseInt (cell.rowSpan, 10) || 1 ));


  // イベントハンドラ
  var onDrag = function (evt) {
    var target = evt.target;
    var doc = target.ownerDocument;
    var view = doc.defaultView;
    var selection = view.getSelection ();
    var table = getParent (target, 'nodeName', 'TABLE');
    var dt = evt.dataTransfer;
    var row1 = getParent (target, 'nodeName', 'TR');
    var row2;
    var no;

    if (! row1)
      return;

    switch (evt.type) {
    case 'dragstart':

      if (! hasInsideTable (target)) // 登録されていない
        return;

      if ('TBODY' === selection.anchorNode.nodeName) {
        dt.setData ('text/plain', selection.anchorOffset + '/' + selection.focusOffset);
        dt.effectAllowed = 'move';
      }
      return;
      
    case 'drop':
      if ('TD' === row1.cells[0].nodeName) {
        no = dt.getData ('text/plain').split('/');
        var rows = [], I = + no[1];
        for (var i = +no[0]; i < I; i++)
          rows.push (table.rows[i]);

          rows.forEach (function (r) row1.parentNode.insertBefore (r, row1));
      }
      break;
    
    default :
    }

    evt.preventDefault ();
    return;
  };


  var onExtensionRowsRange = function (evt) {
    var node = evt.target;
    var doc  = node.ownerDocument;
    var view  = doc.defaultView;
    var selection = view.getSelection ();
    var range = doc.createRange ();
    var sNode = selection.anchorNode
    var eNode = selection.focusNode;
    var table;
    var sRange;
    var eRange;

    if (selection.isCollapsed) //選択範囲がない
      return;
    
    if (! hasInsideTable(sNode)) // 選択範囲の最初が登録されたテーブルか?
      return;
      
    if (! hasInsideTable(eNode)) // 選択範囲の最後が登録されたテーブルか?
      return;

    table = getParent (node, 'nodeName', 'TABLE'); //選択範囲のテーブルを取得
    
    sNode = getParent (sNode, 'nodeName', 'TR'); //最初の行
    eNode = getParent (eNode, 'nodeName', 'TR'); //最後の行
    
    sRange = getRowsRange.call (table.rows, sNode.rowIndex); //rowSpanを探し出し、範囲を調べる
    eRange = getRowsRange.call (table.rows, eNode.rowIndex);
    
    selection.removeAllRanges (); // 選択範囲を削除
    range.setStartBefore (table.rows[ Math.min (sRange.min, eRange.min)]); //選択範囲を設定する
    range.setEndAfter (table.rows[ Math.max (sRange.max, eRange.max)]);
    selection.addRange (range); // 選択範囲を追加
  };


  // 初期化(イベント設定)
  var init = function (view) {
    var win = view || window;
    var doc = win.document;

    doc.addEventListener ('dragstart', onDrag, false);
    doc.addEventListener ('dragover', onDrag, false);
    doc.addEventListener ('drop', onDrag, false);
    doc.addEventListener ('mouseup', onExtensionRowsRange, false);
  };


  // テーブルの追加
  const add =
    (function (hasArray)
      (function (target)
        (target instanceof HTMLTableElement)
        ? (! hasArray.call (TableDrag.table, target))
          ? TableDrag.table.push (target)
          : null
        : null))

    ((function (checker)
      (function (target)
        this.some (checker, target)))

    ((function (m) m == this)))

  //__________

  TableDrag.add  = add;
  TableDrag.init = init;

  //__________

  this.TableDrag = TableDrag;

})();

TableDrag.init (this);
TableDrag.add (document.getElementById ('huga'));
</script>