Ajout de HelloFresh

This commit is contained in:
2025-09-03 20:17:50 +02:00
parent bcef0a472b
commit d287112b7d
429 changed files with 82881 additions and 22074 deletions

View File

@@ -0,0 +1,20 @@
.sommeCard {
}
.card-title {
}
.divAffichageCarte {
display: flex;
}
.subTitleModal {
font-weight: bold;
padding-bottom: 0px;
margin-bottom: 0;
}
div.dt-container div.dt-layout-row {
margin: .25em 0!important;
}

View File

@@ -0,0 +1,325 @@
:root {
--bg: #0b0d12;
--panel: #11141b;
--muted: #8a93a6;
--text: #e8ecf3;
--accent: #4f8cff;
--accent-2: #63e;
--card: #151a22;
--border: #1f2632;
}
* {
box-sizing: border-box
}
html, body {
height: 100%;
margin: 0;
background: var(--bg);
color: var(--text);
}
body {
overflow: hidden;
font: 14px/1.4 system-ui,-apple-system,Segoe UI,Roboto,"Helvetica Neue",Arial;
}
.cui-root {
height: 100dvh;
display: grid;
grid-template-columns: 320px 1fr;
gap: 0;
}
.cui-root.collapsed {
grid-template-columns: 56px 1fr;
}
.cui-side {
background: var(--panel);
border-right: 1px solid var(--border);
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.cui-side-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 12px 8px;
border-bottom: 1px solid var(--border);
}
.cui-side h2 {
margin: 0;
font-size: 16px;
font-weight: 700;
}
.cui-side .ghost {
width: 28px;
height: 28px;
border-radius: 10px;
}
.cui-root.collapsed #toggleSide {
transform: rotate(180deg)
}
.ghost {
background: transparent;
border: 1px solid var(--border);
color: var(--muted);
cursor: pointer
}
.ghost:hover {
border-color: #2a3343;
color: #b7c2da
}
.cui-cards {
padding: 10px;
overflow: auto;
height: 100%;
}
/* --- Cartes --- */
.cui-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 14px;
padding: 8px;
display: grid;
grid-template-columns: 40px 1fr;
gap: 8px;
align-items: center;
margin-bottom: 8px;
cursor: pointer;
transition: transform .12s ease, border-color .12s ease, background .12s ease, box-shadow .12s ease;
position: relative; /* pour l'effet d'état */
}
.cui-card:hover {
transform: translateY(-1px);
border-color: #2a3343
}
/* ✅ Remplace loutline par un box-shadow interne : plus de “carré bleu” coupé */
.cui-card.active {
box-shadow: inset 0 0 0 2px var(--accent), 0 0 0 3px rgba(79,140,255,.15);
border-color: rgba(79,140,255,.45);
}
/* contenu carte */
.cui-thumb {
width: 40px;
height: 40px;
border-radius: 10px;
object-fit: cover;
background: #222
}
.cui-info {
min-width: 0;
}
/* ✅ permet lellipsis */
.cui-name {
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #cfd6e6
}
.cui-meta {
font-size: 11px;
color: var(--muted)
}
.cui-main {
display: flex;
flex-direction: column;
min-width: 0;
height: 100%;
}
.cui-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
border-bottom: 1px solid var(--border);
background: linear-gradient(180deg, #0f131a, #0c1016);
gap: 12px;
}
.cui-title {
font-size: 15px;
font-weight: 600;
flex: 1 1 auto; /* ✅ prend lespace dispo */
min-width: 0; /* ✅ autorise lellipsis */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.primary {
background: var(--accent);
color: white;
border: 0;
padding: 8px 12px;
border-radius: 10px;
font-weight: 700;
cursor: pointer;
box-shadow: 0 6px 16px rgba(79,140,255,.25);
}
.primary:disabled {
opacity: .5;
cursor: not-allowed;
box-shadow: none
}
.cui-view {
position: relative;
flex: 1;
min-height: 0;
background: #0a0d13;
}
.cui-iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
display: none;
}
.cui-placeholder {
position: absolute;
inset: 0;
display: grid;
place-items: center;
color: var(--muted);
font-size: 15px;
}
.viewer {
position: fixed;
inset: 0;
background: #000;
display: flex;
flex-direction: column;
z-index: 9999
}
.viewer.hidden {
display: none
}
.viewer-top {
height: 48px;
display: flex;
align-items: center;
gap: 12px;
padding: 8px 10px;
background: rgba(5,8,12,.7);
backdrop-filter: blur(6px);
}
.viewer-title {
color: #dfe6f5;
font-weight: 600;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
min-width: 0;
}
.viewer-iframe {
flex: 1;
width: 100%;
border: 0
}
#toggleSide {
transition: transform .15s ease
}
.cui-root.collapsed .cui-side h2 {
display: none
}
.cui-root.collapsed .cui-card {
grid-template-columns: 40px;
padding: 6px
}
.cui-root.collapsed .cui-name, .cui-root.collapsed .cui-meta {
display: none
}
/* petites cartes = pas de scroll vertical à larrivée */
@media (min-width: 1200px) {
.cui-card {
grid-template-columns: 36px 1fr;
gap: 6px
}
.cui-thumb {
width: 36px;
height: 36px
}
}
/* tablette : sidebar plus étroite (toujours sans scroll body) */
@media (max-width: 900px) {
.cui-root {
grid-template-columns: 86px 1fr;
}
}
/* --- Correctifs ciblés --- */
/* Anneau interne pour la carte active (ne sera plus coupé) */
.cui-card.active {
/* on remplace l'outline externe par un halo interne */
box-shadow: 0 0 0 2px var(--accent) inset;
outline: none; /* par sûreté */
}
/* quand la sidebar est réduite, même anneau interne */
.cui-root.collapsed .cui-card.active {
box-shadow: 0 0 0 2px var(--accent) inset;
}
/* Titre de la barre du haut : ellipsis propre si le nom est long */
.cui-title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: calc(100vw - 220px); /* laisse la place au bouton */
}
/* Liframe de prévisualisation occupe exactement lespace dispo */
.cui-iframe {
display: block; /* évite certains comportements inline-iframe */
width: 100%;
height: 100%;
}
/* Par sécurité, pas de scroll horizontal dans le PDF */
.cui-view {
overflow: hidden;
}
/* Dans la liste, garantir lellipsis sur les noms longs */
.cui-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

View File

@@ -0,0 +1,464 @@
:root {
--hf-bg: #fff;
--hf-muted: #777;
--hf-border: #ececec;
--hf-ink: #1c1c1c;
--hf-pill: #f9f9f9;
}
.hf-ingredients {
background: var(--hf-bg);
border-radius: 12px;
padding: 16px 18px;
box-shadow: 0 2px 14px rgba(0,0,0,.06);
font-family: system-ui, Segoe UI, Roboto, sans-serif;
max-width: 105rem;
margin: 18px auto;
}
.hf-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.hf-head h2 {
margin: 0;
font-size: 1.6rem;
font-weight: 800;
}
.hf-qty {
display: flex;
align-items: center;
gap: 10px;
color: #444;
font-weight: 600;
}
.hf-qty select {
padding: 6px 10px;
border: 1px solid #cfcfcf;
border-radius: 8px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 1.5rem;
margin: 3%;
margin-top: 2%;
margin-bottom: 1%;
}
.card {
background: white;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
overflow: hidden;
display: flex;
flex-direction: column;
transition: transform 0.2s;
cursor: pointer;
width: 15rem;
position: relative; /* Pour positionner les éléments absolus à l'intérieur */
}
.card:hover {
transform: scale(1.02);
}
.card img {
width: 100%;
height: 150px;
object-fit: cover;
}
.card-content {
padding: 1rem;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* Titre de la recette */
.card h3 {
margin: 0;
font-size: 0.82rem;
color: #333;
font-weight: 800;
}
/* (Optionnel) Sous-titre ou autres infos */
.card h5 {
margin-top: 1%;
font-size: 0.80rem;
}
/* Lien (non utilisé ici, mais présent dans d'autres contextes) */
.card a {
text-decoration: none;
color: #fff;
background: #27ae60;
padding: 0.5rem 1rem;
border-radius: 8px;
font-weight: bold;
display: inline-block;
margin-top: 1rem;
transition: background 0.2s;
}
.card a:hover {
background: #1e874b;
}
.placeholder {
text-align: center;
color: #888;
}
/* Conteneur des étiquettes (tags) de chaque recette */
.label-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
padding: 0 10px;
position: absolute;
top: 0px;
left: 0;
}
.label-badge {
padding: 4px 10px;
border-radius: 8px;
font-size: 0.60rem;
font-weight: 600;
white-space: nowrap;
line-height: 1.5;
opacity: .9;
}
/* Informations de recette (temps de préparation, etc.) */
.recipe-info {
font-size: 0.9rem;
font-weight: 700;
/* Le texte (temps) sera aligné à droite par le jeu de flexbox dans .footerCard */
}
/* Infobulle (tooltip) */
.tooltip {
position: absolute;
bottom: 110%;
left: 10px;
background-color: #333;
color: #fff;
padding: 8px 12px;
border-radius: 6px;
font-size: 0.9em;
white-space: pre-wrap;
z-index: 1000;
opacity: 0;
pointer-events: none;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease;
width: max-content;
max-width: 250px;
}
.card:hover .tooltip {
opacity: 1;
pointer-events: auto;
transform: translateY(-14px);
}
/* Styles pour la section pliable */
.section-toggle {
position: relative;
margin: 45px 0 0 0;
}
.section-hr {
border: 0;
border-top: 2px solid #ddd;
margin: 0;
}
.section-chip {
position: absolute;
left: 50%;
top: 0;
transform: translate(-50%, -50%);
display: flex;
gap: 8px;
align-items: center;
padding: 6px 12px;
border-radius: 5px;
border: 1px solid #e5e7eb;
background: #f4f4f4;
font-weight: 600;
cursor: pointer;
user-select: none;
}
.collapse-icon {
transition: transform 0.25s ease;
}
.section-toggle.open .collapse-icon {
transform: rotate(180deg);
}
.collapse-wrap {
overflow: hidden;
height: 0;
opacity: 0;
transition: height 0.26s ease, opacity 0.26s ease;
}
.collapse-wrap.open {
opacity: 1;
}
/* Pour la zone de contenu défilable (si applicable) */
.content {
overflow: auto;
}
.sticky-search {
position: sticky;
top: 0;
z-index: 10;
padding: 8px 12px;
}
/* Conteneur en bas de la carte pour temps + contrôles de portions */
.footerCard {
display: flex;
justify-content: space-between;
align-items: flex-end !important;
flex-direction: row-reverse;
}
/* Badge affichant le nombre de portions sélectionnées sur l'image */
.badge-portions {
position: absolute;
right: 8px;
background: rgba(0, 0, 0, 0.75);
color: #fff;
font-size: 0.55rem;
padding: 4px 8px;
border-radius: 12px;
top: 10px;
}
/* Contrôles de quantité (boutons +/ et affichage du nombre) */
.quantity-controls {
display: flex;
align-items: center;
gap: 4px;
/* Initialement, on les masque dans le HTML via style="display:none" (géré en JS) */
}
.quantity-controls button {
background: #f1f1f1;
border: 0;
padding: 0px 4px;
border-radius: 6px;
cursor: pointer;
}
.quantity-controls button:hover {
background: #e6e6e6;
}
.quantity {
font-weight: 700;
min-width: 1.5rem;
text-align: center;
}
/* Base: un léger contour pour qu'on voie le surlignage quand sélectionnée */
.card {
border: 1px solid #eee;
}
/* Les cartes dans OWNED n'ont pas de surlignage vert (c'est implicite qu'elles sont choisies) */
#recipeGridOwned .card {
border-color: #eee; /* force un rendu "neutre" */
box-shadow: 0 4px 10px rgba(0,0,0,0.08);
}
.footerCard {
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: row; /* plus de row-reverse */
}
/* Contrôles visibles seulement dans OWNED */
#recipeGrid .quantity-controls {
display: none !important;
}
#recipeGridOwned .quantity-controls {
display: flex;
}
.placeholder-result {
background: none;
color: black;
width: 40rem;
text-align: left;
font-style: italic;
}
.pagination-modern {
bottom: 20px;
right: 65px;
backdrop-filter: blur(6px);
border-radius: 12px;
padding: 6px 14px;
display: flex;
align-items: center;
gap: 8px;
font-family: 'Segoe UI', sans-serif;
font-size: 14px;
z-index: 999;
width: fit-content;
MARGIN: auto;
margin-right: 2.4%;
}
.pagination-btn {
border: none;
background: #4CAF50;
color: white;
font-size: 16px;
padding: 6px 10px;
border-radius: 8px;
cursor: pointer;
transition: background 0.2s ease, transform 0.1s ease;
}
.pagination-btn:hover {
background: #45a049;
}
.pagination-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
#pageInfo {
font-weight: 500;
color: #333;
}
.owned-empty {
display: block;
color: #666;
font-weight: 600;
border-radius: 10px;
font-style: italic;
}
/* Section pliable ULTRA FIABLE en max-height */
/* --- FIX: n'utiliser que max-height pour la section pliable --- */
.collapse-wrap {
/* on annule la vieille règle à base de height:0 */
height: auto !important;
/* on garde le système au max-height */
overflow: hidden;
max-height: 0;
opacity: 0;
transition: max-height .30s ease, opacity .30s ease;
}
.collapse-wrap.open {
opacity: 1;
max-height: 2000px; /* assez grand pour tout contenir */
}
/* Message "aucune recette" masqué par défaut */
#noSelectedMsg {
display: none;
font-style: italic;
color: #666; /* texte grisé par exemple */
}
/* Emplacement réservé pour l'indicateur (évite les décalages) */
.selection-slot {
display: flex;
align-items: center;
gap: 6px;
min-height: 18px; /* hauteur de la pastille */
min-width: 110px; /* réserve de la place pour "Sélectionné" */
visibility: hidden; /* masqué par défaut mais prend la place */
}
.selection-slot.active { /* quand sélectionné */
visibility: visible;
}
.selection-indicator {
display: flex;
align-items: center;
gap: 6px;
font-weight: 700;
color: #27ae60;
}
.selection-indicator .check-icon {
background: #27ae60;
color: #fff;
font-size: .75rem;
width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.selection-text {
font-size: .8rem;
font-weight: 900;
}
#pdfModal {
z-index: 2000 !important; /* au-dessus de tout */
}
.modal-backdrop {
z-index: 1999 !important;
}
.placeholder {
color: #666;
}
.hf-grid {
display: flex;
gap: 17px;
margin-bottom: 40px;
}
label {
margin-bottom: 0rem;
}
h3.history-date {
font-size: 1.1rem;
font-weight: 500;
}
div#historyContainer {
margin-top: 1.5rem;
}

View File

@@ -0,0 +1,737 @@
:root {
--hf-bg: #fff;
--hf-line: #e8eaf0;
--hf-text: #20222a;
--hf-muted: #6b7280;
--hf-accent: #5b6cff;
--hf-accent-weak: #eef0ff;
--hf-radius: 16px;
--hf-shadow: 0 10px 30px rgba(15,23,42,.10), 0 2px 10px rgba(15,23,42,.04);
}
/* ancre du popover */
.hf-filter-anchor {
position: relative;
display: inline-block;
}
/* carte popover */
.hf-popover {
position: absolute;
top: calc(100% + 8px);
right: 0;
width: min(720px, 92vw);
background: var(--hf-bg);
border: 1px solid var(--hf-line);
border-radius: 16px;
box-shadow: var(--hf-shadow);
z-index: 2000;
display: grid;
grid-template-rows: auto 1fr auto;
}
.hf-popover[hidden] {
display: none;
}
.hf-filter-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 14px;
border-bottom: 1px solid var(--hf-line);
}
.hf-filter-header .title {
display: flex;
gap: 8px;
align-items: center;
font-weight: 700
}
.hf-filter-body {
display: grid;
gap: 14px;
padding: 14px
}
.hf-filter-footer {
display: flex;
gap: 8px;
justify-content: flex-end;
border-top: 1px solid var(--hf-line);
padding: 12px 14px
}
/* assure le menu Select2 DANS le popover */
.select2-container.select2-container--open {
z-index: 2100 !important;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 1.5rem;
margin: 3%;
margin-top: 2%;
margin-bottom: 1%;
}
.card {
background: white;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
overflow: hidden;
display: flex;
flex-direction: column;
transition: transform 0.2s;
cursor: pointer;
width: 17rem;
height: 17rem;
position: relative; /* Pour positionner les éléments absolus à l'intérieur */
}
.card:hover {
transform: scale(1.02);
}
.card img {
width: 100%;
height: 180px;
object-fit: cover;
}
.card-content {
padding: 1rem;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* Titre de la recette */
.card h3 {
margin: 0;
font-size: 0.82rem;
color: #333;
font-weight: 800;
}
/* (Optionnel) Sous-titre ou autres infos */
.card h5 {
margin-top: 1%;
font-size: 0.80rem;
}
/* Lien (non utilisé ici, mais présent dans d'autres contextes) */
.card a {
text-decoration: none;
color: #fff;
background: #27ae60;
padding: 0.5rem 1rem;
border-radius: 8px;
font-weight: bold;
display: inline-block;
margin-top: 1rem;
transition: background 0.2s;
}
.card a:hover {
background: #1e874b;
}
.placeholder {
text-align: center;
color: #888;
}
/* Conteneur des étiquettes (tags) de chaque recette */
.label-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
padding: 0 10px;
position: absolute;
top: 8px;
left: 0;
}
.label-badge {
padding: 4px 10px;
border-radius: 8px;
font-size: 0.60rem;
font-weight: 600;
white-space: nowrap;
line-height: 1.5;
opacity: .9;
}
/* Informations de recette (temps de préparation, etc.) */
.recipe-info {
font-size: 0.9rem;
font-weight: 700; /* Le texte (temps) sera aligné à droite par le jeu de flexbox dans .footerCard */
}
/* Infobulle (tooltip) */
.tooltip {
position: absolute;
bottom: 110%;
left: 10px;
background-color: #333;
color: #fff;
padding: 8px 12px;
border-radius: 6px;
font-size: 0.9em;
white-space: pre-wrap;
z-index: 1000;
opacity: 0;
pointer-events: none;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease;
width: max-content;
max-width: 250px;
}
.card:hover .tooltip {
opacity: 1;
pointer-events: auto;
transform: translateY(-14px);
}
/* Styles pour la section pliable */
.section-toggle {
position: relative;
margin: 45px 0 0 0;
}
.section-hr {
border: 0;
border-top: 2px solid #ddd;
margin: 0;
}
.section-chip {
position: absolute;
left: 50%;
top: 0;
transform: translate(-50%, -50%);
display: flex;
gap: 8px;
align-items: center;
padding: 6px 12px;
border-radius: 5px;
border: 1px solid #e5e7eb;
background: #f4f4f4;
font-weight: 600;
cursor: pointer;
user-select: none;
}
.collapse-icon {
transition: transform 0.25s ease;
}
.section-toggle.open .collapse-icon {
transform: rotate(180deg);
}
.collapse-wrap {
overflow: hidden;
height: 0;
opacity: 0;
transition: height 0.26s ease, opacity 0.26s ease;
}
.collapse-wrap.open {
opacity: 1;
}
/* Pour la zone de contenu défilable (si applicable) */
.content {
overflow: auto;
}
.sticky-search {
position: sticky;
top: 0;
z-index: 10;
padding: 8px 12px;
}
/* Conteneur en bas de la carte pour temps + contrôles de portions */
.footerCard {
display: flex;
justify-content: space-between;
align-items: flex-end !important;
flex-direction: row-reverse;
}
/* Badge affichant le nombre de portions sélectionnées sur l'image */
.badge-portions {
position: absolute;
right: 8px;
bottom: 8px;
background: rgba(0,0,0,0.75);
color: #fff;
font-size: 0.8rem;
padding: 4px 8px;
border-radius: 12px;
display: none; /* Masqué par défaut, s'affichera si portions > 0 */
}
/* Contrôles de quantité (boutons +/ et affichage du nombre) */
.quantity-controls {
display: flex;
align-items: center;
gap: 4px; /* Initialement, on les masque dans le HTML via style="display:none" (géré en JS) */
}
.quantity-controls button {
background: #f1f1f1;
border: 0;
padding: 0px 4px;
border-radius: 6px;
cursor: pointer;
}
.quantity-controls button:hover {
background: #e6e6e6;
}
.quantity {
font-weight: 700;
min-width: 1.5rem;
text-align: center;
}
/* Base: un léger contour pour qu'on voie le surlignage quand sélectionnée */
.card {
border: 1px solid #eee;
}
/* Les cartes dans OWNED n'ont pas de surlignage vert (c'est implicite qu'elles sont choisies) */
#recipeGridOwned .card {
border-color: #eee; /* force un rendu "neutre" */
box-shadow: 0 4px 10px rgba(0,0,0,0.08);
}
.footerCard {
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: row; /* plus de row-reverse */
}
/* Contrôles visibles seulement dans OWNED */
#recipeGrid .quantity-controls {
display: none !important;
}
#recipeGridOwned .quantity-controls {
display: flex;
}
.placeholder-result {
background: none;
color: black;
width: 40rem;
text-align: left;
font-style: italic;
}
.pagination-modern {
bottom: 20px;
right: 65px;
backdrop-filter: blur(6px);
border-radius: 12px;
padding: 6px 14px;
display: flex;
align-items: center;
gap: 8px;
font-family: 'Segoe UI', sans-serif;
font-size: 14px;
z-index: 999;
width: fit-content;
MARGIN: auto;
margin-right: 2.4%;
}
.pagination-btn {
border: none;
background: #4CAF50;
color: white;
font-size: 16px;
padding: 6px 10px;
border-radius: 8px;
cursor: pointer;
transition: background 0.2s ease, transform 0.1s ease;
}
.pagination-btn:hover {
background: #45a049;
}
.pagination-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
#pageInfo {
font-weight: 500;
color: #333;
}
.owned-empty {
display: block;
color: #666;
font-weight: 600;
border-radius: 10px;
font-style: italic;
}
/* Section pliable ULTRA FIABLE en max-height */ /* --- FIX: n'utiliser que max-height pour la section pliable --- */
.collapse-wrap { /* on annule la vieille règle à base de height:0 */
height: auto !important; /* on garde le système au max-height */
overflow: hidden;
max-height: 0;
opacity: 0;
transition: max-height .30s ease, opacity .30s ease;
}
.collapse-wrap.open {
opacity: 1;
max-height: 2000px; /* assez grand pour tout contenir */
}
/* Message "aucune recette" masqué par défaut */
#noSelectedMsg {
display: none;
font-style: italic;
color: #666; /* texte grisé par exemple */
}
/* Emplacement réservé pour l'indicateur (évite les décalages) */
.selection-slot {
display: flex;
align-items: center;
gap: 6px;
min-height: 18px; /* hauteur de la pastille */
min-width: 110px; /* réserve de la place pour "Sélectionné" */
visibility: hidden; /* masqué par défaut mais prend la place */
}
.selection-slot.active { /* quand sélectionné */
visibility: visible;
}
.selection-indicator {
display: flex;
align-items: center;
gap: 6px;
font-weight: 700;
color: #27ae60;
}
.selection-indicator .check-icon {
background: #27ae60;
color: #fff;
font-size: .75rem;
width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.selection-text {
font-size: .8rem;
font-weight: 900;
}
#pdfModal {
z-index: 2000 !important; /* au-dessus de tout */
}
.modal-backdrop {
z-index: 1999 !important;
}
/* Trigger & badge */
.hf-icon-btn {
display: inline-flex;
align-items: center;
gap: .5rem;
border: 1px solid var(--hf-line);
background: #fff;
padding: .5rem .65rem;
border-radius: 12px;
box-shadow: none;
cursor: pointer;
}
.hf-icon-btn svg {
fill: #475569
}
.hf-icon-btn:hover {
border-color: #cbd5e1;
background: #fcfcfe
}
.hf-badge {
display: inline-block;
min-width: 1.5rem;
padding: .1rem .45rem;
font-size: .75rem;
border-radius: 999px;
background: var(--hf-accent);
color: #fff;
text-align: center;
}
/* Overlay + Drawer */
.hf-drawer-overlay {
position: fixed;
inset: 0;
background: rgba(15,23,42,.35);
backdrop-filter: saturate(80%) blur(2px);
z-index: 1040;
}
.hf-drawer {
position: fixed;
top: 0;
right: 0;
height: 100%;
width: 380px;
max-width: 90vw;
background: var(--hf-bg);
box-shadow: var(--hf-shadow);
z-index: 1050;
border-left: 1px solid var(--hf-line);
display: flex;
flex-direction: column;
transform: translateX(20px);
opacity: 0;
pointer-events: none;
transition: transform .18s ease, opacity .18s ease;
}
.hf-drawer[open] {
transform: translateX(0);
opacity: 1;
pointer-events: auto;
}
.hf-drawer[hidden] {
display: block
}
/* pour pouvoir animer malgré hidden initial */
.hf-drawer-header {
position: sticky;
top: 0;
background: var(--hf-bg);
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 18px;
border-bottom: 1px solid var(--hf-line);
}
.hf-drawer-header h3 {
margin: 0;
font-size: 1rem;
font-weight: 700;
color: var(--hf-text)
}
.hf-drawer-body {
padding: 18px;
overflow: auto;
display: grid;
gap: 18px;
}
.hf-section-title {
font-weight: 600;
color: var(--hf-text);
margin-bottom: 8px;
}
.hf-field {
display: block
}
.hf-time-row {
display: flex;
align-items: center;
gap: 8px
}
.hf-input {
border: 1px solid var(--hf-line);
border-radius: 12px;
padding: .55rem .7rem;
width: 100%;
}
.hf-sep {
color: var(--hf-muted)
}
.hf-chips {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-top: 10px
}
.hf-chip {
border: 1px solid var(--hf-line);
background: #fff;
padding: .35rem .6rem;
border-radius: 999px;
cursor: pointer;
font-size: .85rem;
}
.hf-chip:hover {
border-color: #cbd5e1;
background: #fafbff
}
.hf-chip.active {
background: var(--hf-accent-weak);
border-color: var(--hf-accent);
color: var(--hf-accent);
margin-bottom: 10px;
margin-top: 7px;
font-size: 10px;
margin-right: 5px;
border-radius: 13px;
}
.hf-drawer-footer {
margin-top: auto;
padding: 14px 18px;
display: flex;
gap: 10px;
border-top: 1px solid var(--hf-line);
}
.hf-btn {
border-radius: 12px;
padding: .65rem 1rem;
border: 1px solid var(--hf-line);
background: #fff;
cursor: pointer;
}
.hf-btn.primary {
background: var(--hf-accent);
border-color: var(--hf-accent);
color: #fff
}
.hf-btn.ghost {
background: transparent
}
/* Tooltip plus petit (tu lavais demandé) */
.tooltip-wrap {
display: grid;
gap: 6px;
max-width: 360px;
font-size: .85rem
}
.tooltip-wrap .tt-title {
font-weight: 700;
font-size: .9rem
}
.tooltip-wrap .tt-desc {
font-size: .85rem;
line-height: 1.35
}
.tooltip-wrap .tt-foot {
display: flex;
justify-content: flex-end
}
.tooltip-wrap .tt-history {
font-size: .8rem;
opacity: .9
}
/* Cartes “déjà fait” */
.card.card--done {
opacity: .55;
transition: opacity .15s ease
}
.card.card--done:hover {
opacity: .7
}
/* Select2 rafraîchi pour saligner au style */
.select2-container--default .select2-selection--multiple {
border: 1px solid var(--hf-line) !important;
border-radius: 12px !important;
padding: .25rem !important;
}
.select2-selection__choice {
border-radius: 999px !important;
padding: .15rem .6rem !important;
border: 0 !important;
background: var(--hf-accent-weak) !important;
color: var(--hf-text) !important;
}
.select2-selection__choice__remove {
margin-right: .35rem !important;
color: #64748b !important
}
.select2-container--default .select2-search--inline .select2-search__field {
margin-top: .25rem !important;
font-size: .9rem !important;
}
/* ancre du bouton pour positionner la card en absolu */
/* ancre pour positionner la card juste sous le bouton */
.filter-anchor {
position: relative;
}
.hf-filter-card {
position: absolute;
top: calc(100% + 8px);
right: 0;
z-index: 1200;
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 12px;
padding: 12px;
width: 360px;
max-width: min(90vw, 420px);
box-shadow: 0 10px 30px rgba(2, 6, 23, .12);
opacity: 0;
transform: translateY(-6px);
pointer-events: none;
transition: opacity .14s ease, transform .14s ease;
}
/* masquée par défaut quand lattribut hidden est présent */
.hf-filter-card[hidden] {
display: none;
}
/* visible quand la CLASSE .open est posée */
.hf-filter-card.open {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}

View File

