Merge pull request #4311 from IgorA100/patch-617239

Feat: Added left side menu
This commit is contained in:
Isaac Connor
2025-06-28 14:55:23 -04:00
committed by GitHub
40 changed files with 7466 additions and 92 deletions

View File

@@ -1147,6 +1147,7 @@ class Filter extends ZM_Object {
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $availableTags, $selected, $options).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
// $html .= '<span>'.htmlSelect("filter[Query][terms][$i][val]", array_combine($availableTags,$availableTags), $term['val'],
// $options).'</span>'.PHP_EOL;
// $html .= '<span>'.htmlSelect("filter[Query][terms][$i][val]", $availableTags, $term['val'], $options).'</span>'.PHP_EOL;
@@ -1192,7 +1193,7 @@ class Filter extends ZM_Object {
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $booleanValues, $term['val'], ['class'=>'chosen chosen-auto-width']).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
} else if ( $term['attr'] == 'Group') {
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", Group::get_dropdown_options(), $term['val'],
@@ -1200,14 +1201,17 @@ class Filter extends ZM_Object {
'multiple'=>'multiple',
'data-placeholder'=>translate('All Groups')]).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
} else if ( $term['attr'] == 'StateId' ) {
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $states, $term['val'], ['class'=>'chosen chosen-auto-width']).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
} else if ( strpos($term['attr'], 'Weekday') !== false ) {
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $weekdays, $term['val'], ['class'=>'chosen chosen-auto-width']).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
} else if ( $term['attr'] == 'Monitor' ) {
$monitors = [];
foreach (Monitor::find(['Deleted'=>false], ['order'=>'lower(Name)']) as $m) {
@@ -1230,6 +1234,7 @@ class Filter extends ZM_Object {
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $monitors, $selected, $options).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
} else if ( $term['attr'] == 'MonitorName' ) {
$monitor_names = [];
foreach (Monitor::find(['Deleted'=>false], ['order'=>'lower(Name)']) as $m) {
@@ -1241,11 +1246,13 @@ class Filter extends ZM_Object {
$html .= htmlSelect("filter[Query][terms][$i][val]", array_combine($monitor_names,$monitor_names), $term['val'],
['class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple', 'data-placeholder'=>translate('All Monitors')]).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
} else if ( $term['attr'] == 'ServerId' || $term['attr'] == 'MonitorServerId' || $term['attr'] == 'StorageServerId' || $term['attr'] == 'FilterServerId' ) {
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $servers, $term['val'],
['class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple']).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
} else if ( ($term['attr'] == 'StorageId') || ($term['attr'] == 'SecondaryStorageId') ) {
if (!$storageareas) {
$storageareas = array('' => array('Name'=>'NULL Unspecified'), '0' => array('Name'=>'Zero')) + ZM_Object::Objects_Indexed_By_Id('ZM\Storage');
@@ -1254,11 +1261,13 @@ class Filter extends ZM_Object {
$html .= htmlSelect("filter[Query][terms][$i][val]", $storageareas, $term['val'],
['class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple']).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
} else if ( $term['attr'] == 'AlarmedZoneId' ) {
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $zones, $term['val'],
['class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple']).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
} else if ( $term['attr'] == 'Notes' ) {
$attrs = ['id'=>'filterNotes', 'class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple', 'data-placeholder'=>translate('Event Type')];
$selected = explode(',', $term['val']);
@@ -1286,6 +1295,7 @@ class Filter extends ZM_Object {
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $options, $selected, $attrs).PHP_EOL;
$html .= '</span>';
$html .= $this->addButtonForFilterSelect("filter[Query][terms][$i][val]");
} else {
#$html .= $term['attr'];
$html .= '<span class="term-value-wrapper">';
@@ -1374,5 +1384,19 @@ class Filter extends ZM_Object {
return false;
}
function addButtonForFilterSelect($nameSelect) {
if (isset($_COOKIE['zmUseOldMenuView']) and $_COOKIE["zmUseOldMenuView"] === 'true') {
$html = '';
} else {
$html = PHP_EOL . '
<span class="btn-term-remove-all">
<button type="button" name="deleteBtn" data-on-click-this="resetSelectElement" data-select-target="'.$nameSelect.'">
<i class="material-icons">clear</i>
<span class="text"></span>
</button>
</span>' . PHP_EOL;
}
return $html;
}
} # end class Filter
?>

View File

@@ -0,0 +1,550 @@
/*
* ******************************************************************************
* jquery.mb.components
* file: mbExtruder.js
*
* Copyright (c) 2001-2014. Matteo Bicocchi (Pupunzi);
* Open lab srl, Firenze - Italy
* email: matteo@open-lab.com
* site: http://pupunzi.com
* blog: http://pupunzi.open-lab.com
* http://open-lab.com
*
* Licences: MIT, GPL
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* last modified: 08/05/14 20.00
* *****************************************************************************
*/
(function($) {
document.extruder=new Object();
document.extruder.left = 0;
document.extruder.top = 0;
document.extruder.bottom = 0;
document.extruder.right = 0;
document.extruder.idx=0;
$.mbExtruder= {
author:"Matteo Bicocchi & IgorA100",
version:"2.6.0",
defaults:{
width:350,
noFlap:false,
positionFixed:true,
/*+*/ selectorResponsiveBlock: null,
/*+*/ attachToParentSide:"left",
/*+*/ bindToButtonByHeight: null,
/*+*/ extruderParentElement: null,
sensibility:800,
position:"top",
accordionPanels:true,
/*~*/ top:"auto", // =topFlap. For compatibility with the previous version
/*+*/ topFlap:"auto",
/*+*/ topBlock:0,
extruderOpacity:1,
zIndex:'max',
flapMargin:35,
textOrientation:"bt", // or "tb" (top-bottom or bottom-top)
onExtOpen:function(){},
onExtContentLoad:function(){},
onExtClose:function(){},
hidePanelsOnClose:true,
closeOnClick:true,
closeOnExternalClick:true,
autoCloseTime:0,
autoOpenTime:0,
slideTimer:300
},
buildMbExtruder: function(options){
return this.each (function (){
this.options = {};
$.extend (this.options, $.mbExtruder.defaults);
$.extend (this.options, options);
this.idx=document.extruder.idx;
document.extruder.idx++;
var extruder,extruderContent,wrapper,extruderStyle,wrapperStyle,txt,closeTimer,openTimer;
extruder= $(this);
extruderContent=extruder.html();
/*+*/
if (this.options.top) this.options.topFlap = this.options.top;
if (this.options.width > window.innerWidth) {
this.options.width = window.innerWidth;
}
/*-*/
extruder.css("zIndex",100);
var isVertical = this.options.position=="left" || this.options.position=="right";
var extW= isVertical?1: this.options.width;
/*+*/ if (this.options.selectorResponsiveBlock) {
/*+*/ var c= $("<div/>").addClass("extruder-content").css({width:extW, display:"none"});
} else {
var c= $("<div/>").addClass("extruder-content").css({overflow:"hidden", width:extW, display:"none"});
}
c.append(extruderContent);
extruder.html(c);
var position=this.options.positionFixed?"fixed":"absolute";
extruder.addClass("extruder");
extruder.addClass(this.options.position);
var isHorizontal = this.options.position=="top" || this.options.position=="bottom";
extruderStyle =
this.options.position=="top" ?
{position:position,top:0,left:"50%",marginLeft:-this.options.width/2,width:this.options.width}:
this.options.position=="bottom" ?
{position:position,bottom:0,left:"50%",marginLeft:-this.options.width/2,width:this.options.width}:
this.options.position=="left" ?
{position:position,top:this.options.topBlock,left:0,width:1}:
{position:position,top:this.options.topBlock,right:0,width:1};
extruder.css(extruderStyle);
extruder.css({opacity:this.options.extruderOpacity});
extruder.wrapInner("<div class='extruder-wrapper'></div>");
wrapper= extruder.find(".extruder-wrapper");
wrapperStyle={position:"absolute", width:isVertical?1:this.options.width};
wrapper.css(wrapperStyle);
if (isHorizontal){
this.options.position=="top"? document.extruder.top++ : document.extruder.bottom++;
if (document.extruder.top >1 || document.extruder.bottom>1){
alert("more than 1 mb.extruder on top or bottom is not supported jet... hope soon!");
return;
}
}
if ($.metadata){
$.metadata.setType("class");
if (extruder.metadata().title) extruder.attr("extTitle",extruder.metadata().title);
if (extruder.metadata().url) extruder.attr("extUrl",extruder.metadata().url);
if (extruder.metadata().data) extruder.attr("extData",extruder.metadata().data);
}
var flapFooter=$("<div class='footer'/>");
var flap=$("<div class='flap'><span class='flapLabel'/></div>");
if (!this.options.noFlap) { /*+*/
if (document.extruder.bottom){
wrapper.prepend(flapFooter);
wrapper.prepend(flap);
}else{
wrapper.append(flapFooter);
wrapper.append(flap);
}
}
txt=extruder.attr("extTitle")?extruder.attr("extTitle"): "";
var flapLabel = extruder.find(".flapLabel");
flapLabel.text(txt);
if(isVertical){
flapLabel.html(txt).css({whiteSpace:"noWrap"});
var orientation = this.options.textOrientation == "tb";
/*+*/ flapLabel.css({transform: "rotate(180deg)", writingMode: "vertical-rl"});
}else{
flapLabel.html(txt).css({whiteSpace:"noWrap"});
}
if (extruder.attr("extUrl")){
extruder.setMbExtruderContent({
url:extruder.attr("extUrl"),
data:extruder.attr("extData"),
callback: function(){
if (extruder.get(0).options.onExtContentLoad) extruder.get(0).options.onExtContentLoad();
}
})
}else{
/*+*/
const width = (this.options.noFlap) ? extruder.get(0).options.width : extruder.get(0).options.width-20;
var container=$("<div>").addClass("text").css({width:width, overflowY:"auto"});
c.wrapInner(container);
extruder.setExtruderVoicesAction();
}
flap.on("click",function(){
if (!extruder.attr("isOpened")){
extruder.openMbExtruder();
}else{
extruder.closeMbExtruder();
}
}).on("mouseenter",function(){
if(extruder.get(0).options.autoOpenTime>0){
openTimer=setTimeout(function(){
extruder.openMbExtruder();
$(document).one("click.extruder"+extruder.get(0).idx,function(){extruder.closeMbExtruder();});
},extruder.get(0).options.autoOpenTime);
}
}).on("mouseleave",function(){
clearTimeout(openTimer);
});
c.on("mouseleave", function(e){
if(extruder.get(0).options.closeOnExternalClick){
//Chrome bug: FORMELEMENT fire mouseleave event.
if(!$(e.target).parents().is(".text") && !$(e.target).is("select"))
$(document).one("click.extruder"+extruder.get(0).idx,function(){extruder.closeMbExtruder();});
}
closeTimer=setTimeout(function(){
if(extruder.get(0).options.autoCloseTime > 0){
extruder.closeMbExtruder();
}
},extruder.get(0).options.autoCloseTime);
}).on("mouseenter", function(){
clearTimeout(closeTimer);
$(document).off("click.extruder"+extruder.get(0).idx);
});
if (isVertical){
c.css({ height:"100%"});
if(this.options.topFlap=="auto") {
flap.css({top:100+(this.options.position=="left"?document.extruder.left:document.extruder.right)});
//this.options.position=="left"?document.extruder.left+=labelH+this.options.flapMargin:document.extruder.right+= labelH+this.options.flapMargin;
}else{
flap.css({top:this.options.topFlap});
}
var clicDiv=$("<div/>").css({position:"absolute",top:0,left:0,width:"100%",height:"100%",background:"transparent"});
flap.append(clicDiv);
}
this.originalWidth = this.options.width;
$(window).on("resize",function(){ // Perhaps this can be removed?
extruder.adjustSize();
})
/*+*/
// Tracking menu scrolling
if (this.options.bindToButtonByHeight) {
document.querySelectorAll('.sidebar-layout, .sidebar-content').forEach(function(el) {
el.addEventListener('scroll', function () {
extruder.adjustSize();
});
});
// Tracking the resizing (collapse/expand) of the menu
const observerLeftSidebar = new window.ResizeObserver(entries => {
extruder.adjustSize();
})
if (this.options.extruderParentElement) {
observerLeftSidebar.observe(this.options.extruderParentElement);
}
}
/*-*/
});
},
adjustSize:function(){
var $extruder = this;
var extruder = $extruder.get(0);
var isHorizontal = extruder.options.position=="top" || extruder.options.position=="bottom";
if(isHorizontal){
if( $(window).width() < extruder.options.width){
$extruder.css({width:$(window).width(), marginLeft:0, left:0});
$extruder.find(".extruder-wrapper, .extruder-content, .extruder-container").css({width:"100%"});
}else{
$extruder.css({width:extruder.options.width, left:"50%",marginLeft:-extruder.options.width/2});
$extruder.find(".extruder-wrapper, .extruder-content, .extruder-container").css({width:"100%"});
}
} else{
/*+*/
if (extruder.options.bindToButtonByHeight) {
const anchorElement = $j(extruder.options.bindToButtonByHeight);
const extWrapper = extruder.querySelector('.extruder-wrapper');
const responsiveBlock = extWrapper.querySelector(extruder.options.selectorResponsiveBlock);
const responsiveBlockHeight = (responsiveBlock) ? $(responsiveBlock)[0].clientHeight : 0;
let topPointBlock = 0;
const anchorOffsetTop = anchorElement.offset().top;
const bottom = (anchorOffsetTop > 0 ? anchorOffsetTop : 0) + $(extWrapper)[0].clientHeight; // BOTTOM of the extruder block
if (anchorOffsetTop > 0) { // verything is fine, we move our extruder block up
topPointBlock = anchorOffsetTop;
}
if (bottom > window.innerHeight) { // We've hit bottom
topPointBlock = window.innerHeight - responsiveBlockHeight;
}
if (responsiveBlock) {
const deltaH = $(extWrapper)[0].clientHeight - responsiveBlockHeight;
if ($(responsiveBlock)[0].scrollHeight > window.innerHeight - deltaH) { // Doesn't fit vertically into the window
topPointBlock = 0;
responsiveBlock.style.height = window.innerHeight - deltaH +'px';
responsiveBlock.style.overflow = 'auto';
} else {
responsiveBlock.style.height = 'auto';
responsiveBlock.style.overflow = 'visible'; // Otherwise, the Select dropdown elements will not extend beyond the block.
}
}
extruder.options.topBlock = topPointBlock;
$extruder.css({top:extruder.options.topBlock});
}
if (extruder.options.attachToParentSide == 'right') {
extruder.options.left = (extruder.options.extruderParentElement) ? $j(extruder.options.extruderParentElement)[0].clientWidth : 0;
$extruder.css({left:extruder.options.left});
}
/*-*/
if(window.innerWidth-20 < extruder.originalWidth){
extruder.options.width = window.innerWidth-20;
}else{
extruder.options.width = extruder.originalWidth;
}
if($extruder.attr("isOpened")){
$extruder.find('.extruder-content').css({width: extruder.options.width});
$extruder.find(".extruder-wrapper, .extruder-content, .extruder-container").css({width:extruder.options.width});
}
}
},
setMbExtruderContent: function(options){
this.options = {
url:false,
data:"",
callback:function(){}
};
$.extend (this.options, options);
if (!this.options.url || this.options.url.length==0){
alert("internal error: no URL to call");
return;
}
var url=this.options.url;
var data=this.options.data;
var where=$(this), voice;
var cb= this.options.callback;
var container=$("<div>").addClass("extruder-container");
container.css({width:$(this).get(0).options.width});
where.find(".extruder-content").wrapInner(container);
$.ajax({
type: "GET",
url: url,
data: data,
async:true,
dataType:"html",
success: function(html){
where.find(".extruder-container").html(html);
voice=where.find(".voice");
voice.hover(function(){$(this).addClass("hover");},function(){$(this).removeClass("hover");});
where.setExtruderVoicesAction();
if (cb) {
setTimeout(function(){cb();},100);
}
}
});
},
openMbExtruder:function(c){
var extruder= $(this);
extruder.adjustSize();
extruder.attr("isOpened",true);
$(document).off("click.extruder"+extruder.get(0).idx);
var opt= extruder.get(0).options;
extruder.addClass("isOpened");
var position = opt.position;
extruder.mb_bringToFront(opt.zIndex);
if (position=="top" || position=="bottom"){
/*+*/ extruder.css("left",opt.left);
extruder.find('.extruder-content').slideDown( opt.slideTimer);
if(opt.onExtOpen) opt.onExtOpen();
}else{
/*+*/ extruder.css("top",opt.topBlock);
extruder.find('.extruder-wrapper').css({width:""});
extruder.find('.extruder-content').css({display:"block"});
extruder.find('.extruder-content').animate({ width: opt.width}, opt.slideTimer,function(){
// There may be a negative impact, but it hasn't shown up yet...
extruder.find(".extruder-container").css({width: opt.width});
});
if(opt.onExtOpen) opt.onExtOpen();
}
if (c) {
setTimeout(function(){
$(document).one("click.extruder"+extruder.get(0).idx,function(){extruder.closeMbExtruder();});
},100);
}
if (opt.bindToButtonByHeight) {
setTimeout(function(){
extruder.adjustSize();
},100);
}
},
closeMbExtruder:function(){
var extruder= $(this);
extruder.removeAttr("isOpened");
var opt= extruder.get(0).options;
if (!opt) return;
extruder.removeClass("isOpened");
$(document).off("click.extruder"+extruder.get(0).idx);
extruder.css("opacity",opt.extruderOpacity);
if(opt.hidePanelsOnClose) extruder.hidePanelsOnClose();
if (opt.position=="top" || opt.position=="bottom"){
extruder.find('.extruder-content').slideUp(opt.slideTimer);
if(opt.onExtClose) opt.onExtClose();
}else if (opt.position=="left" || opt.position=="right"){
extruder.find('.extruder-content').animate({ width: 1 }, opt.slideTimer,function(){
// There may be a negative impact, but it hasn't shown up yet...
extruder.find('.extruder-wrapper').css({width:1});
extruder.find('.extruder-content').css({display:"none"});
if(opt.onExtClose) opt.onExtClose();
});
}
}
};
jQuery.fn.mb_bringToFront = function(zIndex){
if (zIndex == 'max') {
var zi=10;
$('*').each(function() {
if($(this).css("position")=="absolute" || $(this).css("position")=="fixed"){
const cur = parseInt($(this).css('zIndex'));
zi = cur > zi ? cur : zi;
}
});
$(this).css('zIndex',zi+=1);
return zi;
} else {
$(this).css('zIndex',zIndex);
return zIndex;
}
};
/*
* EXTRUDER CONTENT
*/
$.fn.setExtruderVoicesAction=function(){
var extruder=$(this);
var opt=extruder.get(0).options;
var voices= $(this).find(".voice");
voices.each(function(){
var voice=$(this);
if ($.metadata){
$.metadata.setType("class");
if (voice.metadata().panel) voice.attr("panel",voice.metadata().panel);
if (voice.metadata().data) voice.attr("data",voice.metadata().data);
if (voice.metadata().disabled) voice.attr("setDisabled", voice.metadata().disabled);
}
if (voice.attr("setDisabled"))
voice.disableExtruderVoice();
if (voice.attr("panel") && voice.attr("panel")!="false"){
voice.append("<span class='settingsBtn'/>");
voice.find(".settingsBtn").css({opacity:.5});
voice.find(".settingsBtn").hover(
function(){
$(this).css({opacity:1});
},
function(){
$(this).not(".sel").css({opacity:.5});
}).click(function(){
if ($(this).parents().hasClass("sel")){
if(opt.accordionPanels)
extruder.hidePanelsOnClose();
else
$(this).closePanel();
return;
}
if(opt.accordionPanels){
extruder.find(".optionsPanel").slideUp(400,function(){$(this).remove();});
voices.removeClass("sel");
voices.find(".settingsBtn").removeClass("sel").css({opacity:.5});
}
var content=$("<div class='optionsPanel'></div>");
voice.after(content);
$.ajax({
type: "GET",
url: voice.attr("panel"),
data: voice.attr("data"),
async:true,
dataType:"html",
success: function(html){
var c= $(html);
content.html(c);
content.children().not(".text")
.addClass("panelVoice")
.click(function(){
if(opt.closeOnClick)
extruder.closeMbExtruder();
});
content.slideDown(400);
}
});
voice.addClass("sel");
voice.find(".settingsBtn").addClass("sel").css({opacity:1});
});
}
if (voice.find("a").length==0 && voice.attr("panel")){
voice.find(".label").not(".disabled").css("cursor","pointer").click(function(){
voice.find(".settingsBtn").click();
});
}
if ((!voice.attr("panel") || voice.attr("panel")=="false" ) && (!voice.attr("setDisabled") || voice.attr("setDisabled")!="true")){
voice.find(".label").click(function(){
extruder.hidePanelsOnClose();
if(opt.closeOnClick)
extruder.closeMbExtruder();
});
}
});
};
$.fn.disableExtruderVoice=function(){
var voice=$(this);
var label = voice.find(".label");
voice.removeClass("sel");
voice.next(".optionsPanel").slideUp(400,function(){$(this).remove();});
voice.attr("setDisabled",true);
label.css("opacity",.4);
voice.hover(function(){$(this).removeClass("hover");},function(){$(this).removeClass("hover");});
label.addClass("disabled").css("cursor","default");
voice.find(".settingsBtn").hide();
voice.on("click",function(event){
event.stopPropagation();
return false;
});
};
$.fn.enableExtruderVoice=function(){
var voice=$(this);
voice.attr("setDisabled",false);
voice.find(".label").css("opacity",1);
voice.find(".label").removeClass("disabled").css("cursor","pointer");
voice.off("click");
voice.find(".settingsBtn").show();
};
$.fn.hidePanelsOnClose=function(){
var voices= $(this).find(".voice");
$(this).find(".optionsPanel").slideUp(400,function(){$(this).remove();});
voices.removeClass("sel");
voices.find(".settingsBtn").removeClass("sel").css("opacity",.5);
};
$.fn.openPanel=function(){
var voice=$(this).hasClass("voice") ? $(this) : $(this).find(".voice");
voice.each(function(){
if($(this).hasClass("sel")) return;
$(this).find(".settingsBtn").click();
})
};
$.fn.closePanel=function(){
var voice=$(this).hasClass("voice") ? $(this) : $(this).parent(".voice");
voice.next(".optionsPanel").slideUp(400,function(){$(this).remove();});
voice.removeClass("sel");
$(this).removeClass("sel").css("opacity",.5);
};
$.fn.buildMbExtruder=$.mbExtruder.buildMbExtruder;
$.fn.setMbExtruderContent=$.mbExtruder.setMbExtruderContent;
$.fn.closeMbExtruder=$.mbExtruder.closeMbExtruder;
$.fn.openMbExtruder=$.mbExtruder.openMbExtruder;
$.fn.adjustSize=$.mbExtruder.adjustSize;
})(jQuery);

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Mohamed AZOUAOUI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,246 @@
# Pro sidebar template
Responsive layout with advanced sidebar menu built with SCSS and vanilla Javascript
## Demo
[See it live](https://azouaoui-med.github.io/pro-sidebar-template)
## Screenshot
![Pro Sidebar](https://user-images.githubusercontent.com/25878302/215290325-e5c6043b-4411-404c-83b8-dcc227df70ad.jpg)
## Installation
```
# clone the repo
$ git clone https://github.com/azouaoui-med/pro-sidebar-template.git my-project
# go into app's directory
$ cd my-project
# install app's dependencies
$ yarn install
```
## Usage
```
# serve with hot reload at localhost:8080
$ yarn start
# build app for production
$ yarn build
```
## Documentation
### Layout
The layout for this template is based on [css pro layout](https://github.com/azouaoui-med/css-pro-layout) package, please refer to the [docs](https://azouaoui-med.github.io/css-pro-layout/) for more information
### Sidebar
Responsive navigation element for building vertical menu items
**Sidebar Image**
Adding background image requires adding `.has-bg-image` class to sidebar component, and the image needs to be inside `.image-wrapper` component
```html
<aside id="sidebar" class="sidebar break-point-lg has-bg-image">
<div class="image-wrapper">
<img src="assets/images/sidebar-bg.jpg" alt="sidebar background" />
</div>
<div class="sidebar-layout">
<!-- Content here -->
</div>
</aside>
```
### Sidebar Layout
Sidebar comes with layout support for better organization of the inner structure
```html
<aside id="sidebar" class="sidebar break-point-lg">
<div class="sidebar-layout">
<div class="sidebar-header">
<!-- Header content here -->
</div>
<div class="sidebar-content">
<!-- Content here -->
</div>
<div class="sidebar-footer">
<!-- Footer content here -->
</div>
</div>
</aside>
```
More on the sidebar [here](https://azouaoui-med.github.io/css-pro-layout/docs/reference/sidebar)
### Menu
Wrapper component that groups all menu items
```html
<nav class="menu">
<!-- Content here -->
</nav>
```
**Open current submenu**
Use `.open-current-submenu` to enable opening only one submenu component at a time
```html
<nav class="menu open-current-submenu">
<!-- Content here -->
</nav>
```
**Icon shape**
A set of classes are provided to restyle menu icons
- `.icon-shape-square`
- `.icon-shape-rounded`
- `.icon-shape-circle`
```html
<nav class="menu icon-shape-circle">
<!-- Content here -->
</nav>
```
### Menu Item
Building menu item requires having `.menu-item` class in the wrapper and `.menu-title` for the text
```html
<nav class="menu">
<ul>
<li class="menu-item">
<a href="#">
<span class="menu-title">menu text</span>
</a>
</li>
<!-- More menu items -->
</ul>
</nav>
```
**Menu Icon**
Use `.menu-icon` to add an icon to menu items
```html
<nav class="menu">
<ul>
<li class="menu-item">
<a href="#">
<span class="menu-icon">
<i class="ri-service-fill"></i>
</span>
<span class="menu-title">menu text</span>
</a>
</li>
<!-- More menu items -->
</ul>
</nav>
```
**Prefix & Suffix**
Menu item also supports having prefix and suffix components
```html
<nav class="menu">
<ul>
<li class="menu-item">
<a href="#">
<span class="menu-icon">
<i class="ri-service-fill"></i>
</span>
<span class="menu-prefix">prefix</span>
<span class="menu-title">menu text</span>
<span class="menu-suffix">suffix</span>
</a>
</li>
<!-- More menu items -->
</ul>
</nav>
```
### Sub Menu
Add `.sub-menu` class to menu item and create a wrapper component with `sub-menu-list` class to group sub menu items
> Its possible to have unlimited nesting menu items
```html
<nav class="menu">
<ul>
<li class="menu-item sub-menu">
<a href="#">
<span class="menu-title">menu text</span>
</a>
<div class="sub-menu-list">
<li class="menu-item">
<a href="#">
<span class="menu-title">sub menu text</span>
</a>
</li>
<!-- More sub menu items -->
</div>
</li>
<!-- More menu items -->
</ul>
</nav>
```
**Open default**
Use `.open` class to have sub menu expanded by default
```html
<nav class="menu">
<ul>
<li class="menu-item sub-menu open">
<a href="#">
<span class="menu-title">menu text</span>
</a>
<div class="sub-menu-list">
<li class="menu-item">
<a href="#">
<span class="menu-title">sub menu text</span>
</a>
</li>
<!-- More sub menu items -->
</div>
</li>
<!-- More menu items -->
</ul>
</nav>
```
### Customization
Update SCSS variables in `src/styles/_variables.scss` to customize the template
```scss
$text-color: #b3b8d4;
$secondary-text-color: #dee2ec;
$bg-color: #0c1e35;
$secondary-bg-color: #0b1a2c;
$border-color: rgba(#535d7d, 0.3);
$sidebar-header-height: 64px;
$sidebar-footer-height: 64px;
```
## License
This code is released under the [MIT](https://github.com/azouaoui-med/pro-sidebar-template/blob/gh-pages/LICENSE) license.

View File

@@ -0,0 +1,6 @@
Source: https://github.com/azouaoui-med/pro-sidebar-template
Since the source has not changed since the beginning of 2023, IgorA100 decided to make small local changes to the code.
Make all changes ONLY to files in the ./src folder
Then build with the ./build.sh command
The ./dist folder will contain the minimized files for PRODUCT mode
The ./dist_dev folder will contain the files for DEVELOP mode

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,5 @@
#!/bin/bash
yarn install
yarn build
yarn build:dev

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,763 @@
@import url(https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap);
/*@import 'css-pro-layout/dist/scss/css-pro-layout.scss';*/
.layout-main {
min-height: 100%;
display: flex;
flex-direction: column;
position: relative;
flex-grow: 1;
}
.layout-main.has-sidebar {
flex-direction: row;
}
.layout-main .header {
transition: width, 0.3s;
height: 64px;
min-height: 64px;
position: relative;
}
.layout-main .sidebar-main {
width: 250px;
min-width: 250px;
transition: width, left, right, 0.3s;
}
.layout-main .sidebar-main.collapsed {
width: 80px;
min-width: 80px;
}
@media (max-width: 480px) {
.layout-main .sidebar-main.break-point-xs {
position: fixed;
left: -250px;
height: 100%;
top: 0;
z-index: 100;
}
.layout-main .sidebar-main.break-point-xs.collapsed {
left: -80px;
}
.layout-main .sidebar-main.break-point-xs.toggled {
left: 0;
}
.layout-main .sidebar-main.break-point-xs.toggled ~ .overlay {
display: block;
}
.layout-main .sidebar-main.break-point-xs ~ .layout-main .header {
width: 100% !important;
transition: none;
}
}
@media (max-width: 576px) {
.layout-main .sidebar-main.break-point-sm {
position: fixed;
left: -250px;
height: 100%;
top: 0;
z-index: 100;
}
.layout-main .sidebar-main.break-point-sm.collapsed {
left: -80px;
}
.layout-main .sidebar-main.break-point-sm.toggled {
left: 0;
}
.layout-main .sidebar-main.break-point-sm.toggled ~ .overlay {
display: block;
}
.layout-main .sidebar-main.break-point-sm ~ .layout-main .header {
width: 100% !important;
transition: none;
}
}
@media (max-width: 768px) {
.layout-main .sidebar-main.break-point-md {
position: fixed;
left: -250px;
height: 100%;
top: 0;
z-index: 100;
}
.layout-main .sidebar-main.break-point-md.collapsed {
left: -80px;
}
.layout-main .sidebar-main.break-point-md.toggled {
left: 0;
}
.layout-main .sidebar-main.break-point-md.toggled ~ .overlay {
display: block;
}
.layout-main .sidebar-main.break-point-md ~ .layout-main .header {
width: 100% !important;
transition: none;
}
}
@media (max-width: 992px) {
.layout-main .sidebar-main.break-point-lg {
position: fixed;
left: -250px;
height: 100%;
top: 0;
z-index: 100;
}
.layout-main .sidebar-main.break-point-lg.collapsed {
left: -80px;
}
.layout-main .sidebar-main.break-point-lg.toggled {
left: 0;
}
.layout-main .sidebar-main.break-point-lg.toggled ~ .overlay {
display: block;
}
.layout-main .sidebar-main.break-point-lg ~ .layout-main .header {
width: 100% !important;
transition: none;
}
}
@media (max-width: 1200px) {
.layout-main .sidebar-main.break-point-xl {
position: fixed;
left: -250px;
height: 100%;
top: 0;
z-index: 100;
}
.layout-main .sidebar-main.break-point-xl.collapsed {
left: -80px;
}
.layout-main .sidebar-main.break-point-xl.toggled {
left: 0;
}
.layout-main .sidebar-main.break-point-xl.toggled ~ .overlay {
display: block;
}
.layout-main .sidebar-main.break-point-xl ~ .layout-main .header {
width: 100% !important;
transition: none;
}
}
@media (max-width: 1600px) {
.layout-main .sidebar-main.break-point-xxl {
position: fixed;
left: -250px;
height: 100%;
top: 0;
z-index: 100;
}
.layout-main .sidebar-main.break-point-xxl.collapsed {
left: -80px;
}
.layout-main .sidebar-main.break-point-xxl.toggled {
left: 0;
}
.layout-main .sidebar-main.break-point-xxl.toggled ~ .overlay {
display: block;
}
.layout-main .sidebar-main.break-point-xxl ~ .layout-main .header {
width: 100% !important;
transition: none;
}
}
.layout-main .footer {
height: 64px;
min-height: 64px;
}
.layout-main .content {
flex-grow: 1;
}
.layout-main .overlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.3);
z-index: 99;
display: none;
}
.layout-main .sidebar-toggler {
display: none;
}
@media (max-width: 480px) {
.layout-main .sidebar-toggler.break-point-xs {
display: initial;
}
}
@media (max-width: 576px) {
.layout-main .sidebar-toggler.break-point-sm {
display: initial;
}
}
@media (max-width: 768px) {
.layout-main .sidebar-toggler.break-point-md {
display: initial;
}
}
@media (max-width: 992px) {
.layout-main .sidebar-toggler.break-point-lg {
display: initial;
}
}
@media (max-width: 1200px) {
.layout-main .sidebar-toggler.break-point-xl {
display: initial;
}
}
@media (max-width: 1600px) {
.layout-main .sidebar-toggler.break-point-xxl {
display: initial;
}
}
.layout-main.fixed-sidebar {
height: 100%;
}
.layout-main.fixed-sidebar .sidebar {
height: 100%;
overflow: auto;
}
.layout-main.fixed-sidebar .sidebar ~ .layout-main {
height: 100%;
overflow: auto;
}
.layout-main.fixed-header .header {
position: fixed;
width: 100%;
z-index: 2;
}
.layout-main.fixed-header .header ~ .layout-main,
.layout-main.fixed-header .header ~ .content {
margin-top: 64px;
}
.layout-main.fixed-header.fixed-sidebar .header {
width: calc(100% - 250px);
}
.layout-main.fixed-header.fixed-sidebar .sidebar.collapsed ~ .layout-main .header {
width: calc(100% - 80px);
}
.layout-main.rtl {
direction: rtl;
}
@media (max-width: 480px) {
.layout-main.rtl .sidebar.break-point-xs {
left: auto;
right: -250px;
}
.layout-main.rtl .sidebar.break-point-xs.collapsed {
left: auto;
right: -80px;
}
.layout-main.rtl .sidebar.break-point-xs.toggled {
left: auto;
right: 0;
}
}
@media (max-width: 576px) {
.layout-main.rtl .sidebar.break-point-sm {
left: auto;
right: -250px;
}
.layout-main.rtl .sidebar.break-point-sm.collapsed {
left: auto;
right: -80px;
}
.layout-main.rtl .sidebar.break-point-sm.toggled {
left: auto;
right: 0;
}
}
@media (max-width: 768px) {
.layout-main.rtl .sidebar.break-point-md {
left: auto;
right: -250px;
}
.layout-main.rtl .sidebar.break-point-md.collapsed {
left: auto;
right: -80px;
}
.layout-main.rtl .sidebar.break-point-md.toggled {
left: auto;
right: 0;
}
}
@media (max-width: 992px) {
.layout-main.rtl .sidebar.break-point-lg {
left: auto;
right: -250px;
}
.layout-main.rtl .sidebar.break-point-lg.collapsed {
left: auto;
right: -80px;
}
.layout-main.rtl .sidebar.break-point-lg.toggled {
left: auto;
right: 0;
}
}
@media (max-width: 1200px) {
.layout-main.rtl .sidebar.break-point-xl {
left: auto;
right: -250px;
}
.layout-main.rtl .sidebar.break-point-xl.collapsed {
left: auto;
right: -80px;
}
.layout-main.rtl .sidebar.break-point-xl.toggled {
left: auto;
right: 0;
}
}
@media (max-width: 1600px) {
.layout-main.rtl .sidebar.break-point-xxl {
left: auto;
right: -250px;
}
.layout-main.rtl .sidebar.break-point-xxl.collapsed {
left: auto;
right: -80px;
}
.layout-main.rtl .sidebar.break-point-xxl.toggled {
left: auto;
right: 0;
}
}
/*@import '../../public//assets//remixicon/remixicon.css';*/
/*
.layout {
z-index: 1;
.header {
display: flex;
align-items: center;
padding: 20px;
}
.content {
padding: 12px 50px;
display: flex;
flex-direction: column;
}
.footer {
text-align: center;
margin-top: auto;
margin-bottom: 20px;
padding: 20px;
}
}
*/
.sidebar-main {
color: #44b8eb;
position: relative;
height: 100%;
max-height: 100%;
overflow: hidden !important;
}
.sidebar-main .image-wrapper {
overflow: hidden;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
display: none;
}
.sidebar-main .image-wrapper > img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.sidebar-main.has-bg-image .sidebar-layout {
background-color: rgba(12, 30, 53, 0.85);
}
.sidebar-main.has-bg-image .image-wrapper {
display: block;
opacity: 0.8;
}
.sidebar-main .sidebar-layout {
height: 100%;
max-height: 100%;
min-height: 100%;
overflow-x: hidden;
overflow-y: auto;
display: flex;
flex-direction: column;
position: relative;
background-color: #0c1e35;
z-index: 2;
opacity: 0.85;
}
.sidebar-main .sidebar-layout::-webkit-scrollbar-thumb {
border-radius: 4px;
}
.sidebar-main .sidebar-layout:hover::-webkit-scrollbar-thumb {
background-color: rgb(26.1230769231, 65.3076923077, 115.3769230769);
}
.sidebar-main .sidebar-layout::-webkit-scrollbar {
width: 6px;
background-color: #0c1e35;
}
.sidebar-main .sidebar-layout .sidebar-header {
height: 40px;
min-height: 40px;
display: flex;
align-items: center;
padding: 8px 22px;
}
.sidebar-main .sidebar-layout .sidebar-header > span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.sidebar-main .sidebar-layout .sidebar-content {
flex-grow: 1;
padding: 10px 0;
}
.sidebar-main .sidebar-layout .sidebar-footer {
height: 230px;
min-height: 230px;
display: flex;
align-items: center;
padding: 0 20px;
}
.sidebar-main .sidebar-layout .sidebar-footer > span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
@keyframes swing {
0%, 30%, 50%, 70%, 100% {
transform: rotate(0deg);
}
10% {
transform: rotate(10deg);
}
40% {
transform: rotate(-10deg);
}
60% {
transform: rotate(5deg);
}
80% {
transform: rotate(-5deg);
}
}
.layout-main .sidebar-main .sidebar-main-menu ul {
list-style-type: none;
padding: 0;
margin: 0;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-header {
font-weight: 600;
padding: 10px 25px;
font-size: 0.8em;
letter-spacing: 2px;
transition: opacity 0.3s;
opacity: 0.5;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item a {
display: flex;
align-items: center;
height: 38px;
padding: 0 20px;
color: #44b8eb;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item a .menu-icon {
font-size: 1.2rem;
width: 35px;
min-width: 35px;
height: 35px;
line-height: 35px;
text-align: center;
display: inline-block;
margin-right: 10px;
border-radius: 2px;
transition: color 0.3s;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item a .menu-title {
font-size: 0.9em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 1;
transition: color 0.3s;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item a .menu-prefix,
.layout-main .sidebar-main .sidebar-main-menu .menu-item a .menu-suffix {
display: inline-block;
padding: 5px;
opacity: 1;
transition: opacity 0.3s;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item a:hover .menu-title {
color: #dee2ec;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item a:hover .menu-icon {
color: #dee2ec;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item a:hover .menu-icon i {
animation: swing ease-in-out 0.5s 1 alternate;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item a:hover::after {
border-color: #dee2ec !important;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item.sub-menu {
position: relative;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item.sub-menu > a::after {
content: "";
transition: transform 0.3s;
border-right: 2px solid currentcolor;
border-bottom: 2px solid currentcolor;
width: 5px;
height: 5px;
transform: rotate(-45deg);
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item.sub-menu > .sub-menu-list {
padding-left: 20px;
display: none;
overflow: hidden;
z-index: 999;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item.sub-menu.open > a {
color: #dee2ec;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item.sub-menu.open > a::after {
transform: rotate(45deg);
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item.active > a .menu-title {
color: #dee2ec;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item.active > a::after {
border-color: #dee2ec;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item.active > a .menu-icon {
color: #dee2ec;
}
.layout-main .sidebar-main .sidebar-main-menu > ul > .sub-menu > .sub-menu-list {
background-color: #0b1a2c;
}
.layout-main .sidebar-main .sidebar-main-menu.icon-shape-circle .menu-item a .menu-icon, .layout-main .sidebar-main .sidebar-main-menu.icon-shape-rounded .menu-item a .menu-icon, .layout-main .sidebar-main .sidebar-main-menu.icon-shape-square .menu-item a .menu-icon {
background-color: #0b1a2c;
}
.layout-main .sidebar-main .sidebar-main-menu.icon-shape-circle .menu-item a .menu-icon {
border-radius: 50%;
}
.layout-main .sidebar-main .sidebar-main-menu.icon-shape-rounded .menu-item a .menu-icon {
border-radius: 4px;
}
.layout-main .sidebar-main .sidebar-main-menu.icon-shape-square .menu-item a .menu-icon {
border-radius: 0;
}
.layout-main .sidebar-main:not(.collapsed) .sidebar-main-menu > ul > .menu-item.sub-menu > .sub-menu-list {
visibility: visible !important;
position: static !important;
transform: translate(0, 0) !important;
}
.layout-main .sidebar-main.collapsed .sidebar-main-menu > ul > .menu-header {
opacity: 0;
}
.layout-main .sidebar-main.collapsed .sidebar-main-menu > ul > .menu-item > a .menu-prefix,
.layout-main .sidebar-main.collapsed .sidebar-main-menu > ul > .menu-item > a .menu-suffix {
opacity: 0;
}
.layout-main .sidebar-main.collapsed .sidebar-main-menu > ul > .menu-item.sub-menu > a::after {
content: "";
width: 5px;
height: 5px;
background-color: currentcolor;
border-radius: 50%;
display: inline-block;
position: absolute;
right: 10px;
top: 50%;
border: none;
transform: translateY(-50%);
}
.layout-main .sidebar-main.collapsed .sidebar-main-menu > ul > .menu-item.sub-menu > a:hover::after {
background-color: #dee2ec;
}
.layout-main .sidebar-main.collapsed .sidebar-main-menu > ul > .menu-item.sub-menu > .sub-menu-list {
transition: none !important;
width: 200px;
margin-left: 3px !important;
border-radius: 4px;
display: block !important;
}
.layout-main .sidebar-main.collapsed .sidebar-main-menu > ul > .menu-item.active > a::after {
background-color: #dee2ec;
}
.layout-main .sidebar-main.has-bg-image .sidebar-main-menu.icon-shape-circle .menu-item a .menu-icon, .layout-main .sidebar-main.has-bg-image .sidebar-main-menu.icon-shape-rounded .menu-item a .menu-icon, .layout-main .sidebar-main.has-bg-image .sidebar-main-menu.icon-shape-square .menu-item a .menu-icon {
background-color: rgba(11, 26, 44, 0.6);
}
.layout-main .sidebar-main.has-bg-image:not(.collapsed) .sidebar-main-menu > ul > .sub-menu > .sub-menu-list {
background-color: rgba(11, 26, 44, 0.6);
}
.layout-main.rtl .sidebar-main .sidebar-main-menu .menu-item a .menu-icon {
margin-left: 10px;
margin-right: 0;
}
.layout-main.rtl .sidebar-main .sidebar-main-menu .menu-item.sub-menu > a::after {
transform: rotate(135deg);
}
.layout-main.rtl .sidebar-main .sidebar-main-menu .menu-item.sub-menu > .sub-menu-list {
padding-left: 0;
padding-right: 20px;
}
.layout-main.rtl .sidebar-main .sidebar-main-menu .menu-item.sub-menu.open > a::after {
transform: rotate(45deg);
}
.layout-main.rtl .sidebar-main.collapsed .sidebar-main-menu > ul > .menu-item.sub-menu a::after {
right: auto;
left: 10px;
}
.layout-main.rtl .sidebar-main.collapsed .sidebar-main-menu > ul > .menu-item.sub-menu > .sub-menu-list {
margin-left: -3px !important;
}
/*
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
font-family: 'Poppins', sans-serif;
color: #3f4750;
font-size: 0.9rem;
}
*/
a {
text-decoration: none;
}
@media (max-width: 992px) {
#btn-collapse {
display: none;
}
}
.layout-main .sidebar-main .pro-sidebar-logo {
color: #ffa801;
display: flex;
align-items: center;
}
.layout-main .sidebar-main .pro-sidebar-logo > div {
width: 45px;
min-width: 45px;
height: 25px;
min-height: 25px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
color: white;
font-size: 22px;
font-weight: 700;
background-color: #ffa801;
margin-right: 10px;
margin-left: -10px;
}
.layout-main .sidebar-main .pro-sidebar-logo > h5 {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 22px;
line-height: 25px;
transition: opacity 0.3s;
opacity: 1;
margin: 0 17px;
}
.layout-main .sidebar-main .footer-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
/* font-size: 0.8em; */
padding: 20px 0;
border-radius: 8px;
width: 180px;
min-width: 190px;
margin: 0 auto;
background-color: #162d4a;
}
.layout-main .sidebar-main .footer-box img.react-logo {
width: 40px;
height: 40px;
margin-bottom: 10px;
}
.layout-main .sidebar-main .footer-box a {
color: #fff;
font-weight: 600;
margin-bottom: 10px;
}
.layout-main .sidebar-main .sidebar-collapser {
transition: left, right, 0.3s;
position: fixed;
left: calc(250px - 20px);
top: 40px;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #00829f;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2em;
transform: translateX(50%);
z-index: 111;
cursor: pointer;
color: white;
box-shadow: 1px 1px 4px #0c1e35;
}
.layout-main .sidebar-main.has-bg-image .footer-box {
/* background-color: rgba(#162d4a, 0.7); */
background-color: #34495e;
}
.layout-main .sidebar-main.collapsed .pro-sidebar-logo > h5 {
opacity: 0;
}
.layout-main .sidebar-main.collapsed .footer-box {
display: none;
}
.layout-main .sidebar-main.collapsed .sidebar-collapser {
left: calc(80px - 20px);
}
.layout-main .sidebar-main.collapsed .sidebar-collapser i {
transform: rotate(180deg);
}
.badge {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #6c757d;
}
.badge.primary {
background-color: #ab2dff;
}
.badge.secondary {
background-color: #079b0b;
}
.sidebar-toggler {
position: fixed;
right: 20px;
top: 20px;
}
.social-links a {
margin: 0 10px;
color: #3f4750;
}
/*# sourceMappingURL=main.css.map*/

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
module.exports = {
plugins: ['postcss-preset-env'],
};

View File

@@ -0,0 +1,99 @@
import './styles/styles.scss';
import { slideToggle, slideUp, slideDown } from './libs/slide';
import {
ANIMATION_DURATION,
FIRST_SUB_MENUS_BTN,
INNER_SUB_MENUS_BTN,
SIDEBAR_EL,
} from './libs/constants';
import Poppers from './libs/poppers';
const PoppersInstance = new Poppers();
/**
* wait for the current animation to finish and update poppers position
*/
const updatePoppersTimeout = () => {
setTimeout(() => {
PoppersInstance.updatePoppers();
}, ANIMATION_DURATION);
};
/**
* sidebar collapse handler
*/
const btnCollapse = document.getElementById('btn-collapse');
if (btnCollapse) {
btnCollapse.addEventListener('click', () => {
SIDEBAR_EL.classList.toggle('collapsed');
PoppersInstance.closePoppers();
if (SIDEBAR_EL.classList.contains('collapsed'))
FIRST_SUB_MENUS_BTN.forEach((element) => {
element.parentElement.classList.remove('open');
});
updatePoppersTimeout();
});
}
/**
* sidebar toggle handler (on break point )
*/
const btnToggle = document.getElementById('btn-toggle');
if (btnToggle) {
btnToggle.addEventListener('click', () => {
SIDEBAR_EL.classList.toggle('toggled');
updatePoppersTimeout();
});
}
/**
* toggle sidebar on overlay click
*/
const btnOverlay = document.getElementById('overlay');
if (btnOverlay) {
btnOverlay.addEventListener('click', () => {
SIDEBAR_EL.classList.toggle('toggled');
});
}
const defaultOpenMenus = document.querySelectorAll('.menu-item.sub-menu.open');
defaultOpenMenus.forEach((element) => {
element.lastElementChild.style.display = 'block';
});
/**
* handle top level submenu click
*/
FIRST_SUB_MENUS_BTN.forEach((element) => {
element.addEventListener('click', () => {
if (SIDEBAR_EL.classList.contains('collapsed'))
PoppersInstance.togglePopper(element.nextElementSibling);
else {
/**
* if menu has "open-current-only" class then only one submenu opens at a time
*/
const parentMenu = element.closest('.menuLeft.open-current-submenu');
if (parentMenu)
parentMenu
.querySelectorAll(':scope > ul > .menu-item.sub-menu > a')
.forEach(
(el) =>
window.getComputedStyle(el.nextElementSibling).display !==
'none' && slideUp(el.nextElementSibling)
);
slideToggle(element.nextElementSibling);
}
});
});
/**
* handle inner submenu click
*/
INNER_SUB_MENUS_BTN.forEach((element) => {
element.addEventListener('click', () => {
slideToggle(element.nextElementSibling);
});
});

View File

@@ -0,0 +1,15 @@
export const ANIMATION_DURATION = 300;
export const SIDEBAR_EL = document.getElementById('sidebarMain');
export const SUB_MENU_ELS = document.querySelectorAll(
'.sidebar-main-menu > ul > .menu-item.sub-menu'
);
export const FIRST_SUB_MENUS_BTN = document.querySelectorAll(
'.sidebar-main-menu > ul > .menu-item.sub-menu > a'
);
export const INNER_SUB_MENUS_BTN = document.querySelectorAll(
'.sidebar-main-menu > ul > .menu-item.sub-menu .menu-item.sub-menu > a'
);

View File

@@ -0,0 +1,65 @@
import { createPopper } from '@popperjs/core';
import { SIDEBAR_EL } from './constants';
class Popper {
instance = null;
reference = null;
popperTarget = null;
constructor(reference, popperTarget) {
this.init(reference, popperTarget);
}
init(reference, popperTarget) {
this.reference = reference;
this.popperTarget = popperTarget;
this.instance = createPopper(this.reference, this.popperTarget, {
placement: 'right',
strategy: 'fixed',
resize: true,
modifiers: [
{
name: 'computeStyles',
options: {
adaptive: false,
},
},
{
name: 'flip',
options: {
fallbackPlacements: ['left', 'right'],
},
},
],
});
document.addEventListener(
'click',
(e) => this.clicker(e, this.popperTarget, this.reference),
false,
);
const ro = new ResizeObserver(() => {
this.instance.update();
});
ro.observe(this.popperTarget);
ro.observe(this.reference);
}
clicker(event, popperTarget, reference) {
if (
SIDEBAR_EL.classList.contains('collapsed') &&
!popperTarget.contains(event.target) &&
!reference.contains(event.target)
) {
this.hide();
}
}
hide() {
this.instance.state.elements.popper.style.visibility = 'hidden';
}
}
export default Popper;

View File

@@ -0,0 +1,44 @@
import { SUB_MENU_ELS } from './constants';
import Popper from './popper';
class Poppers {
subMenuPoppers = [];
constructor() {
this.init();
}
init() {
SUB_MENU_ELS.forEach((element) => {
this.subMenuPoppers.push(new Popper(element, element.lastElementChild));
this.closePoppers();
});
}
togglePopper(target) {
if (window.getComputedStyle(target).visibility === 'hidden') {
target.style.visibility = 'visible';
target.style.height = '100%';
} else {
target.style.visibility = 'hidden';
target.style.height = 0;
}
}
updatePoppers() {
this.subMenuPoppers.forEach((element) => {
if (!element.instance.state.elements.popper.parentElement.classList.contains('open')) {
element.instance.state.elements.popper.style.display = 'none';
}
element.instance.update();
});
}
closePoppers() {
this.subMenuPoppers.forEach((element) => {
element.hide();
});
}
}
export default Poppers;

View File

@@ -0,0 +1,67 @@
/* eslint-disable no-param-reassign */
/* eslint-disable no-unused-expressions */
import { ANIMATION_DURATION } from './constants';
export const slideUp = (target, duration = ANIMATION_DURATION) => {
const { parentElement } = target;
parentElement.classList.remove('open');
target.style.transitionProperty = 'height, margin, padding';
target.style.transitionDuration = `${duration}ms`;
target.style.boxSizing = 'border-box';
target.style.height = `${target.offsetHeight}px`;
target.offsetHeight;
target.style.overflow = 'hidden';
target.style.height = 0;
target.style.paddingTop = 0;
target.style.paddingBottom = 0;
target.style.marginTop = 0;
target.style.marginBottom = 0;
window.setTimeout(() => {
target.style.display = 'none';
target.style.removeProperty('height');
target.style.removeProperty('padding-top');
target.style.removeProperty('padding-bottom');
target.style.removeProperty('margin-top');
target.style.removeProperty('margin-bottom');
target.style.removeProperty('overflow');
target.style.removeProperty('transition-duration');
target.style.removeProperty('transition-property');
}, duration);
};
export const slideDown = (target, duration = ANIMATION_DURATION) => {
const { parentElement } = target;
parentElement.classList.add('open');
target.style.removeProperty('display');
let { display } = window.getComputedStyle(target);
if (display === 'none') display = 'block';
target.style.display = display;
const height = target.offsetHeight;
target.style.overflow = 'hidden';
target.style.height = 0;
target.style.paddingTop = 0;
target.style.paddingBottom = 0;
target.style.marginTop = 0;
target.style.marginBottom = 0;
target.offsetHeight;
target.style.boxSizing = 'border-box';
target.style.transitionProperty = 'height, margin, padding';
target.style.transitionDuration = `${duration}ms`;
target.style.height = `${height}px`;
target.style.removeProperty('padding-top');
target.style.removeProperty('padding-bottom');
target.style.removeProperty('margin-top');
target.style.removeProperty('margin-bottom');
window.setTimeout(() => {
//target.style.height = '100%'; // Зачем-то я хотел так сделать.....
target.style.removeProperty('height');
target.style.removeProperty('overflow');
target.style.removeProperty('transition-duration');
target.style.removeProperty('transition-property');
}, duration);
};
export const slideToggle = (target, duration = ANIMATION_DURATION) => {
if (window.getComputedStyle(target).display === 'none') return slideDown(target, duration);
return slideUp(target, duration);
};

View File

@@ -0,0 +1,22 @@
/*
.layout {
z-index: 1;
.header {
display: flex;
align-items: center;
padding: 20px;
}
.content {
padding: 12px 50px;
display: flex;
flex-direction: column;
}
.footer {
text-align: center;
margin-top: auto;
margin-bottom: 20px;
padding: 20px;
}
}
*/

View File

@@ -0,0 +1,301 @@
@keyframes swing {
0%,
30%,
50%,
70%,
100% {
transform: rotate(0deg);
}
10% {
transform: rotate(10deg);
}
40% {
transform: rotate(-10deg);
}
60% {
transform: rotate(5deg);
}
80% {
transform: rotate(-5deg);
}
}
.layout-main {
.sidebar-main {
.sidebar-main-menu {
ul {
list-style-type: none;
padding: 0;
margin: 0;
}
.menu-header {
font-weight: 600;
padding: 10px 25px;
font-size: 0.8em;
letter-spacing: 2px;
transition: opacity 0.3s;
opacity: 0.5;
}
.menu-item {
a {
display: flex;
align-items: center;
height: 38px;
padding: 0 20px;
color: $text-color;
.menu-icon {
font-size: 1.2rem;
width: 35px;
min-width: 35px;
height: 35px;
line-height: 35px;
text-align: center;
display: inline-block;
margin-right: 10px;
border-radius: 2px;
transition: color 0.3s;
i {
// display: inline-block;
}
}
.menu-title {
font-size: 0.9em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 1;
transition: color 0.3s;
}
.menu-prefix,
.menu-suffix {
display: inline-block;
padding: 5px;
opacity: 1;
transition: opacity 0.3s;
}
&:hover {
.menu-title {
color: $secondary-text-color;
}
.menu-icon {
color: $secondary-text-color;
i {
animation: swing ease-in-out 0.5s 1 alternate;
}
}
&::after {
border-color: $secondary-text-color !important;
}
}
}
&.sub-menu {
position: relative;
> a {
&::after {
content: '';
transition: transform 0.3s;
border-right: 2px solid currentcolor;
border-bottom: 2px solid currentcolor;
width: 5px;
height: 5px;
transform: rotate(-45deg);
}
}
> .sub-menu-list {
padding-left: 20px;
display: none;
overflow: hidden;
z-index: 999;
}
&.open {
> a {
color: $secondary-text-color;
&::after {
transform: rotate(45deg);
}
}
}
}
&.active {
> a {
.menu-title {
color: $secondary-text-color;
}
&::after {
border-color: $secondary-text-color;
}
.menu-icon {
color: $secondary-text-color;
}
}
}
}
> ul > .sub-menu > .sub-menu-list {
background-color: $secondary-bg-color;
}
&.icon-shape-circle,
&.icon-shape-rounded,
&.icon-shape-square {
.menu-item a .menu-icon {
background-color: $secondary-bg-color;
}
}
&.icon-shape-circle .menu-item a .menu-icon {
border-radius: 50%;
}
&.icon-shape-rounded .menu-item a .menu-icon {
border-radius: 4px;
}
&.icon-shape-square .menu-item a .menu-icon {
border-radius: 0;
}
}
&:not(.collapsed) {
.sidebar-main-menu > ul {
> .menu-item {
&.sub-menu {
> .sub-menu-list {
visibility: visible !important;
position: static !important;
transform: translate(0, 0) !important;
}
}
}
}
}
&.collapsed {
.sidebar-main-menu > ul {
> .menu-header {
opacity: 0;
}
> .menu-item {
> a {
.menu-prefix,
.menu-suffix {
opacity: 0;
}
}
&.sub-menu {
> a {
&::after {
content: '';
width: 5px;
height: 5px;
background-color: currentcolor;
border-radius: 50%;
display: inline-block;
position: absolute;
right: 10px;
top: 50%;
border: none;
transform: translateY(-50%);
}
&:hover {
&::after {
background-color: $secondary-text-color;
}
}
}
> .sub-menu-list {
transition: none !important;
width: 200px;
margin-left: 3px !important;
border-radius: 4px;
display: block !important;
}
}
&.active {
> a {
&::after {
background-color: $secondary-text-color;
}
}
}
}
}
}
&.has-bg-image {
.sidebar-main-menu {
&.icon-shape-circle,
&.icon-shape-rounded,
&.icon-shape-square {
.menu-item a .menu-icon {
background-color: rgba($secondary-bg-color, 0.6);
}
}
}
&:not(.collapsed) {
.sidebar-main-menu {
> ul > .sub-menu > .sub-menu-list {
background-color: rgba($secondary-bg-color, 0.6);
}
}
}
}
}
&.rtl {
.sidebar-main {
.sidebar-main-menu {
.menu-item {
a {
.menu-icon {
margin-left: 10px;
margin-right: 0;
}
}
&.sub-menu {
> a {
&::after {
transform: rotate(135deg);
}
}
> .sub-menu-list {
padding-left: 0;
padding-right: 20px;
}
&.open {
> a {
&::after {
transform: rotate(45deg);
}
}
}
}
}
}
&.collapsed {
.sidebar-main-menu > ul {
> .menu-item {
&.sub-menu {
a::after {
right: auto;
left: 10px;
}
> .sub-menu-list {
margin-left: -3px !important;
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,92 @@
.sidebar-main {
color: $text-color;
position: relative;
height: 100%;
max-height: 100%;
overflow: hidden !important;
.image-wrapper {
overflow: hidden;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
display: none;
> img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
}
&.has-bg-image {
.sidebar-layout {
background-color: rgba($bg-color, 0.85);
}
.image-wrapper {
display: block;
opacity: 0.8;
// filter: blur(3px);
}
}
.sidebar-layout {
height: 100%;
max-height: 100%;
min-height: 100%;
overflow-x: hidden;
overflow-y: auto;
display: flex;
flex-direction: column;
position: relative;
background-color: $bg-color;
z-index: 2;
opacity: 0.85;
&::-webkit-scrollbar-thumb {
border-radius: 4px;
}
&:hover {
&::-webkit-scrollbar-thumb {
background-color: lighten($bg-color, 15);
}
}
&::-webkit-scrollbar {
width: 6px;
background-color: $bg-color;
}
.sidebar-header {
height: $sidebar-header-height;
min-height: $sidebar-header-height;
display: flex;
align-items: center;
padding: 8px 22px;
> span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.sidebar-content {
flex-grow: 1;
padding: 10px 0;
}
.sidebar-footer {
height: $sidebar-footer-height;
min-height: $sidebar-footer-height;
display: flex;
align-items: center;
padding: 0 20px;
> span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}

View File

@@ -0,0 +1,10 @@
$text-color: #44b8eb;
$secondary-text-color: #dee2ec;
$bg-color: #0c1e35;
$secondary-bg-color: #0b1a2c;
$border-color: rgba(#535d7d, 0.3);
$sidebar-header-height: 40px;
$sidebar-footer-height: 230px;

View File

@@ -0,0 +1,165 @@
@import './variables_css-pro-layout.scss';
@mixin break-point {
position: fixed;
left: -$sidebar-width;
height: 100%;
top: 0;
z-index: 100;
&.collapsed {
left: -$sidebar-collapsed-width;
}
&.toggled {
left: 0;
~ .overlay {
display: block;
}
}
~ .layout-main {
.header {
width: 100% !important;
transition: none;
}
}
}
$breakpoints: (
'break-point-xs': $breakpoint-xs,
'break-point-sm': $breakpoint-sm,
'break-point-md': $breakpoint-md,
'break-point-lg': $breakpoint-lg,
'break-point-xl': $breakpoint-xl,
'break-point-xxl': $breakpoint-xxl,
);
@mixin break-point-rtl {
left: auto;
right: -$sidebar-width;
&.collapsed {
left: auto;
right: -$sidebar-collapsed-width;
}
&.toggled {
left: auto;
right: 0;
}
}
@mixin setBreakPoint($name, $value) {
&.#{$name} {
@media (max-width: $value) {
@include break-point;
}
}
}
.layout-main {
min-height: 100%;
display: flex;
flex-direction: column;
position: relative;
flex-grow: 1;
&.has-sidebar {
flex-direction: row;
}
.header {
transition: width, 0.3s;
height: $header-height;
min-height: $header-height;
position: relative;
}
.sidebar-main {
width: $sidebar-width;
min-width: $sidebar-width;
transition: width, left, right, 0.3s;
&.collapsed {
width: $sidebar-collapsed-width;
min-width: $sidebar-collapsed-width;
}
@each $key, $value in $breakpoints {
@include setBreakPoint($key, $value);
}
}
.footer {
height: $footer-height;
min-height: $footer-height;
}
.content {
flex-grow: 1;
}
.overlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(#000, 0.3);
z-index: 99;
display: none;
}
.sidebar-toggler {
display: none;
@each $key, $value in $breakpoints {
&.#{$key} {
@media (max-width: $value) {
display: initial;
}
}
}
}
&.fixed-sidebar {
height: 100%;
.sidebar {
height: 100%;
overflow: auto;
~ .layout-main {
height: 100%;
overflow: auto;
}
}
}
&.fixed-header {
.header {
position: fixed;
width: 100%;
z-index: 2;
~ .layout-main,
~ .content {
margin-top: $header-height;
}
}
&.fixed-sidebar {
.header {
width: calc(100% - #{$sidebar-width});
}
.sidebar.collapsed {
~ .layout-main {
.header {
width: calc(100% - #{$sidebar-collapsed-width});
}
}
}
}
}
&.rtl {
direction: rtl;
.sidebar {
@each $key, $value in $breakpoints {
&.#{$key} {
@media (max-width: $value) {
@include break-point-rtl;
}
}
}
}
}
}

View File

@@ -0,0 +1,179 @@
$sidebar-width: 250px;
$sidebar-collapsed-width: 80px;
/*@import 'css-pro-layout/dist/scss/css-pro-layout.scss';*/
@import './css-pro-layout.scss';
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap');
/*@import '../../public//assets//remixicon/remixicon.css';*/
@import './variables';
@import './layout';
@import './sidebar';
@import './menu';
/*
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
font-family: 'Poppins', sans-serif;
color: #3f4750;
font-size: 0.9rem;
}
*/
a {
text-decoration: none;
}
@media (max-width: $breakpoint-lg) {
#btn-collapse {
display: none;
}
}
.layout-main {
.sidebar-main {
.pro-sidebar-logo {
color: #ffa801;
display: flex;
align-items: center;
> div {
width: 45px;
min-width: 45px;
height: 25px;
min-height: 25px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
color: white;
font-size: 22px;
font-weight: 700;
background-color: #ffa801;
margin-right: 10px;
margin-left: -10px;
}
> h5 {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 22px;
line-height: 25px;
transition: opacity 0.3s;
opacity: 1;
margin: 0 17px;
}
}
.footer-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
/* font-size: 0.8em; */
padding: 20px 0;
border-radius: 8px;
width: 180px;
min-width: 190px;
margin: 0 auto;
background-color: #162d4a;
img.react-logo {
width: 40px;
height: 40px;
margin-bottom: 10px;
}
a {
color: #fff;
font-weight: 600;
margin-bottom: 10px;
}
}
.sidebar-collapser {
transition: left, right, 0.3s;
position: fixed;
left: calc(#{$sidebar-width} - 20px);
top: 40px;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #00829f;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
align-items: center;
justify-content: center;
font-size: 1.2em;
transform: translateX(50%);
z-index: 111;
cursor: pointer;
color: white;
box-shadow: 1px 1px 4px $bg-color;
}
&.has-bg-image {
.footer-box {
/* background-color: rgba(#162d4a, 0.7); */
background-color: #34495e;
}
}
&.collapsed {
.pro-sidebar-logo {
> h5 {
opacity: 0;
}
}
.footer-box {
display: none;
}
.sidebar-collapser {
left: calc(#{$sidebar-collapsed-width} - 20px);
i {
transform: rotate(180deg);
}
}
}
}
}
.badge {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #6c757d;
&.primary {
background-color: #ab2dff;
}
&.secondary {
background-color: #079b0b;
}
}
.sidebar-toggler {
position: fixed;
right: 20px;
top: 20px;
}
.social-links {
a {
margin: 0 10px;
color: #3f4750;
}
}

View File

@@ -0,0 +1,11 @@
$sidebar-width: 280px !default;
$sidebar-collapsed-width: 80px !default;
$header-height: 64px !default;
$footer-height: 64px !default;
$breakpoint-xs: 480px !default;
$breakpoint-sm: 576px !default;
$breakpoint-md: 768px !default;
$breakpoint-lg: 992px !default;
$breakpoint-xl: 1200px !default;
$breakpoint-xxl: 1600px !default;

View File

@@ -0,0 +1,47 @@
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
//const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');
module.exports = {
plugins: [
new MiniCssExtractPlugin(),
// new CopyWebpackPlugin({
// patterns: [{ from: 'public' }],
// }),
],
module: {
rules: [
{
test: /\.js$/,
use: { loader: 'babel-loader' },
exclude: /node_modules/,
},
{
test: /\.(css|scss|sass|less)$/i,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
// 'less-loader',
'postcss-loader',
],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader'],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader'],
},
],
},
mode: 'production',
target: 'web',
devtool: 'source-map',
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
},
};

View File

@@ -0,0 +1,9 @@
/*!
* swiped-events.js - v1.2.0
* Pure JavaScript swipe events
* https://github.com/john-doherty/swiped-events
* @inspiration https://stackoverflow.com/questions/16348031/disable-scrolling-when-touch-moving-certain-element
* @author John Doherty <www.johndoherty.info>
* @license MIT
*/
!function(t,e){"use strict";"function"!=typeof t.CustomEvent&&(t.CustomEvent=function(t,n){n=n||{bubbles:!1,cancelable:!1,detail:void 0};var a=e.createEvent("CustomEvent");return a.initCustomEvent(t,n.bubbles,n.cancelable,n.detail),a},t.CustomEvent.prototype=t.Event.prototype),e.addEventListener("touchstart",function(t){if("true"===t.target.getAttribute("data-swipe-ignore"))return;l=t.target,r=Date.now(),n=t.touches[0].clientX,a=t.touches[0].clientY,u=0,i=0,o=t.touches.length},!1),e.addEventListener("touchmove",function(t){if(!n||!a)return;var e=t.touches[0].clientX,r=t.touches[0].clientY;u=n-e,i=a-r},!1),e.addEventListener("touchend",function(t){if(l!==t.target)return;var c=parseInt(s(l,"data-swipe-threshold","20"),10),d=s(l,"data-swipe-unit","px"),p=parseInt(s(l,"data-swipe-timeout","500"),10),h=Date.now()-r,v="",b=t.changedTouches||t.touches||[];"vh"===d&&(c=Math.round(c/100*e.documentElement.clientHeight));"vw"===d&&(c=Math.round(c/100*e.documentElement.clientWidth));Math.abs(u)>Math.abs(i)?Math.abs(u)>c&&h<p&&(v=u>0?"swiped-left":"swiped-right"):Math.abs(i)>c&&h<p&&(v=i>0?"swiped-up":"swiped-down");if(""!==v){var E={dir:v.replace(/swiped-/,""),touchType:(b[0]||{}).touchType||"direct",fingers:o,xStart:parseInt(n,10),xEnd:parseInt((b[0]||{}).clientX||-1,10),yStart:parseInt(a,10),yEnd:parseInt((b[0]||{}).clientY||-1,10)};l.dispatchEvent(new CustomEvent("swiped",{bubbles:!0,cancelable:!0,detail:E})),l.dispatchEvent(new CustomEvent(v,{bubbles:!0,cancelable:!0,detail:E}))}n=null,a=null,r=null},!1);var n=null,a=null,u=null,i=null,r=null,l=null,o=0;function s(t,n,a){for(;t&&t!==e.documentElement;){var u=t.getAttribute(n);if(u)return u;t=t.parentNode}return a}}(window,document);

View File

@@ -5,3 +5,12 @@ https://unpkg.com/@panzoom/panzoom@4.5.1/dist/panzoom.min.js
***********************************
gridstack - v11.1.2 on Dec 9, 2024 (Newer versions have a significant bug: https://github.com/gridstack/gridstack.js/issues/2978)
https://github.com/gridstack/gridstack.js
***********************************
pro-sidebar-template - Feb 1, 2023 with minor changes IgorA100
https://github.com/azouaoui-med/pro-sidebar-template
***********************************
swiped-events - v. 1.2.0 Apr 27, 2024
https://github.com/john-doherty/swiped-events
***********************************
mb.extruder Oct 24, 2018 with magor changes IgorA100
https://github.com/pupunzi/jquery.mb.extruder

View File

@@ -0,0 +1,323 @@
.layout-main .sidebar-main a.text-success:focus,
.layout-main .sidebar-main a.text-success:hover { /* FIX bootstrap on a dark background*/
color: #007bff !important;
}
.layout-main .sidebar-main.collapsed .hidden-for-collapsed {
display: none;
}
/* +++ Scrollbar for menu in left sidebar NEEDS MORE COMPACT */
#navbar-container,
.layout-main .sidebar-main .sidebar-layout,
.layout-main .sidebar-main .sidebar-content::-webkit-scrollbar-track,
.layout-main .sidebar-main .sidebar-layout::-webkit-scrollbar-track ,
.layout-main .sidebar-main .extruder-wrapper::-webkit-scrollbar-track ,
.layout-main .sidebar-main .extruder-content .filter-block::-webkit-scrollbar-track {
background-color: var(--backgroundMediumDark); /* #485460 */
}
.layout-main .sidebar-main .sidebar-content::-webkit-scrollbar-thumb,
.layout-main .sidebar-main .sidebar-layout::-webkit-scrollbar-thumb,
.layout-main .sidebar-main .extruder-wrapper::-webkit-scrollbar-thumb,
.layout-main .sidebar-main .extruder-content .filter-block::-webkit-scrollbar-thumb {
background-color: var(--sliderBG);
border-radius: 6px;
border: 0px solid var(--scrollbarBG);
width: 6px;
height: 6px;
}
.layout-main .sidebar-main .sidebar-content::-webkit-scrollbar,
.layout-main .sidebar-main .sidebar-layout::-webkit-scrollbar,
.layout-main .sidebar-main .extruder-wrapper::-webkit-scrollbar,
.layout-main .sidebar-main .extruder-content .filter-block::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.layout-main .sidebar-main .sidebar-layout:hover::-webkit-scrollbar-thumb { /* this is also defined in the _sidebar.scss file */
background-color: var(--sliderBG);
}
/* --- Scrollbar for menu in left sidebar */
.layout-main .sidebar-main .sidebar-post-header {
padding: 5px 0 5px 0;
}
.layout-main .sidebar-main .sidebar-main-menu,
.layout-main .sidebar-main .sidebar-post-header {
text-align: left;
}
.layout-main .sidebar-main .sidebar-layout .sidebar-content {
overflow-x: hidden;
overflow-y: auto;
}
.layout-main .sidebar-main.has-bg-image:not(.collapsed) .sidebar-main-menu > ul > .sub-menu > .sub-menu-list {
background-color: #34495e;
}
.layout-main .sidebar-main .sidebar-main-menu .menu-item a .menu-title, /* FIX _menu.scss */
.layout-main .sidebar-main .sidebar-post-header .menu-item a .menu-title {
/*font-size: 0.9rem;*/
font-size: inherit;
overflow: hidden;
white-space: nowrap;
}
.layout-main .sidebar-main .sidebar-post-header .menu-item a {
display: flex;
align-items: center;
padding: 0 20px;
}
.layout-main .sidebar-main .sidebar-post-header .menu-item a .menu-icon {
font-size: 1.2rem;
width: 35px;
min-width: 35px;
height: 35px;
line-height: 35px;
text-align: center;
display: inline-block;
margin-right: 10px;
border-radius: 2px;
transition: color 0.3s;
}
/* +++ MontageRewiev Filter Alignment */
.layout-main .sidebar-main .chosen-container {
text-align: left;
min-width: 11em;
width: 100% !important;
}
/* --- */
body.sticky .content-main {
display: flex;
flex-direction: column;
overflow: hidden;
}
.content-main {
width: 100%;
overflow: auto;
position: relative;
}
/* We don't need to see the filter and other things (that we cut out). */
.montagereview-page .content-main .controlHeader,
.montagereview-page .content-main #fieldsTable,
.console-page .content-main #fbpanel,
.console-page a[data-flip-control-object="#fbpanel"], /* Filter visibility control button */
.montage-page .content-main #filters_form,
.watch-page .content-main .controlHeader,
.report_event_audit-page #content form,
.events-page .content-main #fieldsTable,
.events-page a[data-flip-control-object="#fieldsTable"], /* Filter visibility control button */
.options-page .content-main #sidebar,
.navbar-brand,
#navbar-one {
display: none !important;
}
#monitorList {
overflow-y: auto; /* CHECK!!! PROBABLY THIS SHOULD ALWAYS BE USED, AND NOT JUST FOR STICK MODE!!! */
}
/* Align the entire bar to the left side */
.navbars {
text-align: left;
}
#btn-collapse {
top: 11px;
}
#wrapperBtnCloseExtruder{ /* Extruder block close button on mobile devices */
display: none;
}
@media (max-width: 992px) {
/* +++ Manage the collapse/expand menu button for mobile devices with hidden menu */
#btn-collapse {
display: flex;
}
#sidebarMain.sidebar-main:not(.toggled) #btn-collapse {
left: -50px; /* Ideally, you need to set the value here as for ".layout-main .sidebar-main.break-point-lg" or -250 or -80. But even now the icon motion looks very good */
}
/* --- */
#extruderLeft {
top: 0 !important;
left: 0 !important;
}
#wrapperBtnCloseExtruder{
position: relative;
top: 0px;
right: 0px;
display: flex;
justify-content: flex-end;
}
.extruder-content > .text:first-child {
width: 100% !important;
}
}
@media (max-height: 500px) {
.layout-main .sidebar-main .sidebar-content,
.layout-main .sidebar-main .sidebar-footer { /* Sidebar is not divided into Header, Footer and content. Everything is one and everything is scrollable */
display: contents;
}
}
.sidebar-toggler { /* also set in pro-sidebar-template/src/styles/styles.scss */
position: relative;
right: 0;
top: 0;
}
#navbar-two { /* THE PANEL MUST ALWAYS BE VISIBLE, OTHERWISE IT COULD BE HIDDEN BY A BUTTON (id="flip") */
display: block !important;
}
/* Choice at the bar */
#panel .dropdown-menu.show.overflown {
display: contents;
}
#panel .dropdown-menu.show:not(.overflown) {
display: block;
top: 27px;
}
/* +++ THIS IS REMAINING FROM THE TOP STATUS LINE */
#reload {
position: relative;
display: inline-block;
white-space: nowrap;
overflow-x: auto;
padding: 10px 5px 10px 15px
}
@media (max-width: 992px) { /* moment hamburger menu button appears */
#reload {
left: 35px;
}
}
#reload > ul,
#reload > ul > ul,
#reload > ul > li { /* remove the first part with the divas */
display: inline-block;
white-space: nowrap;
}
#reload > ul:last-child{ /* the last element when scrolling should have an indent */
margin-right: 30px;
}
#reload::-webkit-scrollbar {
width: 3px;
height: 3px;
}
#reload::-webkit-scrollbar-thumb {
border: 0px solid;
}
.high-scroll-bar::-webkit-scrollbar {
width: 7px !important;
height: 7px !important;
}
/* --- THIS IS REMAINING FROM THE TOP STATUS LINE */
#sidebarMain.collapsed .sub-menu-list {
height: 0; /* Otherwise it will flicker when the page loads. */
}
/* Set submenu to scroll vertically (for narrow option) when submenu is displayed on the side */
body #sidebarMain .sub-menu-list {
max-height: 100%;
overflow: auto;
}
/* +++ TRANSFERRED FILTERS */
.extruder-wrapper span.term .term-label-wrapper,
.extruder-wrapper span.term label {
display: flex !important;
text-align: left !important;
width: auto !important;
padding-right: 10px;
white-space: nowrap;
}
.extruder-wrapper span.term .term-value-wrapper {
display: flex !important;
justify-content: flex-end !important;
width: 100% !important;
}
.extruder-wrapper span.term,
.extruder-wrapper span.term .term-value-wrapper input,
.extruder-wrapper span.term > span { /* Aligning input fields */
width: 100%;
}
.extruder-wrapper span.term > span:first-child{
text-align: left;
}
.extruder-content {
background-color: var(--backgroundMediumDark);
}
.extruder-content > .text {
overflow: visible !important; /* Allows dropdowns to extend beyond the block if there is space on the screen */
}
.extruder-content .filter-block {
margin: 0;
padding: 10px;
}
.extruder-content > .text .term {
flex-direction: row;
margin-left: 0;
padding-bottom: 10px;
}
@media (max-width: 564px) {
.extruder-content span.term {
margin: 0;
}
.extruder-content button .material-icons {
font-size: 18px;
}
}
.extruder .extruder-content .chosen-container .chosen-single,
.extruder-wrapper span.term .term-value-wrapper input {
height: 27px !important;
}
/* Clear Filter Button Select Multiple Selection */
.extruder .extruder-content .btn-term-remove-all {
display: flex;
flex-direction: row-reverse;
width: auto;
padding-left: 10px;
max-height: 35px;
}
/* --- TRANSFERRED FILTERS */
/* !HACK! Otherwise the height of the parent block will be calculated based on the height of the drop-down list, even if the selection is not active! */
div:not(.chosen-container-active) > .chosen-drop {
display: none;
}

View File

@@ -26,6 +26,7 @@
--sliderBG: #C1C1C1;
--alarmBG: #FFC0C0;
--alarmText: inherit;
--backgroundMediumDark: #485460;
}
html,

View File

@@ -25,6 +25,7 @@
--scrollbarBG: #CFD8DC;
--sliderBG: #95AFC0;
--alarmBG: #352424;
--backgroundMediumDark: #485460;
}
body {

View File

@@ -18,6 +18,8 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
$useOldMenuView = (isset($_COOKIE['zmUseOldMenuView']) and $_COOKIE["zmUseOldMenuView"] === 'true') ? true : false;
function xhtmlHeaders($file, $title) {
xhtmlHeadersStart($file, $title);
xhtmlHeadersEnd();
@@ -117,6 +119,14 @@ if ( $css != 'base' )
'css/'.$css.'/jquery-ui-theme.css',
));
global $useOldMenuView;
if (!$useOldMenuView) {
echo output_link_if_exists(array(
'/assets/pro-sidebar-template/dist/main.css',
'/css/base/sidebar.css',
));
}
if ( $basename == 'watch' ) {
echo output_link_if_exists(array('/css/base/views/control.css'));
if ( $css != 'base' )
@@ -146,8 +156,11 @@ function xhtmlHeadersEnd() {
// Outputs an opening body tag, and any additional content that should go at the very top, like warnings and error messages.
function getBodyTopHTML() {
global $view;
//Needed for more flexible global governance
$classHTML = ' class="'.$view.'-page'.((defined('ZM_WEB_NAVBAR_STICKY') and ZM_WEB_NAVBAR_STICKY) ? ' sticky"' : '"');
echo '
<body'.((defined('ZM_WEB_NAVBAR_STICKY') and ZM_WEB_NAVBAR_STICKY) ? ' class="sticky"' : '').'>
<body data-swipe-threshold="10" data-swipe-unit="vw" data-swipe-timeout="300"'.$classHTML.'>
<noscript>
<div style="background-color:red;color:white;font-size:x-large;">
'. validHtmlStr(ZM_WEB_TITLE) .' requires Javascript. Please enable Javascript in your browser for this site.
@@ -159,8 +172,200 @@ function getBodyTopHTML() {
if ( $error_message ) {
echo '<div id="error">'.$error_message.'</div>';
}
global $useOldMenuView;
if ($useOldMenuView !== true) {
getSidebarTopHTML();
}
} // end function getBodyTopHTML
function buildMenuItem($viewItemName, $id, $itemName, $href, $icon, $classNameForTag_A = '', $subMenu = '') {
global $view;
/* Highlighting the active menu section */
if ($viewItemName == 'watch') {
$activeClass = ($view == $viewItemName && (isset($_REQUEST['cycle']) && $_REQUEST['cycle'] == "true")) ? ' active' : '';
} else {
$activeClass = $view == $viewItemName ? ' active' : '';
}
$itemName = translate($itemName);
$result = '
<li id="' . $id . '" class="menu-item '.$activeClass.'">
<a href="' . $href . '" class="' . $classNameForTag_A . '">
<span class="menu-icon"><i class="material-icons">' . $icon . '</i></span>
<span class="menu-title">'.$itemName.'</span>
</a>
</li>'.PHP_EOL;
return $result;
}
function buildSidebarMenu() {
global $view;
global $useOldMenuView;
global $user;
if ( $user and $user->Username() ) {
$menuForAuthUser = '
<li class="menu-header"><span> GENERAL </span></li> ' .
getConsoleHTML($forLeftBar = true) .
getMontageHTML($view, $forLeftBar = true) .
getCycleHTML($view, $forLeftBar = true) .
getMontageReviewHTML($view, $forLeftBar = true) .
getEventsHTML($view, $forLeftBar = true) .
getOptionsHTML($forLeftBar = true) .
getLogHTML($forLeftBar = true) .
getDevicesHTML($forLeftBar = true) .
getGroupsHTML($view, $forLeftBar = true) .
getFilterHTML($view, $forLeftBar = true) .
getSnapshotsHTML($view, $forLeftBar = true) .
getReportsHTML($view, $forLeftBar = true) .
getRprtEvntAuditHTML($view, $forLeftBar = true) .
getMapHTML($view, $forLeftBar = true)
;
} else { // USER IS NOT AUTHORIZED!
$menuForAuthUser = '';
}
$menu = '
<nav class="sidebar-main-menu open-current-submenu">
<ul>
' . $menuForAuthUser . '
<li class="menu-header" style="padding-top: 20px"><span> OTHER </span></li>
<li class="menu-item">
<a href="http://zoneminder.com/" target="_blank">
<span class="menu-icon">
<i class="material-icons">videocam</i>
</span>
<span class="menu-title">' . translate("ZoneMinder") . '</span>
<span class="menu-suffix">
<span class="badge secondary"><i class="material-icons md-14">thumb_up</i></span>
</span>
</a>
</li>
<li class="menu-item">
<a href="http://zoneminder.readthedocs.org/en/latest/" target="_blank">
<span class="menu-icon">
<i class="material-icons">description</i>
</span>
<span class="menu-title">' . translate("Documentation") . '</span>
<!--<span class="menu-suffix">
<span class="badge secondary">Beta</span>
</span>-->
</a>
</li>
<li class="menu-item">
<a href="https://zmninja.zoneminder.com/" target="_blank">
<span class="menu-icon">
<i class="material-icons">face_5</i>
</span>
<span class="menu-title">' . translate("zmNinja") . '</span>
</a>
</li>
<li class="menu-item">
<a href="https://wiki.zoneminder.com/" target="_blank">
<span class="menu-icon">
<i class="material-icons">article</i>
</span>
<span class="menu-title">' . translate("Wiki") . '</span>
</a>
</li>
<li class="menu-item">
<a href="https://forums.zoneminder.com/" target="_blank">
<span class="menu-icon">
<i class="material-icons">forum</i>
</span>
<span class="menu-title">' . translate("Forums") . '</span>
</a>
</li>
<li class="menu-item">
<a href="https://zoneminder-chat.slack.com/" target="_blank">
<span class="menu-icon">
<i class="material-icons">chat</i>
</span>
<span class="menu-title">' . translate("Slack") . '</span>
</a>
</li>
<li class="menu-header hidden-for-collapsed" style="padding-top: 20px">
<label for="useOldMenuView" class="control-label text-md-right">' . translate("USE OLD MENU VIEW") . '</label>
<input type="checkbox" name="useOldMenuView" id="useOldMenuView" value="1"' . ( $useOldMenuView ? ' checked="checked"' : '') . '/>
</li>
</ul>
</nav>
';
return $menu;
}
function getSidebarTopHTML() {
global $skin;
global $user;
global $running;
$blockExtruder = '
<div id="extruderLeft">
<div id="contextExtruderLeft" class="text">
<! -- Pull-out panel FILLED VIA JS -->
</div>
</div>
';
$block = '
<div class="layout-main has-sidebar fixed-sidebar fixed-header">
<aside id="sidebarMain" class="sidebar-main break-point-lg has-bg-image '. (( isset($_COOKIE['zmSidebarMainCollapse']) and $_COOKIE["zmSidebarMainCollapse"] === 'true' ) ? "collapsed" : "") . '">
' . $blockExtruder . '
<a id="btn-collapse" class="sidebar-collapser"><i class="material-icons">chevron_left</i></a>
<div class="image-wrapper">
<img src="skins/'.$skin.'/assets/pro-sidebar-template/assets/images/sidebar-bg.jpg" alt="sidebar background" />
</div>
<div class="sidebar-layout">
<div class="sidebar-header">
<div class="pro-sidebar-logo">
<div>ZM</div>
<h5>ZoneMinder</h5>
</div>
</div>
<!-- End of header, before scrolling menu -->
<div id="menuControlModule" class="sidebar-post-header">
<!-- FILLED VIA JS -->
</div>
<!-- Start of scrolling menu -->
<div class="sidebar-content">
' . buildSidebarMenu() . '
</div>
<div class="sidebar-footer hidden-for-collapsed">
<div class="footer-box">
<div>
' . getAccountCircleHTML($skin, $user, $forLeftBar = true) . '
</div>
<div style="padding: 0 10px">
<span style="display: block; margin-bottom: 10px">
<ul id="versionSidebar">
' . getZMVersionHTML() . '
</ul>
</span>
<ul id="statusSidebar">
' . getStatusBtnHTML(runtimeStatus($running)) . '
</ul>
</div>
</div>
</div>
</div>
</aside>
<div id="overlay" class="overlay"></div>
<div class="content-main">
';
echo $block;
} // end function getSidebarTopHTML
function getSidebarBottomHTML() {
global $skin;
$block = '
<div class="overlay"></div>
</div> <!-- class="content-main" -->
</div> <!-- class="layout-main has-sidebar fixed-sidebar fixed-header" -->
<style>
</style>
';
echo $block;
} // end function getSidebarBottomHTML
function getNavBarHTML() {
# Provide a facility to turn off the headers if you put navbar=0 into the url
if ( isset($_REQUEST['navbar']) and $_REQUEST['navbar'] == '0' )
@@ -298,9 +503,7 @@ function getNormalNavBarHTML($running, $user, $bandwidth_options, $view, $skin)
echo getMapHTML($view);
echo getAdditionalLinksHTML($view);
echo getHeaderFlipHTML();
echo '</ul></div><div id="accountstatus">
';
echo '</ul></div><div id="accountstatus">';
echo '<ul class="nav navbar-nav justify-content-end align-self-start flex-grow-1">';
echo getAccountCircleHTML($skin, $user);
echo getStatusBtnHTML($status);
@@ -317,27 +520,12 @@ function getNormalNavBarHTML($running, $user, $bandwidth_options, $view, $skin)
<?php
// *** Build the statistics shown on the navigation bar ***
?>
<div id="reload" class="container-fluid">
<ul id="Bandwidth" class="navbar-nav justify-content-start">
<?php echo getBandwidthHTML($bandwidth_options, $user) ?>
</ul>
<ul class="navbar-nav list-inline justify-content-center">
<?php
echo getSysLoadHTML();
echo getCpuUsageHTML();
echo getDbConHTML();
echo getStorageHTML();
echo getRamHTML();
?>
</ul>
<ul id="Version" class="nav navbar-nav justify-content-end">
<?php echo getZMVersionHTML() ?>
</ul>
</div>
<?php
global $useOldMenuView;
if ($useOldMenuView !== true) {
echo buildStatisticsBar($forLeftBar = true);
} else {
echo buildStatisticsBar($forLeftBar = false);
}
?>
</div><!-- End Collapsible Panel -->
</nav><!-- End Second Navbar -->
@@ -351,6 +539,46 @@ function getNormalNavBarHTML($running, $user, $bandwidth_options, $view, $skin)
<?php
} // end function getNormalNavBarHTML()
function buildStatisticsBar($forLeftBar = false) {
// *** Build the statistics shown on the navigation bar ***
global $bandwidth_options;
global $user;
$block = '';
if ($forLeftBar) {
// Mobile menu hamburger button
$block .= '
<div style="left: 5px; top: 10px; position: absolute;">
<a id="btn-toggle" href="#" class="sidebar-toggler break-point-lg">
<i class="material-icons">menu</i>
</a>
</div>
';
}
$block .= '
<div id="reload" class="container-fluid">
<ul id="Bandwidth" class="navbar-nav justify-content-start">
' . getBandwidthHTML($bandwidth_options, $user) .'
</ul>
<ul class="navbar-nav list-inline justify-content-center">
'.
getSysLoadHTML().
getCpuUsageHTML().
getDbConHTML().
getStorageHTML().
getRamHTML()
.'
</ul>
<ul id="Version" class="nav navbar-nav justify-content-end">
' . getZMVersionHTML() . '
</ul>
</div>
';
return $block;
}
//
// A new, slimmer navigation bar, permanently collapsed into a dropdown
//
@@ -464,7 +692,7 @@ function getSysLoadHTML() {
$thisServer->ReadStats();
$result .= '<li id="getSysLoadHTML" class="Load nav-item mx-2">';
$result .= '<i class="material-icons md-18" style="display: inline-block;">trending_up</i>';
$result .= '<i class="material-icons md-18">trending_up</i>';
$result .= '&nbsp;'.translate('Load').': '.number_format($thisServer->CpuLoad, 2, '.', '');
$result .= '</li>'.PHP_EOL;
}
@@ -481,7 +709,7 @@ function getDbConHTML() {
$class = ( $percent_used > 90 ) ? ' text-warning' : '';
$result .= '<li id="getDbConHTML" class="nav-item dropdown mx-2' .$class. '">'.PHP_EOL;
$result .= '<i class="material-icons md-18 mr-1" style="display: inline-block;">storage</i>'.PHP_EOL;
$result .= '<i class="material-icons md-18 mr-1">storage</i>'.PHP_EOL;
$result .= translate('DB'). ': ' .$connections. '/' .$max_connections.PHP_EOL;
$result .= '</li>'.PHP_EOL;
@@ -529,7 +757,7 @@ function getStorageHTML() {
$result .= '</li>'.PHP_EOL;
} else {
$result .= '<li id="getStorageHTML" class="nav-item dropdown mx-2">'.PHP_EOL;
$result .= '<a class="dropdown-toggle mr-2 '.$class.'" href="#" id="dropdown_storage" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="material-icons md-18 mr-1" style="display: inline-block;">folder_shared</i>Storage</a>'.PHP_EOL;
$result .= '<a class="dropdown-toggle mr-2 '.$class.'" href="#" id="dropdown_storage" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="material-icons md-18 mr-1">folder_shared</i>Storage</a>'.PHP_EOL;
$result .= '<div class="dropdown-menu" aria-labelledby="dropdown_storage">'.PHP_EOL;
foreach ( $storage_areas as $area ) {
@@ -625,7 +853,7 @@ function getBandwidthHTML($bandwidth_options, $user) {
$result = '';
if (count($bandwidth_options) > 1) {
$result .= '<li id="getBandwidthHTML" class="nav-item dropdown mx-2">'.PHP_EOL;
$result .= '<a class="dropdown-toggle mr-2" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="dropdown_bandwidth"><i class="material-icons md-18 mr-1" style="display: inline-block;">network_check</i>'.translate($bandwidth_options[$_COOKIE['zmBandwidth']]).'</a>'.PHP_EOL;
$result .= '<a class="dropdown-toggle mr-2" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="dropdown_bandwidth"><i class="material-icons md-18 mr-1">network_check</i>'.translate($bandwidth_options[$_COOKIE['zmBandwidth']]).'</a>'.PHP_EOL;
$result .= '<div class="dropdown-menu" aria-labelledby="dropdown_bandwidth">'.PHP_EOL;
if ( isset($bandwidth_options['high']) )
@@ -696,42 +924,142 @@ function getNavBrandHTML() {
} else {
$result .= '<a id="getNavBrandHTML" href="' .validHtmlStr(ZM_HOME_URL). '" target="' .validHtmlStr(ZM_WEB_TITLE). '">' .ZM_HOME_CONTENT. '</a>'.PHP_EOL;
}
global $useOldMenuView;
if ($useOldMenuView) {
$result .= '
<div id="blockUseOldMenuView" style="display: flex;">
<label style="font-size: 14px; color: white;" for="useOldMenuView" class="control-label text-md-right">' . translate("Use old menu view") . '</label>
<input type="checkbox" name="useOldMenuView" id="useOldMenuView" value="1"' . ( $useOldMenuView ? ' checked="checked"' : '') . '/>
</div>
'.PHP_EOL;
}
return $result;
}
// Returns the html representing the Console menu item
function getConsoleHTML() {
function getConsoleHTML($forLeftBar = false) {
global $user;
$result = '';
if (count($user->viewableMonitorIds()) or !ZM\Monitor::find_one()) {
$result .= '<li id="getConsoleHTML" class="nav-item"><a class="nav-link" href="?view=console">'.translate('Console').'</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'console',
$id = 'getConsoleHTML',
$itemName = 'Console',
$href = '?view=console',
$icon = 'dashboard',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getConsoleHTML" class="nav-item"><a class="nav-link" href="?view=console">'.translate('Console').'</a></li>'.PHP_EOL;
}
}
return $result;
}
// Returns the html representing the Options menu item
function getOptionsHTML() {
function getOptionsHTML($forLeftBar = false) {
$result = '';
if ( canView('System') ) {
$result .= '<li id="getOptionsHTML" class="nav-item"><a class="nav-link" href="?view=options">'.translate('Options').'</a></li>'.PHP_EOL;
if ($forLeftBar) {
// Copied from web/skins/classic/views/options.php
// When using the old top menu, the list of options pages is still generated in the views/options.php file.
global $view;
$tabs = array();
if (!defined('ZM_FORCE_CSS_DEFAULT') or !defined('ZM_FORCE_SKIN_DEFAULT'))
$tabs['skins'] = translate('Display');
$tabs['system'] = translate('System');
$tabs['auth'] = translate('Authentication');
$tabs['config'] = translate('Config');
if (defined('ZM_PATH_DNSMASQ_CONF') and ZM_PATH_DNSMASQ_CONF) {
$tabs['dnsmasq'] = translate('DHCP');
}
$tabs['API'] = translate('API');
$tabs['servers'] = translate('Servers');
$tabs['storage'] = translate('Storage');
$tabs['web'] = translate('Web');
$tabs['images'] = translate('Images');
$tabs['logging'] = translate('Logging');
$tabs['network'] = translate('Network');
$tabs['mail'] = translate('Email');
$tabs['upload'] = translate('Upload');
$tabs['x10'] = translate('X10');
$tabs['highband'] = translate('HighBW');
$tabs['medband'] = translate('MediumBW');
$tabs['lowband'] = translate('LowBW');
$tabs['users'] = translate('Users');
$tabs['groups'] = translate('Groups');
$tabs['control'] = translate('Control');
$tabs['privacy'] = translate('Privacy');
$tabs['MQTT'] = translate('MQTT');
$tabs['telemetry'] = translate('Telemetry');
$tabs['version'] = translate('Versions');
$view_ = 'options';
//$tab = isset($_REQUEST['tab']) ? validHtmlStr($_REQUEST['tab']) : 'system';
$tab = isset($_REQUEST['tab']) ? validHtmlStr($_REQUEST['tab']) : '';
$subMenuOptions = '
<div class="sub-menu-list">
<ul>
';
foreach ($tabs as $name=>$value) {
$subMenuOptions .= '
<li class="menu-item '.$name.' '.($tab == $name ? ' active' : '').'">
<a href="?view='.$view_.'&amp;tab='.$name.'">
<span class="menu-title">'.$value.'</span>
</a>
</li>'.PHP_EOL;
}
$subMenuOptions .= '
</ul>
</div>
';
$result .= '
<li id="getOptionsHTML" class="menu-item sub-menu '.($view == "options" ? ' open' : '').'">
<a href="#<!--?view='.$view_.'&amp;tab=system-->">
<span class="menu-icon"><i class="material-icons">settings</i></span>
<span class="menu-title">'.translate('Options').'</span>
</a>
' . $subMenuOptions . '
</li>'.PHP_EOL;
} else {
$result .= '<li id="getOptionsHTML" class="nav-item"><a class="nav-link" href="?view=options">'.translate('Options').'</a></li>'.PHP_EOL;
}
}
return $result;
}
// Returns the html representing the Log menu item
function getLogHTML() {
function getLogHTML($forLeftBar = false) {
$result = '';
if ( canView('System') ) {
if ( ZM\logToDatabase() > ZM\Logger::NOLOG ) {
$logstate = logState();
$class = ($logstate == 'ok') ? 'text-success' : ($logstate == 'alert' ? 'text-warning' : (($logstate == 'alarm' ? 'text-danger' : '')));
$result .= '<li id="getLogHTML" class="nav-item"><a class="nav-link '.$class.'" href="?view=log">'.translate('Log').'</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'log',
$id = 'getLogHTML',
$itemName = 'Log',
$href = '?view=log',
$icon = 'notification_important',
$classNameForTag_A = $class,
$subMenu = ''
);
} else {
$result .= '<li id="getLogHTML" class="nav-item"><a class="nav-link '.$class.'" href="?view=log">'.translate('Log').'</a></li>'.PHP_EOL;
}
}
}
@@ -747,7 +1075,7 @@ function getLogIconHTML() {
$logstate = logState();
$class = ($logstate == 'ok') ? 'text-success' : ($logstate == 'alert' ? 'text-warning' : (($logstate == 'alarm' ? 'text-danger' : '')));
$result .= '<li id="getLogIconHTML" class="nav-item">'.
makeLink('?view=log', '<span class="mx-1 ' .$class. '"><i class="material-icons md-18" style="display: inline-block;">report</i>'.translate('Log').'</span>').
makeLink('?view=log', '<span class="mx-1 ' .$class. '"><i class="material-icons md-18">report</i>'.translate('Log').'</span>').
'</li>'.PHP_EOL;
}
}
@@ -756,65 +1084,126 @@ function getLogIconHTML() {
}
// Returns the html representing the X10 Devices menu item
function getDevicesHTML() {
function getDevicesHTML($forLeftBar = false) {
$result = '';
if ( ZM_OPT_X10 && canView('Devices') ) {
$result .= '<li id="getDevicesHTML" class="nav-item"><a class="nav-link" href="?view=devices">Devices</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'devices',
$id = 'getDevicesHTML',
$itemName = 'Devices',
$href = '?view=devices',
$icon = 'devices_other',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getDevicesHTML" class="nav-item"><a class="nav-link" href="?view=devices">'.translate('Devices').'</a></li>'.PHP_EOL;
}
}
return $result;
}
// Returns the html representing the Groups menu item
function getGroupsHTML($view) {
function getGroupsHTML($view, $forLeftBar = false) {
$result = '';
if ( !canView('Groups') ) return $result;
$class = $view == 'groups' ? ' selected' : '';
$result .= '<li id="getGroupsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=groups">'. translate('Groups') .'</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'groups',
$id = 'getGroupsHTML',
$itemName = 'Groups',
$href = '?view=groups',
$icon = 'group',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getGroupsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=groups">'. translate('Groups') .'</a></li>'.PHP_EOL;
}
return $result;
}
// Returns the html representing the Filter menu item
function getFilterHTML($view) {
function getFilterHTML($view, $forLeftBar = false) {
$result = '';
if ( !canView('Events') ) return $result;
$class = $view == 'filter' ? ' selected' : '';
$result .= '<li id="getFilterHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=filter">'.translate('Filters').'</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'filter',
$id = 'getFilterHTML',
$itemName = 'Filters',
$href = '?view=filter',
$icon = 'filter_alt',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getFilterHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=filter">'.translate('Filters').'</a></li>'.PHP_EOL;
}
return $result;
}
// Returns the html representing the Cycle menu item
function getCycleHTML($view) {
function getCycleHTML($view, $forLeftBar = false) {
$result = '';
if ( canView('Stream') ) {
$class = $view == 'cycle' ? ' selected' : '';
$result .= '<li id="getCycleHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=watch&amp;cycle=true">' .translate('Cycle'). '</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'watch',
$id = 'getCycleHTML',
$itemName = 'Cycle',
$href = '?view=watch&amp;cycle=true',
//$icon = 'cyclone',
$icon = 'repeat',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getCycleHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=watch&amp;cycle=true">' .translate('Cycle'). '</a></li>'.PHP_EOL;
}
}
return $result;
}
// Returns the html representing the Montage menu item
function getMontageHTML($view) {
function getMontageHTML($view, $forLeftBar = false) {
global $user;
$result = '';
if (canView('Stream') and count($user->viewableMonitorIds())) {
$class = $view == 'montage' ? ' selected' : '';
$result .= '<li id="getMontageHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=montage">' .translate('Montage'). '</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'montage',
$id = 'getMontageHTML',
$itemName = 'Montage',
$href = '?view=montage',
$icon = 'live_tv',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getMontageHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=montage">' .translate('Montage'). '</a></li>'.PHP_EOL;
}
}
return $result;
}
// Returns the html representing the MontageReview menu item
function getMontageReviewHTML($view) {
function getMontageReviewHTML($view, $forLeftBar = false) {
$result = '';
if ( canView('Events') ) {
@@ -834,54 +1223,139 @@ function getMontageReviewHTML($view) {
}
$live = isset($montageReviewQuery) ? '&fit=1'.$montageReviewQuery.'&live=0' : '';
$class = $view == 'montagereview' ? ' selected' : '';
$result .= '<li id="getMontageReviewHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=montagereview' .$live. '">'.translate('MontageReview').'</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'montagereview',
$id = 'getMontageReviewHTML',
$itemName = 'MontageReview',
$href = '?view=montagereview' .$live,
$icon = 'movie',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getMontageReviewHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=montagereview' .$live. '">'.translate('MontageReview').'</a></li>'.PHP_EOL;
}
}
return $result;
}
// Returns the html representing the Montage menu item
function getSnapshotsHTML($view) {
function getSnapshotsHTML($view, $forLeftBar = false) {
$result = '';
if (defined('ZM_FEATURES_SNAPSHOTS') and ZM_FEATURES_SNAPSHOTS and canView('Snapshots')) {
$class = $view == 'snapshots' ? ' selected' : '';
$result .= '<li id="getSnapshotsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=snapshots">' .translate('Snapshots'). '</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'snapshots',
$id = 'getSnapshotsHTML',
$itemName = 'Snapshots',
$href = '?view=snapshots',
$icon = 'preview',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getSnapshotsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=snapshots">' .translate('Snapshots'). '</a></li>'.PHP_EOL;
}
}
return $result;
}
function getReportsHTML($view) {
// Returns the html representing the Events menu item
function getEventsHTML($view, $forLeftBar = false) {
global $user;
$result = '';
if (canView('Events')) {
$class = $view == 'events' ? ' selected' : '';
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'events',
$id = 'getEventsHTML',
$itemName = 'Events',
$href = '?view=events',
$icon = 'event',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getEventsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=events">' .translate('Events'). '</a></li>'.PHP_EOL;
}
}
return $result;
}
function getReportsHTML($view, $forLeftBar = false) {
$result = '';
if (canView('Events')) {
$class = ($view == 'reports' or $view == 'report') ? ' selected' : '';
$result .= '<li id="getReportsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=reports">'.translate('Reports').'</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'reports',
$id = 'getReportsHTML',
$itemName = 'Reports',
$href = '?view=reports',
$icon = 'report',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getReportsHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=reports">'.translate('Reports').'</a></li>'.PHP_EOL;
}
}
return $result;
}
// Returns the html representing the Audit Events Report menu item
function getRprtEvntAuditHTML($view) {
function getRprtEvntAuditHTML($view, $forLeftBar = false) {
$result = '';
if ( canView('Events') ) {
$class = $view == 'report_event_audit' ? ' selected' : '';
$result .= '<li id="getRprtEvntAuditHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=report_event_audit">'.translate('ReportEventAudit').'</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'report_event_audit',
$id = 'getRprtEvntAuditHTML',
$itemName = 'ReportEventAudit',
$href = '?view=report_event_audit',
$icon = 'shield',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getRprtEvntAuditHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=report_event_audit">'.translate('ReportEventAudit').'</a></li>'.PHP_EOL;
}
}
return $result;
}
// Returns the html representing the Audit Events Report menu item
function getMapHTML($view) {
function getMapHTML($view, $forLeftBar = false) {
$result = '';
if (defined('ZM_OPT_USE_GEOLOCATION') and ZM_OPT_USE_GEOLOCATION) {
$class = $view == 'map' ? ' selected' : '';
$result .= '<li id="getMapHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=map">'.translate('Map').'</a></li>'.PHP_EOL;
if ($forLeftBar) {
$result .= buildMenuItem(
$viewItemName = 'map',
$id = 'getMapHTML',
$itemName = 'Map',
$href = '?view=map',
$icon = 'language',
$classNameForTag_A = '',
$subMenu = ''
);
} else {
$result .= '<li id="getMapHTML" class="nav-item"><a class="nav-link'.$class.'" href="?view=map">'.translate('Map').'</a></li>'.PHP_EOL;
}
}
return $result;
@@ -909,13 +1383,13 @@ function getHeaderFlipHTML() {
$result = '';
$header = ( isset($_COOKIE['zmHeaderFlip']) and $_COOKIE['zmHeaderFlip'] == 'down') ? 'down' : 'up';
$result .= '<li id="getHeaderFlipHTML" class="nav-item dropdown"><a class="nav-link" href="#"><i id="flip" class="material-icons md-18" style="display: inline-block;">keyboard_arrow_' .$header. '</i></a></li>'.PHP_EOL;
$result .= '<li id="getHeaderFlipHTML" class="nav-item dropdown"><a class="nav-link" href="#"><i id="flip" class="material-icons md-18">keyboard_arrow_' .$header. '</i></a></li>'.PHP_EOL;
return $result;
}
// Returns the html representing the logged in user name and avatar
function getAccountCircleHTML($skin, $user=null) {
function getAccountCircleHTML($skin, $user=null, $forLeftBar = false) {
$result = '';
if ( ZM_OPT_USE_AUTH and $user ) {
@@ -939,7 +1413,7 @@ function getStatusBtnHTML($status) {
if (ZM_SYSTEM_SHUTDOWN) {
$result .= '<li class="shutdown">'.PHP_EOL;
$result .= '<button id="shutdownButton" class="btn btn-default navbar-btn" data-on-click="getShutdownModal" data-toggle="tooltip" data-placement="top" title="' .translate('Shutdown'). '"><i class="material-icons md-18" style="display: inline-block;">power_settings_new</i></button>'.PHP_EOL;
$result .= '<button id="shutdownButton" class="btn btn-default navbar-btn" data-on-click="getShutdownModal" data-toggle="tooltip" data-placement="top" title="' .translate('Shutdown'). '"><i class="material-icons md-18">power_settings_new</i></button>'.PHP_EOL;
$result .= '</li>'.PHP_EOL;
}
@@ -1040,6 +1514,10 @@ function getCSRFinputHTML() {
}
function xhtmlFooter() {
global $useOldMenuView;
if ($useOldMenuView !== true) {
getSidebarBottomHTML();
}
global $css;
global $cspNonce;
global $view;
@@ -1055,6 +1533,11 @@ function xhtmlFooter() {
<script src="<?php echo cache_bust('js/ajaxQueue.js') ?>"></script>
<script src="skins/<?php echo $skin; ?>/js/bootstrap-4.5.0.min.js"></script>
<?php
if (!$useOldMenuView) {
echo output_script_if_exists(array('assets/pro-sidebar-template/dist/main.js'));
echo output_script_if_exists(array('assets/mb.extruder/inc/mbExtruder.js'));
echo output_script_if_exists(array('assets/swiped-events/dist/swiped-events.min.js'));
}
if ( $basename == 'montage' ) {
echo output_script_if_exists(array('assets/gridstack/dist/gridstack-all.js'));
echo output_script_if_exists(array('assets/jquery.panzoom/dist/jquery.panzoom.js'));

View File

@@ -43,6 +43,13 @@ var expiredTap; //Time between touch screen clicks. Used to analyze double click
var shifted = ctrled = alted = false;
var mainContent = document.getElementById('content');
const useOldMenuView = !!getCookie('zmUseOldMenuView');
const showExtruderPanelOnMouseHover = false;
const NAVBAR_RELOAD = document.getElementById('reload'); // Top panel with statistics
const BTN_COLLAPSE = document.getElementById('btn-collapse'); // Button to switch the menu view collapsed/expanded
const SIDEBAR_MAIN = document.getElementById('sidebarMain'); // Left Sidebar with Menu
const SIDEBAR_MAIN_EXTRUDER = document.getElementById('extruderLeft'); // Sliding extruder panel from the left Sidebar
function checkSize() {
if ( 0 ) {
if (window.outerHeight) {
@@ -550,12 +557,43 @@ function submitTab(evt) {
evt.preventDefault();
}
function submitThisForm() {
if ( ! this.form ) {
// Called by 'data-on-change=submitThisForm' for Select and in Montage page when date/time changes
function submitThisForm(param = null) {
var form = null;
var filter = null; // The filter that we previously moved to the left sidebar menu
if (currentView == 'console' && !useOldMenuView) {
// We get the form that we process
form = document.querySelector('form[name="monitorForm"]');
// We get a filter
filter = document.querySelector('#fbpanel');
} else if (currentView == 'montage' && !useOldMenuView) {
form = document.querySelector('#filters_form');
// Filter is inside the form.
} else if (currentView == 'montagereview' && !useOldMenuView) {
form = document.querySelector('#montagereview_form');
filter = document.querySelector('#filterMontagereview');
} else if (currentView == 'watch' && !useOldMenuView) {
form = document.querySelector('#wrapperFilter form');
} else {
form = this.form;
}
if ( ! form ) {
console.log("No this.form. element with onchange is not in a form");
return;
}
this.form.submit();
if (filter && !useOldMenuView) {
// Let's hide the old filter so that it doesn't appear during the transfer...
filter.style.display = 'none';
// We return the filter to its place in the form, since in the left side menu the filter should always be inside the form.
form.prepend(filter);
}
if (param && typeof param === 'string') { //ON WATCH PAGE WHEN SELECTING A MONITOR, the object is transferred as PARAM!!!
var uri = "?" + $j(form).serialize() + param;
window.location = uri;
} else {
form.submit();
}
}
/**
@@ -687,6 +725,7 @@ function endOfResize(e) {
//Only for scaleToFit
changeScale();
}
isNavbarOverflown();
}, 250);
}
window.onresize = endOfResize;
@@ -1205,6 +1244,10 @@ function isMobile() {
return result;
}
function isTouchDevice() {
return !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch);
}
function destroyChosen(selector = '') {
if (typeof selector === 'string') {
$j(selector + '.chosen').chosen('destroy');
@@ -1432,18 +1475,359 @@ function canPlayCodec(filename) {
return false;
}
/*
* Top bar with statistics
* This function is necessary because
* .dropdown-menu cannot extend beyond #reload container if #reload overflow == auto
* overflow == auto for #reload is needed to scroll #reload on narrow screen.
* Now on a wide screen we have a beautiful .dropdown-menu, on a narrow screen it is less beautiful.
*/
function isNavbarOverflown() {
if (!NAVBAR_RELOAD) return; // For example, when you log in, nothing will happen...
// If it doesn't fit, turn on overflow
if (NAVBAR_RELOAD.scrollWidth > NAVBAR_RELOAD.clientWidth) {
NAVBAR_RELOAD.style.overflowX = 'auto';
document.querySelectorAll("#panel .dropdown-menu").forEach(function(el) {
el.classList.add('overflown');
});
} else {
NAVBAR_RELOAD.style.overflowX = 'visible';
document.querySelectorAll("#panel .dropdown-menu").forEach(function(el) {
el.classList.remove('overflown');
});
}
if (useOldMenuView) {
if (window.innerWidth < 600) { // To support the old top menu
document.getElementById('main-header-nav').appendChild(document.getElementById('blockUseOldMenuView'));
} else {
document.querySelector('#navbar-container .navbar-brand').appendChild(document.getElementById('blockUseOldMenuView'));
}
}
}
function changeAttrTitle(collapsed = null) {
if (!SIDEBAR_MAIN) return;
if (collapsed === null) collapsed = SIDEBAR_MAIN.classList.contains('collapsed');
// First level menu
// Let's create (for collapsed, to make it clearer or if the title doesn't fit) or remove (for expanded, so as not to irritate) the "Title" attribute
SIDEBAR_MAIN.querySelectorAll("nav>ul>li.menu-item>a").forEach(function(el) {
const titleEl = el.querySelector("span.menu-title");
if (collapsed || titleEl.scrollWidth > titleEl.clientWidth) {
el.setAttribute('title', el.querySelector("span.menu-title").textContent);
} else {
el.setAttribute('title', '');
}
});
// For submenu we will add only if the title does not fit in the visible area in width...
SIDEBAR_MAIN.querySelectorAll("nav>ul>li.menu-item.sub-menu .sub-menu-list>ul>li.menu-item a").forEach(function(el) {
const titleEl = el.querySelector("span");
if (titleEl.scrollWidth > titleEl.clientWidth) {
el.setAttribute('title', el.querySelector("span.menu-title").textContent);
} else {
el.setAttribute('title', '');
}
});
}
/* We create a retractable extruder block with filter settings (we move filters from the top panel) and a button in the left Sidebar menu */
function insertControlModuleMenu() {
var filter = null;
if (currentView == 'console') {
destroyChosen(); // It is required to be performed BEFORE receiving the object and only for those pages on which we transfer the filter
filter = document.querySelector('#fbpanel');
} else if (currentView == 'montage') {
destroyChosen();
filter = document.querySelector('#filters_form');
} else if (currentView == 'montagereview') {
destroyChosen();
filter = document.createElement('div');
filter.setAttribute("id", "filterMontagereview");
const filterOne = document.querySelector('#mfbpanel .controlHeader');
const filterTwo = document.querySelector('#fieldsTable');
filter.prepend(filterTwo);
filter.prepend(filterOne);
} else if (currentView == 'watch') {
destroyChosen();
filter = document.querySelector('.controlHeader form');
} else if (currentView == 'report_event_audit') {
destroyChosen();
filter = document.querySelector('#content form');
} else if (currentView == 'events') {
destroyChosen();
filter = document.querySelector('#fieldsTable');
// We change the layout to remove the resulting void.
const toolbar = document.querySelector('#toolbar');
// The first div that is "col-sm-1"
const divFirst = toolbar.querySelector('.col-sm-1');
if (divFirst) {
divFirst.classList.remove('col-sm-1');
divFirst.classList.add('col-sm-4');
}
// The second div, which is "col-sm-9"
const divSecond = toolbar.querySelector('.col-sm-9');
if (divSecond) {
divSecond.style.display = 'none';
}
// The third div, which is "col-sm-2"
const divThird = toolbar.querySelector('.col-sm-2');
if (divThird) {
divThird.classList.remove('col-sm-2');
divThird.classList.add('col-sm-8');
}
}
if (!filter) return;
filter.classList.add('filter-block');
// Restore visibility. If the block was hidden
filter.classList.remove('hidden-shift');
filter.style.display = '';
//Create a button to open/hide the filter settings control
var el = document.createElement('div');
el.setAttribute("class", "menu-item");
el.innerHTML = `
<a href="#" class="text-success" title="Filter settings">
<span class="bg-light menu-icon"><i class="material-icons">tune</i></span>
<span class="menu-title">Filter settings</span>
</a>
`;
const menuControlModule = $j(document.querySelector('#menuControlModule'));
const containerExtruderLeft = $j(document.querySelector('#contextExtruderLeft'));
menuControlModule.prepend(el); // Menu line
// Let's create a wrapper for the filter, since the filter can start with a <span> tag or something similar.
var wrapper = document.createElement('div');
wrapper.setAttribute("id", "wrapperFilter");
// Let's add a filter to wrapper
wrapper.prepend(filter);
// We will create a button to close extruder panel, which is relevant for narrow screens
var btnClose = document.createElement('span');
btnClose.setAttribute("class", "btn");
btnClose.setAttribute("id", "wrapperBtnCloseExtruder");
//btnClose.style.position = "absolute";
//btnClose.style.top = "0";
//btnClose.style.right = "0";
btnClose.innerHTML = `<i id="btnCloseExtruder" class="material-icons">close</i>`;
wrapper.prepend(btnClose);
// Let's place the filter itself
containerExtruderLeft.replaceWith(wrapper);
$j(SIDEBAR_MAIN_EXTRUDER).buildMbExtruder({
attachToParentSide: "right", // Bind to right side of parent
bindToButtonByHeight: document.querySelector('#menuControlModule'), // Bind vertical position to button. Relevant when the button moves vertically, so that the extruder follows it.
position: "left",
selectorResponsiveBlock: '.filter-block',
extruderParentElement: document.getElementById('sidebarMain'),
noFlap: true,
width: 400,
topBlock: 300,
topFlap: 100,
extruderOpacity: .9,
//zIndex: 100,
onExtOpen: function() {},
onExtContentLoad: function() {},
onExtClose: function() {}
});
// Assign to Show/hide button
menuControlModule.on("click", function() {
if (!SIDEBAR_MAIN_EXTRUDER.classList.contains('isOpened')) {
$j(SIDEBAR_MAIN_EXTRUDER).openMbExtruder();
} else {
$j(SIDEBAR_MAIN_EXTRUDER).closeMbExtruder();
}
});
// NECESSARY BECAUSE DOM HAS BEEN REBUILDED!
dataOnClickThis();
dataOnClick();
dataOnClickTrue();
dataOnChangeThis();
dataOnChange();
dataOnInput();
dataOnInputThis();
applyChosen();
initDatepicker();
if (getCookie('zmVisibleMbExtruder')) {
// The extruder panel was open when the previous page was closed. Perhaps the filter settings were changed before the previous page was closed.
$j(SIDEBAR_MAIN_EXTRUDER).openMbExtruder();
}
/* Need to change overflow for extruder sidebar
* If the "chosen" drop-down list fits vertically into the browser window, then overflow = 'visible' is for beauty.
* That is, the list will be on top of the block, and the block will not increase in height. Otherwise, we will set overflow = 'auto' for the panel so that the "chosen" drop-down list is fully accessible.
* We listen to ".chosen-container" for ".chosen-container-active" to be added or removed.
*/
document.querySelectorAll(".chosen-container").forEach(function(el) {
const ob = new MutationObserver(function() {
const parent = SIDEBAR_MAIN_EXTRUDER.querySelector('.filter-block');
if (!parent) return; // Page closing moment
if (el.classList.contains("chosen-container-active")) {
const chosenDropBlock = el.querySelector('.chosen-drop');
//const [leftDropBlock, topDropBlock] = findPos(chosenDropBlock); // Eslint complains about not using leftDropBlock
const topDropBlock = findPos(chosenDropBlock)[1];
if (chosenDropBlock.clientHeight + topDropBlock > window.innerHeight ) { // There is not enough space at the bottom
parent.style.overflow = 'auto';
} else {
parent.style.overflow = 'visible';
}
} else {
//parent.style.overflow = 'visible';
}
});
ob.observe(el, {
attributes: true,
attributeFilter: ["class"]
});
});
}
// Get the position of an element. Relevant for nested position == fixed&absolute
function findPos(obj, foundScrollLeft, foundScrollTop) {
var curleft = 0;
var curtop = 0;
if (obj.offsetLeft) curleft += parseInt(obj.offsetLeft);
if (obj.offsetTop) curtop += parseInt(obj.offsetTop);
if (obj.scrollTop && obj.scrollTop > 0) {
curtop -= parseInt(obj.scrollTop);
foundScrollTop = true;
}
if (obj.scrollLeft && obj.scrollLeft > 0) {
curleft -= parseInt(obj.scrollLeft);
foundScrollLeft = true;
}
if (obj.offsetParent) {
var pos = findPos(obj.offsetParent, foundScrollLeft, foundScrollTop);
curleft += pos[0];
curtop += pos[1];
} else if (obj.ownerDocument) {
var thewindow = obj.ownerDocument.defaultView;
if (!thewindow && obj.ownerDocument.parentWindow) thewindow = obj.ownerDocument.parentWindow;
if (thewindow) {
if (!foundScrollTop && thewindow.scrollY && thewindow.scrollY > 0) curtop -= parseInt(thewindow.scrollY);
if (!foundScrollLeft && thewindow.scrollX && thewindow.scrollX > 0) curleft -= parseInt(thewindow.scrollX);
if (thewindow.frameElement) {
var pos = findPos(thewindow.frameElement);
curleft += pos[0];
curtop += pos[1];
}
}
}
return [curleft, curtop];
}
/* Handling <input> change */
function handleChangeInputTag(evt) {
// Managing availability of channel stream selection
manageRTSP2WebChannelStream();
}
/* Handling a mouse click */
function handleClickGeneral(evt) {
const target = evt.target;
if (!useOldMenuView) {
if (SIDEBAR_MAIN_EXTRUDER.contains(target)) {
// Click on any element inside the extruder panel from the Sidebar
} else {
// Click outside the extruder panel
if (!SIDEBAR_MAIN.contains(target) &&
//"datepicker" changes the DOM very quickly when the month changes. Closest does not have time to go through all the parents, but only manages to get to the first parent. It is necessary to additionally analyze '.ui-datepicker-prev' and '.ui-datepicker-next'
!target.closest('.ui-datepicker') &&
!target.closest('.ui-datepicker-prev') &&
!target.closest('.ui-datepicker-next') &&
!target.matches('button[name="deleteBtn"]')) { // Multi select clear button
// Click outside the extruder panel Sidebar (except clicking on "datepicker"). Close the extruder panel
closeMbExtruder();
}
}
// Collapse or expand the menu.
if (BTN_COLLAPSE && BTN_COLLAPSE.contains(target)) {
setTimeout(function() {
// Call only after finishing expanding or collapsing. Otherwise the dimensions will be incorrect.
const collapsed = SIDEBAR_MAIN.classList.contains('collapsed');
setCookie('zmSidebarMainCollapse', collapsed);
changeAttrTitle(collapsed);
}, 500);
}
// Extruder panel close button with filters. Appears on narrow screens
if ('btnCloseExtruder' == target.id && SIDEBAR_MAIN_EXTRUDER) {
closeMbExtruder();
}
}
// Toggle between using the old or new menu
if (document.getElementById('useOldMenuView') == target) {
setCookie('zmUseOldMenuView', target.checked);
refreshWindow();
}
// Click on <input> to open the "datepicker" window
// For ui-datepicker we track the change of style "display" and to front
// This is necessary because some blocks, such as "extruder" always push themselves to the foreground. At the same time, "datepicker" should ALWAYS be displayed on top of all blocks. "z-index: XXX !important will not help in this case!
if (target.classList.contains('hasDatepicker')) {
const datepicker = document.getElementById('ui-datepicker-div');
if (datepicker) {
datepicker.style.removeProperty('z-index'); // Without this, problems are possible.
datepicker.style.setProperty('z-index', '1000000', 'important'); //Now "datepicker" will always be displayed on top of other blocks.
}
}
}
/* Handle any action on the touch screen */
function handleTouchActionGeneral(action, evt) {
//https://developer.mozilla.org/en-US/docs/Web/API/Touch_events
if (action == 'touchstart') {
managePanZoomButton(evt);
} else if (action == 'touchend') {
} else if (action == 'touchcancel') {
} else if (action == 'touchmove') {
//evt.preventDefault();
}
}
/* Processing any key press*/
function handleKeydownGeneral(evt) {
const target = evt.target;
const key = evt.key;
// Controls pressing "Enter" inside the sliding panel from Sidebar. Used to submit the form to the Console page.
if (!useOldMenuView && key == 'Enter') {
if (SIDEBAR_MAIN_EXTRUDER.contains(target)) {
submitThisForm();
}
}
}
function handleMouseover(evt) {
manageVisibilityVideoPlayerControlPanel(evt, 'show');
if (!useOldMenuView) {
manageVisibilitySidebarExtruderPanel(evt, 'show');
}
}
function handleMouseout(evt) {
manageVisibilityVideoPlayerControlPanel(evt, 'hide');
if (!useOldMenuView) {
manageVisibilitySidebarExtruderPanel(evt, 'hide');
}
}
function manageVisibilitySidebarExtruderPanel(evt, action) {
if (!showExtruderPanelOnMouseHover) return;
if (evt.target.closest('#menuControlModule') &&
// Avoid false positives when moving mouse inside '#menuControlModule'
(evt.relatedTarget && evt.relatedTarget.closest('#menuControlModule') != evt.target.closest('#menuControlModule'))) {
if (action == 'show') {
$j(SIDEBAR_MAIN_EXTRUDER).openMbExtruder();
} else if (action == 'hide') {
// We don't do anything yet, because now the block closes either when you click on the button or anywhere on the page
}
}
}
function manageVisibilityVideoPlayerControlPanel(evt, action) {
@@ -1466,16 +1850,30 @@ function manageVisibilityVideoPlayerControlPanel(evt, action) {
}
}
/* Handle any action on the touch screen */
function handleTouchActionGeneral(action, evt) {
//https://developer.mozilla.org/en-US/docs/Web/API/Touch_events
if (action == 'touchstart') {
managePanZoomButton(evt);
} else if (action == 'touchend') {
} else if (action == 'touchcancel') {
} else if (action == 'touchmove') {
//evt.preventDefault();
function initDatepicker() {
if (currentView == 'events') {
initDatepickerEventsPage();
} else if (currentView == 'montagereview') {
initDatepickerMontageReviewPage();
} else if (currentView == 'report_event_audit') {
initDatepickerReportEventAuditPage();
}
// When hiding the "datepicker" you need to clear the 'z-index' property so that it does not affect other elements
document.querySelectorAll(".ui-datepicker").forEach(function(el) {
const ob = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'style' && mutation.target.style.display === 'none' && mutation.target.style.zIndex) {
console.log(">>zIndex>", mutation.target.style.zIndex);
mutation.target.style.removeProperty('z-index');
}
});
});
ob.observe(el, {
attributes: true,
attributeFilter: ["style"]
});
});
}
function managePanZoomButton(evt) {
@@ -1490,6 +1888,7 @@ function managePanZoomButton(evt) {
} else {
$j('#button_zoom' + stringToNumber(targetId)).removeClass('hidden');
}
if (!('getAttribute' in evt.currentTarget)) return; // Touchscreen tap on '.imageFeed' does not have 'currentTarget'
//evt.preventDefault();
// We are looking for an object with an ID, because there may be another element in the button.
const obj = targetId ? evt.target : evt.target.parentElement;
@@ -1533,13 +1932,90 @@ function managePanZoomButton(evt) {
}
}
function closeMbExtruder(updateCookie = false) {
if (!SIDEBAR_MAIN_EXTRUDER) return;
if (updateCookie) {
// We will save the panel visibility in cookies so that we can restore the state after opening a new page.
// If the panel was open, it means that the filter was most likely adjusted.
// It is probably worth analyzing whether a new page will be opened or the old one but with different parameters.
setCookie('zmVisibleMbExtruder', !!SIDEBAR_MAIN_EXTRUDER.classList.contains('isOpened'));
}
$j(SIDEBAR_MAIN_EXTRUDER).closeMbExtruder();
}
/*
* The call is configured in \www\skins\classic\views\_monitor_filters.php
* If many options were selected in the filter, then deleting them one by one takes a long time; it is easier to delete everything with one button.
*/
function resetSelectElement(el) {
console.log("clearSelectMultiply_el=>", el);
const selectElement = document.querySelector('select[name="'+el.getAttribute('data-select-target')+'"]');
if (!selectElement) return;
Array.from(selectElement.options).forEach((option) => {
option.selected = false;
});
applyChosen(selectElement);
if (currentView == 'events') {
filterEvents(clickedElement = selectElement);
} else {
submitThisForm();
}
}
function initPageGeneral() {
$j(document).on('keyup.global keydown.global', function(e) {
$j(document).on('keyup.global keydown.global', function handleKey(e) {
shifted = e.shiftKey ? e.shiftKey : e.shift;
ctrled = e.ctrlKey;
alted = e.altKey;
});
if (!useOldMenuView) {
if ((!isTouchDevice() || !isMobile()) && NAVBAR_RELOAD) {
// Increase the width of the scrollbar for NON-mobile or NON-touch devices
NAVBAR_RELOAD.classList.add('high-scroll-bar');
}
changeAttrTitle();
setTimeout(function() {
// It takes time for all DOM actions to complete, especially with regard to block visibility.
insertControlModuleMenu();
isNavbarOverflown();
}, 200);
// Swipe support, configurable via "data-swipe" in \skins\classic\includes\functions.php
document.addEventListener('swiped', function addListenerGlobalSwiped(e) {
//console.log(e.detail); // see event data below
if (e.detail.dir == 'right') {
if (e.detail.xStart < 80) {
// SideBar Management
if (SIDEBAR_MAIN.classList.contains('toggled')) { // The menu is already visible, but you can expand it wider
SIDEBAR_MAIN.classList.remove('collapsed');
} else {
SIDEBAR_MAIN.classList.add('toggled');
}
}
} else if (e.detail.dir == 'left') {
if ((e.detail.xStart < 80) || ((e.detail.xStart < 250) && (!SIDEBAR_MAIN.classList.contains('collapsed') && SIDEBAR_MAIN.classList.contains('toggled')))) {
// SideBar Management
if (!SIDEBAR_MAIN.classList.contains('collapsed') && SIDEBAR_MAIN.classList.contains('toggled')) { // Fully expanded
SIDEBAR_MAIN.classList.add('collapsed');
} else {
SIDEBAR_MAIN.classList.remove('toggled');
}
}
} else if (e.detail.dir == 'down') {
} else if (e.detail.dir == 'up') {
}
});
}
var observerNavbarReload = new window.ResizeObserver((entries) => {
endOfResize();
});
if (NAVBAR_RELOAD) {
observerNavbarReload.observe(NAVBAR_RELOAD);
}
if (['montage', 'watch', 'devices', 'reports', 'monitorpreset', 'monitorprobe', 'onvifprobe', 'timeline'].includes(currentView)) {
mainContent = document.getElementById('page');
} else if (currentView == 'options') {
@@ -1550,20 +2026,26 @@ function initPageGeneral() {
/* Assigning global handlers!
** IMPORTANT! It will not be possible to remove assigned handlers using the removeEventListener method, since the functions are anonymous
*/
document.body.addEventListener('input', function(event) {
document.body.addEventListener('input', function addListenerGlobalInputTag(event) {
handleChangeInputTag(event);
});
document.body.addEventListener('click', function addListenerGlobalClick(event) {
handleClickGeneral(event);
});
document.body.addEventListener('keydown', function addListenerGlobalKeydown(event) {
handleKeydownGeneral(event);
});
document.body.addEventListener('mouseover', function(event) {
document.body.addEventListener('mouseover', function addListenerGlobalMouseover(event) {
handleMouseover(event);
});
document.body.addEventListener('mouseout', function(event) {
document.body.addEventListener('mouseout', function addListenerGlobalMouseout(event) {
handleMouseout(event);
});
// Support for touch devices.
['touchstart', 'touchend', 'touchcancel', 'touchmove'].forEach(function(action) {
document.addEventListener(action, function(event) {
document.addEventListener(action, function addListenerGlobalTouchAction(event) {
handleTouchActionGeneral(action, event);
}, {passive: false}); // false - to avoid an error "Unable to preventDefault inside passive event listener due to target being treated as passive."
});
@@ -1588,11 +2070,10 @@ function initPageGeneral() {
window.addEventListener('beforeunload', function addListenerGlobalBeforeunload(event) {
//event.preventDefault();
/*
if (!useOldMenuView) {
closeMbExtruder(updateCookie = true);
}
*/
if (mainContentJ) {
if (mainContentJ.css('display') == 'flex') {
// If flex-grow is set to a value > 0 then "height" will be ignored!

View File

@@ -31,10 +31,26 @@ function addFilterSelect($name, $options) {
)
);
$html .= '</span>';
$html .= addButtonResetForFilterSelect($name.'[]');
$html .= '</span>'.PHP_EOL;
return $html;
}
function addButtonResetForFilterSelect($nameSelect) {
if (isset($_COOKIE['zmUseOldMenuView']) and $_COOKIE["zmUseOldMenuView"] === 'true') {
$html = '';
} else {
$html = PHP_EOL . '
<span class="btn-term-remove-all">
<button type="button" name="deleteBtn" data-on-click-this="resetSelectElement" data-select-target="'.$nameSelect.'">
<i class="material-icons">clear</i>
<span class="text"></span>
</button>
</span>' . PHP_EOL;
}
return $html;
}
function buildMonitorsFilters() {
global $user, $Servers;
require_once('includes/Monitor.php');
@@ -87,6 +103,7 @@ function buildMonitorsFilters() {
$html .= ZM\Group::get_group_dropdown();
$groupSql = ZM\Group::get_group_sql($group_id);
$html .= '</span>';
$html .= addButtonResetForFilterSelect('GroupId[]');
$html .= '</span>';
}
}
@@ -141,6 +158,7 @@ function buildMonitorsFilters() {
)
);
$html .= '</span>';
$html .= addButtonResetForFilterSelect('ServerId[]');
$html .= '</span>';
} # end if have Servers
@@ -156,6 +174,7 @@ function buildMonitorsFilters() {
'data-placeholder'=>'All',
) );
$html .= '</span>';
$html .= addButtonResetForFilterSelect('StorageId[]');
$html .= '</span>';
} # end if have Storage Areas
@@ -176,6 +195,7 @@ function buildMonitorsFilters() {
'data-placeholder'=>'All'
) );
$html .= '</span>';
$html .= addButtonResetForFilterSelect('Status[]');
$html .= '</span>';
$html .= '<span class="term SourceFilter"><label>'.translate('Source').'</label>';
@@ -284,6 +304,7 @@ function buildMonitorsFilters() {
# Repurpose this variable to be the list of MonitorIds as a result of all the filtering
$display_monitor_ids = array_map(function($monitor_row){return $monitor_row['Id'];}, $displayMonitors);
$html .= '</span>';
$html .= addButtonResetForFilterSelect('MonitorId[]');
$html .= '</span>';
$html .= '</div>';

View File

@@ -513,6 +513,23 @@ function initPage() {
table.find('tr td:nth-child(' + (thumb_ndx+1) + ')').addClass('colThumbnail');
});
if (useOldMenuView) {
// If new menu is used, then Datepicker initialization occurs in main "skin.js"
// Reinitialization is not allowed because the 'Destroy' method is missing.
initDatepickerEventsPage();
}
window.onpageshow = function(evt) {
console.log('Refreshing table');
table.bootstrapTable('refresh');
};
table.bootstrapTable('resetSearch');
// The table is initially given a hidden style, so now that we are done rendering, show it
table.show();
}
function initDatepickerEventsPage() {
$j('#fieldsTable input, #fieldsTable select').each(function(index) {
const el = $j(this);
if (el.hasClass('datetimepicker')) {
@@ -523,15 +540,6 @@ function initPage() {
el.on('change', filterEvents);
}
});
window.onpageshow = function(evt) {
console.log('Refreshing table');
table.bootstrapTable('refresh');
};
table.bootstrapTable('resetSearch');
// The table is initially given a hidden style, so now that we are done rendering, show it
table.show();
}
function filterEvents(clickedElement) {

View File

@@ -1257,6 +1257,15 @@ function initPage() {
$j('#archive_status').bind('change', function() {
this.form.submit();
});
if (useOldMenuView) {
// If new menu is used, then Datepicker initialization occurs in main "skin.js"
// Reinitialization is not allowed because the 'Destroy' method is missing.
initDatepickerMontageReviewPage();
}
}
function initDatepickerMontageReviewPage() {
$j('#fieldsTable input, #fieldsTable select').each(function(index) {
const el = $j(this);
if (el.hasClass('datetimepicker')) {

View File

@@ -21,7 +21,7 @@ function datetime_change(newDate, oldData) {
}
}
function initPage() {
function initDatepickerReportEventAuditPage() {
$j('#minTime').datetimepicker({
timeFormat: "HH:mm:ss",
dateFormat: "yy-mm-dd",
@@ -40,5 +40,13 @@ function initPage() {
});
}
function initPage() {
if (useOldMenuView) {
// If new menu is used, then Datepicker initialization occurs in main "skin.js"
// Reinitialization is not allowed because the 'Destroy' method is missing.
initDatepickerReportEventAuditPage();
}
}
// Kick everything off
window.addEventListener( 'DOMContentLoaded', initPage );