mirror of
https://github.com/Icinga/icinga-php-library.git
synced 2025-07-02 03:14:30 +02:00
Version v0.11.1
This commit is contained in:
parent
77c423f41c
commit
06b5d62c5c
35
asset/css/awesome-icons.less
Normal file
35
asset/css/awesome-icons.less
Normal file
@ -0,0 +1,35 @@
|
||||
:root, :host {
|
||||
--fa-font-regular: normal 400 1em/1 "Font Awesome 6 Free";
|
||||
--fa-font-solid: normal 900 1em/1 "Font Awesome 6 Free";
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url('@{iplWebAssets}/font/awesome/fa-regular-400.woff2') format('woff2'),
|
||||
url('@{iplWebAssets}/font/awesome/fa-regular-400.ttf') format('truetype');
|
||||
}
|
||||
|
||||
.far,
|
||||
.fa-regular {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url('@{iplWebAssets}/font/awesome/fa-solid-900.woff2') format('woff2'),
|
||||
url('@{iplWebAssets}/font/awesome/fa-solid-900.ttf') format('truetype');
|
||||
}
|
||||
|
||||
.fa,
|
||||
.fas,
|
||||
.fa-solid {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-weight: 900;
|
||||
}
|
148
asset/css/balls.less
Normal file
148
asset/css/balls.less
Normal file
@ -0,0 +1,148 @@
|
||||
@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:not(.ball-size-l):not(.ball-size-xl) {
|
||||
.ball-solid(var(--state-pending, @state-pending));
|
||||
}
|
||||
|
||||
&.state-pending.ball-size-l,
|
||||
&.state-pending.ball-size-xl {
|
||||
.ball-outline(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;
|
||||
}
|
||||
}
|
||||
}
|
34
asset/css/cancel-button.less
Normal file
34
asset/css/cancel-button.less
Normal file
@ -0,0 +1,34 @@
|
||||
.cancel-button {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
padding: .5em 1em;
|
||||
|
||||
.appearance(none);
|
||||
.rounded-corners();
|
||||
line-height: normal;
|
||||
cursor: pointer;
|
||||
|
||||
background: var(--cancel-button-bg, @cancel-button-bg);
|
||||
border: 1px solid var(--cancel-button-border-color, @cancel-button-border-color);
|
||||
color: var(--cancel-button-color, @cancel-button-color);
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background-color: var(--cancel-button-hover-bg, @cancel-button-hover-bg);
|
||||
color: var(--cancel-button-hover-color, @cancel-button-hover-color);
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
background: none;
|
||||
cursor: default;
|
||||
|
||||
border: 1px solid var(--control-disabled-color, @control-disabled-color);
|
||||
color: var(--control-disabled-color, @control-disabled-color);
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: none;
|
||||
color: var(--control-disabled-color, @control-disabled-color);
|
||||
}
|
||||
}
|
||||
}
|
70
asset/css/compat.less
Normal file
70
asset/css/compat.less
Normal file
@ -0,0 +1,70 @@
|
||||
// General input styles
|
||||
|
||||
.icinga-controls {
|
||||
.uploaded-files {
|
||||
background-color: @default-input-bg;
|
||||
}
|
||||
}
|
||||
|
||||
form.icinga-form {
|
||||
.uploaded-files {
|
||||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.icinga-controls {
|
||||
.uploaded-files {
|
||||
font-size: inherit;
|
||||
padding: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
// Button styles
|
||||
|
||||
// The `form` selector is only required to overrule the hover effect applied by Icinga Web.
|
||||
// It's not required if done by Icinga Web itself, only here because this is applied earlier
|
||||
// as it's part of a library.
|
||||
form.icinga-controls {
|
||||
button[type="submit"].remove-uploaded-file {
|
||||
all: unset;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule Element styles
|
||||
|
||||
.icinga-form > .schedule-element,
|
||||
.icinga-form > .schedule-element > fieldset {
|
||||
margin-top: 1em;
|
||||
|
||||
> .control-group:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.icinga-form .schedule-element {
|
||||
.control-group > fieldset > .weekly,
|
||||
.control-group > .ordinal,
|
||||
.control-group > .monthly,
|
||||
.control-group > .annually {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
// TODO: This effectively restricts the weekly fields to always be aligned to the right,
|
||||
// regardless of the using an icinga-form or not. So this should be removed once we
|
||||
// have re-implemented the decorators.
|
||||
.control-group > fieldset > .weekly {
|
||||
margin-left: 14em;
|
||||
}
|
||||
}
|
||||
|
||||
form.icinga-form .control-group {
|
||||
> .monthly,
|
||||
> .ordinal {
|
||||
margin-right: 2em;
|
||||
}
|
||||
|
||||
> .ordinal.annually {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
151
asset/css/controls.less
Normal file
151
asset/css/controls.less
Normal file
@ -0,0 +1,151 @@
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-control {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.form-element {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
margin-right: .5em;
|
||||
|
||||
label {
|
||||
margin-right: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
.control-button {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.search-controls {
|
||||
display: flex;
|
||||
min-width: 100%;
|
||||
|
||||
.search-bar {
|
||||
flex: 1 1 auto;
|
||||
|
||||
& ~ .control-button {
|
||||
margin-left: .5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The default layout of list controls in Icinga Web
|
||||
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ .pagination-control .limit-control .sort-control │
|
||||
│ <-------------------- .search-controls ----------------------> │
|
||||
└────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
.controls.default-layout {
|
||||
.box-shadow(0, 0, 0, 1px, @gray-lighter);
|
||||
z-index: 1; // The content may clip, this ensures the separator is always visible
|
||||
|
||||
> .pagination-control {
|
||||
float: left;
|
||||
}
|
||||
|
||||
> .sort-control,
|
||||
> .limit-control {
|
||||
float: right;
|
||||
}
|
||||
|
||||
> .limit-control {
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
> .search-controls {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
> :not(:only-child) {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
> .sort-control,
|
||||
> .search-controls > .control-button:last-child {
|
||||
margin-right: -.5em;
|
||||
}
|
||||
|
||||
> .search-controls > .search-bar .search-suggestions {
|
||||
// Suggestions should be kept at a distance from the bottom of the page
|
||||
margin-bottom: 2.5em;
|
||||
}
|
||||
}
|
9
asset/css/datetime-picker.less
Normal file
9
asset/css/datetime-picker.less
Normal file
@ -0,0 +1,9 @@
|
||||
.flatpickr-input + .input {
|
||||
padding-right: 2em;
|
||||
|
||||
& + .fa-calendar {
|
||||
margin: .5em 1em 0 -3.5em;
|
||||
padding: 0 .5em 0 1em;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
40
asset/css/file-element.less
Normal file
40
asset/css/file-element.less
Normal file
@ -0,0 +1,40 @@
|
||||
form .uploaded-files {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
> li:not(:last-of-type) {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
button[type="submit"].remove-uploaded-file {
|
||||
.icon {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
&:focus, &:hover {
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// text-overflow: ellipsis layout rules, yes, exclusively
|
||||
> li {
|
||||
display: flex;
|
||||
|
||||
> button[type="submit"].remove-uploaded-file {
|
||||
display: inline-flex;
|
||||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
|
||||
> span {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6372
asset/css/fontawesome.css
vendored
Normal file
6372
asset/css/fontawesome.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
17
asset/css/horizontal-key-value.less
Normal file
17
asset/css/horizontal-key-value.less
Normal file
@ -0,0 +1,17 @@
|
||||
.horizontal-key-value {
|
||||
display: flex;
|
||||
padding: .25em 0;
|
||||
align-items: baseline;
|
||||
|
||||
.key {
|
||||
color: var(--default-text-color-light, @default-text-color-light);
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
width: 12em;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: var(--default-text-color, @default-text-color);
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
36
asset/css/icinga-icons.less
Normal file
36
asset/css/icinga-icons.less
Normal file
@ -0,0 +1,36 @@
|
||||
@font-face {
|
||||
font-family: 'Icinga-Icons';
|
||||
src: url('@{iplWebAssets}/font/Icinga-Icons.eot');
|
||||
src: url('@{iplWebAssets}/font/Icinga-Icons.eot') format('embedded-opentype'),
|
||||
url('@{iplWebAssets}/font/Icinga-Icons.ttf') format('truetype'),
|
||||
url('@{iplWebAssets}/font/Icinga-Icons.woff') format('woff'),
|
||||
url('@{iplWebAssets}/font/Icinga-Icons.svg') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
[class^="iicon-"]:before, [class*=" iicon-"]:before {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: 'Icinga-Icons';
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1em;
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iicon-minimal:before {
|
||||
content: "\e900";
|
||||
}
|
||||
.iicon-detailed:before {
|
||||
content: "\e901";
|
||||
}
|
||||
.iicon-default:before {
|
||||
content: "\e902";
|
||||
}
|
12
asset/css/icons-base.less
Normal file
12
asset/css/icons-base.less
Normal file
@ -0,0 +1,12 @@
|
||||
i.icon {
|
||||
vertical-align: middle; // Firefox will place icons weird otherwise
|
||||
|
||||
&:before {
|
||||
display: inline-block;
|
||||
min-width: 1em;
|
||||
margin-right: .2em;
|
||||
|
||||
text-align: center;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
}
|
23
asset/css/mixin/card.less
Normal file
23
asset/css/mixin/card.less
Normal file
@ -0,0 +1,23 @@
|
||||
.card() {
|
||||
&.card {
|
||||
.rounded-corners(.5em);
|
||||
border: 1px solid var(--card-border-color, @card-border-color);
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
padding: .5em;
|
||||
|
||||
border-bottom: 1px solid var(--card-border-color, @card-border-color);
|
||||
|
||||
.meta span {
|
||||
font-size: 11/12em;
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: .5em;
|
||||
}
|
||||
}
|
||||
}
|
20
asset/css/mixin/mixins.less
Normal file
20
asset/css/mixin/mixins.less
Normal file
@ -0,0 +1,20 @@
|
||||
.rounded-corners(@border-radius: 0.4em) {
|
||||
border-radius: @border-radius;
|
||||
|
||||
-webkit-background-clip: padding-box;
|
||||
-moz-background-clip: padding;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.appearance(@appearance) {
|
||||
-webkit-appearance: @appearance;
|
||||
-moz-appearance: @appearance;
|
||||
-ms-appearance: @appearance;
|
||||
appearance: @appearance;
|
||||
}
|
||||
|
||||
.box-shadow(@x: 0.2em; @y: 0.2em; @blur: 0.2em; @spread: 0; @color: rgba(83, 83, 83, 0.25)) {
|
||||
-webkit-box-shadow: @arguments;
|
||||
-moz-box-shadow: @arguments;
|
||||
box-shadow: @arguments;
|
||||
}
|
31
asset/css/mixin/state-badges.less
Normal file
31
asset/css/mixin/state-badges.less
Normal file
@ -0,0 +1,31 @@
|
||||
.state-badges() {
|
||||
&.state-badges {
|
||||
padding: 0;
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
li > ul > li:first-child:not(:last-child) .state-badge {
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
li > ul > li:last-child:not(:first-child) .state-badge {
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
> li:not(:last-child) {
|
||||
margin-right: .25em;
|
||||
}
|
||||
|
||||
li > ul > li:last-child {
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
}
|
16
asset/css/primary-submit-btn-duplicate.less
Normal file
16
asset/css/primary-submit-btn-duplicate.less
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
Automatically set CSS class for duplicated submit buttons
|
||||
used for implicit form submission that should be invisible
|
||||
and not take up any space. `display: none` is not an option,
|
||||
because at least Safari will then ignore the element completely
|
||||
when submitting a form.
|
||||
*/
|
||||
.primary-submit-btn-duplicate {
|
||||
border: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
}
|
210
asset/css/schedule-element.less
Normal file
210
asset/css/schedule-element.less
Normal file
@ -0,0 +1,210 @@
|
||||
// Schedule form element
|
||||
|
||||
.schedule-element {
|
||||
@input-border-radius: .25em;
|
||||
|
||||
.ordinal {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.radio-label {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
select {
|
||||
flex: 1 1 auto;
|
||||
|
||||
&:first-of-type {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: @schedule-element-fields-disabled-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radio-label {
|
||||
width: 100%;
|
||||
margin-bottom: .5em;
|
||||
display: flex;
|
||||
align-items: center; // To center the radio element on safari
|
||||
}
|
||||
|
||||
.number-specifier > input[type="number"] {
|
||||
width: 5em;
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
.monthly, .ordinal:not(.annually) {
|
||||
padding: .5em;
|
||||
margin-left: -.5em;
|
||||
border: 1px solid @schedule-element-fields-border-color;
|
||||
.rounded-corners(.75em);
|
||||
}
|
||||
|
||||
.schedule-element-fields {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
.rounded-corners(.25em);
|
||||
overflow: hidden;
|
||||
display:flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&.disabled { // When the "On the" radio button is checked
|
||||
pointer-events: none;
|
||||
|
||||
label {
|
||||
color: @schedule-element-fields-disabled-color;
|
||||
background-color: @schedule-element-fields-disabled-bg;
|
||||
}
|
||||
|
||||
input:checked + label {
|
||||
background: @schedule-element-fields-disabled-selected-bg;
|
||||
color: @schedule-element-fields-disabled-color;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
width: calc(100% / 7); /* default for week based cols makes sense */
|
||||
|
||||
label {
|
||||
display: block;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
padding: .75em 0;
|
||||
background: @schedule-element-fields-bg;
|
||||
color: @schedule-element-fields-color;
|
||||
|
||||
&:hover {
|
||||
background-color: @schedule-element-fields-hover-bg;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
input:checked + label {
|
||||
background-color: @schedule-element-fields-selected-bg;
|
||||
color: @schedule-element-fields-selected-color;
|
||||
}
|
||||
|
||||
input:checked + label:hover {
|
||||
background-color: @schedule-element-fields-selected-hover-bg;
|
||||
border-color: @schedule-element-fields-selected-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&.multiple-fields {
|
||||
li:not(:last-child) label {
|
||||
border-right: 1px solid @schedule-element-fields-border-color;
|
||||
}
|
||||
|
||||
input:focus + label {
|
||||
box-shadow: inset 0 0 0 3px @schedule-element-fields-outline-color;
|
||||
}
|
||||
|
||||
input:checked:focus + label {
|
||||
box-shadow: inset 0 0 0 3px @schedule-element-fields-selected-outline-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.single-fields {
|
||||
li {
|
||||
padding-right: 1px;
|
||||
}
|
||||
|
||||
li label {
|
||||
.rounded-corners(.25em);
|
||||
margin-right: 1px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
li label {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
outline: 3px solid @schedule-element-fields-outline-color;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:focus-within + .note {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input:checked + label:hover {
|
||||
background-color: @schedule-element-fields-selected-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.note {
|
||||
display: none;
|
||||
padding: .5em;
|
||||
background: @schedule-element-keyboard-note-bg;
|
||||
.rounded-corners(.25em);
|
||||
text-align: center;
|
||||
margin-top: 1em;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
/* .weekly */
|
||||
.weekly { }
|
||||
|
||||
/* .monthly styles */
|
||||
.monthly {
|
||||
li label {
|
||||
border-top: 1px solid @schedule-element-fields-border-color;
|
||||
}
|
||||
|
||||
li:first-child,
|
||||
li:nth-child(2),
|
||||
li:nth-child(3),
|
||||
li:nth-child(4),
|
||||
li:nth-child(5),
|
||||
li:nth-child(6),
|
||||
li:nth-child(7) {
|
||||
label {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* last of row should not have a border */
|
||||
.schedule-element-fields li:nth-child(7n) label {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* .annually styles */
|
||||
.annually {
|
||||
li {
|
||||
width: 25%; // 100% / 4 elements
|
||||
}
|
||||
|
||||
li:nth-child(4n) label {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.toggle-slider-controls {
|
||||
display: flex;
|
||||
column-gap: 1em;
|
||||
align-items: center;
|
||||
margin-top: 1em;
|
||||
margin-bottom: -.6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.schedule-recurrences {
|
||||
line-height: 1.1em;
|
||||
padding-top: 0.5625em;
|
||||
|
||||
p {
|
||||
color: @schedule-element-fields-disabled-color;
|
||||
}
|
||||
}
|
316
asset/css/search-bar.less
Normal file
316
asset/css/search-bar.less
Normal file
@ -0,0 +1,316 @@
|
||||
// Style
|
||||
.search-bar {
|
||||
.rounded-corners(.25em);
|
||||
background: var(--searchbar-bg, @searchbar-bg);
|
||||
|
||||
// Reset all input styles
|
||||
input, [type="button"] {
|
||||
.appearance(none);
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
// Submit button styles
|
||||
input[type=submit],
|
||||
button[type=submit],
|
||||
button:not([type]) {
|
||||
background: var(--primary-button-bg, @primary-button-bg);
|
||||
color: var(--primary-button-color, @primary-button-color);
|
||||
border-top-right-radius: .25em;
|
||||
border-bottom-right-radius: .25em;
|
||||
}
|
||||
|
||||
// General input styles
|
||||
input:focus {
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
// Hide the submit button, it must exist, but shouldn't be shown to the user
|
||||
input[type=submit][value="hidden"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Left-most search dropdown style
|
||||
button.search-options {
|
||||
i.icon:before {
|
||||
font-size: 1.2em;
|
||||
margin-right: 0;
|
||||
color: var(--control-color, @control-color);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
i.icon:before {
|
||||
color: var(--control-disabled-color, @control-disabled-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scrollbar style
|
||||
.filter-input-area {
|
||||
// Firefox
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--searchbar-scrollbar-bg, @searchbar-scrollbar-bg) transparent;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
height: .5em;
|
||||
}
|
||||
|
||||
&:hover::-webkit-scrollbar {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: .25em;
|
||||
background: var(--searchbar-scrollbar-bg, @searchbar-scrollbar-bg);
|
||||
}
|
||||
}
|
||||
|
||||
// Term styles
|
||||
.filter-condition {
|
||||
button {
|
||||
border-radius: .4em 0 0 .4em;
|
||||
background-color: var(--search-condition-remove-bg, @search-condition-remove-bg);
|
||||
color: var(--search-condition-remove-color, @search-condition-remove-color);
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: .4em;
|
||||
height: 100%;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
||||
background-color: var(--searchbar-bg, @searchbar-bg);
|
||||
border: .2em solid var(--search-condition-remove-bg, @search-condition-remove-bg);
|
||||
border-width: 0 0 0 .2em;
|
||||
border-top-left-radius: .4em;
|
||||
border-bottom-left-radius: .4em;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: var(--search-term-bg, @search-term-bg);
|
||||
color: var(--search-term-color, @search-term-color);
|
||||
}
|
||||
}
|
||||
|
||||
.terms > .filter-condition:first-child button {
|
||||
border-radius: 0 .4em .4em 0;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: .4em;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
background-color: var(--searchbar-bg, @searchbar-bg);
|
||||
border: .2em solid var(--search-condition-remove-bg, @search-condition-remove-bg);
|
||||
border-width: 0 .2em 0 0;
|
||||
border-top-right-radius: .4em;
|
||||
border-bottom-right-radius: .4em;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
.logical_operator,
|
||||
.grouping_operator_open,
|
||||
.grouping_operator_close {
|
||||
input {
|
||||
.rounded-corners();
|
||||
background-color: var(--search-logical-operator-bg, @search-logical-operator-bg);
|
||||
color: var(--search-logical-operator-color, @search-logical-operator-color);
|
||||
}
|
||||
}
|
||||
|
||||
.operator,
|
||||
.logical_operator,
|
||||
.grouping_operator_open,
|
||||
.grouping_operator_close {
|
||||
input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
[data-index] input:invalid {
|
||||
background-color: var(--search-term-invalid-bg, @search-term-invalid-bg);
|
||||
color: var(--search-term-invalid-color, @search-term-invalid-color);
|
||||
}
|
||||
|
||||
[data-index] input:disabled {
|
||||
background-color: var(--search-term-disabled-bg, @search-term-disabled-bg);
|
||||
}
|
||||
|
||||
.column input {
|
||||
.rounded-corners(.4em);
|
||||
}
|
||||
.column:not(:last-of-type),
|
||||
.column.last-term {
|
||||
input {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.operator:last-of-type:not(.last-term) input {
|
||||
.rounded-corners(.4em);
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.value input {
|
||||
.rounded-corners(.4em);
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.highlighted input {
|
||||
background-color: var(--search-term-highlighted-bg, @search-term-highlighted-bg);
|
||||
color: var(--search-term-highlighted-color, @search-term-highlighted-color);
|
||||
}
|
||||
|
||||
.selected input {
|
||||
background-color: var(--search-term-selected-bg, @search-term-selected-bg);
|
||||
color: var(--search-term-selected-color, @search-term-selected-color);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
ul.comma-separated {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
|
||||
list-style-type: none;
|
||||
|
||||
li {
|
||||
display: inline;
|
||||
|
||||
&:not(:first-of-type):before {
|
||||
display: inline;
|
||||
content: ', ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Layout
|
||||
.search-bar {
|
||||
height: 2em;
|
||||
display: flex;
|
||||
position: relative; // Required for the suggestions
|
||||
|
||||
button.search-options {
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.filter-input-area {
|
||||
overflow: auto hidden;
|
||||
overflow-x: overlay; // Not invalid, but proprietary feature by chrome/webkit
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: ~"calc(2em + 10px)"; // Search bar height + approximate scrollbar height
|
||||
padding: 2/12em; // 2 (px) desired / default font size (px)
|
||||
|
||||
// Lets inputs grow based on their contents, Inspired by https://css-tricks.com/auto-growing-inputs-textareas/
|
||||
label {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
min-width: 2em;
|
||||
|
||||
&::after,
|
||||
input {
|
||||
width: auto;
|
||||
padding: 0 .5em;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
line-height: 20/12; // 20 (px) desired / default font size (px)
|
||||
}
|
||||
|
||||
&::after {
|
||||
height: 0;
|
||||
content: attr(data-label);
|
||||
visibility: hidden;
|
||||
white-space: nowrap;
|
||||
padding: 0 7/12em; // 7 (px) desired / default font size (px)
|
||||
}
|
||||
}
|
||||
|
||||
> label {
|
||||
flex: 1 0 auto;
|
||||
|
||||
&::after,
|
||||
input {
|
||||
max-width: none;
|
||||
min-width: 8em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.terms {
|
||||
display: inline;
|
||||
flex-shrink: 0;
|
||||
|
||||
.filter-chain,
|
||||
.filter-condition {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.filter-condition {
|
||||
position: relative;
|
||||
|
||||
button {
|
||||
display: none;
|
||||
z-index: 1;
|
||||
width: ~"calc(2em + 2px)";
|
||||
padding: .15em .6em .15em .4em;
|
||||
position: absolute;
|
||||
left: ~"calc(-2em - 2px)"; // That's min-width + margin-right of an operator
|
||||
line-height: 16/12; // 16 (px) desired / default font size (px)
|
||||
|
||||
i:before {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(._hover_delay):hover button {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
> .filter-condition:first-child button {
|
||||
padding: .15em .4em .15em .6em;
|
||||
left: auto;
|
||||
right: ~"calc(-2em - 1px)"; // That's min-width + margin-left of an operator
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: 1px;
|
||||
|
||||
&.logical_operator,
|
||||
&.grouping_operator_open,
|
||||
&.grouping_operator_close {
|
||||
margin-left: 1px; // adds up to 2px with the previous term
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
.terms .filter-condition:hover button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.search-suggestions {
|
||||
// 2 (px) desired / default font-size to match .filter-input outline-offset (-1px) + outline-width (3px)
|
||||
margin-top: 2/12em;
|
||||
}
|
||||
}
|
131
asset/css/search-base.less
Normal file
131
asset/css/search-base.less
Normal file
@ -0,0 +1,131 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
.nothing-to-suggest {
|
||||
color: var(--suggestions-color, @suggestions-color);
|
||||
}
|
||||
|
||||
.relation-path {
|
||||
padding: 0 .2em;
|
||||
background-color: var(--suggestions-relation-path-bg, @suggestions-relation-path-bg);
|
||||
}
|
||||
|
||||
[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;
|
||||
|
||||
.relation-path {
|
||||
background-color: var(--suggestions-relation-path-focus-bg, @suggestions-relation-path-focus-bg);
|
||||
}
|
||||
}
|
||||
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
li.nothing-to-suggest {
|
||||
padding: .5em 1em;
|
||||
}
|
||||
}
|
||||
|
||||
[type="button"] {
|
||||
padding: .5em 1em;
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
||||
&[data-class="operator"], &[data-class="logical_operator"] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.has-details {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.relation-path {
|
||||
margin-left: .5em;
|
||||
|
||||
&::first-line {
|
||||
font-size: .8em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
268
asset/css/search-editor.less
Normal file
268
asset/css/search-editor.less
Normal file
@ -0,0 +1,268 @@
|
||||
// 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);
|
||||
color: var(--search-term-invalid-color, @search-term-invalid-color);
|
||||
}
|
||||
|
||||
.search-errors {
|
||||
color: var(--search-editor-error-color, @search-editor-error-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;
|
||||
}
|
||||
}
|
47
asset/css/state-badge.less
Normal file
47
asset/css/state-badge.less
Normal file
@ -0,0 +1,47 @@
|
||||
.state-badge {
|
||||
.rounded-corners();
|
||||
color: var(--default-text-color-inverted, @default-text-color-inverted);
|
||||
display: inline-block;
|
||||
font-size: 1em;
|
||||
min-width: 2em;
|
||||
padding: .25em;
|
||||
text-align: center;
|
||||
|
||||
&.handled {
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
&.state-critical {
|
||||
background-color: var(--state-critical, @state-critical);
|
||||
}
|
||||
|
||||
&.state-down {
|
||||
background-color: var(--state-down, @state-down);
|
||||
}
|
||||
|
||||
&.state-ok {
|
||||
background-color: var(--state-ok, @state-ok);
|
||||
}
|
||||
|
||||
&.state-pending {
|
||||
background-color: var(--state-pending, @state-pending);
|
||||
}
|
||||
|
||||
&.state-unknown {
|
||||
background-color: var(--state-unknown, @state-unknown);
|
||||
}
|
||||
|
||||
&.state-up {
|
||||
background-color: var(--state-up, @state-up);
|
||||
}
|
||||
|
||||
&.state-warning {
|
||||
background-color: var(--state-warning, @state-warning);
|
||||
}
|
||||
}
|
||||
|
||||
a .state-badge {
|
||||
&:not(.disabled):hover {
|
||||
filter: brightness(80%);
|
||||
}
|
||||
}
|
200
asset/css/variables.less
Normal file
200
asset/css/variables.less
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
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;
|
||||
@base-primary-dark: #0081a6;
|
||||
@base-primary-light: fade(@base-primary-bg, 50%);
|
||||
|
||||
@default-text-color: #fff;
|
||||
@default-text-color-light: fade(@default-text-color, 75%);
|
||||
@default-text-color-inverted: @default-bg;
|
||||
@default-input-bg: #404d72;
|
||||
|
||||
@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: @base-primary-dark;
|
||||
|
||||
@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: @default-input-bg;
|
||||
@searchbar-scrollbar-bg: @base-gray-light;
|
||||
|
||||
@search-editor-error-color: @state-critical;
|
||||
@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;
|
||||
@suggestions-relation-path-bg: @base-gray-light;
|
||||
@suggestions-relation-path-focus-bg: @base-gray;
|
||||
|
||||
@card-border-color: @base-gray-light;
|
||||
|
||||
@schedule-element-fields-bg: @default-input-bg;
|
||||
@schedule-element-fields-color: @base-primary-color;
|
||||
@schedule-element-fields-border-color: @base-gray-light;
|
||||
@schedule-element-fields-selected-bg: @primary-button-bg;
|
||||
@schedule-element-fields-selected-color: @default-text-color-inverted;
|
||||
@schedule-element-fields-hover-bg: @base-primary-light;
|
||||
@schedule-element-fields-outline-color: fade(@base-primary-bg, 50%);
|
||||
@schedule-element-fields-selected-outline-color: fade(#fff, 50%);
|
||||
@schedule-element-fields-selected-hover-bg: @primary-button-hover-bg;
|
||||
@schedule-element-fields-disabled-color: @base-gray;
|
||||
@schedule-element-fields-disabled-bg: @base-gray-lighter;
|
||||
@schedule-element-fields-disabled-selected-bg: @base-gray-light;
|
||||
@schedule-element-keyboard-note-bg: @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;
|
||||
--default-input-bg: #DEECF1;
|
||||
|
||||
--primary-button-color: var(--default-text-color-inverted);
|
||||
--primary-button-bg: @primary-button-bg;
|
||||
--primary-button-hover-bg: @primary-button-hover-bg;
|
||||
|
||||
--searchbar-bg: var(--default-input-bg);
|
||||
--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-error-color: var(--base-remove-bg);
|
||||
--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);
|
||||
--suggestions-relation-path-bg: var(--base-gray-lighter);
|
||||
--suggestions-relation-path-focus-bg: var(--base-gray);
|
||||
|
||||
--card-border-color: var(--base-gray-light);
|
||||
|
||||
--schedule-element-fields-bg: var(--default-input-bg);
|
||||
--schedule-element-fields-color: var(--base-primary-color);
|
||||
--schedule-element-fields-border-color: var(--base-gray-light);
|
||||
--schedule-element-fields-selected-bg: var(--primary-button-bg);
|
||||
--schedule-element-fields-selected-color: var(--default-text-color-inverted);
|
||||
--schedule-element-fields-hover-bg: @base-primary-light;
|
||||
--schedule-element-fields-outline-color: fade(@base-primary-bg, 50%);
|
||||
--schedule-element-fields-selected-outline-color: fade(#fff, 50%);
|
||||
--schedule-element-fields-selected-hover-bg: var(--primary-button-hover-bg);
|
||||
--schedule-element-fields-disabled-color: var(--base-gray);
|
||||
--schedule-element-fields-disabled-bg: var(--base-gray-lighter);
|
||||
--schedule-element-fields-disabled-selected-bg: var(--base-gray-light);
|
||||
--schedule-element-keyboard-note-bg: var(--base-gray-light);
|
||||
}
|
||||
};
|
791
asset/css/vendor/flatpickr.css
vendored
Normal file
791
asset/css/vendor/flatpickr.css
vendored
Normal file
@ -0,0 +1,791 @@
|
||||
.flatpickr-calendar {
|
||||
background: transparent;
|
||||
opacity: 0;
|
||||
display: none;
|
||||
text-align: center;
|
||||
visibility: hidden;
|
||||
padding: 0;
|
||||
-webkit-animation: none;
|
||||
animation: none;
|
||||
direction: ltr;
|
||||
border: 0;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
border-radius: 5px;
|
||||
position: absolute;
|
||||
width: 307.875px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-ms-touch-action: manipulation;
|
||||
touch-action: manipulation;
|
||||
background: #fff;
|
||||
-webkit-box-shadow: 1px 0 0 #e6e6e6, -1px 0 0 #e6e6e6, 0 1px 0 #e6e6e6, 0 -1px 0 #e6e6e6, 0 3px 13px rgba(0,0,0,0.08);
|
||||
box-shadow: 1px 0 0 #e6e6e6, -1px 0 0 #e6e6e6, 0 1px 0 #e6e6e6, 0 -1px 0 #e6e6e6, 0 3px 13px rgba(0,0,0,0.08);
|
||||
}
|
||||
.flatpickr-calendar.open,
|
||||
.flatpickr-calendar.inline {
|
||||
opacity: 1;
|
||||
max-height: 640px;
|
||||
visibility: visible;
|
||||
}
|
||||
.flatpickr-calendar.open {
|
||||
display: inline-block;
|
||||
z-index: 99999;
|
||||
}
|
||||
.flatpickr-calendar.animate.open {
|
||||
-webkit-animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1);
|
||||
animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
.flatpickr-calendar.inline {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
.flatpickr-calendar.static {
|
||||
position: absolute;
|
||||
top: calc(100% + 2px);
|
||||
}
|
||||
.flatpickr-calendar.static.open {
|
||||
z-index: 999;
|
||||
display: block;
|
||||
}
|
||||
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7) {
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1) {
|
||||
-webkit-box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
|
||||
box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
|
||||
}
|
||||
.flatpickr-calendar .hasWeeks .dayContainer,
|
||||
.flatpickr-calendar .hasTime .dayContainer {
|
||||
border-bottom: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
.flatpickr-calendar .hasWeeks .dayContainer {
|
||||
border-left: 0;
|
||||
}
|
||||
.flatpickr-calendar.hasTime .flatpickr-time {
|
||||
height: 40px;
|
||||
border-top: 1px solid #e6e6e6;
|
||||
}
|
||||
.flatpickr-calendar.noCalendar.hasTime .flatpickr-time {
|
||||
height: auto;
|
||||
}
|
||||
.flatpickr-calendar:before,
|
||||
.flatpickr-calendar:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
border: solid transparent;
|
||||
content: '';
|
||||
height: 0;
|
||||
width: 0;
|
||||
left: 22px;
|
||||
}
|
||||
.flatpickr-calendar.rightMost:before,
|
||||
.flatpickr-calendar.arrowRight:before,
|
||||
.flatpickr-calendar.rightMost:after,
|
||||
.flatpickr-calendar.arrowRight:after {
|
||||
left: auto;
|
||||
right: 22px;
|
||||
}
|
||||
.flatpickr-calendar.arrowCenter:before,
|
||||
.flatpickr-calendar.arrowCenter:after {
|
||||
left: 50%;
|
||||
right: 50%;
|
||||
}
|
||||
.flatpickr-calendar:before {
|
||||
border-width: 5px;
|
||||
margin: 0 -5px;
|
||||
}
|
||||
.flatpickr-calendar:after {
|
||||
border-width: 4px;
|
||||
margin: 0 -4px;
|
||||
}
|
||||
.flatpickr-calendar.arrowTop:before,
|
||||
.flatpickr-calendar.arrowTop:after {
|
||||
bottom: 100%;
|
||||
}
|
||||
.flatpickr-calendar.arrowTop:before {
|
||||
border-bottom-color: #e6e6e6;
|
||||
}
|
||||
.flatpickr-calendar.arrowTop:after {
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.flatpickr-calendar.arrowBottom:before,
|
||||
.flatpickr-calendar.arrowBottom:after {
|
||||
top: 100%;
|
||||
}
|
||||
.flatpickr-calendar.arrowBottom:before {
|
||||
border-top-color: #e6e6e6;
|
||||
}
|
||||
.flatpickr-calendar.arrowBottom:after {
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.flatpickr-calendar:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.flatpickr-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.flatpickr-months {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.flatpickr-months .flatpickr-month {
|
||||
background: transparent;
|
||||
color: rgba(0,0,0,0.9);
|
||||
fill: rgba(0,0,0,0.9);
|
||||
height: 34px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month,
|
||||
.flatpickr-months .flatpickr-next-month {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 34px;
|
||||
padding: 10px;
|
||||
z-index: 3;
|
||||
color: rgba(0,0,0,0.9);
|
||||
fill: rgba(0,0,0,0.9);
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-disabled,
|
||||
.flatpickr-months .flatpickr-next-month.flatpickr-disabled {
|
||||
display: none;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month i,
|
||||
.flatpickr-months .flatpickr-next-month i {
|
||||
position: relative;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,
|
||||
.flatpickr-months .flatpickr-next-month.flatpickr-prev-month {
|
||||
/*
|
||||
/*rtl:begin:ignore*/
|
||||
/*
|
||||
*/
|
||||
left: 0;
|
||||
/*
|
||||
/*rtl:end:ignore*/
|
||||
/*
|
||||
*/
|
||||
}
|
||||
/*
|
||||
/*rtl:begin:ignore*/
|
||||
/*
|
||||
/*rtl:end:ignore*/
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-next-month,
|
||||
.flatpickr-months .flatpickr-next-month.flatpickr-next-month {
|
||||
/*
|
||||
/*rtl:begin:ignore*/
|
||||
/*
|
||||
*/
|
||||
right: 0;
|
||||
/*
|
||||
/*rtl:end:ignore*/
|
||||
/*
|
||||
*/
|
||||
}
|
||||
/*
|
||||
/*rtl:begin:ignore*/
|
||||
/*
|
||||
/*rtl:end:ignore*/
|
||||
.flatpickr-months .flatpickr-prev-month:hover,
|
||||
.flatpickr-months .flatpickr-next-month:hover {
|
||||
color: #959ea9;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month:hover svg,
|
||||
.flatpickr-months .flatpickr-next-month:hover svg {
|
||||
fill: #f64747;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month svg,
|
||||
.flatpickr-months .flatpickr-next-month svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month svg path,
|
||||
.flatpickr-months .flatpickr-next-month svg path {
|
||||
-webkit-transition: fill 0.1s;
|
||||
transition: fill 0.1s;
|
||||
fill: inherit;
|
||||
}
|
||||
.numInputWrapper {
|
||||
position: relative;
|
||||
height: auto;
|
||||
}
|
||||
.numInputWrapper input,
|
||||
.numInputWrapper span {
|
||||
display: inline-block;
|
||||
}
|
||||
.numInputWrapper input {
|
||||
width: 100%;
|
||||
}
|
||||
.numInputWrapper input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
.numInputWrapper input::-webkit-outer-spin-button,
|
||||
.numInputWrapper input::-webkit-inner-spin-button {
|
||||
margin: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.numInputWrapper span {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 14px;
|
||||
padding: 0 4px 0 2px;
|
||||
height: 50%;
|
||||
line-height: 50%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
border: 1px solid rgba(57,57,57,0.15);
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.numInputWrapper span:hover {
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
.numInputWrapper span:active {
|
||||
background: rgba(0,0,0,0.2);
|
||||
}
|
||||
.numInputWrapper span:after {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
}
|
||||
.numInputWrapper span.arrowUp {
|
||||
top: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.numInputWrapper span.arrowUp:after {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-bottom: 4px solid rgba(57,57,57,0.6);
|
||||
top: 26%;
|
||||
}
|
||||
.numInputWrapper span.arrowDown {
|
||||
top: 50%;
|
||||
}
|
||||
.numInputWrapper span.arrowDown:after {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid rgba(57,57,57,0.6);
|
||||
top: 40%;
|
||||
}
|
||||
.numInputWrapper span svg {
|
||||
width: inherit;
|
||||
height: auto;
|
||||
}
|
||||
.numInputWrapper span svg path {
|
||||
fill: rgba(0,0,0,0.5);
|
||||
}
|
||||
.numInputWrapper:hover {
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
.numInputWrapper:hover span {
|
||||
opacity: 1;
|
||||
}
|
||||
.flatpickr-current-month {
|
||||
font-size: 135%;
|
||||
line-height: inherit;
|
||||
font-weight: 300;
|
||||
color: inherit;
|
||||
position: absolute;
|
||||
width: 75%;
|
||||
left: 12.5%;
|
||||
padding: 7.48px 0 0 0;
|
||||
line-height: 1;
|
||||
height: 34px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
-webkit-transform: translate3d(0px, 0px, 0px);
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
}
|
||||
.flatpickr-current-month span.cur-month {
|
||||
font-family: inherit;
|
||||
font-weight: 700;
|
||||
color: inherit;
|
||||
display: inline-block;
|
||||
margin-left: 0.5ch;
|
||||
padding: 0;
|
||||
}
|
||||
.flatpickr-current-month span.cur-month:hover {
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
.flatpickr-current-month .numInputWrapper {
|
||||
width: 6ch;
|
||||
width: 7ch\0;
|
||||
display: inline-block;
|
||||
}
|
||||
.flatpickr-current-month .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: rgba(0,0,0,0.9);
|
||||
}
|
||||
.flatpickr-current-month .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: rgba(0,0,0,0.9);
|
||||
}
|
||||
.flatpickr-current-month input.cur-year {
|
||||
background: transparent;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: inherit;
|
||||
cursor: text;
|
||||
padding: 0 0 0 0.5ch;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: 300;
|
||||
line-height: inherit;
|
||||
height: auto;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
vertical-align: initial;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
.flatpickr-current-month input.cur-year:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.flatpickr-current-month input.cur-year[disabled],
|
||||
.flatpickr-current-month input.cur-year[disabled]:hover {
|
||||
font-size: 100%;
|
||||
color: rgba(0,0,0,0.5);
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months {
|
||||
appearance: menulist;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-sizing: border-box;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: 300;
|
||||
height: auto;
|
||||
line-height: inherit;
|
||||
margin: -1px 0 0 0;
|
||||
outline: none;
|
||||
padding: 0 0 0 0.5ch;
|
||||
position: relative;
|
||||
vertical-align: initial;
|
||||
-webkit-box-sizing: border-box;
|
||||
-webkit-appearance: menulist;
|
||||
-moz-appearance: menulist;
|
||||
width: auto;
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months:focus,
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months:active {
|
||||
outline: none;
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month {
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
.flatpickr-weekdays {
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
}
|
||||
.flatpickr-weekdays .flatpickr-weekdaycontainer {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
span.flatpickr-weekday {
|
||||
cursor: default;
|
||||
font-size: 90%;
|
||||
background: transparent;
|
||||
color: rgba(0,0,0,0.54);
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
display: block;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
font-weight: bolder;
|
||||
}
|
||||
.dayContainer,
|
||||
.flatpickr-weeks {
|
||||
padding: 1px 0 0 0;
|
||||
}
|
||||
.flatpickr-days {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: start;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: start;
|
||||
align-items: flex-start;
|
||||
width: 307.875px;
|
||||
}
|
||||
.flatpickr-days:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.dayContainer {
|
||||
padding: 0;
|
||||
outline: 0;
|
||||
text-align: left;
|
||||
width: 307.875px;
|
||||
min-width: 307.875px;
|
||||
max-width: 307.875px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
-ms-flex-wrap: wrap;
|
||||
-ms-flex-pack: justify;
|
||||
-webkit-justify-content: space-around;
|
||||
justify-content: space-around;
|
||||
-webkit-transform: translate3d(0px, 0px, 0px);
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
opacity: 1;
|
||||
}
|
||||
.dayContainer + .dayContainer {
|
||||
-webkit-box-shadow: -1px 0 0 #e6e6e6;
|
||||
box-shadow: -1px 0 0 #e6e6e6;
|
||||
}
|
||||
.flatpickr-day {
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 150px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: #393939;
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
width: 14.2857143%;
|
||||
-webkit-flex-basis: 14.2857143%;
|
||||
-ms-flex-preferred-size: 14.2857143%;
|
||||
flex-basis: 14.2857143%;
|
||||
max-width: 39px;
|
||||
height: 39px;
|
||||
line-height: 39px;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
.flatpickr-day.inRange,
|
||||
.flatpickr-day.prevMonthDay.inRange,
|
||||
.flatpickr-day.nextMonthDay.inRange,
|
||||
.flatpickr-day.today.inRange,
|
||||
.flatpickr-day.prevMonthDay.today.inRange,
|
||||
.flatpickr-day.nextMonthDay.today.inRange,
|
||||
.flatpickr-day:hover,
|
||||
.flatpickr-day.prevMonthDay:hover,
|
||||
.flatpickr-day.nextMonthDay:hover,
|
||||
.flatpickr-day:focus,
|
||||
.flatpickr-day.prevMonthDay:focus,
|
||||
.flatpickr-day.nextMonthDay:focus {
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
background: #e6e6e6;
|
||||
border-color: #e6e6e6;
|
||||
}
|
||||
.flatpickr-day.today {
|
||||
border-color: #959ea9;
|
||||
}
|
||||
.flatpickr-day.today:hover,
|
||||
.flatpickr-day.today:focus {
|
||||
border-color: #959ea9;
|
||||
background: #959ea9;
|
||||
color: #fff;
|
||||
}
|
||||
.flatpickr-day.selected,
|
||||
.flatpickr-day.startRange,
|
||||
.flatpickr-day.endRange,
|
||||
.flatpickr-day.selected.inRange,
|
||||
.flatpickr-day.startRange.inRange,
|
||||
.flatpickr-day.endRange.inRange,
|
||||
.flatpickr-day.selected:focus,
|
||||
.flatpickr-day.startRange:focus,
|
||||
.flatpickr-day.endRange:focus,
|
||||
.flatpickr-day.selected:hover,
|
||||
.flatpickr-day.startRange:hover,
|
||||
.flatpickr-day.endRange:hover,
|
||||
.flatpickr-day.selected.prevMonthDay,
|
||||
.flatpickr-day.startRange.prevMonthDay,
|
||||
.flatpickr-day.endRange.prevMonthDay,
|
||||
.flatpickr-day.selected.nextMonthDay,
|
||||
.flatpickr-day.startRange.nextMonthDay,
|
||||
.flatpickr-day.endRange.nextMonthDay {
|
||||
background: #569ff7;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
border-color: #569ff7;
|
||||
}
|
||||
.flatpickr-day.selected.startRange,
|
||||
.flatpickr-day.startRange.startRange,
|
||||
.flatpickr-day.endRange.startRange {
|
||||
border-radius: 50px 0 0 50px;
|
||||
}
|
||||
.flatpickr-day.selected.endRange,
|
||||
.flatpickr-day.startRange.endRange,
|
||||
.flatpickr-day.endRange.endRange {
|
||||
border-radius: 0 50px 50px 0;
|
||||
}
|
||||
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),
|
||||
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),
|
||||
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) {
|
||||
-webkit-box-shadow: -10px 0 0 #569ff7;
|
||||
box-shadow: -10px 0 0 #569ff7;
|
||||
}
|
||||
.flatpickr-day.selected.startRange.endRange,
|
||||
.flatpickr-day.startRange.startRange.endRange,
|
||||
.flatpickr-day.endRange.startRange.endRange {
|
||||
border-radius: 50px;
|
||||
}
|
||||
.flatpickr-day.inRange {
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: -5px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
|
||||
box-shadow: -5px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
|
||||
}
|
||||
.flatpickr-day.flatpickr-disabled,
|
||||
.flatpickr-day.flatpickr-disabled:hover,
|
||||
.flatpickr-day.prevMonthDay,
|
||||
.flatpickr-day.nextMonthDay,
|
||||
.flatpickr-day.notAllowed,
|
||||
.flatpickr-day.notAllowed.prevMonthDay,
|
||||
.flatpickr-day.notAllowed.nextMonthDay {
|
||||
color: rgba(57,57,57,0.3);
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
.flatpickr-day.flatpickr-disabled,
|
||||
.flatpickr-day.flatpickr-disabled:hover {
|
||||
cursor: not-allowed;
|
||||
color: rgba(57,57,57,0.1);
|
||||
}
|
||||
.flatpickr-day.week.selected {
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: -5px 0 0 #569ff7, 5px 0 0 #569ff7;
|
||||
box-shadow: -5px 0 0 #569ff7, 5px 0 0 #569ff7;
|
||||
}
|
||||
.flatpickr-day.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
.rangeMode .flatpickr-day {
|
||||
margin-top: 1px;
|
||||
}
|
||||
.flatpickr-weekwrapper {
|
||||
float: left;
|
||||
}
|
||||
.flatpickr-weekwrapper .flatpickr-weeks {
|
||||
padding: 0 12px;
|
||||
-webkit-box-shadow: 1px 0 0 #e6e6e6;
|
||||
box-shadow: 1px 0 0 #e6e6e6;
|
||||
}
|
||||
.flatpickr-weekwrapper .flatpickr-weekday {
|
||||
float: none;
|
||||
width: 100%;
|
||||
line-height: 28px;
|
||||
}
|
||||
.flatpickr-weekwrapper span.flatpickr-day,
|
||||
.flatpickr-weekwrapper span.flatpickr-day:hover {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
color: rgba(57,57,57,0.3);
|
||||
background: transparent;
|
||||
cursor: default;
|
||||
border: none;
|
||||
}
|
||||
.flatpickr-innerContainer {
|
||||
display: block;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
.flatpickr-rContainer {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.flatpickr-time {
|
||||
text-align: center;
|
||||
outline: 0;
|
||||
display: block;
|
||||
height: 0;
|
||||
line-height: 40px;
|
||||
max-height: 40px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.flatpickr-time:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
.flatpickr-time .numInputWrapper {
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
width: 40%;
|
||||
height: 40px;
|
||||
float: left;
|
||||
}
|
||||
.flatpickr-time .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: #393939;
|
||||
}
|
||||
.flatpickr-time .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: #393939;
|
||||
}
|
||||
.flatpickr-time.hasSeconds .numInputWrapper {
|
||||
width: 26%;
|
||||
}
|
||||
.flatpickr-time.time24hr .numInputWrapper {
|
||||
width: 49%;
|
||||
}
|
||||
.flatpickr-time input {
|
||||
background: transparent;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: inherit;
|
||||
line-height: inherit;
|
||||
color: #393939;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
.flatpickr-time input.flatpickr-hour {
|
||||
font-weight: bold;
|
||||
}
|
||||
.flatpickr-time input.flatpickr-minute,
|
||||
.flatpickr-time input.flatpickr-second {
|
||||
font-weight: 400;
|
||||
}
|
||||
.flatpickr-time input:focus {
|
||||
outline: 0;
|
||||
border: 0;
|
||||
}
|
||||
.flatpickr-time .flatpickr-time-separator,
|
||||
.flatpickr-time .flatpickr-am-pm {
|
||||
height: inherit;
|
||||
float: left;
|
||||
line-height: inherit;
|
||||
color: #393939;
|
||||
font-weight: bold;
|
||||
width: 2%;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-align-self: center;
|
||||
-ms-flex-item-align: center;
|
||||
align-self: center;
|
||||
}
|
||||
.flatpickr-time .flatpickr-am-pm {
|
||||
outline: 0;
|
||||
width: 18%;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
}
|
||||
.flatpickr-time input:hover,
|
||||
.flatpickr-time .flatpickr-am-pm:hover,
|
||||
.flatpickr-time input:focus,
|
||||
.flatpickr-time .flatpickr-am-pm:focus {
|
||||
background: #eee;
|
||||
}
|
||||
.flatpickr-input[readonly] {
|
||||
cursor: pointer;
|
||||
}
|
||||
@-webkit-keyframes fpFadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -20px, 0);
|
||||
transform: translate3d(0, -20px, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
@keyframes fpFadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -20px, 0);
|
||||
transform: translate3d(0, -20px, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
327
asset/css/vendor/flatpickr.vars.less
vendored
Normal file
327
asset/css/vendor/flatpickr.vars.less
vendored
Normal file
@ -0,0 +1,327 @@
|
||||
/**
|
||||
* This file's only purpose is to make the flatpickr themeable. DO NOT add ANY custom style here!
|
||||
* Also, DO NOT re-arrange the CSS blocks to make them more LESS like. They're based off of the
|
||||
* pre-compiled flatpickr.css file and so can easily identified when updating to a new version.
|
||||
*
|
||||
* Non-standard LESS variables were added to allow usage of CSS variables. All of them hold a
|
||||
* value calculated by LESS functions. If not temporarily stored in another LESS variable,
|
||||
* they wouldn't be available to CSS variable usage.
|
||||
*
|
||||
* Latest state from version: v4.6.9
|
||||
*/
|
||||
|
||||
@fp-calendarBackground: #ffffff;
|
||||
@fp-calendarBorderColor: #e6e6e6;
|
||||
|
||||
@fp-arrowColor: fadeout(@fp-dayForeground, 40%); // Non-standard variable
|
||||
@fp-arrow_hover_color: #f64747;
|
||||
|
||||
@fp-monthForeground: fadeout(black, 10%);
|
||||
@fp-monthBackground: transparent;
|
||||
|
||||
@fp-weekdaysBackground: transparent;
|
||||
@fp-weekdaysForeground: fadeout(black, 46%);
|
||||
@fp-weekNumberForeground: fadeout(@fp-dayForeground, 70%); // Non-standard variable
|
||||
|
||||
@fp-dayForeground: #393939;
|
||||
@fp-dayHoverBackground: #e6e6e6;
|
||||
@fp-disabledDayForeground: fadeout(@fp-dayForeground, 90%); // Non-standard variable
|
||||
@fp-outsideRangeDayForeground: @fp-weekNumberForeground; // Non-standard variable
|
||||
@fp-selectedDayBackground: #569FF7;
|
||||
@fp-todayColor: #959ea9;
|
||||
|
||||
@fp-timeHoverBg: lighten(@fp-dayHoverBackground, 3); // Non-standard variable
|
||||
|
||||
@fp-invertedBg: black;
|
||||
@fp-hoverInvertedBg: fadeout(@fp-invertedBg, 95%); // Non-standard variable
|
||||
|
||||
@fp-numChooserSvgFillColor: fadeout(@fp-monthForeground, 50%); // Non-standard variable
|
||||
@fp-hoverNumChooserBg: fadeout(@fp-invertedBg, 90%); // Non-standard variable
|
||||
@fp-numChooserBorderColor: fadeout(@fp-dayForeground, 85%); // Non-standard variable
|
||||
|
||||
.icinga-datetime-picker {
|
||||
&.flatpickr-calendar {
|
||||
background: @fp-calendarBackground;
|
||||
background: var(--fp-calendarBackground, @fp-calendarBackground);
|
||||
box-shadow: 1px 0 0 @fp-calendarBorderColor,
|
||||
-1px 0 0 @fp-calendarBorderColor,
|
||||
0 1px 0 @fp-calendarBorderColor,
|
||||
0 -1px 0 @fp-calendarBorderColor,
|
||||
0 3px 13px fadeout(black, 92%);
|
||||
box-shadow: 1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor),
|
||||
-1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor),
|
||||
0 1px 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor),
|
||||
0 -1px 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor),
|
||||
0 3px 13px fadeout(black, 92%);
|
||||
}
|
||||
|
||||
&.flatpickr-calendar.arrowTop:before {
|
||||
border-bottom-color: @fp-calendarBorderColor;
|
||||
border-bottom-color: var(--fp-calendarBorderColor, @fp-calendarBorderColor);
|
||||
}
|
||||
&.flatpickr-calendar.arrowTop:after {
|
||||
border-bottom-color: @fp-calendarBackground;
|
||||
border-bottom-color: var(--fp-calendarBackground, @fp-calendarBackground);
|
||||
}
|
||||
&.flatpickr-calendar.arrowBottom:before {
|
||||
border-top-color: @fp-calendarBorderColor;
|
||||
border-top-color: var(--fp-calendarBorderColor, @fp-calendarBorderColor);
|
||||
}
|
||||
&.flatpickr-calendar.arrowBottom:after {
|
||||
border-top-color: @fp-calendarBackground;
|
||||
border-top-color: var(--fp-calendarBackground, @fp-calendarBackground);
|
||||
}
|
||||
|
||||
&.flatpickr-calendar.hasTime .flatpickr-time {
|
||||
border-top-color: @fp-calendarBorderColor;
|
||||
border-top-color: var(--fp-calendarBorderColor, @fp-calendarBorderColor);
|
||||
}
|
||||
|
||||
.dayContainer + .dayContainer {
|
||||
-webkit-box-shadow: -1px 0 0 @fp-calendarBorderColor;
|
||||
-webkit-box-shadow: -1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor);
|
||||
box-shadow: -1px 0 0 @fp-calendarBorderColor;
|
||||
box-shadow: -1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor);
|
||||
}
|
||||
|
||||
.flatpickr-day {
|
||||
color: @fp-dayForeground;
|
||||
color: var(--fp-dayForeground, @fp-dayForeground);
|
||||
}
|
||||
.flatpickr-day.today {
|
||||
border-color: @fp-todayColor;
|
||||
border-color: var(--fp-todayColor, @fp-todayColor);
|
||||
}
|
||||
.flatpickr-day.today:hover,
|
||||
.flatpickr-day.today:focus {
|
||||
border-color: @fp-todayColor;
|
||||
border-color: var(--fp-todayColor, @fp-todayColor);
|
||||
background: @fp-todayColor;
|
||||
background: var(--fp-todayColor, @fp-todayColor);
|
||||
color: @fp-calendarBackground;
|
||||
color: var(--fp-calendarBackground, @fp-calendarBackground);
|
||||
}
|
||||
.flatpickr-day.selected,
|
||||
.flatpickr-day.startRange,
|
||||
.flatpickr-day.endRange,
|
||||
.flatpickr-day.selected.inRange,
|
||||
.flatpickr-day.startRange.inRange,
|
||||
.flatpickr-day.endRange.inRange,
|
||||
.flatpickr-day.selected:focus,
|
||||
.flatpickr-day.startRange:focus,
|
||||
.flatpickr-day.endRange:focus,
|
||||
.flatpickr-day.selected:hover,
|
||||
.flatpickr-day.startRange:hover,
|
||||
.flatpickr-day.endRange:hover,
|
||||
.flatpickr-day.selected.prevMonthDay,
|
||||
.flatpickr-day.startRange.prevMonthDay,
|
||||
.flatpickr-day.endRange.prevMonthDay,
|
||||
.flatpickr-day.selected.nextMonthDay,
|
||||
.flatpickr-day.startRange.nextMonthDay,
|
||||
.flatpickr-day.endRange.nextMonthDay {
|
||||
color: @fp-calendarBackground;
|
||||
color: var(--fp-calendarBackground, @fp-calendarBackground);
|
||||
}
|
||||
.flatpickr-day.inRange,
|
||||
.flatpickr-day.prevMonthDay.inRange,
|
||||
.flatpickr-day.nextMonthDay.inRange,
|
||||
.flatpickr-day.today.inRange,
|
||||
.flatpickr-day.prevMonthDay.today.inRange,
|
||||
.flatpickr-day.nextMonthDay.today.inRange,
|
||||
.flatpickr-day:hover,
|
||||
.flatpickr-day.prevMonthDay:hover,
|
||||
.flatpickr-day.nextMonthDay:hover,
|
||||
.flatpickr-day:focus,
|
||||
.flatpickr-day.nextMonthDay:focus {
|
||||
background: @fp-dayHoverBackground;
|
||||
background: var(--fp-dayHoverBackground, @fp-dayHoverBackground);
|
||||
border-color: @fp-dayHoverBackground;
|
||||
border-color: var(--fp-dayHoverBackground, @fp-dayHoverBackground);
|
||||
}
|
||||
.flatpickr-day.inRange {
|
||||
-webkit-box-shadow: -5px 0 0 @fp-dayHoverBackground, 5px 0 0 @fp-dayHoverBackground;
|
||||
-webkit-box-shadow: -5px 0 0 var(--fp-dayHoverBackground, @fp-dayHoverBackground),
|
||||
5px 0 0 var(--fp-dayHoverBackground, @fp-dayHoverBackground);
|
||||
box-shadow: -5px 0 0 @fp-dayHoverBackground, 5px 0 0 @fp-dayHoverBackground;
|
||||
box-shadow: -5px 0 0 var(--fp-dayHoverBackground, @fp-dayHoverBackground),
|
||||
5px 0 0 var(--fp-dayHoverBackground, @fp-dayHoverBackground);
|
||||
}
|
||||
.flatpickr-day.prevMonthDay,
|
||||
.flatpickr-day.nextMonthDay,
|
||||
.flatpickr-day.notAllowed,
|
||||
.flatpickr-day.notAllowed.prevMonthDay,
|
||||
.flatpickr-day.notAllowed.nextMonthDay {
|
||||
color: @fp-outsideRangeDayForeground;
|
||||
color: var(--fp-outsideRangeDayForeground, @fp-outsideRangeDayForeground);
|
||||
}
|
||||
.flatpickr-day.flatpickr-disabled,
|
||||
.flatpickr-day.flatpickr-disabled:hover {
|
||||
color: @fp-disabledDayForeground;
|
||||
color: var(--fp-disabledDayForeground, @fp-disabledDayForeground);
|
||||
}
|
||||
.flatpickr-day.selected,
|
||||
.flatpickr-day.startRange,
|
||||
.flatpickr-day.endRange,
|
||||
.flatpickr-day.selected.inRange,
|
||||
.flatpickr-day.startRange.inRange,
|
||||
.flatpickr-day.endRange.inRange,
|
||||
.flatpickr-day.selected:focus,
|
||||
.flatpickr-day.startRange:focus,
|
||||
.flatpickr-day.endRange:focus,
|
||||
.flatpickr-day.selected:hover,
|
||||
.flatpickr-day.startRange:hover,
|
||||
.flatpickr-day.endRange:hover,
|
||||
.flatpickr-day.selected.prevMonthDay,
|
||||
.flatpickr-day.startRange.prevMonthDay,
|
||||
.flatpickr-day.endRange.prevMonthDay,
|
||||
.flatpickr-day.selected.nextMonthDay,
|
||||
.flatpickr-day.startRange.nextMonthDay,
|
||||
.flatpickr-day.endRange.nextMonthDay {
|
||||
background: @fp-selectedDayBackground;
|
||||
background: var(--fp-selectedDayBackground, @fp-selectedDayBackground);
|
||||
border-color: @fp-selectedDayBackground;
|
||||
border-color: var(--fp-selectedDayBackground, @fp-selectedDayBackground);
|
||||
}
|
||||
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),
|
||||
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),
|
||||
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) {
|
||||
-webkit-box-shadow: -10px 0 0 @fp-selectedDayBackground;
|
||||
-webkit-box-shadow: -10px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground);
|
||||
box-shadow: -10px 0 0 @fp-selectedDayBackground;
|
||||
box-shadow: -10px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground);
|
||||
}
|
||||
.flatpickr-day.week.selected {
|
||||
-webkit-box-shadow: -5px 0 0 @fp-selectedDayBackground, 5px 0 0 @fp-selectedDayBackground;
|
||||
-webkit-box-shadow: -5px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground),
|
||||
5px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground);
|
||||
box-shadow: -5px 0 0 @fp-selectedDayBackground, 5px 0 0 @fp-selectedDayBackground;
|
||||
box-shadow: -5px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground),
|
||||
5px 0 0 var(--fp-selectedDayBackground, @fp-selectedDayBackground);
|
||||
}
|
||||
|
||||
.flatpickr-weekwrapper .flatpickr-weeks {
|
||||
-webkit-box-shadow: 1px 0 0 @fp-calendarBorderColor;
|
||||
-webkit-box-shadow: 1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor);
|
||||
box-shadow: 1px 0 0 @fp-calendarBorderColor;
|
||||
box-shadow: 1px 0 0 var(--fp-calendarBorderColor, @fp-calendarBorderColor);
|
||||
}
|
||||
.flatpickr-weekwrapper span.flatpickr-day,
|
||||
.flatpickr-weekwrapper span.flatpickr-day:hover {
|
||||
color: @fp-weekNumberForeground;
|
||||
color: var(--fp-weekNumberForeground, @fp-weekNumberForeground);
|
||||
}
|
||||
.flatpickr-weekdays {
|
||||
background: @fp-weekdaysBackground;
|
||||
background: var(--fp-weekdaysBackground, @fp-weekdaysBackground);
|
||||
}
|
||||
span.flatpickr-weekday {
|
||||
background: @fp-monthBackground;
|
||||
background: var(--fp-monthBackground, @fp-monthBackground);
|
||||
color: @fp-weekdaysForeground;
|
||||
color: var(--fp-weekdaysForeground, @fp-weekdaysForeground);
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-month {
|
||||
background: @fp-monthBackground;
|
||||
background: var(--fp-monthBackground, @fp-monthBackground);
|
||||
color: @fp-monthForeground;
|
||||
color: var(--fp-monthForeground, @fp-monthForeground);
|
||||
fill: @fp-monthForeground;
|
||||
fill: var(--fp-monthForeground, @fp-monthForeground);
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month,
|
||||
.flatpickr-months .flatpickr-next-month {
|
||||
color: @fp-monthForeground;
|
||||
color: var(--fp-monthForeground, @fp-monthForeground);
|
||||
fill: @fp-monthForeground;
|
||||
fill: var(--fp-monthForeground, @fp-monthForeground);
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month:hover,
|
||||
.flatpickr-months .flatpickr-next-month:hover {
|
||||
color: @fp-todayColor;
|
||||
color: var(--fp-todayColor, @fp-todayColor);
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month:hover svg,
|
||||
.flatpickr-months .flatpickr-next-month:hover svg {
|
||||
fill: @fp-arrow_hover_color;
|
||||
fill: var(--fp-arrow_hover_color, @fp-arrow_hover_color);
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months {
|
||||
background: @fp-monthBackground;
|
||||
background: var(--fp-monthBackground, @fp-monthBackground);
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month {
|
||||
background-color: @fp-monthBackground;
|
||||
background-color: var(--fp-monthBackground, @fp-monthBackground);
|
||||
}
|
||||
.flatpickr-current-month .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: @fp-monthForeground;
|
||||
border-bottom-color: var(--fp-monthForeground, @fp-monthForeground);
|
||||
}
|
||||
.flatpickr-current-month .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: @fp-monthForeground;
|
||||
border-top-color: var(--fp-monthForeground, @fp-monthForeground);
|
||||
}
|
||||
|
||||
.numInputWrapper span {
|
||||
border-color: @fp-numChooserBorderColor;
|
||||
border-color: var(--fp-numChooserBorderColor, @fp-numChooserBorderColor);
|
||||
}
|
||||
.numInputWrapper span:hover {
|
||||
background: @fp-hoverNumChooserBg;
|
||||
background: var(--fp-hoverNumChooserBg, @fp-hoverNumChooserBg);
|
||||
}
|
||||
.numInputWrapper span:active {
|
||||
background: @fp-hoverNumChooserBg;
|
||||
background: var(--fp-hoverNumChooserBg, @fp-hoverNumChooserBg);
|
||||
}
|
||||
.numInputWrapper span svg path {
|
||||
fill: @fp-numChooserSvgFillColor;
|
||||
fill: var(--fp-numChooserSvgFillColor, @fp-numChooserSvgFillColor);
|
||||
}
|
||||
.numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: @fp-arrowColor;
|
||||
border-bottom-color: var(--fp-arrowColor, @fp-arrowColor);
|
||||
}
|
||||
.numInputWrapper span.arrowDown:after {
|
||||
border-top-color: @fp-arrowColor;
|
||||
border-top-color: var(--fp-arrowColor, @fp-arrowColor);
|
||||
}
|
||||
.numInputWrapper:hover {
|
||||
background: @fp-hoverInvertedBg;
|
||||
background: var(--fp-hoverInvertedBg, @fp-hoverInvertedBg);
|
||||
}
|
||||
.flatpickr-current-month span.cur-month:hover {
|
||||
background: @fp-hoverInvertedBg;
|
||||
background: var(--fp-hoverInvertedBg, @fp-hoverInvertedBg);
|
||||
}
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
|
||||
background: @fp-hoverInvertedBg;
|
||||
background: var(--fp-hoverInvertedBg, @fp-hoverInvertedBg);
|
||||
}
|
||||
|
||||
.flatpickr-time input:hover,
|
||||
.flatpickr-time .flatpickr-am-pm:hover,
|
||||
.flatpickr-time input:focus,
|
||||
.flatpickr-time .flatpickr-am-pm:focus {
|
||||
background: @fp-timeHoverBg;
|
||||
background: var(--fp-timeHoverBg, @fp-timeHoverBg);
|
||||
}
|
||||
.flatpickr-time .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: @fp-dayForeground;
|
||||
border-bottom-color: var(--fp-dayForeground, @fp-dayForeground);
|
||||
}
|
||||
.flatpickr-time .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: @fp-dayForeground;
|
||||
border-top-color: var(--fp-dayForeground, @fp-dayForeground);
|
||||
}
|
||||
.flatpickr-time input {
|
||||
color: @fp-dayForeground;
|
||||
color: var(--fp-dayForeground, @fp-dayForeground);
|
||||
}
|
||||
.flatpickr-time .flatpickr-time-separator,
|
||||
.flatpickr-time .flatpickr-am-pm {
|
||||
color: @fp-dayForeground;
|
||||
color: var(--fp-dayForeground, @fp-dayForeground);
|
||||
}
|
||||
}
|
17
asset/css/vertical-key-value.less
Normal file
17
asset/css/vertical-key-value.less
Normal file
@ -0,0 +1,17 @@
|
||||
.vertical-key-value {
|
||||
display: inline-block;
|
||||
line-height: .75;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
.key {
|
||||
font-size: 10/12em;
|
||||
color: var(--default-text-color-light, @default-text-color-light);
|
||||
}
|
||||
|
||||
.value {
|
||||
color: var(--default-text-color, @default-text-color);
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
161
asset/js/notjQuery.js
Normal file
161
asset/js/notjQuery.js
Normal file
@ -0,0 +1,161 @@
|
||||
define(function () {
|
||||
|
||||
"use strict";
|
||||
|
||||
class notjQuery {
|
||||
/**
|
||||
* Create a new notjQuery object
|
||||
*
|
||||
* @param {Element} element
|
||||
*/
|
||||
constructor(element) {
|
||||
if (! element) {
|
||||
throw new Error("Can't create a notjQuery object for `" + element + "`");
|
||||
}
|
||||
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event listener to the element
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {string} selector
|
||||
* @param {function} handler
|
||||
* @param {object} context
|
||||
*/
|
||||
on(type, selector, handler, context = null) {
|
||||
if (typeof selector === 'function') {
|
||||
context = handler;
|
||||
handler = selector;
|
||||
selector = null;
|
||||
}
|
||||
|
||||
if (selector === null) {
|
||||
this.element.addEventListener(type, e => {
|
||||
if (type === 'focusin' && e.target.receivesCustomFocus) {
|
||||
// Ignore native focus event if a custom one follows
|
||||
if (e instanceof FocusEvent) {
|
||||
delete e.target.receivesCustomFocus;
|
||||
e.stopImmediatePropagation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (context === null) {
|
||||
handler.apply(e.currentTarget, [e]);
|
||||
} else {
|
||||
handler.apply(context, [e]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.element.addEventListener(type, e => {
|
||||
if (type === 'focusin' && e.target.receivesCustomFocus) {
|
||||
// Ignore native focus event if a custom one follows
|
||||
if (e instanceof FocusEvent) {
|
||||
delete e.target.receivesCustomFocus;
|
||||
e.stopImmediatePropagation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(e, 'currentTarget', { value: e.currentTarget, writable: true });
|
||||
|
||||
let currentParent = e.currentTarget.parentNode;
|
||||
for (let target = e.target; target && target !== currentParent; target = target.parentNode) {
|
||||
if (target.matches(selector)) {
|
||||
e.currentTarget = target;
|
||||
if (context === null) {
|
||||
handler.apply(target, [e]);
|
||||
} else {
|
||||
handler.apply(context, [e]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a custom event on the element, asynchronously
|
||||
*
|
||||
* The event will bubble and is not cancelable.
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {{}} detail
|
||||
*/
|
||||
trigger(type, detail = null) {
|
||||
setTimeout(() => {
|
||||
this.element.dispatchEvent(new CustomEvent(type, {
|
||||
cancelable: true, // TODO: this should depend on whether it's a native or custom event
|
||||
bubbles: true,
|
||||
detail: detail
|
||||
}));
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus the element
|
||||
*
|
||||
* Any other option than `preventScroll` is used as `event.detail`.
|
||||
*
|
||||
* @param {{}} options
|
||||
*/
|
||||
focus(options = {}) {
|
||||
let { preventScroll = false, ...data } = options;
|
||||
|
||||
const hasData = Object.keys(data).length > 0;
|
||||
if (hasData) {
|
||||
this.element.receivesCustomFocus = true;
|
||||
}
|
||||
|
||||
// Put separately on the event loop because focus() forces layout.
|
||||
setTimeout(() => this.element.focus({ preventScroll: preventScroll }), 0);
|
||||
|
||||
if (hasData) {
|
||||
this.trigger('focusin', data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the element string as DOM Element
|
||||
*
|
||||
* @param {string} html
|
||||
* @return {Element}
|
||||
*/
|
||||
static render(html) {
|
||||
if (typeof html !== 'string') {
|
||||
throw new Error("Can\'t render `" + html + "`");
|
||||
}
|
||||
|
||||
let template = document.createElement('template');
|
||||
template.innerHTML = html;
|
||||
return template.content.firstChild;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a notjQuery object for the given element
|
||||
*
|
||||
* @param {Element} element
|
||||
* @return {notjQuery}
|
||||
*/
|
||||
let factory = function (element) {
|
||||
return new notjQuery(element);
|
||||
}
|
||||
|
||||
// Define the static methods on the factory
|
||||
for (let name of Object.getOwnPropertyNames(notjQuery)) {
|
||||
if (['length', 'prototype', 'name'].includes(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object.defineProperty(factory, name, {
|
||||
value: notjQuery[name]
|
||||
});
|
||||
}
|
||||
|
||||
return factory;
|
||||
});
|
3721
asset/js/vendor/Sortable.js
vendored
Normal file
3721
asset/js/vendor/Sortable.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
asset/js/vendor/Sortable.min.js
vendored
Normal file
2
asset/js/vendor/Sortable.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2705
asset/js/vendor/flatpickr.js
vendored
Normal file
2705
asset/js/vendor/flatpickr.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
asset/js/vendor/flatpickr.min.js
vendored
Normal file
2
asset/js/vendor/flatpickr.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
62
asset/js/vendor/flatpickr/l10n/ar.js
vendored
Normal file
62
asset/js/vendor/flatpickr/l10n/ar.js
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ar = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
|
||||
? window.flatpickr
|
||||
: {
|
||||
l10ns: {},
|
||||
};
|
||||
var Arabic = {
|
||||
weekdays: {
|
||||
shorthand: ["أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
|
||||
longhand: [
|
||||
"الأحد",
|
||||
"الاثنين",
|
||||
"الثلاثاء",
|
||||
"الأربعاء",
|
||||
"الخميس",
|
||||
"الجمعة",
|
||||
"السبت",
|
||||
],
|
||||
},
|
||||
months: {
|
||||
shorthand: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
|
||||
longhand: [
|
||||
"يناير",
|
||||
"فبراير",
|
||||
"مارس",
|
||||
"أبريل",
|
||||
"مايو",
|
||||
"يونيو",
|
||||
"يوليو",
|
||||
"أغسطس",
|
||||
"سبتمبر",
|
||||
"أكتوبر",
|
||||
"نوفمبر",
|
||||
"ديسمبر",
|
||||
],
|
||||
},
|
||||
firstDayOfWeek: 6,
|
||||
rangeSeparator: " إلى ",
|
||||
weekAbbreviation: "Wk",
|
||||
scrollTitle: "قم بالتمرير للزيادة",
|
||||
toggleTitle: "اضغط للتبديل",
|
||||
amPM: ["ص", "م"],
|
||||
yearAriaLabel: "سنة",
|
||||
monthAriaLabel: "شهر",
|
||||
hourAriaLabel: "ساعة",
|
||||
minuteAriaLabel: "دقيقة",
|
||||
time_24hr: false,
|
||||
};
|
||||
fp.l10ns.ar = Arabic;
|
||||
var ar = fp.l10ns;
|
||||
|
||||
exports.Arabic = Arabic;
|
||||
exports.default = ar;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
70
asset/js/vendor/flatpickr/l10n/de.js
vendored
Normal file
70
asset/js/vendor/flatpickr/l10n/de.js
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.de = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
|
||||
? window.flatpickr
|
||||
: {
|
||||
l10ns: {},
|
||||
};
|
||||
var German = {
|
||||
weekdays: {
|
||||
shorthand: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
|
||||
longhand: [
|
||||
"Sonntag",
|
||||
"Montag",
|
||||
"Dienstag",
|
||||
"Mittwoch",
|
||||
"Donnerstag",
|
||||
"Freitag",
|
||||
"Samstag",
|
||||
],
|
||||
},
|
||||
months: {
|
||||
shorthand: [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mär",
|
||||
"Apr",
|
||||
"Mai",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Okt",
|
||||
"Nov",
|
||||
"Dez",
|
||||
],
|
||||
longhand: [
|
||||
"Januar",
|
||||
"Februar",
|
||||
"März",
|
||||
"April",
|
||||
"Mai",
|
||||
"Juni",
|
||||
"Juli",
|
||||
"August",
|
||||
"September",
|
||||
"Oktober",
|
||||
"November",
|
||||
"Dezember",
|
||||
],
|
||||
},
|
||||
firstDayOfWeek: 1,
|
||||
weekAbbreviation: "KW",
|
||||
rangeSeparator: " bis ",
|
||||
scrollTitle: "Zum Ändern scrollen",
|
||||
toggleTitle: "Zum Umschalten klicken",
|
||||
time_24hr: true,
|
||||
};
|
||||
fp.l10ns.de = German;
|
||||
var de = fp.l10ns;
|
||||
|
||||
exports.German = German;
|
||||
exports.default = de;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
70
asset/js/vendor/flatpickr/l10n/es.js
vendored
Normal file
70
asset/js/vendor/flatpickr/l10n/es.js
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.es = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
|
||||
? window.flatpickr
|
||||
: {
|
||||
l10ns: {},
|
||||
};
|
||||
var Spanish = {
|
||||
weekdays: {
|
||||
shorthand: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"],
|
||||
longhand: [
|
||||
"Domingo",
|
||||
"Lunes",
|
||||
"Martes",
|
||||
"Miércoles",
|
||||
"Jueves",
|
||||
"Viernes",
|
||||
"Sábado",
|
||||
],
|
||||
},
|
||||
months: {
|
||||
shorthand: [
|
||||
"Ene",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Abr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Ago",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dic",
|
||||
],
|
||||
longhand: [
|
||||
"Enero",
|
||||
"Febrero",
|
||||
"Marzo",
|
||||
"Abril",
|
||||
"Mayo",
|
||||
"Junio",
|
||||
"Julio",
|
||||
"Agosto",
|
||||
"Septiembre",
|
||||
"Octubre",
|
||||
"Noviembre",
|
||||
"Diciembre",
|
||||
],
|
||||
},
|
||||
ordinal: function () {
|
||||
return "º";
|
||||
},
|
||||
firstDayOfWeek: 1,
|
||||
rangeSeparator: " a ",
|
||||
time_24hr: true,
|
||||
};
|
||||
fp.l10ns.es = Spanish;
|
||||
var es = fp.l10ns;
|
||||
|
||||
exports.Spanish = Spanish;
|
||||
exports.default = es;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
69
asset/js/vendor/flatpickr/l10n/fi.js
vendored
Normal file
69
asset/js/vendor/flatpickr/l10n/fi.js
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.fi = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
|
||||
? window.flatpickr
|
||||
: {
|
||||
l10ns: {},
|
||||
};
|
||||
var Finnish = {
|
||||
firstDayOfWeek: 1,
|
||||
weekdays: {
|
||||
shorthand: ["su", "ma", "ti", "ke", "to", "pe", "la"],
|
||||
longhand: [
|
||||
"sunnuntai",
|
||||
"maanantai",
|
||||
"tiistai",
|
||||
"keskiviikko",
|
||||
"torstai",
|
||||
"perjantai",
|
||||
"lauantai",
|
||||
],
|
||||
},
|
||||
months: {
|
||||
shorthand: [
|
||||
"tammi",
|
||||
"helmi",
|
||||
"maalis",
|
||||
"huhti",
|
||||
"touko",
|
||||
"kesä",
|
||||
"heinä",
|
||||
"elo",
|
||||
"syys",
|
||||
"loka",
|
||||
"marras",
|
||||
"joulu",
|
||||
],
|
||||
longhand: [
|
||||
"tammikuu",
|
||||
"helmikuu",
|
||||
"maaliskuu",
|
||||
"huhtikuu",
|
||||
"toukokuu",
|
||||
"kesäkuu",
|
||||
"heinäkuu",
|
||||
"elokuu",
|
||||
"syyskuu",
|
||||
"lokakuu",
|
||||
"marraskuu",
|
||||
"joulukuu",
|
||||
],
|
||||
},
|
||||
ordinal: function () {
|
||||
return ".";
|
||||
},
|
||||
time_24hr: true,
|
||||
};
|
||||
fp.l10ns.fi = Finnish;
|
||||
var fi = fp.l10ns;
|
||||
|
||||
exports.Finnish = Finnish;
|
||||
exports.default = fi;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
75
asset/js/vendor/flatpickr/l10n/fr.js
vendored
Normal file
75
asset/js/vendor/flatpickr/l10n/fr.js
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.fr = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
|
||||
? window.flatpickr
|
||||
: {
|
||||
l10ns: {},
|
||||
};
|
||||
var French = {
|
||||
firstDayOfWeek: 1,
|
||||
weekdays: {
|
||||
shorthand: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"],
|
||||
longhand: [
|
||||
"dimanche",
|
||||
"lundi",
|
||||
"mardi",
|
||||
"mercredi",
|
||||
"jeudi",
|
||||
"vendredi",
|
||||
"samedi",
|
||||
],
|
||||
},
|
||||
months: {
|
||||
shorthand: [
|
||||
"janv",
|
||||
"févr",
|
||||
"mars",
|
||||
"avr",
|
||||
"mai",
|
||||
"juin",
|
||||
"juil",
|
||||
"août",
|
||||
"sept",
|
||||
"oct",
|
||||
"nov",
|
||||
"déc",
|
||||
],
|
||||
longhand: [
|
||||
"janvier",
|
||||
"février",
|
||||
"mars",
|
||||
"avril",
|
||||
"mai",
|
||||
"juin",
|
||||
"juillet",
|
||||
"août",
|
||||
"septembre",
|
||||
"octobre",
|
||||
"novembre",
|
||||
"décembre",
|
||||
],
|
||||
},
|
||||
ordinal: function (nth) {
|
||||
if (nth > 1)
|
||||
return "";
|
||||
return "er";
|
||||
},
|
||||
rangeSeparator: " au ",
|
||||
weekAbbreviation: "Sem",
|
||||
scrollTitle: "Défiler pour augmenter la valeur",
|
||||
toggleTitle: "Cliquer pour basculer",
|
||||
time_24hr: true,
|
||||
};
|
||||
fp.l10ns.fr = French;
|
||||
var fr = fp.l10ns;
|
||||
|
||||
exports.French = French;
|
||||
exports.default = fr;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
71
asset/js/vendor/flatpickr/l10n/it.js
vendored
Normal file
71
asset/js/vendor/flatpickr/l10n/it.js
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.it = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
|
||||
? window.flatpickr
|
||||
: {
|
||||
l10ns: {},
|
||||
};
|
||||
var Italian = {
|
||||
weekdays: {
|
||||
shorthand: ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"],
|
||||
longhand: [
|
||||
"Domenica",
|
||||
"Lunedì",
|
||||
"Martedì",
|
||||
"Mercoledì",
|
||||
"Giovedì",
|
||||
"Venerdì",
|
||||
"Sabato",
|
||||
],
|
||||
},
|
||||
months: {
|
||||
shorthand: [
|
||||
"Gen",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"Mag",
|
||||
"Giu",
|
||||
"Lug",
|
||||
"Ago",
|
||||
"Set",
|
||||
"Ott",
|
||||
"Nov",
|
||||
"Dic",
|
||||
],
|
||||
longhand: [
|
||||
"Gennaio",
|
||||
"Febbraio",
|
||||
"Marzo",
|
||||
"Aprile",
|
||||
"Maggio",
|
||||
"Giugno",
|
||||
"Luglio",
|
||||
"Agosto",
|
||||
"Settembre",
|
||||
"Ottobre",
|
||||
"Novembre",
|
||||
"Dicembre",
|
||||
],
|
||||
},
|
||||
firstDayOfWeek: 1,
|
||||
ordinal: function () { return "°"; },
|
||||
rangeSeparator: " al ",
|
||||
weekAbbreviation: "Se",
|
||||
scrollTitle: "Scrolla per aumentare",
|
||||
toggleTitle: "Clicca per cambiare",
|
||||
time_24hr: true,
|
||||
};
|
||||
fp.l10ns.it = Italian;
|
||||
var it = fp.l10ns;
|
||||
|
||||
exports.Italian = Italian;
|
||||
exports.default = it;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
71
asset/js/vendor/flatpickr/l10n/ja.js
vendored
Normal file
71
asset/js/vendor/flatpickr/l10n/ja.js
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ja = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
|
||||
? window.flatpickr
|
||||
: {
|
||||
l10ns: {},
|
||||
};
|
||||
var Japanese = {
|
||||
weekdays: {
|
||||
shorthand: ["日", "月", "火", "水", "木", "金", "土"],
|
||||
longhand: [
|
||||
"日曜日",
|
||||
"月曜日",
|
||||
"火曜日",
|
||||
"水曜日",
|
||||
"木曜日",
|
||||
"金曜日",
|
||||
"土曜日",
|
||||
],
|
||||
},
|
||||
months: {
|
||||
shorthand: [
|
||||
"1月",
|
||||
"2月",
|
||||
"3月",
|
||||
"4月",
|
||||
"5月",
|
||||
"6月",
|
||||
"7月",
|
||||
"8月",
|
||||
"9月",
|
||||
"10月",
|
||||
"11月",
|
||||
"12月",
|
||||
],
|
||||
longhand: [
|
||||
"1月",
|
||||
"2月",
|
||||
"3月",
|
||||
"4月",
|
||||
"5月",
|
||||
"6月",
|
||||
"7月",
|
||||
"8月",
|
||||
"9月",
|
||||
"10月",
|
||||
"11月",
|
||||
"12月",
|
||||
],
|
||||
},
|
||||
time_24hr: true,
|
||||
rangeSeparator: " から ",
|
||||
monthAriaLabel: "月",
|
||||
amPM: ["午前", "午後"],
|
||||
yearAriaLabel: "年",
|
||||
hourAriaLabel: "時間",
|
||||
minuteAriaLabel: "分",
|
||||
};
|
||||
fp.l10ns.ja = Japanese;
|
||||
var ja = fp.l10ns;
|
||||
|
||||
exports.Japanese = Japanese;
|
||||
exports.default = ja;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
66
asset/js/vendor/flatpickr/l10n/pt.js
vendored
Normal file
66
asset/js/vendor/flatpickr/l10n/pt.js
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.pt = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
|
||||
? window.flatpickr
|
||||
: {
|
||||
l10ns: {},
|
||||
};
|
||||
var Portuguese = {
|
||||
weekdays: {
|
||||
shorthand: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
|
||||
longhand: [
|
||||
"Domingo",
|
||||
"Segunda-feira",
|
||||
"Terça-feira",
|
||||
"Quarta-feira",
|
||||
"Quinta-feira",
|
||||
"Sexta-feira",
|
||||
"Sábado",
|
||||
],
|
||||
},
|
||||
months: {
|
||||
shorthand: [
|
||||
"Jan",
|
||||
"Fev",
|
||||
"Mar",
|
||||
"Abr",
|
||||
"Mai",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Ago",
|
||||
"Set",
|
||||
"Out",
|
||||
"Nov",
|
||||
"Dez",
|
||||
],
|
||||
longhand: [
|
||||
"Janeiro",
|
||||
"Fevereiro",
|
||||
"Março",
|
||||
"Abril",
|
||||
"Maio",
|
||||
"Junho",
|
||||
"Julho",
|
||||
"Agosto",
|
||||
"Setembro",
|
||||
"Outubro",
|
||||
"Novembro",
|
||||
"Dezembro",
|
||||
],
|
||||
},
|
||||
rangeSeparator: " até ",
|
||||
time_24hr: true,
|
||||
};
|
||||
fp.l10ns.pt = Portuguese;
|
||||
var pt = fp.l10ns;
|
||||
|
||||
exports.Portuguese = Portuguese;
|
||||
exports.default = pt;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
75
asset/js/vendor/flatpickr/l10n/ru.js
vendored
Normal file
75
asset/js/vendor/flatpickr/l10n/ru.js
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ru = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
|
||||
? window.flatpickr
|
||||
: {
|
||||
l10ns: {},
|
||||
};
|
||||
var Russian = {
|
||||
weekdays: {
|
||||
shorthand: ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"],
|
||||
longhand: [
|
||||
"Воскресенье",
|
||||
"Понедельник",
|
||||
"Вторник",
|
||||
"Среда",
|
||||
"Четверг",
|
||||
"Пятница",
|
||||
"Суббота",
|
||||
],
|
||||
},
|
||||
months: {
|
||||
shorthand: [
|
||||
"Янв",
|
||||
"Фев",
|
||||
"Март",
|
||||
"Апр",
|
||||
"Май",
|
||||
"Июнь",
|
||||
"Июль",
|
||||
"Авг",
|
||||
"Сен",
|
||||
"Окт",
|
||||
"Ноя",
|
||||
"Дек",
|
||||
],
|
||||
longhand: [
|
||||
"Январь",
|
||||
"Февраль",
|
||||
"Март",
|
||||
"Апрель",
|
||||
"Май",
|
||||
"Июнь",
|
||||
"Июль",
|
||||
"Август",
|
||||
"Сентябрь",
|
||||
"Октябрь",
|
||||
"Ноябрь",
|
||||
"Декабрь",
|
||||
],
|
||||
},
|
||||
firstDayOfWeek: 1,
|
||||
ordinal: function () {
|
||||
return "";
|
||||
},
|
||||
rangeSeparator: " — ",
|
||||
weekAbbreviation: "Нед.",
|
||||
scrollTitle: "Прокрутите для увеличения",
|
||||
toggleTitle: "Нажмите для переключения",
|
||||
amPM: ["ДП", "ПП"],
|
||||
yearAriaLabel: "Год",
|
||||
time_24hr: true,
|
||||
};
|
||||
fp.l10ns.ru = Russian;
|
||||
var ru = fp.l10ns;
|
||||
|
||||
exports.Russian = Russian;
|
||||
exports.default = ru;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
66
asset/js/vendor/flatpickr/l10n/uk.js
vendored
Normal file
66
asset/js/vendor/flatpickr/l10n/uk.js
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.icinga ? define(["exports"], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.uk = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var fp = typeof window !== "undefined" && window.flatpickr !== undefined
|
||||
? window.flatpickr
|
||||
: {
|
||||
l10ns: {},
|
||||
};
|
||||
var Ukrainian = {
|
||||
firstDayOfWeek: 1,
|
||||
weekdays: {
|
||||
shorthand: ["Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"],
|
||||
longhand: [
|
||||
"Неділя",
|
||||
"Понеділок",
|
||||
"Вівторок",
|
||||
"Середа",
|
||||
"Четвер",
|
||||
"П'ятниця",
|
||||
"Субота",
|
||||
],
|
||||
},
|
||||
months: {
|
||||
shorthand: [
|
||||
"Січ",
|
||||
"Лют",
|
||||
"Бер",
|
||||
"Кві",
|
||||
"Тра",
|
||||
"Чер",
|
||||
"Лип",
|
||||
"Сер",
|
||||
"Вер",
|
||||
"Жов",
|
||||
"Лис",
|
||||
"Гру",
|
||||
],
|
||||
longhand: [
|
||||
"Січень",
|
||||
"Лютий",
|
||||
"Березень",
|
||||
"Квітень",
|
||||
"Травень",
|
||||
"Червень",
|
||||
"Липень",
|
||||
"Серпень",
|
||||
"Вересень",
|
||||
"Жовтень",
|
||||
"Листопад",
|
||||
"Грудень",
|
||||
],
|
||||
},
|
||||
time_24hr: true,
|
||||
};
|
||||
fp.l10ns.uk = Ukrainian;
|
||||
var uk = fp.l10ns;
|
||||
|
||||
exports.Ukrainian = Ukrainian;
|
||||
exports.default = uk;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
899
asset/js/widget/BaseInput.js
Normal file
899
asset/js/widget/BaseInput.js
Normal file
@ -0,0 +1,899 @@
|
||||
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;
|
||||
}
|
||||
|
||||
if (label.title) {
|
||||
termData['title'] = label.title;
|
||||
}
|
||||
|
||||
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, force = false) {
|
||||
let termIndex = input.parentNode.dataset.index;
|
||||
let termData = this.readFullTerm(input, termIndex);
|
||||
|
||||
// Only save if something has changed, unless forced
|
||||
if (termData === false) {
|
||||
console.warn('[BaseInput] Input is empty, cannot save');
|
||||
} else if (force || this.usedTerms[termIndex].label !== termData.label) {
|
||||
let oldTermData = this.usedTerms[termIndex];
|
||||
this.usedTerms[termIndex] = termData;
|
||||
this.updateTermData(termData, input);
|
||||
|
||||
return oldTermData;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
updateTermData(termData, input) {
|
||||
let label = input.parentNode;
|
||||
label.dataset.label = termData.label;
|
||||
|
||||
if (!! termData.search || termData.search === '') {
|
||||
label.dataset.search = termData.search;
|
||||
}
|
||||
|
||||
if (!! termData.title) {
|
||||
label.title = termData.title;
|
||||
} else {
|
||||
label.title = '';
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (termData.title) {
|
||||
label.title = termData.title;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (changeType === 'save') {
|
||||
// Replace old term data with the new one, as required by the backend
|
||||
for (const termIndex of Object.keys(changedTerms)) {
|
||||
changedTerms[termIndex] = this.usedTerms[termIndex];
|
||||
}
|
||||
}
|
||||
|
||||
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, false, true) });
|
||||
} 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) {
|
||||
if (this.readPartialTerm(input)) {
|
||||
this.saveTerm(input, false);
|
||||
} else {
|
||||
this.removeTerm(input.parentNode, 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 we 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 termIndex = Number(input.parentNode.dataset.index);
|
||||
if (this.readPartialTerm(input)) {
|
||||
let previousTerm = this.saveTerm(input);
|
||||
if (previousTerm !== false) {
|
||||
this.autoSubmit(input, 'save', { [termIndex]: previousTerm });
|
||||
}
|
||||
} else {
|
||||
this.autoSubmit(input, 'remove', { [termIndex]: this.removeTerm(input.parentNode) });
|
||||
}
|
||||
}
|
||||
}, 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;
|
||||
});
|
523
asset/js/widget/Completer.js
Normal file
523
asset/js/widget/Completer.js
Normal file
@ -0,0 +1,523 @@
|
||||
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];
|
||||
} else if (name === 'title') {
|
||||
input.title = 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;
|
||||
});
|
1557
asset/js/widget/FilterInput.js
Normal file
1557
asset/js/widget/FilterInput.js
Normal file
File diff suppressed because it is too large
Load Diff
81
asset/js/widget/SearchBar.js
Normal file
81
asset/js/widget/SearchBar.js
Normal file
@ -0,0 +1,81 @@
|
||||
define(["../notjQuery"], function ($) {
|
||||
|
||||
"use strict";
|
||||
|
||||
class SearchBar {
|
||||
constructor(form) {
|
||||
this.form = form;
|
||||
this.filterInput = null;
|
||||
}
|
||||
|
||||
bind() {
|
||||
$(this.form.parentNode).on('click', '[data-search-editor-url]', this.onOpenerClick, this);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
refresh(form) {
|
||||
if (form === this.form) {
|
||||
// If the DOM node is still the same, nothing has changed
|
||||
return;
|
||||
}
|
||||
|
||||
this.form = form;
|
||||
this.bind();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.form = null;
|
||||
this.filterInput = null;
|
||||
}
|
||||
|
||||
setFilterInput(filterInput) {
|
||||
this.filterInput = filterInput;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
onOpenerClick(event) {
|
||||
let opener = event.currentTarget;
|
||||
let editorUrl = opener.dataset.searchEditorUrl;
|
||||
let filterQueryString = this.filterInput.getQueryString();
|
||||
let layout = document.getElementById('layout');
|
||||
|
||||
editorUrl += (editorUrl.indexOf('?') > -1 ? '&' : '?') + filterQueryString;
|
||||
|
||||
// Disable pointer events to block further function calls
|
||||
opener.style.pointerEvents = 'none';
|
||||
|
||||
let observer = new MutationObserver((mutations) => {
|
||||
for (let mutation of mutations) {
|
||||
if (mutation.type === 'childList') {
|
||||
mutation.removedNodes.forEach((node) => {
|
||||
// Remove the pointerEvent none style to make the button clickable again
|
||||
// after the modal has been removed
|
||||
if (node.id === 'modal') {
|
||||
opener.style.pointerEvents = '';
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(layout, {childList: true});
|
||||
|
||||
// The search editor should open in a modal. We simulate a click on an anchor
|
||||
// appropriately prepared so that Icinga Web 2 will handle it natively.
|
||||
let a = document.createElement('a');
|
||||
a.classList.add('modal-opener');
|
||||
a.href = editorUrl;
|
||||
a.dataset.noIcingaAjax = '';
|
||||
a.dataset.icingaModal = '';
|
||||
|
||||
opener.parentNode.insertBefore(a, opener.nextSibling);
|
||||
a.click();
|
||||
a.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return SearchBar;
|
||||
});
|
79
asset/js/widget/SearchEditor.js
Normal file
79
asset/js/widget/SearchEditor.js
Normal file
@ -0,0 +1,79 @@
|
||||
define(["../notjQuery", "../vendor/Sortable"], function ($, Sortable) {
|
||||
|
||||
"use strict";
|
||||
|
||||
class SearchEditor {
|
||||
constructor(form) {
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
bind() {
|
||||
$(this.form).on('end', this.onRuleDropped, this);
|
||||
|
||||
this.form.querySelectorAll('ol').forEach(sortable => {
|
||||
let options = {
|
||||
scroll: true,
|
||||
group: 'rules',
|
||||
direction: 'vertical',
|
||||
invertSwap: true,
|
||||
handle: '.drag-initiator'
|
||||
};
|
||||
|
||||
Sortable.create(sortable, options);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
refresh(form) {
|
||||
if (form === this.form) {
|
||||
// If the DOM node is still the same, nothing has changed
|
||||
return;
|
||||
}
|
||||
|
||||
this.form = form;
|
||||
this.bind();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.form = null;
|
||||
this.filterInput = null;
|
||||
}
|
||||
|
||||
onRuleDropped(event) {
|
||||
if (event.to === event.from && event.newIndex === event.oldIndex) {
|
||||
// The user dropped the rule at its previous position
|
||||
return;
|
||||
}
|
||||
|
||||
let placement = 'before';
|
||||
let neighbour = event.to.querySelector(':scope > :nth-child(' + (event.newIndex + 2) + ')');
|
||||
if (! neighbour) {
|
||||
// User dropped the rule at the end of a group
|
||||
placement = 'after';
|
||||
neighbour = event.to.querySelector(':scope > :nth-child(' + event.newIndex + ')')
|
||||
if (! neighbour) {
|
||||
// User dropped the rule into an empty group
|
||||
placement = 'to';
|
||||
neighbour = event.to.closest('[id]');
|
||||
}
|
||||
}
|
||||
|
||||
// It's a submit element, the very first one, otherwise Icinga Web 2 sends another "structural-change"
|
||||
this.form.insertBefore(
|
||||
$.render(
|
||||
'<input type="hidden" name="structural-change[1]" value="' + placement + ':' + neighbour.id + '">'
|
||||
),
|
||||
this.form.firstChild
|
||||
);
|
||||
this.form.insertBefore(
|
||||
$.render('<input type="submit" name="structural-change[0]" value="move-rule:' + event.item.id + '">'),
|
||||
this.form.firstChild
|
||||
);
|
||||
|
||||
$(this.form).trigger('submit');
|
||||
}
|
||||
}
|
||||
|
||||
return SearchEditor;
|
||||
});
|
128
asset/js/widget/TermInput.js
Normal file
128
asset/js/widget/TermInput.js
Normal file
@ -0,0 +1,128 @@
|
||||
define(["BaseInput"], function (BaseInput) {
|
||||
|
||||
"use strict";
|
||||
|
||||
class TermInput extends BaseInput {
|
||||
constructor(input) {
|
||||
super(input);
|
||||
|
||||
this.separator = ' ';
|
||||
this.ignoreSpaceUntil = null;
|
||||
this.ignoreSpaceSince = null;
|
||||
}
|
||||
|
||||
reset() {
|
||||
super.reset();
|
||||
|
||||
this.ignoreSpaceUntil = null;
|
||||
this.ignoreSpaceSince = null;
|
||||
}
|
||||
|
||||
writePartialTerm(value, input) {
|
||||
if (this.ignoreSpaceUntil !== null && this.ignoreSpaceSince === 0) {
|
||||
value = this.ignoreSpaceUntil + value;
|
||||
}
|
||||
|
||||
super.writePartialTerm(value, input);
|
||||
}
|
||||
|
||||
readFullTerm(input, termIndex = null) {
|
||||
let termData = super.readFullTerm(input, termIndex);
|
||||
if (this.ignoreSpaceUntil !== null && termData.label[this.ignoreSpaceSince] === this.ignoreSpaceUntil) {
|
||||
if (termData.label.length - 1 === this.ignoreSpaceSince
|
||||
|| termData.label.slice(-1) !== this.ignoreSpaceUntil
|
||||
|| (this.ignoreSpaceSince === 0 && (termData.label.length < 2
|
||||
|| termData.label.slice(0, 1) !== this.ignoreSpaceUntil)
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return termData;
|
||||
}
|
||||
|
||||
addTerm(termData, termIndex = null) {
|
||||
if (this.ignoreSpaceUntil !== null) {
|
||||
if (this.ignoreSpaceSince === 0 && termData.label[this.ignoreSpaceSince] === this.ignoreSpaceUntil) {
|
||||
termData.label = termData.label.slice(1, -1);
|
||||
}
|
||||
|
||||
this.ignoreSpaceUntil = null;
|
||||
this.ignoreSpaceSince = null;
|
||||
}
|
||||
|
||||
super.addTerm(termData, termIndex);
|
||||
}
|
||||
|
||||
complete(input, data) {
|
||||
data.exclude = this.usedTerms.map(termData => termData.search);
|
||||
|
||||
super.complete(input, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listeners
|
||||
*/
|
||||
|
||||
onSubmit(event) {
|
||||
super.onSubmit(event);
|
||||
|
||||
this.ignoreSpaceUntil = null;
|
||||
this.ignoreSpaceSince = null;
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
super.onKeyDown(event);
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
let label = event.target.parentNode;
|
||||
if (label.dataset.index >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key !== this.separator) {
|
||||
return;
|
||||
}
|
||||
|
||||
let addedTerms = this.exchangeTerm();
|
||||
if (addedTerms.length) {
|
||||
this.togglePlaceholder();
|
||||
event.preventDefault();
|
||||
this.autoSubmit(this.input, 'exchange', addedTerms);
|
||||
}
|
||||
}
|
||||
|
||||
onKeyUp(event) {
|
||||
super.onKeyUp(event);
|
||||
|
||||
let label = event.target.parentNode;
|
||||
if (label.dataset.index >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ignoreSpaceUntil !== null) {
|
||||
// Reset if the user changes/removes the source char
|
||||
let value = event.target.value;
|
||||
if (value[this.ignoreSpaceSince] !== this.ignoreSpaceUntil) {
|
||||
this.ignoreSpaceUntil = null;
|
||||
this.ignoreSpaceSince = null;
|
||||
}
|
||||
}
|
||||
|
||||
let input = event.target;
|
||||
switch (event.key) {
|
||||
case '"':
|
||||
case "'":
|
||||
if (this.ignoreSpaceUntil === null) {
|
||||
this.ignoreSpaceUntil = event.key;
|
||||
this.ignoreSpaceSince = input.selectionStart - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TermInput;
|
||||
});
|
BIN
asset/static/font/Icinga-Icons.eot
Normal file
BIN
asset/static/font/Icinga-Icons.eot
Normal file
Binary file not shown.
13
asset/static/font/Icinga-Icons.svg
Normal file
13
asset/static/font/Icinga-Icons.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Generated by IcoMoon</metadata>
|
||||
<defs>
|
||||
<font id="Icinga-Icons" horiz-adv-x="1024">
|
||||
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||
<missing-glyph horiz-adv-x="1024" />
|
||||
<glyph unicode=" " 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 |
BIN
asset/static/font/Icinga-Icons.ttf
Normal file
BIN
asset/static/font/Icinga-Icons.ttf
Normal file
Binary file not shown.
BIN
asset/static/font/Icinga-Icons.woff
Normal file
BIN
asset/static/font/Icinga-Icons.woff
Normal file
Binary file not shown.
BIN
asset/static/font/awesome/fa-brands-400.ttf
Normal file
BIN
asset/static/font/awesome/fa-brands-400.ttf
Normal file
Binary file not shown.
BIN
asset/static/font/awesome/fa-brands-400.woff2
Normal file
BIN
asset/static/font/awesome/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
asset/static/font/awesome/fa-regular-400.ttf
Normal file
BIN
asset/static/font/awesome/fa-regular-400.ttf
Normal file
Binary file not shown.
BIN
asset/static/font/awesome/fa-regular-400.woff2
Normal file
BIN
asset/static/font/awesome/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
asset/static/font/awesome/fa-solid-900.ttf
Normal file
BIN
asset/static/font/awesome/fa-solid-900.ttf
Normal file
Binary file not shown.
BIN
asset/static/font/awesome/fa-solid-900.woff2
Normal file
BIN
asset/static/font/awesome/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
asset/static/font/awesome/fa-v4compatibility.ttf
Normal file
BIN
asset/static/font/awesome/fa-v4compatibility.ttf
Normal file
Binary file not shown.
BIN
asset/static/font/awesome/fa-v4compatibility.woff2
Normal file
BIN
asset/static/font/awesome/fa-v4compatibility.woff2
Normal file
Binary file not shown.
1
asset/static/img/select-icon-text-color.svg
Normal file
1
asset/static/img/select-icon-text-color.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg height="32" viewBox="0 0 24 32" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m5.20126707.78766623 4.45386238 4.20402191c.16721345.15783356.16291017.40979541-.00961164.56277256-.081158.0719638-.18974398.11220597-.3027668.11220597h-8.90772462c-.24025844 0-.43502639-.17818569-.43502639-.39798892 0-.10340014.04398717-.20274128.12264801-.27698961l4.45386234-4.20402191c.16721345-.15783357.44262326-.16177048.61514507-.00879333.00325382.00288518.00645805.00581661.00961165.00879333z" fill="#282E39" transform="matrix(1 0 0 -1 7 20.666667)"/></svg>
|
After Width: | Height: | Size: 559 B |
1
asset/static/img/select-icon.svg
Normal file
1
asset/static/img/select-icon.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg height="32" viewBox="0 0 24 32" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m5.20126707.78766623 4.45386238 4.20402191c.16721345.15783356.16291017.40979541-.00961164.56277256-.081158.0719638-.18974398.11220597-.3027668.11220597h-8.90772462c-.24025844 0-.43502639-.17818569-.43502639-.39798892 0-.10340014.04398717-.20274128.12264801-.27698961l4.45386234-4.20402191c.16721345-.15783357.44262326-.16177048.61514507-.00879333.00325382.00288518.00645805.00581661.00961165.00879333z" fill="#00c3ed" transform="matrix(1 0 0 -1 7 20.666667)"/></svg>
|
After Width: | Height: | Size: 558 B |
1410
composer.lock
generated
Normal file
1410
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
vendor/autoload.php
vendored
Normal file
25
vendor/autoload.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = '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;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitfc727941fa2114d4aa0d216fe957bafd::getLoader();
|
585
vendor/composer/ClassLoader.php
vendored
Normal file
585
vendor/composer/ClassLoader.php
vendored
Normal file
@ -0,0 +1,585 @@
|
||||
<?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 \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @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;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 = self::$includeFile;
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function initializeIncludeClosure()
|
||||
{
|
||||
if (self::$includeFile !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
self::$includeFile = \Closure::bind(static function($file) {
|
||||
include $file;
|
||||
}, null, null);
|
||||
}
|
||||
}
|
352
vendor/composer/InstalledVersions.php
vendored
Normal file
352
vendor/composer/InstalledVersions.php
vendored
Normal file
@ -0,0 +1,352 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagesByType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints($constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
|
||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||
self::$installed = $installed[count($installed) - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = require __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
$installed[] = self::$installed;
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
21
vendor/composer/LICENSE
vendored
Normal file
21
vendor/composer/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
10
vendor/composer/autoload_classmap.php
vendored
Normal file
10
vendor/composer/autoload_classmap.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
);
|
17
vendor/composer/autoload_files.php
vendored
Normal file
17
vendor/composer/autoload_files.php
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
<?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',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
||||
'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
|
||||
'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php',
|
||||
'8e4ccce73649a2b516ec3b4571432da5' => $vendorDir . '/ipl/scheduler/src/register_cron_aliases.php',
|
||||
);
|
11
vendor/composer/autoload_namespaces.php
vendored
Normal file
11
vendor/composer/autoload_namespaces.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Evenement' => array($vendorDir . '/evenement/evenement/src'),
|
||||
'AssetLoader' => array($baseDir . '/'),
|
||||
);
|
29
vendor/composer/autoload_psr4.php
vendored
Normal file
29
vendor/composer/autoload_psr4.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?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\\Scheduler\\' => array($vendorDir . '/ipl/scheduler/src'),
|
||||
'ipl\\Orm\\' => array($vendorDir . '/ipl/orm/src'),
|
||||
'ipl\\I18n\\' => array($vendorDir . '/ipl/i18n/src'),
|
||||
'ipl\\Html\\' => array($vendorDir . '/ipl/html/src'),
|
||||
'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
|
||||
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
|
||||
'Recurr\\' => array($vendorDir . '/simshaun/recurr/src/Recurr'),
|
||||
'React\\Promise\\' => array($vendorDir . '/react/promise/src'),
|
||||
'React\\EventLoop\\' => array($vendorDir . '/react/event-loop/src'),
|
||||
'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
|
||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
||||
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
|
||||
'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations'),
|
||||
'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib/Doctrine/Common/Collections'),
|
||||
'Cron\\' => array($vendorDir . '/dragonmantank/cron-expression/src/Cron'),
|
||||
);
|
50
vendor/composer/autoload_real.php
vendored
Normal file
50
vendor/composer/autoload_real.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInitfc727941fa2114d4aa0d216fe957bafd
|
||||
{
|
||||
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('ComposerAutoloaderInitfc727941fa2114d4aa0d216fe957bafd', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitfc727941fa2114d4aa0d216fe957bafd', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitfc727941fa2114d4aa0d216fe957bafd::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInitfc727941fa2114d4aa0d216fe957bafd::$files;
|
||||
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
}, null, null);
|
||||
foreach ($filesToLoad as $fileIdentifier => $file) {
|
||||
$requireFile($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
181
vendor/composer/autoload_static.php
vendored
Normal file
181
vendor/composer/autoload_static.php
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInitfc727941fa2114d4aa0d216fe957bafd
|
||||
{
|
||||
public static $files = array (
|
||||
'a2c78434f64e5f5ed402f42eee19c025' => __DIR__ . '/..' . '/ipl/stdlib/src/functions_include.php',
|
||||
'6076de347104821999fcfc82c8f19bc5' => __DIR__ . '/..' . '/ipl/i18n/src/functions_include.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
|
||||
'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
|
||||
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
|
||||
'8e4ccce73649a2b516ec3b4571432da5' => __DIR__ . '/..' . '/ipl/scheduler/src/register_cron_aliases.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'i' =>
|
||||
array (
|
||||
'ipl\\Web\\' => 8,
|
||||
'ipl\\Validator\\' => 14,
|
||||
'ipl\\Stdlib\\' => 11,
|
||||
'ipl\\Sql\\' => 8,
|
||||
'ipl\\Scheduler\\' => 14,
|
||||
'ipl\\Orm\\' => 8,
|
||||
'ipl\\I18n\\' => 9,
|
||||
'ipl\\Html\\' => 9,
|
||||
),
|
||||
'W' =>
|
||||
array (
|
||||
'Webmozart\\Assert\\' => 17,
|
||||
),
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Polyfill\\Ctype\\' => 23,
|
||||
),
|
||||
'R' =>
|
||||
array (
|
||||
'Recurr\\' => 7,
|
||||
'React\\Promise\\' => 14,
|
||||
'React\\EventLoop\\' => 16,
|
||||
'Ramsey\\Uuid\\' => 12,
|
||||
),
|
||||
'P' =>
|
||||
array (
|
||||
'Psr\\Log\\' => 8,
|
||||
'Psr\\Http\\Message\\' => 17,
|
||||
),
|
||||
'G' =>
|
||||
array (
|
||||
'GuzzleHttp\\Psr7\\' => 16,
|
||||
),
|
||||
'D' =>
|
||||
array (
|
||||
'Doctrine\\Deprecations\\' => 22,
|
||||
'Doctrine\\Common\\Collections\\' => 28,
|
||||
),
|
||||
'C' =>
|
||||
array (
|
||||
'Cron\\' => 5,
|
||||
),
|
||||
);
|
||||
|
||||
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\\Scheduler\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ipl/scheduler/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',
|
||||
),
|
||||
'Webmozart\\Assert\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/webmozart/assert/src',
|
||||
),
|
||||
'Symfony\\Polyfill\\Ctype\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
|
||||
),
|
||||
'Recurr\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/simshaun/recurr/src/Recurr',
|
||||
),
|
||||
'React\\Promise\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/promise/src',
|
||||
),
|
||||
'React\\EventLoop\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/event-loop/src',
|
||||
),
|
||||
'Ramsey\\Uuid\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ramsey/uuid/src',
|
||||
),
|
||||
'Psr\\Log\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
|
||||
),
|
||||
'Psr\\Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||
),
|
||||
'GuzzleHttp\\Psr7\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
|
||||
),
|
||||
'Doctrine\\Deprecations\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations',
|
||||
),
|
||||
'Doctrine\\Common\\Collections\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/collections/lib/Doctrine/Common/Collections',
|
||||
),
|
||||
'Cron\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron',
|
||||
),
|
||||
);
|
||||
|
||||
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 = ComposerStaticInitfc727941fa2114d4aa0d216fe957bafd::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitfc727941fa2114d4aa0d216fe957bafd::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInitfc727941fa2114d4aa0d216fe957bafd::$prefixesPsr0;
|
||||
$loader->classMap = ComposerStaticInitfc727941fa2114d4aa0d216fe957bafd::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
1464
vendor/composer/installed.json
vendored
Normal file
1464
vendor/composer/installed.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
257
vendor/composer/installed.php
vendored
Normal file
257
vendor/composer/installed.php
vendored
Normal file
@ -0,0 +1,257 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => 'icinga/icinga-php-library',
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '77c423f41cf7f520e65bdaff324ffb5769139175',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'doctrine/collections' => array(
|
||||
'pretty_version' => '1.8.0',
|
||||
'version' => '1.8.0.0',
|
||||
'reference' => '2b44dd4cbca8b5744327de78bafef5945c7e7b5e',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../doctrine/collections',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'doctrine/deprecations' => array(
|
||||
'pretty_version' => 'v1.0.0',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => '0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../doctrine/deprecations',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'dragonmantank/cron-expression' => array(
|
||||
'pretty_version' => 'v3.3.2',
|
||||
'version' => '3.3.2.0',
|
||||
'reference' => '782ca5968ab8b954773518e9e49a6f892a34b2a8',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../dragonmantank/cron-expression',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'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.4.0',
|
||||
'version' => '6.4.0.0',
|
||||
'reference' => '0698449d50f2b95517562295a59d414afc68b369',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../fortawesome/font-awesome',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'guzzlehttp/psr7' => array(
|
||||
'pretty_version' => '1.9.0',
|
||||
'version' => '1.9.0.0',
|
||||
'reference' => 'e98e3e6d4f86621a9b75f623996e6bbdeb4b9318',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'icinga/icinga-php-library' => array(
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '77c423f41cf7f520e65bdaff324ffb5769139175',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ipl/html' => array(
|
||||
'pretty_version' => 'v0.7.0',
|
||||
'version' => '0.7.0.0',
|
||||
'reference' => '8740b9efd42607e1b03a792897a9d3d51bc01e7f',
|
||||
'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.5.1',
|
||||
'version' => '0.5.1.0',
|
||||
'reference' => '3b7dca66f691ca69f62d877a1e8ad1f74a869540',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ipl/orm',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ipl/scheduler' => array(
|
||||
'pretty_version' => 'v0.1.0',
|
||||
'version' => '0.1.0.0',
|
||||
'reference' => 'af643e1a9576dcb7940835ccaeff3f7f4c754662',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ipl/scheduler',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ipl/sql' => array(
|
||||
'pretty_version' => 'v0.6.0',
|
||||
'version' => '0.6.0.0',
|
||||
'reference' => 'cb4f5a239bfb5046a4ad7206d77a1c1d82d6cdce',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ipl/sql',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ipl/stdlib' => array(
|
||||
'pretty_version' => 'v0.12.1',
|
||||
'version' => '0.12.1.0',
|
||||
'reference' => 'be75213aa3ba1c73756368f17be20a2bcbdeb835',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ipl/stdlib',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ipl/validator' => array(
|
||||
'pretty_version' => 'v0.5.0',
|
||||
'version' => '0.5.0.0',
|
||||
'reference' => 'a601fae0ed330e63cea50e4a2a6659ca1ad97bde',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ipl/validator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ipl/web' => array(
|
||||
'pretty_version' => 'v0.7.1',
|
||||
'version' => '0.7.1.0',
|
||||
'reference' => '82dcd7d59050eb23c0dc1d3f567eab8a6f7da5b8',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ipl/web',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'mtdowling/cron-expression' => array(
|
||||
'dev_requirement' => false,
|
||||
'replaced' => array(
|
||||
0 => '^1.0',
|
||||
),
|
||||
),
|
||||
'paragonie/random_compat' => array(
|
||||
'pretty_version' => 'v9.99.100',
|
||||
'version' => '9.99.100.0',
|
||||
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../paragonie/random_compat',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/http-message' => array(
|
||||
'pretty_version' => '1.1',
|
||||
'version' => '1.1.0.0',
|
||||
'reference' => 'cb6ce4845ce34a8ad9e68117c10ee90a29919eba',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/http-message',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/http-message-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0',
|
||||
),
|
||||
),
|
||||
'psr/log' => array(
|
||||
'pretty_version' => '1.1.4',
|
||||
'version' => '1.1.4.0',
|
||||
'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/log',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ralouphie/getallheaders' => array(
|
||||
'pretty_version' => '3.0.3',
|
||||
'version' => '3.0.3.0',
|
||||
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ralouphie/getallheaders',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ramsey/uuid' => array(
|
||||
'pretty_version' => '3.9.7',
|
||||
'version' => '3.9.7.0',
|
||||
'reference' => 'dc75aa439eb4c1b77f5379fd958b3dc0e6014178',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ramsey/uuid',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/event-loop' => array(
|
||||
'pretty_version' => 'v1.3.0',
|
||||
'version' => '1.3.0.0',
|
||||
'reference' => '187fb56f46d424afb6ec4ad089269c72eec2e137',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/event-loop',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/promise' => array(
|
||||
'pretty_version' => 'v2.9.0',
|
||||
'version' => '2.9.0.0',
|
||||
'reference' => '234f8fd1023c9158e2314fa9d7d0e6a83db42910',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/promise',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'rhumsaa/uuid' => array(
|
||||
'dev_requirement' => false,
|
||||
'replaced' => array(
|
||||
0 => '3.9.7',
|
||||
),
|
||||
),
|
||||
'simshaun/recurr' => array(
|
||||
'pretty_version' => 'v5.0.1',
|
||||
'version' => '5.0.1.0',
|
||||
'reference' => '6887b7bd7075de97c8c69835e0939ff68d23c47e',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../simshaun/recurr',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-ctype' => array(
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '5bbc823adecdae860bb64756d639ecfec17b050a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'webmozart/assert' => array(
|
||||
'pretty_version' => '1.11.0',
|
||||
'version' => '1.11.0.0',
|
||||
'reference' => '11cb2199493b2f8a3b53e7f19068fc6aac760991',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../webmozart/assert',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
),
|
||||
);
|
26
vendor/composer/platform_check.php
vendored
Normal file
26
vendor/composer/platform_check.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 70200)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
32
vendor/doctrine/collections/.doctrine-project.json
vendored
Normal file
32
vendor/doctrine/collections/.doctrine-project.json
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"active": true,
|
||||
"name": "Collections",
|
||||
"slug": "collections",
|
||||
"docsSlug": "doctrine-collections",
|
||||
"versions": [
|
||||
{
|
||||
"name": "2.0",
|
||||
"branchName": "2.0.x",
|
||||
"slug": "latest",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "1.8",
|
||||
"branchName": "1.8.x",
|
||||
"slug": "1.8",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "1.7",
|
||||
"branchName": "1.7.x",
|
||||
"slug": "1.7",
|
||||
"current": true
|
||||
},
|
||||
{
|
||||
"name": "1.6",
|
||||
"branchName": "1.6.x",
|
||||
"slug": "1.6",
|
||||
"maintained": false
|
||||
}
|
||||
]
|
||||
}
|
19
vendor/doctrine/collections/LICENSE
vendored
Normal file
19
vendor/doctrine/collections/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2006-2013 Doctrine Project
|
||||
|
||||
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.
|
61
vendor/doctrine/collections/composer.json
vendored
Normal file
61
vendor/doctrine/collections/composer.json
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "doctrine/collections",
|
||||
"description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"php",
|
||||
"collections",
|
||||
"array",
|
||||
"iterators"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Guilherme Blanco",
|
||||
"email": "guilhermeblanco@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Borschel",
|
||||
"email": "roman@code-factory.org"
|
||||
},
|
||||
{
|
||||
"name": "Benjamin Eberlei",
|
||||
"email": "kontakt@beberlei.de"
|
||||
},
|
||||
{
|
||||
"name": "Jonathan Wage",
|
||||
"email": "jonwage@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Johannes Schmitt",
|
||||
"email": "schmittjoh@gmail.com"
|
||||
}
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org/projects/collections.html",
|
||||
"require": {
|
||||
"php": "^7.1.3 || ^8.0",
|
||||
"doctrine/deprecations": "^0.5.3 || ^1"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9.0 || ^10.0",
|
||||
"phpstan/phpstan": "^1.4.8",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.1.5",
|
||||
"vimeo/psalm": "^4.22"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Common\\Collections\\": "lib/Doctrine/Common/Collections"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Tests\\": "tests/Doctrine/Tests"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
}
|
||||
}
|
||||
}
|
389
vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php
vendored
Normal file
389
vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php
vendored
Normal file
@ -0,0 +1,389 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections;
|
||||
|
||||
use Closure;
|
||||
use LogicException;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Lazy collection that is backed by a concrete collection
|
||||
*
|
||||
* @psalm-template TKey of array-key
|
||||
* @psalm-template T
|
||||
* @template-implements Collection<TKey,T>
|
||||
*/
|
||||
abstract class AbstractLazyCollection implements Collection
|
||||
{
|
||||
/**
|
||||
* The backed collection to use
|
||||
*
|
||||
* @psalm-var Collection<TKey,T>|null
|
||||
* @var Collection<mixed>|null
|
||||
*/
|
||||
protected $collection;
|
||||
|
||||
/** @var bool */
|
||||
protected $initialized = false;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function add($element)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->add($element);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->initialize();
|
||||
$this->collection->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @template TMaybeContained
|
||||
*/
|
||||
public function contains($element)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->contains($element);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->remove($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function removeElement($element)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->removeElement($element);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function containsKey($key)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->containsKey($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getKeys()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->getKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->getValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->initialize();
|
||||
$this->collection->set($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function first()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function last()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->last();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->key();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function exists(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->exists($p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function filter(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->filter($p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function forAll(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->forAll($p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function map(Closure $func)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->map($func);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function partition(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->partition($p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @template TMaybeContained
|
||||
*/
|
||||
public function indexOf($element)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->indexOf($element);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function slice($offset, $length = null)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->slice($offset, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return Traversable<int|string, mixed>
|
||||
* @psalm-return Traversable<TKey,T>
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->getIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TKey $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->offsetExists($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TKey $offset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->collection->offsetGet($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TKey|null $offset
|
||||
* @param T $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->initialize();
|
||||
$this->collection->offsetSet($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TKey $offset
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$this->initialize();
|
||||
$this->collection->offsetUnset($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the lazy collection already initialized?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @psalm-assert-if-true Collection<TKey,T> $this->collection
|
||||
*/
|
||||
public function isInitialized()
|
||||
{
|
||||
return $this->initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the collection
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-assert Collection<TKey,T> $this->collection
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
if ($this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->doInitialize();
|
||||
$this->initialized = true;
|
||||
|
||||
if ($this->collection === null) {
|
||||
throw new LogicException('You must initialize the collection property in the doInitialize() method.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the initialization logic
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract protected function doInitialize();
|
||||
}
|
466
vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php
vendored
Normal file
466
vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php
vendored
Normal file
@ -0,0 +1,466 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections;
|
||||
|
||||
use ArrayIterator;
|
||||
use Closure;
|
||||
use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
use function array_filter;
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_reverse;
|
||||
use function array_search;
|
||||
use function array_slice;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function current;
|
||||
use function end;
|
||||
use function in_array;
|
||||
use function key;
|
||||
use function next;
|
||||
use function reset;
|
||||
use function spl_object_hash;
|
||||
use function uasort;
|
||||
|
||||
use const ARRAY_FILTER_USE_BOTH;
|
||||
|
||||
/**
|
||||
* An ArrayCollection is a Collection implementation that wraps a regular PHP array.
|
||||
*
|
||||
* Warning: Using (un-)serialize() on a collection is not a supported use-case
|
||||
* and may break when we change the internals in the future. If you need to
|
||||
* serialize a collection use {@link toArray()} and reconstruct the collection
|
||||
* manually.
|
||||
*
|
||||
* @psalm-template TKey of array-key
|
||||
* @psalm-template T
|
||||
* @template-implements Collection<TKey,T>
|
||||
* @template-implements Selectable<TKey,T>
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class ArrayCollection implements Collection, Selectable
|
||||
{
|
||||
/**
|
||||
* An array containing the entries of this collection.
|
||||
*
|
||||
* @psalm-var array<TKey,T>
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $elements;
|
||||
|
||||
/**
|
||||
* Initializes a new ArrayCollection.
|
||||
*
|
||||
* @param array $elements
|
||||
* @psalm-param array<TKey,T> $elements
|
||||
*/
|
||||
public function __construct(array $elements = [])
|
||||
{
|
||||
$this->elements = $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function first()
|
||||
{
|
||||
return reset($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from the specified elements.
|
||||
*
|
||||
* This method is provided for derived classes to specify how a new
|
||||
* instance should be created when constructor semantics have changed.
|
||||
*
|
||||
* @param array $elements Elements.
|
||||
* @psalm-param array<K,V> $elements
|
||||
*
|
||||
* @return static
|
||||
* @psalm-return static<K,V>
|
||||
*
|
||||
* @psalm-template K of array-key
|
||||
* @psalm-template V
|
||||
*/
|
||||
protected function createFrom(array $elements)
|
||||
{
|
||||
return new static($elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function last()
|
||||
{
|
||||
return end($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return next($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
if (! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$removed = $this->elements[$key];
|
||||
unset($this->elements[$key]);
|
||||
|
||||
return $removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function removeElement($element)
|
||||
{
|
||||
$key = array_search($element, $this->elements, true);
|
||||
|
||||
if ($key === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($this->elements[$key]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by interface ArrayAccess.
|
||||
*
|
||||
* @param TKey $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return $this->containsKey($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by interface ArrayAccess.
|
||||
*
|
||||
* @param TKey $offset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by interface ArrayAccess.
|
||||
*
|
||||
* @param TKey|null $offset
|
||||
* @param T $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (! isset($offset)) {
|
||||
$this->add($value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by interface ArrayAccess.
|
||||
*
|
||||
* @param TKey $offset
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$this->remove($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function containsKey($key)
|
||||
{
|
||||
return isset($this->elements[$key]) || array_key_exists($key, $this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @template TMaybeContained
|
||||
*/
|
||||
public function contains($element)
|
||||
{
|
||||
return in_array($element, $this->elements, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function exists(Closure $p)
|
||||
{
|
||||
foreach ($this->elements as $key => $element) {
|
||||
if ($p($key, $element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-param TMaybeContained $element
|
||||
*
|
||||
* @psalm-return (TMaybeContained is T ? TKey|false : false)
|
||||
*
|
||||
* @template TMaybeContained
|
||||
*/
|
||||
public function indexOf($element)
|
||||
{
|
||||
return array_search($element, $this->elements, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return $this->elements[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getKeys()
|
||||
{
|
||||
return array_keys($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
return array_values($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->elements[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress InvalidPropertyAssignmentValue
|
||||
*
|
||||
* This breaks assumptions about the template type, but it would
|
||||
* be a backwards-incompatible change to remove this method
|
||||
*/
|
||||
public function add($element)
|
||||
{
|
||||
$this->elements[] = $element;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return Traversable<int|string, mixed>
|
||||
* @psalm-return Traversable<TKey,T>
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-param Closure(T):U $func
|
||||
*
|
||||
* @return static
|
||||
* @psalm-return static<TKey, U>
|
||||
*
|
||||
* @psalm-template U
|
||||
*/
|
||||
public function map(Closure $func)
|
||||
{
|
||||
return $this->createFrom(array_map($func, $this->elements));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return static
|
||||
* @psalm-return static<TKey,T>
|
||||
*/
|
||||
public function filter(Closure $p)
|
||||
{
|
||||
return $this->createFrom(array_filter($this->elements, $p, ARRAY_FILTER_USE_BOTH));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function forAll(Closure $p)
|
||||
{
|
||||
foreach ($this->elements as $key => $element) {
|
||||
if (! $p($key, $element)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function partition(Closure $p)
|
||||
{
|
||||
$matches = $noMatches = [];
|
||||
|
||||
foreach ($this->elements as $key => $element) {
|
||||
if ($p($key, $element)) {
|
||||
$matches[$key] = $element;
|
||||
} else {
|
||||
$noMatches[$key] = $element;
|
||||
}
|
||||
}
|
||||
|
||||
return [$this->createFrom($matches), $this->createFrom($noMatches)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this object.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return self::class . '@' . spl_object_hash($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->elements = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function slice($offset, $length = null)
|
||||
{
|
||||
return array_slice($this->elements, $offset, $length, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function matching(Criteria $criteria)
|
||||
{
|
||||
$expr = $criteria->getWhereExpression();
|
||||
$filtered = $this->elements;
|
||||
|
||||
if ($expr) {
|
||||
$visitor = new ClosureExpressionVisitor();
|
||||
$filter = $visitor->dispatch($expr);
|
||||
$filtered = array_filter($filtered, $filter);
|
||||
}
|
||||
|
||||
$orderings = $criteria->getOrderings();
|
||||
|
||||
if ($orderings) {
|
||||
$next = null;
|
||||
foreach (array_reverse($orderings) as $field => $ordering) {
|
||||
$next = ClosureExpressionVisitor::sortByField($field, $ordering === Criteria::DESC ? -1 : 1, $next);
|
||||
}
|
||||
|
||||
uasort($filtered, $next);
|
||||
}
|
||||
|
||||
$offset = $criteria->getFirstResult();
|
||||
$length = $criteria->getMaxResults();
|
||||
|
||||
if ($offset || $length) {
|
||||
$filtered = array_slice($filtered, (int) $offset, $length);
|
||||
}
|
||||
|
||||
return $this->createFrom($filtered);
|
||||
}
|
||||
}
|
99
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php
vendored
Normal file
99
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections;
|
||||
|
||||
use ArrayAccess;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* The missing (SPL) Collection/Array/OrderedMap interface.
|
||||
*
|
||||
* A Collection resembles the nature of a regular PHP array. That is,
|
||||
* it is essentially an <b>ordered map</b> that can also be used
|
||||
* like a list.
|
||||
*
|
||||
* A Collection has an internal iterator just like a PHP array. In addition,
|
||||
* a Collection can be iterated with external iterators, which is preferable.
|
||||
* To use an external iterator simply use the foreach language construct to
|
||||
* iterate over the collection (which calls {@link getIterator()} internally) or
|
||||
* explicitly retrieve an iterator though {@link getIterator()} which can then be
|
||||
* used to iterate over the collection.
|
||||
* You can not rely on the internal iterator of the collection being at a certain
|
||||
* position unless you explicitly positioned it before. Prefer iteration with
|
||||
* external iterators.
|
||||
*
|
||||
* @psalm-template TKey of array-key
|
||||
* @psalm-template T
|
||||
* @template-extends ReadableCollection<TKey, T>
|
||||
* @template-extends ArrayAccess<TKey, T>
|
||||
*/
|
||||
interface Collection extends ReadableCollection, ArrayAccess
|
||||
{
|
||||
/**
|
||||
* Adds an element at the end of the collection.
|
||||
*
|
||||
* @param mixed $element The element to add.
|
||||
* @psalm-param T $element
|
||||
*
|
||||
* @return true Always TRUE.
|
||||
*/
|
||||
public function add($element);
|
||||
|
||||
/**
|
||||
* Clears the collection, removing all elements.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear();
|
||||
|
||||
/**
|
||||
* Removes the element at the specified index from the collection.
|
||||
*
|
||||
* @param string|int $key The key/index of the element to remove.
|
||||
* @psalm-param TKey $key
|
||||
*
|
||||
* @return mixed The removed element or NULL, if the collection did not contain the element.
|
||||
* @psalm-return T|null
|
||||
*/
|
||||
public function remove($key);
|
||||
|
||||
/**
|
||||
* Removes the specified element from the collection, if it is found.
|
||||
*
|
||||
* @param mixed $element The element to remove.
|
||||
* @psalm-param T $element
|
||||
*
|
||||
* @return bool TRUE if this collection contained the specified element, FALSE otherwise.
|
||||
*/
|
||||
public function removeElement($element);
|
||||
|
||||
/**
|
||||
* Sets an element in the collection at the specified key/index.
|
||||
*
|
||||
* @param string|int $key The key/index of the element to set.
|
||||
* @param mixed $value The element to set.
|
||||
* @psalm-param TKey $key
|
||||
* @psalm-param T $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $value);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return Collection<mixed> A collection with the results of the filter operation.
|
||||
* @psalm-return Collection<TKey, T>
|
||||
*/
|
||||
public function filter(Closure $p);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return Collection<mixed>[] An array with two elements. The first element contains the collection
|
||||
* of elements where the predicate returned TRUE, the second element
|
||||
* contains the collection of elements where the predicate returned FALSE.
|
||||
* @psalm-return array{0: Collection<TKey, T>, 1: Collection<TKey, T>}
|
||||
*/
|
||||
public function partition(Closure $p);
|
||||
}
|
245
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php
vendored
Normal file
245
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php
vendored
Normal file
@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections;
|
||||
|
||||
use Doctrine\Common\Collections\Expr\CompositeExpression;
|
||||
use Doctrine\Common\Collections\Expr\Expression;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
|
||||
use function array_map;
|
||||
use function func_num_args;
|
||||
use function strtoupper;
|
||||
|
||||
/**
|
||||
* Criteria for filtering Selectable collections.
|
||||
*
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class Criteria
|
||||
{
|
||||
public const ASC = 'ASC';
|
||||
|
||||
public const DESC = 'DESC';
|
||||
|
||||
/** @var ExpressionBuilder|null */
|
||||
private static $expressionBuilder;
|
||||
|
||||
/** @var Expression|null */
|
||||
private $expression;
|
||||
|
||||
/** @var string[] */
|
||||
private $orderings = [];
|
||||
|
||||
/** @var int|null */
|
||||
private $firstResult;
|
||||
|
||||
/** @var int|null */
|
||||
private $maxResults;
|
||||
|
||||
/**
|
||||
* Creates an instance of the class.
|
||||
*
|
||||
* @return Criteria
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expression builder.
|
||||
*
|
||||
* @return ExpressionBuilder
|
||||
*/
|
||||
public static function expr()
|
||||
{
|
||||
if (self::$expressionBuilder === null) {
|
||||
self::$expressionBuilder = new ExpressionBuilder();
|
||||
}
|
||||
|
||||
return self::$expressionBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new Criteria.
|
||||
*
|
||||
* @param string[]|null $orderings
|
||||
* @param int|null $firstResult
|
||||
* @param int|null $maxResults
|
||||
*/
|
||||
public function __construct(?Expression $expression = null, ?array $orderings = null, $firstResult = null, $maxResults = null)
|
||||
{
|
||||
$this->expression = $expression;
|
||||
|
||||
if ($firstResult === null && func_num_args() > 2) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/collections',
|
||||
'https://github.com/doctrine/collections/pull/311',
|
||||
'Passing null as $firstResult to the constructor of %s is deprecated. Pass 0 instead or omit the argument.',
|
||||
self::class
|
||||
);
|
||||
}
|
||||
|
||||
$this->setFirstResult($firstResult);
|
||||
$this->setMaxResults($maxResults);
|
||||
|
||||
if ($orderings === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->orderBy($orderings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the where expression to evaluate when this Criteria is searched for.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function where(Expression $expression)
|
||||
{
|
||||
$this->expression = $expression;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the where expression to evaluate when this Criteria is searched for
|
||||
* using an AND with previous expression.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function andWhere(Expression $expression)
|
||||
{
|
||||
if ($this->expression === null) {
|
||||
return $this->where($expression);
|
||||
}
|
||||
|
||||
$this->expression = new CompositeExpression(
|
||||
CompositeExpression::TYPE_AND,
|
||||
[$this->expression, $expression]
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the where expression to evaluate when this Criteria is searched for
|
||||
* using an OR with previous expression.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function orWhere(Expression $expression)
|
||||
{
|
||||
if ($this->expression === null) {
|
||||
return $this->where($expression);
|
||||
}
|
||||
|
||||
$this->expression = new CompositeExpression(
|
||||
CompositeExpression::TYPE_OR,
|
||||
[$this->expression, $expression]
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression attached to this Criteria.
|
||||
*
|
||||
* @return Expression|null
|
||||
*/
|
||||
public function getWhereExpression()
|
||||
{
|
||||
return $this->expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current orderings of this Criteria.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getOrderings()
|
||||
{
|
||||
return $this->orderings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ordering of the result of this Criteria.
|
||||
*
|
||||
* Keys are field and values are the order, being either ASC or DESC.
|
||||
*
|
||||
* @see Criteria::ASC
|
||||
* @see Criteria::DESC
|
||||
*
|
||||
* @param string[] $orderings
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function orderBy(array $orderings)
|
||||
{
|
||||
$this->orderings = array_map(
|
||||
static function (string $ordering): string {
|
||||
return strtoupper($ordering) === Criteria::ASC ? Criteria::ASC : Criteria::DESC;
|
||||
},
|
||||
$orderings
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current first result option of this Criteria.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getFirstResult()
|
||||
{
|
||||
return $this->firstResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of first result that this Criteria should return.
|
||||
*
|
||||
* @param int|null $firstResult The value to set.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setFirstResult($firstResult)
|
||||
{
|
||||
if ($firstResult === null) {
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
'doctrine/collections',
|
||||
'https://github.com/doctrine/collections/pull/311',
|
||||
'Passing null to %s() is deprecated, pass 0 instead.',
|
||||
__METHOD__
|
||||
);
|
||||
}
|
||||
|
||||
$this->firstResult = $firstResult;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets maxResults.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getMaxResults()
|
||||
{
|
||||
return $this->maxResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets maxResults.
|
||||
*
|
||||
* @param int|null $maxResults The value to set.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMaxResults($maxResults)
|
||||
{
|
||||
$this->maxResults = $maxResults;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
269
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php
vendored
Normal file
269
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php
vendored
Normal file
@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections\Expr;
|
||||
|
||||
use ArrayAccess;
|
||||
use Closure;
|
||||
use RuntimeException;
|
||||
|
||||
use function explode;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_scalar;
|
||||
use function iterator_to_array;
|
||||
use function method_exists;
|
||||
use function preg_match;
|
||||
use function preg_replace_callback;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function strtoupper;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Walks an expression graph and turns it into a PHP closure.
|
||||
*
|
||||
* This closure can be used with {@Collection#filter()} and is used internally
|
||||
* by {@ArrayCollection#select()}.
|
||||
*/
|
||||
class ClosureExpressionVisitor extends ExpressionVisitor
|
||||
{
|
||||
/**
|
||||
* Accesses the field of a given object. This field has to be public
|
||||
* directly or indirectly (through an accessor get*, is*, or a magic
|
||||
* method, __get, __call).
|
||||
*
|
||||
* @param object|mixed[] $object
|
||||
* @param string $field
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getObjectFieldValue($object, $field)
|
||||
{
|
||||
if (strpos($field, '.') !== false) {
|
||||
[$field, $subField] = explode('.', $field, 2);
|
||||
$object = self::getObjectFieldValue($object, $field);
|
||||
|
||||
return self::getObjectFieldValue($object, $subField);
|
||||
}
|
||||
|
||||
if (is_array($object)) {
|
||||
return $object[$field];
|
||||
}
|
||||
|
||||
$accessors = ['get', 'is', ''];
|
||||
|
||||
foreach ($accessors as $accessor) {
|
||||
$accessor .= $field;
|
||||
|
||||
if (method_exists($object, $accessor)) {
|
||||
return $object->$accessor();
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/^is[A-Z]+/', $field) === 1 && method_exists($object, $field)) {
|
||||
return $object->$field();
|
||||
}
|
||||
|
||||
// __call should be triggered for get.
|
||||
$accessor = $accessors[0] . $field;
|
||||
|
||||
if (method_exists($object, '__call')) {
|
||||
return $object->$accessor();
|
||||
}
|
||||
|
||||
if ($object instanceof ArrayAccess) {
|
||||
return $object[$field];
|
||||
}
|
||||
|
||||
if (isset($object->$field)) {
|
||||
return $object->$field;
|
||||
}
|
||||
|
||||
// camelcase field name to support different variable naming conventions
|
||||
$ccField = preg_replace_callback('/_(.?)/', static function ($matches) {
|
||||
return strtoupper($matches[1]);
|
||||
}, $field);
|
||||
|
||||
foreach ($accessors as $accessor) {
|
||||
$accessor .= $ccField;
|
||||
|
||||
if (method_exists($object, $accessor)) {
|
||||
return $object->$accessor();
|
||||
}
|
||||
}
|
||||
|
||||
return $object->$field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for sorting arrays of objects based on multiple fields + orientations.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $orientation
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
public static function sortByField($name, $orientation = 1, ?Closure $next = null)
|
||||
{
|
||||
if (! $next) {
|
||||
$next = static function (): int {
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
return static function ($a, $b) use ($name, $next, $orientation): int {
|
||||
$aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name);
|
||||
|
||||
$bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name);
|
||||
|
||||
if ($aValue === $bValue) {
|
||||
return $next($a, $b);
|
||||
}
|
||||
|
||||
return ($aValue > $bValue ? 1 : -1) * $orientation;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkComparison(Comparison $comparison)
|
||||
{
|
||||
$field = $comparison->getField();
|
||||
$value = $comparison->getValue()->getValue(); // shortcut for walkValue()
|
||||
|
||||
switch ($comparison->getOperator()) {
|
||||
case Comparison::EQ:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value;
|
||||
};
|
||||
|
||||
case Comparison::NEQ:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value;
|
||||
};
|
||||
|
||||
case Comparison::LT:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value;
|
||||
};
|
||||
|
||||
case Comparison::LTE:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value;
|
||||
};
|
||||
|
||||
case Comparison::GT:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value;
|
||||
};
|
||||
|
||||
case Comparison::GTE:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value;
|
||||
};
|
||||
|
||||
case Comparison::IN:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
$fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
|
||||
|
||||
return in_array($fieldValue, $value, is_scalar($fieldValue));
|
||||
};
|
||||
|
||||
case Comparison::NIN:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
$fieldValue = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
|
||||
|
||||
return ! in_array($fieldValue, $value, is_scalar($fieldValue));
|
||||
};
|
||||
|
||||
case Comparison::CONTAINS:
|
||||
return static function ($object) use ($field, $value) {
|
||||
return strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value) !== false;
|
||||
};
|
||||
|
||||
case Comparison::MEMBER_OF:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
$fieldValues = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
|
||||
|
||||
if (! is_array($fieldValues)) {
|
||||
$fieldValues = iterator_to_array($fieldValues);
|
||||
}
|
||||
|
||||
return in_array($value, $fieldValues, true);
|
||||
};
|
||||
|
||||
case Comparison::STARTS_WITH:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
return strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value) === 0;
|
||||
};
|
||||
|
||||
case Comparison::ENDS_WITH:
|
||||
return static function ($object) use ($field, $value): bool {
|
||||
return $value === substr(ClosureExpressionVisitor::getObjectFieldValue($object, $field), -strlen($value));
|
||||
};
|
||||
|
||||
default:
|
||||
throw new RuntimeException('Unknown comparison operator: ' . $comparison->getOperator());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkValue(Value $value)
|
||||
{
|
||||
return $value->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkCompositeExpression(CompositeExpression $expr)
|
||||
{
|
||||
$expressionList = [];
|
||||
|
||||
foreach ($expr->getExpressionList() as $child) {
|
||||
$expressionList[] = $this->dispatch($child);
|
||||
}
|
||||
|
||||
switch ($expr->getType()) {
|
||||
case CompositeExpression::TYPE_AND:
|
||||
return $this->andExpressions($expressionList);
|
||||
|
||||
case CompositeExpression::TYPE_OR:
|
||||
return $this->orExpressions($expressionList);
|
||||
|
||||
default:
|
||||
throw new RuntimeException('Unknown composite ' . $expr->getType());
|
||||
}
|
||||
}
|
||||
|
||||
/** @param callable[] $expressions */
|
||||
private function andExpressions(array $expressions): callable
|
||||
{
|
||||
return static function ($object) use ($expressions): bool {
|
||||
foreach ($expressions as $expression) {
|
||||
if (! $expression($object)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/** @param callable[] $expressions */
|
||||
private function orExpressions(array $expressions): callable
|
||||
{
|
||||
return static function ($object) use ($expressions): bool {
|
||||
foreach ($expressions as $expression) {
|
||||
if ($expression($object)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
74
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php
vendored
Normal file
74
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections\Expr;
|
||||
|
||||
/**
|
||||
* Comparison of a field with a value by the given operator.
|
||||
*/
|
||||
class Comparison implements Expression
|
||||
{
|
||||
public const EQ = '=';
|
||||
public const NEQ = '<>';
|
||||
public const LT = '<';
|
||||
public const LTE = '<=';
|
||||
public const GT = '>';
|
||||
public const GTE = '>=';
|
||||
public const IS = '='; // no difference with EQ
|
||||
public const IN = 'IN';
|
||||
public const NIN = 'NIN';
|
||||
public const CONTAINS = 'CONTAINS';
|
||||
public const MEMBER_OF = 'MEMBER_OF';
|
||||
public const STARTS_WITH = 'STARTS_WITH';
|
||||
public const ENDS_WITH = 'ENDS_WITH';
|
||||
|
||||
/** @var string */
|
||||
private $field;
|
||||
|
||||
/** @var string */
|
||||
private $op;
|
||||
|
||||
/** @var Value */
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param string $operator
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct($field, $operator, $value)
|
||||
{
|
||||
if (! ($value instanceof Value)) {
|
||||
$value = new Value($value);
|
||||
}
|
||||
|
||||
$this->field = $field;
|
||||
$this->op = $operator;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/** @return Value */
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getOperator()
|
||||
{
|
||||
return $this->op;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function visit(ExpressionVisitor $visitor)
|
||||
{
|
||||
return $visitor->walkComparison($this);
|
||||
}
|
||||
}
|
67
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php
vendored
Normal file
67
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections\Expr;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Expression of Expressions combined by AND or OR operation.
|
||||
*/
|
||||
class CompositeExpression implements Expression
|
||||
{
|
||||
public const TYPE_AND = 'AND';
|
||||
public const TYPE_OR = 'OR';
|
||||
|
||||
/** @var string */
|
||||
private $type;
|
||||
|
||||
/** @var Expression[] */
|
||||
private $expressions = [];
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param mixed[] $expressions
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function __construct($type, array $expressions)
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
foreach ($expressions as $expr) {
|
||||
if ($expr instanceof Value) {
|
||||
throw new RuntimeException('Values are not supported expressions as children of and/or expressions.');
|
||||
}
|
||||
|
||||
if (! ($expr instanceof Expression)) {
|
||||
throw new RuntimeException('No expression given to CompositeExpression.');
|
||||
}
|
||||
|
||||
$this->expressions[] = $expr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of expressions nested in this composite.
|
||||
*
|
||||
* @return Expression[]
|
||||
*/
|
||||
public function getExpressionList()
|
||||
{
|
||||
return $this->expressions;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function visit(ExpressionVisitor $visitor)
|
||||
{
|
||||
return $visitor->walkCompositeExpression($this);
|
||||
}
|
||||
}
|
12
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php
vendored
Normal file
12
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections\Expr;
|
||||
|
||||
/**
|
||||
* Expression for the {@link Selectable} interface.
|
||||
*/
|
||||
interface Expression
|
||||
{
|
||||
/** @return mixed */
|
||||
public function visit(ExpressionVisitor $visitor);
|
||||
}
|
59
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php
vendored
Normal file
59
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections\Expr;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
use function get_class;
|
||||
|
||||
/**
|
||||
* An Expression visitor walks a graph of expressions and turns them into a
|
||||
* query for the underlying implementation.
|
||||
*/
|
||||
abstract class ExpressionVisitor
|
||||
{
|
||||
/**
|
||||
* Converts a comparison expression into the target query language output.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function walkComparison(Comparison $comparison);
|
||||
|
||||
/**
|
||||
* Converts a value expression into the target query language part.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function walkValue(Value $value);
|
||||
|
||||
/**
|
||||
* Converts a composite expression into the target query language output.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function walkCompositeExpression(CompositeExpression $expr);
|
||||
|
||||
/**
|
||||
* Dispatches walking an expression to the appropriate handler.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function dispatch(Expression $expr)
|
||||
{
|
||||
switch (true) {
|
||||
case $expr instanceof Comparison:
|
||||
return $this->walkComparison($expr);
|
||||
|
||||
case $expr instanceof Value:
|
||||
return $this->walkValue($expr);
|
||||
|
||||
case $expr instanceof CompositeExpression:
|
||||
return $this->walkCompositeExpression($expr);
|
||||
|
||||
default:
|
||||
throw new RuntimeException('Unknown Expression ' . get_class($expr));
|
||||
}
|
||||
}
|
||||
}
|
29
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php
vendored
Normal file
29
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections\Expr;
|
||||
|
||||
class Value implements Expression
|
||||
{
|
||||
/** @var mixed */
|
||||
private $value;
|
||||
|
||||
/** @param mixed $value */
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/** @return mixed */
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function visit(ExpressionVisitor $visitor)
|
||||
{
|
||||
return $visitor->walkValue($this);
|
||||
}
|
||||
}
|
181
vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php
vendored
Normal file
181
vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections;
|
||||
|
||||
use Doctrine\Common\Collections\Expr\Comparison;
|
||||
use Doctrine\Common\Collections\Expr\CompositeExpression;
|
||||
use Doctrine\Common\Collections\Expr\Value;
|
||||
|
||||
use function func_get_args;
|
||||
|
||||
/**
|
||||
* Builder for Expressions in the {@link Selectable} interface.
|
||||
*
|
||||
* Important Notice for interoperable code: You have to use scalar
|
||||
* values only for comparisons, otherwise the behavior of the comparison
|
||||
* may be different between implementations (Array vs ORM vs ODM).
|
||||
*/
|
||||
class ExpressionBuilder
|
||||
{
|
||||
/**
|
||||
* @param mixed ...$x
|
||||
*
|
||||
* @return CompositeExpression
|
||||
*/
|
||||
public function andX($x = null)
|
||||
{
|
||||
return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed ...$x
|
||||
*
|
||||
* @return CompositeExpression
|
||||
*/
|
||||
public function orX($x = null)
|
||||
{
|
||||
return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function eq($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::EQ, new Value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function gt($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::GT, new Value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function lt($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::LT, new Value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function gte($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::GTE, new Value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function lte($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::LTE, new Value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function neq($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::NEQ, new Value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function isNull($field)
|
||||
{
|
||||
return new Comparison($field, Comparison::EQ, new Value(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed[] $values
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function in($field, array $values)
|
||||
{
|
||||
return new Comparison($field, Comparison::IN, new Value($values));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed[] $values
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function notIn($field, array $values)
|
||||
{
|
||||
return new Comparison($field, Comparison::NIN, new Value($values));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function contains($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::CONTAINS, new Value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function memberOf($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::MEMBER_OF, new Value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function startsWith($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::STARTS_WITH, new Value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return Comparison
|
||||
*/
|
||||
public function endsWith($field, $value)
|
||||
{
|
||||
return new Comparison($field, Comparison::ENDS_WITH, new Value($value));
|
||||
}
|
||||
}
|
213
vendor/doctrine/collections/lib/Doctrine/Common/Collections/ReadableCollection.php
vendored
Normal file
213
vendor/doctrine/collections/lib/Doctrine/Common/Collections/ReadableCollection.php
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections;
|
||||
|
||||
use Closure;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* @psalm-template TKey of array-key
|
||||
* @template-covariant T
|
||||
* @template-extends IteratorAggregate<TKey, T>
|
||||
*/
|
||||
interface ReadableCollection extends Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Checks whether an element is contained in the collection.
|
||||
* This is an O(n) operation, where n is the size of the collection.
|
||||
*
|
||||
* @param mixed $element The element to search for.
|
||||
* @psalm-param TMaybeContained $element
|
||||
*
|
||||
* @return bool TRUE if the collection contains the element, FALSE otherwise.
|
||||
* @psalm-return (TMaybeContained is T ? bool : false)
|
||||
*
|
||||
* @template TMaybeContained
|
||||
*/
|
||||
public function contains($element);
|
||||
|
||||
/**
|
||||
* Checks whether the collection is empty (contains no elements).
|
||||
*
|
||||
* @return bool TRUE if the collection is empty, FALSE otherwise.
|
||||
*/
|
||||
public function isEmpty();
|
||||
|
||||
/**
|
||||
* Checks whether the collection contains an element with the specified key/index.
|
||||
*
|
||||
* @param string|int $key The key/index to check for.
|
||||
* @psalm-param TKey $key
|
||||
*
|
||||
* @return bool TRUE if the collection contains an element with the specified key/index,
|
||||
* FALSE otherwise.
|
||||
*/
|
||||
public function containsKey($key);
|
||||
|
||||
/**
|
||||
* Gets the element at the specified key/index.
|
||||
*
|
||||
* @param string|int $key The key/index of the element to retrieve.
|
||||
* @psalm-param TKey $key
|
||||
*
|
||||
* @return mixed
|
||||
* @psalm-return T|null
|
||||
*/
|
||||
public function get($key);
|
||||
|
||||
/**
|
||||
* Gets all keys/indices of the collection.
|
||||
*
|
||||
* @return int[]|string[] The keys/indices of the collection, in the order of the corresponding
|
||||
* elements in the collection.
|
||||
* @psalm-return list<TKey>
|
||||
*/
|
||||
public function getKeys();
|
||||
|
||||
/**
|
||||
* Gets all values of the collection.
|
||||
*
|
||||
* @return mixed[] The values of all elements in the collection, in the
|
||||
* order they appear in the collection.
|
||||
* @psalm-return list<T>
|
||||
*/
|
||||
public function getValues();
|
||||
|
||||
/**
|
||||
* Gets a native PHP array representation of the collection.
|
||||
*
|
||||
* @return mixed[]
|
||||
* @psalm-return array<TKey,T>
|
||||
*/
|
||||
public function toArray();
|
||||
|
||||
/**
|
||||
* Sets the internal iterator to the first element in the collection and returns this element.
|
||||
*
|
||||
* @return mixed
|
||||
* @psalm-return T|false
|
||||
*/
|
||||
public function first();
|
||||
|
||||
/**
|
||||
* Sets the internal iterator to the last element in the collection and returns this element.
|
||||
*
|
||||
* @return mixed
|
||||
* @psalm-return T|false
|
||||
*/
|
||||
public function last();
|
||||
|
||||
/**
|
||||
* Gets the key/index of the element at the current iterator position.
|
||||
*
|
||||
* @return int|string|null
|
||||
* @psalm-return TKey|null
|
||||
*/
|
||||
public function key();
|
||||
|
||||
/**
|
||||
* Gets the element of the collection at the current iterator position.
|
||||
*
|
||||
* @return mixed
|
||||
* @psalm-return T|false
|
||||
*/
|
||||
public function current();
|
||||
|
||||
/**
|
||||
* Moves the internal iterator position to the next element and returns this element.
|
||||
*
|
||||
* @return mixed
|
||||
* @psalm-return T|false
|
||||
*/
|
||||
public function next();
|
||||
|
||||
/**
|
||||
* Extracts a slice of $length elements starting at position $offset from the Collection.
|
||||
*
|
||||
* If $length is null it returns all elements from $offset to the end of the Collection.
|
||||
* Keys have to be preserved by this method. Calling this method will only return the
|
||||
* selected slice and NOT change the elements contained in the collection slice is called on.
|
||||
*
|
||||
* @param int $offset The offset to start from.
|
||||
* @param int|null $length The maximum number of elements to return, or null for no limit.
|
||||
*
|
||||
* @return mixed[]
|
||||
* @psalm-return array<TKey,T>
|
||||
*/
|
||||
public function slice($offset, $length = null);
|
||||
|
||||
/**
|
||||
* Tests for the existence of an element that satisfies the given predicate.
|
||||
*
|
||||
* @param Closure $p The predicate.
|
||||
* @psalm-param Closure(TKey, T):bool $p
|
||||
*
|
||||
* @return bool TRUE if the predicate is TRUE for at least one element, FALSE otherwise.
|
||||
*/
|
||||
public function exists(Closure $p);
|
||||
|
||||
/**
|
||||
* Returns all the elements of this collection that satisfy the predicate p.
|
||||
* The order of the elements is preserved.
|
||||
*
|
||||
* @param Closure $p The predicate used for filtering.
|
||||
* @psalm-param Closure(T):bool $p
|
||||
*
|
||||
* @return ReadableCollection<mixed> A collection with the results of the filter operation.
|
||||
* @psalm-return ReadableCollection<TKey, T>
|
||||
*/
|
||||
public function filter(Closure $p);
|
||||
|
||||
/**
|
||||
* Applies the given function to each element in the collection and returns
|
||||
* a new collection with the elements returned by the function.
|
||||
*
|
||||
* @psalm-param Closure(T):U $func
|
||||
*
|
||||
* @return Collection<mixed>
|
||||
* @psalm-return Collection<TKey, U>
|
||||
*
|
||||
* @psalm-template U
|
||||
*/
|
||||
public function map(Closure $func);
|
||||
|
||||
/**
|
||||
* Partitions this collection in two collections according to a predicate.
|
||||
* Keys are preserved in the resulting collections.
|
||||
*
|
||||
* @param Closure $p The predicate on which to partition.
|
||||
* @psalm-param Closure(TKey, T):bool $p
|
||||
*
|
||||
* @return ReadableCollection<mixed>[] An array with two elements. The first element contains the collection
|
||||
* of elements where the predicate returned TRUE, the second element
|
||||
* contains the collection of elements where the predicate returned FALSE.
|
||||
* @psalm-return array{0: ReadableCollection<TKey, T>, 1: ReadableCollection<TKey, T>}
|
||||
*/
|
||||
public function partition(Closure $p);
|
||||
|
||||
/**
|
||||
* Tests whether the given predicate p holds for all elements of this collection.
|
||||
*
|
||||
* @param Closure $p The predicate.
|
||||
* @psalm-param Closure(TKey, T):bool $p
|
||||
*
|
||||
* @return bool TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.
|
||||
*/
|
||||
public function forAll(Closure $p);
|
||||
|
||||
/**
|
||||
* Gets the index/key of a given element. The comparison of two elements is strict,
|
||||
* that means not only the value but also the type must match.
|
||||
* For objects this means reference equality.
|
||||
*
|
||||
* @param mixed $element The element to search for.
|
||||
* @psalm-param TMaybeContained $element
|
||||
*
|
||||
* @return int|string|bool The key/index of the element or FALSE if the element was not found.
|
||||
* @psalm-return (TMaybeContained is T ? TKey|false : false)
|
||||
*
|
||||
* @template TMaybeContained
|
||||
*/
|
||||
public function indexOf($element);
|
||||
}
|
30
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php
vendored
Normal file
30
vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Common\Collections;
|
||||
|
||||
/**
|
||||
* Interface for collections that allow efficient filtering with an expression API.
|
||||
*
|
||||
* Goal of this interface is a backend independent method to fetch elements
|
||||
* from a collections. {@link Expression} is crafted in a way that you can
|
||||
* implement queries from both in-memory and database-backed collections.
|
||||
*
|
||||
* For database backed collections this allows very efficient access by
|
||||
* utilizing the query APIs, for example SQL in the ORM. Applications using
|
||||
* this API can implement efficient database access without having to ask the
|
||||
* EntityManager or Repositories.
|
||||
*
|
||||
* @psalm-template TKey as array-key
|
||||
* @psalm-template T
|
||||
*/
|
||||
interface Selectable
|
||||
{
|
||||
/**
|
||||
* Selects all elements from a selectable that match the expression and
|
||||
* returns a new collection containing these elements.
|
||||
*
|
||||
* @return Collection<mixed>&Selectable<mixed>
|
||||
* @psalm-return Collection<TKey,T>&Selectable<TKey,T>
|
||||
*/
|
||||
public function matching(Criteria $criteria);
|
||||
}
|
19
vendor/doctrine/deprecations/LICENSE
vendored
Normal file
19
vendor/doctrine/deprecations/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2020-2021 Doctrine Project
|
||||
|
||||
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.
|
32
vendor/doctrine/deprecations/composer.json
vendored
Normal file
32
vendor/doctrine/deprecations/composer.json
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
"type": "library",
|
||||
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
|
||||
"homepage": "https://www.doctrine-project.org/",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.1|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.5",
|
||||
"psr/log": "^1|^2|^3",
|
||||
"doctrine/coding-standard": "^9"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"DeprecationTests\\": "test_fixtures/src",
|
||||
"Doctrine\\Foo\\": "test_fixtures/vendor/doctrine/foo"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
}
|
||||
}
|
||||
}
|
266
vendor/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php
vendored
Normal file
266
vendor/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php
vendored
Normal file
@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Deprecations;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_reduce;
|
||||
use function debug_backtrace;
|
||||
use function sprintf;
|
||||
use function strpos;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
use function trigger_error;
|
||||
|
||||
use const DEBUG_BACKTRACE_IGNORE_ARGS;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Manages Deprecation logging in different ways.
|
||||
*
|
||||
* By default triggered exceptions are not logged.
|
||||
*
|
||||
* To enable different deprecation logging mechanisms you can call the
|
||||
* following methods:
|
||||
*
|
||||
* - Minimal collection of deprecations via getTriggeredDeprecations()
|
||||
* \Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
|
||||
*
|
||||
* - Uses @trigger_error with E_USER_DEPRECATED
|
||||
* \Doctrine\Deprecations\Deprecation::enableWithTriggerError();
|
||||
*
|
||||
* - Sends deprecation messages via a PSR-3 logger
|
||||
* \Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
|
||||
*
|
||||
* Packages that trigger deprecations should use the `trigger()` or
|
||||
* `triggerIfCalledFromOutside()` methods.
|
||||
*/
|
||||
class Deprecation
|
||||
{
|
||||
private const TYPE_NONE = 0;
|
||||
private const TYPE_TRACK_DEPRECATIONS = 1;
|
||||
private const TYPE_TRIGGER_ERROR = 2;
|
||||
private const TYPE_PSR_LOGGER = 4;
|
||||
|
||||
/** @var int */
|
||||
private static $type = self::TYPE_NONE;
|
||||
|
||||
/** @var LoggerInterface|null */
|
||||
private static $logger;
|
||||
|
||||
/** @var array<string,bool> */
|
||||
private static $ignoredPackages = [];
|
||||
|
||||
/** @var array<string,int> */
|
||||
private static $ignoredLinks = [];
|
||||
|
||||
/** @var bool */
|
||||
private static $deduplication = true;
|
||||
|
||||
/**
|
||||
* Trigger a deprecation for the given package and identfier.
|
||||
*
|
||||
* The link should point to a Github issue or Wiki entry detailing the
|
||||
* deprecation. It is additionally used to de-duplicate the trigger of the
|
||||
* same deprecation during a request.
|
||||
*
|
||||
* @param mixed $args
|
||||
*/
|
||||
public static function trigger(string $package, string $link, string $message, ...$args): void
|
||||
{
|
||||
if (self::$type === self::TYPE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_key_exists($link, self::$ignoredLinks)) {
|
||||
self::$ignoredLinks[$link]++;
|
||||
} else {
|
||||
self::$ignoredLinks[$link] = 1;
|
||||
}
|
||||
|
||||
if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset(self::$ignoredPackages[$package])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
|
||||
$message = sprintf($message, ...$args);
|
||||
|
||||
self::delegateTriggerToBackend($message, $backtrace, $link, $package);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a deprecation for the given package and identifier when called from outside.
|
||||
*
|
||||
* "Outside" means we assume that $package is currently installed as a
|
||||
* dependency and the caller is not a file in that package. When $package
|
||||
* is installed as a root package then deprecations triggered from the
|
||||
* tests folder are also considered "outside".
|
||||
*
|
||||
* This deprecation method assumes that you are using Composer to install
|
||||
* the dependency and are using the default /vendor/ folder and not a
|
||||
* Composer plugin to change the install location. The assumption is also
|
||||
* that $package is the exact composer packge name.
|
||||
*
|
||||
* Compared to {@link trigger()} this method causes some overhead when
|
||||
* deprecation tracking is enabled even during deduplication, because it
|
||||
* needs to call {@link debug_backtrace()}
|
||||
*
|
||||
* @param mixed $args
|
||||
*/
|
||||
public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args): void
|
||||
{
|
||||
if (self::$type === self::TYPE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
|
||||
// first check that the caller is not from a tests folder, in which case we always let deprecations pass
|
||||
if (strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) {
|
||||
$path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package . DIRECTORY_SEPARATOR;
|
||||
|
||||
if (strpos($backtrace[0]['file'], $path) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($backtrace[1]['file'], $path) !== false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($link, self::$ignoredLinks)) {
|
||||
self::$ignoredLinks[$link]++;
|
||||
} else {
|
||||
self::$ignoredLinks[$link] = 1;
|
||||
}
|
||||
|
||||
if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset(self::$ignoredPackages[$package])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message = sprintf($message, ...$args);
|
||||
|
||||
self::delegateTriggerToBackend($message, $backtrace, $link, $package);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $backtrace
|
||||
*/
|
||||
private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void
|
||||
{
|
||||
if ((self::$type & self::TYPE_PSR_LOGGER) > 0) {
|
||||
$context = [
|
||||
'file' => $backtrace[0]['file'],
|
||||
'line' => $backtrace[0]['line'],
|
||||
'package' => $package,
|
||||
'link' => $link,
|
||||
];
|
||||
|
||||
self::$logger->notice($message, $context);
|
||||
}
|
||||
|
||||
if (! ((self::$type & self::TYPE_TRIGGER_ERROR) > 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message .= sprintf(
|
||||
' (%s:%d called by %s:%d, %s, package %s)',
|
||||
self::basename($backtrace[0]['file']),
|
||||
$backtrace[0]['line'],
|
||||
self::basename($backtrace[1]['file']),
|
||||
$backtrace[1]['line'],
|
||||
$link,
|
||||
$package
|
||||
);
|
||||
|
||||
@trigger_error($message, E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-local-aware version of PHPs basename function.
|
||||
*/
|
||||
private static function basename(string $filename): string
|
||||
{
|
||||
$pos = strrpos($filename, DIRECTORY_SEPARATOR);
|
||||
|
||||
if ($pos === false) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
return substr($filename, $pos + 1);
|
||||
}
|
||||
|
||||
public static function enableTrackingDeprecations(): void
|
||||
{
|
||||
self::$type |= self::TYPE_TRACK_DEPRECATIONS;
|
||||
}
|
||||
|
||||
public static function enableWithTriggerError(): void
|
||||
{
|
||||
self::$type |= self::TYPE_TRIGGER_ERROR;
|
||||
}
|
||||
|
||||
public static function enableWithPsrLogger(LoggerInterface $logger): void
|
||||
{
|
||||
self::$type |= self::TYPE_PSR_LOGGER;
|
||||
self::$logger = $logger;
|
||||
}
|
||||
|
||||
public static function withoutDeduplication(): void
|
||||
{
|
||||
self::$deduplication = false;
|
||||
}
|
||||
|
||||
public static function disable(): void
|
||||
{
|
||||
self::$type = self::TYPE_NONE;
|
||||
self::$logger = null;
|
||||
self::$deduplication = true;
|
||||
|
||||
foreach (self::$ignoredLinks as $link => $count) {
|
||||
self::$ignoredLinks[$link] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static function ignorePackage(string $packageName): void
|
||||
{
|
||||
self::$ignoredPackages[$packageName] = true;
|
||||
}
|
||||
|
||||
public static function ignoreDeprecations(string ...$links): void
|
||||
{
|
||||
foreach ($links as $link) {
|
||||
self::$ignoredLinks[$link] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getUniqueTriggeredDeprecationsCount(): int
|
||||
{
|
||||
return array_reduce(self::$ignoredLinks, static function (int $carry, int $count) {
|
||||
return $carry + $count;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns each triggered deprecation link identifier and the amount of occurrences.
|
||||
*
|
||||
* @return array<string,int>
|
||||
*/
|
||||
public static function getTriggeredDeprecations(): array
|
||||
{
|
||||
return self::$ignoredLinks;
|
||||
}
|
||||
}
|
66
vendor/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php
vendored
Normal file
66
vendor/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Deprecations\PHPUnit;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
trait VerifyDeprecations
|
||||
{
|
||||
/** @var array<string,int> */
|
||||
private $doctrineDeprecationsExpectations = [];
|
||||
|
||||
/** @var array<string,int> */
|
||||
private $doctrineNoDeprecationsExpectations = [];
|
||||
|
||||
public function expectDeprecationWithIdentifier(string $identifier): void
|
||||
{
|
||||
$this->doctrineDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
||||
}
|
||||
|
||||
public function expectNoDeprecationWithIdentifier(string $identifier): void
|
||||
{
|
||||
$this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @before
|
||||
*/
|
||||
public function enableDeprecationTracking(): void
|
||||
{
|
||||
Deprecation::enableTrackingDeprecations();
|
||||
}
|
||||
|
||||
/**
|
||||
* @after
|
||||
*/
|
||||
public function verifyDeprecationsAreTriggered(): void
|
||||
{
|
||||
foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) {
|
||||
$actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
||||
|
||||
$this->assertTrue(
|
||||
$actualCount > $expectation,
|
||||
sprintf(
|
||||
"Expected deprecation with identifier '%s' was not triggered by code executed in test.",
|
||||
$identifier
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this->doctrineNoDeprecationsExpectations as $identifier => $expectation) {
|
||||
$actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
||||
|
||||
$this->assertTrue(
|
||||
$actualCount === $expectation,
|
||||
sprintf(
|
||||
"Expected deprecation with identifier '%s' was triggered by code executed in test, but expected not to.",
|
||||
$identifier
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
19
vendor/dragonmantank/cron-expression/LICENSE
vendored
Normal file
19
vendor/dragonmantank/cron-expression/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2011 Michael Dowling <mtdowling@gmail.com>, 2016 Chris Tankersley <chris@ctankersley.com>, and contributors
|
||||
|
||||
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.
|
47
vendor/dragonmantank/cron-expression/composer.json
vendored
Normal file
47
vendor/dragonmantank/cron-expression/composer.json
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "dragonmantank/cron-expression",
|
||||
"type": "library",
|
||||
"description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
|
||||
"keywords": ["cron", "schedule"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Chris Tankersley",
|
||||
"email": "chris@ctankersley.com",
|
||||
"homepage": "https://github.com/dragonmantank"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2|^8.0",
|
||||
"webmozart/assert": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"phpunit/phpunit": "^7.0|^8.0|^9.0",
|
||||
"phpstan/phpstan-webmozart-assert": "^1.0",
|
||||
"phpstan/extension-installer": "^1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cron\\": "src/Cron/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Cron\\Tests\\": "tests/Cron/"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"mtdowling/cron-expression": "^1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"phpstan": "./vendor/bin/phpstan analyze",
|
||||
"test": "phpunit"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"ocramius/package-versions": true,
|
||||
"phpstan/extension-installer": true
|
||||
}
|
||||
}
|
||||
}
|
346
vendor/dragonmantank/cron-expression/src/Cron/AbstractField.php
vendored
Normal file
346
vendor/dragonmantank/cron-expression/src/Cron/AbstractField.php
vendored
Normal file
@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cron;
|
||||
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
* Abstract CRON expression field.
|
||||
*/
|
||||
abstract class AbstractField implements FieldInterface
|
||||
{
|
||||
/**
|
||||
* Full range of values that are allowed for this field type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fullRange = [];
|
||||
|
||||
/**
|
||||
* Literal values we need to convert to integers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $literals = [];
|
||||
|
||||
/**
|
||||
* Start value of the full range.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $rangeStart;
|
||||
|
||||
/**
|
||||
* End value of the full range.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $rangeEnd;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->fullRange = range($this->rangeStart, $this->rangeEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a field is satisfied by a value.
|
||||
*
|
||||
* @internal
|
||||
* @param int $dateValue Date value to check
|
||||
* @param string $value Value to test
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSatisfied(int $dateValue, string $value): bool
|
||||
{
|
||||
if ($this->isIncrementsOfRanges($value)) {
|
||||
return $this->isInIncrementsOfRanges($dateValue, $value);
|
||||
}
|
||||
|
||||
if ($this->isRange($value)) {
|
||||
return $this->isInRange($dateValue, $value);
|
||||
}
|
||||
|
||||
return '*' === $value || $dateValue === (int) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is a range.
|
||||
*
|
||||
* @internal
|
||||
* @param string $value Value to test
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRange(string $value): bool
|
||||
{
|
||||
return false !== strpos($value, '-');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is an increments of ranges.
|
||||
*
|
||||
* @internal
|
||||
* @param string $value Value to test
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isIncrementsOfRanges(string $value): bool
|
||||
{
|
||||
return false !== strpos($value, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a value is within a range.
|
||||
*
|
||||
* @internal
|
||||
* @param int $dateValue Set date value
|
||||
* @param string $value Value to test
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInRange(int $dateValue, $value): bool
|
||||
{
|
||||
$parts = array_map(
|
||||
function ($value) {
|
||||
$value = trim($value);
|
||||
|
||||
return $this->convertLiterals($value);
|
||||
},
|
||||
explode('-', $value, 2)
|
||||
);
|
||||
|
||||
return $dateValue >= $parts[0] && $dateValue <= $parts[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a value is within an increments of ranges (offset[-to]/step size).
|
||||
*
|
||||
* @internal
|
||||
* @param int $dateValue Set date value
|
||||
* @param string $value Value to test
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInIncrementsOfRanges(int $dateValue, string $value): bool
|
||||
{
|
||||
$chunks = array_map('trim', explode('/', $value, 2));
|
||||
$range = $chunks[0];
|
||||
$step = $chunks[1] ?? 0;
|
||||
|
||||
// No step or 0 steps aren't cool
|
||||
/** @phpstan-ignore-next-line */
|
||||
if (null === $step || '0' === $step || 0 === $step) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Expand the * to a full range
|
||||
if ('*' === $range) {
|
||||
$range = $this->rangeStart . '-' . $this->rangeEnd;
|
||||
}
|
||||
|
||||
// Generate the requested small range
|
||||
$rangeChunks = explode('-', $range, 2);
|
||||
$rangeStart = (int) $rangeChunks[0];
|
||||
$rangeEnd = $rangeChunks[1] ?? $rangeStart;
|
||||
$rangeEnd = (int) $rangeEnd;
|
||||
|
||||
if ($rangeStart < $this->rangeStart || $rangeStart > $this->rangeEnd || $rangeStart > $rangeEnd) {
|
||||
throw new \OutOfRangeException('Invalid range start requested');
|
||||
}
|
||||
|
||||
if ($rangeEnd < $this->rangeStart || $rangeEnd > $this->rangeEnd || $rangeEnd < $rangeStart) {
|
||||
throw new \OutOfRangeException('Invalid range end requested');
|
||||
}
|
||||
|
||||
// Steps larger than the range need to wrap around and be handled
|
||||
// slightly differently than smaller steps
|
||||
|
||||
// UPDATE - This is actually false. The C implementation will allow a
|
||||
// larger step as valid syntax, it never wraps around. It will stop
|
||||
// once it hits the end. Unfortunately this means in future versions
|
||||
// we will not wrap around. However, because the logic exists today
|
||||
// per the above documentation, fixing the bug from #89
|
||||
if ($step > $this->rangeEnd) {
|
||||
$thisRange = [$this->fullRange[$step % \count($this->fullRange)]];
|
||||
} else {
|
||||
if ($step > ($rangeEnd - $rangeStart)) {
|
||||
$thisRange[$rangeStart] = (int) $rangeStart;
|
||||
} else {
|
||||
$thisRange = range($rangeStart, $rangeEnd, (int) $step);
|
||||
}
|
||||
}
|
||||
|
||||
return \in_array($dateValue, $thisRange, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a range of values for the given cron expression.
|
||||
*
|
||||
* @param string $expression The expression to evaluate
|
||||
* @param int $max Maximum offset for range
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRangeForExpression(string $expression, int $max): array
|
||||
{
|
||||
$values = [];
|
||||
$expression = $this->convertLiterals($expression);
|
||||
|
||||
if (false !== strpos($expression, ',')) {
|
||||
$ranges = explode(',', $expression);
|
||||
$values = [];
|
||||
foreach ($ranges as $range) {
|
||||
$expanded = $this->getRangeForExpression($range, $this->rangeEnd);
|
||||
$values = array_merge($values, $expanded);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ($this->isRange($expression) || $this->isIncrementsOfRanges($expression)) {
|
||||
if (!$this->isIncrementsOfRanges($expression)) {
|
||||
[$offset, $to] = explode('-', $expression);
|
||||
$offset = $this->convertLiterals($offset);
|
||||
$to = $this->convertLiterals($to);
|
||||
$stepSize = 1;
|
||||
} else {
|
||||
$range = array_map('trim', explode('/', $expression, 2));
|
||||
$stepSize = $range[1] ?? 0;
|
||||
$range = $range[0];
|
||||
$range = explode('-', $range, 2);
|
||||
$offset = $range[0];
|
||||
$to = $range[1] ?? $max;
|
||||
}
|
||||
$offset = '*' === $offset ? $this->rangeStart : $offset;
|
||||
if ($stepSize >= $this->rangeEnd) {
|
||||
$values = [$this->fullRange[$stepSize % \count($this->fullRange)]];
|
||||
} else {
|
||||
for ($i = $offset; $i <= $to; $i += $stepSize) {
|
||||
$values[] = (int) $i;
|
||||
}
|
||||
}
|
||||
sort($values);
|
||||
} else {
|
||||
$values = [$expression];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert literal.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function convertLiterals(string $value): string
|
||||
{
|
||||
if (\count($this->literals)) {
|
||||
$key = array_search(strtoupper($value), $this->literals, true);
|
||||
if (false !== $key) {
|
||||
return (string) $key;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a value is valid for the field.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(string $value): bool
|
||||
{
|
||||
$value = $this->convertLiterals($value);
|
||||
|
||||
// All fields allow * as a valid value
|
||||
if ('*' === $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate each chunk of a list individually
|
||||
if (false !== strpos($value, ',')) {
|
||||
foreach (explode(',', $value) as $listItem) {
|
||||
if (!$this->validate($listItem)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (false !== strpos($value, '/')) {
|
||||
[$range, $step] = explode('/', $value);
|
||||
|
||||
// Don't allow numeric ranges
|
||||
if (is_numeric($range)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->validate($range) && filter_var($step, FILTER_VALIDATE_INT);
|
||||
}
|
||||
|
||||
if (false !== strpos($value, '-')) {
|
||||
if (substr_count($value, '-') > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$chunks = explode('-', $value);
|
||||
$chunks[0] = $this->convertLiterals($chunks[0]);
|
||||
$chunks[1] = $this->convertLiterals($chunks[1]);
|
||||
|
||||
if ('*' === $chunks[0] || '*' === $chunks[1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->validate($chunks[0]) && $this->validate($chunks[1]);
|
||||
}
|
||||
|
||||
if (!is_numeric($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false !== strpos($value, '.')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should have a numeric by now, so coerce this into an integer
|
||||
$value = (int) $value;
|
||||
|
||||
return \in_array($value, $this->fullRange, true);
|
||||
}
|
||||
|
||||
protected function timezoneSafeModify(DateTimeInterface $dt, string $modification): DateTimeInterface
|
||||
{
|
||||
$timezone = $dt->getTimezone();
|
||||
$dt = $dt->setTimezone(new \DateTimeZone("UTC"));
|
||||
$dt = $dt->modify($modification);
|
||||
$dt = $dt->setTimezone($timezone);
|
||||
return $dt;
|
||||
}
|
||||
|
||||
protected function setTimeHour(DateTimeInterface $date, bool $invert, int $originalTimestamp): DateTimeInterface
|
||||
{
|
||||
$date = $date->setTime((int)$date->format('H'), ($invert ? 59 : 0));
|
||||
|
||||
// setTime caused the offset to change, moving time in the wrong direction
|
||||
$actualTimestamp = $date->format('U');
|
||||
if ((! $invert) && ($actualTimestamp <= $originalTimestamp)) {
|
||||
$date = $this->timezoneSafeModify($date, "+1 hour");
|
||||
} elseif ($invert && ($actualTimestamp >= $originalTimestamp)) {
|
||||
$date = $this->timezoneSafeModify($date, "-1 hour");
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
568
vendor/dragonmantank/cron-expression/src/Cron/CronExpression.php
vendored
Normal file
568
vendor/dragonmantank/cron-expression/src/Cron/CronExpression.php
vendored
Normal file
@ -0,0 +1,568 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cron;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use RuntimeException;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* CRON expression parser that can determine whether or not a CRON expression is
|
||||
* due to run, the next run date and previous run date of a CRON expression.
|
||||
* The determinations made by this class are accurate if checked run once per
|
||||
* minute (seconds are dropped from date time comparisons).
|
||||
*
|
||||
* Schedule parts must map to:
|
||||
* minute [0-59], hour [0-23], day of month, month [1-12|JAN-DEC], day of week
|
||||
* [1-7|MON-SUN], and an optional year.
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/Cron
|
||||
*/
|
||||
class CronExpression
|
||||
{
|
||||
public const MINUTE = 0;
|
||||
public const HOUR = 1;
|
||||
public const DAY = 2;
|
||||
public const MONTH = 3;
|
||||
public const WEEKDAY = 4;
|
||||
|
||||
/** @deprecated */
|
||||
public const YEAR = 5;
|
||||
|
||||
public const MAPPINGS = [
|
||||
'@yearly' => '0 0 1 1 *',
|
||||
'@annually' => '0 0 1 1 *',
|
||||
'@monthly' => '0 0 1 * *',
|
||||
'@weekly' => '0 0 * * 0',
|
||||
'@daily' => '0 0 * * *',
|
||||
'@midnight' => '0 0 * * *',
|
||||
'@hourly' => '0 * * * *',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array CRON expression parts
|
||||
*/
|
||||
protected $cronParts;
|
||||
|
||||
/**
|
||||
* @var FieldFactoryInterface CRON field factory
|
||||
*/
|
||||
protected $fieldFactory;
|
||||
|
||||
/**
|
||||
* @var int Max iteration count when searching for next run date
|
||||
*/
|
||||
protected $maxIterationCount = 1000;
|
||||
|
||||
/**
|
||||
* @var array Order in which to test of cron parts
|
||||
*/
|
||||
protected static $order = [
|
||||
self::YEAR,
|
||||
self::MONTH,
|
||||
self::DAY,
|
||||
self::WEEKDAY,
|
||||
self::HOUR,
|
||||
self::MINUTE,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private static $registeredAliases = self::MAPPINGS;
|
||||
|
||||
/**
|
||||
* Registered a user defined CRON Expression Alias.
|
||||
*
|
||||
* @throws LogicException If the expression or the alias name are invalid
|
||||
* or if the alias is already registered.
|
||||
*/
|
||||
public static function registerAlias(string $alias, string $expression): void
|
||||
{
|
||||
try {
|
||||
new self($expression);
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
throw new LogicException("The expression `$expression` is invalid", 0, $exception);
|
||||
}
|
||||
|
||||
$shortcut = strtolower($alias);
|
||||
if (1 !== preg_match('/^@\w+$/', $shortcut)) {
|
||||
throw new LogicException("The alias `$alias` is invalid. It must start with an `@` character and contain alphanumeric (letters, numbers, regardless of case) plus underscore (_).");
|
||||
}
|
||||
|
||||
if (isset(self::$registeredAliases[$shortcut])) {
|
||||
throw new LogicException("The alias `$alias` is already registered.");
|
||||
}
|
||||
|
||||
self::$registeredAliases[$shortcut] = $expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregistered a user defined CRON Expression Alias.
|
||||
*
|
||||
* @throws LogicException If the user tries to unregister a built-in alias
|
||||
*/
|
||||
public static function unregisterAlias(string $alias): bool
|
||||
{
|
||||
$shortcut = strtolower($alias);
|
||||
if (isset(self::MAPPINGS[$shortcut])) {
|
||||
throw new LogicException("The alias `$alias` is a built-in alias; it can not be unregistered.");
|
||||
}
|
||||
|
||||
if (!isset(self::$registeredAliases[$shortcut])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset(self::$registeredAliases[$shortcut]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether a CRON Expression alias is registered.
|
||||
*/
|
||||
public static function supportsAlias(string $alias): bool
|
||||
{
|
||||
return isset(self::$registeredAliases[strtolower($alias)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all registered aliases as an associated array where the aliases are the key
|
||||
* and their associated expressions are the values.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function getAliases(): array
|
||||
{
|
||||
return self::$registeredAliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.0.2, use __construct instead.
|
||||
*/
|
||||
public static function factory(string $expression, FieldFactoryInterface $fieldFactory = null): CronExpression
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
return new static($expression, $fieldFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a CronExpression.
|
||||
*
|
||||
* @param string $expression the CRON expression to validate
|
||||
*
|
||||
* @return bool True if a valid CRON expression was passed. False if not.
|
||||
*/
|
||||
public static function isValidExpression(string $expression): bool
|
||||
{
|
||||
try {
|
||||
new CronExpression($expression);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a CRON expression.
|
||||
*
|
||||
* @param string $expression CRON expression (e.g. '8 * * * *')
|
||||
* @param null|FieldFactoryInterface $fieldFactory Factory to create cron fields
|
||||
*/
|
||||
public function __construct(string $expression, FieldFactoryInterface $fieldFactory = null)
|
||||
{
|
||||
$shortcut = strtolower($expression);
|
||||
$expression = self::$registeredAliases[$shortcut] ?? $expression;
|
||||
|
||||
$this->fieldFactory = $fieldFactory ?: new FieldFactory();
|
||||
$this->setExpression($expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or change the CRON expression.
|
||||
*
|
||||
* @param string $value CRON expression (e.g. 8 * * * *)
|
||||
*
|
||||
* @throws \InvalidArgumentException if not a valid CRON expression
|
||||
*
|
||||
* @return CronExpression
|
||||
*/
|
||||
public function setExpression(string $value): CronExpression
|
||||
{
|
||||
$split = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
Assert::isArray($split);
|
||||
|
||||
$this->cronParts = $split;
|
||||
if (\count($this->cronParts) < 5) {
|
||||
throw new InvalidArgumentException(
|
||||
$value . ' is not a valid CRON expression'
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this->cronParts as $position => $part) {
|
||||
$this->setPart($position, $part);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set part of the CRON expression.
|
||||
*
|
||||
* @param int $position The position of the CRON expression to set
|
||||
* @param string $value The value to set
|
||||
*
|
||||
* @throws \InvalidArgumentException if the value is not valid for the part
|
||||
*
|
||||
* @return CronExpression
|
||||
*/
|
||||
public function setPart(int $position, string $value): CronExpression
|
||||
{
|
||||
if (!$this->fieldFactory->getField($position)->validate($value)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid CRON field value ' . $value . ' at position ' . $position
|
||||
);
|
||||
}
|
||||
|
||||
$this->cronParts[$position] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set max iteration count for searching next run dates.
|
||||
*
|
||||
* @param int $maxIterationCount Max iteration count when searching for next run date
|
||||
*
|
||||
* @return CronExpression
|
||||
*/
|
||||
public function setMaxIterationCount(int $maxIterationCount): CronExpression
|
||||
{
|
||||
$this->maxIterationCount = $maxIterationCount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a next run date relative to the current date or a specific date
|
||||
*
|
||||
* @param string|\DateTimeInterface $currentTime Relative calculation date
|
||||
* @param int $nth Number of matches to skip before returning a
|
||||
* matching next run date. 0, the default, will return the
|
||||
* current date and time if the next run date falls on the
|
||||
* current date and time. Setting this value to 1 will
|
||||
* skip the first match and go to the second match.
|
||||
* Setting this value to 2 will skip the first 2
|
||||
* matches and so on.
|
||||
* @param bool $allowCurrentDate Set to TRUE to return the current date if
|
||||
* it matches the cron expression.
|
||||
* @param null|string $timeZone TimeZone to use instead of the system default
|
||||
*
|
||||
* @throws \RuntimeException on too many iterations
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getNextRunDate($currentTime = 'now', int $nth = 0, bool $allowCurrentDate = false, $timeZone = null): DateTime
|
||||
{
|
||||
return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate, $timeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a previous run date relative to the current date or a specific date.
|
||||
*
|
||||
* @param string|\DateTimeInterface $currentTime Relative calculation date
|
||||
* @param int $nth Number of matches to skip before returning
|
||||
* @param bool $allowCurrentDate Set to TRUE to return the
|
||||
* current date if it matches the cron expression
|
||||
* @param null|string $timeZone TimeZone to use instead of the system default
|
||||
*
|
||||
* @throws \RuntimeException on too many iterations
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return \DateTime
|
||||
*
|
||||
* @see \Cron\CronExpression::getNextRunDate
|
||||
*/
|
||||
public function getPreviousRunDate($currentTime = 'now', int $nth = 0, bool $allowCurrentDate = false, $timeZone = null): DateTime
|
||||
{
|
||||
return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate, $timeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple run dates starting at the current date or a specific date.
|
||||
*
|
||||
* @param int $total Set the total number of dates to calculate
|
||||
* @param string|\DateTimeInterface|null $currentTime Relative calculation date
|
||||
* @param bool $invert Set to TRUE to retrieve previous dates
|
||||
* @param bool $allowCurrentDate Set to TRUE to return the
|
||||
* current date if it matches the cron expression
|
||||
* @param null|string $timeZone TimeZone to use instead of the system default
|
||||
*
|
||||
* @return \DateTime[] Returns an array of run dates
|
||||
*/
|
||||
public function getMultipleRunDates(int $total, $currentTime = 'now', bool $invert = false, bool $allowCurrentDate = false, $timeZone = null): array
|
||||
{
|
||||
$timeZone = $this->determineTimeZone($currentTime, $timeZone);
|
||||
|
||||
if ('now' === $currentTime) {
|
||||
$currentTime = new DateTime();
|
||||
} elseif ($currentTime instanceof DateTime) {
|
||||
$currentTime = clone $currentTime;
|
||||
} elseif ($currentTime instanceof DateTimeImmutable) {
|
||||
$currentTime = DateTime::createFromFormat('U', $currentTime->format('U'));
|
||||
} elseif (\is_string($currentTime)) {
|
||||
$currentTime = new DateTime($currentTime);
|
||||
}
|
||||
|
||||
Assert::isInstanceOf($currentTime, DateTime::class);
|
||||
$currentTime->setTimezone(new DateTimeZone($timeZone));
|
||||
|
||||
$matches = [];
|
||||
for ($i = 0; $i < $total; ++$i) {
|
||||
try {
|
||||
$result = $this->getRunDate($currentTime, 0, $invert, $allowCurrentDate, $timeZone);
|
||||
} catch (RuntimeException $e) {
|
||||
break;
|
||||
}
|
||||
|
||||
$allowCurrentDate = false;
|
||||
$currentTime = clone $result;
|
||||
$matches[] = $result;
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all or part of the CRON expression.
|
||||
*
|
||||
* @param int|string|null $part specify the part to retrieve or NULL to get the full
|
||||
* cron schedule string
|
||||
*
|
||||
* @return null|string Returns the CRON expression, a part of the
|
||||
* CRON expression, or NULL if the part was specified but not found
|
||||
*/
|
||||
public function getExpression($part = null): ?string
|
||||
{
|
||||
if (null === $part) {
|
||||
return implode(' ', $this->cronParts);
|
||||
}
|
||||
|
||||
if (array_key_exists($part, $this->cronParts)) {
|
||||
return $this->cronParts[$part];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parts of the cron expression as an array.
|
||||
*
|
||||
* @return string[]
|
||||
* The array of parts that make up this expression.
|
||||
*/
|
||||
public function getParts()
|
||||
{
|
||||
return $this->cronParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to output the full expression.
|
||||
*
|
||||
* @return string Full CRON expression
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string) $this->getExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the cron is due to run based on the current date or a
|
||||
* specific date. This method assumes that the current number of
|
||||
* seconds are irrelevant, and should be called once per minute.
|
||||
*
|
||||
* @param string|\DateTimeInterface $currentTime Relative calculation date
|
||||
* @param null|string $timeZone TimeZone to use instead of the system default
|
||||
*
|
||||
* @return bool Returns TRUE if the cron is due to run or FALSE if not
|
||||
*/
|
||||
public function isDue($currentTime = 'now', $timeZone = null): bool
|
||||
{
|
||||
$timeZone = $this->determineTimeZone($currentTime, $timeZone);
|
||||
|
||||
if ('now' === $currentTime) {
|
||||
$currentTime = new DateTime();
|
||||
} elseif ($currentTime instanceof DateTime) {
|
||||
$currentTime = clone $currentTime;
|
||||
} elseif ($currentTime instanceof DateTimeImmutable) {
|
||||
$currentTime = DateTime::createFromFormat('U', $currentTime->format('U'));
|
||||
} elseif (\is_string($currentTime)) {
|
||||
$currentTime = new DateTime($currentTime);
|
||||
}
|
||||
|
||||
Assert::isInstanceOf($currentTime, DateTime::class);
|
||||
$currentTime->setTimezone(new DateTimeZone($timeZone));
|
||||
|
||||
// drop the seconds to 0
|
||||
$currentTime->setTime((int) $currentTime->format('H'), (int) $currentTime->format('i'), 0);
|
||||
|
||||
try {
|
||||
return $this->getNextRunDate($currentTime, 0, true)->getTimestamp() === $currentTime->getTimestamp();
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next or previous run date of the expression relative to a date.
|
||||
*
|
||||
* @param string|\DateTimeInterface|null $currentTime Relative calculation date
|
||||
* @param int $nth Number of matches to skip before returning
|
||||
* @param bool $invert Set to TRUE to go backwards in time
|
||||
* @param bool $allowCurrentDate Set to TRUE to return the
|
||||
* current date if it matches the cron expression
|
||||
* @param string|null $timeZone TimeZone to use instead of the system default
|
||||
*
|
||||
* @throws \RuntimeException on too many iterations
|
||||
* @throws Exception
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
protected function getRunDate($currentTime = null, int $nth = 0, bool $invert = false, bool $allowCurrentDate = false, $timeZone = null): DateTime
|
||||
{
|
||||
$timeZone = $this->determineTimeZone($currentTime, $timeZone);
|
||||
|
||||
if ($currentTime instanceof DateTime) {
|
||||
$currentDate = clone $currentTime;
|
||||
} elseif ($currentTime instanceof DateTimeImmutable) {
|
||||
$currentDate = DateTime::createFromFormat('U', $currentTime->format('U'));
|
||||
} elseif (\is_string($currentTime)) {
|
||||
$currentDate = new DateTime($currentTime);
|
||||
} else {
|
||||
$currentDate = new DateTime('now');
|
||||
}
|
||||
|
||||
Assert::isInstanceOf($currentDate, DateTime::class);
|
||||
$currentDate->setTimezone(new DateTimeZone($timeZone));
|
||||
// Workaround for setTime causing an offset change: https://bugs.php.net/bug.php?id=81074
|
||||
$currentDate = DateTime::createFromFormat("!Y-m-d H:iO", $currentDate->format("Y-m-d H:iP"), $currentDate->getTimezone());
|
||||
if ($currentDate === false) {
|
||||
throw new \RuntimeException('Unable to create date from format');
|
||||
}
|
||||
$currentDate->setTimezone(new DateTimeZone($timeZone));
|
||||
|
||||
$nextRun = clone $currentDate;
|
||||
|
||||
// We don't have to satisfy * or null fields
|
||||
$parts = [];
|
||||
$fields = [];
|
||||
foreach (self::$order as $position) {
|
||||
$part = $this->getExpression($position);
|
||||
if (null === $part || '*' === $part) {
|
||||
continue;
|
||||
}
|
||||
$parts[$position] = $part;
|
||||
$fields[$position] = $this->fieldFactory->getField($position);
|
||||
}
|
||||
|
||||
if (isset($parts[self::DAY]) && isset($parts[self::WEEKDAY])) {
|
||||
$domExpression = sprintf('%s %s %s %s *', $this->getExpression(0), $this->getExpression(1), $this->getExpression(2), $this->getExpression(3));
|
||||
$dowExpression = sprintf('%s %s * %s %s', $this->getExpression(0), $this->getExpression(1), $this->getExpression(3), $this->getExpression(4));
|
||||
|
||||
$domExpression = new self($domExpression);
|
||||
$dowExpression = new self($dowExpression);
|
||||
|
||||
$domRunDates = $domExpression->getMultipleRunDates($nth + 1, $currentTime, $invert, $allowCurrentDate, $timeZone);
|
||||
$dowRunDates = $dowExpression->getMultipleRunDates($nth + 1, $currentTime, $invert, $allowCurrentDate, $timeZone);
|
||||
|
||||
if ($parts[self::DAY] === '?' || $parts[self::DAY] === '*') {
|
||||
$domRunDates = [];
|
||||
}
|
||||
|
||||
if ($parts[self::WEEKDAY] === '?' || $parts[self::WEEKDAY] === '*') {
|
||||
$dowRunDates = [];
|
||||
}
|
||||
|
||||
$combined = array_merge($domRunDates, $dowRunDates);
|
||||
usort($combined, function ($a, $b) {
|
||||
return $a->format('Y-m-d H:i:s') <=> $b->format('Y-m-d H:i:s');
|
||||
});
|
||||
if ($invert) {
|
||||
$combined = array_reverse($combined);
|
||||
}
|
||||
|
||||
return $combined[$nth];
|
||||
}
|
||||
|
||||
// Set a hard limit to bail on an impossible date
|
||||
for ($i = 0; $i < $this->maxIterationCount; ++$i) {
|
||||
foreach ($parts as $position => $part) {
|
||||
$satisfied = false;
|
||||
// Get the field object used to validate this part
|
||||
$field = $fields[$position];
|
||||
// Check if this is singular or a list
|
||||
if (false === strpos($part, ',')) {
|
||||
$satisfied = $field->isSatisfiedBy($nextRun, $part, $invert);
|
||||
} else {
|
||||
foreach (array_map('trim', explode(',', $part)) as $listPart) {
|
||||
if ($field->isSatisfiedBy($nextRun, $listPart, $invert)) {
|
||||
$satisfied = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the field is not satisfied, then start over
|
||||
if (!$satisfied) {
|
||||
$field->increment($nextRun, $invert, $part);
|
||||
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip this match if needed
|
||||
if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) {
|
||||
$this->fieldFactory->getField(self::MINUTE)->increment($nextRun, $invert, $parts[self::MINUTE] ?? null);
|
||||
continue;
|
||||
}
|
||||
|
||||
return $nextRun;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new RuntimeException('Impossible CRON expression');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Workout what timeZone should be used.
|
||||
*
|
||||
* @param string|\DateTimeInterface|null $currentTime Relative calculation date
|
||||
* @param string|null $timeZone TimeZone to use instead of the system default
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function determineTimeZone($currentTime, ?string $timeZone): string
|
||||
{
|
||||
if (null !== $timeZone) {
|
||||
return $timeZone;
|
||||
}
|
||||
|
||||
if ($currentTime instanceof DateTimeInterface) {
|
||||
return $currentTime->getTimezone()->getName();
|
||||
}
|
||||
|
||||
return date_default_timezone_get();
|
||||
}
|
||||
}
|
164
vendor/dragonmantank/cron-expression/src/Cron/DayOfMonthField.php
vendored
Normal file
164
vendor/dragonmantank/cron-expression/src/Cron/DayOfMonthField.php
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cron;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
* Day of month field. Allows: * , / - ? L W.
|
||||
*
|
||||
* 'L' stands for "last" and specifies the last day of the month.
|
||||
*
|
||||
* The 'W' character is used to specify the weekday (Monday-Friday) nearest the
|
||||
* given day. As an example, if you were to specify "15W" as the value for the
|
||||
* day-of-month field, the meaning is: "the nearest weekday to the 15th of the
|
||||
* month". So if the 15th is a Saturday, the trigger will fire on Friday the
|
||||
* 14th. If the 15th is a Sunday, the trigger will fire on Monday the 16th. If
|
||||
* the 15th is a Tuesday, then it will fire on Tuesday the 15th. However if you
|
||||
* specify "1W" as the value for day-of-month, and the 1st is a Saturday, the
|
||||
* trigger will fire on Monday the 3rd, as it will not 'jump' over the boundary
|
||||
* of a month's days. The 'W' character can only be specified when the
|
||||
* day-of-month is a single day, not a range or list of days.
|
||||
*
|
||||
* @author Michael Dowling <mtdowling@gmail.com>
|
||||
*/
|
||||
class DayOfMonthField extends AbstractField
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $rangeStart = 1;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $rangeEnd = 31;
|
||||
|
||||
/**
|
||||
* Get the nearest day of the week for a given day in a month.
|
||||
*
|
||||
* @param int $currentYear Current year
|
||||
* @param int $currentMonth Current month
|
||||
* @param int $targetDay Target day of the month
|
||||
*
|
||||
* @return \DateTime|null Returns the nearest date
|
||||
*/
|
||||
private static function getNearestWeekday(int $currentYear, int $currentMonth, int $targetDay): ?DateTime
|
||||
{
|
||||
$tday = str_pad((string) $targetDay, 2, '0', STR_PAD_LEFT);
|
||||
$target = DateTime::createFromFormat('Y-m-d', "{$currentYear}-{$currentMonth}-{$tday}");
|
||||
|
||||
if ($target === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$currentWeekday = (int) $target->format('N');
|
||||
|
||||
if ($currentWeekday < 6) {
|
||||
return $target;
|
||||
}
|
||||
|
||||
$lastDayOfMonth = $target->format('t');
|
||||
foreach ([-1, 1, -2, 2] as $i) {
|
||||
$adjusted = $targetDay + $i;
|
||||
if ($adjusted > 0 && $adjusted <= $lastDayOfMonth) {
|
||||
$target->setDate($currentYear, $currentMonth, $adjusted);
|
||||
|
||||
if ((int) $target->format('N') < 6 && (int) $target->format('m') === $currentMonth) {
|
||||
return $target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isSatisfiedBy(DateTimeInterface $date, $value, bool $invert): bool
|
||||
{
|
||||
// ? states that the field value is to be skipped
|
||||
if ('?' === $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$fieldValue = $date->format('d');
|
||||
|
||||
// Check to see if this is the last day of the month
|
||||
if ('L' === $value) {
|
||||
return $fieldValue === $date->format('t');
|
||||
}
|
||||
|
||||
// Check to see if this is the nearest weekday to a particular value
|
||||
if ($wPosition = strpos($value, 'W')) {
|
||||
// Parse the target day
|
||||
$targetDay = (int) substr($value, 0, $wPosition);
|
||||
// Find out if the current day is the nearest day of the week
|
||||
$nearest = self::getNearestWeekday(
|
||||
(int) $date->format('Y'),
|
||||
(int) $date->format('m'),
|
||||
$targetDay
|
||||
);
|
||||
if ($nearest) {
|
||||
return $date->format('j') === $nearest->format('j');
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Unable to return nearest weekday');
|
||||
}
|
||||
|
||||
return $this->isSatisfied((int) $date->format('d'), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param \DateTime|\DateTimeImmutable $date
|
||||
*/
|
||||
public function increment(DateTimeInterface &$date, $invert = false, $parts = null): FieldInterface
|
||||
{
|
||||
if (! $invert) {
|
||||
$date = $date->add(new \DateInterval('P1D'));
|
||||
$date = $date->setTime(0, 0);
|
||||
} else {
|
||||
$date = $date->sub(new \DateInterval('P1D'));
|
||||
$date = $date->setTime(23, 59);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(string $value): bool
|
||||
{
|
||||
$basicChecks = parent::validate($value);
|
||||
|
||||
// Validate that a list don't have W or L
|
||||
if (false !== strpos($value, ',') && (false !== strpos($value, 'W') || false !== strpos($value, 'L'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$basicChecks) {
|
||||
if ('?' === $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ('L' === $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (preg_match('/^(.*)W$/', $value, $matches)) {
|
||||
return $this->validate($matches[1]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $basicChecks;
|
||||
}
|
||||
}
|
194
vendor/dragonmantank/cron-expression/src/Cron/DayOfWeekField.php
vendored
Normal file
194
vendor/dragonmantank/cron-expression/src/Cron/DayOfWeekField.php
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cron;
|
||||
|
||||
use DateTimeInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Day of week field. Allows: * / , - ? L #.
|
||||
*
|
||||
* Days of the week can be represented as a number 0-7 (0|7 = Sunday)
|
||||
* or as a three letter string: SUN, MON, TUE, WED, THU, FRI, SAT.
|
||||
*
|
||||
* 'L' stands for "last". It allows you to specify constructs such as
|
||||
* "the last Friday" of a given month.
|
||||
*
|
||||
* '#' is allowed for the day-of-week field, and must be followed by a
|
||||
* number between one and five. It allows you to specify constructs such as
|
||||
* "the second Friday" of a given month.
|
||||
*/
|
||||
class DayOfWeekField extends AbstractField
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $rangeStart = 0;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $rangeEnd = 7;
|
||||
|
||||
/**
|
||||
* @var array Weekday range
|
||||
*/
|
||||
protected $nthRange;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $literals = [1 => 'MON', 2 => 'TUE', 3 => 'WED', 4 => 'THU', 5 => 'FRI', 6 => 'SAT', 7 => 'SUN'];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->nthRange = range(1, 5);
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function isSatisfiedBy(DateTimeInterface $date, $value, bool $invert): bool
|
||||
{
|
||||
if ('?' === $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert text day of the week values to integers
|
||||
$value = $this->convertLiterals($value);
|
||||
|
||||
$currentYear = (int) $date->format('Y');
|
||||
$currentMonth = (int) $date->format('m');
|
||||
$lastDayOfMonth = (int) $date->format('t');
|
||||
|
||||
// Find out if this is the last specific weekday of the month
|
||||
if ($lPosition = strpos($value, 'L')) {
|
||||
$weekday = $this->convertLiterals(substr($value, 0, $lPosition));
|
||||
$weekday %= 7;
|
||||
|
||||
$daysInMonth = (int) $date->format('t');
|
||||
$remainingDaysInMonth = $daysInMonth - (int) $date->format('d');
|
||||
return (($weekday === (int) $date->format('w')) && ($remainingDaysInMonth < 7));
|
||||
}
|
||||
|
||||
// Handle # hash tokens
|
||||
if (strpos($value, '#')) {
|
||||
[$weekday, $nth] = explode('#', $value);
|
||||
|
||||
if (!is_numeric($nth)) {
|
||||
throw new InvalidArgumentException("Hashed weekdays must be numeric, {$nth} given");
|
||||
} else {
|
||||
$nth = (int) $nth;
|
||||
}
|
||||
|
||||
// 0 and 7 are both Sunday, however 7 matches date('N') format ISO-8601
|
||||
if ('0' === $weekday) {
|
||||
$weekday = 7;
|
||||
}
|
||||
|
||||
$weekday = (int) $this->convertLiterals((string) $weekday);
|
||||
|
||||
// Validate the hash fields
|
||||
if ($weekday < 0 || $weekday > 7) {
|
||||
throw new InvalidArgumentException("Weekday must be a value between 0 and 7. {$weekday} given");
|
||||
}
|
||||
|
||||
if (!\in_array($nth, $this->nthRange, true)) {
|
||||
throw new InvalidArgumentException("There are never more than 5 or less than 1 of a given weekday in a month, {$nth} given");
|
||||
}
|
||||
|
||||
// The current weekday must match the targeted weekday to proceed
|
||||
if ((int) $date->format('N') !== $weekday) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tdate = clone $date;
|
||||
$tdate = $tdate->setDate($currentYear, $currentMonth, 1);
|
||||
$dayCount = 0;
|
||||
$currentDay = 1;
|
||||
while ($currentDay < $lastDayOfMonth + 1) {
|
||||
if ((int) $tdate->format('N') === $weekday) {
|
||||
if (++$dayCount >= $nth) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$tdate = $tdate->setDate($currentYear, $currentMonth, ++$currentDay);
|
||||
}
|
||||
|
||||
return (int) $date->format('j') === $currentDay;
|
||||
}
|
||||
|
||||
// Handle day of the week values
|
||||
if (false !== strpos($value, '-')) {
|
||||
$parts = explode('-', $value);
|
||||
if ('7' === $parts[0]) {
|
||||
$parts[0] = 0;
|
||||
} elseif ('0' === $parts[1]) {
|
||||
$parts[1] = 7;
|
||||
}
|
||||
$value = implode('-', $parts);
|
||||
}
|
||||
|
||||
// Test to see which Sunday to use -- 0 == 7 == Sunday
|
||||
$format = \in_array(7, array_map(function ($value) {
|
||||
return (int) $value;
|
||||
}, str_split($value)), true) ? 'N' : 'w';
|
||||
$fieldValue = (int) $date->format($format);
|
||||
|
||||
return $this->isSatisfied($fieldValue, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function increment(DateTimeInterface &$date, $invert = false, $parts = null): FieldInterface
|
||||
{
|
||||
if (! $invert) {
|
||||
$date = $date->add(new \DateInterval('P1D'));
|
||||
$date = $date->setTime(0, 0);
|
||||
} else {
|
||||
$date = $date->sub(new \DateInterval('P1D'));
|
||||
$date = $date->setTime(23, 59);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(string $value): bool
|
||||
{
|
||||
$basicChecks = parent::validate($value);
|
||||
|
||||
if (!$basicChecks) {
|
||||
if ('?' === $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle the # value
|
||||
if (false !== strpos($value, '#')) {
|
||||
$chunks = explode('#', $value);
|
||||
$chunks[0] = $this->convertLiterals($chunks[0]);
|
||||
|
||||
if (parent::validate($chunks[0]) && is_numeric($chunks[1]) && \in_array((int) $chunks[1], $this->nthRange, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/^(.*)L$/', $value, $matches)) {
|
||||
return $this->validate($matches[1]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $basicChecks;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user