Version v0.9.1

This commit is contained in:
Johannes Meyer 2022-07-06 08:59:52 +00:00
parent 5f5afe1540
commit ede13be898
313 changed files with 127903 additions and 0 deletions

1
VERSION Normal file
View File

@ -0,0 +1 @@
v0.9.1

View File

@ -0,0 +1,35 @@
:root, :host {
--fa-font-regular: normal 400 1em/1 "Font Awesome 6 Free";
--fa-font-solid: normal 900 1em/1 "Font Awesome 6 Free";
}
@font-face {
font-family: 'Font Awesome 6 Free';
font-style: normal;
font-weight: 400;
font-display: block;
src: url('@{iplWebAssets}/font/awesome/fa-regular-400.woff2') format('woff2'),
url('@{iplWebAssets}/font/awesome/fa-regular-400.ttf') format('truetype');
}
.far,
.fa-regular {
font-family: 'Font Awesome 6 Free';
font-weight: 400;
}
@font-face {
font-family: 'Font Awesome 6 Free';
font-style: normal;
font-weight: 900;
font-display: block;
src: url('@{iplWebAssets}/font/awesome/fa-solid-900.woff2') format('woff2'),
url('@{iplWebAssets}/font/awesome/fa-solid-900.ttf') format('truetype');
}
.fa,
.fas,
.fa-solid {
font-family: 'Font Awesome 6 Free';
font-weight: 900;
}

143
asset/css/balls.less Normal file
View File

@ -0,0 +1,143 @@
@ball-pad: 1/6em;
.ball {
border-radius: 50%;
display: inline-block;
text-align: center;
}
.ball-size-xs {
height: 1/3em;
width: 1/3em;
}
.ball-size-s {
height: 0.5em;
width: 0.5em;
}
.ball-size-m {
height: 0.75em;
width: 0.75em;
line-height: 0;
i.icon:before {
font-size: .75 - @ball-pad * 2;
line-height: 1em;
}
}
.ball-size-ml {
height: 1em;
width: 1em;
line-height: 0;
i.icon {
line-height: 0.3;
&:before {
font-size: 0.8 - @ball-pad * 2;
line-height: 1 - @ball-pad * 2;
}
}
}
.ball-size-l {
height: 1.5em;
width: 1.5em;
line-height: 1em;
i.icon:before {
font-size: 1 - @ball-pad * 2;
line-height: 1.5 - @ball-pad * 2;
}
}
.ball-size-xl {
width: 2em;
height: 2em;
i.icon:before {
line-height: 2 - @ball-pad * 2;
}
}
.ball-outline(@color) {
border: @ball-pad solid @color;
color: @color;
}
.ball-solid(@color) {
background-color: @color;
color: var(--default-text-color-inverted, @default-text-color-inverted);
padding: @ball-pad;
}
.state-ball {
.ball();
&.state-pending {
.ball-solid(var(--state-pending, @state-pending));
}
&.state-up:not(.ball-size-l):not(.ball-size-xl) {
.ball-solid(var(--state-up, @state-up));
}
&.state-up.ball-size-l,
&.state-up.ball-size-xl {
.ball-outline(var(--state-up, @state-up));
}
&.state-down {
.ball-solid(var(--state-down, @state-down));
}
&.state-ok:not(.ball-size-l):not(.ball-size-xl) {
.ball-solid(var(--state-ok, @state-ok));
}
&.state-ok.ball-size-l,
&.state-ok.ball-size-xl {
.ball-outline(var(--state-ok, @state-ok));
}
&.state-warning {
.ball-solid(var(--state-warning, @state-warning));
}
&.state-critical {
.ball-solid(var(--state-critical, @state-critical));
}
&.state-unknown {
.ball-solid(var(--state-unknown, @state-unknown));
}
&.handled {
opacity: 0.6;
}
i {
text-align: center;
display: block;
&:before {
margin-right: 0;
}
}
// Specific icon styles
&.ball-size-l i {
&.fa-sitemap:before {
font-size: 8px; // px to ignore browser min font-size
}
}
&.ball-size-xl i {
&.fa-sitemap:before {
font-size: .857em;
line-height: (2 - @ball-pad * 2) / .857;
}
}
}

View File

@ -0,0 +1,34 @@
.cancel-button {
display: inline-flex;
align-items: baseline;
padding: .5em 1em;
.appearance(none);
.rounded-corners();
line-height: normal;
cursor: pointer;
background: var(--cancel-button-bg, @cancel-button-bg);
border: 1px solid var(--cancel-button-border-color, @cancel-button-border-color);
color: var(--cancel-button-color, @cancel-button-color);
&:focus,
&:hover {
background-color: var(--cancel-button-hover-bg, @cancel-button-hover-bg);
color: var(--cancel-button-hover-color, @cancel-button-hover-color);
}
&[disabled] {
background: none;
cursor: default;
border: 1px solid var(--control-disabled-color, @control-disabled-color);
color: var(--control-disabled-color, @control-disabled-color);
&:focus,
&:hover {
background: none;
color: var(--control-disabled-color, @control-disabled-color);
}
}
}

76
asset/css/controls.less Normal file
View File

@ -0,0 +1,76 @@
.pagination-control {
li > a {
color: var(--control-color, @control-color);
border-radius: .25em;
}
li > a:hover {
background: var(--control-hover-bg, @control-hover-bg);
}
.previous-page,
.next-page {
padding: .5em .25em;
i {
display: block;
}
i:before {
margin: 0;
}
}
.previous-page > i {
margin-left: -.125em;
}
.next-page > i {
margin-right: -.125em;
}
}
// Style
.control-button {
.appearance(none);
background: none;
border: none;
color: var(--control-color, @control-color);
.rounded-corners();
&:hover, &:focus, &.active {
background-color: var(--control-hover-bg, @control-hover-bg);
text-decoration: none;
}
&.disabled {
color: var(--control-disabled-color, @control-disabled-color);
&:hover {
background: none;
}
}
i.icon:before {
color: inherit;
}
}
// Layout
.control-button {
display: inline-block;
padding: .25em .5em;
> i.icon {
display: inline-flex;
align-items: center;
height: 100%;
}
i.icon:before {
margin-right: 0;
}
}

View File

@ -0,0 +1,9 @@
.flatpickr-input + .input {
padding-right: 2em;
& + .fa-calendar {
margin: .5em 1em 0 -3.5em;
padding: 0 .5em 0 1em;
pointer-events: none;
}
}

6338
asset/css/fontawesome.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
.horizontal-key-value {
display: flex;
padding: .25em 0;
align-items: baseline;
.key {
color: var(--default-text-color-light, @default-text-color-light);
flex: 0 0 auto;
white-space: nowrap;
width: 12em;
}
.value {
color: var(--default-text-color, @default-text-color);
flex: 1 1 auto;
}
}

View File

