First commit

This commit is contained in:
2026-07-20 11:05:07 +02:00
commit b592ab669f
159 changed files with 10294 additions and 0 deletions
@@ -0,0 +1,199 @@
/* ============================================================
Emitter — Django Admin Theme Override
Thème sombre, couleurs depuis main.css
============================================================ */
/* --- Variables Django admin 4.x ----------------------------- */
:root {
--primary : #00d4aa;
--secondary : #00a884;
--accent : #00d4aa;
--primary-fg : #0d1117;
--body-fg : #e6edf3;
--body-bg : #0d1117;
--body-quiet-color : #8b949e;
--body-loud-color : #e6edf3;
--header-color : #e6edf3;
--header-branding-color: #00d4aa;
--header-bg : #161b22;
--header-link-color : #e6edf3;
--breadcrumbs-fg : #8b949e;
--breadcrumbs-link-fg : #00d4aa;
--breadcrumbs-bg : #161b22;
--link-fg : #00d4aa;
--link-hover-color : #00a884;
--link-selected-fg : #00d4aa;
--hairline-color : #1f2a36;
--border-color : #1f2a36;
--error-fg : #f85149;
--message-success-bg : rgba(63,185,80,.15);
--message-warning-bg : rgba(210,153,34,.15);
--message-error-bg : rgba(248,81,73,.15);
--darkened-bg : #161b22;
--selected-bg : #21262d;
--selected-row : #2d333b;
--button-fg : #0d1117;
--button-bg : #00d4aa;
--button-hover-bg : #00a884;
--default-button-fg : #0d1117;
--default-button-bg : #00d4aa;
--default-button-hover-bg: #00a884;
--close-button-bg : #21262d;
--close-button-hover-bg : #2d333b;
--object-tools-fg : #0d1117;
--object-tools-bg : #00d4aa;
--object-tools-hover-bg : #00a884;
--font-family : 'Segoe UI', system-ui, -apple-system, sans-serif;
}
/* --- Global ------------------------------------------------- */
html, body { font-family: var(--font-family); background: var(--body-bg); color: var(--body-fg); }
a { color: var(--link-fg); }
a:hover { color: var(--link-hover-color); }
/* --- Header ------------------------------------------------- */
#header {
background : var(--header-bg);
color : var(--header-color);
border-bottom: 1px solid #1f2a36;
}
#header a:link, #header a:visited { color: var(--header-color); }
#branding h1, #branding h1 a:link, #branding h1 a:visited {
color: var(--accent);
font-weight: 700;
}
#user-tools a { color: #8b949e; }
#user-tools a:hover { color: var(--accent); }
/* --- Breadcrumbs -------------------------------------------- */
div.breadcrumbs {
background : var(--breadcrumbs-bg);
color : var(--breadcrumbs-fg);
border-bottom: 1px solid #1f2a36;
}
div.breadcrumbs a { color: var(--accent); }
div.breadcrumbs a:hover { color: var(--link-hover-color); }
/* --- Sidebar / nav ------------------------------------------ */
#nav-sidebar { background: #161b22; border-right: 1px solid #1f2a36; }
#nav-sidebar .current-app .section:link,
#nav-sidebar .current-app .section:visited { color: var(--accent); }
/* --- Content area ------------------------------------------- */
#content { background: var(--body-bg); }
#content-main { background: var(--body-bg); }
.colMS #content-main { border-right: 1px solid #1f2a36; }
/* --- Module headers (blocs gris-bleu → sombre+accent) ------- */
.module caption,
.inline-group h2,
fieldset.module h2,
div.submit-row {
background : #161b22;
color : var(--body-fg);
border-bottom: 1px solid #1f2a36;
}
.module { background: #161b22; border: 1px solid #1f2a36; }
fieldset.module { background: #161b22; border: 1px solid #1f2a36; }
/* --- Tables ------------------------------------------------- */
#result_list thead th { background: #161b22; color: var(--body-quiet-color); border-bottom: 1px solid #1f2a36; }
#result_list tr.row1 { background: #0d1117; }
#result_list tr.row2 { background: #111820; }
#result_list tr:hover td { background: #21262d !important; }
#result_list td, #result_list th { border-right-color: #1f2a36; }
table { border-color: #1f2a36; }
td, th { border-color: #1f2a36; }
/* --- Filtres (colonne droite) -------------------------------- */
#changelist-filter { background: #161b22; border-left: 1px solid #1f2a36; }
#changelist-filter h2 { background: #161b22; color: var(--accent); border-bottom: 1px solid #1f2a36; }
#changelist-filter h3 { color: var(--body-quiet-color); border-bottom: 1px solid #1f2a36; }
#changelist-filter li.selected a { color: var(--accent); font-weight: 600; }
#changelist-filter a { color: var(--body-fg); }
#changelist-filter a:hover { color: var(--accent); }
/* --- Formulaires -------------------------------------------- */
input, select, textarea {
background : #21262d;
color : var(--body-fg);
border : 1px solid #2d3748;
border-radius: 4px;
}
input:focus, select:focus, textarea:focus {
border-color: var(--accent);
outline : none;
box-shadow : 0 0 0 2px rgba(0,212,170,.2);
}
.form-row { border-bottom-color: #1f2a36; }
.aligned label { color: var(--body-quiet-color); }
.help { color: #8b949e; }
/* --- Boutons ------------------------------------------------ */
.button, input[type=submit], input[type=button], .submit-row input, a.button {
background : var(--button-bg);
color : var(--button-fg);
border : none;
border-radius: 4px;
font-weight : 600;
}
.button:hover, input[type=submit]:hover, input[type=button]:hover {
background: var(--button-hover-bg);
color : var(--button-fg);
}
.button.default, input[type=submit].default {
background: var(--accent);
color : #0d1117;
}
a.deletelink { background: #7f1d1d; color: #fca5a5; border-radius: 4px; padding: 4px 8px; }
a.deletelink:hover { background: #991b1b; color: #fca5a5; }
/* --- Object tools (haut des formulaires) ------------------- */
ul.object-tools li a { background: #21262d; color: var(--accent); border: 1px solid #1f2a36; }
ul.object-tools li a:hover { background: #2d333b; }
ul.object-tools li a.addlink { background: var(--accent); color: #0d1117; border: none; }
ul.object-tools li a.addlink:hover { background: var(--link-hover-color); }
/* --- Pagination -------------------------------------------- */
.paginator a, .paginator span {
background : #21262d;
color : var(--body-fg);
border : 1px solid #1f2a36;
border-radius: 3px;
}
.paginator a:hover { background: #2d333b; }
.paginator .this-page { background: var(--accent); color: #0d1117; border-color: var(--accent); }
/* --- Messages (success/warning/error) ---------------------- */
.messagelist li { border-radius: 4px; padding: 10px 14px; }
.messagelist li.success { background: rgba(63,185,80,.15); color: #3fb950; border: 1px solid rgba(63,185,80,.3); }
.messagelist li.warning { background: rgba(210,153,34,.15); color: #d29922; border: 1px solid rgba(210,153,34,.3); }
.messagelist li.error { background: rgba(248,81,73,.15); color: #f85149; border: 1px solid rgba(248,81,73,.3); }
/* --- Dashboard (index admin) -------------------------------- */
#content-main .module h2 { color: var(--body-fg); }
.dashboard #content { background: var(--body-bg); }
/* --- Inlines ------------------------------------------------ */
.inline-group { border: 1px solid #1f2a36; }
.inline-group thead tr { background: #161b22; }
.inline-related h3 { background: #161b22; border: 1px solid #1f2a36; color: var(--accent); }
/* --- Misc --------------------------------------------------- */
.errornote { background: rgba(248,81,73,.1); color: #f85149; border: 1px solid rgba(248,81,73,.4); }
.errorlist li { color: #f85149; }
.selector { background: #161b22; border: 1px solid #1f2a36; }
.selector select { background: #0d1117; color: var(--body-fg); border: none; }
.selector-available h2, .selector-chosen h2 { background: #161b22; color: var(--body-quiet-color); }
.selector-filter input { background: #21262d; color: var(--body-fg); border-color: #2d3748; }
@@ -0,0 +1,760 @@
/* ============================================================
Feuille de style principale — main
Thème sombre, zéro dépendance externe
============================================================ */
/* --- Variables -------------------------------------------- */
:root {
--bg-base : #0d1117;
--bg-surface : #1c2128;
--bg-elevated : #2d333b;
--bg-hover : #373e47;
--accent : #00d4aa;
--accent-dim : #00a884;
--accent-glow : rgba(0,212,170,.15);
--danger : #f85149;
--warning : #e3b341;
--info : #58a6ff;
--success : #56d364;
--text-primary : #f0f6fc;
--text-secondary:#b1bac4;
--text-muted : #768390;
--border : #30363d;
--radius-sm : 6px;
--radius-md : 10px;
--radius-lg : 16px;
--shadow-sm : 0 1px 3px rgba(0,0,0,.4);
--shadow-md : 0 4px 16px rgba(0,0,0,.5);
--shadow-lg : 0 8px 32px rgba(0,0,0,.6);
--sidebar-w : 220px;
--font : 'Segoe UI', system-ui, -apple-system, sans-serif;
}
/* --- Reset ------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 15px; scroll-behavior: smooth; }
body {
font-family : var(--font);
background : var(--bg-base);
color : var(--text-primary);
min-height : 100vh;
display : flex;
flex-direction : row;
line-height : 1.6;
}
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-dim); }
img { display: block; }
button { font-family: var(--font); cursor: pointer; }
/* --- Sidebar ---------------------------------------------- */
.vc-sidebar {
position : fixed;
top: 0; left: 0; bottom: 0;
width : var(--sidebar-w);
background : var(--bg-surface);
border-right : 1px solid var(--border);
display : flex;
flex-direction : column;
z-index : 1000;
overflow-y : auto;
}
.vc-brand {
display : flex;
flex-direction : column;
align-items : flex-start;
gap : 2px;
font-size : 1.1rem;
font-weight : 700;
color : var(--accent);
padding : 20px 16px 16px;
border-bottom : 1px solid var(--border);
white-space : nowrap;
}
.vc-brand:hover { color: var(--accent); }
.vc-brand small {
display : block;
font-size : 0.7rem;
font-weight : 400;
color : var(--text-muted);
}
.vc-sidebar-nav {
display : flex;
flex-direction : column;
gap : 2px;
padding : 12px 8px;
flex : 1;
}
.vc-nav-link {
display : flex;
align-items : center;
gap : 10px;
padding : 9px 12px;
border-radius : var(--radius-sm);
color : var(--text-secondary);
font-size : .9rem;
transition : background .15s, color .15s;
}
.vc-nav-link:hover,
.vc-nav-link--active {
background : var(--bg-hover);
color : var(--text-primary);
}
.vc-nav-link--active {
color : var(--accent);
}
.vc-nav-link { position: relative; }
button.vc-nav-link {
width : 100%;
background : transparent;
border : none;
cursor : pointer;
text-align : left;
font-family : inherit;
}
.vc-nav-alert-badge {
margin-left : auto;
display : inline-flex;
align-items : center;
justify-content: center;
min-width : 18px;
height : 18px;
padding : 0 5px;
border-radius : 9px;
font-size : .7rem;
font-weight : 700;
background : var(--danger, #f85149);
color : #fff;
line-height : 1;
}
.vc-sidebar-bottom {
border-top : 1px solid var(--border);
padding : 12px 8px;
display : flex;
flex-direction : column;
gap : 4px;
}
.vc-sidebar-user {
display : flex;
align-items : center;
gap : 8px;
padding : 8px 12px;
border-radius : var(--radius-sm);
color : var(--text-secondary);
font-size : .88rem;
}
.vc-logout-btn {
display : flex;
align-items : center;
gap : 8px;
width : 100%;
padding : 8px 12px;
background : transparent;
border : none;
border-radius : var(--radius-sm);
color : var(--danger);
font-size : .88rem;
text-align : left;
transition : background .12s;
}
.vc-logout-btn:hover { background: rgba(248,81,73,.1); }
/* --- Navbar pagination ------------------------------------ */
.vc-nav-page {
display : none;
flex-direction : column;
gap : 2px;
}
.vc-nav-page--active {
display : flex;
animation : vc-page-in .18s ease;
}
@keyframes vc-page-in {
from { opacity: 0; transform: translateY(5px); }
to { opacity: 1; transform: translateY(0); }
}
.vc-nav-pager {
display : flex;
align-items : center;
justify-content: center;
gap : 6px;
padding : 5px 0 2px;
}
.vc-pager-btn {
background : transparent;
border : none;
color : var(--text-muted);
font-size : 1.15rem;
line-height : 1;
padding : 1px 5px;
border-radius : var(--radius-sm);
cursor : pointer;
transition : color .12s, background .12s;
}
.vc-pager-btn:hover:not(:disabled) {
color : var(--text-primary);
background : var(--bg-hover);
}
.vc-pager-btn:disabled { opacity: .25; cursor: default; }
.vc-pager-dots {
display : flex;
gap : 5px;
align-items : center;
}
.vc-pager-dot {
width : 5px;
height : 5px;
border-radius : 50%;
background : var(--text-muted);
cursor : pointer;
transition : background .15s, transform .15s;
}
.vc-pager-dot:hover { background: var(--text-secondary); }
.vc-pager-dot--active {
background : var(--accent);
transform : scale(1.4);
}
/* --- Sidebar collapse (desktop) --------------------------- */
.vc-sidebar {
transition : transform .25s ease;
}
.vc-sidebar--collapsed {
transform : translateX(-100%);
}
.vc-sidebar-collapse-btn {
display : flex;
align-items : center;
justify-content: center;
width : 100%;
padding : 7px 12px;
background : transparent;
border : none;
border-top : 1px solid var(--border);
color : var(--text-muted);
font-size : .8rem;
gap : 6px;
cursor : pointer;
transition : color .15s, background .15s;
font-family : inherit;
}
.vc-sidebar-collapse-btn:hover { color: var(--text-primary); background: var(--bg-hover); }
/* Bouton flottant pour ré-ouvrir le sidebar (desktop) */
.vc-sidebar-open-btn {
display : none;
position : fixed;
top : 12px;
left : 8px;
z-index : 1100;
background : var(--bg-surface);
border : 1px solid var(--border);
border-radius : var(--radius-sm);
padding : 6px 8px;
color : var(--text-primary);
cursor : pointer;
}
.vc-sidebar-open-btn:hover { background: var(--bg-hover); }
.vc-main-wrap { transition: margin-left .25s ease; }
/* --- Hamburger (mobile) ----------------------------------- */
.vc-sidebar-toggle {
display : none;
position : fixed;
top : 12px;
left : 12px;
z-index : 1100;
background : var(--bg-surface);
border : 1px solid var(--border);
border-radius : var(--radius-sm);
padding : 6px 8px;
color : var(--text-primary);
}
/* --- Layout wrapper --------------------------------------- */
.vc-main-wrap {
flex : 1;
display : flex;
flex-direction : column;
min-height : 100vh;
min-width : 0;
}
.vc-main-wrap--with-sidebar {
margin-left : var(--sidebar-w);
}
/* --- Main / layout ---------------------------------------- */
.vc-main { flex: 1; }
.vc-container { max-width: 1280px; margin: 0 auto; padding: 32px 24px; }
.vc-container--narrow { max-width: 900px; }
.vc-grid-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
/* --- En-tête de page -------------------------------------- */
.vc-page-header {
display : flex;
align-items : center;
justify-content: space-between;
margin-bottom : 28px;
gap : 16px;
flex-wrap : wrap;
}
.vc-page-header h2 { font-size: 1.5rem; font-weight: 700; display: flex; align-items: center; gap: 10px; }
.vc-page-header h2 svg { color: var(--accent); }
.vc-header-actions { display: flex; gap: 8px; }
/* --- Cartes ----------------------------------------------- */
.vc-card {
background : var(--bg-surface);
border : 1px solid var(--border);
border-radius : var(--radius-lg);
padding : 24px;
margin-bottom : 20px;
}
.vc-card-title {
font-size : 1rem;
font-weight : 600;
margin-bottom : 16px;
display : flex;
align-items : center;
gap : 8px;
}
.vc-card-title svg { color: var(--accent); }
/* --- Boutons ---------------------------------------------- */
.vc-btn {
display : inline-flex;
align-items : center;
justify-content: center;
gap : 7px;
padding : 8px 18px;
border-radius : var(--radius-sm);
font-size : .9rem;
font-weight : 500;
line-height : 1.4;
border : 1px solid transparent;
transition : all .15s;
white-space : nowrap;
text-decoration: none;
}
.vc-btn:focus { outline: 2px solid var(--accent); outline-offset: 2px; }
.vc-btn--primary { background: var(--accent); color: #0d1117; border-color: var(--accent); }
.vc-btn--primary:hover { background: var(--accent-dim); border-color: var(--accent-dim); color: #0d1117; }
.vc-btn--ghost { background: var(--bg-surface); color: var(--text-primary); border-color: #6e7681; }
.vc-btn--ghost:hover { background: var(--bg-hover); color: var(--text-primary); border-color: #8b949e; }
.vc-btn--danger { background: transparent; color: var(--danger); border-color: var(--danger); }
.vc-btn--danger:hover { background: var(--danger); color: #fff; }
.vc-btn--large { padding: 12px 28px; font-size: 1rem; }
.vc-btn--block { width: 100%; }
.vc-btn--sm { padding: 5px 10px; font-size: .82rem; }
/* --- Formulaires ------------------------------------------ */
.vc-form-group { margin-bottom: 18px; }
.vc-form-group label {
display : flex;
align-items : center;
gap : 6px;
font-size : .85rem;
font-weight : 500;
color : var(--text-secondary);
margin-bottom : 6px;
}
.vc-form-group input[type="text"],
.vc-form-group input[type="email"],
.vc-form-group input[type="password"],
.vc-form-group input[type="number"],
.vc-form-group textarea,
.vc-form-group select {
width : 100%;
padding : 9px 12px;
background : var(--bg-elevated);
border : 1px solid var(--border);
border-radius : var(--radius-sm);
color : var(--text-primary);
font-size : .9rem;
font-family : var(--font);
transition : border-color .15s, box-shadow .15s;
}
.vc-form-group input:focus,
.vc-form-group textarea:focus,
.vc-form-group select:focus {
outline : none;
border-color : var(--accent);
box-shadow : 0 0 0 3px var(--accent-glow);
}
.vc-form-group input::placeholder,
.vc-form-group textarea::placeholder { color: var(--text-muted); }
.vc-field-error { color: var(--danger); font-size: .82rem; margin-top: 4px; }
.vc-field-hint { color: var(--text-muted); font-size: .8rem; margin-top: 4px; }
.vc-form-actions {
display : flex;
gap : 10px;
justify-content: flex-end;
padding-top : 16px;
border-top : 1px solid var(--border);
margin-top : 20px;
}
/* --- Badges ----------------------------------------------- */
.vc-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 3px 10px;
border-radius: 20px;
font-size: .78rem;
font-weight: 600;
}
.vc-badge--success { background: rgba(63,185,80,.15); color: var(--success); }
.vc-badge--danger { background: rgba(248,81,73,.15); color: var(--danger); }
.vc-badge--info { background: rgba(56,139,253,.15); color: var(--info); }
.vc-badge--warning { background: rgba(210,153,34,.15); color: var(--warning); }
/* --- Messages système ------------------------------------- */
.vc-messages {
position : fixed;
top : 12px;
right : 16px;
z-index : 2000;
display : flex;
flex-direction : column;
gap : 8px;
max-width : 360px;
}
.vc-alert {
display : flex;
align-items : center;
justify-content: space-between;
gap : 12px;
padding : 12px 16px;
border-radius : var(--radius-md);
background : var(--bg-elevated);
border-left : 4px solid var(--accent);
box-shadow : var(--shadow-md);
font-size : .88rem;
}
.vc-alert--error { border-color: var(--danger); }
.vc-alert--warning { border-color: var(--warning); }
.vc-alert--info { border-color: var(--info); }
.vc-alert button { background: none; border: none; color: var(--text-muted); font-size: .9rem; padding: 2px; flex-shrink: 0; }
.vc-alert button:hover { color: var(--text-primary); }
/* --- Authentification ------------------------------------- */
.vc-auth-wrap {
min-height : 100vh;
display : flex;
align-items : center;
justify-content: center;
padding : 32px 16px;
background : radial-gradient(ellipse at 60% 30%, rgba(0,212,170,.06) 0%, transparent 70%);
}
.vc-auth-card {
width : 100%;
max-width : 420px;
background : var(--bg-surface);
border : 1px solid var(--border);
border-radius : var(--radius-lg);
padding : 40px 36px;
box-shadow : var(--shadow-lg);
}
.vc-auth-header { text-align: center; margin-bottom: 28px; }
.vc-auth-icon {
width : 56px; height: 56px;
border-radius : 50%;
background : var(--accent-glow);
border : 1px solid var(--accent);
display : flex;
align-items : center;
justify-content: center;
margin : 0 auto 16px;
color : var(--accent);
}
.vc-auth-header h1 { font-size: 1.6rem; margin-bottom: 6px; }
.vc-auth-header p { color: var(--text-secondary); font-size: .88rem; }
/* --- Table générique -------------------------------------- */
.vc-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.vc-table th {
text-align: left; padding: 10px 12px;
border-bottom: 2px solid var(--border);
color: var(--text-secondary); font-weight: 600; font-size: .8rem;
text-transform: uppercase; letter-spacing: .05em; white-space: nowrap;
}
.vc-table td { padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.vc-table tr:last-child td { border-bottom: none; }
.vc-table tr:hover td { background: var(--bg-elevated); }
/* --- État vide -------------------------------------------- */
.vc-empty-state { text-align: center; padding: 72px 24px; }
.vc-empty-state svg { color: var(--bg-elevated); display: block; margin: 0 auto 16px; }
.vc-empty-state h3 { color: var(--text-secondary); margin-bottom: 8px; }
.vc-empty-state p { color: var(--text-muted); margin-bottom: 16px; }
/* --- Dashboard grille ------------------------------------- */
.vc-dashboard-grid {
display : grid;
grid-template-columns: repeat(12, 1fr);
grid-auto-rows : 120px;
gap : 12px;
padding : 24px;
align-items : start;
}
.vc-widget {
background : var(--bg-surface);
border : 1px solid var(--border);
border-radius : var(--radius-lg);
overflow : hidden;
display : flex;
flex-direction : column;
transition : border-color .2s, box-shadow .2s;
height : 100%;
}
.vc-widget:hover { border-color: var(--accent); box-shadow: 0 4px 20px rgba(0,212,170,.08); }
.vc-widget--dragging {
opacity : .5;
border-color : var(--accent);
}
.vc-widget--drag-over {
border-color : var(--accent);
box-shadow : 0 0 0 2px var(--accent-glow);
}
.vc-widget[draggable="true"] { cursor: grab; }
.vc-widget[draggable="true"]:active { cursor: grabbing; }
.vc-widget-header {
display : flex;
align-items : center;
justify-content: space-between;
padding : 10px 14px;
border-bottom : 1px solid var(--border);
font-size : .85rem;
font-weight : 600;
flex-shrink : 0;
gap : 8px;
}
.vc-widget-header svg { color: var(--accent); flex-shrink: 0; }
.vc-widget-body {
padding : 14px;
flex : 1;
overflow : hidden;
}
/* Widget : texte / boîte */
.vc-widget-text {
font-size : .9rem;
line-height : 1.7;
color : var(--text-secondary);
word-break : break-word;
}
/* Widget : Grafana iframe */
.vc-widget-iframe {
width : 100%;
height : 100%;
border : none;
display : block;
}
.vc-widget-body--iframe {
padding : 0;
}
/* Widget : placeholder (caméra / device) */
.vc-widget-placeholder {
height : 100%;
display : flex;
flex-direction : column;
align-items : center;
justify-content: center;
gap : 8px;
color : var(--text-muted);
font-size : .82rem;
}
.vc-widget-placeholder svg { color: var(--bg-elevated); }
/* Widget : grille imbriquée */
.vc-widget-grid {
display : grid;
grid-template-columns: 1fr 1fr;
gap : 8px;
height : 100%;
}
.vc-widget-grid-cell {
background : var(--bg-elevated);
border-radius : var(--radius-sm);
display : flex;
align-items : center;
justify-content: center;
color : var(--text-muted);
font-size : .8rem;
}
/* --- Footer ----------------------------------------------- */
.vc-footer {
background : var(--bg-surface);
border-top : 1px solid var(--border);
padding : 12px 24px;
font-size : .8rem;
color : var(--text-muted);
display : flex;
align-items : center;
gap : 12px;
}
.vc-footer a { color: var(--text-muted); }
.vc-footer a:hover { color: var(--accent); }
/* --- Texte utilitaire ------------------------------------- */
.vc-text-muted { color: var(--text-muted); font-size: .88rem; display: flex; align-items: center; gap: 6px; }
.vc-section-title { font-size: 1rem; font-weight: 600; color: var(--text-secondary); margin: 0 0 16px; display: flex; align-items: center; gap: 8px; }
.vc-section-title svg { color: var(--accent); }
/* --- Sélecteur de langue (Phase 5) -------------------------- */
.vc-language-switcher {
padding: 10px 12px;
border-top: 1px solid var(--border);
margin-top: auto;
}
.vc-language-switcher select {
width: 100%;
background: transparent;
color: var(--text-muted);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 4px 8px;
font-size: .85rem;
}
/* --- Stats système sidebar --------------------------------- */
.vc-sidebar-stats {
padding : 10px 12px 8px;
border-bottom : 1px solid var(--border);
display : flex;
flex-direction : column;
gap : 6px;
}
.vc-stat-row {
display : flex;
align-items : center;
gap : 7px;
color : var(--text-muted);
cursor : default;
}
.vc-stat-icon {
width : 13px;
height : 13px;
flex-shrink : 0;
color : var(--text-secondary);
}
.vc-stat-track {
flex : 1;
height : 3px;
background : var(--bg-elevated);
border-radius : 2px;
overflow : hidden;
}
.vc-stat-fill {
height : 100%;
width : 0%;
border-radius : 2px;
transition : width .6s ease;
}
.vc-stat-fill--cpu { background: #6366f1; }
.vc-stat-fill--mem { background: #f97316; }
.vc-stat-fill--disk { background: #eab308; }
.vc-stat-fill--warn { background: #ef4444; }
.vc-stat-pct {
width : 30px;
text-align : right;
font-size : 10px;
font-variant-numeric: tabular-nums;
color : var(--text-muted);
flex-shrink : 0;
}
/* Compteurs top-jail/top-pays : réutilisent le mécanisme track+fill+pct
ci-dessus (barre relative au plus grand compte de la liste, pas un
pourcentage réel), avec un libellé texte à la place de l'icône —
catégories nominales sans ordre naturel, donc UNE seule couleur par
widget plutôt qu'une rotation par ligne (cf. skill dataviz : ne pas
double-encoder la longueur de barre avec une teinte différente par
catégorie quand rien ne justifie un ordre). */
.vc-stat-label {
width : 84px;
flex-shrink : 0;
overflow : hidden;
text-overflow : ellipsis;
white-space : nowrap;
font-size : 11px;
}
.vc-stat-label--link { color: var(--text-secondary); text-decoration: none; }
.vc-stat-label--link:hover { color: var(--accent); text-decoration: underline; }
.vc-stat-fill--jail { background: var(--accent); }
.vc-stat-fill--country { background: var(--info); }
.vc-sidebar-stats-title {
font-size : .68rem;
text-transform : uppercase;
letter-spacing : .05em;
color : var(--text-muted);
margin-top : 2px;
}
.vc-sidebar-stats-title:first-child { margin-top: 0; }
/* Plafonne le bloc "Noeuds" à ~6 lignes avec défilement propre à lui —
sans ça, beaucoup de noeuds (roster, Phase 3) repoussent indéfiniment
"Top jails"/"Top pays attaquants" plus bas, jusqu'à devoir scroller
toute la sidebar (nav comprise) pour les atteindre. */
.vc-node-list {
display : flex;
flex-direction : column;
gap : 6px;
max-height : 150px;
overflow-y : auto;
}
.vc-stat-dot {
width : 7px;
height : 7px;
border-radius : 50%;
flex-shrink : 0;
}
.vc-stat-dot--online { background: var(--success); }
.vc-stat-dot--offline { background: var(--danger); }
.vc-stat-time {
margin-left : auto;
font-size : 10px;
color : var(--text-muted);
flex-shrink : 0;
}
/* --- Responsive ------------------------------------------- */
@media (max-width: 1200px) {
.vc-dashboard-grid {
grid-template-columns: repeat(8, 1fr);
}
}
@media (max-width: 768px) {
.vc-sidebar {
transform : translateX(-100%);
transition : transform .25s ease;
}
.vc-sidebar--open {
transform : translateX(0);
}
.vc-sidebar-toggle { display: flex; align-items: center; }
.vc-sidebar-collapse-btn { display: none; }
.vc-sidebar-open-btn { display: none !important; }
.vc-main-wrap--with-sidebar { margin-left: 0; }
.vc-grid-2col { grid-template-columns: 1fr; }
.vc-container { padding: 16px; }
.vc-auth-card { padding: 28px 20px; }
.vc-page-header { flex-direction: column; align-items: flex-start; }
.vc-dashboard-grid {
grid-template-columns: repeat(4, 1fr);
padding: 12px;
gap: 8px;
}
}
@@ -0,0 +1,44 @@
document.addEventListener('DOMContentLoaded', () => {
const selectedNode = document.body.dataset.selectedNode || '';
const matchesFilter = (banEvent) => !selectedNode || banEvent.node === selectedNode;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const stream = new BanEventStream(`${protocol}//${window.location.host}/ws/banevents/`);
const notifications = new NotificationCenter('#banevents-messages');
stream.onEvent((message) => {
if (message.kind === 'command') {
notifications.show(message);
}
});
const table = new BanEventTable('#banevents-table-body', '#banevents-empty-state');
stream.onEvent((banEvent) => {
if (banEvent.kind === 'ban' && matchesFilter(banEvent)) {
table.prependRow(banEvent);
}
});
const mapElement = document.getElementById('banevents-map');
if (mapElement) {
const map = new BanEventMap('banevents-map');
stream.onEvent((banEvent) => {
if (banEvent.kind === 'ban' && matchesFilter(banEvent)) {
map.addMarker(banEvent);
}
});
const initialDataElement = document.getElementById('banevents-initial-data');
if (initialDataElement) {
const initialEvents = JSON.parse(initialDataElement.textContent);
initialEvents.forEach((banEvent) => map.addMarker(banEvent));
}
const resetButton = document.getElementById('banevents-map-reset');
if (resetButton) {
resetButton.addEventListener('click', () => map.resetView());
}
}
stream.connect();
});
@@ -0,0 +1,19 @@
document.addEventListener('DOMContentLoaded', () => {
const mapElement = document.getElementById('banevents-map');
if (!mapElement) {
return;
}
const map = new BanEventMap('banevents-map');
const initialDataElement = document.getElementById('banevents-initial-data');
if (initialDataElement) {
const initialEvents = JSON.parse(initialDataElement.textContent);
initialEvents.forEach((banEvent) => map.addMarker(banEvent));
}
const resetButton = document.getElementById('banevents-map-reset');
if (resetButton) {
resetButton.addEventListener('click', () => map.resetView());
}
});
@@ -0,0 +1,42 @@
/* Carte Leaflet : un marqueur par BanEvent géolocalisé. Les événements
* sans latitude/longitude (géolocalisation pas encore terminée) sont
* ignorés jusqu'à la mise à jour poussée par la tâche Celery. */
class BanEventMap {
static DEFAULT_CENTER = [20, 0];
static DEFAULT_ZOOM = 2;
constructor(elementId) {
this.map = L.map(elementId).setView(BanEventMap.DEFAULT_CENTER, BanEventMap.DEFAULT_ZOOM);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
maxZoom: 18,
}).addTo(this.map);
this.markers = L.layerGroup().addTo(this.map);
this.markersByEventId = new Map();
}
resetView() {
this.map.setView(BanEventMap.DEFAULT_CENTER, BanEventMap.DEFAULT_ZOOM);
}
addMarker(banEvent) {
if (banEvent.latitude == null || banEvent.longitude == null) {
return;
}
this.removeMarker(banEvent.id);
const marker = L.marker([banEvent.latitude, banEvent.longitude]);
const location = [banEvent.city, banEvent.country].filter(Boolean).join(', ');
marker.bindPopup(`<strong>${banEvent.ip_address}</strong><br>${banEvent.jail_name}${banEvent.action}<br>${location}`);
marker.addTo(this.markers);
this.markersByEventId.set(banEvent.id, marker);
}
removeMarker(eventId) {
const existing = this.markersByEventId.get(eventId);
if (existing) {
this.markers.removeLayer(existing);
this.markersByEventId.delete(eventId);
}
}
}
@@ -0,0 +1,50 @@
/* Notifications transitoires (.vc-messages/.vc-alert, main.css) pour les
* commandes reçues du master (banevents.consumers.command_notification) —
* un SYNC_BAN réussi apparaît déjà indirectement via la ligne de table
* qu'il déclenche ; ceci couvre les échecs et commandes non gérées, qui
* autrement ne laissent aucune trace visible hors journalctl. */
const ALERT_CLASSES = {
ok: '',
error: 'vc-alert--error',
unhandled: 'vc-alert--warning',
};
const AUTO_DISMISS_MS = 8000;
// Chaînes traduites (Phase 5) posées par dashboard.html (#banevents-i18n,
// json_script-style) — jamais codées en dur ici, pour rester cohérentes
// avec la langue active côté serveur (fr/en).
const I18N_EL = document.getElementById('banevents-i18n');
const I18N = I18N_EL ? JSON.parse(I18N_EL.textContent) : {
unhandled: 'Commande reçue du master (non gérée)', ok: 'appliqué', error: 'échec', close: 'Fermer',
};
function commandNotificationText(notification) {
const { cmd, ip, status, detail } = notification;
if (status === 'unhandled') {
return `${I18N.unhandled} : ${cmd}`;
}
const outcome = status === 'ok' ? I18N.ok : `${I18N.error}${detail ? ` (${detail})` : ''}`;
return `${cmd} ${ip}${outcome}`;
}
class NotificationCenter {
constructor(containerSelector) {
this.container = document.querySelector(containerSelector);
}
show(notification) {
if (!this.container) {
return;
}
const alertClass = ALERT_CLASSES[notification.status] || '';
const alert = document.createElement('div');
alert.className = `vc-alert ${alertClass}`.trim();
alert.innerHTML = `
<span>${commandNotificationText(notification)}</span>
<button type="button" aria-label="${I18N.close}">✕</button>
`;
alert.querySelector('button').addEventListener('click', () => alert.remove());
this.container.appendChild(alert);
setTimeout(() => alert.remove(), AUTO_DISMISS_MS);
}
}
@@ -0,0 +1,26 @@
/* Connexion WebSocket unique au tableau de bord, redistribuée aux écouteurs
* (tableau, carte) via onEvent(). Reconnexion automatique sur coupure. */
class BanEventStream {
constructor(url) {
this.url = url;
this.listeners = [];
this.socket = null;
}
onEvent(callback) {
this.listeners.push(callback);
}
connect() {
this.socket = new WebSocket(this.url);
this.socket.addEventListener('message', (event) => {
const banEvent = JSON.parse(event.data);
this.listeners.forEach((callback) => callback(banEvent));
});
this.socket.addEventListener('close', () => this.scheduleReconnect());
}
scheduleReconnect() {
setTimeout(() => this.connect(), 3000);
}
}
@@ -0,0 +1,38 @@
/* Ajoute chaque BanEvent reçu en tête du tableau du tableau de bord. */
const ACTION_BADGE_CLASSES = {
BAN: 'vc-badge--danger',
UNBAN: 'vc-badge--success',
};
function actionBadgeClass(action) {
return ACTION_BADGE_CLASSES[action] || 'vc-badge--info';
}
class BanEventTable {
constructor(tableBodySelector, emptyStateSelector) {
this.tableBody = document.querySelector(tableBodySelector);
this.emptyState = document.querySelector(emptyStateSelector);
}
prependRow(banEvent) {
if (this.emptyState) {
this.emptyState.remove();
this.emptyState = null;
}
if (!this.tableBody) {
return;
}
const row = document.createElement('tr');
row.innerHTML = `
<td>${banEvent.received_at}</td>
<td>${banEvent.node_alias}</td>
<td>${banEvent.jail_name}</td>
<td><span class="vc-badge ${actionBadgeClass(banEvent.action)}">${banEvent.action}</span></td>
<td>${banEvent.ip_address}</td>
<td>${banEvent.port || '—'}/${banEvent.protocol || '—'}</td>
<td>${banEvent.bantime ?? '—'}</td>
<td>${banEvent.city || banEvent.country || '—'}</td>
`;
this.tableBody.prepend(row);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

@@ -0,0 +1,661 @@
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Prevents IE11 from highlighting tiles in blue */
.leaflet-tile::selection {
background: transparent;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg {
max-width: none !important;
max-height: none !important;
}
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer,
.leaflet-container .leaflet-tile {
max-width: none !important;
max-height: none !important;
width: auto;
padding: 0;
}
.leaflet-container img.leaflet-tile {
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
mix-blend-mode: plus-lighter;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
/* Fallback for FF which doesn't support pinch-zoom */
touch-action: none;
touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
svg.leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-interactive {
cursor: pointer;
}
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
pointer-events: none;
}
.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive,
svg.leaflet-image-layer.leaflet-interactive path {
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline-offset: 1px;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
font-size: 12px;
font-size: 0.75rem;
line-height: 1.5;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover,
.leaflet-bar a:focus {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
font-size: 22px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
font-size: 13px;
font-size: 1.08333em;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* Default icon URLs */
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
background-image: url(images/marker-icon.png);
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.8);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
line-height: 1.4;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover,
.leaflet-control-attribution a:focus {
text-decoration: underline;
}
.leaflet-attribution-flag {
display: inline !important;
vertical-align: baseline !important;
width: 1em;
height: 0.6669em;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
white-space: nowrap;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.8);
text-shadow: 1px 1px #fff;
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
margin-bottom: 20px;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 24px 13px 20px;
line-height: 1.3;
font-size: 13px;
font-size: 1.08333em;
min-height: 1px;
}
.leaflet-popup-content p {
margin: 17px 0;
margin: 1.3em 0;
}
.leaflet-popup-tip-container {
width: 40px;
height: 20px;
position: absolute;
left: 50%;
margin-top: -1px;
margin-left: -20px;
overflow: hidden;
pointer-events: none;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
pointer-events: auto;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
border: none;
text-align: center;
width: 24px;
height: 24px;
font: 16px/24px Tahoma, Verdana, sans-serif;
color: #757575;
text-decoration: none;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover,
.leaflet-container a.leaflet-popup-close-button:focus {
color: #585858;
}
.leaflet-popup-scrolled {
overflow: auto;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
-ms-zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
position: absolute;
padding: 6px;
background-color: #fff;
border: 1px solid #fff;
border-radius: 3px;
color: #222;
white-space: nowrap;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.leaflet-tooltip.leaflet-interactive {
cursor: pointer;
pointer-events: auto;
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
position: absolute;
pointer-events: none;
border: 6px solid transparent;
background: transparent;
content: "";
}
/* Directions */
.leaflet-tooltip-bottom {
margin-top: 6px;
}
.leaflet-tooltip-top {
margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
.leaflet-tooltip-left {
margin-left: -6px;
}
.leaflet-tooltip-right {
margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
border-right-color: #fff;
}
/* Printing */
@media print {
/* Prevent printers from removing background-images of controls. */
.leaflet-control {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long