Merge branch 'helping-hand' into 'dev'

Helping Hand : Active Tutorial

See merge request Shinobi-Systems/Shinobi!413
refactor-sql-commands-on-startup
Moe 2022-12-01 14:09:51 +00:00
commit aed15d3623
9 changed files with 810 additions and 261 deletions

View File

@ -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
]
}
})
},
}
},
})
}

View File

@ -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",

View File

@ -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;
}

View File

@ -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()
})
})

View File

@ -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
}

View File

@ -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`
},
},
]
}
}

View File

@ -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) -->

View File

@ -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;

View File

@ -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;