@ -0,0 +1,36 @@
@font-face {
font-family: 'Icinga-Icons';
src: url('@{iplWebAssets}/font/Icinga-Icons.eot');
src: url('@{iplWebAssets}/font/Icinga-Icons.eot') format('embedded-opentype'),
url('@{iplWebAssets}/font/Icinga-Icons.ttf') format('truetype'),
url('@{iplWebAssets}/font/Icinga-Icons.woff') format('woff'),
url('@{iplWebAssets}/font/Icinga-Icons.svg') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
}
[class^="iicon-"]:before, [class*=" iicon-"]:before {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'Icinga-Icons';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1em;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iicon-minimal:before {
content: "\e900";
}
.iicon-detailed:before {
content: "\e901";
}
.iicon-default:before {
content: "\e902";
}

12
asset/css/icons-base.less Normal file
View File

@ -0,0 +1,12 @@
i.icon {
vertical-align: middle; // Firefox will place icons weird otherwise
&:before {
display: inline-block;
min-width: 1em;
margin-right: .2em;
text-align: center;
text-decoration: inherit;
}
}

23
asset/css/mixin/card.less Normal file
View File

@ -0,0 +1,23 @@
.card() {
&.card {
.rounded-corners(.5em);
border: 1px solid var(--card-border-color, @card-border-color);
.card-header {
display: flex;
align-items: baseline;
justify-content: space-between;
padding: .5em;
border-bottom: 1px solid var(--card-border-color, @card-border-color);
.meta span {
font-size: 11/12em;
}
}
.card-body {
padding: .5em;
}
}
}

View File

@ -0,0 +1,20 @@
.rounded-corners(@border-radius: 0.4em) {
border-radius: @border-radius;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
.appearance(@appearance) {
-webkit-appearance: @appearance;
-moz-appearance: @appearance;
-ms-appearance: @appearance;
appearance: @appearance;
}
.box-shadow(@x: 0.2em; @y: 0.2em; @blur: 0.2em; @spread: 0; @color: rgba(83, 83, 83, 0.25)) {
-webkit-box-shadow: @arguments;
-moz-box-shadow: @arguments;
box-shadow: @arguments;
}

View File

@ -0,0 +1,31 @@
.state-badges() {
&.state-badges {
padding: 0;
ul {
padding: 0;
}
li {
display: inline-block;
}
li > ul > li:first-child:not(:last-child) .state-badge {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
li > ul > li:last-child:not(:first-child) .state-badge {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
> li:not(:last-child) {
margin-right: .25em;
}
li > ul > li:last-child {
margin-left: 1px;
}
}
}

316
asset/css/search-bar.less Normal file
View File

@ -0,0 +1,316 @@
// Style
.search-bar {
.rounded-corners(.25em);
background: var(--searchbar-bg, @searchbar-bg);
// Reset all input styles
input, [type="button"] {
.appearance(none);
border: none;
background: none;
}
// Submit button styles
input[type=submit],
button[type=submit],
button:not([type]) {
background: var(--primary-button-bg, @primary-button-bg);
color: var(--primary-button-color, @primary-button-color);
border-top-right-radius: .25em;
border-bottom-right-radius: .25em;
}
// General input styles
input:focus {
outline-offset: -1px;
}
// Hide the submit button, it must exist, but shouldn't be shown to the user
input[type=submit][value="hidden"] {
display: none;
}
// Left-most search dropdown style
button.search-options {
i.icon:before {
font-size: 1.2em;
margin-right: 0;
color: var(--control-color, @control-color);
}
&:disabled {
i.icon:before {
color: var(--control-disabled-color, @control-disabled-color);
}
}
}
// Scrollbar style
.filter-input-area {
// Firefox
scrollbar-width: thin;
scrollbar-color: var(--searchbar-scrollbar-bg, @searchbar-scrollbar-bg) transparent;
&::-webkit-scrollbar {
display: none;
height: .5em;
}
&:hover::-webkit-scrollbar {
display: initial;
}
&::-webkit-scrollbar-thumb {
border-radius: .25em;
background: var(--searchbar-scrollbar-bg, @searchbar-scrollbar-bg);
}
}
// Term styles
.filter-condition {
button {
border-radius: .4em 0 0 .4em;
background-color: var(--search-condition-remove-bg, @search-condition-remove-bg);
color: var(--search-condition-remove-color, @search-condition-remove-color);
&:after {
content: "";
position: absolute;
width: .4em;
height: 100%;
right: 0;
top: 0;
background-color: var(--searchbar-bg, @searchbar-bg);
border: .2em solid var(--search-condition-remove-bg, @search-condition-remove-bg);
border-width: 0 0 0 .2em;
border-top-left-radius: .4em;
border-bottom-left-radius: .4em;
}
}
input {
background-color: var(--search-term-bg, @search-term-bg);
color: var(--search-term-color, @search-term-color);
}
}
.terms > .filter-condition:first-child button {
border-radius: 0 .4em .4em 0;
&:before {
content: "";
position: absolute;
width: .4em;
height: 100%;
left: 0;
top: 0;
background-color: var(--searchbar-bg, @searchbar-bg);
border: .2em solid var(--search-condition-remove-bg, @search-condition-remove-bg);
border-width: 0 .2em 0 0;
border-top-right-radius: .4em;
border-bottom-right-radius: .4em;
}
&:after {
content: none;
}
}
.logical_operator,
.grouping_operator_open,
.grouping_operator_close {
input {
.rounded-corners();
background-color: var(--search-logical-operator-bg, @search-logical-operator-bg);
color: var(--search-logical-operator-color, @search-logical-operator-color);
}
}
.operator,
.logical_operator,
.grouping_operator_open,
.grouping_operator_close {
input {
text-align: center;
}
}
[data-index] input:invalid {
background-color: var(--search-term-invalid-bg, @search-term-invalid-bg);
color: var(--search-term-invalid-color, @search-term-invalid-color);
}
[data-index] input:disabled {
background-color: var(--search-term-disabled-bg, @search-term-disabled-bg);
}
.column input {
.rounded-corners(.4em);
}
.column:not(:last-of-type),
.column.last-term {
input {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
.operator:last-of-type:not(.last-term) input {
.rounded-corners(.4em);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.value input {
.rounded-corners(.4em);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.highlighted input {
background-color: var(--search-term-highlighted-bg, @search-term-highlighted-bg);
color: var(--search-term-highlighted-color, @search-term-highlighted-color);
}
.selected input {
background-color: var(--search-term-selected-bg, @search-term-selected-bg);
color: var(--search-term-selected-color, @search-term-selected-color);
font-style: italic;
}
ul.comma-separated {
display: inline;
padding: 0;
list-style-type: none;
li {
display: inline;
&:not(:first-of-type):before {
display: inline;
content: ', ';
}
}
}
}
// Layout
.search-bar {
height: 2em;
display: flex;
position: relative; // Required for the suggestions
button.search-options {
line-height: 1em;
}
.filter-input-area {
overflow: auto hidden;
overflow-x: overlay; // Not invalid, but proprietary feature by chrome/webkit
display: flex;
width: 100%;
height: ~"calc(2em + 10px)"; // Search bar height + approximate scrollbar height
padding: 2/12em; // 2 (px) desired / default font size (px)
// Lets inputs grow based on their contents, Inspired by https://css-tricks.com/auto-growing-inputs-textareas/
label {
position: relative;
display: inline-block;
min-width: 2em;
&::after,
input {
width: auto;
padding: 0 .5em;
resize: none;
}
input {
width: 100%;
position: absolute;
line-height: 20/12; // 20 (px) desired / default font size (px)
}
&::after {
height: 0;
content: attr(data-label);
visibility: hidden;
white-space: nowrap;
padding: 0 7/12em; // 7 (px) desired / default font size (px)
}
}
> label {
flex: 1 0 auto;
&::after,
input {
max-width: none;
min-width: 8em;
}
}
}
.terms {
display: inline;
flex-shrink: 0;
.filter-chain,
.filter-condition {
display: inline;
}
.filter-condition {
position: relative;
button {
display: none;
z-index: 1;
width: ~"calc(2em + 2px)";
padding: .15em .6em .15em .4em;
position: absolute;
left: ~"calc(-2em - 2px)"; // That's min-width + margin-right of an operator
line-height: 16/12; // 16 (px) desired / default font size (px)
i:before {
margin-right: 0;
}
}
&:not(._hover_delay):hover button {
display: inline;
}
}
> .filter-condition:first-child button {
padding: .15em .4em .15em .6em;
left: auto;
right: ~"calc(-2em - 1px)"; // That's min-width + margin-left of an operator
}
label {
margin-right: 1px;
&.logical_operator,
&.grouping_operator_open,
&.grouping_operator_close {
margin-left: 1px; // adds up to 2px with the previous term
margin-right: 2px;
}
}
}
&.disabled {
.terms .filter-condition:hover button {
display: none;
}
}
.search-suggestions {
// 2 (px) desired / default font-size to match .filter-input outline-offset (-1px) + outline-width (3px)
margin-top: 2/12em;
}
}

100
asset/css/search-base.less Normal file
View File

@ -0,0 +1,100 @@
// Style
.search-suggestions {
background: var(--suggestions-bg, @suggestions-bg);
border: 1px solid var(--suggestions-border-color, @suggestions-border-color);
border-bottom-right-radius: .5em;
border-bottom-left-radius: .5em;
> ul {
list-style-type: none;
> li {
border-top: 1px solid var(--suggestions-separation-bg, @suggestions-separation-bg);
}
> li.suggestion-title + li {
border: none;
}
> li:not(.default) + li.suggestion-title {
border: none;
}
}
.default {
color: var(--suggestions-default-opt-color, @suggestions-default-opt-color);
font-style: italic;
[type="button"] {
background-color: var(--suggestions-default-opt-bg, @suggestions-default-opt-bg);
}
}
.suggestion-title {
color: var(--suggestions-color, @suggestions-color);
font-size: 80%;
}
.failure-message {
font-weight: bold;
em {
font-weight: normal;
color: var(--suggestions-failure-message-color, @suggestions-failure-message-color);
}
}
[type="button"] {
.appearance(none);
border: none;
background: none;
}
[type="button"]:focus {
background: var(--suggestions-focus-bg, @suggestions-focus-bg);
color: var(--suggestions-focus-color, @suggestions-focus-color);
outline: none;
}
[type="button"]:not(:focus):hover {
background: var(--suggestions-hover-bg, @suggestions-hover-bg);
}
}
// Layout
.search-suggestions {
z-index: 2; // Required so that nothing else can overlap it (such as opaque elements and the impact overlay)
position: absolute;
overflow: auto;
min-width: 5em;
&:empty {
display: none;
}
> ul {
margin: 0;
padding: 0;
li.suggestion-title {
padding: 1.25em .625em 0 .625em;
}
li.failure-message {
padding: .5em 1em;
em {
margin-right: .5em;
}
}
}
[type="button"] {
padding: .5em 1em;
display: block;
width: 100%;
text-align: left;
&[data-class="operator"], &[data-class="logical_operator"] {
text-align: center;
}
}
}

View File

@ -0,0 +1,267 @@
// Style
.search-editor {
ul, ol {
list-style-type: none;
}
fieldset {
border: none;
}
button, input[type="submit"] {
.appearance(none);
background: none;
&:not(.cancel-button) {
border: none;
}
}
select:not([multiple]) {
.appearance(none);
padding-right: 1.5625em;
background-image: url('@{iplWebAssets}/img/select-icon-text-color.svg');
background-repeat: no-repeat;
background-position: right center;
background-size: contain;
.rounded-corners(0);
}
i.icon:before {
color: var(--search-editor-control-color, @search-editor-control-color);
}
.drag-initiator {
cursor: grab;
}
input[type="text"], select {
border: none;
background: var(--search-term-bg, @search-term-bg);
color: var(--search-term-color, @search-term-color);
text-overflow: ellipsis;
}
:not(fieldset) > select {
.rounded-corners();
}
fieldset > input[data-type="column"] {
.rounded-corners(.4em 0 0 .4em);
}
fieldset > input[data-type="value"] {
.rounded-corners(0 .4em .4em 0);
}
.search-error {
input:invalid {
background: var(--search-term-invalid-bg, @search-term-invalid-bg);
}
.search-errors {
color: var(--search-term-invalid-color, @search-term-invalid-color);
font-weight: bold;
}
}
li > select:not([multiple]) {
background-color: var(--search-logical-operator-bg, @search-logical-operator-bg);
color: var(--search-logical-operator-color, @search-logical-operator-color);
.rounded-corners();
}
.sortable-ghost {
border: dashed .2em var(--search-editor-drag-outline-color, @search-editor-drag-outline-color);
fieldset {
opacity: .5;
}
}
.buttons {
ul {
.rounded-corners();
.box-shadow(0, 0, .5em);
border: 1px solid var(--search-editor-context-menu-border-color, @search-editor-context-menu-border-color);
background: var(--search-editor-context-menu-bg, @search-editor-context-menu-bg);
li:not(:first-child) {
border-top: 1px solid var(--search-editor-context-menu-border-color, @search-editor-context-menu-border-color);
}
button:hover {
background: var(--primary-button-bg, @primary-button-bg);
color: var(--primary-button-color, @primary-button-color);
}
// Add rounded corners to buttons as well, otherwise their
// background is not rounded and overlaps the list's corners
:first-child button {
.rounded-corners();
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
:last-child button {
.rounded-corners();
border-top-left-radius: 0;
border-top-right-radius: 0;
}
&:before {
// The left pointing arrow
border-bottom: 1px solid var(--search-editor-context-menu-border-color, @search-editor-context-menu-border-color);
border-left: 1px solid var(--search-editor-context-menu-border-color, @search-editor-context-menu-border-color);
background: var(--search-editor-context-menu-bg, @search-editor-context-menu-bg);
}
}
&:hover i.icon:before {
.rounded-corners();
background: var(--primary-button-bg, @primary-button-bg);
color: var(--primary-button-color, @primary-button-color);
}
}
input[type="submit"] {
.rounded-corners();
background: var(--primary-button-bg, @primary-button-bg);
color: var(--primary-button-color, @primary-button-color);
&:hover {
background: var(--primary-button-hover-bg, @primary-button-hover-bg);
}
}
}
// Layout
.search-editor-opener + a.modal-opener {
display: none;
}
.search-editor {
padding: 1em;
@item-spacing: .5em;
ul, ol {
width: 100%;
margin: 0;
padding: 0;
}
li {
display: flex;
> :not(:first-child) {
margin-left: @item-spacing;
}
}
ol {
padding-left: 1em;
padding-bottom: @item-spacing;
> li:first-child,
> :not(.filter-chain) + li {
margin-top: @item-spacing;
}
}
input[type="text"], select {
padding: 0 .5em;
}
li > select {
margin-right: auto;
}
fieldset {
display: flex;
flex: 1 1 auto;
margin: 0;
padding: 0;
input[data-type="value"] {
flex: 1 1 auto;
}
> :not(:first-child) {
margin-left: .1em;
}
}
input, button, select {
height: 28/12em; // Target Pixels @ default font size / default font size
}
.search-errors {
margin-left: .5em;
}
i.icon:before {
margin: 0;
font-size: 1.5em;
line-height: 1.5;
}
.buttons {
position: relative;
ul {
position: absolute;
right: 32/12em; // Target distance @ default font size / default font size
z-index: 1;
width: auto;
padding: 0;
display: none;
button {
z-index: 1;
width: 100%;
text-align: left;
white-space: nowrap;
}
&:before {
// The left pointing arrow
content: "";
display: block;
height: 1em;
transform: rotate(-135deg);
width: 1em;
z-index: 1;
position: absolute;
top: ((28/12)/2)-.5em; // ((First row pixels @ default font size / default font size) / 2) - own half width
right: -.5em;
}
}
&:hover ul {
display: block;
}
i.icon:before {
padding: ((28/18)-1)/2em; // (Container pixels / default font size) - line height / (padding-top,padding-bottom)
line-height: 1;
}
}
.cancel-button {
margin-top: 2em - @item-spacing;
}
input[type="submit"] {
float: right;
width: 6em;
margin-top: 2em - @item-spacing;
}
input[type="submit"]:not(:last-of-type) {
display: none;
}
}

View File

@ -0,0 +1,47 @@
.state-badge {
.rounded-corners();
color: var(--default-text-color-inverted, @default-text-color-inverted);
display: inline-block;
font-size: 1em;
min-width: 2em;
padding: .25em;
text-align: center;
&.handled {
opacity: .8;
}
&.state-critical {
background-color: var(--state-critical, @state-critical);
}
&.state-down {
background-color: var(--state-down, @state-down);
}
&.state-ok {
background-color: var(--state-ok, @state-ok);
}
&.state-pending {
background-color: var(--state-pending, @state-pending);
}
&.state-unknown {
background-color: var(--state-unknown, @state-unknown);
}
&.state-up {
background-color: var(--state-up, @state-up);
}
&.state-warning {
background-color: var(--state-warning, @state-warning);
}
}
a .state-badge {
&:not(.disabled):hover {
filter: brightness(80%);
}
}

162
asset/css/variables.less Normal file
View File

@ -0,0 +1,162 @@
/*
RECOMMENDATION:
Please do not use the base color variables directly,
define a new variable instead that assigns the value of this base variable.
Examples:
- @base-color: red;
- @my-new-var: @base-color;
- @my-second-new-var: @base-color;
Do not use the same variable for different use cases, but define a new variable for each use case.
NOTICE:
Color vars identification:
- Vars with `-bg` suffix are background-color vars. Please use only for setting bg-color.
- Vars with `-color` suffix are color vars. Please use only for setting fg-color.
MODE SUPPORT:
The standard LESS variables represent the dark mode. The LESS detached ruleset `@iplWebLightRules`
contains CSS variables that represent the light mode. It must be used explicitly to have any effect.
If you use media queries to support modes, just call the ruleset inside your media query:
@media (prefers-color-scheme: light) {
@iplWebLightRules();
}
*/
@default-bg: #282E39;
@base-gray: #c4c4c4;
@base-gray-light: #5c5c5c;
@base-gray-lighter: #4b4b4b;
@base-disabled: #9a9a9a;
@base-primary-color: #00C3ED;
@base-primary-bg: #00C3ED;
@default-text-color: #fff;
@default-text-color-light: fade(@default-text-color, 75%);
@default-text-color-inverted: @default-bg;
@state-ok: #44bb77;
@state-up: @state-ok;
@state-warning: #ffaa44;
@state-critical: #ff5566;
@state-down: @state-critical;
@state-pending: #77aaff;
@state-unknown: #aa44ff;
@primary-button-color: @default-text-color-inverted;
@primary-button-bg: @base-primary-bg;
@primary-button-hover-bg: #0081a6;
@search-term-bg: @base-gray;
@search-term-color: @default-text-color-inverted;
@search-term-selected-bg: @base-disabled;
@search-term-invalid-bg: @state-critical;
@search-term-invalid-color: @default-text-color-inverted;
@search-term-disabled-bg: @base-disabled;
@search-term-selected-color: @base-gray-light;
@search-term-highlighted-bg: @base-primary-bg;
@search-term-highlighted-color: @default-text-color-inverted;
@search-condition-remove-bg: @state-critical;
@search-condition-remove-color: @default-text-color-inverted;
@search-logical-operator-bg: @base-gray-light;
@search-logical-operator-color: @default-text-color-light;
@searchbar-bg: #404d72;
@searchbar-scrollbar-bg: @base-gray-light;
@search-editor-control-color: @base-gray-light;
@search-editor-logical-op-bg: @base-gray-light;
@search-editor-context-menu-border-color: @base-gray-light;
@search-editor-context-menu-bg: @default-bg;
@search-editor-drag-outline-color: @base-gray;
@control-color: @base-primary-color;
@control-hover-bg: @base-gray-lighter;
@control-disabled-color: @base-disabled;
@cancel-button-bg: none;
@cancel-button-border-color: @state-critical;
@cancel-button-color: @state-critical;
@cancel-button-hover-bg: @state-critical;
@cancel-button-hover-color: @default-text-color-inverted;
@suggestions-bg: @default-bg;
@suggestions-color: @default-text-color-light;
@suggestions-focus-bg: @base-primary-bg;
@suggestions-focus-color: @default-text-color-inverted;
@suggestions-default-opt-bg: fade(@base-primary-bg, 10%);
@suggestions-default-opt-color: @default-text-color-light;
@suggestions-hover-bg: fade(@base-primary-bg, 30%);
@suggestions-border-color: @base-gray-light;
@suggestions-separation-bg: @base-gray-lighter;
@suggestions-failure-message-color: @default-text-color-light;
@card-border-color: @base-gray-light;
@iplWebLightRules: {
:root {
--base-gray: #819398;
--base-gray-light: #d0d3da;
--base-gray-lighter: #e8ecef;
--base-disabled: var(--base-gray-light);
--base-remove-bg: @state-critical;
--default-text-color: #535353;
--default-text-color-light: fade(#535353, 75%); // --default-text-color
--default-text-color-inverted: #F5F9FA;
--primary-button-color: var(--default-text-color-inverted);
--primary-button-bg: @primary-button-bg;
--primary-button-hover-bg: @primary-button-hover-bg;
--searchbar-bg: #DEECF1;
--searchbar-scrollbar-bg: var(--base-gray-light);
--search-term-bg: var(--base-gray-light);
--search-term-color: var(--default-text-color);
--search-term-selected-bg: var(--base-disabled);
--search-term-invalid-bg: var(--base-remove-bg);
--search-term-invalid-color: var(--default-text-color-inverted);
--search-term-disabled-bg: var(--base-gray-light);
--search-term-selected-color: var(--base-gray);
--search-term-highlighted-bg: var(--primary-button-bg);
--search-term-highlighted-color: var(--default-text-color-inverted);
--search-condition-remove-bg: var(--base-remove-bg);
--search-condition-remove-color: var(--default-text-color-inverted);
--search-logical-operator-bg: fade(#819398, 50%); // --base-gray
--search-logical-operator-color: var(--default-text-color-light);
--search-editor-control-color: var(--base-gray-light);
--search-editor-logical-op-bg: var(--base-gray-light);
--search-editor-context-menu-border-color: var(--base-gray-light);
--search-editor-context-menu-bg: var(--default-text-color-inverted);
--search-editor-drag-outline-color: var(--base-gray);
--control-color: var(--primary-button-bg);
--control-hover-bg: var(--base-gray-lighter);
--control-disabled-color: var(--base-gray-light);
--cancel-button-hover-color: var(--default-text-color-inverted);
--suggestions-bg: var(--default-text-color-inverted);
--suggestions-color: var(--default-text-color-light);
--suggestions-focus-bg: var(--primary-button-bg);
--suggestions-focus-color: var(--default-text-color-inverted);
--suggestions-default-opt-bg: fade(@primary-button-bg, 10%);
--suggestions-default-opt-color: var(--default-text-color-light);
--suggestions-hover-bg: fade(@primary-button-bg, 30%);
--suggestions-border-color: var(--base-gray-light);
--suggestions-separation-bg: var(--base-gray-lighter);
--suggestions-failure-message-color: var(--default-text-color-light);
--card-border-color: var(--base-gray-light);
}
};

791
asset/css/vendor/flatpickr.css vendored Normal file
View File

@ -0,0 +1,791 @@
.flatpickr-calendar {
background: transparent;
opacity: 0;
display: none;
text-align: center;
visibility: hidden;
padding: 0;
-webkit-animation: none;
animation: none;
direction: ltr;
border: 0;
font-size: 14px;
line-height: 24px;
border-radius: 5px;
position: absolute;
width: 307.875px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-ms-touch-action: manipulation;
touch-action: manipulation;
background: #fff;
-webkit-box-shadow: 1px 0 0 #e6e6e6, -1px 0 0 #e6e6e6, 0 1px 0 #e6e6e6, 0 -1px 0 #e6e6e6, 0 3px 13px rgba(0,0,0,0.08);
box-shadow: 1px 0 0 #e6e6e6, -1px 0 0 #e6e6e6, 0 1px 0 #e6e6e6, 0 -1px 0 #e6e6e6, 0 3px 13px rgba(0,0,0,0.08);
}
.flatpickr-calendar.open,
.flatpickr-calendar.inline {
opacity: 1;
max-height: 640px;
visibility: visible;
}
.flatpickr-calendar.open {
display: inline-block;
z-index: 99999;
}
.flatpickr-calendar.animate.open {
-webkit-animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1);
animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.flatpickr-calendar.inline {
display: block;
position: relative;
top: 2px;
}
.flatpickr-calendar.static {
position: absolute;
top: calc(100% + 2px);
}
.flatpickr-calendar.static.open {
z-index: 999;
display: block;
}
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7) {
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1) {
-webkit-box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
}
.flatpickr-calendar .hasWeeks .dayContainer,
.flatpickr-calendar .hasTime .dayContainer {
border-bottom: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.flatpickr-calendar .hasWeeks .dayContainer {
border-left: 0;
}
.flatpickr-calendar.hasTime .flatpickr-time {
height: 40px;
border-top: 1px solid #e6e6e6;
}
.flatpickr-calendar.noCalendar.hasTime .flatpickr-time {
height: auto;
}
.flatpickr-calendar:before,
.flatpickr-calendar:after {
position: absolute;
display: block;
pointer-events: none;
border: solid transparent;
content: '';
height: 0;
width: 0;
left: 22px;
}
.flatpickr-calendar.rightMost:before,
.flatpickr-calendar.arrowRight:before,
.flatpickr-calendar.rightMost:after,
.flatpickr-calendar.arrowRight:after {
left: auto;
right: 22px;
}
.flatpickr-calendar.arrowCenter:before,
.flatpickr-calendar.arrowCenter:after {
left: 50%;
right: 50%;
}
.flatpickr-calendar:before {
border-width: 5px;
margin: 0 -5px;
}
.flatpickr-calendar:after {
border-width: 4px;
margin: 0 -4px;
}
.flatpickr-calendar.arrowTop:before,
.flatpickr-calendar.arrowTop:after {
bottom: 100%;
}
.flatpickr-calendar.arrowTop:before {
border-bottom-color: #e6e6e6;
}
.flatpickr-calendar.arrowTop:after {
border-bottom-color: #fff;
}
.flatpickr-calendar.arrowBottom:before,
.flatpickr-calendar.arrowBottom:after {
top: 100%;
}
.flatpickr-calendar.arrowBottom:before {
border-top-color: #e6e6e6;
}
.flatpickr-calendar.arrowBottom:after {
border-top-color: #fff;
}
.flatpickr-calendar:focus {
outline: 0;
}
.flatpickr-wrapper {
position: relative;
display: inline-block;
}
.flatpickr-months {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.flatpickr-months .flatpickr-month {
background: transparent;
color: rgba(0,0,0,0.9);
fill: rgba(0,0,0,0.9);
height: 34px;
line-height: 1;
text-align: center;
position: relative;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
overflow: hidden;
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
text-decoration: none;
cursor: pointer;
position: absolute;
top: 0;
height: 34px;
padding: 10px;
z-index: 3;
color: rgba(0,0,0,0.9);
fill: rgba(0,0,0,0.9);
}
.flatpickr-months .flatpickr-prev-month.flatpickr-disabled,
.flatpickr-months .flatpickr-next-month.flatpickr-disabled {
display: none;
}
.flatpickr-months .flatpickr-prev-month i,
.flatpickr-months .flatpickr-next-month i {
position: relative;
}
.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month.flatpickr-prev-month {
/*
/*rtl:begin:ignore*/
/*
*/
left: 0;
/*
/*rtl:end:ignore*/
/*
*/
}
/*
/*rtl:begin:ignore*/
/*
/*rtl:end:ignore*/
.flatpickr-months .flatpickr-prev-month.flatpickr-next-month,
.flatpickr-months .flatpickr-next-month.flatpickr-next-month {
/*
/*rtl:begin:ignore*/
/*
*/
right: 0;
/*
/*rtl:end:ignore*/
/*
*/
}
/*
/*rtl:begin:ignore*/
/*
/*rtl:end:ignore*/
.flatpickr-months .flatpickr-prev-month:hover,
.flatpickr-months .flatpickr-next-month:hover {
color: #959ea9;
}
.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg {
fill: #f64747;
}
.flatpickr-months .flatpickr-prev-month svg,
.flatpickr-months .flatpickr-next-month svg {
width: 14px;
height: 14px;
}
.flatpickr-months .flatpickr-prev-month svg path,
.flatpickr-months .flatpickr-next-month svg path {
-webkit-transition: fill 0.1s;
transition: fill 0.1s;
fill: inherit;
}
.numInputWrapper {
position: relative;
height: auto;
}
.numInputWrapper input,
.numInputWrapper span {
display: inline-block;
}
.numInputWrapper input {
width: 100%;
}
.numInputWrapper input::-ms-clear {
display: none;
}
.numInputWrapper input::-webkit-outer-spin-button,
.numInputWrapper input::-webkit-inner-spin-button {
margin: 0;
-webkit-appearance: none;
}
.numInputWrapper span {
position: absolute;
right: 0;
width: 14px;
padding: 0 4px 0 2px;
height: 50%;
line-height: 50%;
opacity: 0;
cursor: pointer;
border: 1px solid rgba(57,57,57,0.15);
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.numInputWrapper span:hover {
background: rgba(0,0,0,0.1);
}
.numInputWrapper span:active {
background: rgba(0,0,0,0.2);
}
.numInputWrapper span:after {
display: block;
content: "";
position: absolute;
}
.numInputWrapper span.arrowUp {
top: 0;
border-bottom: 0;
}
.numInputWrapper span.arrowUp:after {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid rgba(57,57,57,0.6);
top: 26%;
}
.numInputWrapper span.arrowDown {
top: 50%;
}
.numInputWrapper span.arrowDown:after {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid rgba(57,57,57,0.6);
top: 40%;
}
.numInputWrapper span svg {
width: inherit;
height: auto;
}
.numInputWrapper span svg path {
fill: rgba(0,0,0,0.5);
}
.numInputWrapper:hover {
background: rgba(0,0,0,0.05);
}
.numInputWrapper:hover span {
opacity: 1;
}
.flatpickr-current-month {
font-size: 135%;
line-height: inherit;
font-weight: 300;
color: inherit;
position: absolute;
width: 75%;
left: 12.5%;
padding: 7.48px 0 0 0;
line-height: 1;
height: 34px;
display: inline-block;
text-align: center;
-webkit-transform: translate3d(0px, 0px, 0px);
transform: translate3d(0px, 0px, 0px);
}
.flatpickr-current-month span.cur-month {
font-family: inherit;
font-weight: 700;
color: inherit;
display: inline-block;
margin-left: 0.5ch;
padding: 0;
}
.flatpickr-current-month span.cur-month:hover {
background: rgba(0,0,0,0.05);
}
.flatpickr-current-month .numInputWrapper {
width: 6ch;
width: 7ch\0;
display: inline-block;
}
.flatpickr-current-month .numInputWrapper span.arrowUp:after {
border-bottom-color: rgba(0,0,0,0.9);
}
.flatpickr-current-month .numInputWrapper span.arrowDown:after {
border-top-color: rgba(0,0,0,0.9);
}
.flatpickr-current-month input.cur-year {
background: transparent;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: inherit;
cursor: text;
padding: 0 0 0 0.5ch;
margin: 0;
display: inline-block;
font-size: inherit;
font-family: inherit;
font-weight: 300;
line-height: inherit;
height: auto;
border: 0;
border-radius: 0;
vertical-align: initial;
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
}
.flatpickr-current-month input.cur-year:focus {
outline: 0;
}
.flatpickr-current-month input.cur-year[disabled],
.flatpickr-current-month input.cur-year[disabled]:hover {
font-size: 100%;
color: rgba(0,0,0,0.5);
background: transparent;
pointer-events: none;
}
.flatpickr-current-month .flatpickr-monthDropdown-months {
appearance: menulist;
background: transparent;
border: none;
border-radius: 0;
box-sizing: border-box;
color: inherit;
cursor: pointer;
font-size: inherit;
font-family: inherit;
font-weight: 300;
height: auto;
line-height: inherit;
margin: -1px 0 0 0;
outline: none;
padding: 0 0 0 0.5ch;
position: relative;
vertical-align: initial;
-webkit-box-sizing: border-box;
-webkit-appearance: menulist;
-moz-appearance: menulist;
width: auto;
}
.flatpickr-current-month .flatpickr-monthDropdown-months:focus,
.flatpickr-current-month .flatpickr-monthDropdown-months:active {
outline: none;
}
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
background: rgba(0,0,0,0.05);
}
.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month {
background-color: transparent;
outline: none;
padding: 0;
}
.flatpickr-weekdays {
background: transparent;
text-align: center;
overflow: hidden;
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
height: 28px;
}
.flatpickr-weekdays .flatpickr-weekdaycontainer {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
span.flatpickr-weekday {
cursor: default;
font-size: 90%;
background: transparent;
color: rgba(0,0,0,0.54);
line-height: 1;
margin: 0;
text-align: center;
display: block;
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
font-weight: bolder;
}
.dayContainer,
.flatpickr-weeks {
padding: 1px 0 0 0;
}
.flatpickr-days {
position: relative;
overflow: hidden;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: start;
-webkit-align-items: flex-start;
-ms-flex-align: start;
align-items: flex-start;
width: 307.875px;
}
.flatpickr-days:focus {
outline: 0;
}
.dayContainer {
padding: 0;
outline: 0;
text-align: left;
width: 307.875px;
min-width: 307.875px;
max-width: 307.875px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
display: inline-block;
display: -ms-flexbox;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
-ms-flex-wrap: wrap;
-ms-flex-pack: justify;
-webkit-justify-content: space-around;
justify-content: space-around;
-webkit-transform: translate3d(0px, 0px, 0px);
transform: translate3d(0px, 0px, 0px);
opacity: 1;
}
.dayContainer + .dayContainer {
-webkit-box-shadow: -1px 0 0 #e6e6e6;
box-shadow: -1px 0 0 #e6e6e6;
}
.flatpickr-day {
background: none;
border: 1px solid transparent;
border-radius: 150px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: #393939;
cursor: pointer;
font-weight: 400;
width: 14.2857143%;
-webkit-flex-basis: 14.2857143%;
-ms-flex-preferred-size: 14.2857143%;
flex-basis: 14.2857143%;
max-width: 39px;
height: 39px;
line-height: 39px;
margin: 0;
display: inline-block;
position: relative;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
text-align: center;
}
.flatpickr-day.inRange,
.flatpickr-day.prevMonthDay.inRange,
.flatpickr-day.nextMonthDay.inRange,
.flatpickr-day.today.inRange,
.flatpickr-day.prevMonthDay.today.inRange,
.flatpickr-day.nextMonthDay.today.inRange,
.flatpickr-day:hover,
.flatpickr-day.prevMonthDay:hover,
.flatpickr-day.nextMonthDay:hover,
.flatpickr-day:focus,
.flatpickr-day.prevMonthDay:focus,
.flatpickr-day.nextMonthDay:focus {
cursor: pointer;
outline: 0;
background: #e6e6e6;
border-color: #e6e6e6;
}
.flatpickr-day.today {
border-color: #959ea9;
}
.flatpickr-day.today:hover,
.flatpickr-day.today:focus {
border-color: #959ea9;
background: #959ea9;
color: #fff;
}
.flatpickr-day.selected,
.flatpickr-day.startRange,
.flatpickr-day.endRange,
.flatpickr-day.selected.inRange,
.flatpickr-day.startRange.inRange,
.flatpickr-day.endRange.inRange,
.flatpickr-day.selected:focus,
.flatpickr-day.startRange:focus,
.flatpickr-day.endRange:focus,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange:hover,
.flatpickr-day.endRange:hover,
.flatpickr-day.selected.prevMonthDay,
.flatpickr-day.startRange.prevMonthDay,
.flatpickr-day.endRange.prevMonthDay,
.flatpickr-day.selected.nextMonthDay,
.flatpickr-day.startRange.nextMonthDay,
.flatpickr-day.endRange.nextMonthDay {
background: #569ff7;
-webkit-box-shadow: none;
box-shadow: none;
color: #fff;
border-color: #569ff7;
}
.flatpickr-day.selected.startRange,
.flatpickr-day.startRange.startRange,
.flatpickr-day.endRange.startRange {
border-radius: 50px 0 0 50px;
}
.flatpickr-day.selected.endRange,
.flatpickr-day.startRange.endRange,
.flatpickr-day.endRange.endRange {
border-radius: 0 50px 50px 0;
}
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) {
-webkit-box-shadow: -10px 0 0 #569ff7;
box-shadow: -10px 0 0 #569ff7;
}
.flatpickr-day.selected.startRange.endRange,
.flatpickr-day.startRange.startRange.endRange,
.flatpickr-day.endRange.startRange.endRange {
border-radius: 50px;
}
.flatpickr-day.inRange {
border-radius: 0;
-webkit-box-shadow: -5px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
box-shadow: -5px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
}
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover,
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay,
.flatpickr-day.notAllowed,
.flatpickr-day.notAllowed.prevMonthDay,
.flatpickr-day.notAllowed.nextMonthDay {
color: rgba(57,57,57,0.3);
background: transparent;
border-color: transparent;
cursor: default;
}
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover {
cursor: not-allowed;
color: rgba(57,57,57,0.1);
}
.flatpickr-day.week.selected {
border-radius: 0;
-webkit-box-shadow: -5px 0 0 #569ff7, 5px 0 0 #569ff7;
box-shadow: -5px 0 0 #569ff7, 5px 0 0 #569ff7;
}
.flatpickr-day.hidden {
visibility: hidden;
}
.rangeMode .flatpickr-day {
margin-top: 1px;
}
.flatpickr-weekwrapper {
float: left;
}
.flatpickr-weekwrapper .flatpickr-weeks {
padding: 0 12px;
-webkit-box-shadow: 1px 0 0 #e6e6e6;
box-shadow: 1px 0 0 #e6e6e6;
}
.flatpickr-weekwrapper .flatpickr-weekday {
float: none;
width: 100%;
line-height: 28px;
}
.flatpickr-weekwrapper span.flatpickr-day,
.flatpickr-weekwrapper span.flatpickr-day:hover {
display: block;
width: 100%;
max-width: none;
color: rgba(57,57,57,0.3);
background: transparent;
cursor: default;
border: none;
}
.flatpickr-innerContainer {
display: block;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
}
.flatpickr-rContainer {
display: inline-block;
padding: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.flatpickr-time {
text-align: center;
outline: 0;
display: block;
height: 0;
line-height: 40px;
max-height: 40px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.flatpickr-time:after {
content: "";
display: table;
clear: both;
}
.flatpickr-time .numInputWrapper {
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
width: 40%;
height: 40px;
float: left;
}
.flatpickr-time .numInputWrapper span.arrowUp:after {
border-bottom-color: #393939;
}
.flatpickr-time .numInputWrapper span.arrowDown:after {
border-top-color: #393939;
}
.flatpickr-time.hasSeconds .numInputWrapper {
width: 26%;
}
.flatpickr-time.time24hr .numInputWrapper {
width: 49%;
}
.flatpickr-time input {
background: transparent;
-webkit-box-shadow: none;
box-shadow: none;
border: 0;
border-radius: 0;
text-align: center;
margin: 0;
padding: 0;
height: inherit;
line-height: inherit;
color: #393939;
font-size: 14px;
position: relative;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
}
.flatpickr-time input.flatpickr-hour {
font-weight: bold;
}
.flatpickr-time input.flatpickr-minute,
.flatpickr-time input.flatpickr-second {
font-weight: 400;
}
.flatpickr-time input:focus {
outline: 0;
border: 0;
}
.flatpickr-time .flatpickr-time-separator,
.flatpickr-time .flatpickr-am-pm {
height: inherit;
float: left;
line-height: inherit;
color: #393939;
font-weight: bold;
width: 2%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-align-self: center;
-ms-flex-item-align: center;
align-self: center;
}
.flatpickr-time .flatpickr-am-pm {
outline: 0;
width: 18%;
cursor: pointer;
text-align: center;
font-weight: 400;
}
.flatpickr-time input:hover,
.flatpickr-time .flatpickr-am-pm:hover,
.flatpickr-time input:focus,
.flatpickr-time .flatpickr-am-pm:focus {
background: #eee;
}
.flatpickr-input[readonly] {
cursor: pointer;
}
@-webkit-keyframes fpFadeInDown {
from {
opacity: 0;
-webkit-transform: translate3d(0, -20px, 0);
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}
@keyframes fpFadeInDown {
from {
opacity: 0;
-webkit-transform: translate3d(0, -20px, 0);
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}

327
asset/css/vendor/flatpickr.vars.less vendored Normal file
View File

@ -0,0 +1,327 @@
/**
* This file's only purpose is to make the flatpickr themeable. DO NOT add ANY custom style here!
* Also, DO NOT re-arrange the CSS blocks to make them more LESS like. They're based off of the
* pre-compiled flatpickr.css file and so can easily identified when updating to a new version.
*
* Non-standard LESS variables were added to allow usage of CSS variables. All of them hold a
* value calculated by LESS functions. If not temporarily stored in another LESS variable,
* they wouldn't be available to CSS variable usage.
*
* Latest state from version: v4.6.9
*/
@fp-calendarBackground: #ffffff;
@fp-calendarBorderColor: #e6e6e6;
@fp-arrowColor: fadeout(@fp-dayForeground, 40%); // Non-standard variable
@fp-arrow_hover_color: #f64747;
@fp-monthForeground: fadeout(black, 10%);
@fp-monthBackground: transparent;
@fp-weekdaysBackground: transparent;
@fp-weekdaysForeground: fadeout(black, 46%);
@fp-weekNumberForeground: fadeout(@fp-dayForeground, 70%); // Non-standard variable
@fp-dayForeground: #393939;
@fp-dayHoverBackground: #e6e6e6;
@fp-disabledDayForeground: fadeout(@fp-dayForeground, 90%); // Non-standard variable
@fp-outsideRangeDayForeground: @fp-weekNumberForeground; // Non-standard variable
@fp-selectedDayBackground: #569FF7;
@fp-todayColor: #959ea9;
@fp-timeHoverBg: lighten(@fp-dayHoverBackground, 3); // Non-standard variable
@fp-invertedBg: black;
@fp-hoverInvertedBg: fadeout(@fp-invertedBg, 95%); // Non-standard variable
@fp-numChooserSvgFillColor: fadeout(@fp-monthForeground, 50%); // Non-standard variable
@fp-hoverNumChooserBg: fadeout(@fp-invertedBg, 90%); // Non-standard variable
@fp-numChooserBorderColor: fadeout(@fp-dayForeground, 85%); // Non-standard variable
.icinga-datetime-picker {
&.flatpickr-calendar {
background: @fp-calendarBackground;
background: var(--fp-calendarBackground, @fp-calendarBackground);
box-shadow: 1px 0 0 @fp-calendarBorderColor,
-1px 0 0 @fp-calendarBorderColor,
0 1px 0 @fp-calendarBorderColor,
0 -1px 0 @fp-calendarBorderColor,
0 3px 13px fadeout(black, 92%);
box-shadow: 1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor),
-1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor),
0 1px 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor),
0 -1px 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor),
0 3px 13px fadeout(black, 92%);
}
&.flatpickr-calendar.arrowTop:before {
border-bottom-color: @fp-calendarBorderColor;
border-bottom-color: var(--fp-calendarBorderColor, @fp-calendarBorderColor);
}
&.flatpickr-calendar.arrowTop:after {
border-bottom-color: @fp-calendarBackground;
border-bottom-color: var(--fp-calendarBackground, @fp-calendarBackground);
}
&.flatpickr-calendar.arrowBottom:before {
border-top-color: @fp-calendarBorderColor;
border-top-color: var(--fp-calendarBorderColor, @fp-calendarBorderColor);
}
&.flatpickr-calendar.arrowBottom:after {
border-top-color: @fp-calendarBackground;
border-top-color: var(--fp-calendarBackground, @fp-calendarBackground);
}
&.flatpickr-calendar.hasTime .flatpickr-time {
border-top-color: @fp-calendarBorderColor;
border-top-color: var(--fp-calendarBorderColor, @fp-calendarBorderColor);
}
.dayContainer + .dayContainer {
-webkit-box-shadow: -1px 0 0 @fp-calendarBorderColor;
-webkit-box-shadow: -1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor);
box-shadow: -1px 0 0 @fp-calendarBorderColor;
box-shadow: -1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor);
}
.flatpickr-day {
color: @fp-dayForeground;
color: var(--fp-dayForeground, @fp-dayForeground);
}
.flatpickr-day.today {
border-color: @fp-todayColor;
border-color: var(--fp-todayColor, @fp-todayColor);
}
.flatpickr-day.today:hover,
.flatpickr-day.today:focus {
border-color: @fp-todayColor;
border-color: var(--fp-todayColor, @fp-todayColor);
background: @fp-todayColor;
background: var(--fp-todayColor, @fp-todayColor);
color: @fp-calendarBackground;
color: var(--fp-calendarBackground, @fp-calendarBackground);
}
.flatpickr-day.selected,
.flatpickr-day.startRange,
.flatpickr-day.endRange,
.flatpickr-day.selected.inRange,
.flatpickr-day.startRange.inRange,
.flatpickr-day.endRange.inRange,
.flatpickr-day.selected:focus,
.flatpickr-day.startRange:focus,
.flatpickr-day.endRange:focus,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange:hover,
.flatpickr-day.endRange:hover,
.flatpickr-day.selected.prevMonthDay,
.flatpickr-day.startRange.prevMonthDay,
.flatpickr-day.endRange.prevMonthDay,
.flatpickr-day.selected.nextMonthDay,
.flatpickr-day.startRange.nextMonthDay,
.flatpickr-day.endRange.nextMonthDay {
color: @fp-calendarBackground;
color: var(--fp-calendarBackground, @fp-calendarBackground);
}
.flatpickr-day.inRange,
.flatpickr-day.prevMonthDay.inRange,
.flatpickr-day.nextMonthDay.inRange,
.flatpickr-day.today.inRange,
.flatpickr-day.prevMonthDay.today.inRange,
.flatpickr-day.nextMonthDay.today.inRange,
.flatpickr-day:hover,
.flatpickr-day.prevMonthDay:hover,
.flatpickr-day.nextMonthDay:hover,
.flatpickr-day:focus,
.flatpickr-day.nextMonthDay:focus {
background: @fp-dayHoverBackground;
background: var(--fp-dayHoverBackground, @fp-dayHoverBackground);
border-color: @fp-dayHoverBackground;
border-color: var(--fp-dayHoverBackground, @fp-dayHoverBackground);
}
.flatpickr-day.inRange {
-webkit-box-shadow: -5px 0 0 @fp-dayHoverBackground, 5px 0 0 @fp-dayHoverBackground;
-webkit-box-shadow: -5px 0 0 var(--fp-dayHoverBackground, @fp-dayHoverBackground),
5px 0 0 var(--fp-dayHoverBackground, @fp-dayHoverBackground);
box-shadow: -5px 0 0 @fp-dayHoverBackground, 5px 0 0 @fp-dayHoverBackground;
box-shadow: -5px 0 0 var(--fp-dayHoverBackground, @fp-dayHoverBackground),
5px 0 0 var(--fp-dayHoverBackground, @fp-dayHoverBackground);
}
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay,
.flatpickr-day.notAllowed,
.flatpickr-day.notAllowed.prevMonthDay,
.flatpickr-day.notAllowed.nextMonthDay {
color: @fp-outsideRangeDayForeground;
color: var(--fp-outsideRangeDayForeground, @fp-outsideRangeDayForeground);
}
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover {
color: @fp-disabledDayForeground;
color: var(--fp-disabledDayForeground, @fp-disabledDayForeground);
}
.flatpickr-day.selected,
.flatpickr-day.startRange,
.flatpickr-day.endRange,
.flatpickr-day.selected.inRange,
.flatpickr-day.startRange.inRange,
.flatpickr-day.endRange.inRange,
.flatpickr-day.selected:focus,
.flatpickr-day.startRange:focus,
.flatpickr-day.endRange:focus,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange:hover,
.flatpickr-day.endRange:hover,
.flatpickr-day.selected.prevMonthDay,
.flatpickr-day.startRange.prevMonthDay,
.flatpickr-day.endRange.prevMonthDay,
.flatpickr-day.selected.nextMonthDay,
.flatpickr-day.startRange.nextMonthDay,
.flatpickr-day.endRange.nextMonthDay {
background: @fp-selectedDayBackground;
background: var(--fp-selectedDayBackground, @fp-selectedDayBackground);
border-color: @fp-selectedDayBackground;
border-color: var(--fp-selectedDayBackground, @fp-selectedDayBackground);
}
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) {
-webkit-box-shadow: -10px 0 0 @fp-selectedDayBackground;
-webkit-box-shadow: -10px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground);
box-shadow: -10px 0 0 @fp-selectedDayBackground;
box-shadow: -10px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground);
}
.flatpickr-day.week.selected {
-webkit-box-shadow: -5px 0 0 @fp-selectedDayBackground, 5px 0 0 @fp-selectedDayBackground;
-webkit-box-shadow: -5px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground),
5px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground);
box-shadow: -5px 0 0 @fp-selectedDayBackground, 5px 0 0 @fp-selectedDayBackground;
box-shadow: -5px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground),
5px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground);
}
.flatpickr-weekwrapper .flatpickr-weeks {
-webkit-box-shadow: 1px 0 0 @fp-calendarBorderColor;
-webkit-box-shadow: 1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor);
box-shadow: 1px 0 0 @fp-calendarBorderColor;
box-shadow: 1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor);
}
.flatpickr-weekwrapper span.flatpickr-day,
.flatpickr-weekwrapper span.flatpickr-day:hover {
color: @fp-weekNumberForeground;
color: var(--fp-weekNumberForeground, @fp-weekNumberForeground);
}
.flatpickr-weekdays {
background: @fp-weekdaysBackground;
background: var(--fp-weekdaysBackground, @fp-weekdaysBackground);
}
span.flatpickr-weekday {
background: @fp-monthBackground;
background: var(--fp-monthBackground, @fp-monthBackground);
color: @fp-weekdaysForeground;
color: var(--fp-weekdaysForeground, @fp-weekdaysForeground);
}
.flatpickr-months .flatpickr-month {
background: @fp-monthBackground;
background: var(--fp-monthBackground, @fp-monthBackground);
color: @fp-monthForeground;
color: var(--fp-monthForeground, @fp-monthForeground);
fill: @fp-monthForeground;
fill: var(--fp-monthForeground, @fp-monthForeground);
}
.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
color: @fp-monthForeground;
color: var(--fp-monthForeground, @fp-monthForeground);
fill: @fp-monthForeground;
fill: var(--fp-monthForeground, @fp-monthForeground);
}
.flatpickr-months .flatpickr-prev-month:hover,
.flatpickr-months .flatpickr-next-month:hover {
color: @fp-todayColor;
color: var(--fp-todayColor, @fp-todayColor);
}
.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg {
fill: @fp-arrow_hover_color;
fill: var(--fp-arrow_hover_color, @fp-arrow_hover_color);
}
.flatpickr-current-month .flatpickr-monthDropdown-months {
background: @fp-monthBackground;
background: var(--fp-monthBackground, @fp-monthBackground);
}
.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month {
background-color: @fp-monthBackground;
background-color: var(--fp-monthBackground, @fp-monthBackground);
}
.flatpickr-current-month .numInputWrapper span.arrowUp:after {
border-bottom-color: @fp-monthForeground;
border-bottom-color: var(--fp-monthForeground, @fp-monthForeground);
}
.flatpickr-current-month .numInputWrapper span.arrowDown:after {
border-top-color: @fp-monthForeground;
border-top-color: var(--fp-monthForeground, @fp-monthForeground);
}
.numInputWrapper span {
border-color: @fp-numChooserBorderColor;
border-color: var(--fp-numChooserBorderColor, @fp-numChooserBorderColor);
}
.numInputWrapper span:hover {
background: @fp-hoverNumChooserBg;
background: var(--fp-hoverNumChooserBg, @fp-hoverNumChooserBg);
}
.numInputWrapper span:active {
background: @fp-hoverNumChooserBg;
background: var(--fp-hoverNumChooserBg, @fp-hoverNumChooserBg);
}
.numInputWrapper span svg path {
fill: @fp-numChooserSvgFillColor;
fill: var(--fp-numChooserSvgFillColor, @fp-numChooserSvgFillColor);
}
.numInputWrapper span.arrowUp:after {
border-bottom-color: @fp-arrowColor;
border-bottom-color: var(--fp-arrowColor, @fp-arrowColor);
}
.numInputWrapper span.arrowDown:after {
border-top-color: @fp-arrowColor;
border-top-color: var(--fp-arrowColor, @fp-arrowColor);
}
.numInputWrapper:hover {
background: @fp-hoverInvertedBg;
background: var(--fp-hoverInvertedBg, @fp-hoverInvertedBg);
}
.flatpickr-current-month span.cur-month:hover {
background: @fp-hoverInvertedBg;
background: var(--fp-hoverInvertedBg, @fp-hoverInvertedBg);
}
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
background: @fp-hoverInvertedBg;
background: var(--fp-hoverInvertedBg, @fp-hoverInvertedBg);
}
.flatpickr-time input:hover,
.flatpickr-time .flatpickr-am-pm:hover,
.flatpickr-time input:focus,
.flatpickr-time .flatpickr-am-pm:focus {
background: @fp-timeHoverBg;
background: var(--fp-timeHoverBg, @fp-timeHoverBg);
}
.flatpickr-time .numInputWrapper span.arrowUp:after {
border-bottom-color: @fp-dayForeground;
border-bottom-color: var(--fp-dayForeground, @fp-dayForeground);
}
.flatpickr-time .numInputWrapper span.arrowDown:after {
border-top-color: @fp-dayForeground;
border-top-color: var(--fp-dayForeground, @fp-dayForeground);
}
.flatpickr-time input {
color: @fp-dayForeground;
color: var(--fp-dayForeground, @fp-dayForeground);
}
.flatpickr-time .flatpickr-time-separator,
.flatpickr-time .flatpickr-am-pm {
color: @fp-dayForeground;
color: var(--fp-dayForeground, @fp-dayForeground);
}
}

