Resolved FELIX-2121
Initial import of ProSyst donation of UPnP plugin for the web console
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@925979 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole-plugins/upnp/src/main/resources/res/upnp.js b/webconsole-plugins/upnp/src/main/resources/res/upnp.js
new file mode 100644
index 0000000..8899f7d
--- /dev/null
+++ b/webconsole-plugins/upnp/src/main/resources/res/upnp.js
@@ -0,0 +1,326 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// device selection
+var deviceData = false;
+var deviceTableBody = false;
+
+// service selection
+var serviceData = false;
+var serviceDataVars = false;
+var serviceDataInfoID = false;
+var serviceDataInfoType = false;
+
+// actions
+var actionsContainer = false;
+var actionsSelect = false;
+var actionsInvoke = false;
+var actionsTable = false;
+var actionsTableBody = false;
+var actionsTableRow = false;
+
+// tree browser, buttons, error dialog
+var browser = false;
+var searching = false;
+var reloadVars = false;
+
+/* BEGIN HELPERS */
+
+/* helper functions for tree node */
+function _id(dn) { return dn.replace(/[:-\\.\\$]/g,'_') }
+/* creates a node in the device tree */
+function treeNode(id, name, icon, span) {
+ var li = createElement('li', null, { 'id' : _id(id) }, [
+ createElement('span', null, null, [
+ icon ?
+ createElement('img', 'icon', { 'src' : icon }) :
+ createElement('span', 'ui-icon ui-icon-' + span) ,
+ text(name)
+ ])
+ ]);
+ return $(li);
+}
+/* creates a service node in the devices tree, and associates the click action */
+function servNode(udn, urn) {
+ return treeNode(udn+urn, urn, null, 'extlink').click(function() {
+ if (selectServiceTime) {
+ clearTimeout(selectServiceTime);
+ selectServiceTime = false;
+ }
+ $.post(pluginRoot, {
+ 'action': 'serviceDetails',
+ 'udn' : udn,
+ 'urn' : urn
+ }, function(data) {
+ renderService(udn, urn, data)
+ }, 'json');
+ return false;
+ });
+}
+/* a helper function to format device properties values - specially
+ * converts arrays and strings, if the last are links */
+function _val(val) {
+ var ret = '';
+ if ($.isArray(val)) {
+ for (i in val) ret += _val(val[i]) + '<br/>';
+ } else {
+ ret = (typeof val == 'string' && val.indexOf('http://') != -1) ?
+ '<a target="blank" href="' + val + '">' + val + '</a>' : val;
+ }
+ return ret;
+}
+
+
+/* BEGIN UI-ELEMENTS CREATION */
+
+/* add element to the tree, just creates the node */
+function addDevice(device) {
+ var udn = device.props['UPnP.device.UDN'];
+ var name = device.props['UPnP.device.friendlyName'];
+ var icon = null;
+ if (device.icon) icon = pluginRoot + '?icon=' + udn;
+
+ var node = treeNode(udn, name, icon, 'lightbulb').click(function() {
+ renderDevice(device);
+ });
+
+ var ul, hasChildren;
+
+ // services
+ hasChildren = false;
+ ul = $(createElement('ul', 'ui-helper-clearfix'));
+ for(var i in device.services) {
+ hasChildren = true;
+ ul.append( servNode(udn, device.services[i]) );
+ }
+ if (hasChildren) node.append(ul);
+
+ // child devices
+ hasChildren = false;
+ ul = $(createElement('ul'));
+ for(var i in device.children) {
+ hasChildren = true;
+ addDevice(device.children[i]);
+ }
+ if (hasChildren) node.append(ul);
+
+ return node;
+}
+/* fills in the list of state variables */
+function renderVars(data) {
+ serviceDataVars.empty();
+ for(i in data.variables) {
+ var _var = data.variables[i];
+ var _tr = tr(null, null, [
+ td(null, null, [ text(_var.name) ]),
+ td(null, null, [ text(_var.value) ]),
+ td(null, null, [ text(_var.sendsEvents) ])
+ ]);
+ serviceDataVars.append(_tr);
+ }
+ initStaticWidgets();
+}
+
+/* BEGIN ACTION HANDLERS */
+
+var selectedDevice = false; // the LI element of the selected device, reset on load
+function renderDevice(device) {
+ // generate content
+ var table = '';
+ for(var key in device.props) {
+ table += '<tr><td class="ui-priority-primary">' + key + '</td><td>' + _val(device.props[key]) + '</td></tr>';
+ }
+
+ // update the UI
+ deviceTableBody.html(table);
+ reloadVars.addClass('ui-state-disabled');
+ deviceData.removeClass('ui-helper-hidden');
+ serviceData.addClass('ui-helper-hidden')
+
+ // reset selected items
+ if (selectedDevice) selectedDevice.css('font-weight', 'normal');
+ selectedDevice = $('#' + _id(device.props['UPnP.device.UDN']) + ' span').css('font-weight', 'bold');
+}
+
+var selectedUdn = false;
+var selectedUrn = false;
+var selectServiceTime = false;
+function renderService(udn, urn, data) {
+ // save selection
+ selectedUdn = udn;
+ selectedUrn = urn;
+
+ // append service info
+ serviceDataInfoID.text(data.id);
+ serviceDataInfoType.text(data.type);
+
+ // append state variables
+ renderVars(data);
+
+ // append actions
+ if (data.actions) {
+ var html = '';
+ var x = data.actions;
+ for (var a in x) html += '<option value="' + a + '">' + x[a].name + '</option>';
+ actionsSelect.html(html).unbind('change').change(function() {
+ var index = $(this).val();
+ actionSelected(udn, urn, x[index]);
+ }).trigger('change');
+ actionsContainer.removeClass('ui-helper-hidden');
+ } else {
+ actionsContainer.addClass('ui-helper-hidden');
+ }
+
+ // update UI
+ deviceData.addClass('ui-helper-hidden');
+ serviceData.removeClass('ui-helper-hidden');
+ reloadVars.removeClass('ui-state-disabled');
+ initStaticWidgets();
+
+ // refresh once - to get updates asynchronously
+ selectServiceTime = setTimeout('reloadVars.click()', 3000);
+}
+
+function actionSelected(udn, urn, action) {
+ // add input arguments
+ if (action.inVars) {
+ actionsTableBody.empty();
+ for (var i in action.inVars) {
+ var _arg = action.inVars[i];
+ var _tr = actionsTableRow.clone().appendTo(actionsTableBody);
+ _tr.find('td:eq(0)').text(_arg.name);
+ _tr.find('td:eq(1)').text(_arg.type);
+ var _el = _tr.find('input').attr('id', 'arg'+i);
+ _arg['element'] = _el;
+ }
+ actionsTable.removeClass('ui-helper-hidden');
+ } else {
+ actionsTable.addClass('ui-helper-hidden');
+ }
+
+ actionsInvoke.unbind('click').click(function() {
+ invokeAction(udn, urn, action);
+ });
+
+ initStaticWidgets(actionsTableBody);
+}
+
+function invokeAction(udn, urn, action) {
+ // prepare arguments
+ var names = new Array();
+ var vals = new Array();
+ for (var i in action.inVars) {
+ var x = action.inVars[i];
+ names.push(x['name']);
+ vals.push(x['element'].val());
+ }
+ // invoke action
+ $.post(pluginRoot, {
+ 'udn' : udn,
+ 'urn' : urn,
+ 'action': 'invokeAction',
+ 'actionID' : action.name,
+ 'names' : names,
+ 'vals' : vals
+ }, function(data) {
+ var html = i18n.no_params_out;
+ if (data.output) {
+ html = '<table class="nicetable"><tr><th>'+i18n.args_name+'</th><th>'+i18n.args_type+'</th><th>' + i18n.args_value + '</th></tr>';
+ for(var i in data.output) {
+ var arg = data.output[i];
+ html += '<tr><td>' + arg['name'] + '</td><td>' + arg['type'] + '</td><td>' + arg['value'] + '</td></tr>';
+ }
+ html += '</table>';
+ }
+ Xalert(html, i18n.dl_title_ok);
+ }, 'json');
+}
+
+function listDevices() {
+ browser.empty().addClass('ui-helper-hidden');
+ searching.removeClass('ui-helper-hidden');
+
+ $.post(pluginRoot, { 'action': 'listDevices' }, function(data) {
+ if (data && data.devices) {
+ $.each(data.devices, function(index) {
+ var html = addDevice(this);
+ browser.treeview( { add: html.appendTo(browser) } );
+ });
+ } else {
+ browser.append('','No devices available', '');
+ }
+
+ // update selected items
+ selectedDevice = false;
+ selectedUdn = false;
+ selectedUrn = false;
+
+ // update IU elements
+ browser.removeClass('ui-helper-hidden');
+ searching.addClass('ui-helper-hidden');
+ deviceData.addClass('ui-helper-hidden');
+ serviceData.addClass('ui-helper-hidden');
+ }, 'json');
+
+ return false;
+}
+
+
+
+$(document).ready( function() {
+ // init elements of style
+ searching = $('#searching');
+ deviceData = $('#deviceData');
+ deviceTableBody = $('#deviceTable tbody');
+
+ // services
+ serviceData = $('#serviceData');
+ serviceDataInfoID = $('#serviceDataInfoID');
+ serviceDataInfoType= $('#serviceDataInfoType');
+ serviceDataVars = $('#serviceDataVars tbody');
+
+ // actions
+ actionsContainer = $('#actionsContainer');
+ actionsSelect = actionsContainer.find('select');
+ actionsInvoke = actionsContainer.find('button');
+ actionsTable = actionsContainer.find('table');
+ actionsTableBody = actionsTable.find('tbody');
+ actionsTableRow = actionsTableBody.find('tr').clone();
+ actionsTableBody.empty();
+
+ // init navigation tree
+ browser = $('#browser').treeview({
+ animated: 'fast',
+ collapsed: true,
+ unique: true
+ });
+
+ // reload button
+ reloadVars = $('#reloadVars').click(function() {
+ if (selectedUdn && selectedUrn) {
+ $.post(pluginRoot, {
+ 'action': 'serviceDetails',
+ 'udn' : selectedUdn,
+ 'urn' : selectedUrn
+ }, renderVars, 'json');
+ }
+ })
+
+ $('#reloadDevices').click(listDevices);
+
+ listDevices();
+});
+