parent
df6e49ac32
commit
0214ce1068
17 changed files with 1112 additions and 15 deletions
@ -1,4 +1,5 @@ |
||||
data |
||||
node_modules |
||||
npm-debug.log |
||||
tmp |
||||
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