+## Classes
+
+<dl>
+<dt><a href="#StatusComponent">StatusComponent</a></dt>
+<dd></dd>
+<dt><a href="#WaveRenderer">WaveRenderer</a></dt>
+<dd></dd>
+<dt><a href="#DataService">DataService</a></dt>
+<dd></dd>
+<dt><a href="#SortingHat">SortingHat</a></dt>
+<dd></dd>
+</dl>
+
+## Typedefs
+
+<dl>
+<dt><a href="#tSortingHatData">tSortingHatData</a> : <code>object</code></dt>
+<dd><p>The data structure representing the sorting hat data</p>
+</dd>
+<dt><a href="#tRunningAverages">tRunningAverages</a> : <code>object</code></dt>
+<dd><p>The running averages, including the current sum and count</p>
+</dd>
+</dl>
+
+<a name="StatusComponent"></a>
+
+## StatusComponent
+**Kind**: global class
+<a name="new_StatusComponent_new"></a>
+
+### new StatusComponent()
+The status class, renders the winner during the poll stage, and a
+message while waiting
+
+<a name="WaveRenderer"></a>
+
+## WaveRenderer
+**Kind**: global class
+<a name="new_WaveRenderer_new"></a>
+
+### new WaveRenderer()
+The wave renderer, draws some waves in a canvas to represent a set of
+cateogirzed averages
+
+<a name="DataService"></a>
+
+## DataService
+**Kind**: global class
+
+* [DataService](#DataService)
+ * [new DataService()](#new_DataService_new)
+ * [.data()](#DataService.data) ⇒
+
+<a name="new_DataService_new"></a>
+
+### new DataService()
+The main data service, connects to a socket and updates the internal
+data structure
+
+<a name="DataService.data"></a>
+
+### DataService.data() ⇒
+Returns the internal data structure, intended to be used as the data
+property in vue components
+
+**Kind**: static method of [<code>DataService</code>](#DataService)
+**Returns**: tSortingHatData
+<a name="SortingHat"></a>
+
+## SortingHat
+**Kind**: global class
+<a name="new_SortingHat_new"></a>
+
+### new SortingHat()
+The main vue application, it is composed by the other components, no real
+logic otherwise
+
+<a name="tSortingHatData"></a>
+
+## tSortingHatData : <code>object</code>
+The data structure representing the sorting hat data
+
+**Kind**: global typedef
+
+| Param | Type | Description |
+| --- | --- | --- |
+| state | <code>number</code> | the current state: 0 for waiting, 1 for polling, 2 for cool down. |
+| [winner] | <code>string</code> | the winner after polling, might be null if no winner is detected |
+| runningAverages | <code>Object.<string, tRunningAverages></code> | the running averages for the different categories, used to render the waves |
+
+<a name="tRunningAverages"></a>
+
+## tRunningAverages : <code>object</code>
+The running averages, including the current sum and count
+
+**Kind**: global typedef
+
+| Param | Type | Description |
+| --- | --- | --- |
+| sum | <code>number</code> | the current total |
+| count | <code>number</code> | the number of samples |
+| average | <code>number</code> | the average (sum / count) |
+
const internals = {};
-export default Vue.component('posts', {
+/**
+ * The status class, renders the winner during the poll stage, and a
+ * message while waiting
+ *
+ * @class StatusComponent
+ */
+export default Vue.component('status', {
template: '<div class="status-widget">' +
'<transition name="fade">' +
'<div v-if="state === 0" class="waiting-message">Waiting</div>' +
import Vue from 'vue';
import DataService from '../services/data';
+/* global window */
+
const internals = {
// Constants
}
};
-export default Vue.component('posts', {
+/**
+ * The wave renderer, draws some waves in a canvas to represent a set of
+ * cateogirzed averages
+ *
+ * @class WaveRenderer
+ */
+export default Vue.component('waveRenderer', {
template: '<transition name="fade">' +
'<canvas v-show="state === 1" class="wave-renderer"></canvas>' +
'</transition>',
const keys = Object.keys(this.runningAverages);
- const averages = keys.reduce((averages, averageCategory) => {
+ const averages = keys.reduce((averagesObject, averageCategory) => {
const runningAverage = this.runningAverages[averageCategory];
- averages[averageCategory] = runningAverage.average;
- return averages;
+ averagesObject[averageCategory] = runningAverage.average;
+ return averagesObject;
}, {});
const max = Math.max(...Object.values(averages));
const min = Math.min(...Object.values(averages));
- const scaledAverages = Object.keys(averages).reduce((scaledAverages, averageCategory) => {
+ const scaledAverages = Object.keys(averages).reduce((scaledAveragesObject, averageCategory) => {
const value = averages[averageCategory];
- scaledAverages[averageCategory] = internals.scale(value, min, max);
- return scaledAverages;
+ scaledAveragesObject[averageCategory] = internals.scale(value, min, max);
+ return scaledAveragesObject;
}, {});
return scaledAverages;
},
methods: {
+
+ // Reset the size of the canvas on resize
+
onResize() {
this.$el.width = window.innerWidth;
// Iterate over the segments
- for (let currentSegment = 0; currentSegment < segments; ++currentSegment) {
- const segmentStart = currentSegment * period;
- const segmentEnd = currentSegment + period;
+ for (let i = 0; i < segments; ++i) {
+ const segmentStart = i * period;
- const category = keys[currentSegment];
- const magnitude = values[currentSegment];
+ const category = keys[i];
+ const magnitude = values[i];
const segmentHeight = Math.round(Math.random() * internals.kRandomJitter + magnitude * (canvas.height / 2) / 100);
// Calculate the gradient using the correct color according to
// scale
const color = internals.kColors[category] || 'black';
- let currentGradientPosition = currentSegment * gradientBandWidth + internals.kGradientFade;
+ let currentGradientPosition = i * gradientBandWidth + internals.kGradientFade;
fillGradient.addColorStop(currentGradientPosition, color);
- currentGradientPosition = (currentSegment + 1) * gradientBandWidth - internals.kGradientFade;
+ currentGradientPosition = (i + 1) * gradientBandWidth - internals.kGradientFade;
fillGradient.addColorStop(currentGradientPosition, color);
// This draws the sine wave
- for (let angle = 0; angle < 180; ++angle) {
- const currentPixel = segmentStart + angle * period / 180;
- const currentAngle = angle + 180 * (currentSegment % 2);
+ for (let j = 0; j < 180; ++j) {
+ const currentPixel = segmentStart + j * period / 180;
+ const currentAngle = j + 180 * (i % 2);
const currentRadians = currentAngle * Math.PI / 180;
const currentHeight = segmentHeight * Math.sin(internals.kFrequency * currentRadians);
+/* global WebSocket */
const internals = {
kSocketLocation: 'ws://localhost:1987',
}
};
+/**
+ * The data structure representing the sorting hat data
+ *
+ * @typedef tSortingHatData
+ * @type object
+ * @param {number} state the current state: 0 for waiting, 1 for
+ * polling, 2 for cool down.
+ * @param {string} [winner] the winner after polling, might be null if
+ * no winner is detected
+ * @param {Object.<string,tRunningAverages>} runningAverages the running averages for
+ * the different categories, used to render the waves
+ */
+
+/**
+ * The running averages, including the current sum and count
+ *
+ * @typedef tRunningAverages
+ * @type object
+ * @param {number} sum the current total
+ * @param {number} count the number of samples
+ * @param {number} average the average (sum / count)
+ */
+
+/**
+ * The main data service, connects to a socket and updates the internal
+ * data structure
+ *
+ * @class DataService
+ */
export default {
+
+ /**
+ * Returns the internal data structure, intended to be used as the data
+ * property in vue components
+ *
+ * @memberof DataService
+ * @method data
+ * @return tSortingHatData
+ */
data() {
if (!internals.socket) {
const internals = {};
+/**
+ * The main vue application, it is composed by the other components, no real
+ * logic otherwise
+ *
+ * @class SortingHat
+ */
internals.SortingHat = {
start() {
}
};
+// Instantiates the app
internals.run = function () {