Version v0.16.0

This commit is contained in:
Johannes Meyer 2025-05-22 11:11:56 +00:00
parent 84c91fbac0
commit ce55e29d13
818 changed files with 461555 additions and 0 deletions

1
VERSION Normal file
View File

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

View File

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

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

@ -0,0 +1,163 @@
@ball-pad: 1/6em;
.ball {
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
}
.ball-size-xs {
height: 1/3em;
width: 1/3em;
i.icon, span {
display: none;
}
}
.ball-size-s {
height: 0.5em;
width: 0.5em;
i.icon, span {
display: none;
}
}
.ball-size-m {
height: 0.75em;
width: 0.75em;
line-height: 0;
i.icon:before {
font-size: .75 - @ball-pad * 2;
line-height: 1em;
}
span {
display: none;
}
}
.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;
}
}
span {
display: none;
}
}
.ball-size-l {
height: 1.5em;
width: 1.5em;
line-height: 1em;
i.icon:before, span {
font-size: 1 - @ball-pad * 2;
line-height: 1.5 - @ball-pad * 2;
}
}
.ball-size-xl {
width: 2em;
height: 2em;
i.icon:before, span {
line-height: 2 - @ball-pad * 2;
}
}
.ball-outline(@color) {
border: @ball-pad solid @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.icon {
text-align: center;
&::before {
margin-right: 0;
}
}
// Specific icon styles
&.ball-size-l i {
&.fa-sitemap:before {
font-size: 8px; // px to ignore browser min font-size
}
}
&.ball-size-xl i {
&.fa-sitemap:before {
font-size: .857em;
line-height: (2 - @ball-pad * 2) / .857;
}
}
}

View File

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

127
asset/css/compat.less Normal file
View File

@ -0,0 +1,127 @@
// 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;
}
}
// TermInput styles
form.icinga-form .control-group {
> .term-input-area {
flex: 1 1 auto;
width: auto;
&.vertical {
width: 0;
}
input[type="text"] {
flex: unset;
width: 100%;
}
}
}
.module-icingadb {
// Icinga DB Web (legacy) table header layout (e.g. in group details)
> .controls {
> .table-row {
display: flex;
gap: .5em;
> .col.title {
margin-right: auto;
}
}
}
// Icinga DB Web (legacy) object grid layout
> .content > .item-table.group-grid:has(.col.title) {
grid-template-columns: repeat(auto-fit, 15em) !important;
> .group-grid-cell {
display: revert;
&::before, &::after {
display: none !important;
}
> .col.title {
border: none;
> .column-content {
overflow: hidden;
> * {
.text-ellipsis();
}
}
}
}
}
}

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

@ -0,0 +1,159 @@
.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, @controls-separator-bg);
z-index: 1; // The content may clip, this ensures the separator is always visible
& + .content {
z-index: 0;
}
> .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;
}
> .search-controls > .search-bar .filter-input-area {
--term-padding-v: 0px;
}
}

View File

@ -0,0 +1,41 @@
.clipboard-wrapper {
position: relative;
> .copy-to-clipboard {
margin: 0.1em 0.1em 0 0;
position: absolute;
right: 0;
top: 0;
}
&:hover {
.copy-to-clipboard.active {
opacity: 0.8;
}
}
}
.copy-to-clipboard {
.appearance(none);
border: none;
opacity: 0;
background: none;
padding: 0.25em 0.5em;
cursor: default;
&.active {
cursor: pointer;
&:hover, &:focus {
opacity: 0.8;
color: var(--control-color, @control-color);
outline-offset: -2px;
}
}
}
.copy-to-clipboard.active,
.clipboard-wrapper:hover .copy-to-clipboard.active {
&.copied {
opacity: 0;
transition: opacity 1s linear 3s;
}
}

View File

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

View File

@ -0,0 +1,12 @@
.empty-state {
color: @empty-state-color;
}
.empty-state-bar {
padding: 1em;
text-align: center;
.rounded-corners();
background-color: @empty-state-bar-bg;
color: @default-text-color;
}

View 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;
}
}
}
}

6243
asset/css/fontawesome.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,64 @@
@font-face {
font-family: 'Icinga-Icons';
src: url('@{iplWebAssets}/font/icinga-icons/fonts/Icinga-Icons.ttf') format('truetype'),
url('@{iplWebAssets}/font/icinga-icons/fonts/Icinga-Icons.woff') format('woff'),
url('@{iplWebAssets}/font/icinga-icons/fonts/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-certificate:before {
content: "\e906";
}
.iicon-filter-check-circle:before {
content: "\e90b";
}
.iicon-ca-check-circle:before {
content: "\e908";
}
.iicon-refresh-cert:before {
content: "\e909";
}
.iicon-th-list:before {
content: "\e90a";
}
.iicon-icinga:before {
content: "\e907";
}
.iicon-minimal:before,
.iicon-list-view-minimal:before {
content: "\e900";
}
.iicon-detailed:before,
.iicon-list-view-detailed:before {
content: "\e901";
}
.iicon-default:before,
.iicon-list-view-default:before {
content: "\e902";
}
.iicon-grid:before {
content: "\e903";
}
.iicon-bracket-open:before {
content: "\e904";
}
.iicon-bracket-close:before {
content: "\e905";
}

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

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

157
asset/css/item-layout.less Normal file
View File

@ -0,0 +1,157 @@
// Layout
.item-layout {
// Note that mode specific rules are as strict as possible to avoid conflicts with nested layouts.
// Consider an item which contains another item in a different layout mode. Coincidentally, this is
// already the case in Icinga DB Web with the last comment flyout.
.flowing-content(@layout) when (@layout = "default") {
display: inline-flex;
align-items: baseline;
white-space: nowrap;
min-width: 0;
column-gap: .28125em; // calculated &nbsp; width
> .ellipsize, > .subject { // .subject is compat only, Icinga DB Web used it thoroughly
.text-ellipsis();
}
}
.flowing-content(@layout) when (@layout = "detailed") {
display: inline-flex;
align-items: baseline;
flex-wrap: wrap;
column-gap: .28125em; // calculated &nbsp; width
}
display: flex;
.visual {
display: flex;
flex-direction: column;
width: auto;
padding: .25em 0;
margin-right: 1em;
> i.icon {
font-size: 1.5em;
&::before {
margin-right: 0;
}
}
}
.main {
flex: 1 1 auto;
padding: .25em 0;
width: 0;
}
header {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
&.minimal-item-layout > .main > header {
max-width: 100%;
}
.caption {
p {
display: inline-block;
}
img {
max-height: 1em;
}
}
&.default-item-layout > .main > .caption {
height: 1.5em;
.text-ellipsis();
}
&.minimal-item-layout > .main > header > .caption {
flex: 1 1 auto;
height: 1.5em;
width: 0;
&:not(:empty) {
margin-right: 1em;
}
.text-ellipsis();
}
&.detailed-item-layout > .main > .caption {
display: block;
overflow: hidden;
position: relative;
.line-clamp(5);
}
footer {
display: flex;
justify-content: space-between;
padding-top: .5em;
}
.title {
margin-right: 1em;
}
&.default-item-layout > .main > header > .title {
.flowing-content("default");
}
&.detailed-item-layout > .main > header > .title {
.flowing-content("detailed");
word-break: break-word;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
}
&.default-item-layout > .main > header > .extended-info {
.flowing-content("default");
}
&.detailed-item-layout > .main > header > .extended-info {
.flowing-content("detailed");
}
}
// Style
.item-layout {
color: @default-text-color-light;
.caption {
i {
opacity: 0.8;
}
a {
color: @default-text-color;
}
}
.title {
.subject {
color: @default-text-color;
}
a {
color: @default-text-color;
font-weight: bold;
&:hover {
color: @link-hover-color;
text-decoration: none;
.subject {
color: @link-hover-color;
}
}
}
}
}

View File

@ -0,0 +1,35 @@
// Style
.item-list {
list-style-type: none;
}
.content:has(> .item-list) > .item-list > .empty-state {
.empty-state-bar();
}
// Layout
.item-list {
margin: 0;
padding: 0;
> .empty-state-bar {
margin: 0 1em;
}
}
.controls .list-item:not(:last-child) {
margin-bottom: .5em;
}
:not(.dashboard) > .container > .content:has(> .item-list), // compat only, for Icinga Web (See #286)
.content:has(> .item-list) {
padding-left: 0;
padding-right: 0;
> .item-list > .list-item {
padding-left: 1em;
padding-right: 1em;
}
}

View File

@ -0,0 +1,127 @@
// Style
ul.item-table {
list-style-type: none;
}
.content:has(> .item-table) > .item-table > .empty-state {
.empty-state-bar();
}
// Layout
ul.item-table {
// Grid specific rules
display: grid;
grid-template-columns: minmax(0, 1fr) repeat(var(--columns), auto);
&:has(> li > .visual) {
grid-template-columns: auto minmax(0, 1fr) repeat(var(--columns), auto);
}
> li {
display: contents;
&.item-layout .main {
// Usually, the parent is flex, but here it's contents. .main is still stretched though,
// because it's a grid item with a width of 1fr. But the default-item-layout sets a width
// which needs to be overridden.
width: auto;
}
.col, &::before, &::after {
// The li might get a background on hover. Though, this won't be visible
// as it has no box model since we apply display:contents to it.
background-color: inherit;
}
}
}
:not(.dashboard) > .container > .content:has(> .item-table), // compat only, for Icinga Web (See #286)
.content:has(> .item-table) {
padding-left: 0;
padding-right: 0;
> ul.item-table {
// Again, since the li has no box model, it cannot have padding. So the first
// and last child need to get the left and right padding respectively.
// But we don't want to have a border that spans to the very right or left,
// so pseudo elements are required. We could add empty cells instead, but
// that would require hard coding the width here, which I'd like to avoid.
grid-template-columns: ~"auto minmax(0, 1fr) repeat(var(--columns), auto) auto";
&:has(> li > .visual) {
grid-template-columns: ~"auto auto minmax(0, 1fr) repeat(var(--columns), auto) auto";
}
> li.table-row {
&::before, &::after {
display: inline-block;
content: '\00a0';
width: 0;
margin-bottom: 1px;
}
&::before {
padding-left: 1em;
}
&::after {
padding-right: 1em;
}
}
}
}
ul.item-table {
// General rules
padding: 0;
margin: 0;
.table-row {
.col {
margin-right: 0; // Otherwise background has gaps
padding: .5em 1em .5em 0;
&:last-child {
padding-right: 0;
}
.title {
margin-right: 0;
}
}
// This is for the legacy layout only
// TODO: Drop this together with BaseTableRowItem
.col.title:has(> .visual) {
display: flex;
> .visual {
padding-right: .5em;
}
}
&:not(:last-of-type) {
.col {
border-bottom: 1px solid @list-item-separation-bg;
&.visual {
border-color: @default-bg;
}
}
}
}
}
div.item-table {
> .empty-state-bar {
margin: 0 1em;
}
}
@media print {
.item-table li.page-break-follows:not(:last-of-type) {
.col {
border-bottom: none;
}
}
}

View File

@ -0,0 +1,29 @@
// Style
.list-item {
&:not(:first-child) > .main {
border-top: 1px solid @list-item-separation-bg;
}
&:not(:first-child) .visual {
margin-top: 1px;
}
}
@media print {
.list-item.page-break-follows + .list-item {
.main {
border-top: 1px solid transparent;
}
}
}
// Layout
.list-item.item-layout {
.main,
.visual {
padding-top: .5em;
padding-bottom: .5em;
}
}

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

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

View File

@ -0,0 +1,41 @@
.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;
}
.line-clamp(@numOfLines: 2) when (@numOfLines > 1) {
display: -webkit-box;
-webkit-line-clamp: @numOfLines;
-webkit-box-orient: vertical;
}
.line-clamp(@numOfLines: 2) when (@numOfLines = "reset") {
display: revert;
-webkit-line-clamp: initial;
-webkit-box-orient: initial;
}
.text-ellipsis() {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.monospace-font() {
font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
}

View File

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

View 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;
}

View 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;
}
}

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

@ -0,0 +1,232 @@
// 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);
}
}
}
// 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;
}
}
.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);
}
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 {
padding: 2/12em; // 2 (px) desired / default font size (px)
}
.terms {
.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 {
&.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;
}
}

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

@ -0,0 +1,413 @@
// Style
.search-bar .filter-input-area,
.term-input-area:not(.vertical) {
// Scrollbar style
// 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);
}
}
.search-bar,
.term-input-area {
[data-index] input:invalid,
[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);
}
.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;
}
[data-drag-initiator] {
cursor: grab;
}
.sortable-drag > label {
border: 1px dashed var(--search-term-drag-border-color, @search-term-drag-border-color);
}
.sortable-ghost {
opacity: .5;
}
}
fieldset:disabled .term-input-area [data-drag-initiator] {
cursor: not-allowed;
}
.term-input-area {
.invalid-reason {
padding: .25em;
.rounded-corners(.25em);
border: 1px solid black;
font-weight: bold;
background: var(--search-term-invalid-reason-bg, @search-term-invalid-reason-bg);
opacity: 0;
visibility: hidden;
transition: opacity 2s, visibility 2s;
&.visible {
opacity: 1;
visibility: visible;
transition: none;
}
}
.remove-action {
background: @search-term-remove-action-bg;
color: @search-term-remove-action-color;
.rounded-corners(0.25em);
}
}
.search-suggestions {
background: var(--suggestions-bg, @suggestions-bg);
color: var(--suggestions-color, @suggestions-color);
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 {
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,
[type="button"].selected {
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,
[type="button"]:not(.selected):hover {
background: var(--suggestions-hover-bg, @suggestions-hover-bg);
}
}
// Layout
.search-bar .filter-input-area,
.term-input-area:not(.vertical) {
--term-padding-v: .25em;
--term-padding-h: .5em;
overflow: auto hidden;
display: flex;
flex-wrap: nowrap;
width: 100%;
// input line-height + (input vertical padding * 2) + approximate scrollbar height
height: ~"calc(20px + calc(var(--term-padding-v) * 2) + 10px)";
// 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;
height: 100%;
&::after,
input {
width: auto;
padding: var(--term-padding-v) var(--term-padding-h);
resize: none;
}
input {
width: 100%;
position: absolute;
top: 0;
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;
label {
margin-right: 1px;
}
}
&.read-only [data-index] .remove-action {
line-height: 20/12;
padding: var(--term-padding-v) var(--term-padding-h);
}
}
.term-input-area.vertical {
display: flex;
flex-direction: column-reverse;
@itemGap: 1px;
> .terms {
margin-top: @itemGap;
input {
text-overflow: ellipsis;
}
}
> div.terms {
@termsPerRow: 2;
display: flex;
flex-wrap: wrap;
gap: @itemGap;
label {
@termWidth: 100%/@termsPerRow;
@totalGapWidthPerRow: (@termsPerRow - 1) * @itemGap;
min-width: ~"calc(@{termWidth} - (@{totalGapWidthPerRow} / @{termsPerRow}))";
flex: 1 1 auto;
}
}
> ol.terms {
padding: 0;
margin-bottom: 0;
list-style-type: none;
li:not(:first-child) {
margin-top: @itemGap;
}
li {
display: flex;
align-items: center;
gap: .25em;
> label {
flex: 1 1 auto;
}
> [data-drag-initiator]::before {
font-size: 1.75em;
margin: 0;
}
}
}
}
.term-input-area {
label input:focus {
@labelPad: 7/12em;
outline-width: 3px;
outline-offset: ~"calc(-@{labelPad} + 3px)";
}
&.read-only {
[data-index] {
position: relative;
input {
text-align: center;
cursor: pointer;
&:disabled {
cursor: default;
}
+ i {
position: absolute;
display: none;
top: .5em;
left: .5em;
}
&:not(:disabled):hover + i,
&:not(:disabled):focus + i {
display: revert;
}
}
.invalid-reason {
position: absolute;
z-index: 1;
top: 85%;
left: .5em;
}
.remove-action {
display: flex;
align-items: center;
visibility: visible;
position: absolute;
width: 100%;
top: 0;
line-height: normal;
padding: 0.5em;
cursor: pointer;
i.icon {
margin-left: auto;
}
.remove-action-label {
margin-right: auto;
.text-ellipsis();
}
}
input:invalid ~ .remove-action,
input.invalid ~ .remove-action {
pointer-events: none;
}
&:not(:hover) .remove-action {
visibility: hidden;
}
}
}
}
.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;
}
}
}
}

View 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;
}
}

View File

@ -0,0 +1,52 @@
.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);
}
&.state-none {
background-color: var(--state-none, @state-none);
color: var(--default-text-color-light, @default-text-color-light);
}
}
a .state-badge {
&:not(.disabled):hover {
filter: brightness(80%);
}
}

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