View File

@ -0,0 +1,17 @@
.vertical-key-value {
display: inline-block;
line-height: .75;
text-align: center;
vertical-align: middle;
.key {
font-size: 10/12em;
color: var(--default-text-color-light, @default-text-color-light);
}
.value {
color: var(--default-text-color, @default-text-color);
font-size: 1.5em;
font-weight: bold;
}
}

161
asset/js/notjQuery.js Normal file
View File

@ -0,0 +1,161 @@
define(function () {
"use strict";
class notjQuery {
/**
* Create a new notjQuery object
*
* @param {Element} element
*/
constructor(element) {
if (! element) {
throw new Error("Can't create a notjQuery object for `" + element + "`");
}
this.element = element;
}
/**
* Add an event listener to the element
*
* @param {string} type
* @param {string} selector
* @param {function} handler
* @param {object} context
*/
on(type, selector, handler, context = null) {
if (typeof selector === 'function') {
context = handler;
handler = selector;
selector = null;
}
if (selector === null) {
this.element.addEventListener(type, e => {
if (type === 'focusin' && e.target.receivesCustomFocus) {
// Ignore native focus event if a custom one follows
if (e instanceof FocusEvent) {
delete e.target.receivesCustomFocus;
e.stopImmediatePropagation();
return;
}
}
if (context === null) {
handler.apply(e.currentTarget, [e]);
} else {
handler.apply(context, [e]);
}
});
} else {
this.element.addEventListener(type, e => {
if (type === 'focusin' && e.target.receivesCustomFocus) {
// Ignore native focus event if a custom one follows
if (e instanceof FocusEvent) {
delete e.target.receivesCustomFocus;
e.stopImmediatePropagation();
return;
}
}
Object.defineProperty(e, 'currentTarget', { value: e.currentTarget, writable: true });
let currentParent = e.currentTarget.parentNode;
for (let target = e.target; target && target !== currentParent; target = target.parentNode) {
if (target.matches(selector)) {
e.currentTarget = target;
if (context === null) {
handler.apply(target, [e]);
} else {
handler.apply(context, [e]);
}
break;
}
}
}, false);
}
}
/**
* Trigger a custom event on the element, asynchronously
*
* The event will bubble and is not cancelable.
*
* @param {string} type
* @param {{}} detail
*/
trigger(type, detail = null) {
setTimeout(() => {
this.element.dispatchEvent(new CustomEvent(type, {
cancelable: true, // TODO: this should depend on whether it's a native or custom event
bubbles: true,
detail: detail
}));
}, 0);
}
/**
* Focus the element
*
* Any other option than `preventScroll` is used as `event.detail`.
*
* @param {{}} options
*/
focus(options = {}) {
let { preventScroll = false, ...data } = options;
const hasData = Object.keys(data).length > 0;
if (hasData) {
this.element.receivesCustomFocus = true;
}
// Put separately on the event loop because focus() forces layout.
setTimeout(() => this.element.focus({ preventScroll: preventScroll }), 0);
if (hasData) {
this.trigger('focusin', data);
}
}
/**
* Render the element string as DOM Element
*
* @param {string} html
* @return {Element}
*/
static render(html) {
if (typeof html !== 'string') {
throw new Error("Can\'t render `" + html + "`");
}
let template = document.createElement('template');
template.innerHTML = html;
return template.content.firstChild;
}
}
/**
* Return a notjQuery object for the given element
*
* @param {Element} element
* @return {notjQuery}
*/
let factory = function (element) {
return new notjQuery(element);
}
// Define the static methods on the factory
for (let name of Object.getOwnPropertyNames(notjQuery)) {
if (['length', 'prototype', 'name'].includes(name)) {
continue;
}
Object.defineProperty(factory, name, {
value: notjQuery[name]
});
}
return factory;
});

3721
asset/js/vendor/Sortable.js vendored Normal file

File diff suppressed because it is too large Load Diff

2
asset/js/vendor/Sortable.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2705
asset/js/vendor/flatpickr.js vendored Normal file

File diff suppressed because it is too large Load Diff

2
asset/js/vendor/flatpickr.min.js vendored Normal file

File diff suppressed because one or more lines are too long

62
asset/js/vendor/flatpickr/l10n/ar.js vendored Normal file
View File

@ -0,0 +1,62 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ar = {}));
}(this, (function (exports) { 'use strict';
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
? window.flatpickr
: {
l10ns: {},
};
var Arabic = {
weekdays: {
shorthand: ["أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
longhand: [
"الأحد",
"الاثنين",
"الثلاثاء",
"الأربعاء",
"الخميس",
"الجمعة",
"السبت",
],
},
months: {
shorthand: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
longhand: [
"يناير",
"فبراير",
"مارس",
"أبريل",
"مايو",
"يونيو",
"يوليو",
"أغسطس",
"سبتمبر",
"أكتوبر",
"نوفمبر",
"ديسمبر",
],
},
firstDayOfWeek: 6,
rangeSeparator: " إلى ",
weekAbbreviation: "Wk",
scrollTitle: "قم بالتمرير للزيادة",
toggleTitle: "اضغط للتبديل",
amPM: ["ص", "م"],
yearAriaLabel: "سنة",
monthAriaLabel: "شهر",
hourAriaLabel: "ساعة",
minuteAriaLabel: "دقيقة",
time_24hr: false,
};
fp.l10ns.ar = Arabic;
var ar = fp.l10ns;
exports.Arabic = Arabic;
exports.default = ar;
Object.defineProperty(exports, '__esModule', { value: true });
})));

70
asset/js/vendor/flatpickr/l10n/de.js vendored Normal file
View File

@ -0,0 +1,70 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.de = {}));
}(this, (function (exports) { 'use strict';
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
? window.flatpickr
: {
l10ns: {},
};
var German = {
weekdays: {
shorthand: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
longhand: [
"Sonntag",
"Montag",
"Dienstag",
"Mittwoch",
"Donnerstag",
"Freitag",
"Samstag",
],
},
months: {
shorthand: [
"Jan",
"Feb",
"Mär",
"Apr",
"Mai",
"Jun",
"Jul",
"Aug",
"Sep",
"Okt",
"Nov",
"Dez",
],
longhand: [
"Januar",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember",
],
},
firstDayOfWeek: 1,
weekAbbreviation: "KW",
rangeSeparator: " bis ",
scrollTitle: "Zum Ändern scrollen",
toggleTitle: "Zum Umschalten klicken",
time_24hr: true,
};
fp.l10ns.de = German;
var de = fp.l10ns;
exports.German = German;
exports.default = de;
Object.defineProperty(exports, '__esModule', { value: true });
})));

70
asset/js/vendor/flatpickr/l10n/es.js vendored Normal file
View File

@ -0,0 +1,70 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.es = {}));
}(this, (function (exports) { 'use strict';
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
? window.flatpickr
: {
l10ns: {},
};
var Spanish = {
weekdays: {
shorthand: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"],
longhand: [
"Domingo",
"Lunes",
"Martes",
"Miércoles",
"Jueves",
"Viernes",
"Sábado",
],
},
months: {
shorthand: [
"Ene",
"Feb",
"Mar",
"Abr",
"May",
"Jun",
"Jul",
"Ago",
"Sep",
"Oct",
"Nov",
"Dic",
],
longhand: [
"Enero",
"Febrero",
"Marzo",
"Abril",
"Mayo",
"Junio",
"Julio",
"Agosto",
"Septiembre",
"Octubre",
"Noviembre",
"Diciembre",
],
},
ordinal: function () {
return "º";
},
firstDayOfWeek: 1,
rangeSeparator: " a ",
time_24hr: true,
};
fp.l10ns.es = Spanish;
var es = fp.l10ns;
exports.Spanish = Spanish;
exports.default = es;
Object.defineProperty(exports, '__esModule', { value: true });
})));

69
asset/js/vendor/flatpickr/l10n/fi.js vendored Normal file
View File

@ -0,0 +1,69 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.fi = {}));
}(this, (function (exports) { 'use strict';
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
? window.flatpickr
: {
l10ns: {},
};
var Finnish = {
firstDayOfWeek: 1,
weekdays: {
shorthand: ["su", "ma", "ti", "ke", "to", "pe", "la"],
longhand: [
"sunnuntai",
"maanantai",
"tiistai",
"keskiviikko",
"torstai",
"perjantai",
"lauantai",
],
},
months: {
shorthand: [
"tammi",
"helmi",
"maalis",
"huhti",
"touko",
"kesä",
"heinä",
"elo",
"syys",
"loka",
"marras",
"joulu",
],
longhand: [
"tammikuu",
"helmikuu",
"maaliskuu",
"huhtikuu",
"toukokuu",
"kesäkuu",
"heinäkuu",
"elokuu",
"syyskuu",
"lokakuu",
"marraskuu",
"joulukuu",
],
},
ordinal: function () {
return ".";
},
time_24hr: true,
};
fp.l10ns.fi = Finnish;
var fi = fp.l10ns;
exports.Finnish = Finnish;
exports.default = fi;
Object.defineProperty(exports, '__esModule', { value: true });
})));

75
asset/js/vendor/flatpickr/l10n/fr.js vendored Normal file
View File

@ -0,0 +1,75 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.fr = {}));
}(this, (function (exports) { 'use strict';
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
? window.flatpickr
: {
l10ns: {},
};
var French = {
firstDayOfWeek: 1,
weekdays: {
shorthand: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"],
longhand: [
"dimanche",
"lundi",
"mardi",
"mercredi",
"jeudi",
"vendredi",
"samedi",
],
},
months: {
shorthand: [
"janv",
"févr",
"mars",
"avr",
"mai",
"juin",
"juil",
"août",
"sept",
"oct",
"nov",
"déc",
],
longhand: [
"janvier",
"février",
"mars",
"avril",
"mai",
"juin",
"juillet",
"août",
"septembre",
"octobre",
"novembre",
"décembre",
],
},
ordinal: function (nth) {
if (nth > 1)
return "";
return "er";
},
rangeSeparator: " au ",
weekAbbreviation: "Sem",
scrollTitle: "Défiler pour augmenter la valeur",
toggleTitle: "Cliquer pour basculer",
time_24hr: true,
};
fp.l10ns.fr = French;
var fr = fp.l10ns;
exports.French = French;
exports.default = fr;
Object.defineProperty(exports, '__esModule', { value: true });
})));

71
asset/js/vendor/flatpickr/l10n/it.js vendored Normal file
View File

@ -0,0 +1,71 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.it = {}));
}(this, (function (exports) { 'use strict';
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
? window.flatpickr
: {
l10ns: {},
};
var Italian = {
weekdays: {
shorthand: ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"],
longhand: [
"Domenica",
"Lunedì",
"Martedì",
"Mercoledì",
"Giovedì",
"Venerdì",
"Sabato",
],
},
months: {
shorthand: [
"Gen",
"Feb",
"Mar",
"Apr",
"Mag",
"Giu",
"Lug",
"Ago",
"Set",
"Ott",
"Nov",
"Dic",
],
longhand: [
"Gennaio",
"Febbraio",
"Marzo",
"Aprile",
"Maggio",
"Giugno",
"Luglio",
"Agosto",
"Settembre",
"Ottobre",
"Novembre",
"Dicembre",
],
},
firstDayOfWeek: 1,
ordinal: function () { return "°"; },
rangeSeparator: " al ",
weekAbbreviation: "Se",
scrollTitle: "Scrolla per aumentare",
toggleTitle: "Clicca per cambiare",
time_24hr: true,
};
fp.l10ns.it = Italian;
var it = fp.l10ns;
exports.Italian = Italian;
exports.default = it;
Object.defineProperty(exports, '__esModule', { value: true });
})));

71
asset/js/vendor/flatpickr/l10n/ja.js vendored Normal file
View File

@ -0,0 +1,71 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ja = {}));
}(this, (function (exports) { 'use strict';
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
? window.flatpickr
: {
l10ns: {},
};
var Japanese = {
weekdays: {
shorthand: ["日", "月", "火", "水", "木", "金", "土"],
longhand: [
"日曜日",
"月曜日",
"火曜日",
"水曜日",
"木曜日",
"金曜日",
"土曜日",
],
},
months: {
shorthand: [
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月",
],
longhand: [
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月",
],
},
time_24hr: true,
rangeSeparator: " から ",
monthAriaLabel: "月",
amPM: ["午前", "午後"],
yearAriaLabel: "年",
hourAriaLabel: "時間",
minuteAriaLabel: "分",
};
fp.l10ns.ja = Japanese;
var ja = fp.l10ns;
exports.Japanese = Japanese;
exports.default = ja;
Object.defineProperty(exports, '__esModule', { value: true });
})));

66
asset/js/vendor/flatpickr/l10n/pt.js vendored Normal file
View File

@ -0,0 +1,66 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.pt = {}));
}(this, (function (exports) { 'use strict';
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
? window.flatpickr
: {
l10ns: {},
};
var Portuguese = {
weekdays: {
shorthand: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
longhand: [
"Domingo",
"Segunda-feira",
"Terça-feira",
"Quarta-feira",
"Quinta-feira",
"Sexta-feira",
"Sábado",
],
},
months: {
shorthand: [
"Jan",
"Fev",
"Mar",
"Abr",
"Mai",
"Jun",
"Jul",
"Ago",
"Set",
"Out",
"Nov",
"Dez",
],
longhand: [
"Janeiro",
"Fevereiro",
"Março",
"Abril",
"Maio",
"Junho",
"Julho",
"Agosto",
"Setembro",
"Outubro",
"Novembro",
"Dezembro",
],
},
rangeSeparator: " até ",
time_24hr: true,
};
fp.l10ns.pt = Portuguese;
var pt = fp.l10ns;
exports.Portuguese = Portuguese;
exports.default = pt;
Object.defineProperty(exports, '__esModule', { value: true });
})));