@@ -0,0 +1,441 @@
:root {
--hf-bg: #fff;
--hf-muted: #777;
--hf-border: #ececec;
--hf-ink: #1c1c1c;
--hf-accent: #111;
--hf-pill: #f9f9f9;
}
.hf-ingredients {
background: var(--hf-bg);
border-radius: 12px;
padding: 16px 18px;
box-shadow: 0 2px 14px rgba(0, 0, 0, .06);
font-family: system-ui, Segoe UI, Roboto, sans-serif;
max-width: 95%;
margin: auto;
}
.hf-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.hf-head h2 {
margin: 0;
font-size: 1.6rem;
font-weight: 800;
}
#titleAndSearch {
display:flex;
}
.hf-qty {
display: flex;
align-items: center;
gap: 10px;
color: #444;
font-weight: 600;
}
.hf-qty > span {
margin-right: 2px;
}
.hf-segment {
display: inline-grid;
grid-auto-flow: column;
border: 1px solid #cfcfcf;
border-radius: 8px;
overflow: hidden
}
.hf-segment button {
padding: 6px 14px;
background: var(--hf-pill);
border: 0;
border-right: 1px solid #e2e2e2;
cursor: pointer;
font-weight: 700;
min-width: 40px;
}
.hf-segment button:last-child {
border-right: 0
}
.hf-segment button.active {
background: #111;
color: #fff
}
.hf-grid {
list-style: none;
margin: 14px 0 0;
padding: 0;
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 12px 28px;
}
@media (max-width:640px) {
.hf-grid {
grid-template-columns: 1fr;
}
}
.hf-item {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 8px 6px;
border-radius: 8px;
}
.hf-thumb {
width: 52px;
height: 52px;
border-radius: 50%;
overflow: hidden;
flex: 0 0 52px;
background: #f3f3f3;
border: 1px solid var(--hf-border);
display: grid;
place-items: center;
}
.hf-thumb img {
width: 100%;
height: 100%;
object-fit: cover
}
.hf-info {
line-height: 1.2
}
.hf-qtyline {
font-weight: 800;
font-size: .95rem;
color: var(--hf-ink);
margin-bottom: 4px;
}
.hf-name {
font-size: .95rem;
color: var(--hf-ink);
}
.hf-note {
margin-top: 6px;
font-size: .85rem;
color: var(--hf-muted);
}
.hf-sep {
border: 0;
border-top: 1px solid var(--hf-border);
margin: 18px 0 12px;
}
.hf-subtitle {
margin: 0 0 8px;
font-size: 1.15rem;
font-weight: 800;
}
.card {
border: 1px solid #eee;
}
.card {
background: white;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
overflow: hidden;
display: flex;
flex-direction: column;
transition: transform 0.2s;
width: 17rem;
height: 17rem;
position: relative;
}
.card-content > h3 {
font-size: 15px;
font-weight: 500;
padding-bottom: 0;
margin-bottom: 0;
}
.card.checked {
opacity: .3;
background: #f6f6f6;
}
.card.checked .card-content h3,
.card.checked .prep-time {
text-decoration: line-through;
color: #888;
}
.card.checked .image-container img {
filter: grayscale(1);
}
.hf-search {
margin-left: auto;
}
#hfSearch {
border: 1px solid #e3e3e3;
border-radius: 10px;
padding: 8px 12px;
min-width: 260px;
outline: none;
}
#hfSearch:focus {
border-color: #b9b9b9;
box-shadow: 0 0 0 3px rgba(0,0,0,.05);
}
.card-content {
padding: 1rem;
flex: 1;
display: flex;
justify-content: space-between;
}
.footerCard {
display: flex;
justify-content: space-between;
flex-direction: row;
align-items: flex-end !important;
}
/* Titre de la recette */
.card h3 {
margin: 0;
font-size: 0.82rem;
color: #333;
font-weight: 800;
}
/* (Optionnel) Sous-titre ou autres infos */
.card h5 {
margin-top: 1%;
font-size: 0.80rem;
}
/* Informations de recette (temps de préparation, etc.) */
.recipe-info {
font-size: 0.9rem;
font-weight: 700;
margin-bottom: 3px;
/* Le texte (temps) sera aligné à droite par le jeu de flexbox dans .footerCard */
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 1.5rem;
margin: 3%;
margin-top: 2%;
margin-bottom: 1%;
}
/* Conteneur des étiquettes (tags) de chaque recette */
.label-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
padding: 0 10px;
position: absolute;
top: 8px;
left: 0;
}
.label-badge {
padding: 4px 10px;
border-radius: 8px;
font-size: 0.60rem;
font-weight: 600;
white-space: nowrap;
line-height: 1.5;
opacity: .9;
background-color: rgb(14 14 14);
color: rgb(255, 255, 255);
}
#recipeStrip {
display: grid;
grid-auto-flow: column; /* force en colonnes */
grid-auto-columns: 240px; /* largeur fixe de chaque card */
gap: 2.7rem;
overflow-x: auto;
scroll-snap-type: x mandatory; /* optionnel */
}
#recipeStrip .card {
scroll-snap-align: start;
}
/* Style spécifique pour les cartes ingrédients */
#ingredientsList .card {
display: flex;
align-items: center;
gap: 12px;
background: #fff;
border-radius: 12px;
box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
border: 1px solid #e5e7eb;
cursor: pointer;
transition: transform .15s ease, box-shadow .15s ease;
flex-direction: row;
flex-wrap: nowrap;
height: fit-content;
}
#ingredientsList .card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(0,0,0,.15);
}
/* Image à gauche */
#ingredientsList .image-container {
flex: 0 0 56px; /* Taille fixe */
height: 56px;
border-radius: 10px;
overflow: hidden;
display: grid;
place-items: center;
}
#ingredientsList .image-container img {
width: 100%;
height: 100%;
object-fit: cover; /* Image couvre bien son bloc */
}
/* Texte à droite */
#ingredientsList .card-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
#ingredientsList .card-content h3 {
font-size: .8rem;
font-weight: 600;
margin: 0;
color: #1f2937; /* Texte sombre sur fond blanc */
}
/* Quantité en petit badge */
#ingredientsList .footerCard {
margin-top: 4px;
display: flex;
align-items: center;
gap: 10px;
}
#ingredientsList .prep-time {
background: #f3f4f6;
border-radius: 6px;
padding: 2px 8px;
font-size: 0.85rem;
font-weight: 500;
color: #374151;
}
#ingredientsList .card-content {
padding: 0 1rem 0 0;
}
/* Style spécifique pour les cartes ingrédients */
#neededIngredients .card {
display: flex;
align-items: center;
gap: 12px;
background: #fff;
border-radius: 12px;
box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
border: 1px solid #e5e7eb;
cursor: pointer;
transition: transform .15s ease, box-shadow .15s ease;
flex-direction: row;
flex-wrap: nowrap;
height: fit-content;
}
#neededIngredients .card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(0,0,0,.15);
}
/* Image à gauche */
#neededIngredients .image-container {
flex: 0 0 56px; /* Taille fixe */
height: 56px;
border-radius: 10px;
overflow: hidden;
display: grid;
place-items: center;
}
#neededIngredients .image-container img {
width: 100%;
height: 100%;
object-fit: cover; /* Image couvre bien son bloc */
}
/* Texte à droite */
#neededIngredients .card-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
#neededIngredients .card-content h3 {
font-size: .8rem;
font-weight: 600;
margin: 0;
color: #1f2937; /* Texte sombre sur fond blanc */
}
/* Quantité en petit badge */
#neededIngredients .footerCard {
margin-top: 4px;
display: flex;
align-items: center;
gap: 10px;
flex-direction: row-reverse;
}
#neededIngredients .prep-time {
background: #f3f4f6;
border-radius: 6px;
padding: 2px 8px;
font-size: 0.85rem;
font-weight: 500;
color: #374151;
}
#neededIngredients .card-content {
padding: 0 1rem 0 0;
}

65
wwwroot/css/home.css Normal file
View File

@@ -0,0 +1,65 @@
div#containerCard {
display: flex;
padding: 2%;
gap: 2rem;
}
.cardHome {
width: 240px;
height: 240px;
background: white;
border-radius: 20px;
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.4s ease, box-shadow 0.4s ease;
transform-style: preserve-3d;
perspective: 1000px;
cursor: pointer;
}
.cardHome:hover {
transform: rotateY(5deg) scale(1.05);
box-shadow: 0 25px 40px rgba(0, 0, 0, 0.2);
}
.cardHome img {
width: 100%;
height: 60%;
object-fit: cover;
}
.cardHome .content {
padding: 20px;
}
.cardHome .content h2 {
margin: 0 0 10px;
font-size: 1.5em;
color: #333;
}
.cardHome .content p {
color: #666;
font-size: 1em;
}
.icon {
margin-right: 10px;
}
.arrow {
float: right;
transition: transform 0.3s ease;
}
.dropdown.active .arrow {
transform: rotate(90deg);
}
#page-top {
overflow: clip!important;
}
#sidebarUl {
color:black
}

View File

@@ -1,10 +0,0 @@
.sommeCard {
}
.card-title {
}
.divAffichageCarte {
display: flex;
}

View File