@ -0,0 +1,234 @@
/*
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-gray-semilight: #888;
@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;
@default-remove-bg: @state-critical;
@default-remove-color: @default-text-color-inverted;
@default-delete-bg: @state-critical;
@default-delete-color: @default-text-color-inverted;
@state-ok: #44bb77;
@state-up: @state-ok;
@state-warning: #ffaa44;
@state-critical: #ff5566;
@state-down: @state-critical;
@state-pending: #77aaff;
@state-unknown: #aa44ff;
@state-none: @base-gray-light;
@primary-button-color: @default-text-color-inverted;
@primary-button-bg: @base-primary-bg;
@primary-button-hover-bg: @base-primary-dark;
@link-hover-color: @base-primary-color;
@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-invalid-reason-bg: @base-gray-lighter;
@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-term-drag-border-color: @base-gray;
@search-term-remove-action-bg: @default-remove-bg;
@search-term-remove-action-color: @default-remove-color;
@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;
@controls-separator-bg: @base-gray-lighter;
@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;
@empty-state-color: @base-gray-semilight;
@empty-state-bar-bg: @base-gray-lighter;
@list-item-title-hover-color: @link-hover-color;
@list-item-separation-bg: @base-gray-light;
@iplWebLightRules: {
:root {
--base-gray: #819398;
--base-gray-light: #d0d3da;
--base-gray-lighter: #e8ecef;
--base-gray-semilight: #94a5a6;
--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;
--default-remove-bg: var(--base-remove-bg);
--default-remove-color: var(--default-text-color-inverted);
--default-delete-bg: var(--base-remove-bg);
--default-delete-color: var(--default-text-color-inverted);
--primary-button-color: var(--default-text-color-inverted);
--primary-button-bg: @primary-button-bg;
--primary-button-hover-bg: @primary-button-hover-bg;
--link-hover-color: var(--base-primary-color);
--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-invalid-reason-bg: var(--base-gray-lighter);
--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-term-drag-border-color: var(--base-gray);
--search-term-remove-action-bg: var(--default-remove-bg);
--search-term-remove-action-color: var(--default-remove-color);
--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);
--empty-state-color: var(--base-gray-semilight);
--empty-state-bar-bg: var(--base-gray-lighter);
--list-item-title-hover-color: var(--link-hover-color);
--list-item-separation-bg: var(--base-gray-light);
}
};

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

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

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

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

View File

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

96
asset/js/iterator.js Normal file
View File

@ -0,0 +1,96 @@
(function (root, factory) {
"use strict";
if (typeof define === "function" && define.icinga) {
define(["exports"], factory);
} else {
factory(root.icingaIteratorPolyfill = root.icingaIteratorPolyfill || {});
}
}(self, function (exports) {
/**
* Polyfill for `Iterator.filter`
*
* @param {Symbol.iterator} iterator
* @param {function} callback
* @returns {Generator<*, void, *>}
*/
function* filter(iterator, callback) {
if (typeof iterator.filter === "function") {
yield* iterator.filter(callback);
}
for (const item of iterator) {
if (callback(item)) {
yield item;
}
}
}
/**
* Polyfill for `Iterator.find`
*
* @param {Symbol.iterator} iterator
* @param {function} callback
* @returns {*}
*/
function find(iterator, callback) {
if (typeof iterator.find === "function") {
return iterator.find(callback);
}
for (const item of iterator) {
if (callback(item)) {
return item;
}
}
}
/**
* Polyfill for `Iterator.map`
*
* @param {Symbol.iterator} iterator
* @param {function} callback
* @returns {Generator<*, void, *>}
*/
function* map(iterator, callback) {
if (typeof iterator.map === "function") {
yield* iterator.map(callback);
}
for (const item of iterator) {
yield callback(item);
}
}
/**
* Find the first key in the map whose value satisfies the provided testing function.
* @param {Map} map
* @param {function} callback Passed arguments are: value, key, map
* @returns {*} Returns undefined if no key satisfies the testing function.
*/
function findKey(map, callback) {
for (const key of findKeys(map, callback)) {
return key;
}
}
/**
* Find all keys in the map whose value satisfies the provided testing function.
* @param {Map} map
* @param {function} callback Passed arguments are: value, key, map
* @returns {Generator<*, void, *>}
*/
function* findKeys(map, callback) {
for (const [ key, value ] of map) {
if (callback(value, key, map)) {
yield key;
}
}
}
exports.findKeys = findKeys;
exports.findKey = findKey;
exports.filter = filter;
exports.find = find;
exports.map = map;
}));

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

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

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because one or more lines are too long

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1074
asset/js/widget/BaseInput.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,750 @@
define(["../notjQuery"], function ($) {
"use strict";
class Completer {
constructor(input, instrumented = false) {
this.input = input;
this.instrumented = instrumented;
this.selectionStartInput = null;
this.selectionActive = false;
this.mouseSelectionActive = false;
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.selectionEnabled()) {
$(this.termSuggestions).on('keyup', '[type="button"]', this.onSuggestionKeyUp, this);
$(this.termSuggestions).on('mouseover', '[type="button"]', this.onSuggestionMouseOver, this);
$(this.termSuggestions).on('mousedown', '[type="button"]', this.onSuggestionMouseDown, this);
$(this.termSuggestions).on('mouseup', '[type="button"]', this.onSuggestionsMouseUp, this);
$(this.termSuggestions).on('mouseleave', this.onSuggestionsMouseLeave, 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;
this.endSelection();
}
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('X-Requested-With', 'XMLHttpRequest');
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, stopAtEdge = 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 if (stopAtEdge) {
return null;
} else {
input = this.completedInput;
}
} else {
input = inputs[backwards ? inputs.length - 1 : 0];
}
$(input).focus();
if (! stopAtEdge && 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 && this.hasSuggestions()
&& (! activeElement || input === activeElement || this.termSuggestions.contains(activeElement));
}
selectionEnabled() {
return this.instrumented && 'withMultiCompletion' in this.input.dataset;
}
selectionAllowed() {
return this.completedInput === this.input && this.selectionEnabled();
}
startSelection(input) {
this.selectionActive = true;
this.selectionStartInput = input;
}
isSelectionActive() {
return this.selectionActive;
}
endSelection() {
this.selectionStartInput = null;
this.selectionActive = false;
this.mouseSelectionActive = false;
}
selectSuggestion(input) {
input.classList.add('selected');
}
deselectSuggestion(input) {
input.classList.remove('selected');
}
toggleSelection(input) {
input.classList.toggle('selected');
let selected = input.classList.contains('selected');
if (selected && ! this.isSelectionActive()) {
this.startSelection(input);
$(input).focus();
}
if (! selected && input === this.selectionStartInput) {
this.selectionStartInput = this.termSuggestions.querySelector('[type="button"].selected');
if (! this.selectionStartInput) {
this.endSelection();
$(this.input).focus();
} else {
$(this.selectionStartInput).focus();
}
}
return selected;
}
isSelectedSuggestion(input) {
return input.classList.contains('selected');
}
getSelectedSuggestions() {
return this.termSuggestions.querySelectorAll('[type="button"].selected');
}
clearSelection() {
if (! this.isSelectionActive()) {
return;
}
for (const selectedInput of this.getSelectedSuggestions()) {
this.deselectSuggestion(selectedInput);
}
this.endSelection();
}
handleKeySelection(input, newInput) {
if (! this.isSelectionActive()) {
this.startSelection(input);
this.selectSuggestion(input);
this.selectSuggestion(newInput);
this.suggest(this.completedInput, '');
} else if (this.isSelectedSuggestion(newInput)) {
this.deselectSuggestion(input);
} else {
this.selectSuggestion(newInput);
}
}
startMouseSelection(input) {
this.startSelection(input);
this.mouseSelectionActive = true;
}
isMouseSelectionActive() {
return this.mouseSelectionActive;
}
finishMouseSelection() {
if (! this.mouseSelectionActive) {
return;
}
this.mouseSelectionActive = false;
this.selectSuggestion(this.selectionStartInput);
let selectionFound = false;
let selectionCandidates = [];
for (const input of this.termSuggestions.querySelectorAll('[type="button"]')) {
if (input.classList.contains('selected')) {
if (selectionFound) {
for (const candidate of selectionCandidates) {
this.selectSuggestion(candidate);
}
selectionCandidates = [];
} else {
selectionFound = true;
}
} else if (selectionFound) {
selectionCandidates.push(input);
}
}
}
/**
* Event listeners
*/
onSubmit(event) {
if (! event.detail || ! event.detail.submittedBy) {
// 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);
}
onSuggestionMouseDown(event) {
if (! this.selectionAllowed()) {
return;
}
if (event.ctrlKey || event.metaKey) {
// onSuggestionClick only toggles the suggestion's selection and should
// be the only one who decides which other suggestion should be focused
event.preventDefault();
} else {
this.clearSelection();
this.startMouseSelection(event.target);
}
}
onSuggestionsMouseUp(event) {
if (! event.ctrlKey && ! event.metaKey) {
this.finishMouseSelection();
}
}
onSuggestionsMouseLeave(_) {
this.finishMouseSelection();
}
onSuggestionMouseOver(event) {
if (this.isMouseSelectionActive()) {
this.selectSuggestion(event.target);
}
}
onSuggestionKeyUp(event) {
if (this.completedInput === null) {
return;
}
let input = event.target;
switch (event.key) {
case 'Shift':
if (this.isSelectionActive()) {
event.preventDefault();
if (input === this.selectionStartInput && this.getSelectedSuggestions().length === 1) {
this.deselectSuggestion(input);
this.endSelection();
}
}
break;
}
}
onSuggestionKeyDown(event) {
if (this.completedInput === null) {
return;
}
let newInput;
let input = event.target;
let allowSelection = event.shiftKey && this.selectionAllowed();
switch (event.key) {
case 'Escape':
$(this.completedInput).focus({ scripted: true });
this.suggest(this.completedInput, this.completedValue);
this.clearSelection();
break;
case 'Tab':
event.preventDefault();
this.moveToSuggestion(event.shiftKey);
break;
case 'ArrowLeft':
case 'ArrowUp':
event.preventDefault();
newInput = this.moveToSuggestion(true, allowSelection);
if (allowSelection) {
if (newInput !== null) {
this.handleKeySelection(input, newInput);
}
} else {
this.clearSelection();
}
break;
case 'ArrowRight':
case 'ArrowDown':
event.preventDefault();
newInput = this.moveToSuggestion(false, allowSelection);
if (allowSelection) {
if (newInput !== null) {
this.handleKeySelection(input, newInput);
}
} else {
this.clearSelection();
}
break;
}
}
onSuggestionClick(event) {
if (this.completedInput === null) {
return;
}
if (event.ctrlKey || event.metaKey) {
if (this.selectionAllowed()) {
this.toggleSelection(event.target);
event.preventDefault();
}
} else if (this.isSelectionActive() && this.isSelectedSuggestion(event.target)) {
let terms = [];
for (const suggestion of this.getSelectedSuggestions()) {
terms.push({ ...suggestion.dataset });
}
this.complete(this.completedInput, null, { type: 'terms', terms: terms });
} else {
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;
});

View File

@ -0,0 +1,49 @@
define(["../notjQuery"], function ($) {
"use strict";
class CopyToClipboard {
constructor(button)
{
button.classList.add('active');
button.removeAttribute('tabindex');
$(button).on('click', null, this.onClick, this);
}
onClick(event)
{
let button = event.currentTarget;
let clipboardSource = button.parentElement.querySelector("[data-clipboard-source]");
let copyText;
if (clipboardSource) {
copyText = clipboardSource.innerText;
} else {
throw new Error('Clipboard source is required but not provided');
}
if (navigator.clipboard) {
navigator.clipboard.writeText(copyText).then(() => {
let previousHtml = button.innerHTML;
button.innerText = button.dataset.copiedLabel;
button.classList.add('copied');
setTimeout(() => {
// after 4 second, reset it.
button.classList.remove('copied');
button.innerHTML = previousHtml;
}, 4000);
}).catch((err) => {
console.error('Failed to copy: ', err);
});
} else {
throw new Error('Copy to clipboard requires HTTPS connection');
}
event.stopPropagation();
event.preventDefault();
}
}
return CopyToClipboard;
});

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -0,0 +1,344 @@
define(["../notjQuery", "../vendor/Sortable", "BaseInput"], function ($, Sortable, BaseInput) {
"use strict";
class TermInput extends BaseInput {
constructor(input) {
super(input);
this.separator = this.input.dataset.termSeparator || ' ';
this.ordered = 'maintainTermOrder' in this.input.dataset;
this.readOnly = 'readOnlyTerms' in this.input.dataset;
this.ignoreSpaceUntil = null;
}
bind() {
super.bind();
if (this.readOnly) {
$(this.termContainer).on('click', '[data-index] > input', this.onTermClick, this);
}
if (this.ordered) {
$(this.termContainer).on('end', this.onDrop, this);
Sortable.create(this.termContainer, {
scroll: true,
direction: 'vertical',
handle: '[data-drag-initiator]'
});
}
// TODO: Compatibility only. Remove as soon as possible once Web 2.12 (?) is out.
// Or upon any other update which lets Web trigger a real submit upon auto submit.
$(this.input.form).on('change', 'select.autosubmit', this.onSubmit, this);
$(this.input.form).on('change', 'input.autosubmit', this.onSubmit, this);
return this;
}
reset() {
super.reset();
this.ignoreSpaceUntil = null;
}
registerTerm(termData, termIndex = null) {
termIndex = super.registerTerm(termData, termIndex);
if (this.readOnly) {
const label = this.termContainer.querySelector(`[data-index="${ termIndex }"]`);
if (label) {
// The label only exists in DOM at this time if it was transmitted
// by the server. So it's safe to assume that it needs validation
this.validate(label.firstChild);
}
}
return termIndex;
}
readPartialTerm(input) {
let value = super.readPartialTerm(input);
if (value && this.ignoreSpaceUntil && value[0] === this.ignoreSpaceUntil) {
value = value.slice(1);
if (value.slice(-1) === this.ignoreSpaceUntil) {
value = value.slice(0, -1);
}
}
return value;
}
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 (termData && this.ignoreSpaceUntil !== null && input.value[0] === this.ignoreSpaceUntil) {
if (input.value.slice(-1) !== this.ignoreSpaceUntil || input.value.length < 2) {
return false;
}
this.ignoreSpaceUntil = null;
}
return termData;
}
hasSyntaxError(input) {
if ((typeof input === 'undefined' || input === this.input) && this.ignoreSpaceUntil !== null) {
if (this.input.value === this.ignoreSpaceUntil) {
return true;
}
}
return super.hasSyntaxError(input);
}
checkValidity(input) {
if (! this.readOnly) {
return super.checkValidity(input);
}
// Readonly terms don't participate in constraint validation, so we have to do it ourselves
return ! (input.pattern && ! input.value.match(input.pattern));
}
reportValidity(element) {
if (! this.readOnly) {
return super.reportValidity(element);
}
// Once invalid, it stays invalid since it's readonly
element.classList.add('invalid');
if (element.dataset.invalidMsg) {
const reason = element.parentNode.querySelector(':scope > .invalid-reason');
if (! reason.matches('.visible')) {
element.title = element.dataset.invalidMsg;
reason.textContent = element.dataset.invalidMsg;
reason.classList.add('visible');
setTimeout(() => reason.classList.remove('visible'), 5000);
}
}
}
termsToQueryString(terms) {
let quoted = [];
for (const termData of terms) {
let search = this.encodeTerm(termData).search;
if (search.indexOf(this.separator) >= 0) {
search = '"' + termData.search + '"';
}
quoted.push(search);
}
return quoted.join(this.separator).trim();
}
addRenderedTerm(label) {
if (! this.ordered) {
return super.addRenderedTerm(label);
}
const listItem = document.createElement('li');
listItem.appendChild(label);
listItem.appendChild($.render('<i data-drag-initiator class="icon fa-bars fa"></i>'));
this.termContainer.appendChild(listItem);
}
insertRenderedTerm(label) {
if (! this.ordered) {
return super.insertRenderedTerm(label);
}
const termIndex = Number(label.dataset.index);
const nextListItemLabel = this.termContainer.querySelector(`[data-index="${ termIndex + 1 }"]`);
const nextListItem = nextListItemLabel?.parentNode || null;
const listItem = document.createElement('li');
listItem.appendChild(label);
listItem.appendChild($.render('<i data-drag-initiator class="icon fa-bars fa"></i>'));
this.termContainer.insertBefore(listItem, nextListItem);
return label;
}
removeRenderedTerm(label) {
if (! this.ordered) {
return super.removeRenderedTerm(label);
}
label.parentNode.remove();
}
complete(input, data) {
data.exclude = this.usedTerms.map(termData => termData.search);
super.complete(input, data);
}
renderTerm(termData, termIndex) {
const label = super.renderTerm(termData, termIndex);
if (this.readOnly) {
const removeLabel = this.termContainer.dataset.removeActionLabel;
label.firstChild.readOnly = true;
label.appendChild(
$.render(
`<div class="remove-action" title="${ removeLabel }">` +
'<i class="icon fa-trash fa"></i>' +
`<span class="remove-action-label">${ removeLabel }</span>` +
'</div>'
)
);
label.appendChild($.render('<span class="invalid-reason"></span>'));
}
return label;
}
/**
* Event listeners
*/
onTermClick(event) {
let termIndex = Number(event.target.parentNode.dataset.index);
this.removeTerm(event.target.parentNode);
this.moveFocusForward(termIndex - 1);
}
onDrop(event) {
if (event.to === event.from && event.newIndex === event.oldIndex) {
// The user dropped the term at its previous position
return;
}
// The item is the list item, not the term's label
const label = event.item.firstChild;
// Remove the term from the internal map, but not the DOM, as it's been moved already
const termData = this.removeTerm(label, false);
delete label.dataset.index; // Which is why we have to take it out of the equation for now
let newIndex = 0; // event.newIndex is intentionally not used, as we have our own indexing
if (event.item.previousSibling) {
newIndex = Number(event.item.previousSibling.firstChild.dataset.index) + 1;
}
// This is essentially insertTerm() with custom DOM manipulation
this.reIndexTerms(newIndex, 1, true); // Free up the new index
this.registerTerm(termData, newIndex); // Re-register the term with the new index
label.dataset.index = `${ newIndex }`; // Update the DOM, we didn't do that during removal
}
onSubmit(event) {
super.onSubmit(event);
this.ignoreSpaceUntil = null;
}
onInput(event) {
let label = event.target.parentNode;
if (label.dataset.index >= 0) {
super.onInput(event);
return;
}
let input = event.target;
let firstChar = input.value[0];
if (this.ignoreSpaceUntil !== null) {
// Reset if the user changes/removes the source char
if (firstChar !== this.ignoreSpaceUntil) {
this.ignoreSpaceUntil = null;
}
}
if (this.ignoreSpaceUntil === null && (firstChar === "'" || firstChar === '"')) {
this.ignoreSpaceUntil = firstChar;
}
super.onInput(event);
}
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 && event.key !== 'Enter') {
return;
}
let addedTerms = this.exchangeTerm();
if (Object.keys(addedTerms).length) {
this.togglePlaceholder();
event.preventDefault();
this.autoSubmit(this.input, 'exchange', { terms: 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;
}
}
}
onButtonClick(event) {
// Exchange terms only when an Enter key is pressed while not in the term input.
// If the pointerType is not empty, the click event is triggered by clicking the Submit button in the form,
// and the default submit event should not be prevented.
// The below solution does not work if the click event is triggered by pressing Space while on the Submit button.
// In which case the Submit button needs to be clicked again to trigger the form submission.
if (! this.hasSyntaxError() && event.pointerType === '') {
let addedTerms = this.exchangeTerm();
if (Object.keys(addedTerms).length) {
this.togglePlaceholder();
event.preventDefault();
this.autoSubmit(this.input, 'exchange', { terms: addedTerms });
this.ignoreSpaceUntil = null;
return;
}
}
super.onButtonClick(event);
}
}
return TermInput;
});

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,7 @@
Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures.
To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/#docs/local-fonts
You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects.
You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection.

View File

@ -0,0 +1,22 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="Icinga-Icons" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe900;" glyph-name="list-view-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.026zM320.006 863.997h640.004v-64.005h-640.004v64.005zM320.006 479.674h640.004v-64.005h-640.004v64.005zM320.006 671.674h640.004v-64.005h-640.004v64.005zM320.006 287.674h640.004v-64.005h-640.004v64.005zM320.006 95.674h640.004v-64.005h-640.004v64.005zM192.009 255.348c0-35.361-28.665-64.026-64.026-64.026s-64.026 28.665-64.026 64.026c0 35.361 28.665 64.026 64.026 64.026s64.026-28.665 64.026-64.026zM192.009 63.671c0-35.361-28.665-64.026-64.026-64.026s-64.026 28.665-64.026 64.026c0 35.361 28.665 64.026 64.026 64.026s64.026-28.665 64.026-64.026z" />
<glyph unicode="&#xe901;" glyph-name="list-view-detailed" d="M320.007 831.679h639.993v-191.358h-639.993v191.358zM320.007 575.997h639.993v-63.992h-639.993v63.992zM256.014 736.317c0-53.041-42.998-96.039-96.039-96.039s-96.039 42.998-96.039 96.039c0 53.041 42.998 96.039 96.039 96.039s96.039-42.998 96.039-96.039zM256.014 287.992c0-53.041-42.998-96.039-96.039-96.039s-96.039 42.998-96.039 96.039c0 53.041 42.998 96.039 96.039 96.039s96.039-42.998 96.039-96.039zM320.007 384.004h639.993v-192.005h-639.993v192.005zM320.007 128.005h639.993v-63.992h-639.993v63.992z" />
<glyph unicode="&#xe902;" glyph-name="list-view-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" />
<glyph unicode="&#xe903;" glyph-name="grid" d="M64.059 48.925v274.364c0 14.39 4.781 25.902 14.342 35.495s21.035 14.39 35.377 15.349h273.451c13.386 0 24.859-4.797 35.377-15.349s15.298-22.064 14.342-35.495v-274.364c0-13.43-4.781-24.942-14.342-34.535s-21.035-14.39-35.377-14.39h-273.451c-14.342 0-25.815 4.797-35.377 14.39s-14.342 21.105-14.342 34.535zM64.059 572.711v274.364c0 13.43 4.781 24.942 14.342 34.535s21.035 14.39 35.377 14.39h273.451c13.386 0 24.859-4.797 35.377-14.39s15.298-21.105 14.342-34.535v-274.364c0-14.39-4.781-25.902-14.342-35.495s-21.035-14.39-35.377-15.349h-273.451c-13.386 0-24.859 4.797-35.377 15.349s-15.298 22.064-14.342 35.495zM148.197 83.461h204.61v205.293h-204.61v-205.293zM148.197 607.247h204.61v204.334h-204.61v-204.334zM587.058 48.925v274.364c0 14.39 4.781 25.902 14.342 35.495s21.035 14.39 35.377 15.349h273.451c13.386 0 24.859-4.797 35.377-15.349s15.298-22.064 14.342-35.495v-274.364c0-13.43-4.781-24.942-14.342-34.535s-21.035-14.39-35.377-14.39h-273.451c-13.386 0-24.859 4.797-35.377 14.39s-15.298 21.105-14.342 34.535zM587.058 572.711v274.364c0 13.43 4.781 24.942 14.342 34.535s21.035 14.39 35.377 14.39h273.451c14.342 0 25.815-4.797 35.377-14.39s14.342-21.105 14.342-34.535v-274.364c0-14.39-4.781-25.902-14.342-35.495s-21.035-14.39-35.377-15.349h-273.451c-13.386 0-24.859 4.797-35.377 15.349s-15.298 22.064-14.342 35.495zM671.197 83.461h205.566v205.293h-205.566v-205.293zM671.197 607.247h205.566v204.334h-205.566v-204.334z" />
<glyph unicode="&#xe904;" glyph-name="bracket-open" d="M473.568 179.2c0-59.098 44.467-107.789 101.76-114.432l6.682-0.576 6.758-0.192h-153.6l-6.758 0.192c-58.253 3.379-104.87 49.997-108.25 108.25l-0.192 6.758v537.6l0.192 6.758c3.226 55.91 46.349 101.107 101.299 107.635l6.95 0.614 6.758 0.192h153.6l-6.758-0.192c-55.91-3.226-101.107-46.349-107.635-101.299l-0.614-6.95-0.192-6.758v-537.6z" />
<glyph unicode="&#xe905;" glyph-name="bracket-close" d="M447.136 179.2c0-59.098-44.467-107.789-101.76-114.432l-6.682-0.576-6.758-0.192h153.6l6.758 0.192c58.253 3.379 104.87 49.997 108.25 108.25l0.192 6.758v537.6l-0.192 6.758c-3.226 55.91-46.349 101.107-101.299 107.635l-6.95 0.614-6.758 0.192h-153.6l6.758-0.192c55.91-3.226 101.107-46.349 107.635-101.299l0.614-6.95 0.192-6.758v-537.6z" />
<glyph unicode="&#xe906;" glyph-name="certificate" d="M64 960h896c35.328 0 64-28.672 64-64v-640c0-35.328-28.672-64-64-64h-350.72v64h350.72v640h-896v-640h33.28v-64h-33.28c-35.328 0-64 28.672-64 64v640c0 35.328 28.672 64 64 64zM576.048 478.878c0-123.698-100.277-223.976-223.976-223.976s-223.976 100.277-223.976 223.976c0 123.698 100.277 223.976 223.976 223.976s223.976-100.277 223.976-223.976zM511.488 239.104l64-303.104-224.32 128-223.168-128 64 303.488c45.76-30.656 100.8-48.576 160-48.576 58.944 0 113.792 17.728 159.488 48.192zM896 768c0 16.96-6.784 33.28-18.752 45.248-12.032 12.032-28.288 18.752-45.248 18.752-71.040 0-185.024 0-256 0-17.024 0-33.28-6.72-45.248-18.752-12.032-11.968-18.752-28.288-18.752-45.248s6.72-33.28 18.752-45.248c11.968-12.032 28.224-18.752 45.248-18.752 70.976 0 184.96 0 256 0 16.96 0 33.216 6.72 45.248 18.752 11.968 11.968 18.752 28.288 18.752 45.248zM896 576c0 16.96-6.784 33.28-18.752 45.248-12.032 12.032-28.288 18.752-45.248 18.752-71.040 0-57.024 0-128 0-17.024 0-33.28-6.72-45.248-18.752-12.032-11.968-18.752-28.288-18.752-45.248s6.72-33.28 18.752-45.248c11.968-12.032 28.224-18.752 45.248-18.752 70.976 0 56.96 0 128 0 16.96 0 33.216 6.72 45.248 18.752 11.968 11.968 18.752 28.288 18.752 45.248z" />
<glyph unicode="&#xe907;" glyph-name="icinga" d="M574.496 878.589l45.262-8.286-149.128-422.304-45.261 8.286 149.126 422.304zM896.001 590.671v-29.341l-379.111-128.001-9.78 29.341 388.891 128.001zM656 192v-43.787l-164.906 224.295 41.812 23.571 123.093-204.079zM205.566 114.412l-27.131 27.175 205.565 242.414 63.999-13.589-242.434-255.999zM192.001 639.999l-38.798 52.36 308.952-225.624-28.309-38.763-241.845 212.028zM576.418 959.103c75.061 0 136.002-60.94 136.002-136s-60.941-136-136.002-136c-75.058 0-135.999 60.94-135.999 136s60.941 136 135.999 136zM911.998 687.103c61.815 0 111.999-50.187 111.999-112 0-61.816-50.185-112-111.999-112s-111.999 50.184-111.999 112c0 61.813 50.185 112 111.999 112zM656 240.002c44.154 0 80.002-35.85 80.002-80.001 0-44.155-35.848-80.001-80.002-80.001-44.15 0-79.998 35.846-79.998 80.001 0 44.151 35.848 80.001 79.998 80.001zM143.999 224.001c79.478 0 144.002-64.526 144.002-144.002s-64.524-143.999-144.002-143.999c-79.475 0-143.999 64.524-143.999 143.999s64.524 144.002 143.999 144.002zM139.048 768.978c52.984 0 96.001-43.016 96.001-96.001 0-52.982-43.018-95.998-96.001-95.998s-95.998 43.016-95.998 95.998c0 52.985 43.014 96.001 95.998 96.001zM448 640.294c105.968 0 192-86.034 192-192.001s-86.032-191.999-192-191.999c-105.968 0-192 86.032-192 191.999s86.032 192.001 192 192.001z" />
<glyph unicode="&#xe908;" glyph-name="ca-circle-check" d="M177.038 352.521c-5.88-0.539-12.299-0.833-19.306-0.833-22.54 0-43.317 4.018-62.378 12.103-19.061 8.036-35.672 19.453-49.883 34.202-14.259 14.749-25.382 32.34-33.418 52.725s-12.103 43.072-12.103 68.013c0 24.941 4.165 47.727 12.495 68.405s19.6 38.22 33.81 52.725c14.21 14.504 30.968 25.48 50.324 33.026 19.306 7.497 40.23 11.27 62.77 11.27 20.384 0 40.671-3.92 60.761-11.711 20.139-7.742 36.358-19.159 48.707-34.202l-48.266-52.333c-6.468 10.241-15.043 17.591-25.774 22.148s-22.001 6.86-33.81 6.86c-11.809 0-22.932-2.401-33.418-7.252-10.437-4.851-19.453-11.515-26.95-20.139-7.497-8.575-13.279-18.767-17.297-30.576s-6.076-24.696-6.076-38.613c0-13.965 2.058-26.999 6.076-39.054 4.018-12.103 9.8-22.295 17.297-30.576 7.497-8.33 16.219-14.896 26.166-19.747 9.898-4.802 21.070-7.252 33.369-7.252 13.965 0 26.313 2.989 37.044 8.869 8.575 4.851 15.827 11.025 21.756 18.522l-41.896-96.58zM484.864 365.888l-18.752 49.408h-123.2l-23.296-63.616h-82.944l132.8 321.984h74.88l90.688-222.4c-21.632-25.92-38.336-54.336-50.176-85.376zM405.696 587.52l-40.192-110.272h79.68l-39.488 110.272zM1024 254.976c-2.176-72.512-27.456-132.992-75.84-181.504-48.384-48.576-108.672-73.344-180.992-74.496-72.256 1.152-132.288 25.92-180.096 74.496-47.808 48.512-72.832 108.992-75.072 181.504 2.24 72.512 27.264 133.056 75.072 181.568s107.84 73.344 180.096 74.432c72.32-1.088 132.608-25.92 180.992-74.432s73.664-109.056 75.84-181.568zM966.144 378.752c-0.512 0.704-1.088 1.344-1.728 1.92-22.656 22.72-59.52 22.72-82.176 0l-80.448-80.32-33.024-76.608c0 0-25.6 65.728-25.6 65.728l-29.312 29.312c-22.656 22.656-59.52 22.656-82.176 0-22.72-22.72-22.72-59.52 0-82.24l94.272-94.272c11.2-11.2 25.856-16.832 40.512-16.96 0.448 0 0.896 0 1.28 0 14.656 0.192 29.248 5.824 40.384 16.96l156.224 156.224c22.016 22.016 22.656 57.472 1.792 80.256z" />
<glyph unicode="&#xe909;" glyph-name="refresh-cert" d="M319.936 512h384v-257.088h-384v257.088zM383.936 544v96c0 35.328 28.672 64 64 64h128c35.328 0 64-28.672 64-64v-96h-256zM575.936 640h-128v-96h128v96zM192.448 235.136c68.864-103.168 186.368-171.136 319.552-171.136 141.568 0 265.344 76.8 331.904 190.912h142.4c-76.288-187.008-260.032-318.912-474.304-318.912-180.544 0-339.392 93.632-430.592 235.008l-81.536-47.104 2.112 324.096 281.472-160.384-91.008-52.48zM833.472 657.92c-68.544 104.832-187.008 174.080-321.472 174.080-167.040 0-309.376-106.944-362.112-256h-133.76c56.896 220.736 257.472 384 495.872 384 181.824 0 341.632-94.976 432.448-237.952l79.68 45.952-2.112-324.096-281.472 160.384 92.928 53.632z" />
<glyph unicode="&#xe90a;" glyph-name="th-list" d="M64.491 172.602c0-19.125 6.694-35.381 20.081-48.768s29.644-20.081 48.768-20.081h30.6c19.125 0 35.381 6.694 48.768 20.081s20.081 29.644 20.081 48.768c0 19.125-6.694 35.381-20.081 48.768s-29.644 20.081-48.768 20.081h-30.6c-19.125 0-35.381-6.694-48.768-20.081s-20.081-29.644-20.081-48.768zM64.491 448c0-19.125 6.694-35.381 20.081-48.768s29.644-20.081 48.768-20.081h30.6c19.125 0 35.381 6.694 48.768 20.081s20.081 29.644 20.081 48.768c0 19.125-6.694 35.381-20.081 48.768s-29.644 20.081-48.768 20.081h-30.6c-19.125 0-35.381-6.694-48.768-20.081s-20.081-29.644-20.081-48.768zM64.491 723.398c0-19.125 6.694-35.381 20.081-48.768s29.644-20.081 48.768-20.081h30.6c19.125 0 35.381 6.694 48.768 20.081s20.081 29.644 20.081 48.768c0 19.125-6.694 35.381-20.081 48.768s-29.644 20.081-48.768 20.081h-30.6c-19.125 0-35.381-6.694-48.768-20.081s-20.081-29.644-20.081-48.768zM288.252 172.602c0-19.125 6.694-35.381 20.081-48.768s29.644-20.081 48.768-20.081h534.54c19.125 0 35.381 6.694 48.768 20.081s20.081 29.644 20.081 48.768c0 19.125-6.694 35.381-20.081 48.768s-29.644 20.081-48.768 20.081h-534.54c-19.125 0-35.381-6.694-48.768-20.081s-20.081-29.644-20.081-48.768zM288.252 448c0-19.125 6.694-35.381 20.081-48.768s29.644-20.081 48.768-20.081h534.54c19.125 0 35.381 6.694 48.768 20.081s20.081 29.644 20.081 48.768c0 19.125-6.694 35.381-20.081 48.768s-29.644 20.081-48.768 20.081h-534.54c-19.125 0-35.381-6.694-48.768-20.081s-20.081-29.644-20.081-48.768zM288.252 723.398c0-19.125 6.694-35.381 20.081-48.768s29.644-20.081 48.768-20.081h534.54c19.125 0 35.381 6.694 48.768 20.081s20.081 29.644 20.081 48.768c0 19.125-6.694 35.381-20.081 48.768s-29.644 20.081-48.768 20.081h-534.54c-19.125 0-35.381-6.694-48.768-20.081s-20.081-29.644-20.081-48.768z" />
<glyph unicode="&#xe90b;" glyph-name="filter-check-circle" d="M7.232 869.504c11.776 24.896 36.672 40.704 64.192 40.704h768c27.584 0 52.48-15.808 64.192-40.704s8.192-54.208-9.216-75.52l-189.376-231.488c-142.592-29.312-249.6-155.392-249.6-306.496 0-48.704 11.2-94.912 31.104-136-2.816 1.408-5.696 3.2-8.32 5.184l-113.792 85.312c-14.4 10.688-22.784 27.52-22.784 45.504v140.608l-325.312 397.504c-17.216 21.184-20.992 50.688-9.088 75.392zM1024 254.976c-2.176-72.512-27.456-132.992-75.84-181.504-48.384-48.576-108.672-73.344-180.992-74.496-72.256 1.152-132.288 25.92-180.096 74.496-47.808 48.512-72.832 108.992-75.072 181.504 2.24 72.512 27.264 133.056 75.072 181.568s107.84 73.344 180.096 74.432c72.32-1.088 132.608-25.92 180.992-74.432s73.664-109.056 75.84-181.568zM966.144 378.752c-0.512 0.704-1.088 1.344-1.728 1.92-22.656 22.72-59.52 22.72-82.176 0l-80.448-80.32-33.024-76.608c0 0-25.6 65.728-25.6 65.728l-29.312 29.312c-22.656 22.656-59.52 22.656-82.176 0-22.72-22.72-22.72-59.52 0-82.24l94.272-94.272c11.2-11.2 25.856-16.832 40.512-16.96 0.448 0 0.896 0 1.28 0 14.656 0.192 29.248 5.824 40.384 16.96l156.224 156.224c22.016 22.016 22.656 57.472 1.792 80.256z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-120,-134)">
<g transform="matrix(0.25,0,0,0.25,120,134)">
<rect id="bracket-close" x="0" y="0" width="64" height="64" style="fill:none;"/>
<g id="bracket-close1" serif:id="bracket-close">
<g transform="matrix(2.4,0,0,2.4,20.746,8)">
<path d="M3,17C3,18.539 1.842,19.807 0.35,19.98L0.176,19.995L0,20L4,20L4.176,19.995C5.693,19.907 6.907,18.693 6.995,17.176L7,17L7,3L6.995,2.824C6.911,1.368 5.788,0.191 4.357,0.021L4.176,0.005L4,0L0,0L0.176,0.005C1.632,0.089 2.809,1.212 2.979,2.643L2.995,2.824L3,3L3,17Z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-100,-134)">
<g transform="matrix(0.25,0,0,0.25,100,134)">
<rect id="bracket-open" x="0" y="0" width="64" height="64" style="fill:none;"/>
<g id="bracket-open1" serif:id="bracket-open">
<g transform="matrix(-2.4,0,0,2.4,36.846,8)">
<path d="M3,17C3,18.539 1.842,19.807 0.35,19.98L0.176,19.995L0,20L4,20L4.176,19.995C5.693,19.907 6.907,18.693 6.995,17.176L7,17L7,3L6.995,2.824C6.911,1.368 5.788,0.191 4.357,0.021L4.176,0.005L4,0L0,0L0.176,0.005C1.632,0.089 2.809,1.212 2.979,2.643L2.995,2.824L3,3L3,17Z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-216,-134)">
<g transform="matrix(0.25,0,0,0.25,216,134)">
<rect id="filter-circle-check" x="0" y="0" width="64" height="64" style="fill:none;"/>
<g id="filter-circle-check1" serif:id="filter-circle-check">
<g transform="matrix(3.06254,0,0,3.06254,-937.661,-403.905)">
<path d="M309.784,144.283C309.664,144.294 309.533,144.3 309.39,144.3C308.93,144.3 308.506,144.218 308.117,144.053C307.728,143.889 307.389,143.656 307.099,143.355C306.808,143.054 306.581,142.695 306.417,142.279C306.253,141.863 306.17,141.4 306.17,140.891C306.17,140.382 306.255,139.917 306.425,139.495C306.595,139.073 306.825,138.715 307.115,138.419C307.405,138.123 307.747,137.899 308.142,137.745C308.536,137.592 308.963,137.515 309.423,137.515C309.839,137.515 310.253,137.595 310.663,137.754C311.074,137.912 311.405,138.145 311.657,138.452L310.672,139.52C310.54,139.311 310.365,139.161 310.146,139.068C309.927,138.975 309.697,138.928 309.456,138.928C309.215,138.928 308.988,138.977 308.774,139.076C308.561,139.175 308.377,139.311 308.224,139.487C308.071,139.662 307.953,139.87 307.871,140.111C307.789,140.352 307.747,140.615 307.747,140.899C307.747,141.184 307.789,141.45 307.871,141.696C307.953,141.943 308.071,142.151 308.224,142.32C308.377,142.49 308.555,142.624 308.758,142.723C308.96,142.821 309.188,142.871 309.439,142.871C309.724,142.871 309.976,142.81 310.195,142.69C310.37,142.591 310.518,142.465 310.639,142.312L309.784,144.283Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(4,0,0,4,-1224.68,-536)">
<path d="M313.746,143.283L313.453,142.511L311.528,142.511L311.164,143.505L309.868,143.505L311.943,138.474L313.113,138.474L314.53,141.949C314.192,142.354 313.931,142.798 313.746,143.283ZM312.509,139.82L311.881,141.543L313.126,141.543L312.509,139.82Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(4,0,0,4,-1224.68,-536)">
<path d="M322.17,145.016C322.136,146.149 321.741,147.094 320.985,147.852C320.229,148.611 319.287,148.998 318.157,149.016C317.028,148.998 316.09,148.611 315.343,147.852C314.596,147.094 314.205,146.149 314.17,145.016C314.205,143.883 314.596,142.937 315.343,142.179C316.09,141.421 317.028,141.033 318.157,141.016C319.287,141.033 320.229,141.421 320.985,142.179C321.741,142.937 322.136,143.883 322.17,145.016ZM321.266,143.082C321.258,143.071 321.249,143.061 321.239,143.052C320.885,142.697 320.309,142.697 319.955,143.052L318.698,144.307L318.182,145.504C318.182,145.504 317.782,144.477 317.782,144.477L317.324,144.019C316.97,143.665 316.394,143.665 316.04,144.019C315.685,144.374 315.685,144.949 316.04,145.304L317.513,146.777C317.688,146.952 317.917,147.04 318.146,147.042C318.153,147.042 318.16,147.042 318.166,147.042C318.395,147.039 318.623,146.951 318.797,146.777L321.238,144.336C321.582,143.992 321.592,143.438 321.266,143.082Z" style="fill-rule:nonzero;"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-242,-134)">
<g transform="matrix(0.25,0,0,0.25,242,134)">
<rect id="certificate" x="0" y="0" width="64" height="64" style="fill:none;"/>
<g id="certificate1" serif:id="certificate">
<g transform="matrix(4,0,0,4,-1329.92,-536)">
<path d="M333.48,134L347.48,134C348.032,134 348.48,134.448 348.48,135L348.48,145C348.48,145.552 348.032,146 347.48,146L342,146L342,145L347.48,145L347.48,135L333.48,135L333.48,145L334,145L334,146L333.48,146C332.928,146 332.48,145.552 332.48,145L332.48,135C332.48,134.448 332.928,134 333.48,134Z"/>
</g>
<g transform="matrix(4.32185,0,0,4.32185,-1439.2,-580.832)">
<circle cx="338.097" cy="141.352" r="3.239"/>
</g>
<g transform="matrix(4,0,0,4,-1329.92,-536)">
<path d="M340.472,145.264L341.472,150L337.967,148L334.48,150L335.48,145.258C336.195,145.737 337.055,146.017 337.98,146.017C338.901,146.017 339.758,145.74 340.472,145.264Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(4,0,0,4,-1329.92,-536)">
<path d="M346.48,137C346.48,136.735 346.374,136.48 346.187,136.293C345.999,136.105 345.745,136 345.48,136C344.37,136 342.589,136 341.48,136C341.214,136 340.96,136.105 340.773,136.293C340.585,136.48 340.48,136.735 340.48,137C340.48,137.265 340.585,137.52 340.773,137.707C340.96,137.895 341.214,138 341.48,138C342.589,138 344.37,138 345.48,138C345.745,138 345.999,137.895 346.187,137.707C346.374,137.52 346.48,137.265 346.48,137Z"/>
</g>
<g transform="matrix(4,0,0,4,-1329.92,-524)">
<path d="M346.48,137C346.48,136.735 346.374,136.48 346.187,136.293C345.999,136.105 345.745,136 345.48,136C344.37,136 344.589,136 343.48,136C343.214,136 342.96,136.105 342.773,136.293C342.585,136.48 342.48,136.735 342.48,137C342.48,137.265 342.585,137.52 342.773,137.707C342.96,137.895 343.214,138 343.48,138C344.589,138 344.37,138 345.48,138C345.745,138 345.999,137.895 346.187,137.707C346.374,137.52 346.48,137.265 346.48,137Z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-162,-134)">
<g transform="matrix(0.25,0,0,0.25,162,134)">
<rect id="filter-circle-check" x="0" y="0" width="64" height="64" style="fill:none;"/>
<g id="filter-circle-check1" serif:id="filter-circle-check">
<g transform="matrix(4,0,0,4,-1008,-536)">
<path d="M252.113,135.414C252.297,135.025 252.686,134.778 253.116,134.778L265.116,134.778C265.547,134.778 265.936,135.025 266.119,135.414C266.302,135.803 266.247,136.261 265.975,136.594L263.016,140.211C260.788,140.669 259.116,142.639 259.116,145C259.116,145.761 259.291,146.483 259.602,147.125C259.558,147.103 259.513,147.075 259.472,147.044L257.694,145.711C257.469,145.544 257.338,145.281 257.338,145L257.338,142.803L252.255,136.592C251.986,136.261 251.927,135.8 252.113,135.414Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(4,0,0,4,-1224.68,-536)">
<path d="M322.17,145.016C322.136,146.149 321.741,147.094 320.985,147.852C320.229,148.611 319.287,148.998 318.157,149.016C317.028,148.998 316.09,148.611 315.343,147.852C314.596,147.094 314.205,146.149 314.17,145.016C314.205,143.883 314.596,142.937 315.343,142.179C316.09,141.421 317.028,141.033 318.157,141.016C319.287,141.033 320.229,141.421 320.985,142.179C321.741,142.937 322.136,143.883 322.17,145.016ZM321.266,143.082C321.258,143.071 321.249,143.061 321.239,143.052C320.885,142.697 320.309,142.697 319.955,143.052L318.698,144.307L318.182,145.504C318.182,145.504 317.782,144.477 317.782,144.477L317.324,144.019C316.97,143.665 316.394,143.665 316.04,144.019C315.685,144.374 315.685,144.949 316.04,145.304L317.513,146.777C317.688,146.952 317.917,147.04 318.146,147.042C318.153,147.042 318.16,147.042 318.166,147.042C318.395,147.039 318.623,146.951 318.797,146.777L321.238,144.336C321.582,143.992 321.592,143.438 321.266,143.082Z" style="fill-rule:nonzero;"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-140,-134)">
<g transform="matrix(0.25,0,0,0.25,140,134)">
<rect id="icinga" x="0" y="0" width="64" height="64" style="fill:none;"/>
<clipPath id="_clip1">
<rect id="icinga1" serif:id="icinga" x="0" y="0" width="64" height="64"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(0.028493,0,0,0.028493,1.07196,0)">
<g transform="matrix(4,0,0,4,0,0)">
<path d="M305.637,44.644L330.458,49.188L248.679,280.771L223.859,276.227L305.637,44.644Z"/>
</g>
<g transform="matrix(4,0,0,4,0,0)">
<path d="M481.944,202.533L481.944,218.623L274.047,288.816L268.684,272.726L481.944,202.533Z"/>
</g>
<g transform="matrix(4,0,0,4,0,0)">
<path d="M350.332,421.156L350.332,445.168L259.901,322.169L282.83,309.243L350.332,421.156Z"/>
</g>
<g transform="matrix(4,0,0,4,0,0)">
<path d="M103.323,463.704L88.445,448.802L201.173,315.867L236.269,323.319L103.323,463.704Z"/>
</g>
<g transform="matrix(4,0,0,4,0,0)">
<path d="M95.884,175.482L74.608,146.769L244.031,270.497L228.507,291.754L95.884,175.482Z"/>
</g>
<g id="Orb-1" serif:id="Orb 1" transform="matrix(6.01058,0,0,6.08489,-684.883,1.9677)">
<path d="M318.047,0C345.44,0 367.68,21.968 367.68,49.026C367.68,76.084 345.44,98.052 318.047,98.052C290.655,98.052 268.415,76.084 268.415,49.026C268.415,21.968 290.655,0 318.047,0Z"/>
</g>
<g id="Orb-2" serif:id="Orb 2" transform="matrix(6.2603,0,0,6.3377,-1189.12,-493.419)">
<path d="M503.488,172.306C525.147,172.306 542.731,189.676 542.731,211.07C542.731,232.465 525.147,249.834 503.488,249.834C481.829,249.834 464.245,232.465 464.245,211.07C464.245,189.676 481.829,172.306 503.488,172.306Z"/>
</g>
<g id="Orb-3" serif:id="Orb 3" transform="matrix(9.01966,0,0,9.13117,-1337.13,-2229.08)">
<path d="M303.61,417.078C314.348,417.078 323.066,425.69 323.066,436.296C323.066,446.903 314.348,455.514 303.61,455.514C292.873,455.514 284.155,446.903 284.155,436.296C284.155,425.69 292.873,417.078 303.61,417.078Z"/>
</g>
<g id="Orb-4" serif:id="Orb 4" transform="matrix(4.93841,0,0,4.99946,-37.6219,-561.24)">
<path d="M63.961,435.181C99.263,435.181 127.923,463.492 127.923,498.362C127.923,533.232 99.263,561.542 63.961,561.542C28.66,561.542 0,533.232 0,498.362C0,463.492 28.66,435.181 63.961,435.181Z"/>
</g>
<g id="Orb-5" serif:id="Orb 5" transform="matrix(6.84016,0,0,6.92473,-189.854,-460.346)">
<path d="M66.846,126.988C83.837,126.988 97.632,140.614 97.632,157.398C97.632,174.181 83.837,187.807 66.846,187.807C49.855,187.807 36.061,174.181 36.061,157.398C36.061,140.614 49.855,126.988 66.846,126.988Z"/>
</g>
<g id="Orb-Center" serif:id="Orb Center" transform="matrix(4.34182,0,0,4.3955,19.5912,-110.981)">
<path d="M213.156,184.794C266.692,184.794 310.156,227.728 310.156,280.61C310.156,333.492 266.692,376.425 213.156,376.425C159.62,376.425 116.156,333.492 116.156,280.61C116.156,227.728 159.62,184.794 213.156,184.794Z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-40,-134)">
<g transform="matrix(0.25,0,0,0.25,40,134)">
<rect id="list-view-default" x="0" y="0" width="64" height="64" style="fill:none;"/>
<clipPath id="_clip1">
<rect id="list-view-default1" serif:id="list-view-default" x="0" y="0" width="64" height="64"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g id="_16-list-view-default" serif:id="16/list-view-default" transform="matrix(1.02005,0,0,1.02005,-2.30518,-1.16359)">
<g transform="matrix(3.92139,0,0,3.92139,2.25988,-492.954)">
<rect x="0" y="126" width="16" height="16" style="fill:none;"/>
</g>
<g transform="matrix(5.76344,0,0,5.76344,-25.17,-51.3628)">
<circle cx="6.46" cy="11.151" r="1.021"/>
</g>
<g transform="matrix(0.802102,0,0,0.961081,-54.422,-3.54908)">
<rect x="100" y="11" width="44" height="12.241"/>
</g>
<g transform="matrix(0.802102,0,0,0.961081,-54.422,16.0579)">
<rect x="100" y="11" width="44" height="12.241"/>
</g>
<g transform="matrix(0.802102,0,0,0.961081,-54.422,35.6648)">
<rect x="100" y="11" width="44" height="12.241"/>
</g>
<g transform="matrix(5.76344,0,0,5.76344,-25.17,-31.7558)">
<circle cx="6.46" cy="11.151" r="1.021"/>
</g>
<g transform="matrix(5.76344,0,0,5.76344,-25.17,-12.1489)">
<circle cx="6.46" cy="11.151" r="1.021"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-20,-134)">
<g transform="matrix(0.25,0,0,0.25,20,134)">
<rect id="list-view-detailed" x="0" y="0" width="64" height="64" style="fill:none;"/>
<g id="list-view-detailed1" serif:id="list-view-detailed">
<g id="_16-list-view-detailed" serif:id="16/list-view-detailed">
<g transform="matrix(4,0,0,4,0,-504)">
<rect x="0" y="126" width="16" height="16" style="fill:none;"/>
</g>
<g transform="matrix(1.00393,0,0,1.07437,1.27514,2.18191)">
<rect x="18.652" y="5.434" width="39.843" height="11.132"/>
</g>
<g transform="matrix(1.00393,0,0,1.03883,1.27514,14.5728)">
<rect x="18.652" y="9.075" width="39.843" height="3.85"/>
</g>
<g transform="matrix(5.87898,0,0,5.87898,-27.9798,-51.5763)">
<circle cx="6.46" cy="11.151" r="1.021"/>
</g>
<g transform="matrix(5.87898,0,0,5.87898,-27.9798,-23.556)">
<circle cx="6.46" cy="11.151" r="1.021"/>
</g>
<g transform="matrix(1.00393,0,0,1.078,1.27514,30.1419)">
<rect x="18.652" y="5.434" width="39.843" height="11.132"/>
</g>
<g transform="matrix(1.00393,0,0,1.03533,1.27513,42.6113)">
<rect x="18.652" y="9.068" width="39.843" height="3.863"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,0,-134)">
<g transform="matrix(0.25,0,0,0.25,0,134)">
<rect id="list-view-minimal" x="0" y="0" width="64" height="64" style="fill:none;"/>
<g id="list-view-minimal1" serif:id="list-view-minimal">
<g id="_16-list-view-minimal" serif:id="16/list-view-minimal">
<g transform="matrix(4,0,0,4,0,-504)">
<rect x="0" y="126" width="16" height="16" style="fill:none;"/>
</g>
<g transform="matrix(3.91932,0,0,3.91932,-17.3199,-35.704)">
<circle cx="6.46" cy="11.151" r="1.021"/>
</g>
<g transform="matrix(3.91932,0,0,3.91932,-17.3199,-23.6838)">
<circle cx="6.46" cy="11.151" r="1.021"/>
</g>
<g transform="matrix(3.91932,0,0,3.91932,-17.3199,-11.6838)">
<circle cx="6.46" cy="11.151" r="1.021"/>
</g>
<g transform="matrix(1.02326,0,0,0.6611,-1.44408,0.727897)">
<rect x="20.957" y="7.975" width="39.091" height="6.051"/>
</g>
<g transform="matrix(1.02326,0,0,0.6611,-1.44408,24.7481)">
<rect x="20.957" y="7.975" width="39.091" height="6.051"/>
</g>
<g transform="matrix(1.02326,0,0,0.6611,-1.44408,12.7481)">
<rect x="20.957" y="7.975" width="39.091" height="6.051"/>
</g>
<g transform="matrix(1.02326,0,0,0.6611,-1.44408,36.7481)">
<rect x="20.957" y="7.975" width="39.091" height="6.051"/>
</g>
<g transform="matrix(1.02326,0,0,0.6611,-1.44408,48.7481)">
<rect x="20.957" y="7.975" width="39.091" height="6.051"/>
</g>
<g transform="matrix(3.91932,0,0,3.91932,-17.3199,0.336422)">
<circle cx="6.46" cy="11.151" r="1.021"/>
</g>
<g transform="matrix(3.91932,0,0,3.91932,-17.3199,12.3162)">
<circle cx="6.46" cy="11.151" r="1.021"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-268,-134)">
<g transform="matrix(0.25,0,0,0.25,268,134)">
<rect id="refresh-cert" x="0" y="0" width="64" height="64" style="fill:none;"/>
<clipPath id="_clip1">
<rect id="refresh-cert1" serif:id="refresh-cert" x="0" y="0" width="64" height="64"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(4,0,0,4,-1435.16,-536)">
<rect x="363.789" y="141" width="6" height="4.017" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(4,0,0,4,-1435.16,-536)">
<path d="M364.789,140.5L364.789,139C364.789,138.448 365.237,138 365.789,138L367.789,138C368.341,138 368.789,138.448 368.789,139L368.789,140.5L364.789,140.5ZM367.789,139L365.789,139L365.789,140.5L367.789,140.5L367.789,139Z"/>
</g>
<g transform="matrix(4,0,0,4,-1432,-536)">
<path d="M361.007,145.326C362.083,146.938 363.919,148 366,148C368.212,148 370.146,146.8 371.186,145.017L373.411,145.017C372.219,147.939 369.348,150 366,150C363.179,150 360.697,148.537 359.272,146.328L357.998,147.064L358.031,142L362.429,144.506L361.007,145.326ZM371.023,138.72C369.952,137.082 368.101,136 366,136C363.39,136 361.166,137.671 360.342,140L358.252,140C359.141,136.551 362.275,134 366,134C368.841,134 371.338,135.484 372.757,137.718L374.002,137L373.969,142.064L369.571,139.558L371.023,138.72Z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-80,-134)">
<g transform="matrix(0.25,0,0,0.25,80,134)">
<rect id="th-list" x="0" y="0" width="64" height="64" style="fill:none;"/>
<clipPath id="_clip1">
<rect id="th-list1" serif:id="th-list" x="0" y="0" width="64" height="64"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g id="th-list2" serif:id="th-list">
<g transform="matrix(5.33333,0,0,5.33333,-563.593,-880)">
<rect x="105.674" y="165" width="12" height="12" style="fill:none;"/>
</g>
<g transform="matrix(59.7652,0,0,59.7652,4.03068,52.9178)">
<path d="M0,-0.062C0,-0.042 0.007,-0.025 0.021,-0.011C0.035,0.003 0.052,0.01 0.072,0.01L0.104,0.01C0.124,0.01 0.141,0.003 0.155,-0.011C0.169,-0.025 0.176,-0.042 0.176,-0.062C0.176,-0.082 0.169,-0.099 0.155,-0.113C0.141,-0.127 0.124,-0.134 0.104,-0.134L0.072,-0.134C0.052,-0.134 0.035,-0.127 0.021,-0.113C0.007,-0.099 0,-0.082 0,-0.062ZM0,-0.35C0,-0.33 0.007,-0.313 0.021,-0.299C0.035,-0.285 0.052,-0.278 0.072,-0.278L0.104,-0.278C0.124,-0.278 0.141,-0.285 0.155,-0.299C0.169,-0.313 0.176,-0.33 0.176,-0.35C0.176,-0.37 0.169,-0.387 0.155,-0.401C0.141,-0.415 0.124,-0.422 0.104,-0.422L0.072,-0.422C0.052,-0.422 0.035,-0.415 0.021,-0.401C0.007,-0.387 0,-0.37 0,-0.35ZM0,-0.638C0,-0.618 0.007,-0.601 0.021,-0.587C0.035,-0.573 0.052,-0.566 0.072,-0.566L0.104,-0.566C0.124,-0.566 0.141,-0.573 0.155,-0.587C0.169,-0.601 0.176,-0.618 0.176,-0.638C0.176,-0.658 0.169,-0.675 0.155,-0.689C0.141,-0.703 0.124,-0.71 0.104,-0.71L0.072,-0.71C0.052,-0.71 0.035,-0.703 0.021,-0.689C0.007,-0.675 0,-0.658 0,-0.638ZM0.234,-0.062C0.234,-0.042 0.241,-0.025 0.255,-0.011C0.269,0.003 0.286,0.01 0.306,0.01L0.865,0.01C0.885,0.01 0.902,0.003 0.916,-0.011C0.93,-0.025 0.937,-0.042 0.937,-0.062C0.937,-0.082 0.93,-0.099 0.916,-0.113C0.902,-0.127 0.885,-0.134 0.865,-0.134L0.306,-0.134C0.286,-0.134 0.269,-0.127 0.255,-0.113C0.241,-0.099 0.234,-0.082 0.234,-0.062ZM0.234,-0.35C0.234,-0.33 0.241,-0.313 0.255,-0.299C0.269,-0.285 0.286,-0.278 0.306,-0.278L0.865,-0.278C0.885,-0.278 0.902,-0.285 0.916,-0.299C0.93,-0.313 0.937,-0.33 0.937,-0.35C0.937,-0.37 0.93,-0.387 0.916,-0.401C0.902,-0.415 0.885,-0.422 0.865,-0.422L0.306,-0.422C0.286,-0.422 0.269,-0.415 0.255,-0.401C0.241,-0.387 0.234,-0.37 0.234,-0.35ZM0.234,-0.638C0.234,-0.618 0.241,-0.601 0.255,-0.587C0.269,-0.573 0.286,-0.566 0.306,-0.566L0.865,-0.566C0.885,-0.566 0.902,-0.573 0.916,-0.587C0.93,-0.601 0.937,-0.618 0.937,-0.638C0.937,-0.658 0.93,-0.675 0.916,-0.689C0.902,-0.703 0.885,-0.71 0.865,-0.71L0.306,-0.71C0.286,-0.71 0.269,-0.703 0.255,-0.689C0.241,-0.675 0.234,-0.658 0.234,-0.638Z" style="fill-rule:nonzero;"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-60,-134)">
<g transform="matrix(0.25,0,0,0.25,60,134)">
<rect id="grid" x="0" y="0" width="64" height="64" style="fill:none;"/>
<g id="grid1" serif:id="grid">
<g>
<g transform="matrix(5.33333,0,0,5.33333,-320,-880)">
<g>
<rect x="60" y="165" width="12" height="12" style="fill:none;"/>
</g>
</g>
<g transform="matrix(59.7577,0,0,59.9572,4.01886,52.985)">
<path d="M0,0.066L0,-0.22C0,-0.235 0.005,-0.247 0.015,-0.257C0.025,-0.267 0.037,-0.272 0.052,-0.273L0.338,-0.273C0.352,-0.273 0.364,-0.268 0.375,-0.257C0.386,-0.246 0.391,-0.234 0.39,-0.22L0.39,0.066C0.39,0.08 0.385,0.092 0.375,0.102C0.365,0.112 0.353,0.117 0.338,0.117L0.052,0.117C0.037,0.117 0.025,0.112 0.015,0.102C0.005,0.092 0,0.08 0,0.066ZM0,-0.48L0,-0.766C0,-0.78 0.005,-0.792 0.015,-0.802C0.025,-0.812 0.037,-0.817 0.052,-0.817L0.338,-0.817C0.352,-0.817 0.364,-0.812 0.375,-0.802C0.386,-0.792 0.391,-0.78 0.39,-0.766L0.39,-0.48C0.39,-0.465 0.385,-0.453 0.375,-0.443C0.365,-0.433 0.353,-0.428 0.338,-0.427L0.052,-0.427C0.038,-0.427 0.026,-0.432 0.015,-0.443C0.004,-0.454 -0.001,-0.466 0,-0.48ZM0.088,0.03L0.302,0.03L0.302,-0.184L0.088,-0.184L0.088,0.03ZM0.088,-0.516L0.302,-0.516L0.302,-0.729L0.088,-0.729L0.088,-0.516ZM0.547,0.066L0.547,-0.22C0.547,-0.235 0.552,-0.247 0.562,-0.257C0.572,-0.267 0.584,-0.272 0.599,-0.273L0.885,-0.273C0.899,-0.273 0.911,-0.268 0.922,-0.257C0.933,-0.246 0.938,-0.234 0.937,-0.22L0.937,0.066C0.937,0.08 0.932,0.092 0.922,0.102C0.912,0.112 0.9,0.117 0.885,0.117L0.599,0.117C0.585,0.117 0.573,0.112 0.562,0.102C0.551,0.092 0.546,0.08 0.547,0.066ZM0.547,-0.48L0.547,-0.766C0.547,-0.78 0.552,-0.792 0.562,-0.802C0.572,-0.812 0.584,-0.817 0.599,-0.817L0.885,-0.817C0.9,-0.817 0.912,-0.812 0.922,-0.802C0.932,-0.792 0.937,-0.78 0.937,-0.766L0.937,-0.48C0.937,-0.465 0.932,-0.453 0.922,-0.443C0.912,-0.433 0.9,-0.428 0.885,-0.427L0.599,-0.427C0.585,-0.427 0.573,-0.432 0.562,-0.443C0.551,-0.454 0.546,-0.466 0.547,-0.48ZM0.635,0.03L0.85,0.03L0.85,-0.184L0.635,-0.184L0.635,0.03ZM0.635,-0.516L0.85,-0.516L0.85,-0.729L0.635,-0.729L0.635,-0.516Z" style="fill-rule:nonzero;"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,62 @@
@font-face {
font-family: 'Icinga-Icons';
src:
url('fonts/Icinga-Icons.ttf?xnw9vw') format('truetype'),
url('fonts/Icinga-Icons.woff?xnw9vw') format('woff'),
url('fonts/Icinga-Icons.svg?xnw9vw#Icinga-Icons') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
}
[class^="iicon-"], [class*=" iicon-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'Icinga-Icons' !important;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iicon-certificate:before {
content: "\e906";
}
.iicon-filter-check-circle:before {
content: "\e90b";
}
.iicon-ca-check-circle:before {
content: "\e908";
}
.iicon-refresh-cert:before {
content: "\e909";
}
.iicon-th-list:before {
content: "\e90a";
}
.iicon-icinga:before {
content: "\e907";
}
.iicon-list-view-minimal:before {
content: "\e900";
}
.iicon-list-view-detailed:before {
content: "\e901";
}
.iicon-list-view-default:before {
content: "\e902";
}
.iicon-grid:before {
content: "\e903";
}
.iicon-bracket-open:before {
content: "\e904";
}
.iicon-bracket-close:before {
content: "\e905";
}

