Version v0.9.1
This commit is contained in:
parent
5f5afe1540
commit
ede13be898
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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%);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
});
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -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 });
|
||||
|
||||
})));
|
|
@ -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 });
|
||||
|
||||
})));
|
|
@ -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 });
|
||||
|
||||
})));
|
|
@ -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 });
|
||||
|
||||
})));
|
|
@ -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 });
|
||||
|
||||
})));
|
|
@ -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 });
|
||||
|
||||
})));
|
|
@ -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 });
|
||||
|
||||
})));
|
|
@ -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 });
|
||||
|
||||
})));
|
|
@ -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 });
|
||||
|
||||
})));
|
|
@ -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 });
|
||||
|
||||
})));
|
|
@ -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;
|
||||
});
|
|
@ -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
|
@ -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;
|
||||
});
|
|
@ -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;
|
||||
});
|
|
@ -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.
|
@ -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=" " horiz-adv-x="512" d="" />
|
||||
<glyph unicode="" 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="" 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="" 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.
|
@ -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 |
|
@ -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 |
|
@ -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"
|
||||
}
|
|
@ -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();
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
@ -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',
|
||||
);
|
|
@ -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',
|
||||
);
|
|
@ -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 . '/'),
|
||||
);
|
|
@ -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'),
|
||||
);
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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": []
|
||||
}
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
);
|
|
@ -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
|
||||
);
|
||||
}
|
|
@ -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.
|
|
@ -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"]
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 = []);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
31
vendor/fortawesome/font-awesome/js-packages/@fortawesome/fontawesome-common-types/package.json
vendored
Normal file
31
vendor/fortawesome/font-awesome/js-packages/@fortawesome/fontawesome-common-types/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
32
vendor/fortawesome/font-awesome/js-packages/@fortawesome/fontawesome-free/package.json
vendored
Normal file
32
vendor/fortawesome/font-awesome/js-packages/@fortawesome/fontawesome-free/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
73
vendor/fortawesome/font-awesome/js-packages/@fortawesome/fontawesome-svg-core/package.json
vendored
Normal file
73
vendor/fortawesome/font-awesome/js-packages/@fortawesome/fontawesome-svg-core/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
55
vendor/fortawesome/font-awesome/js-packages/@fortawesome/free-brands-svg-icons/package.json
vendored
Normal file
55
vendor/fortawesome/font-awesome/js-packages/@fortawesome/free-brands-svg-icons/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
55
vendor/fortawesome/font-awesome/js-packages/@fortawesome/free-regular-svg-icons/package.json
vendored
Normal file
55
vendor/fortawesome/font-awesome/js-packages/@fortawesome/free-regular-svg-icons/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
55
vendor/fortawesome/font-awesome/js-packages/@fortawesome/free-solid-svg-icons/package.json
vendored
Normal file
55
vendor/fortawesome/font-awesome/js-packages/@fortawesome/free-solid-svg-icons/package.json
vendored
Normal 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
|
@ -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.
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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('"', '"', $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('&', '&', $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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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']);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue