Итоговый код третьей функции:
function groupByIP() {
var tables = document.getElementsByTagName('table')
for (var i = 0; i < tables.length; i++) { //для каждой таблицы в документе
var map = new Array() //здесь будем хранить объекты (небольшие ассоциативные массивы)
//с информацией о группах
var rows = tables[i].rows
for (var j = 1; j < rows.length; j++) { //для каждой строки в таблице
var found = false //этот флаг сигнализирует о том, что группа уже существует, т.е. её объект найден
for (var k = 0; k < map.length; k++) { //обходим массив map
if (map[k]['ip'] == rows[j].cells[0].innerHTML) { //группа найдена!
found = true
var index = map[k]['from'] + map[k]['entries'] //вычисляем позицию для вставки новой строки
//(вставляем снизу)
if (index == j) { //если первая строка группы прямо над нами, удалять текущую строку незачем
rows[j].deleteCell(0) //вместо этого удаляем в ней первую и последнюю ячейки
rows[j].deleteCell(-1)
} else {
tables[i].insertRow(index) //вставляем новую строчку в таблицу
if (index <= j) j++; //если вставка произошла выше, корректируем текущий индекс j (!)
for (var m = 3; m >= 1; m--) { //добавляем в новую строку ячейки и копируем содержимое
rows[index].insertCell(0)
rows[index].cells[0].innerHTML = rows[j].cells[m].innerHTML
}
rows[index].cells[0].align = 'center' //копируем атрибуты
tables[i].deleteRow(j) //удаляем старую строку
if (index <= j) j--; //и корректируем индекс в обратную сторону (!!)
}
rows[map[k]['from']].cells[0].rowSpan += 1 //расширяем первую и последнюю ячейки
rows[map[k]['from']].cells[4].rowSpan += 1 //ещё на одну строчку вниз
rows[map[k]['from']].cells[0].style.verticalAlign = 'top' //Ставим выравнивание так, чтобы текст в
rows[map[k]['from']].cells[4].style.verticalAlign = 'top' //огромных ячейках выводился сверху, а не
//посередине
map[k]['entries']++ //увеличиваем размер группы на единицу
for (var l = 0; l < map.length; l++) { //(важно!) проходим по всему массиву объектов и у тех групп,
//которые расположены ниже по таблице, корректируем
//начальные индексы
if (map[l]['from'] > map[k]['from']) {
map[l]['from']++
}
}
break; //дальше по массиву можно не идти
}
}
if (!found) { //если же группа не найдена, создаём для неё новый объект...
var entry = {
from: j,
ip: rows[j].cells[0].innerHTML,
entries: 1
}
map.push(entry) //и помещаем его в массив map
}
}
}
}