mirror of
https://gitlab.com/Shinobi-Systems/ShinobiCE.git
synced 2025-03-09 15:40:15 +00:00
Shinobi CE officially lands on Gitlab
This commit is contained in:
commit
f1406d4eec
431 changed files with 118157 additions and 0 deletions
239
web/pages/admin.ejs
Normal file
239
web/pages/admin.ejs
Normal file
|
@ -0,0 +1,239 @@
|
|||
<% include blocks/header %>
|
||||
<script>$user=<%-JSON.stringify($user)%></script>
|
||||
<style>
|
||||
.shinobi-bg {
|
||||
background: url(/libs/img/bg.jpg);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
}.shinobi-bg-shade {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: rgba(0,0,0,0.5);
|
||||
}
|
||||
nav{margin-top:20px}
|
||||
</style>
|
||||
<link rel="stylesheet" href="libs/css/pnotify.custom.min.css">
|
||||
<link rel="stylesheet" href="libs/css/vbox.css">
|
||||
<link rel="stylesheet" href="libs/css/circles.css">
|
||||
<link rel="stylesheet" href="libs/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="libs/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="libs/css/fullcalendar.min.css">
|
||||
<link rel="stylesheet" href="libs/css/bootstrap-table.min.css">
|
||||
<link rel="stylesheet" href="libs/css/main.dash2.css">
|
||||
<body class="shinobi-bg">
|
||||
<div class="shinobi-bg-shade">
|
||||
<div class="container-fluid">
|
||||
<div class="container">
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container-fluid">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="/">Shinobi : Admin</a>
|
||||
</div>
|
||||
|
||||
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<!-- <li><a href="#">Link</a></li>-->
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> <i class="fa fa-bars"></i> </a>
|
||||
<ul class="dropdown-menu">
|
||||
<!--
|
||||
<li><a href="#">Action</a></li>
|
||||
<li><a href="#">Another action</a></li>
|
||||
<li><a href="#">Something else here</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
-->
|
||||
<li><a class="logout"><i class="fa fa-sign-out pull-right"></i> Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group-group grey">
|
||||
<h4>Sub-Accounts</h4>
|
||||
<table class="table table-striped" id="sub_accounts"></table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<form id="add_new">
|
||||
<div class="form-group-group grey">
|
||||
<h4>Add<small id="msg" class="pull-right" style="color:#fff"></small></h4>
|
||||
<div class="form-group">
|
||||
<label><div><span>Email</span></div>
|
||||
<div><input class="form-control" type="email" name="mail"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span>Password</span></div>
|
||||
<div><input class="form-control" type="password" name="pass"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span>Password Again</span></div>
|
||||
<div><input class="form-control" type="password" name="password_again"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<button type="reset" class="btn btn-danger"><i class="fa fa-undo"></i> Clear</button>
|
||||
<div class="pull-right">
|
||||
<button type="submit" class="btn btn-success"><i class="fa fa-check"></i> Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% include blocks/confirm.ejs %>
|
||||
<% include blocks/subpermissions.ejs %>
|
||||
</div>
|
||||
</body>
|
||||
<script><% include ../libs/js/socket.io.js %></script>
|
||||
<script><% include ../libs/js/pnotify.custom.min.js %></script>
|
||||
<script><% include ../libs/js/moment.js %></script>
|
||||
<script><% include ../libs/js/livestamp.min.js %></script>
|
||||
<script><% include ../libs/js/placeholder.js %></script>
|
||||
<script><% include ../libs/js/bootstrap.min.js %></script>
|
||||
<script><% include ../libs/js/bootstrap-table.min.js %></script>
|
||||
<script>
|
||||
$.ccio={subs:{}};$.ls=localStorage;
|
||||
$.ccio.ws=io(location.origin);
|
||||
$.ccio.cx=function(x){if(!x.ke){x.ke=$user.ke;};if(!x.uid){x.uid=$user.uid;};return $.ccio.ws.emit('a',x)}
|
||||
$.ccio.ws.on('connect',function(d){
|
||||
$.ccio.cx({f:'init',auth:$user.auth_token});
|
||||
})
|
||||
$.ccio.ws.on('f',function(d){
|
||||
console.log(d);
|
||||
switch(d.f){
|
||||
case'edit_sub_account':
|
||||
$.each(d.form,function(n,v){
|
||||
$.ccio.subs[d.uid][n]=v;
|
||||
});
|
||||
break;
|
||||
case'add_sub_account':
|
||||
$.ccio.tm(0,d,'#sub_accounts')
|
||||
break;
|
||||
case'delete_sub_account':
|
||||
$('#sub_accounts tr[uid="'+d.uid+'"]').remove()
|
||||
break;
|
||||
}
|
||||
})
|
||||
$.ccio.tm=function(x,d,z,k){
|
||||
var tmp='';if(!d){d={}};if(!k){k={}};
|
||||
if(d.id&&!d.mid){d.mid=d.id;}
|
||||
switch(x){
|
||||
case 0://sub account row
|
||||
d.detailsJSON=JSON.parse(d.details);
|
||||
$.ccio.subs[d.uid]=d;
|
||||
tmp+='<tr uid="'+d.uid+'"><td><b class="mail">'+d.mail+'</b></td><td><span class="uid">'+d.uid+'</span></td><td><a class="permission btn btn-xs btn-primary"><i class="fa fa-lock"></i></a></td><td><a class="delete btn btn-xs btn-danger"><i class="fa fa-trash-o"></i></a></td></tr>';
|
||||
break;
|
||||
}
|
||||
if(z){
|
||||
$(z).prepend(tmp)
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
$subs=<%-JSON.stringify($subs)%>;
|
||||
$.each($subs,function(n,v){
|
||||
$.ccio.tm(0,v,'#sub_accounts')
|
||||
})
|
||||
|
||||
//add new
|
||||
$.aN={e:$('#add_new')};
|
||||
$.aN.e.submit(function(e){
|
||||
e.preventDefault();
|
||||
e.s=$.aN.e.serializeObject();e.m=$('#msg').empty()
|
||||
$.post($user.auth_token+'/register/'+$user.ke+'/'+$user.uid,e.s,function(d){
|
||||
if(d.msg){
|
||||
e.m.text(d.msg)
|
||||
};
|
||||
});
|
||||
return false;
|
||||
});
|
||||
//sub simple lister
|
||||
$.sU={e:$('#sub_accounts')};
|
||||
$.sU.e.on('click','.delete',function(e){
|
||||
e.e=$(this).parents('tr');e.m=e.e.find('.mail').text();e.u=e.e.attr('uid');
|
||||
$.confirm.e.modal('show');
|
||||
$.confirm.title.html('Delete Sub-Account <small>'+e.u+'</small>')
|
||||
e.html='Do you want to delete <b>'+e.m+'</b>? You cannot recover this account.'
|
||||
$.confirm.body.html(e.html)
|
||||
$.confirm.click({title:'Delete',class:'btn-danger'},function(){
|
||||
$.ccio.cx({f:'accounts',ff:'delete',$uid:e.u,auth:$user.auth_token,mail:e.m})
|
||||
});
|
||||
})
|
||||
$.sU.e.on('click','.permission',function(e){
|
||||
e.e=$(this).parents('tr');e.m=e.e.find('.mail').text();e.u=e.e.attr('uid');
|
||||
$.pR.e.modal('show');
|
||||
$.pR.l.text(e.m);
|
||||
$.pR.user=e.u;
|
||||
e.d=$.ccio.subs[e.u].detailsJSON;
|
||||
//load values
|
||||
$.each($.ccio.subs[$.pR.user],function(n,v){
|
||||
$.pR.e.find('[name="'+n+'"]').val(v)
|
||||
})
|
||||
$.pR.e.find('[detail="allmonitors"]').val(e.d.allmonitors).change()
|
||||
$.each(['monitors','monitor_edit','video_delete','video_view'],function(m,b){
|
||||
if(e.d[b]){
|
||||
$.each(e.d[b],function(n,v){
|
||||
$.pR.e.find('[monitor="'+b+'"][mid="'+v+'"]').val(1)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
//permission window
|
||||
$.pR={e:$('#permissions'),l:$('#permissionsLabel small')};$.pR.f=$.pR.e.find('form')
|
||||
$.pR.e.on('change','[detail="allmonitors"]',function(e){
|
||||
e.e=$(this),
|
||||
e.mon=$('#monitors_section,#monitors_edit_section,#video_delete_section')
|
||||
e.details=$.pR.e.find('[name="details"]')
|
||||
e.json=JSON.parse(e.details.val())
|
||||
if(e.e.val()=='1'){
|
||||
e.mon.hide();
|
||||
e.json.allmonitors='1';
|
||||
}else{
|
||||
e.mon.show()
|
||||
e.json.allmonitors='0';
|
||||
$.pR.e.find('[monitor]').first().change()
|
||||
}
|
||||
e.details.val(JSON.stringify(e.json))
|
||||
})
|
||||
$.pR.e.on('click','[check]',function(e){
|
||||
$(this).parents('.form-group-group').find('select').val($(this).attr('check')).first().change()
|
||||
})
|
||||
$.pR.e.on('change','[monitor]',function(e){
|
||||
e.monitors=[];
|
||||
e.key=$(this).attr('monitor');
|
||||
e.details=$.pR.e.find('[name="details"]')
|
||||
try{e.detail=JSON.parse(e.details.val())}catch(err){e.detail={}}
|
||||
if(!e.detail){e.detail={}}
|
||||
$.pR.e.find('[monitor="'+e.key+'"]').each(function(n,v){
|
||||
v=$(v)
|
||||
if(v.val()=='1'){
|
||||
e.monitors.push(v.attr('mid'))
|
||||
}
|
||||
});
|
||||
e.detail[e.key]=e.monitors;
|
||||
e.details.val(JSON.stringify(e.detail))
|
||||
});
|
||||
$.pR.f.submit(function(e){
|
||||
e.preventDefault();
|
||||
e.s=$(this).serializeObject()
|
||||
$.ccio.cx({f:'accounts',ff:'edit',$uid:$.pR.user,auth:$user.auth_token,mail:$.ccio.subs[$.pR.user].mail,form:e.s})
|
||||
$.pR.e.modal('hide')
|
||||
return false;
|
||||
})
|
||||
|
||||
$('body')
|
||||
.on('click','.logout',function(e){
|
||||
localStorage.removeItem('ShinobiLogin_'+location.host);location.href=location.href;
|
||||
})
|
||||
</script>
|
95
web/pages/blocks/api.ejs
Normal file
95
web/pages/blocks/api.ejs
Normal file
|
@ -0,0 +1,95 @@
|
|||
<!--API Window-->
|
||||
<div class="modal medium fade dark" id="apis" role="dialog" aria-labelledby="apisLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="apisLabel"><i class="fa fa-code"></i> <%-lang['API Keys']%> <small><%-lang['Group Key']%> : <%- $user.ke %></small></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="text-center msg"></div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Allowed IPs']%> <small><%-lang['Separate with commas, no spaces']%></small></span></div>
|
||||
<div><input class="form-control" name="ip" placeholder="0.0.0.0 <%-lang['for Global Access']%>"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Can Authenticate Websocket']%></span></div>
|
||||
<div><select class="form-control" detail="auth_socket">
|
||||
<option value="1"><%-lang['Yes']%></option>
|
||||
<option value="0" selected><%-lang['No']%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Can Get Monitors']%></span></div>
|
||||
<div><select class="form-control" detail="get_monitors">
|
||||
<option value="1" selected><%-lang['Yes']%></option>
|
||||
<option value="0"><%-lang['No']%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Can Control Monitors']%></span></div>
|
||||
<div><select class="form-control" detail="control_monitors">
|
||||
<option value="1" selected><%-lang['Yes']%></option>
|
||||
<option value="0"><%-lang['No']%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Can Get Logs']%></span></div>
|
||||
<div><select class="form-control" detail="get_logs">
|
||||
<option value="1" selected><%-lang['Yes']%></option>
|
||||
<option value="0"><%-lang['No']%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Can View Streams']%></span></div>
|
||||
<div><select class="form-control" detail="watch_stream">
|
||||
<option value="1" selected><%-lang['Yes']%></option>
|
||||
<option value="0"><%-lang['No']%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Can View Snapshots']%></span></div>
|
||||
<div><select class="form-control" detail="watch_snapshot">
|
||||
<option value="1" selected><%-lang['Yes']%></option>
|
||||
<option value="0"><%-lang['No']%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Can View Videos']%></span></div>
|
||||
<div><select class="form-control" detail="watch_videos">
|
||||
<option value="1" selected><%-lang['Yes']%></option>
|
||||
<option value="0"><%-lang['No']%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Can Delete Videos']%></span></div>
|
||||
<div><select class="form-control" detail="delete_videos">
|
||||
<option value="1" selected><%-lang['Yes']%></option>
|
||||
<option value="0"><%-lang['No']%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<table class="table table-striped" id="api_list"></table>
|
||||
</div>
|
||||
<input type="hidden" name="details">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal"><%-lang['Close']%></button>
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-plus"></i> <%-lang['Add']%></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
37
web/pages/blocks/confirm.ejs
Normal file
37
web/pages/blocks/confirm.ejs
Normal file
|
@ -0,0 +1,37 @@
|
|||
<!--Confirmation Window-->
|
||||
<div class="modal fade" id="confirm_window" role="dialog" aria-labelledby="confirm_windowLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="confirm_windowLabel"><i class="fa fa-exclamation-circle"></i> <span><%- lang['Confirm'] %></span></h4>
|
||||
</div>
|
||||
<div class="modal-body"></div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default pull-left" data-dismiss="modal"><i class="fa fa-times"></i> <%- lang['Close'] %></button>
|
||||
<button type="button" class="btn btn-success confirmaction"><%- lang['Save'] %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
//confirmwindow
|
||||
$.confirm={e:$('#confirm_window')};
|
||||
$.confirm.title=$.confirm.e.find('.modal-title span')
|
||||
$.confirm.body=$.confirm.e.find('.modal-body')
|
||||
$.confirm.click=function(x,e){
|
||||
if(!x.class){x.class='btn-success'}
|
||||
if(!x.title){x.title='Save changes'}
|
||||
x.e=$.confirm.e.find('.confirmaction').removeClass('btn-danger btn-warning btn-primary btn-success').addClass(x.class).text(x.title);
|
||||
x.e.click(function(){
|
||||
x.e.unbind('click');$.confirm.e.modal('hide');e();
|
||||
})
|
||||
}
|
||||
$(document).ready(function(){
|
||||
$.confirm.e.on('hidden.bs.modal', function () {
|
||||
$.confirm.e.find('.confirmaction').unbind('click');
|
||||
})
|
||||
})
|
||||
</script>
|
118
web/pages/blocks/filters.ejs
Normal file
118
web/pages/blocks/filters.ejs
Normal file
|
@ -0,0 +1,118 @@
|
|||
<!--Filters Window-->
|
||||
<div class="modal fade dark medium" id="filters" role="dialog" aria-labelledby="filtersLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="filtersLabel"><i class="fa fa-filter"></i> <%- lang['Filters'] %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="text-center msg"></div>
|
||||
<div class="form-group-group green">
|
||||
<h4><%- lang['Saved Filters'] %> : <small class="filter_name"></small></h4>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<div><select class="form-control" id="saved_filters">
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%- lang['Filter Name'] %></span></div>
|
||||
<div><input class="form-control" name="id" type="hidden">
|
||||
<input class="form-control" name="name"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group-group blue where">
|
||||
<h4><%- lang['Find Where'] %>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-default btn-xs add"> <i class="fa fa-plus"></i> </a>
|
||||
<a class="btn btn-danger btn-xs remove"> <i class="fa fa-minus"></i> </a>
|
||||
</div>
|
||||
</h4>
|
||||
<div id="filters_where">
|
||||
</div>
|
||||
<h4><%- lang['Sort By'] %></h4>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-4">
|
||||
<label>
|
||||
<div><select class="form-control" name="sort_by">
|
||||
<option value="time" selected><%- lang['Start Time'] %></option>
|
||||
<option value="end"><%- lang['End Time'] %></option>
|
||||
<option value="mid"><%- lang['Monitor ID'] %></option>
|
||||
<option value="ext"><%- lang['File Type'] %></option>
|
||||
<option value="size"><%- lang['Filesize'] %></option>
|
||||
<option value="status"><%- lang['Video Status'] %></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label>
|
||||
<div><select class="form-control" name="sort_by_direction">
|
||||
<option value="ASC" selected><%- lang['ASC'] %></option>
|
||||
<option value="DESC"><%- lang['DESC'] %></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label>
|
||||
<input class="form-control" title="Row Limit" placeholder="Row Limit" name="limit">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group-group red">
|
||||
<h4><%- lang['Action for Selected'] %></h4>
|
||||
<div class="form-group">
|
||||
<label><div><span><%- lang['Enabled'] %></span></div>
|
||||
<div><select class="form-control" name="enabled">
|
||||
<option value="0" selected><%- lang['No'] %></option>
|
||||
<option value="1"><%- lang['Yes'] %></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-12 hidden">
|
||||
<label><div><span><%- lang['Archive'] %></span></div>
|
||||
<div><select class="form-control" name="archive">
|
||||
<option value="0" selected><%- lang['No'] %></option>
|
||||
<option value="1"><%- lang['Yes'] %></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label><div><span><%- lang['Email Details'] %></span></div>
|
||||
<div><select class="form-control" name="email">
|
||||
<option value="0" selected><%- lang['No'] %></option>
|
||||
<option value="1"><%- lang['Yes'] %></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label><div><span><%- lang['Delete Matches'] %></span></div>
|
||||
<div><select class="form-control" name="delete">
|
||||
<option value="0" selected><%- lang['No'] %></option>
|
||||
<option value="1"><%- lang['Yes'] %></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%- lang['Execute Command'] %></span></div>
|
||||
<div><input class="form-control" name="execute"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="pull-left">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal"><%- lang['Close'] %></button>
|
||||
<button type="button" class="btn btn-danger delete"><%- lang['Delete'] %></button>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success"><%- lang['Save'] %></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
36
web/pages/blocks/header.ejs
Normal file
36
web/pages/blocks/header.ejs
Normal file
|
@ -0,0 +1,36 @@
|
|||
<head>
|
||||
<title><%-lang.Shinobi%></title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
|
||||
<meta name="viewport" content="width=device-width, maximum-scale=1, user-scalable=no" />
|
||||
<meta name="description" content="Shinobi, Open Source CCTV Technology">
|
||||
<meta name="author" content="Moe Alam, moeiscool on GitHub">
|
||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="stylesheet" href="libs/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="libs/css/login.css">
|
||||
<link rel="stylesheet" href="libs/css/material.min.css">
|
||||
<link rel="stylesheet" href="libs/css/material.style.css">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<link rel="icon" href="libs/img/icon/favicon.ico" type="image/x-icon" />
|
||||
<link rel="shortcut icon" href="libs/img/icon/favicon.ico" type="image/x-icon" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="Shinobi">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="libs/img/icon/apple-touch-icon-57x57.png" />
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="libs/img/icon/apple-touch-icon-72x72.png" />
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="libs/img/icon/apple-touch-icon-76x76.png" />
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="libs/img/icon/apple-touch-icon-114x114.png" />
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="libs/img/icon/apple-touch-icon-120x120.png" />
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="libs/img/icon/apple-touch-icon-144x144.png" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="libs/img/icon/apple-touch-icon-152x152.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="libs/img/icon/apple-touch-icon-180x180.png" />
|
||||
<script src="libs/js/jquery.min.js"></script>
|
||||
<script src="libs/js/jquery-ui.min.js"></script>
|
||||
<script src="libs/js/jquery.serialize.js"></script>
|
||||
</head>
|
||||
<% cleanLang=function(string){
|
||||
if(!string){string=''}
|
||||
return string.replace(/'/g,"\\'")
|
||||
} %>
|
45
web/pages/blocks/help.ejs
Normal file
45
web/pages/blocks/help.ejs
Normal file
|
@ -0,0 +1,45 @@
|
|||
<!--Help Window-->
|
||||
<!--
|
||||
<div class="modal fade" id="help_window" tabindex="-1" role="dialog" aria-labelledby="help_windowLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="help_windowLabel"><i class="fa fa-question-circle"></i> <span><%-lang.Help%></span></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<a href="http://shinobi.video/docs/donate"><img src="libs/img/icon/apple-touch-icon-152x152.png" class="pull-right" style="border-radius:50%;margin-left:20px;border: 1px solid #c3996b;"></a>
|
||||
<p>For information about how to use Shinobi you can check out the <a target="_blank" href="http://shinobi.video/docs">Docs</a>.</p>
|
||||
<p>If you would like to get professional support please consider ordering a <a href="http://shinobi.video/support">support package</a>. You can find <b>Moe Alam, The Author</b>, on the <a target="_blank" href="https://discord.gg/ehRd8Zz">Discord</a> Community Chat. For general questions and community support you can also try <a href="https://www.reddit.com/r/ShinobiCCTV">Reddit</a>.</p>
|
||||
<p><strong>Support </strong> <a href="http://shinobi.video/support">from @moeiscool</a></p>
|
||||
<p><b>Like Shinobi?</b> Please consider donating!</p>
|
||||
<div><p><strong>Paypal Address :</strong> <a href="https://www.paypal.me/ShinobiCCTV" target="_blank">paypal@m03.ca</a></p>
|
||||
</div>
|
||||
<p><strong>Bitcoin Address :</strong> 1FvC7M8P9wVT1hXwHj8ugxpZj8JGfGyUDu</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger pull-left hide_donate"><%-lang['Don\'t show this anymore']%></button>
|
||||
<a target="_blank" href="http://discordapp.com/invite/mdhmvuH/" class="btn btn-primary"><%-lang['Chat on Discord']%></a>
|
||||
<a target="_blank" href="http://shinobi.video/docs" class="btn btn-success"><%-lang.Documentation%></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$.help={hide:$('#help_window .hide_donate'),e:$('#help_window')}
|
||||
if($.ccio.op().hide_donate!==1){
|
||||
setTimeout(function(){
|
||||
$.help.e.modal('show')
|
||||
},1000*60*5)
|
||||
$.help.hide.click(function(){
|
||||
$.ccio.op('hide_donate',1);
|
||||
$.help.e.modal('hide');
|
||||
})
|
||||
}else{
|
||||
$.help.hide.hide()
|
||||
}
|
||||
})
|
||||
</script>-->
|
37
web/pages/blocks/logs.ejs
Normal file
37
web/pages/blocks/logs.ejs
Normal file
|
@ -0,0 +1,37 @@
|
|||
<!--Log Window-->
|
||||
<div class="modal full fade search-parent" id="logs_modal" role="dialog" aria-labelledby="logsLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="logsLabel"><i class="fa fa-exclamation-triangle"></i> <%-lang.Logs%></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<div>
|
||||
<select class="form-control" id="log_monitors">
|
||||
<option class="hard" value="all"><%-lang['All Logs']%></option>
|
||||
<option class="hard" value="$USER"><%-lang['For Group']%></option>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input class="form-control search-controller" placeholder="<%-lang.Search%>">
|
||||
</label>
|
||||
</div>
|
||||
<table class="table table-striped">
|
||||
<tbody class="search-body"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default pull-left" data-dismiss="modal"><i class="fa fa-times"></i> <%-lang.Close%></button>
|
||||
<button onclick="$.log.lm.change()" class="btn btn-success"><%-lang.Check%></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
219
web/pages/blocks/mainpermissions.ejs
Normal file
219
web/pages/blocks/mainpermissions.ejs
Normal file
|
@ -0,0 +1,219 @@
|
|||
<!--Confirmation Window-->
|
||||
<div class="modal fade" id="add_edit" tabindex="-1" role="dialog" aria-labelledby="permissionsLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="permissionsLabel"><i class="fa fa-lock"></i> <span id="title"></span>
|
||||
<div class="pull-right">
|
||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="edit">
|
||||
<input type="checkbox" id="edit" value="1" class="mdl-switch__input">
|
||||
<span class="monospace mdl-switch__label"></span>
|
||||
</label>
|
||||
</div>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="max-height:600px;overflow:auto">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<div><span><%-lang.Email%></span></div>
|
||||
<div><input class="form-control" type="email" name="mail"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Group Key']%> <small><%-lang['Leave blank for random.']%></small></span></div>
|
||||
<div><input class="form-control" name="ke"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.Password%> <small class="h_e_input h_e_edit"><%-lang.blankPassword%></small></span></div>
|
||||
<div><input class="form-control" type="password" name="pass"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Password Again']%></span></div>
|
||||
<div><input class="form-control" type="password" name="password_again"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['2-Factor Authentication']%> (<%-lang.Email%>)</span></div>
|
||||
<div><select class="form-control" detail="factorAuth">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Max Storage Amount']%></span></div>
|
||||
<div><input class="form-control" detail="size" placeholder="10000"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Number of Days to keep']%> <b><%-lang.Videos%></b></span></div>
|
||||
<div><input class="form-control" detail="days" placeholder="5"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Number of Days to keep']%> <b><%-lang.Events%></b></span></div>
|
||||
<div><input class="form-control" detail="event_days" placeholder="10"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Number of Days to keep']%> <b><%-lang.Logs%></b></span></div>
|
||||
<div><input class="form-control" detail="log_days" placeholder="10"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Max Number of Cameras']%></span></div>
|
||||
<div><input class="form-control" detail="max_camera" placeholder="<%-lang['Leave blank for unlimited']%>"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Permissions']%></span></div>
|
||||
<div><select class="form-control" detail="permissions" selector="h_l">
|
||||
<option value="all" selected><%-lang['All Privileges']%></option>
|
||||
<option value="limited"><%-lang.Limited%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group h_l_input h_l_limited">
|
||||
<label><div><span><%-lang['Can edit Max Storage']%></span></div>
|
||||
<div><select class="form-control" detail="edit_size">
|
||||
<option value="1" selected><%-lang.Yes%></option>
|
||||
<option value="0"><%-lang.No%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group h_l_input h_l_limited">
|
||||
<label><div><span><%-lang['Can edit Max Days']%></span></div>
|
||||
<div><select class="form-control" detail="edit_days">
|
||||
<option value="1" selected><%-lang.Yes%></option>
|
||||
<option value="0"><%-lang.No%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group h_l_input h_l_limited">
|
||||
<label><div><span><%-lang['Can edit how long to keep Events']%> <small><%-lang['in Days']%></small></span></div>
|
||||
<div><select class="form-control" detail="edit_event_days">
|
||||
<option value="1" selected><%-lang.Yes%></option>
|
||||
<option value="0"><%-lang.No%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group h_l_input h_l_limited">
|
||||
<label><div><span><%-lang['Can edit how long to keep Logs']%> <small><%-lang['in Days']%></small></span></div>
|
||||
<div><select class="form-control" detail="edit_log_days">
|
||||
<option value="1" selected><%-lang.Yes%></option>
|
||||
<option value="0"><%-lang.No%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group h_l_input h_l_limited">
|
||||
<label><div><span><%-lang['Can use Admin Panel']%></span></div>
|
||||
<div><select class="form-control" detail="use_admin">
|
||||
<option value="1" selected><%-lang.Yes%></option>
|
||||
<option value="0"><%-lang.No%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group h_l_input h_l_limited">
|
||||
<label><div><span><%-lang['Can use WebDAV']%></span></div>
|
||||
<div><select class="form-control" detail="use_webdav">
|
||||
<option value="1" selected><%-lang.Yes%></option>
|
||||
<option value="0"><%-lang.No%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group h_l_input h_l_limited">
|
||||
<label><div><span><%-lang['Can use LDAP']%></span></div>
|
||||
<div><select class="form-control" detail="use_ldap">
|
||||
<option value="1" selected><%-lang.Yes%></option>
|
||||
<option value="0"><%-lang.No%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default pull-left" data-dismiss="modal"><i class="fa fa-times"></i> <%-lang.Close%></button>
|
||||
<button type="submit" class="btn btn-success"><i class="fa fa-check"></i> <%-lang.Save%></button>
|
||||
</div>
|
||||
<input type="hidden" name="details" value="{}">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
//add new
|
||||
$.aN={e:$('#add_edit')};$.aN.f=$.aN.e.find('form')
|
||||
$.aN.f.submit(function(e){
|
||||
e.preventDefault();
|
||||
e.s=$.aN.f.serializeObject()
|
||||
e.cx={f:'accounts',ff:'register',form:e.s};
|
||||
if($('#edit').is(':checked')){
|
||||
e.cx.ff='edit';
|
||||
e.cx.account=$.aN.selected;
|
||||
}
|
||||
$.ccio.cx(e.cx)
|
||||
$.aN.e.modal('hide')
|
||||
return false;
|
||||
});
|
||||
$.aN.e.on('change','[detail]',function(e){
|
||||
e.ar={},e.f=$(this).parents('form');
|
||||
$.each(e.f.find('[detail]'),function(n,v){
|
||||
v=$(v);e.ar[v.attr('detail')]=v.val();
|
||||
});
|
||||
e.f.find('[name="details"]').val(JSON.stringify(e.ar));
|
||||
})
|
||||
$('#edit').change(function(e){
|
||||
if($('#edit').is(':checked')){
|
||||
$('#title').text("<%-lang['Edit']%>")
|
||||
$.aN.e.find('[name="mail"],[name="ke"]').prop('disabled',true)
|
||||
}else{
|
||||
$('#title').text("<%-lang['Add New']%>")
|
||||
$.aN.e.find('input,select').prop('disabled',false)
|
||||
}
|
||||
}).change()
|
||||
$.aN.e.on('change','[selector]',function(e){
|
||||
e.v=$(this).val();e.a=$(this).attr('selector')
|
||||
$.aN.e.find('.'+e.a+'_input').hide()
|
||||
$.aN.e.find('.'+e.a+'_'+e.v).show();
|
||||
}).find('[selector]').change();
|
||||
//simple lister
|
||||
$.aC={e:$('#accounts')};$.aC.t=$.aC.e.find('table')
|
||||
$.aC.e.on('click','.add',function(e){
|
||||
$.aN.e.modal('show')
|
||||
$('#edit').prop('checked',false).change().parent().removeClass('is-checked')
|
||||
$.aN.f[0].reset()
|
||||
})
|
||||
$.aC.e.on('click','.delete',function(e){
|
||||
e.e=$(this).parents('tr');
|
||||
e.u=e.e.attr('ke');
|
||||
e.account=$.ccio.accounts[e.u];
|
||||
$.confirm.e.modal('show');
|
||||
$.confirm.title.html('Delete Account <small>'+e.u+'</small>')
|
||||
e.html='Do you want to delete <b>'+e.account.mail+'</b>? You cannot recover this account. Files will remain in the filesystem. If you choose to create an account with the same Group Key it will have the previous events activated in that account.'
|
||||
$.confirm.body.html(e.html)
|
||||
$.confirm.click({title:'Delete',class:'btn-danger'},function(){
|
||||
$.ccio.cx({f:'accounts',ff:'delete',account:e.account})
|
||||
});
|
||||
})
|
||||
$.aC.e.on('click','.permission',function(e){
|
||||
$.aN.e.modal('show')
|
||||
$('#edit').prop('checked',true).change().parent().addClass('is-checked')
|
||||
e.e=$(this).parents('tr');
|
||||
e.u=e.e.attr('ke');
|
||||
e.account=$.ccio.accounts[e.u];
|
||||
$.aN.selected=e.account;
|
||||
$.aN.e.find('input').val('');
|
||||
$.each(e.account,function(n,v){
|
||||
if(n=='pass'){return}
|
||||
$.aN.e.find('[name="'+n+'"]').val(v).change()
|
||||
})
|
||||
$.each(JSON.parse(e.account.details),function(n,v){
|
||||
$.aN.e.find('[detail="'+n+'"]').val(v).change()
|
||||
})
|
||||
// $.pR.e.modal('show');
|
||||
|
||||
})
|
||||
</script>
|
81
web/pages/blocks/migrator.ejs
Normal file
81
web/pages/blocks/migrator.ejs
Normal file
|
@ -0,0 +1,81 @@
|
|||
<!--Migrator-->
|
||||
<div class="modal full fade dark" id="migrator" role="dialog" aria-labelledby="migratorLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="migratorLabel"><i class="fa fa-play-circle"></i> <span></span></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="form-group-group green">
|
||||
<h4><%-lang['']%></h4>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Monitor JSON']%></span></div>
|
||||
<div><textarea name="json" class="form-control"></textarea></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!--
|
||||
<form>
|
||||
<div class="form-group-group green">
|
||||
<h4><%-lang['Connection']%></h4>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.Username%></span></div>
|
||||
<div><input class="form-control" name="name" value=""></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Password']%></span></div>
|
||||
<div><input class="form-control" name="password" type="password" value=""></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Host']%></span></div>
|
||||
<div><input class="form-control" name="host" value=""></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Host Type']%></span></div>
|
||||
<div><select class="form-control" name="host_type">
|
||||
<option value="zm" selected>ZoneMinder</option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
-->
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="btn btn-primary download_link"><i class="fa fa-download"></i> <%-lang.Import%></a>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal"><%-lang.Close%></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$.migrator = {
|
||||
e : $('#migrator')
|
||||
}
|
||||
$.migrator.f = $.migrator.e.find('form')
|
||||
$.migrator.f.submit(function(e){
|
||||
e.preventDefault()
|
||||
var postMonitor = function(v){
|
||||
$.post($.ccio.init('location',$user)+$user.auth_token+'/configureMonitor/'+$user.ke+'/'+v.mid,{data:JSON.stringify(v,null,3)},function(d){
|
||||
$.ccio.log(d)
|
||||
})
|
||||
}
|
||||
var form = $.migrator.f.serializeObject()
|
||||
switch(form.host_type){
|
||||
case'zm':
|
||||
$.get(form.host+'/index.php?username='+form.name+'&password='+form.password+'&action=login&view=console',function(data){
|
||||
console.log(data)
|
||||
})
|
||||
break;
|
||||
}
|
||||
return false
|
||||
})
|
||||
</script>
|
1475
web/pages/blocks/monitoredit.ejs
Normal file
1475
web/pages/blocks/monitoredit.ejs
Normal file
File diff suppressed because it is too large
Load diff
148
web/pages/blocks/multimon.ejs
Normal file
148
web/pages/blocks/multimon.ejs
Normal file
|
@ -0,0 +1,148 @@
|
|||
<!--Multi Monitor Manager-->
|
||||
<div class="modal medium fade dark" mid ke id="multi_mon" tabindex="-1" role="dialog" aria-labelledby="multi_monLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="multi_monLabel"><i class="fa fa-clone"></i> <%- lang['Monitors'] %></h4>
|
||||
</div>
|
||||
<form class="modal-body">
|
||||
<table class="table table-striped tableData">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><div class="checkbox"><input id="multimon_select_all" type="checkbox"><label for="multimon_select_all"></label></div></th>
|
||||
<th><%-lang['Name']%></th>
|
||||
<th><%-lang['Mode']%></th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default pull-left" data-dismiss="modal">Close</button>
|
||||
<div>
|
||||
<!-- <a class="btn btn-success edit_all"><%-lang['Edit Selected']%></a>-->
|
||||
<a class="btn btn-danger delete"><%-lang['Delete']%></a>
|
||||
<a class="btn btn-primary save_config"><%-lang['Export']%></a>
|
||||
<a class="btn btn-default import_config"><%-lang['Import']%></a>
|
||||
<a class="btn btn-success" data-dismiss="modal" monitor="edit"><%-lang['Add New']%></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Multi Monitor Manager-->
|
||||
<!--
|
||||
<div class="modal medium fade dark" mid ke id="multi_mon_edit" tabindex="-1" role="dialog" aria-labelledby="multi_mon_editLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="multi_mon_editLabel"><i class="fa fa-clone"></i> <%- lang['Monitors'] %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table table-striped tableData">
|
||||
<thead></thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default pull-left" data-dismiss="modal">Close</button>
|
||||
<div>
|
||||
<a class="btn btn-success save"><%-lang['Save']%></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
|
||||
//multiple monitor editor, multimonitoredit
|
||||
$.multimonedit={
|
||||
e:$('#multi_mon_edit'),
|
||||
|
||||
}
|
||||
|
||||
$.multimonedit.findFieldInDefintion = function(){
|
||||
$define['Monitor Settings']
|
||||
}
|
||||
$.multimonedit.submit = function(){
|
||||
|
||||
}
|
||||
$.multimonedit.refreshMonitors = function(){
|
||||
// $.get('/'+$user.auth_token+'/monitor/'+$user.ke,function(monitors){
|
||||
// $.multimonedit.monitorsList = monitors;
|
||||
// var selectedMonitors = $.multimonedit.selectedList;
|
||||
// $.each(monitors,function(n,monitor){
|
||||
// if(selectedMonitors[monitor.ke+monitor.mid]){
|
||||
// monitor.details = JSON.parse(monitor.details)
|
||||
// }
|
||||
// })
|
||||
// console.log(monitors)
|
||||
// })
|
||||
var tmp = '';
|
||||
var tmpHeader = '<tr>';
|
||||
$.each($.multimonedit.selectedList,function(n,monitor){
|
||||
var details = '';
|
||||
var detailsHeader = '';
|
||||
tmp += '<tr>';
|
||||
monitor.details = JSON.parse(monitor.details)
|
||||
var drawField = function(e){
|
||||
if(!e.attr){
|
||||
e.attr='name'
|
||||
}
|
||||
if(!e.value){
|
||||
e.value=''
|
||||
}
|
||||
return '<td><input class="form-control" '+e.attr+'='+e.name+' value="'+e.value+'" style="width:100px"></td>'
|
||||
}
|
||||
$.each(monitor,function(m,field){
|
||||
console.log(n)
|
||||
var drawHeader = function(mm){
|
||||
if(n === 0){
|
||||
return '<th>'+mm+'</th>'
|
||||
}
|
||||
}
|
||||
if(m === 'ke'){
|
||||
return
|
||||
}
|
||||
if(m === 'details'){
|
||||
$.each(field,function(k,detailField){
|
||||
detailsHeader += drawHeader(k)
|
||||
details += drawField({
|
||||
attr:'detail',
|
||||
name:k,
|
||||
value:detailField
|
||||
})
|
||||
})
|
||||
}else{
|
||||
tmpHeader += drawHeader(m)
|
||||
tmp += drawField({
|
||||
attr:'name',
|
||||
name:m,
|
||||
value:field
|
||||
})
|
||||
}
|
||||
})
|
||||
tmpHeader += detailsHeader;
|
||||
tmp += details;
|
||||
tmp += '</tr>';
|
||||
})
|
||||
tmpHeader += '</tr>';
|
||||
$.multimonedit.e.find('tbody').html(tmp)
|
||||
$.multimonedit.e.find('thead').html(tmpHeader)
|
||||
}
|
||||
$.multimonedit.e.on('shown.bs.modal', function() {
|
||||
$.multimonedit.refreshMonitors()
|
||||
})
|
||||
})
|
||||
</script>-->
|
86
web/pages/blocks/powervideo.ejs
Normal file
86
web/pages/blocks/powervideo.ejs
Normal file
|
@ -0,0 +1,86 @@
|
|||
<!--Video File Window-->
|
||||
<div class="modal full fade dark" id="pvideo_viewer" role="dialog" aria-labelledby="pvideo_viewerLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="pvideo_viewerLabel"><i class="fa fa-map-marker"></i> <%-lang['Power Video Viewer']%><span style="display:none" class="_loading"> <i class="fa fa-pulse fa-spinner"></i></span></h4>
|
||||
</div>
|
||||
<div class="modal-body" style="overflow:inherit">
|
||||
<form class="row text-center" style="margin:0">
|
||||
<div id="live_view" class="col-md-4"><h3><%-lang['Live View']%> : <span></span></h3><iframe></iframe></div>
|
||||
<div id="video_preview" class="col-md-4">
|
||||
<h3></h3>
|
||||
<div style="position:relative">
|
||||
<div class="stream-objects holder"></div>
|
||||
</div>
|
||||
<div style="margin-top:10px">
|
||||
<div class="form-group">
|
||||
<div class="progress" id="pwrvid_seekBar" style="height:10px;">
|
||||
<div class="progress-bar progress-bar-primary" role="progressbar" style="width:0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="progress motion-meter" title="<%-lang['Motion Meter']%>">
|
||||
<div class="progress-bar progress-bar-danger" role="progressbar" style="width:0%;"><span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix" style="margin:5px"></div>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<a preview="stepBackBack" class="active btn btn-primary"><i class="fa fa-backward"></i></a>
|
||||
<a preview="stepBack" class="btn btn-primary"><i class="fa fa-arrow-circle-o-left"></i></a>
|
||||
<a preview="play" class="btn btn-default"><i class="fa fa-play"></i></a>
|
||||
<a preview="mute" class="btn btn-default"><i class="fa fa-volume-up"></i></a>
|
||||
<a preview="stepFront" class="btn btn-primary"><i class="fa fa-arrow-circle-o-right"></i></a>
|
||||
<a preview="stepFrontFront" class="active btn btn-primary"><i class="fa fa-forward"></i></a>
|
||||
<a preview="fullscreen" class="btn btn-default"><i class="fa fa-arrows-alt"></i></a>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<a onclick="$.pwrvid.f.submit()" class="active btn btn-success"><i class="fa fa-refresh"></i></a>
|
||||
<a download="" href="" class="btn btn-sm btn-default"><i class="fa fa-download"></i></a>
|
||||
<% if(config.DropboxAppKey){ %><a video="download" host="dropbox" download="" href="" class="btn btn-sm btn-default"><i class="fa fa-dropbox"></i></a><% } %>
|
||||
</div>
|
||||
<!-- <a class="btn btn-sm btn-primary" video="launch" href=""><i class="fa fa-play-circle"></i> <span>No Video Selected</span></a>-->
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="temp hidden"></div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Monitor']%></span></div>
|
||||
<div><select class="form-control dark" id="vis_monitors"></select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Date Range']%></span></div>
|
||||
<div><input type="text" id="pvideo_daterange" class="form-control dark" value="" /></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Event Limit']%></span></div>
|
||||
<div><input type="text" id="pvideo_event_limit" class="form-control dark" placeholder="500" localStorage="powerEventLimit" /></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Video Limit']%></span></div>
|
||||
<div><input type="text" id="pvideo_video_limit" class="form-control dark" value="200" placeholder="0 for No Limit" localStorage="powerVideoLimit" /></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<ul id="motion_list" class="list-group text-left"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="row" style="position:relative">
|
||||
<div class="nodata super-center"><%-lang['No Data']%> <div class="divider"></div><i class="fa fa-4x fa-area-chart"></i></div>
|
||||
<div class="loading super-center" style="display:none">Loading <div class="divider"></div><i class="fa fa-4x fa-spinner fa-pulse"></i></div>
|
||||
<div id="vis_pwrvideo" class="col-md-12"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal"><%-lang['Close']%></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
94
web/pages/blocks/probe.ejs
Normal file
94
web/pages/blocks/probe.ejs
Normal file
|
@ -0,0 +1,94 @@
|
|||
<!--ffPROBE Window-->
|
||||
<div class="modal dark fade" id="probe" role="dialog" aria-labelledby="probeLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="probeLabel"><i class="fa fa-search"></i> <%-lang.FFprobe%> <small><span style="display:none" class="_loading"> <i class="fa fa-pulse fa-spinner"></i></span></small></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="text-center msg"></div>
|
||||
<div class="form-group">
|
||||
<i>"<%-lang['FFmpegTip']%>"</i> - FFmpegTips
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Complete Stream URL']%></span></div>
|
||||
<div><input class="form-control" name="url" placeholder="<%-lang.Example%> : http://192.168.88.126/videostream.cgi or /dev/video0"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Mode']%></span></div>
|
||||
<div><select class="form-control" name="mode">
|
||||
<option value="default" selected><%-lang.Default%></option>
|
||||
<option value="json">JSON</option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="output_data"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default pull-left" data-dismiss="modal"><i class="fa fa-times"></i> <%-lang.Close%></button>
|
||||
<a class="btn btn-danger stop" style="display:none"><%-lang.Stop%><span> <i class="fa fa-pulse fa-spinner"></i></span></a>
|
||||
<button type="submit" class="btn btn-success"><%-lang.Check%></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!--onvif PROBE Window-->
|
||||
<div class="modal full dark fade" id="onvif_probe" role="dialog" aria-labelledby="onvif_probeLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="onvif_probeLabel"><i class="fa fa-rss"></i> <%-lang['ONVIF Scanner']%><span style="display:none" class="_loading"> <i class="fa fa-pulse fa-spinner"></i></span></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group-group navy">
|
||||
<h4><%-lang['Scan Settings']%></h4>
|
||||
<p class="text-center"><%-lang.ONVIFnote%></p>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['IP Address']%> <small><%-lang['Range or Single']%></small></span></div>
|
||||
<div><input class="form-control" name="ip" placeholder="Example : 10.1.100.1-10.1.100.254"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Port']%> <small>Separate by Commas or a Range</small></span></div>
|
||||
<div><input class="form-control" name="port" placeholder="Example : 80,7575,8000,8080,8081"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Camera Username']%></span></div>
|
||||
<div><input class="form-control" name="user" placeholder="Can be left blank."></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Camera Password']%></span></div>
|
||||
<div><input class="form-control" name="pass"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group text-right">
|
||||
<button type="submit" class="btn btn-success"><%-lang.Search%><span class="_loading" style="display:none"> <i class="fa fa-pulse fa-spinner"></i></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group-group blue">
|
||||
<h4><%-lang['Found Devices']%></h4>
|
||||
<!-- <div id="onvif_video"></div>-->
|
||||
<table class="output_data table table-striped" style="margin:0"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default pull-left" data-dismiss="modal"><i class="fa fa-times"></i> <%-lang.Close%></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
67
web/pages/blocks/region.ejs
Normal file
67
web/pages/blocks/region.ejs
Normal file
|
@ -0,0 +1,67 @@
|
|||
<!--Zone Editor Window-->
|
||||
<div class="modal full fade dark" id="region_editor" tabindex="-1" role="dialog" aria-labelledby="region_editorLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="region_editorLabel"><i class="fa fa-grav"></i> <%-lang['Region Editor']%></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6 text-left">
|
||||
<div class="form-group-group orange where">
|
||||
<h4><span class="cord_name"></span>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-success btn-xs add"> <i class="fa fa-plus"></i> </a>
|
||||
<a class="btn btn-danger btn-xs erase"> <i class="fa fa-trash-o"></i> </a>
|
||||
</div>
|
||||
</h4>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<div><span><%-lang.Regions%></span></div>
|
||||
<div><select id="regions_list" class="form-control"></select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-12">
|
||||
<label>
|
||||
<div><span><%-lang['Region Name']%></span></div>
|
||||
<div><input class="form-control" name="name"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label>
|
||||
<div><span><%-lang.Indifference%></span></div>
|
||||
<div><input class="form-control" name="sensitivity"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.Points%></span></div></label>
|
||||
<table class="table table-striped" id="regions_points"><tbody></tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p><%-lang.RegionNote%></p>
|
||||
<div class="canvas_holder">
|
||||
<div id="region_editor_live"><iframe></iframe><img></div>
|
||||
<textarea id="regions_canvas" rows=3 class="hidden canvas-area input-xxlarge" disabled></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="pull-left">
|
||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="region_still_image" title="<%-lang['Switch on for Still Image']%>">
|
||||
<input type="checkbox" id="region_still_image" value="1" name="remember" class="mdl-switch__input">
|
||||
<span class="monospace mdl-switch__label"><%-lang['Live Stream Toggle']%></span>
|
||||
</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-default pull-right" data-dismiss="modal"><i class="fa fa-times"></i> <%-lang.Close%></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
282
web/pages/blocks/settings.ejs
Normal file
282
web/pages/blocks/settings.ejs
Normal file
|
@ -0,0 +1,282 @@
|
|||
<!--Settings Window-->
|
||||
<% var audioFiles = fs.readdirSync('./web/libs/audio') %>
|
||||
<div class="modal fade medium dark" id="settings" tabindex="-1" role="dialog" aria-labelledby="settingsLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="settingsLabel"><i class="fa fa-gears"></i> <%-lang.Settings%></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="text-center msg"></div>
|
||||
<div class="form-group-group green">
|
||||
<h4><%-lang['Account Info']%></h4>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.Email%></span></div>
|
||||
<div><input class="form-control" value="<%- $user.mail %>" disabled></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Group Key']%></span></div>
|
||||
<div><input class="form-control" value="<%- $user.ke %>" disabled></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group-group grey">
|
||||
<h4><%-lang.Profile%></h4>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.Email%></span></div>
|
||||
<div><input class="form-control" type="email" name="mail"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.Password%> <small><%-lang.blankPassword%></small></span></div>
|
||||
<div><input class="form-control" type="password" name="pass"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.Password%> <%-lang.Again%></span></div>
|
||||
<div><input class="form-control" type="password" name="password_again"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['2-Factor Authentication']%> (<%-lang.Email%>)</span></div>
|
||||
<div><select class="form-control" detail="factorAuth">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<% if(!details.sub){ %>
|
||||
<% if(details.edit_size!=='0'){ %>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Max Storage Amount']%></span></div>
|
||||
<div><input class="form-control" detail="size" placeholder="10000"></div>
|
||||
</label>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if(details.edit_days!=='0'){ %>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Number of Days to keep']%> <%-lang.Videos%></span></div>
|
||||
<div><input class="form-control" detail="days" placeholder="5"></div>
|
||||
</label>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if(details.edit_event_days!=='0'){ %>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Number of Days to keep']%> <%-lang.Events%></span></div>
|
||||
<div><input class="form-control" detail="event_days" placeholder="10"></div>
|
||||
</label>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if(details.edit_log_days!=='0'){ %>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Number of Days to keep']%> <%-lang.Logs%></span></div>
|
||||
<div><input class="form-control" detail="log_days" placeholder="10"></div>
|
||||
</label>
|
||||
</div>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Dashboard Language']%></span></div>
|
||||
<div>
|
||||
<select class="form-control" detail="lang">
|
||||
<option value="" selected>Default</option>
|
||||
<% fs.readdirSync('./languages').forEach(function(v,n){
|
||||
v=v.replace('.json','') %>
|
||||
<option value="<%-v%>"><%-v%></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Notification Sound']%></span></div>
|
||||
<div>
|
||||
<select class="form-control" detail="audio_note">
|
||||
<option value="" selected>No Sound</option>
|
||||
<% audioFiles.forEach(function(v,n){ %>
|
||||
<option value="<%-v%>"><%-v%></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Alert Sound']%></span></div>
|
||||
<div>
|
||||
<select class="form-control" detail="audio_alert">
|
||||
<option value="" selected>No Sound</option>
|
||||
<% audioFiles.forEach(function(v,n){ %>
|
||||
<option value="<%-v%>"><%-v%></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Alert Sound Delay']%></span></div>
|
||||
<div><input class="form-control" detail="audio_delay" placeholder="1"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<% if(!details.sub){ %>
|
||||
<div class="form-group-group blue">
|
||||
<h4 class="mon_groups">
|
||||
<%-lang['Monitor Groups']%>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-success btn-xs add"><i class="fa fa-plus"></i></a>
|
||||
<a class="btn btn-danger btn-xs delete"><i class="fa fa-trash-o"></i></a>
|
||||
</div>
|
||||
</h4>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Monitor Groups']%></span></div>
|
||||
<div><select class="form-control" id="settings_mon_groups"></select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Group Name']%></span></div>
|
||||
<div><input type="hidden" group="id">
|
||||
<input class="form-control" group="name"></div>
|
||||
</label>
|
||||
</div>
|
||||
<input type="hidden" detail="mon_groups">
|
||||
</div>
|
||||
<% if(details.use_webdav!=='0'){ %>
|
||||
<div class="form-group-group forestgreen">
|
||||
<h4><%-lang.WebDAV%></h4>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.URL%></span></div>
|
||||
<div><input class="form-control" detail="webdav_url"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-12">
|
||||
<label><div><span><%-lang.Username%></span></div>
|
||||
<div><input class="form-control" detail="webdav_user"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<label><div><span><%-lang.Password%></span></div>
|
||||
<div><input class="form-control" type="password" detail="webdav_pass"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.Autosave%></span></div>
|
||||
<div><select class="form-control" detail="webdav_save">
|
||||
<option value="0"><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Save Directory']%></span></div>
|
||||
<div><input class="form-control" placeholder="/" detail="webdav_dir"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if(details.use_ldap!=='0'){ %>
|
||||
<div class="form-group-group forestgreen">
|
||||
<h4><%-lang.LDAP%></h4>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.Enable%></span></div>
|
||||
<div><select class="form-control" detail="ldap_enable" selector="ldap_i">
|
||||
<option value="0"><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="ldap_i_input ldap_i_1" style="display:none">
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.URL%></span></div>
|
||||
<div><input class="form-control" detail="ldap_url"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['bindDN']%></span></div>
|
||||
<div><input class="form-control" detail="ldap_bindDN" placeholder="<%-lang.Example%> : cn=admin,dc=test,dc=com"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Bind Credentials']%></span></div>
|
||||
<div><input class="form-control" detail="ldap_bindCredentials"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Search Filter']%></span></div>
|
||||
<div><input class="form-control" detail="ldap_searchFilter" placeholder="cn={{username}}"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Search Base']%></span></div>
|
||||
<div><input class="form-control" detail="ldap_searchBase" placeholder="dc=test,dc=com"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<div class="form-group-group navy">
|
||||
<h4><%-lang.Preferences%></h4>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.CSS%></span></div>
|
||||
<div><textarea class="form-control" placeholder="#main_header{background:#b59f00}" detail="css"></textarea></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Force Monitors Per Row']%></span></div>
|
||||
<div><select class="form-control" localStorage="montage_use" selector="st_force_mon_rows">
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group st_force_mon_rows_input st_force_mon_rows_1">
|
||||
<label><div><span><%-lang['Monitors per row']%></span></div>
|
||||
<div><input class="form-control" placeholder="3" localStorage="montage"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Browser Console Log']%></span></div>
|
||||
<div><select class="form-control" localStorage="browserLog">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Get Logs to Client']%></span></div>
|
||||
<div><select class="form-control" detail="get_server_log">
|
||||
<option value="1" selected><%-lang.Yes%></option>
|
||||
<option value="0"><%-lang.No%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Themes']%></span></div>
|
||||
<div><select class="form-control" detail="theme">
|
||||
<option value="" selected>Default</option>
|
||||
<% fs.readdirSync(__dirname+'/web/libs/themes').forEach(function(v){ %>
|
||||
<option value="<%-v%>"><%-v%></option>
|
||||
<% }) %>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group-group green linkShinobi">
|
||||
<h4><%-lang['Link Shinobi']%><div class="pull-right"><a class="btn btn-success btn-xs add"><i class="fa fa-plus"></i></a></div></h4>
|
||||
<div id="linkShinobi"></div>
|
||||
</div>
|
||||
<input type="hidden" name="details">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default pull-left" data-dismiss="modal"><i class="fa fa-times"></i> <%-lang.Close%></button>
|
||||
<button type="submit" class="btn btn-success"><i class="fa fa-check"></i> <%-lang.Save%></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
105
web/pages/blocks/subpermissions.ejs
Normal file
105
web/pages/blocks/subpermissions.ejs
Normal file
|
@ -0,0 +1,105 @@
|
|||
<!--Confirmation Window-->
|
||||
<div class="modal fade" id="permissions" tabindex="-1" role="dialog" aria-labelledby="permissionsLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="permissionsLabel"><i class="fa fa-lock"></i> <%-lang.Permissions%></h4>
|
||||
</div>
|
||||
<div class="modal-body" style="max-height:600px;overflow:auto">
|
||||
<div class="text-center msg"></div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['All Monitors and Privileges']%></span></div>
|
||||
<div><select class="form-control" detail="allmonitors">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group-group blue" id="monitors_section">
|
||||
<h4>
|
||||
<%-lang['Can View Monitor']%>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-success btn-xs" check="1"><i class="fa fa-check-square-o"></i></a>
|
||||
<a class="btn btn-danger btn-xs" check="0"><i class="fa fa-square-o"></i></a>
|
||||
</div>
|
||||
</h4>
|
||||
<% $mons.forEach(function(v,n){ %>
|
||||
<div class="form-group">
|
||||
<label><div><span><%- v.name %></span></div>
|
||||
<div><select class="form-control" monitor="monitors" mid="<%- v.mid %>">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<% }) %>
|
||||
</div>
|
||||
<div class="form-group-group forestgreen" id="monitors_edit_section">
|
||||
<h4>
|
||||
<%-lang['Can Edit Monitor']%>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-success btn-xs" check="1"><i class="fa fa-check-square-o"></i></a>
|
||||
<a class="btn btn-danger btn-xs" check="0"><i class="fa fa-square-o"></i></a>
|
||||
</div>
|
||||
</h4>
|
||||
<% $mons.forEach(function(v,n){ %>
|
||||
<div class="form-group">
|
||||
<label><div><span><%- v.name %></span></div>
|
||||
<div><select class="form-control" monitor="monitor_edit" mid="<%- v.mid %>">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<% }) %>
|
||||
</div>
|
||||
<div class="form-group-group navy" id="video_view_section">
|
||||
<h4>
|
||||
<%-lang['Can View Videos and Events']%>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-success btn-xs" check="1"><i class="fa fa-check-square-o"></i></a>
|
||||
<a class="btn btn-danger btn-xs" check="0"><i class="fa fa-square-o"></i></a>
|
||||
</div>
|
||||
</h4>
|
||||
<% $mons.forEach(function(v,n){ %>
|
||||
<div class="form-group">
|
||||
<label><div><span><%- v.name %></span></div>
|
||||
<div><select class="form-control" monitor="video_view" mid="<%- v.mid %>">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<% }) %>
|
||||
</div>
|
||||
<div class="form-group-group red" id="video_delete_section">
|
||||
<h4>
|
||||
<%-lang['Can Delete Videos and Events']%>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-success btn-xs" check="1"><i class="fa fa-check-square-o"></i></a>
|
||||
<a class="btn btn-danger btn-xs" check="0"><i class="fa fa-square-o"></i></a>
|
||||
</div>
|
||||
</h4>
|
||||
<% $mons.forEach(function(v,n){ %>
|
||||
<div class="form-group">
|
||||
<label><div><span><%- v.name %></span></div>
|
||||
<div><select class="form-control" monitor="video_delete" mid="<%- v.mid %>">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<% }) %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default pull-left" data-dismiss="modal"><i class="fa fa-times"></i> <%-lang.Close%></button>
|
||||
<button type="submit" class="btn btn-success"><i class="fa fa-check"></i> <%-lang.Save%></button>
|
||||
</div>
|
||||
<input type="hidden" name="details">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
80
web/pages/blocks/timelapse.ejs
Normal file
80
web/pages/blocks/timelapse.ejs
Normal file
|
@ -0,0 +1,80 @@
|
|||
<!--Timelapse Window-->
|
||||
<div class="modal full fade dark" id="timelapse" role="dialog" aria-labelledby="timelapseLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="timelapseLabel"><i class="fa fa-angle-double-right"></i> <span><%-lang['Time-lapse Tool']%></span></h4>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="timelapse_hud">
|
||||
<div class="timelapse_playRate hover-hide"></div>
|
||||
<div class="controlBar hover-hide">
|
||||
<div class="form-group">
|
||||
<div class="progress" id="timelapse_seekBar">
|
||||
<div class="progress-bar progress-bar-warning" role="progressbar" style="width:0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="form-group monospace">
|
||||
<div class="btn-group timelapseSpeedUseBasicSwitch">
|
||||
<a timelapse="stepBackBack" class="active btn btn-primary"> <i class="fa fa-backward"></i> </a>
|
||||
<a timelapse="stepBack" class="btn btn-primary"> <i class="fa fa-arrow-circle-o-left"></i> </a>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a timelapse="play" class="btn btn-default"> <i class="fa fa-play"></i> </a>
|
||||
<a timelapse="mute" class="btn btn-default"> <i class="fa fa-volume-up"></i> </a>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a timelapse="stepFront" class="btn btn-primary"> <i class="fa fa-arrow-circle-o-right"></i> </a>
|
||||
<a timelapse="stepFrontFront" class="active btn btn-primary"> <i class="fa fa-forward"></i> </a>
|
||||
</div>
|
||||
<a timelapse="download" class="btn btn-info"> <i class="fa fa-download"></i> </a>
|
||||
<div class="btn-group">
|
||||
<a timelapse="setPlayBackRate" playRate="1" class="btn btn-default">1</a>
|
||||
<a timelapse="setPlayBackRate" playRate="5" class="btn btn-default">5</a>
|
||||
<a timelapse="setPlayBackRate" playRate="15" class="btn btn-default">15</a>
|
||||
</div>
|
||||
<div class="btn-group timelapseSpeedUseBasicSwitch">
|
||||
<a timelapse="setPlayBackRate" playRate="30" class="btn btn-default">30</a>
|
||||
<a timelapse="setPlayBackRate" playRate="60" class="btn btn-default">60</a>
|
||||
<a timelapse="setPlayBackRate" playRate="120" class="btn btn-default">120</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="marc-toggle abs-bot-left">
|
||||
<input title="<%-lang['Use HTML5 Play Method']%>" type="checkbox" id="timelapseSpeedUseBasic"/><label for="timelapseSpeedUseBasic"><%-lang['Use HTML5 Play Method']%></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="timelapse_video_display"></div>
|
||||
</div>
|
||||
</div>
|
||||
<form class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang.Monitor%></span></div>
|
||||
<div><select class="form-control monitors_list"></select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Date Range']%></span></div>
|
||||
<div><input type="text" id="timelapse_daterange" class="form-control" value="" /></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<ul id="timelapse_video_line" class="list-group"></ul>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal"><%-lang.Close%></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
69
web/pages/blocks/videoview.ejs
Normal file
69
web/pages/blocks/videoview.ejs
Normal file
|
@ -0,0 +1,69 @@
|
|||
<!--Videos List Window-->
|
||||
<div class="modal full fade" id="videos_viewer" role="dialog" aria-labelledby="videos_viewerLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="videos_viewerLabel"><i class="fa fa-film"></i> <%-lang.Videos%> <small> <span class="video_viewer_total"></span> <%-lang.total%></small></h4>
|
||||
</div>
|
||||
<div class="modal-body flex-container-modal-body">
|
||||
<div style="padding:20px 0" class="flex-block flex-modal-block">
|
||||
<div class="col-md-3">
|
||||
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Date Range']%></span></div>
|
||||
<div><input type="text" id="videos_viewer_daterange" class="form-control" value="" /></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Video Limit']%></span></div>
|
||||
<div><input id="videos_viewer_limit" value="100" class="form-control" placeholder="0 for No Limit" localStorage="videosViewerLimit" /></div>
|
||||
</label>
|
||||
</div>
|
||||
<div id="videos_viewer_preview" class="text-center"></div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="contents"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="row">
|
||||
<div class="col-md-4 text-left">
|
||||
<a class="btn btn-danger delete_selected"><i class="fa fa-trash-o"></i> <%-lang['Delete selected']%></a>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-center" id="videos_viewer_pages"></div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<button type="button" class="btn btn-danger" data-dismiss="modal"><%-lang.Close%></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!--Video File Window-->
|
||||
<div class="modal full fade dark" id="video_viewer" role="dialog" aria-labelledby="video_viewerLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="video_viewerLabel"><i class="fa fa-play-circle"></i> <span></span></h4>
|
||||
</div>
|
||||
<div class="modal-body text-center"></div>
|
||||
<div class="modal-footer">
|
||||
<a class="btn btn-danger pull-left" video="delete" data-dismiss="modal"><%-lang.Delete%></a>
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-primary download_link"><i class="fa fa-download"></i> <%-lang.Save%></a>
|
||||
<% if(config.DropboxAppKey){ %><a class="btn btn-primary" monitor="download" host="dropbox"><i class="fa fa-dropbox"></i> Dropbox</a><% } %>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal"><%-lang.Close%></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
268
web/pages/dashcam.ejs
Normal file
268
web/pages/dashcam.ejs
Normal file
|
@ -0,0 +1,268 @@
|
|||
<% include blocks/header %>
|
||||
<script>var $user=<%- JSON.stringify($user) %>;</script>
|
||||
<link rel="stylesheet" href="libs/css/bootstrap.min.css">
|
||||
<div id="main" class="container">
|
||||
<a class="btn btn-danger btn-lg" record>Stream</a>
|
||||
<div>
|
||||
<img id="img">
|
||||
</div>
|
||||
<div id="msg">Monitor to Stream and Save under <span id="selected"></span></div>
|
||||
requires https or firefox
|
||||
<div style="margin-top:30px;">
|
||||
<div id="monitors" class="list-group"></div>
|
||||
</div>
|
||||
<div style="margin-top:30px;" id="data">
|
||||
</div>
|
||||
<div style="margin-top:30px;">
|
||||
<a class="logout btn-danger btn btn-xs">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
<video id="video"><source></video>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script src="libs/js/jquery.min.js"></script>
|
||||
<script src="libs/js/socket.io.js"></script>
|
||||
<script src="libs/js/menu.js"></script>
|
||||
<script>
|
||||
$.ccio={};$.ls=localStorage;
|
||||
$.ccio.ws=io(location.origin);
|
||||
$.ccio.ws.on('connect',function (d){
|
||||
$(document).ready(function(e){
|
||||
$.ccio.cx=function(x){if(!x.ke){x.ke=$user.ke;};if(!x.uid){x.uid=$user.uid;};return $.ccio.ws.emit('r',x)}
|
||||
console.log($user)
|
||||
$.ccio.cx({f:'init',ke:$user.ke,auth:$user.auth_token,uid:$user.uid})
|
||||
})
|
||||
})
|
||||
$.ccio.ws.on('f',function(d){
|
||||
if(d.f!=='monitor_frame'&&d.f!=='cpu'&&d.f!=='event_delete'){console.log(d);}
|
||||
switch(d.f){
|
||||
case'monitor_edit':
|
||||
if(!$.ccio.mon[d.mid]){$.ccio.mon[d.mid]={}}
|
||||
$.each(d.mon,function(n,v){
|
||||
$.ccio.mon[d.mid][n]=v;
|
||||
});
|
||||
break;
|
||||
}
|
||||
})
|
||||
function _base64ToArrayBuffer(base64) {
|
||||
var binary_string = window.atob(base64.replace(/\s/g, "").replace("data:image/jpeg;base64,", ""));
|
||||
var len = binary_string.length;
|
||||
var bytes = new Uint8Array( len );
|
||||
for (var i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
$.ccio.framerate=2;
|
||||
|
||||
$.ccio.stopSending=function(){
|
||||
$('#main').removeClass('recording');
|
||||
clearInterval($.ccio.cutChunk);
|
||||
$('[record]').unbind('click').click($.ccio.startSending)
|
||||
}
|
||||
$.ccio.startSending=function(){
|
||||
if($.ccio.selected===null){return false;}
|
||||
$('#main').addClass('recording');
|
||||
if (typeof MediaRecorder.isTypeSupported == 'function'){
|
||||
/*
|
||||
MediaRecorder.isTypeSupported is a function announced in https://developers.google.com/web/updates/2016/01/mediarecorder and later introduced in the MediaRecorder API spec http://www.w3.org/TR/mediastream-recording/
|
||||
*/
|
||||
if (MediaRecorder.isTypeSupported('video/webm;codecs=vp9')) {
|
||||
var options = {mimeType: 'video/webm;codecs=vp9'};
|
||||
} else if (MediaRecorder.isTypeSupported('video/webm;codecs=h264')) {
|
||||
var options = {mimeType: 'video/webm;codecs=h264'};
|
||||
} else if (MediaRecorder.isTypeSupported('video/webm;codecs=vp8')) {
|
||||
var options = {mimeType: 'video/webm;codecs=vp8'};
|
||||
}
|
||||
log('Using '+options.mimeType);
|
||||
mediaRecorder = new MediaRecorder($.ccio.vid.stream, options);
|
||||
}else{
|
||||
log('Using default codecs for browser');
|
||||
mediaRecorder = new MediaRecorder($.ccio.vid.stream);
|
||||
}
|
||||
|
||||
mediaRecorder.start(10);
|
||||
$.ccio.chunker = function(){
|
||||
clearInterval($.ccio.cutChunk)
|
||||
$.ccio.cutChunk=setInterval(function(){
|
||||
mediaRecorder.stop()
|
||||
mediaRecorder.start()
|
||||
},5000)
|
||||
}
|
||||
mediaRecorder.ondataavailable = function(e) {
|
||||
//log('Data available...');
|
||||
//console.log(e.data);
|
||||
//console.log(e.data.type);
|
||||
//console.log(e);
|
||||
chunks.push(e.data);
|
||||
};
|
||||
|
||||
mediaRecorder.onerror = function(e){
|
||||
clearInterval($.ccio.cutChunk)
|
||||
};
|
||||
|
||||
|
||||
mediaRecorder.onstart = function(){
|
||||
$.ccio.chunker()
|
||||
};
|
||||
|
||||
mediaRecorder.onstop = function(){
|
||||
var blob = new Blob(chunks, {type: "video/webm"});
|
||||
$.ccio.cx({f:'monitor_chunk',chunk:blob,mid:$.ccio.selected});
|
||||
// var reader = new FileReader();
|
||||
// reader.addEventListener("loadend", function() {
|
||||
// console.log(reader.result)
|
||||
// });
|
||||
// reader.readAsArrayBuffer(blob);
|
||||
chunks = [];
|
||||
};
|
||||
|
||||
mediaRecorder.onpause = function(){
|
||||
clearInterval($.ccio.cutChunk)
|
||||
}
|
||||
|
||||
mediaRecorder.onresume = function(){
|
||||
$.ccio.chunker()
|
||||
}
|
||||
|
||||
mediaRecorder.onwarning = function(e){
|
||||
clearInterval($.ccio.cutChunk)
|
||||
};
|
||||
$('[record]').unbind('click').click($.ccio.stopSending)
|
||||
};
|
||||
function log(message){
|
||||
$.ccio.vid.data[0].innerHTML = $.ccio.vid.data[0].innerHTML+'<br>'+message ;
|
||||
}
|
||||
|
||||
|
||||
/* Webcam */
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
|
||||
if(getBrowser() == "Chrome"){
|
||||
var constraints = {"audio": true, "video": { "mandatory": { "minWidth": 640, "maxWidth": 640, "minHeight": 480,"maxHeight": 480 }, "optional": [] } };//Chrome
|
||||
}else if(getBrowser() == "Firefox"){
|
||||
var constraints = {audio: true,video: { width: { min: 640, ideal: 640, max: 640 }, height: { min: 480, ideal: 480, max: 480 }}}; //Firefox
|
||||
}
|
||||
var mediaRecorder;
|
||||
var chunks = [];
|
||||
var count = 0;
|
||||
|
||||
$.ccio.vid = {element:$('#video')[0],canvas:$('#canvas')[0],data:$('#data')};
|
||||
$.ccio.vid.element.controls = false;
|
||||
navigator.getUserMedia(constraints,function(stream,fn) {
|
||||
$.ccio.vid.stream=stream;
|
||||
$('[record]').click($.ccio.startSending)
|
||||
}, function(err){console.error('getUserMedia',err)});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//draw selectable mons
|
||||
var tmp='';
|
||||
if($user.mons&&$user.mons.length>0){
|
||||
$.ccio.mon={};
|
||||
$.each($user.mons,function(n,v){
|
||||
v.details=JSON.parse(v.details);
|
||||
$.ccio.mon[v.mid]=v;
|
||||
tmp+='<a class="list-group-item" monitor="'+v.mid+'">'+v.name+'</a>';
|
||||
})
|
||||
$('#monitors').html(tmp)
|
||||
}else{
|
||||
tmp+="<h2>No Streamer Monitors Setup</h2>"
|
||||
tmp+="<small>Login to the Dashboard and add one. Set it to record or watch only.</small>"
|
||||
$('#msg').html(tmp)
|
||||
}
|
||||
delete(tmp);
|
||||
$('body').on('click','[monitor]',function(e){
|
||||
e.e=$(this);e.a=e.e.attr('monitor'),e.m=$.ccio.mon[e.a];
|
||||
$.ccio.selected=e.a;$('#selected').html(e.a);
|
||||
if(e.m.details.sfps&&e.m.details.sfps!==''){$.ccio.framerate=e.m.details.sfps}else{$.ccio.framerate=1;}
|
||||
$.ls.setItem('Shinobi_Dashcam',e.a)
|
||||
$('[monitor]').removeClass('active'),e.e.addClass('active');
|
||||
})
|
||||
|
||||
|
||||
if($user.mons.length>0&&$.ls.getItem('Shinobi_Dashcam')){$('[monitor="'+$.ls.getItem('Shinobi_Dashcam')+'"]').click()}else{$.ccio.selected=null;}
|
||||
|
||||
$('body')
|
||||
.on('click','.logout',function(e){
|
||||
localStorage.removeItem('ShinobiLogin_'+location.host);location.href='/';
|
||||
})
|
||||
function getBrowser(){
|
||||
var nVer = navigator.appVersion;
|
||||
var nAgt = navigator.userAgent;
|
||||
var browserName = navigator.appName;
|
||||
var fullVersion = ''+parseFloat(navigator.appVersion);
|
||||
var majorVersion = parseInt(navigator.appVersion,10);
|
||||
var nameOffset,verOffset,ix;
|
||||
|
||||
// In Opera, the true version is after "Opera" or after "Version"
|
||||
if ((verOffset=nAgt.indexOf("Opera"))!=-1) {
|
||||
browserName = "Opera";
|
||||
fullVersion = nAgt.substring(verOffset+6);
|
||||
if ((verOffset=nAgt.indexOf("Version"))!=-1)
|
||||
fullVersion = nAgt.substring(verOffset+8);
|
||||
}
|
||||
// In MSIE, the true version is after "MSIE" in userAgent
|
||||
else if ((verOffset=nAgt.indexOf("MSIE"))!=-1) {
|
||||
browserName = "Microsoft Internet Explorer";
|
||||
fullVersion = nAgt.substring(verOffset+5);
|
||||
}
|
||||
// In Chrome, the true version is after "Chrome"
|
||||
else if ((verOffset=nAgt.indexOf("Chrome"))!=-1) {
|
||||
browserName = "Chrome";
|
||||
fullVersion = nAgt.substring(verOffset+7);
|
||||
}
|
||||
// In Safari, the true version is after "Safari" or after "Version"
|
||||
else if ((verOffset=nAgt.indexOf("Safari"))!=-1) {
|
||||
browserName = "Safari";
|
||||
fullVersion = nAgt.substring(verOffset+7);
|
||||
if ((verOffset=nAgt.indexOf("Version"))!=-1)
|
||||
fullVersion = nAgt.substring(verOffset+8);
|
||||
}
|
||||
// In Firefox, the true version is after "Firefox"
|
||||
else if ((verOffset=nAgt.indexOf("Firefox"))!=-1) {
|
||||
browserName = "Firefox";
|
||||
fullVersion = nAgt.substring(verOffset+8);
|
||||
}
|
||||
// In most other browsers, "name/version" is at the end of userAgent
|
||||
else if ( (nameOffset=nAgt.lastIndexOf(' ')+1) <
|
||||
(verOffset=nAgt.lastIndexOf('/')) )
|
||||
{
|
||||
browserName = nAgt.substring(nameOffset,verOffset);
|
||||
fullVersion = nAgt.substring(verOffset+1);
|
||||
if (browserName.toLowerCase()==browserName.toUpperCase()) {
|
||||
browserName = navigator.appName;
|
||||
}
|
||||
}
|
||||
// trim the fullVersion string at semicolon/space if present
|
||||
if ((ix=fullVersion.indexOf(";"))!=-1)
|
||||
fullVersion=fullVersion.substring(0,ix);
|
||||
if ((ix=fullVersion.indexOf(" "))!=-1)
|
||||
fullVersion=fullVersion.substring(0,ix);
|
||||
|
||||
majorVersion = parseInt(''+fullVersion,10);
|
||||
if (isNaN(majorVersion)) {
|
||||
fullVersion = ''+parseFloat(navigator.appVersion);
|
||||
majorVersion = parseInt(navigator.appVersion,10);
|
||||
}
|
||||
|
||||
|
||||
return browserName;
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
a{cursor:pointer}
|
||||
body{color:#fff;background:#333;padding-top:30px;position:relative}
|
||||
.recording [record].btn{color: #fff;background-color: #5cb85c;border-color: #4cae4c;}
|
||||
#monitors{max-width:250px;width:100%;display:inline-block}
|
||||
#main{text-align:center}
|
||||
#msg h2{margin-top:0}
|
||||
#img{border-radius:5px;overflow:hidden;max-width:100%;display:inline-block;margin-top: 30px}
|
||||
#video,#canvas{position: absolute;display:none}
|
||||
</style>
|
413
web/pages/embed.ejs
Normal file
413
web/pages/embed.ejs
Normal file
|
@ -0,0 +1,413 @@
|
|||
<script>var io=null;</script>
|
||||
<%
|
||||
data.name='SHINOBI_'+data.ke+'_'+data.id;
|
||||
if(config.ssl&&config.ssl.port&&data.protocol==='https'){
|
||||
data.port=config.ssl.port
|
||||
}else{
|
||||
data.port=config.port
|
||||
}
|
||||
if(!data.port||data.port===''||data.port==80||data.port==443){data.url=baseUrl}else{data.url=baseUrl+':'+data.port}
|
||||
if(data.addon || data.addon.indexOf('relative')>-1){
|
||||
data.url=''
|
||||
}
|
||||
if(data.url.charAt(data.url.length - 1) !== '/'){
|
||||
data.url += '/'
|
||||
}
|
||||
%>
|
||||
<script src="<%=data.url%>libs/js/socket.io.js"></script>
|
||||
<script src="<%=data.url%>libs/js/poseidon.js"></script>
|
||||
<script src="<%=data.url%>libs/js/hls.min.js"></script>
|
||||
<script src="<%=data.url%>libs/js/flv.min.js"></script>
|
||||
<% if(data.addon){
|
||||
var ar={}
|
||||
decodeURI(data.addon).split('|').forEach(function(v){
|
||||
if(v.indexOf('=')>-1){
|
||||
v=v.split('=');
|
||||
ar[v[0]]=v[1];
|
||||
}
|
||||
})
|
||||
if(!ar.width){ar.width=640;}
|
||||
if(!ar.height){ar.height=480;}
|
||||
if(data.addon.indexOf('jquery')>-1){ %>
|
||||
<script src="<%=data.url%>libs/js/jquery.min.js"></script>
|
||||
<% };
|
||||
if(data.addon.indexOf('gui')>-1){ %>
|
||||
<style>
|
||||
body {position:relative;}
|
||||
.shinobi_stream{position:absolute;width:100%;height:100%;top:0;left:0;}
|
||||
.shinobi_hud{position:absolute;width:100%;height:100%;top:0;left:0;opacity:0;transition:0.2s}
|
||||
.shinobi_hud:hover{opacity:1}
|
||||
.shinobi_hud .shinobi_viewers{position:absolute;top:5px;left:5px;}
|
||||
.shinobi_hud .shinobi_viewers{
|
||||
display: inline-block;
|
||||
min-width: 10px;
|
||||
padding: 3px 7px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
background-color: #777;
|
||||
border-radius: 10px;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
iframe.stream-element{border:0;}
|
||||
/* All-CSS Toggle Switch (Checkbox Hack) by Marcus Burnette - https://codepen.io/mburnette/pen/LxNxNg */
|
||||
.shinobi_ws_http_toggle {
|
||||
width: 50px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
.shinobi_ws_http_toggle input[type=checkbox]{
|
||||
height: 0;
|
||||
width: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.shinobi_ws_http_toggle label {
|
||||
cursor: pointer;
|
||||
text-indent: -9999px;
|
||||
width: 100px;
|
||||
height: 20px;
|
||||
background: grey;
|
||||
display: block;
|
||||
border-radius: 100px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.shinobi_ws_http_toggle label:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #fff;
|
||||
border-radius: 90px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.shinobi_ws_http_toggle input:checked + label {
|
||||
background: #00118c;
|
||||
}
|
||||
|
||||
.shinobi_ws_http_toggle input:checked + label:after {
|
||||
left: calc(100% - 5px);
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.shinobi_ws_http_toggle label:active:after {
|
||||
width: 10px;
|
||||
}
|
||||
</style>
|
||||
<% };
|
||||
if(data.addon.indexOf('fullscreen')>-1){ %>
|
||||
<style>
|
||||
body,html{overflow: hidden;}
|
||||
*{margin:0;padding:0;border:0}
|
||||
.stream-element,.shinobi_stream{position:absolute;top:0;left:0;}
|
||||
</style>
|
||||
<script>
|
||||
$(window).resize(function(){
|
||||
$('.stream-element').attr('width',$('body').width())
|
||||
$('.stream-element').attr('height',$('body').height())
|
||||
})
|
||||
</script>
|
||||
<% } %>
|
||||
<% }else{
|
||||
//no addon set, do defaults
|
||||
var ar={};
|
||||
ar.width=640;
|
||||
ar.height=480;
|
||||
} %>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$('#<%= data.name %> canvas').attr('width',<%=ar.width%>).attr('height',<%=ar.height%>)
|
||||
})
|
||||
</script>
|
||||
<div class="shinobi_stream" id="<%= data.name %>">
|
||||
|
||||
<% switch(mon.details.stream_type){
|
||||
case'jpeg':
|
||||
%><img class="stream-element"><%
|
||||
break;
|
||||
case'flv':case'hls':case'mp4':
|
||||
%><video class="stream-element" autoplay></video><%
|
||||
break;
|
||||
case'mjpeg':
|
||||
%><iframe class="stream-element"></iframe><%
|
||||
break;
|
||||
default:
|
||||
%><canvas class="stream-element"></canvas><%
|
||||
break;
|
||||
} %>
|
||||
|
||||
|
||||
<% if(data.addon&&data.addon.indexOf('gui')>-1){ %>
|
||||
<div class="shinobi_hud">
|
||||
<div class="shinobi_viewers" title="Current number of viewers"></div>
|
||||
<div class="shinobi_ws_http_toggle" style="display:none">
|
||||
<input type="checkbox" id="shinobi_ws_http_toggle" <% if(mon.details.stream_flv_type === 'ws'){ %>checked<% } %> /><label for="shinobi_ws_http_toggle">WebSocket</label>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<script>
|
||||
var SHINOBI_TIMER=setInterval(function(){
|
||||
if(io){
|
||||
clearInterval(SHINOBI_TIMER);delete(SHINOBI_TIMER);
|
||||
if(!$.shinobi){
|
||||
$.shinobi={}
|
||||
};
|
||||
if(!$.shinobi.mon){
|
||||
$.shinobi.mon={}
|
||||
};
|
||||
$.shinobi.init=function(d){
|
||||
switch($.shinobi.mon[d.id].details.stream_type){
|
||||
case'b64':
|
||||
if($.shinobi.mon[d.id].Base64 && $.shinobi.mon[d.id].Base64.connected){
|
||||
$.shinobi.mon[d.id].Base64.disconnect()
|
||||
}
|
||||
$.shinobi.mon[d.id].Base64 = io('<%=data.url%>',{transports: ['websocket'], forceNew: false})
|
||||
var ws = $.shinobi.mon[d.id].Base64
|
||||
ws.on('diconnect',function(){
|
||||
console.log('Base64 Stream Disconnected')
|
||||
})
|
||||
ws.on('connect',function(){
|
||||
ws.emit('Base64',{
|
||||
auth:'<%=data.auth%>',
|
||||
ke:d.ke,
|
||||
uid:'<%=data.uid%>',
|
||||
id:d.id,
|
||||
url: '<%=data.url%>'
|
||||
})
|
||||
if(!$.shinobi.mon[d.id].ctx||$.shinobi.mon[d.id].ctx.length===0){
|
||||
$.shinobi.mon[d.id].ctx = $('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element')
|
||||
}
|
||||
var ctx = $.shinobi.mon[d.id].ctx[0]
|
||||
$.shinobi.mon[d.id].image = new Image()
|
||||
var image = $.shinobi.mon[d.id].image
|
||||
image.onload = function() {
|
||||
$.shinobi.mon[d.id].imageLoading = false
|
||||
d.x = 0
|
||||
d.y = 0
|
||||
// d.ratio = Math.min(ctx.width/image.width,ctx.height/image.height)
|
||||
// d.height = image.height * d.ratio
|
||||
// d.width = image.width * d.ratio
|
||||
// if(d.width < ctx.width){
|
||||
// d.x = (ctx.width / 2) - (d.width / 2)
|
||||
// }
|
||||
// if(d.height < ctx.height){
|
||||
// d.y = (ctx.height / 2) - (d.height / 2)
|
||||
// }
|
||||
// ctx.getContext("2d").drawImage(image,d.x,d.y,d.width,d.height)
|
||||
ctx.getContext("2d").drawImage(image,d.x,d.y,ctx.width,ctx.height)
|
||||
URL.revokeObjectURL($.ccio.mon[d.ke+d.id+user.auth_token].imageUrl)
|
||||
}
|
||||
ws.on('data',function(imageData){
|
||||
try{
|
||||
if($.shinobi.mon[d.id].imageLoading === true)return console.log('drop');
|
||||
// var base64Frame = 'data:image/jpeg;base64,'+$.ccio.base64ArrayBuffer(imageData)
|
||||
$.shinobi.mon[d.id].imageLoading = true
|
||||
// $.shinobi.mon[d.id].image.src = base64Frame
|
||||
var arrayBufferView = new Uint8Array(imageData);
|
||||
var blob = new Blob( [ arrayBufferView ], { type: "image/jpeg" } );
|
||||
$.shinobi.mon[d.id].imageUrl = URL.createObjectURL( blob );
|
||||
$.shinobi.mon[d.id].image.src = $.shinobi.mon[d.id].imageUrl
|
||||
$.shinobi.mon[d.id].last_frame = 'data:image/jpeg;base64,'+$.ccio.base64ArrayBuffer(imageData)
|
||||
}catch(er){
|
||||
console.log(er)
|
||||
$.ccio.log('base64 frame')
|
||||
}
|
||||
})
|
||||
})
|
||||
break;
|
||||
case'mp4':
|
||||
var stream = $('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element');
|
||||
if($.shinobi.mon[d.id].details.stream_flv_type==='ws'){
|
||||
if($.shinobi.mon[d.id].Poseidon){
|
||||
$.shinobi.mon[d.id].Poseidon.destroy()
|
||||
}
|
||||
var createPoseidon = function(){
|
||||
$.shinobi.mon[d.id].Poseidon = new Poseidon({
|
||||
video: stream[0],
|
||||
auth_token:'<%=data.auth%>',
|
||||
ke:d.ke,
|
||||
uid:'<%=data.uid%>',
|
||||
id:d.id,
|
||||
url: '<%=data.url%>'
|
||||
});
|
||||
$.shinobi.mon[d.id].Poseidon.start();
|
||||
$.shinobi.mon[d.id].Poseidon._socket.on('data',function(res){
|
||||
console.log(res)
|
||||
})
|
||||
}
|
||||
try{
|
||||
createPoseidon()
|
||||
}catch(err){
|
||||
console.log(err)
|
||||
setTimeout(function(){
|
||||
createPoseidon()
|
||||
},3000)
|
||||
}
|
||||
}else{
|
||||
stream.attr('src','<%=data.url%><%=data.auth%>/mp4/'+d.ke+'/'+d.id+'/s.mp4')
|
||||
}
|
||||
break;
|
||||
case'flv':
|
||||
if (flvjs.isSupported()) {
|
||||
if($.shinobi.mon[d.id].flv){
|
||||
$.shinobi.mon[d.id].flv.destroy()
|
||||
}
|
||||
var options = {};
|
||||
// if($.shinobi.mon[d.id].details.stream_flv_type==='ws'){
|
||||
// if($.shinobi.mon[d.id].details.stream_flv_maxLatency&&$.shinobi.mon[d.id].details.stream_flv_maxLatency!==''){
|
||||
// $.shinobi.mon[d.id].details.stream_flv_maxLatency = parseInt($.shinobi.mon[d.id].details.stream_flv_maxLatency)
|
||||
// }else{
|
||||
// $.shinobi.mon[d.id].details.stream_flv_maxLatency = 20000;
|
||||
// }
|
||||
// options = {
|
||||
// type: 'flv',
|
||||
// isLive: true,
|
||||
// auth_token:'<%=data.auth%>',
|
||||
// ke:d.ke,
|
||||
// uid:'<%=data.uid%>',
|
||||
// id:d.id,
|
||||
// maxLatency:$.shinobi.mon[d.id].details.stream_flv_maxLatency,
|
||||
// hasAudio:false,
|
||||
// url: '<%=data.url%>'
|
||||
// }
|
||||
// }else{
|
||||
options = {
|
||||
type: 'flv',
|
||||
isLive: true,
|
||||
url: '<%=data.url%><%=data.auth%>/flv/'+d.ke+'/'+d.id+'/s.flv'
|
||||
}
|
||||
// }
|
||||
$.shinobi.mon[d.id].flv = flvjs.createPlayer(options);
|
||||
$.shinobi.mon[d.id].flv.attachMediaElement($('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element')[0]);
|
||||
$.shinobi.mon[d.id].flv.on('error',function(err){
|
||||
console.log(err)
|
||||
});
|
||||
$.shinobi.mon[d.id].flv.load();
|
||||
$.shinobi.mon[d.id].flv.play();
|
||||
}else{
|
||||
alert({title:'Stream cannot be started',text:'FLV.js is not supported on this browser. Try another stream type.',type:'error'})
|
||||
}
|
||||
break;
|
||||
case'jpeg':
|
||||
d.mon=$.shinobi.mon[d.id]
|
||||
k=d.mon.details;
|
||||
k.jpegInterval=parseFloat(k.jpegInterval);
|
||||
if(!k.jpegInterval||k.jpegInterval===''||isNaN(k.jpegInterval)){k.jpegInterval=1}
|
||||
if(!$.shinobi.mon[d.mon.mid].jpegInterval){
|
||||
clearInterval($.shinobi.mon[d.mon.mid].jpegInterval);
|
||||
$.shinobi.mon[d.mon.mid].jpegInterval=setInterval(function(){
|
||||
$('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element').attr('src','<%=data.url%><%=data.auth%>/jpeg/'+d.mon.ke+'/'+d.mon.mid+'/s.jpg?time='+(new Date()).getTime())
|
||||
},1000/k.jpegInterval);
|
||||
}
|
||||
break;
|
||||
case'hls':
|
||||
var video = $('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element')[0];
|
||||
d.url='<%=data.url%><%=data.auth%>/hls/'+d.ke+'/'+d.id+'/s.m3u8';
|
||||
if($.shinobi.mon[d.id].hls){
|
||||
$.shinobi.mon[d.id].hls.destroy()
|
||||
}
|
||||
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)||(navigator.userAgent.match(/(Safari)/)&&!navigator.userAgent.match('Chrome'))) {
|
||||
video.src=d.url;
|
||||
video.play();
|
||||
}else{
|
||||
$.shinobi.mon[d.id].hls = new Hls();
|
||||
$.shinobi.mon[d.id].hls.loadSource(d.url);
|
||||
$.shinobi.mon[d.id].hls.attachMedia(video);
|
||||
$.shinobi.mon[d.id].hls.on(Hls.Events.MANIFEST_PARSED,function() {
|
||||
video.play();
|
||||
});
|
||||
}
|
||||
break;
|
||||
case'mjpeg':
|
||||
$('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element').attr('src','<%=data.url%><%=data.auth%>/mjpeg/'+d.ke+'/'+d.id+'?full=true')
|
||||
break;
|
||||
}
|
||||
}
|
||||
$.shinobi.mon['<%=data.id%>']=<%- JSON.stringify(mon) %>;
|
||||
if(!$.shinobi.callback){$.shinobi.callback=function(){}}
|
||||
if(!$.shinobi.ws||$.shinobi.ws.connected===false){
|
||||
$.shinobi.ws=io('<%=data.url%>');
|
||||
$.shinobi.ws.on('f',function (d){
|
||||
if(d.viewers){
|
||||
$('#SHINOBI_'+d.ke+'_'+d.id+' .shinobi_viewers').html(d.viewers);
|
||||
}
|
||||
switch(d.f){
|
||||
case'monitor_frame':
|
||||
var image = new Image();
|
||||
var ctx = $('#SHINOBI_'+d.ke+'_'+d.id+' canvas')[0];
|
||||
image.onload = function() {
|
||||
ctx.getContext("2d").drawImage(image,0,0,ctx.width,ctx.height);
|
||||
delete(d.frame);
|
||||
delete(image);
|
||||
};
|
||||
image.src='data:image/jpeg;base64,'+d.frame
|
||||
break;
|
||||
case'monitor_watch_off':case'monitor_watch_on':
|
||||
$('#SHINOBI_'+d.ke+'_'+d.id+' .shinobi_viewers').html(d.viewers)
|
||||
$.shinobi.init(d)
|
||||
break;
|
||||
case'monitor_edit':
|
||||
if(!d.id){d.id=d.mon.mid;}
|
||||
if($.shinobi.mon[d.id]){
|
||||
clearInterval($.shinobi.mon[d.id].jpegInterval);
|
||||
}
|
||||
d.e=$('#SHINOBI_'+d.ke+'_'+d.id+'');
|
||||
d.e.find('.stream-element').remove();
|
||||
d.tmp='';
|
||||
switch(d.mon.details.stream_type){
|
||||
case'hls':
|
||||
d.tmp+='<video class="stream-element" controls autoplay></video>';
|
||||
break;
|
||||
case'mjpeg':
|
||||
d.tmp+='<iframe class="stream-element"></iframe>';
|
||||
break;
|
||||
case'jpeg'://base64
|
||||
d.tmp+='<img class="stream-element">';
|
||||
break;
|
||||
default://base64
|
||||
d.tmp+='<canvas class="stream-element"></canvas>';
|
||||
break;
|
||||
}
|
||||
d.e.append(d.tmp).find('.stream-element').resize();
|
||||
$(window).resize();
|
||||
// d.mon.details=JSON.stringify(d.mon.details);
|
||||
d.mon.id = d.mon.mid
|
||||
$.shinobi.mon[d.mon.mid]=d.mon;
|
||||
$.shinobi.init(d.mon);
|
||||
break;
|
||||
}
|
||||
$.shinobi.callback()
|
||||
});
|
||||
};
|
||||
$.shinobi.ws.emit('e',{f:'init',auth:'<%=data.auth%>',id:'<%=data.id%>',ke:'<%=data.ke%>'})
|
||||
$(window).resize();
|
||||
}
|
||||
},1000);
|
||||
//websocket / http toggle
|
||||
$('#shinobi_ws_http_toggle').change(function(){
|
||||
var monitor = $.shinobi.mon['<%=data.id%>'];
|
||||
var parent = $(this).parents('.shinobi_ws_http_toggle')
|
||||
var label = parent.find('label')
|
||||
if(monitor.details.stream_flv_type !== 'ws'){
|
||||
label.text('WebSocket')
|
||||
monitor.details.stream_flv_type = 'ws'
|
||||
}else{
|
||||
label.text('HTTP')
|
||||
monitor.details.stream_flv_type = 'http'
|
||||
}
|
||||
monitor.id = monitor.mid
|
||||
$.shinobi.init(monitor);
|
||||
})
|
||||
$('.shinobi_ws_http_toggle').show()
|
||||
</script>
|
69
web/pages/factor.ejs
Normal file
69
web/pages/factor.ejs
Normal file
|
@ -0,0 +1,69 @@
|
|||
<% include blocks/header %>
|
||||
<div class="shinobi-bg display-table" style="height:100%">
|
||||
<div class="shinobi-bg-shade">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="panel panel-login">
|
||||
<div class="panel-heading">
|
||||
<div class="panel-heading-shade">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<a href="http://shinobi.video" target="_blank">
|
||||
<img src="libs/img/icon/apple-touch-icon-76x76.png" style="border-radius:50%">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<form id="auth-form" action="/" method="post" style="display: block;">
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-md-12 monospace" style="width:calc(100% - 48px)">
|
||||
<input type="hidden" name="ke" id="ke" value="<%-$user.ke%>">
|
||||
<input type="hidden" name="id" id="uid" value="<%-$user.uid%>">
|
||||
<input type="hidden" name="machineID" id="machineID" value="">
|
||||
<input type="password" name="factorAuthKey" id="factorAuthKey" tabindex="2" class="monospace form-control" placeholder="<%-lang['2-Factor Authentication']%>">
|
||||
</div>
|
||||
<div class="text-right" title="Remember This Machine" style="display:inline-block">
|
||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="remember_me">
|
||||
<input type="checkbox" id="remember_me" value="1" name="remember" class="mdl-switch__input">
|
||||
<span class="monospace mdl-switch__label"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin:20px"></div>
|
||||
<div class="form-group">
|
||||
<button type="submit" name="login-submit" id="login-submit" tabindex="4" class="btn btn-success btn-block"><%-lang.Authenticate%></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="libs/js/material.min.js"></script>
|
||||
<script>
|
||||
$.ccio={f:$('#auth-form'),ls:localStorage.getItem('ShinobiAuth_'+location.host)}
|
||||
$.ccio.gid=function(x){
|
||||
if(!x){x=10};var t = "";var p = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for( var i=0; i < x; i++ )
|
||||
t += p.charAt(Math.floor(Math.random() * p.length));
|
||||
return t;
|
||||
};
|
||||
if(!$.ccio.ls||$.ccio.ls===''){
|
||||
$.ccio.ls=$.ccio.gid(20)
|
||||
localStorage.setItem('ShinobiAuth_'+location.host,$.ccio.ls)
|
||||
}
|
||||
$(document).ready(function(){
|
||||
$('#machineID').val($.ccio.ls)
|
||||
})
|
||||
</script>
|
117
web/pages/grid.ejs
Normal file
117
web/pages/grid.ejs
Normal file
|
@ -0,0 +1,117 @@
|
|||
<%
|
||||
if(config.ssl&&config.ssl.port&&data.protocol==='https'){
|
||||
data.port=config.ssl.port
|
||||
}else{
|
||||
data.port=config.port
|
||||
}
|
||||
if(!data.port||data.port===''||data.port==80||data.port==443){data.url=baseUrl}else{data.url=baseUrl+':'+data.port}
|
||||
if(data.addon && data.addon.indexOf('relative')>-1){
|
||||
data.url=''
|
||||
}
|
||||
%>
|
||||
<script>
|
||||
var data = <%- JSON.stringify(data) %>
|
||||
if(!data.group || data.group === ''){
|
||||
data.group = '$'
|
||||
}
|
||||
</script>
|
||||
<link rel="stylesheet" href="<%=data.url%>/libs/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="<%=data.url%>/libs/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="<%=data.url%>/libs/css/gridstack.min.css">
|
||||
<link rel="stylesheet" href="<%=data.url%>/libs/css/gridstack-extra.min.css">
|
||||
<style>
|
||||
.grid-stack-item{
|
||||
border:1px solid #333;
|
||||
}
|
||||
.grid-stack-item-content-overlay{
|
||||
z-index:2;
|
||||
}
|
||||
.grid-stack-item-content-overlay,.grid-stack-item iframe{
|
||||
margin:0;width:100%;height:100%;position:absolute;top:0;left:0;border:0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="<%=data.url%>/libs/js/jquery.min.js"></script>
|
||||
<script src="<%=data.url%>/libs/js/jquery-ui.min.js"></script>
|
||||
<script src="<%=data.url%>/libs/js/bootstrap.min.js"></script>
|
||||
<script src="<%=data.url%>/libs/js/lodash.min.js"></script>
|
||||
<script src="<%=data.url%>/libs/js/gridstack.min.js"></script>
|
||||
<script src="<%=data.url%>/libs/js/gridstack.jQueryUI.min.js"></script>
|
||||
<div class="grid-stack"></div>
|
||||
<script type="text/javascript">
|
||||
$.grid = {};
|
||||
$.grid.op=function(r,rr,rrr){
|
||||
if(!rrr){rrr={};};if(typeof rrr === 'string'){rrr={n:rrr}};if(!rrr.n){rrr.n='ShinobiOptions_'+location.host}
|
||||
ii={o:localStorage.getItem(rrr.n)};try{ii.o=JSON.parse(ii.o)}catch(e){ii.o={}}
|
||||
if(!ii.o){ii.o={}}
|
||||
if(r&&rr&&!rrr.x){
|
||||
ii.o[r]=rr;
|
||||
}
|
||||
switch(rrr.x){
|
||||
case 0:
|
||||
delete(ii.o[r])
|
||||
break;
|
||||
case 1:
|
||||
delete(ii.o[r][rr])
|
||||
break;
|
||||
}
|
||||
localStorage.setItem(rrr.n,JSON.stringify(ii.o))
|
||||
return ii.o
|
||||
}
|
||||
$(function () {
|
||||
//init grid
|
||||
var gridTag = '.grid-stack';
|
||||
var gridEl = $(gridTag);
|
||||
var options = {
|
||||
cellHeight: 80,
|
||||
verticalMargin: 0,
|
||||
};
|
||||
//monitor="watch_off"
|
||||
gridEl.gridstack(options);
|
||||
gridEl.on('change', function (event, ui) {
|
||||
var savedGrids = $.grid.op().savedGrids;
|
||||
if(!savedGrids)savedGrids = {};
|
||||
savedGrids[data.group] = {};
|
||||
gridEl.find('.grid-stack-item').each(function(n,v){
|
||||
var el = $(v);
|
||||
var mid = el.attr('mid')
|
||||
savedGrids[data.group][mid] = {
|
||||
mid:mid,
|
||||
x:el.attr('data-gs-x'),
|
||||
y:el.attr('data-gs-y'),
|
||||
width:el.attr('data-gs-width'),
|
||||
height:el.attr('data-gs-height')
|
||||
};
|
||||
})
|
||||
$.grid.op('savedGrids',savedGrids)
|
||||
});
|
||||
|
||||
//draw grid items
|
||||
var gridX = 0;
|
||||
var gridY = 0;
|
||||
var itemW = 4;
|
||||
var itemH = 4;
|
||||
var savedGrids = $.grid.op().savedGrids;
|
||||
if(!savedGrids)savedGrids = {};
|
||||
if(!savedGrids[data.group])savedGrids[data.group] = {};
|
||||
var grid = gridEl.data('gridstack');
|
||||
$.each(<%- JSON.stringify(monitors) %>,function(n,monitor){
|
||||
var x = gridX;
|
||||
var y = gridY;
|
||||
var width = itemW;
|
||||
var height = itemH;
|
||||
var autoPlacement = true;
|
||||
var saved = savedGrids[data.group][monitor.mid];
|
||||
if(saved){
|
||||
x = saved.x;
|
||||
y = saved.y;
|
||||
width = saved.width;
|
||||
height = saved.height;
|
||||
autoPlacement = false;
|
||||
}
|
||||
var tmp = '<div auth="'+data.auth+'" ke="'+data.ke+'" mid="'+monitor.mid+'" class="grid-stack-item"><div class="grid-stack-item-content"><div class="grid-stack-item-content-overlay"><a monitor="watch_off"><i class="fa fa-times-circle"></i></a></div><iframe src="'+data.url+'/'+data.auth+'/embed/'+monitor.ke+'/'+monitor.mid+'/fullscreen|jquery|relative"></iframe></div></div>'
|
||||
grid.addWidget($(tmp), x, y, width, height, autoPlacement);
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
206
web/pages/home.ejs
Normal file
206
web/pages/home.ejs
Normal file
|
@ -0,0 +1,206 @@
|
|||
<% var details=JSON.parse($user.details) %>
|
||||
<% include blocks/header %>
|
||||
<script>var $user=<%- JSON.stringify($user) %>;</script>
|
||||
<link rel="stylesheet" href="libs/css/pnotify.custom.min.css">
|
||||
<link rel="stylesheet" href="libs/css/vbox.css">
|
||||
<link rel="stylesheet" href="libs/css/daterangepicker.css">
|
||||
<link rel="stylesheet" href="libs/css/circles.css">
|
||||
<!--<link rel="stylesheet" href="libs/css/poseidon.css">-->
|
||||
<link rel="stylesheet" href="libs/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="libs/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="libs/css/fullcalendar.min.css">
|
||||
<link rel="stylesheet" href="libs/css/gridstack.min.css">
|
||||
<link rel="stylesheet" href="libs/css/gridstack-extra.min.css">
|
||||
<link rel="stylesheet" href="libs/css/bootstrap-table.min.css">
|
||||
<link rel="stylesheet" href="libs/css/main.dash2.css">
|
||||
<style id="theme">
|
||||
<% if(details.theme&&details.theme!==''){ %><%- include(__dirname+'/web/libs/themes/'+details.theme+'/style.css'); %><% } %>
|
||||
</style>
|
||||
<style id="custom_css">
|
||||
<%= details.css %>
|
||||
</style>
|
||||
<style>
|
||||
<% if(details.sub&&details.allmonitors==='0'){
|
||||
if(details.monitor_edit&&details.monitor_edit!==''){
|
||||
details.monitor_edit.forEach(function(v,n){ %>
|
||||
[mid="<%= v %>"] .permission_monitor_edit{display:inline-block}
|
||||
<%
|
||||
})
|
||||
}
|
||||
if(details.video_delete&&details.video_delete!==''){
|
||||
details.video_delete.forEach(function(v,n){ %>
|
||||
[mid="<%= v %>"] .permission_video_delete{display:inline-block}
|
||||
<%
|
||||
})
|
||||
}
|
||||
}else{ %>
|
||||
.permission_video_delete,.permission_monitor_edit{display:inline-block}
|
||||
th.permission_video_delete,td.permission_video_delete{display:table-cell}
|
||||
<% } %>
|
||||
</style>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&lang=en">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<!-- <link rel="stylesheet" href="$$hosted_libs_prefix$$/$$version$$/material.cyan-light_blue.min.css">-->
|
||||
</head>
|
||||
<body>
|
||||
<div class="demo-layout demo-blog mdl-layout mdl-js-layout mdl-layout--fixed-drawer mdl-layout--fixed-header">
|
||||
<header id="main_header" class="demo-header mdl-layout__header">
|
||||
<div class="mdl-layout__header-row">
|
||||
<ul class="nav navbar-nav">
|
||||
<li title="<%-lang['Toggle Sidebar']%>" class_toggle="hide-side" data-target=".mdl-js-layout"><a> <i class="fa fa-bars"></i> </a></li>
|
||||
<li title="<%-lang['Add Monitor']%>" mid="" ke="" class="hidden-xs permission_monitor_edit"><a monitor="edit"> <i class="fa fa-plus"></i> </a></li>
|
||||
<li title="<%-lang['Power Video Viewer']%>" class="hidden-xs" mid="" ke=""><a monitor="powerview"> <i class="fa fa-map-marker"></i> </a></li>
|
||||
<li>
|
||||
<a title="<%-lang['Monitor Groups']%>" id="group_list_button" class="mdl-js-button"> <i class="fa fa-video-camera"></i> </a>
|
||||
<ul class="mdl-menu mdl-menu--bottom-left mdl-js-menu mdl-js-ripple-effect" for="group_list_button" id="group_list"></ul>
|
||||
</li>
|
||||
<li class="os_bars hidden-xs">
|
||||
<div class="display-table">
|
||||
<div class="cpu_load display-table-cell">
|
||||
<span class="pull-right percent"></span>
|
||||
<label><span class="os_cpuCount"></span> <%-lang.CPU%><span class="os_cpuCount_trailer"></span> <%-lang.on%> <span class="os_platform" style="text-transform:capitalize"></span></label>
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-warning" role="progressbar" style="width:0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ram_load display-table-cell">
|
||||
<span class="pull-right percent"></span>
|
||||
<label><span class="os_totalmem" style="letter-spacing:2px;font-weight:100"></span> <%-lang.MB%> <%-lang.RAM%></label>
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-warning" role="progressbar" style="width:0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="diskUsed display-table-cell">
|
||||
<span class="pull-right percent"></span>
|
||||
<label><span class="value" style="letter-spacing:2px;font-weight:100"></span></label>
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-warning" role="progressbar" style="width:0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mdl-layout-spacer"></div>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li role="presentation" class="dropdown">
|
||||
<div id="clock" class_toggle="twentyfour" data-target="#time-hours">
|
||||
<div id="time-date"></div>
|
||||
<ul>
|
||||
<li id="time-hours"></li>
|
||||
<li class="point">:</li>
|
||||
<li id="time-min"></li>
|
||||
<li class="point">:</li>
|
||||
<li id="time-sec"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li role="presentation" class="dropdown">
|
||||
<a href="javascript:;" class="dropdown-toggle info-number" data-toggle="dropdown" aria-expanded="true"> <i class="fa fa-group"></i> </a>
|
||||
<ul id="users_online" class="dropdown-menu list-unstyled msg_list scrollable" role="menu"></ul>
|
||||
</li>
|
||||
<!--
|
||||
<li role="presentation" class="dropdown">
|
||||
<a href="javascript:;" class="dropdown-toggle info-number" data-toggle="dropdown" aria-expanded="true"> <i class="fa fa-info-circle"></i> </a>
|
||||
<ul id="notifications" class="dropdown-menu list-unstyled msg_list scrollable" role="menu"></ul>
|
||||
</li>
|
||||
-->
|
||||
<li role="presentation" class="dropdown">
|
||||
<a href="javascript:;" class="dropdown-toggle info-number" data-toggle="dropdown" aria-expanded="true"> <i class="fa fa-exclamation-triangle"></i> </a>
|
||||
<ul id="logs" class="dropdown-menu list-unstyled msg_list scrollable" role="menu">
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<div class="demo-drawer mdl-layout__drawer">
|
||||
<header class="demo-drawer-header">
|
||||
<div class="demo-avatar-dropdown">
|
||||
<button id="accbtn" style="color:#fff" class="usermail mdl-button mdl-js-button mdl-js-ripple-effect"></button>
|
||||
<ul class="mdl-menu mdl-menu--bottom-left mdl-js-menu mdl-js-ripple-effect" for="accbtn">
|
||||
<li class="mdl-menu__item" data-toggle="modal" data-target="#multi_mon"><div><i class="fa fa-clone"></i><div><%- lang['Monitors'] %></div></div></li>
|
||||
<li class="mdl-menu__item" mid="" ke=""><div class="flex" monitor="powerview"><i class="fa fa-map-marker"></i><div><%- lang['Power Viewer'] %></div></div></li>
|
||||
<li class="mdl-menu__item" mid="" ke=""><div class="flex" monitor="timelapse"><i class="fa fa-angle-double-right"></i><div><%- lang['Time-lapse'] %></div></div></li>
|
||||
<li class="mdl-menu__item" data-toggle="modal" data-target="#settings"><div><i class="fa fa-gears"></i><div><%- lang.Settings %></div></div></li>
|
||||
<li class="mdl-menu__item" data-toggle="modal" data-target="#apis"><div><i class="fa fa-code"></i><div><%- lang.API %></div></div></li>
|
||||
<% if(!details.sub){ %>
|
||||
<li class="mdl-menu__item" data-toggle="modal" data-target="#onvif_probe"><div><i class="fa fa-rss"></i><div><%- lang.ONVIF %></div></div></li>
|
||||
<li class="mdl-menu__item" data-toggle="modal" data-target="#probe"><div><i class="fa fa-search"></i><div><%- lang.FFprobe %></div></div></li>
|
||||
<li class="mdl-menu__item" data-toggle="modal" data-target="#filters"><div><i class="fa fa-filter"></i><div><%- lang.Filters %></div></div></li>
|
||||
<% } %>
|
||||
<li class="mdl-menu__item" data-toggle="modal" data-target="#logs_modal"><div><i class="fa fa-exclamation-triangle"></i><div><%- lang.Logs %></div></div></li>
|
||||
<li class="mdl-menu__item" class_toggle="list-blocks" data-target="#left_menu"><div><i class="fa fa-camera"></i><div><%- lang['List Toggle'] %></div></div></li>
|
||||
<li class="mdl-menu__item" class_toggle="hide-side" data-target=".mdl-js-layout"><div><i class="fa fa-bars"></i><div><%- lang['Hide List'] %></div></div></li>
|
||||
<li class="mdl-menu__item shinobi-detector-motion shinobi-detector-opencv shinobi-detector_plug" class_toggle="hide_indifference" data-target="body" style="display:none"><div><i class="fa fa-bolt"></i><div><%- lang['Motion GUI'] %></div></div></li>
|
||||
<li class="mdl-menu__item" system="jpegToggle"><div><i class="fa fa-file-image-o"></i><div><%- lang['JPEG Mode'] %></div></div></li>
|
||||
<li class="mdl-menu__item" system="switch" switch="monitorOrder" type="text"><div><i class="fa fa-sort"></i><div><%- lang['Order Streams'] %></div></div></li>
|
||||
<li class="mdl-menu__item" system="switch" switch="notifyHide" type="text"><div><i class="fa fa-exclamation-circle"></i><div><%- lang['Hide Notes'] %></div></div></li>
|
||||
<li class="mdl-menu__item" data-toggle="modal" data-target="#migrator"><div><i class="fa fa-clone"></i><div><%- lang['Migrator'] %></div></div></li>
|
||||
<li class="mdl-menu__item logout"><div><i class="fa fa-sign-out"></i><div><%- lang.Logout %></div></div></li>
|
||||
</ul>
|
||||
<div class="mdl-layout-spacer"></div>
|
||||
<button class_toggle="list-blocks" data-target="#left_menu" class="mdl-button mdl-button--icon">
|
||||
<i class="fa fa-camera"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<nav class="demo-navigation mdl-navigation side-menu" id="left_menu">
|
||||
<div class="form-group col-md-12">
|
||||
<input class="form-control dark" placeholder="Search Monitors" id="monitors_list_search">
|
||||
</div>
|
||||
<div id="monitors_list" class="display-table"></div>
|
||||
<div class="mdl-layout-spacer"></div>
|
||||
</nav>
|
||||
</div>
|
||||
<main class="mdl-layout__content selected" id="main_canvas">
|
||||
<div class="grid-stack" id="monitors_live"></div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<!---->
|
||||
<div class="hidden" id="temp"></div>
|
||||
<% include blocks/multimon.ejs %>
|
||||
<% include blocks/api.ejs %>
|
||||
<% include blocks/logs.ejs %>
|
||||
<% include blocks/settings.ejs %>
|
||||
<% include blocks/filters.ejs %>
|
||||
<% include blocks/powervideo.ejs %>
|
||||
<% include blocks/timelapse.ejs %>
|
||||
<% include blocks/videoview.ejs %>
|
||||
<% include blocks/monitoredit.ejs %>
|
||||
<% include blocks/probe.ejs %>
|
||||
<% include blocks/region.ejs %>
|
||||
<% include blocks/migrator.ejs %>
|
||||
<% include blocks/confirm.ejs %>
|
||||
<% if(config.DropboxAppKey){ %>
|
||||
<!--Dropbox Library, Change data-app-key to your app key. -->
|
||||
<script type="text/javascript" src="https://www.dropbox.com/static/api/2/dropins.js" id="dropboxjs" data-app-key="<%= config.DropboxAppKey %>"></script>
|
||||
<!--Dropbox End-->
|
||||
<% } %>
|
||||
<script src="libs/js/material.min.js"></script>
|
||||
<script src="libs/js/pnotify.custom.min.js"></script>
|
||||
<script><% include ../libs/js/moment.js %></script>
|
||||
<script><% include ../libs/js/livestamp.min.js %></script>
|
||||
<script src="libs/js/placeholder.js"></script>
|
||||
<script src="libs/js/bootstrap.min.js"></script>
|
||||
<script src="libs/js/bootstrap-table.min.js"></script>
|
||||
<script src="libs/js/socket.io.js"></script>
|
||||
<script src="libs/js/fullcalendar.min.js"></script>
|
||||
<script src="libs/js/hls.min.js"></script>
|
||||
<script type="text/javascript" src="libs/js/flv.shinobi.js">;</script>
|
||||
<script src="libs/js/menu.js"></script>
|
||||
<script src="libs/js/clock.js"></script>
|
||||
<script src="libs/js/poseidon.js"></script>
|
||||
<script src="libs/js/Chart.js"></script>
|
||||
<script src="libs/js/clusterPoints.js"></script>
|
||||
<script src="libs/js/daterangepicker.js"></script>
|
||||
<script src="libs/js/jquery.canvasAreaDraw.js"></script>
|
||||
<script src="libs/js/jquery-ui.min.js"></script>
|
||||
<script src="libs/js/lodash.min.js"></script>
|
||||
<script src="libs/js/gridstack.min.js"></script>
|
||||
<script src="libs/js/gridstack.jQueryUI.min.js"></script>
|
||||
<script><% include ../libs/js/main.dash2.js %></script>
|
||||
<% include blocks/help.ejs %>
|
142
web/pages/index.ejs
Normal file
142
web/pages/index.ejs
Normal file
|
@ -0,0 +1,142 @@
|
|||
<% include blocks/header %>
|
||||
<% if(screen){screen=screen.toLowerCase()} %>
|
||||
<div class="shinobi-bg display-table" style="height:100%">
|
||||
<div class="shinobi-bg-shade">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="panel panel-login">
|
||||
<div class="panel-heading">
|
||||
<div class="panel-heading-shade">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<a href="http://shinobi.video" target="_blank">
|
||||
<img src="libs/img/icon/apple-touch-icon-76x76.png" style="border-radius:50%">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<form id="login-form" method="post" style="display: block;">
|
||||
<input type="hidden" name="machineID" id="machineID" value="">
|
||||
<div class="form-group">
|
||||
<input type="text" name="mail" id="email" tabindex="1" class="monospace form-control" placeholder="Email" value="">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" name="pass" id="pass" tabindex="2" class="monospace form-control" placeholder="Password">
|
||||
</div>
|
||||
<div class="form-group f_i_input f_i_ldap" style="display:none">
|
||||
<input name="key" id="key" tabindex="2" class="monospace form-control" placeholder="Group Key">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-md-12 monospace" style="width:calc(100% - 48px)">
|
||||
<select class="form-control" name="function" selector="f_i">
|
||||
<% switch(screen){
|
||||
case'super': %>
|
||||
<option value="super"><%- lang.Superuser %></option>
|
||||
<% break;
|
||||
case'admin': %>
|
||||
<option value="admin"><%- lang.Admin %></option>
|
||||
<% break;
|
||||
default: %>
|
||||
<option value="dash" selected><%- lang.Dashboard %></option>
|
||||
<% if(config.productType==='Pro'){ %>
|
||||
<option value="ldap"><%- lang.LDAP %></option>
|
||||
<% } %>
|
||||
<option value="streamer"><%- lang.Streamer %></option>
|
||||
<option value="cam"><%- lang.Dashcam %> (<%- lang.Streamer %> v2)</option>
|
||||
<% break;
|
||||
} %>
|
||||
</select>
|
||||
</div>
|
||||
<div class="text-right" title="<%- lang['Remember Me'] %>" style="display:inline-block">
|
||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="remember_me">
|
||||
<input type="checkbox" id="remember_me" value="1" name="remember" class="mdl-switch__input">
|
||||
<span class="monospace mdl-switch__label"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin:20px"></div>
|
||||
<div class="form-group">
|
||||
<button type="submit" name="login-submit" id="login-submit" tabindex="4" class="btn btn-success btn-block"><%- lang.Login %></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="libs/js/material.min.js"></script>
|
||||
<script>
|
||||
<% var failedLogin;if(failedLogin===true){ %>
|
||||
localStorage.removeItem('ShinobiLogin_'+location.host)
|
||||
<% } %>
|
||||
$.ccio={f:$('#login-form'),ls:localStorage.getItem('ShinobiLogin_'+location.host),auth:localStorage.getItem('ShinobiAuth_'+location.host)}
|
||||
$.ccio.gid=function(x){
|
||||
if(!x){x=10};var t = "";var p = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for( var i=0; i < x; i++ )
|
||||
t += p.charAt(Math.floor(Math.random() * p.length));
|
||||
return t;
|
||||
};
|
||||
if(!$.ccio.auth||$.ccio.auth===''){
|
||||
$.ccio.auth=$.ccio.gid(20)
|
||||
localStorage.setItem('ShinobiAuth_'+location.host,$.ccio.auth)
|
||||
}
|
||||
$(document).ready(function(){
|
||||
$('#machineID').val($.ccio.auth)
|
||||
})
|
||||
$.ccio.f.submit(function(e){
|
||||
$('input').css('border-color','')
|
||||
e.e=$(this),e.s=e.e.serializeObject(),e.inputs=e.e.find('input,button');
|
||||
if(e.s.remember){
|
||||
localStorage.setItem('ShinobiLogin_'+location.host,JSON.stringify({mail:e.s.mail,pass:e.s.pass,function:e.s.function}))
|
||||
}else{localStorage.removeItem('ShinobiLogin_'+location.host)}
|
||||
})
|
||||
if($.ccio.ls){
|
||||
$.ccio.ls=JSON.parse($.ccio.ls);
|
||||
$.each($.ccio.ls,function(n,v){
|
||||
n=$.ccio.f.find('[name="'+n+'"]')
|
||||
if(n.attr('type')==='checkbox'){
|
||||
n.prop('checked',true)
|
||||
}else{
|
||||
n.val(v)
|
||||
}
|
||||
});
|
||||
$.ccio.f.find('[name="remember"]').prop('checked',true);
|
||||
$.ccio.f.submit()
|
||||
}
|
||||
$('[name="function"]').change(function(){
|
||||
e={};
|
||||
e.removeClass='btn-danger btn-primary btn-success btn-warning'
|
||||
switch($(this).val()){
|
||||
case'streamer':
|
||||
e.addClass='btn-warning'
|
||||
break;
|
||||
case'admin':
|
||||
e.addClass='btn-primary'
|
||||
break;
|
||||
case'super':
|
||||
e.addClass='btn-danger'
|
||||
break;
|
||||
default:
|
||||
e.addClass='btn-success'
|
||||
break;
|
||||
}
|
||||
$('#login-submit').removeClass(e.removeClass).addClass(e.addClass)
|
||||
})
|
||||
$('[selector]').change(function(e){
|
||||
e.v=$(this).val();e.a=$(this).attr('selector')
|
||||
$('.'+e.a+'_input').hide()
|
||||
$('.'+e.a+'_'+e.v).show();
|
||||
$('.'+e.a+'_text').text($(this).find('option:selected').text())
|
||||
}).change();
|
||||
</script>
|
5
web/pages/mjpeg.ejs
Normal file
5
web/pages/mjpeg.ejs
Normal file
|
@ -0,0 +1,5 @@
|
|||
<img class="shinobi-mjpeg-stream" src="<%= url %>">
|
||||
<style>
|
||||
body{margin:0;padding:0;}
|
||||
.shinobi-mjpeg-stream{width:100%;height:100%;position:absolute}
|
||||
</style>
|
1
web/pages/script.ejs
Normal file
1
web/pages/script.ejs
Normal file
|
@ -0,0 +1 @@
|
|||
<%- include('../libs/js/'+file) %>
|
143
web/pages/streamer.ejs
Normal file
143
web/pages/streamer.ejs
Normal file
|
@ -0,0 +1,143 @@
|
|||
<% include blocks/header %>
|
||||
<script>var $user=<%- JSON.stringify($user) %>;</script>
|
||||
<link rel="stylesheet" href="libs/css/bootstrap.min.css">
|
||||
<div id="main" class="container">
|
||||
<a class="btn btn-danger btn-lg" record>Stream</a>
|
||||
<div>
|
||||
<img id="img">
|
||||
</div>
|
||||
<div id="msg">Monitor to Stream and Save under <span id="selected"></span></div>
|
||||
requires https or firefox
|
||||
<div style="margin-top:30px;">
|
||||
<div id="monitors" class="list-group"></div>
|
||||
</div>
|
||||
<div style="margin-top:30px;">
|
||||
<a class="logout btn-danger btn btn-xs">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
<video id="video"><source></video>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script src="libs/js/jquery.min.js"></script>
|
||||
<script src="libs/js/socket.io.js"></script>
|
||||
<script src="libs/js/menu.js"></script>
|
||||
<script>
|
||||
$.ccio={};$.ls=localStorage;
|
||||
$.ccio.ws=io(location.origin);
|
||||
$.ccio.ws.on('connect',function (d){
|
||||
$(document).ready(function(e){
|
||||
$.ccio.cx=function(x){if(!x.ke){x.ke=$user.ke;};if(!x.uid){x.uid=$user.uid;};return $.ccio.ws.emit('r',x)}
|
||||
console.log($user)
|
||||
$.ccio.cx({f:'init',ke:$user.ke,auth:$user.auth_token,uid:$user.uid})
|
||||
})
|
||||
})
|
||||
$.ccio.ws.on('f',function(d){
|
||||
if(d.f!=='monitor_frame'&&d.f!=='cpu'&&d.f!=='event_delete'){console.log(d);}
|
||||
switch(d.f){
|
||||
case'monitor_edit':
|
||||
if(!$.ccio.mon[d.mid]){$.ccio.mon[d.mid]={}}
|
||||
$.each(d.mon,function(n,v){
|
||||
$.ccio.mon[d.mid][n]=v;
|
||||
});
|
||||
break;
|
||||
}
|
||||
})
|
||||
function _base64ToArrayBuffer(base64) {
|
||||
var binary_string = window.atob(base64.replace(/\s/g, "").replace("data:image/jpeg;base64,", ""));
|
||||
var len = binary_string.length;
|
||||
var bytes = new Uint8Array( len );
|
||||
for (var i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
$.ccio.framerate=2;
|
||||
$.ccio.end=function(){$('#main').removeClass('recording');clearInterval($.ccio.interval);$('[record]').unbind('click').click($.ccio.start)}
|
||||
$.ccio.start=function(){
|
||||
if($.ccio.selected===null){return false;}
|
||||
$('#main').addClass('recording');
|
||||
$.ccio.interval=setInterval(function(){
|
||||
var ctx = $.ccio.vid.c[0].getContext('2d');
|
||||
ctx.drawImage($.ccio.vid.e[0],0,0);
|
||||
ctx=$.ccio.vid.c[0].toDataURL('image/jpeg');$('#img')[0].src=ctx;
|
||||
ctx=_base64ToArrayBuffer(ctx);
|
||||
$.ccio.cx({f:'monitor_frame',frame:ctx,mid:$.ccio.selected});
|
||||
delete(ctx);
|
||||
},1000/$.ccio.framerate);
|
||||
$('[record]').unbind('click').click($.ccio.end)
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$.ccio.vid = {e:$('#video'),c:$('#canvas')};
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia||navigator.mediaDevices.getUserMedia;
|
||||
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
|
||||
navigator.getUserMedia({video: true},function(stream,fn) {
|
||||
//set video element
|
||||
if ($.ccio.vid.e[0].mozSrcObject !== undefined) {
|
||||
$.ccio.vid.e[0].mozSrcObject = stream;
|
||||
} else {
|
||||
$.ccio.vid.e[0].src = (window.URL && window.URL.createObjectURL(stream)) || stream;
|
||||
};
|
||||
$.ccio.vid.e[0].play();
|
||||
$.ccio.vid.e[0].addEventListener('loadeddata', function(){
|
||||
$.ccio.vid.c[0].width = $.ccio.vid.e.width();
|
||||
$.ccio.vid.c[0].height = $.ccio.vid.e.height();
|
||||
$('[record]').click($.ccio.start)
|
||||
})
|
||||
}, function(err){console.error('getUserMedia',err)});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//draw selectable mons
|
||||
var tmp='';
|
||||
if($user.mons&&$user.mons.length>0){
|
||||
$.ccio.mon={};
|
||||
$.each($user.mons,function(n,v){
|
||||
v.details=JSON.parse(v.details);
|
||||
$.ccio.mon[v.mid]=v;
|
||||
tmp+='<a class="list-group-item" monitor="'+v.mid+'">'+v.name+'</a>';
|
||||
})
|
||||
$('#monitors').html(tmp)
|
||||
}else{
|
||||
tmp+="<h2>No Streamer Monitors Setup</h2>"
|
||||
tmp+="<small>Login to the Dashboard and add one. Set it to record or watch only.</small>"
|
||||
$('#msg').html(tmp)
|
||||
}
|
||||
delete(tmp);
|
||||
$('body').on('click','[monitor]',function(e){
|
||||
e.e=$(this);e.a=e.e.attr('monitor'),e.m=$.ccio.mon[e.a];
|
||||
$.ccio.selected=e.a;$('#selected').html(e.a);
|
||||
if(e.m.details.sfps&&e.m.details.sfps!==''){$.ccio.framerate=e.m.details.sfps}else{$.ccio.framerate=1;}
|
||||
$.ls.setItem('Shinobi_socket_camera',e.a)
|
||||
$('[monitor]').removeClass('active'),e.e.addClass('active');
|
||||
})
|
||||
|
||||
|
||||
if($user.mons.length>0&&$.ls.getItem('Shinobi_socket_camera')){$('[monitor="'+$.ls.getItem('Shinobi_socket_camera')+'"]').click()}else{$.ccio.selected=null;}
|
||||
|
||||
$('body')
|
||||
.on('click','.logout',function(e){
|
||||
localStorage.removeItem('ShinobiLogin_'+location.host);location.href='/';
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
a{cursor:pointer}
|
||||
body{color:#fff;background:#333;padding-top:30px;position:relative}
|
||||
.recording [record].btn{color: #fff;background-color: #5cb85c;border-color: #4cae4c;}
|
||||
#monitors{max-width:250px;width:100%;display:inline-block}
|
||||
#main{text-align:center}
|
||||
#msg h2{margin-top:0}
|
||||
#img{border-radius:5px;overflow:hidden;max-width:100%;display:inline-block;margin-top: 30px}
|
||||
#video,#canvas{position: absolute;display:none}
|
||||
</style>
|
300
web/pages/super.ejs
Normal file
300
web/pages/super.ejs
Normal file
|
@ -0,0 +1,300 @@
|
|||
<% include blocks/header %>
|
||||
<link rel="stylesheet" href="libs/css/pnotify.custom.min.css">
|
||||
<link rel="stylesheet" href="libs/css/vbox.css">
|
||||
<link rel="stylesheet" href="libs/css/circles.css">
|
||||
<link rel="stylesheet" href="libs/css/material.min.css">
|
||||
<link rel="stylesheet" href="libs/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="libs/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="libs/css/fullcalendar.min.css">
|
||||
<link rel="stylesheet" href="libs/css/bootstrap-table.min.css">
|
||||
<link rel="stylesheet" href="libs/css/main.dash2.css">
|
||||
<style>
|
||||
.form-group label>div:first-child{width:40%}
|
||||
</style>
|
||||
<body style="background:rgba(33,33,33,1);">
|
||||
<div class="container-fluid">
|
||||
<div class="container">
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container-fluid">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="/"><%-lang.superAdminTitle%></a>
|
||||
</div>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<!-- <li><a href="#">Link</a></li>-->
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> <i class="fa fa-bars"></i> </a>
|
||||
<ul class="dropdown-menu">
|
||||
<!--
|
||||
<li><a href="#">Action</a></li>
|
||||
<li><a href="#">Another action</a></li>
|
||||
<li><a href="#">Something else here</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
-->
|
||||
<li><a class="logout"><i class="fa fa-sign-out pull-right"></i> <%-lang.Logout%></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.container-fluid -->
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group-group grey" id="accounts">
|
||||
<h4><%-lang.Accounts%><a class="pull-right add btn btn-success btn-xs"><i class="fa fa-plus"></i></a></h4>
|
||||
<table class="table table-striped"></table>
|
||||
</div>
|
||||
<div class="form-group-group primary no-padding" id="logs">
|
||||
<h4 class="no-margin"><%-lang['Logs']%><a class="pull-right delete btn btn-danger btn-xs"><i class="fa fa-trash-o"></i></a></h4>
|
||||
<div style="height:400px;overflow:auto">
|
||||
<table class="table table-striped"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<form class="form-group-group red" id="conf_json">
|
||||
<h4><%-lang['Configuration']%> <small class="pull-right msg"></small></h4>
|
||||
<div class="form-group">
|
||||
<textarea name="json" style="height:300px;font-family: monospace;" class="form-control"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div class="pull-right">
|
||||
<button type="submit" class="btn btn-success"><i class="fa fa-check"></i> <%-lang.Save%></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-group-group blue" id="system">
|
||||
<h4><%-lang['System']%> <small class="pull-right msg"></small></h4>
|
||||
<div class="btn-group btn-group-justified">
|
||||
<a restart="system" class="btn btn-danger"><i class="fa fa-retweet"></i> <%-lang['Restart Core']%></a>
|
||||
<a restart="cron" class="btn btn-danger"><i class="fa fa-retweet"></i> <%-lang['Restart CRON']%></a>
|
||||
<a restart="logs" class="btn btn-danger"><i class="fa fa-retweet"></i> <%-lang['Flush PM2 Logs']%></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% include blocks/confirm.ejs %>
|
||||
<div id="toast" class="mdl-js-snackbar mdl-snackbar">
|
||||
<div class="mdl-snackbar__text"></div>
|
||||
<button class="mdl-snackbar__action" type="button"></button>
|
||||
</div>
|
||||
</body>
|
||||
<script><% include ../libs/js/material.min.js %></script>
|
||||
<script><% include ../libs/js/pnotify.custom.min.js %></script>
|
||||
<script><% include ../libs/js/moment.js %></script>
|
||||
<script><% include ../libs/js/livestamp.min.js %></script>
|
||||
<script><% include ../libs/js/placeholder.js %></script>
|
||||
<script><% include ../libs/js/bootstrap.min.js %></script>
|
||||
<script><% include ../libs/js/bootstrap-table.min.js %></script>
|
||||
<script><% include ../libs/js/socket.io.js %></script>
|
||||
<script>$user=<%-JSON.stringify($user)%></script>
|
||||
<script>
|
||||
$.ccio={accounts:{}};$.ls=localStorage;
|
||||
if(!$user.lang||$user.lang==''){
|
||||
$user.lang="<%-config.language%>"
|
||||
}
|
||||
switch($user.lang){
|
||||
case'ar'://Arabic
|
||||
case'bn'://Bengali
|
||||
$('body').addClass('right-to-left')
|
||||
$('.mdl-menu__item').each(function(n,v){
|
||||
v=$(v).find('i')
|
||||
v.appendTo(v.parent())
|
||||
})
|
||||
break;
|
||||
}
|
||||
$.ccio.ws=io(location.origin);
|
||||
$.ccio.cx=function(x){return $.ccio.ws.emit('super',x)}
|
||||
$.ccio.ws.on('connect',function(d){
|
||||
$.ccio.cx({f:'init',mail:$user.mail,pass:$user.pass});
|
||||
})
|
||||
$.ccio.ws.on('f',function(d){
|
||||
switch(d.f){
|
||||
case'log':
|
||||
$.ccio.tm(4,d.log,'#logs table')
|
||||
break;
|
||||
case'save_configuration':
|
||||
$.conf.msg.html('Saved, Restart Shinobi to apply changes.')
|
||||
break;
|
||||
case'edit_account':
|
||||
d.msg='Account Edited';
|
||||
$.each(d.form,function(n,v){
|
||||
$.ccio.accounts[d.ke][n]=v;
|
||||
});
|
||||
break;
|
||||
case'add_account':
|
||||
d.msg='Account Created';
|
||||
$.ccio.tm(0,d,'#accounts table')
|
||||
$.aN.selected=$.ccio.accounts[d.ke];
|
||||
break;
|
||||
case'delete_account':
|
||||
$('#accounts table tr[ke="'+d.ke+'"]').remove()
|
||||
break;
|
||||
}
|
||||
if(d.msg&&typeof d.msg==='string'){
|
||||
$('#toast')[0].MaterialSnackbar.showSnackbar({message:d.msg})
|
||||
}
|
||||
})
|
||||
|
||||
$.ccio.tm=function(x,d,z,k){
|
||||
var tmp='';if(!d){d={}};if(!k){k={}};
|
||||
if(d.id&&!d.mid){d.mid=d.id;}
|
||||
switch(x){
|
||||
case 0://account row
|
||||
d.detailsJSON=JSON.parse(d.details);
|
||||
$.ccio.accounts[d.ke]=d;
|
||||
tmp+='<tr ke="'+d.ke+'"><td><b class="mail">'+d.mail+'</b></td><td>'+d.ke+'</td><td><a class="permission btn btn-xs btn-primary"><i class="fa fa-lock"></i></a></td><td><a class="delete btn btn-xs btn-danger"><i class="fa fa-trash-o"></i></a></td></tr>';
|
||||
break;
|
||||
case 4://log row, draw to global and monitor
|
||||
if(!d.time){d.time=$.ccio.init('t')}
|
||||
tmp+='<tr class="search-row"><td><span title="'+d.time+'" class="livestamp"></span><br><small>'+d.time+'</small><br><small>'+d.mid+'</small></td><td></td><td><pre class="pre-inline">'+$.ccio.init('jsontoblock',JSON.parse(d.info))+'</pre></td></tr>'
|
||||
break;
|
||||
}
|
||||
if(z){
|
||||
$(z).prepend(tmp)
|
||||
}
|
||||
switch(x){
|
||||
case 4:
|
||||
$.ccio.init('ls')
|
||||
break;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
users=<%-JSON.stringify(users)%>;
|
||||
if(users){
|
||||
$.each(users,function(n,v){
|
||||
$.ccio.tm(0,v,'#accounts table')
|
||||
})
|
||||
}
|
||||
$.ccio.init=function(x,d,z,k){
|
||||
if(!k){k={}};k.tmp='';
|
||||
switch(x){
|
||||
case 'ls'://livestamp all
|
||||
g={e:jQuery('.livestamp')};
|
||||
g.e.each(function(){g.v=jQuery(this),g.t=g.v.attr('title');if(!g.t){return};g.v.toggleClass('livestamp livestamped').attr('title',$.ccio.init('t',g.t)).livestamp(g.t);})
|
||||
return g.e
|
||||
break;
|
||||
case't'://format time
|
||||
if(!d){d=new Date();}
|
||||
return moment(d).format('YYYY-MM-DD HH:mm:ss')
|
||||
break;
|
||||
case'jsontoblock'://draw json as block
|
||||
if(d instanceof Object){
|
||||
k.tmp+='<ul>'
|
||||
$.each(d,function(n,v){
|
||||
k.tmp+='<li>';
|
||||
k.tmp+='<b>'+n+'</b> : '+$.ccio.init('jsontoblock',v);
|
||||
k.tmp+='</li>';
|
||||
})
|
||||
k.tmp+='</ul>'
|
||||
}else{
|
||||
k.tmp+='<span>';
|
||||
k.tmp+=d;
|
||||
k.tmp+='</span>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
return k.tmp;
|
||||
}
|
||||
//logs
|
||||
$.logs={e:$('#logs')}
|
||||
$.logs.e.find('.delete').click(function(e){
|
||||
$.confirm.e.modal('show');
|
||||
$.confirm.title.html('Delete Logs <small>'+e.u+'</small>')
|
||||
e.html='Do you want to delete these logs? User logs will <b>not</b> be deleted.'
|
||||
$.confirm.body.html(e.html)
|
||||
$.confirm.click({title:'Delete',class:'btn-danger'},function(){
|
||||
$.ccio.cx({f:'logs',ff:'delete',ke:'$'})
|
||||
$.logs.e.find('table').empty()
|
||||
});
|
||||
})
|
||||
//config editor
|
||||
$.conf={e:$('#conf_json')};$.conf.draw=$.conf.e.find('[name="json"]'),$.conf.msg=$.conf.e.find('.msg')
|
||||
$.conf.valid=1;
|
||||
$.conf.draw.val(JSON.stringify(<%-JSON.stringify(plainConfig)%>,null,3))
|
||||
$.conf.draw.keyup(function(){
|
||||
var msg=''
|
||||
var color=''
|
||||
try{
|
||||
$.parseJSON($.conf.draw.val())
|
||||
msg='Valid JSON'
|
||||
color='success'
|
||||
$.conf.valid=1
|
||||
}catch(er){
|
||||
msg='Not a valid JSON'
|
||||
color='danger'
|
||||
$.conf.valid=0
|
||||
}
|
||||
$.conf.msg.text(msg).css('color',color)
|
||||
})
|
||||
$.conf.e.submit(function(e){
|
||||
e.preventDefault()
|
||||
if($.conf.valid===1){
|
||||
$.confirm.e.modal('show');
|
||||
$.confirm.title.html('Save Configuration <small>conf.json</small>')
|
||||
e.html='<p>This is a change being applied to the configuration file (conf.json). Are you sure you want to do this? You must restart Shinobi for these changes to take effect. <b>The JSON below is what you are about to save.</b></p>'
|
||||
e.html+='<pre>'+JSON.stringify($.parseJSON($.conf.draw.val()),null,3)+'</pre>'
|
||||
$.confirm.body.html(e.html)
|
||||
$.confirm.click({title:'Save',class:'btn-success'},function(){
|
||||
$.ccio.cx({f:'system',ff:'configure',data:$.parseJSON($.conf.draw.val())})
|
||||
});
|
||||
}else{
|
||||
$.conf.msg.text('Invalid JSON Syntax, Cannot Save.')
|
||||
}
|
||||
return false;
|
||||
})
|
||||
//sys controls
|
||||
$.system={e:$('#system')}
|
||||
$.system.e.find('[system]').click(function(e){
|
||||
switch($(this).attr('system')){
|
||||
case'update':
|
||||
e.distro=$(this).attr('distro')
|
||||
if(!e.distro){e.distro='master'}
|
||||
$.confirm.e.modal('show')
|
||||
$.confirm.title.html('Update Shinobi?')
|
||||
$.confirm.body.html('Updating Shinobi means overwriting files. If you have modified any files yourself you should update Shinobi manually. The updater will download the latest '+e.distro+' from the <a href="https://github.com/ShinobiCCTV/Shinobi" target="_blank">Pro repository</a>.')
|
||||
$.confirm.click({title:'Update',class:'btn-danger'},function(){
|
||||
$.ccio.cx({f:'system',ff:'update',distro:e.distro})
|
||||
});
|
||||
break;
|
||||
}
|
||||
})
|
||||
$.system.e.find('[restart]').click(function(e){
|
||||
$.confirm.e.modal('show');
|
||||
e.html=''
|
||||
e.title=''
|
||||
e.target=$(this).attr('restart').split(',')
|
||||
e.target.forEach(function(v){
|
||||
switch(v){
|
||||
case'system':
|
||||
e.html+='<p>Do you want to restart the core (camera.js)? plugins will not be restarted. They will reconnect when Shinobi is back online.</p>'
|
||||
break;
|
||||
case'cron':
|
||||
e.html+='<p>Do you want to restart the CRON (cron.js)?</p>'
|
||||
break;
|
||||
case'logs':
|
||||
e.html+='<p>Flush PM2 console logs? The logs saved in the database will <b>not</b> be deleted.</p>'
|
||||
break;
|
||||
}
|
||||
})
|
||||
$.confirm.title.html('Restart?')
|
||||
$.confirm.body.html(e.html)
|
||||
$.confirm.click({title:'Restart',class:'btn-danger'},function(){
|
||||
$.ccio.cx({f:'system',ff:'restart',target:e.target})
|
||||
});
|
||||
})
|
||||
$(document).ready(function(){
|
||||
$.each(<%-JSON.stringify(Logs)%>,function(n,v){
|
||||
$.ccio.tm(4,v,'#logs table')
|
||||
})
|
||||
})
|
||||
//
|
||||
$('body')
|
||||
.on('click','.logout',function(e){
|
||||
localStorage.removeItem('ShinobiLogin_'+location.host);location.href='/';
|
||||
})
|
||||
</script>
|
||||
<% include blocks/mainpermissions.ejs %>
|
Loading…
Add table
Add a link
Reference in a new issue