<!doctype html>
<meta charset=utf-8>
<title>Deletion tests</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<div contenteditable></div>
<script>
var div = document.querySelector("div");

// Format: [start html, start pos, expected html, expected pos, command]
// Positions are a sequence of offsets starting from div, e.g., "1,2,0"
// translates to node = div.childNodes[1].childNodes[2], offset = 0.  For a
// non-collapsed selection, use a hyphen, like "0,0-1,0".  The selections are
// created with collapse() followed by extend() to allow reverse selections, so
// order is significant.
//
// Expected values can be arrays, in which case any is acceptable.
var tests = [
  ["<p><br></p><p><br></p>", "1,0", "<p><br></p>", "0,0", "delete"],
  ["<p><br></p><p><br></p>", "0,0", "<p><br></p>", "0,0", "forwarddelete"],

  // Range
  ["<p><br></p><p><br></p>", "0,0-1,0", "<p><br></p>", "0,0", "delete"],
  ["<p><br></p><p><br></p>", "0,0-1,0", "<p><br></p>", "0,0", "forwarddelete"],
  ["<p><br></p><p><br></p>", "1,0-0,0", "<p><br></p>", "0,0", "delete"],
  ["<p><br></p><p><br></p>", "1,0-0,0", "<p><br></p>", "0,0", "forwarddelete"],

  // Different start values
  ["<p>x<br></p><p><br></p>", "1,0",
   // WebKit/Blink like to get rid of the extra <br>
   ["<p>x<br></p>", "<p>x</p>"],
   // The selection should really be collapsed inside the text node, but in the
   // parent is close enough.
   ["0,0,1", "0,1"], "delete"],
  ["<p><br><br></p><p><br></p>", "1,0", "<p><br><br></p>", "0,1", "delete"],
  ["<p><br></p><p><br><br></p>", "1,1",
   "<p><br></p><p><br></p>", "1,0", "delete"],
  ["<p><br><br><br></p>", "0,2", "<p><br><br></p>", "0,1", "delete"],
  ["<p><br></p><p><br><br><br></p>", "1,2",
   "<p><br></p><p><br><br></p>", "1,1", "delete"],
  ["<p><br><br></p><p><br><br></p>", "1,1",
   "<p><br><br></p><p><br></p>", "1,0", "delete"],
  ["<p><br></p><br>", "1", "<p><br></p>", "0,0", "delete"],

  // The trailing \n in these cases is actually significant, because it was
  // necessary to trigger an actual Gecko bug (somehow!).
  ["<p><br></p><p><br></p>\n", "1,0", "<p><br></p>\n", "0,0", "delete"],
  ["<p><br></p><p><br></p>\n", "0,0", "<p><br></p>\n", "0,0", "forwarddelete"],
  ["\n<p><tt>x</tt></p><p><tt><br></tt></p><p><tt><br></tt></p>\n", "3,0,0",
   "\n<p><tt>x</tt></p><p><tt><br></tt></p>\n", "2,0,0", "delete"],
];

div.focus();

for (var i = 0; i < tests.length; i++) {
  test(function() {
    var test = tests[i];
    div.innerHTML = test[0];
    setSelection(test[1]);

    document.execCommand(test[4], false, "");

    if (typeof test[2] == "string") {
      assert_equals(div.innerHTML, test[2], "innerHTML");
    } else {
      assert_in_array(div.innerHTML, test[2], "innerHTML");
    }

    var actualSel = recordSelection();
    var expectedSel = [];
    if (typeof test[3] == "string") {
      test[3] = [test[3]];
    }
    for (var j = 0; j < test[3].length; j++) {
      setSelection(test[3][j]);
      expectedSel.push(recordSelection());
    }
    assertSelectionEquals(actualSel, expectedSel, test[2]);
  }, i + ": " + format_value(tests[i][0]) + " " + tests[i][1] +
  " " + tests[i][4]);
}

function setSelection(selstr) {
  var parts = selstr.split("-");
  var collapsePoint = getPointFromArray(parts[0].split(","));
  getSelection().collapse(collapsePoint[0], collapsePoint[1]);

  if (parts[1]) {
    var extendPoint = getPointFromArray(parts[1].split(","));
    getSelection().extend(extendPoint[0], extendPoint[1]);
  }
}

function getPointFromArray(offsets) {
  var retNode = div, retOffset;
  var offset;
  while (offset = offsets.shift()) {
    if (!offsets.length) {
      retOffset = offset;
    } else {
      retNode = retNode.childNodes[offset];
    }
  }
  return [retNode, retOffset];
}

function recordSelection() {
  return [getSelection().anchorNode, getSelection().anchorOffset,
          getSelection().focusNode, getSelection().focusOffset];
}

function assertSelectionEquals(actual, expected, html) {
  if (typeof expected == "string") {
    expected = [expected];
  }
  var pass = false;
  for (var i = 0; i < expected.length; i++) {
    if (expected[i][0] === actual[0] &&
        expected[i][1] === actual[1] &&
        expected[i][2] === actual[2] &&
        expected[i][3] === actual[3]) {
      pass = true;
      break;
    }
  }

  assert_true(pass, "Wrong selection, expected " + formatSel(expected) +
              ", got " + formatSel(actual) +
              " (in HTML " + format_value(html) + ")");
}

function formatSel(arr) {
  if (arr.length == 1) {
    arr = arr[0];
  }
  if (Array.isArray(arr[0])) {
    var ret = [];
    for (var i = 0; i < arr.length; i++) {
      ret.push(formatSel(arr[i]));
    }
    return ret.join(" or ");
  }
  if (arr[0] == arr[2] && arr[1] == arr[3]) {
    return "collapsed (" + format_value(arr[0]) + ", " + arr[1] + ")";
  }
  return "(" + format_value(arr[0]) + ", " + arr[1] +
         ")-(" + format_value(arr[2]) + ", " + arr[3] + ")";
}
</script>
