static: format code
This commit is contained in:
		
							
								
								
									
										3
									
								
								passbook/static/static/.prettierignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								passbook/static/static/.prettierignore
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					# Ignore artifacts:
 | 
				
			||||||
 | 
					dist
 | 
				
			||||||
 | 
					coverage
 | 
				
			||||||
							
								
								
									
										1
									
								
								passbook/static/static/.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								passbook/static/static/.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					{}
 | 
				
			||||||
							
								
								
									
										1332
									
								
								passbook/static/static/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1332
									
								
								passbook/static/static/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,28 +1,29 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "license": "MIT",
 | 
					    "license": "MIT",
 | 
				
			||||||
  "scripts": {
 | 
					    "scripts": {
 | 
				
			||||||
    "build": "rollup -c ./rollup.config.js",
 | 
					        "build": "rollup -c ./rollup.config.js",
 | 
				
			||||||
    "watch": "rollup -c -w"
 | 
					        "watch": "rollup -c -w"
 | 
				
			||||||
  },
 | 
					    },
 | 
				
			||||||
  "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
    "@fortawesome/fontawesome-free": "^5.15.1",
 | 
					        "@fortawesome/fontawesome-free": "^5.15.1",
 | 
				
			||||||
    "@patternfly/patternfly": "^4.65.6",
 | 
					        "@patternfly/patternfly": "^4.65.6",
 | 
				
			||||||
    "@types/chart.js": "^2.9.28",
 | 
					        "@types/chart.js": "^2.9.28",
 | 
				
			||||||
    "@types/codemirror": "0.0.98",
 | 
					        "@types/codemirror": "0.0.98",
 | 
				
			||||||
    "chart.js": "^2.9.4",
 | 
					        "chart.js": "^2.9.4",
 | 
				
			||||||
    "codemirror": "^5.58.3",
 | 
					        "codemirror": "^5.58.3",
 | 
				
			||||||
    "lit-element": "^2.4.0",
 | 
					        "lit-element": "^2.4.0",
 | 
				
			||||||
    "lit-html": "^1.3.0",
 | 
					        "lit-html": "^1.3.0",
 | 
				
			||||||
    "rollup": "^2.33.3",
 | 
					        "rollup": "^2.33.3",
 | 
				
			||||||
    "rollup-plugin-cssimport": "^1.0.2",
 | 
					        "rollup-plugin-cssimport": "^1.0.2",
 | 
				
			||||||
    "tslib": "^2.0.3"
 | 
					        "tslib": "^2.0.3"
 | 
				
			||||||
  },
 | 
					    },
 | 
				
			||||||
  "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
    "@rollup/plugin-typescript": "^6.1.0",
 | 
					        "@rollup/plugin-typescript": "^6.1.0",
 | 
				
			||||||
    "rollup-plugin-commonjs": "^10.1.0",
 | 
					        "prettier": "2.2.0",
 | 
				
			||||||
    "rollup-plugin-minify-html-literals": "^1.2.5",
 | 
					        "rollup-plugin-commonjs": "^10.1.0",
 | 
				
			||||||
    "rollup-plugin-node-resolve": "^5.2.0",
 | 
					        "rollup-plugin-minify-html-literals": "^1.2.5",
 | 
				
			||||||
    "rollup-plugin-sourcemaps": "^0.6.3",
 | 
					        "rollup-plugin-node-resolve": "^5.2.0",
 | 
				
			||||||
    "rollup-plugin-terser": "^7.0.2"
 | 
					        "rollup-plugin-sourcemaps": "^0.6.3",
 | 
				
			||||||
  }
 | 
					        "rollup-plugin-terser": "^7.0.2"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
