Generate lottery numbers

Voor pubquiz

by PhilQ

HTML

<button onclick="window.print();">Print</button>
<div id="app">
	<div id="controls">
		Eerste nummer: <input type="number" v-model="number_start"/><br/>
		Laatste nummer: <input type="number" v-model="number_end"/><br/>
		Aantal teams: <input type="number" v-model="number_of_teams"/><br/>
		<input type="checkbox" v-model="distribute_leftovers"/> Overgebleven nummers verdelen over teams<br/>
		<input type="checkbox" v-model="mark_starts"/> Markeer eerste nummer per team<br/>
		<input type="checkbox" v-model="mark_leftovers"/> Markeer overgebleven nummers<br/>
	</div>
	<div class="number"
		v-for="(v,i) in a"
		:class="{ 'underline':isUnderline(v), 'start':isStart(i), 'leftover':isLeftover(i) }"
	>{{ v }}</div>
</div>

SCSS

*,::before,::after {
	margin: 0; padding: 0;
	box-sizing: border-box;
}
html, body {
	width: 100%;
	font-family: sans-serif;
}

#app {
	width: 680px;
	display: block;
	margin: 0px auto;
}

button {
	position: fixed;
	top: 20px;
	right: 20px;
	font-size: 18px;
	padding: 5px 10px;
}

@media print {
	button, #controls {
		display: none;
	}
}

.number {
	position: relative;
	float: left;
	font-family: sans-serif;
	font-weight: bold;
	font-size: 50px;
	display: inline-block;
	padding: 10px;
	border: 2px solid #ccc;
	width: 85px;
	height: 85px;
	text-align: center;
	
	&.start::before, &.leftover::before {
		content: '';
		position: absolute;
		top: 3px;
		left: 3px;
		border-radius: 50%;
		width: 6px;
		height: 6px;
		border: 3px solid #ccc;
	}
	
	&.leftover::after {
		content: '';
		position: absolute;
		top: 3px;
		left: 11px;
		border-radius: 50%;
		width: 6px;
		height: 6px;
		border: 3px solid #ccc;
	}
}
.underline {
	text-decoration: underline;
}

Vue

new Vue({
  el: "#app",
  data: {
    number_start: 1,
	number_end: 64,
	number_of_teams: 1,
	mark_starts: true,
	mark_leftovers: true,
	distribute_leftovers: true,
  	a: [],
  },
  computed: {
	per_team: function() {
		return Math.floor(this.a.length/this.number_of_teams);
	},
	left_over: function() {
		return this.a.length-this.per_team*this.number_of_teams;
	},
  },
  methods: {
  	number_range(start, end) {
		return new Array(end - start).fill().map((d, i) => i + start);
	},
	shuffle_fisher_yates(array) {
		var m = array.length, t, i;
		// While there remain elements to shuffle
		while (m) {
			// Pick a remaining element
			i = Math.floor(Math.random() * m--);
			// And swap it with the current element.
			t = array[m];
			array[m] = array[i];
			array[i] = t;
		}
		return array;
	},
  	isUnderline(v) {
		return /^([69][689]*|8[69][689]*)$/gi.test(v);
	},
  	isStart(i) {
		return this.mark_starts && (
			(
				this.distribute_leftovers &&
				(
					(
						0 == i % (this.per_team+1) &&
						Math.floor(i/(this.per_team+1)) < this.left_over
					) || (
						i >= this.left_over * (this.per_team+1) &&
						(i - this.left_over * (this.per_team+1)) % this.per_team == 0
					)
				)
			) || (
				!this.distribute_leftovers &&
				i % this.per_team == 0 &&
				i < (this.number_of_teams * this.per_team)
			)
		);
	},
  	isLeftover(i) {
		return this.mark_leftovers && (
			(
				!this.distribute_leftovers &&
				i >= this.per_team*this.number_of_teams
			) || (
				this.distribute_leftovers &&
				i < this.left_over * (this.per_team+1) &&
				0 == (i-this.per_team) % (this.per_team+1)
			)
		);
	},
	generateList() {
		if (
			/\-?[0-9]+/.test(this.number_start) &&
			/\-?[0-9]+/.test(this.number_end) &&
			this.number_end >= this.number_start &&
			this.number_of_teams > 0
		) {
			this.a = this.shuffle_fisher_yates(
				this.number_range(
					parseInt(this.number_start),
					parseInt(this.number_end)+1
				)
			);
		} else {
			this.a = [];
		}
	},
  },
 ...