75
asset/js/vendor/flatpickr/l10n/ru.js vendored Normal file
View File

@ -0,0 +1,75 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ru = {}));
}(this, (function (exports) { 'use strict';
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
? window.flatpickr
: {
l10ns: {},
};
var Russian = {
weekdays: {
shorthand: ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"],
longhand: [
"Воскресенье",
"Понедельник",
"Вторник",
"Среда",
"Четверг",
"Пятница",
"Суббота",
],
},
months: {
shorthand: [
"Янв",
"Фев",
"Март",
"Апр",
"Май",
"Июнь",
"Июль",
"Авг",
"Сен",
"Окт",
"Ноя",
"Дек",
],
longhand: [
"Январь",
"Февраль",
"Март",
"Апрель",
"Май",
"Июнь",
"Июль",
"Август",
"Сентябрь",
"Октябрь",
"Ноябрь",
"Декабрь",
],
},
firstDayOfWeek: 1,
ordinal: function () {
return "";
},
rangeSeparator: " — ",
weekAbbreviation: "Нед.",
scrollTitle: "Прокрутите для увеличения",
toggleTitle: "Нажмите для переключения",
amPM: ["ДП", "ПП"],
yearAriaLabel: "Год",
time_24hr: true,
};
fp.l10ns.ru = Russian;
var ru = fp.l10ns;
exports.Russian = Russian;
exports.default = ru;
Object.defineProperty(exports, '__esModule', { value: true });
})));

66
asset/js/vendor/flatpickr/l10n/uk.js vendored Normal file
View File

@ -0,0 +1,66 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.uk = {}));
}(this, (function (exports) { 'use strict';
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
? window.flatpickr
: {
l10ns: {},
};
var Ukrainian = {
firstDayOfWeek: 1,
weekdays: {
shorthand: ["Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"],
longhand: [
"Неділя",
"Понеділок",
"Вівторок",
"Середа",
"Четвер",
"П'ятниця",
"Субота",
],
},
months: {
shorthand: [
"Січ",
"Лют",
"Бер",
"Кві",
"Тра",
"Чер",
"Лип",
"Сер",
"Вер",
"Жов",
"Лис",
"Гру",
],
longhand: [
"Січень",
"Лютий",
"Березень",
"Квітень",
"Травень",
"Червень",
"Липень",
"Серпень",
"Вересень",
"Жовтень",
"Листопад",
"Грудень",
],
},
time_24hr: true,
};
fp.l10ns.uk = Ukrainian;
var uk = fp.l10ns;
exports.Ukrainian = Ukrainian;
exports.default = uk;
Object.defineProperty(exports, '__esModule', { value: true });
})));

View File

@ -0,0 +1,869 @@
define(["../notjQuery", "Completer"], function ($, Completer) {
"use strict";
class BaseInput {
constructor(input) {
this.input = input;
this.disabled = false;
this.separator = '';
this.usedTerms = [];
this.completer = null;
this.lastCompletedTerm = null;
this._dataInput = null;
this._termInput = null;
this._termContainer = null;
}
get dataInput() {
if (this._dataInput === null) {
this._dataInput = document.querySelector(this.input.dataset.dataInput);
}
return this._dataInput;
}
get termInput() {
if (this._termInput === null) {
this._termInput = document.querySelector(this.input.dataset.termInput);
}
return this._termInput;
}
get termContainer() {
if (this._termContainer === null) {
this._termContainer = document.querySelector(this.input.dataset.termContainer);
}
return this._termContainer;
}
bind() {
// Form submissions
$(this.input.form).on('submit', this.onSubmit, this);
$(this.input.form).on(
'click', 'button:not([type]), button[type="submit"], input[type="submit"]', this.onButtonClick, this);
// User interactions
$(this.input).on('input', this.onInput, this);
$(this.input).on('keydown', this.onKeyDown, this);
$(this.input).on('keyup', this.onKeyUp, this);
$(this.input).on('blur', this.onInputBlur, this);
$(this.input).on('focusin', this.onTermFocus, this);
$(this.termContainer).on('input', '[data-label]', this.onInput, this);
$(this.termContainer).on('keydown', '[data-label]', this.onKeyDown, this);
$(this.termContainer).on('keyup', '[data-label]', this.onKeyUp, this);
$(this.termContainer).on('focusout', '[data-index]', this.onTermFocusOut, this);
$(this.termContainer).on('focusin', '[data-index]', this.onTermFocus, this);
// Copy/Paste
$(this.input).on('paste', this.onPaste, this);
$(this.input).on('copy', this.onCopyAndCut, this);
$(this.input).on('cut', this.onCopyAndCut, this);
// Should terms be completed?
if (this.input.dataset.suggestUrl) {
if (this.completer === null) {
this.completer = new Completer(this.input, true);
this.completer.bind(this.termContainer);
}
$(this.input).on('suggestion', this.onSuggestion, this);
$(this.input).on('completion', this.onCompletion, this);
$(this.termContainer).on('suggestion', '[data-label]', this.onSuggestion, this);
$(this.termContainer).on('completion', '[data-label]', this.onCompletion, this);
}
return this;
}
refresh(input) {
if (input === this.input) {
// If the DOM node is still the same, nothing has changed
return;
}
this._termInput = null;
this._termContainer = null;
this.input = input;
this.bind();
if (this.completer !== null) {
this.completer.refresh(input, this.termContainer);
}
if (! this.restoreTerms()) {
this.reset();
}
}
reset() {
this.usedTerms = [];
this.lastCompletedTerm = null;
this.togglePlaceholder();
this.termInput.value = '';
this.termContainer.innerHTML = '';
}
destroy() {
this._termContainer = null;
this._termInput = null;
this.input = null;
if (this.completer !== null) {
this.completer.destroy();
this.completer = null;
}
}
disable() {
this.disabled = true;
this.input.disabled = true;
this.input.form.classList.add('disabled');
this.termContainer.querySelectorAll('[data-index]').forEach(el => el.firstChild.disabled = true);
if (this.completer !== null) {
this.completer.reset();
}
}
enable() {
this.input.disabled = false;
this.input.form.classList.remove('disabled');
this.termContainer.querySelectorAll('[data-index]').forEach(el => el.firstChild.disabled = false);
this.disabled = false;
}
restoreTerms() {
if (this.hasTerms()) {
this.usedTerms.forEach((termData, termIndex) => this.addTerm(termData, termIndex));
this.togglePlaceholder();
this.clearPartialTerm(this.input);
} else {
this.registerTerms();
this.togglePlaceholder();
}
return this.hasTerms();
}
registerTerms() {
this.termContainer.querySelectorAll('[data-index]').forEach((label) => {
let termData = { ...label.dataset };
delete termData.index;
if (label.className) {
termData['class'] = label.className;
}
this.registerTerm(this.decodeTerm(termData), label.dataset.index);
});
}
registerTerm(termData, termIndex = null) {
if (termIndex !== null) {
this.usedTerms.splice(termIndex, 0, termData);
return termIndex;
} else {
return this.usedTerms.push(termData) - 1;
}
}
updateTerms(changedTerms) {
// Reset the data input, otherwise the value remains and is sent continuously with subsequent requests
this.dataInput.value = '';
for (const termIndex of Object.keys(changedTerms)) {
let label = this.termContainer.querySelector(`[data-index="${ termIndex }"]`);
if (! label) {
continue;
}
let input = label.firstChild;
let termData = changedTerms[termIndex];
if (termData.label) {
this.writePartialTerm(termData.label, input);
}
this.updateTermData(termData, input);
this.usedTerms[termIndex] = termData;
}
}
clearPartialTerm(input) {
if (this.completer !== null) {
this.completer.reset();
}
this.writePartialTerm('', input);
}
writePartialTerm(value, input) {
input.value = value;
this.updateTermData({ label: value }, input);
}
readPartialTerm(input) {
return input.value.trim();
}
readFullTerm(input, termIndex = null) {
let value = this.readPartialTerm(input);
if (! value) {
return false;
}
let termData = {};
if (termIndex !== null) {
termData = { ...this.usedTerms[termIndex] };
}
termData.label = value;
termData.search = value;
if (this.lastCompletedTerm !== null) {
if (termData.label === this.lastCompletedTerm.label) {
Object.assign(termData, this.lastCompletedTerm);
}
this.lastCompletedTerm = null;
}
return termData;
}
exchangeTerm() {
if (this.completer !== null) {
this.completer.reset();
}
let termData = this.readFullTerm(this.input);
if (! termData) {
return {};
}
let addedTerms = {};
if (Array.isArray(termData)) {
for (let data of termData) {
this.addTerm(data);
addedTerms[this.usedTerms.length - 1] = data;
}
} else {
this.addTerm(termData);
addedTerms[this.usedTerms.length - 1] = termData;
}
this.clearPartialTerm(this.input);
return addedTerms;
}
insertTerm(termData, termIndex) {
this.reIndexTerms(termIndex, 1, true);
this.registerTerm(termData, termIndex);
return this.insertRenderedTerm(this.renderTerm(termData, termIndex));
}
insertRenderedTerm(label) {
let next = this.termContainer.querySelector(`[data-index="${ label.dataset.index + 1 }"]`);
this.termContainer.insertBefore(label, next);
return label;
}
addTerm(termData, termIndex = null) {
if (termIndex === null) {
termIndex = this.registerTerm(termData);
}
this.addRenderedTerm(this.renderTerm(termData, termIndex));
}
addRenderedTerm(label) {
this.termContainer.appendChild(label);
}
hasTerms() {
return this.usedTerms.length > 0;
}
hasSyntaxError(input) {
if (typeof input === 'undefined') {
input = this.input;
}
return 'hasSyntaxError' in input.dataset;
}
clearSyntaxError(input) {
if (typeof input === 'undefined') {
input = this.input;
}
delete input.dataset.hasSyntaxError;
input.removeAttribute('pattern');
input.removeAttribute('title');
}
getQueryString() {
return this.termsToQueryString(this.usedTerms);
}
saveTerm(input, updateDOM = true) {
let termIndex = input.parentNode.dataset.index;
let termData = this.readFullTerm(input, termIndex);
// Only save if something has changed
if (termData === false) {
return this.removeTerm(input.parentNode, updateDOM);
} else if (this.usedTerms[termIndex].label !== termData.label) {
this.usedTerms[termIndex] = termData;
this.updateTermData(termData, input);
return termData;
}
return false;
}
updateTermData(termData, input) {
let label = input.parentNode;
label.dataset.label = termData.label;
if (!! termData.search || termData.search === '') {
label.dataset.search = termData.search;
}
}
termsToQueryString(terms) {
return terms.map(e => this.encodeTerm(e).search).join(this.separator).trim();
}
lastTerm() {
if (! this.hasTerms()) {
return null;
}
return this.usedTerms[this.usedTerms.length - 1];
}
popTerm() {
let lastTermIndex = this.usedTerms.length - 1;
return this.removeTerm(this.termContainer.querySelector(`[data-index="${ lastTermIndex }"]`));
}
removeTerm(label, updateDOM = true) {
if (this.completer !== null) {
this.completer.reset();
}
let termIndex = Number(label.dataset.index);
// Re-index following remaining terms
this.reIndexTerms(termIndex);
// Cut the term's data
let [termData] = this.usedTerms.splice(termIndex, 1);
// Avoid saving the term, it's removed after all
label.firstChild.skipSaveOnBlur = true;
if (updateDOM) {
// Remove it from the DOM
this.removeRenderedTerm(label);
}
return termData;
}
removeRenderedTerm(label) {
label.remove();
}
removeRange(labels) {
let from = Number(labels[0].dataset.index);
let to = Number(labels[labels.length - 1].dataset.index);
let deleteCount = to - from + 1;
if (to < this.usedTerms.length - 1) {
// Only re-index if there's something left
this.reIndexTerms(to, deleteCount);
}
let removedData = this.usedTerms.splice(from, deleteCount);
this.removeRenderedRange(labels);
let removedTerms = {};
for (let i = from; removedData.length; i++) {
removedTerms[i] = removedData.shift();
}
return removedTerms;
}
removeRenderedRange(labels) {
labels.forEach(label => this.removeRenderedTerm(label));
}
reIndexTerms(from, howMuch = 1, forward = false) {
if (forward) {
for (let i = this.usedTerms.length - 1; i >= from; i--) {
let label = this.termContainer.querySelector(`[data-index="${ i }"]`);
label.dataset.index = `${ i + howMuch }`;
}
} else {
for (let i = ++from; i < this.usedTerms.length; i++) {
let label = this.termContainer.querySelector(`[data-index="${ i }"]`);
label.dataset.index = `${ i - howMuch }`;
}
}
}
complete(input, data) {
if (this.completer !== null) {
$(input).trigger('complete', data);
}
}
selectTerms() {
this.termContainer.querySelectorAll('[data-index]').forEach(el => el.classList.add('selected'));
}
deselectTerms() {
this.termContainer.querySelectorAll('.selected').forEach(el => el.classList.remove('selected'));
}
clearSelectedTerms() {
if (this.hasTerms()) {
let labels = this.termContainer.querySelectorAll('.selected');
if (labels.length) {
return this.removeRange(Array.from(labels));
}
}
return {};
}
togglePlaceholder() {
let placeholder = '';
if (! this.hasTerms()) {
if (this.input.dataset.placeholder) {
placeholder = this.input.dataset.placeholder;
} else {
return;
}
} else if (this.input.placeholder) {
if (! this.input.dataset.placeholder) {
this.input.dataset.placeholder = this.input.placeholder;
}
}
this.input.placeholder = placeholder;
}
renderTerm(termData, termIndex) {
let label = $.render('<label><input type="text"></label>');
if (termData.class) {
label.classList.add(termData.class);
}
label.dataset.label = termData.label;
label.dataset.search = termData.search;
label.dataset.index = termIndex;
label.firstChild.value = termData.label;
return label;
}
encodeTerm(termData) {
termData = { ...termData };
termData.search = encodeURIComponent(termData.search);
return termData;
}
decodeTerm(termData) {
termData.search = decodeURIComponent(termData.search);
return termData;
}
shouldNotAutoSubmit() {
return 'noAutoSubmit' in this.input.dataset;
}
autoSubmit(input, changeType, changedTerms) {
if (this.shouldNotAutoSubmit()) {
return;
}
this.dataInput.value = JSON.stringify({
type: changeType,
terms: changedTerms
});
if (Object.keys(changedTerms).length) {
$(this.input.form).trigger('submit', { submittedBy: input });
}
}
submitTerms(terms) {
$(this.input.form).trigger(
'submit',
{ terms: terms }
);
}
moveFocusForward(from = null) {
let toFocus;
let inputs = Array.from(this.termContainer.querySelectorAll('input'));
if (from === null) {
let focused = this.termContainer.querySelector('input:focus');
from = inputs.indexOf(focused);
}
if (from === -1) {
toFocus = inputs.shift();
} else if (from + 1 < inputs.length) {
toFocus = inputs[from + 1];
} else {
toFocus = this.input;
}
toFocus.selectionStart = toFocus.selectionEnd = 0;
$(toFocus).focus();
return toFocus;
}
moveFocusBackward(from = null) {
let toFocus;
let inputs = Array.from(this.termContainer.querySelectorAll('input'));
if (from === null) {
let focused = this.termContainer.querySelector('input:focus');
from = inputs.indexOf(focused);
}
if (from === -1) {
toFocus = inputs.pop();
} else if (from > 0 && from - 1 < inputs.length) {
toFocus = inputs[from - 1];
} else {
toFocus = this.input;
}
toFocus.selectionStart = toFocus.selectionEnd = toFocus.value.length;
$(toFocus).focus();
return toFocus;
}
/**
* Event listeners
*/
onSubmit(event) {
// Unset the input's name, to prevent its submission (It may actually have a name, as no-js fallback)
this.input.name = '';
// Set the hidden input's value, it's what's sent
if (event.detail && 'terms' in event.detail) {
this.termInput.value = event.detail.terms;
} else {
let renderedTerms = this.termsToQueryString(this.usedTerms);
if (this.hasSyntaxError()) {
renderedTerms += this.input.value;
}
this.termInput.value = renderedTerms;
}
// Enable the hidden input, otherwise it's not submitted
this.termInput.disabled = false;
}
onSuggestion(event) {
let data = event.detail;
let input = event.target;
let termData;
if (typeof data === 'object') {
termData = data;
} else {
termData = { label: data, search: data };
}
this.lastCompletedTerm = termData;
this.writePartialTerm(termData.label, input);
}
onCompletion(event) {
let input = event.target;
let termData = event.detail;
let termIndex = Number(input.parentNode.dataset.index);
this.lastCompletedTerm = termData;
this.writePartialTerm(termData.label, input);
if (termIndex >= 0) {
this.autoSubmit(input, 'save', { [termIndex]: this.saveTerm(input) });
} else {
this.autoSubmit(input, 'exchange', this.exchangeTerm());
this.togglePlaceholder();
}
}
onInput(event) {
let input = event.target;
let isTerm = input.parentNode.dataset.index >= 0;
let termData = { label: this.readPartialTerm(input) };
this.updateTermData(termData, input);
if (! input.value && this.hasSyntaxError(input)) {
this.clearSyntaxError(input);
}
if (! this.hasSyntaxError(input)) {
this.complete(input, { term: termData });
}
if (! isTerm) {
this.autoSubmit(this.input, 'remove', this.clearSelectedTerms());
this.togglePlaceholder();
}
}
onKeyDown(event) {
let input = event.target;
let termIndex = Number(input.parentNode.dataset.index);
if (this.hasSyntaxError(input) && ! (/[A-Z]/.test(event.key.charAt(0)) || event.ctrlKey || event.metaKey)) {
// Clear syntax error flag if the user types entirely new input after having selected the entire input
// (This way the input isn't empty but switches from input to input immediately, causing the clearing
// in onInput to not work)
if (input.selectionEnd - input.selectionStart === input.value.length) {
this.clearSyntaxError(input);
}
}
let removedTerms;
switch (event.key) {
case ' ':
if (! this.readPartialTerm(input)) {
this.complete(input, { term: { label: '' } });
event.preventDefault();
}
break;
case 'Backspace':
removedTerms = this.clearSelectedTerms();
if (termIndex >= 0 && ! input.value) {
let removedTerm = this.removeTerm(input.parentNode);
if (removedTerm !== false) {
input = this.moveFocusBackward(termIndex);
if (event.ctrlKey || event.metaKey) {
this.clearPartialTerm(input);
} else {
this.writePartialTerm(input.value.slice(0, -1), input);
}
removedTerms[termIndex] = removedTerm;
event.preventDefault();
}
} else if (isNaN(termIndex)) {
if (! input.value && this.hasTerms()) {
let termData = this.popTerm();
if (! event.ctrlKey && ! event.metaKey) {
// Removing the last char programmatically is not
// necessary since the browser default is not prevented
this.writePartialTerm(termData.label, input);
}
removedTerms[this.usedTerms.length] = termData;
}
}
this.togglePlaceholder();
this.autoSubmit(input, 'remove', removedTerms);
break;
case 'Delete':
removedTerms = this.clearSelectedTerms();
if (termIndex >= 0 && ! input.value) {
let removedTerm = this.removeTerm(input.parentNode);
if (removedTerm !== false) {
input = this.moveFocusForward(termIndex - 1);
if (event.ctrlKey || event.metaKey) {
this.clearPartialTerm(input);
} else {
this.writePartialTerm(input.value.slice(1), input);
}
removedTerms[termIndex] = removedTerm;
event.preventDefault();
}
}
this.togglePlaceholder();
this.autoSubmit(input, 'remove', removedTerms);
break;
case 'Enter':
if (termIndex >= 0) {
this.saveTerm(input, false);
}
break;
case 'ArrowLeft':
if (input.selectionStart === 0 && this.hasTerms()) {
event.preventDefault();
this.moveFocusBackward();
}
break;
case 'ArrowRight':
if (input.selectionStart === input.value.length && this.hasTerms()) {
event.preventDefault();
this.moveFocusForward();
}
break;
case 'a':
if ((event.ctrlKey || event.metaKey) && ! this.readPartialTerm(input)) {
this.selectTerms();
}
}
}
onKeyUp(event) {
if (event.target.parentNode.dataset.index >= 0) {
return;
}
switch (event.key) {
case 'End':
case 'ArrowLeft':
case 'ArrowRight':
this.deselectTerms();
break;
case 'Home':
if (this.input.selectionStart === 0 && this.input.selectionEnd === 0) {
if (event.shiftKey) {
this.selectTerms();
} else {
this.deselectTerms();
}
}
break;
case 'Delete':
this.autoSubmit(event.target, 'remove', this.clearSelectedTerms());
this.togglePlaceholder();
break;
}
}
onInputBlur() {
this.deselectTerms();
}
onTermFocusOut(event) {
let input = event.target;
if (this.hasSyntaxError(input)) {
return;
}
// skipSaveOnBlur is set if the input is about to be removed anyway.
// If saveTerm would remove the input as well, the other removal will fail
// without any chance to handle it. (Element.remove() blurs the input)
if (typeof input.skipSaveOnBlur === 'undefined' || ! input.skipSaveOnBlur) {
setTimeout(() => {
if (this.completer === null || ! this.completer.isBeingCompleted(input)) {
let savedTerm = this.saveTerm(input);
if (savedTerm !== false) {
let termIndex = Number(input.parentNode.dataset.index);
this.autoSubmit(input, 'save', { [termIndex]: savedTerm });
}
}
}, 0);
}
}
onTermFocus(event) {
if (event.detail.scripted) {
// Only request suggestions if the user manually focuses the term
return;
}
this.deselectTerms();
let input = event.target;
if (! this.hasSyntaxError(input) && ! this.completer.isBeingCompleted(input, false)) {
// Only request suggestions if the input is valid and not already being completed
let value = this.readPartialTerm(input);
this.complete(input, { trigger: 'script', term: { label: value } });
}
}
onButtonClick(event) {
if (! this.hasSyntaxError()) {
// Register current input value, otherwise it's not included
this.exchangeTerm();
}
if (this.hasTerms()) {
this.input.required = false;
// This is not part of `onSubmit()` because otherwise it would override what `autoSubmit()` does
this.dataInput.value = JSON.stringify({ type: 'submit', terms: this.usedTerms });
} else if (typeof this.input.dataset.manageRequired !== 'undefined') {
this.input.required = true;
}
}
onPaste(event) {
if (this.hasTerms() || this.input.value) {
return;
}
this.submitTerms(event.clipboardData.getData('text/plain'));
event.preventDefault();
}
onCopyAndCut(event) {
if (! this.hasTerms()) {
return;
}
let data = '';
let selectedTerms = this.termContainer.querySelectorAll('.selected');
if (selectedTerms.length) {
data = Array.from(selectedTerms).map(label => label.dataset.search).join(this.separator);
}
if (this.input.selectionStart < this.input.selectionEnd) {
data += this.separator + this.input.value.slice(this.input.selectionStart, this.input.selectionEnd);
}
event.clipboardData.setData('text/plain', data);
event.preventDefault();
if (event.type === 'cut') {
this.clearPartialTerm(this.input);
this.autoSubmit(this.input, 'remove', this.clearSelectedTerms());
this.togglePlaceholder();
}
}
}
return BaseInput;
});

View File