View File

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

After

Width:  |  Height:  |  Size: 559 B

View File

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

After

Width:  |  Height:  |  Size: 558 B

1658
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

25
vendor/autoload.php vendored Normal file
View 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 ComposerAutoloaderInitcf5ad91649510c1a7d389314192c90cd::getLoader();

20
vendor/brick/math/LICENSE vendored Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013-present Benjamin Morel
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.

39
vendor/brick/math/composer.json vendored Normal file
View File

@ -0,0 +1,39 @@
{
"name": "brick/math",
"description": "Arbitrary-precision arithmetic library",
"type": "library",
"keywords": [
"Brick",
"Math",
"Mathematics",
"Arbitrary-precision",
"Arithmetic",
"BigInteger",
"BigDecimal",
"BigRational",
"BigNumber",
"Bignum",
"Decimal",
"Rational",
"Integer"
],
"license": "MIT",
"require": {
"php": "^8.1"
},
"require-dev": {
"phpunit/phpunit": "^10.1",
"php-coveralls/php-coveralls": "^2.2",
"vimeo/psalm": "6.8.8"
},
"autoload": {
"psr-4": {
"Brick\\Math\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Brick\\Math\\Tests\\": "tests/"
}
}
}

792
vendor/brick/math/src/BigDecimal.php vendored Normal file
View File

