Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 1 | /*
|
| 2 | * Licensed to the Apache Software Foundation (ASF) under one or more
|
| 3 | * contributor license agreements. See the NOTICE file distributed with
|
| 4 | * this work for additional information regarding copyright ownership.
|
| 5 | * The ASF licenses this file to You under the Apache License, Version 2.0
|
| 6 | * (the "License"); you may not use this file except in compliance with
|
| 7 | * the License. You may obtain a copy of the License at
|
| 8 | *
|
| 9 | * http://www.apache.org/licenses/LICENSE-2.0
|
| 10 | *
|
| 11 | * Unless required by applicable law or agreed to in writing, software
|
| 12 | * distributed under the License is distributed on an "AS IS" BASIS,
|
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 14 | * See the License for the specific language governing permissions and
|
| 15 | * limitations under the License.
|
| 16 | */
|
| 17 | // device selection
|
| 18 | var deviceData = false;
|
| 19 | var deviceTableBody = false;
|
| 20 |
|
| 21 | // service selection
|
| 22 | var serviceData = false;
|
| 23 | var serviceDataVars = false;
|
| 24 | var serviceDataInfoID = false;
|
| 25 | var serviceDataInfoType = false;
|
| 26 |
|
| 27 | // actions
|
| 28 | var actionsContainer = false;
|
| 29 | var actionsSelect = false;
|
| 30 | var actionsInvoke = false;
|
| 31 | var actionsTable = false;
|
| 32 | var actionsTableBody = false;
|
| 33 | var actionsTableRow = false;
|
| 34 |
|
| 35 | // tree browser, buttons, error dialog
|
| 36 | var browser = false;
|
| 37 | var searching = false;
|
| 38 | var reloadVars = false;
|
| 39 |
|
| 40 | /* BEGIN HELPERS */
|
| 41 |
|
| 42 | /* helper functions for tree node */
|
| 43 | function _id(dn) { return dn.replace(/[:-\\.\\$]/g,'_') }
|
| 44 | /* creates a node in the device tree */
|
| 45 | function treeNode(id, name, icon, span) {
|
| 46 | var li = createElement('li', null, { 'id' : _id(id) }, [
|
| 47 | createElement('span', null, null, [
|
| 48 | icon ?
|
| 49 | createElement('img', 'icon', { 'src' : icon }) :
|
| 50 | createElement('span', 'ui-icon ui-icon-' + span) ,
|
| 51 | text(name)
|
| 52 | ])
|
| 53 | ]);
|
| 54 | return $(li);
|
| 55 | }
|
| 56 | /* creates a service node in the devices tree, and associates the click action */
|
| 57 | function servNode(udn, urn) {
|
| 58 | return treeNode(udn+urn, urn, null, 'extlink').click(function() {
|
| 59 | if (selectServiceTime) {
|
| 60 | clearTimeout(selectServiceTime);
|
| 61 | selectServiceTime = false;
|
| 62 | }
|
| 63 | $.post(pluginRoot, {
|
| 64 | 'action': 'serviceDetails',
|
| 65 | 'udn' : udn,
|
| 66 | 'urn' : urn
|
| 67 | }, function(data) {
|
| 68 | renderService(udn, urn, data)
|
| 69 | }, 'json');
|
| 70 | return false;
|
| 71 | });
|
| 72 | }
|
| 73 | /* a helper function to format device properties values - specially
|
| 74 | * converts arrays and strings, if the last are links */
|
| 75 | function _val(val) {
|
| 76 | var ret = '';
|
| 77 | if ($.isArray(val)) {
|
| 78 | for (i in val) ret += _val(val[i]) + '<br/>';
|
| 79 | } else {
|
| 80 | ret = (typeof val == 'string' && val.indexOf('http://') != -1) ?
|
| 81 | '<a target="blank" href="' + val + '">' + val + '</a>' : val;
|
| 82 | }
|
| 83 | return ret;
|
| 84 | }
|
| 85 |
|
| 86 |
|
| 87 | /* BEGIN UI-ELEMENTS CREATION */
|
| 88 |
|
| 89 | /* add element to the tree, just creates the node */
|
| 90 | function addDevice(device) {
|
| 91 | var udn = device.props['UPnP.device.UDN'];
|
| 92 | var name = device.props['UPnP.device.friendlyName'];
|
| 93 | var icon = null;
|
| 94 | if (device.icon) icon = pluginRoot + '?icon=' + udn;
|
| 95 |
|
| 96 | var node = treeNode(udn, name, icon, 'lightbulb').click(function() {
|
| 97 | renderDevice(device);
|
Valentin Valchev | b8468aa | 2010-03-22 14:42:02 +0000 | [diff] [blame] | 98 | return false;
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 99 | });
|
| 100 |
|
| 101 | var ul, hasChildren;
|
| 102 |
|
| 103 | // services
|
| 104 | hasChildren = false;
|
| 105 | ul = $(createElement('ul', 'ui-helper-clearfix'));
|
| 106 | for(var i in device.services) {
|
| 107 | hasChildren = true;
|
| 108 | ul.append( servNode(udn, device.services[i]) );
|
| 109 | }
|
| 110 | if (hasChildren) node.append(ul);
|
| 111 |
|
| 112 | // child devices
|
| 113 | hasChildren = false;
|
| 114 | ul = $(createElement('ul'));
|
| 115 | for(var i in device.children) {
|
| 116 | hasChildren = true;
|
Valentin Valchev | b8468aa | 2010-03-22 14:42:02 +0000 | [diff] [blame] | 117 | ul.append( addDevice(device.children[i]) );
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 118 | }
|
| 119 | if (hasChildren) node.append(ul);
|
| 120 |
|
| 121 | return node;
|
| 122 | }
|
| 123 | /* fills in the list of state variables */
|
Valentin Valchev | f5bf095 | 2012-04-04 14:18:29 +0000 | [diff] [blame] | 124 | function sortVarNm(a) { return typeof a != 'undefined' && typeof a.name != undefined ? a.name.toLowerCase() : '' }
|
| 125 | function namedObjectSorter(a,b) { return sortVarNm(a) > sortVarNm(b) ? 1 : -1 }
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 126 | function renderVars(data) {
|
| 127 | serviceDataVars.empty();
|
Valentin Valchev | ae0dbd7 | 2012-04-05 11:07:18 +0000 | [diff] [blame] | 128 | if (typeof data.variables == 'undefined') return;
|
| 129 |
|
Valentin Valchev | f5bf095 | 2012-04-04 14:18:29 +0000 | [diff] [blame] | 130 | data.variables.sort(namedObjectSorter);
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 131 | for(i in data.variables) {
|
| 132 | var _var = data.variables[i];
|
| 133 | var _tr = tr(null, null, [
|
| 134 | td(null, null, [ text(_var.name) ]),
|
| 135 | td(null, null, [ text(_var.value) ]),
|
| 136 | td(null, null, [ text(_var.sendsEvents) ])
|
| 137 | ]);
|
| 138 | serviceDataVars.append(_tr);
|
| 139 | }
|
| 140 | initStaticWidgets();
|
| 141 | }
|
| 142 |
|
| 143 | /* BEGIN ACTION HANDLERS */
|
| 144 |
|
| 145 | var selectedDevice = false; // the LI element of the selected device, reset on load
|
| 146 | function renderDevice(device) {
|
| 147 | // generate content
|
| 148 | var table = '';
|
Valentin Valchev | f5bf095 | 2012-04-04 14:18:29 +0000 | [diff] [blame] | 149 | var sortedKeys = [];
|
| 150 | for(var key in device.props) sortedKeys.push(key);
|
| 151 | sortedKeys.sort();
|
| 152 | for(var x in sortedKeys) {
|
| 153 | var key = sortedKeys[x];
|
| 154 | var xvalue = _val(device.props[key]);
|
| 155 | if ('objectClass' == key) continue;
|
| 156 | if ('service.id' == key) {
|
Valentin Valchev | 3ccd248 | 2012-07-11 08:12:14 +0000 | [diff] [blame] | 157 | xvalue = '<a href="' + appRoot + '/services/' + xvalue + '">' + xvalue + '</a>';
|
Valentin Valchev | f5bf095 | 2012-04-04 14:18:29 +0000 | [diff] [blame] | 158 | }
|
| 159 | table += '<tr><td class="ui-priority-primary">' + key + '</td><td>' + xvalue + '</td></tr>';
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 160 | }
|
| 161 |
|
| 162 | // update the UI
|
| 163 | deviceTableBody.html(table);
|
| 164 | reloadVars.addClass('ui-state-disabled');
|
| 165 | deviceData.removeClass('ui-helper-hidden');
|
| 166 | serviceData.addClass('ui-helper-hidden')
|
| 167 |
|
| 168 | // reset selected items
|
| 169 | if (selectedDevice) selectedDevice.css('font-weight', 'normal');
|
| 170 | selectedDevice = $('#' + _id(device.props['UPnP.device.UDN']) + ' span').css('font-weight', 'bold');
|
| 171 | }
|
| 172 |
|
| 173 | var selectedUdn = false;
|
| 174 | var selectedUrn = false;
|
| 175 | var selectServiceTime = false;
|
| 176 | function renderService(udn, urn, data) {
|
| 177 | // save selection
|
| 178 | selectedUdn = udn;
|
| 179 | selectedUrn = urn;
|
| 180 |
|
| 181 | // append service info
|
| 182 | serviceDataInfoID.text(data.id);
|
| 183 | serviceDataInfoType.text(data.type);
|
| 184 |
|
| 185 | // append state variables
|
| 186 | renderVars(data);
|
| 187 |
|
| 188 | // append actions
|
| 189 | if (data.actions) {
|
| 190 | var html = '';
|
| 191 | var x = data.actions;
|
Valentin Valchev | f5bf095 | 2012-04-04 14:18:29 +0000 | [diff] [blame] | 192 | x.sort(namedObjectSorter);
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 193 | for (var a in x) html += '<option value="' + a + '">' + x[a].name + '</option>';
|
| 194 | actionsSelect.html(html).unbind('change').change(function() {
|
| 195 | var index = $(this).val();
|
| 196 | actionSelected(udn, urn, x[index]);
|
| 197 | }).trigger('change');
|
| 198 | actionsContainer.removeClass('ui-helper-hidden');
|
| 199 | } else {
|
| 200 | actionsContainer.addClass('ui-helper-hidden');
|
| 201 | }
|
| 202 |
|
| 203 | // update UI
|
| 204 | deviceData.addClass('ui-helper-hidden');
|
| 205 | serviceData.removeClass('ui-helper-hidden');
|
| 206 | reloadVars.removeClass('ui-state-disabled');
|
| 207 | initStaticWidgets();
|
| 208 |
|
| 209 | // refresh once - to get updates asynchronously
|
| 210 | selectServiceTime = setTimeout('reloadVars.click()', 3000);
|
| 211 | }
|
| 212 |
|
| 213 | function actionSelected(udn, urn, action) {
|
| 214 | // add input arguments
|
| 215 | if (action.inVars) {
|
| 216 | actionsTableBody.empty();
|
| 217 | for (var i in action.inVars) {
|
| 218 | var _arg = action.inVars[i];
|
| 219 | var _tr = actionsTableRow.clone().appendTo(actionsTableBody);
|
| 220 | _tr.find('td:eq(0)').text(_arg.name);
|
| 221 | _tr.find('td:eq(1)').text(_arg.type);
|
Valentin Valchev | 6eb0f51 | 2013-04-05 13:28:50 +0000 | [diff] [blame^] | 222 | var _el = false;
|
| 223 | if (_arg.allowed) {
|
| 224 | var selectData = '<select class="ui-state-default">';
|
| 225 | for(var s in _arg.allowed) selectData += '<option>' + _arg.allowed[s] + '</option>';
|
| 226 | _el = $(selectData + '</select>').appendTo( _tr.find('td:eq(2)').empty() );
|
| 227 | } else {
|
| 228 | _el = _tr.find('input');
|
| 229 | }
|
| 230 | _arg['element'] = _el.attr('id', 'arg'+i);
|
| 231 | var infoData = false;
|
| 232 | if (_arg.min || _arg.max || _arg.step) {
|
| 233 | infoData = '<div class="info">';
|
| 234 | if (_arg.min || _arg.max) infoData += '[' + _arg.min + ', ' + _arg.max + ']';
|
| 235 | if (_arg.step) infoData += '/' + _arg.step;
|
| 236 | infoData += '</div>';
|
| 237 | }
|
| 238 | if (infoData) _el.after(infoData);
|
| 239 | if (_arg['default']) _el.val(_arg['default']);
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 240 | }
|
| 241 | actionsTable.removeClass('ui-helper-hidden');
|
| 242 | } else {
|
| 243 | actionsTable.addClass('ui-helper-hidden');
|
| 244 | }
|
| 245 |
|
| 246 | actionsInvoke.unbind('click').click(function() {
|
| 247 | invokeAction(udn, urn, action);
|
| 248 | });
|
| 249 |
|
| 250 | initStaticWidgets(actionsTableBody);
|
| 251 | }
|
| 252 |
|
| 253 | function invokeAction(udn, urn, action) {
|
| 254 | // prepare arguments
|
| 255 | var names = new Array();
|
| 256 | var vals = new Array();
|
| 257 | for (var i in action.inVars) {
|
| 258 | var x = action.inVars[i];
|
| 259 | names.push(x['name']);
|
| 260 | vals.push(x['element'].val());
|
| 261 | }
|
| 262 | // invoke action
|
| 263 | $.post(pluginRoot, {
|
| 264 | 'udn' : udn,
|
| 265 | 'urn' : urn,
|
| 266 | 'action': 'invokeAction',
|
| 267 | 'actionID' : action.name,
|
| 268 | 'names' : names,
|
| 269 | 'vals' : vals
|
| 270 | }, function(data) {
|
| 271 | var html = i18n.no_params_out;
|
| 272 | if (data.output) {
|
| 273 | html = '<table class="nicetable"><tr><th>'+i18n.args_name+'</th><th>'+i18n.args_type+'</th><th>' + i18n.args_value + '</th></tr>';
|
| 274 | for(var i in data.output) {
|
| 275 | var arg = data.output[i];
|
| 276 | html += '<tr><td>' + arg['name'] + '</td><td>' + arg['type'] + '</td><td>' + arg['value'] + '</td></tr>';
|
| 277 | }
|
| 278 | html += '</table>';
|
| 279 | }
|
| 280 | Xalert(html, i18n.dl_title_ok);
|
| 281 | }, 'json');
|
| 282 | }
|
| 283 |
|
Valentin Valchev | ae38b5b | 2012-04-03 16:16:05 +0000 | [diff] [blame] | 284 | function sortNm(a) {
|
| 285 | if (typeof a != 'undefined' && typeof a.props != undefined) {
|
| 286 | var nm = a.props['UPnP.device.friendlyName'];
|
| 287 | if (typeof nm != 'undefined') return nm.toLowerCase();
|
| 288 | }
|
| 289 | return '';
|
| 290 | }
|
| 291 |
|
| 292 | function sortFn(a,b) {
|
| 293 | return sortNm(a) > sortNm(b) ? 1 : -1;
|
| 294 | }
|
| 295 |
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 296 | function listDevices() {
|
| 297 | browser.empty().addClass('ui-helper-hidden');
|
| 298 | searching.removeClass('ui-helper-hidden');
|
Valentin Valchev | ae38b5b | 2012-04-03 16:16:05 +0000 | [diff] [blame] | 299 |
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 300 | $.post(pluginRoot, { 'action': 'listDevices' }, function(data) {
|
| 301 | if (data && data.devices) {
|
Valentin Valchev | ae38b5b | 2012-04-03 16:16:05 +0000 | [diff] [blame] | 302 | data.devices.sort(sortFn);
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 303 | $.each(data.devices, function(index) {
|
| 304 | var html = addDevice(this);
|
| 305 | browser.treeview( { add: html.appendTo(browser) } );
|
| 306 | });
|
Valentin Valchev | 1c7be6f | 2012-04-04 11:52:14 +0000 | [diff] [blame] | 307 | $('.statline').text(i18n.status_devs.msgFormat(data.devices.length));
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 308 | } else {
|
Valentin Valchev | 1c7be6f | 2012-04-04 11:52:14 +0000 | [diff] [blame] | 309 | $('.statline').text(i18n.status_none);
|
| 310 | browser.append('', i18n.status_none, '');
|
Valentin Valchev | d350cc7 | 2010-03-22 06:52:06 +0000 | [diff] [blame] | 311 | }
|
| 312 |
|
| 313 | // update selected items
|
| 314 | selectedDevice = false;
|
| 315 | selectedUdn = false;
|
| 316 | selectedUrn = false;
|
| 317 |
|
| 318 | // update IU elements
|
| 319 | browser.removeClass('ui-helper-hidden');
|
| 320 | searching.addClass('ui-helper-hidden');
|
| 321 | deviceData.addClass('ui-helper-hidden');
|
| 322 | serviceData.addClass('ui-helper-hidden');
|
| 323 | }, 'json');
|
| 324 |
|
| 325 | return false;
|
| 326 | }
|
| 327 |
|
| 328 |
|
| 329 |
|
| 330 | $(document).ready( function() {
|
| 331 | // init elements of style
|
| 332 | searching = $('#searching');
|
| 333 | deviceData = $('#deviceData');
|
| 334 | deviceTableBody = $('#deviceTable tbody');
|
| 335 |
|
| 336 | // services
|
| 337 | serviceData = $('#serviceData');
|
| 338 | serviceDataInfoID = $('#serviceDataInfoID');
|
| 339 | serviceDataInfoType= $('#serviceDataInfoType');
|
| 340 | serviceDataVars = $('#serviceDataVars tbody');
|
| 341 |
|
| 342 | // actions
|
| 343 | actionsContainer = $('#actionsContainer');
|
| 344 | actionsSelect = actionsContainer.find('select');
|
| 345 | actionsInvoke = actionsContainer.find('button');
|
| 346 | actionsTable = actionsContainer.find('table');
|
| 347 | actionsTableBody = actionsTable.find('tbody');
|
| 348 | actionsTableRow = actionsTableBody.find('tr').clone();
|
| 349 | actionsTableBody.empty();
|
| 350 |
|
| 351 | // init navigation tree
|
| 352 | browser = $('#browser').treeview({
|
| 353 | animated: 'fast',
|
| 354 | collapsed: true,
|
| 355 | unique: true
|
| 356 | });
|
| 357 |
|
| 358 | // reload button
|
| 359 | reloadVars = $('#reloadVars').click(function() {
|
| 360 | if (selectedUdn && selectedUrn) {
|
| 361 | $.post(pluginRoot, {
|
| 362 | 'action': 'serviceDetails',
|
| 363 | 'udn' : selectedUdn,
|
| 364 | 'urn' : selectedUrn
|
| 365 | }, renderVars, 'json');
|
| 366 | }
|
| 367 | })
|
| 368 |
|
| 369 | $('#reloadDevices').click(listDevices);
|
| 370 |
|
| 371 | listDevices();
|
| 372 | });
|
| 373 |
|