@ -0,0 +1,521 @@
define(["../notjQuery"], function ($) {
"use strict";
class Completer {
constructor(input, instrumented = false) {
this.input = input;
this.instrumented = instrumented;
this.nextSuggestion = null;
this.activeSuggestion = null;
this.suggestionKiller = null;
this.completedInput = null;
this.completedValue = null;
this.completedData = null;
this._termSuggestions = null;
}
get termSuggestions() {
if (this._termSuggestions === null) {
this._termSuggestions = document.querySelector(this.input.dataset.termSuggestions);
}
return this._termSuggestions;
}
bind(to = null) {
// Form submissions
$(this.input.form).on('submit', this.onSubmit, this);
// User interactions
$(this.termSuggestions).on('focusout', '[type="button"]', this.onFocusOut, this);
$(this.termSuggestions).on('click', '[type="button"]', this.onSuggestionClick, this);
$(this.termSuggestions).on('keydown', '[type="button"]', this.onSuggestionKeyDown, this);
if (this.instrumented) {
if (to !== null) {
$(to).on('focusout', 'input[type="text"]', this.onFocusOut, this);
$(to).on('keydown', 'input[type="text"]', this.onKeyDown, this);
$(to).on('complete', 'input[type="text"]', this.onComplete, this);
}
$(this.input).on('complete', this.onComplete, this);
} else {
$(this.input).on('input', this.onInput, this);
}
$(this.input).on('focusout', this.onFocusOut, this);
$(this.input).on('keydown', this.onKeyDown, this);
return this;
}
refresh(input, bindTo = null) {
if (input === this.input) {
// If the DOM node is still the same, nothing has changed
return;
}
this._termSuggestions = null;
this.abort();
this.input = input;
this.bind(bindTo);
}
reset() {
this.abort();
this.hideSuggestions();
}
destroy() {
this._termSuggestions = null;
this.input = null;
}
renderSuggestions(html) {
let template = document.createElement('template');
template.innerHTML = html;
return template.content;
}
showSuggestions(suggestions, input) {
this.termSuggestions.innerHTML = '';
this.termSuggestions.appendChild(suggestions);
this.termSuggestions.style.display = '';
let containingBlock = this.termSuggestions.offsetParent || document.body;
let containingBlockRect = containingBlock.getBoundingClientRect();
let inputRect = input.getBoundingClientRect();
let inputPosX = inputRect.left - containingBlockRect.left;
let inputPosY = inputRect.bottom - containingBlockRect.top;
let suggestionWidth = this.termSuggestions.offsetWidth;
let maxAvailableHeight = document.body.clientHeight - inputRect.bottom;
let localMarginBottom = window.getComputedStyle(this.termSuggestions).marginBottom;
this.termSuggestions.style.top = `${ inputPosY }px`;
this.termSuggestions.style.maxHeight = `calc(${maxAvailableHeight}px - ${localMarginBottom})`;
if (inputPosX + suggestionWidth > containingBlockRect.right - containingBlockRect.left) {
this.termSuggestions.style.left =
`${ containingBlockRect.right - containingBlockRect.left - suggestionWidth }px`;
} else {
this.termSuggestions.style.left = `${ inputPosX }px`;
}
}
hasSuggestions() {
return this.termSuggestions.childNodes.length > 0;
}
hideSuggestions() {
if (this.nextSuggestion !== null || this.activeSuggestion !== null) {
return;
}
if (this.suggestionKiller !== null) {
// onFocusOut initiates this timer in order to hide the suggestions if the user
// doesn't navigate them. Since it does this by checking after a short interval
// if the focus is inside the suggestions, the interval has to be long enough to
// have a chance to detect the focus. `focusout` runs before `blur` and `focus`,
// so this may lead to a race condition which is addressed by the timeout. Though,
// to not close the newly opened suggestions of the next input the timer has to
// be cancelled here since it's purpose is already fulfilled.
clearTimeout(this.suggestionKiller);
this.suggestionKiller = null;
}
this.termSuggestions.style.display = 'none';
this.termSuggestions.innerHTML = '';
this.completedInput = null;
this.completedValue = null;
this.completedData = null;
}
prepareCompletionData(input, data = null) {
if (data === null) {
data = { term: { ...input.dataset } };
data.term.label = input.value;
}
let value = data.term.label;
data.term.search = value;
data.term.label = this.addWildcards(value);
if (input.parentElement instanceof HTMLFieldSetElement) {
for (let element of input.parentElement.elements) {
if (element !== input
&& element.name !== input.name + '-search'
&& (element.name.substr(-7) === '-search'
|| typeof input.form[element.name + '-search'] === 'undefined')
) {
// Make sure we'll use a key that the server can understand..
let dataName = element.name;
if (dataName.substr(-7) === '-search') {
dataName = dataName.substr(0, dataName.length - 7);
}
if (dataName.substr(0, input.parentElement.name.length) === input.parentElement.name) {
dataName = dataName.substr(input.parentElement.name.length);
}
if (! dataName in data || element.value) {
data[dataName] = element.value;
}
}
}
}
return [value, data];
}
addWildcards(value) {
if (! value) {
return '*';
}
if (value.slice(0, 1) !== '*' && value.slice(-1) !== '*') {
return '*' + value + '*';
}
return value;
}
abort() {
if (this.activeSuggestion !== null) {
this.activeSuggestion.abort();
this.activeSuggestion = null;
}
if (this.nextSuggestion !== null) {
clearTimeout(this.nextSuggestion);
this.nextSuggestion = null;
}
}
requestCompletion(input, data, trigger = 'user') {
this.abort();
this.nextSuggestion = setTimeout(() => {
let req = new XMLHttpRequest();
req.open('POST', this.input.dataset.suggestUrl, true);
req.setRequestHeader('Content-Type', 'application/json');
if (typeof icinga !== 'undefined') {
let windowId = icinga.ui.getWindowId();
let containerId = icinga.ui.getUniqueContainerId(this.termSuggestions);
if (containerId) {
req.setRequestHeader('X-Icinga-WindowId', windowId + '_' + containerId);
} else {
req.setRequestHeader('X-Icinga-WindowId', windowId);
}
}
req.addEventListener('loadend', () => {
if (req.readyState > 0) {
if (req.responseText) {
let suggestions = this.renderSuggestions(req.responseText);
if (trigger === 'script') {
// If the suggestions are to be displayed due to a scripted event,
// show them only if the completed input is still focused..
if (document.activeElement === input) {
this.showSuggestions(suggestions, input);
}
} else {
this.showSuggestions(suggestions, input);
}
} else {
this.hideSuggestions();
}
}
this.activeSuggestion = null;
this.nextSuggestion = null;
});
req.send(JSON.stringify(data));
this.activeSuggestion = req;
}, 200);
}
suggest(input, value, data = {}) {
if (this.instrumented) {
if (! Object.keys(data).length) {
data = value;
}
$(input).trigger('suggestion', data);
} else {
input.value = value;
}
}
complete(input, value, data) {
$(input).focus({ scripted: true });
if (this.instrumented) {
if (! Object.keys(data).length) {
data = value;
}
$(input).trigger('completion', data);
} else {
input.value = value;
for (let name in data) {
let dataElement = input.form[input.name + '-' + name];
if (typeof dataElement !== 'undefined') {
if (dataElement instanceof RadioNodeList) {
dataElement = dataElement[dataElement.length - 1];
}
dataElement.value = data[name];
}
}
}
this.hideSuggestions();
}
moveToSuggestion(backwards = false) {
let focused = this.termSuggestions.querySelector('[type="button"]:focus');
let inputs = Array.from(this.termSuggestions.querySelectorAll('[type="button"]'));
let input;
if (focused !== null) {
let sibling = inputs[backwards ? inputs.indexOf(focused) - 1 : inputs.indexOf(focused) + 1];
if (sibling) {
input = sibling;
} else {
input = this.completedInput;
}
} else {
input = inputs[backwards ? inputs.length - 1 : 0];
}
$(input).focus();
if (this.completedValue !== null) {
if (input === this.completedInput) {
this.suggest(this.completedInput, this.completedValue);
} else {
this.suggest(this.completedInput, input.value, { ...input.dataset });
}
}
return input;
}
isBeingCompleted(input, activeElement = null) {
if (activeElement === null) {
activeElement = document.activeElement;
}
return input === this.completedInput && (
(! activeElement && this.hasSuggestions())
|| (activeElement && this.termSuggestions.contains(activeElement))
);
}
/**
* Event listeners
*/
onSubmit(event) {
// Reset all states, the user is about to navigate away
this.reset();
}
onFocusOut(event) {
if (this.completedInput === null) {
// If there are multiple instances of Completer bound to the same suggestion container
// all of them try to handle the event. Though, only one of them is responsible and
// that's the one which has a completed input set.
return;
}
let input = event.target;
let completedInput = this.completedInput;
this.suggestionKiller = setTimeout(() => {
if (completedInput !== this.completedInput) {
// Don't hide another input's suggestions
} else if (document.activeElement !== completedInput
&& ! this.termSuggestions.contains(document.activeElement)
) {
// Hide the suggestions if the user doesn't navigate them
if (input !== completedInput) {
// Restore input if a suggestion lost focus
this.suggest(completedInput, this.completedValue);
}
this.hideSuggestions();
}
}, 250);
}
onSuggestionKeyDown(event) {
if (this.completedInput === null) {
return;
}
switch (event.key) {
case 'Escape':
$(this.completedInput).focus({ scripted: true });
this.suggest(this.completedInput, this.completedValue);
break;
case 'Tab':
event.preventDefault();
this.moveToSuggestion(event.shiftKey);
break;
case 'ArrowLeft':
case 'ArrowUp':
event.preventDefault();
this.moveToSuggestion(true);
break;
case 'ArrowRight':
case 'ArrowDown':
event.preventDefault();
this.moveToSuggestion();
break;
}
}
onSuggestionClick(event) {
if (this.completedInput === null) {
return;
}
let input = event.currentTarget;
this.complete(this.completedInput, input.value, { ...input.dataset });
}
onKeyDown(event) {
let suggestions;
switch (event.key) {
case ' ':
if (this.instrumented) {
break;
}
let input = event.target;
if (! input.value) {
if (input.minLength <= 0) {
let [value, data] = this.prepareCompletionData(input);
this.completedInput = input;
this.completedValue = value;
this.completedData = data;
this.requestCompletion(input, data);
}
event.preventDefault();
}
break;
case 'Tab':
suggestions = this.termSuggestions.querySelectorAll('[type="button"]');
if (suggestions.length === 1) {
event.preventDefault();
let input = event.target;
let suggestion = suggestions[0];
this.complete(input, suggestion.value, { ...suggestion.dataset });
}
break;
case 'Enter':
let defaultSuggestion = this.termSuggestions.querySelector('.default > [type="button"]');
if (defaultSuggestion !== null) {
event.preventDefault();
let input = event.target;
this.complete(input, defaultSuggestion.value, { ...defaultSuggestion.dataset });
}
break;
case 'Escape':
if (this.hasSuggestions()) {
this.hideSuggestions()
event.preventDefault();
}
break;
case 'ArrowUp':
suggestions = this.termSuggestions.querySelectorAll('[type="button"]');
if (suggestions.length) {
event.preventDefault();
this.moveToSuggestion(true);
}
break;
case 'ArrowDown':
suggestions = this.termSuggestions.querySelectorAll('[type="button"]');
if (suggestions.length) {
event.preventDefault();
this.moveToSuggestion();
}
break;
default:
if (/[A-Z]/.test(event.key.charAt(0)) || event.key === '"') {
// Ignore control keys not resulting in new input data
break;
}
let typedSuggestion = this.termSuggestions.querySelector(`[value="${ event.key }"]`);
if (typedSuggestion !== null) {
this.hideSuggestions();
}
}
}
onInput(event) {
let input = event.target;
if (input.minLength > 0 && input.value.length < input.minLength) {
return;
}
// Set the input's value as search value. This ensures that if the user doesn't
// choose a suggestion, an up2date contextual value will be transmitted with
// completion requests and the server can properly identify a new value upon submit
input.dataset.search = input.value;
if (typeof input.form[input.name + '-search'] !== 'undefined') {
let dataElement = input.form[input.name + '-search'];
if (dataElement instanceof RadioNodeList) {
dataElement = dataElement[dataElement.length - 1];
}
dataElement.value = input.value;
}
let [value, data] = this.prepareCompletionData(input);
this.completedInput = input;
this.completedValue = value;
this.completedData = data;
this.requestCompletion(input, data);
}
onComplete(event) {
let input = event.target;
let { trigger = 'user' , ...detail } = event.detail;
let [value, data] = this.prepareCompletionData(input, detail);
this.completedInput = input;
this.completedValue = value;
this.completedData = data;
if (typeof data.suggestions !== 'undefined') {
this.showSuggestions(data.suggestions, input);
} else {
this.requestCompletion(input, data, trigger);
}
}
}
return Completer;
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
define(["../notjQuery"], function ($) {
"use strict";
class SearchBar {
constructor(form) {
this.form = form;
this.filterInput = null;
}
bind() {
$(this.form.parentNode).on('click', '[data-search-editor-url]', this.onOpenerClick, this);
return this;
}
refresh(form) {
if (form === this.form) {
// If the DOM node is still the same, nothing has changed
return;
}
this.form = form;
this.bind();
}
destroy() {
this.form = null;
this.filterInput = null;
}
setFilterInput(filterInput) {
this.filterInput = filterInput;
return this;
}
onOpenerClick(event) {
let opener = event.currentTarget;
let editorUrl = opener.dataset.searchEditorUrl;
let filterQueryString = this.filterInput.getQueryString();
let layout = document.getElementById('layout');
editorUrl += (editorUrl.indexOf('?') > -1 ? '&' : '?') + filterQueryString;
// Disable pointer events to block further function calls
opener.style.pointerEvents = 'none';
let observer = new MutationObserver((mutations) => {
for (let mutation of mutations) {
if (mutation.type === 'childList') {
mutation.removedNodes.forEach((node) => {
// Remove the pointerEvent none style to make the button clickable again
// after the modal has been removed
if (node.id === 'modal') {
opener.style.pointerEvents = '';
observer.disconnect();
}
});
}
}
});
observer.observe(layout, {childList: true});
// The search editor should open in a modal. We simulate a click on an anchor
// appropriately prepared so that Icinga Web 2 will handle it natively.
let a = document.createElement('a');
a.classList.add('modal-opener');
a.href = editorUrl;
a.dataset.noIcingaAjax = '';
a.dataset.icingaModal = '';
opener.parentNode.insertBefore(a, opener.nextSibling);
a.click();
a.remove();
}
}
return SearchBar;
});

View File

@ -0,0 +1,79 @@
define(["../notjQuery", "../vendor/Sortable"], function ($, Sortable) {
"use strict";
class SearchEditor {
constructor(form) {
this.form = form;
}
bind() {
$(this.form).on('end', this.onRuleDropped, this);
this.form.querySelectorAll('ol').forEach(sortable => {
let options = {
scroll: true,
group: 'rules',
direction: 'vertical',
invertSwap: true,
handle: '.drag-initiator'
};
Sortable.create(sortable, options);
});
return this;
}
refresh(form) {
if (form === this.form) {
// If the DOM node is still the same, nothing has changed
return;
}
this.form = form;
this.bind();
}
destroy() {
this.form = null;
this.filterInput = null;
}
onRuleDropped(event) {
if (event.to === event.from && event.newIndex === event.oldIndex) {
// The user dropped the rule at its previous position
return;
}
let placement = 'before';
let neighbour = event.to.querySelector(':scope > :nth-child(' + (event.newIndex + 2) + ')');
if (! neighbour) {
// User dropped the rule at the end of a group
placement = 'after';
neighbour = event.to.querySelector(':scope > :nth-child(' + event.newIndex + ')')
if (! neighbour) {
// User dropped the rule into an empty group
placement = 'to';
neighbour = event.to.closest('[id]');
}
}
// It's a submit element, the very first one, otherwise Icinga Web 2 sends another "structural-change"
this.form.insertBefore(
$.render(
'<input type="hidden" name="structural-change[1]" value="' + placement + ':' + neighbour.id + '">'
),
this.form.firstChild
);
this.form.insertBefore(
$.render('<input type="submit" name="structural-change[0]" value="move-rule:' + event.item.id + '">'),
this.form.firstChild
);
$(this.form).trigger('submit');
}
}
return SearchEditor;
});

View File

@ -0,0 +1,128 @@
define(["BaseInput"], function (BaseInput) {
"use strict";
class TermInput extends BaseInput {
constructor(input) {
super(input);
this.separator = ' ';
this.ignoreSpaceUntil = null;
this.ignoreSpaceSince = null;
}
reset() {
super.reset();
this.ignoreSpaceUntil = null;
this.ignoreSpaceSince = null;
}
writePartialTerm(value, input) {
if (this.ignoreSpaceUntil !== null && this.ignoreSpaceSince === 0) {
value = this.ignoreSpaceUntil + value;
}
super.writePartialTerm(value, input);
}
readFullTerm(input, termIndex = null) {
let termData = super.readFullTerm(input, termIndex);
if (this.ignoreSpaceUntil !== null && termData.label[this.ignoreSpaceSince] === this.ignoreSpaceUntil) {
if (termData.label.length - 1 === this.ignoreSpaceSince
|| termData.label.slice(-1) !== this.ignoreSpaceUntil
|| (this.ignoreSpaceSince === 0 && (termData.label.length < 2
|| termData.label.slice(0, 1) !== this.ignoreSpaceUntil)
)
) {
return false;
}
}
return termData;
}
addTerm(termData, termIndex = null) {
if (this.ignoreSpaceUntil !== null) {
if (this.ignoreSpaceSince === 0 && termData.label[this.ignoreSpaceSince] === this.ignoreSpaceUntil) {
termData.label = termData.label.slice(1, -1);
}
this.ignoreSpaceUntil = null;
this.ignoreSpaceSince = null;
}
super.addTerm(termData, termIndex);
}
complete(input, data) {
data.exclude = this.usedTerms.map(termData => termData.search);
super.complete(input, data);
}
/**
* Event listeners
*/
onSubmit(event) {
super.onSubmit(event);
this.ignoreSpaceUntil = null;
this.ignoreSpaceSince = null;
}
onKeyDown(event) {
super.onKeyDown(event);
if (event.defaultPrevented) {
return;
}
let label = event.target.parentNode;
if (label.dataset.index >= 0) {
return;
}
if (event.key !== this.separator) {
return;
}
let addedTerms = this.exchangeTerm();
if (addedTerms.length) {
this.togglePlaceholder();
event.preventDefault();
this.autoSubmit(this.input, 'exchange', addedTerms);
}
}
onKeyUp(event) {
super.onKeyUp(event);
let label = event.target.parentNode;
if (label.dataset.index >= 0) {
return;
}
if (this.ignoreSpaceUntil !== null) {
// Reset if the user changes/removes the source char
let value = event.target.value;
if (value[this.ignoreSpaceSince] !== this.ignoreSpaceUntil) {
this.ignoreSpaceUntil = null;
this.ignoreSpaceSince = null;
}
}
let input = event.target;
switch (event.key) {
case '"':
case "'":
if (this.ignoreSpaceUntil === null) {
this.ignoreSpaceUntil = event.key;
this.ignoreSpaceSince = input.selectionStart - 1;
}
}
}
}
return TermInput;
});

Binary file not shown.

View File

@ -0,0 +1,13 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="Icinga-Icons" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe900;" glyph-name="minimal" d="M192.009 831.995c0-35.361-28.665-64.026-64.026-64.026s-64.026 28.665-64.026 64.026c0 35.361 28.665 64.026 64.026 64.026s64.026-28.665 64.026-64.026zM192.009 639.671c0-35.361-28.665-64.026-64.026-64.026s-64.026 28.665-64.026 64.026c0 35.361 28.665 64.026 64.026 64.026s64.026-28.665 64.026-64.026zM192.009 447.671c0-35.361-28.665-64.026-64.026-64.026s-64.026 28.665-64.026 64.026c0 35.361 28.665 64.026 64.026 64.026s64.026-28.665 64.026-64.026zM256.005 863.997h704.001v-64.005h-704.001v64.005zM256.005 479.674h704.001v-64.005h-704.001v64.005zM256.005 671.674h704.001v-64.005h-704.001v64.005zM256.005 287.674h704.001v-64.005h-704.001v64.005zM256.005 95.674h704.001v-64.005h-704.001v64.005zM192.009 255.348c0-35.361-28.665-64.026-64.026-64.026s-64.026 28.665-64.026 64.026c0 35.361 28.665 64.026 64.026 64.026s64.026-28.665 64.026-64.026zM192.009 63.671c0-35.361-28.665-64.026-64.026-64.026s-64.026 28.665-64.026 64.026c0 35.361 28.665 64.026 64.026 64.026s64.026-28.665 64.026-64.026z" />
<glyph unicode="&#xe901;" glyph-name="detailed" d="M320.007 831.679h639.993v-191.358h-639.993v191.358zM320.007 575.997h639.993v-63.992h-639.993v63.992zM256.014 736.317c0-53.041-42.998-96.039-96.039-96.039s-96.039 42.998-96.039 96.039c0 53.041 42.998 96.039 96.039 96.039s96.039-42.998 96.039-96.039zM256.014 287.992c0-53.041-42.998-96.039-96.039-96.039s-96.039 42.998-96.039 96.039c0 53.041 42.998 96.039 96.039 96.039s96.039-42.998 96.039-96.039zM320.007 384.004h639.993v-192.005h-639.993v192.005zM320.007 128.005h639.993v-63.992h-639.993v63.992z" />
<glyph unicode="&#xe902;" glyph-name="default" d="M256.015 767.992c0-53.041-42.998-96.039-96.039-96.039s-96.039 42.998-96.039 96.039c0 53.041 42.998 96.039 96.039 96.039s96.039-42.998 96.039-96.039zM384.001 864h576.002v-192.008h-576.002v192.008zM384.001 543.998h576.002v-192.008h-576.002v192.008zM384.001 223.998h576.002v-192.008h-576.002v192.008zM256.015 447.99c0-53.041-42.998-96.039-96.039-96.039s-96.039 42.998-96.039 96.039c0 53.041 42.998 96.039 96.039 96.039s96.039-42.998 96.039-96.039zM256.015 127.99c0-53.041-42.998-96.039-96.039-96.039s-96.039 42.998-96.039 96.039c0 53.041 42.998 96.039 96.039 96.039s96.039-42.998 96.039-96.039z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
<svg height="32" viewBox="0 0 24 32" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m5.20126707.78766623 4.45386238 4.20402191c.16721345.15783356.16291017.40979541-.00961164.56277256-.081158.0719638-.18974398.11220597-.3027668.11220597h-8.90772462c-.24025844 0-.43502639-.17818569-.43502639-.39798892 0-.10340014.04398717-.20274128.12264801-.27698961l4.45386234-4.20402191c.16721345-.15783357.44262326-.16177048.61514507-.00879333.00325382.00288518.00645805.00581661.00961165.00879333z" fill="#282E39" transform="matrix(1 0 0 -1 7 20.666667)"/></svg>

After

Width:  |  Height:  |  Size: 559 B

View File

@ -0,0 +1 @@
<svg height="32" viewBox="0 0 24 32" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m5.20126707.78766623 4.45386238 4.20402191c.16721345.15783356.16291017.40979541-.00961164.56277256-.081158.0719638-.18974398.11220597-.3027668.11220597h-8.90772462c-.24025844 0-.43502639-.17818569-.43502639-.39798892 0-.10340014.04398717-.20274128.12264801-.27698961l4.45386234-4.20402191c.16721345-.15783357.44262326-.16177048.61514507-.00879333.00325382.00288518.00645805.00581661.00961165.00879333z" fill="#00c3ed" transform="matrix(1 0 0 -1 7 20.666667)"/></svg>

After

Width:  |  Height:  |  Size: 558 B

464
composer.lock generated Normal file
View File

@ -0,0 +1,464 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "5cda0d9f19074ae89902f263bb6a3695",
"packages": [
{
"name": "evenement/evenement",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/igorw/evenement.git",
"reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
"reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
"shasum": ""
},
"require": {
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Evenement": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch"
}
],
"description": "Événement is a very simple event dispatching library for PHP",
"keywords": [
"event-dispatcher",
"event-emitter"
],
"support": {
"issues": "https://github.com/igorw/evenement/issues",
"source": "https://github.com/igorw/evenement/tree/master"
},
"time": "2017-07-23T21:35:13+00:00"
},
{
"name": "fortawesome/font-awesome",
"version": "6.1.1",
"source": {
"type": "git",
"url": "https://github.com/FortAwesome/Font-Awesome.git",
"reference": "28e297f07af26f148c15e6cbbd12cea3027371d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/FortAwesome/Font-Awesome/zipball/28e297f07af26f148c15e6cbbd12cea3027371d3",
"reference": "28e297f07af26f148c15e6cbbd12cea3027371d3",
"shasum": ""
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"CC-BY-4.0",
"OFL-1.1",
"MIT"
],
"authors": [
{
"name": "The Font Awesome Team",
"homepage": "https://github.com/orgs/FortAwesome/people"
}
],
"description": "The iconic font, CSS, and SVG framework",
"homepage": "https://fontawesome.com",
"keywords": [
"FontAwesome",
"awesome",
"bootstrap",
"font",
"icon",
"svg"
],
"support": {
"docs": "http://fontawesome.com/docs",
"email": "hello@fontawesome.com",
"issues": "https://github.com/FortAwesome/Font-Awesome/issues",
"source": "https://github.com/FortAwesome/Font-Awesome"
},
"time": "2022-03-22T15:24:29+00:00"
},
{
"name": "ipl/html",
"version": "v0.6.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-html.git",
"reference": "239b215ab81205f69d8df2663b0fecb138562547"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-html/zipball/239b215ab81205f69d8df2663b0fecb138562547",
"reference": "239b215ab81205f69d8df2663b0fecb138562547",
"shasum": ""
},
"require": {
"ipl/stdlib": ">=0.12.0",
"ipl/validator": ">=0.4.0",
"php": ">=7.2",
"psr/http-message": "~1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"ipl\\Html\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - HTML abstraction layer",
"homepage": "https://github.com/Icinga/ipl-html",
"keywords": [
"html"
],
"support": {
"issues": "https://github.com/Icinga/ipl-html/issues",
"source": "https://github.com/Icinga/ipl-html/tree/v0.6.0"
},
"time": "2022-06-15T08:17:00+00:00"
},
{
"name": "ipl/i18n",
"version": "v0.2.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-i18n.git",
"reference": "3ee2a8c0c38879cb743c866d9202f8620d4c2800"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-i18n/zipball/3ee2a8c0c38879cb743c866d9202f8620d4c2800",
"reference": "3ee2a8c0c38879cb743c866d9202f8620d4c2800",
"shasum": ""
},
"require": {
"ext-gettext": "*",
"ext-intl": "*",
"ipl/stdlib": ">=0.12.0",
"php": ">=7.2"
},
"type": "library",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"ipl\\I18n\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - Internationalization",
"homepage": "https://github.com/Icinga/ipl-i18n",
"keywords": [
"gettext",
"i18n",
"internationalization",
"localization",
"translation"
],
"support": {
"issues": "https://github.com/Icinga/ipl-i18n/issues",
"source": "https://github.com/Icinga/ipl-i18n/tree/v0.2.0"
},
"time": "2022-06-15T07:33:40+00:00"
},
{
"name": "ipl/orm",
"version": "v0.4.1",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-orm.git",
"reference": "0b76de078b9ebff608ce07b1ea051fa7d82f6261"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-orm/zipball/0b76de078b9ebff608ce07b1ea051fa7d82f6261",
"reference": "0b76de078b9ebff608ce07b1ea051fa7d82f6261",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"ipl/sql": ">=0.5.0",
"ipl/stdlib": ">=0.12.0",
"php": ">=7.2"
},
"require-dev": {
"ext-pdo_sqlite": "*"
},
"type": "library",
"autoload": {
"psr-4": {
"ipl\\Orm\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - ORM",
"homepage": "https://github.com/Icinga/ipl-orm",
"keywords": [
"database",
"orm",
"sql"
],
"support": {
"issues": "https://github.com/Icinga/ipl-orm/issues",
"source": "https://github.com/Icinga/ipl-orm/tree/v0.4.1"
},
"time": "2022-07-01T16:15:30+00:00"
},
{
"name": "ipl/sql",
"version": "v0.5.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-sql.git",
"reference": "cbe5d0854ef0612c7108b84b0864b4e69e81afd1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-sql/zipball/cbe5d0854ef0612c7108b84b0864b4e69e81afd1",
"reference": "cbe5d0854ef0612c7108b84b0864b4e69e81afd1",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"ipl/stdlib": ">=0.12.0",
"php": ">=7.2"
},
"type": "library",
"autoload": {
"psr-4": {
"ipl\\Sql\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - SQL abstraction layer",
"homepage": "https://github.com/Icinga/ipl-sql",
"keywords": [
"database",
"sql"
],
"support": {
"issues": "https://github.com/Icinga/ipl-sql/issues",
"source": "https://github.com/Icinga/ipl-sql/tree/v0.5.0"
},
"time": "2022-06-15T08:43:21+00:00"
},
{
"name": "ipl/stdlib",
"version": "v0.12.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-stdlib.git",
"reference": "d42a16122975a629ab7d60eff780b9fd9949545e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-stdlib/zipball/d42a16122975a629ab7d60eff780b9fd9949545e",
"reference": "d42a16122975a629ab7d60eff780b9fd9949545e",
"shasum": ""
},
"require": {
"evenement/evenement": "^3",
"ext-openssl": "*",
"php": ">=7.2"
},
"type": "library",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"ipl\\Stdlib\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "ipl Standard Library",
"support": {
"issues": "https://github.com/Icinga/ipl-stdlib/issues",
"source": "https://github.com/Icinga/ipl-stdlib/tree/v0.12.0"
},
"time": "2022-06-15T07:29:19+00:00"
},
{
"name": "ipl/validator",
"version": "v0.4.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-validator.git",
"reference": "b8af9ef02654e04b63c6f28507a80270f5248ecb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-validator/zipball/b8af9ef02654e04b63c6f28507a80270f5248ecb",
"reference": "b8af9ef02654e04b63c6f28507a80270f5248ecb",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ipl/i18n": ">=0.2.0",
"ipl/stdlib": ">=0.12.0",
"php": ">=7.2"
},
"type": "library",
"autoload": {
"psr-4": {
"ipl\\Validator\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - Common validators and validator chaining",
"homepage": "https://github.com/Icinga/ipl-validator",
"support": {
"issues": "https://github.com/Icinga/ipl-validator/issues",
"source": "https://github.com/Icinga/ipl-validator/tree/v0.4.0"
},
"time": "2022-06-15T07:51:54+00:00"
},
{
"name": "ipl/web",
"version": "v0.5.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-web.git",
"reference": "9c3423a504ebea823309452e8414f5b0b1746bdd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-web/zipball/9c3423a504ebea823309452e8414f5b0b1746bdd",
"reference": "9c3423a504ebea823309452e8414f5b0b1746bdd",
"shasum": ""
},
"require": {
"ext-json": "*",
"fortawesome/font-awesome": "^6",
"ipl/html": ">=0.6.0",
"ipl/stdlib": ">=0.12.0",
"php": ">=7.2"
},
"type": "library",
"autoload": {
"psr-4": {
"ipl\\Web\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - Web Components",
"homepage": "https://github.com/Icinga/ipl-web",
"keywords": [
"html"
],
"support": {
"issues": "https://github.com/Icinga/ipl-web/issues",
"source": "https://github.com/Icinga/ipl-web/tree/v0.5.0"
},
"time": "2022-06-15T12:25:42+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/master"
},
"time": "2016-08-06T14:39:51+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=7.2"
},
"platform-dev": [],
"platform-overrides": {
"php": "7.2"
},
"plugin-api-version": "2.3.0"
}

12
vendor/autoload.php vendored Normal file
View File

@ -0,0 +1,12 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
exit(1);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit9b4593d5384b4d54213d2614c1347711::getLoader();

572
vendor/composer/ClassLoader.php vendored Normal file
View File

@ -0,0 +1,572 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
}

