var playoff = { settings: { 'winner_h_offset': 5, 'loser_h_offset': 20, 'place_winner_h_offset': 10, 'place_loser_h_offset': 15, 'finish_winner_h_offset': 5, 'finish_loser_h_offset': 20, 'winner_v_offset': -10, 'loser_v_offset': 10, 'place_winner_v_offset': 2, 'place_loser_v_offset': 9, 'finish_winner_v_offset': -4, 'finish_loser_v_offset': 4, 'loser_colour': '#ff0000', 'winner_colour': '#00ff00', 'place_loser_colour': '#dddd00', 'place_winner_colour': '#00dddd', 'finish_loser_colour': '#ff0000', 'finish_winner_colour': '#00ff00' }, drawLine: function(ctx, line) { ctx.beginPath(); ctx.moveTo(line[0], line[1]); ctx.lineTo(line[2], line[3]); ctx.stroke(); }, loadSettings: function(canvas, defaults) { for (var setting in defaults) { var attr = 'data-' + setting.replace(/_/g, '-'); var attr_value = canvas.getAttribute(attr); if (attr_value) { if (attr.substr(attr.length-6) == 'offset') { attr_value = parseInt(attr_value); } defaults[setting] = attr_value; } } return defaults; }, run: function() { var boxes = document.getElementsByClassName('playoff_matchbox'); var lines = { 'winner': {}, 'loser': {}, 'place-winner': {}, 'place-loser': {}, 'finish-winner': {}, 'finish-loser': {} }; var boxes_idx = {}; for (var b = 0; b < boxes.length; b++) { var id = boxes[b].getAttribute('data-id'); boxes_idx[id] = boxes[b]; for (var attr in lines) { var value = boxes[b].getAttribute('data-' + attr); if (value) { if (!lines[attr][value]) { lines[attr][value] = []; } lines[attr][value].push(id); } } } var canvas = document.getElementById('playoff_canvas'); this.settings = this.loadSettings(canvas, this.settings); var lineMethods = { 'place-winner': 'to', 'place-loser': 'to', 'finish-winner': 'midpoint', 'finish-loser': 'midpoint', 'winner': 'midpoint', 'loser': 'midpoint' }; var lineCalculator = { correctLines: function(hLines, vLine, comparator) { for (var l1 in hLines) { for (var l2 in hLines) { hLines[l1][2] = comparator(hLines[l1][2], hLines[l2][2]); hLines[l2][2] = hLines[l1][2]; } } for (var l1 in hLines) { vLine[0] = vLine[2] = comparator(hLines[l1][2], vLine[2]); vLine[1] = Math.min(vLine[1], hLines[l1][3]); vLine[3] = Math.max(vLine[3], hLines[l1][3]); } }, template: function() { return { hFrom: [], vFrom: [0, canvas.height, 0, 0], hTo: [], vTo: [canvas.width, canvas.height, canvas.width, 0], midpoints: [] }; }, from: function(from, to, hOffset, vOffset) { var lines = this.template(); for (var f = 0; f < from.length; f++) { var box = boxes_idx[from[f]]; var line = [ Math.floor(parseInt(box.offsetLeft) + parseInt(box.clientWidth)), Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset), Math.floor(parseInt(box.offsetLeft) + parseInt(box.clientWidth) + hOffset), Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset) ]; lines.hFrom.push(line); } this.correctLines(lines.hFrom, lines.vFrom, Math.max); for (var t = 0; t < to.length; t++) { var box = boxes_idx[to[t]]; var line = [ Math.floor(parseInt(box.offsetLeft)), Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset), lines.vFrom[0], Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset) ]; lines.hTo.push(line); } this.correctLines(lines.hTo, lines.vTo, Math.min); lines.midpoints = [ [lines.vFrom[0], lines.vFrom[1]], [lines.vTo[0], lines.vTo[1]] ]; return lines; }, to: function(from, to, hOffset, vOffset) { var lines = this.template(); for (var t = 0; t < to.length; t++) { var box = boxes_idx[to[t]]; var line = [ parseInt(box.offsetLeft), Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset), Math.floor(parseInt(box.offsetLeft) - hOffset), Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset) ]; lines.hTo.push(line); } this.correctLines(lines.hTo, lines.vTo, Math.min); for (var f = 0; f < from.length; f++) { var box = boxes_idx[from[f]]; var line = [ Math.floor(parseInt(box.offsetLeft) + parseInt(box.clientWidth)), Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset), lines.vTo[0], Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset) ]; lines.hFrom.push(line); } this.correctLines(lines.hFrom, lines.vFrom, Math.max); lines.midpoints = [ [lines.vFrom[0], lines.vFrom[1]], [lines.vTo[0], lines.vTo[1]] ]; return lines; }, midpoint: function(from, to, hOffset, vOffset) { var lines = this.template(); for (var f = 0; f < from.length; f++) { var box = boxes_idx[from[f]]; var line = [ Math.floor(parseInt(box.offsetLeft) + parseInt(box.clientWidth)), Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset), Math.floor(parseInt(box.offsetLeft) + parseInt(box.clientWidth) + hOffset), Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset) ]; lines.hFrom.push(line); } this.correctLines(lines.hFrom, lines.vFrom, Math.max); for (var t = 0; t < to.length; t++) { var box = boxes_idx[to[t]]; var line = [ parseInt(box.offsetLeft), Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset), Math.floor(parseInt(box.offsetLeft) - hOffset), Math.floor(parseInt(box.offsetTop) + 0.5 * parseInt(box.clientHeight) + vOffset) ]; lines.hTo.push(line); } this.correctLines(lines.hTo, lines.vTo, Math.min); lines.midpoints = [ [ (lines.vFrom[0] + lines.vFrom[2]) / 2, (lines.vFrom[1] + lines.vFrom[3]) / 2 ], [ hOffset / 2 + (lines.vFrom[0] + lines.vFrom[2] + lines.vTo[0] + lines.vTo[2]) / 4, (lines.vFrom[1] + lines.vFrom[3]) / 2 ], [ hOffset / 2 + (lines.vFrom[0] + lines.vFrom[2] + lines.vTo[0] + lines.vTo[2]) / 4, (lines.vTo[1] + lines.vTo[3]) / 2 ], [ (lines.vTo[0] + lines.vTo[2]) / 2, (lines.vTo[1] + lines.vTo[3]) / 2 ] ]; for (var h in lines.hTo) { lines.hTo[h][2] = Math.max( lines.hTo[h][2], lines.midpoints[2][0] ); } for (var h in lines.hFrom) { lines.hFrom[h][2] = Math.min( lines.hFrom[h][2], lines.midpoints[0][0] ); } return lines; } }; var ctx = canvas.getContext('2d'); for (var type in lines) { styleType = type.replace('-', '_'); ctx.strokeStyle = this.settings[styleType + '_colour']; for (var from in lines[type]) { var to = lines[type][from]; from = from.split(' '); var linesToDraw = lineCalculator[lineMethods[type]]( from, to, this.settings[styleType + '_h_offset'], this.settings[styleType + '_v_offset']); for (var l in linesToDraw.hFrom) { this.drawLine(ctx, linesToDraw.hFrom[l]); } this.drawLine(ctx, linesToDraw.vFrom); for (var l in linesToDraw.hTo) { this.drawLine(ctx, linesToDraw.hTo[l]); } this.drawLine(ctx, linesToDraw.vTo); for (var m = 0; m < linesToDraw.midpoints.length-1; m++) { if (linesToDraw.midpoints[m][0] <= linesToDraw.midpoints[m+1][0]) { this.drawLine(ctx, [ linesToDraw.midpoints[m][0], linesToDraw.midpoints[m][1], linesToDraw.midpoints[m+1][0], linesToDraw.midpoints[m+1][1] ]); } } } } } }; playoff.run();