When values are pasted into a bigger range, the values should be repeated as needed to fill the range
ECMAScript 6

If you're able to use ECMAScript 6 then arrow functions could help clean up some of your code.

var dataArr = data.split('\n').map(row =&gt; row.split('\t'));




Unit Testing

You're logging the results, but it would be extremely helpful for your refactoring process to create some test cases for your function.

function test(input, expected) {
  var result = fillIn(input.data, input.numRows, input.numCols);

  function matrixEquals(m1, m2) {
    if (m1.length !== m2.length) return false;

    for (var r = 0; r &lt; m1.length; r++) {
      if (m1[r].length !== m2[r].length) return false;

      for (var c = 0; c &lt; m1[r].length; c++) {
        if (m1[r][c] !== m2[r][c]) return false;
      }
    }

    return true;
  }

  if (!matrixEquals(expected, result)) {
    throw "Test Failed for: input=" + JSON.stringify(input) +
      ", result=" + JSON.stringify(result);
  }
}

test({
  data: "foo\tbar",
  numRows: 2,
  numCols: 2
}, [
  ["foo", "bar"],
  ["foo", "bar"]
]);

test({
  data: "foo\tbar\nfat\tcat",
  numRows: 4,
  numCols: 4
}, [
  ["foo", "bar", "foo", "bar"],
  ["fat", "cat", "fat", "cat"],
  ["foo", "bar", "foo", "bar"],
  ["fat", "cat", "fat", "cat"]
]);


If you write your test cases well, you'll be able to tell if you broke your function simply by running the test cases.



Do you really even need to create all the arrays?

You could just extend the initial array directly:

function fillIn(data, numRows, numCols) {
  var matrix = data.split('\n').map(row =&gt; row.split('\t'));

  var missingRows = numRows - matrix.length;
  for (var r = 0; r &lt; missingRows; r++) {
    matrix.push(matrix[r].slice());
  }

  var missingCols = numCols - matrix[0].length;
  for (var c = 0; c &lt; missingCols; c++) {
    matrix.forEach(function(row) {
      row.push(row[c]);
    });
  }

  return matrix;
}