352
vendor/composer/InstalledVersions.php vendored Normal file
View File

@ -0,0 +1,352 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
}

21
vendor/composer/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

10
vendor/composer/autoload_classmap.php vendored Normal file
View File

@ -0,0 +1,10 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

11
vendor/composer/autoload_files.php vendored Normal file
View File

@ -0,0 +1,11 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'a2c78434f64e5f5ed402f42eee19c025' => $vendorDir . '/ipl/stdlib/src/functions_include.php',
'6076de347104821999fcfc82c8f19bc5' => $vendorDir . '/ipl/i18n/src/functions_include.php',
);

11
vendor/composer/autoload_namespaces.php vendored Normal file
View File

@ -0,0 +1,11 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Evenement' => array($vendorDir . '/evenement/evenement/src'),
'AssetLoader' => array($baseDir . '/'),
);

17
vendor/composer/autoload_psr4.php vendored Normal file
View File

@ -0,0 +1,17 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'ipl\\Web\\' => array($vendorDir . '/ipl/web/src'),
'ipl\\Validator\\' => array($vendorDir . '/ipl/validator/src'),
'ipl\\Stdlib\\' => array($vendorDir . '/ipl/stdlib/src'),
'ipl\\Sql\\' => array($vendorDir . '/ipl/sql/src'),
'ipl\\Orm\\' => array($vendorDir . '/ipl/orm/src'),
'ipl\\I18n\\' => array($vendorDir . '/ipl/i18n/src'),
'ipl\\Html\\' => array($vendorDir . '/ipl/html/src'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
);

57
vendor/composer/autoload_real.php vendored Normal file
View File

@ -0,0 +1,57 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit9b4593d5384b4d54213d2614c1347711
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit9b4593d5384b4d54213d2614c1347711', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit9b4593d5384b4d54213d2614c1347711', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit9b4593d5384b4d54213d2614c1347711::getInitializer($loader));
$loader->register(true);
$includeFiles = \Composer\Autoload\ComposerStaticInit9b4593d5384b4d54213d2614c1347711::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire9b4593d5384b4d54213d2614c1347711($fileIdentifier, $file);
}
return $loader;
}
}
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequire9b4593d5384b4d54213d2614c1347711($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}

97
vendor/composer/autoload_static.php vendored Normal file
View File

@ -0,0 +1,97 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit9b4593d5384b4d54213d2614c1347711
{
public static $files = array (
'a2c78434f64e5f5ed402f42eee19c025' => __DIR__ . '/..' . '/ipl/stdlib/src/functions_include.php',
'6076de347104821999fcfc82c8f19bc5' => __DIR__ . '/..' . '/ipl/i18n/src/functions_include.php',
);
public static $prefixLengthsPsr4 = array (
'i' =>
array (
'ipl\\Web\\' => 8,
'ipl\\Validator\\' => 14,
'ipl\\Stdlib\\' => 11,
'ipl\\Sql\\' => 8,
'ipl\\Orm\\' => 8,
'ipl\\I18n\\' => 9,
'ipl\\Html\\' => 9,
),
'P' =>
array (
'Psr\\Http\\Message\\' => 17,
),
);
public static $prefixDirsPsr4 = array (
'ipl\\Web\\' =>
array (
0 => __DIR__ . '/..' . '/ipl/web/src',
),
'ipl\\Validator\\' =>
array (
0 => __DIR__ . '/..' . '/ipl/validator/src',
),
'ipl\\Stdlib\\' =>
array (
0 => __DIR__ . '/..' . '/ipl/stdlib/src',
),
'ipl\\Sql\\' =>
array (
0 => __DIR__ . '/..' . '/ipl/sql/src',
),
'ipl\\Orm\\' =>
array (
0 => __DIR__ . '/..' . '/ipl/orm/src',
),
'ipl\\I18n\\' =>
array (
0 => __DIR__ . '/..' . '/ipl/i18n/src',
),
'ipl\\Html\\' =>
array (
0 => __DIR__ . '/..' . '/ipl/html/src',
),
'Psr\\Http\\Message\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-message/src',
),
);
public static $prefixesPsr0 = array (
'E' =>
array (
'Evenement' =>
array (
0 => __DIR__ . '/..' . '/evenement/evenement/src',
),
),
'A' =>
array (
'AssetLoader' =>
array (
0 => __DIR__ . '/../..' . '/',
),
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit9b4593d5384b4d54213d2614c1347711::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit9b4593d5384b4d54213d2614c1347711::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit9b4593d5384b4d54213d2614c1347711::$prefixesPsr0;
$loader->classMap = ComposerStaticInit9b4593d5384b4d54213d2614c1347711::$classMap;
}, null, ClassLoader::class);
}
}

476
vendor/composer/installed.json vendored Normal file
View File

@ -0,0 +1,476 @@
{
"packages": [
{
"name": "evenement/evenement",
"version": "v3.0.1",
"version_normalized": "3.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/igorw/evenement.git",
"reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
"reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
"shasum": ""
},
"require": {
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
},
"time": "2017-07-23T21:35:13+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Evenement": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch"
}
],
"description": "Événement is a very simple event dispatching library for PHP",
"keywords": [
"event-dispatcher",
"event-emitter"
],
"support": {
"issues": "https://github.com/igorw/evenement/issues",
"source": "https://github.com/igorw/evenement/tree/master"
},
"install-path": "../evenement/evenement"
},
{
"name": "fortawesome/font-awesome",
"version": "6.1.1",
"version_normalized": "6.1.1.0",
"source": {
"type": "git",
"url": "https://github.com/FortAwesome/Font-Awesome.git",
"reference": "28e297f07af26f148c15e6cbbd12cea3027371d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/FortAwesome/Font-Awesome/zipball/28e297f07af26f148c15e6cbbd12cea3027371d3",
"reference": "28e297f07af26f148c15e6cbbd12cea3027371d3",
"shasum": ""
},
"time": "2022-03-22T15:24:29+00:00",
"type": "library",
"installation-source": "dist",
"notification-url": "https://packagist.org/downloads/",
"license": [
"CC-BY-4.0",
"OFL-1.1",
"MIT"
],
"authors": [
{
"name": "The Font Awesome Team",
"homepage": "https://github.com/orgs/FortAwesome/people"
}
],
"description": "The iconic font, CSS, and SVG framework",
"homepage": "https://fontawesome.com",
"keywords": [
"FontAwesome",
"awesome",
"bootstrap",
"font",
"icon",
"svg"
],
"support": {
"docs": "http://fontawesome.com/docs",
"email": "hello@fontawesome.com",
"issues": "https://github.com/FortAwesome/Font-Awesome/issues",
"source": "https://github.com/FortAwesome/Font-Awesome"
},
"install-path": "../fortawesome/font-awesome"
},
{
"name": "ipl/html",
"version": "v0.6.0",
"version_normalized": "0.6.0.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-html.git",
"reference": "239b215ab81205f69d8df2663b0fecb138562547"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-html/zipball/239b215ab81205f69d8df2663b0fecb138562547",
"reference": "239b215ab81205f69d8df2663b0fecb138562547",
"shasum": ""
},
"require": {
"ipl/stdlib": ">=0.12.0",
"ipl/validator": ">=0.4.0",
"php": ">=7.2",
"psr/http-message": "~1.0"
},
"time": "2022-06-15T08:17:00+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"ipl\\Html\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - HTML abstraction layer",
"homepage": "https://github.com/Icinga/ipl-html",
"keywords": [
"html"
],
"support": {
"issues": "https://github.com/Icinga/ipl-html/issues",
"source": "https://github.com/Icinga/ipl-html/tree/v0.6.0"
},
"install-path": "../ipl/html"
},
{
"name": "ipl/i18n",
"version": "v0.2.0",
"version_normalized": "0.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-i18n.git",
"reference": "3ee2a8c0c38879cb743c866d9202f8620d4c2800"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-i18n/zipball/3ee2a8c0c38879cb743c866d9202f8620d4c2800",
"reference": "3ee2a8c0c38879cb743c866d9202f8620d4c2800",
"shasum": ""
},
"require": {
"ext-gettext": "*",
"ext-intl": "*",
"ipl/stdlib": ">=0.12.0",
"php": ">=7.2"
},
"time": "2022-06-15T07:33:40+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"ipl\\I18n\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - Internationalization",
"homepage": "https://github.com/Icinga/ipl-i18n",
"keywords": [
"gettext",
"i18n",
"internationalization",
"localization",
"translation"
],
"support": {
"issues": "https://github.com/Icinga/ipl-i18n/issues",
"source": "https://github.com/Icinga/ipl-i18n/tree/v0.2.0"
},
"install-path": "../ipl/i18n"
},
{
"name": "ipl/orm",
"version": "v0.4.1",
"version_normalized": "0.4.1.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-orm.git",
"reference": "0b76de078b9ebff608ce07b1ea051fa7d82f6261"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-orm/zipball/0b76de078b9ebff608ce07b1ea051fa7d82f6261",
"reference": "0b76de078b9ebff608ce07b1ea051fa7d82f6261",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"ipl/sql": ">=0.5.0",
"ipl/stdlib": ">=0.12.0",
"php": ">=7.2"
},
"require-dev": {
"ext-pdo_sqlite": "*"
},
"time": "2022-07-01T16:15:30+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"ipl\\Orm\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - ORM",
"homepage": "https://github.com/Icinga/ipl-orm",
"keywords": [
"database",
"orm",
"sql"
],
"support": {
"issues": "https://github.com/Icinga/ipl-orm/issues",
"source": "https://github.com/Icinga/ipl-orm/tree/v0.4.1"
},
"install-path": "../ipl/orm"
},
{
"name": "ipl/sql",
"version": "v0.5.0",
"version_normalized": "0.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-sql.git",
"reference": "cbe5d0854ef0612c7108b84b0864b4e69e81afd1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-sql/zipball/cbe5d0854ef0612c7108b84b0864b4e69e81afd1",
"reference": "cbe5d0854ef0612c7108b84b0864b4e69e81afd1",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"ipl/stdlib": ">=0.12.0",
"php": ">=7.2"
},
"time": "2022-06-15T08:43:21+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"ipl\\Sql\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - SQL abstraction layer",
"homepage": "https://github.com/Icinga/ipl-sql",
"keywords": [
"database",
"sql"
],
"support": {
"issues": "https://github.com/Icinga/ipl-sql/issues",
"source": "https://github.com/Icinga/ipl-sql/tree/v0.5.0"
},
"install-path": "../ipl/sql"
},
{
"name": "ipl/stdlib",
"version": "v0.12.0",
"version_normalized": "0.12.0.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-stdlib.git",
"reference": "d42a16122975a629ab7d60eff780b9fd9949545e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-stdlib/zipball/d42a16122975a629ab7d60eff780b9fd9949545e",
"reference": "d42a16122975a629ab7d60eff780b9fd9949545e",
"shasum": ""
},
"require": {
"evenement/evenement": "^3",
"ext-openssl": "*",
"php": ">=7.2"
},
"time": "2022-06-15T07:29:19+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"ipl\\Stdlib\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "ipl Standard Library",
"support": {
"issues": "https://github.com/Icinga/ipl-stdlib/issues",
"source": "https://github.com/Icinga/ipl-stdlib/tree/v0.12.0"
},
"install-path": "../ipl/stdlib"
},
{
"name": "ipl/validator",
"version": "v0.4.0",
"version_normalized": "0.4.0.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-validator.git",
"reference": "b8af9ef02654e04b63c6f28507a80270f5248ecb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-validator/zipball/b8af9ef02654e04b63c6f28507a80270f5248ecb",
"reference": "b8af9ef02654e04b63c6f28507a80270f5248ecb",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ipl/i18n": ">=0.2.0",
"ipl/stdlib": ">=0.12.0",
"php": ">=7.2"
},
"time": "2022-06-15T07:51:54+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"ipl\\Validator\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - Common validators and validator chaining",
"homepage": "https://github.com/Icinga/ipl-validator",
"support": {
"issues": "https://github.com/Icinga/ipl-validator/issues",
"source": "https://github.com/Icinga/ipl-validator/tree/v0.4.0"
},
"install-path": "../ipl/validator"
},
{
"name": "ipl/web",
"version": "v0.5.0",
"version_normalized": "0.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/Icinga/ipl-web.git",
"reference": "9c3423a504ebea823309452e8414f5b0b1746bdd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Icinga/ipl-web/zipball/9c3423a504ebea823309452e8414f5b0b1746bdd",
"reference": "9c3423a504ebea823309452e8414f5b0b1746bdd",
"shasum": ""
},
"require": {
"ext-json": "*",
"fortawesome/font-awesome": "^6",
"ipl/html": ">=0.6.0",
"ipl/stdlib": ">=0.12.0",
"php": ">=7.2"
},
"time": "2022-06-15T12:25:42+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"ipl\\Web\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Icinga PHP Library - Web Components",
"homepage": "https://github.com/Icinga/ipl-web",
"keywords": [
"html"
],
"support": {
"issues": "https://github.com/Icinga/ipl-web/issues",
"source": "https://github.com/Icinga/ipl-web/tree/v0.5.0"
},
"install-path": "../ipl/web"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2016-08-06T14:39:51+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/master"
},
"install-path": "../psr/http-message"
}
],
"dev": true,
"dev-package-names": []
}

113
vendor/composer/installed.php vendored Normal file
View File

@ -0,0 +1,113 @@
<?php return array(
'root' => array(
'name' => 'icinga/icinga-php-library',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => '5f5afe15404fe4f9cf896507e5dddcc92415e1bd',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
'evenement/evenement' => array(
'pretty_version' => 'v3.0.1',
'version' => '3.0.1.0',
'reference' => '531bfb9d15f8aa57454f5f0285b18bec903b8fb7',
'type' => 'library',
'install_path' => __DIR__ . '/../evenement/evenement',
'aliases' => array(),
'dev_requirement' => false,
),
'fortawesome/font-awesome' => array(
'pretty_version' => '6.1.1',
'version' => '6.1.1.0',
'reference' => '28e297f07af26f148c15e6cbbd12cea3027371d3',
'type' => 'library',
'install_path' => __DIR__ . '/../fortawesome/font-awesome',
'aliases' => array(),
'dev_requirement' => false,
),
'icinga/icinga-php-library' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => '5f5afe15404fe4f9cf896507e5dddcc92415e1bd',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'ipl/html' => array(
'pretty_version' => 'v0.6.0',
'version' => '0.6.0.0',
'reference' => '239b215ab81205f69d8df2663b0fecb138562547',
'type' => 'library',
'install_path' => __DIR__ . '/../ipl/html',
'aliases' => array(),
'dev_requirement' => false,
),
'ipl/i18n' => array(
'pretty_version' => 'v0.2.0',
'version' => '0.2.0.0',
'reference' => '3ee2a8c0c38879cb743c866d9202f8620d4c2800',
'type' => 'library',
'install_path' => __DIR__ . '/../ipl/i18n',
'aliases' => array(),
'dev_requirement' => false,
),
'ipl/orm' => array(
'pretty_version' => 'v0.4.1',
'version' => '0.4.1.0',
'reference' => '0b76de078b9ebff608ce07b1ea051fa7d82f6261',
'type' => 'library',
'install_path' => __DIR__ . '/../ipl/orm',
'aliases' => array(),
'dev_requirement' => false,
),
'ipl/sql' => array(
'pretty_version' => 'v0.5.0',
'version' => '0.5.0.0',
'reference' => 'cbe5d0854ef0612c7108b84b0864b4e69e81afd1',
'type' => 'library',
'install_path' => __DIR__ . '/../ipl/sql',
'aliases' => array(),
'dev_requirement' => false,
),
'ipl/stdlib' => array(
'pretty_version' => 'v0.12.0',
'version' => '0.12.0.0',
'reference' => 'd42a16122975a629ab7d60eff780b9fd9949545e',
'type' => 'library',
'install_path' => __DIR__ . '/../ipl/stdlib',
'aliases' => array(),
'dev_requirement' => false,
),
'ipl/validator' => array(
'pretty_version' => 'v0.4.0',
'version' => '0.4.0.0',
'reference' => 'b8af9ef02654e04b63c6f28507a80270f5248ecb',
'type' => 'library',
'install_path' => __DIR__ . '/../ipl/validator',
'aliases' => array(),
'dev_requirement' => false,
),
'ipl/web' => array(
'pretty_version' => 'v0.5.0',
'version' => '0.5.0.0',
'reference' => '9c3423a504ebea823309452e8414f5b0b1746bdd',
'type' => 'library',
'install_path' => __DIR__ . '/../ipl/web',
'aliases' => array(),
'dev_requirement' => false,
),
'psr/http-message' => array(
'pretty_version' => '1.0.1',
'version' => '1.0.1.0',
'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(),
'dev_requirement' => false,
),
),
);

26
vendor/composer/platform_check.php vendored Normal file
View File

@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 70200)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

19
vendor/evenement/evenement/LICENSE vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2011 Igor Wiedler
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,29 @@
{
"name": "evenement/evenement",
"description": "Événement is a very simple event dispatching library for PHP",
"keywords": ["event-dispatcher", "event-emitter"],
"license": "MIT",
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch"
}
],
"require": {
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
},
"autoload": {
"psr-0": {
"Evenement": "src"
}
},
"autoload-dev": {
"psr-0": {
"Evenement": "tests"
},
"files": ["tests/Evenement/Tests/functions.php"]
}
}

View File

