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', ' '); | |
var div_time_estimated = addDiv('estimated', ' ', '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(' ', ' ', 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