This commit is contained in:
jr-k 2024-07-23 00:44:07 +02:00
parent fc4273b585
commit 629ac4654d
30 changed files with 377 additions and 162 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,9 @@
jQuery(document).ready(function ($) {
const loadDateTimePicker = function ($els, timeOnly) {
const loadDateTimePicker = function ($els) {
const d = new Date();
$els.each(function () {
var $el = $(this);
const $el = $(this);
const timeOnly = isScheduleInWeekMoment($el);
const options = {
enableTime: true,
@ -10,27 +11,53 @@ jQuery(document).ready(function ($) {
allowInput: false,
noCalendar: false,
allowInvalidPreload: false,
dateFormat: 'Y-m-d H:i',
dateFormat: timeOnly ? 'H:i' : 'Y-m-d H:i',
defaultHour: d.getHours(),
defaultMinute: d.getMinutes(),
onChange: function (selectedDates, dateStr, instance) {
const d = selectedDates[0];
const $target = $el.parents('.widget:eq(0)').find('.target');
$target.val(
d ? `${d.getMinutes()} ${d.getHours()} ${d.getDate()} ${(d.getMonth() + 1)} * ${d.getFullYear()}` : ''
);
callScheduleChange($el);
}
};
if (timeOnly) {
// options['noCalendar'] = true;
options['noCalendar'] = true;
} else {
if ($el.val().indexOf('-') < 0) {
$el.val('');
}
}
$el.flatpickr(options);
$el.addClass('hidden');
});
};
const onInDateTimeMomentChanged = function($el) {
const $holder = $el.parents('.widget:eq(0)');
const $datetimepicker = $holder.find('.datetimepicker');
const $cronTarget = $holder.find('.target');
const matches = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2}) (?<hour>\d{2}):(?<minute>\d{2})/.exec($datetimepicker.val());
if (matches) {
const {year, month, day, hour, minute} = matches.groups;
$cronTarget.val(`${parseInt(minute)} ${parseInt(hour)} ${parseInt(day)} ${parseInt(month)} * ${parseInt(year)}`);
} else {
$cronTarget.val('');
}
};
const onInWeekMomentChanged = function($el) {
const $holder = $el.parents('.widget:eq(0)');
const $datetimepicker = $holder.find('.datetimepicker');
const $weekdaypicker = $holder.find('.weekdaypicker');
const $cronTarget = $holder.find('.target');
const matches = $datetimepicker.val().split(':').map(function(e) { return parseInt(e) });
if (matches.length === 2) {
[hour, minute] = matches;
$cronTarget.val(`${minute} ${hour} * * ${$weekdaypicker.val()}`);
} else {
$cronTarget.val('');
}
};
const getId = function ($el) {
return $el.hasClass('slide-item') ? $el.attr('data-level') : $el.parents('.slide-item:eq(0)').attr('data-level');
};
@ -49,6 +76,31 @@ jQuery(document).ready(function ($) {
});
};
const getScheduleValue = function ($el) {
const $scheduleGroup = $el.parents('.form-group:eq(0)');
const $cronTrigger = $scheduleGroup.find('.trigger');
return $cronTrigger.val();
};
const isScheduleInDateTimeMoment = function($el) {
const scheduleValue = getScheduleValue($el);
return scheduleValue === 'datetime';
};
const isScheduleInWeekMoment = function($el) {
const scheduleValue = getScheduleValue($el);
return scheduleValue === 'inweek';
};
const callScheduleChange = function($el) {
if (isScheduleInWeekMoment($el)) {
onInWeekMomentChanged($el);
}
if (isScheduleInDateTimeMoment($el)) {
onInDateTimeMomentChanged($el);
}
};
const inputSchedulerUpdate = function () {
const $modal = $('.modal-slide:visible');
const $scheduleStartGroup = $modal.find('.slide-schedule-group');
@ -66,6 +118,9 @@ jQuery(document).ready(function ($) {
const $datetimepickerStart = $scheduleStartGroup.find('.datetimepicker');
const $datetimepickerEnd = $scheduleEndGroup.find('.datetimepicker');
const $weekdaypickerStart = $scheduleStartGroup.find('.weekdaypicker');
const $weekdaypickerEnd = $scheduleEndGroup.find('.weekdaypicker');
const $isNotification = $modal.find('.slide-is-notification');
const isNotification = $isNotification.val() === '1';
@ -89,24 +144,27 @@ jQuery(document).ready(function ($) {
const isDatetimeEnd = $triggerEnd.val() === 'datetime';
const isStayloopEnd = $triggerEnd.val() === 'stayloop';
const isDurationEnd = $triggerEnd.val() === 'duration';
const isInWeekMomentEnd = $triggerEnd.val() === 'inweek';
const flushValueStart = isLoopStart;
const flushValueEnd = isLoopStart || isStayloopEnd || isDurationEnd;
const flushDuration = isNotification && isDatetimeEnd;
const datetimepickerWithCalendar = !isInWeekMomentStart;
const delegateDuration = $targetDelegateDuration.prop('checked');
function toggleVisibility() {
$targetCronFieldStart.toggleClass('hidden', !isCronStart);
$targetCronFieldEnd.toggleClass('hidden', !isCronEnd);
$datetimepickerStart.toggleClass('hidden', !isDatetimeStart);
$datetimepickerEnd.toggleClass('hidden', !isDatetimeEnd);
$datetimepickerStart.toggleClass('hidden', !(isDatetimeStart || isInWeekMomentStart));
$datetimepickerEnd.toggleClass('hidden', !isDatetimeEnd && !isInWeekMomentEnd);
$weekdaypickerStart.toggleClass('hidden', !isInWeekMomentStart);
$weekdaypickerEnd.toggleClass('hidden', !isInWeekMomentEnd);
$delegateDurationGroup.toggleClass('hidden', (isNotification && isDatetimeEnd) || !isVideo);
$durationGroup.toggleClass('hidden', (isNotification && isDatetimeEnd) || $targetDelegateDuration.prop('checked'));
$durationGroup.toggleClass('hidden', (isNotification && isDatetimeEnd) || delegateDuration);
$targetDuration.prop('required', $durationGroup.is(':visible'));
$targetDelegateDuration.prop('disabled', !$delegateDurationGroup.is(':visible'));
$scheduleEndGroup.toggleClass('hidden', isLoopStart);
}
@ -126,9 +184,14 @@ jQuery(document).ready(function ($) {
}
}
loadDateTimePicker($modal.find('.datetimepicker'), datetimepickerWithCalendar);
loadDateTimePicker($modal.find('.datetimepicker'));
toggleVisibility();
flushValues();
callScheduleChange($weekdaypickerStart);
callScheduleChange($weekdaypickerEnd);
callScheduleChange($datetimepickerStart);
callScheduleChange($weekdaypickerEnd);
};
const main = function () {
@ -138,6 +201,10 @@ jQuery(document).ready(function ($) {
});
};
$(document).on('change', '.weekdaypicker', function() {
callScheduleChange($(this));
});
$(document).on('change', '.modal-slide select.trigger, .modal-slide input.trigger', function () {
inputSchedulerUpdate();
});
@ -193,10 +260,12 @@ jQuery(document).ready(function ($) {
const $modal = $('.modal-slide:visible');
const hasCron = slide.cron_schedule && slide.cron_schedule.length > 0;
const hasDateTime = hasCron && validateCronDateTime(slide.cron_schedule);
const isInDateTimeMomentStart = hasCron && isCronInDatetimeMoment(slide.cron_schedule);
const isInWeekMomentStart = hasCron && isCronInWeekMoment(slide.cron_schedule);
const hasCronEnd = slide.cron_schedule_end && slide.cron_schedule_end.length > 0;
const hasDateTimeEnd = hasCronEnd && validateCronDateTime(slide.cron_schedule_end);
const isInDateTimeMomentEnd = hasCronEnd && isCronInDatetimeMoment(slide.cron_schedule_end);
const isInWeekMomentEnd = hasCronEnd && isCronInWeekMoment(slide.cron_schedule_end);
const isNotification = slide.is_notification;
const tclass = '#slide-' + (isNotification ? 'notification-' : '') + 'edit';
@ -214,22 +283,31 @@ jQuery(document).ready(function ($) {
$modal.find(tclass + '-duration').val(slide.duration);
$modal.find(tclass + '-enabled').prop('checked', slide.enabled);
$modal.find(tclass + '-cron-schedule').val(slide.cron_schedule).toggleClass('hidden', !hasCron || hasDateTime);
$modal.find(tclass + '-cron-schedule-trigger').val(hasDateTime ? 'datetime' : (hasCron ? 'cron' : 'loop'));
$modal.find(tclass + '-cron-schedule').val(slide.cron_schedule).toggleClass('hidden', !hasCron || isInDateTimeMomentStart || isInWeekMomentStart);
$modal.find(tclass + '-cron-schedule-trigger').val(isInWeekMomentStart ? 'inweek' : (isInDateTimeMomentStart ? 'datetime' : (hasCron ? 'cron' : 'loop')));
$modal.find(tclass + '-cron-schedule-end').val(slide.cron_schedule_end).toggleClass('hidden', !hasCronEnd || hasDateTimeEnd);
$modal.find(tclass + '-cron-schedule-end-trigger').val(hasDateTimeEnd ? 'datetime' : (hasCronEnd ? 'cron' : (isNotification ? 'duration' : 'stayloop')));
$modal.find(tclass + '-cron-schedule-datetimepicker').toggleClass('hidden', !hasDateTime).val(
hasDateTime ? getCronDateTime(slide.cron_schedule) : ''
);
$modal.find(tclass + '-cron-schedule-end-datetimepicker').toggleClass('hidden', !hasDateTimeEnd).val(
hasDateTimeEnd ? getCronDateTime(slide.cron_schedule_end) : ''
);
$modal.find(tclass + '-id').val(slide.id);
loadDateTimePicker($modal.find('.datetimepicker'));
inputCallbacks();
$modal.find(tclass + '-cron-schedule-end').val(slide.cron_schedule_end).toggleClass('hidden', !hasCronEnd || isInDateTimeMomentEnd || isInWeekMomentEnd);
$modal.find(tclass + '-cron-schedule-end-trigger').val(isInWeekMomentEnd ? 'inweek' : (isInDateTimeMomentEnd ? 'datetime' : (hasCronEnd ? 'cron' : (isNotification ? 'duration' : 'stayloop'))));
$modal.find(tclass + '-cron-schedule-datetimepicker').toggleClass('hidden', !(isInDateTimeMomentStart || isInWeekMomentStart)).val(
isInWeekMomentStart ? getCronTime(slide.cron_schedule) : (isInDateTimeMomentStart ? getCronDateTime(slide.cron_schedule) : '')
);
$modal.find(tclass + '-cron-schedule-weekdaypicker').toggleClass('hidden', !isInWeekMomentStart).val(
isInWeekMomentStart ? getCronDayInWeek(slide.cron_schedule) : '1'
);
$modal.find(tclass + '-cron-schedule-end-datetimepicker').toggleClass('hidden', !(isInDateTimeMomentEnd || isInWeekMomentEnd)).val(
isInWeekMomentEnd ? getCronTime(slide.cron_schedule_end) : (isInDateTimeMomentEnd ? getCronDateTime(slide.cron_schedule_end) : '')
);
$modal.find(tclass + '-cron-schedule-end-weekdaypicker').toggleClass('hidden', !isInWeekMomentEnd).val(
isInWeekMomentEnd ? getCronDayInWeek(slide.cron_schedule_end) : '1'
);
$modal.find(tclass + '-id').val(slide.id);
inputCallbacks();
loadDateTimePicker($modal.find('.datetimepicker'));
});
$(document).on('click', '.slide-delete', function () {

View File

@ -8,13 +8,28 @@ const getCronDateTime = function(cronExpression) {
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}`;
};
const validateCronDateTime = function(cronExpression) {
const getCronTime = function(cronExpression) {
const [minutes, hours, day, month, _, year] = cronExpression.split(' ');
return `${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}`;
};
const getCronDayInWeek = function(cronExpression) {
const [minutes, hours, day, month, day_week, year] = cronExpression.split(' ');
return day_week;
};
const isCronInDatetimeMoment = function(cronExpression) {
const pattern = /^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+\*\s+(\d+)$/;
return pattern.test(cronExpression);
};
const isCronInWeekMoment = function(cronExpression) {
const pattern = /^(\d+)\s+(\d+)\s+\*\s+\*\s+(\d+)$/;
return pattern.test(cronExpression);
};
const cronToDateTimeObject = function(cronExpression) {
if (!validateCronDateTime(cronExpression)) {
if (!isCronInDatetimeMoment(cronExpression)) {
return null;
}

View File

@ -6,7 +6,7 @@
}
html {
background-color: $gscale1;
background-color: $layoutBackground;
}
body, html {
@ -96,9 +96,7 @@ main {
align-items: flex-start;
flex: 1;
overflow-y: auto;
background: radial-gradient(circle at 0% 53%, rgba($pinkyRed, 0.8) 10%, transparent 45%),
radial-gradient(circle at 135% 53%, rgba($seaBlue, 0.8) 10%, transparent 95%),
radial-gradient(circle at 50% 80%, rgba($limeGreen, 0.8) 40%, transparent 95%);
background: $fancyBackground;
.page-content {
flex: 2;
@ -118,7 +116,7 @@ main {
justify-content: flex-start;
align-items: flex-start;
padding: 0 10px 40px 10px;
background: $gscale1;
background: $layoutBackground;
align-self: stretch;
}
}
@ -127,7 +125,7 @@ main {
flex: 1;
overflow-y: auto;
align-self: stretch;
background: $gscale1;
background: $layoutBackground;
border-top: none;
&.left-panel {
@ -138,7 +136,7 @@ main {
flex: 0.5;
overflow-y: auto;
padding: 0;
background: $gscale1;
background: $layoutBackground;
box-shadow: 1px 1px .5px .5px inset rgba($gscale0, 0.2);
max-width: 250px;
}

View File

@ -1,6 +1,6 @@
menu {
width: 300px;
background: $gscale1;
background: $layoutBackground;
overflow-y: auto;
overflow-x: visible;
padding: 20px;

View File

@ -197,7 +197,7 @@ ul.explr-dirview {
.img-holder {
width: 64px;
height: 64px;
background: $gscale07;
background: $gscale17;
border-radius: 8px;
display: flex;
flex-direction: column;

View File

@ -41,7 +41,7 @@
padding-bottom: 2px;
.modals-inner {
background: $gscale1;
background: $layoutBackground;
border-radius: 10px;
color: lighten($gscale0, 20%);
padding: 40px;

View File

@ -14,7 +14,7 @@
align-items: flex-start;
align-self: stretch;
margin: 1px 1px 28px 1px;
background: $gscale1;
background: $layoutBackground;
border-radius: $baseRadius;
border: 4px solid rgba($gscaleF, .05);
@ -46,7 +46,7 @@
}
&:nth-child(even) {
background-color: $gscale04;
background-color: $gscale14;
}
.pane-cell,

View File

@ -36,7 +36,7 @@
&.active {
border-left: 4px solid $seaBlue;
border-radius: $baseRadius;
border-bottom: 2px solid $gscale07;
border-bottom: 2px solid $gscale17;
background: $gscale2;
color: $seaBlue;

View File

@ -0,0 +1,5 @@
$layoutBackground: $gscale1;
$fancyBackground: radial-gradient(circle at 0% 53%, rgba($pinkyRed, 0.8) 10%, transparent 45%),
radial-gradient(circle at 135% 53%, rgba($seaBlue, 0.8) 10%, transparent 95%),
radial-gradient(circle at 50% 80%, rgba($limeGreen, 0.8) 40%, transparent 95%);

View File

@ -1,2 +1,8 @@
$limeGreen: $bitterGreen;
$yellow: rgb(255, 167, 10);
$layoutBackground: $white;
$fancyBackground: radial-gradient(circle at 0% 53%, rgba($seaBlue, 0.8) 10%, transparent 45%),
radial-gradient(circle at 135% 53%, rgba($seaBlue, 0.8) 10%, transparent 95%),
radial-gradient(circle at 50% 80%, rgba($bitterPurple, 0.8) 40%, transparent 95%);

View File

@ -11,23 +11,60 @@ ul.explr-dirview li a i {
color: $seaBlue;
}
button.btn-neutral:hover,
.btn.btn-neutral:hover {
box-shadow: 0 2px 0 1px $gkscale6 inset;
}
button,
.btn {
&.btn-pixel {
background: $white;
color: $gkscale4;
button.btn-naked,
.btn.btn-naked {
color: $gkscale7;
@include pixel-box(4, $gkscaleC);
&:hover {
&:hover {
@include pixel-box(6, $gkscaleC);
}
}
&.btn-naked {
color: $gkscale7;
&:hover {
box-shadow: 0 2px 0 1px $gkscale6 inset;
}
}
&.btn-neutral:hover {
box-shadow: 0 2px 0 1px $gkscale6 inset;
}
}
.tiles .tiles-inner .tile-item {
border-top: 2px solid transparent;
border-right: 2px solid transparent;
border-bottom: 2px solid transparent;
.tiles .tiles-inner .tile-item:hover, .tiles .tiles-inner .tile-item.active {
background: $white;
&:hover,
&.active {
border-left: 2px solid $gscale17;
border-top: 2px solid $gscale17;
border-right: 2px solid $gscale17;
border-bottom: 2px solid $gscale17;
background: $white;
}
&:hover,
&.active {
border-color: $seaBlue;
}
&:hover.starred,
&.active.starred {
border-color: $yellow;
}
&:hover.disabled,
&.active.disabled {
border-color: $gscale4;
}
}
.panes {
@ -37,41 +74,44 @@ button.btn-naked,
.pane-item,
tr {
&:nth-child(odd) {
background-color: $gkscaleF4;
background-color: $gkscaleF;
}
&:nth-child(even) {
background-color: $gscale1;
background-color: $gkscaleF7;
}
}
}
}
.form-group .widget select, .form-group .widget input, .form-group .widget textarea {
box-shadow: 0 2px 1px $gkscaleA, 0 4px 2px $gkscaleA inset;
box-shadow: 0 2px 1px $gkscaleD, 0 4px 2px $gkscaleD inset;
color: $gkscale5;
background: $gkscaleC;
background: $gkscaleE;
}
.toggle label {
box-shadow: 0 2px 2px $gkscaleA inset;
&::after {
box-shadow: 0 2px rgba($gkscaleA, 0.9);
.toggle {
label {
box-shadow: 0 2px 2px $gkscaleC inset;
&::after {
box-shadow: 0 2px rgba($gkscaleC, 0.9);
}
}
input:checked + label {
box-shadow: 0 2px 2px rgba($gkscale0, .3) inset;
}
}
.modal-explr-picker {
.explr-tree {
background: $white;
}
}
ul.pills {
box-shadow: 1px 1px .5px .5px inset rgba($gkscaleA, 0.2);
background: #EEE;
li {
a {
color: $gkscale4;
background: $white;
}
&.active {
@ -85,6 +125,8 @@ ul.pills {
.breadcrumb-container ul.breadcrumb {
box-shadow: 1px 1px .5px .5px inset rgba($gkscaleA, 0.2);
background: #EEE;
li a,
li span,
li {
@ -100,23 +142,14 @@ ul.pills {
}
}
.view-player-group-list main .main-container .bottom-content .page-content .inner .node-player-group-holder .preview,
.view-playlist-list main .main-container .bottom-content .page-content .inner .playlist-holder .preview {
border-color: $gkscaleC;
}
.view-player-group-list main .main-container .players-holder ul.players li.player-item .body,
.view-playlist-list main .main-container .slides-holder ul.slides li.slide-item .body {
background: $gkscaleD;
}
.dropdown ul.dropdown-menu li.danger:hover a {
color: $white;
}
.inner-empty i {
color: $gkscaleB;
text-shadow: 0 -1px $gkscale7, 0 0px .5px $gkscale4;
color: $gkscaleD;
text-shadow: 0 -1px $gkscale9, 0 0px .5px $gkscale6;
}
.view-player-group-list main .main-container .players-holder ul.players li.player-item .tail a,
@ -126,4 +159,29 @@ ul.pills {
&:hover {
color: $white;
}
}
}
.form-group {
.widget {
select,
input,
textarea {
&.disabled,
&[disabled] {
border: none;
background: $gkscaleE;
border-radius: $baseRadius;
padding-left: 10px;
padding-right: 10px;
}
}
}
}
.modal-explr-picker {
.explr-tree {
background: $gkscaleF7;
}
}

View File

@ -100,7 +100,7 @@
justify-content: flex-start;
align-items: center;
margin: 0 10px 0 10px;
background: $gscale0B;
background: $gscale1B;
padding: 10px;
align-self: stretch;
flex: 1;

View File

@ -115,7 +115,7 @@
justify-content: flex-start;
align-items: center;
margin: 0 10px 0 10px;
background: $gscale0B;
background: $gscale1B;
padding: 10px;
align-self: stretch;
flex: 1;

View File

@ -24,8 +24,8 @@
color: transparent;
}
@mixin pixel-box($pixelOffset: 1) {
box-shadow: #{$pixelOffset}px 0 0 $white, 0 #{$pixelOffset}px 0 $limeGreen, -#{$pixelOffset}px 0 0 $seaBlue, 0 -#{$pixelOffset}px 0 $pinkyRed;
@mixin pixel-box($pixelOffset: 1, $whiteColor: $white) {
box-shadow: #{$pixelOffset}px 0 0 $whiteColor, 0 #{$pixelOffset}px 0 $limeGreen, -#{$pixelOffset}px 0 0 $seaBlue, 0 -#{$pixelOffset}px 0 $pinkyRed;
}
@mixin generate-color-classes($color-map) {

View File

@ -1,10 +1,11 @@
// Greyscales
$gscale0: #000000;
$gscale04: #141414;
$gscale05: #151515;
$gscale07: #171717;
$gscale0B: #1B1B1B;
$gscale07: #070707;
$gscale14: #141414;
$gscale15: #151515;
$gscale17: #171717;
$gscale1B: #1B1B1B;
$gscale1: #111;
$gscale2: #222;
$gscale3: #333;
@ -19,17 +20,19 @@ $gscaleB: #BBB;
$gscaleC: #CCC;
$gscaleD: #DDD;
$gscaleE: #EEE;
$gscaleF4: #E4E4E4;
$gscaleF5: #E5E5E5;
$gscaleF7: #E7E7E7;
$gscaleFB: #EBEBEB;
$gscaleE4: #E4E4E4;
$gscaleE5: #E5E5E5;
$gscaleE7: #E7E7E7;
$gscaleEB: #EBEBEB;
$gscaleF7: #F7F7F7;
$gscaleF: #FFFFFF;
$gkscale0: #000000;
$gkscale04: #141414;
$gkscale05: #151515;
$gkscale07: #171717;
$gkscale0B: #1B1B1B;
$gkscale07: #070707;
$gkscale14: #141414;
$gkscale15: #151515;
$gkscale17: #171717;
$gkscale1B: #1B1B1B;
$gkscale1: #111;
$gkscale2: #222;
$gkscale3: #333;
@ -44,8 +47,9 @@ $gkscaleB: #BBB;
$gkscaleC: #CCC;
$gkscaleD: #DDD;
$gkscaleE: #EEE;
$gkscaleF4: #E4E4E4;
$gkscaleF5: #E5E5E5;
$gkscaleF7: #E7E7E7;
$gkscaleFB: #EBEBEB;
$gkscaleE4: #E4E4E4;
$gkscaleE5: #E5E5E5;
$gkscaleE7: #E7E7E7;
$gkscaleEB: #EBEBEB;
$gkscaleF7: #F7F7F7;
$gkscaleF: #FFFFFF;

View File

@ -1,10 +1,11 @@
// Greyscales
$gscale0: #FFFFFF;
$gscale04: #E4E4E4;
$gscale05: #E5E5E5;
$gscale07: #E7E7E7;
$gscale0B: #EBEBEB;
$gscale07: #F7F7F7;
$gscale1B: #EBEBEB;
$gscale17: #E7E7E7;
$gscale15: #E5E5E5;
$gscale14: #E4E4E4;
$gscale1: #EEE;
$gscale2: #DDD;
$gscale3: #CCC;
@ -19,17 +20,19 @@ $gscaleB: #444;
$gscaleC: #333;
$gscaleD: #222;
$gscaleE: #111;
$gscaleF4: #141414;
$gscaleF5: #151515;
$gscaleF7: #171717;
$gscaleFB: #1B1B1B;
$gscaleE4: #141414;
$gscaleE5: #151515;
$gscaleE7: #171717;
$gscaleEB: #1B1B1B;
$gscaleF7: #070707;
$gscaleF: #000000;
$gkscale0: #000000;
$gkscale04: #141414;
$gkscale05: #151515;
$gkscale07: #171717;
$gkscale0B: #1B1B1B;
$gkscale07: #070707;
$gkscale14: #141414;
$gkscale15: #151515;
$gkscale17: #171717;
$gkscale1B: #1B1B1B;
$gkscale1: #111;
$gkscale2: #222;
$gkscale3: #333;
@ -44,8 +47,9 @@ $gkscaleB: #BBB;
$gkscaleC: #CCC;
$gkscaleD: #DDD;
$gkscaleE: #EEE;
$gkscaleF4: #E4E4E4;
$gkscaleF5: #E5E5E5;
$gkscaleF7: #E7E7E7;
$gkscaleFB: #EBEBEB;
$gkscaleE4: #E4E4E4;
$gkscaleE5: #E5E5E5;
$gkscaleE7: #E7E7E7;
$gkscaleEB: #EBEBEB;
$gkscaleF7: #F7F7F7;
$gkscaleF: #FFFFFF;

View File

@ -48,6 +48,7 @@
"slideshow_slide_form_label_cron_scheduled_end": "End",
"slideshow_slide_form_label_cron_scheduled_loop": "Always in loop",
"slideshow_slide_form_label_cron_scheduled_duration": "Duration",
"slideshow_slide_form_label_cron_scheduled_inweek": "Moment in week",
"slideshow_slide_form_label_cron_scheduled_stayloop": "Follow the loop",
"slideshow_slide_form_label_cron_scheduled_duration_unit": "seconds",
"slideshow_slide_form_label_cron_scheduled_datetime": "Date & Time",

View File

@ -48,6 +48,7 @@
"slideshow_slide_form_label_cron_scheduled_end": "Fin",
"slideshow_slide_form_label_cron_scheduled_loop": "Siempre en bucle",
"slideshow_slide_form_label_cron_scheduled_duration": "Duración",
"slideshow_slide_form_label_cron_scheduled_inweek": "Momento de la semana",
"slideshow_slide_form_label_cron_scheduled_stayloop": "Seguir el bucle",
"slideshow_slide_form_label_cron_scheduled_duration_unit": "segundos",
"slideshow_slide_form_label_cron_scheduled_datetime": "Fecha y Hora",

View File

@ -48,6 +48,7 @@
"slideshow_slide_form_label_cron_scheduled_end": "Fin",
"slideshow_slide_form_label_cron_scheduled_loop": "Toujours en boucle",
"slideshow_slide_form_label_cron_scheduled_duration": "Durée",
"slideshow_slide_form_label_cron_scheduled_inweek": "Moment de la semaine",
"slideshow_slide_form_label_cron_scheduled_stayloop": "Suit la boucle",
"slideshow_slide_form_label_cron_scheduled_duration_unit": "secondes",
"slideshow_slide_form_label_cron_scheduled_datetime": "Date & Heure",

View File

@ -48,6 +48,7 @@
"slideshow_slide_form_label_cron_scheduled_end": "Fine",
"slideshow_slide_form_label_cron_scheduled_loop": "Sempre in loop",
"slideshow_slide_form_label_cron_scheduled_duration": "Durata",
"slideshow_slide_form_label_cron_scheduled_inweek": "Momento della settimana",
"slideshow_slide_form_label_cron_scheduled_stayloop": "Seguire il ciclo",
"slideshow_slide_form_label_cron_scheduled_duration_unit": "secondi",
"slideshow_slide_form_label_cron_scheduled_datetime": "Data e ora",

View File

@ -12,7 +12,7 @@ from src.model.enum.ContentType import ContentType
from src.exceptions.NoFallbackPlaylistException import NoFallbackPlaylistException
from src.service.ModelStore import ModelStore
from src.interface.ObController import ObController
from src.util.utils import get_safe_cron_descriptor, is_cron_calendar_moment, is_cron_in_day_moment, get_cron_date_time
from src.util.utils import get_safe_cron_descriptor, is_cron_in_datetime_moment, is_cron_in_week_moment, is_now_after_cron_date_time_moment, is_now_after_cron_week_moment
from src.util.UtilNetwork import get_safe_remote_addr, get_network_interfaces
from src.model.enum.AnimationSpeed import animation_speed_duration
@ -170,22 +170,28 @@ class PlayerController(ObController):
return playlists
def _check_slide_enablement(self, loop: List, notifications: List, slide: Dict) -> None:
has_valid_start_date = 'cron_schedule' in slide and slide['cron_schedule'] and get_safe_cron_descriptor(slide['cron_schedule']) and is_cron_calendar_moment(slide['cron_schedule'])
has_valid_end_date = 'cron_schedule_end' in slide and slide['cron_schedule_end'] and get_safe_cron_descriptor(slide['cron_schedule_end']) and is_cron_calendar_moment(slide['cron_schedule_end'])
is_in_datetime_moment_start = 'cron_schedule' in slide and slide['cron_schedule'] and get_safe_cron_descriptor(slide['cron_schedule']) and is_cron_in_datetime_moment(slide['cron_schedule'])
is_in_datetime_moment_end = 'cron_schedule_end' in slide and slide['cron_schedule_end'] and get_safe_cron_descriptor(slide['cron_schedule_end']) and is_cron_in_datetime_moment(slide['cron_schedule_end'])
is_in_week_moment_start = 'cron_schedule' in slide and slide['cron_schedule'] and get_safe_cron_descriptor(slide['cron_schedule']) and is_cron_in_week_moment(slide['cron_schedule'])
is_in_week_moment_end = 'cron_schedule_end' in slide and slide['cron_schedule_end'] and get_safe_cron_descriptor(slide['cron_schedule_end']) and is_cron_in_week_moment(slide['cron_schedule_end'])
if slide['is_notification']:
if has_valid_start_date:
if is_in_datetime_moment_start:
return notifications.append(slide)
return logging.warn('Slide \'{}\' is a notification but start date is invalid'.format(slide['name']))
if has_valid_start_date:
start_date = get_cron_date_time(slide['cron_schedule'], object=True)
if datetime.now() <= start_date:
if is_in_datetime_moment_start:
if not is_now_after_cron_date_time_moment(slide['cron_schedule']):
return
elif is_in_week_moment_start:
if not is_now_after_cron_week_moment(slide['cron_schedule']):
return
if has_valid_end_date:
end_date = get_cron_date_time(slide['cron_schedule_end'], object=True)
if datetime.now() >= end_date:
if is_in_datetime_moment_end:
if is_now_after_cron_date_time_moment(slide['cron_schedule_end']):
return
elif is_in_week_moment_end:
if is_now_after_cron_week_moment(slide['cron_schedule_end']):
return
loop.append(slide)

View File

@ -11,9 +11,7 @@ from src.model.hook.StaticHookRegistration import StaticHookRegistration
from src.model.hook.FunctionalHookRegistration import FunctionalHookRegistration
from src.constant.WebDirConstant import WebDirConstant
from src.util.utils import get_safe_cron_descriptor, \
is_cron_calendar_moment, \
is_cron_in_day_moment, \
is_cron_in_week_moment, \
is_cron_in_datetime_moment, \
seconds_to_hhmmss, am_i_in_docker, \
truncate, merge_dicts, dictsort
@ -48,8 +46,7 @@ class TemplateRenderer:
cron_descriptor=self.cron_descriptor,
str=str,
seconds_to_hhmmss=seconds_to_hhmmss,
is_cron_calendar_moment=is_cron_calendar_moment,
is_cron_in_day_moment=is_cron_in_day_moment,
is_cron_in_datetime_moment=is_cron_in_datetime_moment,
json_dumps=json.dumps,
merge_dicts=merge_dicts,
dictsort=dictsort,

View File

@ -8,7 +8,7 @@ import unicodedata
import platform
from datetime import datetime
from datetime import datetime, timedelta
from typing import Optional, List, Dict
from enum import Enum
from cron_descriptor import ExpressionDescriptor
@ -59,21 +59,21 @@ def camel_to_snake(camel: str) -> str:
return CAMEL_CASE_TO_SNAKE_CASE_PATTERN.sub('_', camel).lower()
def is_cron_calendar_moment(expression: str) -> bool:
def is_cron_in_datetime_moment(expression: str) -> bool:
pattern = re.compile(r'^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+\*\s+(\d+)$')
return bool(pattern.match(expression))
def is_cron_in_week_moment(expression: str) -> bool:
pattern = re.compile(r'^(\d+)\s+(\d+)\s+\*\s+\*\s+(\d+)$')
return bool(pattern.match(expression))
def is_cron_in_year_moment(expression: str) -> bool:
pattern = re.compile(r'^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+\*\s+\*$')
return bool(pattern.match(expression))
def is_cron_in_week_moment(expression: str) -> bool:
pattern = re.compile(r'^(\d+)\s+(\d+)\s+\*\s+\*\s+(\d+)\s+\*$')
return bool(pattern.match(expression))
def is_cron_in_month_moment(expression: str) -> bool:
pattern = re.compile(r'^(\d+)\s+(\d+)\s+(\d+)\s+\*\s+\*\s+\*$')
return bool(pattern.match(expression))
@ -84,14 +84,29 @@ def is_cron_in_day_moment(expression: str) -> bool:
return bool(pattern.match(expression))
def get_cron_date_time(cron_expression: str, object: bool) -> str:
minutes, hours, day, month, _, year = cron_expression.split(' ')
def is_now_after_cron_date_time_moment(expression: str) -> bool:
minutes, hours, day, month, _, year = expression.split(' ')
formatted_date_time = f"{year}-{month.zfill(2)}-{day.zfill(2)} {hours.zfill(2)}:{minutes.zfill(2)}"
return datetime.strptime(formatted_date_time, '%Y-%m-%d %H:%M') if object else formatted_date_time
start_date = datetime.strptime(formatted_date_time, '%Y-%m-%d %H:%M')
return datetime.now() >= start_date
def is_now_after_cron_week_moment(cron_expression: str) -> bool:
minutes, hours, day, month, day_of_week = cron_expression.split(' ')
now = datetime.now()
week_start = now - timedelta(days=now.isoweekday() - 1)
for day_offset in range(7):
check_date = week_start + timedelta(days=day_offset)
if check_date.isoweekday() == int(day_of_week):
check_date = check_date.replace(hour=int(hours), minute=int(minutes), second=0, microsecond=0)
if datetime.now() >= check_date:
return True
return False
def get_safe_cron_descriptor(expression: str, use_24hour_time_format=True, locale_code: Optional[str] = None) -> str:
if is_cron_calendar_moment(expression):
if is_cron_in_datetime_moment(expression):
[minutes, hours, day, month, _, year] = expression.split(' ')
return "{}-{}-{} at {}:{}".format(
year,

View File

@ -15,12 +15,6 @@
<button type="button" class="btn btn-info node-player-group-player-assign" data-route="{{ url_for('fleet_node_player_group_assign_player', player_group_id=current_player_group.id, player_id='__id__') }}">
<i class="fa fa-plus"></i> {{ l.fleet_node_player_group_assign_player }}
</button>
<a href="{{ url_for('fleet_node_player_list') }}" class="btn btn-neutral" target="_blank">
<i class="fa fa-display main"></i>
<sub>
<i class="fa fa-download"></i>
</sub>
</a>
</div>
</div>

View File

@ -399,8 +399,8 @@
const isFullyElapsedMinute = (new Date()).getSeconds() === 0;
const hasCron = item.cron_schedule && item.cron_schedule.length > 0;
const hasCronEnd = item.cron_schedule_end && item.cron_schedule_end.length > 0;
const hasDateTime = hasCron && validateCronDateTime(item.cron_schedule);
const hasDateTimeEnd = hasCronEnd && validateCronDateTime(item.cron_schedule_end);
const hasDateTime = hasCron && isCronInDatetimeMoment(item.cron_schedule);
const hasDateTimeEnd = hasCronEnd && isCronInDatetimeMoment(item.cron_schedule_end);
if (notificationItemIndex !== i && hasDateTime) {
const startDate = cronToDateTimeObject(item.cron_schedule);

View File

@ -12,16 +12,14 @@
{% block add_js %}
<script type="text/javascript">
var route_slide_position = '{{ url_for('slideshow_slide_position') }}';
var choices_translations = {
'loop': '{{ l.slideshow_slide_form_label_cron_scheduled_loop }}',
'datetime': '{{ l.slideshow_slide_form_label_cron_scheduled_datetime }}',
'cron': '{{ l.slideshow_slide_form_label_cron_scheduled_cron }}',
'stayloop': '{{ l.slideshow_slide_form_label_cron_scheduled_stayloop }}',
'duration': '{{ l.slideshow_slide_form_label_cron_scheduled_duration }}',
'inweek': 'in week',
'inweek': '{{ l.slideshow_slide_form_label_cron_scheduled_inweek }}',
};
var choices_map = {
'normal': {
@ -30,14 +28,19 @@
'stayloop': choices_translations['stayloop'],
'datetime': choices_translations['datetime']
},
{#'inweek': {'stayloop': choices_translations['stayloop'], 'inweek': choices_translations['inweek']},#}
'inweek': {
'stayloop': choices_translations['stayloop'],
'inweek': choices_translations['inweek']
}
},
'notification': {
'datetime': {
'datetime': choices_translations['datetime'],
'duration': choices_translations['duration']
},
'cron': {'duration': choices_translations['duration']}
'cron': {
'duration': choices_translations['duration']
}
}
}
var contents = {{ json_dumps(contents) | safe }}

View File

@ -26,7 +26,7 @@
{% if slide.cron_schedule %}
{% set cron_desc = cron_descriptor(slide.cron_schedule) %}
{% if cron_desc %}
{% if is_cron_calendar_moment(slide.cron_schedule) %}
{% if is_cron_in_datetime_moment(slide.cron_schedule) %}
{% if slide.is_notification %}
🔔 <span class="prefix">{{ l.slideshow_slide_panel_td_cron_scheduled_notify }}</span>
<span class="cron-description">{{ cron_desc }}</span>
@ -48,7 +48,7 @@
{% if slide.cron_schedule_end %}
{% set cron_desc_end = cron_descriptor(slide.cron_schedule_end) %}
{% if cron_desc_end %}
{% if is_cron_calendar_moment(slide.cron_schedule_end) %}
{% if is_cron_in_datetime_moment(slide.cron_schedule_end) %}
{% if slide.is_notification %}
📆 <span class="prefix">{{ l.slideshow_slide_panel_td_cron_scheduled_date }}</span>
<span class="cron-description">{{ cron_desc_end }}</span>

View File

@ -54,6 +54,13 @@
<label for="{{ tclass }}-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label>
<div class="widget">
<select id="{{ tclass }}-cron-schedule-trigger" class="trigger"></select>
<select name="" id="{{ tclass }}-cron-schedule-weekdaypicker" class="input-naked weekdaypicker">
{% for day_number in range(1,8) %}
<option value="{{ day_number }}">
{{ t('basic_day_' ~ day_number) }}
</option>
{% endfor %}
</select>
<input type="text" id="{{ tclass }}-cron-schedule-datetimepicker" class="input-naked datetimepicker"
value=""
placeholder="{{ l.slideshow_slide_form_label_cron_scheduled_datetime_placeholder }}"/>
@ -66,6 +73,13 @@
<label for="{{ tclass }}-cron-schedule-end">{{ l.slideshow_slide_form_label_cron_scheduled_end }}</label>
<div class="widget">
<select id="{{ tclass }}-cron-schedule-end-trigger" class="trigger"> </select>
<select name="" id="{{ tclass }}-cron-schedule-end-weekdaypicker" class="input-naked weekdaypicker">
{% for day_number in range(1,8) %}
<option value="{{ day_number }}">
{{ t('basic_day_' ~ day_number) }}
</option>
{% endfor %}
</select>
<input type="text" id="{{ tclass }}-cron-schedule-end-datetimepicker" class="input-naked datetimepicker"
value=""
placeholder="{{ l.slideshow_slide_form_label_cron_scheduled_datetime_placeholder }}"/>

View File

@ -52,6 +52,13 @@
<label for="{{ tclass }}-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label>
<div class="widget">
<select id="{{ tclass }}-cron-schedule-trigger" class="trigger"></select>
<select name="" id="{{ tclass }}-cron-schedule-weekdaypicker" class="input-naked weekdaypicker">
{% for day_number in range(1,8) %}
<option value="{{ day_number }}">
{{ t('basic_day_' ~ day_number) }}
</option>
{% endfor %}
</select>
<input type="text" id="{{ tclass }}-cron-schedule-datetimepicker" class="input-naked datetimepicker"
value=""
placeholder="{{ l.slideshow_slide_form_label_cron_scheduled_datetime_placeholder }}"/>
@ -64,6 +71,13 @@
<label for="{{ tclass }}-cron-schedule-end">{{ l.slideshow_slide_form_label_cron_scheduled_end }}</label>
<div class="widget">
<select id="{{ tclass }}-cron-schedule-end-trigger" class="trigger"></select>
<select name="" id="{{ tclass }}-cron-schedule-end-weekdaypicker" class="input-naked weekdaypicker">
{% for day_number in range(1,8) %}
<option value="{{ day_number }}">
{{ t('basic_day_' ~ day_number) }}
</option>
{% endfor %}
</select>
<input type="text" id="{{ tclass }}-cron-schedule-end-datetimepicker" class="input-naked datetimepicker"
value=""
placeholder="{{ l.slideshow_slide_form_label_cron_scheduled_datetime_placeholder }}"/>