@ -0,0 +1,792 @@
<?php
declare(strict_types=1);
namespace Brick\Math;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NegativeNumberException;
use Brick\Math\Internal\Calculator;
use Override;
/**
* Immutable, arbitrary-precision signed decimal numbers.
*
* @psalm-immutable
*/
final class BigDecimal extends BigNumber
{
/**
* The unscaled value of this decimal number.
*
* This is a string of digits with an optional leading minus sign.
* No leading zero must be present.
* No leading minus sign must be present if the value is 0.
*/
private readonly string $value;
/**
* The scale (number of digits after the decimal point) of this decimal number.
*
* This must be zero or more.
*/
private readonly int $scale;
/**
* Protected constructor. Use a factory method to obtain an instance.
*
* @param string $value The unscaled value, validated.
* @param int $scale The scale, validated.
*/
protected function __construct(string $value, int $scale = 0)
{
$this->value = $value;
$this->scale = $scale;
}
/**
* @psalm-pure
*/
#[Override]
protected static function from(BigNumber $number): static
{
return $number->toBigDecimal();
}
/**
* Creates a BigDecimal from an unscaled value and a scale.
*
* Example: `(12345, 3)` will result in the BigDecimal `12.345`.
*
* @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
* @param int $scale The scale of the number, positive or zero.
*
* @throws \InvalidArgumentException If the scale is negative.
*
* @psalm-pure
*/
public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal
{
if ($scale < 0) {
throw new \InvalidArgumentException('The scale cannot be negative.');
}
return new BigDecimal((string) BigInteger::of($value), $scale);
}
/**
* Returns a BigDecimal representing zero, with a scale of zero.
*
* @psalm-pure
*/
public static function zero() : BigDecimal
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigDecimal|null $zero
*/
static $zero;
if ($zero === null) {
$zero = new BigDecimal('0');
}
return $zero;
}
/**
* Returns a BigDecimal representing one, with a scale of zero.
*
* @psalm-pure
*/
public static function one() : BigDecimal
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigDecimal|null $one
*/
static $one;
if ($one === null) {
$one = new BigDecimal('1');
}
return $one;
}
/**
* Returns a BigDecimal representing ten, with a scale of zero.
*
* @psalm-pure
*/
public static function ten() : BigDecimal
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigDecimal|null $ten
*/
static $ten;
if ($ten === null) {
$ten = new BigDecimal('10');
}
return $ten;
}
/**
* Returns the sum of this number and the given one.
*
* The result has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal.
*
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
*/
public function plus(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->value === '0' && $that->scale <= $this->scale) {
return $this;
}
if ($this->value === '0' && $this->scale <= $that->scale) {
return $that;
}
[$a, $b] = $this->scaleValues($this, $that);
$value = Calculator::get()->add($a, $b);
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
return new BigDecimal($value, $scale);
}
/**
* Returns the difference of this number and the given one.
*
* The result has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal.
*
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
*/
public function minus(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->value === '0' && $that->scale <= $this->scale) {
return $this;
}
[$a, $b] = $this->scaleValues($this, $that);
$value = Calculator::get()->sub($a, $b);
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
return new BigDecimal($value, $scale);
}
/**
* Returns the product of this number and the given one.
*
* The result has a scale of `$this->scale + $that->scale`.
*
* @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal.
*
* @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal.
*/
public function multipliedBy(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->value === '1' && $that->scale === 0) {
return $this;
}
if ($this->value === '1' && $this->scale === 0) {
return $that;
}
$value = Calculator::get()->mul($this->value, $that->value);
$scale = $this->scale + $that->scale;
return new BigDecimal($value, $scale);
}
/**
* Returns the result of the division of this number by the given one, at the given scale.
*
* @param BigNumber|int|float|string $that The divisor.
* @param int|null $scale The desired scale, or null to use the scale of this number.
* @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY.
*
* @throws \InvalidArgumentException If the scale or rounding mode is invalid.
* @throws MathException If the number is invalid, is zero, or rounding was necessary.
*/
public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}
if ($scale === null) {
$scale = $this->scale;
} elseif ($scale < 0) {
throw new \InvalidArgumentException('Scale cannot be negative.');
}
if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) {
return $this;
}
$p = $this->valueWithMinScale($that->scale + $scale);
$q = $that->valueWithMinScale($this->scale - $scale);
$result = Calculator::get()->divRound($p, $q, $roundingMode);
return new BigDecimal($result, $scale);
}
/**
* Returns the exact result of the division of this number by the given one.
*
* The scale of the result is automatically calculated to fit all the fraction digits.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero,
* or the result yields an infinite number of digits.
*/
public function exactlyDividedBy(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->value === '0') {
throw DivisionByZeroException::divisionByZero();
}
[, $b] = $this->scaleValues($this, $that);
$d = \rtrim($b, '0');
$scale = \strlen($b) - \strlen($d);
$calculator = Calculator::get();
foreach ([5, 2] as $prime) {
for (;;) {
$lastDigit = (int) $d[-1];
if ($lastDigit % $prime !== 0) {
break;
}
$d = $calculator->divQ($d, (string) $prime);
$scale++;
}
}
return $this->dividedBy($that, $scale)->stripTrailingZeros();
}
/**
* Returns this number exponentiated to the given value.
*
* The result has a scale of `$this->scale * $exponent`.
*
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
*/
public function power(int $exponent) : BigDecimal
{
if ($exponent === 0) {
return BigDecimal::one();
}
if ($exponent === 1) {
return $this;
}
if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
throw new \InvalidArgumentException(\sprintf(
'The exponent %d is not in the range 0 to %d.',
$exponent,
Calculator::MAX_POWER
));
}
return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent);
}
/**
* Returns the quotient of the division of this number by the given one.
*
* The quotient has a scale of `0`.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @throws MathException If the divisor is not a valid decimal number, or is zero.
*/
public function quotient(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}
$p = $this->valueWithMinScale($that->scale);
$q = $that->valueWithMinScale($this->scale);
$quotient = Calculator::get()->divQ($p, $q);
return new BigDecimal($quotient, 0);
}
/**
* Returns the remainder of the division of this number by the given one.
*
* The remainder has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @throws MathException If the divisor is not a valid decimal number, or is zero.
*/
public function remainder(BigNumber|int|float|string $that) : BigDecimal
{
$that = BigDecimal::of($that);
if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}
$p = $this->valueWithMinScale($that->scale);
$q = $that->valueWithMinScale($this->scale);
$remainder = Calculator::get()->divR($p, $q);
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
return new BigDecimal($remainder, $scale);
}
/**
* Returns the quotient and remainder of the division of this number by the given one.
*
* The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
*
* @return BigDecimal[] An array containing the quotient and the remainder.
*
* @psalm-return array{BigDecimal, BigDecimal}
*
* @throws MathException If the divisor is not a valid decimal number, or is zero.
*/
public function quotientAndRemainder(BigNumber|int|float|string $that) : array
{
$that = BigDecimal::of($that);
if ($that->isZero()) {
throw DivisionByZeroException::divisionByZero();
}
$p = $this->valueWithMinScale($that->scale);
$q = $that->valueWithMinScale($this->scale);
[$quotient, $remainder] = Calculator::get()->divQR($p, $q);
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
$quotient = new BigDecimal($quotient, 0);
$remainder = new BigDecimal($remainder, $scale);
return [$quotient, $remainder];
}
/**
* Returns the square root of this number, rounded down to the given number of decimals.
*
* @throws \InvalidArgumentException If the scale is negative.
* @throws NegativeNumberException If this number is negative.
*/
public function sqrt(int $scale) : BigDecimal
{
if ($scale < 0) {
throw new \InvalidArgumentException('Scale cannot be negative.');
}
if ($this->value === '0') {
return new BigDecimal('0', $scale);
}
if ($this->value[0] === '-') {
throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
}
$value = $this->value;
$addDigits = 2 * $scale - $this->scale;
if ($addDigits > 0) {
// add zeros
$value .= \str_repeat('0', $addDigits);
} elseif ($addDigits < 0) {
// trim digits
if (-$addDigits >= \strlen($this->value)) {
// requesting a scale too low, will always yield a zero result
return new BigDecimal('0', $scale);
}
$value = \substr($value, 0, $addDigits);
}
$value = Calculator::get()->sqrt($value);
return new BigDecimal($value, $scale);
}
/**
* Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
*/
public function withPointMovedLeft(int $n) : BigDecimal
{
if ($n === 0) {
return $this;
}
if ($n < 0) {
return $this->withPointMovedRight(-$n);
}
return new BigDecimal($this->value, $this->scale + $n);
}
/**
* Returns a copy of this BigDecimal with the decimal point moved $n places to the right.
*/
public function withPointMovedRight(int $n) : BigDecimal
{
if ($n === 0) {
return $this;
}
if ($n < 0) {
return $this->withPointMovedLeft(-$n);
}
$value = $this->value;
$scale = $this->scale - $n;
if ($scale < 0) {
if ($value !== '0') {
$value .= \str_repeat('0', -$scale);
}
$scale = 0;
}
return new BigDecimal($value, $scale);
}
/**
* Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
*/
public function stripTrailingZeros() : BigDecimal
{
if ($this->scale === 0) {
return $this;
}
$trimmedValue = \rtrim($this->value, '0');
if ($trimmedValue === '') {
return BigDecimal::zero();
}
$trimmableZeros = \strlen($this->value) - \strlen($trimmedValue);
if ($trimmableZeros === 0) {
return $this;
}
if ($trimmableZeros > $this->scale) {
$trimmableZeros = $this->scale;
}
$value = \substr($this->value, 0, -$trimmableZeros);
$scale = $this->scale - $trimmableZeros;
return new BigDecimal($value, $scale);
}
/**
* Returns the absolute value of this number.
*/
public function abs() : BigDecimal
{
return $this->isNegative() ? $this->negated() : $this;
}
/**
* Returns the negated value of this number.
*/
public function negated() : BigDecimal
{
return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
}
#[Override]
public function compareTo(BigNumber|int|float|string $that) : int
{
$that = BigNumber::of($that);
if ($that instanceof BigInteger) {
$that = $that->toBigDecimal();
}
if ($that instanceof BigDecimal) {
[$a, $b] = $this->scaleValues($this, $that);
return Calculator::get()->cmp($a, $b);
}
return - $that->compareTo($this);
}
#[Override]
public function getSign() : int
{
return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
}
public function getUnscaledValue() : BigInteger
{
return self::newBigInteger($this->value);
}
public function getScale() : int
{
return $this->scale;
}
/**
* Returns the number of significant digits in the number.
*
* This is the number of digits to both sides of the decimal point, stripped of leading zeros.
* The sign has no impact on the result.
*
* Examples:
* 0 => 0
* 0.0 => 0
* 123 => 3
* 123.456 => 6
* 0.00123 => 3
* 0.0012300 => 5
*/
public function getPrecision(): int
{
$value = $this->value;
if ($value === '0') {
return 0;
}
$length = \strlen($value);
return ($value[0] === '-') ? $length - 1 : $length;
}
/**
* Returns a string representing the integral part of this decimal number.
*
* Example: `-123.456` => `-123`.
*/
public function getIntegralPart() : string
{
if ($this->scale === 0) {
return $this->value;
}
$value = $this->getUnscaledValueWithLeadingZeros();
return \substr($value, 0, -$this->scale);
}
/**
* Returns a string representing the fractional part of this decimal number.
*
* If the scale is zero, an empty string is returned.
*
* Examples: `-123.456` => '456', `123` => ''.
*/
public function getFractionalPart() : string
{
if ($this->scale === 0) {
return '';
}
$value = $this->getUnscaledValueWithLeadingZeros();
return \substr($value, -$this->scale);
}
/**
* Returns whether this decimal number has a non-zero fractional part.
*/
public function hasNonZeroFractionalPart() : bool
{
return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
}
#[Override]
public function toBigInteger() : BigInteger
{
$zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0);
return self::newBigInteger($zeroScaleDecimal->value);
}
#[Override]
public function toBigDecimal() : BigDecimal
{
return $this;
}
#[Override]
public function toBigRational() : BigRational
{
$numerator = self::newBigInteger($this->value);
$denominator = self::newBigInteger('1' . \str_repeat('0', $this->scale));
return self::newBigRational($numerator, $denominator, false);
}
#[Override]
public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
if ($scale === $this->scale) {
return $this;
}
return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
}
#[Override]
public function toInt() : int
{
return $this->toBigInteger()->toInt();
}
#[Override]
public function toFloat() : float
{
return (float) (string) $this;
}
#[Override]
public function __toString() : string
{
if ($this->scale === 0) {
return $this->value;
}
$value = $this->getUnscaledValueWithLeadingZeros();
return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
}
/**
* This method is required for serializing the object and SHOULD NOT be accessed directly.
*
* @internal
*
* @return array{value: string, scale: int}
*/
public function __serialize(): array
{
return ['value' => $this->value, 'scale' => $this->scale];
}
/**
* This method is only here to allow unserializing the object and cannot be accessed directly.
*
* @internal
* @psalm-suppress RedundantPropertyInitializationCheck
*
* @param array{value: string, scale: int} $data
*
* @throws \LogicException
*/
public function __unserialize(array $data): void
{
if (isset($this->value)) {
throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
}
$this->value = $data['value'];
$this->scale = $data['scale'];
}
/**
* Puts the internal values of the given decimal numbers on the same scale.
*
* @return array{string, string} The scaled integer values of $x and $y.
*/
private function scaleValues(BigDecimal $x, BigDecimal $y) : array
{
$a = $x->value;
$b = $y->value;
if ($b !== '0' && $x->scale > $y->scale) {
$b .= \str_repeat('0', $x->scale - $y->scale);
} elseif ($a !== '0' && $x->scale < $y->scale) {
$a .= \str_repeat('0', $y->scale - $x->scale);
}
return [$a, $b];
}
private function valueWithMinScale(int $scale) : string
{
$value = $this->value;
if ($this->value !== '0' && $scale > $this->scale) {
$value .= \str_repeat('0', $scale - $this->scale);
}
return $value;
}
/**
* Adds leading zeros if necessary to the unscaled value to represent the full decimal number.
*/
private function getUnscaledValueWithLeadingZeros() : string
{
$value = $this->value;
$targetLength = $this->scale + 1;
$negative = ($value[0] === '-');
$length = \strlen($value);
if ($negative) {
$length--;
}
if ($length >= $targetLength) {
return $this->value;
}
if ($negative) {
$value = \substr($value, 1);
}
$value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT);
if ($negative) {
$value = '-' . $value;
}
return $value;
}
}

