sweeper/webif/plugin/sweeper/script.js

958 lines
21 KiB
JavaScript

var mroot = '(Media Root)';
function quot(str)
{
if (!str)
return '""';
if (str.indexOf(" ") == -1 && str.indexOf('"') == -1)
return str;
return '{' + str + '}';
}
function fixupdown()
{
$('#ruleset a.uprule').enable().filter(':first').disable();
$('#ruleset a.downrule').enable().filter(':last').disable();
$('#ruleset a.uprule img').attr('src', '/img/nav/up.png')
.first().attr('src', '/img/nav/up-grey.png');
$('#ruleset a.downrule img').attr('src', '/img/nav/down.png')
.last().attr('src', '/img/nav/down-grey.png');
}
function changed(c)
{
if (typeof(changed.last) == 'number' && c == changed.last)
return;
if (c)
{
$('#b_save,#b_revert').button('enable');
$('#pendingnote').show();
}
else
{
$('#b_save,#b_revert').button('disable');
$('#pendingnote').hide();
}
changed.last = c;
}
var setters = {
int: function(cmd, a) {
var b = a.split(" ");
if (b.length == 1)
{
op = '=';
val = a;
}
else
{
op = b[0];
val = b[1];
}
return op + ' ' + val;
},
substr: function(cmd, a) {
return a;
},
'string': function(cmd, a) {
return a;
},
select: function(cmd, a) {
return schema.criterion[cmd].select[a];
}
};
var getters = {
int: function(cmd, a) {
var b = a.split(" ");
if (b.length != 2)
return a;
if (b[0] == '=')
return b[1];
return a;
},
substr: function(cmd, a) {
return a;
},
'string': function(cmd, a) {
return a;
},
select: function(cmd, a) {
var c = schema.criterion[cmd].select;
for (key in c)
if (c[key] == a)
return key;
return 'UNKNOWNKEY (' + a + ')';
}
};
function clauseconf(clause)
{
var s = '';
clause.find('tbody:first > tr.clause,tbody:first > tr.compositeclause')
.each(function() {
if ($(this).hasClass('compositeclause'))
{
cc = clauseconf($(this).find('table:first'));
cmd = $(this).attr('cmd');
if (cc && cc.length)
s += cmd + ' {' + cc + '} ';
return;
}
cmd = $(this).find('th.cmd').attr('cmd');
negate = $(this).find('th.cmd').attr('negate');
c = schema.criterion[cmd];
val = $.trim($(this).find('td.val').html());
if (getters[c.type])
val = getters[c.type](cmd, val);
if (negate == '1')
s += '!';
s += cmd + ' ' + quot(val) + ' ';
});
return s;
}
function ruleconf(rule)
{
var s = '';
if (rule.hasClass('ruledisabled'))
s += '## ';
if (rule.attr('type') == 'folder')
s += 'folder ';
s += clauseconf(rule);
act = rule.find('tr.action th.cmd').attr('cmd');
arg = $.trim(rule.find('tr.action td.val').text());
if (schema.action[act].argtype == 'folder' && arg == mroot)
arg = '';
if (act != 'continue')
{
if (schema.action[act].argtype == 'none')
s += 'action ' + act;
else
s += 'action {' + act + ' ' + quot(arg) + '}';
}
return s;
}
function conffile()
{
var s = '';
$('div.rule').each(function(i) {
comment = $(this).find('span.comment').text();
if (comment != 'Unnamed rule')
s += '# ' + comment + '\n';
s += ruleconf($(this)) + '\n';
});
return s;
}
function rulerefresh(rule)
{
if (!showraw)
rule.find('.raw').hide();
rule.find('.raw').html(ruleconf(rule));
rule.find('tr.clause th.title,tr.compositeclause th.title')
.text('And:').first().text('If:');
rule.find('tr.compositeclause').each(function(e) {
cmd = $(this).attr('cmd');
label = schema.criterion[cmd].label;
$(this).find('table th.title').text(label + ':')
.first().text('If:');
});
if (rule.find('tr.clause,tr.compositeclause').length < 1)
{
rule.find('tr.action th.title').text('Always:');
rule.find('tr.otherwise,div.criteria,div.arrow').hide();
}
else
{
rule.find('tr.action th.title').text('Then:');
rule.find('tr.otherwise,div.criteria,div.arrow').show();
}
}
function critdesc(cmd, negate)
{
var c = schema.criterion[cmd];
if (!c)
return 'Unknown (' + cmd + ')';
if (negate == '1' && c.idesc)
return c.idesc;
else if (negate == '1')
return c.desc + ' is not';
else
return c.desc;
}
function composite_criterion(c, data)
{
s = '<tr class="compositeclause" cmd=' + data.cmd + '>' +
'<th class=title>And:</th>' +
'<td colspan=3>' +
'<table class=compositeclause>' +
'<tr><td colspan=4>' +
'<a href=# class=addsubcriterion>' +
'<img src=img/plus.png height=15 ' +
'title="Add condition to sub-clause"></a>' +
'&nbsp;' +
'<a class=delsubclause href=# ' +
'title="Delete sub-clause and the conditions within it">' +
'<img src=/img/context/delete.png></a>' +
'&nbsp;' +
c.desc +
'</td></tr>';
$.each(data.criteria, function(key, val) {
s += criterion(val, 'comp');
});
s += '</table></td></tr>';
return s;
}
function criterion(data, classes)
{
var c = schema.criterion[data.cmd];
var s;
if (!c)
{
alert('Unknown Criterion (' + data.cmd + ')');
return;
}
if (c.type == 'composite')
return composite_criterion(c, data);
s = '<tr class="clause';
if (classes) s += ' ' + classes;
s += '"><th class=title>And:</th>' +
'<th class=cmd negate=' + data.negate + ' cmd=' + data.cmd + '>' +
critdesc(data.cmd, data.negate) + '</th><td class=val>';
if (setters[c.type])
s += setters[c.type](data.cmd, data.arg);
else
s += 'UNKNOWN (' + data.arg + ')';
s += '</td><td>' +
'<a class=editclause href=# title="Edit condition">' +
'<img src=/img/context/edit.png></a>' +
'&nbsp;' +
'<a class=delclause href=# title="Delete condition">' +
'<img src=/img/context/delete.png></a>' +
'</td></tr>';
return s;
}
function action(data)
{
var c = schema.action[data.cmd];
var s;
if (!c)
{
alert('Unknown action - ' + data.cmd);
return '';
}
if (c.argtype == 'folder' && !data.arg)
data.arg = mroot;
s = '<tr class=action>' +
'<th class=title>Then:</th>' +
'<th class=cmd cmd=' + data.cmd + '>' + c.desc + '</th>' +
'<td class=val>' + data.arg + '</td>' +
'<td><a class=editaction href=#>' +
'<img src=/img/context/edit.png></a></td>' +
'</tr>';
if (data.cmd != 'continue')
{
if (c.continues)
s += '<tr><th class=title>And:</th><td class=title>' +
'Continue to next rule.</td></tr>';
else if (data.cmd != 'stop')
s += '<tr><th class=title>And:</th><td class=title>' +
'Stop processing rules.</td></tr>';
s += '<tr class=blank><td>&nbsp;</th></tr>' +
'<tr class=otherwise><th class=title>Otherwise:</th>' +
'<td class=title>Continue to next rule.</td></tr>';
}
return s;
}
var last_ruleid = 0;
function addrule(id, data)
{
if ($('#ruleset').attr('empty'))
$('#ruleset').empty().removeAttr('empty');
last_ruleid = id;
var rule = $('#rule_template')
.clone()
.attr('id', 'rule_' + id)
.addClass('rule')
.removeClass('hidden');
rule.find('span.comment').html(data.name);
if (data.type == 'folder')
rule.find('.commentleft > span.folder').show();
else
rule.find('.commentleft > span.file').show();
rule.attr('type', data.type);
var $c = rule.find('table.criteria');
$.each(data.criteria, function(key, val) {
if (val.cmd == 'lock' && (
data.action.cmd == 'lock' || data.action.cmd == 'unlock'))
{
alert('Removed legacy lock/unlock - ' +
'check rules before saving.');
return;
}
$c.find('tbody:first').append(criterion(val));
});
$c.find('th.title:first').text('If:');
rule.find('table.action tbody').append(action(data.action));
if (data.enabled == 0)
{
rule.addClass('ruledisabled');
rule.find('span.disabledtext').removeClass('hidden');
}
$('#ruleset').append(rule);
rulerefresh(rule);
return rule;
}
function edit_text(obj, title, text, callback)
{
$('#edit_text_field').val(text);
$('#edit_text').dialog({
title: title,
height: 'auto', width: 'auto',
draggable: false, resizable: false,
autoOpen: true,
position: {
my: 'bottom left',
at: 'top right',
of: obj
},
buttons: {
"Save": function() {
var val = $.trim($('#edit_text_field').val());
if (!val)
alert("Bad value");
else
{
callback(obj, val);
$(this).dialog('close');
}
},
"Close": function() {
$(this).dialog('close');
}
}
});
if (text == 'Enter text here...')
$('#edit_text_field').focus().select();
}
function edit_int(obj, title, op, val, callback)
{
$('#edit_int_field').val(val);
$('#edit_int_op').val(op);
$('#edit_int').dialog({
title: title,
height: 'auto', width: 'auto',
draggable: false, resizable: false,
autoOpen: true,
position: {
my: 'bottom left',
at: 'top right',
of: obj
},
buttons: {
"Save": function() {
var op = $.trim($('#edit_int_op').val());
var val = $.trim($('#edit_int_field').val());
if (!op || !val)
alert("Bad value");
else
{
callback(obj, op, val);
$(this).dialog('close');
}
},
"Close": function() {
$(this).dialog('close');
}
}
});
}
function edit_select(obj, title, options, val, callback)
{
$('#edit_select_field').empty();
$.each(options, function(k, v) {
$('#edit_select_field').append(
$('<option></option>').attr('value', k).html(v)
);
});
if (val && val != '')
$('#edit_select_field').val(val);
$('#edit_select').dialog({
title: title,
height: 'auto', width: 'auto',
draggable: false, resizable: false,
autoOpen: true,
position: {
my: 'bottom left',
at: 'top right',
of: obj
},
buttons: {
"Save": function() {
callback(obj, $('#edit_select_field').val());
$(this).dialog('close');
},
"Close": function() {
$(this).dialog('close');
}
}
});
}
function edit_clause(obj)
{
var cmd = $(obj).find('th.cmd').attr('cmd');
var target = $(obj).find('td.val');
var tval = $.trim($(target).text());
var val = $.trim($(target).html());
var title = $(obj).find('th.cmd').html();
if (!schema.criterion[cmd])
{
alert('Unhandled command (' + cmd + ')');
return;
}
switch (schema.criterion[cmd].type)
{
case 'string':
case 'substr':
edit_text(target, title, tval, function(obj, text) {
$(obj).html(text);
rulerefresh($(obj).closest('div.rule'));
changed(1);
});
break;
case 'int':
b = tval.split(" ");
if (b.length != 2)
{
alert('Bad int value');
break;
}
edit_int(target, title, b[0], b[1], function(obj, op, val) {
$(obj).html(op + ' ' + val);
rulerefresh($(obj).closest('div.rule'));
changed(1);
});
break;
case 'select':
edit_select(target, title, schema.criterion[cmd].select,
getters.select(cmd, val), function(obj, val) {
$(obj).html(setters.select(cmd, val));
rulerefresh($(obj).closest('div.rule'));
changed(1);
});
break;
default:
alert('Unhandled clause type (' + cmd + ')');
}
}
function loadrules(dir)
{
$('#ruleset')
.html('<img src=/img/loading.gif> Loading rules...');
$.getJSON('rules_json.jim', 'dir=' + dir, function(data) {
$('#ruleset').empty();
if (!data.length)
{
$('#ruleset').attr('empty', true)
.html($('#empty_rulebase').html());
return;
}
$.each(data, function(key, val) {
addrule(key, val);
});
fixupdown();
if (!showraw)
$('#ruleset div.raw').hide();
});
}
$(function() {
$('#b_add').button({icons: {primary: "ui-icon-plus"}})
.on('click', function(e) {
e.preventDefault();
$('#newrule').dialog({
height: 'auto', width: 'auto',
draggable: false, resizable: false,
autoOpen: true,
position: {
my: 'bottom left',
at: 'top right',
of: $(this)
},
buttons: {
"Create Rule": function() {
rule = addrule(last_ruleid + 1, {
raw: '',
name: $('#newrule_comment').val(),
type: $('input[name=newrule_type]' +
':checked').val(),
criteria: [],
action: {
cmd: 'continue',
arg: ''
}
});
changed(1);
$(this).dialog('close');
},
"Close": function() {
$(this).dialog('close');
}
}
});
$('#newrule_type_file').prop('checked', true);
$('#newrule_comment').focus().val('').val('Unnamed rule').select();
});
$('#b_save').button({icons: {primary: "ui-icon-disk"}});
$('#buttons')
.on('click', '#b_save', function(e) {
e.preventDefault();
$(this).dojConfirmAction({
question: 'Save changes?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
$.post('save.jim', {
dir: $('span.dir').text(),
data: conffile()
}, function(data) {
$('#output').fadeIn('fast').html(data)
.delay(5000).fadeOut('slow');
changed(0);
});
});
});
$('#b_revert').button({icons: {primary: "ui-icon-arrowrefresh-1-w"}});
$('#buttons')
.on('click', '#b_revert', function(e) {
e.preventDefault();
$(this).dojConfirmAction({
question: 'Discard changes?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
loadrules($('span.dir').text());
changed(0);
});
});
$('#b_show').button({icons: {primary: "ui-icon-clipboard"}})
.on('click', function(e) {
e.preventDefault();
$('#showconf')
.empty()
.html('<pre>' + conffile() + '</pre>')
.dialog({
height: 'auto', width: 'auto',
autoOpen: true,
modal: true,
buttons: {
"Close": function() {
$(this).dialog('close');
}
}
});
});
$('#b_test').button({icons: {primary: "ui-icon-check"}})
.on('click', function(e) {
e.preventDefault();
$('#testresults_inner').empty()
.html('<img src=/img/loading.gif> Running rules... Please wait...');
$('#testresults').dialog({
height: '600', width: '800',
draggable: true, resizable: true,
autoOpen: true,
buttons: {
"Close": function() {
$(this).dialog('close');
}
}
});
$.post('test.jim', {
dir: $('span.dir').text(),
data: conffile()
}, function(data) {
$('#testresults_inner').text(data);
});
});
$('#b_raw').button({icons: {primary: "ui-icon-gear"}})
.on('click', function(e) {
$('#ruleset div.raw').toggle('slow');
showraw = !showraw;
$.get('save.jim?act=raw&val=' + (showraw ? 1 : 0));
});
$('#b_macro').button({disabled: true, icons: {primary: "ui-icon-plus"}});
$('#macros').on('click', '#b_macro', function(e) {
e.preventDefault();
var set = $('#macroselect').val();
if (!set) return;
var desc = macros[set].desc;
$(this).dojConfirmAction({
question: 'Add ' + desc + '?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
$('.jcaquestion').remove();
$.each(macros[set].rules, function(k, v) {
addrule(last_ruleid + 1, v).hide().addClass('hl');
});
$('.hl').slideDown('slow', function() {
$(this).removeClass('hl');
});
fixupdown();
changed(1);
$('#macroselect').val(0);
});
});
changed(0);
function addcriterion(rule, target)
{
var type = rule.attr('type');
if (type == 'folder')
options = select_folder_criteria;
else
options = select_file_criteria;
// Populate select box and reset form.
$('#newcondition_cmd').empty();
$.each(options, function(k, v) {
$('#newcondition_cmd').append(
$('<option></option>').attr('value', k).html(v)
);
});
$('#newcondition_negate').prop('checked', false);
$('#newcondition_cmd').trigger('change');
$('#newcondition').dialog({
height: 'auto', width: 'auto',
draggable: false, resizable: false,
autoOpen: true,
position: {
my: 'bottom left',
at: 'top right',
of: target
},
buttons: {
"Add Condition": function() {
$(this).dialog('close');
var val = $('#newcondition_cmd').val();
var negate = $('#newcondition_negate')
.prop('checked') ? '1' : '0';
var obj = {
cmd: val,
negate: negate,
arg: schema.criterion[val].def,
criteria: []
};
target.find('tbody:first')
.append(criterion(obj));
changed(1);
rulerefresh(rule);
},
"Cancel": function() {
$(this).dialog('close');
}
}
});
}
$('#ruleset')
.on('click', 'a.editclause', function(e) {
e.preventDefault();
edit_clause($(this).closest('tr.clause'));
})
.on('click', 'a.addcriterion', function(e) {
e.preventDefault();
var rule = $(this).closest('div.rule');
addcriterion(rule, rule.find('table.criteria'));
})
.on('click', 'a.editcomment', function(e) {
e.preventDefault();
edit_text($(this), 'Edit rule name',
$(this).siblings('span.comment').text(),
function(obj, text) {
$(obj).siblings('span.comment').text(text);
changed(1);
});
})
.on('click', 'a.uprule', function(e) {
e.preventDefault();
if ($(this).hasClass('ui-state-disabled'))
return false;
var rule = $(this).closest('div.rule');
var target = $(rule).prev('div.rule');
if (!target || !target.length)
return false;
$(rule).addClass('hl').slideUp('slow', function() {
$(this).insertBefore($(target))
.slideDown('slow', function() {
$(this).removeClass('hl');
fixupdown();
changed(1);
});
});
})
.on('click', 'a.downrule', function(e) {
e.preventDefault();
if ($(this).hasClass('ui-state-disabled'))
return false;
var rule = $(this).closest('div.rule');
var target = $(rule).next('div.rule');
if (!target || !target.length)
return false;
$(rule).addClass('hl').slideUp('slow', function() {
$(this).insertAfter($(target))
.slideDown('slow', function() {
$(this).removeClass('hl');
fixupdown();
changed(1);
});
});
})
.on('click', 'a.cprule', function(e) {
e.preventDefault();
$(this).dojConfirmAction({
question: 'Duplicate rule?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
$('.jcaquestion').remove();
var rule = $(el).closest('div.rule');
rule.clone()
.attr('id', 'rule_' + ++last_ruleid)
.addClass('hl').hide()
.insertAfter(rule)
.slideDown('slow', function() {
$(this).removeClass('hl');
changed(1);
})
.find('span.comment').append(' (copy)');
});
})
.on('click', 'a.enadisrule', function(e) {
e.preventDefault();
var rule = $(this).closest('div.rule')
.toggleClass('ruledisabled');
rule.find('span.disabledtext').toggleClass('hidden',
!rule.hasClass('ruledisabled'));
rulerefresh(rule);
changed(1);
})
.on('click', 'a.editaction', function(e) {
e.preventDefault();
var rule = $(this).closest('div.rule');
var type = rule.attr('type');
if (type == 'folder')
options = select_folder_actions;
else
options = select_file_actions;
$('#edit_action_act').empty();
$.each(options, function(k, v) {
$('#edit_action_act').append(
$('<option></option>').attr('value', k).text(v)
);
});
var cmd = rule.find('tr.action th.cmd').attr('cmd');
var arg = rule.find('tr.action td.val').html();
if (schema.action[cmd].argtype == 'none')
{
arg = '';
$('#edit_action_arg').disable();
}
else
$('#edit_action_arg').enable();
$('.edit_action_help').hide();
$('#edit_action_help_' + schema.action[cmd].argtype).show();
if (schema.action[cmd].argtype == 'folder' && arg == mroot)
arg = '';
$('#edit_action_act').val(cmd);
$('#edit_action_arg').val(arg);
$('#edit_action').dialog({
height: 'auto', width: 'auto',
draggable: false, resizable: false,
autoOpen: true,
position: {
my: 'bottom left',
at: 'top right',
of: rule
},
buttons: {
"Save": function() {
cmd = $('#edit_action_act').val();
arg = $('#edit_action_arg').val();
rule.find('table.action tbody').empty()
.append(action({cmd: cmd, arg: arg}));
$(this).dialog('close');
rulerefresh(rule);
changed(1);
},
"Close": function() {
$(this).dialog('close');
}
}
});
})
.on('click', 'a.delclause', function(e) {
e.preventDefault();
$(this).dojConfirmAction({
question: 'Delete condition?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
var rule = $(el).closest('div.rule');
$(el).closest('tr.clause').fadeOut('slow', function() {
$(this).remove();
rulerefresh(rule);
changed(1);
});
});
})
.on('click', 'a.delrule', function(e) {
e.preventDefault();
$(this).dojConfirmAction({
question: 'Delete entire rule?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
$(el).closest('div.rule')
.addClass('hl')
.slideUp('slow', function() {
$(this).remove();
changed(1);
fixupdown();
if ($('div.rule').length < 1)
$('#ruleset').attr('empty', true)
.html($('#empty_rulebase').html());
});
});
})
.on('click', 'a.delsubclause', function(e) {
e.preventDefault();
$(this).dojConfirmAction({
question: 'Delete sub-condition?',
yesAnswer: 'Yes',
cancelAnswer: 'No'
}, function(el) {
var rule = $(el).closest('div.rule');
$(el).closest('tr.compositeclause')
.fadeOut('slow', function() {
$(this).remove();
rulerefresh(rule);
changed(1);
});
});
})
.on('click', 'a.addsubcriterion', function(e) {
e.preventDefault();
var rule = $(this).closest('div.rule');
var clause = $(this).closest('table.compositeclause');
addcriterion(rule, clause);
})
.on('dblclick', 'div.raw', function(e) {
e.preventDefault();
$(this).toggleClass("rawvis");
});
$('#edit_action_act').on('change', function(e) {
var cmd = $(this).val();
if (schema.action[cmd].argtype == 'none')
$('#edit_action_arg').disable();
else
$('#edit_action_arg').enable();
$('.edit_action_help').hide();
$('#edit_action_help_' + schema.action[cmd].argtype).show();
});
$('#newcondition_cmd').on('change', function(e) {
var cmd = $(this).val();
if (schema.criterion[cmd].negate)
$('#newcondition_negate').enable();
else
$('#newcondition_negate')
.prop('checked', false)
.disable();
});
// Set up macros
$.each(macros, function(key, val) {
$('#macroselect').append(
$('<option></option>').attr('value', key).text(val.desc)
);
});
$('#macroselect').on('change', function(el) {
if ($(this).val() == "0")
$('#b_macro').button('disable');
else
$('#b_macro').button('enable');
});
loadrules($('span.dir').text());
$(document).tooltip();
});