@ -0,0 +1,17 @@
<?php declare(strict_types=1);
/*
* This file is part of Evenement.
*
* (c) Igor Wiedler <igor@wiedler.ch>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Evenement;
class EventEmitter implements EventEmitterInterface
{
use EventEmitterTrait;
}

View File

@ -0,0 +1,22 @@
<?php declare(strict_types=1);
/*
* This file is part of Evenement.
*
* (c) Igor Wiedler <igor@wiedler.ch>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Evenement;
interface EventEmitterInterface
{
public function on($event, callable $listener);
public function once($event, callable $listener);
public function removeListener($event, callable $listener);
public function removeAllListeners($event = null);
public function listeners($event = null);
public function emit($event, array $arguments = []);
}

View File

@ -0,0 +1,135 @@
<?php declare(strict_types=1);
/*
* This file is part of Evenement.
*
* (c) Igor Wiedler <igor@wiedler.ch>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Evenement;
use InvalidArgumentException;
trait EventEmitterTrait
{
protected $listeners = [];
protected $onceListeners = [];
public function on($event, callable $listener)
{
if ($event === null) {
throw new InvalidArgumentException('event name must not be null');
}
if (!isset($this->listeners[$event])) {
$this->listeners[$event] = [];
}
$this->listeners[$event][] = $listener;
return $this;
}
public function once($event, callable $listener)
{
if ($event === null) {
throw new InvalidArgumentException('event name must not be null');
}
if (!isset($this->onceListeners[$event])) {
$this->onceListeners[$event] = [];
}
$this->onceListeners[$event][] = $listener;
return $this;
}
public function removeListener($event, callable $listener)
{
if ($event === null) {
throw new InvalidArgumentException('event name must not be null');
}
if (isset($this->listeners[$event])) {
$index = \array_search($listener, $this->listeners[$event], true);
if (false !== $index) {
unset($this->listeners[$event][$index]);
if (\count($this->listeners[$event]) === 0) {
unset($this->listeners[$event]);
}
}
}
if (isset($this->onceListeners[$event])) {
$index = \array_search($listener, $this->onceListeners[$event], true);
if (false !== $index) {
unset($this->onceListeners[$event][$index]);
if (\count($this->onceListeners[$event]) === 0) {
unset($this->onceListeners[$event]);
}
}
}
}
public function removeAllListeners($event = null)
{
if ($event !== null) {
unset($this->listeners[$event]);
} else {
$this->listeners = [];
}
if ($event !== null) {
unset($this->onceListeners[$event]);
} else {
$this->onceListeners = [];
}
}
public function listeners($event = null): array
{
if ($event === null) {
$events = [];
$eventNames = \array_unique(
\array_merge(\array_keys($this->listeners), \array_keys($this->onceListeners))
);
foreach ($eventNames as $eventName) {
$events[$eventName] = \array_merge(
isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : [],
isset($this->onceListeners[$eventName]) ? $this->onceListeners[$eventName] : []
);
}
return $events;
}
return \array_merge(
isset($this->listeners[$event]) ? $this->listeners[$event] : [],
isset($this->onceListeners[$event]) ? $this->onceListeners[$event] : []
);
}
public function emit($event, array $arguments = [])
{
if ($event === null) {
throw new InvalidArgumentException('event name must not be null');
}
if (isset($this->listeners[$event])) {
foreach ($this->listeners[$event] as $listener) {
$listener(...$arguments);
}
}
if (isset($this->onceListeners[$event])) {
$listeners = $this->onceListeners[$event];
unset($this->onceListeners[$event]);
foreach ($listeners as $listener) {
$listener(...$arguments);
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"name": "fortawesome/font-awesome",
"description": "The iconic font, CSS, and SVG framework",
"keywords": ["font", "awesome", "fontawesome", "icon", "svg", "font", "bootstrap"],
"homepage": "https://fontawesome.com",
"authors": [
{
"name": "The Font Awesome Team",
"homepage": "https://github.com/orgs/FortAwesome/people"
}
],
"support": {
"email": "hello@fontawesome.com",
"issues": "https://github.com/FortAwesome/Font-Awesome/issues",
"source": "https://github.com/FortAwesome/Font-Awesome",
"docs": "http://fontawesome.com/docs"
},
"license": [
"CC-BY-4.0",
"OFL-1.1",
"MIT"
]
}

View File

@ -0,0 +1,31 @@
{
"description": "The iconic font, CSS, and SVG framework",
"keywords": [
"font",
"awesome",
"fontawesome",
"icon",
"svg",
"bootstrap"
],
"homepage": "https://fontawesome.com",
"bugs": {
"url": "https://github.com/FortAwesome/Font-Awesome/issues"
},
"author": "The Font Awesome Team (https://github.com/orgs/FortAwesome/people)",
"repository": {
"type": "git",
"url": "https://github.com/FortAwesome/Font-Awesome"
},
"engines": {
"node": ">=6"
},
"dependencies": {},
"version": "6.1.1",
"name": "@fortawesome/fontawesome-common-types",
"license": "MIT",
"types": "./index.d.ts",
"scripts": {
"postinstall": "node attribution.js"
}
}

View File

@ -0,0 +1,32 @@
{
"description": "The iconic font, CSS, and SVG framework",
"keywords": [
"font",
"awesome",
"fontawesome",
"icon",
"svg",
"bootstrap"
],
"homepage": "https://fontawesome.com",
"bugs": {
"url": "https://github.com/FortAwesome/Font-Awesome/issues"
},
"author": "The Font Awesome Team (https://github.com/orgs/FortAwesome/people)",
"repository": {
"type": "git",
"url": "https://github.com/FortAwesome/Font-Awesome"
},
"engines": {
"node": ">=6"
},
"dependencies": {},
"version": "6.1.1",
"name": "@fortawesome/fontawesome-free",
"main": "js/fontawesome.js",
"style": "css/fontawesome.css",
"license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)",
"scripts": {
"postinstall": "node attribution.js"
}
}

View File

@ -0,0 +1,73 @@
{
"description": "The iconic font, CSS, and SVG framework",
"keywords": [
"font",
"awesome",
"fontawesome",
"icon",
"svg",
"bootstrap"
],
"homepage": "https://fontawesome.com",
"bugs": {
"url": "https://github.com/FortAwesome/Font-Awesome/issues"
},
"author": "The Font Awesome Team (https://github.com/orgs/FortAwesome/people)",
"repository": {
"type": "git",
"url": "https://github.com/FortAwesome/Font-Awesome"
},
"engines": {
"node": ">=6"
},
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.1.1"
},
"version": "6.1.1",
"name": "@fortawesome/fontawesome-svg-core",
"main": "index.js",
"module": "index.es.js",
"jsnext:main": "index.es.js",
"style": "styles.css",
"license": "MIT",
"types": "./index.d.ts",
"exports": {
".": {
"module": "./index.es.js",
"import": "./index.es.js",
"require": "./index.js",
"style": "./styles.css",
"default": "./index.js"
},
"./index": {
"module": "./index.es.js",
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./index.js": {
"module": "./index.es.js",
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./plugins": {
"module": "./plugins.es.js",
"import": "./plugins.es.js",
"default": "./plugins.es.js"
},
"./import.macro": "./import.macro.js",
"./import.macro.js": "./import.macro.js",
"./styles": "./styles.css",
"./styles.css": "./styles.css",
"./package.json": "./package.json"
},
"sideEffects": [
"./index.js",
"./index.es.js",
"./styles.css"
],
"scripts": {
"postinstall": "node attribution.js"
}
}

View File

@ -0,0 +1,55 @@
{
"description": "The iconic font, CSS, and SVG framework",
"keywords": [
"font",
"awesome",
"fontawesome",
"icon",
"svg",
"bootstrap"
],
"homepage": "https://fontawesome.com",
"bugs": {
"url": "https://github.com/FortAwesome/Font-Awesome/issues"
},
"author": "The Font Awesome Team (https://github.com/orgs/FortAwesome/people)",
"repository": {
"type": "git",
"url": "https://github.com/FortAwesome/Font-Awesome"
},
"engines": {
"node": ">=6"
},
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.1.1"
},
"version": "6.1.1",
"name": "@fortawesome/free-brands-svg-icons",
"main": "index.js",
"module": "index.es.js",
"jsnext:main": "index.es.js",
"license": "(CC-BY-4.0 AND MIT)",
"types": "./index.d.ts",
"sideEffects": false,
"exports": {
".": {
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./index": {
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./index.js": {
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./*": "./*.js"
},
"scripts": {
"postinstall": "node attribution.js"
}
}

View File

@ -0,0 +1,55 @@
{
"description": "The iconic font, CSS, and SVG framework",
"keywords": [
"font",
"awesome",
"fontawesome",
"icon",
"svg",
"bootstrap"
],
"homepage": "https://fontawesome.com",
"bugs": {
"url": "https://github.com/FortAwesome/Font-Awesome/issues"
},
"author": "The Font Awesome Team (https://github.com/orgs/FortAwesome/people)",
"repository": {
"type": "git",
"url": "https://github.com/FortAwesome/Font-Awesome"
},
"engines": {
"node": ">=6"
},
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.1.1"
},
"version": "6.1.1",
"name": "@fortawesome/free-regular-svg-icons",
"main": "index.js",
"module": "index.es.js",
"jsnext:main": "index.es.js",
"license": "(CC-BY-4.0 AND MIT)",
"types": "./index.d.ts",
"sideEffects": false,
"exports": {
".": {
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./index": {
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./index.js": {
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./*": "./*.js"
},
"scripts": {
"postinstall": "node attribution.js"
}
}

View File

@ -0,0 +1,55 @@
{
"description": "The iconic font, CSS, and SVG framework",
"keywords": [
"font",
"awesome",
"fontawesome",
"icon",
"svg",
"bootstrap"
],
"homepage": "https://fontawesome.com",
"bugs": {
"url": "https://github.com/FortAwesome/Font-Awesome/issues"
},
"author": "The Font Awesome Team (https://github.com/orgs/FortAwesome/people)",
"repository": {
"type": "git",
"url": "https://github.com/FortAwesome/Font-Awesome"
},
"engines": {
"node": ">=6"
},
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.1.1"
},
"version": "6.1.1",
"name": "@fortawesome/free-solid-svg-icons",
"main": "index.js",
"module": "index.es.js",
"jsnext:main": "index.es.js",
"license": "(CC-BY-4.0 AND MIT)",
"types": "./index.d.ts",
"sideEffects": false,
"exports": {
".": {
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./index": {
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./index.js": {
"import": "./index.es.js",
"require": "./index.js",
"default": "./index.js"
},
"./*": "./*.js"
},
"scripts": {
"postinstall": "node attribution.js"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

21
vendor/ipl/html/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2018 Icinga GmbH https://www.icinga.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

27
vendor/ipl/html/composer.json vendored Normal file
View File

@ -0,0 +1,27 @@
{
"name": "ipl/html",
"type": "library",
"description": "Icinga PHP Library - HTML abstraction layer",
"license": "MIT",
"keywords": ["html"],
"homepage": "https://github.com/Icinga/ipl-html",
"config": {
"sort-packages": true
},
"require": {
"php": ">=7.2",
"ipl/stdlib": ">=0.12.0",
"ipl/validator": ">=0.4.0",
"psr/http-message": "~1.0"
},
"autoload": {
"psr-4": {
"ipl\\Html\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"ipl\\Tests\\Html\\": "tests"
}
}
}

301
vendor/ipl/html/src/Attribute.php vendored Normal file
View File

@ -0,0 +1,301 @@
<?php
namespace ipl\Html;
use InvalidArgumentException;
/**
* HTML Attribute
*
* Every single HTML attribute is (or should be) an instance of this class.
* This guarantees that every attribute is safe and escaped correctly.
*
* Usually attributes are not instantiated directly, but created through an HTML
* element's exposed methods.
*/
class Attribute
{
/** @var string */
protected $name;
/** @var string|array|bool|null */
protected $value;
/**
* Create a new HTML attribute from the given name and value
*
* @param string $name The name of the attribute
* @param string|bool|array|null $value The value of the attribute
*
* @throws InvalidArgumentException If the name of the attribute contains special characters
*/
public function __construct($name, $value = null)
{
$this->setName($name)->setValue($value);
}
/**
* Create a new HTML attribute from the given name and value
*
* @param string $name The name of the attribute
* @param string|bool|array|null $value The value of the attribute
*
* @return static
*
* @throws InvalidArgumentException If the name of the attribute contains special characters
*/
public static function create($name, $value)
{
return new static($name, $value);
}
/**
* Create a new empty HTML attribute from the given name
*
* The value of the attribute will be null after construction.
*
* @param string $name The name of the attribute
*
* @return static
*
* @throws InvalidArgumentException If the name of the attribute contains special characters
*/
public static function createEmpty($name)
{
return new static($name, null);
}
/**
* Escape the name of an attribute
*
* Makes sure that the name of an attribute really is a string.
*
* @param string $name
*
* @return string
*/
public static function escapeName($name)
{
return (string) $name;
}
/**
* Escape the value of an attribute
*
* If the value is an array, returns the string representation
* of all array elements joined with the specified glue string.
*
* Values are escaped according to the HTML5 double-quoted attribute value syntax:
* {@link https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 }.
*
* @param string|array $value
* @param string $glue Glue string to join elements if value is an array
*
* @return string
*/
public static function escapeValue($value, $glue = ' ')
{
if (is_array($value)) {
$value = implode($glue, $value);
}
// We force double-quoted attribute value syntax so let's start by escaping double quotes
$value = str_replace('"', '&quot;', $value);
// In addition, values must not contain ambiguous ampersands
$value = preg_replace_callback(
'/&[0-9A-Z]+;/i',
function ($match) {
$subject = $match[0];
if (htmlspecialchars_decode($subject, ENT_COMPAT | ENT_HTML5) === $subject) {
// Ambiguous ampersand
return str_replace('&', '&amp;', $subject);
}
return $subject;
},
$value
);
return $value;
}
/**
* Get the name of the attribute
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set the name of the attribute
*
* @param string $name
*
* @return $this
*
* @throws InvalidArgumentException If the name contains special characters
*/
protected function setName($name)
{
if (! preg_match('/^[a-z][a-z0-9:-]*$/i', $name)) {
throw new InvalidArgumentException(sprintf(
'Attribute names with special characters are not yet allowed: %s',
$name
));
}
$this->name = $name;
return $this;
}
/**
* Get the value of the attribute
*
* @return string|bool|array|null
*/
public function getValue()
{
return $this->value;
}
/**
* Set the value of the attribute
*
* @param string|bool|array|null $value
*
* @return $this
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* Add the given value(s) to the attribute
*
* @param string|array $value The value(s) to add
*
* @return $this
*/
public function addValue($value)
{
$this->value = array_merge((array) $this->value, (array) $value);
return $this;
}
/**
* Remove the given value(s) from the attribute
*
* The current value is set to null if it matches the value to remove
* or is in the array of values to remove.
*
* If the current value is an array, all elements are removed which
* match the value(s) to remove.
*
* Does nothing if there is no such value to remove.
*
* @param string|array $value The value(s) to remove
*
* @return $this
*/
public function removeValue($value)
{
$value = (array) $value;
$current = $this->getValue();
if (is_array($current)) {
$this->setValue(array_diff($current, $value));
} elseif (in_array($current, $value, true)) {
$this->setValue(null);
}
return $this;
}
/**
* Test and return true if the attribute is boolean, false otherwise
*
* @return bool
*/
public function isBoolean()
{
return is_bool($this->value);
}
/**
* Test and return true if the attribute is empty, false otherwise
*
* Null and the empty array will be considered empty.
*
* @return bool
*/
public function isEmpty()
{
return $this->value === null || $this->value === [];
}
/**
* Render the attribute to HTML
*
* If the value of the attribute is of type boolean, it will be rendered as
* {@link http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes boolean attribute}.
* Note that in this case if the value of the attribute is false, the empty string will be returned.
*
* If the value of the attribute is null or an empty array,
* the empty string will be returned as well.
*
* Escaping of the attribute's value takes place automatically using {@link Attribute::escapeValue()}.
*
* @return string
*/
public function render()
{
if ($this->isEmpty()) {
return '';
}
if ($this->isBoolean()) {
if ($this->value) {
return $this->renderName();
}
return '';
} else {
return sprintf(
'%s="%s"',
$this->renderName(),
$this->renderValue()
);
}
}
/**
* Render the name of the attribute to HTML
*
* @return string
*/
public function renderName()
{
return static::escapeName($this->name);
}
/**
* Render the value of the attribute to HTML
*
* @return string
*/
public function renderValue()
{
return static::escapeValue($this->value);
}
}

521
vendor/ipl/html/src/Attributes.php vendored Normal file
View File

@ -0,0 +1,521 @@
<?php
namespace ipl\Html;
use ArrayAccess;
use ArrayIterator;
use InvalidArgumentException;
use IteratorAggregate;
use Traversable;
use function ipl\Stdlib\get_php_type;
/**
* HTML attributes
*
* HTML attributes provide additional information about HTML elements, that configure the elements or adjust their
* behavior in various ways.
*
* Attributes usually come in name-value pairs and are rendered as name="value".
*/
class Attributes implements ArrayAccess, IteratorAggregate
{
/** @var Attribute[] */
protected $attributes = [];
/** @var callable[] */
protected $callbacks = [];
/** @var string */
protected $prefix = '';
/** @var callable[] */
protected $setterCallbacks = [];
/**
* Create new HTML attributes
*
* @param array $attributes
*/
public function __construct(array $attributes = null)
{
if (empty($attributes)) {
return;
}
foreach ($attributes as $key => $value) {
if ($value instanceof Attribute) {
$this->addAttribute($value);
} elseif (is_string($key)) {
$this->add($key, $value);
} elseif (is_array($value) && count($value) === 2) {
$this->add(array_shift($value), array_shift($value));
}
}
}
/**
* Create new HTML attributes
*
* @param array $attributes
*
* @return static
*/
public static function create(array $attributes = null)
{
return new static($attributes);
}
/**
* Ensure that the given attributes of mixed type are converted to an instance of attributes
*
* The conversion procedure is as follows:
*
* If the given attributes is already an instance of Attributes, returns the very same element.
* If the attributes are given as an array of attribute name-value pairs, they are used to
* construct and return a new Attributes instance.
* If the attributes are null, an empty new instance of Attributes is returned.
*
* @param array|static|null $attributes
*
* @return static
*
* @throws InvalidArgumentException In case the given attributes are of an unsupported type
*/
public static function wantAttributes($attributes)
{
if ($attributes instanceof self) {
return $attributes;
}
if (is_array($attributes)) {
return new static($attributes);
}
if ($attributes === null) {
return new static();
}
throw new InvalidArgumentException(sprintf(
'Attributes instance, array or null expected. Got %s instead.',
get_php_type($attributes)
));
}
/**
* Get the collection of attributes as array
*
* @return Attribute[]
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* Merge the given attributes
*
* @param Attributes $attributes
*
* @return $this
*/
public function merge(Attributes $attributes)
{
foreach ($attributes as $attribute) {
$this->addAttribute($attribute);
}
foreach ($attributes->callbacks as $name => $getter) {
$setter = null;
if (isset($attributes->setterCallbacks[$name])) {
$setter = $attributes->setterCallbacks[$name];
}
$this->registerAttributeCallback($name, $getter, $setter);
}
return $this;
}
/**
* Return true if the attribute with the given name exists, false otherwise
*
* @param string $name
*
* @return bool
*/
public function has($name)
{
return array_key_exists($name, $this->attributes);
}
/**
* Get the attribute with the given name
*
* If the attribute does not yet exist, it is automatically created and registered to this Attributes instance.
*
* @param string $name
*
* @return Attribute
*
* @throws InvalidArgumentException If the attribute does not yet exist and its name contains special characters
*/
public function get($name)
{
if (! $this->has($name)) {
$this->attributes[$name] = Attribute::createEmpty($name);
}
return $this->attributes[$name];
}
/**
* Set the given attribute(s)
*
* If the attribute with the given name already exists, it gets overridden.
*
* @param string|array|Attribute|self $attribute The attribute(s) to add
* @param string|bool|array $value The value of the attribute
*
* @return $this
*
* @throws InvalidArgumentException If the attribute name contains special characters
*/
public function set($attribute, $value = null)
{
if ($attribute instanceof self) {
foreach ($attribute as $a) {
$this->setAttribute($a);
}
return $this;
}
if ($attribute instanceof Attribute) {
$this->setAttribute($attribute);
return $this;
}
if (is_array($attribute)) {
foreach ($attribute as $name => $value) {
$this->set($name, $value);
}
return $this;
}
if (array_key_exists($attribute, $this->setterCallbacks)) {
$callback = $this->setterCallbacks[$attribute];
$callback($value);
return $this;
}
$this->attributes[$attribute] = Attribute::create($attribute, $value);
return $this;
}
/**
* Add the given attribute(s)
*
* If an attribute with the same name already exists, the attribute's value will be added to the current value of
* the attribute.
*
* @param string|array|Attribute|self $attribute The attribute(s) to add
* @param string|bool|array $value The value of the attribute
*
* @return $this
*
* @throws InvalidArgumentException If the attribute does not yet exist and its name contains special characters
*/
public function add($attribute, $value = null)
{
if ($attribute === null) {
return $this;
}
if ($attribute instanceof self) {
foreach ($attribute as $attr) {
$this->add($attr);
}
return $this;
}
if (is_array($attribute)) {
foreach ($attribute as $name => $value) {
$this->add($name, $value);
}
return $this;
}
if ($attribute instanceof Attribute) {
$this->addAttribute($attribute);
return $this;
}
if (array_key_exists($attribute, $this->setterCallbacks)) {
$callback = $this->setterCallbacks[$attribute];
$callback($value);
return $this;
}
if (! array_key_exists($attribute, $this->attributes)) {
$this->attributes[$attribute] = Attribute::create($attribute, $value);
} else {
$this->attributes[$attribute]->addValue($value);
}
return $this;
}
/**
* Remove the attribute with the given name or remove the given value from the attribute
*
* @param string $name The name of the attribute
* @param null|string|array $value The value to remove if specified
*
* @return Attribute|false
*/
public function remove($name, $value = null)
{
if (! $this->has($name)) {
return false;
}
$attribute = $this->attributes[$name];
if ($value === null) {
unset($this->attributes[$name]);
} else {
$attribute->removeValue($value);
}
return $attribute;
}
/**
* Set the specified attribute
*
* @param Attribute $attribute
*
* @return $this
*/
public function setAttribute(Attribute $attribute)
{
$this->attributes[$attribute->getName()] = $attribute;
return $this;
}
/**
* Add the specified attribute
*
* If an attribute with the same name already exists, the given attribute's value
* will be added to the current value of the attribute.
*
* @param Attribute $attribute
*
* @return $this
*/
public function addAttribute(Attribute $attribute)
{
$name = $attribute->getName();
if ($this->has($name)) {
$this->attributes[$name]->addValue($attribute->getValue());
} else {
$this->attributes[$name] = $attribute;
}
return $this;
}
/**
* Get the attributes name prefix
*
* @return string|null
*/
public function getPrefix()
{
return $this->prefix;
}
/**
* Set the attributes name prefix
*
* @param string $prefix
*
* @return $this
*/
public function setPrefix($prefix)
{
$this->prefix = $prefix;
return $this;
}
/**
* Register callback for an attribute
*
* @param string $name Name of the attribute to register the callback for
* @param callable $callback Callback to call when retrieving the attribute
* @param callable $setterCallback Callback to call when setting the attribute
*
* @return $this
*
* @throws InvalidArgumentException If $callback is not callable or if $setterCallback is set and not callable
*/
public function registerAttributeCallback($name, $callback, $setterCallback = null)
{
if ($callback !== null) {
if (! is_callable($callback)) {
throw new InvalidArgumentException(__METHOD__ . ' expects a callable callback');
}
$this->callbacks[$name] = $callback;
}
if ($setterCallback !== null) {
if (! is_callable($setterCallback)) {
throw new InvalidArgumentException(__METHOD__ . ' expects a callable setterCallback');
}
$this->setterCallbacks[$name] = $setterCallback;
}
return $this;
}
/**
* Render attributes to HTML
*
* If the value of an attribute is of type boolean, it will be rendered as
* {@link http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes boolean attribute}.
*
* If the value of an attribute is null, it will be skipped.
*
* HTML-escaping of the attributes' values takes place automatically using {@link Attribute::escapeValue()}.
*
* @return string
*
* @throws InvalidArgumentException If the result of a callback is invalid
*/
public function render()
{
$attributes = $this->attributes;
foreach ($this->callbacks as $name => $callback) {
$attribute = call_user_func($callback);
if ($attribute instanceof Attribute) {
if ($attribute->isEmpty()) {
continue;
}
} elseif ($attribute === null) {
continue;
} elseif (is_scalar($attribute)) {
$attribute = Attribute::create($name, $attribute);
} else {
throw new InvalidArgumentException(sprintf(
'A registered attribute callback must return a scalar, null'
. ' or an Attribute, got a %s',
get_php_type($attribute)
));
}
$name = $attribute->getName();
if (isset($attributes[$name])) {
$attributes[$name] = clone $attributes[$name];
$attributes[$name]->addValue($attribute->getValue());
} else {
$attributes[$name] = $attribute;
}
}
$parts = [];
foreach ($attributes as $attribute) {
if ($attribute->isEmpty()) {
continue;
}
$parts[] = $attribute->render();
}
if (empty($parts)) {
return '';
}
$separator = ' ' . $this->getPrefix();
return $separator . implode($separator, $parts);
}
/**
* Get whether the attribute with the given name exists
*
* @param string $name Name of the attribute
*
* @return bool
*/
public function offsetExists($name): bool
{
return $this->has($name);
}
/**
* Get the attribute with the given name
*
* If the attribute does not yet exist, it is automatically created and registered to this Attributes instance.
*
* @param string $name Name of the attribute
*
* @return Attribute
*
* @throws InvalidArgumentException If the attribute does not yet exist and its name contains special characters
*/
public function offsetGet($name): Attribute
{
return $this->get($name);
}
/**
* Set the given attribute
*
* If the attribute with the given name already exists, it gets overridden.
*
* @param string $name Name of the attribute
* @param mixed $value Value of the attribute
*
* @throws InvalidArgumentException If the attribute name contains special characters
*/
public function offsetSet($name, $value): void
{
$this->set($name, $value);
}
/**
* Remove the attribute with the given name
*
* @param string $name Name of the attribute
*/
public function offsetUnset($name): void
{
$this->remove($name);
}
/**
* Get an iterator for traversing the attributes
*
* @return Attribute[]|ArrayIterator
*/
public function getIterator(): Traversable
{
return new ArrayIterator($this->attributes);
}
}

355
vendor/ipl/html/src/BaseHtmlElement.php vendored Normal file
View File