1062
vendor/brick/math/src/BigInteger.php vendored Normal file

File diff suppressed because it is too large Load Diff

515
vendor/brick/math/src/BigNumber.php vendored Normal file
View File

@ -0,0 +1,515 @@
<?php
declare(strict_types=1);
namespace Brick\Math;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\RoundingNecessaryException;
use Override;
/**
* Common interface for arbitrary-precision rational numbers.
*
* @psalm-immutable
*/
abstract class BigNumber implements \JsonSerializable
{
/**
* The regular expression used to parse integer or decimal numbers.
*/
private const PARSE_REGEXP_NUMERICAL =
'/^' .
'(?<sign>[\-\+])?' .
'(?<integral>[0-9]+)?' .
'(?<point>\.)?' .
'(?<fractional>[0-9]+)?' .
'(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
'$/';
/**
* The regular expression used to parse rational numbers.
*/
private const PARSE_REGEXP_RATIONAL =
'/^' .
'(?<sign>[\-\+])?' .
'(?<numerator>[0-9]+)' .
'\/?' .
'(?<denominator>[0-9]+)' .
'$/';
/**
* Creates a BigNumber of the given value.
*
* The concrete return type is dependent on the given value, with the following rules:
*
* - BigNumber instances are returned as is
* - integer numbers are returned as BigInteger
* - floating point numbers are converted to a string then parsed as such
* - strings containing a `/` character are returned as BigRational
* - strings containing a `.` character or using an exponential notation are returned as BigDecimal
* - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
*
* @throws NumberFormatException If the format of the number is not valid.
* @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
* @throws RoundingNecessaryException If the value cannot be converted to an instance of the subclass without rounding.
*
* @psalm-pure
*/
final public static function of(BigNumber|int|float|string $value) : static
{
$value = self::_of($value);
if (static::class === BigNumber::class) {
// https://github.com/vimeo/psalm/issues/10309
assert($value instanceof static);
return $value;
}
return static::from($value);
}
/**
* @throws NumberFormatException If the format of the number is not valid.
* @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
*
* @psalm-pure
*/
private static function _of(BigNumber|int|float|string $value) : BigNumber
{
if ($value instanceof BigNumber) {
return $value;
}
if (\is_int($value)) {
return new BigInteger((string) $value);
}
if (is_float($value)) {
$value = (string) $value;
}
if (str_contains($value, '/')) {
// Rational number
if (\preg_match(self::PARSE_REGEXP_RATIONAL, $value, $matches, PREG_UNMATCHED_AS_NULL) !== 1) {
throw NumberFormatException::invalidFormat($value);
}
$sign = $matches['sign'];
$numerator = $matches['numerator'];
$denominator = $matches['denominator'];
assert($numerator !== null);
assert($denominator !== null);
$numerator = self::cleanUp($sign, $numerator);
$denominator = self::cleanUp(null, $denominator);
if ($denominator === '0') {
throw DivisionByZeroException::denominatorMustNotBeZero();
}
return new BigRational(
new BigInteger($numerator),
new BigInteger($denominator),
false
);
} else {
// Integer or decimal number
if (\preg_match(self::PARSE_REGEXP_NUMERICAL, $value, $matches, PREG_UNMATCHED_AS_NULL) !== 1) {
throw NumberFormatException::invalidFormat($value);
}
$sign = $matches['sign'];
$point = $matches['point'];
$integral = $matches['integral'];
$fractional = $matches['fractional'];
$exponent = $matches['exponent'];
if ($integral === null && $fractional === null) {
throw NumberFormatException::invalidFormat($value);
}
if ($integral === null) {
$integral = '0';
}
if ($point !== null || $exponent !== null) {
$fractional = ($fractional ?? '');
$exponent = ($exponent !== null) ? (int)$exponent : 0;
if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) {
throw new NumberFormatException('Exponent too large.');
}
$unscaledValue = self::cleanUp($sign, $integral . $fractional);
$scale = \strlen($fractional) - $exponent;
if ($scale < 0) {
if ($unscaledValue !== '0') {
$unscaledValue .= \str_repeat('0', -$scale);
}
$scale = 0;
}
return new BigDecimal($unscaledValue, $scale);
}
$integral = self::cleanUp($sign, $integral);
return new BigInteger($integral);
}
}
/**
* Overridden by subclasses to convert a BigNumber to an instance of the subclass.
*
* @throws RoundingNecessaryException If the value cannot be converted.
*
* @psalm-pure
*/
abstract protected static function from(BigNumber $number): static;
/**
* Proxy method to access BigInteger's protected constructor from sibling classes.
*
* @internal
* @psalm-pure
*/
final protected function newBigInteger(string $value) : BigInteger
{
return new BigInteger($value);
}
/**
* Proxy method to access BigDecimal's protected constructor from sibling classes.
*
* @internal
* @psalm-pure
*/
final protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal
{
return new BigDecimal($value, $scale);
}
/**
* Proxy method to access BigRational's protected constructor from sibling classes.
*
* @internal
* @psalm-pure
*/
final protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational
{
return new BigRational($numerator, $denominator, $checkDenominator);
}
/**
* Returns the minimum of the given values.
*
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
* to an instance of the class this method is called on.
*
* @throws \InvalidArgumentException If no values are given.
* @throws MathException If an argument is not valid.
*
* @psalm-pure
*/
final public static function min(BigNumber|int|float|string ...$values) : static
{
$min = null;
foreach ($values as $value) {
$value = static::of($value);
if ($min === null || $value->isLessThan($min)) {
$min = $value;
}
}
if ($min === null) {
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
}
return $min;
}
/**
* Returns the maximum of the given values.
*
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
* to an instance of the class this method is called on.
*
* @throws \InvalidArgumentException If no values are given.
* @throws MathException If an argument is not valid.
*
* @psalm-pure
*/
final public static function max(BigNumber|int|float|string ...$values) : static
{
$max = null;
foreach ($values as $value) {
$value = static::of($value);
if ($max === null || $value->isGreaterThan($max)) {
$max = $value;
}
}
if ($max === null) {
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
}
return $max;
}
/**
* Returns the sum of the given values.
*
* @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible
* to an instance of the class this method is called on.
*
* @throws \InvalidArgumentException If no values are given.
* @throws MathException If an argument is not valid.
*
* @psalm-pure
*/
final public static function sum(BigNumber|int|float|string ...$values) : static
{
/** @var static|null $sum */
$sum = null;
foreach ($values as $value) {
$value = static::of($value);
$sum = $sum === null ? $value : self::add($sum, $value);
}
if ($sum === null) {
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
}
return $sum;
}
/**
* Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
*
* @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to
* concrete classes the responsibility to perform the addition themselves or delegate it to the given number,
* depending on their ability to perform the operation. This will also require a version bump because we're
* potentially breaking custom BigNumber implementations (if any...)
*
* @psalm-pure
*/
private static function add(BigNumber $a, BigNumber $b) : BigNumber
{
if ($a instanceof BigRational) {
return $a->plus($b);
}
if ($b instanceof BigRational) {
return $b->plus($a);
}
if ($a instanceof BigDecimal) {
return $a->plus($b);
}
if ($b instanceof BigDecimal) {
return $b->plus($a);
}
/** @var BigInteger $a */
return $a->plus($b);
}
/**
* Removes optional leading zeros and applies sign.
*
* @param string|null $sign The sign, '+' or '-', optional. Null is allowed for convenience and treated as '+'.
* @param string $number The number, validated as a non-empty string of digits.
*
* @psalm-pure
*/
private static function cleanUp(string|null $sign, string $number) : string
{
$number = \ltrim($number, '0');
if ($number === '') {
return '0';
}
return $sign === '-' ? '-' . $number : $number;
}
/**
* Checks if this number is equal to the given one.
*/
final public function isEqualTo(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) === 0;
}
/**
* Checks if this number is strictly lower than the given one.
*/
final public function isLessThan(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) < 0;
}
/**
* Checks if this number is lower than or equal to the given one.
*/
final public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) <= 0;
}
/**
* Checks if this number is strictly greater than the given one.
*/
final public function isGreaterThan(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) > 0;
}
/**
* Checks if this number is greater than or equal to the given one.
*/
final public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) >= 0;
}
/**
* Checks if this number equals zero.
*/
final public function isZero() : bool
{
return $this->getSign() === 0;
}
/**
* Checks if this number is strictly negative.
*/
final public function isNegative() : bool
{
return $this->getSign() < 0;
}
/**
* Checks if this number is negative or zero.
*/
final public function isNegativeOrZero() : bool
{
return $this->getSign() <= 0;
}
/**
* Checks if this number is strictly positive.
*/
final public function isPositive() : bool
{
return $this->getSign() > 0;
}
/**
* Checks if this number is positive or zero.
*/
final public function isPositiveOrZero() : bool
{
return $this->getSign() >= 0;
}
/**
* Returns the sign of this number.
*
* @psalm-return -1|0|1
*
* @return int -1 if the number is negative, 0 if zero, 1 if positive.
*/
abstract public function getSign() : int;
/**
* Compares this number to the given one.
*
* @psalm-return -1|0|1
*
* @return int -1 if `$this` is lower than, 0 if equal to, 1 if greater than `$that`.
*
* @throws MathException If the number is not valid.
*/
abstract public function compareTo(BigNumber|int|float|string $that) : int;
/**
* Converts this number to a BigInteger.
*
* @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
*/
abstract public function toBigInteger() : BigInteger;
/**
* Converts this number to a BigDecimal.
*
* @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
*/
abstract public function toBigDecimal() : BigDecimal;
/**
* Converts this number to a BigRational.
*/
abstract public function toBigRational() : BigRational;
/**
* Converts this number to a BigDecimal with the given scale, using rounding if necessary.
*
* @param int $scale The scale of the resulting `BigDecimal`.
* @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY.
*
* @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding.
* This only applies when RoundingMode::UNNECESSARY is used.
*/
abstract public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;
/**
* Returns the exact value of this number as a native integer.
*
* If this number cannot be converted to a native integer without losing precision, an exception is thrown.
* Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
*
* @throws MathException If this number cannot be exactly converted to a native integer.
*/
abstract public function toInt() : int;
/**
* Returns an approximation of this number as a floating-point value.
*
* Note that this method can discard information as the precision of a floating-point value
* is inherently limited.
*
* If the number is greater than the largest representable floating point number, positive infinity is returned.
* If the number is less than the smallest representable floating point number, negative infinity is returned.
*/
abstract public function toFloat() : float;
/**
* Returns a string representation of this number.
*
* The output of this method can be parsed by the `of()` factory method;
* this will yield an object equal to this one, without any information loss.
*/
abstract public function __toString() : string;
#[Override]
final public function jsonSerialize() : string
{
return $this->__toString();
}
}

