/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var _matrices = __webpack_require__(1); var _matrices2 = _interopRequireDefault(_matrices); var _diagram = __webpack_require__(2); var _diagram2 = _interopRequireDefault(_diagram); var _ui = __webpack_require__(4); var _ui2 = _interopRequireDefault(_ui); var _sorter = __webpack_require__(3); var _sorter2 = _interopRequireDefault(_sorter); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } __webpack_require__(5); __webpack_require__(6); __webpack_require__(7); __webpack_require__(8); __webpack_require__(9); __webpack_require__(10); var main = { changeEvent: function changeEvent(e) { var target = _ui2.default.findRoot(e.target); main.setState({ eventKey: target.getAttribute(_ui2.default.DATA.EVENT) }); main.updateUI(); }, changeSort: function changeSort(e) { var target = _ui2.default.findRoot(e.target); main.setState({ sort: target.getAttribute(_ui2.default.DATA.SORT) }); main.updateUI(); }, changeRound: function changeRound(e) { var target = _ui2.default.findRoot(e.target); var r = target.getAttribute(_ui2.default.DATA.ROUND); var state = main.getState(); var roundsToShow = state.rounds ? state.rounds.split(',') : []; var i = roundsToShow.indexOf(r); i === -1 ? roundsToShow.push(r) : roundsToShow.splice(i, 1); main.setState({ rounds: roundsToShow }); main.updateUI(); }, getRounds: function getRounds(eventKey) { var rounds = {}; main.json.tourneys[eventKey].games.forEach(function (game) { var name = _ui2.default.getRoundName(main.json.rounds[game.rId]); if (rounds[name] === undefined) { rounds[name] = []; } rounds[name].push({ id: game.rId, name: name }); }); return rounds; }, generateUI: function generateUI() { var state = main.getState(); _ui2.default.buildEventsPane(main.changeEvent); _ui2.default.buildSortPane(main.changeSort); _ui2.default.buildRoundsPane(main.changeRound); }, updateUI: function updateUI() { var state = main.getState(); var matrix = _matrices2.default.buildMatrix(main.json, state.eventKey); _diagram2.default.clear(); _diagram2.default.build(main.json, state.eventKey, state.sort, _ui2.default.SORT_TYPES, matrix); _ui2.default.updateEventsPane(state.eventKey); _ui2.default.updateSortPane(state.sort); _ui2.default.updateRoundsPane(state.rounds.split(','), main.json.rounds); }, fetch: function fetch(url) { return new Promise(function (resolve, reject) { var listener = function listener(_ref) { var req = _ref.srcElement; req.status === 200 ? resolve(req.responseText) : reject("busted"); }; var req = new XMLHttpRequest(); req.addEventListener('load', listener); req.open('GET', url); req.send(); }); }, initJSON: function initJSON(strData) { main.json = JSON.parse(strData); }, getState: function getState() { var params = window.location.href.split('?')[1]; if (!params) { return {}; } return params.split('&').reduce(function (acc, v) { var tmp = v.split('='); acc[tmp[0]] = tmp[1]; return acc; }, {}); }, initState: function initState() { var state = main.getState(); if (state.eventKey === undefined) { state.eventKey = "1930"; } if (state.sort === undefined) { state.sort = null; } if (state.rounds === undefined) { state.rounds = Object.values(_ui2.default.ROUND_TYPES); } main.setState(state); }, setState: function setState(next) { var state = main.getState(); var url = window.location.href.split('?')[0]; state.eventKey = next.eventKey || state.eventKey; state.rounds = next.rounds || state.rounds; state.sort = next.sort || state.sort || null; var params = []; for (var key in state) { params.push(key + '=' + state[key]); } history.pushState(null, null, url + '?' + params.join('&')); } }; main.fetch('worldcup.json').then(main.initJSON).then(main.initState).then(main.generateUI).then(main.updateUI); /***/ }, /* 1 */ /***/ function(module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Matrices = { // A x A array of nulls; null-null relationships are omitted in chord diagram. createEmptyMatrix: function createEmptyMatrix(len) { var empty = Array.apply(null, Array(len)); return empty.map(function () { return empty.map(function () { return null; }); }); }, // Scalar data points (sum of goals scored). buildMatrix: function buildMatrix(json, eventKey) { var teams = json.tourneys[eventKey].teams; var matrix = Matrices.createEmptyMatrix(teams.length); json.tourneys[eventKey].games.forEach(function (g) { var i1 = teams.findIndex(function (v) { return v.tId === g.t1; }); var i2 = teams.findIndex(function (v) { return v.tId === g.t2; }); matrix[i1][i2] = g.s1 + g.se1 + g.sp1; matrix[i2][i1] = g.s2 + g.se2 + g.sp2; }, []); return matrix; }, swapIndices: function swapIndices(arr, i, j) { var tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; return arr; } }; exports.default = Matrices; /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _sorter = __webpack_require__(3); var _sorter2 = _interopRequireDefault(_sorter); var _matrices = __webpack_require__(1); var _matrices2 = _interopRequireDefault(_matrices); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var Diagram = { clear: function clear() { return d3.select('svg').selectAll("*").remove(); }, swapGroupArcs: function swapGroupArcs(chords, i, j) { if (i < 0 || j < 0 || i === j) { return chords; } // Determine which arc is closer to 0, so earlier arc always swaps with later. var f = chords.groups[i].endAngle < chords.groups[j].endAngle ? i : j; var s = chords.groups[i].endAngle < chords.groups[j].endAngle ? j : i; var fst = Object.assign({}, chords.groups[f]); var snd = Object.assign({}, chords.groups[s]); var fstAngle = fst.endAngle - fst.startAngle; var sndAngle = snd.endAngle - snd.startAngle; var offsetAngle = sndAngle - fstAngle; // Bring first forward. chords.groups[f].startAngle = snd.endAngle - fstAngle; chords.groups[f].endAngle = snd.endAngle; chords.groups[f].index = snd.index; // Bring second back. chords.groups[s].startAngle = fst.startAngle; chords.groups[s].endAngle = fst.startAngle + sndAngle; chords.groups[s].index = fst.index; // Bump other groups forward. for (var ii = f + 1; ii < s; ii++) { chords.groups[ii].startAngle += offsetAngle; chords.groups[ii].endAngle += offsetAngle; } chords.forEach(function (v) { if (v.source.index === f) { v.source.startAngle += snd.endAngle - fst.endAngle; v.source.endAngle += snd.endAngle - fst.endAngle; } if (v.target.index === f) { v.target.startAngle += snd.endAngle - fst.endAngle; v.target.endAngle += snd.endAngle - fst.endAngle; } if (v.source.index === s) { v.source.startAngle -= snd.startAngle - fst.startAngle; v.source.endAngle -= snd.startAngle - fst.startAngle; } if (v.target.index === s) { v.target.startAngle -= snd.startAngle - fst.startAngle; v.target.endAngle -= snd.startAngle - fst.startAngle; } if (v.source.index > f && v.source.index < s) { v.source.startAngle += offsetAngle; v.source.endAngle += offsetAngle; } if (v.target.index > f && v.target.index < s) { v.target.startAngle += offsetAngle; v.target.endAngle += offsetAngle; } if ((v.source.index === f || v.source.index === s) && (v.target.index === f || v.target.index === s)) { var _tmp = v.source.index; v.source.index = v.target.index; v.target.index = _tmp; } else if (v.source.index === f) { v.source.index = s; } else if (v.target.index === f) { v.target.index = s; } else if (v.source.index === s) { v.source.index = f; } else if (v.target.index === s) { v.target.index = f; } }); // Swap array positions. var tmp = chords.groups[f]; chords.groups[f] = chords.groups[s]; chords.groups[s] = tmp; return chords; }, swapGroups: function swapGroups(data, eventKey, chords, i, j) { Diagram.swapGroupArcs(chords, i, j); _matrices2.default.swapIndices(data.tourneys[eventKey].teams, i, j); return chords; }, getCountryName: function getCountryName(data, eventKey, n) { var team = data.tourneys[eventKey].teams[n]; return data.countries[team.cId]; }, getPopulation: function getPopulation(data, eventKey, n) { var team = data.tourneys[eventKey].teams[n]; return team.p; }, getGoalsFor: function getGoalsFor(data, eventKey, n) { var team = data.tourneys[eventKey].teams[n]; return team.gf; }, getGoalsAgainst: function getGoalsAgainst(data, eventKey, n) { var team = data.tourneys[eventKey].teams[n]; return team.ga; }, build: function build(data, eventKey, sort, SORT_TYPES, matrix) { var svg = d3.select("svg"), width = +svg.attr("width"), height = +svg.attr("height"), outerArcThickness = 5, outerRadius = Math.min(width, height) * 0.5 - 125, innerRadius = outerRadius - outerArcThickness; var chords = d3.chord().padAngle(0.05).call(null, matrix); var sortedChords = chords; switch (sort) { case SORT_TYPES.COUNTRY: sortedChords = _sorter2.default.sort(chords, 0, chords.groups.length - 1, Diagram.getCountryName.bind(null, data, eventKey), Diagram.swapGroups.bind(null, data, eventKey)); break; case SORT_TYPES.GOALS: sortedChords = _sorter2.default.sort(chords, 0, chords.groups.length - 1, Diagram.getGoalsFor.bind(null, data, eventKey), Diagram.swapGroups.bind(null, data, eventKey)); break; case SORT_TYPES.POPULATION: sortedChords = _sorter2.default.sort(chords, 0, chords.groups.length - 1, Diagram.getPopulation.bind(null, data, eventKey), Diagram.swapGroups.bind(null, data, eventKey)); break; } var arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius); var ribbon = d3.ribbon().radius(innerRadius); // const color = d3.scaleOrdinal(d3.schemeCategory20); // const color = d3.scaleOrdinal(d3.schemeCategory10); var len = data.tourneys[eventKey].teams.length; // const color = d3.scaleLinear().domain([0, len]).range(["#edf8b1", "#081d58"]).interpolate(d3.interpolateRgb); // const color = d3.scaleLinear().domain([0, len]).range(["#aaa", "green"]).interpolate(d3.interpolateRgb); var color = d3.scaleLinear().domain([0, len]).range(["gainsboro", "darkgreen"]).interpolate(d3.interpolateRgb); // const colors = ["#ffffcc","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#0c2c84"]; // const colors = ["#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"]; // const color = i => colors[i % colors.length]; var g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")").datum(sortedChords); var group = g.append("g").attr("class", "groups").selectAll("g").data(function (chords) { return chords.groups; }).enter().append("g"); group.append("path").style("fill", function (d) { return color(d.index); }).style("stroke", function (d) { return d3.rgb(color(d.index)).darker(); }).attr("d", arc); g.append("g").attr("class", "ribbons").selectAll("path").data(function (chords) { return chords; }).enter().append("path").attr("d", ribbon).attr("data-round-id", function (d) { var t1 = data.tourneys[eventKey].teams[d.source.index]; var t2 = data.tourneys[eventKey].teams[d.target.index]; var game = data.tourneys[eventKey].games.find(function (v) { return (v.t1 === t1.tId || v.t1 === t2.tId) && (v.t2 === t1.tId || v.t2 === t2.tId); }); return game.rId; }).style("fill", function (d) { return color(d.target.index); }).style("stroke", function (d) { return d3.rgb(color(d.target.index)).darker(); }).classed("ribbon", true).append("title").text(function (d) { var t1 = data.tourneys[eventKey].teams[d.source.index]; var t2 = data.tourneys[eventKey].teams[d.target.index]; var c1 = data.countries[t1.cId]; var c2 = data.countries[t2.cId]; var game = data.tourneys[eventKey].games.find(function (v) { return (v.t1 === t1.tId || v.t1 === t2.tId) && (v.t2 === t1.tId || v.t2 === t2.tId); }); var e1 = game.se1 ? '(+' + game.se1 + ' in extended time)' : ''; var e2 = game.se2 ? '(+' + game.se2 + ' in extended time)' : ''; var p1 = game.sp1 ? '(+' + game.sp1 + ' in penalties)' : ''; var p2 = game.sp2 ? '(+' + game.sp2 + ' in penalties)' : ''; return c1 + ': ' + game.s1 + ' ' + e1 + ' ' + p1 + '\n' + c2 + ': ' + game.s2 + ' ' + e2 + ' ' + p2; }); group.append("text").each(function (d) { d.angle = (d.startAngle + d.endAngle) / 2; }).attr("dy", ".35em").attr("transform", function (d) { return "rotate(" + (d.angle * 180 / Math.PI - 91) + ")" + "translate(" + (innerRadius + 26) + ")" + (d.angle > Math.PI ? "rotate(180)" : ""); }).style("text-anchor", function (d) { return d.angle > Math.PI ? "end" : null; }).text(function (d) { var team = data.tourneys[eventKey].teams[d.index]; var metric = ''; switch (sort) { case SORT_TYPES.GOALS: metric = '(' + team.gf + ')'; break; case SORT_TYPES.POPULATION: metric = '(' + Number(team.p).toLocaleString() + ')'; break; } return data.countries[team.cId] + ' ' + metric; }); } }; exports.default = Diagram; /***/ }, /* 3 */ /***/ function(module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Sorter = { sort: function sort(chords, start, end, getVal, swap) { if (end <= start) { return chords; } var left = start; var right = end; var pivot = Math.floor((right + left) / 2); var pivotval = getVal(pivot); var tmp; var rval; while (left <= right) { while (getVal(left) < pivotval) { left++; } while (getVal(right) > pivotval) { right--; } if (left <= right) { chords = swap(chords, left, right); left++; right--; } break; } Sorter.sort(chords, start, right, getVal, swap); Sorter.sort(chords, left, end, getVal, swap); return chords; } }; exports.default = Sorter; /***/ }, /* 4 */ /***/ function(module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var UI = { CLASSNAMES: { EVENT: { ITEM: 'event-item', YEAR: 'event-year', FLAG: 'event-flag', LOCATION: 'event-location' }, SORT: { ITEM: 'sort-item', TEXT: 'sort-text' }, ROUND: { ITEM: 'round-item', HIDE: 'round-hide', TEXT: 'round-text' } }, SORT_TYPES: { GOALS: 'goals', COUNTRY: 'country', POPULATION: 'population' }, ROUND_TYPES: { PRELIM: 'prelims', ROUNDOF16: 'round-of-16', QUARTERFINAL: 'quarterfinals', SEMIFINAL: 'semifinals', CONSOLATION: 'consolation', FINAL: 'finals' }, DATA: { ROOT: 'data-root', EVENT: 'data-event-key', ROUND: 'data-round-key', SORT: 'data-sort-key' }, I18N: { HIDE: 'hide', SHOW: 'show' }, findRoot: function findRoot(node) { while (node !== document.body && node.getAttribute(UI.DATA.ROOT) === null) { node = node.parentNode; } return node; }, clearActive: function clearActive(nodes) { nodes.forEach(function (node) { var classes = node.className.split(' '); classes.splice(1, classes.indexOf('active')); node.className = classes.join(' '); }); }, updateEventsPane: function updateEventsPane(eventKey) { var eventItems = document.querySelectorAll('.' + UI.CLASSNAMES.EVENT.ITEM); UI.clearActive(eventItems); var activeEventNode = document.querySelector('[' + UI.DATA.EVENT + '="' + eventKey + '"'); activeEventNode.className += ' active'; }, updateRoundsPane: function updateRoundsPane(roundsToShow, allRounds) { var roundsItems = document.querySelectorAll('.' + UI.CLASSNAMES.ROUND.ITEM); UI.clearActive(roundsItems); roundsItems.forEach(function (item) { var type = item.getAttribute(UI.DATA.ROUND); var hide = item.querySelector('.' + UI.CLASSNAMES.ROUND.HIDE); hide.innerHTML = UI.I18N.SHOW; if (roundsToShow.indexOf(type) > -1) { item.className += ' active'; hide.innerHTML = UI.I18N.HIDE; } }); var ribbons = document.querySelectorAll('.ribbon'); ribbons.forEach(function (ribbon) { var name = allRounds[ribbon.getAttribute("data-round-id")]; var type = UI.getRoundType(name); roundsToShow.indexOf(type) === -1 ? ribbon.style.display = 'none' : ribbon.style.display = 'inline'; }); }, updateSortPane: function updateSortPane(sort) { var sortItems = document.querySelectorAll('.sort-item'); UI.clearActive(sortItems); sortItems.forEach(function (node) { if (node.getAttribute(UI.DATA.SORT) === sort) { node.className += ' active'; } }); }, buildEventsPane: function buildEventsPane(onClick) { var eventsList = [{ year: 1930, location: "Uruguay", icon: "uy" }, { year: 1934, location: "Italy", icon: "it" }, { year: 1938, location: "France", icon: "fr" }, { year: 1954, location: "Switzerland", icon: "ch" }, { year: 1958, location: "Sweden", icon: "se" }, { year: 1962, location: "Chile", icon: "cl" }, { year: 1966, location: "England", icon: "gb" }, { year: 1970, location: "Mexico", icon: "mx" }, { year: 1974, location: "West Germany", icon: "de" }, { year: 1978, location: "Argentina", icon: "ar" }, { year: 1982, location: "Spain", icon: "es" }, { year: 1986, location: "Mexico", icon: "mx" }, { year: 1990, location: "Italy", icon: "it" }, { year: 1994, location: "USA", icon: "us" }, { year: 1998, location: "France", icon: "fr" }, { year: 2002, location: "Japan / South Korea", icon: "jp" }, { year: 2006, location: "Germany", icon: "de" }, { year: 2010, location: "Johannesburg", icon: "za" }, { year: 2014, location: "Brazil", icon: "br" }]; var eventsDiv = document.querySelector('.events'); while (eventsDiv.hasChildNodes()) { eventsDiv.firstChild.remove(); } eventsList.forEach(function (evt) { var item = document.createElement('div'); var year = document.createElement('div'); var location = document.createElement('div'); var flag = document.createElement('div'); var flagIcon = document.createElement('div'); item.className = UI.CLASSNAMES.EVENT.ITEM; item.addEventListener('click', onClick); item.setAttribute(UI.DATA.EVENT, evt.year); item.setAttribute(UI.DATA.ROOT, true); year.className = UI.CLASSNAMES.EVENT.YEAR; year.innerHTML = evt.year; location.className = UI.CLASSNAMES.EVENT.LOCATION; location.innerHTML = evt.location; flag.className = UI.CLASSNAMES.EVENT.FLAG; flagIcon.className = 'flag-icon flag-icon-' + evt.icon; flag.appendChild(flagIcon); item.appendChild(flag); item.appendChild(year); item.appendChild(location); eventsDiv.appendChild(item); }); }, buildSortPane: function buildSortPane(onClick) { var sortList = [{ text: 'Order by continent', value: null }, { text: 'Order by goals scored', value: UI.SORT_TYPES.GOALS }, { text: 'Order by country name', value: UI.SORT_TYPES.COUNTRY }, { text: 'Order by country population', value: UI.SORT_TYPES.POPULATION }]; var sortDiv = document.querySelector('.sort'); while (sortDiv.hasChildNodes()) { sortDiv.firstChild.remove(); } sortList.forEach(function (sort) { var item = document.createElement('div'); var text = document.createElement('div'); item.className = UI.CLASSNAMES.SORT.ITEM; item.addEventListener('click', onClick); item.setAttribute(UI.DATA.SORT, sort.value); item.setAttribute(UI.DATA.ROOT, true); text.className = UI.CLASSNAMES.SORT.TEXT; text.innerHTML = sort.text; item.appendChild(text); sortDiv.appendChild(item); }); }, buildRoundsPane: function buildRoundsPane(onClick) { var roundsList = [{ text: 'Preliminaries', value: UI.ROUND_TYPES.PRELIM }, { text: 'Round of 16', value: UI.ROUND_TYPES.ROUNDOF16 }, { text: 'Quarterfinals', value: UI.ROUND_TYPES.QUARTERFINAL }, { text: 'Semifinals', value: UI.ROUND_TYPES.SEMIFINAL }, { text: 'Consolation', value: UI.ROUND_TYPES.CONSOLATION }, { text: 'Final', value: UI.ROUND_TYPES.FINAL }]; var roundsDiv = document.querySelector('.rounds'); while (roundsDiv.hasChildNodes()) { roundsDiv.firstChild.remove(); } roundsList.forEach(function (round) { var item = document.createElement('div'); item.className = UI.CLASSNAMES.ROUND.ITEM; item.addEventListener('click', onClick); item.setAttribute(UI.DATA.ROUND, round.value); item.setAttribute(UI.DATA.ROOT, true); var hide = document.createElement('div'); hide.className = UI.CLASSNAMES.ROUND.HIDE; hide.innerHTML = 'hide'; var text = document.createElement('div'); text.className = UI.CLASSNAMES.ROUND.TEXT; text.innerHTML = round.text; item.appendChild(text); item.appendChild(hide); roundsDiv.appendChild(item); }); }, getRoundType: function getRoundType(name) { var name2 = name.match(/^Matchday/) ? 'First round' : name; switch (name2) { case 'Preliminary round': case 'First round': case 'First round replays': case 'Group-1 Play-off': case 'Group-2 Play-off': case 'Group-3 Play-off': case 'Group-4 Play-off': return UI.ROUND_TYPES.PRELIM; case 'Round of 16': return UI.ROUND_TYPES.ROUNDOF16; case 'Third place match': case 'Third-place match': case 'Match for third place': case 'Third place play-off': case 'Third-place play-off': return UI.ROUND_TYPES.CONSOLATION; case 'Quarterfinals': case 'Quarter-finals': case 'Quarter-finals replays': return UI.ROUND_TYPES.QUARTERFINAL; case 'Semifinals': case 'Semi-finals': return UI.ROUND_TYPES.SEMIFINAL; case 'Final': case 'Finals': return UI.ROUND_TYPES.FINAL; } console.error('Unknown round name: ' + name); return name; } }; exports.default = UI; /***/ }, /* 5 */ /***/ function(module, exports) { // removed by extract-text-webpack-plugin /***/ }, /* 6 */ /***/ function(module, exports) { // removed by extract-text-webpack-plugin /***/ }, /* 7 */ /***/ function(module, exports) { // removed by extract-text-webpack-plugin /***/ }, /* 8 */ /***/ function(module, exports) { // removed by extract-text-webpack-plugin /***/ }, /* 9 */ /***/ function(module, exports) { // removed by extract-text-webpack-plugin /***/ }, /* 10 */ /***/ function(module, exports) { // removed by extract-text-webpack-plugin /***/ } /******/ ]);