@ -0,0 +1,355 @@
<?php
namespace ipl\Html;
use RuntimeException;
/**
* Base class for HTML elements
*
* Extend this class in order to provide concrete HTML elements or series of HTML elements, e.g. widgets.
* When extending this class you should provide the element's tag with {@link $tag}. Setting default attributes is
* possible via {@link $defaultAttributes}. And the content of the element is provided in {@link assemble()}.
*
* # Example Usage
* ```
* namespace Acme\Widgets;
*
* use ipl\Html\BaseHtmlElement;
*
* class Dashboard extends BaseHtmlElement
* {
* protected $defaultAttributes = ['class' => 'acme-dashboard'];
*
* protected $tag = 'div';
*
* protected function assemble()
* {
* // ...
* $this->add($content);
* }
* }
* ```
*/
abstract class BaseHtmlElement extends HtmlDocument
{
/**
* List of void elements which must not contain end tags or content
*
* If {@link $isVoid} is null, this property should be used to decide whether the content and end tag has to be
* rendered.
*
* @var array
*
* @see https://www.w3.org/TR/html5/syntax.html#void-elements
*/
protected static $voidElements = [
'area' => 1,
'base' => 1,
'br' => 1,
'col' => 1,
'embed' => 1,
'hr' => 1,
'img' => 1,
'input' => 1,
'link' => 1,
'meta' => 1,
'param' => 1,
'source' => 1,
'track' => 1,
'wbr' => 1
];
/** @var Attributes */
protected $attributes;
/** @var bool Whether possible attribute callbacks have been registered */
protected $attributeCallbacksRegistered = false;
/** @var bool|null Whether the element is void. If null, void check should use {@link $voidElements} */
protected $isVoid;
/** @var array You may want to set default attributes when extending this class */
protected $defaultAttributes;
/** @var string Tag of element. Set this property in order to provide the element's tag when extending this class */
protected $tag;
/**
* Get the attributes of the element
*
* @return Attributes
*/
public function getAttributes()
{
if ($this->attributes === null) {
$default = $this->getDefaultAttributes();
if (empty($default)) {
$this->attributes = new Attributes();
} else {
$this->attributes = Attributes::wantAttributes($default);
}
$this->ensureAttributeCallbacksRegistered();
}
return $this->attributes;
}
/**
* Set the attributes of the element
*
* @param Attributes|array|null $attributes
*
* @return $this
*/
public function setAttributes($attributes)
{
$this->attributes = Attributes::wantAttributes($attributes);
$this->attributeCallbacksRegistered = false;
$this->ensureAttributeCallbacksRegistered();
return $this;
}
/**
* Set the attribute with the given name and value
*
* If the attribute with the given name already exists, it gets overridden.
*
* @param string $name The name of the attribute
* @param string|bool|array $value The value of the attribute
*
* @return $this
*/
public function setAttribute($name, $value)
{
$this->getAttributes()->set($name, $value);
return $this;
}
/**
* Add the given attributes
*
* @param Attributes|array $attributes
*
* @return $this
*/
public function addAttributes($attributes)
{
$this->getAttributes()->add($attributes);
return $this;
}
/**
* Get the default attributes of the element
*
* @return array
*/
public function getDefaultAttributes()
{
return $this->defaultAttributes;
}
/**
* Get the tag of the element
*
* Since HTML Elements must have a tag, this method throws an exception if the element does not have a tag.
*
* @return string
*
* @throws RuntimeException If the element does not have a tag
*/
final public function getTag()
{
$tag = $this->tag();
if (! $tag) {
throw new RuntimeException('Element must have a tag');
}
return $tag;
}
/**
* Set the tag of the element
*
* @param string $tag
*
* @return $this
*/
public function setTag($tag)
{
$this->tag = $tag;
return $this;
}
/**
* Get whether the element is void
*
* The default void detection which checks whether the element's tag is in the list of void elements according to
* https://www.w3.org/TR/html5/syntax.html#void-elements.
*
* If you want to override this behavior, use {@link setVoid()}.
*
* @return bool
*/
public function isVoid()
{
if ($this->isVoid !== null) {
return $this->isVoid;
}
$tag = $this->getTag();
return isset(self::$voidElements[$tag]);
}
/**
* Set whether the element is void
*
* You may use this method to override the default void detection which checks whether the element's tag is in the
* list of void elements according to https://www.w3.org/TR/html5/syntax.html#void-elements.
*
* If you specify null, void detection is reset to its default behavior.
*
* @param bool|null $void
*
* @return $this
*/
public function setVoid($void = true)
{
$this->isVoid = $void === null ?: (bool) $void;
return $this;
}
/**
* Ensure that possible attribute callbacks have been registered
*
* Note that this method is automatically called in {@link getAttributes()} and {@link setAttributes()}.
*
* @return $this
*/
public function ensureAttributeCallbacksRegistered()
{
if (! $this->attributeCallbacksRegistered) {
$this->attributeCallbacksRegistered = true;
$this->registerAttributeCallbacks($this->attributes);
}
return $this;
}
/**
* Render the content of the element to HTML
*
* @return string
*/
public function renderContent()
{
return parent::renderUnwrapped();
}
/**
* Get whether the closing tag should be rendered
*
* @return bool True for void elements, false otherwise
*/
public function wantsClosingTag()
{
// TODO: There is more. SVG and MathML namespaces
return ! $this->isVoid();
}
/**
* Use this element to wrap the given document
*
* @param HtmlDocument $document
*
* @return $this
*/
public function wrap(HtmlDocument $document)
{
$document->addWrapper($this);
return $this;
}
/**
* Internal method for accessing the tag
*
* You may override this method in order to provide the tag dynamically
*
* @return string
*/
protected function tag()
{
return $this->tag;
}
/**
* Register attribute callbacks
*
* Override this method in order to register attribute callbacks in concrete classes.
*/
protected function registerAttributeCallbacks(Attributes $attributes)
{
}
public function addHtml(ValidHtml ...$content)
{
$this->ensureAssembled();
parent::addHtml(...$content);
return $this;
}
/**
* @inheritdoc
*
* @throws RuntimeException If the element does not have a tag or is void but has content
*/
public function renderUnwrapped()
{
$this->ensureAssembled();
$tag = $this->getTag();
$content = $this->renderContent();
$attributes = $this->getAttributes()->render();
if (strlen($this->contentSeparator)) {
$length = strlen($content);
if ($length > 0) {
if ($content[0] === '<') {
$content = $this->contentSeparator . $content;
$length++;
}
if ($content[$length - 1] === '>') {
$content .= $this->contentSeparator;
}
}
}
if (! $this->wantsClosingTag()) {
if (strlen($content)) {
throw new RuntimeException('Void elements must not have content');
}
return sprintf('<%s%s />', $tag, $attributes);
}
return sprintf(
'<%s%s>%s</%s>',
$tag,
$attributes,
$content,
$tag
);
}
}

View File

@ -0,0 +1,125 @@
<?php
namespace ipl\Html\Contract;
use ipl\Html\Attributes;
use ipl\Html\Form;
/**
* Representation of form elements
*/
interface FormElement extends Wrappable
{
/**
* Get the attributes or options of the element
*
* @return Attributes
*/
public function getAttributes();
/**
* Add attributes or options to the form element
*
* @param iterable $attributes
*
* @return $this
*/
public function addAttributes($attributes);
/**
* Get the description for the element, if any
*
* @return string|null
*/
public function getDescription();
/**
* Get the label for the element, if any
*
* @return string|null
*/
public function getLabel();
/**
* Get the validation error messages
*
* @return array
*/
public function getMessages();
/**
* Add a validation error message
*
* @param string $message
*
* @return $this
*/
public function addMessage($message);
/**
* Get the name of the element
*
* @return string
*/
public function getName();
/**
* Get whether the element has a value
*
* @return bool False if the element's value is null, the empty string or the empty array; true otherwise
*/
public function hasValue();
/**
* Get the value of the element
*
* @return mixed
*/
public function getValue();
/**
* Set the value of the element
*
* @param mixed $value
*
* @return $this
*/
public function setValue($value);
/**
* Get whether the element has been validated
*
* @return bool
*/
public function hasBeenValidated();
/**
* Get whether the element is ignored
*
* @return bool
*/
public function isIgnored();
/**
* Get whether the element is required
*
* @return bool
*/
public function isRequired();
/**
* Get whether the element is valid
*
* @return bool
*/
public function isValid();
/**
* Handler which is called after this element has been registered
*
* @param Form $form
*
* @return void
*/
public function onRegistered(Form $form);
}

View File

@ -0,0 +1,18 @@
<?php
namespace ipl\Html\Contract;
use ipl\Html\ValidHtml;
/**
* Representation of form element decorators
*/
interface FormElementDecorator extends ValidHtml
{
/**
* Decorate the given form element
*
* @param FormElement $formElement
*/
public function decorate(FormElement $formElement);
}

View File

@ -0,0 +1,13 @@
<?php
namespace ipl\Html\Contract;
interface FormSubmitElement extends FormElement
{
/**
* Get whether the element has been pressed
*
* @return bool
*/
public function hasBeenPressed();
}

View File

@ -0,0 +1,22 @@
<?php
namespace ipl\Html\Contract;
interface ValueCandidates
{
/**
* Get value candidates of this element
*
* @return array
*/
public function getValueCandidates();
/**
* Set value candidates of this element
*
* @param array $values
*
* @return $this
*/
public function setValueCandidates(array $values);
}

View File

@ -0,0 +1,45 @@
<?php
namespace ipl\Html\Contract;
use ipl\Html\ValidHtml;
/**
* Representation of wrappable elements
*/
interface Wrappable extends ValidHtml
{
/**
* Get the wrapper, if any
*
* @return Wrappable|null
*/
public function getWrapper();
/**
* Set the wrapper
*
* @param Wrappable $wrapper
*
* @return $this
*/
public function setWrapper(Wrappable $wrapper);
/**
* Add a wrapper
*
* @param Wrappable $wrapper
*
* @return $this
*/
public function addWrapper(Wrappable $wrapper);
/**
* Prepend a wrapper
*
* @param Wrappable $wrapper
*
* @return $this
*/
public function prependWrapper(Wrappable $wrapper);
}

114
vendor/ipl/html/src/DeferredText.php vendored Normal file
View File

@ -0,0 +1,114 @@
<?php
namespace ipl\Html;
use Exception;
/**
* Text node where content creation is deferred until rendering
*
* The content is created by a passed in callback which is only called when the node is going to be rendered and
* automatically escaped to HTML.
* If the created content is already escaped, see {@link setEscaped()} to indicate this.
*
* # Example Usage
* ```
* $benchmark = new Benchmark();
*
* $performance = new DeferredText(function () use ($benchmark) {
* return $benchmark->summary();
* });
*
* execute_query();
*
* $benchmark->tick('Fetched results');
*
* generate_report();
*
* $benchmark->tick('Report generated');
*
* echo $performance;
* ```
*/
class DeferredText implements ValidHtml
{
/** @var callable will return the text that should be rendered */
protected $callback;
/** @var bool */
protected $escaped = false;
/**
* Create a new text node where content creation is deferred until rendering
*
* @param callable $callback Must return the content that should be rendered
*/
public function __construct(callable $callback)
{
$this->callback = $callback;
}
/**
* Create a new text node where content creation is deferred until rendering
*
* @param callable $callback Must return the content that should be rendered
*
* @return static
*/
public static function create(callable $callback)
{
return new static($callback);
}
/**
* Get whether the callback promises that its content is already escaped
*
* @return bool
*/
public function isEscaped()
{
return $this->escaped;
}
/**
* Set whether the callback's content is already escaped
*
* @param bool $escaped
*
* @return $this
*/
public function setEscaped($escaped = true)
{
$this->escaped = $escaped;
return $this;
}
/**
* Render text to HTML when treated like a string
*
* Calls {@link render()} internally in order to render the text to HTML.
* Exceptions will be automatically caught and returned as HTML string as well using {@link Error::render()}.
*
* @return string
*/
public function __toString()
{
try {
return $this->render();
} catch (Exception $e) {
return Error::render($e);
}
}
public function render()
{
$callback = $this->callback;
if ($this->escaped) {
return $callback();
} else {
return Html::escape($callback());
}
}
}

117
vendor/ipl/html/src/Error.php vendored Normal file
View File

@ -0,0 +1,117 @@
<?php
namespace ipl\Html;
use Exception;
use Throwable;
use function ipl\Stdlib\get_php_type;
/**
* Class Error
*
* TODO: Eventually allow to (statically) inject a custom error renderer.
*
* @package ipl\Html
*/
abstract class Error
{
/** @var bool */
protected static $showTraces = true;
/**
*
* @param Exception|Throwable|string $error
* @return HtmlDocument
*/
public static function show($error)
{
if ($error instanceof Throwable) {
// PHP 7+
$msg = static::createMessageForException($error);
} elseif ($error instanceof Exception) {
// PHP 5.x
$msg = static::createMessageForException($error);
} elseif (is_string($error)) {
$msg = $error;
} else {
// TODO: translate?
$msg = 'Got an invalid error';
}
$result = static::renderErrorMessage($msg);
if (static::showTraces()) {
$result->addHtml(Html::tag('pre', $error->getTraceAsString()));
}
return $result;
}
/**
*
* @param Exception|Throwable|string $error
* @return string
*/
public static function render($error)
{
return static::show($error)->render();
}
/**
* @param bool|null $show
* @return bool
*/
public static function showTraces($show = null)
{
if ($show !== null) {
self::$showTraces = (bool) $show;
}
return self::$showTraces;
}
/**
* @deprecated Use {@link get_php_type()} instead
*/
public static function getPhpTypeName($any)
{
return get_php_type($any);
}
/**
* @param Exception|Throwable $exception
* @return string
*/
protected static function createMessageForException($exception)
{
$file = preg_split('/[\/\\\]/', $exception->getFile(), -1, PREG_SPLIT_NO_EMPTY);
$file = array_pop($file);
return sprintf(
'%s (%s:%d)',
$exception->getMessage(),
$file,
$exception->getLine()
);
}
/**
* @param string
* @return HtmlDocument
*/
protected static function renderErrorMessage($message)
{
$output = new HtmlDocument();
$output->addHtml(
Html::tag('div', ['class' => 'exception'], [
Html::tag('h1', [
Html::tag('i', ['class' => 'icon-bug']),
// TODO: Translate? More hints?
'Oops, an error occurred!'
]),
Html::tag('pre', $message)
])
);
return $output;
}
}

384
vendor/ipl/html/src/Form.php vendored Normal file
View File

@ -0,0 +1,384 @@
<?php
namespace ipl\Html;
use Exception;
use ipl\Html\Contract\FormElement;
use ipl\Html\Contract\FormSubmitElement;
use ipl\Html\FormElement\FormElements;
use ipl\Stdlib\Messages;
use Psr\Http\Message\ServerRequestInterface;
class Form extends BaseHtmlElement
{
use FormElements {
FormElements::remove as private removeElement;
}
use Messages;
const ON_ELEMENT_REGISTERED = 'elementRegistered';
const ON_ERROR = 'error';
const ON_REQUEST = 'request';
const ON_SUCCESS = 'success';
const ON_SENT = 'sent';
const ON_VALIDATE = 'validate';
/** @var string Form submission URL */
protected $action;
/** @var string HTTP method to submit the form with */
protected $method = 'POST';
/** @var FormSubmitElement Primary submit button */
protected $submitButton;
/** @var FormSubmitElement[] Other elements that may submit the form */
protected $submitElements = [];
/** @var bool Whether the form is valid */
protected $isValid;
/** @var ServerRequestInterface The server request being processed */
protected $request;
/** @var string */
protected $redirectUrl;
protected $tag = 'form';
/**
* Get the Form submission URL
*
* @return string|null
*/
public function getAction()
{
return $this->action;
}
/**
* Set the Form submission URL
*
* @param string $action
*
* @return $this
*/
public function setAction($action)
{
$this->action = $action;
return $this;
}
/**
* Get the HTTP method to submit the form with
*
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* Set the HTTP method to submit the form with
*
* @param string $method
*
* @return $this
*/
public function setMethod($method)
{
$this->method = strtoupper($method);
return $this;
}
/**
* Get whether the form has a primary submit button
*
* @return bool
*/
public function hasSubmitButton()
{
return $this->submitButton !== null;
}
/**
* Get the primary submit button
*
* @return FormSubmitElement|null
*/
public function getSubmitButton()
{
return $this->submitButton;
}
/**
* Set the primary submit button
*
* @param FormSubmitElement $element
*
* @return $this
*/
public function setSubmitButton(FormSubmitElement $element)
{
$this->submitButton = $element;
return $this;
}
/**
* Get the submit element used to send the form
*
* @return FormSubmitElement|null
*/
public function getPressedSubmitElement()
{
foreach ($this->submitElements as $submitElement) {
if ($submitElement->hasBeenPressed()) {
return $submitElement;
}
}
return null;
}
/**
* @return ServerRequestInterface|null
*/
public function getRequest()
{
return $this->request;
}
public function setRequest($request)
{
$this->request = $request;
$this->emit(Form::ON_REQUEST, [$request]);
return $this;
}
/**
* Get the url to redirect to on success
*
* @return string
*/
public function getRedirectUrl()
{
return $this->redirectUrl;
}
/**
* Set the url to redirect to on success
*
* @param string $url
*
* @return $this
*/
public function setRedirectUrl($url)
{
$this->redirectUrl = $url;
return $this;
}
/**
* @param ServerRequestInterface $request
* @return $this
*/
public function handleRequest(ServerRequestInterface $request)
{
$this->setRequest($request);
if (! $this->hasBeenSent()) {
// Always assemble
$this->ensureAssembled();
return $this;
}
switch ($request->getMethod()) {
case 'POST':
$params = $request->getParsedBody();
break;
case 'GET':
parse_str($request->getUri()->getQuery(), $params);
break;
default:
$params = [];
}
$this->populate($params);
// Assemble after populate in order to conditionally provide form elements
$this->ensureAssembled();
if ($this->hasBeenSubmitted()) {
if ($this->isValid()) {
try {
$this->emit(Form::ON_SENT, [$this]);
$this->onSuccess();
$this->emitOnce(Form::ON_SUCCESS, [$this]);
} catch (Exception $e) {
$this->addMessage($e);
$this->onError();
$this->emit(Form::ON_ERROR, [$e, $this]);
}
} else {
$this->onError();
}
} else {
$this->validatePartial();
$this->emit(Form::ON_SENT, [$this]);
}
return $this;
}
/**
* Get whether the form has been sent
*
* A form is considered sent if the request's method equals the form's method.
*
* @return bool
*/
public function hasBeenSent()
{
if ($this->request === null) {
return false;
}
return $this->request->getMethod() === $this->getMethod();
}
/**
* Get whether the form has been submitted
*
* A form is submitted when it has been sent and when the primary submit button, if set, has been pressed.
* This method calls {@link hasBeenSent()} in order to detect whether the form has been sent.
*
* @return bool
*/
public function hasBeenSubmitted()
{
if (! $this->hasBeenSent()) {
return false;
}
if ($this->hasSubmitButton()) {
return $this->getSubmitButton()->hasBeenPressed();
}
return true;
}
/**
* Get whether the form is valid
*
* {@link validate()} is called automatically if the form has not been validated before.
*
* @return bool
*/
public function isValid()
{
if ($this->isValid === null) {
$this->validate();
$this->emit(self::ON_VALIDATE, [$this]);
}
return $this->isValid;
}
/**
* Validate all elements
*
* @return $this
*/
public function validate()
{
$valid = true;
foreach ($this->elements as $element) {
if ($element->isRequired() && ! $element->hasValue()) {
$element->addMessage('This field is required');
$valid = false;
continue;
}
if (! $element->isValid()) {
$valid = false;
}
}
$this->isValid = $valid;
return $this;
}
/**
* Validate all elements that have a value
*
* @return $this
*/
public function validatePartial()
{
foreach ($this->getElements() as $element) {
$element->validate();
}
return $this;
}
public function remove(ValidHtml $elementOrHtml)
{
if ($this->submitButton === $elementOrHtml) {
$this->submitButton = null;
}
$this->removeElement($elementOrHtml);
}
protected function onError()
{
$errors = Html::tag('ul', ['class' => 'errors']);
foreach ($this->getMessages() as $message) {
if ($message instanceof Exception) {
$message = $message->getMessage();
}
$errors->addHtml(Html::tag('li', $message));
}
if (! $errors->isEmpty()) {
$this->prependHtml($errors);
}
}
protected function onSuccess()
{
// $this->redirectOnSuccess();
}
protected function onElementRegistered(FormElement $element)
{
if ($element instanceof FormSubmitElement) {
$this->submitElements[$element->getName()] = $element;
if (! $this->hasSubmitButton()) {
$this->setSubmitButton($element);
}
}
$element->onRegistered($this);
}
protected function registerAttributeCallbacks(Attributes $attributes)
{
$attributes
->registerAttributeCallback('action', [$this, 'getAction'], [$this, 'setAction'])
->registerAttributeCallback('method', [$this, 'getMethod'], [$this, 'setMethod']);
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace ipl\Html\FormDecorator;
use Closure;
use ipl\Html\Contract\FormElement;
use ipl\Html\Contract\FormElementDecorator;
use ipl\Html\HtmlDocument;
class CallbackDecorator extends HtmlDocument implements FormElementDecorator
{
/** @var Closure The decorating callback */
protected $callback;
/** @var FormElement The decorated form element */
protected $formElement;
/**
* Create a new CallbackDecorator
*
* @param Closure $callback
*/
public function __construct(Closure $callback)
{
$this->callback = $callback;
}
public function decorate(FormElement $formElement)
{
$decorator = clone $this;
$decorator->formElement = $formElement;
$formElement->prependWrapper($decorator);
}
protected function assemble()
{
call_user_func($this->callback, $this->formElement, $this);
}
}

View File

@ -0,0 +1,141 @@
<?php
namespace ipl\Html\FormDecorator;
use ipl\Html\BaseHtmlElement;
use ipl\Html\FormElement\BaseFormElement;
use ipl\Html\Html;
use ipl\Html\ValidHtml;
class DdDtDecorator extends BaseHtmlElement implements DecoratorInterface
{
protected $tag = 'dl';
protected $dt;
protected $dd;
/** @var BaseFormElement */
protected $wrappedElement;
protected $ready = false;
/**
* @param BaseFormElement $element
* @return static
*/
public function decorate(BaseFormElement $element)
{
// TODO: ignore hidden?
$newWrapper = clone($this);
$newWrapper->wrappedElement = $element;
$element->prependWrapper($newWrapper);
return $newWrapper;
}
protected function renderLabel()
{
if ($this->wrappedElement instanceof BaseFormElement) {
$label = $this->wrappedElement->getLabel();
if ($label) {
return Html::tag('label', null, $label);
}
}
return null;
}
public function getAttributes()
{
$attributes = parent::getAttributes();
// TODO: only when sent?!
if ($this->wrappedElement->hasBeenValidatedAndIsNotValid()) {
$classes = $attributes->get('class');
if (
empty($classes)
|| (is_array($classes) && ! in_array('errors', $classes))
|| (is_string($classes) && $classes !== 'errors')
) {
$attributes->add('class', 'errors');
}
}
return $attributes;
}
protected function renderDescription()
{
if ($this->wrappedElement instanceof BaseFormElement) {
$description = $this->wrappedElement->getDescription();
if ($description) {
return Html::tag('p', ['class' => 'description'], $description);
}
}
return null;
}
protected function renderErrors()
{
if ($this->wrappedElement instanceof BaseFormElement) {
$errors = [];
foreach ($this->wrappedElement->getMessages() as $message) {
$errors[] = Html::tag('p', ['class' => 'error'], $message);
}
if (! empty($errors)) {
return $errors;
}
}
return null;
}
public function addHtml(ValidHtml ...$content)
{
// TODO: is this required?
if (! in_array($this->wrappedElement, $content, true)) {
parent::addHtml(...$content);
}
return $this;
}
protected function assemble()
{
$this->addHtml($this->dt(), $this->dd());
$this->ready = true;
}
protected function dt()
{
if ($this->dt === null) {
$this->dt = Html::tag('dt', null, $this->renderLabel());
}
return $this->dt;
}
/**
* @return \ipl\Html\HtmlElement
*/
protected function dd()
{
if ($this->dd === null) {
$this->dd = Html::tag('dd', null, [
$this->wrappedElement,
$this->renderErrors(),
$this->renderDescription()
]);
}
return $this->dd;
}
public function __destruct()
{
$this->wrapper = null;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace ipl\Html\FormDecorator;
use ipl\Html\Contract\FormElementDecorator;
use ipl\Html\FormElement\BaseFormElement;
/** @deprecated Use {@link FormElementDecorator} instead */
interface DecoratorInterface
{
/**
* Set the form element to decorate
*
* @param BaseFormElement $formElement
*
* @return static
*/
public function decorate(BaseFormElement $formElement);
}

Some files were not shown because too many files have changed in this diff Show More