424
vendor/brick/math/src/BigRational.php vendored Normal file
View File

@ -0,0 +1,424 @@
<?php
declare(strict_types=1);
namespace Brick\Math;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\RoundingNecessaryException;
use Override;
/**
* An arbitrarily large rational number.
*
* This class is immutable.
*
* @psalm-immutable
*/
final class BigRational extends BigNumber
{
/**
* The numerator.
*/
private readonly BigInteger $numerator;
/**
* The denominator. Always strictly positive.
*/
private readonly BigInteger $denominator;
/**
* Protected constructor. Use a factory method to obtain an instance.
*
* @param BigInteger $numerator The numerator.
* @param BigInteger $denominator The denominator.
* @param bool $checkDenominator Whether to check the denominator for negative and zero.
*
* @throws DivisionByZeroException If the denominator is zero.
*/
protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator)
{
if ($checkDenominator) {
if ($denominator->isZero()) {
throw DivisionByZeroException::denominatorMustNotBeZero();
}
if ($denominator->isNegative()) {
$numerator = $numerator->negated();
$denominator = $denominator->negated();
}
}
$this->numerator = $numerator;
$this->denominator = $denominator;
}
/**
* @psalm-pure
*/
#[Override]
protected static function from(BigNumber $number): static
{
return $number->toBigRational();
}
/**
* Creates a BigRational out of a numerator and a denominator.
*
* If the denominator is negative, the signs of both the numerator and the denominator
* will be inverted to ensure that the denominator is always positive.
*
* @param BigNumber|int|float|string $numerator The numerator. Must be convertible to a BigInteger.
* @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger.
*
* @throws NumberFormatException If an argument does not represent a valid number.
* @throws RoundingNecessaryException If an argument represents a non-integer number.
* @throws DivisionByZeroException If the denominator is zero.
*
* @psalm-pure
*/
public static function nd(
BigNumber|int|float|string $numerator,
BigNumber|int|float|string $denominator,
) : BigRational {
$numerator = BigInteger::of($numerator);
$denominator = BigInteger::of($denominator);
return new BigRational($numerator, $denominator, true);
}
/**
* Returns a BigRational representing zero.
*
* @psalm-pure
*/
public static function zero() : BigRational
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigRational|null $zero
*/
static $zero;
if ($zero === null) {
$zero = new BigRational(BigInteger::zero(), BigInteger::one(), false);
}
return $zero;
}
/**
* Returns a BigRational representing one.
*
* @psalm-pure
*/
public static function one() : BigRational
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigRational|null $one
*/
static $one;
if ($one === null) {
$one = new BigRational(BigInteger::one(), BigInteger::one(), false);
}
return $one;
}
/**
* Returns a BigRational representing ten.
*
* @psalm-pure
*/
public static function ten() : BigRational
{
/**
* @psalm-suppress ImpureStaticVariable
* @var BigRational|null $ten
*/
static $ten;
if ($ten === null) {
$ten = new BigRational(BigInteger::ten(), BigInteger::one(), false);
}
return $ten;
}
public function getNumerator() : BigInteger
{
return $this->numerator;
}
public function getDenominator() : BigInteger
{
return $this->denominator;
}
/**
* Returns the quotient of the division of the numerator by the denominator.
*/
public function quotient() : BigInteger
{
return $this->numerator->quotient($this->denominator);
}
/**
* Returns the remainder of the division of the numerator by the denominator.
*/
public function remainder() : BigInteger
{
return $this->numerator->remainder($this->denominator);
}
/**
* Returns the quotient and remainder of the division of the numerator by the denominator.
*
* @return BigInteger[]
*
* @psalm-return array{BigInteger, BigInteger}
*/
public function quotientAndRemainder() : array
{
return $this->numerator->quotientAndRemainder($this->denominator);
}
/**
* Returns the sum of this number and the given one.
*
* @param BigNumber|int|float|string $that The number to add.
*
* @throws MathException If the number is not valid.
*/
public function plus(BigNumber|int|float|string $that) : BigRational
{
$that = BigRational::of($that);
$numerator = $this->numerator->multipliedBy($that->denominator);
$numerator = $numerator->plus($that->numerator->multipliedBy($this->denominator));
$denominator = $this->denominator->multipliedBy($that->denominator);
return new BigRational($numerator, $denominator, false);
}
/**
* Returns the difference of this number and the given one.
*
* @param BigNumber|int|float|string $that The number to subtract.
*
* @throws MathException If the number is not valid.
*/
public function minus(BigNumber|int|float|string $that) : BigRational
{
$that = BigRational::of($that);
$numerator = $this->numerator->multipliedBy($that->denominator);
$numerator = $numerator->minus($that->numerator->multipliedBy($this->denominator));
$denominator = $this->denominator->multipliedBy($that->denominator);
return new BigRational($numerator, $denominator, false);
}
/**
* Returns the product of this number and the given one.
*
* @param BigNumber|int|float|string $that The multiplier.
*
* @throws MathException If the multiplier is not a valid number.
*/
public function multipliedBy(BigNumber|int|float|string $that) : BigRational
{
$that = BigRational::of($that);
$numerator = $this->numerator->multipliedBy($that->numerator);
$denominator = $this->denominator->multipliedBy($that->denominator);
return new BigRational($numerator, $denominator, false);
}
/**
* Returns the result of the division of this number by the given one.
*
* @param BigNumber|int|float|string $that The divisor.
*
* @throws MathException If the divisor is not a valid number, or is zero.
*/
public function dividedBy(BigNumber|int|float|string $that) : BigRational
{
$that = BigRational::of($that);
$numerator = $this->numerator->multipliedBy($that->denominator);
$denominator = $this->denominator->multipliedBy($that->numerator);
return new BigRational($numerator, $denominator, true);
}
/**
* Returns this number exponentiated to the given value.
*
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
*/
public function power(int $exponent) : BigRational
{
if ($exponent === 0) {
$one = BigInteger::one();
return new BigRational($one, $one, false);
}
if ($exponent === 1) {
return $this;
}
return new BigRational(
$this->numerator->power($exponent),
$this->denominator->power($exponent),
false
);
}
/**
* Returns the reciprocal of this BigRational.
*
* The reciprocal has the numerator and denominator swapped.
*
* @throws DivisionByZeroException If the numerator is zero.
*/
public function reciprocal() : BigRational
{
return new BigRational($this->denominator, $this->numerator, true);
}
/**
* Returns the absolute value of this BigRational.
*/
public function abs() : BigRational
{
return new BigRational($this->numerator->abs(), $this->denominator, false);
}
/**
* Returns the negated value of this BigRational.
*/
public function negated() : BigRational
{
return new BigRational($this->numerator->negated(), $this->denominator, false);
}
/**
* Returns the simplified value of this BigRational.
*/
public function simplified() : BigRational
{
$gcd = $this->numerator->gcd($this->denominator);
$numerator = $this->numerator->quotient($gcd);
$denominator = $this->denominator->quotient($gcd);
return new BigRational($numerator, $denominator, false);
}
#[Override]
public function compareTo(BigNumber|int|float|string $that) : int
{
return $this->minus($that)->getSign();
}
#[Override]
public function getSign() : int
{
return $this->numerator->getSign();
}
#[Override]
public function toBigInteger() : BigInteger
{
$simplified = $this->simplified();
if (! $simplified->denominator->isEqualTo(1)) {
throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.');
}
return $simplified->numerator;
}
#[Override]
public function toBigDecimal() : BigDecimal
{
return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator);
}
#[Override]
public function toBigRational() : BigRational
{
return $this;
}
#[Override]
public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
}
#[Override]
public function toInt() : int
{
return $this->toBigInteger()->toInt();
}
#[Override]
public function toFloat() : float
{
$simplified = $this->simplified();
return $simplified->numerator->toFloat() / $simplified->denominator->toFloat();
}
#[Override]
public function __toString() : string
{
$numerator = (string) $this->numerator;
$denominator = (string) $this->denominator;
if ($denominator === '1') {
return $numerator;
}
return $numerator . '/' . $denominator;
}
/**
* This method is required for serializing the object and SHOULD NOT be accessed directly.
*
* @internal
*
* @return array{numerator: BigInteger, denominator: BigInteger}
*/
public function __serialize(): array
{
return ['numerator' => $this->numerator, 'denominator' => $this->denominator];
}
/**
* This method is only here to allow unserializing the object and cannot be accessed directly.
*
* @internal
* @psalm-suppress RedundantPropertyInitializationCheck
*
* @param array{numerator: BigInteger, denominator: BigInteger} $data
*
* @throws \LogicException
*/
public function __unserialize(array $data): void
{
if (isset($this->numerator)) {
throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
}
$this->numerator = $data['numerator'];
$this->denominator = $data['denominator'];
}
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Exception thrown when a division by zero occurs.
*/
class DivisionByZeroException extends MathException
{
/**
* @psalm-pure
*/
public static function divisionByZero() : DivisionByZeroException
{
return new self('Division by zero.');
}
/**
* @psalm-pure
*/
public static function modulusMustNotBeZero() : DivisionByZeroException
{
return new self('The modulus must not be zero.');
}
/**
* @psalm-pure
*/
public static function denominatorMustNotBeZero() : DivisionByZeroException
{
return new self('The denominator of a rational number cannot be zero.');
}
}

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
use Brick\Math\BigInteger;
/**
* Exception thrown when an integer overflow occurs.
*/
class IntegerOverflowException extends MathException
{
/**
* @psalm-pure
*/
public static function toIntOverflow(BigInteger $value) : IntegerOverflowException
{
$message = '%s is out of range %d to %d and cannot be represented as an integer.';
return new self(\sprintf($message, (string) $value, PHP_INT_MIN, PHP_INT_MAX));
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Base class for all math exceptions.
*/
class MathException extends \Exception
{
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number.
*/
class NegativeNumberException extends MathException
{
}

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Exception thrown when attempting to create a number from a string with an invalid format.
*/
class NumberFormatException extends MathException
{
public static function invalidFormat(string $value) : self
{
return new self(\sprintf(
'The given value "%s" does not represent a valid number.',
$value,
));
}
/**
* @param string $char The failing character.
*
* @psalm-pure
*/
public static function charNotInAlphabet(string $char) : self
{
$ord = \ord($char);
if ($ord < 32 || $ord > 126) {
$char = \strtoupper(\dechex($ord));
if ($ord < 10) {
$char = '0' . $char;
}
} else {
$char = '"' . $char . '"';
}
return new self(\sprintf('Char %s is not a valid character in the given alphabet.', $char));
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Exception thrown when a number cannot be represented at the requested scale without rounding.
*/
class RoundingNecessaryException extends MathException
{
/**
* @psalm-pure
*/
public static function roundingNecessary() : RoundingNecessaryException
{
return new self('Rounding is necessary to represent the result of the operation at this scale.');
}
}

View File

@ -0,0 +1,668 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Internal;
use Brick\Math\Exception\RoundingNecessaryException;
use Brick\Math\RoundingMode;
/**
* Performs basic operations on arbitrary size integers.
*
* Unless otherwise specified, all parameters must be validated as non-empty strings of digits,
* without leading zero, and with an optional leading minus sign if the number is not zero.
*
* Any other parameter format will lead to undefined behaviour.
* All methods must return strings respecting this format, unless specified otherwise.
*
* @internal
*
* @psalm-immutable
*/
abstract class Calculator
{
/**
* The maximum exponent value allowed for the pow() method.
*/
public const MAX_POWER = 1_000_000;
/**
* The alphabet for converting from and to base 2 to 36, lowercase.
*/
public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
/**
* The Calculator instance in use.
*/
private static ?Calculator $instance = null;
/**
* Sets the Calculator instance to use.
*
* An instance is typically set only in unit tests: the autodetect is usually the best option.
*
* @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect.
*/
final public static function set(?Calculator $calculator) : void
{
self::$instance = $calculator;
}
/**
* Returns the Calculator instance to use.
*
* If none has been explicitly set, the fastest available implementation will be returned.
*
* @psalm-pure
* @psalm-suppress ImpureStaticProperty
*/
final public static function get() : Calculator
{
if (self::$instance === null) {
/** @psalm-suppress ImpureMethodCall */
self::$instance = self::detect();
}
return self::$instance;
}
/**
* Returns the fastest available Calculator implementation.
*
* @codeCoverageIgnore
*/
private static function detect() : Calculator
{
if (\extension_loaded('gmp')) {
return new Calculator\GmpCalculator();
}
if (\extension_loaded('bcmath')) {
return new Calculator\BcMathCalculator();
}
return new Calculator\NativeCalculator();
}
/**
* Extracts the sign & digits of the operands.
*
* @return array{bool, bool, string, string} Whether $a and $b are negative, followed by their digits.
*/
final protected function init(string $a, string $b) : array
{
return [
$aNeg = ($a[0] === '-'),
$bNeg = ($b[0] === '-'),
$aNeg ? \substr($a, 1) : $a,
$bNeg ? \substr($b, 1) : $b,
];
}
/**
* Returns the absolute value of a number.
*/
final public function abs(string $n) : string
{
return ($n[0] === '-') ? \substr($n, 1) : $n;
}
/**
* Negates a number.
*/
final public function neg(string $n) : string
{
if ($n === '0') {
return '0';
}
if ($n[0] === '-') {
return \substr($n, 1);
}
return '-' . $n;
}
/**
* Compares two numbers.
*
* @psalm-return -1|0|1
*
* @return int -1 if the first number is less than, 0 if equal to, 1 if greater than the second number.
*/
final public function cmp(string $a, string $b) : int
{
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
if ($aNeg && ! $bNeg) {
return -1;
}
if ($bNeg && ! $aNeg) {
return 1;
}
$aLen = \strlen($aDig);
$bLen = \strlen($bDig);
if ($aLen < $bLen) {
$result = -1;
} elseif ($aLen > $bLen) {
$result = 1;
} else {
$result = $aDig <=> $bDig;
}
return $aNeg ? -$result : $result;
}
/**
* Adds two numbers.
*/
abstract public function add(string $a, string $b) : string;
/**
* Subtracts two numbers.
*/
abstract public function sub(string $a, string $b) : string;
/**
* Multiplies two numbers.
*/
abstract public function mul(string $a, string $b) : string;
/**
* Returns the quotient of the division of two numbers.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
*
* @return string The quotient.
*/
abstract public function divQ(string $a, string $b) : string;
/**
* Returns the remainder of the division of two numbers.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
*
* @return string The remainder.
*/
abstract public function divR(string $a, string $b) : string;
/**
* Returns the quotient and remainder of the division of two numbers.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
*
* @return array{string, string} An array containing the quotient and remainder.
*/
abstract public function divQR(string $a, string $b) : array;
/**
* Exponentiates a number.
*
* @param string $a The base number.
* @param int $e The exponent, validated as an integer between 0 and MAX_POWER.
*
* @return string The power.
*/
abstract public function pow(string $a, int $e) : string;
/**
* @param string $b The modulus; must not be zero.
*/
public function mod(string $a, string $b) : string
{
return $this->divR($this->add($this->divR($a, $b), $b), $b);
}
/**
* Returns the modular multiplicative inverse of $x modulo $m.
*
* If $x has no multiplicative inverse mod m, this method must return null.
*
* This method can be overridden by the concrete implementation if the underlying library has built-in support.
*
* @param string $m The modulus; must not be negative or zero.
*/
public function modInverse(string $x, string $m) : ?string
{
if ($m === '1') {
return '0';
}
$modVal = $x;
if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) {
$modVal = $this->mod($x, $m);
}
[$g, $x] = $this->gcdExtended($modVal, $m);
if ($g !== '1') {
return null;
}
return $this->mod($this->add($this->mod($x, $m), $m), $m);
}
/**
* Raises a number into power with modulo.
*
* @param string $base The base number; must be positive or zero.
* @param string $exp The exponent; must be positive or zero.
* @param string $mod The modulus; must be strictly positive.
*/
abstract public function modPow(string $base, string $exp, string $mod) : string;
/**
* Returns the greatest common divisor of the two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for GCD calculations.
*
* @return string The GCD, always positive, or zero if both arguments are zero.
*/
public function gcd(string $a, string $b) : string
{
if ($a === '0') {
return $this->abs($b);
}
if ($b === '0') {
return $this->abs($a);
}
return $this->gcd($b, $this->divR($a, $b));
}
/**
* @return array{string, string, string} GCD, X, Y
*/
private function gcdExtended(string $a, string $b) : array
{
if ($a === '0') {
return [$b, '0', '1'];
}
[$gcd, $x1, $y1] = $this->gcdExtended($this->mod($b, $a), $a);
$x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1));
$y = $x1;
return [$gcd, $x, $y];
}
/**
* Returns the square root of the given number, rounded down.
*
* The result is the largest x such that n.
* The input MUST NOT be negative.
*/
abstract public function sqrt(string $n) : string;
/**
* Converts a number from an arbitrary base.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for base conversion.
*
* @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base.
* @param int $base The base of the number, validated from 2 to 36.
*
* @return string The converted number, following the Calculator conventions.
*/
public function fromBase(string $number, int $base) : string
{
return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base);
}
/**
* Converts a number to an arbitrary base.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for base conversion.
*
* @param string $number The number to convert, following the Calculator conventions.
* @param int $base The base to convert to, validated from 2 to 36.
*
* @return string The converted number, lowercase.
*/
public function toBase(string $number, int $base) : string
{
$negative = ($number[0] === '-');
if ($negative) {
$number = \substr($number, 1);
}
$number = $this->toArbitraryBase($number, self::ALPHABET, $base);
if ($negative) {
return '-' . $number;
}
return $number;
}
/**
* Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10.
*
* @param string $number The number to convert, validated as a non-empty string,
* containing only chars in the given alphabet/base.
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
* @param int $base The base of the number, validated from 2 to alphabet length.
*
* @return string The number in base 10, following the Calculator conventions.
*/
final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string
{
// remove leading "zeros"
$number = \ltrim($number, $alphabet[0]);
if ($number === '') {
return '0';
}
// optimize for "one"
if ($number === $alphabet[1]) {
return '1';
}
$result = '0';
$power = '1';
$base = (string) $base;
for ($i = \strlen($number) - 1; $i >= 0; $i--) {
$index = \strpos($alphabet, $number[$i]);
if ($index !== 0) {
$result = $this->add($result, ($index === 1)
? $power
: $this->mul($power, (string) $index)
);
}
if ($i !== 0) {
$power = $this->mul($power, $base);
}
}
return $result;
}
/**
* Converts a non-negative number to an arbitrary base using a custom alphabet.
*
* @param string $number The number to convert, positive or zero, following the Calculator conventions.
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
* @param int $base The base to convert to, validated from 2 to alphabet length.
*
* @return string The converted number in the given alphabet.
*/
final public function toArbitraryBase(string $number, string $alphabet, int $base) : string
{
if ($number === '0') {
return $alphabet[0];
}
$base = (string) $base;
$result = '';
while ($number !== '0') {
[$number, $remainder] = $this->divQR($number, $base);
$remainder = (int) $remainder;
$result .= $alphabet[$remainder];
}
return \strrev($result);
}
/**
* Performs a rounded division.
*
* Rounding is performed when the remainder of the division is not zero.
*
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
* @param RoundingMode $roundingMode The rounding mode.
*
* @throws \InvalidArgumentException If the rounding mode is invalid.
* @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary.
*
* @psalm-suppress ImpureFunctionCall
*/
final public function divRound(string $a, string $b, RoundingMode $roundingMode) : string
{
[$quotient, $remainder] = $this->divQR($a, $b);
$hasDiscardedFraction = ($remainder !== '0');
$isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-');
$discardedFractionSign = function() use ($remainder, $b) : int {
$r = $this->abs($this->mul($remainder, '2'));
$b = $this->abs($b);
return $this->cmp($r, $b);
};
$increment = false;
switch ($roundingMode) {
case RoundingMode::UNNECESSARY:
if ($hasDiscardedFraction) {
throw RoundingNecessaryException::roundingNecessary();
}
break;
case RoundingMode::UP:
$increment = $hasDiscardedFraction;
break;
case RoundingMode::DOWN:
break;
case RoundingMode::CEILING:
$increment = $hasDiscardedFraction && $isPositiveOrZero;
break;
case RoundingMode::FLOOR:
$increment = $hasDiscardedFraction && ! $isPositiveOrZero;
break;
case RoundingMode::HALF_UP:
$increment = $discardedFractionSign() >= 0;
break;
case RoundingMode::HALF_DOWN:
$increment = $discardedFractionSign() > 0;
break;
case RoundingMode::HALF_CEILING:
$increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0;
break;
case RoundingMode::HALF_FLOOR:
$increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
break;
case RoundingMode::HALF_EVEN:
$lastDigit = (int) $quotient[-1];
$lastDigitIsEven = ($lastDigit % 2 === 0);
$increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
break;
default:
throw new \InvalidArgumentException('Invalid rounding mode.');
}
if ($increment) {
return $this->add($quotient, $isPositiveOrZero ? '1' : '-1');
}
return $quotient;
}
/**
* Calculates bitwise AND of two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for bitwise operations.
*/
public function and(string $a, string $b) : string
{
return $this->bitwise('and', $a, $b);
}
/**
* Calculates bitwise OR of two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for bitwise operations.
*/
public function or(string $a, string $b) : string
{
return $this->bitwise('or', $a, $b);
}
/**
* Calculates bitwise XOR of two numbers.
*
* This method can be overridden by the concrete implementation if the underlying library
* has built-in support for bitwise operations.
*/
public function xor(string $a, string $b) : string
{
return $this->bitwise('xor', $a, $b);
}
/**
* Performs a bitwise operation on a decimal number.
*
* @param 'and'|'or'|'xor' $operator The operator to use.
* @param string $a The left operand.
* @param string $b The right operand.
*/
private function bitwise(string $operator, string $a, string $b) : string
{
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
$aBin = $this->toBinary($aDig);
$bBin = $this->toBinary($bDig);
$aLen = \strlen($aBin);
$bLen = \strlen($bBin);
if ($aLen > $bLen) {
$bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin;
} elseif ($bLen > $aLen) {
$aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin;
}
if ($aNeg) {
$aBin = $this->twosComplement($aBin);
}
if ($bNeg) {
$bBin = $this->twosComplement($bBin);
}
$value = match ($operator) {
'and' => $aBin & $bBin,
'or' => $aBin | $bBin,
'xor' => $aBin ^ $bBin,
};
$negative = match ($operator) {
'and' => $aNeg and $bNeg,
'or' => $aNeg or $bNeg,
'xor' => $aNeg xor $bNeg,
};
if ($negative) {
$value = $this->twosComplement($value);
}
$result = $this->toDecimal($value);
return $negative ? $this->neg($result) : $result;
}
/**
* @param string $number A positive, binary number.
*/
private function twosComplement(string $number) : string
{
$xor = \str_repeat("\xff", \strlen($number));
$number ^= $xor;
for ($i = \strlen($number) - 1; $i >= 0; $i--) {
$byte = \ord($number[$i]);
if (++$byte !== 256) {
$number[$i] = \chr($byte);
break;
}
$number[$i] = "\x00";
if ($i === 0) {
$number = "\x01" . $number;
}
}
return $number;
}
/**
* Converts a decimal number to a binary string.
*
* @param string $number The number to convert, positive or zero, only digits.
*/
private function toBinary(string $number) : string
{
$result = '';
while ($number !== '0') {
[$number, $remainder] = $this->divQR($number, '256');
$result .= \chr((int) $remainder);
}
return \strrev($result);
}
/**
* Returns the positive decimal representation of a binary number.
*
* @param string $bytes The bytes representing the number.
*/
private function toDecimal(string $bytes) : string
{
$result = '0';
$power = '1';
for ($i = \strlen($bytes) - 1; $i >= 0; $i--) {
$index = \ord($bytes[$i]);
if ($index !== 0) {
$result = $this->add($result, ($index === 1)
? $power
: $this->mul($power, (string) $index)
);
}
if ($i !== 0) {
$power = $this->mul($power, '256');
}
}
return $result;
}
}

View File

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Internal\Calculator;
use Brick\Math\Internal\Calculator;
use Override;
/**
* Calculator implementation built around the bcmath library.
*
* @internal
*
* @psalm-immutable
*/
class BcMathCalculator extends Calculator
{
#[Override]
public function add(string $a, string $b) : string
{
return \bcadd($a, $b, 0);
}
#[Override]
public function sub(string $a, string $b) : string
{
return \bcsub($a, $b, 0);
}
#[Override]
public function mul(string $a, string $b) : string
{
return \bcmul($a, $b, 0);
}
#[Override]
public function divQ(string $a, string $b) : string
{
return \bcdiv($a, $b, 0);
}
#[Override]
public function divR(string $a, string $b) : string
{
return \bcmod($a, $b, 0);
}
#[Override]
public function divQR(string $a, string $b) : array
{
$q = \bcdiv($a, $b, 0);
$r = \bcmod($a, $b, 0);
return [$q, $r];
}
#[Override]
public function pow(string $a, int $e) : string
{
return \bcpow($a, (string) $e, 0);
}
#[Override]
public function modPow(string $base, string $exp, string $mod) : string
{
return \bcpowmod($base, $exp, $mod, 0);
}
#[Override]
public function sqrt(string $n) : string
{
return \bcsqrt($n, 0);
}
}

View File

@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Internal\Calculator;
use Brick\Math\Internal\Calculator;
use Override;
/**
* Calculator implementation built around the GMP library.
*
* @internal
*
* @psalm-immutable
*/
class GmpCalculator extends Calculator
{
#[Override]
public function add(string $a, string $b) : string
{
return \gmp_strval(\gmp_add($a, $b));
}
#[Override]
public function sub(string $a, string $b) : string
{
return \gmp_strval(\gmp_sub($a, $b));
}
#[Override]
public function mul(string $a, string $b) : string
{
return \gmp_strval(\gmp_mul($a, $b));
}
#[Override]
public function divQ(string $a, string $b) : string
{
return \gmp_strval(\gmp_div_q($a, $b));
}
#[Override]
public function divR(string $a, string $b) : string
{
return \gmp_strval(\gmp_div_r($a, $b));
}
#[Override]
public function divQR(string $a, string $b) : array
{
[$q, $r] = \gmp_div_qr($a, $b);
return [
\gmp_strval($q),
\gmp_strval($r)
];
}
#[Override]
public function pow(string $a, int $e) : string
{
return \gmp_strval(\gmp_pow($a, $e));
}
#[Override]
public function modInverse(string $x, string $m) : ?string
{
$result = \gmp_invert($x, $m);
if ($result === false) {
return null;
}
return \gmp_strval($result);
}
#[Override]
public function modPow(string $base, string $exp, string $mod) : string
{
return \gmp_strval(\gmp_powm($base, $exp, $mod));
}
#[Override]
public function gcd(string $a, string $b) : string
{
return \gmp_strval(\gmp_gcd($a, $b));
}
#[Override]
public function fromBase(string $number, int $base) : string
{
return \gmp_strval(\gmp_init($number, $base));
}
#[Override]
public function toBase(string $number, int $base) : string
{
return \gmp_strval($number, $base);
}
#[Override]
public function and(string $a, string $b) : string
{
return \gmp_strval(\gmp_and($a, $b));
}
#[Override]
public function or(string $a, string $b) : string
{
return \gmp_strval(\gmp_or($a, $b));
}
#[Override]
public function xor(string $a, string $b) : string
{
return \gmp_strval(\gmp_xor($a, $b));
}
#[Override]
public function sqrt(string $n) : string
{
return \gmp_strval(\gmp_sqrt($n));
}
}

View File

@ -0,0 +1,598 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Internal\Calculator;
use Brick\Math\Internal\Calculator;
use Override;
/**
* Calculator implementation using only native PHP code.
*
* @internal
*
* @psalm-immutable
*/
class NativeCalculator extends Calculator
{
/**
* The max number of digits the platform can natively add, subtract, multiply or divide without overflow.
* For multiplication, this represents the max sum of the lengths of both operands.
*
* In addition, it is assumed that an extra digit can hold a carry (1) without overflowing.
* Example: 32-bit: max number 1,999,999,999 (9 digits + carry)
* 64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry)
*/
private readonly int $maxDigits;
/**
* @codeCoverageIgnore
*/
public function __construct()
{
$this->maxDigits = match (PHP_INT_SIZE) {
4 => 9,
8 => 18,
default => throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.')
};
}
#[Override]
public function add(string $a, string $b) : string
{
/**
* @psalm-var numeric-string $a
* @psalm-var numeric-string $b
*/
$result = $a + $b;
if (is_int($result)) {
return (string) $result;
}
if ($a === '0') {
return $b;
}
if ($b === '0') {
return $a;
}
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
$result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig);
if ($aNeg) {
$result = $this->neg($result);
}
return $result;
}
#[Override]
public function sub(string $a, string $b) : string
{
return $this->add($a, $this->neg($b));
}
#[Override]
public function mul(string $a, string $b) : string
{
/**
* @psalm-var numeric-string $a
* @psalm-var numeric-string $b
*/
$result = $a * $b;
if (is_int($result)) {
return (string) $result;
}
if ($a === '0' || $b === '0') {
return '0';
}
if ($a === '1') {
return $b;
}
if ($b === '1') {
return $a;
}
if ($a === '-1') {
return $this->neg($b);
}
if ($b === '-1') {
return $this->neg($a);
}
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
$result = $this->doMul($aDig, $bDig);
if ($aNeg !== $bNeg) {
$result = $this->neg($result);
}
return $result;
}
#[Override]
public function divQ(string $a, string $b) : string
{
return $this->divQR($a, $b)[0];
}
#[Override]
public function divR(string $a, string $b): string
{
return $this->divQR($a, $b)[1];
}
#[Override]
public function divQR(string $a, string $b) : array
{
if ($a === '0') {
return ['0', '0'];
}
if ($a === $b) {
return ['1', '0'];
}
if ($b === '1') {
return [$a, '0'];
}
if ($b === '-1') {
return [$this->neg($a), '0'];
}
/** @psalm-var numeric-string $a */
$na = $a * 1; // cast to number
if (is_int($na)) {
/** @psalm-var numeric-string $b */
$nb = $b * 1;
if (is_int($nb)) {
// the only division that may overflow is PHP_INT_MIN / -1,
// which cannot happen here as we've already handled a divisor of -1 above.
$q = intdiv($na, $nb);
$r = $na % $nb;
return [
(string) $q,
(string) $r
];
}
}
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
[$q, $r] = $this->doDiv($aDig, $bDig);
if ($aNeg !== $bNeg) {
$q = $this->neg($q);
}
if ($aNeg) {
$r = $this->neg($r);
}
return [$q, $r];
}
#[Override]
public function pow(string $a, int $e) : string
{
if ($e === 0) {
return '1';
}
if ($e === 1) {
return $a;
}
$odd = $e % 2;
$e -= $odd;
$aa = $this->mul($a, $a);
/** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */
$result = $this->pow($aa, $e / 2);
if ($odd === 1) {
$result = $this->mul($result, $a);
}
return $result;
}
/**
* Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/
*/
#[Override]
public function modPow(string $base, string $exp, string $mod) : string
{
// special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0)
if ($base === '0' && $exp === '0' && $mod === '1') {
return '0';
}
// special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0)
if ($exp === '0' && $mod === '1') {
return '0';
}
$x = $base;
$res = '1';
// numbers are positive, so we can use remainder instead of modulo
$x = $this->divR($x, $mod);
while ($exp !== '0') {
if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd
$res = $this->divR($this->mul($res, $x), $mod);
}
$exp = $this->divQ($exp, '2');
$x = $this->divR($this->mul($x, $x), $mod);
}
return $res;
}
/**
* Adapted from https://cp-algorithms.com/num_methods/roots_newton.html
*/
#[Override]
public function sqrt(string $n) : string
{
if ($n === '0') {
return '0';
}
// initial approximation
$x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1);
$decreased = false;
for (;;) {
$nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2');
if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) {
break;
}
$decreased = $this->cmp($nx, $x) < 0;
$x = $nx;
}
return $x;
}
/**
* Performs the addition of two non-signed large integers.
*/
private function doAdd(string $a, string $b) : string
{
[$a, $b, $length] = $this->pad($a, $b);
$carry = 0;
$result = '';
for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
$blockLength = $this->maxDigits;
if ($i < 0) {
$blockLength += $i;
/** @psalm-suppress LoopInvalidation */
$i = 0;
}
/** @psalm-var numeric-string $blockA */
$blockA = \substr($a, $i, $blockLength);
/** @psalm-var numeric-string $blockB */
$blockB = \substr($b, $i, $blockLength);
$sum = (string) ($blockA + $blockB + $carry);
$sumLength = \strlen($sum);
if ($sumLength > $blockLength) {
$sum = \substr($sum, 1);
$carry = 1;
} else {
if ($sumLength < $blockLength) {
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
}
$carry = 0;
}
$result = $sum . $result;
if ($i === 0) {
break;
}
}
if ($carry === 1) {
$result = '1' . $result;
}
return $result;
}
/**
* Performs the subtraction of two non-signed large integers.
*/
private function doSub(string $a, string $b) : string
{
if ($a === $b) {
return '0';
}
// Ensure that we always subtract to a positive result: biggest minus smallest.
$cmp = $this->doCmp($a, $b);
$invert = ($cmp === -1);
if ($invert) {
$c = $a;
$a = $b;
$b = $c;
}
[$a, $b, $length] = $this->pad($a, $b);
$carry = 0;
$result = '';
$complement = 10 ** $this->maxDigits;
for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
$blockLength = $this->maxDigits;
if ($i < 0) {
$blockLength += $i;
/** @psalm-suppress LoopInvalidation */
$i = 0;
}
/** @psalm-var numeric-string $blockA */
$blockA = \substr($a, $i, $blockLength);
/** @psalm-var numeric-string $blockB */
$blockB = \substr($b, $i, $blockLength);
$sum = $blockA - $blockB - $carry;
if ($sum < 0) {
$sum += $complement;
$carry = 1;
} else {
$carry = 0;
}
$sum = (string) $sum;
$sumLength = \strlen($sum);
if ($sumLength < $blockLength) {
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
}
$result = $sum . $result;
if ($i === 0) {
break;
}
}
// Carry cannot be 1 when the loop ends, as a > b
assert($carry === 0);
$result = \ltrim($result, '0');
if ($invert) {
$result = $this->neg($result);
}
return $result;
}
/**
* Performs the multiplication of two non-signed large integers.
*/
private function doMul(string $a, string $b) : string
{
$x = \strlen($a);
$y = \strlen($b);
$maxDigits = \intdiv($this->maxDigits, 2);
$complement = 10 ** $maxDigits;
$result = '0';
for ($i = $x - $maxDigits;; $i -= $maxDigits) {
$blockALength = $maxDigits;
if ($i < 0) {
$blockALength += $i;
/** @psalm-suppress LoopInvalidation */
$i = 0;
}
$blockA = (int) \substr($a, $i, $blockALength);
$line = '';
$carry = 0;
for ($j = $y - $maxDigits;; $j -= $maxDigits) {
$blockBLength = $maxDigits;
if ($j < 0) {
$blockBLength += $j;
/** @psalm-suppress LoopInvalidation */
$j = 0;
}
$blockB = (int) \substr($b, $j, $blockBLength);
$mul = $blockA * $blockB + $carry;
$value = $mul % $complement;
$carry = ($mul - $value) / $complement;
$value = (string) $value;
$value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT);
$line = $value . $line;
if ($j === 0) {
break;
}
}
if ($carry !== 0) {
$line = $carry . $line;
}
$line = \ltrim($line, '0');
if ($line !== '') {
$line .= \str_repeat('0', $x - $blockALength - $i);
$result = $this->add($result, $line);
}
if ($i === 0) {
break;
}
}
return $result;
}
/**
* Performs the division of two non-signed large integers.
*
* @return string[] The quotient and remainder.
*/
private function doDiv(string $a, string $b) : array
{
$cmp = $this->doCmp($a, $b);
if ($cmp === -1) {
return ['0', $a];
}
$x = \strlen($a);
$y = \strlen($b);
// we now know that a >= b && x >= y
$q = '0'; // quotient
$r = $a; // remainder
$z = $y; // focus length, always $y or $y+1
/** @psalm-var numeric-string $b */
$nb = $b * 1; // cast to number
// performance optimization in cases where the remainder will never cause int overflow
if (is_int(($nb - 1) * 10 + 9)) {
$r = (int) \substr($a, 0, $z - 1);
for ($i = $z - 1; $i < $x; $i++) {
$n = $r * 10 + (int) $a[$i];
/** @psalm-var int $nb */
$q .= \intdiv($n, $nb);
$r = $n % $nb;
}
return [\ltrim($q, '0') ?: '0', (string) $r];
}
for (;;) {
$focus = \substr($a, 0, $z);
$cmp = $this->doCmp($focus, $b);
if ($cmp === -1) {
if ($z === $x) { // remainder < dividend
break;
}
$z++;
}
$zeros = \str_repeat('0', $x - $z);
$q = $this->add($q, '1' . $zeros);
$a = $this->sub($a, $b . $zeros);
$r = $a;
if ($r === '0') { // remainder == 0
break;
}
$x = \strlen($a);
if ($x < $y) { // remainder < dividend
break;
}
$z = $y;
}
return [$q, $r];
}
/**
* Compares two non-signed large numbers.
*
* @psalm-return -1|0|1
*/
private function doCmp(string $a, string $b) : int
{
$x = \strlen($a);
$y = \strlen($b);
$cmp = $x <=> $y;
if ($cmp !== 0) {
return $cmp;
}
return \strcmp($a, $b) <=> 0; // enforce -1|0|1
}
/**
* Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length.
*
* The numbers must only consist of digits, without leading minus sign.
*
* @return array{string, string, int}
*/
private function pad(string $a, string $b) : array
{
$x = \strlen($a);
$y = \strlen($b);
if ($x > $y) {
$b = \str_repeat('0', $x - $y) . $b;
return [$a, $b, $x];
}
if ($x < $y) {
$a = \str_repeat('0', $y - $x) . $a;
return [$a, $b, $y];
}
return [$a, $b, $x];
}
}

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