@font-face {
 | 
					@font-face {
 | 
				
			||||||
    font-family: 'DIN 1451 Std';
 | 
					    font-family: "DIN 1451 Std";
 | 
				
			||||||
    src: url('fonts/DINEngschriftStd.woff2') format('woff2'),
 | 
					    src: url("fonts/DINEngschriftStd.woff2") format("woff2"),
 | 
				
			||||||
        url('fonts/DINEngschriftStd.woff') format('woff');
 | 
					        url("fonts/DINEngschriftStd.woff") format("woff");
 | 
				
			||||||
    font-weight: normal;
 | 
					    font-weight: normal;
 | 
				
			||||||
    font-style: normal;
 | 
					    font-style: normal;
 | 
				
			||||||
    font-display: swap;
 | 
					    font-display: swap;
 | 
				
			||||||
@ -13,7 +13,7 @@
 | 
				
			|||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    flex-direction: row;
 | 
					    flex-direction: row;
 | 
				
			||||||
    align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
    margin-right: .5em;
 | 
					    margin-right: 0.5em;
 | 
				
			||||||
    color: var(--pf-global--Color--light-200);
 | 
					    color: var(--pf-global--Color--light-200);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,11 +23,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.pb-brand > img {
 | 
					.pb-brand > img {
 | 
				
			||||||
    max-height: 68px;
 | 
					    max-height: 68px;
 | 
				
			||||||
    margin-right: .5em;
 | 
					    margin-right: 0.5em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.pf-c-background-image::before {
 | 
					.pf-c-background-image::before {
 | 
				
			||||||
    background-image: url('./flow_background.jpg');
 | 
					    background-image: url("./flow_background.jpg");
 | 
				
			||||||
    background-position: center;
 | 
					    background-position: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -91,7 +91,8 @@ select[multiple] {
 | 
				
			|||||||
    flex: 1 1;
 | 
					    flex: 1 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.selector-available, .selector-chosen {
 | 
					.selector-available,
 | 
				
			||||||
 | 
					.selector-chosen {
 | 
				
			||||||
    width: auto;
 | 
					    width: auto;
 | 
				
			||||||
    flex: 1 1;
 | 
					    flex: 1 1;
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
@ -114,7 +115,8 @@ select[multiple] {
 | 
				
			|||||||
    list-style: none;
 | 
					    list-style: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.selector-add, .selector-remove {
 | 
					.selector-add,
 | 
				
			||||||
 | 
					.selector-remove {
 | 
				
			||||||
    width: 20px;
 | 
					    width: 20px;
 | 
				
			||||||
    height: 20px;
 | 
					    height: 20px;
 | 
				
			||||||
    background-size: 20px auto;
 | 
					    background-size: 20px auto;
 | 
				
			||||||
@ -128,7 +130,8 @@ select[multiple] {
 | 
				
			|||||||
    background-position: 0 -80px;
 | 
					    background-position: 0 -80px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
a.selector-chooseall, a.selector-clearall {
 | 
					a.selector-chooseall,
 | 
				
			||||||
 | 
					a.selector-clearall {
 | 
				
			||||||
    align-self: center;
 | 
					    align-self: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -145,7 +148,8 @@ a.selector-chooseall, a.selector-clearall {
 | 
				
			|||||||
    margin-bottom: 0;
 | 
					    margin-bottom: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.stacked .selector-available, .stacked .selector-chosen {
 | 
					.stacked .selector-available,
 | 
				
			||||||
 | 
					.stacked .selector-chosen {
 | 
				
			||||||
    width: auto;
 | 
					    width: auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -161,7 +165,8 @@ a.selector-chooseall, a.selector-clearall {
 | 
				
			|||||||
    padding: 3px;
 | 
					    padding: 3px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.stacked .selector-add, .stacked .selector-remove {
 | 
					.stacked .selector-add,
 | 
				
			||||||
 | 
					.stacked .selector-remove {
 | 
				
			||||||
    background-size: 20px auto;
 | 
					    background-size: 20px auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -181,7 +186,8 @@ a.selector-chooseall, a.selector-clearall {
 | 
				
			|||||||
    background-position: 0 -20px;
 | 
					    background-position: 0 -20px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.help-tooltip, .selector .help-icon {
 | 
					.help-tooltip,
 | 
				
			||||||
 | 
					.selector .help-icon {
 | 
				
			||||||
    display: none;
 | 
					    display: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -212,7 +218,8 @@ form .form-row p.datetime {
 | 
				
			|||||||
    overflow: auto;
 | 
					    overflow: auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.selector-add, .selector-remove {
 | 
					.selector-add,
 | 
				
			||||||
 | 
					.selector-remove {
 | 
				
			||||||
    width: 16px;
 | 
					    width: 16px;
 | 
				
			||||||
    height: 16px;
 | 
					    height: 16px;
 | 
				
			||||||
    display: block;
 | 
					    display: block;
 | 
				
			||||||
@ -222,11 +229,13 @@ form .form-row p.datetime {
 | 
				
			|||||||
    opacity: 0.3;
 | 
					    opacity: 0.3;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.active.selector-add, .active.selector-remove {
 | 
					.active.selector-add,
 | 
				
			||||||
 | 
					.active.selector-remove {
 | 
				
			||||||
    opacity: 1;
 | 
					    opacity: 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.active.selector-add:hover, .active.selector-remove:hover {
 | 
					.active.selector-add:hover,
 | 
				
			||||||
 | 
					.active.selector-remove:hover {
 | 
				
			||||||
    cursor: pointer;
 | 
					    cursor: pointer;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -234,7 +243,8 @@ form .form-row p.datetime {
 | 
				
			|||||||
    background: url(../admin/img/selector-icons.svg) 0 -96px no-repeat;
 | 
					    background: url(../admin/img/selector-icons.svg) 0 -96px no-repeat;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.active.selector-add:focus, .active.selector-add:hover {
 | 
					.active.selector-add:focus,
 | 
				
			||||||
 | 
					.active.selector-add:hover {
 | 
				
			||||||
    background-position: 0 -112px;
 | 
					    background-position: 0 -112px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,27 +1,29 @@
 | 
				
			|||||||
import resolve from 'rollup-plugin-node-resolve';
 | 
					import resolve from "rollup-plugin-node-resolve";
 | 
				
			||||||
import commonjs from 'rollup-plugin-commonjs';
 | 
					import commonjs from "rollup-plugin-commonjs";
 | 
				
			||||||
import minifyHTML from 'rollup-plugin-minify-html-literals';
 | 
					import minifyHTML from "rollup-plugin-minify-html-literals";
 | 
				
			||||||
import { terser } from 'rollup-plugin-terser';
 | 
					import { terser } from "rollup-plugin-terser";
 | 
				
			||||||
import sourcemaps from 'rollup-plugin-sourcemaps';
 | 
					import sourcemaps from "rollup-plugin-sourcemaps";
 | 
				
			||||||
import typescript from '@rollup/plugin-typescript';
 | 
					import typescript from "@rollup/plugin-typescript";
 | 
				
			||||||
import cssimport from "rollup-plugin-cssimport";
 | 
					import cssimport from "rollup-plugin-cssimport";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default [{
 | 
					export default [
 | 
				
			||||||
  input: './src/main.ts',
 | 
					 | 
				
			||||||
  output: [
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      format: 'es',
 | 
					        input: "./src/main.ts",
 | 
				
			||||||
      dir: 'dist',
 | 
					        output: [
 | 
				
			||||||
      sourcemap: true
 | 
					            {
 | 
				
			||||||
    }
 | 
					                format: "es",
 | 
				
			||||||
  ],
 | 
					                dir: "dist",
 | 
				
			||||||
  plugins: [
 | 
					                sourcemap: true,
 | 
				
			||||||
    cssimport(),
 | 
					            },
 | 
				
			||||||
    typescript(),
 | 
					        ],
 | 
				
			||||||
    resolve({browser: true}),
 | 
					        plugins: [
 | 
				
			||||||
    commonjs(),
 | 
					            cssimport(),
 | 
				
			||||||
    sourcemaps(),
 | 
					            typescript(),
 | 
				
			||||||
    minifyHTML(),
 | 
					            resolve({ browser: true }),
 | 
				
			||||||
    terser(),
 | 
					            commonjs(),
 | 
				
			||||||
  ],
 | 
					            sourcemaps(),
 | 
				
			||||||
}];
 | 
					            minifyHTML(),
 | 
				
			||||||
 | 
					            terser(),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
				
			|||||||
@ -9,10 +9,11 @@ const PROGRESS_CLASSES = ["pf-m-progress", "pf-m-in-progress"];
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@customElement("pb-action-button")
 | 
					@customElement("pb-action-button")
 | 
				
			||||||
export class ActionButton extends LitElement {
 | 
					export class ActionButton extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        this.querySelector("button")?.addEventListener('click', e => this.callAction());
 | 
					        this.querySelector("button")?.addEventListener("click", (e) =>
 | 
				
			||||||
 | 
					            this.callAction()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
@ -41,33 +42,39 @@ export class ActionButton extends LitElement {
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.setLoading();
 | 
					        this.setLoading();
 | 
				
			||||||
        const csrftoken = getCookie('passbook_csrf');
 | 
					        const csrftoken = getCookie("passbook_csrf");
 | 
				
			||||||
        const request = new Request(
 | 
					        const request = new Request(this.url, {
 | 
				
			||||||
            this.url,
 | 
					            headers: { "X-CSRFToken": csrftoken! },
 | 
				
			||||||
            { headers: { 'X-CSRFToken': csrftoken! } }
 | 
					        });
 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        fetch(request, {
 | 
					        fetch(request, {
 | 
				
			||||||
            method: "POST",
 | 
					            method: "POST",
 | 
				
			||||||
            mode: 'same-origin',
 | 
					            mode: "same-origin",
 | 
				
			||||||
        }).then(r => r.json()).then(r => {
 | 
					        })
 | 
				
			||||||
            this.setDone(SUCCESS_CLASS);
 | 
					            .then((r) => r.json())
 | 
				
			||||||
        }).catch(() => {
 | 
					            .then((r) => {
 | 
				
			||||||
            this.setDone(ERROR_CLASS);
 | 
					                this.setDone(SUCCESS_CLASS);
 | 
				
			||||||
        });
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(() => {
 | 
				
			||||||
 | 
					                this.setDone(ERROR_CLASS);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return html`<button class="pf-c-button pf-m-primary">
 | 
					        return html`<button class="pf-c-button pf-m-primary">
 | 
				
			||||||
            ${this.isRunning ? html`
 | 
					            ${this.isRunning
 | 
				
			||||||
            <span class="pf-c-button__progress">
 | 
					                ? html` <span class="pf-c-button__progress">
 | 
				
			||||||
                <span class="pf-c-spinner pf-m-md" role="progressbar" aria-valuetext="Loading...">
 | 
					                      <span
 | 
				
			||||||
                    <span class="pf-c-spinner__clipper"></span>
 | 
					                          class="pf-c-spinner pf-m-md"
 | 
				
			||||||
                    <span class="pf-c-spinner__lead-ball"></span>
 | 
					                          role="progressbar"
 | 
				
			||||||
                    <span class="pf-c-spinner__tail-ball"></span>
 | 
					                          aria-valuetext="Loading..."
 | 
				
			||||||
                </span>
 | 
					                      >
 | 
				
			||||||
            </span>` : ""}
 | 
					                          <span class="pf-c-spinner__clipper"></span>
 | 
				
			||||||
 | 
					                          <span class="pf-c-spinner__lead-ball"></span>
 | 
				
			||||||
 | 
					                          <span class="pf-c-spinner__tail-ball"></span>
 | 
				
			||||||
 | 
					                      </span>
 | 
				
			||||||
 | 
					                  </span>`
 | 
				
			||||||
 | 
					                : ""}
 | 
				
			||||||
            <slot></slot>
 | 
					            <slot></slot>
 | 
				
			||||||
        </button>`;
 | 
					        </button>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,6 @@ interface TickValue {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@customElement("pb-admin-logins-chart")
 | 
					@customElement("pb-admin-logins-chart")
 | 
				
			||||||
export class AdminLoginsChart extends LitElement {
 | 
					export class AdminLoginsChart extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    url: string = "";
 | 
					    url: string = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -16,23 +15,23 @@ export class AdminLoginsChart extends LitElement {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    static get styles() {
 | 
					    static get styles() {
 | 
				
			||||||
        return css`
 | 
					        return css`
 | 
				
			||||||
        :host {
 | 
					            :host {
 | 
				
			||||||
            position: relative;
 | 
					                position: relative;
 | 
				
			||||||
            height: 100%;
 | 
					                height: 100%;
 | 
				
			||||||
            width: 100%;
 | 
					                width: 100%;
 | 
				
			||||||
            display: block;
 | 
					                display: block;
 | 
				
			||||||
            min-height: 25rem;
 | 
					                min-height: 25rem;
 | 
				
			||||||
        }
 | 
					            }
 | 
				
			||||||
        canvas {
 | 
					            canvas {
 | 
				
			||||||
            width: 100px;
 | 
					                width: 100px;
 | 
				
			||||||
            height: 100px;
 | 
					                height: 100px;
 | 
				
			||||||
        }
 | 
					            }
 | 
				
			||||||
        `;
 | 
					        `;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        window.addEventListener('resize', () => {
 | 
					        window.addEventListener("resize", () => {
 | 
				
			||||||
            if (this.chart) {
 | 
					            if (this.chart) {
 | 
				
			||||||
                this.chart.resize();
 | 
					                this.chart.resize();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -40,62 +39,79 @@ export class AdminLoginsChart extends LitElement {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    firstUpdated() {
 | 
					    firstUpdated() {
 | 
				
			||||||
        fetch(this.url).then(r => r.json()).catch(e => console.error(e)).then(r => {
 | 
					        fetch(this.url)
 | 
				
			||||||
            let ctx = (<HTMLCanvasElement>this.shadowRoot?.querySelector("canvas")).getContext('2d')!;
 | 
					            .then((r) => r.json())
 | 
				
			||||||
            this.chart = new Chart(ctx, {
 | 
					            .catch((e) => console.error(e))
 | 
				
			||||||
                type: 'bar',
 | 
					            .then((r) => {
 | 
				
			||||||
                data: {
 | 
					                let ctx = (<HTMLCanvasElement>(
 | 
				
			||||||
                    datasets: [
 | 
					                    this.shadowRoot?.querySelector("canvas")
 | 
				
			||||||
                        {
 | 
					                )).getContext("2d")!;
 | 
				
			||||||
                            label: 'Failed Logins',
 | 
					                this.chart = new Chart(ctx, {
 | 
				
			||||||
                            backgroundColor: "rgba(201, 25, 11, .5)",
 | 
					                    type: "bar",
 | 
				
			||||||
                            spanGaps: true,
 | 
					                    data: {
 | 
				
			||||||
                            data: r.logins_failed_per_1h,
 | 
					                        datasets: [
 | 
				
			||||||
                        },
 | 
					                            {
 | 
				
			||||||
                        {
 | 
					                                label: "Failed Logins",
 | 
				
			||||||
                            label: 'Successful Logins',
 | 
					                                backgroundColor: "rgba(201, 25, 11, .5)",
 | 
				
			||||||
                            backgroundColor: "rgba(189, 229, 184, .5)",
 | 
					                                spanGaps: true,
 | 
				
			||||||
                            spanGaps: true,
 | 
					                                data: r.logins_failed_per_1h,
 | 
				
			||||||
                            data: r.logins_per_1h,
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                    ]
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                options: {
 | 
					 | 
				
			||||||
                    maintainAspectRatio: false,
 | 
					 | 
				
			||||||
                    spanGaps: true,
 | 
					 | 
				
			||||||
                    scales: {
 | 
					 | 
				
			||||||
                        xAxes: [{
 | 
					 | 
				
			||||||
                            stacked: true,
 | 
					 | 
				
			||||||
                            gridLines: {
 | 
					 | 
				
			||||||
                                color: "rgba(0, 0, 0, 0)",
 | 
					 | 
				
			||||||
                            },
 | 
					                            },
 | 
				
			||||||
                            type: 'time',
 | 
					                            {
 | 
				
			||||||
                            offset: true,
 | 
					                                label: "Successful Logins",
 | 
				
			||||||
                            ticks: {
 | 
					                                backgroundColor: "rgba(189, 229, 184, .5)",
 | 
				
			||||||
                                callback: function (value, index: number, values) {
 | 
					                                spanGaps: true,
 | 
				
			||||||
                                    const valueStamp = <TickValue>(<unknown>values[index]);
 | 
					                                data: r.logins_per_1h,
 | 
				
			||||||
                                    const delta = (Date.now() - valueStamp.value);
 | 
					                            },
 | 
				
			||||||
                                    const ago = Math.round(delta / 1000 / 3600);
 | 
					                        ],
 | 
				
			||||||
                                    return `${ago} Hours ago`;
 | 
					                    },
 | 
				
			||||||
 | 
					                    options: {
 | 
				
			||||||
 | 
					                        maintainAspectRatio: false,
 | 
				
			||||||
 | 
					                        spanGaps: true,
 | 
				
			||||||
 | 
					                        scales: {
 | 
				
			||||||
 | 
					                            xAxes: [
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    stacked: true,
 | 
				
			||||||
 | 
					                                    gridLines: {
 | 
				
			||||||
 | 
					                                        color: "rgba(0, 0, 0, 0)",
 | 
				
			||||||
 | 
					                                    },
 | 
				
			||||||
 | 
					                                    type: "time",
 | 
				
			||||||
 | 
					                                    offset: true,
 | 
				
			||||||
 | 
					                                    ticks: {
 | 
				
			||||||
 | 
					                                        callback: function (
 | 
				
			||||||
 | 
					                                            value,
 | 
				
			||||||
 | 
					                                            index: number,
 | 
				
			||||||
 | 
					                                            values
 | 
				
			||||||
 | 
					                                        ) {
 | 
				
			||||||
 | 
					                                            const valueStamp = <TickValue>(
 | 
				
			||||||
 | 
					                                                (<unknown>values[index])
 | 
				
			||||||
 | 
					                                            );
 | 
				
			||||||
 | 
					                                            const delta =
 | 
				
			||||||
 | 
					                                                Date.now() - valueStamp.value;
 | 
				
			||||||
 | 
					                                            const ago = Math.round(
 | 
				
			||||||
 | 
					                                                delta / 1000 / 3600
 | 
				
			||||||
 | 
					                                            );
 | 
				
			||||||
 | 
					                                            return `${ago} Hours ago`;
 | 
				
			||||||
 | 
					                                        },
 | 
				
			||||||
 | 
					                                        autoSkip: true,
 | 
				
			||||||
 | 
					                                        maxTicksLimit: 8,
 | 
				
			||||||
 | 
					                                    },
 | 
				
			||||||
                                },
 | 
					                                },
 | 
				
			||||||
                                autoSkip: true,
 | 
					                            ],
 | 
				
			||||||
                                maxTicksLimit: 8
 | 
					                            yAxes: [
 | 
				
			||||||
                            }
 | 
					                                {
 | 
				
			||||||
                        }],
 | 
					                                    stacked: true,
 | 
				
			||||||
                        yAxes: [{
 | 
					                                    gridLines: {
 | 
				
			||||||
                            stacked: true,
 | 
					                                        color: "rgba(0, 0, 0, 0)",
 | 
				
			||||||
                            gridLines: {
 | 
					                                    },
 | 
				
			||||||
                                color: "rgba(0, 0, 0, 0)",
 | 
					                                },
 | 
				
			||||||
                            }
 | 
					                            ],
 | 
				
			||||||
                        }]
 | 
					                        },
 | 
				
			||||||
                    }
 | 
					                    },
 | 
				
			||||||
                }
 | 
					                });
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return html`<canvas></canvas>`;
 | 
					        return html`<canvas></canvas>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,6 @@ interface RegexAnchor {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@customElement("pb-admin-sidebar")
 | 
					@customElement("pb-admin-sidebar")
 | 
				
			||||||
export class AdminSideBar extends LitElement {
 | 
					export class AdminSideBar extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    activePath: string;
 | 
					    activePath: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -16,33 +15,35 @@ export class AdminSideBar extends LitElement {
 | 
				
			|||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        this.activePath = window.location.hash.slice(1, Infinity);
 | 
					        this.activePath = window.location.hash.slice(1, Infinity);
 | 
				
			||||||
        window.addEventListener("hashchange", e => {
 | 
					        window.addEventListener("hashchange", (e) => {
 | 
				
			||||||
            this.activePath = window.location.hash.slice(1, Infinity);
 | 
					            this.activePath = window.location.hash.slice(1, Infinity);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        this.querySelectorAll<HTMLAnchorElement>(".pf-c-nav__link").forEach(a => {
 | 
					        this.querySelectorAll<HTMLAnchorElement>(".pf-c-nav__link").forEach(
 | 
				
			||||||
            let rawValue = a.attributes.getNamedItem("pb-url-prefix")?.value;
 | 
					            (a) => {
 | 
				
			||||||
            if (!rawValue) {
 | 
					                let rawValue = a.attributes.getNamedItem("pb-url-prefix")
 | 
				
			||||||
                const parsedURL = new URL(a.href);
 | 
					                    ?.value;
 | 
				
			||||||
                if (parsedURL.hash === "") {
 | 
					                if (!rawValue) {
 | 
				
			||||||
                    console.log(`Ignoring ${a}`);
 | 
					                    const parsedURL = new URL(a.href);
 | 
				
			||||||
                    return;
 | 
					                    if (parsedURL.hash === "") {
 | 
				
			||||||
 | 
					                        console.log(`Ignoring ${a}`);
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    rawValue = `^${parsedURL.hash.slice(1, Infinity)}$`;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                rawValue = `^${parsedURL.hash.slice(1, Infinity)}$`;
 | 
					                const regexp = RegExp(rawValue);
 | 
				
			||||||
 | 
					                this.paths.push({ anchor: a, match: regexp });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            const regexp = RegExp(rawValue);
 | 
					        );
 | 
				
			||||||
            this.paths.push({anchor: a, match: regexp});
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        this.paths.forEach(path => {
 | 
					        this.paths.forEach((path) => {
 | 
				
			||||||
            if (path.match.exec(this.activePath)) {
 | 
					            if (path.match.exec(this.activePath)) {
 | 
				
			||||||
                path.anchor.classList.add("pf-m-current");
 | 
					                path.anchor.classList.add("pf-m-current");
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                path.anchor.classList.remove("pf-m-current");
 | 
					                path.anchor.classList.remove("pf-m-current");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
        return html`<slot></slot>`;
 | 
					        return html`<slot></slot>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,6 @@ import "codemirror/mode/python/python.js";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@customElement("pb-codemirror")
 | 
					@customElement("pb-codemirror")
 | 
				
			||||||
export class CodeMirrorTextarea extends LitElement {
 | 
					export class CodeMirrorTextarea extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    readOnly: boolean = false;
 | 
					    readOnly: boolean = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -29,7 +28,7 @@ export class CodeMirrorTextarea extends LitElement {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        this.editor = CodeMirror.fromTextArea(textarea, {
 | 
					        this.editor = CodeMirror.fromTextArea(textarea, {
 | 
				
			||||||
            mode: this.mode,
 | 
					            mode: this.mode,
 | 
				
			||||||
            theme: 'monokai',
 | 
					            theme: "monokai",
 | 
				
			||||||
            lineNumbers: false,
 | 
					            lineNumbers: false,
 | 
				
			||||||
            readOnly: this.readOnly,
 | 
					            readOnly: this.readOnly,
 | 
				
			||||||
            autoRefresh: true,
 | 
					            autoRefresh: true,
 | 
				
			||||||
@ -38,5 +37,4 @@ export class CodeMirrorTextarea extends LitElement {
 | 
				
			|||||||
            this.editor?.save();
 | 
					            this.editor?.save();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,12 +2,11 @@ import { customElement, html, LitElement } from "lit-element";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@customElement("pb-dropdown")
 | 
					@customElement("pb-dropdown")
 | 
				
			||||||
export class DropdownButton extends LitElement {
 | 
					export class DropdownButton extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super()
 | 
					        super();
 | 
				
			||||||
        const menu = <HTMLElement>this.querySelector('.pf-c-dropdown__menu')!;
 | 
					        const menu = <HTMLElement>this.querySelector(".pf-c-dropdown__menu")!;
 | 
				
			||||||
        this.querySelectorAll("button").forEach(btn => {
 | 
					        this.querySelectorAll("button").forEach((btn) => {
 | 
				
			||||||
            btn.addEventListener("click", e => {
 | 
					            btn.addEventListener("click", (e) => {
 | 
				
			||||||
                menu.hidden = !menu.hidden;
 | 
					                menu.hidden = !menu.hidden;
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@ -16,5 +15,4 @@ export class DropdownButton extends LitElement {
 | 
				
			|||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return html`<slot></slot>`;
 | 
					        return html`<slot></slot>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,11 @@
 | 
				
			|||||||
import { LitElement, html, customElement, property } from 'lit-element';
 | 
					import { LitElement, html, customElement, property } from "lit-element";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ComparisonHash {
 | 
					interface ComparisonHash {
 | 
				
			||||||
    [key: string]: (a: any, b: any) => boolean
 | 
					    [key: string]: (a: any, b: any) => boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("fetch-fill-slot")
 | 
					@customElement("fetch-fill-slot")
 | 
				
			||||||
export class FetchFillSlot extends LitElement {
 | 
					export class FetchFillSlot extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    url: string = "";
 | 
					    url: string = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -18,14 +17,30 @@ export class FetchFillSlot extends LitElement {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    comparison(slotName: string) {
 | 
					    comparison(slotName: string) {
 | 
				
			||||||
        let comparisonOperatorsHash = <ComparisonHash>{
 | 
					        let comparisonOperatorsHash = <ComparisonHash>{
 | 
				
			||||||
            '<': function (a: any, b: any) { return a < b; },
 | 
					            "<": function (a: any, b: any) {
 | 
				
			||||||
            '>': function (a: any, b: any) { return a > b; },
 | 
					                return a < b;
 | 
				
			||||||
            '>=': function (a: any, b: any) { return a >= b; },
 | 
					            },
 | 
				
			||||||
            '<=': function (a: any, b: any) { return a <= b; },
 | 
					            ">": function (a: any, b: any) {
 | 
				
			||||||
            '==': function (a: any, b: any) { return a == b; },
 | 
					                return a > b;
 | 
				
			||||||
            '!=': function (a: any, b: any) { return a != b; },
 | 
					            },
 | 
				
			||||||
            '===': function (a: any, b: any) { return a === b; },
 | 
					            ">=": function (a: any, b: any) {
 | 
				
			||||||
            '!==': function (a: any, b: any) { return a !== b; },
 | 
					                return a >= b;
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "<=": function (a: any, b: any) {
 | 
				
			||||||
 | 
					                return a <= b;
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "==": function (a: any, b: any) {
 | 
				
			||||||
 | 
					                return a == b;
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "!=": function (a: any, b: any) {
 | 
				
			||||||
 | 
					                return a != b;
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "===": function (a: any, b: any) {
 | 
				
			||||||
 | 
					                return a === b;
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "!==": function (a: any, b: any) {
 | 
				
			||||||
 | 
					                return a !== b;
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        const tokens = slotName.split(" ");
 | 
					        const tokens = slotName.split(" ");
 | 
				
			||||||
        if (tokens.length < 3) {
 | 
					        if (tokens.length < 3) {
 | 
				
			||||||
@ -45,13 +60,16 @@ export class FetchFillSlot extends LitElement {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        const comp = tokens[1];
 | 
					        const comp = tokens[1];
 | 
				
			||||||
        if (!(comp in comparisonOperatorsHash)) {
 | 
					        if (!(comp in comparisonOperatorsHash)) {
 | 
				
			||||||
            throw new Error("Invalid comparison")
 | 
					            throw new Error("Invalid comparison");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return comparisonOperatorsHash[comp](a, b);
 | 
					        return comparisonOperatorsHash[comp](a, b);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    firstUpdated() {
 | 
					    firstUpdated() {
 | 
				
			||||||
        fetch(this.url).then(r => r.json()).then(r => r[this.key]).then(r => this.value = r);
 | 
					        fetch(this.url)
 | 
				
			||||||
 | 
					            .then((r) => r.json())
 | 
				
			||||||
 | 
					            .then((r) => r[this.key])
 | 
				
			||||||
 | 
					            .then((r) => (this.value = r));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
@ -59,13 +77,13 @@ export class FetchFillSlot extends LitElement {
 | 
				
			|||||||
            return html`<slot></slot>`;
 | 
					            return html`<slot></slot>`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let selectedSlot = "";
 | 
					        let selectedSlot = "";
 | 
				
			||||||
        this.querySelectorAll("[slot]").forEach(slot => {
 | 
					        this.querySelectorAll("[slot]").forEach((slot) => {
 | 
				
			||||||
            const comp = slot.getAttribute("slot")!;
 | 
					            const comp = slot.getAttribute("slot")!;
 | 
				
			||||||
            if (this.comparison(comp)) {
 | 
					            if (this.comparison(comp)) {
 | 
				
			||||||
                selectedSlot = comp;
 | 
					                selectedSlot = comp;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        this.querySelectorAll("[data-value]").forEach(dv => {
 | 
					        this.querySelectorAll("[data-value]").forEach((dv) => {
 | 
				
			||||||
            dv.textContent = this.value;
 | 
					            dv.textContent = this.value;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        return html`<slot name=${selectedSlot}></slot>`;
 | 
					        return html`<slot name=${selectedSlot}></slot>`;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
import { LitElement, html, customElement, property } from 'lit-element';
 | 
					import { LitElement, html, customElement, property } from "lit-element";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const LEVEL_ICON_MAP: { [key: string]: string } = {
 | 
					const LEVEL_ICON_MAP: { [key: string]: string } = {
 | 
				
			||||||
    "error": "fas fa-exclamation-circle",
 | 
					    error: "fas fa-exclamation-circle",
 | 
				
			||||||
    "warning": "fas fa-exclamation-triangle",
 | 
					    warning: "fas fa-exclamation-triangle",
 | 
				
			||||||
    "success": "fas fa-check-circle",
 | 
					    success: "fas fa-check-circle",
 | 
				
			||||||
    "info": "fas fa-info",
 | 
					    info: "fas fa-info",
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let ID = function (prefix: string) {
 | 
					let ID = function (prefix: string) {
 | 
				
			||||||
@ -23,7 +23,6 @@ interface Message {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@customElement("pb-messages")
 | 
					@customElement("pb-messages")
 | 
				
			||||||
export class Messages extends LitElement {
 | 
					export class Messages extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    url: string = "";
 | 
					    url: string = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -39,20 +38,27 @@ export class Messages extends LitElement {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetchMessages() {
 | 
					    fetchMessages() {
 | 
				
			||||||
        return fetch(this.url).then(r => r.json()).then(r => this.messages = r).then((r) => {
 | 
					        return fetch(this.url)
 | 
				
			||||||
            const container = <HTMLElement>this.querySelector(".pf-c-alert-group")!;
 | 
					            .then((r) => r.json())
 | 
				
			||||||
            r.forEach((message: Message) => {
 | 
					            .then((r) => (this.messages = r))
 | 
				
			||||||
                const messageElement = this.renderMessage(message);
 | 
					            .then((r) => {
 | 
				
			||||||
                container.appendChild(messageElement);
 | 
					                const container = <HTMLElement>(
 | 
				
			||||||
 | 
					                    this.querySelector(".pf-c-alert-group")!
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                r.forEach((message: Message) => {
 | 
				
			||||||
 | 
					                    const messageElement = this.renderMessage(message);
 | 
				
			||||||
 | 
					                    container.appendChild(messageElement);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderMessage(message: Message): ChildNode {
 | 
					    renderMessage(message: Message): ChildNode {
 | 
				
			||||||
        const id = ID("pb-message");
 | 
					        const id = ID("pb-message");
 | 
				
			||||||
        const el = document.createElement("template");
 | 
					        const el = document.createElement("template");
 | 
				
			||||||
        el.innerHTML = `<li id=${id} class="pf-c-alert-group__item">
 | 
					        el.innerHTML = `<li id=${id} class="pf-c-alert-group__item">
 | 
				
			||||||
            <div class="pf-c-alert pf-m-${message.level_tag} ${message.level_tag === 'error' ? 'pf-m-danger': ''}">
 | 
					            <div class="pf-c-alert pf-m-${message.level_tag} ${
 | 
				
			||||||
 | 
					            message.level_tag === "error" ? "pf-m-danger" : ""
 | 
				
			||||||
 | 
					        }">
 | 
				
			||||||
                <div class="pf-c-alert__icon">
 | 
					                <div class="pf-c-alert__icon">
 | 
				
			||||||
                    <i class="${LEVEL_ICON_MAP[message.level_tag]}"></i>
 | 
					                    <i class="${LEVEL_ICON_MAP[message.level_tag]}"></i>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,6 @@ import { convertToSlug } from "../utils";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@customElement("pb-modal-button")
 | 
					@customElement("pb-modal-button")
 | 
				
			||||||
export class ModalButton extends LitElement {
 | 
					export class ModalButton extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    href: string = "";
 | 
					    href: string = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -18,12 +17,12 @@ export class ModalButton extends LitElement {
 | 
				
			|||||||
    open: boolean = false;
 | 
					    open: boolean = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static get styles() {
 | 
					    static get styles() {
 | 
				
			||||||
        return [ModalBoxStyle, BullseyeStyle, BackdropStyle]
 | 
					        return [ModalBoxStyle, BullseyeStyle, BackdropStyle];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        window.addEventListener("keyup", e => {
 | 
					        window.addEventListener("keyup", (e) => {
 | 
				
			||||||
            if (e.code === "Escape") {
 | 
					            if (e.code === "Escape") {
 | 
				
			||||||
                this.open = false;
 | 
					                this.open = false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -33,84 +32,116 @@ export class ModalButton extends LitElement {
 | 
				
			|||||||
    setContent(content: string) {
 | 
					    setContent(content: string) {
 | 
				
			||||||
        this.querySelector("[slot=modal]")!.innerHTML = content;
 | 
					        this.querySelector("[slot=modal]")!.innerHTML = content;
 | 
				
			||||||
        // Ensure links close the modal
 | 
					        // Ensure links close the modal
 | 
				
			||||||
        this.querySelectorAll<HTMLAnchorElement>("[slot=modal] a").forEach(a => {
 | 
					        this.querySelectorAll<HTMLAnchorElement>("[slot=modal] a").forEach(
 | 
				
			||||||
            // Make click on a close the modal
 | 
					            (a) => {
 | 
				
			||||||
            a.addEventListener("click", e => {
 | 
					                // Make click on a close the modal
 | 
				
			||||||
                e.preventDefault();
 | 
					                a.addEventListener("click", (e) => {
 | 
				
			||||||
                this.open = false;
 | 
					                    e.preventDefault();
 | 
				
			||||||
            });
 | 
					                    this.open = false;
 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        // Make name field update slug field
 | 
					 | 
				
			||||||
        this.querySelectorAll<HTMLInputElement>("input[name=name]").forEach((input) => {
 | 
					 | 
				
			||||||
            input.addEventListener("input", (e) => {
 | 
					 | 
				
			||||||
                const form = input.closest("form");
 | 
					 | 
				
			||||||
                if (form === null) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                const slugField = form.querySelector<HTMLInputElement>("input[name=slug]");
 | 
					 | 
				
			||||||
                if (!slugField) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                slugField.value = convertToSlug(input.value);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        // Ensure forms sends in AJAX
 | 
					 | 
				
			||||||
        this.querySelectorAll<HTMLFormElement>("[slot=modal] form").forEach(form => {
 | 
					 | 
				
			||||||
            form.addEventListener('submit', (e) => {
 | 
					 | 
				
			||||||
                e.preventDefault();
 | 
					 | 
				
			||||||
                let formData = new FormData(form);
 | 
					 | 
				
			||||||
                fetch((form.action === window.location.toString()) ? this.href : form.action, {
 | 
					 | 
				
			||||||
                    method: form.method,
 | 
					 | 
				
			||||||
                    body: formData,
 | 
					 | 
				
			||||||
                }).then((response) => {
 | 
					 | 
				
			||||||
                    return response.text();
 | 
					 | 
				
			||||||
                }).then(data => {
 | 
					 | 
				
			||||||
                    if (data.indexOf("csrfmiddlewaretoken") !== -1) {
 | 
					 | 
				
			||||||
                        this.setContent(data);
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        this.open = false;
 | 
					 | 
				
			||||||
                        this.dispatchEvent(new CustomEvent('hashchange', { bubbles: true }));
 | 
					 | 
				
			||||||
                        updateMessages();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }).catch((e) => {
 | 
					 | 
				
			||||||
                    console.error(e);
 | 
					 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            });
 | 
					            }
 | 
				
			||||||
        });
 | 
					        );
 | 
				
			||||||
 | 
					        // Make name field update slug field
 | 
				
			||||||
 | 
					        this.querySelectorAll<HTMLInputElement>("input[name=name]").forEach(
 | 
				
			||||||
 | 
					            (input) => {
 | 
				
			||||||
 | 
					                input.addEventListener("input", (e) => {
 | 
				
			||||||
 | 
					                    const form = input.closest("form");
 | 
				
			||||||
 | 
					                    if (form === null) {
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    const slugField = form.querySelector<HTMLInputElement>(
 | 
				
			||||||
 | 
					                        "input[name=slug]"
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    if (!slugField) {
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    slugField.value = convertToSlug(input.value);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        // Ensure forms sends in AJAX
 | 
				
			||||||
 | 
					        this.querySelectorAll<HTMLFormElement>("[slot=modal] form").forEach(
 | 
				
			||||||
 | 
					            (form) => {
 | 
				
			||||||
 | 
					                form.addEventListener("submit", (e) => {
 | 
				
			||||||
 | 
					                    e.preventDefault();
 | 
				
			||||||
 | 
					                    let formData = new FormData(form);
 | 
				
			||||||
 | 
					                    fetch(
 | 
				
			||||||
 | 
					                        form.action === window.location.toString()
 | 
				
			||||||
 | 
					                            ? this.href
 | 
				
			||||||
 | 
					                            : form.action,
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            method: form.method,
 | 
				
			||||||
 | 
					                            body: formData,
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                        .then((response) => {
 | 
				
			||||||
 | 
					                            return response.text();
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                        .then((data) => {
 | 
				
			||||||
 | 
					                            if (data.indexOf("csrfmiddlewaretoken") !== -1) {
 | 
				
			||||||
 | 
					                                this.setContent(data);
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                this.open = false;
 | 
				
			||||||
 | 
					                                this.dispatchEvent(
 | 
				
			||||||
 | 
					                                    new CustomEvent("hashchange", {
 | 
				
			||||||
 | 
					                                        bubbles: true,
 | 
				
			||||||
 | 
					                                    })
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					                                updateMessages();
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                        .catch((e) => {
 | 
				
			||||||
 | 
					                            console.error(e);
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onClick(e: MouseEvent) {
 | 
					    onClick(e: MouseEvent) {
 | 
				
			||||||
        const request = new Request(
 | 
					        const request = new Request(this.href);
 | 
				
			||||||
            this.href,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        fetch(request, {
 | 
					        fetch(request, {
 | 
				
			||||||
            mode: 'same-origin',
 | 
					            mode: "same-origin",
 | 
				
			||||||
        }).then(r => r.text()).then((t) => {
 | 
					        })
 | 
				
			||||||
            this.setContent(t);
 | 
					            .then((r) => r.text())
 | 
				
			||||||
            this.open = true;
 | 
					            .then((t) => {
 | 
				
			||||||
        }).catch(e => {
 | 
					                this.setContent(t);
 | 
				
			||||||
            console.error(e);
 | 
					                this.open = true;
 | 
				
			||||||
        });
 | 
					            })
 | 
				
			||||||
 | 
					            .catch((e) => {
 | 
				
			||||||
 | 
					                console.error(e);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderModal() {
 | 
					    renderModal() {
 | 
				
			||||||
        return html`<div class="pf-c-backdrop">
 | 
					        return html`<div class="pf-c-backdrop">
 | 
				
			||||||
            <div class="pf-l-bullseye">
 | 
					            <div class="pf-l-bullseye">
 | 
				
			||||||
                <div class="pf-c-modal-box pf-m-md" role="dialog" aria-modal="true" aria-labelledby="modal-md-title" aria-describedby="modal-md-description">
 | 
					                <div
 | 
				
			||||||
                    <button @click=${() => this.open = false} class="pf-c-button pf-m-plain" type="button" aria-label="Close dialog">
 | 
					                    class="pf-c-modal-box pf-m-md"
 | 
				
			||||||
 | 
					                    role="dialog"
 | 
				
			||||||
 | 
					                    aria-modal="true"
 | 
				
			||||||
 | 
					                    aria-labelledby="modal-md-title"
 | 
				
			||||||
 | 
					                    aria-describedby="modal-md-description"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <button
 | 
				
			||||||
 | 
					                        @click=${() => (this.open = false)}
 | 
				
			||||||
 | 
					                        class="pf-c-button pf-m-plain"
 | 
				
			||||||
 | 
					                        type="button"
 | 
				
			||||||
 | 
					                        aria-label="Close dialog"
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
                        <i class="fas fa-times" aria-hidden="true"></i>
 | 
					                        <i class="fas fa-times" aria-hidden="true"></i>
 | 
				
			||||||
                    </button>
 | 
					                    </button>
 | 
				
			||||||
                    <slot name="modal">
 | 
					                    <slot name="modal"> </slot>
 | 
				
			||||||
                    </slot>
 | 
					 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>`;
 | 
					        </div>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return html`
 | 
					        return html` <slot
 | 
				
			||||||
            <slot name="trigger" @click=${(e: any) => this.onClick(e)}></slot>
 | 
					                name="trigger"
 | 
				
			||||||
 | 
					                @click=${(e: any) => this.onClick(e)}
 | 
				
			||||||
 | 
					            ></slot>
 | 
				
			||||||
            ${this.open ? this.renderModal() : ""}`;
 | 
					            ${this.open ? this.renderModal() : ""}`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,27 +1,34 @@
 | 
				
			|||||||
import { LitElement, html, customElement } from 'lit-element';
 | 
					import { LitElement, html, customElement } from "lit-element";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("pb-tabs")
 | 
					@customElement("pb-tabs")
 | 
				
			||||||
export class Tabs extends LitElement {
 | 
					export class Tabs extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    _currentPage? = "";
 | 
					    _currentPage? = "";
 | 
				
			||||||
    _firstPage? = "";
 | 
					    _firstPage? = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get currentPage() {
 | 
					    get currentPage() {
 | 
				
			||||||
        return this._currentPage
 | 
					        return this._currentPage;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    set currentPage(value) {
 | 
					    set currentPage(value) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            // Show active tab page
 | 
					            // Show active tab page
 | 
				
			||||||
            this.querySelector(`.pf-c-tab-content[tab-name='${value}']`)?.removeAttribute("hidden");
 | 
					            this.querySelector(
 | 
				
			||||||
 | 
					                `.pf-c-tab-content[tab-name='${value}']`
 | 
				
			||||||
 | 
					            )?.removeAttribute("hidden");
 | 
				
			||||||
            // Update active status on buttons
 | 
					            // Update active status on buttons
 | 
				
			||||||
            this.querySelector(`.pf-c-tabs__item[tab-name='${value}']`)?.classList.add("pf-m-current");
 | 
					            this.querySelector(
 | 
				
			||||||
 | 
					                `.pf-c-tabs__item[tab-name='${value}']`
 | 
				
			||||||
 | 
					            )?.classList.add("pf-m-current");
 | 
				
			||||||
            // Hide other tab pages
 | 
					            // Hide other tab pages
 | 
				
			||||||
            this.querySelectorAll(`.pf-c-tab-content:not([tab-name='${value}'])`).forEach((el) => {
 | 
					            this.querySelectorAll(
 | 
				
			||||||
 | 
					                `.pf-c-tab-content:not([tab-name='${value}'])`
 | 
				
			||||||
 | 
					            ).forEach((el) => {
 | 
				
			||||||
                el.setAttribute("hidden", "");
 | 
					                el.setAttribute("hidden", "");
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            // Update active status on other buttons
 | 
					            // Update active status on other buttons
 | 
				
			||||||
            this.querySelectorAll(`.pf-c-tabs__item:not([tab-name='${value}'])`).forEach((el) => {
 | 
					            this.querySelectorAll(
 | 
				
			||||||
 | 
					                `.pf-c-tabs__item:not([tab-name='${value}'])`
 | 
				
			||||||
 | 
					            ).forEach((el) => {
 | 
				
			||||||
                el.classList.remove("pf-m-current");
 | 
					                el.classList.remove("pf-m-current");
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            // Update window hash
 | 
					            // Update window hash
 | 
				
			||||||
@ -37,7 +44,9 @@ export class Tabs extends LitElement {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    firstUpdated() {
 | 
					    firstUpdated() {
 | 
				
			||||||
        this._firstPage = this.querySelector(".pf-c-tab-content")?.getAttribute("tab-name")!;
 | 
					        this._firstPage = this.querySelector(".pf-c-tab-content")?.getAttribute(
 | 
				
			||||||
 | 
					            "tab-name"
 | 
				
			||||||
 | 
					        )!;
 | 
				
			||||||
        if (window.location.hash) {
 | 
					        if (window.location.hash) {
 | 
				
			||||||
            this.currentPage = window.location.hash;
 | 
					            this.currentPage = window.location.hash;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@ -47,8 +56,7 @@ export class Tabs extends LitElement {
 | 
				
			|||||||
            button.addEventListener("click", (e) => {
 | 
					            button.addEventListener("click", (e) => {
 | 
				
			||||||
                let tabPage = button.parentElement?.getAttribute("tab-name")!;
 | 
					                let tabPage = button.parentElement?.getAttribute("tab-name")!;
 | 
				
			||||||
                this.currentPage = tabPage;
 | 
					                this.currentPage = tabPage;
 | 
				
			||||||
            })
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,23 +11,25 @@ document.querySelectorAll("input[type=search]").forEach((si) => {
 | 
				
			|||||||
document.querySelectorAll("[data-pb-fetch-fill]").forEach((el) => {
 | 
					document.querySelectorAll("[data-pb-fetch-fill]").forEach((el) => {
 | 
				
			||||||
    const url = el.dataset.pbFetchFill;
 | 
					    const url = el.dataset.pbFetchFill;
 | 
				
			||||||
    const key = el.dataset.pbFetchKey;
 | 
					    const key = el.dataset.pbFetchKey;
 | 
				
			||||||
    fetch(url).then(r => r.json()).then(r => {
 | 
					    fetch(url)
 | 
				
			||||||
        el.textContent = r[key];
 | 
					        .then((r) => r.json())
 | 
				
			||||||
        el.value = r[key];
 | 
					        .then((r) => {
 | 
				
			||||||
    });
 | 
					            el.textContent = r[key];
 | 
				
			||||||
 | 
					            el.value = r[key];
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Modal
 | 
					// Modal
 | 
				
			||||||
document.querySelectorAll("[data-target='modal']").forEach((m) => {
 | 
					document.querySelectorAll("[data-target='modal']").forEach((m) => {
 | 
				
			||||||
    m.addEventListener("click", (e) => {
 | 
					    m.addEventListener("click", (e) => {
 | 
				
			||||||
        const parentContainer = e.target.closest('[data-target="modal"]');
 | 
					        const parentContainer = e.target.closest('[data-target="modal"]');
 | 
				
			||||||
        const modalId = parentContainer.attributes['data-modal'].value;
 | 
					        const modalId = parentContainer.attributes["data-modal"].value;
 | 
				
			||||||
        document.querySelector(`#${modalId}`).removeAttribute("hidden");
 | 
					        document.querySelector(`#${modalId}`).removeAttribute("hidden");
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
document.querySelectorAll(".pf-c-modal-box [data-modal-close]").forEach((b) => {
 | 
					document.querySelectorAll(".pf-c-modal-box [data-modal-close]").forEach((b) => {
 | 
				
			||||||
    b.addEventListener("click", (e) => {
 | 
					    b.addEventListener("click", (e) => {
 | 
				
			||||||
        const parentContainer = e.target.closest('.pf-c-backdrop');
 | 
					        const parentContainer = e.target.closest(".pf-c-backdrop");
 | 
				
			||||||
        parentContainer.setAttribute("hidden", true);
 | 
					        parentContainer.setAttribute("hidden", true);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@ -35,31 +37,37 @@ document.querySelectorAll(".pf-c-modal-box [data-modal-close]").forEach((b) => {
 | 
				
			|||||||
// Make Checkbox label click trigger checkbox toggle
 | 
					// Make Checkbox label click trigger checkbox toggle
 | 
				
			||||||
document.querySelectorAll(".pf-c-check__label").forEach((checkLabel) => {
 | 
					document.querySelectorAll(".pf-c-check__label").forEach((checkLabel) => {
 | 
				
			||||||
    checkLabel.addEventListener("click", (e) => {
 | 
					    checkLabel.addEventListener("click", (e) => {
 | 
				
			||||||
        const checkbox = e.target.parentElement.querySelector("input[type=checkbox]");
 | 
					        const checkbox = e.target.parentElement.querySelector(
 | 
				
			||||||
 | 
					            "input[type=checkbox]"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        checkbox.checked = !checkbox.checked;
 | 
					        checkbox.checked = !checkbox.checked;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Hamburger Menu
 | 
					// Hamburger Menu
 | 
				
			||||||
document.querySelectorAll(".pf-c-page__header-brand-toggle>button").forEach((toggle) => {
 | 
					document
 | 
				
			||||||
    toggle.addEventListener("click", (e) => {
 | 
					    .querySelectorAll(".pf-c-page__header-brand-toggle>button")
 | 
				
			||||||
        const sidebar = document.querySelector(".pf-c-page__sidebar");
 | 
					    .forEach((toggle) => {
 | 
				
			||||||
        if (sidebar.classList.contains("pf-m-expanded")) {
 | 
					        toggle.addEventListener("click", (e) => {
 | 
				
			||||||
            // Sidebar already expanded
 | 
					            const sidebar = document.querySelector(".pf-c-page__sidebar");
 | 
				
			||||||
            sidebar.classList.remove("pf-m-expanded");
 | 
					            if (sidebar.classList.contains("pf-m-expanded")) {
 | 
				
			||||||
            sidebar.style.zIndex = 0;
 | 
					                // Sidebar already expanded
 | 
				
			||||||
        } else {
 | 
					                sidebar.classList.remove("pf-m-expanded");
 | 
				
			||||||
            // Sidebar not expanded yet
 | 
					                sidebar.style.zIndex = 0;
 | 
				
			||||||
            sidebar.classList.add("pf-m-expanded");
 | 
					            } else {
 | 
				
			||||||
            sidebar.style.zIndex = 200;
 | 
					                // Sidebar not expanded yet
 | 
				
			||||||
        }
 | 
					                sidebar.classList.add("pf-m-expanded");
 | 
				
			||||||
 | 
					                sidebar.style.zIndex = 200;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Collapsable Menus in Sidebar
 | 
					// Collapsable Menus in Sidebar
 | 
				
			||||||
document.querySelectorAll(".pf-m-expandable>.pf-c-nav__link").forEach((menu) => {
 | 
					document
 | 
				
			||||||
    menu.addEventListener("click", (e) => {
 | 
					    .querySelectorAll(".pf-m-expandable>.pf-c-nav__link")
 | 
				
			||||||
        e.preventDefault();
 | 
					    .forEach((menu) => {
 | 
				
			||||||
        menu.parentElement.classList.toggle("pf-m-expanded");
 | 
					        menu.addEventListener("click", (e) => {
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            menu.parentElement.classList.toggle("pf-m-expanded");
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,13 @@
 | 
				
			|||||||
import "./legacy.js";
 | 
					import "./legacy.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import './elements/ActionButton';
 | 
					import "./elements/ActionButton";
 | 
				
			||||||
import './elements/AdminSidebar';
 | 
					import "./elements/AdminSidebar";
 | 
				
			||||||
import './elements/CodeMirror';
 | 
					import "./elements/CodeMirror";
 | 
				
			||||||
import './elements/Dropdown';
 | 
					import "./elements/Dropdown";
 | 
				
			||||||
import './elements/FetchFillSlot';
 | 
					import "./elements/FetchFillSlot";
 | 
				
			||||||
import './elements/Messages';
 | 
					import "./elements/Messages";
 | 
				
			||||||
import './elements/ModalButton';
 | 
					import "./elements/ModalButton";
 | 
				
			||||||
import './elements/Tabs';
 | 
					import "./elements/Tabs";
 | 
				
			||||||
import './pages/AdminSiteShell';
 | 
					import "./pages/AdminSiteShell";
 | 
				
			||||||
import './pages/FlowShellCard';
 | 
					import "./pages/FlowShellCard";
 | 
				
			||||||
import "./elements/AdminLoginsChart";
 | 
					import "./elements/AdminLoginsChart";
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,11 @@
 | 
				
			|||||||
import { css, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
					import {
 | 
				
			||||||
 | 
					    css,
 | 
				
			||||||
 | 
					    customElement,
 | 
				
			||||||
 | 
					    html,
 | 
				
			||||||
 | 
					    LitElement,
 | 
				
			||||||
 | 
					    property,
 | 
				
			||||||
 | 
					    TemplateResult,
 | 
				
			||||||
 | 
					} from "lit-element";
 | 
				
			||||||
// @ts-ignore
 | 
					// @ts-ignore
 | 
				
			||||||
import BullseyeStyle from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
 | 
					import BullseyeStyle from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
 | 
				
			||||||
// @ts-ignore
 | 
					// @ts-ignore
 | 
				
			||||||
@ -6,7 +13,6 @@ import SpinnerStyle from "@patternfly/patternfly/components/Spinner/spinner.css"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@customElement("pb-admin-shell")
 | 
					@customElement("pb-admin-shell")
 | 
				
			||||||
export class AdminSiteShell extends LitElement {
 | 
					export class AdminSiteShell extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    set defaultUrl(value: string) {
 | 
					    set defaultUrl(value: string) {
 | 
				
			||||||
        if (window.location.hash === "" && value !== undefined) {
 | 
					        if (window.location.hash === "" && value !== undefined) {
 | 
				
			||||||
@ -18,18 +24,22 @@ export class AdminSiteShell extends LitElement {
 | 
				
			|||||||
    loading: boolean = false;
 | 
					    loading: boolean = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static get styles() {
 | 
					    static get styles() {
 | 
				
			||||||
        return [css`
 | 
					        return [
 | 
				
			||||||
            :host {
 | 
					            css`
 | 
				
			||||||
                position: relative;
 | 
					                :host {
 | 
				
			||||||
            }
 | 
					                    position: relative;
 | 
				
			||||||
            :host .pf-l-bullseye {
 | 
					                }
 | 
				
			||||||
                position: absolute;
 | 
					                :host .pf-l-bullseye {
 | 
				
			||||||
                height: 100%;
 | 
					                    position: absolute;
 | 
				
			||||||
                width: 100%;
 | 
					                    height: 100%;
 | 
				
			||||||
                top: 0;
 | 
					                    width: 100%;
 | 
				
			||||||
                left: 0;
 | 
					                    top: 0;
 | 
				
			||||||
            }
 | 
					                    left: 0;
 | 
				
			||||||
        `, BullseyeStyle, SpinnerStyle];
 | 
					                }
 | 
				
			||||||
 | 
					            `,
 | 
				
			||||||
 | 
					            BullseyeStyle,
 | 
				
			||||||
 | 
					            SpinnerStyle,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
@ -41,49 +51,56 @@ export class AdminSiteShell extends LitElement {
 | 
				
			|||||||
    loadContent() {
 | 
					    loadContent() {
 | 
				
			||||||
        let url = window.location.hash.slice(1, Infinity);
 | 
					        let url = window.location.hash.slice(1, Infinity);
 | 
				
			||||||
        if (url === "") {
 | 
					        if (url === "") {
 | 
				
			||||||
            return
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.loading = true;
 | 
					        this.loading = true;
 | 
				
			||||||
        fetch(url).then(r => r.text()).then((t) => {
 | 
					        fetch(url)
 | 
				
			||||||
            this.querySelector("[slot=body]")!.innerHTML = t;
 | 
					            .then((r) => r.text())
 | 
				
			||||||
        }).then(() => {
 | 
					            .then((t) => {
 | 
				
			||||||
            // Ensure anchors only change the hash
 | 
					                this.querySelector("[slot=body]")!.innerHTML = t;
 | 
				
			||||||
            this.querySelectorAll<HTMLAnchorElement>("a:not(.pb-root-link)").forEach(a => {
 | 
					            })
 | 
				
			||||||
                if (a.href === "") {
 | 
					            .then(() => {
 | 
				
			||||||
                    return;
 | 
					                // Ensure anchors only change the hash
 | 
				
			||||||
                }
 | 
					                this.querySelectorAll<HTMLAnchorElement>(
 | 
				
			||||||
                try {
 | 
					                    "a:not(.pb-root-link)"
 | 
				
			||||||
                    const url = new URL(a.href);
 | 
					                ).forEach((a) => {
 | 
				
			||||||
                    const qs = url.search || "";
 | 
					                    if (a.href === "") {
 | 
				
			||||||
                    a.href = `#${url.pathname}${qs}`;
 | 
					                        return;
 | 
				
			||||||
                } catch (e) {
 | 
					                    }
 | 
				
			||||||
                    a.href = `#${a.href}`;
 | 
					                    try {
 | 
				
			||||||
                }
 | 
					                        const url = new URL(a.href);
 | 
				
			||||||
            });
 | 
					                        const qs = url.search || "";
 | 
				
			||||||
            // Create refresh buttons
 | 
					                        a.href = `#${url.pathname}${qs}`;
 | 
				
			||||||
            this.querySelectorAll("[role=pb-refresh]").forEach(rt => {
 | 
					                    } catch (e) {
 | 
				
			||||||
                rt.addEventListener("click", e => {
 | 
					                        a.href = `#${a.href}`;
 | 
				
			||||||
                    this.loadContent();
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					                // Create refresh buttons
 | 
				
			||||||
 | 
					                this.querySelectorAll("[role=pb-refresh]").forEach((rt) => {
 | 
				
			||||||
 | 
					                    rt.addEventListener("click", (e) => {
 | 
				
			||||||
 | 
					                        this.loadContent();
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                this.loading = false;
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            this.loading = false;
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return html`
 | 
					        return html` ${this.loading
 | 
				
			||||||
        ${this.loading ? html`
 | 
					                ? html` <div class="pf-l-bullseye">
 | 
				
			||||||
        <div class="pf-l-bullseye">
 | 
					                      <div class="pf-l-bullseye__item">
 | 
				
			||||||
            <div class="pf-l-bullseye__item">
 | 
					                          <span
 | 
				
			||||||
                <span class="pf-c-spinner pf-m-xl" role="progressbar" aria-valuetext="Loading...">
 | 
					                              class="pf-c-spinner pf-m-xl"
 | 
				
			||||||
                    <span class="pf-c-spinner__clipper"></span>
 | 
					                              role="progressbar"
 | 
				
			||||||
                    <span class="pf-c-spinner__lead-ball"></span>
 | 
					                              aria-valuetext="Loading..."
 | 
				
			||||||
                    <span class="pf-c-spinner__tail-ball"></span>
 | 
					                          >
 | 
				
			||||||
                </span>
 | 
					                              <span class="pf-c-spinner__clipper"></span>
 | 
				
			||||||
            </div>
 | 
					                              <span class="pf-c-spinner__lead-ball"></span>
 | 
				
			||||||
        </div>`: ""}
 | 
					                              <span class="pf-c-spinner__tail-ball"></span>
 | 
				
			||||||
        <slot name="body">
 | 
					                          </span>
 | 
				
			||||||
        </slot>`;
 | 
					                      </div>
 | 
				
			||||||
 | 
					                  </div>`
 | 
				
			||||||
 | 
					                : ""}
 | 
				
			||||||
 | 
					            <slot name="body"> </slot>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import { LitElement, html, customElement, property } from 'lit-element';
 | 
					import { LitElement, html, customElement, property } from "lit-element";
 | 
				
			||||||
import { updateMessages } from "../elements/Messages";
 | 
					import { updateMessages } from "../elements/Messages";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum ResponseType {
 | 
					enum ResponseType {
 | 
				
			||||||
    redirect = "redirect",
 | 
					    redirect = "redirect",
 | 
				
			||||||
    template = "template"
 | 
					    template = "template",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Response {
 | 
					interface Response {
 | 
				
			||||||
@ -14,7 +14,6 @@ interface Response {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@customElement("pb-flow-shell-card")
 | 
					@customElement("pb-flow-shell-card")
 | 
				
			||||||
export class FlowShellCard extends LitElement {
 | 
					export class FlowShellCard extends LitElement {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    flowBodyUrl: string = "";
 | 
					    flowBodyUrl: string = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,19 +25,23 @@ export class FlowShellCard extends LitElement {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    firstUpdated() {
 | 
					    firstUpdated() {
 | 
				
			||||||
        fetch(this.flowBodyUrl).then(r => {
 | 
					        fetch(this.flowBodyUrl)
 | 
				
			||||||
            if (!r.ok) {
 | 
					            .then((r) => {
 | 
				
			||||||
                throw Error(r.statusText);
 | 
					                if (!r.ok) {
 | 
				
			||||||
            }
 | 
					                    throw Error(r.statusText);
 | 
				
			||||||
            return r;
 | 
					                }
 | 
				
			||||||
        }).then((r) => {
 | 
					                return r;
 | 
				
			||||||
            return r.json();
 | 
					            })
 | 
				
			||||||
        }).then((r) => {
 | 
					            .then((r) => {
 | 
				
			||||||
            this.updateCard(r);
 | 
					                return r.json();
 | 
				
			||||||
        }).catch((e) => {
 | 
					            })
 | 
				
			||||||
            // Catch JSON or Update errors
 | 
					            .then((r) => {
 | 
				
			||||||
            this.errorMessage(e);
 | 
					                this.updateCard(r);
 | 
				
			||||||
        });
 | 
					            })
 | 
				
			||||||
 | 
					            .catch((e) => {
 | 
				
			||||||
 | 
					                // Catch JSON or Update errors
 | 
				
			||||||
 | 
					                this.errorMessage(e);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async updateCard(data: Response) {
 | 
					    async updateCard(data: Response) {
 | 
				
			||||||
@ -54,13 +57,15 @@ export class FlowShellCard extends LitElement {
 | 
				
			|||||||
                this.loadFormCode();
 | 
					                this.loadFormCode();
 | 
				
			||||||
                this.setFormSubmitHandlers();
 | 
					                this.setFormSubmitHandlers();
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                console.log(`passbook/flows: unexpected data type ${data.type}`);
 | 
					                console.log(
 | 
				
			||||||
 | 
					                    `passbook/flows: unexpected data type ${data.type}`
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loadFormCode() {
 | 
					    loadFormCode() {
 | 
				
			||||||
        this.querySelectorAll("script").forEach(script => {
 | 
					        this.querySelectorAll("script").forEach((script) => {
 | 
				
			||||||
            let newScript = document.createElement("script");
 | 
					            let newScript = document.createElement("script");
 | 
				
			||||||
            newScript.src = script.src;
 | 
					            newScript.src = script.src;
 | 
				
			||||||
            document.head.appendChild(newScript);
 | 
					            document.head.appendChild(newScript);
 | 
				
			||||||
@ -78,7 +83,9 @@ export class FlowShellCard extends LitElement {
 | 
				
			|||||||
        for (let index = 0; index < form.elements.length; index++) {
 | 
					        for (let index = 0; index < form.elements.length; index++) {
 | 
				
			||||||
            const element = <HTMLInputElement>form.elements[index];
 | 
					            const element = <HTMLInputElement>form.elements[index];
 | 
				
			||||||
            if (element.value === form.action) {
 | 
					            if (element.value === form.action) {
 | 
				
			||||||
                console.log("passbook/flows: Found Form action URL in form elements, not changing form action.");
 | 
					                console.log(
 | 
				
			||||||
 | 
					                    "passbook/flows: Found Form action URL in form elements, not changing form action."
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -94,26 +101,31 @@ export class FlowShellCard extends LitElement {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setFormSubmitHandlers() {
 | 
					    setFormSubmitHandlers() {
 | 
				
			||||||
        this.querySelectorAll("form").forEach(form => {
 | 
					        this.querySelectorAll("form").forEach((form) => {
 | 
				
			||||||
            console.log(`passbook/flows: Checking for autosubmit attribute ${form}`);
 | 
					            console.log(
 | 
				
			||||||
 | 
					                `passbook/flows: Checking for autosubmit attribute ${form}`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            this.checkAutosubmit(form);
 | 
					            this.checkAutosubmit(form);
 | 
				
			||||||
            console.log(`passbook/flows: Setting action for form ${form}`);
 | 
					            console.log(`passbook/flows: Setting action for form ${form}`);
 | 
				
			||||||
            this.updateFormAction(form);
 | 
					            this.updateFormAction(form);
 | 
				
			||||||
            console.log(`passbook/flows: Adding handler for form ${form}`);
 | 
					            console.log(`passbook/flows: Adding handler for form ${form}`);
 | 
				
			||||||
            form.addEventListener('submit', (e) => {
 | 
					            form.addEventListener("submit", (e) => {
 | 
				
			||||||
                e.preventDefault();
 | 
					                e.preventDefault();
 | 
				
			||||||
                let formData = new FormData(form);
 | 
					                let formData = new FormData(form);
 | 
				
			||||||
                this.flowBody = undefined;
 | 
					                this.flowBody = undefined;
 | 
				
			||||||
                fetch(this.flowBodyUrl, {
 | 
					                fetch(this.flowBodyUrl, {
 | 
				
			||||||
                    method: 'post',
 | 
					                    method: "post",
 | 
				
			||||||
                    body: formData,
 | 
					                    body: formData,
 | 
				
			||||||
                }).then((response) => {
 | 
					                })
 | 
				
			||||||
                    return response.json()
 | 
					                    .then((response) => {
 | 
				
			||||||
                }).then(data => {
 | 
					                        return response.json();
 | 
				
			||||||
                    this.updateCard(data);
 | 
					                    })
 | 
				
			||||||
                }).catch((e) => {
 | 
					                    .then((data) => {
 | 
				
			||||||
                    this.errorMessage(e);
 | 
					                        this.updateCard(data);
 | 
				
			||||||
                });
 | 
					                    })
 | 
				
			||||||
 | 
					                    .catch((e) => {
 | 
				
			||||||
 | 
					                        this.errorMessage(e);
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            form.classList.add("pb-flow-wrapped");
 | 
					            form.classList.add("pb-flow-wrapped");
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@ -141,19 +153,22 @@ export class FlowShellCard extends LitElement {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loading() {
 | 
					    loading() {
 | 
				
			||||||
        return html`
 | 
					        return html` <div class="pf-c-login__main-body pb-loading">
 | 
				
			||||||
            <div class="pf-c-login__main-body pb-loading">
 | 
					            <span
 | 
				
			||||||
                <span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
 | 
					                class="pf-c-spinner"
 | 
				
			||||||
                    <span class="pf-c-spinner__clipper"></span>
 | 
					                role="progressbar"
 | 
				
			||||||
                    <span class="pf-c-spinner__lead-ball"></span>
 | 
					                aria-valuetext="Loading..."
 | 
				
			||||||
                    <span class="pf-c-spinner__tail-ball"></span>
 | 
					            >
 | 
				
			||||||
                </span>
 | 
					                <span class="pf-c-spinner__clipper"></span>
 | 
				
			||||||
            </div>`;
 | 
					                <span class="pf-c-spinner__lead-ball"></span>
 | 
				
			||||||
 | 
					                <span class="pf-c-spinner__tail-ball"></span>
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					        </div>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        if (this.flowBody) {
 | 
					        if (this.flowBody) {
 | 
				
			||||||
            return html(<TemplateStringsArray><unknown>[this.flowBody]);
 | 
					            return html(<TemplateStringsArray>(<unknown>[this.flowBody]));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return this.loading();
 | 
					        return this.loading();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,14 @@
 | 
				
			|||||||
export function getCookie(name: string) {
 | 
					export function getCookie(name: string) {
 | 
				
			||||||
    let cookieValue = null;
 | 
					    let cookieValue = null;
 | 
				
			||||||
    if (document.cookie && document.cookie !== '') {
 | 
					    if (document.cookie && document.cookie !== "") {
 | 
				
			||||||
        const cookies = document.cookie.split(';');
 | 
					        const cookies = document.cookie.split(";");
 | 
				
			||||||
        for (let i = 0; i < cookies.length; i++) {
 | 
					        for (let i = 0; i < cookies.length; i++) {
 | 
				
			||||||
            const cookie = cookies[i].trim();
 | 
					            const cookie = cookies[i].trim();
 | 
				
			||||||
            // Does this cookie string begin with the name we want?
 | 
					            // Does this cookie string begin with the name we want?
 | 
				
			||||||
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
 | 
					            if (cookie.substring(0, name.length + 1) === name + "=") {
 | 
				
			||||||
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
 | 
					                cookieValue = decodeURIComponent(
 | 
				
			||||||
 | 
					                    cookie.substring(name.length + 1)
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -17,6 +19,6 @@ export function getCookie(name: string) {
 | 
				
			|||||||
export function convertToSlug(text: string): string {
 | 
					export function convertToSlug(text: string): string {
 | 
				
			||||||
    return text
 | 
					    return text
 | 
				
			||||||
        .toLowerCase()
 | 
					        .toLowerCase()
 | 
				
			||||||
        .replace(/ /g, '-')
 | 
					        .replace(/ /g, "-")
 | 
				
			||||||
        .replace(/[^\w-]+/g, '');
 | 
					        .replace(/[^\w-]+/g, "");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,10 +9,6 @@
 | 
				
			|||||||
        "target": "es2017",
 | 
					        "target": "es2017",
 | 
				
			||||||
        "module": "es2015",
 | 
					        "module": "es2015",
 | 
				
			||||||
        "moduleResolution": "node",
 | 
					        "moduleResolution": "node",
 | 
				
			||||||
        "lib": [
 | 
					        "lib": ["es2017", "dom", "dom.iterable"]
 | 
				
			||||||
            "es2017",
 | 
					 | 
				
			||||||
            "dom",
 | 
					 | 
				
			||||||
            "dom.iterable"
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user