async function loadScript(url) { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url; document.head.appendChild(script); return new Promise((resolve) => setTimeout(resolve, 1000)); } Promise.all([ loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js") ]).then(() => { refreshOptims(); setInterval(refreshOptims, 2000); }) var $ = jQuery; function refreshOptims() { console.log(_); let $tr = $(".agendaTable tr"); let roomsRow = _.find($tr, tr => isRoomRow(tr)); let $slottableRows = $tr.filter((idx, tr) => !isRoomRow(tr) && !isBreak(tr)); let roomMap = createRoomMapFrom(roomsRow); let stats = createStatsFrom($slottableRows, roomMap); showOptims(stats); console.log($tr.length); } function showOptims(stats) { $(".max-cell-height").removeClass("max-cell-height"); $(".warning-cell-height").removeClass("warning-cell-height"); _.each(stats.overall, stat => { if(stat.contentHeight === stat.maxRowContentHeight) { $(stat.cell).addClass("max-cell-height"); } else if(stat.isConsideredMax) { $(stat.cell).addClass("warning-cell-height"); } }); const roomErrorStats = Array.from($(".agendaTable .rooms").eq(0).find("td").map((roomIndex,td) => { const roomErrors = sumOf($(".agendaTable tr").map((_, tr) => { return $(tr).find("td").filter((cellColIndex, td) => { return cellColIndex === roomIndex && $(td).hasClass("max-cell-height"); }).length; })); return { room: $(td).find(".main-title").text(), errorsCount: roomErrors}; })) console.table(roomErrorStats.reduce((twolinesResultArray, roomErrors, index) => { twolinesResultArray[0][String.fromCharCode(97+index)] = roomErrors.room; twolinesResultArray[1][String.fromCharCode(97+index)] = `${roomErrors.errorsCount}${roomErrors.errorsCount===Math.max(...roomErrorStats.map(s => s.errorsCount))?' *':''}`; return twolinesResultArray; }, [{},{}])) let MAX_TABLE_WIDTH = 1665; let MAX_TABLE_HEIGHT = 1139; let tableWidth = $(".single-content.single-content-page").width(); let tableHeight = $(".single-content.single-content-page").height(); console.log(`Total printable area width=${tableWidth} ${tableWidth > MAX_TABLE_WIDTH?'/!\\':''} ${MAX_TABLE_WIDTH - tableWidth}`); console.log(`Total printable area height=${tableHeight} ${tableHeight > MAX_TABLE_HEIGHT?'/!\\':''} ${MAX_TABLE_HEIGHT - tableHeight}`); } function createStatsFrom($rows, roomMap) { let stats = { byRow: [], byCol: [], overall: [] }; _.each($rows, (row, rowIdx) => { $(row).find("td").each((___, td) => { var roomClassName = extractRoomClassNameFrom(td); var colIdx = roomMap.byClassName[roomClassName].col; var stat = { row: rowIdx, col: colIdx, contentHeight: $(td).find(".content").height(), rowSpan: Number($(td).attr('rowspan') || 1), cell: td, ...guessKindFromCell(td) }; stats.byRow[rowIdx] = stats.byRow[rowIdx] || []; stats.byCol[colIdx] = stats.byCol[colIdx] || []; stats.byRow[rowIdx][colIdx] = stat; stats.byCol[colIdx][rowIdx] = stat; stats.overall.push(stat); }); }); // Browsing every rows in order, for each, to identify max content height and ranking for every rows compared to this ranking _.each(stats.byRow, cols => { const candidateCols = _(cols) .filter(col => col.slotKind === 'proposal' && col.rowSpan === 1) .value(); if(candidateCols.length) { const contentHeights = _(candidateCols).map(stat => stat.contentHeight).uniq(false).sortBy().reverse().value(); const heightRank = _(contentHeights).map((val, idx) => [val, idx]).zipObject().value(); const max = contentHeights[0]; _.each(candidateCols, stat => { stat.maxRowContentHeight = max; stat.rowContentHeightRank = heightRank[stat.contentHeight]; stat.isConsideredMax = ((max - stat.contentHeight)/max) < 0.05; }); } }); return stats; } function guessKindFromCell(td) { if($(td).hasClass("tba")) { return { slotKind: "tba" }; } else if($(td).hasClass("room-closed")){ return { slotKind: "room-closed" }; } else if($(td).hasClass("break")){ return { slotKind: "break" }; } else if($(td).hasClass("proposal")){ return { slotKind: "proposal", proposalType: extractElClassNameMatching(td, 'proposal_type_is_') }; } else { return { slotKind: "unknown" }; } } function createRoomMapFrom(roomsRow) { var roomMap = { byClassName: {}, byColIndex: [] }; _.each($(roomsRow).find("td.room"), (roomCell, colIdx) => { var roomClassName = extractRoomClassNameFrom(roomCell); var entry = { col: colIdx, roomClassName }; roomMap.byClassName[roomClassName] = entry; roomMap.byColIndex[colIdx] = entry; }); return roomMap; } function extractElClassNameMatching(el, prefix) { return [ ...el.classList ].find(className => className.substr(0, prefix.length) === prefix); } function extractRoomClassNameFrom(el) { const roomEl = $(el).parents("table").find("tr.rooms").eq(0).children().eq($(el).index())[0] return extractElClassNameMatching(roomEl, 'room_is_'); } function isRoomRow(tr) { return $(tr).hasClass("rooms"); } function isBreak(tr) { return $(tr).find("td:eq(0)").hasClass("break"); } function sumOf(numArr){ return Array.from(numArr).reduce((total, num) => num+total, 0); }