Merge pull request #3638 from Icinga/feature/js-collapsible-containers
Persistent Collapsible Containers
This commit is contained in:
commit
6cd94080bd
|
@ -833,6 +833,18 @@
|
||||||
"css": "th-list",
|
"css": "th-list",
|
||||||
"code": 61449,
|
"code": 61449,
|
||||||
"src": "mfglabs"
|
"src": "mfglabs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "63b3012c8cbe3654ba5bea598235aa3a",
|
||||||
|
"css": "angle-double-up",
|
||||||
|
"code": 61698,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "dfec4ffa849d8594c2e4b86f6320b8a6",
|
||||||
|
"css": "angle-double-down",
|
||||||
|
"code": 61699,
|
||||||
|
"src": "fontawesome"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -135,5 +135,7 @@
|
||||||
.icon-th-list:before { content: '\f009'; } /* '' */
|
.icon-th-list:before { content: '\f009'; } /* '' */
|
||||||
.icon-th-thumb-empty:before { content: '\f00b'; } /* '' */
|
.icon-th-thumb-empty:before { content: '\f00b'; } /* '' */
|
||||||
.icon-github-circled:before { content: '\f09b'; } /* '' */
|
.icon-github-circled:before { content: '\f09b'; } /* '' */
|
||||||
|
.icon-angle-double-up:before { content: '\f102'; } /* '' */
|
||||||
|
.icon-angle-double-down:before { content: '\f103'; } /* '' */
|
||||||
.icon-history:before { content: '\f1da'; } /* '' */
|
.icon-history:before { content: '\f1da'; } /* '' */
|
||||||
.icon-binoculars:before { content: '\f1e5'; } /* '' */
|
.icon-binoculars:before { content: '\f1e5'; } /* '' */
|
File diff suppressed because one or more lines are too long
|
@ -135,5 +135,7 @@
|
||||||
.icon-th-list { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-th-list { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-th-thumb-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-th-thumb-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-github-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-github-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-angle-double-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-angle-double-down { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-history { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-history { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
|
@ -146,5 +146,7 @@
|
||||||
.icon-th-list { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-th-list { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-th-thumb-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-th-thumb-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-github-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-github-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-angle-double-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-angle-double-down { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-history { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-history { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
|
@ -1,11 +1,11 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'ifont';
|
font-family: 'ifont';
|
||||||
src: url('../font/ifont.eot?15561604');
|
src: url('../font/ifont.eot?42867337');
|
||||||
src: url('../font/ifont.eot?15561604#iefix') format('embedded-opentype'),
|
src: url('../font/ifont.eot?42867337#iefix') format('embedded-opentype'),
|
||||||
url('../font/ifont.woff2?15561604') format('woff2'),
|
url('../font/ifont.woff2?42867337') format('woff2'),
|
||||||
url('../font/ifont.woff?15561604') format('woff'),
|
url('../font/ifont.woff?42867337') format('woff'),
|
||||||
url('../font/ifont.ttf?15561604') format('truetype'),
|
url('../font/ifont.ttf?42867337') format('truetype'),
|
||||||
url('../font/ifont.svg?15561604#ifont') format('svg');
|
url('../font/ifont.svg?42867337#ifont') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'ifont';
|
font-family: 'ifont';
|
||||||
src: url('../font/ifont.svg?15561604#ifont') format('svg');
|
src: url('../font/ifont.svg?42867337#ifont') format('svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
@ -191,5 +191,7 @@
|
||||||
.icon-th-list:before { content: '\f009'; } /* '' */
|
.icon-th-list:before { content: '\f009'; } /* '' */
|
||||||
.icon-th-thumb-empty:before { content: '\f00b'; } /* '' */
|
.icon-th-thumb-empty:before { content: '\f00b'; } /* '' */
|
||||||
.icon-github-circled:before { content: '\f09b'; } /* '' */
|
.icon-github-circled:before { content: '\f09b'; } /* '' */
|
||||||
|
.icon-angle-double-up:before { content: '\f102'; } /* '' */
|
||||||
|
.icon-angle-double-down:before { content: '\f103'; } /* '' */
|
||||||
.icon-history:before { content: '\f1da'; } /* '' */
|
.icon-history:before { content: '\f1da'; } /* '' */
|
||||||
.icon-binoculars:before { content: '\f1e5'; } /* '' */
|
.icon-binoculars:before { content: '\f1e5'; } /* '' */
|
|
@ -229,11 +229,11 @@ body {
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'ifont';
|
font-family: 'ifont';
|
||||||
src: url('./font/ifont.eot?97051739');
|
src: url('./font/ifont.eot?11177247');
|
||||||
src: url('./font/ifont.eot?97051739#iefix') format('embedded-opentype'),
|
src: url('./font/ifont.eot?11177247#iefix') format('embedded-opentype'),
|
||||||
url('./font/ifont.woff?97051739') format('woff'),
|
url('./font/ifont.woff?11177247') format('woff'),
|
||||||
url('./font/ifont.ttf?97051739') format('truetype'),
|
url('./font/ifont.ttf?11177247') format('truetype'),
|
||||||
url('./font/ifont.svg?97051739#ifont') format('svg');
|
url('./font/ifont.svg?11177247#ifont') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
@ -502,6 +502,8 @@ body {
|
||||||
<div class="the-icons span3" title="Code: 0xf09b"><i class="demo-icon icon-github-circled"></i> <span class="i-name">icon-github-circled</span><span class="i-code">0xf09b</span></div>
|
<div class="the-icons span3" title="Code: 0xf09b"><i class="demo-icon icon-github-circled"></i> <span class="i-name">icon-github-circled</span><span class="i-code">0xf09b</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xf102"><i class="demo-icon icon-angle-double-up"></i> <span class="i-name">icon-angle-double-up</span><span class="i-code">0xf102</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xf103"><i class="demo-icon icon-angle-double-down"></i> <span class="i-name">icon-angle-double-down</span><span class="i-code">0xf103</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf1da"><i class="demo-icon icon-history"></i> <span class="i-name">icon-history</span><span class="i-code">0xf1da</span></div>
|
<div class="the-icons span3" title="Code: 0xf1da"><i class="demo-icon icon-history"></i> <span class="i-name">icon-history</span><span class="i-code">0xf1da</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Binary file not shown.
|
@ -278,6 +278,10 @@
|
||||||
|
|
||||||
<glyph glyph-name="github-circled" unicode="" d="M429 779q116 0 215-58t156-156 57-215q0-140-82-252t-211-155q-15-3-22 4t-7 17q0 1 0 43t0 75q0 54-29 79 32 3 57 10t53 22 45 37 30 58 11 84q0 67-44 115 21 51-4 114-16 5-46-6t-51-25l-21-13q-52 15-107 15t-108-15q-8 6-23 15t-47 22-47 7q-25-63-5-114-44-48-44-115 0-47 12-83t29-59 45-37 52-22 57-10q-21-20-27-58-12-5-25-8t-32-3-36 12-31 35q-11 18-27 29t-28 14l-11 1q-12 0-16-2t-3-7 5-8 7-6l4-3q12-6 24-21t18-29l6-13q7-21 24-34t37-17 39-3 31 1l13 3q0-22 0-50t1-30q0-10-8-17t-22-4q-129 43-211 155t-82 252q0 117 58 215t155 156 216 58z m-267-616q2 4-3 7-6 1-8-1-1-4 4-7 5-3 7 1z m18-19q4 3-1 9-6 5-9 2-4-3 1-9 5-6 9-2z m16-25q6 4 0 11-4 7-9 3-5-3 0-10t9-4z m24-23q4 4-2 10-7 7-11 2-5-5 2-11 6-6 11-1z m32-14q1 6-8 9-8 2-10-4t7-9q8-3 11 4z m35-3q0 7-10 6-9 0-9-6 0-7 10-6 9 0 9 6z m32 5q-1 7-10 5-9-1-8-8t10-4 8 7z" horiz-adv-x="857.1" />
|
<glyph glyph-name="github-circled" unicode="" d="M429 779q116 0 215-58t156-156 57-215q0-140-82-252t-211-155q-15-3-22 4t-7 17q0 1 0 43t0 75q0 54-29 79 32 3 57 10t53 22 45 37 30 58 11 84q0 67-44 115 21 51-4 114-16 5-46-6t-51-25l-21-13q-52 15-107 15t-108-15q-8 6-23 15t-47 22-47 7q-25-63-5-114-44-48-44-115 0-47 12-83t29-59 45-37 52-22 57-10q-21-20-27-58-12-5-25-8t-32-3-36 12-31 35q-11 18-27 29t-28 14l-11 1q-12 0-16-2t-3-7 5-8 7-6l4-3q12-6 24-21t18-29l6-13q7-21 24-34t37-17 39-3 31 1l13 3q0-22 0-50t1-30q0-10-8-17t-22-4q-129 43-211 155t-82 252q0 117 58 215t155 156 216 58z m-267-616q2 4-3 7-6 1-8-1-1-4 4-7 5-3 7 1z m18-19q4 3-1 9-6 5-9 2-4-3 1-9 5-6 9-2z m16-25q6 4 0 11-4 7-9 3-5-3 0-10t9-4z m24-23q4 4-2 10-7 7-11 2-5-5 2-11 6-6 11-1z m32-14q1 6-8 9-8 2-10-4t7-9q8-3 11 4z m35-3q0 7-10 6-9 0-9-6 0-7 10-6 9 0 9 6z m32 5q-1 7-10 5-9-1-8-8t10-4 8 7z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
|
<glyph glyph-name="angle-double-up" unicode="" d="M600 118q0-7-6-13l-28-28q-5-5-12-5t-13 5l-220 219-219-219q-5-5-13-5t-12 5l-28 28q-6 6-6 13t6 13l260 260q5 5 12 5t13-5l260-260q6-6 6-13z m0 214q0-7-6-13l-28-28q-5-5-12-5t-13 5l-220 220-219-220q-5-5-13-5t-12 5l-28 28q-6 6-6 13t6 13l260 260q5 6 12 6t13-6l260-260q6-6 6-13z" horiz-adv-x="642.9" />
|
||||||
|
|
||||||
|
<glyph glyph-name="angle-double-down" unicode="" d="M600 368q0-7-6-13l-260-260q-5-6-13-6t-12 6l-260 260q-6 6-6 13t6 13l28 28q5 5 12 5t13-5l219-220 220 220q5 5 13 5t12-5l28-28q6-6 6-13z m0 214q0-7-6-13l-260-260q-5-5-13-5t-12 5l-260 260q-6 6-6 13t6 13l28 28q5 6 12 6t13-6l219-219 220 219q5 6 13 6t12-6l28-28q6-6 6-13z" horiz-adv-x="642.9" />
|
||||||
|
|
||||||
<glyph glyph-name="history" unicode="" d="M857 350q0-87-34-166t-91-137-137-92-166-34q-96 0-183 41t-147 114q-4 6-4 13t5 11l76 77q6 5 14 5 9-1 13-7 41-53 100-82t126-29q58 0 110 23t92 61 61 91 22 111-22 111-61 91-92 61-110 23q-55 0-105-20t-90-57l77-77q17-16 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l72-72q60 57 137 88t159 31q87 0 166-34t137-92 91-137 34-166z m-357 161v-250q0-8-5-13t-13-5h-178q-8 0-13 5t-5 13v35q0 8 5 13t13 5h125v197q0 8 5 13t12 5h36q8 0 13-5t5-13z" horiz-adv-x="857.1" />
|
<glyph glyph-name="history" unicode="" d="M857 350q0-87-34-166t-91-137-137-92-166-34q-96 0-183 41t-147 114q-4 6-4 13t5 11l76 77q6 5 14 5 9-1 13-7 41-53 100-82t126-29q58 0 110 23t92 61 61 91 22 111-22 111-61 91-92 61-110 23q-55 0-105-20t-90-57l77-77q17-16 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l72-72q60 57 137 88t159 31q87 0 166-34t137-92 91-137 34-166z m-357 161v-250q0-8-5-13t-13-5h-178q-8 0-13 5t-5 13v35q0 8 5 13t13 5h125v197q0 8 5 13t12 5h36q8 0 13-5t5-13z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
<glyph glyph-name="binoculars" unicode="" d="M393 671v-428q0-15-11-25t-25-11v-321q0-15-10-25t-26-11h-285q-15 0-25 11t-11 25v285l139 488q4 12 17 12h237z m178 0v-392h-142v392h142z m429-500v-285q0-15-11-25t-25-11h-285q-15 0-25 11t-11 25v321q-15 0-25 11t-11 25v428h237q13 0 17-12z m-589 661v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z m375 0v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z" horiz-adv-x="1000" />
|
<glyph glyph-name="binoculars" unicode="" d="M393 671v-428q0-15-11-25t-25-11v-321q0-15-10-25t-26-11h-285q-15 0-25 11t-11 25v285l139 488q4 12 17 12h237z m178 0v-392h-142v392h142z m429-500v-285q0-15-11-25t-25-11h-285q-15 0-25 11t-11 25v321q-15 0-25 11t-11 25v428h237q13 0 17-12z m-589 661v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z m375 0v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z" horiz-adv-x="1000" />
|
||||||
|
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 62 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -76,6 +76,12 @@ $innerLayoutScript = $this->layout()->innerLayout . '.phtml';
|
||||||
}
|
}
|
||||||
}());
|
}());
|
||||||
</script>
|
</script>
|
||||||
|
<div id="collapsible-control-ghost" class="collapsible-control">
|
||||||
|
<button>
|
||||||
|
<?= $this->icon('angle-double-down', $this->translate('Expand'), ['class' => 'expand-icon']) ?>
|
||||||
|
<?= $this->icon('angle-double-up', $this->translate('Collapse'), ['class' => 'collapse-icon']) ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<!--[if lt IE 10]>
|
<!--[if lt IE 10]>
|
||||||
<iframe id="fileupload-frame-target" name="fileupload-frame-target"></iframe>
|
<iframe id="fileupload-frame-target" name="fileupload-frame-target"></iframe>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
|
@ -13,6 +13,7 @@ class JavaScript
|
||||||
'js/helpers.js',
|
'js/helpers.js',
|
||||||
'js/icinga.js',
|
'js/icinga.js',
|
||||||
'js/icinga/logger.js',
|
'js/icinga/logger.js',
|
||||||
|
'js/icinga/storage.js',
|
||||||
'js/icinga/utils.js',
|
'js/icinga/utils.js',
|
||||||
'js/icinga/ui.js',
|
'js/icinga/ui.js',
|
||||||
'js/icinga/timer.js',
|
'js/icinga/timer.js',
|
||||||
|
@ -24,6 +25,7 @@ class JavaScript
|
||||||
'js/icinga/timezone.js',
|
'js/icinga/timezone.js',
|
||||||
'js/icinga/behavior/application-state.js',
|
'js/icinga/behavior/application-state.js',
|
||||||
'js/icinga/behavior/autofocus.js',
|
'js/icinga/behavior/autofocus.js',
|
||||||
|
'js/icinga/behavior/collapsible.js',
|
||||||
'js/icinga/behavior/detach.js',
|
'js/icinga/behavior/detach.js',
|
||||||
'js/icinga/behavior/tooltip.js',
|
'js/icinga/behavior/tooltip.js',
|
||||||
'js/icinga/behavior/sparkline.js',
|
'js/icinga/behavior/sparkline.js',
|
||||||
|
|
|
@ -161,13 +161,6 @@ a:hover > .icon-cancel {
|
||||||
background-color: @tr-hover-color;
|
background-color: @tr-hover-color;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
caption {
|
|
||||||
border-top: 1px solid @gray-light;
|
|
||||||
caption-side: bottom;
|
|
||||||
font-style: italic;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.name-value-table {
|
.name-value-table {
|
||||||
|
@ -233,3 +226,85 @@ a:hover > .icon-cancel {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collapsible Control
|
||||||
|
#collapsible-control-ghost {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-control {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
button {
|
||||||
|
.rounded-corners(50%);
|
||||||
|
|
||||||
|
background: @gray-lighter;
|
||||||
|
color: @gray;
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
bottom: -1em;
|
||||||
|
right: .25em;
|
||||||
|
-webkit-box-shadow: 0 0 1/3em rgba(0,0,0,.3);
|
||||||
|
-moz-box-shadow: 0 0 1/3em rgba(0,0,0,.3);
|
||||||
|
box-shadow: 0 0 1/3em rgba(0,0,0,.3);
|
||||||
|
|
||||||
|
&:hover:before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: -1/6em;
|
||||||
|
right: -1/6em;
|
||||||
|
bottom: -1/6em;
|
||||||
|
left: -1/6em;
|
||||||
|
background: fade(@text-color, 10);
|
||||||
|
.rounded-corners(50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button i:before {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible.can-collapse:not(.collapsed) + .collapsible-control button {
|
||||||
|
> i.expand-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> i.collapse-icon {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible.collapsed + .collapsible-control button {
|
||||||
|
> i.expand-icon {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
> i.collapse-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collapsibles
|
||||||
|
|
||||||
|
.collapsible.collapsed {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 2em;
|
||||||
|
background: linear-gradient(rgba(255,255,255,0), white);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
/*! Icinga Web 2 | (c) 2019 Icinga GmbH | GPLv2+ */
|
||||||
|
|
||||||
|
;(function(Icinga, $) {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
Icinga.Behaviors = Icinga.Behaviors || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior for collapsible containers.
|
||||||
|
*
|
||||||
|
* @param icinga Icinga The current Icinga Object
|
||||||
|
*/
|
||||||
|
var Collapsible = function(icinga) {
|
||||||
|
Icinga.EventListener.call(this, icinga);
|
||||||
|
|
||||||
|
this.on('layout-change', this.onLayoutChange, this);
|
||||||
|
this.on('rendered', '.container', this.onRendered, this);
|
||||||
|
this.on('click', '.collapsible + .collapsible-control', this.onControlClicked, this);
|
||||||
|
|
||||||
|
this.icinga = icinga;
|
||||||
|
this.defaultVisibleRows = 2;
|
||||||
|
this.defaultVisibleHeight = 36;
|
||||||
|
|
||||||
|
this.state = new Icinga.Storage.StorageAwareMap.withStorage(
|
||||||
|
Icinga.Storage.BehaviorStorage('collapsible'),
|
||||||
|
'expanded'
|
||||||
|
)
|
||||||
|
.on('add', this.onExpand, this)
|
||||||
|
.on('delete', this.onCollapse, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
Collapsible.prototype = new Icinga.EventListener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes all collapsibles. Triggered on rendering of a container.
|
||||||
|
*
|
||||||
|
* @param event Event The `onRender` event triggered by the rendered container
|
||||||
|
*/
|
||||||
|
Collapsible.prototype.onRendered = function(event) {
|
||||||
|
var _this = event.data.self;
|
||||||
|
|
||||||
|
$('.collapsible:not(.can-collapse)', event.currentTarget).each(function() {
|
||||||
|
var $collapsible = $(this);
|
||||||
|
|
||||||
|
// Assumes that any newly rendered elements are expanded
|
||||||
|
if (_this.canCollapse($collapsible)) {
|
||||||
|
$collapsible.after($('#collapsible-control-ghost').clone().removeAttr('id'));
|
||||||
|
$collapsible.addClass('can-collapse');
|
||||||
|
|
||||||
|
if (! _this.state.has(_this.icinga.utils.getCSSPath($collapsible))) {
|
||||||
|
_this.collapse($collapsible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates all collapsibles.
|
||||||
|
*
|
||||||
|
* @param event Event The `layout-change` event triggered by window resizing or column changes
|
||||||
|
*/
|
||||||
|
Collapsible.prototype.onLayoutChange = function(event) {
|
||||||
|
var _this = event.data.self;
|
||||||
|
|
||||||
|
$('.collapsible').each(function() {
|
||||||
|
var $collapsible = $(this);
|
||||||
|
var collapsiblePath = _this.icinga.utils.getCSSPath($collapsible);
|
||||||
|
|
||||||
|
if ($collapsible.is('.can-collapse')) {
|
||||||
|
if (! _this.canCollapse($collapsible)) {
|
||||||
|
$collapsible.next('.collapsible-control').remove();
|
||||||
|
$collapsible.removeClass('can-collapse');
|
||||||
|
_this.expand($collapsible);
|
||||||
|
}
|
||||||
|
} else if (_this.canCollapse($collapsible)) {
|
||||||
|
// It's expanded but shouldn't
|
||||||
|
$collapsible.after($('#collapsible-control-ghost').clone().removeAttr('id'));
|
||||||
|
$collapsible.addClass('can-collapse');
|
||||||
|
|
||||||
|
if (! _this.state.has(collapsiblePath)) {
|
||||||
|
_this.collapse($collapsible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collapsible got expanded in another window, try to apply this here as well
|
||||||
|
*
|
||||||
|
* @param {string} collapsiblePath
|
||||||
|
*/
|
||||||
|
Collapsible.prototype.onExpand = function(collapsiblePath) {
|
||||||
|
var $collapsible = $(collapsiblePath);
|
||||||
|
|
||||||
|
if ($collapsible.length && $collapsible.is('.can-collapse')) {
|
||||||
|
this.expand($collapsible);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collapsible got collapsed in another window, try to apply this here as well
|
||||||
|
*
|
||||||
|
* @param {string} collapsiblePath
|
||||||
|
*/
|
||||||
|
Collapsible.prototype.onCollapse = function(collapsiblePath) {
|
||||||
|
var $collapsible = $(collapsiblePath);
|
||||||
|
|
||||||
|
if ($collapsible.length && this.canCollapse($collapsible)) {
|
||||||
|
this.collapse($collapsible);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler for toggling collapsibles. Switches the collapsed state of the respective container.
|
||||||
|
*
|
||||||
|
* @param event Event The `onClick` event triggered by the clicked collapsible-control element
|
||||||
|
*/
|
||||||
|
Collapsible.prototype.onControlClicked = function(event) {
|
||||||
|
var _this = event.data.self;
|
||||||
|
var $target = $(event.currentTarget);
|
||||||
|
var $collapsible = $target.prev('.collapsible');
|
||||||
|
|
||||||
|
if (! $collapsible.length) {
|
||||||
|
_this.icinga.logger.error('[Collapsible] Collapsible control has no associated .collapsible: ', $target);
|
||||||
|
} else {
|
||||||
|
var collapsiblePath = _this.icinga.utils.getCSSPath($collapsible);
|
||||||
|
if (_this.state.has(collapsiblePath)) {
|
||||||
|
_this.state.delete(collapsiblePath);
|
||||||
|
_this.collapse($collapsible);
|
||||||
|
} else {
|
||||||
|
_this.state.set(collapsiblePath);
|
||||||
|
_this.expand($collapsible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an appropriate row element selector
|
||||||
|
*
|
||||||
|
* @param $collapsible jQuery The given collapsible container element
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
Collapsible.prototype.getRowSelector = function($collapsible) {
|
||||||
|
if ($collapsible.is('table')) {
|
||||||
|
return '> tbody > tr';
|
||||||
|
} else if ($collapsible.is('ul, ol')) {
|
||||||
|
return '> li';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the given collapsible needs to collapse
|
||||||
|
*
|
||||||
|
* @param $collapsible jQuery The given collapsible container element
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
Collapsible.prototype.canCollapse = function($collapsible) {
|
||||||
|
var rowSelector = this.getRowSelector($collapsible);
|
||||||
|
if (!! rowSelector) {
|
||||||
|
return $(rowSelector, $collapsible).length > ($collapsible.data('visibleRows') || this.defaultVisibleRows);
|
||||||
|
} else {
|
||||||
|
var actualHeight = $collapsible[0].scrollHeight;
|
||||||
|
var maxHeight = $collapsible.data('visibleHeight') || this.defaultVisibleHeight;
|
||||||
|
|
||||||
|
if (actualHeight <= maxHeight) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Although the height seems larger than what it should be, make sure it's not just a small fraction
|
||||||
|
// i.e. more than 12 pixel and at least 10% difference
|
||||||
|
return actualHeight - maxHeight > 12 && actualHeight / maxHeight >= 1.1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse the given collapsible
|
||||||
|
*
|
||||||
|
* @param $collapsible jQuery The given collapsible container element
|
||||||
|
*/
|
||||||
|
Collapsible.prototype.collapse = function($collapsible) {
|
||||||
|
$collapsible.addClass('collapsed');
|
||||||
|
|
||||||
|
var rowSelector = this.getRowSelector($collapsible);
|
||||||
|
if (!! rowSelector) {
|
||||||
|
var $rows = $(rowSelector, $collapsible).slice(0, $collapsible.data('visibleRows') || this.defaultVisibleRows);
|
||||||
|
|
||||||
|
var totalHeight = $rows.offset().top - $collapsible.offset().top;
|
||||||
|
$rows.outerHeight(function(_, height) {
|
||||||
|
totalHeight += height;
|
||||||
|
});
|
||||||
|
|
||||||
|
$collapsible.css({display: 'block', height: totalHeight});
|
||||||
|
} else {
|
||||||
|
$collapsible.css({display: 'block', height: $collapsible.data('visibleHeight') || this.defaultVisibleHeight});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand the given collapsible
|
||||||
|
*
|
||||||
|
* @param $collapsible jQuery The given collapsible container element
|
||||||
|
*/
|
||||||
|
Collapsible.prototype.expand = function($collapsible) {
|
||||||
|
$collapsible.removeClass('collapsed');
|
||||||
|
$collapsible.css({display: '', height: ''});
|
||||||
|
};
|
||||||
|
|
||||||
|
Icinga.Behaviors.Collapsible = Collapsible;
|
||||||
|
|
||||||
|
})(Icinga, jQuery);
|
|
@ -0,0 +1,526 @@
|
||||||
|
/*! Icinga Web 2 | (c) 2019 Icinga GmbH | GPLv2+ */
|
||||||
|
|
||||||
|
;(function(Icinga) {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const KEY_TTL = 7776000000; // 90 days (90×24×60×60×1000)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icinga.Storage
|
||||||
|
*
|
||||||
|
* localStorage access
|
||||||
|
*
|
||||||
|
* @param {string} prefix
|
||||||
|
*/
|
||||||
|
Icinga.Storage = function(prefix) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefix to use for keys
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.prefix = prefix;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callbacks for storage events on particular keys
|
||||||
|
*
|
||||||
|
* @type {{function}}
|
||||||
|
*/
|
||||||
|
Icinga.Storage.subscribers = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass storage events to subscribers
|
||||||
|
*
|
||||||
|
* @param {StorageEvent} event
|
||||||
|
*/
|
||||||
|
window.addEventListener('storage', function(event) {
|
||||||
|
var url = icinga.utils.parseUrl(event.url);
|
||||||
|
if (! url.path.substring(0, icinga.config.baseUrl.length) === icinga.config.baseUrl) {
|
||||||
|
// A localStorage is shared between all paths on the same origin.
|
||||||
|
// So we need to make sure it's us who made a change.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof Icinga.Storage.subscribers[event.key] !== 'undefined') {
|
||||||
|
var newValue = null,
|
||||||
|
oldValue = null;
|
||||||
|
if (!! event.newValue) {
|
||||||
|
try {
|
||||||
|
newValue = JSON.parse(event.newValue);
|
||||||
|
} catch(error) {
|
||||||
|
icinga.logger.error('[Storage] Failed to parse new value (\`' + event.newValue
|
||||||
|
+ '\`) for key "' + event.key + '". Error was: ' + error);
|
||||||
|
event.storageArea.removeItem(event.key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!! event.oldValue) {
|
||||||
|
try {
|
||||||
|
oldValue = JSON.parse(event.oldValue);
|
||||||
|
} catch(error) {
|
||||||
|
icinga.logger.warn('[Storage] Failed to parse old value (\`' + event.oldValue
|
||||||
|
+ '\`) of key "' + event.key + '". Error was: ' + error);
|
||||||
|
oldValue = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Icinga.Storage.subscribers[event.key].forEach(function (subscriber) {
|
||||||
|
subscriber[0].call(subscriber[1], newValue, oldValue, event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new storage with `behavior.<name>` as prefix
|
||||||
|
*
|
||||||
|
* @param {string} name
|
||||||
|
*
|
||||||
|
* @returns {Icinga.Storage}
|
||||||
|
*/
|
||||||
|
Icinga.Storage.BehaviorStorage = function(name) {
|
||||||
|
return new Icinga.Storage('behavior.' + name);
|
||||||
|
};
|
||||||
|
|
||||||
|
Icinga.Storage.prototype = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefix the given key
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
prefixKey: function(key) {
|
||||||
|
var prefix = 'icinga.';
|
||||||
|
if (typeof this.prefix !== 'undefined') {
|
||||||
|
prefix = prefix + this.prefix + '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix + key;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the given key-value pair
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
* @param {*} value
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
set: function(key, value) {
|
||||||
|
window.localStorage.setItem(this.prefixKey(key), JSON.stringify(value));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value for the given key
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
*
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
get: function(key) {
|
||||||
|
key = this.prefixKey(key);
|
||||||
|
var value = window.localStorage.getItem(key);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(value);
|
||||||
|
} catch(error) {
|
||||||
|
icinga.logger.error('[Storage] Failed to parse value (\`' + value
|
||||||
|
+ '\`) of key "' + key + '". Error was: ' + error);
|
||||||
|
window.localStorage.removeItem(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove given key from storage
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
remove: function(key) {
|
||||||
|
window.localStorage.removeItem(this.prefixKey(key));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe with a callback for events on a particular key
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
* @param {function} callback
|
||||||
|
* @param {object} context
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
onChange: function(key, callback, context) {
|
||||||
|
var prefixedKey = this.prefixKey(key);
|
||||||
|
|
||||||
|
if (typeof Icinga.Storage.subscribers[prefixedKey] === 'undefined') {
|
||||||
|
Icinga.Storage.subscribers[prefixedKey] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Icinga.Storage.subscribers[prefixedKey].push([callback, context]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icinga.Storage.StorageAwareMap
|
||||||
|
*
|
||||||
|
* @param {object} items
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
Icinga.Storage.StorageAwareMap = function(items) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage object
|
||||||
|
*
|
||||||
|
* @type {Icinga.Storage}
|
||||||
|
*/
|
||||||
|
this.storage = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage key
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.key = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listeners for our internal events
|
||||||
|
*
|
||||||
|
* @type {{}}
|
||||||
|
*/
|
||||||
|
this.eventListeners = {
|
||||||
|
'add': [],
|
||||||
|
'delete': []
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The internal (real) map
|
||||||
|
*
|
||||||
|
* @type {Map<*>}
|
||||||
|
*/
|
||||||
|
this.data = new Map();
|
||||||
|
|
||||||
|
// items is not passed directly because IE11 doesn't support constructor arguments
|
||||||
|
if (typeof items !== 'undefined' && !! items) {
|
||||||
|
Object.keys(items).forEach(function(key) {
|
||||||
|
this.data.set(key, items[key]);
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new StorageAwareMap for the given storage and key
|
||||||
|
*
|
||||||
|
* @param {Icinga.Storage} storage
|
||||||
|
* @param {string} key
|
||||||
|
*
|
||||||
|
* @returns {Icinga.Storage.StorageAwareMap}
|
||||||
|
*/
|
||||||
|
Icinga.Storage.StorageAwareMap.withStorage = function(storage, key) {
|
||||||
|
var items = storage.get(key);
|
||||||
|
if (typeof items !== 'undefined' && !! items) {
|
||||||
|
Object.keys(items).forEach(function(key) {
|
||||||
|
var value = items[key];
|
||||||
|
|
||||||
|
if (typeof value !== 'object' || typeof value['lastAccess'] === 'undefined') {
|
||||||
|
items[key] = {'value': value, 'lastAccess': Date.now()};
|
||||||
|
} else if (Date.now() - value['lastAccess'] > KEY_TTL) {
|
||||||
|
delete items[key];
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!! items && items.length) {
|
||||||
|
storage.set(key, items);
|
||||||
|
} else if(items !== null) {
|
||||||
|
storage.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new Icinga.Storage.StorageAwareMap(items).setStorage(storage, key));
|
||||||
|
};
|
||||||
|
|
||||||
|
Icinga.Storage.StorageAwareMap.prototype = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind this map to the given storage and key
|
||||||
|
*
|
||||||
|
* @param {Icinga.Storage} storage
|
||||||
|
* @param {string} key
|
||||||
|
*
|
||||||
|
* @returns {this}
|
||||||
|
*/
|
||||||
|
setStorage: function(storage, key) {
|
||||||
|
this.storage = storage;
|
||||||
|
this.key = key;
|
||||||
|
|
||||||
|
storage.onChange(key, this.onChange, this);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a boolean indicating this map got a storage
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
hasStorage: function() {
|
||||||
|
return typeof this.storage !== 'undefined' && typeof this.key !== 'undefined';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the storage
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
updateStorage: function() {
|
||||||
|
if (! this.hasStorage()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.size > 0) {
|
||||||
|
this.storage.set(this.key, this.toObject());
|
||||||
|
} else {
|
||||||
|
this.storage.remove(this.key);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the map
|
||||||
|
*
|
||||||
|
* @param {object} newValue
|
||||||
|
*/
|
||||||
|
onChange: function(newValue) {
|
||||||
|
// Check for deletions first. Uses keys() to iterate over a copy
|
||||||
|
this.keys().forEach(function (key) {
|
||||||
|
if (newValue === null || typeof newValue[key] === 'undefined') {
|
||||||
|
var value = this.data.get(key)['value'];
|
||||||
|
this.data.delete(key);
|
||||||
|
this.trigger('delete', key, value);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
if (newValue === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check for new entries
|
||||||
|
Object.keys(newValue).forEach(function(key) {
|
||||||
|
var known = this.data.has(key);
|
||||||
|
// Always override any known value as we want to keep track of all `lastAccess` changes
|
||||||
|
this.data.set(key, newValue[key]);
|
||||||
|
|
||||||
|
if (! known) {
|
||||||
|
this.trigger('add', key, newValue[key]['value']);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an event handler to handle storage updates
|
||||||
|
*
|
||||||
|
* Available events are: add, delete. The callback receives the
|
||||||
|
* key and its value as first and second argument, respectively.
|
||||||
|
*
|
||||||
|
* @param {string} event
|
||||||
|
* @param {function} callback
|
||||||
|
* @param {object} thisArg
|
||||||
|
*
|
||||||
|
* @returns {this}
|
||||||
|
*/
|
||||||
|
on: function(event, callback, thisArg) {
|
||||||
|
if (typeof this.eventListeners[event] === 'undefined') {
|
||||||
|
throw new Error('Invalid event "' + event + '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eventListeners[event].push([callback, thisArg]);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger all event handlers for the given event
|
||||||
|
*
|
||||||
|
* @param {string} event
|
||||||
|
* @param {string} key
|
||||||
|
* @param {*} value
|
||||||
|
*/
|
||||||
|
trigger: function(event, key, value) {
|
||||||
|
this.eventListeners[event].forEach(function (handler) {
|
||||||
|
var thisArg = handler[1];
|
||||||
|
if (typeof thisArg === 'undefined') {
|
||||||
|
thisArg = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler[0].call(thisArg, key, value);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of key/value pairs in the map
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get size() {
|
||||||
|
return this.data.size;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value for the key in the map
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
* @param {*} value Default null
|
||||||
|
*
|
||||||
|
* @returns {this}
|
||||||
|
*/
|
||||||
|
set: function(key, value) {
|
||||||
|
if (typeof value === 'undefined') {
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data.set(key, {'value': value, 'lastAccess': Date.now()});
|
||||||
|
|
||||||
|
this.updateStorage();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all key/value pairs from the map
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
clear: function() {
|
||||||
|
this.data.clear();
|
||||||
|
this.updateStorage();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the given key from the map
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
delete: function(key) {
|
||||||
|
var retVal = this.data.delete(key);
|
||||||
|
|
||||||
|
this.updateStorage();
|
||||||
|
return retVal;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of [key, value] pairs for every item in the map
|
||||||
|
*
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
entries: function() {
|
||||||
|
var list = [];
|
||||||
|
|
||||||
|
if (this.size > 0) {
|
||||||
|
this.data.forEach(function (value, key) {
|
||||||
|
list.push([key, value['value']]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a provided function once for each item in the map, in insertion order
|
||||||
|
*
|
||||||
|
* @param {function} callback
|
||||||
|
* @param {object} thisArg
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
forEach: function(callback, thisArg) {
|
||||||
|
if (typeof thisArg === 'undefined') {
|
||||||
|
thisArg = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data.forEach(function(value, key) {
|
||||||
|
callback.call(thisArg, value['value'], key);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the value associated to the key, or undefined if there is none
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
*
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
get: function(key) {
|
||||||
|
var value = this.data.get(key)['value'];
|
||||||
|
this.set(key, value); // Update `lastAccess`
|
||||||
|
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a boolean asserting whether a value has been associated to the key in the map
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
has: function(key) {
|
||||||
|
return this.data.has(key);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of keys in the map
|
||||||
|
*
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
keys: function() {
|
||||||
|
var list = [];
|
||||||
|
|
||||||
|
if (this.size > 0) {
|
||||||
|
// .forEach() is used because IE11 doesn't support .keys()
|
||||||
|
this.data.forEach(function(_, key) {
|
||||||
|
list.push(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of values in the map
|
||||||
|
*
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
values: function() {
|
||||||
|
var list = [];
|
||||||
|
|
||||||
|
if (this.size > 0) {
|
||||||
|
// .forEach() is used because IE11 doesn't support .values()
|
||||||
|
this.data.forEach(function(value) {
|
||||||
|
list.push(value['value']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return this map as simple object
|
||||||
|
*
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
toObject: function() {
|
||||||
|
var obj = {};
|
||||||
|
|
||||||
|
if (this.size > 0) {
|
||||||
|
this.data.forEach(function (value, key) {
|
||||||
|
obj[key] = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}(Icinga));
|
|
@ -268,6 +268,9 @@
|
||||||
this.currentLayout = matched[1];
|
this.currentLayout = matched[1];
|
||||||
if (this.currentLayout === 'poor' || this.currentLayout === 'minimal') {
|
if (this.currentLayout === 'poor' || this.currentLayout === 'minimal') {
|
||||||
this.layout1col();
|
this.layout1col();
|
||||||
|
} else {
|
||||||
|
// layout1col() also triggers this, that's why an else is required
|
||||||
|
$('#layout').trigger('layout-change');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -293,6 +296,7 @@
|
||||||
this.icinga.logger.debug('Switching to single col');
|
this.icinga.logger.debug('Switching to single col');
|
||||||
$('#layout').removeClass('twocols');
|
$('#layout').removeClass('twocols');
|
||||||
this.closeContainer($('#col2'));
|
this.closeContainer($('#col2'));
|
||||||
|
$('#layout').trigger('layout-change');
|
||||||
// one-column layouts never have any selection active
|
// one-column layouts never have any selection active
|
||||||
$('#col1').removeData('icinga-actiontable-former-href');
|
$('#col1').removeData('icinga-actiontable-former-href');
|
||||||
this.icinga.behaviors.actiontable.clearAll();
|
this.icinga.behaviors.actiontable.clearAll();
|
||||||
|
@ -315,6 +319,7 @@
|
||||||
this.icinga.logger.debug('Switching to double col');
|
this.icinga.logger.debug('Switching to double col');
|
||||||
$('#layout').addClass('twocols');
|
$('#layout').addClass('twocols');
|
||||||
this.fixControls();
|
this.fixControls();
|
||||||
|
$('#layout').trigger('layout-change');
|
||||||
},
|
},
|
||||||
|
|
||||||
getAvailableColumnSpace: function () {
|
getAvailableColumnSpace: function () {
|
||||||
|
|
Loading…
Reference in New Issue