@@ -66,7 +66,7 @@ body {
line-height: 1.5;
color: #858796;
text-align: left;
background-color: #fff;
background-color: #f4f4f4;
}
[tabindex="-1"]:focus:not(:focus-visible) {
@@ -2440,6 +2440,9 @@ textarea.form-control {
display: flex;
flex-flow: row wrap;
align-items: center;
align-items: stretch;
gap: 10px;
justify-content: center;
}
.form-inline .form-check {
@@ -2474,7 +2477,7 @@ textarea.form-control {
.form-inline .input-group,
.form-inline .custom-select {
width: auto;
width: 26%;
}
.form-inline .form-check {
@@ -4711,17 +4714,6 @@ input[type="button"].btn-block {
color: #fff;
}
.card {
position: relative;
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border: 1px solid #e3e6f0;
border-radius: 0.35rem;
}
.card > hr {
margin-right: 0;
@@ -4753,7 +4745,7 @@ input[type="button"].btn-block {
.card-body {
flex: 1 1 auto;
min-height: 1px;
padding: 1.25rem;
padding: .7rem;
}
.card-title {
@@ -6046,6 +6038,7 @@ a.close.disabled {
.modal-body {
position: relative;
flex: 1 1 auto;
color: black;
padding: 1rem;
}
@@ -8204,7 +8197,7 @@ button.bg-dark:focus {
.mb-4,
.my-4 {
margin-bottom: 1.5rem !important;
margin-bottom: 0!important;
}
.ml-4,
@@ -10562,8 +10555,11 @@ a.text-dark:hover, a.text-dark:focus {
size: a3;
}
body {
min-width: 992px !important;
overflow: clip;
}
.container {
@@ -10615,6 +10611,7 @@ html {
}
body {
overflow: hidden;
height: 100%;
}
@@ -10735,7 +10732,7 @@ a:focus {
.bg-gradient-primary {
background-color: #4e73df;
background-image: linear-gradient(180deg, #4e73df 10%, #224abe 100%);
background-image: url(../images/bg.png);
background-size: cover;
}
@@ -10851,6 +10848,7 @@ a:focus {
.text-gray-600 {
color: #858796 !important;
font-size:15px
}
.text-gray-700 {
@@ -10992,7 +10990,7 @@ a:focus {
transform: scale(0.7);
transform-origin: top right;
right: .25rem;
margin-top: -.25rem;
margin-top: -.5rem;
}
.sidebar .nav-item .nav-link .img-profile,
@@ -11145,7 +11143,8 @@ a:focus {
}
.topbar.navbar-light .navbar-nav .nav-item .nav-link {
color: #d1d3e2;
color: #494a4e;
font-weight: 600;
}
.topbar.navbar-light .navbar-nav .nav-item .nav-link:hover {
@@ -11158,7 +11157,7 @@ a:focus {
.sidebar {
width: 6.5rem;
min-height: 100vh;
min-height: 81vh;
}
.sidebar .nav-item {
@@ -11346,7 +11345,8 @@ a:focus {
@media (min-width: 768px) {
.sidebar {
width: 14rem !important;
width: 8rem !important;
box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
}
.sidebar .nav-item .collapse {
@@ -11723,9 +11723,10 @@ a:focus {
}
.bg-login-image {
background: url("https://source.unsplash.com/K4mSJ7kc0As/600x800");
background-image: url(../images/Logo.PNG);
background-position: center;
background-size: cover;
background-size: contain;
background-repeat: no-repeat;
}
.bg-register-image {
@@ -12230,3 +12231,144 @@ body.sidebar-toggled footer.sticky-footer {
transform: rotate(180deg);
}
/* Design personnalis<69> pour le tableau DataTables */
#CRUDModal thead th {
font-size: 12px;
background-color: #f8f9fa;
color: #495057;
font-weight: normal;
text-align: center !important;
}
#CRUDModal tbody td {
font-size: 15px;
text-align: center !important;
vertical-align: middle !important;
}
/* Wrapper du contenu apr<70>s la topbar */
.main-wrapper {
display: flex;
flex: 1;
height: calc(100vh - 60px);
}
.sidebar {
width: 250px;
background-color: #fafafa;
transition: 0.3s ease;
height: 84%;
padding: .9%;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
margin-top: 3%;
}
.sidebar ul {
list-style: none;
margin-left: 0;
padding-left: 0;
font-size: 13px;
}
.sidebar li {
padding: 0px 20px;
cursor: pointer;
}
.sidebar li:hover {
background-color: #f0f0f0;
}
.sidebar .dropdown ul {
display: none;
background-color: #2a2a3a;
}
.sidebar .dropdown.active ul {
display: block;
}
.sidebar .dropdown ul li {
padding-left: 40px;
}
.arrow {
float: right;
transition: transform 0.3s ease;
}
.dropdown.active .arrow {
transform: rotate(90deg);
}
.content {
flex: 1;
padding: 20px;
overflow-y: auto;
background-color: #f4f4f4;
}
img.logoSidebar {
display: inline-block;
width: 100%;
text-align: center;
font-size: 18px;
line-height: 20px;
cursor: pointer;
transition: transform 0.4s ease, box-shadow 0.4s ease;
}
.logoSidebar:hover {
transform: rotateY(5deg) scale(1.05);
}
h2.titleSidebar {
font-size: 1rem;
font-weight: 700;
}
/* Vid<69>o plein <20>cran */
#bg-video {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover; /* S'adapte <20> l'<27>cran */
z-index: -1; /* Derri<72>re le contenu */
}
/* Centrage du formulaire */
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* Fond semi-transparent derri<72>re le formulaire */
.login-card {
background: rgba(255, 255, 255, 0.85);
border-radius: 10px;
padding: 2rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
/* Vid<69>o plein <20>cran */
#bg-video {
position: fixed;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
object-fit: cover;
z-index: -1;
}
.card img {
width: 100%;
height: 180px;
object-fit: cover;
}

BIN
wwwroot/images/Finance.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

BIN
wwwroot/images/HFHome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 KiB

BIN
wwwroot/images/HFLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
wwwroot/images/Logo.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 KiB

BIN
wwwroot/images/bg.mp4 Normal file

Binary file not shown.

BIN
wwwroot/images/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
wwwroot/images/cardLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -0,0 +1,210 @@
/**
* Exemple dutilisation :
* Récupère les loyers avec un nombre de lignes défini
* @param {number} rows - Nombre de lignes à récupérer
*/
const Revenue = {
GetXRevenues: "x_revenues",
GetLastRevenues: "last_revenue",
GetAllRevenues: "all_revenues",
GetRevenueByID: "revenues_by_id",
GetAdditionalRevenues: "additional_revenues"
};
const Expense = {
GetRightExpenses: "last_expenses",
GetExpenseByID: "expense_by_id"
}
var TotalRevenu = 0, TotalExpense = 0;
function loadRevenues() {
return Promise.all([
new Promise((resolve, reject) => {
apiCall(Controller.Revenue, Revenue.GetXRevenues, { rows: 1 }, data => {
document.getElementById('idRevenues').innerText = data[0].id;
const loyer = document.getElementById('salaire');
const salaire = parseFloat(data[0]?.salary || 0);
if (loyer) loyer.innerText = `${toPriceFormat(salaire)}`;
resolve(salaire);
}, reject);
}),
new Promise((resolve, reject) => {
apiCall(Controller.Revenue, Revenue.GetAdditionalRevenues, {}, data => {
const additionalRevenues = document.getElementById('additionalRevenues');
const totalAR = data.reduce((acc, item) => acc + (parseFloat(item.amount) || 0), 0);
if (additionalRevenues) additionalRevenues.innerText = `${toPriceFormat(totalAR)}`;
resolve(totalAR);
}, reject);
})
]).then(([salary, additional]) => {
TotalRevenu = salary + additional;
});
}
function loadExpenses() {
return new Promise((resolve, reject) => {
apiCall(Controller.Expense, Expense.GetRightExpenses, {}, data => {
document.getElementById('idExpenses').innerText = data.id;
const loyer = document.getElementById('loyer');
const trash = document.getElementById('trash');
const electricity = document.getElementById('electricity');
const insurance = document.getElementById('insurance');
const wifi = document.getElementById('wifi');
const groceries = document.getElementById('groceries');
const additionalSourcesExpense = document.getElementById('additionalSourcesExpense');
const additionalExpensesSub = document.getElementById('additionalExpensesSub');
const saving = document.getElementById('saving');
if (loyer) loyer.innerText = `${toPriceFormat(data.rent)}`;
if (trash) trash.innerText = `${toPriceFormat(data.trash)}`;
if (insurance) insurance.innerText = `${toPriceFormat(data.insurance)}`;
if (electricity) electricity.innerText = `${toPriceFormat(data.electricity)}`;
if (wifi) wifi.innerText = `${toPriceFormat(data.wifi)}`;
if (groceries) groceries.innerText = `${toPriceFormat(data.groceries)}`;
if (saving) saving.innerText = `${toPriceFormat(data.saving)}`;
if (additionalSourcesExpense) {
const totalAR = data.additionalSourcesExpense.reduce((acc, item) => acc + (parseFloat(item.amount) || 0), 0);
additionalSourcesExpense.innerText = `${toPriceFormat(totalAR)}`;
}
if (additionalExpensesSub) {
const totalAR = data.additionalSourcesSub.reduce((acc, item) => acc + (parseFloat(item.amount) || 0), 0);
additionalExpensesSub.innerText = `${toPriceFormat(totalAR)}`;
}
TotalExpense = data.total;
resolve();
}, reject);
});
}
// Lance automatiquement lappel au chargement de la page
document.addEventListener('DOMContentLoaded', async () => {
const result = document.getElementById('result');
const monthDisplay = document.getElementById('monthOfToday');
const cards = document.querySelectorAll('.cardDepense');
monthDisplay.textContent = returnMonthOfToday();
try {
await Promise.all([loadExpenses(), loadRevenues()]);
if (result) result.textContent = `${toPriceFormat(TotalRevenu - TotalExpense)}`;
} catch (error) {
console.error("❌ Erreur de chargement :", error);
if (result) result.textContent = "Erreur lors du calcul";
}
cards.forEach(card => {
card.addEventListener('click', () => onClickInCard(card));
});
});
function onClickInCard(card) {
const label = document.getElementById("CRUDModalLabel");
const idExpense = document.getElementById('idExpenses').innerText;
if (label) label.textContent = card.id.replace(/_/g, ' ').toUpperCase();
apiCall(Controller.Expense, Expense.GetExpenseByID, { id: idExpense }, data => {
initExpensesTable(data);
const modal = new bootstrap.Modal(document.getElementById('CRUDModal'));
modal.show();
});
}
/**
* Initialise ou met à jour dynamiquement une table DataTables avec les données de dépenses.
*
* - Si la table est déjà initialisée, elle est vidée et rechargée avec les nouvelles données.
* - Sinon, la table est créée avec les colonnes correspondantes à lobjet Expense.
*
* @param {Array<Object>} data - Tableau dobjets de type Expense à afficher dans le tableau.
* Chaque objet doit contenir : id, date, rent, electricity, trash, wifi, groceries, saving, insurance, userId.
*/
function initExpensesTable(data) {
const tableId = '#expensesTable';
const normalizedData = Array.isArray(data) ? data : [data];
if ($.fn.DataTable.isDataTable(tableId)) {
$(tableId).DataTable().clear().rows.add(normalizedData).draw();
return;
}
$(tableId).DataTable({
data: normalizedData,
paging: false,
searching: false,
ordering: false,
info: false,
language: {
url: "https://cdn.datatables.net/plug-ins/1.13.6/i18n/fr-FR.json"
},
columns: [
{ data: 'id', visible: false }, // ID caché
{
data: 'date',
visible: false // Date cachée
},
{
data: 'rent',
title: 'Loyer',
render: data => `${data}`
},
{
data: 'electricity',
title: 'Électricité',
render: data => `${data}`
},
{
data: 'trash',
title: 'Poubelles',
render: data => `${data}`
},
{
data: 'wifi',
title: 'Wi-Fi',
render: data => `${data}`
},
{
data: 'groceries',
title: 'Courses',
render: data => `${data}`
},
{
data: 'saving',
title: 'Épargne',
render: data => `${data}`
},
{
data: 'insurance',
title: 'Assurance',
render: data => `${data}`
},
{ data: 'userId', visible: false } // Utilisateur caché
]
});
}
/**
* Retourne le nom du mois actuel en français avec la première lettre en majuscule.
*
* @returns {string} Le mois actuel, par exemple : "Août", "Mars", etc.
*/
function returnMonthOfToday() {
const date = new Date();
const moisActuel = date.toLocaleString('fr-FR', { month: 'long' });
return moisActuel.charAt(0).toUpperCase() + moisActuel.slice(1);
}

View File

@@ -0,0 +1,288 @@
// ==============================
// Cuisine / Lecteur de recettes
// ==============================
(() => {
"use strict";
// ---------- Sélecteurs ----------
const $ = (sel) => document.querySelector(sel);
const els = {
root: $("#cuisineApp"),
scroller: $("#recipeScroller"),
title: $("#currentName"),
btnOpen: $("#openPdf"),
btnToggle: $("#toggleSide"),
iframePrev: $("#pdfPreview"),
placeholder: $("#pdfPlaceholder"),
overlay: $("#viewerOverlay"),
overlayTit: $("#viewerTitle"),
iframeFull: $("#pdfFrameFull"),
btnClose: $("#closeViewer"),
};
// ---------- Etat ----------
const PDF_DEFAULT_ZOOM = "page-fit"; // "page-fit" | "page-width" | "100"
const LS_KEY_COLLAPSED = "cui_sidebar_collapsed";
/** @type {{id:string,name:string,image:string,tempsDePreparation:number|null,pdf:string,portions:number}[]} */
let RECIPES = [];
let CUR = -1; // index courant
// ---------- Utils ----------
const normalize = (s) => (s ?? "").toString().trim().toLowerCase();
const proxify = (url) => url ? `/HelloFresh/ProxyPdf?url=${encodeURIComponent(url)}` : "";
function buildPdfSrc(rawUrl) {
if (!rawUrl) return "";
const base = `/HelloFresh/ProxyPdf?url=${encodeURIComponent(rawUrl)}`;
// cache la barre (toolbar=0) + fixe le zoom
return `${base}#toolbar=0&zoom=${encodeURIComponent(PDF_DEFAULT_ZOOM)}`;
}
// Dédup par nom (on garde la 1ère qui a un pdf / image)
function dedupByName(rows) {
const map = new Map(); // key: normalized name -> row
for (const r of rows) {
const key = normalize(r.name);
if (!key) continue;
if (!map.has(key)) {
map.set(key, r);
} else {
const kept = map.get(key);
// privilégier une entrée avec PDF / image
if (!kept.pdf && r.pdf) map.set(key, r);
else if (!kept.image && r.image) map.set(key, r);
}
}
return [...map.values()];
}
// ---------- Rendu de la liste ----------
function renderList(list) {
const wrap = els.scroller;
if (!wrap) return;
wrap.innerHTML = "";
list.forEach((r, idx) => {
const card = document.createElement("div");
card.className = "cui-card";
card.tabIndex = 0;
card.dataset.idx = String(idx);
card.innerHTML = `
<img class="cui-thumb" src="${r.image || ""}" alt="">
<div class="cui-info">
<div class="cui-name" title="${r.name}">${r.name}</div>
<div class="cui-meta">${r.tempsDePreparation ? (r.tempsDePreparation + " min") : ""}</div>
</div>
`;
// clic / entrée -> sélection
card.addEventListener("click", () => selectIdx(idx));
card.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
selectIdx(idx);
}
});
wrap.appendChild(card);
});
markActive();
}
function markActive() {
els.scroller?.querySelectorAll(".cui-card").forEach((el, i) => {
el.classList.toggle("active", i === CUR);
});
}
// ---------- Sélection / Toolbar / Preview ----------
function selectIdx(idx) {
if (idx < 0 || idx >= RECIPES.length) return;
CUR = idx;
const r = RECIPES[CUR];
// Titre (ellipsis géré par CSS)
if (els.title) els.title.textContent = r.name || "Recette";
// Bouton ouvrir
if (els.btnOpen) {
const can = !!r.pdf;
els.btnOpen.disabled = !can;
els.btnOpen.setAttribute("aria-disabled", String(!can));
els.btnOpen.title = can ? "Ouvrir le PDF" : "PDF indisponible";
}
// Aperçu PDF
loadPreview(r.pdf);
// Etat actif visuel
markActive();
}
function loadPreview(url) {
if (!els.iframePrev || !els.placeholder) return;
if (url) {
els.iframePrev.src = buildPdfSrc(url);
els.iframePrev.style.display = "block";
els.placeholder.style.display = "none";
} else {
els.iframePrev.removeAttribute("src");
els.iframePrev.style.display = "none";
els.placeholder.style.display = "grid";
}
}
// ---------- Plein écran ----------
function openViewer() {
if (CUR < 0) return;
const r = RECIPES[CUR];
if (!r.pdf || !els.overlay || !els.iframeFull || !els.overlayTit) return;
els.overlayTit.textContent = r.name || "PDF";
els.iframeFull.src = buildPdfSrc(r.pdf);
els.overlay.classList.remove("hidden");
els.overlay.setAttribute("aria-hidden", "false");
// tente le plein écran (si autorisé par lUA)
try { els.overlay.requestFullscreen(); } catch { }
}
function closeViewer() {
if (!els.overlay || !els.iframeFull) return;
els.iframeFull.removeAttribute("src");
els.overlay.classList.add("hidden");
els.overlay.setAttribute("aria-hidden", "true");
if (document.fullscreenElement) {
try { document.exitFullscreen(); } catch { }
}
}
// Navigation dans le viewer
function next() {
if (CUR < RECIPES.length - 1) {
selectIdx(CUR + 1);
if (!els.overlay?.classList.contains("hidden") && els.iframeFull) {
const r = RECIPES[CUR];
els.iframeFull.src = buildPdfSrc(r.pdf || "");
}
}
}
function prev() {
if (CUR > 0) {
selectIdx(CUR - 1);
if (!els.overlay?.classList.contains("hidden") && els.iframeFull) {
const r = RECIPES[CUR];
els.iframeFull.src = buildPdfSrc(r.pdf || "");
}
}
}
// ---------- Données ----------
async function fetchRecipes() {
const res = await fetch("/HelloFresh/GetRecipesOwned", { credentials: "same-origin" });
if (!res.ok) throw new Error("HTTP " + res.status);
const raw = await res.json();
// Normalisation des propriétés
const norm = (r) => ({
id: r.id ?? r.Id ?? "",
name: r.name ?? r.Name ?? "",
image: r.image ?? r.Image ?? "",
tempsDePreparation: r.tempsDePreparation ?? r.TempsDePreparation ?? null,
pdf: r.pdf ?? r.Pdf ?? "",
portions: Number(r.portions ?? r.Portions ?? 0) || 0
});
let rows = Array.isArray(raw) ? raw.map(norm) : [];
rows = dedupByName(rows);
// Tri par nom (français)
rows.sort((a, b) => String(a.name).localeCompare(String(b.name), "fr", { sensitivity: "base" }));
return rows;
}
async function loadRecipes() {
try {
RECIPES = await fetchRecipes();
renderList(RECIPES);
// Auto-sélection : d'abord la 1ère avec PDF, sinon la 1ère tout court
const firstPdf = RECIPES.findIndex(r => !!r.pdf);
const idx = firstPdf >= 0 ? firstPdf : (RECIPES.length ? 0 : -1);
if (idx >= 0) selectIdx(idx);
else selectIdx(-1);
} catch (err) {
console.error("[Cuisine] GetRecipesOwned failed:", err);
RECIPES = [];
renderList([]);
selectIdx(-1);
}
}
// ---------- Sidebar collapse (persisté) ----------
function setCollapsed(collapsed) {
if (!els.root) return;
els.root.classList.toggle("collapsed", !!collapsed);
els.btnToggle?.setAttribute("aria-expanded", String(!collapsed));
try { localStorage.setItem(LS_KEY_COLLAPSED, JSON.stringify(!!collapsed)); } catch { }
}
function getCollapsed() {
try {
const v = localStorage.getItem(LS_KEY_COLLAPSED);
return v ? JSON.parse(v) : false;
} catch { return false; }
}
// ---------- Gestes / Clavier (viewer) ----------
(function wireGestures() {
if (!els.overlay) return;
// Swipe horizontal (tablette)
let touchStartX = 0;
const SWIPE_MIN = 60;
els.overlay.addEventListener("touchstart", (e) => {
if (!e.changedTouches?.length) return;
touchStartX = e.changedTouches[0].clientX;
}, { passive: true });
els.overlay.addEventListener("touchend", (e) => {
if (!e.changedTouches?.length) return;
const dx = e.changedTouches[0].clientX - touchStartX;
if (Math.abs(dx) > SWIPE_MIN) (dx < 0 ? next : prev)();
}, { passive: true });
// Clavier (visible uniquement dans le viewer)
document.addEventListener("keydown", (e) => {
if (els.overlay?.classList.contains("hidden")) return;
if (e.key === "ArrowRight") next();
else if (e.key === "ArrowLeft") prev();
else if (e.key === "Escape") closeViewer();
});
})();
// ---------- Wiring des boutons ----------
function wireUI() {
els.btnOpen?.addEventListener("click", openViewer);
els.btnClose?.addEventListener("click", closeViewer);
els.btnToggle?.addEventListener("click", () => {
setCollapsed(!els.root?.classList.contains("collapsed"));
});
}
// ---------- Boot ----------
document.addEventListener("DOMContentLoaded", async () => {
// Appliquer létat collapsé mémorisé
setCollapsed(getCollapsed());
wireUI();
await loadRecipes();
});
})();

View File

@@ -0,0 +1,174 @@
// ---------- ÉTAT ----------
let HISTORY_CACHE = []; // [{ date, rawDate, items: [...] }]
let HISTORY_SEARCH = "";
// Normalisation basique pour la recherche
const ACCENT_RX = /[\u0300-\u036f]/g;
const norm = s => (s || "").toString().toLowerCase()
.normalize("NFD").replace(ACCENT_RX, "").trim();
// Palette de couleurs (plus large possible pour différencier)
const USER_COLORS = [
"#C0392B", // rouge foncé
"#2980B9", // bleu foncé
"#27AE60", // vert foncé
"#D35400", // orange foncé
"#8E44AD", // violet foncé
"#2C3E50", // gris/bleu très foncé
"#7D3C98", // violet profond
"#1ABC9C" // turquoise foncé
];
// Cache pour assigner une couleur unique par user
const userColorMap = new Map();
let userColorIndex = 0;
function getUserColor(username) {
if (!username) return "#999";
if (!userColorMap.has(username)) {
// Assigne une couleur en cycle
userColorMap.set(username, USER_COLORS[userColorIndex % USER_COLORS.length]);
userColorIndex++;
}
return userColorMap.get(username);
}
function buildUserBadges(usersArr) {
const wrap = document.createElement("div");
wrap.className = "label-container";
(usersArr || []).forEach(u => {
const span = document.createElement("span");
span.className = "label-badge user-badge";
span.textContent = u;
// Couleur dynamique par utilisateur
span.style.backgroundColor = getUserColor(u);
span.style.color = "#fff"; // texte blanc pour lisibilité
wrap.appendChild(span);
});
return wrap;
}
const truncate = (s, m) => (s && s.length > m ? s.slice(0, m) + "…" : (s ?? ""));
function buildRecipeCardHistory(rec) {
const card = document.createElement("div");
card.className = "card";
card.id = `hist-${rec.id}`;
const name = rec.name || `Recette ${rec.id}`;
const imgSrc = rec.image || "/images/recipe-placeholder.png"; // mets un vrai fichier ici
card.innerHTML = `
<div class="image-container">
<img src="${imgSrc}" alt="${truncate(name, 10)}"
onerror="this.onerror=null;this.src='/images/recipe-placeholder.png'">
<div class="badge-portions">${rec.portions} portion${rec.portions > 1 ? 's' : ''}</div>
</div>
<div class="card-content"><h3>${name}</h3></div>
`;
// badges utilisateurs (JSON camelCase => "users")
const users = Array.isArray(rec.users) ? rec.users : Array.isArray(rec.Users) ? rec.Users : [];
if (users.length) {
card.querySelector(".image-container")?.appendChild(buildUserBadges(users));
}
return card;
}
function buildDateSection(dateStr, items) {
const sec = document.createElement("section");
sec.className = "history-section";
const h = document.createElement("h3");
h.className = "history-date";
h.textContent = dateStr;
sec.appendChild(h);
const grid = document.createElement("div");
grid.className = "hf-grid";
if (Array.isArray(items) && items.length) {
items.forEach(r => grid.appendChild(buildRecipeCardHistory(r)));
} else {
grid.innerHTML = `<p class="placeholder">Aucune recette pour cette date.</p>`;
}
sec.appendChild(grid);
return sec;
}
// Applique la recherche sur le cache et rend
function renderHistoryFiltered() {
const container = document.getElementById("historyContainer");
if (!container) return;
const q = norm(HISTORY_SEARCH);
const groups = [];
for (const g of (HISTORY_CACHE || [])) {
if (!q) {
groups.push(g);
continue;
}
const filteredItems = (g.items || []).filter(it => norm(it.name).includes(q));
if (filteredItems.length) {
groups.push({ date: g.date, rawDate: g.rawDate, items: filteredItems });
}
}
container.innerHTML = "";
if (!groups.length) {
container.innerHTML = `<p class="placeholder">Aucun résultat pour “${HISTORY_SEARCH}”.</p>`;
return;
}
groups.forEach(group => container.appendChild(buildDateSection(group.date, group.items)));
}
// ---------- CHARGEMENT ----------
async function loadHistory(scope = "mine") {
const container = document.getElementById("historyContainer");
if (!container) return;
container.innerHTML = "Chargement…";
try {
const isUserImportant = (scope !== "all");
const res = await fetch(`/HelloFresh/GetHistory?isUserImportant=${isUserImportant ? "true" : "false"}&search=${encodeURIComponent(HISTORY_SEARCH)}`, { credentials: "same-origin" });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json(); // [{ date, rawDate, items }]
HISTORY_CACHE = Array.isArray(data) ? data : [];
renderHistoryFiltered(); // premier rendu (avec éventuel terme déjà saisi)
} catch (e) {
console.error("loadHistory error:", e);
container.innerHTML = `<p class="placeholder">Erreur de chargement.</p>`;
}
}
// ---------- INIT ----------
document.addEventListener("DOMContentLoaded", () => {
const scopeSel = document.getElementById("historyScope");
const search = document.getElementById("historySearch");
// charge au démarrage
loadHistory(scopeSel?.value || "mine");
// changement de portée
scopeSel?.addEventListener("change", () => loadHistory(scopeSel.value || "mine"));
// recherche (debounce)
let timer;
search?.addEventListener("input", (e) => {
HISTORY_SEARCH = e.target.value || "";
clearTimeout(timer);
timer = setTimeout(renderHistoryFiltered, 200);
});
// Entrée = rendu immédiat
search?.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
e.preventDefault();
clearTimeout(timer);
renderHistoryFiltered();
}
});
});

View File

@@ -0,0 +1,842 @@
/***********************
* STATE
***********************/
let currentPage = 1;
let lastPageGlobal = 1;
const countPerPage = 12;
let currentSearchTerm = '';
const MAX_PORTIONS = 14;
let CURRENT_CLIENT_LIST = null; // liste filtrée paginée côté client
let ALL_RECIPES_CACHE = null; // toutes les recettes (toutes pages)
// Filtres : édition (dans la card) vs appliqués (vraiment utilisés)
let draftFilters = { ingredients: [], tags: [], timeMin: null, timeMax: null };
let appliedFilters = { ingredients: [], tags: [], timeMin: null, timeMax: null };
// Map label affiché par valeur normalisée (chips)
const labelMaps = { ingredients: {}, tags: {} };
// idRecette -> portions (0..3)
const ownedMap = new Map();
/***********************
* UTILS
***********************/
const clampQty = n => Math.max(0, Math.min(3, Number(n) || 0));
const truncate = (s, m) => (s && s.length > m ? s.slice(0, m) + '…' : (s ?? ''));
const normalize = (str) => (str ?? '').toString().normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().trim();
const splitTags = (str) => (str || '').split(/[,•]/).map(t => t.trim()).filter(Boolean).filter(t => t !== '•');
const esc = (s) => { const d = document.createElement('div'); d.textContent = (s ?? ''); return d.innerHTML; };
function capFirst(s) { if (s == null) return ''; s = s.toString().trim(); if (!s) return ''; const f = s[0].toLocaleUpperCase('fr-FR'); return f + s.slice(1); }
/***********************
* COLORS for tags (persistées localStorage)
***********************/
const DISTINCT_COLORS = [
{ bg: "#e41a1c", color: "#fff" }, { bg: "#377eb8", color: "#fff" }, { bg: "#4daf4a", color: "#fff" },
{ bg: "#984ea3", color: "#fff" }, { bg: "#ff7f00", color: "#fff" }, { bg: "#ffff33", color: "#333" },
{ bg: "#a65628", color: "#fff" }, { bg: "#f781bf", color: "#333" }, { bg: "#999999", color: "#fff" },
{ bg: "#1f77b4", color: "#fff" }, { bg: "#ff7f0e", color: "#fff" }, { bg: "#2ca02c", color: "#fff" },
{ bg: "#d62728", color: "#fff" }, { bg: "#9467bd", color: "#fff" }, { bg: "#8c564b", color: "#fff" },
{ bg: "#e377c2", color: "#fff" }, { bg: "#7f7f7f", color: "#fff" }, { bg: "#bcbd22", color: "#333" },
{ bg: "#17becf", color: "#fff" }
];
const LS_KEY = "hf_label_colors_v2";
const loadColorMap = () => { try { return JSON.parse(localStorage.getItem(LS_KEY) || "{}"); } catch { return {}; } };
const saveColorMap = (map) => localStorage.setItem(LS_KEY, JSON.stringify(map));
function getOrAssignLabelColor(tagRaw) {
const tag = normalize(tagRaw);
const map = loadColorMap();
if (!map[tag]) {
const used = Object.values(map).map(c => c.bg);
let color = DISTINCT_COLORS.find(c => !used.includes(c.bg)) || DISTINCT_COLORS[Object.keys(map).length % DISTINCT_COLORS.length];
map[tag] = color; saveColorMap(map);
}
return map[tag];
}
function buildLabels(tagsStr) {
const wrap = document.createElement("div"); wrap.className = "label-container";
const tags = splitTags(tagsStr);
tags.forEach(tag => {
const span = document.createElement("span");
span.className = "label-badge";
const c = getOrAssignLabelColor(tag);
span.style.backgroundColor = c.bg; span.style.color = c.color; span.textContent = tag;
wrap.appendChild(span);
});
return wrap;
}
/***********************
* UI helpers
***********************/
function refreshSelectedBorders() {
const grid = document.getElementById('recipeGrid'); if (!grid) return;
grid.querySelectorAll('.card').forEach(card => {
const id = String(card.id); const qty = clampQty(ownedMap.get(id) || 0);
card.classList.toggle('card--selected', qty > 0);
});
}
function renderPagination(curr, last) {
const pageInfo = document.getElementById('pageInfo');
const prevBtn = document.getElementById('prevPage');
const nextBtn = document.getElementById('nextPage');
if (pageInfo) pageInfo.textContent = `${curr}/${last}`;
if (prevBtn) prevBtn.disabled = curr <= 1;
if (nextBtn) nextBtn.disabled = curr >= last;
prevBtn && (prevBtn.onclick = () => { if (currentPage <= 1) return; currentPage--; (CURRENT_CLIENT_LIST ? renderClientPage() : loadRecipes(currentPage)); });
nextBtn && (nextBtn.onclick = () => { if (currentPage >= lastPageGlobal) return; currentPage++; (CURRENT_CLIENT_LIST ? renderClientPage() : loadRecipes(currentPage)); });
}
/***********************
* Owned section toggle
***********************/
function ownedWrap() { return document.getElementById('recipeOwnedWrap'); }
function ownedHeader() { return document.getElementById('ownedSection'); }
function ownedBtn() { return ownedHeader()?.querySelector('.section-chip'); }
function setOwnedToggleUI(open) {
const btn = ownedBtn();
const t = btn?.querySelector('.collapse-text'); const i = btn?.querySelector('.collapse-icon');
t && (t.textContent = open ? 'Cacher recettes choisies' : 'Afficher recettes choisies');
i && (i.textContent = open ? '▲' : '▼');
}
function openOwnedIfClosed() { const wrap = ownedWrap(), header = ownedHeader(); if (!wrap || !header) return; if (!wrap.classList.contains('open')) { wrap.classList.add('open'); wrap.setAttribute('aria-hidden', 'false'); header.classList.add('open'); setOwnedToggleUI(true); } }
function closeOwnedIfOpen() { const wrap = ownedWrap(), header = ownedHeader(); if (!wrap || !header) return; if (wrap.classList.contains('open')) { wrap.classList.remove('open'); wrap.setAttribute('aria-hidden', 'true'); header.classList.remove('open'); setOwnedToggleUI(false); } }
(function initOwnedToggle() {
const header = document.getElementById('ownedSection');
const btn = header?.querySelector('.section-chip');
const wrap = document.getElementById('recipeOwnedWrap');
if (!header || !btn || !wrap) return;
btn.addEventListener('click', async () => {
const open = wrap.classList.contains('open');
if (open) { wrap.classList.remove('open'); wrap.setAttribute('aria-hidden', 'true'); setOwnedToggleUI(false); return; }
await loadRecipesOwned({ onEmpty: 'placeholder' });
wrap.classList.add('open'); wrap.setAttribute('aria-hidden', 'false'); setOwnedToggleUI(true);
});
})();
/***********************
* API (save/remove/clear/archive)
***********************/
async function saveRecipe(id) {
try {
const hadAny = ownedRecipesCount() > 0;
const r = await fetch('/HelloFresh/SaveRecipe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(String(id)) });
if (!r.ok) throw new Error(await r.text());
const { qty = 1 } = await r.json();
ownedMap.set(String(id), clampQty(qty));
applySelectionUIById(id);
await loadRecipesOwned({ onEmpty: 'placeholder' });
updateOwnedCountUI();
if (!hadAny && ownedRecipesCount() > 0) openOwnedIfClosed();
} catch (e) { console.error('saveRecipe error', e); }
}
async function removeRecette(id) {
try {
const r = await fetch('/HelloFresh/DeleteRecipesOwned', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(String(id)) });
if (!r.ok) throw new Error(await r.text());
const { qty = 0 } = await r.json();
ownedMap.set(String(id), clampQty(qty));
applySelectionUIById(id);
await loadRecipesOwned({ onEmpty: 'placeholder' });
updateOwnedCountUI();
if (ownedRecipesCount() === 0) closeOwnedIfOpen();
} catch (e) { console.error('removeRecette error', e); }
}
async function clearRecipe(id) {
try {
const r = await fetch('/HelloFresh/ClearRecipeOwned', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(String(id)) });
if (!r.ok) throw new Error(await r.text());
ownedMap.set(String(id), 0); applySelectionUIById(id);
await loadRecipesOwned({ onEmpty: 'placeholder' });
updateOwnedCountUI();
if (ownedRecipesCount() === 0) closeOwnedIfOpen();
} catch (e) { console.error('clearRecipe error', e); }
}
/***********************
* Cards
***********************/
function setSelectedUI(card, selected) {
if (!card) return;
const slot = card.querySelector('.selection-slot'); if (!slot) return;
slot.classList.toggle('active', !!selected);
slot.innerHTML = selected ? `<div class="selection-indicator"><span class="selection-text">Sélectionné</span></div>` : '';
}
function applySelectionUIById(id) {
const card = document.getElementById(String(id));
const selected = (ownedMap.get(String(id)) || 0) > 0;
setSelectedUI(card, selected);
}
function ownedRecipesCount() {
let c = 0; for (const v of ownedMap.values()) if ((Number(v) || 0) > 0) c++; return c;
}
function buildRecipeCardList(recipe) {
const card = document.createElement('div');
card.className = 'card'; card.id = recipe.id;
const prep = recipe.tempsDePreparation ? recipe.tempsDePreparation + "min" : "<i>Inconnu</i>";
card.innerHTML = `
<div class="image-container">
<img src="${recipe.image}" alt="${truncate(recipe.name, 10)}">
<div class="badge-portions" style="display:none"></div>
</div>
<div class="card-content">
<h3>${recipe.name}</h3>
<div class="footerCard">
<div class="selection-slot"></div>
<div class="recipe-info"><span class="prep-time">${prep}</span></div>
</div>
</div>`;
card.dataset.pdf = recipe.pdf || recipe.Pdf || '';
const tagsVal = (recipe.tags ?? recipe.Tags ?? '').trim();
if (tagsVal) { card.querySelector('.image-container')?.appendChild(buildLabels(tagsVal)); }
const initiallySelected = (ownedMap.get(String(recipe.id)) || 0) > 0;
setSelectedUI(card, initiallySelected);
const tagTokens = splitTags(tagsVal).map(normalize);
card.dataset.tags = tagTokens.join('|');
card.dataset.time = String(recipe.tempsDePreparation || '');
attachRecipeTooltip(card, recipe);
flagCardIfDone(card, recipe.id);
card.addEventListener('click', async (e) => {
if (e.ctrlKey) {
e.preventDefault();
e.stopPropagation();
// 1) ce que renvoie la liste
let pdfUrl = recipe.pdf || recipe.Pdf || card.dataset.pdf || '';
// 2) fallback: demande le détail (si tu exposes pdf côté serveur)
if (!pdfUrl) {
try {
const res = await fetch(`/HelloFresh/GetRecipesDetails?ids=${encodeURIComponent(recipe.id)}`);
if (res.ok) {
const arr = await res.json();
pdfUrl = (arr?.[0]?.pdf || arr?.[0]?.Pdf || '');
}
} catch (_) { }
}
if (!pdfUrl) {
console.warn('Pas de PDF pour la recette', recipe.id);
return;
}
const proxied = `/HelloFresh/ProxyPdf?url=${encodeURIComponent(pdfUrl)}`;
showPdfModal(proxied);
return;
}
const current = clampQty(ownedMap.get(String(recipe.id)) || 0);
if (current === 0 && !canAddPortion()) { alert(`Limite atteinte (${MAX_PORTIONS}).`); return; }
const selectedNow = current > 0;
if (!selectedNow) await saveRecipe(recipe.id); else await clearRecipe(recipe.id);
applySelectionUIById(recipe.id);
updateOwnedCountUI();
});
return card;
}
function buildRecipeCardOwned(recipe) {
const qty = clampQty(recipe.portions || 0);
const card = document.createElement('div');
card.className = 'card';
card.id = 'owned-' + recipe.id;
// 🟢 stocke l'URL PDF sur la carte
card.dataset.pdf = recipe.pdf || recipe.Pdf || '';
card.innerHTML = `
<div class="image-container">
<img src="${recipe.image}" alt="${truncate(recipe.name, 10)}">
<div class="badge-portions">${qty} portion${qty > 1 ? 's' : ''}</div>
</div>
<div class="card-content">
<h3>${recipe.name}</h3>
<div class="footerCard">
<div class="recipe-info"><span class="prep-time">${recipe.tempsDePreparation}min</span></div>
<div class="quantity-controls">
<button class="btn-minus" type="button"></button>
<span class="quantity">${qty}</span>
<button class="btn-plus" type="button">+</button>
</div>
</div>
</div>`;
const tagsVal = (recipe.tags ?? recipe.Tags ?? '').trim();
if (tagsVal) { card.querySelector('.image-container')?.appendChild(buildLabels(tagsVal)); }
// plus/moins existants
card.querySelector('.btn-minus')?.addEventListener('click', (e) => { e.stopPropagation(); removeRecette(recipe.id); updateOwnedCountUI(); if (getTotalPortionsFromMap() === 0) closeOwnedIfOpen(); });
card.querySelector('.btn-plus')?.addEventListener('click', (e) => { e.stopPropagation(); if (!canAddPortion()) { alert(`Limite atteinte (${MAX_PORTIONS}).`); return; } saveRecipe(recipe.id); updateOwnedCountUI(); });
// 🟢 Ctrl+clic = ouvrir le PDF (avec fallback GetRecipesDetails)
card.addEventListener('click', async (e) => {
if (!e.ctrlKey) return;
e.preventDefault();
e.stopPropagation();
let pdfUrl = card.dataset.pdf;
if (!pdfUrl) {
try {
const res = await fetch(`/HelloFresh/GetRecipesDetails?ids=${encodeURIComponent(recipe.id)}`);
if (res.ok) {
const arr = await res.json();
pdfUrl = (arr?.[0]?.pdf || arr?.[0]?.Pdf || '');
}
} catch { }
}
if (!pdfUrl) { alert("Aucun PDF pour cette recette."); return; }
const proxied = `/HelloFresh/ProxyPdf?url=${encodeURIComponent(pdfUrl)}`;
showPdfModal(proxied);
});
return card;
}
/***********************
* Loaders
***********************/
async function loadRecipes(page = 1) {
try {
const url = new URL('/HelloFresh/GetRecipesByPage', window.location.origin);
url.searchParams.set('page', page); url.searchParams.set('count', countPerPage);
const res = await fetch(url.toString()); if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
const { recipes, currentPage: serverPage, lastPage } = data;
currentPage = serverPage; lastPageGlobal = lastPage;
const grid = document.getElementById('recipeGrid'); grid.innerHTML = '';
if (!recipes || recipes.length === 0) {
grid.innerHTML = '<p class="placeholder">Aucune recette disponible pour le moment.</p>';
renderPagination(currentPage, lastPageGlobal); return;
}
recipes.forEach(r => grid.appendChild(buildRecipeCardList(r)));
if (window.tippy) {
tippy('[data-tippy-content]', { placement: 'top', arrow: true, delay: [100, 0], theme: 'light-border', maxWidth: 250, allowHTML: true });
}
await hydrateIngredientsForPage(recipes);
refreshSelectedBorders();
if ((currentSearchTerm ?? '').trim() !== '') applySearchFilter();
refreshDoneFlagsOnVisibleCards();
renderPagination(currentPage, lastPageGlobal);
} catch (e) { console.error('loadRecipes error', e); }
}
async function loadRecipesOwned({ onEmpty = 'placeholder' } = {}) {
const gridOwned = document.getElementById('recipeGridOwned'); if (!gridOwned) return false;
const wasOpen = document.getElementById('recipeOwnedWrap')?.classList.contains('open');
try {
const res = await fetch('/HelloFresh/GetRecipesOwned', { credentials: 'same-origin' });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
ownedMap.clear();
(data || []).forEach(r => ownedMap.set(String(r.id), clampQty(Number(r.portions) || 0)));
gridOwned.innerHTML = '';
if (Array.isArray(data) && data.length) { data.forEach(r => gridOwned.appendChild(buildRecipeCardOwned(r))); return true; }
gridOwned.innerHTML = (onEmpty === 'placeholder') ? `<p class="owned-empty">Aucune recette sélectionnée</p>` : '';
return false;
} catch (err) {
console.error('GetRecipesOwned a échoué :', err);
gridOwned.innerHTML = `<p class="owned-empty">Impossible de charger les recettes choisies.</p>`;
return false;
} finally {
refreshSelectedBorders();
if (wasOpen) { /* no-op */ }
}
}
/***********************
* PDF modal
***********************/
function showPdfModal(pdfUrl) { const iframe = document.getElementById('pdfFrame'); iframe.src = pdfUrl; $('#pdfModal').modal('show'); }
function hidePdfModal() { const modal = document.getElementById('pdfModal'); const iframe = document.getElementById('pdfFrame'); iframe.src = ''; modal.style.display = 'none'; }
document.querySelector('#pdfModal .close')?.addEventListener('click', hidePdfModal);
window.addEventListener('click', (e) => { const modal = document.getElementById('pdfModal'); if (modal && e.target === modal) hidePdfModal(); });
/***********************
* History tooltip
***********************/
const historyCache = new Map();
async function checkRecipeInHistory(recipeId) {
const key = String(recipeId); if (historyCache.has(key)) return historyCache.get(key);
try {
const r = await fetch('/HelloFresh/HasHistory', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(key) });
if (!r.ok) throw 0; const json = await r.json();
const exists = Boolean(json?.exists ?? json?.hasHistory ?? json === true);
historyCache.set(key, exists); return exists;
} catch { historyCache.set(key, false); return false; }
}
function createRecipeTooltipNode(recipe) {
const supplement = capFirst((recipe.supplementText ?? recipe.SupplementText ?? '').trim());
const descriptionRaw = (recipe.description ?? recipe.Description ?? '').trim();
const description = (descriptionRaw === '.') ? '' : capFirst(descriptionRaw);
const root = document.createElement('div');
root.className = 'tooltip-wrap';
root.innerHTML = `
${supplement ? `<div class="tt-title">${esc(supplement)}</div>` : ''}
${description ? `<div class="tt-desc">${esc(description).replace(/\n/g, '<br>')}</div>` : ''}
<div class="tt-foot"><span class="tt-history" aria-live="polite"></span></div>`;
return root;
}
function attachRecipeTooltip(card, recipe) {
if (!window.tippy) return;
const contentNode = createRecipeTooltipNode(recipe);
const historyEl = contentNode.querySelector('.tt-history');
tippy(card, {
content: contentNode, allowHTML: true, interactive: true, placement: 'top-start', theme: 'light-border', arrow: true, maxWidth: 360,
onShow: async (instance) => {
if (instance._historyLoaded) return;
instance._historyLoaded = true;
if (historyEl) historyEl.textContent = '';
const done = await checkRecipeInHistory(recipe.id);
if (done && historyEl) { historyEl.textContent = '✓ déjà fait'; historyEl.style.color = '#2e7d32'; historyEl.style.fontWeight = '800'; }
}
});
}
(function upsertUiStyles() {
const id = 'tt-style'; const css = `
.tooltip-wrap{display:grid;gap:6px;max-width:360px;font-size:.85rem}
.tooltip-wrap .tt-title{font-weight:700;font-size:.9rem}
.tooltip-wrap .tt-desc{font-size:.85rem;line-height:1.35}
.tooltip-wrap .tt-foot{display:flex;justify-content:flex-end}
.tooltip-wrap .tt-history{font-size:.8rem;opacity:.9}
.card.card--done{opacity:.35; transition:opacity .15s ease}
.card.card--done:hover{opacity:.7}`;
let style = document.getElementById(id); if (!style) { style = document.createElement('style'); style.id = id; document.head.appendChild(style); }
style.textContent = css;
})();
async function flagCardIfDone(card, id) {
try { const done = await checkRecipeInHistory(id); card.classList.toggle('card--done', !!done); } catch { }
}
function refreshDoneFlagsOnVisibleCards() {
document.querySelectorAll('#recipeGrid .card').forEach(card => {
const id = card?.id; if (id) checkRecipeInHistory(id).then(done => card.classList.toggle('card--done', !!done));
});
}
/***********************
* Filtering (ALL pages)
***********************/
function anyFilterActive() {
return (normalize(currentSearchTerm) !== '') ||
appliedFilters.ingredients.length ||
appliedFilters.tags.length ||
appliedFilters.timeMin != null ||
appliedFilters.timeMax != null;
}
async function fetchAllRecipes() {
if (ALL_RECIPES_CACHE) return ALL_RECIPES_CACHE;
let page = 1, last = 1, all = [];
do {
const url = new URL('/HelloFresh/GetRecipesByPage', window.location.origin);
url.searchParams.set('page', page); url.searchParams.set('count', countPerPage);
const res = await fetch(url.toString()); if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
all.push(...(data.recipes || []));
last = data.lastPage || 1; page++;
} while (page <= last);
ALL_RECIPES_CACHE = all; return all;
}
function recipeMatchesFilters(cardMeta) {
const term = normalize(currentSearchTerm);
if (term && !cardMeta.name.includes(term)) return false;
if (appliedFilters.tags.length && !appliedFilters.tags.every(t => cardMeta.tags.includes(t))) return false;
if (appliedFilters.ingredients.length && !appliedFilters.ingredients.every(i => cardMeta.ing.includes(i))) return false;
const tmin = appliedFilters.timeMin, tmax = appliedFilters.timeMax;
if (tmin != null && (cardMeta.time ?? 0) < tmin) return false;
if (tmax != null && (cardMeta.time ?? 0) > tmax) return false;
return true;
}
function extractIngredientNames(ingredientsJson) {
const out = new Set();
try {
const obj = JSON.parse(ingredientsJson || "{}");
Object.keys(obj).forEach(k => {
const arr = obj[k]; if (Array.isArray(arr)) {
arr.forEach(el => {
const name = (el?.Name ?? el?.name ?? "").toString().trim(); if (name) out.add(name);
});
}
});
} catch { }
return [...out];
}
async function buildMetaFor(recipes) {
const ids = recipes.map(r => r.id).join(',');
const metaById = {};
try {
const res = await fetch(`/HelloFresh/GetRecipesDetails?ids=${encodeURIComponent(ids)}`);
const details = await res.json(); // [{id, ingredientsJson}]
details.forEach(d => { metaById[d.id] = { ing: extractIngredientNames(d.ingredientsJson).map(normalize) }; });
} catch (e) { console.warn('details fail', e); }
recipes.forEach(r => {
const tagsVal = (r.tags ?? r.Tags ?? '').trim();
const tags = splitTags(tagsVal).map(normalize);
const time = Number(r.tempsDePreparation || 0) || 0;
const name = normalize(r.name || '');
metaById[r.id] = Object.assign({ tags, time, name, ing: [] }, metaById[r.id] || {});
});
return metaById;
}
async function applyAllFilters() {
if (!anyFilterActive()) {
CURRENT_CLIENT_LIST = null;
await loadRecipes(1);
updateActiveFilterBadge(); // peut masquer le badge si rien dactif
return;
}
const all = await fetchAllRecipes();
const meta = await buildMetaFor(all);
const filtered = all.filter(r => recipeMatchesFilters(meta[r.id] || {}));
CURRENT_CLIENT_LIST = filtered;
currentPage = 1;
lastPageGlobal = Math.max(1, Math.ceil(filtered.length / countPerPage));
await renderClientPage();
updateActiveFilterBadge(); // badge = basé sur appliedFilters UNIQUEMENT
}
async function renderClientPage() {
const list = CURRENT_CLIENT_LIST || [];
const start = (currentPage - 1) * countPerPage;
const slice = list.slice(start, start + countPerPage);
const grid = document.getElementById('recipeGrid'); grid.innerHTML = '';
slice.forEach(r => grid.appendChild(buildRecipeCardList(r)));
await hydrateIngredientsForPage(slice);
refreshSelectedBorders();
refreshDoneFlagsOnVisibleCards();
lastPageGlobal = Math.max(1, Math.ceil(list.length / countPerPage));
renderPagination(currentPage, lastPageGlobal);
if (slice.length === 0) {
const p = document.createElement('p'); p.className = 'placeholder'; p.textContent = 'Aucune recette ne correspond aux filtres'; grid.appendChild(p);
}
}
/***********************
* Filters Card (UI)
***********************/
function ensureSelect2() { return !!(window.jQuery && jQuery.fn && typeof jQuery.fn.select2 === 'function'); }
async function populateFilters() {
const ingSel = document.getElementById('filterIngredients');
const tagSel = document.getElementById('filterTags');
if (!ingSel || !tagSel) return;
ingSel.innerHTML = ''; tagSel.innerHTML = '';
labelMaps.ingredients = {}; labelMaps.tags = {};
try {
const [ingsRes, tagsRes] = await Promise.all([fetch('/HelloFresh/GetAllIngredients'), fetch('/HelloFresh/GetAllTags')]);
const ingredients = ingsRes.ok ? await ingsRes.json() : [];
const tags = tagsRes.ok ? await tagsRes.json() : [];
(ingredients || []).forEach(lbl => { const val = normalize(lbl); labelMaps.ingredients[val] = lbl; ingSel.add(new Option(lbl, val)); });
(tags || []).forEach(lbl => { const val = normalize(lbl); labelMaps.tags[val] = lbl; tagSel.add(new Option(lbl, val)); });
if (ensureSelect2()) {
const parent = jQuery('#filtersCard');
const $ing = jQuery(ingSel).select2({ width: '100%', placeholder: 'Choisir des ingrédients', allowClear: true, closeOnSelect: false, dropdownParent: parent });
const $tag = jQuery(tagSel).select2({ width: '100%', placeholder: 'Choisir des tags', allowClear: true, closeOnSelect: false, dropdownParent: parent });
// Écrit dans le DRAFT uniquement
$ing.on('change', () => { draftFilters.ingredients = ($ing.val() || []).map(String); renderChipCloud('ingredients'); });
$tag.on('change', () => { draftFilters.tags = ($tag.val() || []).map(String); renderChipCloud('tags'); });
} else {
ingSel.addEventListener('change', () => { draftFilters.ingredients = Array.from(ingSel.selectedOptions).map(o => o.value); renderChipCloud('ingredients'); });
tagSel.addEventListener('change', () => { draftFilters.tags = Array.from(tagSel.selectedOptions).map(o => o.value); renderChipCloud('tags'); });
}
} catch (e) { console.error('populateFilters failed', e); }
}
// une seule fois, après que la carte existe
document.getElementById('filtersCard')?.addEventListener('click', (e) => {
e.stopPropagation(); // ⬅️ tout clic dans la carte reste dans la carte
});
document.addEventListener('click', (e) => {
const card = document.getElementById('filtersCard');
const btn = document.getElementById('filterOpenBtn');
if (!card || card.hidden) return;
// si le clic vient du bouton ou de la carte → ne pas fermer
if (e.target.closest('#filtersCard') || e.target.closest('#filterOpenBtn')) return;
closeFiltersUI();
});
document.querySelector('.navbar-search')?.addEventListener('submit', (e) => e.preventDefault());
function renderChipCloud(kind) {
const wrap = document.getElementById(kind === 'ingredients' ? 'chipIngredients' : 'chipTags');
if (!wrap) return; wrap.innerHTML = '';
(draftFilters[kind] || []).forEach(val => {
const chip = document.createElement('button');
chip.type = 'button'; chip.className = 'hf-chip active'; chip.dataset.value = val;
chip.textContent = labelMaps[kind][val] || val;
// chips "Ingrédients/Tags" (dans renderChipCloud)
chip.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation(); // ⬅️ bloque le click-outside
const next = draftFilters[kind].filter(v => v !== val);
draftFilters[kind] = next;
const selId = (kind === 'ingredients') ? '#filterIngredients' : '#filterTags';
if (ensureSelect2()) { jQuery(selId).val(next).trigger('change.select2'); }
else {
const sel = document.querySelector(selId);
[...sel.options].forEach(o => o.selected = next.includes(o.value));
sel.dispatchEvent(new Event('change', { bubbles: true }));
}
renderChipCloud(kind);
});
wrap.appendChild(chip);
});
}
// Temps rapides (<= 15 / 30 / 45 / 60)
function wireQuickTimeChips() {
// chips "≤ 15/30/45/60"
document.querySelectorAll('.hf-chip[data-tmax]')?.forEach(btn => {
btn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation(); // ⬅️
document.querySelectorAll('.hf-chip[data-tmax]').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const tmax = Number(btn.dataset.tmax);
const minInput = document.getElementById('filterTimeMin');
const maxInput = document.getElementById('filterTimeMax');
if (minInput) minInput.value = '';
if (maxInput) maxInput.value = String(tmax);
draftFilters.timeMin = null; draftFilters.timeMax = tmax;
});
});
}
function wireTimeInputs() {
const tminInput = document.getElementById('filterTimeMin');
const tmaxInput = document.getElementById('filterTimeMax');
[tminInput, tmaxInput].forEach(inp => {
inp?.addEventListener('input', () => {
document.querySelectorAll('.hf-chip[data-tmax]').forEach(b => b.classList.remove('active'));
const v = Number(inp.value); const val = (inp.value === '' || Number.isNaN(v)) ? null : Math.max(0, v);
if (inp === tminInput) draftFilters.timeMin = val; else draftFilters.timeMax = val;
});
});
}
function updateActiveFilterBadge() {
const b = document.getElementById('activeFilterBadge');
if (!b) return;
// si la carte de filtres est ouverte → ne rien afficher
const cardOpen = !document.getElementById('filtersCard')?.hidden;
if (cardOpen) {
b.style.display = 'none';
b.textContent = '';
return;
}
// Badge basé UNIQUEMENT sur les filtres appliqués (pas les choix en cours)
const n =
(appliedFilters.ingredients.length ? 1 : 0) +
(appliedFilters.tags.length ? 1 : 0) +
((appliedFilters.timeMin != null || appliedFilters.timeMax != null) ? 1 : 0) +
(normalize(currentSearchTerm) ? 1 : 0);
if (n > 0) {
b.style.display = '';
b.textContent = `${n} filtre${n > 1 ? 's' : ''} actif${n > 1 ? 's' : ''}`;
} else {
b.style.display = 'none';
b.textContent = '';
}
}
function openFiltersUI() {
const card = document.getElementById('filtersCard');
if (!card || !card.hidden) return;
// Sync du draft depuis l'appliqué
draftFilters = JSON.parse(JSON.stringify(appliedFilters));
// Refléter l'état dans l'UI
if (ensureSelect2()) {
jQuery('#filterIngredients').val(draftFilters.ingredients).trigger('change.select2');
jQuery('#filterTags').val(draftFilters.tags).trigger('change.select2');
} else {
const ingSel = document.getElementById('filterIngredients');
const tagSel = document.getElementById('filterTags');
[...(ingSel?.options || [])].forEach(o => o.selected = draftFilters.ingredients.includes(o.value));
[...(tagSel?.options || [])].forEach(o => o.selected = draftFilters.tags.includes(o.value));
ingSel?.dispatchEvent(new Event('change', { bubbles: true }));
tagSel?.dispatchEvent(new Event('change', { bubbles: true }));
}
document.getElementById('filterTimeMin').value = draftFilters.timeMin ?? '';
document.getElementById('filterTimeMax').value = draftFilters.timeMax ?? '';
document.querySelectorAll('.hf-chip[data-tmax]').forEach(b => {
b.classList.toggle('active', Number(b.dataset.tmax) === draftFilters.timeMax && draftFilters.timeMin == null);
});
renderChipCloud('ingredients');
renderChipCloud('tags');
// Ouvrir : enlève [hidden] et AJOUTE la classe .open
card.hidden = false;
// petite rafraîch pour la transition si tu veux
requestAnimationFrame(() => card.classList.add('open'));
updateActiveFilterBadge(); // cache le badge pendant lédition
}
function closeFiltersUI() {
const card = document.getElementById('filtersCard');
if (!card || card.hidden) return;
card.classList.remove('open');
setTimeout(() => { card.hidden = true; }, 140); // laisser la transition se finir
}
function toggleFiltersUI() {
const card = document.getElementById('filtersCard');
if (!card) return;
(card.hidden) ? openFiltersUI() : closeFiltersUI();
}
function wireFooterButtons() {
const tminInput = document.getElementById('filterTimeMin');
const tmaxInput = document.getElementById('filterTimeMax');
document.getElementById('applyFilters')?.addEventListener('click', async () => {
appliedFilters = JSON.parse(JSON.stringify(draftFilters)); // applique
await applyAllFilters(); // filtre toutes les recettes
updateActiveFilterBadge(); // badge MAJ ici seulement
closeFiltersUI(); // ferme la card
});
document.getElementById('clearFilters')?.addEventListener('click', async () => {
// reset des deux états
draftFilters = { ingredients: [], tags: [], timeMin: null, timeMax: null };
appliedFilters = { ingredients: [], tags: [], timeMin: null, timeMax: null };
// reset UI
if (ensureSelect2()) {
jQuery('#filterIngredients').val(null).trigger('change.select2');
jQuery('#filterTags').val(null).trigger('change.select2');
} else {
const ingSel = document.getElementById('filterIngredients');
const tagSel = document.getElementById('filterTags');
[...(ingSel?.options || [])].forEach(o => o.selected = false);
[...(tagSel?.options || [])].forEach(o => o.selected = false);
ingSel?.dispatchEvent(new Event('change', { bubbles: true }));
tagSel?.dispatchEvent(new Event('change', { bubbles: true }));
}
tminInput && (tminInput.value = '');
tmaxInput && (tmaxInput.value = '');
document.querySelectorAll('.hf-chip[data-tmax]').forEach(b => b.classList.remove('active'));
renderChipCloud('ingredients'); renderChipCloud('tags');
// applique filtre vide + badge off + ferme
await applyAllFilters();
updateActiveFilterBadge();
closeFiltersUI();
});
}
/***********************
* Misc
***********************/
function getTotalPortionsFromMap() { let total = 0; ownedMap.forEach(v => { total += (Number(v) || 0); }); return total; }
function updateOwnedCountUI() {
const el = document.getElementById('ownedCount'); if (!el) return;
const total = getTotalPortionsFromMap(); el.textContent = `(${total}/${MAX_PORTIONS})`;
el.style.color = (total >= MAX_PORTIONS) ? '#c62828' : '';
}
function canAddPortion() { return getTotalPortionsFromMap() < MAX_PORTIONS; }
/***********************
* Ingredients meta attach (for page)
***********************/
async function hydrateIngredientsForPage(recipes) {
try {
const ids = recipes.map(r => r.id).join(','); if (!ids) return;
const res = await fetch(`/HelloFresh/GetRecipesDetails?ids=${encodeURIComponent(ids)}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const details = await res.json();
details.forEach(d => {
const card = document.getElementById(d.id); if (!card) return;
const names = extractIngredientNames(d.ingredientsJson).map(normalize);
card.dataset.ing = names.join('|');
});
} catch (e) { console.error('hydrateIngredientsForPage error', e); }
}
/***********************
* STARTUP
***********************/
document.addEventListener('DOMContentLoaded', async () => {
// Live search
const searchInput = document.querySelector('#divSearch input');
if (searchInput) {
searchInput.addEventListener('input', async (e) => {
currentSearchTerm = e.target.value;
await applyAllFilters(); // search est immédiat
});
}
// Owned
await loadRecipesOwned({ onEmpty: 'auto' });
updateOwnedCountUI();
// Liste initiale (page 1)
await loadRecipes(1);
// Archive all
const btnAll = document.getElementById('archiveAllBtn');
btnAll?.addEventListener('click', async () => {
const total = getTotalPortionsFromMap(); if (total === 0) return;
const ok = confirm(`Archiver tout (${total} portions) ?`); if (!ok) return;
try {
const r = await fetch('/HelloFresh/ArchiveAll', { method: 'POST' });
if (!r.ok) throw new Error(await r.text());
ownedMap.clear();
await loadRecipesOwned({ onEmpty: 'placeholder' });
refreshSelectedBorders();
updateOwnedCountUI();
closeOwnedIfOpen();
} catch (e) { console.error('ArchiveAll failed', e); alert('Impossible darchiver toutes les recettes.'); }
});
// Filtres (UI + data)
await populateFilters();
// --- Ouvrir / fermer la carte de filtres ---
const openBtn = document.getElementById('filterOpenBtn');
const card = document.getElementById('filtersCard');
if (openBtn && card && !openBtn._wired) {
openBtn._wired = true; // anti-double-bind
// toggle au clic
openBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
toggleFiltersUI(); // -> utilise openFiltersUI()/closeFiltersUI()
});
// fermer en cliquant à l'extérieur
document.addEventListener('click', (e) => {
if (card.hidden) return;
if (card.contains(e.target) || openBtn.contains(e.target)) return;
closeFiltersUI();
});
// fermer avec ÉCHAP
window.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && !card.hidden) closeFiltersUI();
});
}
wireQuickTimeChips();
wireTimeInputs();
wireFooterButtons();
// Badge initial (rien dactif)
updateActiveFilterBadge();
});

View File

@@ -0,0 +1,638 @@
// wwwroot/js/ingredients.js
// ======================
// Globals & Helpers
// ======================
let OWNED_SET = new Set();
let ALL_AGG_INGS = [];
let AI_CTRL = null;
const AI_DELAY = 300;
const SEARCH_STATE = { query: "", mode: null };
const els = { list: null, search: null };
// --- Exclusions par recette ---
const RECIPE_ING_CACHE = new Map(); // id -> { '1': [noms], '2': [...], ... }
const EXCLUDED_BY_RECIPE = new Map(); // id -> Set(noms normalisés)
// État “Ingrédients à avoir”
let LAST_ING_TO_HAD = null;
let LAST_PORTIONS = 1;
let lastNeededSignature = "";
let prevNotShippedCount = -1;
const ACCENT_RX = /[\u0300-\u036f]/g;
const normalizeName = (s) => (s || "").trim().toLowerCase();
const normalizeText = (s) => {
let t = (s || "").toLowerCase();
try { t = t.normalize("NFD").replace(ACCENT_RX, ""); } catch { }
return t.replace(/\s/g, " ").trim();
};
// Fractions unicode → décimaux
const VULGAR_FRAC = { "½": "0.5", "¼": "0.25", "¾": "0.75", "⅓": "0.3333", "⅔": "0.6667", "⅛": "0.125", "⅜": "0.375", "⅝": "0.625", "⅞": "0.875" };
// Rang unités
const UNIT_RANK = { "pièce": 6, "piece": 6, "pcs": 6, "pc": 6, "cs": 5, "tbsp": 5, "cc": 4, "tsp": 4, "g": 3, "kg": 3, "mg": 3, "ml": 3, "cl": 3, "l": 3, "": 1 };
const nameKey = (s) => (s || "").trim().toLowerCase();
// --- Exclusions par recette (ingrédients "principaux" ET "à avoir") ---
const EXCLUDED_NEEDED_BY_RECIPE = new Map(); // id -> Set(noms normalisés)
function excludedUnionFrom(map) {
const out = new Set();
for (const set of map.values()) for (const n of set) out.add(n);
return out;
}
function applyNeededExclusions(items) {
if (EXCLUDED_NEEDED_BY_RECIPE.size === 0) return items;
const ex = excludedUnionFrom(EXCLUDED_NEEDED_BY_RECIPE);
return (items || []).filter(it => !ex.has(nameKey(it?.Name)));
}
// Récupère les noms "à avoir" dune recette (à partir de lobjet recette du strip)
function getNeededNamesForRecipeObj(recipe, portions) {
if (!recipe) return [];
const det = detectIngredientsToHadProp(recipe);
const arr = det ? (det.parsed?.[String(portions)] || []) : [];
return (Array.isArray(arr) ? arr : [])
.map(el => (el?.Name ?? el?.name ?? "").toString().trim())
.filter(Boolean);
}
(function addExcludeStyle() {
const id = "hf-exclude-style";
if (document.getElementById(id)) return;
const st = document.createElement("style"); st.id = id;
st.textContent = `
#recipeStrip .card.card--excluded{ opacity:.45; outline:2px dashed #888; }
#recipeStrip .card.card--excluded:hover{ opacity:.7; }
`;
document.head.appendChild(st);
})();
// Retourne les noms d'ingrédients (par portion 1..4) pour une recette
async function getIngredientNamesForRecipe(recipeId, portions = 1) {
const id = String(recipeId);
if (!RECIPE_ING_CACHE.has(id)) {
try {
const res = await fetch(`/HelloFresh/GetRecipesDetails?ids=${encodeURIComponent(id)}`);
if (!res.ok) throw new Error("HTTP " + res.status);
const arr = await res.json();
const one = Array.isArray(arr) ? arr.find(x => String(x.id) === id) : null;
const map = {};
if (one && one.ingredientsJson) {
try {
const obj = JSON.parse(one.ingredientsJson);
for (const k of ["1", "2", "3", "4"]) {
const list = Array.isArray(obj[k]) ? obj[k] : [];
map[k] = list.map(el => (el?.Name ?? el?.name ?? "").toString().trim()).filter(Boolean);
}
} catch { /* ignore */ }
}
RECIPE_ING_CACHE.set(id, map);
} catch (e) {
console.warn("[ingredients] GetRecipesDetails fail for", id, e);
RECIPE_ING_CACHE.set(id, {}); // évite re-fetch en boucle
}
}
const cache = RECIPE_ING_CACHE.get(id) || {};
return (cache[String(portions)] || []).slice();
}
// Union des exclusions (Set global)
function excludedUnion() {
const out = new Set();
for (const set of EXCLUDED_BY_RECIPE.values()) {
for (const n of set) out.add(n);
}
return out;
}
// Filtre une liste ditems en appliquant les exclusions
function applyExclusionsTo(items) {
if (EXCLUDED_BY_RECIPE.size === 0) return items;
const ex = excludedUnion();
return (items || []).filter(it => !ex.has(nameKey(it?.Name)));
}
// ========== Normalisation quantités ==========
function normalizeQuantityText(q) {
if (!q) return "";
let s = String(q).toLowerCase();
s = s.replace(/[½¼¾⅓⅔⅛⅜⅝⅞]/g, m => VULGAR_FRAC[m] || m);
s = s.replace(/\b(\d+)\s*\/\s*(\d+)\b/g, (_, a, b) => {
const num = parseFloat(a), den = parseFloat(b);
return (!den || isNaN(num) || isNaN(den)) ? `${a}/${b}` : (num / den).toString();
});
s = s.replace(/,/g, ".").replace(/\s+/g, " ").trim();
s = s
.replace(/\b(pieces?)\b/g, "pièce")
.replace(/\b(pcs?)\b/g, "pièce")
.replace(/\b(gr|grammes?|grams?)\b/g, "g")
.replace(/\b(millilitres?|milliliters?|mls?)\b/g, "ml")
.replace(/\b(litres?|liters?)\b/g, "l");
s = s
.replace(/\b(c(?:uill[eè]re)?s?\s*(?:a|à)\s*s(?:oupe)?s?\.?)\b/gi, "cs")
.replace(/\b(c(?:uill[eè]re)?s?\s*(?:a|à)\s*caf[eé]s?s?\.?)\b/gi, "cc");
s = s.replace(/[\/]+/g, " ").trim();
return s;
}
function parseQuantity(q) {
const s = normalizeQuantityText(q);
const m = s.match(/^([0-9]+(?:\.[0-9]+)?)\s*([a-zéû]+)?$/i);
if (!m) return { value: NaN, unit: "", raw: s };
return { value: parseFloat(m[1]), unit: (m[2] || "").trim(), raw: s };
}
function quantityScore(q) {
const { value, unit, raw } = parseQuantity(q);
const isNum = !Number.isNaN(value);
const unitRank = UNIT_RANK[unit] ?? (unit ? 2 : 1);
return { isNum, unitRank, value: isNum ? value : -Infinity, raw };
}
function pickBestQuantity(qtys) {
const arr = Array.from(qtys || []);
if (arr.length === 0) return "";
const dedup = Array.from(new Map(arr.map(q => [normalizeQuantityText(q), q])).values());
dedup.sort((a, b) => {
const sa = quantityScore(a), sb = quantityScore(b);
if (sa.isNum !== sb.isNum) return sa.isNum ? -1 : 1;
if (sa.unitRank !== sb.unitRank) return sb.unitRank - sa.unitRank;
if (sa.value !== sb.value) return sb.value - sa.value;
return sa.raw.localeCompare(sb.raw, "fr");
});
return normalizeQuantityText(dedup[0]);
}
// ========== Ingrédients utils ==========
function normalizeItem(it) {
if (!it || typeof it !== "object") return { Name: "", Quantity: "", Image: "", Owned: false };
return {
Name: it.Name ?? it.name ?? "",
Quantity: it.Quantity ?? it.quantity ?? "",
Image: it.Image ?? it.image ?? "",
Owned: Boolean(it.Owned ?? it.owned ?? false),
};
}
function isOwned(name) { return OWNED_SET.has(normalizeName(name)); }
function mergeDuplicates(items) {
const map = new Map();
for (const raw of items) {
const it = normalizeItem(raw);
if (!it.Name) continue;
const key = normalizeName(it.Name);
const prev = map.get(key);
if (!prev) {
map.set(key, { ...it, _qty: new Set(it.Quantity ? [it.Quantity] : []) });
} else {
if (!prev.Image && it.Image) prev.Image = it.Image;
if (it.Quantity) prev._qty.add(it.Quantity);
prev.Owned = prev.Owned || it.Owned;
}
}
return Array.from(map.values()).map(x => ({ ...x, Quantity: pickBestQuantity(x._qty) }));
}
function sortIngredientsForDisplay(items) {
return items.map(normalizeItem).filter(x => x.Name).sort((a, b) => {
const ao = isOwned(a.Name) ? 1 : 0, bo = isOwned(b.Name) ? 1 : 0;
if (ao !== bo) return ao - bo;
return (a.Name || "").localeCompare((b.Name || ""), "fr", { sensitivity: "base" });
});
}
// ========== UI cartes ingrédients ==========
function renderIngredientCard(ingRaw) {
const ing = normalizeItem(ingRaw);
const div = document.createElement("div");
div.className = "card";
div.dataset.name = ing.Name;
if (isOwned(ing.Name)) div.classList.add("checked");
div.innerHTML = `
<div class="image-container">
<img src="${ing.Image || ""}" alt="${ing.Name}">
</div>
<div class="card-content">
<h3>${ing.Name || "(ingrédient)"}</h3>
<div class="footerCard">
<div class="selection-slot"></div>
<div class="recipe-info">
<span class="prep-time">${ing.Quantity || ""}</span>
</div>
</div>
</div>
`;
let busy = false;
div.addEventListener("click", async () => {
if (busy) return; busy = true;
const name = div.dataset.name, key = normalizeName(name), was = div.classList.contains("checked");
div.classList.toggle("checked");
try {
const res = await fetch("/HelloFresh/ToggleOwnedIngredient", {
method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(name),
});
if (!res.ok) throw new Error("HTTP " + res.status);
const { status } = await res.json();
if (status === "added") OWNED_SET.add(key);
else if (status === "removed") OWNED_SET.delete(key);
else div.classList.toggle("checked", was);
const idx = ALL_AGG_INGS.findIndex(x => normalizeName(x.Name) === key);
if (idx !== -1) ALL_AGG_INGS[idx].Owned = (status === "added");
} catch (e) {
console.error("Erreur toggle ingrédient", e);
div.classList.toggle("checked", was);
} finally {
busy = false; reapplyCurrentSearch();
}
});
return div;
}
function renderItems(items) {
if (!els.list) return;
els.list.innerHTML = "";
items.forEach(ing => els.list.appendChild(renderIngredientCard(ing)));
}
// ========== Recherche ==========
async function applyAISearch(q) {
SEARCH_STATE.query = q; SEARCH_STATE.mode = q ? "ai" : null;
if (!q?.trim()) { renderItems(sortIngredientsForDisplay(mergeDuplicates([...ALL_AGG_INGS]))); return; }
if (els.list) els.list.innerHTML = "<em>Recherche IA…</em>";
try {
if (AI_CTRL) AI_CTRL.abort(); AI_CTRL = new AbortController();
const res = await fetch("/HelloFresh/SmartSearch?debug=true", {
method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: q }), signal: AI_CTRL.signal
});
const raw = await res.clone().text();
if (!res.ok) { console.warn("AI SmartSearch HTTP", res.status, raw); applyLiveFilter(q); return; }
const data = JSON.parse(raw);
const items = Array.isArray(data?.items) ? data.items : [];
if (!items.length) { applyLiveFilter(q); return; }
renderItems(sortIngredientsForDisplay(mergeDuplicates(items)));
} catch (err) {
if (err.name === "AbortError") console.log("[AI] annulée");
else { console.error("AI SmartSearch error:", err); applyLiveFilter(q); }
} finally { AI_CTRL = null; }
}
function applyLiveFilter(q) {
SEARCH_STATE.query = String(q ?? ""); SEARCH_STATE.mode = q ? "live" : null;
const nq = normalizeText(q);
if (!nq) { renderItems(sortIngredientsForDisplay(mergeDuplicates([...ALL_AGG_INGS]))); return; }
const filtered = ALL_AGG_INGS.filter(x => normalizeText(x.Name).includes(nq));
const list = sortIngredientsForDisplay(mergeDuplicates(applyExclusionsTo(filtered)));
renderItems(list);
}
function reapplyCurrentSearch() {
const q = SEARCH_STATE.query || "";
if (!q) {
const base = sortIngredientsForDisplay(mergeDuplicates([...ALL_AGG_INGS]));
renderItems(applyExclusionsTo(base));
return;
}
if (SEARCH_STATE.mode === "ai") applyAISearch(q); else applyLiveFilter(q);
}
// ========== Chargement liste principale ==========
async function loadAndRenderIngredients() {
if (!els.list) return;
els.list.innerHTML = "Chargement…";
try {
const res = await fetch("/HelloFresh/AggregatedIngredients");
if (!res.ok) { const txt = await res.text().catch(() => ""); console.error("[AggregatedIngredients] HTTP", res.status, txt); els.list.innerHTML = `Erreur chargement ingrédients (${res.status})`; return; }
const data = await res.json();
ALL_AGG_INGS = Array.isArray(data) ? data : [];
OWNED_SET = new Set(ALL_AGG_INGS.filter(x => x?.Name && x.Owned === true).map(x => x.Name.trim().toLowerCase()));
els.list.innerHTML = "";
const initialMerged = mergeDuplicates([...ALL_AGG_INGS]);
const initial = sortIngredientsForDisplay(applyExclusionsTo(initialMerged));
els.list.innerHTML = "";
renderItems(initial.length ? initial : []);
if (!initial.length) els.list.innerHTML = "<em>Aucun ingrédient</em>";
} catch (e) { console.error("[AggregatedIngredients] exception", e); els.list.innerHTML = "Erreur chargement ingrédients"; }
}
// ========== Not shipped (DOM) + BDD ==========
function getNotShippedIngredientsFromDOM(root = document) {
const nodes = Array.from(root.querySelectorAll(".ingredient-item-not-shipped"));
return nodes.map(n => {
const name = (n.querySelector('[data-test="ingredient-name"], .ingredient-name, .name, h3, span')?.textContent || n.getAttribute("data-name") || n.querySelector("img")?.alt || "").trim();
const qty = (n.querySelector('[data-test="ingredient-quantity"], .ingredient-amount, .quantity, .qty')?.textContent || n.getAttribute("data-quantity") || "").trim();
const img = (n.querySelector("img")?.src || n.getAttribute("data-img") || "").trim();
return normalizeItem({ Name: name, Quantity: qty, Image: img, Owned: isOwned(name) });
}).filter(x => x.Name);
}
// Détecte la propriété "ingredients à avoir" dans un objet recette,
// même si le nom varie (ingredientsToHad / IngredientsToHad / pantry / etc.)
function detectIngredientsToHadProp(recipe) {
if (!recipe || typeof recipe !== "object") return null;
// Candidats fréquents (essayés d'abord)
const CANDIDATES = [
"ingredientsToHad", "IngredientsToHad", "ingredients_to_had",
"ingredientsToHaveAtHome", "ingredientsToHave", "toHaveAtHome",
"pantry", "pantryItems", "pantry_ingredients", "ingredientsPantry"
];
for (const key of CANDIDATES) {
if (key in recipe && recipe[key]) {
try {
const val = recipe[key];
const obj = (typeof val === "string") ? JSON.parse(val) : val;
if (obj && typeof obj === "object" &&
(Array.isArray(obj["1"]) || Array.isArray(obj["2"]) || Array.isArray(obj["3"]) || Array.isArray(obj["4"]))) {
return { prop: key, parsed: obj };
}
} catch { }
}
}
// Heuristique : scanne toutes les props à la recherche dun objet 1..4 -> array dobjets Name/Quantity
for (const [key, val] of Object.entries(recipe)) {
if (!val) continue;
try {
const obj = (typeof val === "string") ? JSON.parse(val) : val;
if (obj && typeof obj === "object") {
const arr = obj["1"] || obj["2"] || obj["3"] || obj["4"];
if (Array.isArray(arr) && arr.length) {
const it = arr[0] || {};
if (typeof it === "object" && ("Name" in it || "name" in it)) {
return { prop: key, parsed: obj };
}
}
}
} catch { }
}
return null;
}
// Lit une recette en tolérant la casse / formats
function getIngredientsToHadFromRecipe(r) {
const det = detectIngredientsToHadProp(r);
if (!det) return [];
const pRaw = r?.portions ?? r?.Portions ?? 0;
const portions = clampQty(pRaw) + 1; // 1..4
const list = Array.isArray(det.parsed[portions]) ? det.parsed[portions] : [];
return list.map(normalizeItem);
}
function collectIngredientsToHadFromAllRecipes() {
const res = [];
const all = Array.isArray(window.allRecipes) ? window.allRecipes : [];
for (const r of all) res.push(...getIngredientsToHadFromRecipe(r));
return res;
}
function computeSignature(items) {
const merged = mergeDuplicates(items || []);
return JSON.stringify(merged.map(i => ({ n: normalizeName(i.Name), q: normalizeQuantityText(i.Quantity), img: i.Image || "" })));
}
function renderNeededList(itemsArr) {
const container = document.getElementById("neededIngredients");
if (!container) return;
const merged = mergeDuplicates(itemsArr || []);
const sig = computeSignature(merged);
if (sig === lastNeededSignature) return;
lastNeededSignature = sig;
container.innerHTML = "";
for (const ing of merged) {
const li = document.createElement("li");
li.className = "card needed";
li.innerHTML = `
<div class="image-container"><img src="${ing.Image || ""}" alt="${ing.Name}"></div>
<div class="card-content">
<h3>${ing.Name || "(ingrédient)"}</h3>
<div class="footerCard"><div class="recipe-info"></div></div>
</div>`;
container.appendChild(li);
}
}
function updateNeededList(trigger = "manual") {
const container = document.getElementById("neededIngredients");
if (!container) {
console.debug("[needed]", trigger, "→ container #neededIngredients introuvable (on réessaiera)");
return;
}
console.debug("[needed]", trigger, "→ container OK");
// 1) DOM (non livrés)
const domItems = getNotShippedIngredientsFromDOM();
if (domItems.length) {
const filtered = applyNeededExclusions(domItems);
renderNeededList(filtered);
return;
}
// 2) Agrégation de TOUTES les recettes
const aggregated = collectIngredientsToHadFromAllRecipes();
if (aggregated.length) {
const filtered = applyNeededExclusions(aggregated);
renderNeededList(filtered);
return;
}
// 3) Dernière recette vue (fallback)
if (LAST_ING_TO_HAD) {
try {
const parsed = JSON.parse(LAST_ING_TO_HAD || "{}");
const list = Array.isArray(parsed[LAST_PORTIONS]) ? parsed[LAST_PORTIONS] : [];
const filtered = applyNeededExclusions(list);
if (filtered.length) {
renderNeededList(filtered);
return;
}
} catch (e) { /* ... */ }
}
// 4) Rien
const emptySig = "EMPTY";
if (lastNeededSignature !== emptySig) {
lastNeededSignature = emptySig;
container.innerHTML = "<p>Aucun ingrédient à afficher.</p>";
}
console.warn("[needed]", trigger, "→ aucun item");
}
// Scan DOM non-livrés (anti-boucle)
function scanNotShippedAndUpdate() {
const count = document.querySelectorAll(".ingredient-item-not-shipped").length;
if (count !== prevNotShippedCount) { prevNotShippedCount = count; updateNeededList(); }
}
// ========== Recettes (strip) ==========
const ownedMap = new Map();
const MAX_PORTIONS = 14;
const clampQty = n => Math.max(0, Math.min(3, Number(n) || 0));
const truncate = (s, m) => (s && s.length > m ? s.slice(0, m) + "…" : (s ?? ""));
function setSelectedUI(card, selected) {
const slot = card?.querySelector?.(".selection-slot"); if (!slot) return;
if (selected) {
slot.classList.add("active");
if (!slot.querySelector(".selection-indicator")) {
slot.innerHTML = `<div class="selection-indicator"><span class="selection-text">Sélectionné</span></div>`;
}
} else { slot.classList.remove("active"); slot.innerHTML = ""; }
}
function applySelectionUIById(id) {
const card = document.getElementById(String(id));
const selected = (ownedMap.get(String(id)) || 0) > 0;
setSelectedUI(card, selected);
}
function buildLabels(tagsStr) {
const wrap = document.createElement("div");
wrap.className = "label-container";
const tags = (tagsStr || "").split(/[,•]/).map(t => t.trim()).filter(Boolean).filter(t => t !== "•" && t !== "\u2022");
tags.forEach(tag => {
const span = document.createElement("span");
span.className = "label-badge";
const c = (window.labelColors || {})[tag];
span.style.backgroundColor = c?.bg ?? "#eee";
span.style.color = c?.color ?? "#333";
span.textContent = tag;
wrap.appendChild(span);
});
return wrap;
}
function buildRecipeCardList(recipe) {
const card = document.createElement("div");
card.className = "card";
card.id = recipe.id ?? recipe.Id;
card.innerHTML = `
<div class="image-container"><img src="${recipe.image}" alt="${truncate(recipe.name, 10)}"></div>
<div class="card-content">
<h3>${recipe.name}</h3>
<div class="footerCard"><div class="recipe-info"><span class="prep-time">${recipe.tempsDePreparation}min</span></div></div>
</div>
`.replace(/\${/g, "${");
card.addEventListener("click", async (e) => {
if (e.ctrlKey) return; // laisse le Ctrl+clic tranquille
const id = recipe.id ?? recipe.Id;
const portions = clampQty(recipe.portions ?? recipe.Portions ?? 0) + 1; // 1..4
if (EXCLUDED_BY_RECIPE.has(String(id))) {
// ré-afficher
EXCLUDED_BY_RECIPE.delete(String(id));
EXCLUDED_NEEDED_BY_RECIPE.delete(String(id)); // 👈 aussi pour "à avoir"
card.classList.remove("card--excluded");
} else {
// cacher
const namesMain = await getIngredientNamesForRecipe(id, portions); // déjà en place chez toi
EXCLUDED_BY_RECIPE.set(String(id), new Set(namesMain.map(nameKey)));
const namesNeeded = getNeededNamesForRecipeObj(recipe, portions); // 👈 "à avoir" depuis lobjet recette
EXCLUDED_NEEDED_BY_RECIPE.set(String(id), new Set(namesNeeded.map(nameKey)));
card.classList.add("card--excluded");
}
// Re-rendre les deux panneaux
reapplyCurrentSearch(); // liste principale
updateNeededList("exclude-toggle"); // “à avoir”
});
const usersVal = recipe.Users ?? recipe.users ?? recipe.User ?? recipe.user ?? [];
const usersArr = Array.isArray(usersVal) ? usersVal.filter(Boolean) : String(usersVal || "").split(/[,•]/).map(s => s.trim()).filter(Boolean);
if (usersArr.length) {
const img = card.querySelector(".image-container");
const wrap = document.createElement("div"); wrap.className = "label-container";
usersArr.forEach(u => { const span = document.createElement("span"); span.className = "label-badge user-badge"; span.textContent = u; wrap.appendChild(span); });
img?.appendChild(wrap);
}
// Mémorise la dernière recette pour un fallback, via détection auto
const det = detectIngredientsToHadProp(recipe);
const portions = clampQty(recipe.portions ?? recipe.Portions ?? 0) + 1;
LAST_PORTIONS = portions;
LAST_ING_TO_HAD = det ? JSON.stringify(det.parsed) : null;
console.debug("[needed] buildRecipe detected prop:", det?.prop || "(none)", "portions:", portions);
updateNeededList("buildRecipe");
return card;
}
const GET_ALL_RECIPES_URL = "/HelloFresh/GetAllRecipes";
async function loadAllRecipesHorizontal() {
const strip = document.getElementById("recipeStrip");
if (!strip) return;
try {
const r = await fetch("/HelloFresh/GetRecipesOwned?isUserImportant=false", { credentials: "same-origin" });
if (r.ok) { const data = await r.json(); ownedMap.clear(); (data || []).forEach(x => ownedMap.set(String(x.id), clampQty(x.portions || 0))); }
} catch { }
try {
const res = await fetch(GET_ALL_RECIPES_URL, { credentials: "same-origin" });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const all = await res.json();
window.allRecipes = all;
strip.innerHTML = "";
if (!Array.isArray(all) || all.length === 0) { strip.innerHTML = '<p class="placeholder">Aucune recette disponible.</p>'; updateNeededList(); return; }
all.sort((a, b) => {
const ua = String(a.userId ?? ""), ub = String(b.userId ?? "");
if (ua !== ub) return ua.localeCompare(ub);
return String(a.name ?? "").localeCompare(String(b.name ?? ""), "fr", { sensitivity: "base" });
});
all.forEach(r => strip.appendChild(buildRecipeCardList(r)));
updateNeededList("afterGetAllRecipes");
console.debug("[needed] recipes loaded:", Array.isArray(window.allRecipes) ? window.allRecipes.length : 0);
} catch (e) {
console.error("loadAllRecipesHorizontal error", e);
strip.innerHTML = '<p class="placeholder">Erreur de chargement.</p>';
updateNeededList();
}
}
// ======================
// Init
// ======================
document.addEventListener("DOMContentLoaded", () => {
els.list = document.getElementById("ingredientsList") || null;
els.search = document.getElementById("hfSearch") || null;
document.querySelectorAll(".dropdown-toggle").forEach(el => el.addEventListener("click", (e) => e.preventDefault()));
if (els.search) {
let aiTimer;
els.search.addEventListener("input", (e) => { clearTimeout(aiTimer); const val = e.target.value || ""; aiTimer = setTimeout(() => applyAISearch(val), AI_DELAY); });
els.search.addEventListener("keydown", (e) => { if (e.key === "Enter") { e.preventDefault(); clearTimeout(aiTimer); applyAISearch(els.search.value || ""); } });
} else {
console.error("[ingredients] #hfSearch introuvable dans le DOM");
}
loadAndRenderIngredients();
loadAllRecipesHorizontal();
// Premier rendu (même si le UL n'est pas encore dans le DOM, on loggue)
updateNeededList("dom-ready");
// Petits réessaies pour les pages qui injectent tardivement le UL / les cartes
setTimeout(() => updateNeededList("post-300ms"), 300);
setTimeout(() => updateNeededList("post-1200ms"), 1200);
const mo = new MutationObserver(() => {
// Throttle : on regroupe les mutations
if (typeof window !== "undefined") {
if (mo._scheduled) return;
mo._scheduled = true;
setTimeout(() => {
mo._scheduled = false;
updateNeededList("mutation");
}, 120);
} else {
updateNeededList("mutation");
}
});
mo.observe(document.body, { childList: true, subtree: true });
});

View File

@@ -0,0 +1,8 @@
window.labelColors = {
"À faire rapidement": { bg: "#e65100", color: "#fff" },
"Calorie Smart": { bg: "#00838f", color: "#fff" },
"Équilibre": { bg: "#2e7d32", color: "#fff" },
"Faible en calories": { bg: "#f9a825", color: "#fff" },
"Végétarien": { bg: "#6a1b9a", color: "#fff" },
"•": { bg: "#424242", color: "#fff" }
};

View File

@@ -3,7 +3,8 @@ const API_PASSWORD = 'Mf33ksTRLrPKSqQ4cTXitgiSN6BPBt89';
const Controller = {
Revenue: "revenue",
Expense: "expense"
Expense: "expense",
HelloFresh: "hellofresh"
}
function getApiBaseUrl() {
const isLocal = location.hostname === 'localhost' || location.hostname === '127.0.0.1';

View File

@@ -1,118 +0,0 @@
// Set new default font family and font color to mimic Bootstrap's default styling
Chart.defaults.global.defaultFontFamily = 'Nunito', '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.global.defaultFontColor = '#858796';
function number_format(number, decimals, dec_point, thousands_sep) {
// * example: number_format(1234.56, 2, ',', ' ');
// * return: '1 234,56'
number = (number + '').replace(',', '').replace(' ', '');
var n = !isFinite(+number) ? 0 : +number,
prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
s = '',
toFixedFix = function(n, prec) {
var k = Math.pow(10, prec);
return '' + Math.round(n * k) / k;
};
// Fix for IE parseFloat(0.55).toFixed(0) = 0;
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
if (s[0].length > 3) {
s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
}
if ((s[1] || '').length < prec) {
s[1] = s[1] || '';
s[1] += new Array(prec - s[1].length + 1).join('0');
}
return s.join(dec);
}
// Area Chart Example
var ctx = document.getElementById("myAreaChart");
var myLineChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
datasets: [{
label: "Earnings",
lineTension: 0.3,
backgroundColor: "rgba(78, 115, 223, 0.05)",
borderColor: "rgba(78, 115, 223, 1)",
pointRadius: 3,
pointBackgroundColor: "rgba(78, 115, 223, 1)",
pointBorderColor: "rgba(78, 115, 223, 1)",
pointHoverRadius: 3,
pointHoverBackgroundColor: "rgba(78, 115, 223, 1)",
pointHoverBorderColor: "rgba(78, 115, 223, 1)",
pointHitRadius: 10,
pointBorderWidth: 2,
data: [0, 10000, 5000, 15000, 10000, 20000, 15000, 25000, 20000, 30000, 25000, 40000],
}],
},
options: {
maintainAspectRatio: false,
layout: {
padding: {
left: 10,
right: 25,
top: 25,
bottom: 0
}
},
scales: {
xAxes: [{
time: {
unit: 'date'
},
gridLines: {
display: false,
drawBorder: false
},
ticks: {
maxTicksLimit: 7
}
}],
yAxes: [{
ticks: {
maxTicksLimit: 5,
padding: 10,
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return '$' + number_format(value);
}
},
gridLines: {
color: "rgb(234, 236, 244)",
zeroLineColor: "rgb(234, 236, 244)",
drawBorder: false,
borderDash: [2],
zeroLineBorderDash: [2]
}
}],
},
legend: {
display: false
},
tooltips: {
backgroundColor: "rgb(255,255,255)",
bodyFontColor: "#858796",
titleMarginBottom: 10,
titleFontColor: '#6e707e',
titleFontSize: 14,
borderColor: '#dddfeb',
borderWidth: 1,
xPadding: 15,
yPadding: 15,
displayColors: false,
intersect: false,
mode: 'index',
caretPadding: 10,
callbacks: {
label: function(tooltipItem, chart) {
var datasetLabel = chart.datasets[tooltipItem.datasetIndex].label || '';
return datasetLabel + ': $' + number_format(tooltipItem.yLabel);
}
}
}
}
});

View File

@@ -1,111 +0,0 @@
// Set new default font family and font color to mimic Bootstrap's default styling
Chart.defaults.global.defaultFontFamily = 'Nunito', '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.global.defaultFontColor = '#858796';
function number_format(number, decimals, dec_point, thousands_sep) {
// * example: number_format(1234.56, 2, ',', ' ');
// * return: '1 234,56'
number = (number + '').replace(',', '').replace(' ', '');
var n = !isFinite(+number) ? 0 : +number,
prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
s = '',
toFixedFix = function(n, prec) {
var k = Math.pow(10, prec);
return '' + Math.round(n * k) / k;
};
// Fix for IE parseFloat(0.55).toFixed(0) = 0;
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
if (s[0].length > 3) {
s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
}
if ((s[1] || '').length < prec) {
s[1] = s[1] || '';
s[1] += new Array(prec - s[1].length + 1).join('0');
}
return s.join(dec);
}
// Bar Chart Example
var ctx = document.getElementById("myBarChart");
var myBarChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["January", "February", "March", "April", "May", "June"],
datasets: [{
label: "Revenue",
backgroundColor: "#4e73df",
hoverBackgroundColor: "#2e59d9",
borderColor: "#4e73df",
data: [4215, 5312, 6251, 7841, 9821, 14984],
}],
},
options: {
maintainAspectRatio: false,
layout: {
padding: {
left: 10,
right: 25,
top: 25,
bottom: 0
}
},
scales: {
xAxes: [{
time: {
unit: 'month'
},
gridLines: {
display: false,
drawBorder: false
},
ticks: {
maxTicksLimit: 6
},
maxBarThickness: 25,
}],
yAxes: [{
ticks: {
min: 0,
max: 15000,
maxTicksLimit: 5,
padding: 10,
// Include a dollar sign in the ticks
callback: function(value, index, values) {
return '$' + number_format(value);
}
},
gridLines: {
color: "rgb(234, 236, 244)",
zeroLineColor: "rgb(234, 236, 244)",
drawBorder: false,
borderDash: [2],
zeroLineBorderDash: [2]
}
}],
},
legend: {
display: false
},
tooltips: {
titleMarginBottom: 10,
titleFontColor: '#6e707e',
titleFontSize: 14,
backgroundColor: "rgb(255,255,255)",
bodyFontColor: "#858796",
borderColor: '#dddfeb',
borderWidth: 1,
xPadding: 15,
yPadding: 15,
displayColors: false,
caretPadding: 10,
callbacks: {
label: function(tooltipItem, chart) {
var datasetLabel = chart.datasets[tooltipItem.datasetIndex].label || '';
return datasetLabel + ': $' + number_format(tooltipItem.yLabel);
}
}
},
}
});

View File

@@ -1,35 +0,0 @@
// Set new default font family and font color to mimic Bootstrap's default styling
Chart.defaults.global.defaultFontFamily = 'Nunito', '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.global.defaultFontColor = '#858796';
// Pie Chart Example
var ctx = document.getElementById("myPieChart");
var myPieChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ["Direct", "Referral", "Social"],
datasets: [{
data: [55, 30, 15],
backgroundColor: ['#4e73df', '#1cc88a', '#36b9cc'],
hoverBackgroundColor: ['#2e59d9', '#17a673', '#2c9faf'],
hoverBorderColor: "rgba(234, 236, 244, 1)",
}],
},
options: {
maintainAspectRatio: false,
tooltips: {
backgroundColor: "rgb(255,255,255)",
bodyFontColor: "#858796",
borderColor: '#dddfeb',
borderWidth: 1,
xPadding: 15,
yPadding: 15,
displayColors: false,
caretPadding: 10,
},
legend: {
display: false
},
cutoutPercentage: 80,
},
});

View File

@@ -1,4 +0,0 @@
// Call the dataTables jQuery plugin
$(document).ready(function() {
$('#dataTable').DataTable();
});

5
wwwroot/js/home.js Normal file
View File

@@ -0,0 +1,5 @@
document.querySelectorAll('.dropdown .dropdown-toggle').forEach(toggle => {
toggle.addEventListener('click', () => {
toggle.parentElement.classList.toggle('active');
});
});

View File

@@ -1,96 +0,0 @@
/**
* Exemple dutilisation :
* Récupère les loyers avec un nombre de lignes défini
* @param {number} rows - Nombre de lignes à récupérer
*/
const Revenue = {
GetXRevenues: "x_revenues",
GetLastRevenues: "last_revenue",
GetAllRevenues: "all_revenues",
GetAdditionalRevenues: "additional_revenues"
};
const Expense = {
GetRightExpenses: "last_expenses"
}
var TotalRevenu = 0, TotalExpense = 0;
function loadRevenues() {
return Promise.all([
new Promise((resolve, reject) => {
apiCall(Controller.Revenue, Revenue.GetXRevenues, { rows: 1 }, data => {
const loyer = document.getElementById('salaire');
const salaire = parseFloat(data[0]?.salary || 0);
if (loyer) loyer.innerText = `${toPriceFormat(salaire)}`;
resolve(salaire);
}, reject);
}),
new Promise((resolve, reject) => {
apiCall(Controller.Revenue, Revenue.GetAdditionalRevenues, {}, data => {
const additionalRevenues = document.getElementById('additionalRevenues');
const totalAR = data.reduce((acc, item) => acc + (parseFloat(item.amount) || 0), 0);
if (additionalRevenues) additionalRevenues.innerText = `${toPriceFormat(totalAR)}`;
resolve(totalAR);
}, reject);
})
]).then(([salary, additional]) => {
TotalRevenu = salary + additional;
});
}
function loadExpenses() {
return new Promise((resolve, reject) => {
apiCall(Controller.Expense, Expense.GetRightExpenses, {}, data => {
const loyer = document.getElementById('loyer');
const trash = document.getElementById('trash');
const electricity = document.getElementById('electricity');
const insurance = document.getElementById('insurance');
const wifi = document.getElementById('wifi');
const groceries = document.getElementById('groceries');
const additionalSourcesExpense = document.getElementById('additionalSourcesExpense');
const additionalExpensesSub = document.getElementById('additionalExpensesSub');
const saving = document.getElementById('saving');
if (loyer) loyer.innerText = `${toPriceFormat(data.rent)}`;
if (trash) trash.innerText = `${toPriceFormat(data.trash)}`;
if (insurance) insurance.innerText = `${toPriceFormat(data.insurance)}`;
if (electricity) electricity.innerText = `${toPriceFormat(data.electricity)}`;
if (wifi) wifi.innerText = `${toPriceFormat(data.wifi)}`;
if (groceries) groceries.innerText = `${toPriceFormat(data.groceries)}`;
if (saving) saving.innerText = `${toPriceFormat(data.saving)}`;
if (additionalSourcesExpense) {
const totalAR = data.additionalSourcesExpense.reduce((acc, item) => acc + (parseFloat(item.amount) || 0), 0);
additionalSourcesExpense.innerText = `${toPriceFormat(totalAR)}`;
}
if (additionalExpensesSub) {
const totalAR = data.additionalSourcesSub.reduce((acc, item) => acc + (parseFloat(item.amount) || 0), 0);
additionalExpensesSub.innerText = `${toPriceFormat(totalAR)}`;
}
TotalExpense = data.total;
resolve();
}, reject);
});
}
// Lance automatiquement lappel au chargement de la page
document.addEventListener('DOMContentLoaded', () => {
const result = document.getElementById('result');
Promise.all([loadExpenses(), loadRevenues()])
.then(() => {
if (result) result.innerText = `${toPriceFormat(TotalRevenu - TotalExpense)}`;
})
.catch(error => {
console.error("❌ Erreur de chargement :", error);
if (result) result.innerText = "Erreur lors du calcul";
});
});

View File

@@ -75,3 +75,14 @@ function toPriceFormat(valeur) {
maximumFractionDigits: 0
});
}
const video = document.getElementById("bg-video");
video.playbackRate = 0.5; // ralenti
let reverse = false;
video.addEventListener("ended", () => {
reverse = !reverse;
video.playbackRate = reverse ? -1 : 1; // négatif = lecture à l'envers
video.currentTime = reverse ? video.duration : 0; // repartir du bon côté
video.play();
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

155
wwwroot/lib/datatable/datatables.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,210 +0,0 @@
@charset "UTF-8";
table.dataTable {
clear: both;
margin-top: 6px !important;
margin-bottom: 6px !important;
max-width: none !important;
border-collapse: separate !important;
border-spacing: 0;
}
table.dataTable td,
table.dataTable th {
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
table.dataTable td.dataTables_empty,
table.dataTable th.dataTables_empty {
text-align: center;
}
table.dataTable.nowrap th,
table.dataTable.nowrap td {
white-space: nowrap;
}
div.dataTables_wrapper div.dataTables_length label {
font-weight: normal;
text-align: left;
white-space: nowrap;
}
div.dataTables_wrapper div.dataTables_length select {
width: auto;
display: inline-block;
}
div.dataTables_wrapper div.dataTables_filter {
text-align: right;
}
div.dataTables_wrapper div.dataTables_filter label {
font-weight: normal;
white-space: nowrap;
text-align: left;
}
div.dataTables_wrapper div.dataTables_filter input {
margin-left: 0.5em;
display: inline-block;
width: auto;
}
div.dataTables_wrapper div.dataTables_info {
padding-top: 0.85em;
}
div.dataTables_wrapper div.dataTables_paginate {
margin: 0;
white-space: nowrap;
text-align: right;
}
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
margin: 2px 0;
white-space: nowrap;
justify-content: flex-end;
}
div.dataTables_wrapper div.dataTables_processing {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
margin-left: -100px;
margin-top: -26px;
text-align: center;
padding: 1em 0;
}
table.dataTable > thead > tr > th:active,
table.dataTable > thead > tr > td:active {
outline: none;
}
table.dataTable > thead > tr > th:not(.sorting_disabled),
table.dataTable > thead > tr > td:not(.sorting_disabled) {
padding-right: 30px;
}
table.dataTable > thead .sorting,
table.dataTable > thead .sorting_asc,
table.dataTable > thead .sorting_desc,
table.dataTable > thead .sorting_asc_disabled,
table.dataTable > thead .sorting_desc_disabled {
cursor: pointer;
position: relative;
}
table.dataTable > thead .sorting:before, table.dataTable > thead .sorting:after,
table.dataTable > thead .sorting_asc:before,
table.dataTable > thead .sorting_asc:after,
table.dataTable > thead .sorting_desc:before,
table.dataTable > thead .sorting_desc:after,
table.dataTable > thead .sorting_asc_disabled:before,
table.dataTable > thead .sorting_asc_disabled:after,
table.dataTable > thead .sorting_desc_disabled:before,
table.dataTable > thead .sorting_desc_disabled:after {
position: absolute;
bottom: 0.9em;
display: block;
opacity: 0.3;
}
table.dataTable > thead .sorting:before,
table.dataTable > thead .sorting_asc:before,
table.dataTable > thead .sorting_desc:before,
table.dataTable > thead .sorting_asc_disabled:before,
table.dataTable > thead .sorting_desc_disabled:before {
right: 1em;
content: "↑";
}
table.dataTable > thead .sorting:after,
table.dataTable > thead .sorting_asc:after,
table.dataTable > thead .sorting_desc:after,
table.dataTable > thead .sorting_asc_disabled:after,
table.dataTable > thead .sorting_desc_disabled:after {
right: 0.5em;
content: "↓";
}
table.dataTable > thead .sorting_asc:before,
table.dataTable > thead .sorting_desc:after {
opacity: 1;
}
table.dataTable > thead .sorting_asc_disabled:before,
table.dataTable > thead .sorting_desc_disabled:after {
opacity: 0;
}
div.dataTables_scrollHead table.dataTable {
margin-bottom: 0 !important;
}
div.dataTables_scrollBody table {
border-top: none;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
div.dataTables_scrollBody table thead .sorting:before,
div.dataTables_scrollBody table thead .sorting_asc:before,
div.dataTables_scrollBody table thead .sorting_desc:before,
div.dataTables_scrollBody table thead .sorting:after,
div.dataTables_scrollBody table thead .sorting_asc:after,
div.dataTables_scrollBody table thead .sorting_desc:after {
display: none;
}
div.dataTables_scrollBody table tbody tr:first-child th,
div.dataTables_scrollBody table tbody tr:first-child td {
border-top: none;
}
div.dataTables_scrollFoot > .dataTables_scrollFootInner {
box-sizing: content-box;
}
div.dataTables_scrollFoot > .dataTables_scrollFootInner > table {
margin-top: 0 !important;
border-top: none;
}
@media screen and (max-width: 767px) {
div.dataTables_wrapper div.dataTables_length,
div.dataTables_wrapper div.dataTables_filter,
div.dataTables_wrapper div.dataTables_info,
div.dataTables_wrapper div.dataTables_paginate {
text-align: center;
}
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
justify-content: center !important;
}
}
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {
padding-right: 20px;
}
table.dataTable.table-sm .sorting:before,
table.dataTable.table-sm .sorting_asc:before,
table.dataTable.table-sm .sorting_desc:before {
top: 5px;
right: 0.85em;
}
table.dataTable.table-sm .sorting:after,
table.dataTable.table-sm .sorting_asc:after,
table.dataTable.table-sm .sorting_desc:after {
top: 5px;
}
table.table-bordered.dataTable {
border-right-width: 0;
}
table.table-bordered.dataTable th,
table.table-bordered.dataTable td {
border-left-width: 0;
}
table.table-bordered.dataTable th:last-child, table.table-bordered.dataTable th:last-child,
table.table-bordered.dataTable td:last-child,
table.table-bordered.dataTable td:last-child {
border-right-width: 1px;
}
table.table-bordered.dataTable tbody th,
table.table-bordered.dataTable tbody td {
border-bottom-width: 0;
}
div.dataTables_scrollHead table.table-bordered {
border-bottom-width: 0;
}
div.table-responsive > div.dataTables_wrapper > div.row {
margin: 0;
}
div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:first-child {
padding-left: 0;
}
div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-child {
padding-right: 0;
}

View File

@@ -1,184 +0,0 @@
/*! DataTables Bootstrap 4 integration
* ©2011-2017 SpryMedia Ltd - datatables.net/license
*/
/**
* DataTables integration for Bootstrap 4. This requires Bootstrap 4 and
* DataTables 1.10 or newer.
*
* This file sets the defaults and adds options to DataTables to style its
* controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap
* for further information.
*/
(function( factory ){
if ( typeof define === 'function' && define.amd ) {
// AMD
define( ['jquery', 'datatables.net'], function ( $ ) {
return factory( $, window, document );
} );
}
else if ( typeof exports === 'object' ) {
// CommonJS
module.exports = function (root, $) {
if ( ! root ) {
root = window;
}
if ( ! $ || ! $.fn.dataTable ) {
// Require DataTables, which attaches to jQuery, including
// jQuery if needed and have a $ property so we can access the
// jQuery object that is used
$ = require('datatables.net')(root, $).$;
}
return factory( $, root, root.document );
};
}
else {
// Browser
factory( jQuery, window, document );
}
}(function( $, window, document, undefined ) {
'use strict';
var DataTable = $.fn.dataTable;
/* Set the defaults for DataTables initialisation */
$.extend( true, DataTable.defaults, {
dom:
"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
renderer: 'bootstrap'
} );
/* Default class modification */
$.extend( DataTable.ext.classes, {
sWrapper: "dataTables_wrapper dt-bootstrap4",
sFilterInput: "form-control form-control-sm",
sLengthSelect: "custom-select custom-select-sm form-control form-control-sm",
sProcessing: "dataTables_processing card",
sPageButton: "paginate_button page-item"
} );
/* Bootstrap paging button renderer */
DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {
var api = new DataTable.Api( settings );
var classes = settings.oClasses;
var lang = settings.oLanguage.oPaginate;
var aria = settings.oLanguage.oAria.paginate || {};
var btnDisplay, btnClass, counter=0;
var attach = function( container, buttons ) {
var i, ien, node, button;
var clickHandler = function ( e ) {
e.preventDefault();
if ( !$(e.currentTarget).hasClass('disabled') && api.page() != e.data.action ) {
api.page( e.data.action ).draw( 'page' );
}
};
for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
button = buttons[i];
if ( Array.isArray( button ) ) {
attach( container, button );
}
else {
btnDisplay = '';
btnClass = '';
switch ( button ) {
case 'ellipsis':
btnDisplay = '&#x2026;';
btnClass = 'disabled';
break;
case 'first':
btnDisplay = lang.sFirst;
btnClass = button + (page > 0 ?
'' : ' disabled');
break;
case 'previous':
btnDisplay = lang.sPrevious;
btnClass = button + (page > 0 ?
'' : ' disabled');
break;
case 'next':
btnDisplay = lang.sNext;
btnClass = button + (page < pages-1 ?
'' : ' disabled');
break;
case 'last':
btnDisplay = lang.sLast;
btnClass = button + (page < pages-1 ?
'' : ' disabled');
break;
default:
btnDisplay = button + 1;
btnClass = page === button ?
'active' : '';
break;
}
if ( btnDisplay ) {
node = $('<li>', {
'class': classes.sPageButton+' '+btnClass,
'id': idx === 0 && typeof button === 'string' ?
settings.sTableId +'_'+ button :
null
} )
.append( $('<a>', {
'href': '#',
'aria-controls': settings.sTableId,
'aria-label': aria[ button ],
'data-dt-idx': counter,
'tabindex': settings.iTabIndex,
'class': 'page-link'
} )
.html( btnDisplay )
)
.appendTo( container );
settings.oApi._fnBindAction(
node, {action: button}, clickHandler
);
counter++;
}
}
}
};
// IE9 throws an 'unknown error' if document.activeElement is used
// inside an iframe or frame.
var activeEl;
try {
// Because this approach is destroying and recreating the paging
// elements, focus is lost on the select button which is bad for
// accessibility. So we want to restore focus once the draw has
// completed
activeEl = $(host).find(document.activeElement).data('dt-idx');
}
catch (e) {}
attach(
$(host).empty().html('<ul class="pagination"/>').children('ul'),
buttons
);
if ( activeEl !== undefined ) {
$(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
}
};
return DataTable;
}));

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +0,0 @@
/*!
DataTables Bootstrap 4 integration
©2011-2017 SpryMedia Ltd - datatables.net/license
*/
(function(c){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(a){return c(a,window,document)}):"object"===typeof exports?module.exports=function(a,d){a||(a=window);if(!d||!d.fn.dataTable)d=require("datatables.net")(a,d).$;return c(d,a,a.document)}:c(jQuery,window,document)})(function(c,a,d,m){var f=c.fn.dataTable;c.extend(!0,f.defaults,{dom:"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
renderer:"bootstrap"});c.extend(f.ext.classes,{sWrapper:"dataTables_wrapper dt-bootstrap4",sFilterInput:"form-control form-control-sm",sLengthSelect:"custom-select custom-select-sm form-control form-control-sm",sProcessing:"dataTables_processing card",sPageButton:"paginate_button page-item"});f.ext.renderer.pageButton.bootstrap=function(a,h,r,s,j,n){var o=new f.Api(a),t=a.oClasses,k=a.oLanguage.oPaginate,u=a.oLanguage.oAria.paginate||{},e,g,p=0,q=function(d,f){var l,h,i,b,m=function(a){a.preventDefault();
!c(a.currentTarget).hasClass("disabled")&&o.page()!=a.data.action&&o.page(a.data.action).draw("page")};l=0;for(h=f.length;l<h;l++)if(b=f[l],Array.isArray(b))q(d,b);else{g=e="";switch(b){case "ellipsis":e="&#x2026;";g="disabled";break;case "first":e=k.sFirst;g=b+(0<j?"":" disabled");break;case "previous":e=k.sPrevious;g=b+(0<j?"":" disabled");break;case "next":e=k.sNext;g=b+(j<n-1?"":" disabled");break;case "last":e=k.sLast;g=b+(j<n-1?"":" disabled");break;default:e=b+1,g=j===b?"active":""}e&&(i=c("<li>",
{"class":t.sPageButton+" "+g,id:0===r&&"string"===typeof b?a.sTableId+"_"+b:null}).append(c("<a>",{href:"#","aria-controls":a.sTableId,"aria-label":u[b],"data-dt-idx":p,tabindex:a.iTabIndex,"class":"page-link"}).html(e)).appendTo(d),a.oApi._fnBindAction(i,{action:b},m),p++)}},i;try{i=c(h).find(d.activeElement).data("dt-idx")}catch(v){}q(c(h).empty().html('<ul class="pagination"/>').children("ul"),s);i!==m&&c(h).find("[data-dt-idx="+i+"]").trigger("focus")};return f});

File diff suppressed because it is too large Load Diff

View File

@@ -1,168 +0,0 @@
/*!
DataTables 1.10.24
©2008-2021 SpryMedia Ltd - datatables.net/license
*/
(function(h){"function"===typeof define&&define.amd?define(["jquery"],function(E){return h(E,window,document)}):"object"===typeof exports?module.exports=function(E,H){E||(E=window);H||(H="undefined"!==typeof window?require("jquery"):require("jquery")(E));return h(H,E,E.document)}:h(jQuery,window,document)})(function(h,E,H,k){function $(a){var b,c,d={};h.each(a,function(e){if((b=e.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(b[1]+" "))c=e.replace(b[0],b[2].toLowerCase()),
d[c]=e,"o"===b[1]&&$(a[e])});a._hungarianMap=d}function J(a,b,c){a._hungarianMap||$(a);var d;h.each(b,function(e){d=a._hungarianMap[e];if(d!==k&&(c||b[d]===k))"o"===d.charAt(0)?(b[d]||(b[d]={}),h.extend(!0,b[d],b[e]),J(a[d],b[d],c)):b[d]=b[e]})}function Ea(a){var b=l.defaults.oLanguage,c=b.sDecimal;c&&Fa(c);if(a){var d=a.sZeroRecords;!a.sEmptyTable&&(d&&"No data available in table"===b.sEmptyTable)&&F(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&(d&&"Loading..."===b.sLoadingRecords)&&F(a,
a,"sZeroRecords","sLoadingRecords");a.sInfoThousands&&(a.sThousands=a.sInfoThousands);(a=a.sDecimal)&&c!==a&&Fa(a)}}function gb(a){A(a,"ordering","bSort");A(a,"orderMulti","bSortMulti");A(a,"orderClasses","bSortClasses");A(a,"orderCellsTop","bSortCellsTop");A(a,"order","aaSorting");A(a,"orderFixed","aaSortingFixed");A(a,"paging","bPaginate");A(a,"pagingType","sPaginationType");A(a,"pageLength","iDisplayLength");A(a,"searching","bFilter");"boolean"===typeof a.sScrollX&&(a.sScrollX=a.sScrollX?"100%":
"");"boolean"===typeof a.scrollX&&(a.scrollX=a.scrollX?"100%":"");if(a=a.aoSearchCols)for(var b=0,c=a.length;b<c;b++)a[b]&&J(l.models.oSearch,a[b])}function hb(a){A(a,"orderable","bSortable");A(a,"orderData","aDataSort");A(a,"orderSequence","asSorting");A(a,"orderDataType","sortDataType");var b=a.aDataSort;"number"===typeof b&&!Array.isArray(b)&&(a.aDataSort=[b])}function ib(a){if(!l.__browser){var b={};l.__browser=b;var c=h("<div/>").css({position:"fixed",top:0,left:-1*h(E).scrollLeft(),height:1,
width:1,overflow:"hidden"}).append(h("<div/>").css({position:"absolute",top:1,left:1,width:100,overflow:"scroll"}).append(h("<div/>").css({width:"100%",height:10}))).appendTo("body"),d=c.children(),e=d.children();b.barWidth=d[0].offsetWidth-d[0].clientWidth;b.bScrollOversize=100===e[0].offsetWidth&&100!==d[0].clientWidth;b.bScrollbarLeft=1!==Math.round(e.offset().left);b.bBounding=c[0].getBoundingClientRect().width?!0:!1;c.remove()}h.extend(a.oBrowser,l.__browser);a.oScroll.iBarWidth=l.__browser.barWidth}
function jb(a,b,c,d,e,f){var g,j=!1;c!==k&&(g=c,j=!0);for(;d!==e;)a.hasOwnProperty(d)&&(g=j?b(g,a[d],d,a):a[d],j=!0,d+=f);return g}function Ga(a,b){var c=l.defaults.column,d=a.aoColumns.length,c=h.extend({},l.models.oColumn,c,{nTh:b?b:H.createElement("th"),sTitle:c.sTitle?c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[d],mData:c.mData?c.mData:d,idx:d});a.aoColumns.push(c);c=a.aoPreSearchCols;c[d]=h.extend({},l.models.oSearch,c[d]);la(a,d,h(b).data())}function la(a,b,c){var b=a.aoColumns[b],
d=a.oClasses,e=h(b.nTh);if(!b.sWidthOrig){b.sWidthOrig=e.attr("width")||null;var f=(e.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);f&&(b.sWidthOrig=f[1])}c!==k&&null!==c&&(hb(c),J(l.defaults.column,c,!0),c.mDataProp!==k&&!c.mData&&(c.mData=c.mDataProp),c.sType&&(b._sManualType=c.sType),c.className&&!c.sClass&&(c.sClass=c.className),c.sClass&&e.addClass(c.sClass),h.extend(b,c),F(b,c,"sWidth","sWidthOrig"),c.iDataSort!==k&&(b.aDataSort=[c.iDataSort]),F(b,c,"aDataSort"));var g=b.mData,j=S(g),i=
b.mRender?S(b.mRender):null,c=function(a){return"string"===typeof a&&-1!==a.indexOf("@")};b._bAttrSrc=h.isPlainObject(g)&&(c(g.sort)||c(g.type)||c(g.filter));b._setter=null;b.fnGetData=function(a,b,c){var d=j(a,b,k,c);return i&&b?i(d,b,a,c):d};b.fnSetData=function(a,b,c){return N(g)(a,b,c)};"number"!==typeof g&&(a._rowReadObject=!0);a.oFeatures.bSort||(b.bSortable=!1,e.addClass(d.sSortableNone));a=-1!==h.inArray("asc",b.asSorting);c=-1!==h.inArray("desc",b.asSorting);!b.bSortable||!a&&!c?(b.sSortingClass=
d.sSortableNone,b.sSortingClassJUI=""):a&&!c?(b.sSortingClass=d.sSortableAsc,b.sSortingClassJUI=d.sSortJUIAscAllowed):!a&&c?(b.sSortingClass=d.sSortableDesc,b.sSortingClassJUI=d.sSortJUIDescAllowed):(b.sSortingClass=d.sSortable,b.sSortingClassJUI=d.sSortJUI)}function aa(a){if(!1!==a.oFeatures.bAutoWidth){var b=a.aoColumns;Ha(a);for(var c=0,d=b.length;c<d;c++)b[c].nTh.style.width=b[c].sWidth}b=a.oScroll;(""!==b.sY||""!==b.sX)&&ma(a);t(a,null,"column-sizing",[a])}function ba(a,b){var c=na(a,"bVisible");
return"number"===typeof c[b]?c[b]:null}function ca(a,b){var c=na(a,"bVisible"),c=h.inArray(b,c);return-1!==c?c:null}function W(a){var b=0;h.each(a.aoColumns,function(a,d){d.bVisible&&"none"!==h(d.nTh).css("display")&&b++});return b}function na(a,b){var c=[];h.map(a.aoColumns,function(a,e){a[b]&&c.push(e)});return c}function Ia(a){var b=a.aoColumns,c=a.aoData,d=l.ext.type.detect,e,f,g,j,i,h,m,q,s;e=0;for(f=b.length;e<f;e++)if(m=b[e],s=[],!m.sType&&m._sManualType)m.sType=m._sManualType;else if(!m.sType){g=
0;for(j=d.length;g<j;g++){i=0;for(h=c.length;i<h;i++){s[i]===k&&(s[i]=B(a,i,e,"type"));q=d[g](s[i],a);if(!q&&g!==d.length-1)break;if("html"===q)break}if(q){m.sType=q;break}}m.sType||(m.sType="string")}}function kb(a,b,c,d){var e,f,g,j,i,n,m=a.aoColumns;if(b)for(e=b.length-1;0<=e;e--){n=b[e];var q=n.targets!==k?n.targets:n.aTargets;Array.isArray(q)||(q=[q]);f=0;for(g=q.length;f<g;f++)if("number"===typeof q[f]&&0<=q[f]){for(;m.length<=q[f];)Ga(a);d(q[f],n)}else if("number"===typeof q[f]&&0>q[f])d(m.length+
q[f],n);else if("string"===typeof q[f]){j=0;for(i=m.length;j<i;j++)("_all"==q[f]||h(m[j].nTh).hasClass(q[f]))&&d(j,n)}}if(c){e=0;for(a=c.length;e<a;e++)d(e,c[e])}}function O(a,b,c,d){var e=a.aoData.length,f=h.extend(!0,{},l.models.oRow,{src:c?"dom":"data",idx:e});f._aData=b;a.aoData.push(f);for(var g=a.aoColumns,j=0,i=g.length;j<i;j++)g[j].sType=null;a.aiDisplayMaster.push(e);b=a.rowIdFn(b);b!==k&&(a.aIds[b]=f);(c||!a.oFeatures.bDeferRender)&&Ja(a,e,c,d);return e}function oa(a,b){var c;b instanceof
h||(b=h(b));return b.map(function(b,e){c=Ka(a,e);return O(a,c.data,e,c.cells)})}function B(a,b,c,d){var e=a.iDraw,f=a.aoColumns[c],g=a.aoData[b]._aData,j=f.sDefaultContent,i=f.fnGetData(g,d,{settings:a,row:b,col:c});if(i===k)return a.iDrawError!=e&&null===j&&(K(a,0,"Requested unknown parameter "+("function"==typeof f.mData?"{function}":"'"+f.mData+"'")+" for row "+b+", column "+c,4),a.iDrawError=e),j;if((i===g||null===i)&&null!==j&&d!==k)i=j;else if("function"===typeof i)return i.call(g);return null===
i&&"display"==d?"":i}function lb(a,b,c,d){a.aoColumns[c].fnSetData(a.aoData[b]._aData,d,{settings:a,row:b,col:c})}function La(a){return h.map(a.match(/(\\.|[^\.])+/g)||[""],function(a){return a.replace(/\\\./g,".")})}function S(a){if(h.isPlainObject(a)){var b={};h.each(a,function(a,c){c&&(b[a]=S(c))});return function(a,c,f,g){var j=b[c]||b._;return j!==k?j(a,c,f,g):a}}if(null===a)return function(a){return a};if("function"===typeof a)return function(b,c,f,g){return a(b,c,f,g)};if("string"===typeof a&&
(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var c=function(a,b,f){var g,j;if(""!==f){j=La(f);for(var i=0,h=j.length;i<h;i++){f=j[i].match(da);g=j[i].match(X);if(f){j[i]=j[i].replace(da,"");""!==j[i]&&(a=a[j[i]]);g=[];j.splice(0,i+1);j=j.join(".");if(Array.isArray(a)){i=0;for(h=a.length;i<h;i++)g.push(c(a[i],b,j))}a=f[0].substring(1,f[0].length-1);a=""===a?g:g.join(a);break}else if(g){j[i]=j[i].replace(X,"");a=a[j[i]]();continue}if(null===a||a[j[i]]===k)return k;a=a[j[i]]}}return a};
return function(b,e){return c(b,e,a)}}return function(b){return b[a]}}function N(a){if(h.isPlainObject(a))return N(a._);if(null===a)return function(){};if("function"===typeof a)return function(b,d,e){a(b,"set",d,e)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var b=function(a,d,e){var e=La(e),f;f=e[e.length-1];for(var g,j,i=0,h=e.length-1;i<h;i++){if("__proto__"===e[i]||"constructor"===e[i])throw Error("Cannot set prototype values");g=e[i].match(da);j=
e[i].match(X);if(g){e[i]=e[i].replace(da,"");a[e[i]]=[];f=e.slice();f.splice(0,i+1);g=f.join(".");if(Array.isArray(d)){j=0;for(h=d.length;j<h;j++)f={},b(f,d[j],g),a[e[i]].push(f)}else a[e[i]]=d;return}j&&(e[i]=e[i].replace(X,""),a=a[e[i]](d));if(null===a[e[i]]||a[e[i]]===k)a[e[i]]={};a=a[e[i]]}if(f.match(X))a[f.replace(X,"")](d);else a[f.replace(da,"")]=d};return function(c,d){return b(c,d,a)}}return function(b,d){b[a]=d}}function Ma(a){return C(a.aoData,"_aData")}function pa(a){a.aoData.length=0;
a.aiDisplayMaster.length=0;a.aiDisplay.length=0;a.aIds={}}function qa(a,b,c){for(var d=-1,e=0,f=a.length;e<f;e++)a[e]==b?d=e:a[e]>b&&a[e]--; -1!=d&&c===k&&a.splice(d,1)}function ea(a,b,c,d){var e=a.aoData[b],f,g=function(c,d){for(;c.childNodes.length;)c.removeChild(c.firstChild);c.innerHTML=B(a,b,d,"display")};if("dom"===c||(!c||"auto"===c)&&"dom"===e.src)e._aData=Ka(a,e,d,d===k?k:e._aData).data;else{var j=e.anCells;if(j)if(d!==k)g(j[d],d);else{c=0;for(f=j.length;c<f;c++)g(j[c],c)}}e._aSortData=null;
e._aFilterData=null;g=a.aoColumns;if(d!==k)g[d].sType=null;else{c=0;for(f=g.length;c<f;c++)g[c].sType=null;Na(a,e)}}function Ka(a,b,c,d){var e=[],f=b.firstChild,g,j,i=0,h,m=a.aoColumns,q=a._rowReadObject,d=d!==k?d:q?{}:[],s=function(a,b){if("string"===typeof a){var c=a.indexOf("@");-1!==c&&(c=a.substring(c+1),N(a)(d,b.getAttribute(c)))}},G=function(a){if(c===k||c===i)j=m[i],h=a.innerHTML.trim(),j&&j._bAttrSrc?(N(j.mData._)(d,h),s(j.mData.sort,a),s(j.mData.type,a),s(j.mData.filter,a)):q?(j._setter||
(j._setter=N(j.mData)),j._setter(d,h)):d[i]=h;i++};if(f)for(;f;){g=f.nodeName.toUpperCase();if("TD"==g||"TH"==g)G(f),e.push(f);f=f.nextSibling}else{e=b.anCells;f=0;for(g=e.length;f<g;f++)G(e[f])}if(b=b.firstChild?b:b.nTr)(b=b.getAttribute("id"))&&N(a.rowId)(d,b);return{data:d,cells:e}}function Ja(a,b,c,d){var e=a.aoData[b],f=e._aData,g=[],j,i,n,m,q;if(null===e.nTr){j=c||H.createElement("tr");e.nTr=j;e.anCells=g;j._DT_RowIndex=b;Na(a,e);n=0;for(m=a.aoColumns.length;n<m;n++){i=a.aoColumns[n];e=(q=c?
!1:!0)?H.createElement(i.sCellType):d[n];e._DT_CellIndex={row:b,column:n};g.push(e);if(q||(i.mRender||i.mData!==n)&&(!h.isPlainObject(i.mData)||i.mData._!==n+".display"))e.innerHTML=B(a,b,n,"display");i.sClass&&(e.className+=" "+i.sClass);i.bVisible&&!c?j.appendChild(e):!i.bVisible&&c&&e.parentNode.removeChild(e);i.fnCreatedCell&&i.fnCreatedCell.call(a.oInstance,e,B(a,b,n),f,b,n)}t(a,"aoRowCreatedCallback",null,[j,f,b,g])}}function Na(a,b){var c=b.nTr,d=b._aData;if(c){var e=a.rowIdFn(d);e&&(c.id=
e);d.DT_RowClass&&(e=d.DT_RowClass.split(" "),b.__rowc=b.__rowc?ra(b.__rowc.concat(e)):e,h(c).removeClass(b.__rowc.join(" ")).addClass(d.DT_RowClass));d.DT_RowAttr&&h(c).attr(d.DT_RowAttr);d.DT_RowData&&h(c).data(d.DT_RowData)}}function mb(a){var b,c,d,e,f,g=a.nTHead,j=a.nTFoot,i=0===h("th, td",g).length,n=a.oClasses,m=a.aoColumns;i&&(e=h("<tr/>").appendTo(g));b=0;for(c=m.length;b<c;b++)f=m[b],d=h(f.nTh).addClass(f.sClass),i&&d.appendTo(e),a.oFeatures.bSort&&(d.addClass(f.sSortingClass),!1!==f.bSortable&&
(d.attr("tabindex",a.iTabIndex).attr("aria-controls",a.sTableId),Oa(a,f.nTh,b))),f.sTitle!=d[0].innerHTML&&d.html(f.sTitle),Pa(a,"header")(a,d,f,n);i&&fa(a.aoHeader,g);h(g).children("tr").attr("role","row");h(g).children("tr").children("th, td").addClass(n.sHeaderTH);h(j).children("tr").children("th, td").addClass(n.sFooterTH);if(null!==j){a=a.aoFooter[0];b=0;for(c=a.length;b<c;b++)f=m[b],f.nTf=a[b].cell,f.sClass&&h(f.nTf).addClass(f.sClass)}}function ga(a,b,c){var d,e,f,g=[],j=[],i=a.aoColumns.length,
n;if(b){c===k&&(c=!1);d=0;for(e=b.length;d<e;d++){g[d]=b[d].slice();g[d].nTr=b[d].nTr;for(f=i-1;0<=f;f--)!a.aoColumns[f].bVisible&&!c&&g[d].splice(f,1);j.push([])}d=0;for(e=g.length;d<e;d++){if(a=g[d].nTr)for(;f=a.firstChild;)a.removeChild(f);f=0;for(b=g[d].length;f<b;f++)if(n=i=1,j[d][f]===k){a.appendChild(g[d][f].cell);for(j[d][f]=1;g[d+i]!==k&&g[d][f].cell==g[d+i][f].cell;)j[d+i][f]=1,i++;for(;g[d][f+n]!==k&&g[d][f].cell==g[d][f+n].cell;){for(c=0;c<i;c++)j[d+c][f+n]=1;n++}h(g[d][f].cell).attr("rowspan",
i).attr("colspan",n)}}}}function P(a){var b=t(a,"aoPreDrawCallback","preDraw",[a]);if(-1!==h.inArray(!1,b))D(a,!1);else{var b=[],c=0,d=a.asStripeClasses,e=d.length,f=a.oLanguage,g=a.iInitDisplayStart,j="ssp"==y(a),i=a.aiDisplay;a.bDrawing=!0;g!==k&&-1!==g&&(a._iDisplayStart=j?g:g>=a.fnRecordsDisplay()?0:g,a.iInitDisplayStart=-1);var g=a._iDisplayStart,n=a.fnDisplayEnd();if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,D(a,!1);else if(j){if(!a.bDestroying&&!nb(a))return}else a.iDraw++;if(0!==i.length){f=
j?a.aoData.length:n;for(j=j?0:g;j<f;j++){var m=i[j],q=a.aoData[m];null===q.nTr&&Ja(a,m);var s=q.nTr;if(0!==e){var G=d[c%e];q._sRowStripe!=G&&(h(s).removeClass(q._sRowStripe).addClass(G),q._sRowStripe=G)}t(a,"aoRowCallback",null,[s,q._aData,c,j,m]);b.push(s);c++}}else c=f.sZeroRecords,1==a.iDraw&&"ajax"==y(a)?c=f.sLoadingRecords:f.sEmptyTable&&0===a.fnRecordsTotal()&&(c=f.sEmptyTable),b[0]=h("<tr/>",{"class":e?d[0]:""}).append(h("<td />",{valign:"top",colSpan:W(a),"class":a.oClasses.sRowEmpty}).html(c))[0];
t(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],Ma(a),g,n,i]);t(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0],Ma(a),g,n,i]);d=h(a.nTBody);d.children().detach();d.append(h(b));t(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=!1}}function T(a,b){var c=a.oFeatures,d=c.bFilter;c.bSort&&ob(a);d?ha(a,a.oPreviousSearch):a.aiDisplay=a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;P(a);a._drawHold=!1}function pb(a){var b=a.oClasses,
c=h(a.nTable),c=h("<div/>").insertBefore(c),d=a.oFeatures,e=h("<div/>",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});a.nHolding=c[0];a.nTableWrapper=e[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),g,j,i,n,m,q,k=0;k<f.length;k++){g=null;j=f[k];if("<"==j){i=h("<div/>")[0];n=f[k+1];if("'"==n||'"'==n){m="";for(q=2;f[k+q]!=n;)m+=f[k+q],q++;"H"==m?m=b.sJUIHeader:"F"==m&&(m=b.sJUIFooter);-1!=m.indexOf(".")?(n=m.split("."),i.id=n[0].substr(1,n[0].length-
1),i.className=n[1]):"#"==m.charAt(0)?i.id=m.substr(1,m.length-1):i.className=m;k+=q}e.append(i);e=h(i)}else if(">"==j)e=e.parent();else if("l"==j&&d.bPaginate&&d.bLengthChange)g=qb(a);else if("f"==j&&d.bFilter)g=rb(a);else if("r"==j&&d.bProcessing)g=sb(a);else if("t"==j)g=tb(a);else if("i"==j&&d.bInfo)g=ub(a);else if("p"==j&&d.bPaginate)g=vb(a);else if(0!==l.ext.feature.length){i=l.ext.feature;q=0;for(n=i.length;q<n;q++)if(j==i[q].cFeature){g=i[q].fnInit(a);break}}g&&(i=a.aanFeatures,i[j]||(i[j]=
[]),i[j].push(g),e.append(g))}c.replaceWith(e);a.nHolding=null}function fa(a,b){var c=h(b).children("tr"),d,e,f,g,j,i,n,m,q,k;a.splice(0,a.length);f=0;for(i=c.length;f<i;f++)a.push([]);f=0;for(i=c.length;f<i;f++){d=c[f];for(e=d.firstChild;e;){if("TD"==e.nodeName.toUpperCase()||"TH"==e.nodeName.toUpperCase()){m=1*e.getAttribute("colspan");q=1*e.getAttribute("rowspan");m=!m||0===m||1===m?1:m;q=!q||0===q||1===q?1:q;g=0;for(j=a[f];j[g];)g++;n=g;k=1===m?!0:!1;for(j=0;j<m;j++)for(g=0;g<q;g++)a[f+g][n+j]=
{cell:e,unique:k},a[f+g].nTr=d}e=e.nextSibling}}}function sa(a,b,c){var d=[];c||(c=a.aoHeader,b&&(c=[],fa(c,b)));for(var b=0,e=c.length;b<e;b++)for(var f=0,g=c[b].length;f<g;f++)if(c[b][f].unique&&(!d[f]||!a.bSortCellsTop))d[f]=c[b][f].cell;return d}function ta(a,b,c){t(a,"aoServerParams","serverParams",[b]);if(b&&Array.isArray(b)){var d={},e=/(.*?)\[\]$/;h.each(b,function(a,b){var c=b.name.match(e);c?(c=c[0],d[c]||(d[c]=[]),d[c].push(b.value)):d[b.name]=b.value});b=d}var f,g=a.ajax,j=a.oInstance,
i=function(b){t(a,null,"xhr",[a,b,a.jqXHR]);c(b)};if(h.isPlainObject(g)&&g.data){f=g.data;var n="function"===typeof f?f(b,a):f,b="function"===typeof f&&n?n:h.extend(!0,b,n);delete g.data}n={data:b,success:function(b){var c=b.error||b.sError;c&&K(a,0,c);a.json=b;i(b)},dataType:"json",cache:!1,type:a.sServerMethod,error:function(b,c){var d=t(a,null,"xhr",[a,null,a.jqXHR]);-1===h.inArray(!0,d)&&("parsererror"==c?K(a,0,"Invalid JSON response",1):4===b.readyState&&K(a,0,"Ajax error",7));D(a,!1)}};a.oAjaxData=
b;t(a,null,"preXhr",[a,b]);a.fnServerData?a.fnServerData.call(j,a.sAjaxSource,h.map(b,function(a,b){return{name:b,value:a}}),i,a):a.sAjaxSource||"string"===typeof g?a.jqXHR=h.ajax(h.extend(n,{url:g||a.sAjaxSource})):"function"===typeof g?a.jqXHR=g.call(j,b,i,a):(a.jqXHR=h.ajax(h.extend(n,g)),g.data=f)}function nb(a){return a.bAjaxDataGet?(a.iDraw++,D(a,!0),ta(a,wb(a),function(b){xb(a,b)}),!1):!0}function wb(a){var b=a.aoColumns,c=b.length,d=a.oFeatures,e=a.oPreviousSearch,f=a.aoPreSearchCols,g,j=
[],i,n,m,k=Y(a);g=a._iDisplayStart;i=!1!==d.bPaginate?a._iDisplayLength:-1;var s=function(a,b){j.push({name:a,value:b})};s("sEcho",a.iDraw);s("iColumns",c);s("sColumns",C(b,"sName").join(","));s("iDisplayStart",g);s("iDisplayLength",i);var G={draw:a.iDraw,columns:[],order:[],start:g,length:i,search:{value:e.sSearch,regex:e.bRegex}};for(g=0;g<c;g++)n=b[g],m=f[g],i="function"==typeof n.mData?"function":n.mData,G.columns.push({data:i,name:n.sName,searchable:n.bSearchable,orderable:n.bSortable,search:{value:m.sSearch,
regex:m.bRegex}}),s("mDataProp_"+g,i),d.bFilter&&(s("sSearch_"+g,m.sSearch),s("bRegex_"+g,m.bRegex),s("bSearchable_"+g,n.bSearchable)),d.bSort&&s("bSortable_"+g,n.bSortable);d.bFilter&&(s("sSearch",e.sSearch),s("bRegex",e.bRegex));d.bSort&&(h.each(k,function(a,b){G.order.push({column:b.col,dir:b.dir});s("iSortCol_"+a,b.col);s("sSortDir_"+a,b.dir)}),s("iSortingCols",k.length));b=l.ext.legacy.ajax;return null===b?a.sAjaxSource?j:G:b?j:G}function xb(a,b){var c=ua(a,b),d=b.sEcho!==k?b.sEcho:b.draw,e=
b.iTotalRecords!==k?b.iTotalRecords:b.recordsTotal,f=b.iTotalDisplayRecords!==k?b.iTotalDisplayRecords:b.recordsFiltered;if(d!==k){if(1*d<a.iDraw)return;a.iDraw=1*d}pa(a);a._iRecordsTotal=parseInt(e,10);a._iRecordsDisplay=parseInt(f,10);d=0;for(e=c.length;d<e;d++)O(a,c[d]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=!1;P(a);a._bInitComplete||va(a,b);a.bAjaxDataGet=!0;D(a,!1)}function ua(a,b){var c=h.isPlainObject(a.ajax)&&a.ajax.dataSrc!==k?a.ajax.dataSrc:a.sAjaxDataProp;return"data"===c?
b.aaData||b[c]:""!==c?S(c)(b):b}function rb(a){var b=a.oClasses,c=a.sTableId,d=a.oLanguage,e=a.oPreviousSearch,f=a.aanFeatures,g='<input type="search" class="'+b.sFilterInput+'"/>',j=d.sSearch,j=j.match(/_INPUT_/)?j.replace("_INPUT_",g):j+g,b=h("<div/>",{id:!f.f?c+"_filter":null,"class":b.sFilter}).append(h("<label/>").append(j)),i=function(){var b=!this.value?"":this.value;b!=e.sSearch&&(ha(a,{sSearch:b,bRegex:e.bRegex,bSmart:e.bSmart,bCaseInsensitive:e.bCaseInsensitive}),a._iDisplayStart=0,P(a))},
f=null!==a.searchDelay?a.searchDelay:"ssp"===y(a)?400:0,n=h("input",b).val(e.sSearch).attr("placeholder",d.sSearchPlaceholder).on("keyup.DT search.DT input.DT paste.DT cut.DT",f?Qa(i,f):i).on("mouseup",function(){setTimeout(function(){i.call(n[0])},10)}).on("keypress.DT",function(a){if(13==a.keyCode)return!1}).attr("aria-controls",c);h(a.nTable).on("search.dt.DT",function(b,c){if(a===c)try{n[0]!==H.activeElement&&n.val(e.sSearch)}catch(d){}});return b[0]}function ha(a,b,c){var d=a.oPreviousSearch,
e=a.aoPreSearchCols,f=function(a){d.sSearch=a.sSearch;d.bRegex=a.bRegex;d.bSmart=a.bSmart;d.bCaseInsensitive=a.bCaseInsensitive};Ia(a);if("ssp"!=y(a)){yb(a,b.sSearch,c,b.bEscapeRegex!==k?!b.bEscapeRegex:b.bRegex,b.bSmart,b.bCaseInsensitive);f(b);for(b=0;b<e.length;b++)zb(a,e[b].sSearch,b,e[b].bEscapeRegex!==k?!e[b].bEscapeRegex:e[b].bRegex,e[b].bSmart,e[b].bCaseInsensitive);Ab(a)}else f(b);a.bFiltered=!0;t(a,null,"search",[a])}function Ab(a){for(var b=l.ext.search,c=a.aiDisplay,d,e,f=0,g=b.length;f<
g;f++){for(var j=[],i=0,n=c.length;i<n;i++)e=c[i],d=a.aoData[e],b[f](a,d._aFilterData,e,d._aData,i)&&j.push(e);c.length=0;h.merge(c,j)}}function zb(a,b,c,d,e,f){if(""!==b){for(var g=[],j=a.aiDisplay,d=Ra(b,d,e,f),e=0;e<j.length;e++)b=a.aoData[j[e]]._aFilterData[c],d.test(b)&&g.push(j[e]);a.aiDisplay=g}}function yb(a,b,c,d,e,f){var e=Ra(b,d,e,f),g=a.oPreviousSearch.sSearch,j=a.aiDisplayMaster,i,f=[];0!==l.ext.search.length&&(c=!0);i=Bb(a);if(0>=b.length)a.aiDisplay=j.slice();else{if(i||c||d||g.length>
b.length||0!==b.indexOf(g)||a.bSorted)a.aiDisplay=j.slice();b=a.aiDisplay;for(c=0;c<b.length;c++)e.test(a.aoData[b[c]]._sFilterRow)&&f.push(b[c]);a.aiDisplay=f}}function Ra(a,b,c,d){a=b?a:Sa(a);c&&(a="^(?=.*?"+h.map(a.match(/"[^"]+"|[^ ]+/g)||[""],function(a){if('"'===a.charAt(0))var b=a.match(/^"(.*)"$/),a=b?b[1]:a;return a.replace('"',"")}).join(")(?=.*?")+").*$");return RegExp(a,d?"i":"")}function Bb(a){var b=a.aoColumns,c,d,e,f,g,j,i,h,m=l.ext.type.search;c=!1;d=0;for(f=a.aoData.length;d<f;d++)if(h=
a.aoData[d],!h._aFilterData){j=[];e=0;for(g=b.length;e<g;e++)c=b[e],c.bSearchable?(i=B(a,d,e,"filter"),m[c.sType]&&(i=m[c.sType](i)),null===i&&(i=""),"string"!==typeof i&&i.toString&&(i=i.toString())):i="",i.indexOf&&-1!==i.indexOf("&")&&(wa.innerHTML=i,i=Zb?wa.textContent:wa.innerText),i.replace&&(i=i.replace(/[\r\n\u2028]/g,"")),j.push(i);h._aFilterData=j;h._sFilterRow=j.join(" ");c=!0}return c}function Cb(a){return{search:a.sSearch,smart:a.bSmart,regex:a.bRegex,caseInsensitive:a.bCaseInsensitive}}
function Db(a){return{sSearch:a.search,bSmart:a.smart,bRegex:a.regex,bCaseInsensitive:a.caseInsensitive}}function ub(a){var b=a.sTableId,c=a.aanFeatures.i,d=h("<div/>",{"class":a.oClasses.sInfo,id:!c?b+"_info":null});c||(a.aoDrawCallback.push({fn:Eb,sName:"information"}),d.attr("role","status").attr("aria-live","polite"),h(a.nTable).attr("aria-describedby",b+"_info"));return d[0]}function Eb(a){var b=a.aanFeatures.i;if(0!==b.length){var c=a.oLanguage,d=a._iDisplayStart+1,e=a.fnDisplayEnd(),f=a.fnRecordsTotal(),
g=a.fnRecordsDisplay(),j=g?c.sInfo:c.sInfoEmpty;g!==f&&(j+=" "+c.sInfoFiltered);j+=c.sInfoPostFix;j=Fb(a,j);c=c.fnInfoCallback;null!==c&&(j=c.call(a.oInstance,a,d,e,f,g,j));h(b).html(j)}}function Fb(a,b){var c=a.fnFormatNumber,d=a._iDisplayStart+1,e=a._iDisplayLength,f=a.fnRecordsDisplay(),g=-1===e;return b.replace(/_START_/g,c.call(a,d)).replace(/_END_/g,c.call(a,a.fnDisplayEnd())).replace(/_MAX_/g,c.call(a,a.fnRecordsTotal())).replace(/_TOTAL_/g,c.call(a,f)).replace(/_PAGE_/g,c.call(a,g?1:Math.ceil(d/
e))).replace(/_PAGES_/g,c.call(a,g?1:Math.ceil(f/e)))}function ia(a){var b,c,d=a.iInitDisplayStart,e=a.aoColumns,f;c=a.oFeatures;var g=a.bDeferLoading;if(a.bInitialised){pb(a);mb(a);ga(a,a.aoHeader);ga(a,a.aoFooter);D(a,!0);c.bAutoWidth&&Ha(a);b=0;for(c=e.length;b<c;b++)f=e[b],f.sWidth&&(f.nTh.style.width=w(f.sWidth));t(a,null,"preInit",[a]);T(a);e=y(a);if("ssp"!=e||g)"ajax"==e?ta(a,[],function(c){var f=ua(a,c);for(b=0;b<f.length;b++)O(a,f[b]);a.iInitDisplayStart=d;T(a);D(a,!1);va(a,c)},a):(D(a,!1),
va(a))}else setTimeout(function(){ia(a)},200)}function va(a,b){a._bInitComplete=!0;(b||a.oInit.aaData)&&aa(a);t(a,null,"plugin-init",[a,b]);t(a,"aoInitComplete","init",[a,b])}function Ta(a,b){var c=parseInt(b,10);a._iDisplayLength=c;Ua(a);t(a,null,"length",[a,c])}function qb(a){for(var b=a.oClasses,c=a.sTableId,d=a.aLengthMenu,e=Array.isArray(d[0]),f=e?d[0]:d,d=e?d[1]:d,e=h("<select/>",{name:c+"_length","aria-controls":c,"class":b.sLengthSelect}),g=0,j=f.length;g<j;g++)e[0][g]=new Option("number"===
typeof d[g]?a.fnFormatNumber(d[g]):d[g],f[g]);var i=h("<div><label/></div>").addClass(b.sLength);a.aanFeatures.l||(i[0].id=c+"_length");i.children().append(a.oLanguage.sLengthMenu.replace("_MENU_",e[0].outerHTML));h("select",i).val(a._iDisplayLength).on("change.DT",function(){Ta(a,h(this).val());P(a)});h(a.nTable).on("length.dt.DT",function(b,c,d){a===c&&h("select",i).val(d)});return i[0]}function vb(a){var b=a.sPaginationType,c=l.ext.pager[b],d="function"===typeof c,e=function(a){P(a)},b=h("<div/>").addClass(a.oClasses.sPaging+
b)[0],f=a.aanFeatures;d||c.fnInit(a,b,e);f.p||(b.id=a.sTableId+"_paginate",a.aoDrawCallback.push({fn:function(a){if(d){var b=a._iDisplayStart,i=a._iDisplayLength,h=a.fnRecordsDisplay(),m=-1===i,b=m?0:Math.ceil(b/i),i=m?1:Math.ceil(h/i),h=c(b,i),k,m=0;for(k=f.p.length;m<k;m++)Pa(a,"pageButton")(a,f.p[m],m,h,b,i)}else c.fnUpdate(a,e)},sName:"pagination"}));return b}function Va(a,b,c){var d=a._iDisplayStart,e=a._iDisplayLength,f=a.fnRecordsDisplay();0===f||-1===e?d=0:"number"===typeof b?(d=b*e,d>f&&
(d=0)):"first"==b?d=0:"previous"==b?(d=0<=e?d-e:0,0>d&&(d=0)):"next"==b?d+e<f&&(d+=e):"last"==b?d=Math.floor((f-1)/e)*e:K(a,0,"Unknown paging action: "+b,5);b=a._iDisplayStart!==d;a._iDisplayStart=d;b&&(t(a,null,"page",[a]),c&&P(a));return b}function sb(a){return h("<div/>",{id:!a.aanFeatures.r?a.sTableId+"_processing":null,"class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function D(a,b){a.oFeatures.bProcessing&&h(a.aanFeatures.r).css("display",b?"block":"none");
t(a,null,"processing",[a,b])}function tb(a){var b=h(a.nTable);b.attr("role","grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var d=c.sX,e=c.sY,f=a.oClasses,g=b.children("caption"),j=g.length?g[0]._captionSide:null,i=h(b[0].cloneNode(!1)),n=h(b[0].cloneNode(!1)),m=b.children("tfoot");m.length||(m=null);i=h("<div/>",{"class":f.sScrollWrapper}).append(h("<div/>",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,width:d?!d?null:w(d):"100%"}).append(h("<div/>",
{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box",width:c.sXInner||"100%"}).append(i.removeAttr("id").css("margin-left",0).append("top"===j?g:null).append(b.children("thead"))))).append(h("<div/>",{"class":f.sScrollBody}).css({position:"relative",overflow:"auto",width:!d?null:w(d)}).append(b));m&&i.append(h("<div/>",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:d?!d?null:w(d):"100%"}).append(h("<div/>",{"class":f.sScrollFootInner}).append(n.removeAttr("id").css("margin-left",
0).append("bottom"===j?g:null).append(b.children("tfoot")))));var b=i.children(),k=b[0],f=b[1],s=m?b[2]:null;if(d)h(f).on("scroll.DT",function(){var a=this.scrollLeft;k.scrollLeft=a;m&&(s.scrollLeft=a)});h(f).css("max-height",e);c.bCollapse||h(f).css("height",e);a.nScrollHead=k;a.nScrollBody=f;a.nScrollFoot=s;a.aoDrawCallback.push({fn:ma,sName:"scrolling"});return i[0]}function ma(a){var b=a.oScroll,c=b.sX,d=b.sXInner,e=b.sY,b=b.iBarWidth,f=h(a.nScrollHead),g=f[0].style,j=f.children("div"),i=j[0].style,
n=j.children("table"),j=a.nScrollBody,m=h(j),q=j.style,s=h(a.nScrollFoot).children("div"),l=s.children("table"),o=h(a.nTHead),p=h(a.nTable),r=p[0],t=r.style,u=a.nTFoot?h(a.nTFoot):null,U=a.oBrowser,V=U.bScrollOversize,$b=C(a.aoColumns,"nTh"),Q,L,R,xa,v=[],x=[],y=[],z=[],A,B=function(a){a=a.style;a.paddingTop="0";a.paddingBottom="0";a.borderTopWidth="0";a.borderBottomWidth="0";a.height=0};L=j.scrollHeight>j.clientHeight;if(a.scrollBarVis!==L&&a.scrollBarVis!==k)a.scrollBarVis=L,aa(a);else{a.scrollBarVis=
L;p.children("thead, tfoot").remove();u&&(R=u.clone().prependTo(p),Q=u.find("tr"),R=R.find("tr"));xa=o.clone().prependTo(p);o=o.find("tr");L=xa.find("tr");xa.find("th, td").removeAttr("tabindex");c||(q.width="100%",f[0].style.width="100%");h.each(sa(a,xa),function(b,c){A=ba(a,b);c.style.width=a.aoColumns[A].sWidth});u&&I(function(a){a.style.width=""},R);f=p.outerWidth();if(""===c){t.width="100%";if(V&&(p.find("tbody").height()>j.offsetHeight||"scroll"==m.css("overflow-y")))t.width=w(p.outerWidth()-
b);f=p.outerWidth()}else""!==d&&(t.width=w(d),f=p.outerWidth());I(B,L);I(function(a){y.push(a.innerHTML);v.push(w(h(a).css("width")))},L);I(function(a,b){if(h.inArray(a,$b)!==-1)a.style.width=v[b]},o);h(L).height(0);u&&(I(B,R),I(function(a){z.push(a.innerHTML);x.push(w(h(a).css("width")))},R),I(function(a,b){a.style.width=x[b]},Q),h(R).height(0));I(function(a,b){a.innerHTML='<div class="dataTables_sizing">'+y[b]+"</div>";a.childNodes[0].style.height="0";a.childNodes[0].style.overflow="hidden";a.style.width=
v[b]},L);u&&I(function(a,b){a.innerHTML='<div class="dataTables_sizing">'+z[b]+"</div>";a.childNodes[0].style.height="0";a.childNodes[0].style.overflow="hidden";a.style.width=x[b]},R);if(p.outerWidth()<f){Q=j.scrollHeight>j.offsetHeight||"scroll"==m.css("overflow-y")?f+b:f;if(V&&(j.scrollHeight>j.offsetHeight||"scroll"==m.css("overflow-y")))t.width=w(Q-b);(""===c||""!==d)&&K(a,1,"Possible column misalignment",6)}else Q="100%";q.width=w(Q);g.width=w(Q);u&&(a.nScrollFoot.style.width=w(Q));!e&&V&&(q.height=
w(r.offsetHeight+b));c=p.outerWidth();n[0].style.width=w(c);i.width=w(c);d=p.height()>j.clientHeight||"scroll"==m.css("overflow-y");e="padding"+(U.bScrollbarLeft?"Left":"Right");i[e]=d?b+"px":"0px";u&&(l[0].style.width=w(c),s[0].style.width=w(c),s[0].style[e]=d?b+"px":"0px");p.children("colgroup").insertBefore(p.children("thead"));m.trigger("scroll");if((a.bSorted||a.bFiltered)&&!a._drawHold)j.scrollTop=0}}function I(a,b,c){for(var d=0,e=0,f=b.length,g,j;e<f;){g=b[e].firstChild;for(j=c?c[e].firstChild:
null;g;)1===g.nodeType&&(c?a(g,j,d):a(g,d),d++),g=g.nextSibling,j=c?j.nextSibling:null;e++}}function Ha(a){var b=a.nTable,c=a.aoColumns,d=a.oScroll,e=d.sY,f=d.sX,g=d.sXInner,j=c.length,i=na(a,"bVisible"),n=h("th",a.nTHead),m=b.getAttribute("width"),k=b.parentNode,s=!1,l,o,p=a.oBrowser,d=p.bScrollOversize;(l=b.style.width)&&-1!==l.indexOf("%")&&(m=l);for(l=0;l<i.length;l++)o=c[i[l]],null!==o.sWidth&&(o.sWidth=Gb(o.sWidthOrig,k),s=!0);if(d||!s&&!f&&!e&&j==W(a)&&j==n.length)for(l=0;l<j;l++)i=ba(a,l),
null!==i&&(c[i].sWidth=w(n.eq(l).width()));else{j=h(b).clone().css("visibility","hidden").removeAttr("id");j.find("tbody tr").remove();var r=h("<tr/>").appendTo(j.find("tbody"));j.find("thead, tfoot").remove();j.append(h(a.nTHead).clone()).append(h(a.nTFoot).clone());j.find("tfoot th, tfoot td").css("width","");n=sa(a,j.find("thead")[0]);for(l=0;l<i.length;l++)o=c[i[l]],n[l].style.width=null!==o.sWidthOrig&&""!==o.sWidthOrig?w(o.sWidthOrig):"",o.sWidthOrig&&f&&h(n[l]).append(h("<div/>").css({width:o.sWidthOrig,
margin:0,padding:0,border:0,height:1}));if(a.aoData.length)for(l=0;l<i.length;l++)s=i[l],o=c[s],h(Hb(a,s)).clone(!1).append(o.sContentPadding).appendTo(r);h("[name]",j).removeAttr("name");o=h("<div/>").css(f||e?{position:"absolute",top:0,left:0,height:1,right:0,overflow:"hidden"}:{}).append(j).appendTo(k);f&&g?j.width(g):f?(j.css("width","auto"),j.removeAttr("width"),j.width()<k.clientWidth&&m&&j.width(k.clientWidth)):e?j.width(k.clientWidth):m&&j.width(m);for(l=e=0;l<i.length;l++)k=h(n[l]),g=k.outerWidth()-
k.width(),k=p.bBounding?Math.ceil(n[l].getBoundingClientRect().width):k.outerWidth(),e+=k,c[i[l]].sWidth=w(k-g);b.style.width=w(e);o.remove()}m&&(b.style.width=w(m));if((m||f)&&!a._reszEvt)b=function(){h(E).on("resize.DT-"+a.sInstance,Qa(function(){aa(a)}))},d?setTimeout(b,1E3):b(),a._reszEvt=!0}function Gb(a,b){if(!a)return 0;var c=h("<div/>").css("width",w(a)).appendTo(b||H.body),d=c[0].offsetWidth;c.remove();return d}function Hb(a,b){var c=Ib(a,b);if(0>c)return null;var d=a.aoData[c];return!d.nTr?
h("<td/>").html(B(a,c,b,"display"))[0]:d.anCells[b]}function Ib(a,b){for(var c,d=-1,e=-1,f=0,g=a.aoData.length;f<g;f++)c=B(a,f,b,"display")+"",c=c.replace(ac,""),c=c.replace(/&nbsp;/g," "),c.length>d&&(d=c.length,e=f);return e}function w(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function Y(a){var b,c,d=[],e=a.aoColumns,f,g,j,i;b=a.aaSortingFixed;c=h.isPlainObject(b);var n=[];f=function(a){a.length&&!Array.isArray(a[0])?n.push(a):h.merge(n,a)};Array.isArray(b)&&
f(b);c&&b.pre&&f(b.pre);f(a.aaSorting);c&&b.post&&f(b.post);for(a=0;a<n.length;a++){i=n[a][0];f=e[i].aDataSort;b=0;for(c=f.length;b<c;b++)g=f[b],j=e[g].sType||"string",n[a]._idx===k&&(n[a]._idx=h.inArray(n[a][1],e[g].asSorting)),d.push({src:i,col:g,dir:n[a][1],index:n[a]._idx,type:j,formatter:l.ext.type.order[j+"-pre"]})}return d}function ob(a){var b,c,d=[],e=l.ext.type.order,f=a.aoData,g=0,j,i=a.aiDisplayMaster,h;Ia(a);h=Y(a);b=0;for(c=h.length;b<c;b++)j=h[b],j.formatter&&g++,Jb(a,j.col);if("ssp"!=
y(a)&&0!==h.length){b=0;for(c=i.length;b<c;b++)d[i[b]]=b;g===h.length?i.sort(function(a,b){var c,e,g,j,i=h.length,k=f[a]._aSortData,l=f[b]._aSortData;for(g=0;g<i;g++)if(j=h[g],c=k[j.col],e=l[j.col],c=c<e?-1:c>e?1:0,0!==c)return"asc"===j.dir?c:-c;c=d[a];e=d[b];return c<e?-1:c>e?1:0}):i.sort(function(a,b){var c,g,j,i,k=h.length,l=f[a]._aSortData,o=f[b]._aSortData;for(j=0;j<k;j++)if(i=h[j],c=l[i.col],g=o[i.col],i=e[i.type+"-"+i.dir]||e["string-"+i.dir],c=i(c,g),0!==c)return c;c=d[a];g=d[b];return c<
g?-1:c>g?1:0})}a.bSorted=!0}function Kb(a){for(var b,c,d=a.aoColumns,e=Y(a),a=a.oLanguage.oAria,f=0,g=d.length;f<g;f++){c=d[f];var j=c.asSorting;b=c.sTitle.replace(/<.*?>/g,"");var i=c.nTh;i.removeAttribute("aria-sort");c.bSortable&&(0<e.length&&e[0].col==f?(i.setAttribute("aria-sort","asc"==e[0].dir?"ascending":"descending"),c=j[e[0].index+1]||j[0]):c=j[0],b+="asc"===c?a.sSortAscending:a.sSortDescending);i.setAttribute("aria-label",b)}}function Wa(a,b,c,d){var e=a.aaSorting,f=a.aoColumns[b].asSorting,
g=function(a,b){var c=a._idx;c===k&&(c=h.inArray(a[1],f));return c+1<f.length?c+1:b?null:0};"number"===typeof e[0]&&(e=a.aaSorting=[e]);c&&a.oFeatures.bSortMulti?(c=h.inArray(b,C(e,"0")),-1!==c?(b=g(e[c],!0),null===b&&1===e.length&&(b=0),null===b?e.splice(c,1):(e[c][1]=f[b],e[c]._idx=b)):(e.push([b,f[0],0]),e[e.length-1]._idx=0)):e.length&&e[0][0]==b?(b=g(e[0]),e.length=1,e[0][1]=f[b],e[0]._idx=b):(e.length=0,e.push([b,f[0]]),e[0]._idx=0);T(a);"function"==typeof d&&d(a)}function Oa(a,b,c,d){var e=
a.aoColumns[c];Xa(b,{},function(b){!1!==e.bSortable&&(a.oFeatures.bProcessing?(D(a,!0),setTimeout(function(){Wa(a,c,b.shiftKey,d);"ssp"!==y(a)&&D(a,!1)},0)):Wa(a,c,b.shiftKey,d))})}function ya(a){var b=a.aLastSort,c=a.oClasses.sSortColumn,d=Y(a),e=a.oFeatures,f,g;if(e.bSort&&e.bSortClasses){e=0;for(f=b.length;e<f;e++)g=b[e].src,h(C(a.aoData,"anCells",g)).removeClass(c+(2>e?e+1:3));e=0;for(f=d.length;e<f;e++)g=d[e].src,h(C(a.aoData,"anCells",g)).addClass(c+(2>e?e+1:3))}a.aLastSort=d}function Jb(a,
b){var c=a.aoColumns[b],d=l.ext.order[c.sSortDataType],e;d&&(e=d.call(a.oInstance,a,b,ca(a,b)));for(var f,g=l.ext.type.order[c.sType+"-pre"],j=0,i=a.aoData.length;j<i;j++)if(c=a.aoData[j],c._aSortData||(c._aSortData=[]),!c._aSortData[b]||d)f=d?e[j]:B(a,j,b,"sort"),c._aSortData[b]=g?g(f):f}function za(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b={time:+new Date,start:a._iDisplayStart,length:a._iDisplayLength,order:h.extend(!0,[],a.aaSorting),search:Cb(a.oPreviousSearch),columns:h.map(a.aoColumns,
function(b,d){return{visible:b.bVisible,search:Cb(a.aoPreSearchCols[d])}})};t(a,"aoStateSaveParams","stateSaveParams",[a,b]);a.oSavedState=b;a.fnStateSaveCallback.call(a.oInstance,a,b)}}function Lb(a,b,c){var d,e,f=a.aoColumns,b=function(b){if(b&&b.time){var g=t(a,"aoStateLoadParams","stateLoadParams",[a,b]);if(-1===h.inArray(!1,g)&&(g=a.iStateDuration,!(0<g&&b.time<+new Date-1E3*g)&&!(b.columns&&f.length!==b.columns.length))){a.oLoadedState=h.extend(!0,{},b);b.start!==k&&(a._iDisplayStart=b.start,
a.iInitDisplayStart=b.start);b.length!==k&&(a._iDisplayLength=b.length);b.order!==k&&(a.aaSorting=[],h.each(b.order,function(b,c){a.aaSorting.push(c[0]>=f.length?[0,c[1]]:c)}));b.search!==k&&h.extend(a.oPreviousSearch,Db(b.search));if(b.columns){d=0;for(e=b.columns.length;d<e;d++)g=b.columns[d],g.visible!==k&&(f[d].bVisible=g.visible),g.search!==k&&h.extend(a.aoPreSearchCols[d],Db(g.search))}t(a,"aoStateLoaded","stateLoaded",[a,b])}}c()};if(a.oFeatures.bStateSave){var g=a.fnStateLoadCallback.call(a.oInstance,
a,b);g!==k&&b(g)}else c()}function Aa(a){var b=l.settings,a=h.inArray(a,C(b,"nTable"));return-1!==a?b[a]:null}function K(a,b,c,d){c="DataTables warning: "+(a?"table id="+a.sTableId+" - ":"")+c;d&&(c+=". For more information about this error, please see http://datatables.net/tn/"+d);if(b)E.console&&console.log&&console.log(c);else if(b=l.ext,b=b.sErrMode||b.errMode,a&&t(a,null,"error",[a,d,c]),"alert"==b)alert(c);else{if("throw"==b)throw Error(c);"function"==typeof b&&b(a,d,c)}}function F(a,b,c,d){Array.isArray(c)?
h.each(c,function(c,d){Array.isArray(d)?F(a,b,d[0],d[1]):F(a,b,d)}):(d===k&&(d=c),b[c]!==k&&(a[d]=b[c]))}function Ya(a,b,c){var d,e;for(e in b)b.hasOwnProperty(e)&&(d=b[e],h.isPlainObject(d)?(h.isPlainObject(a[e])||(a[e]={}),h.extend(!0,a[e],d)):a[e]=c&&"data"!==e&&"aaData"!==e&&Array.isArray(d)?d.slice():d);return a}function Xa(a,b,c){h(a).on("click.DT",b,function(b){h(a).trigger("blur");c(b)}).on("keypress.DT",b,function(a){13===a.which&&(a.preventDefault(),c(a))}).on("selectstart.DT",function(){return!1})}
function z(a,b,c,d){c&&a[b].push({fn:c,sName:d})}function t(a,b,c,d){var e=[];b&&(e=h.map(a[b].slice().reverse(),function(b){return b.fn.apply(a.oInstance,d)}));null!==c&&(b=h.Event(c+".dt"),h(a.nTable).trigger(b,d),e.push(b.result));return e}function Ua(a){var b=a._iDisplayStart,c=a.fnDisplayEnd(),d=a._iDisplayLength;b>=c&&(b=c-d);b-=b%d;if(-1===d||0>b)b=0;a._iDisplayStart=b}function Pa(a,b){var c=a.renderer,d=l.ext.renderer[b];return h.isPlainObject(c)&&c[b]?d[c[b]]||d._:"string"===typeof c?d[c]||
d._:d._}function y(a){return a.oFeatures.bServerSide?"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function ja(a,b){var c=[],c=Mb.numbers_length,d=Math.floor(c/2);b<=c?c=Z(0,b):a<=d?(c=Z(0,c-2),c.push("ellipsis"),c.push(b-1)):(a>=b-1-d?c=Z(b-(c-2),b):(c=Z(a-d+2,a+d-1),c.push("ellipsis"),c.push(b-1)),c.splice(0,0,"ellipsis"),c.splice(0,0,0));c.DT_el="span";return c}function Fa(a){h.each({num:function(b){return Ba(b,a)},"num-fmt":function(b){return Ba(b,a,Za)},"html-num":function(b){return Ba(b,a,Ca)},"html-num-fmt":function(b){return Ba(b,
a,Ca,Za)}},function(b,c){v.type.order[b+a+"-pre"]=c;b.match(/^html\-/)&&(v.type.search[b+a]=v.type.search.html)})}function Nb(a){return function(){var b=[Aa(this[l.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return l.ext.internal[a].apply(this,b)}}var l=function(a){this.$=function(a,b){return this.api(!0).$(a,b)};this._=function(a,b){return this.api(!0).rows(a,b).data()};this.api=function(a){return a?new r(Aa(this[v.iApiIndex])):new r(this)};this.fnAddData=function(a,b){var c=this.api(!0),
d=Array.isArray(a)&&(Array.isArray(a[0])||h.isPlainObject(a[0]))?c.rows.add(a):c.row.add(a);(b===k||b)&&c.draw();return d.flatten().toArray()};this.fnAdjustColumnSizing=function(a){var b=this.api(!0).columns.adjust(),c=b.settings()[0],d=c.oScroll;a===k||a?b.draw(!1):(""!==d.sX||""!==d.sY)&&ma(c)};this.fnClearTable=function(a){var b=this.api(!0).clear();(a===k||a)&&b.draw()};this.fnClose=function(a){this.api(!0).row(a).child.hide()};this.fnDeleteRow=function(a,b,c){var d=this.api(!0),a=d.rows(a),e=
a.settings()[0],h=e.aoData[a[0][0]];a.remove();b&&b.call(this,e,h);(c===k||c)&&d.draw();return h};this.fnDestroy=function(a){this.api(!0).destroy(a)};this.fnDraw=function(a){this.api(!0).draw(a)};this.fnFilter=function(a,b,c,d,e,h){e=this.api(!0);null===b||b===k?e.search(a,c,d,h):e.column(b).search(a,c,d,h);e.draw()};this.fnGetData=function(a,b){var c=this.api(!0);if(a!==k){var d=a.nodeName?a.nodeName.toLowerCase():"";return b!==k||"td"==d||"th"==d?c.cell(a,b).data():c.row(a).data()||null}return c.data().toArray()};
this.fnGetNodes=function(a){var b=this.api(!0);return a!==k?b.row(a).node():b.rows().nodes().flatten().toArray()};this.fnGetPosition=function(a){var b=this.api(!0),c=a.nodeName.toUpperCase();return"TR"==c?b.row(a).index():"TD"==c||"TH"==c?(a=b.cell(a).index(),[a.row,a.columnVisible,a.column]):null};this.fnIsOpen=function(a){return this.api(!0).row(a).child.isShown()};this.fnOpen=function(a,b,c){return this.api(!0).row(a).child(b,c).show().child()[0]};this.fnPageChange=function(a,b){var c=this.api(!0).page(a);
(b===k||b)&&c.draw(!1)};this.fnSetColumnVis=function(a,b,c){a=this.api(!0).column(a).visible(b);(c===k||c)&&a.columns.adjust().draw()};this.fnSettings=function(){return Aa(this[v.iApiIndex])};this.fnSort=function(a){this.api(!0).order(a).draw()};this.fnSortListener=function(a,b,c){this.api(!0).order.listener(a,b,c)};this.fnUpdate=function(a,b,c,d,e){var h=this.api(!0);c===k||null===c?h.row(b).data(a):h.cell(b,c).data(a);(e===k||e)&&h.columns.adjust();(d===k||d)&&h.draw();return 0};this.fnVersionCheck=
v.fnVersionCheck;var b=this,c=a===k,d=this.length;c&&(a={});this.oApi=this.internal=v.internal;for(var e in l.ext.internal)e&&(this[e]=Nb(e));this.each(function(){var e={},g=1<d?Ya(e,a,!0):a,j=0,i,e=this.getAttribute("id"),n=!1,m=l.defaults,q=h(this);if("table"!=this.nodeName.toLowerCase())K(null,0,"Non-table node initialisation ("+this.nodeName+")",2);else{gb(m);hb(m.column);J(m,m,!0);J(m.column,m.column,!0);J(m,h.extend(g,q.data()),!0);var s=l.settings,j=0;for(i=s.length;j<i;j++){var o=s[j];if(o.nTable==
this||o.nTHead&&o.nTHead.parentNode==this||o.nTFoot&&o.nTFoot.parentNode==this){var r=g.bRetrieve!==k?g.bRetrieve:m.bRetrieve;if(c||r)return o.oInstance;if(g.bDestroy!==k?g.bDestroy:m.bDestroy){o.oInstance.fnDestroy();break}else{K(o,0,"Cannot reinitialise DataTable",3);return}}if(o.sTableId==this.id){s.splice(j,1);break}}if(null===e||""===e)this.id=e="DataTables_Table_"+l.ext._unique++;var p=h.extend(!0,{},l.models.oSettings,{sDestroyWidth:q[0].style.width,sInstance:e,sTableId:e});p.nTable=this;p.oApi=
b.internal;p.oInit=g;s.push(p);p.oInstance=1===b.length?b:q.dataTable();gb(g);Ea(g.oLanguage);g.aLengthMenu&&!g.iDisplayLength&&(g.iDisplayLength=Array.isArray(g.aLengthMenu[0])?g.aLengthMenu[0][0]:g.aLengthMenu[0]);g=Ya(h.extend(!0,{},m),g);F(p.oFeatures,g,"bPaginate bLengthChange bFilter bSort bSortMulti bInfo bProcessing bAutoWidth bSortClasses bServerSide bDeferRender".split(" "));F(p,g,["asStripeClasses","ajax","fnServerData","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu",
"sPaginationType","sAjaxSource","sAjaxDataProp","iStateDuration","sDom","bSortCellsTop","iTabIndex","fnStateLoadCallback","fnStateSaveCallback","renderer","searchDelay","rowId",["iCookieDuration","iStateDuration"],["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"]]);F(p.oScroll,g,[["sScrollX","sX"],["sScrollXInner","sXInner"],["sScrollY","sY"],["bScrollCollapse","bCollapse"]]);F(p.oLanguage,g,"fnInfoCallback");z(p,"aoDrawCallback",g.fnDrawCallback,
"user");z(p,"aoServerParams",g.fnServerParams,"user");z(p,"aoStateSaveParams",g.fnStateSaveParams,"user");z(p,"aoStateLoadParams",g.fnStateLoadParams,"user");z(p,"aoStateLoaded",g.fnStateLoaded,"user");z(p,"aoRowCallback",g.fnRowCallback,"user");z(p,"aoRowCreatedCallback",g.fnCreatedRow,"user");z(p,"aoHeaderCallback",g.fnHeaderCallback,"user");z(p,"aoFooterCallback",g.fnFooterCallback,"user");z(p,"aoInitComplete",g.fnInitComplete,"user");z(p,"aoPreDrawCallback",g.fnPreDrawCallback,"user");p.rowIdFn=
S(g.rowId);ib(p);var u=p.oClasses;h.extend(u,l.ext.classes,g.oClasses);q.addClass(u.sTable);p.iInitDisplayStart===k&&(p.iInitDisplayStart=g.iDisplayStart,p._iDisplayStart=g.iDisplayStart);null!==g.iDeferLoading&&(p.bDeferLoading=!0,e=Array.isArray(g.iDeferLoading),p._iRecordsDisplay=e?g.iDeferLoading[0]:g.iDeferLoading,p._iRecordsTotal=e?g.iDeferLoading[1]:g.iDeferLoading);var w=p.oLanguage;h.extend(!0,w,g.oLanguage);w.sUrl?(h.ajax({dataType:"json",url:w.sUrl,success:function(a){Ea(a);J(m.oLanguage,
a);h.extend(true,w,a);t(p,null,"i18n",[p]);ia(p)},error:function(){ia(p)}}),n=!0):t(p,null,"i18n",[p]);null===g.asStripeClasses&&(p.asStripeClasses=[u.sStripeOdd,u.sStripeEven]);var e=p.asStripeClasses,v=q.children("tbody").find("tr").eq(0);-1!==h.inArray(!0,h.map(e,function(a){return v.hasClass(a)}))&&(h("tbody tr",this).removeClass(e.join(" ")),p.asDestroyStripes=e.slice());e=[];s=this.getElementsByTagName("thead");0!==s.length&&(fa(p.aoHeader,s[0]),e=sa(p));if(null===g.aoColumns){s=[];j=0;for(i=
e.length;j<i;j++)s.push(null)}else s=g.aoColumns;j=0;for(i=s.length;j<i;j++)Ga(p,e?e[j]:null);kb(p,g.aoColumnDefs,s,function(a,b){la(p,a,b)});if(v.length){var U=function(a,b){return a.getAttribute("data-"+b)!==null?b:null};h(v[0]).children("th, td").each(function(a,b){var c=p.aoColumns[a];if(c.mData===a){var d=U(b,"sort")||U(b,"order"),e=U(b,"filter")||U(b,"search");if(d!==null||e!==null){c.mData={_:a+".display",sort:d!==null?a+".@data-"+d:k,type:d!==null?a+".@data-"+d:k,filter:e!==null?a+".@data-"+
e:k};la(p,a)}}})}var V=p.oFeatures,e=function(){if(g.aaSorting===k){var a=p.aaSorting;j=0;for(i=a.length;j<i;j++)a[j][1]=p.aoColumns[j].asSorting[0]}ya(p);V.bSort&&z(p,"aoDrawCallback",function(){if(p.bSorted){var a=Y(p),b={};h.each(a,function(a,c){b[c.src]=c.dir});t(p,null,"order",[p,a,b]);Kb(p)}});z(p,"aoDrawCallback",function(){(p.bSorted||y(p)==="ssp"||V.bDeferRender)&&ya(p)},"sc");var a=q.children("caption").each(function(){this._captionSide=h(this).css("caption-side")}),b=q.children("thead");
b.length===0&&(b=h("<thead/>").appendTo(q));p.nTHead=b[0];b=q.children("tbody");b.length===0&&(b=h("<tbody/>").appendTo(q));p.nTBody=b[0];b=q.children("tfoot");if(b.length===0&&a.length>0&&(p.oScroll.sX!==""||p.oScroll.sY!==""))b=h("<tfoot/>").appendTo(q);if(b.length===0||b.children().length===0)q.addClass(u.sNoFooter);else if(b.length>0){p.nTFoot=b[0];fa(p.aoFooter,p.nTFoot)}if(g.aaData)for(j=0;j<g.aaData.length;j++)O(p,g.aaData[j]);else(p.bDeferLoading||y(p)=="dom")&&oa(p,h(p.nTBody).children("tr"));
p.aiDisplay=p.aiDisplayMaster.slice();p.bInitialised=true;n===false&&ia(p)};g.bStateSave?(V.bStateSave=!0,z(p,"aoDrawCallback",za,"state_save"),Lb(p,g,e)):e()}});b=null;return this},v,r,o,u,$a={},Ob=/[\r\n\u2028]/g,Ca=/<.*?>/g,bc=/^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/,cc=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),Za=/['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi,M=function(a){return!a||!0===a||"-"===a?!0:!1},
Pb=function(a){var b=parseInt(a,10);return!isNaN(b)&&isFinite(a)?b:null},Qb=function(a,b){$a[b]||($a[b]=RegExp(Sa(b),"g"));return"string"===typeof a&&"."!==b?a.replace(/\./g,"").replace($a[b],"."):a},ab=function(a,b,c){var d="string"===typeof a;if(M(a))return!0;b&&d&&(a=Qb(a,b));c&&d&&(a=a.replace(Za,""));return!isNaN(parseFloat(a))&&isFinite(a)},Rb=function(a,b,c){return M(a)?!0:!(M(a)||"string"===typeof a)?null:ab(a.replace(Ca,""),b,c)?!0:null},C=function(a,b,c){var d=[],e=0,f=a.length;if(c!==k)for(;e<
f;e++)a[e]&&a[e][b]&&d.push(a[e][b][c]);else for(;e<f;e++)a[e]&&d.push(a[e][b]);return d},ka=function(a,b,c,d){var e=[],f=0,g=b.length;if(d!==k)for(;f<g;f++)a[b[f]][c]&&e.push(a[b[f]][c][d]);else for(;f<g;f++)e.push(a[b[f]][c]);return e},Z=function(a,b){var c=[],d;b===k?(b=0,d=a):(d=b,b=a);for(var e=b;e<d;e++)c.push(e);return c},Sb=function(a){for(var b=[],c=0,d=a.length;c<d;c++)a[c]&&b.push(a[c]);return b},ra=function(a){var b;a:{if(!(2>a.length)){b=a.slice().sort();for(var c=b[0],d=1,e=b.length;d<
e;d++){if(b[d]===c){b=!1;break a}c=b[d]}}b=!0}if(b)return a.slice();b=[];var e=a.length,f,g=0,d=0;a:for(;d<e;d++){c=a[d];for(f=0;f<g;f++)if(b[f]===c)continue a;b.push(c);g++}return b},Tb=function(a,b){if(Array.isArray(b))for(var c=0;c<b.length;c++)Tb(a,b[c]);else a.push(b);return a};Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)});String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
"")});l.util={throttle:function(a,b){var c=b!==k?b:200,d,e;return function(){var b=this,g=+new Date,j=arguments;if(d&&g<d+c){clearTimeout(e);e=setTimeout(function(){d=k;a.apply(b,j)},c)}else{d=g;a.apply(b,j)}}},escapeRegex:function(a){return a.replace(cc,"\\$1")}};var A=function(a,b,c){a[b]!==k&&(a[c]=a[b])},da=/\[.*?\]$/,X=/\(\)$/,Sa=l.util.escapeRegex,wa=h("<div>")[0],Zb=wa.textContent!==k,ac=/<.*?>/g,Qa=l.util.throttle,Ub=[],x=Array.prototype,dc=function(a){var b,c,d=l.settings,e=h.map(d,function(a){return a.nTable});
if(a){if(a.nTable&&a.oApi)return[a];if(a.nodeName&&a.nodeName.toLowerCase()==="table"){b=h.inArray(a,e);return b!==-1?[d[b]]:null}if(a&&typeof a.settings==="function")return a.settings().toArray();typeof a==="string"?c=h(a):a instanceof h&&(c=a)}else return[];if(c)return c.map(function(){b=h.inArray(this,e);return b!==-1?d[b]:null}).toArray()};r=function(a,b){if(!(this instanceof r))return new r(a,b);var c=[],d=function(a){(a=dc(a))&&c.push.apply(c,a)};if(Array.isArray(a))for(var e=0,f=a.length;e<
f;e++)d(a[e]);else d(a);this.context=ra(c);b&&h.merge(this,b);this.selector={rows:null,cols:null,opts:null};r.extend(this,this,Ub)};l.Api=r;h.extend(r.prototype,{any:function(){return this.count()!==0},concat:x.concat,context:[],count:function(){return this.flatten().length},each:function(a){for(var b=0,c=this.length;b<c;b++)a.call(this,this[b],b,this);return this},eq:function(a){var b=this.context;return b.length>a?new r(b[a],this[a]):null},filter:function(a){var b=[];if(x.filter)b=x.filter.call(this,
a,this);else for(var c=0,d=this.length;c<d;c++)a.call(this,this[c],c,this)&&b.push(this[c]);return new r(this.context,b)},flatten:function(){var a=[];return new r(this.context,a.concat.apply(a,this.toArray()))},join:x.join,indexOf:x.indexOf||function(a,b){for(var c=b||0,d=this.length;c<d;c++)if(this[c]===a)return c;return-1},iterator:function(a,b,c,d){var e=[],f,g,j,i,h,m=this.context,l,o,u=this.selector;if(typeof a==="string"){d=c;c=b;b=a;a=false}g=0;for(j=m.length;g<j;g++){var t=new r(m[g]);if(b===
"table"){f=c.call(t,m[g],g);f!==k&&e.push(f)}else if(b==="columns"||b==="rows"){f=c.call(t,m[g],this[g],g);f!==k&&e.push(f)}else if(b==="column"||b==="column-rows"||b==="row"||b==="cell"){o=this[g];b==="column-rows"&&(l=Da(m[g],u.opts));i=0;for(h=o.length;i<h;i++){f=o[i];f=b==="cell"?c.call(t,m[g],f.row,f.column,g,i):c.call(t,m[g],f,g,i,l);f!==k&&e.push(f)}}}if(e.length||d){a=new r(m,a?e.concat.apply([],e):e);b=a.selector;b.rows=u.rows;b.cols=u.cols;b.opts=u.opts;return a}return this},lastIndexOf:x.lastIndexOf||
function(a,b){return this.indexOf.apply(this.toArray.reverse(),arguments)},length:0,map:function(a){var b=[];if(x.map)b=x.map.call(this,a,this);else for(var c=0,d=this.length;c<d;c++)b.push(a.call(this,this[c],c));return new r(this.context,b)},pluck:function(a){return this.map(function(b){return b[a]})},pop:x.pop,push:x.push,reduce:x.reduce||function(a,b){return jb(this,a,b,0,this.length,1)},reduceRight:x.reduceRight||function(a,b){return jb(this,a,b,this.length-1,-1,-1)},reverse:x.reverse,selector:null,
shift:x.shift,slice:function(){return new r(this.context,this)},sort:x.sort,splice:x.splice,toArray:function(){return x.slice.call(this)},to$:function(){return h(this)},toJQuery:function(){return h(this)},unique:function(){return new r(this.context,ra(this))},unshift:x.unshift});r.extend=function(a,b,c){if(c.length&&b&&(b instanceof r||b.__dt_wrapper)){var d,e,f,g=function(a,b,c){return function(){var d=b.apply(a,arguments);r.extend(d,d,c.methodExt);return d}};d=0;for(e=c.length;d<e;d++){f=c[d];b[f.name]=
f.type==="function"?g(a,f.val,f):f.type==="object"?{}:f.val;b[f.name].__dt_wrapper=true;r.extend(a,b[f.name],f.propExt)}}};r.register=o=function(a,b){if(Array.isArray(a))for(var c=0,d=a.length;c<d;c++)r.register(a[c],b);else for(var e=a.split("."),f=Ub,g,j,c=0,d=e.length;c<d;c++){g=(j=e[c].indexOf("()")!==-1)?e[c].replace("()",""):e[c];var i;a:{i=0;for(var k=f.length;i<k;i++)if(f[i].name===g){i=f[i];break a}i=null}if(!i){i={name:g,val:{},methodExt:[],propExt:[],type:"object"};f.push(i)}if(c===d-1){i.val=
b;i.type=typeof b==="function"?"function":h.isPlainObject(b)?"object":"other"}else f=j?i.methodExt:i.propExt}};r.registerPlural=u=function(a,b,c){r.register(a,c);r.register(b,function(){var a=c.apply(this,arguments);return a===this?this:a instanceof r?a.length?Array.isArray(a[0])?new r(a.context,a[0]):a[0]:k:a})};var Vb=function(a,b){if(Array.isArray(a))return h.map(a,function(a){return Vb(a,b)});if(typeof a==="number")return[b[a]];var c=h.map(b,function(a){return a.nTable});return h(c).filter(a).map(function(){var a=
h.inArray(this,c);return b[a]}).toArray()};o("tables()",function(a){return a!==k&&a!==null?new r(Vb(a,this.context)):this});o("table()",function(a){var a=this.tables(a),b=a.context;return b.length?new r(b[0]):a});u("tables().nodes()","table().node()",function(){return this.iterator("table",function(a){return a.nTable},1)});u("tables().body()","table().body()",function(){return this.iterator("table",function(a){return a.nTBody},1)});u("tables().header()","table().header()",function(){return this.iterator("table",
function(a){return a.nTHead},1)});u("tables().footer()","table().footer()",function(){return this.iterator("table",function(a){return a.nTFoot},1)});u("tables().containers()","table().container()",function(){return this.iterator("table",function(a){return a.nTableWrapper},1)});o("draw()",function(a){return this.iterator("table",function(b){if(a==="page")P(b);else{typeof a==="string"&&(a=a==="full-hold"?false:true);T(b,a===false)}})});o("page()",function(a){return a===k?this.page.info().page:this.iterator("table",
function(b){Va(b,a)})});o("page.info()",function(){if(this.context.length===0)return k;var a=this.context[0],b=a._iDisplayStart,c=a.oFeatures.bPaginate?a._iDisplayLength:-1,d=a.fnRecordsDisplay(),e=c===-1;return{page:e?0:Math.floor(b/c),pages:e?1:Math.ceil(d/c),start:b,end:a.fnDisplayEnd(),length:c,recordsTotal:a.fnRecordsTotal(),recordsDisplay:d,serverSide:y(a)==="ssp"}});o("page.len()",function(a){return a===k?this.context.length!==0?this.context[0]._iDisplayLength:k:this.iterator("table",function(b){Ta(b,
a)})});var Wb=function(a,b,c){if(c){var d=new r(a);d.one("draw",function(){c(d.ajax.json())})}if(y(a)=="ssp")T(a,b);else{D(a,true);var e=a.jqXHR;e&&e.readyState!==4&&e.abort();ta(a,[],function(c){pa(a);for(var c=ua(a,c),d=0,e=c.length;d<e;d++)O(a,c[d]);T(a,b);D(a,false)})}};o("ajax.json()",function(){var a=this.context;if(a.length>0)return a[0].json});o("ajax.params()",function(){var a=this.context;if(a.length>0)return a[0].oAjaxData});o("ajax.reload()",function(a,b){return this.iterator("table",
function(c){Wb(c,b===false,a)})});o("ajax.url()",function(a){var b=this.context;if(a===k){if(b.length===0)return k;b=b[0];return b.ajax?h.isPlainObject(b.ajax)?b.ajax.url:b.ajax:b.sAjaxSource}return this.iterator("table",function(b){h.isPlainObject(b.ajax)?b.ajax.url=a:b.ajax=a})});o("ajax.url().load()",function(a,b){return this.iterator("table",function(c){Wb(c,b===false,a)})});var bb=function(a,b,c,d,e){var f=[],g,j,i,h,m,l;i=typeof b;if(!b||i==="string"||i==="function"||b.length===k)b=[b];i=0;
for(h=b.length;i<h;i++){j=b[i]&&b[i].split&&!b[i].match(/[\[\(:]/)?b[i].split(","):[b[i]];m=0;for(l=j.length;m<l;m++)(g=c(typeof j[m]==="string"?j[m].trim():j[m]))&&g.length&&(f=f.concat(g))}a=v.selector[a];if(a.length){i=0;for(h=a.length;i<h;i++)f=a[i](d,e,f)}return ra(f)},cb=function(a){a||(a={});if(a.filter&&a.search===k)a.search=a.filter;return h.extend({search:"none",order:"current",page:"all"},a)},db=function(a){for(var b=0,c=a.length;b<c;b++)if(a[b].length>0){a[0]=a[b];a[0].length=1;a.length=
1;a.context=[a.context[b]];return a}a.length=0;return a},Da=function(a,b){var c,d,e,f=[],g=a.aiDisplay;e=a.aiDisplayMaster;var j=b.search;c=b.order;d=b.page;if(y(a)=="ssp")return j==="removed"?[]:Z(0,e.length);if(d=="current"){c=a._iDisplayStart;for(d=a.fnDisplayEnd();c<d;c++)f.push(g[c])}else if(c=="current"||c=="applied")if(j=="none")f=e.slice();else if(j=="applied")f=g.slice();else{if(j=="removed"){var i={};c=0;for(d=g.length;c<d;c++)i[g[c]]=null;f=h.map(e,function(a){return!i.hasOwnProperty(a)?
a:null})}}else if(c=="index"||c=="original"){c=0;for(d=a.aoData.length;c<d;c++)if(j=="none")f.push(c);else{e=h.inArray(c,g);(e===-1&&j=="removed"||e>=0&&j=="applied")&&f.push(c)}}return f};o("rows()",function(a,b){if(a===k)a="";else if(h.isPlainObject(a)){b=a;a=""}var b=cb(b),c=this.iterator("table",function(c){var e=b,f;return bb("row",a,function(a){var b=Pb(a),i=c.aoData;if(b!==null&&!e)return[b];f||(f=Da(c,e));if(b!==null&&h.inArray(b,f)!==-1)return[b];if(a===null||a===k||a==="")return f;if(typeof a===
"function")return h.map(f,function(b){var c=i[b];return a(b,c._aData,c.nTr)?b:null});if(a.nodeName){var b=a._DT_RowIndex,n=a._DT_CellIndex;if(b!==k)return i[b]&&i[b].nTr===a?[b]:[];if(n)return i[n.row]&&i[n.row].nTr===a.parentNode?[n.row]:[];b=h(a).closest("*[data-dt-row]");return b.length?[b.data("dt-row")]:[]}if(typeof a==="string"&&a.charAt(0)==="#"){b=c.aIds[a.replace(/^#/,"")];if(b!==k)return[b.idx]}b=Sb(ka(c.aoData,f,"nTr"));return h(b).filter(a).map(function(){return this._DT_RowIndex}).toArray()},
c,e)},1);c.selector.rows=a;c.selector.opts=b;return c});o("rows().nodes()",function(){return this.iterator("row",function(a,b){return a.aoData[b].nTr||k},1)});o("rows().data()",function(){return this.iterator(true,"rows",function(a,b){return ka(a.aoData,b,"_aData")},1)});u("rows().cache()","row().cache()",function(a){return this.iterator("row",function(b,c){var d=b.aoData[c];return a==="search"?d._aFilterData:d._aSortData},1)});u("rows().invalidate()","row().invalidate()",function(a){return this.iterator("row",
function(b,c){ea(b,c,a)})});u("rows().indexes()","row().index()",function(){return this.iterator("row",function(a,b){return b},1)});u("rows().ids()","row().id()",function(a){for(var b=[],c=this.context,d=0,e=c.length;d<e;d++)for(var f=0,g=this[d].length;f<g;f++){var h=c[d].rowIdFn(c[d].aoData[this[d][f]]._aData);b.push((a===true?"#":"")+h)}return new r(c,b)});u("rows().remove()","row().remove()",function(){var a=this;this.iterator("row",function(b,c,d){var e=b.aoData,f=e[c],g,h,i,n,m;e.splice(c,1);
g=0;for(h=e.length;g<h;g++){i=e[g];m=i.anCells;if(i.nTr!==null)i.nTr._DT_RowIndex=g;if(m!==null){i=0;for(n=m.length;i<n;i++)m[i]._DT_CellIndex.row=g}}qa(b.aiDisplayMaster,c);qa(b.aiDisplay,c);qa(a[d],c,false);b._iRecordsDisplay>0&&b._iRecordsDisplay--;Ua(b);c=b.rowIdFn(f._aData);c!==k&&delete b.aIds[c]});this.iterator("table",function(a){for(var c=0,d=a.aoData.length;c<d;c++)a.aoData[c].idx=c});return this});o("rows.add()",function(a){var b=this.iterator("table",function(b){var c,f,g,h=[];f=0;for(g=
a.length;f<g;f++){c=a[f];c.nodeName&&c.nodeName.toUpperCase()==="TR"?h.push(oa(b,c)[0]):h.push(O(b,c))}return h},1),c=this.rows(-1);c.pop();h.merge(c,b);return c});o("row()",function(a,b){return db(this.rows(a,b))});o("row().data()",function(a){var b=this.context;if(a===k)return b.length&&this.length?b[0].aoData[this[0]]._aData:k;var c=b[0].aoData[this[0]];c._aData=a;Array.isArray(a)&&(c.nTr&&c.nTr.id)&&N(b[0].rowId)(a,c.nTr.id);ea(b[0],this[0],"data");return this});o("row().node()",function(){var a=
this.context;return a.length&&this.length?a[0].aoData[this[0]].nTr||null:null});o("row.add()",function(a){a instanceof h&&a.length&&(a=a[0]);var b=this.iterator("table",function(b){return a.nodeName&&a.nodeName.toUpperCase()==="TR"?oa(b,a)[0]:O(b,a)});return this.row(b[0])});var eb=function(a,b){var c=a.context;if(c.length)if((c=c[0].aoData[b!==k?b:a[0]])&&c._details){c._details.remove();c._detailsShow=k;c._details=k}},Xb=function(a,b){var c=a.context;if(c.length&&a.length){var d=c[0].aoData[a[0]];
if(d._details){(d._detailsShow=b)?d._details.insertAfter(d.nTr):d._details.detach();var e=c[0],f=new r(e),g=e.aoData;f.off("draw.dt.DT_details column-visibility.dt.DT_details destroy.dt.DT_details");if(C(g,"_details").length>0){f.on("draw.dt.DT_details",function(a,b){e===b&&f.rows({page:"current"}).eq(0).each(function(a){a=g[a];a._detailsShow&&a._details.insertAfter(a.nTr)})});f.on("column-visibility.dt.DT_details",function(a,b){if(e===b)for(var c,d=W(b),f=0,h=g.length;f<h;f++){c=g[f];c._details&&
c._details.children("td[colspan]").attr("colspan",d)}});f.on("destroy.dt.DT_details",function(a,b){if(e===b)for(var c=0,d=g.length;c<d;c++)g[c]._details&&eb(f,c)})}}}};o("row().child()",function(a,b){var c=this.context;if(a===k)return c.length&&this.length?c[0].aoData[this[0]]._details:k;if(a===true)this.child.show();else if(a===false)eb(this);else if(c.length&&this.length){var d=c[0],c=c[0].aoData[this[0]],e=[],f=function(a,b){if(Array.isArray(a)||a instanceof h)for(var c=0,k=a.length;c<k;c++)f(a[c],
b);else if(a.nodeName&&a.nodeName.toLowerCase()==="tr")e.push(a);else{c=h("<tr><td></td></tr>").addClass(b);h("td",c).addClass(b).html(a)[0].colSpan=W(d);e.push(c[0])}};f(a,b);c._details&&c._details.detach();c._details=h(e);c._detailsShow&&c._details.insertAfter(c.nTr)}return this});o(["row().child.show()","row().child().show()"],function(){Xb(this,true);return this});o(["row().child.hide()","row().child().hide()"],function(){Xb(this,false);return this});o(["row().child.remove()","row().child().remove()"],
function(){eb(this);return this});o("row().child.isShown()",function(){var a=this.context;return a.length&&this.length?a[0].aoData[this[0]]._detailsShow||false:false});var ec=/^([^:]+):(name|visIdx|visible)$/,Yb=function(a,b,c,d,e){for(var c=[],d=0,f=e.length;d<f;d++)c.push(B(a,e[d],b));return c};o("columns()",function(a,b){if(a===k)a="";else if(h.isPlainObject(a)){b=a;a=""}var b=cb(b),c=this.iterator("table",function(c){var e=a,f=b,g=c.aoColumns,j=C(g,"sName"),i=C(g,"nTh");return bb("column",e,function(a){var b=
Pb(a);if(a==="")return Z(g.length);if(b!==null)return[b>=0?b:g.length+b];if(typeof a==="function"){var e=Da(c,f);return h.map(g,function(b,f){return a(f,Yb(c,f,0,0,e),i[f])?f:null})}var k=typeof a==="string"?a.match(ec):"";if(k)switch(k[2]){case "visIdx":case "visible":b=parseInt(k[1],10);if(b<0){var l=h.map(g,function(a,b){return a.bVisible?b:null});return[l[l.length+b]]}return[ba(c,b)];case "name":return h.map(j,function(a,b){return a===k[1]?b:null});default:return[]}if(a.nodeName&&a._DT_CellIndex)return[a._DT_CellIndex.column];
b=h(i).filter(a).map(function(){return h.inArray(this,i)}).toArray();if(b.length||!a.nodeName)return b;b=h(a).closest("*[data-dt-column]");return b.length?[b.data("dt-column")]:[]},c,f)},1);c.selector.cols=a;c.selector.opts=b;return c});u("columns().header()","column().header()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTh},1)});u("columns().footer()","column().footer()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTf},1)});u("columns().data()",
"column().data()",function(){return this.iterator("column-rows",Yb,1)});u("columns().dataSrc()","column().dataSrc()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].mData},1)});u("columns().cache()","column().cache()",function(a){return this.iterator("column-rows",function(b,c,d,e,f){return ka(b.aoData,f,a==="search"?"_aFilterData":"_aSortData",c)},1)});u("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(a,b,c,d,e){return ka(a.aoData,
e,"anCells",b)},1)});u("columns().visible()","column().visible()",function(a,b){var c=this,d=this.iterator("column",function(b,c){if(a===k)return b.aoColumns[c].bVisible;var d=b.aoColumns,j=d[c],i=b.aoData,n,m,l;if(a!==k&&j.bVisible!==a){if(a){var o=h.inArray(true,C(d,"bVisible"),c+1);n=0;for(m=i.length;n<m;n++){l=i[n].nTr;d=i[n].anCells;l&&l.insertBefore(d[c],d[o]||null)}}else h(C(b.aoData,"anCells",c)).detach();j.bVisible=a}});a!==k&&this.iterator("table",function(d){ga(d,d.aoHeader);ga(d,d.aoFooter);
d.aiDisplay.length||h(d.nTBody).find("td[colspan]").attr("colspan",W(d));za(d);c.iterator("column",function(c,d){t(c,null,"column-visibility",[c,d,a,b])});(b===k||b)&&c.columns.adjust()});return d});u("columns().indexes()","column().index()",function(a){return this.iterator("column",function(b,c){return a==="visible"?ca(b,c):c},1)});o("columns.adjust()",function(){return this.iterator("table",function(a){aa(a)},1)});o("column.index()",function(a,b){if(this.context.length!==0){var c=this.context[0];
if(a==="fromVisible"||a==="toData")return ba(c,b);if(a==="fromData"||a==="toVisible")return ca(c,b)}});o("column()",function(a,b){return db(this.columns(a,b))});o("cells()",function(a,b,c){if(h.isPlainObject(a))if(a.row===k){c=a;a=null}else{c=b;b=null}if(h.isPlainObject(b)){c=b;b=null}if(b===null||b===k)return this.iterator("table",function(b){var d=a,e=cb(c),f=b.aoData,g=Da(b,e),i=Sb(ka(f,g,"anCells")),j=h(Tb([],i)),l,n=b.aoColumns.length,o,u,r,t,w,v;return bb("cell",d,function(a){var c=typeof a===
"function";if(a===null||a===k||c){o=[];u=0;for(r=g.length;u<r;u++){l=g[u];for(t=0;t<n;t++){w={row:l,column:t};if(c){v=f[l];a(w,B(b,l,t),v.anCells?v.anCells[t]:null)&&o.push(w)}else o.push(w)}}return o}if(h.isPlainObject(a))return a.column!==k&&a.row!==k&&h.inArray(a.row,g)!==-1?[a]:[];c=j.filter(a).map(function(a,b){return{row:b._DT_CellIndex.row,column:b._DT_CellIndex.column}}).toArray();if(c.length||!a.nodeName)return c;v=h(a).closest("*[data-dt-row]");return v.length?[{row:v.data("dt-row"),column:v.data("dt-column")}]:
[]},b,e)});var d=c?{page:c.page,order:c.order,search:c.search}:{},e=this.columns(b,d),f=this.rows(a,d),g,j,i,l,d=this.iterator("table",function(a,b){var c=[];g=0;for(j=f[b].length;g<j;g++){i=0;for(l=e[b].length;i<l;i++)c.push({row:f[b][g],column:e[b][i]})}return c},1),d=c&&c.selected?this.cells(d,c):d;h.extend(d.selector,{cols:b,rows:a,opts:c});return d});u("cells().nodes()","cell().node()",function(){return this.iterator("cell",function(a,b,c){return(a=a.aoData[b])&&a.anCells?a.anCells[c]:k},1)});
o("cells().data()",function(){return this.iterator("cell",function(a,b,c){return B(a,b,c)},1)});u("cells().cache()","cell().cache()",function(a){a=a==="search"?"_aFilterData":"_aSortData";return this.iterator("cell",function(b,c,d){return b.aoData[c][a][d]},1)});u("cells().render()","cell().render()",function(a){return this.iterator("cell",function(b,c,d){return B(b,c,d,a)},1)});u("cells().indexes()","cell().index()",function(){return this.iterator("cell",function(a,b,c){return{row:b,column:c,columnVisible:ca(a,
c)}},1)});u("cells().invalidate()","cell().invalidate()",function(a){return this.iterator("cell",function(b,c,d){ea(b,c,a,d)})});o("cell()",function(a,b,c){return db(this.cells(a,b,c))});o("cell().data()",function(a){var b=this.context,c=this[0];if(a===k)return b.length&&c.length?B(b[0],c[0].row,c[0].column):k;lb(b[0],c[0].row,c[0].column,a);ea(b[0],c[0].row,"data",c[0].column);return this});o("order()",function(a,b){var c=this.context;if(a===k)return c.length!==0?c[0].aaSorting:k;typeof a==="number"?
a=[[a,b]]:a.length&&!Array.isArray(a[0])&&(a=Array.prototype.slice.call(arguments));return this.iterator("table",function(b){b.aaSorting=a.slice()})});o("order.listener()",function(a,b,c){return this.iterator("table",function(d){Oa(d,a,b,c)})});o("order.fixed()",function(a){if(!a){var b=this.context,b=b.length?b[0].aaSortingFixed:k;return Array.isArray(b)?{pre:b}:b}return this.iterator("table",function(b){b.aaSortingFixed=h.extend(true,{},a)})});o(["columns().order()","column().order()"],function(a){var b=
this;return this.iterator("table",function(c,d){var e=[];h.each(b[d],function(b,c){e.push([c,a])});c.aaSorting=e})});o("search()",function(a,b,c,d){var e=this.context;return a===k?e.length!==0?e[0].oPreviousSearch.sSearch:k:this.iterator("table",function(e){e.oFeatures.bFilter&&ha(e,h.extend({},e.oPreviousSearch,{sSearch:a+"",bRegex:b===null?false:b,bSmart:c===null?true:c,bCaseInsensitive:d===null?true:d}),1)})});u("columns().search()","column().search()",function(a,b,c,d){return this.iterator("column",
function(e,f){var g=e.aoPreSearchCols;if(a===k)return g[f].sSearch;if(e.oFeatures.bFilter){h.extend(g[f],{sSearch:a+"",bRegex:b===null?false:b,bSmart:c===null?true:c,bCaseInsensitive:d===null?true:d});ha(e,e.oPreviousSearch,1)}})});o("state()",function(){return this.context.length?this.context[0].oSavedState:null});o("state.clear()",function(){return this.iterator("table",function(a){a.fnStateSaveCallback.call(a.oInstance,a,{})})});o("state.loaded()",function(){return this.context.length?this.context[0].oLoadedState:
null});o("state.save()",function(){return this.iterator("table",function(a){za(a)})});l.versionCheck=l.fnVersionCheck=function(a){for(var b=l.version.split("."),a=a.split("."),c,d,e=0,f=a.length;e<f;e++){c=parseInt(b[e],10)||0;d=parseInt(a[e],10)||0;if(c!==d)return c>d}return true};l.isDataTable=l.fnIsDataTable=function(a){var b=h(a).get(0),c=false;if(a instanceof l.Api)return true;h.each(l.settings,function(a,e){var f=e.nScrollHead?h("table",e.nScrollHead)[0]:null,g=e.nScrollFoot?h("table",e.nScrollFoot)[0]:
null;if(e.nTable===b||f===b||g===b)c=true});return c};l.tables=l.fnTables=function(a){var b=false;if(h.isPlainObject(a)){b=a.api;a=a.visible}var c=h.map(l.settings,function(b){if(!a||a&&h(b.nTable).is(":visible"))return b.nTable});return b?new r(c):c};l.camelToHungarian=J;o("$()",function(a,b){var c=this.rows(b).nodes(),c=h(c);return h([].concat(c.filter(a).toArray(),c.find(a).toArray()))});h.each(["on","one","off"],function(a,b){o(b+"()",function(){var a=Array.prototype.slice.call(arguments);a[0]=
h.map(a[0].split(/\s/),function(a){return!a.match(/\.dt\b/)?a+".dt":a}).join(" ");var d=h(this.tables().nodes());d[b].apply(d,a);return this})});o("clear()",function(){return this.iterator("table",function(a){pa(a)})});o("settings()",function(){return new r(this.context,this.context)});o("init()",function(){var a=this.context;return a.length?a[0].oInit:null});o("data()",function(){return this.iterator("table",function(a){return C(a.aoData,"_aData")}).flatten()});o("destroy()",function(a){a=a||false;
return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,d=b.oClasses,e=b.nTable,f=b.nTBody,g=b.nTHead,j=b.nTFoot,i=h(e),f=h(f),k=h(b.nTableWrapper),m=h.map(b.aoData,function(a){return a.nTr}),o;b.bDestroying=true;t(b,"aoDestroyCallback","destroy",[b]);a||(new r(b)).columns().visible(true);k.off(".DT").find(":not(tbody *)").off(".DT");h(E).off(".DT-"+b.sInstance);if(e!=g.parentNode){i.children("thead").detach();i.append(g)}if(j&&e!=j.parentNode){i.children("tfoot").detach();i.append(j)}b.aaSorting=
[];b.aaSortingFixed=[];ya(b);h(m).removeClass(b.asStripeClasses.join(" "));h("th, td",g).removeClass(d.sSortable+" "+d.sSortableAsc+" "+d.sSortableDesc+" "+d.sSortableNone);f.children().detach();f.append(m);g=a?"remove":"detach";i[g]();k[g]();if(!a&&c){c.insertBefore(e,b.nTableReinsertBefore);i.css("width",b.sDestroyWidth).removeClass(d.sTable);(o=b.asDestroyStripes.length)&&f.children().each(function(a){h(this).addClass(b.asDestroyStripes[a%o])})}c=h.inArray(b,l.settings);c!==-1&&l.settings.splice(c,
1)})});h.each(["column","row","cell"],function(a,b){o(b+"s().every()",function(a){var d=this.selector.opts,e=this;return this.iterator(b,function(f,g,h,i,l){a.call(e[b](g,b==="cell"?h:d,b==="cell"?d:k),g,h,i,l)})})});o("i18n()",function(a,b,c){var d=this.context[0],a=S(a)(d.oLanguage);a===k&&(a=b);c!==k&&h.isPlainObject(a)&&(a=a[c]!==k?a[c]:a._);return a.replace("%d",c)});l.version="1.10.24";l.settings=[];l.models={};l.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};l.models.oRow=
{nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null,idx:-1};l.models.oColumn={idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,
sWidthOrig:null};l.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,
this.oLanguage.sThousands)},fnHeaderCallback:null,fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((a.iStateDuration===-1?sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){return{}}},fnStateLoadParams:null,fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(a.iStateDuration===-1?sessionStorage:localStorage).setItem("DataTables_"+
a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},fnStateSaveParams:null,iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"},sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",
sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sDecimal:"",sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},oSearch:h.extend({},l.models.oSearch),sAjaxDataProp:"data",sAjaxSource:null,sDom:"lfrtip",searchDelay:null,sPaginationType:"simple_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null,rowId:"DT_RowId"};
$(l.defaults);l.defaults.column={aDataSort:null,iDataSort:-1,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};$(l.defaults.column);l.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,
bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,bScrollbarLeft:!1,bBounding:!1,barWidth:0},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aIds:{},aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],
aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,searchDelay:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,bAjaxDataGet:!0,jqXHR:null,json:k,oAjaxData:k,fnServerData:null,
aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return y(this)=="ssp"?this._iRecordsTotal*1:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return y(this)=="ssp"?this._iRecordsDisplay*1:this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,
b=this._iDisplayStart,c=b+a,d=this.aiDisplay.length,e=this.oFeatures,f=e.bPaginate;return e.bServerSide?f===false||a===-1?b+d:Math.min(b+a,this._iRecordsDisplay):!f||c>d||a===-1?d:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{},rowIdFn:null,rowId:null};l.ext=v={buttons:{},classes:{},builder:"-source-",errMode:"alert",feature:[],search:[],selector:{cell:[],column:[],row:[]},internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},
order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:l.fnVersionCheck,iApiIndex:0,oJUIClasses:{},sVersion:l.version};h.extend(v,{afnFiltering:v.search,aTypes:v.type.detect,ofnSearch:v.type.search,oSort:v.type.order,afnSortData:v.order,aoFeatures:v.feature,oApi:v.internal,oStdClasses:v.classes,oPagination:v.pager});h.extend(l.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",
sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_desc_disabled",sSortableDesc:"sorting_asc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",
sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sJUIHeader:"",sJUIFooter:""});var Mb=l.ext.pager;h.extend(Mb,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},numbers:function(a,b){return[ja(a,
b)]},simple_numbers:function(a,b){return["previous",ja(a,b),"next"]},full_numbers:function(a,b){return["first","previous",ja(a,b),"next","last"]},first_last_numbers:function(a,b){return["first",ja(a,b),"last"]},_numbers:ja,numbers_length:7});h.extend(!0,l.ext.renderer,{pageButton:{_:function(a,b,c,d,e,f){var g=a.oClasses,j=a.oLanguage.oPaginate,i=a.oLanguage.oAria.paginate||{},l,m,o=0,s=function(b,d){var k,u,t,r,v=g.sPageButtonDisabled,w=function(b){Va(a,b.data.action,true)};k=0;for(u=d.length;k<
u;k++){r=d[k];if(Array.isArray(r)){t=h("<"+(r.DT_el||"div")+"/>").appendTo(b);s(t,r)}else{l=null;m=r;t=a.iTabIndex;switch(r){case "ellipsis":b.append('<span class="ellipsis">&#x2026;</span>');break;case "first":l=j.sFirst;if(e===0){t=-1;m=m+(" "+v)}break;case "previous":l=j.sPrevious;if(e===0){t=-1;m=m+(" "+v)}break;case "next":l=j.sNext;if(f===0||e===f-1){t=-1;m=m+(" "+v)}break;case "last":l=j.sLast;if(f===0||e===f-1){t=-1;m=m+(" "+v)}break;default:l=a.fnFormatNumber(r+1);m=e===r?g.sPageButtonActive:
""}if(l!==null){t=h("<a>",{"class":g.sPageButton+" "+m,"aria-controls":a.sTableId,"aria-label":i[r],"data-dt-idx":o,tabindex:t,id:c===0&&typeof r==="string"?a.sTableId+"_"+r:null}).html(l).appendTo(b);Xa(t,{action:r},w);o++}}}},u;try{u=h(b).find(H.activeElement).data("dt-idx")}catch(t){}s(h(b).empty(),d);u!==k&&h(b).find("[data-dt-idx="+u+"]").trigger("focus")}}});h.extend(l.ext.type.detect,[function(a,b){var c=b.oLanguage.sDecimal;return ab(a,c)?"num"+c:null},function(a){if(a&&!(a instanceof Date)&&
!bc.test(a))return null;var b=Date.parse(a);return b!==null&&!isNaN(b)||M(a)?"date":null},function(a,b){var c=b.oLanguage.sDecimal;return ab(a,c,true)?"num-fmt"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Rb(a,c)?"html-num"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Rb(a,c,true)?"html-num-fmt"+c:null},function(a){return M(a)||typeof a==="string"&&a.indexOf("<")!==-1?"html":null}]);h.extend(l.ext.type.search,{html:function(a){return M(a)?a:typeof a==="string"?a.replace(Ob,
" ").replace(Ca,""):""},string:function(a){return M(a)?a:typeof a==="string"?a.replace(Ob," "):a}});var Ba=function(a,b,c,d){if(a!==0&&(!a||a==="-"))return-Infinity;b&&(a=Qb(a,b));if(a.replace){c&&(a=a.replace(c,""));d&&(a=a.replace(d,""))}return a*1};h.extend(v.type.order,{"date-pre":function(a){a=Date.parse(a);return isNaN(a)?-Infinity:a},"html-pre":function(a){return M(a)?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return M(a)?"":typeof a==="string"?a.toLowerCase():
!a.toString?"":a.toString()},"string-asc":function(a,b){return a<b?-1:a>b?1:0},"string-desc":function(a,b){return a<b?1:a>b?-1:0}});Fa("");h.extend(!0,l.ext.renderer,{header:{_:function(a,b,c,d){h(a.nTable).on("order.dt.DT",function(e,f,g,h){if(a===f){e=c.idx;b.removeClass(d.sSortAsc+" "+d.sSortDesc).addClass(h[e]=="asc"?d.sSortAsc:h[e]=="desc"?d.sSortDesc:c.sSortingClass)}})},jqueryui:function(a,b,c,d){h("<div/>").addClass(d.sSortJUIWrapper).append(b.contents()).append(h("<span/>").addClass(d.sSortIcon+
" "+c.sSortingClassJUI)).appendTo(b);h(a.nTable).on("order.dt.DT",function(e,f,g,h){if(a===f){e=c.idx;b.removeClass(d.sSortAsc+" "+d.sSortDesc).addClass(h[e]=="asc"?d.sSortAsc:h[e]=="desc"?d.sSortDesc:c.sSortingClass);b.find("span."+d.sSortIcon).removeClass(d.sSortJUIAsc+" "+d.sSortJUIDesc+" "+d.sSortJUI+" "+d.sSortJUIAscAllowed+" "+d.sSortJUIDescAllowed).addClass(h[e]=="asc"?d.sSortJUIAsc:h[e]=="desc"?d.sSortJUIDesc:c.sSortingClassJUI)}})}}});var fb=function(a){return typeof a==="string"?a.replace(/&/g,
"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"):a};l.render={number:function(a,b,c,d,e){return{display:function(f){if(typeof f!=="number"&&typeof f!=="string")return f;var g=f<0?"-":"",h=parseFloat(f);if(isNaN(h))return fb(f);h=h.toFixed(c);f=Math.abs(h);h=parseInt(f,10);f=c?b+(f-h).toFixed(c).substring(2):"";return g+(d||"")+h.toString().replace(/\B(?=(\d{3})+(?!\d))/g,a)+f+(e||"")}}},text:function(){return{display:fb,filter:fb}}};h.extend(l.ext.internal,{_fnExternApiFunc:Nb,
_fnBuildAjax:ta,_fnAjaxUpdate:nb,_fnAjaxParameters:wb,_fnAjaxUpdateDraw:xb,_fnAjaxDataSrc:ua,_fnAddColumn:Ga,_fnColumnOptions:la,_fnAdjustColumnSizing:aa,_fnVisibleToColumnIndex:ba,_fnColumnIndexToVisible:ca,_fnVisbleColumns:W,_fnGetColumns:na,_fnColumnTypes:Ia,_fnApplyColumnDefs:kb,_fnHungarianMap:$,_fnCamelToHungarian:J,_fnLanguageCompat:Ea,_fnBrowserDetect:ib,_fnAddData:O,_fnAddTr:oa,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==k?b._DT_RowIndex:null},_fnNodeToColumnIndex:function(a,
b,c){return h.inArray(c,a.aoData[b].anCells)},_fnGetCellData:B,_fnSetCellData:lb,_fnSplitObjNotation:La,_fnGetObjectDataFn:S,_fnSetObjectDataFn:N,_fnGetDataMaster:Ma,_fnClearTable:pa,_fnDeleteIndex:qa,_fnInvalidate:ea,_fnGetRowElements:Ka,_fnCreateTr:Ja,_fnBuildHead:mb,_fnDrawHead:ga,_fnDraw:P,_fnReDraw:T,_fnAddOptionsHtml:pb,_fnDetectHeader:fa,_fnGetUniqueThs:sa,_fnFeatureHtmlFilter:rb,_fnFilterComplete:ha,_fnFilterCustom:Ab,_fnFilterColumn:zb,_fnFilter:yb,_fnFilterCreateSearch:Ra,_fnEscapeRegex:Sa,
_fnFilterData:Bb,_fnFeatureHtmlInfo:ub,_fnUpdateInfo:Eb,_fnInfoMacros:Fb,_fnInitialise:ia,_fnInitComplete:va,_fnLengthChange:Ta,_fnFeatureHtmlLength:qb,_fnFeatureHtmlPaginate:vb,_fnPageChange:Va,_fnFeatureHtmlProcessing:sb,_fnProcessingDisplay:D,_fnFeatureHtmlTable:tb,_fnScrollDraw:ma,_fnApplyToChildren:I,_fnCalculateColumnWidths:Ha,_fnThrottle:Qa,_fnConvertToWidth:Gb,_fnGetWidestNode:Hb,_fnGetMaxLenString:Ib,_fnStringToCss:w,_fnSortFlatten:Y,_fnSort:ob,_fnSortAria:Kb,_fnSortListener:Wa,_fnSortAttachListener:Oa,
_fnSortingClasses:ya,_fnSortData:Jb,_fnSaveState:za,_fnLoadState:Lb,_fnSettingsFromNode:Aa,_fnLog:K,_fnMap:F,_fnBindAction:Xa,_fnCallbackReg:z,_fnCallbackFire:t,_fnLengthOverflow:Ua,_fnRenderer:Pa,_fnDataSource:y,_fnRowAttributes:Na,_fnExtend:Ya,_fnCalculateEnd:function(){}});h.fn.dataTable=l;l.$=h;h.fn.dataTableSettings=l.settings;h.fn.dataTableExt=l.ext;h.fn.DataTable=function(a){return h(this).dataTable(a).api()};h.each(l,function(a,b){h.fn.DataTable[a]=b});return h.fn.dataTable});

155
wwwroot/lib/tippy/popper.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long