2015-12-12 14:49:20 +01:00
<!doctype html>
< html >
< head >
< meta charset = "utf-8" / >
< title > Secret Santa Generator< / title >
< script src = "vendors/Lodash-3.10.1.js" > < / script >
< script src = "vendors/Cryptojs.aes-3.1.2.js" > < / script >
< script src = "./SecretSanta.js" > < / script >
< style >
* {
box-sizing: border-box;
}
html, body, .main {
margin: 0;
width: 100%;
height: 100%;
padding: 0;
}
body {
background-image: url(assets/santa.png);
background-position: bottom right;
background-repeat: no-repeat;
font-family: 'Comic Sans MS';
}
.main {
display: flex;
}
.part {
display: flex;
flex-direction: column;
padding: 20px;
flex: 1;
}
.instructions {
margin: auto 10%;
}
.instructions a {
text-decoration: none;
color: #0CB50C;
}
.input {
display: block;
align-self: stretch;
width: 100%;
flex: auto;
border: 10px solid #0CB50C;
border-radius: 10px;
padding: 15px;
background-color: rgba(255, 255, 255, .7);
outline: none;
}
.generate {
display: block;
margin-top: 20px;
flex: none;
height: 100px;
border: 10px solid #0CB50C;
border-radius: 10px;
font-family: inherit;
font-size: 40px;
background-color: #EA1212;
color: #FFFFFF;
}
.generate:focus {
border-color: #E6E020;
outline: none;
}
.result {
margin-top: 20px;
flex: none;
border: 10px solid #E6E020;
border-radius: 10px;
padding: 15px;
background: #FFFFFF;
}
.result a {
color: blue;
}
.result.none {
display: none;
}
.result.error {
border-color: #EA1212;
color: #EA1212;
}
.result-table {
table-layout: fixed;
width: 100%;
}
.result-name {
2018-11-18 15:56:45 +01:00
width: 30%;
2015-12-12 14:49:20 +01:00
2018-11-18 15:56:45 +01:00
padding: 5px 8px;
2015-12-12 14:49:20 +01:00
}
.result-link {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
< / style >
< / head >
< body >
< div class = "main" >
2018-10-30 05:09:22 +01:00
< div class = "part" style = "overflow-y: auto" >
2015-12-12 14:49:20 +01:00
< div class = "instructions" >
< h1 > < img src = "assets/mistletoe.png" style = "vertical-align: middle" / > Secret Santa Generator< / h1 >
2018-10-30 05:09:22 +01:00
< p > No signup, no email, no bullshit. Just a straightforward < a href = "https://github.com/arcanis/secretsanta" > open-source< / a > tool to help you generate your secret santa pairings. One static page, and that's it.< / p >
< p > In the most common case (no exclusion rules, pair each guest with another at random), enter the name of your guests one line at a time. Once done, press "generate" and you're all set: send the generated links to your guests (by mail, chat, whatever floats your boat) and their pairing will be revealed to them (and only them) once they open the link.< / p >
2015-12-12 14:49:20 +01:00
< h2 > Where does this tool come from?< / h2 >
2018-10-30 05:09:22 +01:00
< p > I wanted to make a Secret Santa over Facebook without having to reveal to anyone my guests email addresses (so nothing that would require a backend). I also wanted not to know who was paired with me, so I had to find a way to somehow obfuscate the information. And being a developer, well, my first thought was "Let's AES it, for fun and profits!". Classic.< / p >
2015-12-12 14:49:20 +01:00
< / div >
< / div >
< form id = "form" class = "part" >
< textarea id = "input" class = "input" autofocus > < / textarea >
< button type = "submit" class = "generate" > Generate your pairings< / button >
< div id = "result" class = "result none" > < / div >
< / form >
< / div >
< script id = "input-placeholder" type = "text/placeholder" >
# You can add a user by adding a line
Santa
2018-11-18 15:56:45 +01:00
# You can add some details if you want to, using parentheses after the name
Nicholas (the elf)
2015-12-12 14:49:20 +01:00
# You can prevent someone from being paired with someone else
Maël !Aurélie
Aurélie !Maël
2018-10-30 05:09:22 +01:00
# You can also exclude someone from being paired with multiple people
# Careful: too many exclusion rules can make your secret santa less interesting!
2018-11-18 15:56:45 +01:00
Rudolph !Santa !Nicholas (the elf)
2018-10-30 05:09:22 +01:00
# You can also cheat a bit and force someone to be paired with another
2018-11-18 15:56:45 +01:00
Nicholas (the saint) =Nicholas (the elf)
2018-10-30 05:09:22 +01:00
2015-12-12 14:49:20 +01:00
...
< / script >
< / body >
< script >
2018-10-30 05:09:22 +01:00
function isAmazonEnabled() {
const checkBox = document.getElementById('amazon');
return checkBox ? checkBox.checked : false;
}
2015-12-12 14:49:20 +01:00
function persist() {
if ( ! window.localStorage )
return ;
2018-10-30 05:09:22 +01:00
var amazon = isAmazonEnabled();
2015-12-12 14:49:20 +01:00
var content = document.getElementById( 'input' ).value;
2018-10-30 05:09:22 +01:00
window.localStorage.setItem( 'amazon', amazon );
2015-12-12 14:49:20 +01:00
window.localStorage.setItem( 'input', content );
}
function restore() {
if ( ! window.localStorage )
return ;
2018-10-30 05:09:22 +01:00
var amazon = window.localStorage.getItem( 'amazon' );
var content = window.localStorage.getItem( 'input' );
if (typeof amazon === 'undefined')
amazon = true;
if (typeof content === 'undefined')
content = '';
if ( document.getElementById( 'amazon' ) )
document.getElementById( 'amazon' ).checked = amazon;
2015-12-12 14:49:20 +01:00
document.getElementById( 'input' ).value = content;
}
function reset() {
var result = document.getElementById( 'result' );
result.classList.add( 'none' );
result.classList.remove( 'error' );
}
function error(text) {
var result = document.getElementById( 'result' );
result.classList.remove( 'none' );
result.classList.add( 'error' );
result.innerText = text;
}
function success( pairings ) {
var result = document.getElementById( 'result' );
result.classList.remove( 'none' );
result.classList.remove( 'error' );
result.innerHTML = '';
var table = document.createElement( 'table' );
table.className = 'result-table';
result.appendChild( table );
var names = Object.keys( pairings ).sort();
for ( var t = 0, T = names.length; t < T ; + + t ) {
2018-11-18 16:13:39 +01:00
var name = names[ t ];
var prettyName = names[ t ].replace( /\([^)]+\)/g, ' ' ).replace( / +/g, ' ' ).trim();
2015-12-12 14:49:20 +01:00
var tr = document.createElement( 'tr' );
tr.className = 'result-row';
table.appendChild( tr );
var tdName = document.createElement( 'td' );
tdName.className = 'result-name';
tr.appendChild( tdName );
var tdLink = document.createElement( 'td' );
tdLink.className = 'result-link';
tr.appendChild( tdLink );
var link = document.createElement( 'a' );
tdLink.appendChild( link );
var key = String( _.random( 0x0000, 0xFFFF ) );
var encryptedPairing = CryptoJS.AES.encrypt( pairings[ name ], key );
var pairingPath = window.location.pathname.replace( /[^/]+$/, '' ) + 'pairing.html';
2018-11-18 16:13:39 +01:00
var pairingQueryString = '?name=' + encodeURIComponent( prettyName ) + '& key=' + encodeURIComponent( key ) + '& pairing=' + encodeURIComponent( encryptedPairing );
2015-12-12 14:49:20 +01:00
2018-10-30 05:09:22 +01:00
if ( isAmazonEnabled() )
pairingQueryString += '&extra=1';
2018-11-18 16:13:39 +01:00
tdName.innerText = name;
2015-12-12 15:57:44 +01:00
link.addEventListener( 'click', protect );
2018-11-18 16:13:39 +01:00
link.setAttribute( 'data-name', name );
2015-12-12 14:49:20 +01:00
link.href = window.location.protocol + '//' + window.location.host + pairingPath + pairingQueryString;
link.innerText = link.href;
}
}
2018-10-30 05:09:22 +01:00
function updateAmazon() {
var result = document.getElementById( 'result' );
var links = result.getElementsByTagName( 'a' );
for ( var t = 0; t < links.length ; + + t ) {
var link = links[t];
if ( isAmazonEnabled() )
link.href += '&extra=1';
else
link.href = link.href.replace( /& extra=[01]/, '' );
link.innerText = link.href;
}
}
2015-12-12 14:49:20 +01:00
function generate( e ) {
e.preventDefault();
var content = document.getElementById( 'input' ).value;
// Convert carriage returns into line feeds
content = content.replace( /(\r\n|\r)/g, '\n' );
// Merge adjacent blank characters into a single space
content = content.replace( /[ \t]+/g, ' ' );
// Trim lines
content = content.replace( /^ | $/gm, '' );
// Strip comments
content = content.replace( /^#.*$/gm, '' );
// Strip empty lines
content = content.replace( /\n+/g, '\n' );
2018-10-30 05:09:22 +01:00
// Remove leading/trailing newlines
content = content.replace( /^\n|\n$/g, '' );
2015-12-12 14:49:20 +01:00
var lines = content.split( /\n/g );
if ( lines.length === 0 || lines.length === 1 & & lines[ 0 ].length === 0 )
return reset();
var santa = new SecretSanta();
for ( var t = 0, T = lines.length; t < T ; + + t ) {
2018-11-18 15:56:45 +01:00
var match = lines[ t ].match( /^((?:(?![!=]).)+)((?: [!=](?:(?! [!=]).)+)*)$/ );
2015-12-12 14:49:20 +01:00
if ( ! match )
2018-10-30 05:09:22 +01:00
return error( 'Syntax error: "' + lines[ t ] + '" isn\'t valid' );
2015-12-12 14:49:20 +01:00
var name = match[ 1 ];
2018-11-18 15:56:45 +01:00
var rules = match[ 2 ] ? match[ 2 ].match(/[!=][^!=]+/) : null;
2015-12-12 14:49:20 +01:00
var person = santa.add( name );
2018-10-30 05:09:22 +01:00
if (rules) {
for ( var u = 0, U = rules.length; u < U ; + + u ) {
var fnName = {
'=': 'enforce',
'!': 'blacklist'
}[ rules[ u ].charAt( 0 ) ];
2018-11-18 15:56:45 +01:00
person[ fnName ]( rules[ u ].slice( 1 ).trim() );
2018-10-30 05:09:22 +01:00
}
2015-12-12 14:49:20 +01:00
}
}
try {
return success( santa.generate() );
} catch ( err ) {
console.error(err.stack)
return error( err.message );
}
}
2015-12-12 15:57:44 +01:00
function protect( e ) {
var name = e.currentTarget.getAttribute( 'data-name' );
if ( ! confirm( 'If you click this link, you will be revealed ' + name + '\'s pairing! Are you sure you want to do this? Only do this if you\'re actually ' + name + '.\n\nUse right-click to copy the link target instead.' ) ) {
e.preventDefault();
}
}
2015-12-12 14:49:20 +01:00
< / script >
< script >
document.getElementById( 'input' ).placeholder = document.getElementById( 'input-placeholder' ).innerHTML.trim().replace( /^[ \t]+/gm, '' );
document.getElementById( 'input' ).addEventListener( 'change', persist );
2018-10-30 05:09:22 +01:00
if ( document.getElementById( 'amazon' ) ) {
document.getElementById( 'amazon' ).addEventListener( 'change', updateAmazon );
document.getElementById( 'amazon' ).addEventListener( 'change', persist );
}
2015-12-12 14:49:20 +01:00
document.getElementById( 'form' ).addEventListener( 'submit', generate );
restore();
< / script >
< / html >