212 lines
5.3 KiB
HTML
212 lines
5.3 KiB
HTML
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="en">
|
||
|
|
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8" />
|
||
|
|
<title>Cronjob Syntax Helper</title>
|
||
|
|
<style>
|
||
|
|
body {
|
||
|
|
font-family: sans-serif;
|
||
|
|
padding: 1rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
h1 {
|
||
|
|
text-align: center;
|
||
|
|
margin-bottom: 1rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.container {
|
||
|
|
display: flex;
|
||
|
|
gap: 2rem;
|
||
|
|
width: 60%;
|
||
|
|
/* margin: auto; */
|
||
|
|
}
|
||
|
|
|
||
|
|
.left,
|
||
|
|
.right {
|
||
|
|
flex: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
.field-row {
|
||
|
|
margin-bottom: 0.75rem;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.field-row label {
|
||
|
|
width: 6rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.field-row input {
|
||
|
|
flex: 1;
|
||
|
|
padding: 0.25rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.left .field-row input {
|
||
|
|
/* fix inputs to ~6rem (≈96px) wide */
|
||
|
|
flex: 0 0 6rem;
|
||
|
|
width: 6rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
#cron-desc {
|
||
|
|
padding: 0.5rem;
|
||
|
|
background: #f2f2f2;
|
||
|
|
border-radius: 4px;
|
||
|
|
min-height: 2em;
|
||
|
|
}
|
||
|
|
|
||
|
|
.bottom {
|
||
|
|
width: 60%;
|
||
|
|
margin: 1.5rem auto 0;
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
align-items: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.bottom select,
|
||
|
|
.bottom button {
|
||
|
|
padding: 0.4rem 0.8rem;
|
||
|
|
margin-left: 0.5rem;
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
|
||
|
|
<body>
|
||
|
|
<div class="container">
|
||
|
|
<!-- Left column: build fields -->
|
||
|
|
<div class="left">
|
||
|
|
<div class="field-row">
|
||
|
|
<label for="minute">Minute</label>
|
||
|
|
<input type="text" id="minute" placeholder="*" />
|
||
|
|
</div>
|
||
|
|
<div class="field-row">
|
||
|
|
<label for="hour">Hour</label>
|
||
|
|
<input type="text" id="hour" placeholder="*" />
|
||
|
|
</div>
|
||
|
|
<div class="field-row">
|
||
|
|
<label for="dom">Day of Mo.</label>
|
||
|
|
<input type="text" id="dom" placeholder="*" />
|
||
|
|
</div>
|
||
|
|
<div class="field-row">
|
||
|
|
<label for="month">Month</label>
|
||
|
|
<input type="text" id="month" placeholder="*" />
|
||
|
|
</div>
|
||
|
|
<div class="field-row">
|
||
|
|
<label for="dow">Day of Wk.</label>
|
||
|
|
<input type="text" id="dow" placeholder="*" />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Right column: output -->
|
||
|
|
<div class="right">
|
||
|
|
<div class="field-row">
|
||
|
|
<label for="cron-expr">Cron Expr</label>
|
||
|
|
<input type="text" id="cron-expr" readonly />
|
||
|
|
</div>
|
||
|
|
<div class="field-row">
|
||
|
|
<label>Description</label>
|
||
|
|
<div id="cron-desc"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Presets + Actions -->
|
||
|
|
<div class="bottom">
|
||
|
|
<div>
|
||
|
|
Presets:
|
||
|
|
<select id="preset">
|
||
|
|
<option value="">— choose —</option>
|
||
|
|
<option value="* * * * *">Every minute</option>
|
||
|
|
<option value="0 * * * *">Every hour</option>
|
||
|
|
<option value="0 0 * * *">Daily at midnight</option>
|
||
|
|
<option value="0 0 * * 1">Weekly Mon @ midnight</option>
|
||
|
|
<option value="0 0 1 * *">Monthly 1st @ midnight</option>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<button id="copy-btn">Copy cron expr</button>
|
||
|
|
<button id="reset-btn">Reset</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
// grab elements
|
||
|
|
const minuteIn = document.getElementById('minute');
|
||
|
|
const hourIn = document.getElementById('hour');
|
||
|
|
const domIn = document.getElementById('dom');
|
||
|
|
const monthIn = document.getElementById('month');
|
||
|
|
const dowIn = document.getElementById('dow');
|
||
|
|
const cronExpr = document.getElementById('cron-expr');
|
||
|
|
const cronDesc = document.getElementById('cron-desc');
|
||
|
|
const preset = document.getElementById('preset');
|
||
|
|
const copyBtn = document.getElementById('copy-btn');
|
||
|
|
const resetBtn = document.getElementById('reset-btn');
|
||
|
|
|
||
|
|
// build the cron string
|
||
|
|
function updateCron() {
|
||
|
|
const parts = [
|
||
|
|
minuteIn.value.trim() || '*',
|
||
|
|
hourIn.value.trim() || '*',
|
||
|
|
domIn.value.trim() || '*',
|
||
|
|
monthIn.value.trim() || '*',
|
||
|
|
dowIn.value.trim() || '*'
|
||
|
|
];
|
||
|
|
cronExpr.value = parts.join(' ');
|
||
|
|
updateDesc(parts);
|
||
|
|
}
|
||
|
|
|
||
|
|
// basic plain-English breakdown
|
||
|
|
function updateDesc([min, hr, dom, mon, dow]) {
|
||
|
|
if ([min, hr, dom, mon, dow].every(v => v === '*')) {
|
||
|
|
cronDesc.textContent = 'Every minute';
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const parts = [];
|
||
|
|
if (min !== '*') parts.push(`minute = ${min}`);
|
||
|
|
if (hr !== '*') parts.push(`hour = ${hr}`);
|
||
|
|
if (dom !== '*') parts.push(`day of month = ${dom}`);
|
||
|
|
if (mon !== '*') parts.push(`month = ${mon}`);
|
||
|
|
if (dow !== '*') parts.push(`day of week = ${dow}`);
|
||
|
|
cronDesc.textContent = parts.join(', ');
|
||
|
|
}
|
||
|
|
|
||
|
|
// wire inputs
|
||
|
|
[minuteIn, hourIn, domIn, monthIn, dowIn].forEach(el => {
|
||
|
|
el.addEventListener('input', () => {
|
||
|
|
// clear preset selection when typing freeform
|
||
|
|
preset.value = '';
|
||
|
|
updateCron();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// presets
|
||
|
|
preset.addEventListener('change', () => {
|
||
|
|
const v = preset.value;
|
||
|
|
if (!v) return;
|
||
|
|
const p = v.split(' ');
|
||
|
|
[minuteIn.value, hourIn.value, domIn.value, monthIn.value, dowIn.value] = p;
|
||
|
|
updateCron();
|
||
|
|
});
|
||
|
|
|
||
|
|
// copy button
|
||
|
|
copyBtn.addEventListener('click', () => {
|
||
|
|
navigator.clipboard.writeText(cronExpr.value)
|
||
|
|
.then(() => alert('Copied to clipboard!'));
|
||
|
|
});
|
||
|
|
|
||
|
|
// reset button
|
||
|
|
resetBtn.addEventListener('click', () => {
|
||
|
|
minuteIn.value = hourIn.value = domIn.value = monthIn.value = dowIn.value = '*';
|
||
|
|
preset.value = '';
|
||
|
|
updateCron();
|
||
|
|
});
|
||
|
|
|
||
|
|
// init
|
||
|
|
window.addEventListener('DOMContentLoaded', () => {
|
||
|
|
resetBtn.click(); // sets everything to "* * * * *"
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
</body>
|
||
|
|
|
||
|
|
</html>
|