blob: 2acfc4515a32b9dc153f451eea0ac03afab4ce9d [file] [log] [blame]
Valentin Valchevd350cc72010-03-22 06:52:06 +00001/*
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
18var deviceData = false;
19var deviceTableBody = false;
20
21// service selection
22var serviceData = false;
23var serviceDataVars = false;
24var serviceDataInfoID = false;
25var serviceDataInfoType = false;
26
27// actions
28var actionsContainer = false;
29var actionsSelect = false;
30var actionsInvoke = false;
31var actionsTable = false;
32var actionsTableBody = false;
33var actionsTableRow = false;
34
35// tree browser, buttons, error dialog
36var browser = false;
37var searching = false;
38var reloadVars = false;
39
40/* BEGIN HELPERS */
41
42/* helper functions for tree node */
43function _id(dn) { return dn.replace(/[:-\\.\\$]/g,'_') }
44/* creates a node in the device tree */
45function 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 */
57function 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 */
75function _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 */
90function 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 Valchevb8468aa2010-03-22 14:42:02 +000098 return false;
Valentin Valchevd350cc72010-03-22 06:52:06 +000099 });
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 Valchevb8468aa2010-03-22 14:42:02 +0000117 ul.append( addDevice(device.children[i]) );
Valentin Valchevd350cc72010-03-22 06:52:06 +0000118 }
119 if (hasChildren) node.append(ul);
120
121 return node;
122}
123/* fills in the list of state variables */
Valentin Valchevf5bf0952012-04-04 14:18:29 +0000124function sortVarNm(a) { return typeof a != 'undefined' && typeof a.name != undefined ? a.name.toLowerCase() : '' }
125function namedObjectSorter(a,b) { return sortVarNm(a) > sortVarNm(b) ? 1 : -1 }
Valentin Valchevd350cc72010-03-22 06:52:06 +0000126function renderVars(data) {
127 serviceDataVars.empty();
Valentin Valchevae0dbd72012-04-05 11:07:18 +0000128 if (typeof data.variables == 'undefined') return;
129
Valentin Valchevf5bf0952012-04-04 14:18:29 +0000130 data.variables.sort(namedObjectSorter);
Valentin Valchevd350cc72010-03-22 06:52:06 +0000131 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
145var selectedDevice = false; // the LI element of the selected device, reset on load
146function renderDevice(device) {
147 // generate content
148 var table = '';
Valentin Valchevf5bf0952012-04-04 14:18:29 +0000149 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 Valchev3ccd2482012-07-11 08:12:14 +0000157 xvalue = '<a href="' + appRoot + '/services/' + xvalue + '">' + xvalue + '</a>';
Valentin Valchevf5bf0952012-04-04 14:18:29 +0000158 }
159 table += '<tr><td class="ui-priority-primary">' + key + '</td><td>' + xvalue + '</td></tr>';
Valentin Valchevd350cc72010-03-22 06:52:06 +0000160 }
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
173var selectedUdn = false;
174var selectedUrn = false;
175var selectServiceTime = false;
176function 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 Valchevf5bf0952012-04-04 14:18:29 +0000192 x.sort(namedObjectSorter);
Valentin Valchevd350cc72010-03-22 06:52:06 +0000193 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
213function 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 Valchev6eb0f512013-04-05 13:28:50 +0000222 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 Valchevd350cc72010-03-22 06:52:06 +0000240 }
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
253function 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 Valchevae38b5b2012-04-03 16:16:05 +0000284function 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
292function sortFn(a,b) {
293 return sortNm(a) > sortNm(b) ? 1 : -1;
294}
295
Valentin Valchevd350cc72010-03-22 06:52:06 +0000296function listDevices() {
297 browser.empty().addClass('ui-helper-hidden');
298 searching.removeClass('ui-helper-hidden');
Valentin Valchevae38b5b2012-04-03 16:16:05 +0000299
Valentin Valchevd350cc72010-03-22 06:52:06 +0000300 $.post(pluginRoot, { 'action': 'listDevices' }, function(data) {
301 if (data && data.devices) {
Valentin Valchevae38b5b2012-04-03 16:16:05 +0000302 data.devices.sort(sortFn);
Valentin Valchevd350cc72010-03-22 06:52:06 +0000303 $.each(data.devices, function(index) {
304 var html = addDevice(this);
305 browser.treeview( { add: html.appendTo(browser) } );
306 });
Valentin Valchev1c7be6f2012-04-04 11:52:14 +0000307 $('.statline').text(i18n.status_devs.msgFormat(data.devices.length));
Valentin Valchevd350cc72010-03-22 06:52:06 +0000308 } else {
Valentin Valchev1c7be6f2012-04-04 11:52:14 +0000309 $('.statline').text(i18n.status_none);
310 browser.append('', i18n.status_none, '');
Valentin Valchevd350cc72010-03-22 06:52:06 +0000311 }
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