Merge branch 'helping-hand' into 'dev'
Helping Hand : Active Tutorial See merge request Shinobi-Systems/Shinobi!413refactor-sql-commands-on-startup
commit
aed15d3623
|
|
@ -6796,8 +6796,8 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
"Log Viewer": {
|
||||
"section": "Log Viewer",
|
||||
"blocks": {
|
||||
|
|
@ -8889,5 +8889,224 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
}
|
||||
},
|
||||
"Help Window": {
|
||||
"section": "Help Window",
|
||||
"blocks": {
|
||||
"Column1": {
|
||||
"name": lang["Helping Hand"],
|
||||
"color": "navy",
|
||||
"blockquote": lang.helpFinderDescription,
|
||||
"section-pre-class": "col-md-4",
|
||||
"info": [
|
||||
{
|
||||
"id": "helpinghand-options",
|
||||
"field": lang["Active Tutorial"],
|
||||
"fieldType": "select",
|
||||
"possible": [
|
||||
// {
|
||||
// "name": lang['Date Updated'],
|
||||
// "value": "dateUpdated"
|
||||
// },
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": lang["Monitor"],
|
||||
"form-group-class": "helping-hand-target-monitor",
|
||||
"fieldType": "select",
|
||||
"class": "monitors_list",
|
||||
},
|
||||
{
|
||||
"fieldType": "btn",
|
||||
"class": `btn-primary fill watch-helping-hand mb-1`,
|
||||
"btnContent": lang.Run,
|
||||
},
|
||||
{
|
||||
"id": "helpinghand-results",
|
||||
"fieldType": "div",
|
||||
},
|
||||
]
|
||||
},
|
||||
"Column2": {
|
||||
noHeader: true,
|
||||
"color": "blue",
|
||||
"section-pre-class": "col-md-8",
|
||||
"noDefaultSectionClasses": true,
|
||||
"box-wrapper-class": "row",
|
||||
"info": [
|
||||
{
|
||||
title: "New to Shinobi?",
|
||||
info: `Try reading over some of these links to get yourself started.`,
|
||||
buttons: [
|
||||
{
|
||||
icon: 'newspaper-o',
|
||||
color: 'default',
|
||||
text: 'After Installation Guides',
|
||||
href: 'https://shinobi.video/docs/configure',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'plus',
|
||||
color: 'default',
|
||||
text: 'Adding an H.264 Camera',
|
||||
href: 'https://shinobi.video/docs/configure#content-adding-an-h264h265-camera',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'plus',
|
||||
color: 'default',
|
||||
text: 'Adding an MJPEG Camera',
|
||||
href: 'https://shinobi.video/articles/2018-09-19-how-to-add-an-mjpeg-camera',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'gears',
|
||||
color: 'default',
|
||||
text: 'RTSP Camera Optimization',
|
||||
href: 'https://shinobi.video/articles/2017-07-29-how-i-optimized-my-rtsp-camera',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'comments-o',
|
||||
color: 'info',
|
||||
text: 'Community Chat',
|
||||
href: 'https://discord.gg/ehRd8Zz',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'reddit',
|
||||
color: 'info',
|
||||
text: 'Forum on Reddit',
|
||||
href: 'https://www.reddit.com/r/ShinobiCCTV',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'file-o',
|
||||
color: 'primary',
|
||||
text: 'Documentation',
|
||||
href: 'http://shinobi.video/docs',
|
||||
class: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
bigIcon: "smile-o",
|
||||
title: "It's a proven fact",
|
||||
info: `Generosity makes you a happier person, please consider supporting the development.`,
|
||||
buttons: [
|
||||
{
|
||||
icon: 'share-square-o',
|
||||
color: 'default',
|
||||
text: 'ShinobiShop Subscriptions',
|
||||
href: 'https://licenses.shinobi.video/subscribe',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'paypal',
|
||||
color: 'default',
|
||||
text: 'Donate by PayPal',
|
||||
href: 'https://www.paypal.me/ShinobiCCTV',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'bank',
|
||||
color: 'default',
|
||||
text: 'University of Zurich (UZH)',
|
||||
href: 'https://www.zora.uzh.ch/id/eprint/139275/',
|
||||
class: ''
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Shinobi Mobile",
|
||||
info: `Your subscription key can unlock features for <a href="https://cdn.shinobi.video/installers/ShinobiMobile/" target="_blank"><b>Shinobi Mobile</b></a> running on iOS and Android today!`,
|
||||
buttons: [
|
||||
{
|
||||
icon: 'star',
|
||||
color: 'success',
|
||||
text: 'Join Public Beta',
|
||||
href: 'https://shinobi.video/mobile',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'comments-o',
|
||||
color: 'primary',
|
||||
text: '<b>#mobile-client</b> Chat',
|
||||
href: 'https://discord.gg/ehRd8Zz',
|
||||
class: ''
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Support the Development",
|
||||
info: `Subscribe to any of the following to boost development! Once subscribed put your Subscription ID in at the Super user panel, then restart Shinobi to Activate your installation, thanks! <i class="fa fa-smile-o"></i>`,
|
||||
buttons: [
|
||||
{
|
||||
icon: 'share-square-o',
|
||||
color: 'default',
|
||||
text: 'Shinobi Mobile License ($5/m)',
|
||||
href: 'https://licenses.shinobi.video/subscribe?planSubscribe=plan_G31AZ9mknNCa6z',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'share-square-o',
|
||||
color: 'default',
|
||||
text: 'Tiny Support Subscription ($10/m)',
|
||||
href: 'https://licenses.shinobi.video/subscribe?planSubscribe=plan_G42jNgIqXaWmIC',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'share-square-o',
|
||||
color: 'default',
|
||||
text: 'Shinobi Pro License ($75/m)',
|
||||
href: 'https://licenses.shinobi.video/subscribe?planSubscribe=plan_G3LGdNwA8lSmQy',
|
||||
class: ''
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Donations, One-Time Boost",
|
||||
info: `Sometimes a subscription isn't practical for people. In which case you may show support through a PayPal donation. And as a thank you for doing so your <b>PayPal Transaction ID</b> can be used as a <code>subscriptionId</code> in your Shinobi configuration file. <br><br>Each 5 USD/EUR or 7 CAD will provide one month of activated usage. <i>Meaning, a $20 USD donation today makes this popup go away (or activates the mobile app) for 4 months.</i>`,
|
||||
width: 12,
|
||||
buttons: [
|
||||
{
|
||||
icon: 'paypal',
|
||||
color: 'default',
|
||||
text: 'Donate by PayPal',
|
||||
href: 'https://www.paypal.me/ShinobiCCTV',
|
||||
class: ''
|
||||
},
|
||||
]
|
||||
},
|
||||
].map((block) => {
|
||||
var parsedButtons = block.buttons.map((btn) => {
|
||||
return {
|
||||
"fieldType": "btn",
|
||||
"class": `btn-${btn.color} fill mb-1`,
|
||||
"icon": btn.icon,
|
||||
"attribute": `href="${btn.href}" target="_blank"`,
|
||||
"btnContent": btn.text,
|
||||
}
|
||||
});
|
||||
return {
|
||||
noHeader: true,
|
||||
isFormGroupGroup: true,
|
||||
"section-pre-class": `col-md-${block.width || '6'} mb-3`,
|
||||
"info": [
|
||||
{
|
||||
"fieldType": "div",
|
||||
divContent: `<h3>${block.title}</h3>`
|
||||
},
|
||||
{
|
||||
"fieldType": "div",
|
||||
class: 'mb-3',
|
||||
divContent: block.info
|
||||
},
|
||||
...parsedButtons
|
||||
]
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -618,6 +618,7 @@
|
|||
"accountSettingsDescription": "Manage your Profile and set options like Max Storage Amount and Max Number of Days to keep videos.",
|
||||
"eventFiltersDescription": "Setup filters for when Events occur.",
|
||||
"monitorConfigFinderDescription": "This tool will help you search for configurations for cameras posted by the community. All hosted on <a href='https://hub.shinobi.video/explore' target='_blank'>ShinobiHub</a>. You can post yours too, it would really help the community :)",
|
||||
"helpFinderDescription": "This tool will help you learn to use Shinobi or just do certain things for you.",
|
||||
"License Key": "License Key",
|
||||
"License Activation": "License Activation",
|
||||
"License Activation Failed": "License Activation Failed",
|
||||
|
|
@ -628,6 +629,9 @@
|
|||
"Optional": "Optional",
|
||||
"Prefix": "Prefix",
|
||||
"Saved": "Saved",
|
||||
"Run": "Run",
|
||||
"Helping Hand": "Helping Hand",
|
||||
"Active Tutorial": "Active Tutorial",
|
||||
"Not Saved": "Not Saved",
|
||||
"Not Connected": "Not Connected",
|
||||
"License Plate Detector": "License Plate Detector",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
#helping-hand {
|
||||
z-index: 111;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transition: 1.5s;
|
||||
color: #fff;
|
||||
animation: blinkingText 2s infinite;
|
||||
}
|
||||
@keyframes blinkingText{
|
||||
0% { color: red;}
|
||||
50% { color: yellow;}
|
||||
100% { color: red;}
|
||||
}
|
||||
#helping-hand-player {
|
||||
position: fixed;
|
||||
border: 2px solid yellow;
|
||||
width: 300px;
|
||||
right: 10px;
|
||||
bottom: 70px;
|
||||
z-index: 112;
|
||||
padding: 1rem;
|
||||
border-radius: 10px;
|
||||
background: rgba(0,0,0,0.8);
|
||||
}
|
||||
#helping-hand-annotation {
|
||||
color: #fff;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
$(document).ready(function(){
|
||||
var helpWindow = $('#help_window')
|
||||
function initHelpNotice(){
|
||||
var openMessage = null
|
||||
function lessThanOneWeekAgo(date){
|
||||
const WEEK = 1000 * 60 * 60 * 24 * 7;
|
||||
|
|
@ -77,4 +76,51 @@ $(document).ready(function(){
|
|||
})
|
||||
console.log('Please support the Shinobi development.')
|
||||
console.log('https://licenses.shinobi.video/subscribe')
|
||||
}
|
||||
$(document).ready(function(){
|
||||
var theEnclosure = $('#tab-helpWindow')
|
||||
var helpingHandResults = $('#helpinghand-results')
|
||||
var helpingHandSelector = $('#helpinghand-options')
|
||||
var monitorList = theEnclosure.find('.monitors_list')
|
||||
function loadHelpingHandsOptions(){
|
||||
var html = ``
|
||||
$.each(helpingHandShows,function(showId,show){
|
||||
html += createOptionHtml({
|
||||
label: `${show.name}`,
|
||||
value: showId
|
||||
})
|
||||
})
|
||||
helpingHandSelector.html(html)
|
||||
}
|
||||
window.getSelectedHelpingHandMonitorTarget = function(){
|
||||
return monitorList.val()
|
||||
}
|
||||
function loadHelpingHandSelectors(){
|
||||
loadHelpingHandsOptions()
|
||||
drawMonitorListToSelector(monitorList)
|
||||
}
|
||||
$('.watch-helping-hand').click(function(){
|
||||
var selectedShowId = helpingHandSelector.val()
|
||||
playHelpingHandShow(selectedShowId)
|
||||
})
|
||||
helpingHandSelector.change(function(){
|
||||
var selectedShowId = helpingHandSelector.val()
|
||||
var theShow = helpingHandShows[selectedShowId]
|
||||
var monitorTargetSpecific = theEnclosure.find('.helping-hand-target-monitor')
|
||||
if(theShow.targetMonitor){
|
||||
monitorTargetSpecific.show()
|
||||
}else{
|
||||
monitorTargetSpecific.hide()
|
||||
}
|
||||
})
|
||||
initHelpNotice()
|
||||
addOnTabOpen('helpWindow', function () {
|
||||
loadHelpingHandSelectors()
|
||||
})
|
||||
addOnTabReopen('helpWindow', function () {
|
||||
loadHelpingHandSelectors()
|
||||
})
|
||||
$('body').on('click','.helping-hand-stop',function(){
|
||||
stopHelpingHandShow()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
var helpingHand = null
|
||||
var isWatching = false
|
||||
var helpingHandAnnotationBox = null
|
||||
function drawHelpingHand(){
|
||||
if(!helpingHand){
|
||||
var html = `
|
||||
<div id="helping-hand"><i class="fa fa-3x fa-hand-pointer-o"></i></div>
|
||||
<div id="helping-hand-player">
|
||||
<div class="d-flex flex-row vertical-center">
|
||||
<div style="flex:7" id="helping-hand-annotation"></div>
|
||||
<div style="flex:3">
|
||||
<a class="btn btn-sm btn-danger helping-hand-stop" title="${lang.Stop}"><i class="fa fa-square"></i> ${lang.Stop}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
$('body').append(html)
|
||||
helpingHand = $('#helping-hand')
|
||||
helpingHandAnnotationBox = $('#helping-hand-annotation')
|
||||
}
|
||||
}
|
||||
function removeHelpingHand(){
|
||||
if(helpingHand){
|
||||
helpingHand.fadeOut(2000)
|
||||
helpingHand.remove()
|
||||
$('#helping-hand-player').remove()
|
||||
helpingHand = null
|
||||
}
|
||||
}
|
||||
async function typeWriteInField(txt,fieldTarget){
|
||||
var speed = 100;
|
||||
var element = $(fieldTarget).focus().val('')[0]
|
||||
for (let i = 0; i < txt.length; i++) {
|
||||
element.value += txt.charAt(i);
|
||||
await setPromiseTimeout(speed);
|
||||
}
|
||||
}
|
||||
async function stopHelpingHandShow(showId){
|
||||
isWatching = false
|
||||
}
|
||||
async function playHelpingHandShow(showId){
|
||||
drawHelpingHand()
|
||||
isWatching = true
|
||||
var selectedShow = helpingHandShows[showId]
|
||||
var playlist = selectedShow.playlist
|
||||
for (let i = 0; i < playlist.length; i++) {
|
||||
if(isWatching){
|
||||
var movement = playlist[i];
|
||||
var waitTime = movement.time * 1000
|
||||
await setPromiseTimeout(waitTime);
|
||||
var cmd = movement.cmd
|
||||
var text = movement.text
|
||||
var handPos = movement.handPos
|
||||
var handPosCss = handPos
|
||||
if(handPos.el){
|
||||
var handElOffset = $(handPos.el).offset()
|
||||
handElOffset.top += 30
|
||||
handPosCss = handElOffset
|
||||
}
|
||||
helpingHand.css(handPosCss)
|
||||
if(text)helpingHandAnnotationBox[0].innerHTML = text
|
||||
if(cmd){
|
||||
await setPromiseTimeout(1500);
|
||||
try{
|
||||
if(isWatching)await cmd();
|
||||
}catch(err){
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(isWatching)await setPromiseTimeout(3000);
|
||||
removeHelpingHand()
|
||||
isWatching = false
|
||||
}
|
||||
|
|
@ -0,0 +1,417 @@
|
|||
var helpingHandShows = {
|
||||
"motion-preset-pair": {
|
||||
name: 'Add a Motion Detection On/Off Preset Pair',
|
||||
targetMonitor: true,
|
||||
playlist: [
|
||||
{
|
||||
text: 'Select the <b>Monitor States</b> tab in the Main Menu.',
|
||||
time: 0,
|
||||
handPos: {
|
||||
el: `[page-open="monitorStates"]`
|
||||
},
|
||||
cmd: () => {
|
||||
var sideMenu = $('#menu-side')
|
||||
var theButton = sideMenu.find('[page-open="monitorStates"]')
|
||||
sideMenu.animate({scrollTop: sideMenu.position().top},1000);
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `[page-open="monitorStates"]`
|
||||
},
|
||||
cmd: () => {
|
||||
openTab('monitorStates',{})
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Select <b>Add New</b> under Monitor States.',
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#monitorStatesSelector`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#monitorStatesSelector option').prop('selected',false);
|
||||
$('#monitorStatesSelector').val('').change();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: `Set the name for the new Preset.`,
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates [name="name"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
await typeWriteInField('Motion Detection On','#tab-monitorStates [name="name"]');
|
||||
}
|
||||
},
|
||||
{
|
||||
text: `Add a Monitor to this new Preset.`,
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .add-monitor`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#tab-monitorStates .add-monitor').click();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: `Select the target Monitor.`,
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .state-monitor-row .state-monitor-row-select`
|
||||
},
|
||||
cmd: () => {
|
||||
var monitorId = getSelectedHelpingHandMonitorTarget();
|
||||
$(`#tab-monitorStates .state-monitor-row .state-monitor-row-select`).val(monitorId)
|
||||
}
|
||||
},
|
||||
{
|
||||
text: `Select the options to activate for when this Preset becomes active.`,
|
||||
time: 0.2,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .state-monitor-row-fields-container`
|
||||
},
|
||||
cmd: () => {
|
||||
var optionsSelected = $('#tab-monitorStates .state-monitor-row-fields-container')
|
||||
var scrollBodyHeight = optionsSelected.height()
|
||||
var detectorOption = optionsSelected.find('[data-name="detail=detector"]');
|
||||
optionsSelected.animate({scrollTop: detectorOption.position().top - scrollBodyHeight},1000);
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .state-monitor-row-fields-container [data-name="detail=detector"]`
|
||||
},
|
||||
cmd: () => {
|
||||
var optionsSelected = $('#tab-monitorStates .state-monitor-row-fields-container')
|
||||
var detectorOption = optionsSelected.find('[data-name="detail=detector"]').click();
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .state-monitor-row-fields-container [data-name="detail=detector"] select`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#tab-monitorStates .state-monitor-row-fields-container [data-name="detail=detector"] select').val('1');
|
||||
}
|
||||
},
|
||||
{
|
||||
text: `Save the Preset.`,
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .sticky-bar [type="submit"]`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#tab-monitorStates form').submit();
|
||||
}
|
||||
},
|
||||
/// Motion Off
|
||||
{
|
||||
text: `For this tutorial we're going to add another one for <b>Motion Detection Off</b>.`,
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#monitorStatesSelector`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#monitorStatesSelector option').prop('selected',false);
|
||||
$('#monitorStatesSelector').val('').change();
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates [name="name"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
await typeWriteInField('Motion Detection Off','#tab-monitorStates [name="name"]');
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .add-monitor`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#tab-monitorStates .add-monitor').click();
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .state-monitor-row .state-monitor-row-select`
|
||||
},
|
||||
cmd: () => {
|
||||
var monitorId = getSelectedHelpingHandMonitorTarget();
|
||||
$(`#tab-monitorStates .state-monitor-row .state-monitor-row-select`).val(monitorId)
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 0.2,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .state-monitor-row-fields-container`
|
||||
},
|
||||
cmd: () => {
|
||||
var optionsSelected = $('#tab-monitorStates .state-monitor-row-fields-container')
|
||||
var scrollBodyHeight = optionsSelected.height()
|
||||
var detectorOption = optionsSelected.find('[data-name="detail=detector"]');
|
||||
optionsSelected.animate({scrollTop: detectorOption.position().top - scrollBodyHeight},1000);
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .state-monitor-row-fields-container [data-name="detail=detector"]`
|
||||
},
|
||||
cmd: () => {
|
||||
var optionsSelected = $('#tab-monitorStates .state-monitor-row-fields-container')
|
||||
var detectorOption = optionsSelected.find('[data-name="detail=detector"]').click();
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .state-monitor-row-fields-container [data-name="detail=detector"] select`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#tab-monitorStates .state-monitor-row-fields-container [data-name="detail=detector"] select').val('0');
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-monitorStates .sticky-bar [type="submit"]`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#tab-monitorStates form').submit();
|
||||
}
|
||||
},
|
||||
/// set schedule, motion ON
|
||||
{
|
||||
text: 'Now we set them to activate automatically based on a time frame.',
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `[page-open="schedules"]`
|
||||
},
|
||||
cmd: () => {
|
||||
openTab('schedules',{})
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#schedulesSelector`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#schedulesSelector option').prop('selected',false);
|
||||
$('#schedulesSelector').val('').change();
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="name"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
await typeWriteInField('Motion Detection On','#tab-schedules [name="name"]');
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="enabled"]`
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="start"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
await typeWriteInField('00:00','#tab-schedules [name="start"]');
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="end"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
await typeWriteInField('23:00','#tab-schedules [name="end"]');
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="days"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
var daysSelector = $('#tab-schedules [name="days"]');
|
||||
daysSelector.find('option').prop('selected',false)
|
||||
daysSelector.find('[value="0"],[value="2"],[value="4"],[value="6"]').prop('selected',true)
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 2,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="monitorStates"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
var presetSelector = $('#tab-schedules [name="monitorStates"]');
|
||||
presetSelector.find('option').prop('selected',false)
|
||||
presetSelector.find('[value="Motion Detection On"]').prop('selected',true)
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules .sticky-bar [type="submit"]`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#tab-schedules form').submit();
|
||||
}
|
||||
},
|
||||
/// set schedule, motion OFF
|
||||
{
|
||||
time: 3,
|
||||
handPos: {
|
||||
el: `#schedulesSelector`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#schedulesSelector option').prop('selected',false);
|
||||
$('#schedulesSelector').val('').change();
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="name"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
await typeWriteInField('Motion Detection Off','#tab-schedules [name="name"]');
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="enabled"]`
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="start"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
await typeWriteInField('00:00','#tab-schedules [name="start"]');
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="end"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
await typeWriteInField('23:00','#tab-schedules [name="end"]');
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="days"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
var daysSelector = $('#tab-schedules [name="days"]');
|
||||
daysSelector.find('option').prop('selected',false)
|
||||
daysSelector.find('[value="1"],[value="3"],[value="5"]').prop('selected',true)
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 2,
|
||||
handPos: {
|
||||
el: `#tab-schedules [name="monitorStates"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
var presetSelector = $('#tab-schedules [name="monitorStates"]');
|
||||
presetSelector.find('option').prop('selected',false)
|
||||
presetSelector.find('[value="Motion Detection Off"]').prop('selected',true)
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-schedules .sticky-bar [type="submit"]`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#tab-schedules form').submit();
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
"create-api-key": {
|
||||
name: 'Create API Key',
|
||||
targetMonitor: false,
|
||||
playlist: [
|
||||
{
|
||||
text: 'Select the <b>API Keys</b> tab in the Main Menu.',
|
||||
time: 0,
|
||||
handPos: {
|
||||
el: `[page-open="apiKeys"]`
|
||||
},
|
||||
cmd: () => {
|
||||
var sideMenu = $('#menu-side')
|
||||
var theButton = sideMenu.find('[page-open="apiKeys"]')
|
||||
sideMenu.animate({scrollTop: sideMenu.position().top},1000);
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `[page-open="apiKeys"]`
|
||||
},
|
||||
cmd: () => {
|
||||
openTab('apiKeys',{})
|
||||
}
|
||||
},
|
||||
{
|
||||
text: `Set the name for the new Preset.`,
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-apiKeys [name="ip"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
await typeWriteInField('0.0.0.0','#tab-apiKeys [name="ip"]');
|
||||
}
|
||||
},
|
||||
{
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#tab-apiKeys [detail="permissions"]`
|
||||
},
|
||||
cmd: async () => {
|
||||
var theSelector = $('#tab-apiKeys [detail="permissions"]');
|
||||
theSelector.find('option').prop('selected',true)
|
||||
}
|
||||
},
|
||||
{
|
||||
text: `Generate the Key.`,
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#apiKeySectionAddNew [type="submit"]`
|
||||
},
|
||||
cmd: () => {
|
||||
$('#apiKeySectionAddNew').submit();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: `Generate the Key.`,
|
||||
time: 1,
|
||||
handPos: {
|
||||
el: `#api_list [api_key]:nth-child(1) .copy`
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,8 @@
|
|||
<script src="<%-window.libURL%>assets/js/bs5.onvifScanner.js"></script>
|
||||
<script src="<%-window.libURL%>assets/js/bs5.initial.js"></script>
|
||||
<script src="<%-window.libURL%>assets/js/bs5.startup.js"></script>
|
||||
<script src="<%-window.libURL%>assets/js/bs5.helpinghand.shows.js"></script>
|
||||
<script src="<%-window.libURL%>assets/js/bs5.helpinghand.js"></script>
|
||||
<!-- Live Stream Dependencies -->
|
||||
<script src="<%-window.libURL%>assets/vendor/js/hls.min.js"></script>
|
||||
<!-- Live Stream Dependencies (END) -->
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.ptzControls.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.sideMenu.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.forms.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.helpinghand.css">
|
||||
<style>
|
||||
.bd-placeholder-img {
|
||||
font-size: 1.125rem;
|
||||
|
|
|
|||
|
|
@ -1,261 +1,17 @@
|
|||
<main class="container page-tab pt-3" id="tab-helpWindow">
|
||||
<div style="" class="card form-group-group p-3 bg-dark mb-3 shadow green">
|
||||
<h4 class="form-section-header cursor-pointer mb-3 pb-3 text-white border-bottom-dotted border-color-green "><%- config.poweredByShinobi %></h4>
|
||||
<div class="card-body text-white">
|
||||
<div class="row">
|
||||
<% [
|
||||
{
|
||||
title: "New to Shinobi?",
|
||||
info: `Try reading over some of these links to get yourself started.`,
|
||||
buttons: [
|
||||
{
|
||||
icon: 'newspaper-o',
|
||||
color: 'default',
|
||||
text: 'After Installation Guides',
|
||||
href: 'https://shinobi.video/docs/configure',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'plus',
|
||||
color: 'default',
|
||||
text: 'Adding an H.264 Camera',
|
||||
href: 'https://shinobi.video/docs/configure#content-adding-an-h264h265-camera',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'plus',
|
||||
color: 'default',
|
||||
text: 'Adding an MJPEG Camera',
|
||||
href: 'https://shinobi.video/articles/2018-09-19-how-to-add-an-mjpeg-camera',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'gears',
|
||||
color: 'default',
|
||||
text: 'RTSP Camera Optimization',
|
||||
href: 'https://shinobi.video/articles/2017-07-29-how-i-optimized-my-rtsp-camera',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'comments-o',
|
||||
color: 'info',
|
||||
text: 'Community Chat',
|
||||
href: 'https://discord.gg/ehRd8Zz',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'reddit',
|
||||
color: 'info',
|
||||
text: 'Forum on Reddit',
|
||||
href: 'https://www.reddit.com/r/ShinobiCCTV',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'file-o',
|
||||
color: 'primary',
|
||||
text: 'Documentation',
|
||||
href: 'http://shinobi.video/docs',
|
||||
class: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
bigIcon: "smile-o",
|
||||
title: "It's a proven fact",
|
||||
info: `Generosity makes you a happier person, please consider supporting the development.`,
|
||||
buttons: [
|
||||
{
|
||||
icon: 'share-square-o',
|
||||
color: 'default',
|
||||
text: 'ShinobiShop Subscriptions',
|
||||
href: 'https://licenses.shinobi.video/subscribe',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'paypal',
|
||||
color: 'default',
|
||||
text: 'Donate by PayPal',
|
||||
href: 'https://www.paypal.me/ShinobiCCTV',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'bank',
|
||||
color: 'default',
|
||||
text: 'University of Zurich (UZH)',
|
||||
href: 'https://www.zora.uzh.ch/id/eprint/139275/',
|
||||
class: ''
|
||||
},
|
||||
]
|
||||
},
|
||||
// {
|
||||
// title: "ShinobiHub",
|
||||
// info: `Explore and Contribute to the community of Shinobi all in one place.`,
|
||||
// buttons: [
|
||||
// {
|
||||
// icon: 'newspaper-o',
|
||||
// color: 'primary',
|
||||
// text: 'Read Articles',
|
||||
// href: 'https://hub.shinobi.video/articles',
|
||||
// class: ''
|
||||
// },
|
||||
// {
|
||||
// icon: 'search',
|
||||
// color: 'success',
|
||||
// text: 'Explore Configurations',
|
||||
// href: 'https://hub.shinobi.video/explore',
|
||||
// class: ''
|
||||
// },
|
||||
// {
|
||||
// icon: 'lightbulb-o',
|
||||
// color: 'info',
|
||||
// text: 'View Suggestions',
|
||||
// href: 'https://hub.shinobi.video/suggestions',
|
||||
// class: ''
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
title: "Shinobi Mobile",
|
||||
info: `Your subscription key can unlock features for <a href="https://cdn.shinobi.video/installers/ShinobiMobile/" target="_blank"><b>Shinobi Mobile</b></a> running on iOS and Android today!`,
|
||||
buttons: [
|
||||
{
|
||||
icon: 'star',
|
||||
color: 'success',
|
||||
text: 'Join Public Beta',
|
||||
href: 'https://shinobi.video/mobile',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'comments-o',
|
||||
color: 'primary',
|
||||
text: '<b>#mobile-client</b> Chat',
|
||||
href: 'https://discord.gg/ehRd8Zz',
|
||||
class: ''
|
||||
},
|
||||
// {
|
||||
// icon: 'cube',
|
||||
// color: 'default',
|
||||
// text: lang[`Don't Show for 1 Week`],
|
||||
// class: 'hide_donate',
|
||||
// },
|
||||
]
|
||||
},
|
||||
// {
|
||||
// title: "Shinobi Dashcam",
|
||||
// info: `<a href="https://cdn.shinobi.video/installers/Dashcam/" target="_blank"><b>Shinobi Dashcam</b></a> runs on iOS and Android, Subscribe to a Shinobi Mobile license to unlock extended features!`,
|
||||
// buttons: [
|
||||
// {
|
||||
// icon: 'star',
|
||||
// color: 'success',
|
||||
// text: 'Join Public Beta',
|
||||
// href: 'https://cdn.shinobi.video/installers/Dashcam/',
|
||||
// class: ''
|
||||
// },
|
||||
// {
|
||||
// icon: 'comments-o',
|
||||
// color: 'primary',
|
||||
// text: '<b>#dashcam</b> Chat',
|
||||
// href: 'https://discord.gg/D3cHZ3u',
|
||||
// class: ''
|
||||
// },
|
||||
// // {
|
||||
// // icon: 'cube',
|
||||
// // color: 'default',
|
||||
// // text: lang[`Don't Show for 1 Week`],
|
||||
// // class: 'hide_donate',
|
||||
// // },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// title: "Byaku",
|
||||
// info: `Byaku is a Neural Net Training tool that allows you to build Object Detection sets. <a href="https://cdn.shinobi.video/installers/Byaku/" target="_blank"><b>Byaku</b></a> runs on Linux. Subscribe to use features like automatically downloading thousands of images already pre-annotated and ready for you to train.`,
|
||||
// buttons: [
|
||||
// {
|
||||
// icon: 'star',
|
||||
// color: 'success',
|
||||
// text: 'Join Public Beta',
|
||||
// href: 'https://cdn.shinobi.video/installers/Byaku/',
|
||||
// class: ''
|
||||
// },
|
||||
// {
|
||||
// icon: 'comments-o',
|
||||
// color: 'primary',
|
||||
// text: '<b>#byaku</b> Chat',
|
||||
// href: 'https://discord.gg/3XA9RNP',
|
||||
// class: ''
|
||||
// },
|
||||
// // {
|
||||
// // icon: 'cube',
|
||||
// // color: 'default',
|
||||
// // text: lang[`Don't Show for 1 Week`],
|
||||
// // class: 'hide_donate',
|
||||
// // },
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
title: "Support the Development",
|
||||
info: `Subscribe to any of the following to boost development! Once subscribed put your Subscription ID in at the Super user panel, then restart Shinobi to Activate your installation, thanks! <i class="fa fa-smile-o"></i>`,
|
||||
buttons: [
|
||||
{
|
||||
icon: 'share-square-o',
|
||||
color: 'default',
|
||||
text: 'Shinobi Mobile License ($5/m)',
|
||||
href: 'https://licenses.shinobi.video/subscribe?planSubscribe=plan_G31AZ9mknNCa6z',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'share-square-o',
|
||||
color: 'default',
|
||||
text: 'Tiny Support Subscription ($10/m)',
|
||||
href: 'https://licenses.shinobi.video/subscribe?planSubscribe=plan_G42jNgIqXaWmIC',
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'share-square-o',
|
||||
color: 'default',
|
||||
text: 'Shinobi Pro License ($75/m)',
|
||||
href: 'https://licenses.shinobi.video/subscribe?planSubscribe=plan_G3LGdNwA8lSmQy',
|
||||
class: ''
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Donations, One-Time Boost",
|
||||
info: `Sometimes a subscription isn't practical for people. In which case you may show support through a PayPal donation. And as a thank you for doing so your <b>PayPal Transaction ID</b> can be used as a <code>subscriptionId</code> in your Shinobi configuration file. <br><br>Each 5 USD/EUR or 7 CAD will provide one month of activated usage. <i>Meaning, a $20 USD donation today makes this popup go away (or activates the mobile app) for 4 months.</i>`,
|
||||
width: 12,
|
||||
buttons: [
|
||||
{
|
||||
icon: 'paypal',
|
||||
color: 'default',
|
||||
text: 'Donate by PayPal',
|
||||
href: 'https://www.paypal.me/ShinobiCCTV',
|
||||
class: ''
|
||||
},
|
||||
]
|
||||
},
|
||||
].forEach((promo) => { %>
|
||||
<div class="col-md-<%- promo.width || '6' %> mb-3">
|
||||
<div class="helpquote" style="font-size:10pt;padding: 10px 20px;border-left: 5px solid #eee;">
|
||||
<%- promo.bigIcon ? `<div style="margin-bottom:15px;text-align:center"><i class="fa fa-${promo.bigIcon} fa-5x"></i></div>` : '' %>
|
||||
<%- promo.title ? `<h4>${promo.title}</h4>` : '' %>
|
||||
<p><%- promo.info %></p>
|
||||
<% if(promo.buttons) { %>
|
||||
<div style="margin-top:5px;">
|
||||
<% promo.buttons.forEach((button) => { %>
|
||||
<a style="margin-bottom:4px" <%- button.href ? `href="${button.href}"` : '' %> target="_blank" class="d-flex flex-row btn btn-sm btn-block btn-<%- button.color %> <%- button.class %>">
|
||||
<div><i class="fa fa-<%- button.icon %>" aria-hidden="true"></i></div>
|
||||
<div class="flex-grow-1 text-center"><%- button.text %></div>
|
||||
</a>
|
||||
<% }) %>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
<% }) %>
|
||||
</div>
|
||||
</div>
|
||||
<main class="page-tab pt-3" id="tab-helpWindow">
|
||||
<div class="row <%- define.Theme.isDark ? `dark` : '' %>">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Help Window'].blocks).forEach(function(blockKey){
|
||||
var theBlock = define['Help Window'].blocks[blockKey]
|
||||
drawBlock(theBlock)
|
||||
}) %>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.blockquoteInHelp:before,.blockquoteInHelp:after{
|
||||
display:none;
|
||||
|
|
|
|||
Loading…
Reference in New Issue