Skip to content

Instantly share code, notes, and snippets.

@leblanc-simon
Created November 14, 2016 00:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save leblanc-simon/8339e3bac53a1f5314bafac97be73c2e to your computer and use it in GitHub Desktop.
Save leblanc-simon/8339e3bac53a1f5314bafac97be73c2e to your computer and use it in GitHub Desktop.
Génération de tickets pour les scénarii Scrum
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<title>Génération de tickets pour les scénarii Scrum</title>
<!--
Copyright © 2016 Simon Leblanc <contact@leblanc-simon.eu>
This work is free. You can redistribute it and/or modify it under the
terms of the Do What The Fuck You Want To Public License, Version 2,
as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
-->
<link rel="icon" href="favicon.ico" />
<link rel="icon" type="image/png" href="favicon.png" />
<link href="https://fonts.googleapis.com/css?family=Ubuntu" rel="stylesheet">
<style type="text/css">
html, body {
margin: 0;
padding: 0;
font-family: 'Ubuntu', sans-serif;
}
header, #container {
width: 98%;
margin: 1em auto;
}
#colorpicker {
position: absolute;
left: -10000px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
}
/**
* Header
*/
h1 {
text-align: center;
}
header {
display: flex;
justify-content: space-between;
}
.build {
width: 45%;
border-radius: 1rem;
background-color: #eee;
padding: 1rem;
margin: 0 calc(2.5% - 1em);
display: flex;
flex-direction: column;
}
header .title {
text-align: center;
margin-bottom: 1rem;
font-size: 1.3rem;
}
input, textarea {
display: block;
width: 95%;
margin: 0.5em auto;
padding: 0.2rem;
border: 1px solid #ccc;
}
#empty {
text-align: center;
}
textarea {
height: 10rem;
}
.submit {
text-align: center;
margin-top: auto;
}
.submit button {
padding-left: 2rem;
padding-right: 2rem;
font-weight: bold;
font-size: 1.2rem;
color: #3262F9;
border-radius: 2rem;
background: #ddd;
border: none;
cursor: pointer;
}
.submit button:focus {
outline: none;
}
.submit button::before {
/**
* https://www.iconfinder.com/icons/1086770/bullets_content_form_guide_list_menu_plan_to_do_icon
* Licence : CC BY
* Author : Vladlena Shibaeva
*/
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAADJElEQVRoQ+2YTWjUQBTH35tsk83WoiIKglhE9KLW7qZ4sQeVCiJeeuhBS2m7qYKlFIsXvciiBylaerAilP2gFDzqQQVB8dSDqMmWUvDiB1VRUMHa1e5ms5mR7FKQYjdJTRMXk8se5u0//9+8N28mg1DjD9a4fwgA/M6g8wwkGFkr09JH4JRx1J3o2waQ4npriRqdBMlOJy9wFmt8AeQ+EYPeUibCr+381xaAaZ4C6yAhY0QZF9/ZEV5tTDRe3A/IBkPUSDzPRN5b6dgCaI4XxxCNa9mUOGcl6MZ49HShDSnsU1PhUSs9WwCxePGxmubbrMTcGm/uKexCDoayqXC/lWYAYDVDqxkPMrB81oI14LCOPC2hWG/+iOlPzYhPHPpcMdwzgAO9hRM6kqNAANCgj9RM+L7pak8/W8flfkSqAc1MNnxeadwjAIZSjzYGvHDuuwCkYUEbzU5U+nZULnYBNY5VA8hmxE6fAQBa5GK0xIwLphGOccNKhlfdKCOPMlCx2tSX32H+ziTFt26YNzU8BXDL9O86AcB/s5G1nmIbF4XiBFCAiM53T93Gb26UlCcl1NHBuDfr9VGk9KZp2gDSn91eNwQJpLG41o6Ah6vBKGl+0Pc2KvVqFymwecYAAXH9dEa4Wu5MXbktIYHfVA1ATQovfQcAYCjJ2nnTiJISRgCQ1UwJuWH0H8jA2mB4soiXrDf1LW6r7MSRD27heAbQImt7Swwum8ZDCJdepIRZNyA8AmDYJOfHjAVxaHM9kHmiX59O8wNLp1FkxvGqXSgtnvR9DVT6PWstn0YBp56lhbuVNsrq60QQq+4D4/jVdwDTQEwulM/9air80I3yCU6jf5rF4FbCYW151IUcunIQHgAsn6yaXgOHEiyUm9OTSobvcVAFfxUalfONhJGzSloo33hUe2xdr0u92hXG4QM1yT+1EnRjXJKL3RSAZlP8pJWePYAzP7eWjNAwAtxDhq5dnyw3R0JMoyU8CAhSLlI38OoGaq4AVHZHtoGg3s6Q7bYSXe04Eqoxg8xmG/k75qepHR1bGbAj5FdMAODXzC+9N8iA3xn4BYtZ20B3G0sLAAAAAElFTkSuQmCC);
height: 48px;
display: inline-block;
vertical-align: middle;
}
/**
* Cards
*/
.card {
width: calc(50% - 2px);
height: 250px;
min-height: 250px;
max-height: 250px;
float: left;
border: 1px solid #000;
position: relative;
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
page-break-inside: avoid;
}
#container .title {
position: relative;
background-color: #49962C;
padding: 0.5rem;
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
font-weight: bold;
}
.color, .delete {
position: absolute;
background: none;
border: none;
cursor: pointer;
}
.color {
right: 0.5rem;
}
.delete {
right: calc(0.5em + 16px + 0.5em);
}
.color:focus, .delete:focus {
outline: none;
}
.color::before, .delete::before {
width: 16px;
height: 16px;
}
.color::before {
/**
* https://www.iconfinder.com/icons/1055087/color_wheel_palette_swatch_icon
* Licence : GPL
* Author : Nick Roach
*/
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACoUlEQVQ4T32T3UtUQRjGn/ecs19ubuu6ZOaau4sFXviVEFEiaERGV+FNEHYhZEURQf9Af0AXUVQqREVddOFtUIRWGlFguC4aZtIePxc/cl3X1d31nJmYWV1Wg+Zq3pl5fu/MM+9L2DN0feQY5/wqEVoAqhDbnENXFOpnzOwKBOpD+RLaCebnhwoyGetDgHfshebHnPMeTdu4XV5+clOsS0BWbHkHoFEe5hzpiQi2FpZkaPWVwho8nOMQsQFF2WwVEAnQ9fDTnczJjTS05DpWHjyTIDm8RSi9eRnYiAGuErlERN0VFTXXSLwZwHexOD45j55XfbjefgaOb18BcQNFgbXpBDzaHLToGFhDG5Ti7G04Z/UUiYS6iaiTMYa793oRT6RQW+XDpbZGGIYhMsFut2Fr8DlsqRhM2z6oTVckGMAT0vXQL4Aqf0zMovtlvxTc6jiNoP/QLi/j0Sm4wr3SNLPuAtSSIAD2UzwhA8CyuJzA+w9DcLucOH/2+D8fwTnHn9AnKJkkHJUNcBQfFIBMDrCaBibjBKcGVHm2zduDsaenobA00tYymGpBFhCJjEwQ4ch0gvBGV+QVLx414bbtVitGAp61QRABUbUGmssnbBynqalwl6g8BuDFKEOKWxBwZtAaVHcRClcGYKd1pEwrVt3NUDUVjPFHFIkM1xEpw+L02Gwcn2NFaPauYVZLYjGTlqaeKvKgbG0UXssKplkVnF6/hDOm1MpCyv/KheVV2F1OvF6cw44TblLR5i3GZnwJ+0v8Egrgsd9fe0POZma+OBhzvOVcaRKxYRoYmYsiZhrSkwMWK6p9ZXlPoo+cF54LBAKpXDMJiGEU3BdF9b9mEpk5d90R4lwz5Qu2PekEWAuAQHaPfjOGPkDtCQarw/nn/wLZnxe/RFGwIAAAAABJRU5ErkJggg==);
}
.delete::before {
/**
* https://www.iconfinder.com/icons/928426/bin_delete_empty_junk_remove_trash_icon
* Licence : CC BY
* Author : Chanut is Industries
*/
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABIUlEQVQ4T6XTOyiHURgH4Mc9xWRmYHTJYFCUTSImJUUZyMRiEIPkltFiMighk0FMFspiZDGJYjQpYXDp6IhO/y//cpbv/pzfec/7FfjnKEi+H8UEijLcN6xh8/v5b6ACDyj7I9QzqhCOfgPlaMtzRWd4SYEQuwOlfyCvOMV7CgxjK88Eg9hLgW4cYRsNaI7XIVlXnPURvejEcQrU4hpjaMQkZlCMRczG8wXU4C4FCvGEJXxgOQHCFodUI6jMtY3h3kWMeomNBOjDEOrQkgV8FQY7OEiAVqziHqHgXyPtxPm4ldM4T4BQo0PsxuXlBAYwhx7cJEDo1FuMYz8rQVOceR1TOImVb8dKBOtxlQWURL06o6FCqn6EnyrnEvJsxJ/XPgE5azkRuxeDCAAAAABJRU5ErkJggg==);
}
.description {
padding: 0.5rem;
}
.time {
position: absolute;
bottom: 0;
left: 0;
min-height: 40px;
border-top: 1px solid #000;
width: 100%;
}
.time .estimated, .time .real {
position: relative;
float: right;
min-width: 115px;
min-height: 40px;
text-align: center;
line-height: 40px;
font-weight: bold;
padding-top: 0.6rem;
}
.time .estimated {
border-left: 1px solid #000;
border-right: 1px solid #000;
}
.time .estimated::before, .time .real::before {
position: absolute;
top: 0;
left: 0;
width: 100%;
text-align: center;
font-size: 0.6rem;
line-height: normal;
border-bottom: 1px dashed #000;
}
.time .estimated::before {
content: "Estimation";
}
.time .real::before {
content: "Réel";
}
@media print {
h1, header, .delete, .color {
display: none;
}
@page { {
size: auto; /* auto is the initial value */
margin: 0mm; /* this affects the margin in the printer settings */
}
html {
background-color: #FFFFFF;
margin: 0px; /* this affects the margin on the html before sending to printer */
}
body {
margin: 10mm 15mm 10mm 15mm; /* margin you want for the content */
}
}
</style>
</head>
<body>
<h1>Génération de tickets pour les scénarii Scrum</h1>
<header>
<div class="build">
<div class="title">Générer des tickets vierges</div>
<div class="form">
<input type="number" id="empty" min="1" value="8" />
<input type="color" id="empty-color" value="#49962C">
</div>
<div class="submit">
<button id="generate-empty">Générer</button>
</div>
</div>
<div class="build">
<div class="title">Générer des tickets</div>
<div class="form">
<input type="text" id="title" placeholder="Titre des tickets" />
<textarea id="contents" placeholder="Une ligne par ticket"></textarea>
<input type="color" id="content-color" value="#49962C">
</div>
<div class="submit">
<button id="generate-content">Générer</button>
</div>
</header>
<div id="container"></div>
<input type="color" id="colorpicker">
<script>
/*******************************************************************************
*
* EXTERNAL FUNCTIONS
*
*******************************************************************************/
/**
* Escape / Unescape HTML
* @see http://jsfiddle.net/Daniel_Hug/qPUEX/show/light/
*/
(function() {
var escapeEl = document.createElement('textarea');
window.escapeHTML = function(html) {
escapeEl.textContent = html;
return escapeEl.innerHTML;
};
window.unescapeHTML = function(html) {
escapeEl.innerHTML = html;
return escapeEl.textContent;
};
})();
/**
* Convert RGB to Hex
* @see http://jsfiddle.net/Mottie/xcqpF/1/light/
*/
function rgb2hex(rgb){
rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
return (rgb && rgb.length === 4) ? "#" +
("0" + parseInt(rgb[1],10).toString(16)).slice(-2) +
("0" + parseInt(rgb[2],10).toString(16)).slice(-2) +
("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : '';
}
/*******************************************************************************
*
* BASE FUNCTIONS
*
*******************************************************************************/
function addDiv(css, content, content_editable)
{
var div = document.createElement('div');
if (css) {
div.setAttribute('class', css);
}
if (content) {
div.innerHTML = content;
}
if (content_editable) {
div.setAttribute('contenteditable', 'true');
}
return div;
}
function addButton(css)
{
var button = document.createElement('button');
if (css) {
button.setAttribute('class', css);
}
return button;
}
function generateCard(title, description, color)
{
var div_card = addDiv('card');
var div_title = addDiv('title', title, 'true');
var button_delete = addButton('delete');
var button_color = addButton('color');
var div_description = addDiv('description', description, 'true');
var div_time = addDiv('time');
var div_time_real = addDiv('real', '&nbsp;');
var div_time_estimated = addDiv('estimated', '&nbsp;', 'true');
button_color.addEventListener('click', colorCard, false);
button_delete.addEventListener('click', deleteCard, false);
div_title.appendChild(button_color);
div_title.appendChild(button_delete);
if (color) {
div_title.setAttribute('style', 'background-color:' + color);
}
div_time.appendChild(div_time_real);
div_time.appendChild(div_time_estimated);
div_card.appendChild(div_title);
div_card.appendChild(div_description);
div_card.appendChild(div_time);
return div_card;
}
function deleteCard(event)
{
event.target.parentNode.parentNode.remove();
}
function colorCard(event)
{
var title = event.target.parentNode;
current_card_title = title;
colorpicker.focus();
colorpicker.value = rgb2hex(window.getComputedStyle(title, null).getPropertyValue("background-color"));
colorpicker.click();
}
/*******************************************************************************
*
* MAIN PROGRAM
*
*******************************************************************************/
var container = document.getElementById('container');
var colorpicker = document.getElementById('colorpicker');
var current_card_title = null;
document.getElementById('generate-empty').addEventListener('click', function() {
var number = parseInt(document.getElementById('empty').value, 10);
var color = document.getElementById('empty-color').value;
for (var iterator = 0; iterator < number; iterator++) {
container.appendChild(generateCard('&nbsp;', '&nbsp;', color));
}
}, false);
document.getElementById('generate-content').addEventListener('click', function() {
var title = document.getElementById('title').value;
var color = document.getElementById('content-color').value;
var contents = document.getElementById('contents').value.split('\n');
contents.forEach(function (content) {
if (!content) {
return;
}
container.appendChild(generateCard(escapeHTML(title), escapeHTML(content), color));
});
}, false);
colorpicker.addEventListener('change', function () {
if (current_card_title === null) {
return;
}
current_card_title.style.backgroundColor = this.value;
current_card_title = null;
}, false);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment