281 lines
8.8 KiB
JavaScript
281 lines
8.8 KiB
JavaScript
//
|
|
// ZoneMinder MonitorLink Expression
|
|
// Copyright (C) 2022 ZoneMinder Inc
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
//
|
|
|
|
function tokenize(expr) {
|
|
const tokens = [];
|
|
|
|
let first_index = 0;
|
|
let second_index = 0;
|
|
while (second_index < expr.length) {
|
|
const character = expr.at(second_index);
|
|
|
|
if (character == '&' || character == '|' || character == ',') {
|
|
if (first_index != second_index) {
|
|
tokens[tokens.length] = {type: 'link', value: expr.substring(first_index, second_index)};
|
|
}
|
|
tokens[tokens.length] = {type: character, value: character};
|
|
first_index = second_index+1;
|
|
} else if (character == '(' || character == ')') {
|
|
if (first_index != second_index) {
|
|
tokens[tokens.length] = {type: 'link', value: expr.substring(first_index, second_index)};
|
|
}
|
|
// Now check for repeats
|
|
let third = second_index+1;
|
|
|
|
for (; third<expr.length; third++) {
|
|
if (expr.at(i) != character) break;
|
|
}
|
|
if (third != second_index+1) {
|
|
tokens[tokens.length] = {type: character, value: expr.substring(second_index, third)};
|
|
} else {
|
|
tokens[tokens.length] = {type: character, value: character};
|
|
}
|
|
first_index = third;
|
|
}
|
|
second_index++;
|
|
} // end for second_index
|
|
|
|
if (second_index) {
|
|
if (second_index != first_index) {
|
|
tokens[tokens.length] = {type: 'link', value: expr.substring(first_index, second_index)};
|
|
}
|
|
}
|
|
return tokens;
|
|
}
|
|
|
|
function count_terms(tokens) {
|
|
let term_count = 0;
|
|
for (let i=0; i<tokens.length; i++) {
|
|
if (tokens[i].type == 'link') term_count++;
|
|
}
|
|
return term_count;
|
|
}
|
|
|
|
function expr_to_ui(expr, container) {
|
|
container.html('');
|
|
var tokens = tokenize(expr);
|
|
console.log(tokens);
|
|
//const term_count = count_terms(tokens);
|
|
let brackets = 0;
|
|
const used_monitorlinks = [];
|
|
|
|
if (!tokens.length) return;
|
|
|
|
// Every monitorlink should have possible parenthesis on either side of it
|
|
if (tokens.length > 3) {
|
|
if (tokens[0].type != '(') {
|
|
tokens.unshift({type: '(', value: ''});
|
|
}
|
|
if (tokens[tokens.length-1].type != ')') {
|
|
tokens.push({type: ')', value: ''});
|
|
}
|
|
for (let token_index = 1; token_index < tokens.length-1; token_index++) {
|
|
const token = tokens[token_index];
|
|
|
|
if (token.type == 'link') {
|
|
if (tokens[token_index-1].type != '(' && tokens[token_index-1].type != ')') {
|
|
console.log("Adding () before ", token, token_index);
|
|
tokens.splice(token_index, 0, {type: '()', value: ''});
|
|
token_index ++;
|
|
}
|
|
if (tokens[token_index+1].type != '(' && tokens[token_index+1].type != ')') {
|
|
console.log("Adding () after ", token, token_index);
|
|
tokens.splice(token_index+1, 0, {type: '()', value: ''});
|
|
token_index ++;
|
|
}
|
|
brackets++;
|
|
used_monitorlinks.push(token.value);
|
|
}
|
|
} // end foreach token
|
|
}
|
|
brackets --;
|
|
|
|
for (let token_index = 0; token_index < tokens.length; token_index++) {
|
|
const token = tokens[token_index];
|
|
|
|
if (token.type == 'link') {
|
|
const select = $j('<select></select>');
|
|
for ( monitor_id in monitors ) {
|
|
const monitor = monitors[monitor_id];
|
|
select.append('<option value="' + monitor.Id + '">' + monitor.Name + ' : All Zones</option>');
|
|
for ( zone_id in zones ) {
|
|
const zone = zones[zone_id];
|
|
if ( monitor.Id == zone.MonitorId ) {
|
|
select.append('<option value="' + monitor.Id+':'+zone.Id + '">' + monitor.Name + ' : ' +zone.Name + '</option>');
|
|
}
|
|
} // end foreach zone
|
|
} // end foreach monitor
|
|
select.val(token.value);
|
|
select.on('change', update_expr);
|
|
token.html = select;
|
|
} else if (token.type == '(' || token.type == ')') {
|
|
const select = $j('<select></select>');
|
|
select.append('<option value="0"></option>');
|
|
for (var i = 1; i <= brackets; i++) { // build bracket options
|
|
select.append('<option value="' + token.type.repeat(i) + '">' + token.type.repeat(i) + '</option>');
|
|
}
|
|
select.val(token.value);
|
|
select.on('change', update_expr);
|
|
token.html = select;
|
|
} else if (token.type == '()') {
|
|
const select = $j('<select></select>');
|
|
select.append('<option value=""></option>');
|
|
for (let i = 1; i <= brackets; i++) {
|
|
select.append('<option value="' + String('(').repeat(i) + '">' + String('(').repeat(i) + '</option>');
|
|
}
|
|
for (let i = 1; i <= brackets; i++) { // build bracket options
|
|
select.append('<option value="' + String(")").repeat(i) + '">' + String(")").repeat(i) + '</option>');
|
|
}
|
|
select.val(token.value);
|
|
select.on('change', update_expr);
|
|
token.html = select;
|
|
} else {
|
|
const select = $j('<select></select>');
|
|
select.append('<option value="|">or</option>');
|
|
select.append('<option value="&">and</option>');
|
|
select.val(token.type);
|
|
select.on('change', update_expr);
|
|
token.html = select;
|
|
}
|
|
container.append(token.html);
|
|
} // end foreach token
|
|
container.append('<br/>');
|
|
const select = $j('<select id="monitorLinks"></select>');
|
|
select.append('<option value="">Add MonitorLink</option>');
|
|
for (monitor_id in monitors) {
|
|
const monitor = monitors[monitor_id];
|
|
if (!array_search(monitor.Id, used_monitorlinks)) {
|
|
select.append('<option value="' + monitor.Id + '">' + monitor.Name + ' : All Zones</option>');
|
|
}
|
|
for ( zone_id in zones ) {
|
|
const zone = zones[zone_id];
|
|
if ( monitor.Id == zone.MonitorId ) {
|
|
if (!array_search(monitor.Id+':'+zone.Id, used_monitorlinks)) {
|
|
select.append('<option value="' + monitor.Id+':'+zone.Id + '">' + monitor.Name + ' : ' +zone.Name + '</option>');
|
|
}
|
|
}
|
|
} // end foreach zone
|
|
} // end foreach monitor
|
|
select.on('change', add_to_expr);
|
|
container.append(select);
|
|
} // end expr_to_ui
|
|
|
|
function array_search(needle, haystack) {
|
|
for (index in haystack) {
|
|
if (haystack[index] == needle) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function add_to_expr() {
|
|
$j('[name="newMonitor[LinkedMonitors]"]').val($j('[name="newMonitor[LinkedMonitors]"]').val() + '|' + $j('#monitorLinks').val());
|
|
expr_to_ui($j('[name="newMonitor[LinkedMonitors]"]').val(), $j('#LinkedMonitorsUI'));
|
|
}
|
|
|
|
function update_expr(ev) {
|
|
ui_to_expr($j('#LinkedMonitorsUI'), $j('[name="newMonitor[LinkedMonitors]"]'));
|
|
}
|
|
|
|
function ui_to_expr(container, expr_input) {
|
|
var expr = '';
|
|
var children = container.children();
|
|
for (let i = 0; i < children.length; i++) {
|
|
expr += $j(children[i]).val();
|
|
}
|
|
console.log(expr);
|
|
expr_input.value = expr;
|
|
}
|
|
|
|
function parse_expression(tokens) {
|
|
if (tokens.length == 1) {
|
|
return {token: tokens[0]};
|
|
}
|
|
|
|
let left = parse_and(tokens);
|
|
if (token_index < tokens.length && tokens[token_index] != '|' && tokens[token_index] != ',') {
|
|
return left;
|
|
}
|
|
|
|
while (token_index < tokens.length && ( tokens[token_index] == '|' || tokens[token_index] == ',')) {
|
|
var logical_or = {type: '|'};
|
|
token_index++;
|
|
|
|
var right = parse_and(tokens);
|
|
if (right == null) {
|
|
return null;
|
|
}
|
|
|
|
logical_or.left = left;
|
|
logical_or.right = right;
|
|
left = logical_or;
|
|
}
|
|
|
|
return left;
|
|
} // end function parse_expression
|
|
|
|
function parse_and(tokens) {
|
|
var left = parse_parentheses(tokens);
|
|
|
|
if (left == null) {
|
|
return null;
|
|
}
|
|
|
|
while ((token_index < tokens.length) && (tokens[token_index] == '&')) {
|
|
++token_index;
|
|
|
|
var logical_and = {type: '&'};
|
|
|
|
right = parse_parentheses(tokens);
|
|
if (right == null) {
|
|
return null;
|
|
}
|
|
|
|
logical_and.left = left;
|
|
logical_and.right = right;
|
|
left = logical_and;
|
|
}
|
|
|
|
return left;
|
|
}
|
|
|
|
|
|
function parse_parentheses(tokens) {
|
|
if (token_index == tokens.length) {
|
|
return null;
|
|
}
|
|
|
|
if (tokens[token_index] == '(') {
|
|
++token_index;
|
|
var expression = parse_expression(tokens);
|
|
|
|
// Because we are parsing a left, there SHOULD be a remaining right. If not, invalid.
|
|
if (token_index == tokens.length) return null;
|
|
|
|
if (tokens[token_index++] == ')') {
|
|
return expression;
|
|
}
|
|
} else if (tokens[token_index].type == MONITORLINK) {
|
|
var link = {token: tokens[token_index]};
|
|
token_index++;
|
|
return link;
|
|
}
|
|
|
|
return null;
|
|
}
|