parent
df6e49ac32
commit
0214ce1068
17 changed files with 1112 additions and 15 deletions
@ -1,4 +1,5 @@ |
|||||||
data |
data |
||||||
node_modules |
node_modules |
||||||
|
npm-debug.log |
||||||
tmp |
tmp |
||||||
yarn.lock |
yarn.lock |
||||||
|
@ -0,0 +1,845 @@ |
|||||||
|
/******/ (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
|
||||||
|
|
||||||
|
/***/ } |
||||||
|
/******/ ]); |
@ -0,0 +1,17 @@ |
|||||||
|
* { |
||||||
|
border-sizing: border-box; |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
padding: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
.visualization { |
||||||
|
height: 700px; |
||||||
|
margin: 0 auto; |
||||||
|
position: relative; |
||||||
|
width: 1100px; |
||||||
|
} |
@ -0,0 +1,171 @@ |
|||||||
|
* { |
||||||
|
border-sizing: border-box; |
||||||
|
margin: 0; |
||||||
|
padding: 0; } |
||||||
|
|
||||||
|
body { |
||||||
|
font-family: sans-serif; |
||||||
|
padding: 20px; } |
||||||
|
|
||||||
|
.visualization { |
||||||
|
height: 700px; |
||||||
|
margin: 0 auto; |
||||||
|
position: relative; |
||||||
|
width: 1100px; } |
||||||
|
.options { |
||||||
|
color: white; |
||||||
|
left: 900px; |
||||||
|
height: 100%; |
||||||
|
min-width: 200px; |
||||||
|
position: absolute; |
||||||
|
text-align: right; |
||||||
|
top: 0; |
||||||
|
width: 200px; } |
||||||
|
|
||||||
|
.option-divider { |
||||||
|
margin: 19px 0 19px 25%; |
||||||
|
width: 75%; } |
||||||
|
|
||||||
|
.option-item { |
||||||
|
background: teal; |
||||||
|
border-radius: 16px 0 0 16px; |
||||||
|
cursor: pointer; |
||||||
|
height: 24px; |
||||||
|
line-height: 24px; |
||||||
|
margin-bottom: 4px; |
||||||
|
padding: 4px 16px 4px 4px; } |
||||||
|
|
||||||
|
.option-item:hover { |
||||||
|
opacity: 0.7; } |
||||||
|
|
||||||
|
.option-item.inactive { |
||||||
|
opacity: 0.2; } |
||||||
|
|
||||||
|
.option-text { |
||||||
|
display: inline-block; |
||||||
|
font-size: 12px; } |
||||||
|
|
||||||
|
.option-toggle { |
||||||
|
display: inline-block; |
||||||
|
margin-left: 25px; } |
||||||
|
.events { |
||||||
|
display: table; |
||||||
|
height: 100%; |
||||||
|
left: 0; |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
width: 200px; } |
||||||
|
|
||||||
|
.event-item { |
||||||
|
/*background: #CBFFC0;*/ |
||||||
|
border-color: #fff; |
||||||
|
/*border-radius: 0 16px 16px 0;*/ |
||||||
|
border-style: solid; |
||||||
|
border-width: 1px 0; |
||||||
|
cursor: pointer; |
||||||
|
filter: grayscale(100%); |
||||||
|
height: 24px; |
||||||
|
margin-bottom: 4px; |
||||||
|
opacity: 0.2; |
||||||
|
padding: 4px 4px 4px 44px; |
||||||
|
position: relative; } |
||||||
|
|
||||||
|
.event-item.active, |
||||||
|
.event-item.active:hover { |
||||||
|
background: #fdfdfd; |
||||||
|
border-color: #aaa; |
||||||
|
filter: grayscale(0%); |
||||||
|
opacity: 1; } |
||||||
|
|
||||||
|
.event-item:hover { |
||||||
|
filter: grayscale(0%); |
||||||
|
opacity: 0.8; } |
||||||
|
|
||||||
|
.event-flag { |
||||||
|
font-size: 0; |
||||||
|
left: 4px; |
||||||
|
position: absolute; |
||||||
|
top: 4px; } |
||||||
|
|
||||||
|
.event-flag .flag-icon { |
||||||
|
font-size: 24px; } |
||||||
|
|
||||||
|
.event-location { |
||||||
|
font-size: 10px; |
||||||
|
line-height: 10px; } |
||||||
|
|
||||||
|
.event-year { |
||||||
|
font-size: 13px; |
||||||
|
line-height: 14px; } |
||||||
|
.diagram { |
||||||
|
font-size: 10px; |
||||||
|
height: 700px; |
||||||
|
left: 200px; |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
width: 800px; |
||||||
|
z-index: 0; } |
||||||
|
|
||||||
|
.group-tick line { |
||||||
|
stroke: #ddd; } |
||||||
|
|
||||||
|
.ribbon { |
||||||
|
fill-opacity: 0.4; |
||||||
|
stroke-width: 1; |
||||||
|
stroke-opacity: 0.1; } |
||||||
|
|
||||||
|
.ribbon:hover { |
||||||
|
fill-opacity: 1; |
||||||
|
stroke-opacity: 1; } |
||||||
|
.sort { |
||||||
|
position: absolute; |
||||||
|
right: 0; |
||||||
|
text-align: right; |
||||||
|
top: 0; |
||||||
|
width: 200px; |
||||||
|
z-index: 1; } |
||||||
|
|
||||||
|
.sort-item { |
||||||
|
border: 1px solid brickred; |
||||||
|
cursor: pointer; |
||||||
|
font-size: 13px; |
||||||
|
line-height: 34px; |
||||||
|
padding: 0 10px; } |
||||||
|
|
||||||
|
.sort-item:hover { |
||||||
|
background: yellow; } |
||||||
|
|
||||||
|
.sort-item.active { |
||||||
|
background: red; } |
||||||
|
.rounds { |
||||||
|
bottom: 0; |
||||||
|
color: #000; |
||||||
|
position: absolute; |
||||||
|
right: 0; |
||||||
|
text-align: right; |
||||||
|
width: 200px; |
||||||
|
z-index: 1; } |
||||||
|
|
||||||
|
.round-item { |
||||||
|
cursor: pointer; |
||||||
|
font-size: 13px; |
||||||
|
line-height: 34px; |
||||||
|
opacity: 0.2; |
||||||
|
padding: 0 10px; } |
||||||
|
|
||||||
|
.round-item.active { |
||||||
|
opacity: 1; } |
||||||
|
|
||||||
|
.round-item:hover { |
||||||
|
background: yellow; } |
||||||
|
|
||||||
|
.round-hide { |
||||||
|
display: inline-block; |
||||||
|
font-size: 9px; |
||||||
|
margin-left: 20px; |
||||||
|
text-align: right; |
||||||
|
text-transform: uppercase; |
||||||
|
width: 30px; } |
||||||
|
|
||||||
|
.round-text { |
||||||
|
display: inline-block; } |
@ -0,0 +1,42 @@ |
|||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin'); |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
entry: './js/index.js', |
||||||
|
|
||||||
|
module: { |
||||||
|
loaders: [ |
||||||
|
{ |
||||||
|
test: /\.js$/, |
||||||
|
exclude: __dirname + '/node_modules', |
||||||
|
loader: 'babel-loader', |
||||||
|
query: { |
||||||
|
presets: ['es2015'] |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
test: /\.(scss|css)$/, |
||||||
|
include: __dirname + '/res', |
||||||
|
loader: ExtractTextPlugin.extract('css!sass?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'), |
||||||
|
}, |
||||||
|
{ |
||||||
|
test: /\.png$/, |
||||||
|
loader: "file?name=[path][name].[ext]" |
||||||
|
}, |
||||||
|
{ |
||||||
|
test : /\.woff2?$/, |
||||||
|
loader : 'file-loader' |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
|
||||||
|
output: { |
||||||
|
path: __dirname, |
||||||
|
filename: './js/bundle.js' |
||||||
|
}, |
||||||
|
|
||||||
|
plugins: [ |
||||||
|
new ExtractTextPlugin('./res/style.css', { |
||||||
|
allChunks: true |
||||||
|
}) |
||||||
|
] |
||||||
|
}; |
Loading…
Reference in new issue