Initial commit
Add rest-dbus script and resources for human UI.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
diff --git a/resources/dbus.js b/resources/dbus.js
new file mode 100644
index 0000000..04d61ba
--- /dev/null
+++ b/resources/dbus.js
@@ -0,0 +1,349 @@
+
+var g_bus;
+var initial_state = {
+ 'bus': 'session',
+};
+
+function populate_tree_services(node, cb, bus)
+{
+ jQuery.getJSON('/bus/' + bus.name, function(data) {
+ var children = [];
+ for (var i in data.objects) {
+ var service = data.objects[i];
+ if (service.name[0] == ':')
+ continue;
+ children.push({
+ 'text': service.name,
+ 'children': true,
+ 'type': 'service',
+ 'bus': bus,
+ 'service': service,
+ });
+ }
+ cb(children);
+ });
+}
+
+function populate_tree_objects(node, cb)
+{
+ var ctx = node.original;
+ jQuery.getJSON('/bus/' + ctx.bus.name + '/' + ctx.service.name,
+ function(data) {
+ var children = [];
+ for (var i in data.objects) {
+ var obj = data.objects[i];
+ children.push({
+ 'text': obj.path,
+ 'children': true,
+ 'type': 'object',
+ 'bus': ctx.bus,
+ 'service': ctx.service,
+ 'object': obj,
+ });
+ }
+ cb(children);
+ });
+}
+
+function populate_tree_interfaces(node, cb)
+{
+ var ctx = node.original;
+ jQuery.getJSON('/bus/' + ctx.bus.name + '/' + ctx.service.name +
+ ctx.object.path,
+ function(data) {
+ console.log(data);
+ var children = [];
+ for (var i in data.interfaces) {
+ var iface = data.interfaces[i];
+ children.push({
+ 'text': iface.name,
+ 'children': true,
+ 'type': 'interface',
+ 'bus': ctx.bus,
+ 'service': ctx.service,
+ 'object': ctx.object,
+ 'interface': iface,
+ });
+ }
+ cb(children);
+ });
+}
+
+function format_one_type(type)
+{
+ var ret = Object();
+ var basic_types = {
+ 'v': 'variant',
+ 'i': 'int32',
+ 'x': 'int64',
+ 'u': 'uint32',
+ 't': 'uint64',
+ 'g': 'type',
+ 'h': 'file',
+ 'd': 'double',
+ 's': 'string',
+ 'o': 'objpath',
+ 'y': 'byte',
+ 'b': 'boolean',
+ }
+
+ var c = type[0];
+ if (basic_types[c]) {
+ ret.type = basic_types[c];
+ ret.left = type.substring(1);
+ return ret;
+ }
+
+ if (c == '(') {
+ var i;
+ var n;
+ for (i = 1, n = 1; i < type.length; i++) {
+ var c = type[i];
+ if (c == '(')
+ n++;
+ if (c == ')')
+ if (--n == 0)
+ break;
+
+ }
+ if (i == type.length)
+ return ret;
+ var content = type.substring(1, i);
+ ret.type = 'struct {' + format_types(content).join(', ') + '}';
+ ret.left = type.substring(i+1);
+ return ret;
+ }
+
+ if (c == 'a') {
+ var content = type.substring(1);
+ if (content[0] == '{') {
+ var i;
+ var n;
+ for (i = 1, n = 1; i < content.length; i++) {
+ var c = content[i];
+ if (c == '{')
+ n++;
+ if (c == '}')
+ if (--n == 0)
+ break;
+
+ }
+ content = content.substring(1, i);
+ var types = format_types(content);
+ console.log("{", content, "}", types);
+ ret.type = 'dict {' + types[0] + ' → ' + types[1] + '}';
+ ret.left = type.substring(i+1);
+ } else {
+ var t = format_one_type(content);
+ ret.type = '[' + t.type + ']';
+ ret.left = t.left;
+ }
+ return ret;
+ }
+
+ ret.type = c;
+ ret.left = type.substring(1);
+ return ret;
+
+}
+
+function format_types(type)
+{
+ var t;
+ var ts = [];
+ for (;;) {
+ t = format_one_type(type);
+ ts.push(t.type);
+
+ if (!t.left || !t.left.length)
+ break;
+ type = t.left;
+ }
+ return ts;
+}
+
+function format_type(type)
+{
+ return format_types(type)[0];
+}
+
+function format_method_arg(arg)
+{
+ return '<span class="method-arg">' +
+ '<span class="method-arg-type">' + format_type(arg.type) +
+ '</span> ' +
+ '<span class="method-arg-name">' + arg.name + '</span></span>';
+}
+
+function format_method(method)
+{
+ var in_args = [];
+ var out_args = [];
+ for (var i in method.args) {
+ var arg = method.args[i];
+ var str = format_method_arg(arg);
+ if (arg.direction == 'in')
+ in_args.push(str);
+ else
+ out_args.push(str);
+ }
+ return '<span class="method">' +
+ '<span class="method-name">' + method.name + "</span>" +
+ '<span class="method-args">(' + in_args.join(', ') + ')</span>' +
+ ' → ' +
+ '<span class="method-args">' + out_args.join(', ') + '</span>' +
+ '</span>'
+}
+
+function format_property(name, value)
+{
+ return '<span class="property">' +
+ '<span class="property-name">' + name + '</span>' +
+ ' = ' +
+ '<span class="property-value">' + value + '</span>' +
+ '</span>'
+}
+
+function populate_tree_interface_items(node, cb)
+{
+ var ctx = node.original;
+ jQuery.getJSON('/bus/' + ctx.bus.name + '/' + ctx.service.name +
+ ctx.object.path + '/' + ctx.interface.name,
+ function(data) {
+ console.log(data);
+ var children = [];
+ for (var i in data.methods) {
+ var method = data.methods[i];
+ children.push({
+ 'text': format_method(method),
+ 'children': [],
+ 'type': 'method',
+ 'method': method,
+ 'bus': ctx.bus,
+ 'service': ctx.service,
+ 'object': ctx.object,
+ 'interface': ctx.interface,
+ });
+ }
+ for (var i in data.properties) {
+ var property = data.properties[i];
+ children.push({
+ 'text': format_property(i, property),
+ 'children': [],
+ });
+ }
+ cb(children);
+ });
+}
+function populate_tree(node, cb)
+{
+ if (!node.original) {
+ populate_tree_services(node, cb, g_bus);
+ return;
+ }
+
+ var type = node.original.type;
+ if (type == 'service') {
+ populate_tree_objects(node, cb);
+ } else if (type == 'object') {
+ populate_tree_interfaces(node, cb);
+ } else if (type == 'interface') {
+ populate_tree_interface_items(node, cb);
+ }
+}
+
+function select_node(evt, data)
+{
+ var ctx = data.node.original;
+ if (!ctx || ctx.type != 'method')
+ return;
+
+ var method = ctx.method;
+ jQuery("#method-call-ui").remove();
+
+ var container = data.instance.get_node(data.node, true);
+
+ var in_args = []
+ for (var i in method.args) {
+ var arg = method.args[i];
+ if (arg.direction != 'in')
+ continue;
+ in_args.push({
+ 'type': format_method_arg(arg),
+ 'idx': i,
+ });
+ }
+ method.nargs = in_args.length;
+
+ var template = jQuery.templates('#method-call-ui-template');
+ container.append(template.render({
+ 'method': method,
+ 'in_args': in_args,
+ }));
+ jQuery("form", container).submit(function(e) {
+ var form = jQuery(e.target);
+ var n = jQuery("input[name=argN]", form).val();
+ var data = [];
+ for (var i = 0; i < n; i++) {
+ var val = jQuery("input[name=arg" + i + ']', form).val();
+ try {
+ data.push(JSON.parse(val));
+ } catch (e) {
+ data.push(null);
+ }
+ }
+ jQuery.ajax(form.attr("action"),
+ {
+ 'method': 'POST',
+ 'contentType': 'text/json',
+ 'data': JSON.stringify(data),
+ 'dataType': 'text',
+ 'success': function(data, status) {
+ jQuery('#method-call-ui-result').text(data);
+ },
+ });
+ return false;
+ });
+}
+
+function set_bus(b)
+{
+ g_bus = b;
+ $('#container').jstree({
+ 'core': {
+ 'data': populate_tree,
+ 'multiple': false,
+ 'themes': {
+ 'icons': false,
+ },
+ }
+ }).on('select_node.jstree', select_node);
+}
+
+function init()
+{
+ var select = jQuery('select#bus');
+ select.empty();
+ select.append(jQuery('<option>').text('---'));
+ jQuery.getJSON('/bus/', function(data) {
+ for (var i in data.busses) {
+ var bus = data.busses[i];
+ var elem = jQuery('<option>');
+ elem.text(bus.name);
+ elem.data('bus', bus);
+ select.append(elem);
+ if (initial_state.bus == bus.name) {
+ elem.attr('selected', 'true');
+ set_bus(bus);
+ }
+ }
+ select.bind('change', function(e) {
+ var elem = jQuery("option:selected", e.target);
+ var b = elem.data('bus');
+ if (b)
+ set_bus(b);
+ });
+ })
+}
+
+jQuery(document).ready(init);