summaryrefslogtreecommitdiffstats
path: root/public
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:17:31 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:17:31 +0000
commitf66ab8dae2f3d0418759f81a3a64dc9517a62449 (patch)
treefbff2135e7013f196b891bbde54618eb050e4aaf /public
parentInitial commit. (diff)
downloadicingaweb2-module-director-f66ab8dae2f3d0418759f81a3a64dc9517a62449.tar.xz
icingaweb2-module-director-f66ab8dae2f3d0418759f81a3a64dc9517a62449.zip
Adding upstream version 1.10.2.upstream/1.10.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--public/css/module.less1835
-rw-r--r--public/img/globe.pngbin0 -> 882 bytes
-rw-r--r--public/img/leaf.gifbin0 -> 945 bytes
-rw-r--r--public/img/script.pngbin0 -> 471 bytes
-rw-r--r--public/img/server.pngbin0 -> 423 bytes
-rw-r--r--public/img/service.pngbin0 -> 601 bytes
-rw-r--r--public/img/tree.pngbin0 -> 524 bytes
-rw-r--r--public/js/module.js840
8 files changed, 2675 insertions, 0 deletions
diff --git a/public/css/module.less b/public/css/module.less
new file mode 100644
index 0000000..1f11251
--- /dev/null
+++ b/public/css/module.less
@@ -0,0 +1,1835 @@
+div.action-bar a:focus, .tabs a:focus {
+ outline: none;
+ text-decoration: underline;
+
+ &:before {
+ text-decoration: none;
+ }
+}
+
+table.common-table td {
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+ vertical-align: middle;
+
+ p {
+ word-break: break-word;
+ }
+}
+
+#layout.minimal-layout table.common-table td {
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+}
+
+table.common-table thead th {
+ border-bottom: 1px solid @text-color;
+}
+table.common-table tbody td {
+ border-bottom: 1px solid @gray-lighter;
+}
+
+a:before {
+ text-decoration: none;
+}
+
+form.director-form {
+ max-width: 68em;
+}
+
+form.director-form:focus {
+ outline: none;
+}
+
+div.action-bar a, div.action-bar form i {
+ color: @icinga-blue;
+}
+
+div.action-bar > a {
+ margin-right: 1em;
+}
+
+.controls > .pagination-control li > a {
+ padding: 0.5em 0 0.5em 0;
+}
+
+.controls > .pagination-control > ul {
+ float: right;
+}
+
+div.action-bar {
+ .pagination-control {
+ float: none;
+ clear: none;
+ display: inline-block;
+ line-height: inherit;
+ margin: 0;
+ vertical-align: middle;
+ }
+
+ form.director-form input {
+ margin: 0;
+ }
+ input {
+ max-width: unset;
+ }
+ select {
+ line-height: 1.25em;
+ }
+}
+
+div.action-bar ul {
+ padding: 0;
+ margin: 0;
+
+ li {
+ list-style-type: none;
+ a { display: block; }
+ }
+}
+
+div.action-bar > ul {
+ display: inline-block;
+}
+
+div.action-bar > ul ul {
+ padding: 0.5em 0em 1em 0em;
+ min-width: 10em;
+ position: absolute;
+ display: none;
+ color: @text-color-inverted;
+ background-color: @icinga-blue;
+
+ a {
+ display: block;
+ padding: 0.3em 2em 0.3em 2em;
+ margin: 0;
+ outline: none;
+ color: @text-color-inverted;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+
+ li.active a {
+ font-weight: bold;
+ }
+}
+
+div.action-bar > ul > li:hover ul {
+ display: block;
+}
+div.action-bar > ul > li > a {
+ padding: 0.2em 0.5em;
+}
+div.action-bar > ul > li:hover {
+ background-color: @icinga-blue;
+ & > a {
+ color: @text-color-inverted;
+ text-decoration: none;
+ }
+}
+
+#layout.twocols div.action-bar .pagination-control {
+ li {
+ display: none;
+ }
+
+ li:nth-child(1), li.active, li:last-child {
+ display: list-item;
+ }
+
+ li.active a {
+ border-bottom: none;
+ }
+}
+
+.content a {
+ color: @icinga-blue;
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
+p {
+ max-width: 56em;
+}
+
+table.common-table {
+ max-width: 68em;
+
+ a {
+ color: inherit;
+ text-decoration: none;
+ }
+
+ th {
+ padding-top: 0.5em;
+ }
+ td {
+ vertical-align: top;
+ }
+
+ pre {
+ margin: 0;
+ padding: 0.2em;
+ max-height: 10em;
+ background: none;
+ overflow: auto;
+ word-break: keep-all;
+ white-space: pre;
+ display: inline-block;
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ }
+}
+
+table.history {
+ td:last-of-type {
+ text-align: right;
+ }
+}
+
+span.disabled {
+ cursor: no-drop;
+ color: @gray-light;
+}
+
+.controls span.action-links {
+ display: block;
+ margin-bottom: 0.5em;
+ a {
+ color: @icinga-blue;
+ margin-right: 1em;
+ }
+ form.director-form {
+ display: inline;
+ margin-right: 1em;
+ }
+}
+
+.action-bar {
+ form.director-form {
+ display: inline;
+ margin-right: 1em;
+ }
+}
+
+pre.disabled {
+ color: @disabled-gray;
+}
+
+form.director-form i.link-color::before {
+ color: @icinga-blue;
+}
+
+/* TODO: remove this, but autosubmit looks ugly otherwise */
+form.director-form input[disabled] {
+ background: inherit;
+}
+/* END OF TODO */
+
+pre {
+ background: none;
+}
+
+pre.logfile {
+ font-size: 0.875em;
+ padding: 1em;
+ background: @gray-lighter;
+ color: @gray;
+ overflow: auto;
+ white-space: pre;
+
+ a {
+ color: @link-color;
+ }
+
+ .loglevel, .application {
+ font-weight: bold;
+ }
+
+ .critical {
+ color: @color-critical;
+ }
+
+ .warning {
+ color: @color-warning;
+ }
+
+ .information {
+ color: @color-ok;
+ }
+
+ .notice {
+ // color: @color-ok;
+ }
+
+ .debug {
+ color: @color-pending;
+ }
+
+ .error-hint {
+ color: @text-color;
+ font-weight: 900;
+ }
+}
+
+pre.generated-config {
+
+ a {
+ color: @link-color;
+ font-weight: bold;
+ }
+
+ .highlight {
+ border-bottom: 1px dashed @gray-light;
+ &::before {
+ // icon: right-big
+ font-family: 'ifont';
+ content: '\e826';
+ margin-left: -1em;
+ padding-top: 0em;
+ float: left;
+ }
+
+ &.critical::before {
+ color: @color-critical;
+ }
+ &.warning::before {
+ color: @color-warning;
+ }
+ &.ok::before {
+ color: @color-ok;
+ }
+ }
+}
+
+pre.agent-deployment-instructions {
+ color: @text-color;
+ height: 14em;
+ overflow: scroll;
+}
+
+table.avp th {
+ font-size: inherit;
+}
+
+.content form.director-form {
+ margin-top: 0.5em;
+ margin-bottom: 2em;
+}
+
+.content form.director-form.inline {
+ margin: 0;
+
+ i.icon::before {
+ color: @icinga-blue;
+ }
+}
+
+.invisible {
+ position: absolute;
+ left: -100%;
+}
+
+form.director-form input[type=file] {
+ padding-right: 1em;
+}
+
+
+form.director-form input[type=submit] {
+ .button();
+ border-width: 1px;
+ margin-top: 0.5em;
+
+ &:disabled {
+ border-color: @gray-light;
+ background-color: @gray-light;
+ color: @disabled-gray;
+ }
+}
+
+form.director-form input[type=submit]:first-of-type {
+ border-width: 2px;
+}
+
+form.director-form input[type=submit].link-button {
+ color: @icinga-blue;
+ background: none;
+ border: none;
+ font-weight: normal;
+ padding: 0;
+ margin: 0;
+
+ text-align: left;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
+form.director-form p.description {
+ padding: 1em 1em;
+ margin: 0;
+ font-style: italic;
+ width: 100%;
+}
+
+form.director-form {
+ input[type=text], input[type=button], select, select option, textarea {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ }
+}
+
+form.director-form ul.form-errors {
+ list-style-type: none;
+ margin-bottom: 0.5em;
+ padding: 0;
+
+ ul.errors {
+ list-style-type: none;
+ padding: 0;
+ }
+
+ ul.errors li {
+ background: @color-critical;
+ font-weight: bold;
+ padding: 0.5em 1em;
+ color: @text-color-inverted;
+ }
+}
+
+form.director-form {
+ select::-ms-expand, input::-ms-expand, textarea::-ms-expand { /* for IE 11 */
+ display: none;
+ }
+
+ select {
+ border: 1px solid @gray-light;
+ cursor: pointer;
+ background: none;
+ }
+
+ input[type=text], input[type=password], textarea, select {
+ max-width: 36em;
+ min-width: 20em;
+ width: 100%;
+ line-height: 2em;
+ height: 2.4em;
+ padding-left: 0.5em;
+ border-style: solid;
+ border-color: transparent;
+ border-bottom-color: @gray-lighter;
+ border-width: 1px 1px 1px 3px;
+ background-color: @low-sat-blue;
+
+ &.search {
+ background: transparent url("../img/icons/search.png") no-repeat scroll 0.5em center / 1em 1em;
+ padding-left: 2em;
+ }
+ }
+
+ textarea {
+ max-width: 100%;
+ }
+
+ select[multiple] {
+ height: auto;
+ }
+
+ select option {
+ height: 2em;
+ padding-top: 0.3em;
+ }
+
+ select[multiple=multiple] {
+ height: auto;
+ }
+
+ label {
+ line-height: 2em;
+ }
+}
+
+form.director-form dl {
+ margin: 0;
+ padding: 0;
+}
+
+.strike-links a, table.common-table .strike-links a {
+ text-decoration: line-through;
+ &:hover {
+ text-decoration: line-through;
+ }
+}
+.strike-links span.ro-service {
+ text-decoration: line-through;
+}
+
+// TODO: figure out whether form.editor and filter-related CSS is still required
+div.filter > form.search, div.filter > a {
+ // Duplicated by quicksearch
+ display: none;
+}
+
+div.filter form.editor > ul.tree ul li.active {
+ background-color: @tr-hover-color;
+}
+
+div.filter form.editor {
+ padding-top: 1em;
+ select, input[type=text] {
+ line-height: unset;
+ height: auto;
+ }
+}
+
+form.director-form.editor {
+ select, input[type=text] {
+ background: @low-sat-blue;
+ max-width: unset;
+ min-width: unset;
+ width: auto;
+ }
+ .tree li a {
+ padding: 0;
+ }
+}
+
+ul.extensible-set {
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+ display: inline-block;
+ width: 100%;
+ max-width: 36em;
+ min-width: 20em;
+ border-bottom: 1px solid @gray-lighter;
+
+ input[type=text], input[type=password], textarea, select {
+ border-color: transparent;
+ }
+
+ li {
+ display: inline;
+ }
+
+ select {
+ width: 100%;
+ }
+
+ input[type=text] {
+ background-color: @low-sat-blue;
+ .rounded-corners(0.5em);
+ border: 1px solid transparent;
+ padding: 0.1em 0.3em;
+ margin: 0.2em 0.2em;
+ width: 30%;
+ min-width: 4em;
+ text-overflow: ellipsis;
+ }
+
+ span.inline-buttons {
+ position: absolute;
+ z-index: 10;
+ right: 0.225em;
+ top: -0.275em;
+ input[type=submit] {
+ font-family: 'ifont';
+ width: 2em;
+ height: 2em;
+ font-size: 1em;
+ margin-left: 0.2em;
+ padding: 1px 0 1px 0;
+ }
+ }
+
+ select.extend-set, input.extend-set {
+ display: none;
+ }
+}
+
+form.director-form {
+ #_FAKE_SUBMIT {
+ position: absolute;
+ left: -100%;
+ }
+}
+
+form.director-form dd.active ul.extensible-set, ul.extensible-set.sortable {
+
+ li {
+ display: list-item;
+ position: relative;
+ clear: both;
+ }
+
+ input[type=text], select {
+ width: 100%;
+ }
+
+ input[type=text] {
+ background-color: @low-sat-blue;
+ .rounded-corners(0);
+ border: 1px solid @gray-light;
+ padding: 0.25em 0.5em;
+ margin: 0;
+ }
+}
+
+form.director-form dd.active ul.extensible-set {
+ border: 1px solid @icinga-blue;
+
+ input[type=submit]:first-of-type {
+ border-width: 1px;
+ }
+
+ select.extend-set, input.extend-set {
+ display: inline;
+ }
+}
+
+form.director-form {
+ select::-moz-focus-inner {
+ border: 0;
+ }
+
+ select:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 #000;
+ }
+
+ select, input[type=text], textarea {
+ &:hover {
+ border-style: dotted solid dotted solid;
+ border-color: @gray-light;
+ }
+
+ &:focus, &:focus:hover {
+ border-style: solid;
+ border-color: @icinga-blue;
+ outline: none;
+ }
+ }
+
+ select option {
+ padding-left: 0.5em;
+ }
+
+ select option[value=""] {
+ color: @disabled-gray;
+ background-color: @low-sat-blue;
+ }
+}
+
+a {
+ &.state-critical {
+ color: @color-critical;
+ }
+
+ &.state-warning {
+ color: @color-warning;
+ }
+
+ &.state-ok {
+ color: @color-ok;
+ }
+
+ &.state-unknown {
+ color: @color-unknown;
+ }
+
+ &.state-pending {
+ color: @color-pending;
+ }
+}
+ul.tabs a.state-critical {
+ background-color: @color-critical;
+ font-weight: bold;
+ color: @text-color-inverted;
+}
+ul.tabs a.state-warning {
+ background-color: @color-warning;
+ font-weight: bold;
+ color: @text-color-inverted;
+}
+ul.tabs a.state-ok {
+ background-color: @color-ok;
+ font-weight: bold;
+ color: @text-color-inverted;
+}
+ul.tabs a.state-unknown {
+ background-color: @color-unknown;
+ font-weight: bold;
+ color: @text-color-inverted;
+}
+
+a:hover::before {
+ text-decoration: none;
+}
+
+ul.main-actions {
+ margin: 0;
+ padding: 0;
+ min-width: 38em;
+ max-width: 64em;
+
+ li {
+ list-style-type: none;
+
+ text-align: left;
+ display: inline-block;
+ padding: 0;
+ clear: both;
+ width: 18em;
+ min-width: 16em;
+ vertical-align: top;
+
+ a {
+ i {
+ font-size: 3em;
+ display: block;
+ float: left;
+ line-height: 1em;
+ margin-right: 0.3em;
+ color: @text-color-light;
+ }
+
+ &.state-critical i {
+ color: @color-critical;
+ }
+
+ &.state-warning i {
+ color: @color-warning;
+ }
+
+ &.state-ok i {
+ color: @color-ok;
+ }
+
+ &.state-unknown i {
+ color: @color-unknown;
+ }
+
+ &.state-pending i {
+ color: @color-pending;
+ }
+
+ border-left: 0.5em solid transparent;
+ padding: 1em;
+ color: @text-color;
+ font-weight: bold;
+ display: block;
+ text-decoration: none;
+ min-height: 12em;
+
+ overflow: hidden;
+
+ &.active {
+ border-color: @icinga-blue;
+ background-color: @tr-active-color;
+ }
+
+ &:hover {
+ background-color: @tr-hover-color;
+ text-decoration: none;
+ }
+
+ &:active, &:focus {
+ background-color: @tr-hover-color;
+ outline: none;
+ }
+ }
+
+ p {
+ font-weight: normal;
+ margin-bottom: 0.5em;
+ padding-left: 4.5em;
+ color: @text-color-light;
+ }
+ }
+}
+
+#layout.compact-layout.twocols ul.main-actions,
+#layout.minimal-layout ul.main-actions {
+ max-width: unset;
+ min-width: unset;
+ li {
+ width: 100%;
+ a {
+ height: auto;
+ min-height: unset;
+ }
+ > a > i {
+ font-size: 3em;
+ }
+
+ > a > p {
+ padding-left: 4.5em;
+ }
+ margin-bottom: 0.5em;
+ }
+}
+
+#layout.minimal-layout div.content form.director-form {
+ dt, dd {
+ display: block;
+ width: auto;
+ }
+ fieldset.collapsed {
+ dd, dt, ul, div {
+ display: none;
+ }
+ }
+ dt label {
+ color: @text-color;
+ }
+ fieldset {
+ min-width: unset;
+ }
+
+ input[type=text], input[type=password], textarea, select {
+ max-width: unset;
+ min-width: unset;
+ border-color: @gray-light;
+ }
+ dd.active {
+ input[type=text], input[type=password], textarea, select {
+ border-color: @icinga-blue;
+ }
+ }
+ ul.extensible-set {
+ max-width: unset;
+ }
+ dd ul.extensible-set {
+ border: 1px solid @gray-light;
+ }
+ dd.active ul.extensible-set {
+ border: 1px solid @icinga-blue;
+
+ input[type=submit]:first-of-type {
+ border-width: 1px;
+ }
+ }
+
+ dd.active ul.extensible-set, ul.extensible-set.sortable {
+ input[type=text], select {
+ width: 100%;
+ }
+
+ input[type=text] {
+ background-color: @low-sat-blue;
+ border: 1px solid @gray-light;
+ }
+ }
+
+}
+
+form.director-form fieldset {
+ margin: 0;
+ padding: 0 0 1.5em 0;
+ border: none;
+
+ legend {
+ margin: 0em 0 0.5em 0;
+ font-size: 1em;
+ border-bottom: 1px solid @gray-light;
+ font-weight: bold;
+ display: block;
+ width: 100%;
+ padding-left: 1em;
+ line-height: 2em;
+ cursor: pointer;
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+
+ &:hover {
+ border-color: @text-color;
+ }
+
+ &::before {
+ // icon: down-dir
+ font-family: 'ifont';
+ content: '\e81d';
+ margin-left: -1em;
+ padding-top: 0em;
+ float: left;
+ color: inherit;
+ }
+ }
+
+ &.collapsed {
+ legend {
+ margin: 0;
+ }
+
+ dd, dt, ul, div {
+ display: none;
+ }
+
+ legend::before {
+ // icon: right-dir
+ content: '\e820';
+ }
+
+ margin-bottom: 0.2em;
+ padding-bottom: 0;
+ }
+}
+
+
+/* BEGIN Forms */
+form.director-form dt label {
+ width: auto;
+ font-weight: normal;
+ font-size: inherit;
+
+ &.required {
+ &::after {
+ content: '*'
+ }
+ }
+
+ &:hover {
+ text-decoration: underline;
+ cursor: pointer;
+ }
+}
+
+form.director-form fieldset {
+ min-width: 36em;
+}
+
+form.director-form dd input.related-action[type='submit'] {
+ display: none;
+}
+
+form.director-form dd.active li.active input.related-action[type='submit'] {
+ display: inline-block;
+}
+
+form.director-form {
+ p.description {
+ color: @gray;
+ font-style: italic;
+ padding: 0.25em 0.5em;
+ display: none;
+ }
+
+ dd.active {
+ p.description {
+ font-style: normal;
+ display: block;
+ height: auto;
+ color: @text-color;
+ }
+ }
+}
+
+form.director-form.db-selector {
+ padding: 0;
+ margin: 0;
+ select {
+ float: right;
+ text-align: center;
+ max-width: 20em;
+ margin-top: 0.5em;
+ min-width: 14em;
+ width: auto;
+ }
+}
+
+// Adjustments for the legacy layout to keep backwards compatibility
+#layout.twocols > #main &#col1 {
+ width: 50%;
+}
+#layout.twocols > #main &#col2 {
+ width: 50%;
+}
+#layout.twocols > #main &#col1 + #col2 {
+ width: 50%;
+}
+
+// Adjustments for the flexbox layout
+#layout.twocols > #content-wrapper &#col2,
+#layout.twocols > #content-wrapper &#col1 + #col2 {
+ flex-grow: 1;
+}
+
+form.director-form dd {
+ padding: 0.3em 0.5em;
+ margin: 0;
+}
+
+form.director-form dt {
+ padding: 0.5em 0.5em;
+ margin: 0;
+}
+
+form.director-form dt.active, form.director-form dd.active {
+ background-color: @tr-active-color;
+}
+
+form.director-form dt {
+ display: inline-block;
+ vertical-align: top;
+ min-width: 12em;
+ min-height: 2.5em;
+ width: 30%;
+ &.errors label {
+ color: @color-critical;
+ }
+}
+
+form.director-form .errors label {
+ color: @color-critical;
+}
+
+form.director-form dd {
+ display: inline-block;
+ width: 63%;
+ min-height: 2.5em;
+ vertical-align: top;
+ margin: 0;
+ &.errors {
+ input[type=text], select {
+ border-color: @color-critical;
+ }
+ }
+
+ &.full-width {
+ padding: 0.5em;
+ width: 100%;
+ }
+}
+
+form.director-form dd:after {
+ display: block;
+ content: '';
+}
+
+form.director-form textarea {
+ height: auto;
+}
+
+form.director-form dd ul.errors {
+ list-style-type: none;
+ padding-left: 0.3em;
+
+ li {
+ color: @color-critical;
+ padding: 0.3em;
+ }
+}
+
+form.director-form div.hint {
+ padding: 1em;
+ background-color: @tr-hover-color;
+ margin: 1em 0;
+ max-width: 65em;
+ font-size: 1em;
+
+ pre {
+ font-style: normal;
+ background-color: @body-bg-color;
+ margin: 0;
+ padding: 1em;
+ }
+}
+
+/* END of Forms */
+
+ul.health-check-result {
+ list-style-type: none;
+ padding: 0;
+ margin-bottom: 2em;
+ li {
+ line-height: 2em;
+ }
+ .badge {
+ font-weight: bold;
+ }
+}
+
+.title-badges {
+ .badge {
+ font-size: 0.6em;
+ margin-left: 0.5em;
+ }
+}
+
+li.state {
+ border-left: 0.5em solid transparent;
+ margin-bottom: 0.5em;
+ padding-left: 1em;
+ &.state-ok {
+ border-color: @color-ok;
+ }
+ &.state-warning {
+ border-color: @color-warning;
+ }
+ &.state-critical {
+ border-color: @color-critical;
+ }
+ &.state-unknown {
+ border-color: @color-unknown;
+ }
+ &.state-pending {
+ border-color: @color-pending;
+ }
+}
+
+span.error {
+ color: @color-critical;
+
+ a {
+ color: @color-critical;
+ }
+}
+
+p.legacy-error {
+ color: @text-color-inverted;
+ padding: 1em 2em;
+ background-color: @color-critical;
+
+}
+
+table th.actions, table td.actions {
+ text-align: right;
+}
+
+table tr.disabled td {
+ color: @gray-light;
+ font-style: italic;
+}
+
+/* Simple table, test */
+table.syncstate {
+ tr td:first-child {
+ padding-left: 2em;
+ &::before {
+ font-family: 'ifont';
+ // icon-help:
+ content: '\e85b';
+ float: left;
+ font-weight: bold;
+ margin-left: -1.5em;
+ line-height: 1.5em;
+ }
+ }
+
+ tr.in-sync td:first-child::before {
+ content: '\e803';
+ color: @color-ok;
+ }
+
+ tr.pending-changes td:first-child::before {
+ content: '\e864';
+ color: @color-warning;
+ }
+
+ tr.failing td:first-child::before {
+ content: '\e804';
+ color: @color-critical;
+ }
+}
+
+table.jobs {
+ tr td:first-child {
+ padding-left: 2em;
+ &::before {
+ font-family: 'ifont';
+ // icon-help:
+ content: '\e85b';
+ float: left;
+ font-weight: bold;
+ margin-left: -1.5em;
+ line-height: 1.5em;
+ }
+ }
+
+ tr.ok td:first-child::before {
+ content: '\e803';
+ color: @color-ok;
+ }
+
+ tr.warning td:first-child::before {
+ content: '\e864';
+ color: @color-warning;
+ }
+
+ tr.pending td:first-child::before {
+ content: '\e864';
+ color: @color-pending;
+ }
+
+ tr.critical td:first-child::before {
+ content: '\e804';
+ color: @color-critical;
+ }
+}
+
+table.icinga-objects {
+ tr td:first-child {
+ padding-left: 2em;
+ &::before {
+ font-family: 'ifont';
+ // icon-wrench:
+ content: '\e83d';
+ float: left;
+ font-weight: bold;
+ margin-left: -1.5em;
+ line-height: 1.5em;
+ }
+ }
+
+ tr.icinga-object-external td:first-child::before {
+ color: @gray;
+ // icon-pin
+ content: '\e879';
+ }
+
+ tr.icinga-object td:first-child::before {
+ color: @text-color;
+ // icon-thumbs-up
+ // content: '\e867';
+ // icon-ok
+ content: '\e803';
+ }
+
+ tr.icinga-template td:first-child::before {
+ color: @gray-light;
+ // icon-paste
+ content: '\e817';
+ }
+
+ tr.icinga-apply td:first-child::before {
+ color: @text-color;
+ // resize-full-alt
+ content: '\e829';
+ }
+
+}
+
+div.content.compact table.icinga-objects thead {
+ display: none;
+}
+
+table.deployment-log {
+
+ tr td:nth-child(2), tr th:nth-child(2) {
+ text-align: right;
+ padding-right: 1em;
+ }
+
+ tr th:first-child {
+ padding-left: 2em;
+ }
+
+ tr td:first-child {
+ padding-left: 2em;
+ &::before {
+ font-family: 'ifont';
+ // icon-help:
+ content: '\e85b';
+ float: left;
+ font-weight: bold;
+ margin-left: -1.5em;
+ line-height: 1.5em;
+ }
+ }
+
+ tr.succeeded td:first-child::before {
+ // icon-ok
+ color: @color-ok;
+ content: '\e803';
+ }
+
+ tr.pending td:first-child::before {
+ color: @gray;
+ // icon-spinner
+ content: '\e874';
+ .animate(spin 2s infinite linear);
+ }
+
+ tr.failed td:first-child::before {
+ // icon-ok
+ color: @color-critical;
+ content: '\e804';
+ }
+
+ tr.running td, tr.running td a {
+ font-weight: bold;
+ }
+}
+
+th.table-header-day {
+ text-align: right;
+}
+
+table.activity-log {
+ td {
+ max-height: 2em;
+ }
+ tr th:first-child {
+ padding-left: 2em;
+ }
+
+ tr td:last-child {
+ text-align: right;
+ white-space: nowrap;
+ width:10%;
+ }
+
+ tr td:first-child {
+ padding-left: 2em;
+ &::before {
+ font-family: 'ifont';
+ // icon-help:
+ content: '\e85b';
+ float: left;
+ font-weight: bold;
+ margin-left: -1.5em;
+ line-height: 1.5em;
+ }
+ }
+
+ tr.action-create td:first-child::before {
+ // icon-plus
+ color: @color-pending;
+ content: '\e805';
+ }
+
+ tr.action-modify td:first-child::before {
+ // icon-wrench
+ color: @color-ok;
+ content: '\e83d';
+ }
+
+ tr.action-delete td:first-child::before {
+ // icon-cancel
+ color: @color-critical;
+ content: '\e804';
+ }
+
+ tr.undeployed td, tr.undeployed a {
+ color: @gray;
+ }
+
+ tr.undeployed {
+ background-color: @gray-lightest;
+ &.active {
+ background-color: @gray-lighter;
+ }
+ &[href]:hover {
+ background-color: @gray-light;
+ td, a {
+ color: @text-color;
+ }
+ }
+ }
+
+ tr.branched {
+ background-color: @gray-lightest;
+ color: @color-pending;
+ }
+
+ tr.undeployed td:first-child::before {
+ color: @gray;
+ }
+
+ div.range-comment-container {
+ width: 100%;
+ position: absolute;
+ height: 100%;
+ background: @body-bg-color;
+ border-radius: 1em;
+ }
+ a.range-comment {
+ width: 100%;
+ height: 100%;
+ display: block;
+ border-radius: 1em;
+ padding: 0.2em 1em;
+ vertical-align: middle;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+ overflow-y:auto;
+ overflow-x:hidden;
+ word-break: break-word;
+ &:hover {
+ cursor: default;
+ text-decoration: none;
+ }
+ background: fade(@color-warning-handled, 20%);
+ &:hover {
+ background: fade(@color-warning-handled, 60%);
+ }
+ }
+ td.comment-cell {
+ padding: 0;
+ min-width: 10em;
+ width: 40%;
+ position: relative;
+ &.continuing div.range-comment-container {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ a.range-comment {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+ }
+ &.continued div.range-comment-container {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ a.range-comment {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ }
+ }
+ }
+}
+
+tr.branch_modified {
+ color: @color-pending;
+}
+
+table.config-diff {
+
+ tr th:first-child {
+ padding-left: 2em;
+ }
+
+ tr td:first-child {
+ padding-left: 2em;
+ &::before {
+ font-family: 'ifont';
+ // icon-help:
+ content: '\e85b';
+ float: left;
+ font-weight: bold;
+ margin-left: -1.5em;
+ line-height: 1.5em;
+ }
+ }
+
+ tr.file-unmodified td:first-child::before {
+ // icon-ok
+ color: @color-ok;
+ content: '\e803';
+ }
+
+ tr.file-created td:first-child::before {
+ // icon-plus
+ color: @color-pending;
+ content: '\e805';
+ }
+
+ tr.file-removed td:first-child::before {
+ // icon-cancel
+ color: @color-critical;
+ content: '\e804';
+ }
+
+ tr.file-modified td:first-child::before {
+ // icon-flapping
+ color: @color-warning;
+ content: '\e85d';
+ }
+}
+
+input[type=submit].icon-button {
+ font-family: 'ifont';
+ font-weight: normal;
+ background: none;
+ border: none;
+ padding: 0.2em 0.4em 0.2em 0.4em;
+ margin: 0 0 0 0.2em;
+
+ &:hover {
+ background-color: @icinga-blue;
+ }
+
+ &:disabled {
+ background-color: unset;
+ color: @gray-light;
+ cursor: default;
+ }
+}
+
+/** BEGIN breadcrumb **/
+
+// Hint: .badges is unused right now
+.breadcrumb {
+ list-style: none;
+ overflow: hidden;
+ padding: 0;
+
+ .badges {
+ display: inline-block;
+ padding: 0 0 0 0.5em;
+ .badge {
+ line-height: 1.25em;
+ font-size: 0.8em;
+ border: 1px solid @text-color;
+ margin: -0.25em 1px 0 0;
+ }
+ }
+}
+
+.breadcrumb {
+ > .critical a { color: @text-color-inverted; background: @color-critical; }
+ > .critical.handled a { color: @text-color-inverted; background: @color-critical-handled; }
+ > .unknown a { color: @text-color-inverted; background: @color-unknown; }
+ > .unknown.handled a { color: @text-color-inverted; background: @color-unknown-handled; }
+ > .warning a { color: @text-color-inverted; background: @color-warning; }
+ > .warning.handled a { color: @text-color-inverted; background: @color-warning-handled; }
+ > .ok a { color: @text-color-inverted; background: @color-ok; }
+}
+
+.breadcrumb {
+ > .critical a:after { border-left-color: @color-critical; }
+ > .critical.handled a:after { border-left-color: @color-critical-handled; }
+ > .unknown a:after { border-left-color: @color-unknown; }
+ > .unknown.handled a:after { border-left-color: @color-unknown-handled; }
+ > .warning a:after { border-left-color: @color-warning; }
+ > .warning.handled a:after { border-left-color: @color-warning-handled; }
+ > .ok a:after { border-left-color: @color-ok; }
+}
+
+.breadcrumb:after {
+ content:'';
+ display:block;
+ clear: both;
+}
+.breadcrumb li {
+ float: left;
+ cursor: pointer;
+ user-select: none;
+ background: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+
+}
+.breadcrumb li a {
+ // color: white;
+ color: @icinga-blue;
+ margin: 0;
+ // font-size: 1.2em;
+ text-decoration: none;
+ padding-left: 2em;
+ // line-height: 1.5em;
+ // background: @icinga-blue;
+ border: 1px none @icinga-blue;
+ border-right-style: solid;
+ border-left-style: solid;
+ position: relative;
+ display: block;
+ float: left;
+ &:focus {
+ outline: none;
+ }
+ &:hover {
+ text-decoration: none;
+ }
+}
+.action-bar .breadcrumb li a {
+ padding-left: 2em;
+}
+
+.breadcrumb li a:before, .breadcrumb li a:after {
+ content: " ";
+ display: block;
+ width: 0;
+ height: 0;
+ border-top: 1.3em solid transparent;
+ border-bottom: 1.2em solid transparent;
+ position: absolute;
+ margin-top: -1.2em;
+ top: 50%;
+ left: 100%;
+}
+
+.breadcrumb li a:before {
+ border-left: 1.2em solid @icinga-blue;
+ margin-left: 1px;
+ z-index: 1;
+}
+
+.breadcrumb li a:after {
+ border-left: 1.2em solid @body-bg-color;
+ z-index: 2;
+}
+
+.breadcrumb li:first-child a {
+ padding-left: 1em;
+ padding-right: 0.5em;
+}
+.breadcrumb li:last-child a {
+ color: @text-color;
+}
+
+.breadcrumb li:not(:last-child) a:hover { background: @icinga-blue; color: @text-color-on-icinga-blue; }
+.breadcrumb li:not(:last-child) a:hover:after { border-left-color: @icinga-blue; }
+.breadcrumb li:last-child:hover, .breadcrumb li:last-child a:hover { background: transparent; text-decoration: underline; }
+
+.breadcrumb li a:focus {
+ text-decoration: underline;
+}
+/** END of breadcrumb **/
+
+
+
+ul.filter-root {
+ margin-top: 0;
+ width: 100%;
+ padding-left: 0.5em;
+ list-style-type: none;
+ ul {
+ list-style-type: none;
+ padding-left: 1.5em;
+ }
+
+ ul.filter {
+ padding-left: 1.5em;
+ list-style-type: none;
+ width: 100%;
+ }
+
+ li.filter-chain, div.filter-expression {
+ width: 100%;
+ padding: 0.3em 0.5em;
+ min-width: 30em;
+ }
+
+ ul li.filter-chain::before, ul .filter-expression::before {
+ font-family: 'ifont';
+ // Formerly: icon-down-open: e821
+ // icon-right-small:
+ content: '\e877';
+ float: left;
+ margin-left: -1.5em;
+ margin-top: 0.5em;
+ }
+
+ ul.extensible-set {
+ padding-left: 0;
+ border: none;
+ display: inline-block;
+ vertical-align: top;
+ li::before {
+ content: none;
+ }
+ }
+
+ .filter-chain > input[type=submit].icon-button, .filter-expression > input[type=submit].icon-button {
+ display: none;
+ font-family: 'ifont';
+ font-weight: normal;
+ background: none;
+ border: none;
+ padding: 0.2em 0.4em 0.2em 0.4em;
+ margin: 0 0 0 0.2em;
+ }
+
+ .active input[type=submit].icon-button,
+ li:hover input[type=submit].icon-button,
+ div:hover input[type=submit].icon-button
+ {
+ display: inline;
+ }
+}
+
+.errors > ul.filter-root {
+ input[type=text], select {
+ border-color: transparent;
+ border-bottom-color: @gray-lighter;
+ }
+
+ select.column, select.operator {
+ border-left-color: @color-critical;
+ }
+}
+
+
+form.director-form li.filter-chain > select.operator {
+ min-width: 5em;
+ max-width: 5em;
+ width: 5em;
+}
+
+form.director-form div.filter-expression {
+ .column {
+ min-width: 7em;
+ max-width: 30em;
+ width: auto;
+ }
+
+ .sign {
+ min-width: 4em;
+ max-width: 4em;
+ width: 4em;
+ margin: 0 0.3em;
+ &.wide {
+ min-width: 6em;
+ max-width: 8em;
+ width: 8em;
+ }
+ }
+
+ div.expression-wrapper {
+ display: inline-block;
+ vertical-align: top;
+ }
+
+ div.expression-wrapper > input[type=text],
+ div.expression-wrapper > select {
+ min-width: 7em;
+ width: 10em;
+ max-width: 10em;
+ }
+}
+
+ul.director-suggestions {
+/*
+ min-width: 18.5em;
+ max-width: 34.65em;
+ width: 100%;
+ */
+ width: 20em;
+ max-height: 25em;
+ overflow-y: auto;
+ overflow-x: hidden;
+ border: 1px solid @icinga-blue;
+ position: absolute;
+ z-index: 2000;
+ padding: 0;
+ margin: 0;
+ list-style-type: none;
+ background-color: @low-sat-blue;
+ li {
+ margin: 0;
+ padding: 0.5em 1em;
+ }
+
+ li:hover {
+ background-color: @tr-hover-color;
+ cursor: pointer;
+ }
+
+ li.active {
+ color: @text-color;
+ &:hover {
+ color: @text-color;
+ }
+ }
+
+ table.benchmark {
+ font-size: 0.8em;
+ font-family: @font-family-fixed;
+ }
+}
+
+table.pivot {
+ width: 100%;
+ table-layout: fixed;
+
+ thead th {
+ text-align: center;
+ }
+
+ tbody th {
+ text-align: right;
+ width: 25%;
+ }
+ tbody td {
+ text-align: center;
+ }
+ tbody td > a {
+ display: block;
+ font-size: 2em;
+ line-height: 1.5em;
+ text-decoration: none;
+ color: @icinga-blue;
+ &:hover {
+ background: @tr-active-color;
+ text-decoration: none;
+ }
+ }
+}
+
+.tree li a {
+ display: inline-block;
+ padding-left: 2.4em;
+ line-height: 2em;
+ text-decoration: none;
+ color: @text-color;
+ outline: 0;
+ background-repeat: no-repeat;
+ background-position: 0.8em 0.4em;
+}
+
+ul.tree li > .handle {
+ background-image: none;
+ &:before {
+ content: '\e806';
+ font-family: 'ifont';
+ position: absolute;
+ font-size: 0.6em;
+ margin-left: 0.25em;
+ margin-top: 0.9em;
+ }
+}
+ul.tree li.collapsed > .handle {
+ background-image: none;
+ &:before {
+ content: '\e805';
+ }
+}
+.tree li a {
+ padding-left: 1em;
+}
+
+div.sql-dump {
+ background-color: @gray-lightest;
+ border: 1px solid @gray-light;
+ padding: 1em;
+}
+
+div.exception {
+ margin: 1em;
+}
+
+h2.action-create::before {
+ color: @color-pending;
+}
+h2.action-modify::before {
+ color: @color-ok;
+}
+h2.action-delete::before {
+ color: @color-critical;
+}
+
+/* Special components */
+table.table-basket-changes {
+ min-width: 18em;
+ max-width: 100%;
+ th {
+ width: 80%;
+ font-weight: normal;
+ text-align: left;
+ min-width: 10em;
+ }
+}
diff --git a/public/img/globe.png b/public/img/globe.png
new file mode 100644
index 0000000..48e5b6b
--- /dev/null
+++ b/public/img/globe.png
Binary files differ
diff --git a/public/img/leaf.gif b/public/img/leaf.gif
new file mode 100644
index 0000000..445769d
--- /dev/null
+++ b/public/img/leaf.gif
Binary files differ
diff --git a/public/img/script.png b/public/img/script.png
new file mode 100644
index 0000000..28f7652
--- /dev/null
+++ b/public/img/script.png
Binary files differ
diff --git a/public/img/server.png b/public/img/server.png
new file mode 100644
index 0000000..ee0c771
--- /dev/null
+++ b/public/img/server.png
Binary files differ
diff --git a/public/img/service.png b/public/img/service.png
new file mode 100644
index 0000000..0f1c2fd
--- /dev/null
+++ b/public/img/service.png
Binary files differ
diff --git a/public/img/tree.png b/public/img/tree.png
new file mode 100644
index 0000000..298343e
--- /dev/null
+++ b/public/img/tree.png
Binary files differ
diff --git a/public/js/module.js b/public/js/module.js
new file mode 100644
index 0000000..07fe265
--- /dev/null
+++ b/public/js/module.js
@@ -0,0 +1,840 @@
+
+(function (Icinga) {
+
+ var Director = function (module) {
+ this.module = module;
+
+ this.initialize();
+
+ this.openedFieldsets = {};
+
+ this.module.icinga.logger.debug('Director module loaded');
+ };
+
+ Director.prototype = {
+
+ initialize: function () {
+ /**
+ * Tell Icinga about our event handlers
+ */
+ this.module.on('rendered', this.rendered);
+ this.module.on('beforerender', this.beforeRender);
+ this.module.on('click', 'fieldset > legend', this.toggleFieldset);
+ // Disabled
+ // this.module.on('click', 'div.controls ul.tabs a', this.detailTabClick);
+ this.module.on('click', 'input.related-action', this.extensibleSetAction);
+ this.module.on('click', 'ul.filter-root input[type=submit]', this.setAutoSubmitted);
+ this.module.on('focus', 'form input, form textarea, form select', this.formElementFocus);
+ this.module.on('keyup', '.director-suggest', this.autoSuggest);
+ this.module.on('keydown', '.director-suggest', this.suggestionKeyDown);
+ this.module.on('dblclick', '.director-suggest', this.suggestionDoubleClick);
+ this.module.on('focus', '.director-suggest', this.enterSuggestionField);
+ this.module.on('focusout', '.director-suggest', this.leaveSuggestionField);
+ this.module.on('mousedown', '.director-suggestions li', this.clickSuggestion);
+ this.module.on('dblclick', 'ul.tabs a', this.tabWantsFullscreen);
+ this.module.on('change', 'form input.autosubmit, form select.autosubmit', this.setAutoSubmitted);
+ this.module.icinga.logger.debug('Director module initialized');
+ },
+
+ tabWantsFullscreen: function (ev) {
+ var icinga = this.module.icinga;
+ var $a, $container, id;
+
+ if (icinga.ui.isOneColLayout()) {
+ return;
+ }
+
+ $a = $(ev.currentTarget);
+ if ($a.hasClass('refresh-container-control')) {
+ return;
+ }
+ $container = $a.closest('.container');
+ id = $container.attr('id');
+
+ icinga.loader.stopPendingRequestsFor($container);
+ if (id === 'col2') {
+ icinga.ui.moveToLeft();
+ }
+
+ icinga.ui.layout1col();
+ icinga.history.pushCurrentState();
+ ev.preventDefault();
+ ev.stopPropagation();
+ },
+
+ /**
+ * Autocomplete/suggestion eventhandler
+ *
+ * Triggered when pressing a key in a form element with suggestions
+ * @param ev
+ */
+ suggestionKeyDown: function (ev) {
+ var $el = $(ev.currentTarget);
+ var key = ev.which;
+
+ if (key === 13) {
+ /**
+ * RETURN key pressed. In case there are any suggestions:
+ * - let's choose the active one (if set)
+ * - stop the event
+ *
+ * This let's return bubble up in case there is no suggestion list shown
+ */
+ if (this.hasActiveSuggestion($el)) {
+ this.chooseActiveSuggestion($el);
+ ev.stopPropagation();
+ ev.preventDefault();
+ } else {
+ this.removeSuggestionList($el);
+ if ($el.closest('.extensible-set')) {
+ $el.trigger('change');
+ } else {
+ $el.closest('form').submit();
+ }
+ }
+ } else if (key === 27) {
+ // ESC key pressed. Remove suggestions if any
+ this.removeSuggestionList($el);
+ } else if (key === 39) {
+ /**
+ * RIGHT ARROW key pressed. In case there are any suggestions:
+ * - let's choose the active one (if set)
+ * - stop the event only if an element has been chosen
+ *
+ * This allows to use the right arrow key normally in all other situations
+ */
+ if (this.hasSuggestions($el)) {
+ if (this.chooseActiveSuggestion($el)) {
+ ev.stopPropagation();
+ ev.preventDefault();
+ }
+ }
+ } else if (key === 38 ) {
+ /**
+ * UP ARROW key pressed. In any case:
+ * - stop the event
+ * - activate the previous suggestion if any
+ */
+ ev.stopPropagation();
+ ev.preventDefault();
+ this.activatePrevSuggestion($el);
+ } else if (key === 40 ) { // down
+ /**
+ * DOWN ARROW key pressed. In any case:
+ * - stop the event
+ * - activate the next suggestion if any
+ */
+ ev.stopPropagation();
+ ev.preventDefault();
+ this.activateNextSuggestion($el);
+ }
+ },
+
+ suggestionDoubleClick: function (ev) {
+ var $el = $(ev.currentTarget);
+ this.getSuggestionList($el);
+ },
+
+ /**
+ * Autocomplete/suggestion eventhandler
+ *
+ * Triggered when releasing a key in a form element with suggestions
+ *
+ * @param ev
+ */
+ autoSuggest: function (ev) {
+ // Ignore special keys, most of them have already been handled on 'keydown'
+ var key = ev.which;
+ if (key === 9 || // TAB
+ key === 13 || // RETURN
+ key === 27 || // ESC
+ key === 37 || // LEFT ARROW
+ key === 38 || // UP ARROW
+ key === 39 ) { // RIGHT ARROW
+ return;
+ }
+
+ var $el = $(ev.currentTarget);
+ if (key === 40) { // DOWN ARROW
+ this.getSuggestionList($el);
+ } else {
+ this.getSuggestionList($el, true);
+ }
+ },
+
+ /**
+ * Activate the next related suggestion if any
+ *
+ * This walks down the suggestion list, takes care about scrolling and restarts from
+ * top once reached the bottom
+ *
+ * @param $el
+ */
+ activateNextSuggestion: function ($el) {
+ var $list = this.getSuggestionList($el);
+ var $next;
+ var $active = $list.find('li.active');
+ if ($active.length) {
+ $next = $active.next('li');
+ if ($next.length === 0) {
+ $next = $list.find('li').first();
+ }
+ } else {
+ $next = $list.find('li').first();
+ }
+ if ($next.length) {
+ // Will not happen when list is empty or last element is active
+ $list.find('li.active').removeClass('active');
+ $next.addClass('active');
+ $list.scrollTop($next.offset().top - $list.offset().top - 64 + $list.scrollTop());
+ }
+ },
+
+ /**
+ * Activate the previous related suggestion if any
+ *
+ * This walks up through the suggestion list and takes care about scrolling.
+ * Puts the focus back on the input field once reached the top and restarts
+ * from bottom when moving up from there
+ *
+ * @param $el
+ */
+ activatePrevSuggestion: function ($el) {
+ var $list = this.getSuggestionList($el);
+ var $prev;
+ var $active = $list.find('li.active');
+ if ($active.length) {
+ $prev = $active.prev('li');
+ } else {
+ $prev = $list.find('li').last();
+ }
+ $list.find('li.active').removeClass('active');
+
+ if ($prev.length) {
+ $prev.addClass('active');
+ $list.scrollTop($prev.offset().top - $list.offset().top - 64 + $list.scrollTop());
+ } else {
+ $el.focus();
+ $el.val($el.val());
+ }
+ },
+
+ /**
+ * Whether a related suggestion list element exists
+ *
+ * @param $input
+ * @returns {boolean}
+ */
+ hasSuggestionList: function ($input) {
+ var $ul = $input.siblings('ul.director-suggestions');
+ return $ul.length > 0;
+ },
+
+ /**
+ * Whether any related suggestions are currently being shown
+ *
+ * @param $input
+ * @returns {boolean}
+ */
+ hasSuggestions: function ($input) {
+ var $ul = $input.siblings('ul.director-suggestions');
+ return $ul.length > 0 && $ul.is(':visible');
+ },
+
+ /**
+ * Get a suggestion list. Optionally force refresh
+ *
+ * @param $input
+ * @param $forceRefresh
+ *
+ * @returns {jQuery}
+ */
+ getSuggestionList: function ($input, $forceRefresh) {
+ var $ul = $input.siblings('ul.director-suggestions');
+ if ($ul.length) {
+ if ($forceRefresh) {
+ return this.refreshSuggestionList($ul, $input);
+ } else {
+ return $ul;
+ }
+ } else {
+ $ul = $('<ul class="director-suggestions"></ul>');
+ $input.parent().css({
+ position: 'relative'
+ });
+ $ul.insertAfter($input);
+ var suggestionWidth = (parseInt($input.css('width')) * 2) + 'px';
+ $ul.css({width: suggestionWidth});
+ return this.refreshSuggestionList($ul, $input);
+ }
+ },
+
+ /**
+ * Refresh a given suggestion list
+ *
+ * @param $suggestions
+ *
+ * @param $el
+ * @returns {jQuery}
+ */
+ refreshSuggestionList: function ($suggestions, $el) {
+ // Not sure whether we need this Accept-header
+ var headers = { 'X-Icinga-Accept': 'text/html' };
+ var icinga = this.module.icinga;
+
+ // Ask for a new window id in case we don't already have one
+ if (icinga.ui.hasWindowId()) {
+ headers['X-Icinga-WindowId'] = icinga.ui.getWindowId();
+ } else {
+ headers['X-Icinga-WindowId'] = 'undefined';
+ }
+
+ // var onResponse = function (data, textStatus, req) {
+ var onResponse = function (data) {
+ $suggestions.html(data);
+ var $li = $suggestions.find('li');
+ if ($li.length) {
+ $suggestions.show();
+ } else {
+ $suggestions.hide();
+ }
+ };
+
+ var req = $.ajax({
+ type: 'POST',
+ url: this.module.icinga.config.baseUrl + '/director/suggest',
+ data: {
+ value: $el.val(),
+ context: $el.data('suggestion-context'),
+ for_host: $el.data('suggestion-for-host')
+ },
+ headers: headers
+ });
+ req.done(onResponse);
+
+ return $suggestions;
+ },
+
+ /**
+ * Click handler for proposed suggestions
+ *
+ * @param ev
+ */
+ clickSuggestion: function (ev) {
+ this.chooseSuggestion($(ev.currentTarget));
+ },
+
+ /**
+ * Choose a specific suggestion
+
+ * @param $suggestion
+ */
+ chooseSuggestion: function ($suggestion) {
+ var $el = $suggestion.closest('ul').siblings('.director-suggest');
+ var val = $suggestion.text();
+
+ // extract label and key from key
+ var re = /^(.+) \[(\w+)]$/;
+
+ var withLabel = val.match(re);
+ if (withLabel) {
+ val = withLabel[2];
+ }
+
+ if (val.match(/\.$/)) {
+ $el.val(val);
+ this.getSuggestionList($el, true);
+ } else {
+ $el.focus();
+ $el.val(val);
+ $el.trigger('change');
+ this.getSuggestionList($el).remove();
+ }
+ },
+
+ /**
+ * Choose the current active suggestion related to a given element
+ *
+ * Returns true in case there was any, false otherwise
+ *
+ * @param $el
+ * @returns {boolean}
+ */
+ chooseActiveSuggestion: function ($el) {
+ var $list = this.getSuggestionList($el);
+ var $active = $list.find('li.active');
+ if ($active.length === 0) {
+ $active = $list.find('li:hover');
+ }
+ if ($active.length) {
+ this.chooseSuggestion($active);
+ return true;
+ } else {
+ $list.remove();
+ return false;
+ }
+ },
+
+ hasActiveSuggestion: function ($el) {
+ if (this.hasSuggestions($el)) {
+ var $list = this.getSuggestionList($el);
+ var $active = $list.find('li.active');
+ if ($active.length === 0) {
+ $active = $list.find('li:hover');
+ }
+ return $active.length > 0;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Remove related suggestion list if any
+ *
+ * @param $el
+ */
+ removeSuggestionList: function ($el) {
+ if (this.hasSuggestionList($el)) {
+ this.getSuggestionList($el).remove();
+ }
+ },
+
+ /**
+ * Show suggestions when arriving to an empty auto-completion field
+ *
+ * @param ev
+ */
+ enterSuggestionField: function (ev) {
+ // Has been disabled long time ago, as we do not want to open
+ // extensible Sets on focus. Should we re-enable this and just
+ // blacklist extensible sets?
+ //
+ // var $el = $(ev.currentTarget);
+ // if ($el.val() === '' || $el.val().match(/\.$/)) {
+ // this.getSuggestionList($el)
+ // }
+ },
+
+ /**
+ * Close suggestions when leaving the related form element
+ *
+ * @param ev
+ */
+ leaveSuggestionField: function (ev) {
+// return;
+ var _this = this;
+ setTimeout(function () {
+ _this.removeSuggestionList($(ev.currentTarget));
+ }, 100);
+ },
+
+ /**
+ * Sets an autosubmit flag on the container related to an event
+ *
+ * This will be used in beforeRender to determine whether the request has been triggered by an
+ * auto-submission
+ *
+ * @param ev
+ */
+ setAutoSubmitted: function (ev) {
+ $(ev.currentTarget).closest('.container').data('directorAutosubmit', 'yes');
+ },
+
+ /**
+ * Caused problems with differing tabs, should not be used
+ *
+ * @deprecated
+ */
+ detailTabClick: function (ev) {
+ var $a = $(ev.currentTarget);
+ if ($a.closest('#col2').length === 0) {
+ return;
+ }
+
+ this.alignDetailLinks();
+ },
+
+ /**
+ * Caused problems with differing tabs, should not be used
+ *
+ * @deprecated
+ */
+ alignDetailLinks: function () {
+ var self = this;
+ var $a = $('#col2').find('div.controls ul.tabs li.active a');
+ if ($a.length !== 1) {
+ return;
+ }
+
+ var $leftTable = $('#col1').find('> div.content').find('table.icinga-objects');
+ if ($leftTable.length !== 1) {
+ return;
+ }
+
+ var tabPath = self.pathFromHref($a);
+
+ $leftTable.find('tr').each(function (idx, tr) {
+ var $tr = $(tr);
+ if ($tr.is('[href]')) {
+ self.setHrefPath($tr, tabPath);
+ } else {
+ // Unfortunately we currently run BEFORE the action table
+ // handler
+ var $a = $tr.find('a[href].rowaction');
+ if ($a.length === 0) {
+ $a = $tr.find('a[href]').first();
+ }
+
+ if ($a.length) {
+ self.setHrefPath($a, tabPath);
+ }
+ }
+ });
+
+ $leftTable.find('tr[href]').each(function (idx, tr) {
+ var $tr = $(tr);
+ self.setHrefPath($tr, tabPath);
+ });
+ },
+
+ pathFromHref: function ($el) {
+ return this.module.icinga.utils.parseUrl($el.attr('href')).path
+ },
+
+ setHrefPath: function ($el, path) {
+ var a = this.module.icinga.utils.getUrlHelper();
+ a.href = $el.attr('href');
+ a.pathname = path;
+ $el.attr('href', a.href);
+ },
+
+ extensibleSetAction: function (ev) {
+ var iid, $li, $prev, $next;
+ var el = ev.currentTarget;
+ if (el.name.match(/__MOVE_UP$/)) {
+ $li = $(el).closest('li');
+ $prev = $li.prev();
+ // TODO: document what's going on here.
+ if ($li.find('input[type=text].autosubmit')) {
+ iid = $prev.find('input[type=text]').attr('id');
+ if (iid) {
+ $li.closest('.container').data('activeExtensibleEntry', iid);
+ } else {
+ return true;
+ }
+ }
+ if ($prev.length) {
+ $prev.before($li.detach());
+ this.fixRelatedActions($li.closest('ul'));
+ }
+ ev.preventDefault();
+ ev.stopPropagation();
+ return false;
+ } else if (el.name.match(/__MOVE_DOWN$/)) {
+ $li = $(el).closest('li');
+ $next = $li.next();
+ // TODO: document what's going on here.
+ if ($li.find('input[type=text].autosubmit')) {
+ iid = $next.find('input[type=text]').attr('id');
+ if (iid) {
+ $li.closest('.container').data('activeExtensibleEntry', iid);
+ } else {
+ return true;
+ }
+ }
+ if ($next.length && ! $next.find('.extend-set').length) {
+ $next.after($li.detach());
+ this.fixRelatedActions($li.closest('ul'));
+ }
+ ev.preventDefault();
+ ev.stopPropagation();
+ return false;
+ } else if (el.name.match(/__REMOVE$/)) {
+ $li = $(el).closest('li');
+ if ($li.find('.autosubmit').length) {
+ // Autosubmit element, let the server handle this
+ return true;
+ }
+
+ $li.remove();
+ this.fixRelatedActions($li.closest('ul'));
+ ev.preventDefault();
+ ev.stopPropagation();
+ return false;
+ } else if (el.name.match(/__DROP_DOWN$/)) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ var $el = $(ev.currentTarget).closest('li').find('input[type=text]');
+ this.getSuggestionList($el);
+ return false;
+ }
+ },
+
+ fixRelatedActions: function ($ul) {
+ var $uls = $ul.find('li');
+ var last = $uls.length - 1;
+ if ($ul.find('.extend-set').length) {
+ last--;
+ }
+
+ $uls.each(function (idx, li) {
+ var $li = $(li);
+ if (idx === 0) {
+ $li.find('.action-move-up').attr('disabled', 'disabled');
+ if (last === 0) {
+ $li.find('.action-move-down').attr('disabled', 'disabled');
+ } else {
+ $li.find('.action-move-down').removeAttr('disabled');
+ }
+ } else if (idx === last) {
+ $li.find('.action-move-up').removeAttr('disabled');
+ $li.find('.action-move-down').attr('disabled', 'disabled');
+ } else {
+ $li.find('.action-move-up').removeAttr('disabled');
+ $li.find('.action-move-down').removeAttr('disabled');
+ }
+ });
+ },
+
+ formElementFocus: function (ev) {
+ var $input = $(ev.currentTarget);
+ if ($input.closest('form.editor').length) {
+ return;
+ }
+ var $set = $input.closest('.extensible-set');
+ if ($set.length) {
+ var $textInputs = $('input[type=text]', $set);
+ if ($textInputs.length > 1) {
+ $textInputs.not(':first').attr('tabIndex', '-1');
+ }
+ }
+
+ var $dd = $input.closest('dd');
+ if ($dd.attr('id') && $dd.attr('id').match(/button/)) {
+ return;
+ }
+ var $li = $input.closest('li');
+ var $dt = $dd.prev();
+ var $form = $dd.closest('form');
+
+ $form.find('dt, dd, li').removeClass('active');
+ $li.addClass('active');
+ $dt.addClass('active');
+ $dd.addClass('active');
+ },
+
+ highlightFormErrors: function ($container) {
+ $container.find('dd ul.errors').each(function (idx, ul) {
+ var $ul = $(ul);
+ var $dd = $ul.closest('dd');
+ var $dt = $dd.prev();
+
+ $dt.addClass('errors');
+ $dd.addClass('errors');
+ });
+ },
+
+ toggleFieldset: function (ev) {
+ ev.stopPropagation();
+ var $fieldset = $(ev.currentTarget).closest('fieldset');
+ $fieldset.toggleClass('collapsed');
+ this.fixFieldsetInfo($fieldset);
+ this.openedFieldsets[$fieldset.attr('id')] = ! $fieldset.hasClass('collapsed');
+ },
+
+ beforeRender: function (ev) {
+ var $container = $(ev.currentTarget);
+ var id = $container.attr('id');
+ var requests = this.module.icinga.loader.requests;
+ if (typeof requests[id] !== 'undefined' && requests[id].autorefresh) {
+ $container.data('director-autorefreshed', 'yes');
+ } else {
+ $container.removeData('director-autorefreshed');
+ }
+
+ // Remove the temporary directorAutosubmit flag and set or remove
+ // the directorAutosubmitted property accordingly
+ if ($container.data('directorAutosubmit') === 'yes') {
+ $container.removeData('directorAutosubmit');
+ $container.data('directorAutosubmitted', 'yes');
+ } else {
+ $container.removeData('directorAutosubmitted');
+ }
+ },
+
+ /**
+ * Whether the given container has been autosubmitted
+ *
+ * @param $container
+ * @returns {boolean}
+ */
+ containerIsAutoSubmitted: function ($container) {
+ return $container.data('directorAutosubmitted') === 'yes';
+ },
+
+ /**
+ * Whether the given container has been autorefreshed
+ *
+ * @param $container
+ * @returns {boolean}
+ */
+ containerIsAutorefreshed: function ($container) {
+ return $container.data('director-autorefreshed') === 'yes';
+ },
+
+ rendered: function (ev) {
+ var iid;
+ var icinga = this.module.icinga;
+ var $container = $(ev.currentTarget);
+ if ($container.children('div.controls').first().data('directorWindowId') === '_UNDEFINED_') {
+ var $url = $container.data('icingaUrl');
+ if (typeof $url !== 'undefined') {
+ icinga.loader.loadUrl($url, $container).autorefresh = true;
+ }
+
+ $container.children('div.controls').children().hide();
+ $container.children('div.content').hide();
+ return;
+ }
+ this.restoreContainerFieldsets($container);
+ this.backupAllExtensibleSetDefaultValues($container);
+ this.highlightFormErrors($container);
+ this.scrollHighlightIntoView($container);
+ this.scrollActiveRowIntoView($container);
+ this.highlightActiveDashlet($container);
+ iid = $container.data('activeExtensibleEntry');
+ if (iid) {
+ $('#' + iid).focus();
+ $container.removeData('activeExtensibleEntry');
+ }
+ // Disabled for now
+ // this.alignDetailLinks();
+ if (! this.containerIsAutorefreshed($container) && ! this.containerIsAutoSubmitted($container)) {
+ this.putFocusOnFirstFormElement($container);
+ }
+
+ // Turn off autocomplete for all suggested fields
+ $container.find('input.director-suggest').each(this.disableAutocomplete);
+ },
+
+ highlightActiveDashlet: function ($container) {
+ if (this.module.icinga.ui.isOneColLayout()) {
+ return;
+ }
+
+ var url, $actions, $match;
+ var id = $container.attr('id');
+ if (id === 'col1') {
+ url = $('#col2').data('icingaUrl');
+ $actions = $('.main-actions', $container);
+ } else if (id === 'col2') {
+ url = $container.data('icingaUrl');
+ $actions = $('.main-actions', $('#col1'));
+ }
+ if (! $actions.length) {
+ return;
+ }
+
+ $match = $('li a[href*="' + url + '"]', $actions);
+ if ($match.length) {
+ $('li a.active', $actions).removeClass('active');
+ $match.first().addClass('active');
+ }
+ },
+
+ restoreContainerFieldsets: function ($container) {
+ var self = this;
+ $container.find('form').each(self.restoreFieldsets.bind(self));
+ },
+
+ putFocusOnFirstFormElement: function ($container) {
+ $container.find('form.autofocus').find('label').first().focus();
+ },
+
+ scrollHighlightIntoView: function ($container) {
+ var $hl = $container.find('.highlight');
+ var $content = $container.find('> div.content');
+
+ if ($hl.length) {
+ $container.animate({
+ scrollTop: $hl.offset().top - $content.offset().top
+ }, 700);
+ }
+ },
+
+ scrollActiveRowIntoView: function ($container) {
+ var $tr = $container.find('table.table-row-selectable > tbody > tr.active');
+ var $content = $container.find('> div.content');
+ if ($tr.length) {
+ $container.animate({
+ scrollTop: $tr.offset().top - $content.offset().top
+ }, 500);
+ }
+ },
+
+ backupAllExtensibleSetDefaultValues: function ($container) {
+ var self = this;
+ $container.find('.extensible-set').each(function (idx, eSet) {
+ $(eSet).find('input[type=text]').each(self.backupDefaultValue);
+ $(eSet).find('select').each(self.backupDefaultValue);
+ });
+ },
+
+ backupDefaultValue: function (idx, el) {
+ $(el).data('originalvalue', el.value);
+ },
+
+ restoreFieldsets: function (idx, form) {
+ var $form = $(form);
+ var self = this;
+ var $sets = $('fieldset', $form);
+
+ $sets.each(function (idx, fieldset) {
+ var $fieldset = $(fieldset);
+ if ($fieldset.attr('id') === 'fieldset-assign') {
+ return;
+ }
+ if ($fieldset.find('.required').length === 0 && (! self.fieldsetWasOpened($fieldset))) {
+ $fieldset.addClass('collapsed');
+ self.fixFieldsetInfo($fieldset);
+ }
+ });
+
+ if ($sets.length === 1) {
+ $sets.first().removeClass('collapsed');
+ }
+ },
+
+ fieldsetWasOpened: function ($fieldset) {
+ var id = $fieldset.attr('id');
+ if (typeof this.openedFieldsets[id] === 'undefined') {
+ return false;
+ }
+ return this.openedFieldsets[id];
+ },
+
+ fixFieldsetInfo: function ($fieldset) {
+ if ($fieldset.hasClass('collapsed')) {
+ if ($fieldset.find('legend span.element-count').length === 0) {
+ var cnt = $fieldset.find('dt, li').not('.extensible-set li').length;
+ if (cnt > 0) {
+ $fieldset.find('legend').append($('<span class="element-count"> (' + cnt + ')</span>'));
+ }
+ }
+ } else {
+ $fieldset.find('legend span.element-count').remove();
+ }
+ },
+
+ disableAutocomplete: function () {
+ $(this)
+ .attr('autocomplete', 'off')
+ .attr('autocorrect', 'off')
+ .attr('autocapitalize', 'off')
+ .attr('spellcheck', 'false');
+ }
+ };
+
+ Icinga.availableModules.director = Director;
+
+}(Icinga));