Started work on guest sharing mobile site.

Ylian Saint-Hilaire committed Jan 4, 2022 at 15:59 UTC 3e477b7b4404cf2e87ad18519f838c596e1dad78
2 files changed +2228
translate/translate.js
+1
@@ -76,6 +76,7 @@ var minifyMeshCentralSourceFiles = [
76 "../views/messenger.handlebars",
77 "../views/player.handlebars",
78 "../views/sharing.handlebars",
79 + "../views/sharing-mobile.handlebars",
80 "../views/mstsc.handlebars",
81 "../views/ssh.handlebars",
82 "../public/scripts/agent-desktop-0.0.2.js",
views/sharing-mobile.handlebars new
+2227
@@ -0,0 +1,2227 @@
1 +<!DOCTYPE html>
2 +<html lang="en" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
3 +<head>
4 + <meta http-equiv="X-UA-Compatible" content="IE=edge" />
5 + <meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
6 + <meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
7 + <meta name="apple-mobile-web-app-capable" content="yes" />
8 + <meta name="format-detection" content="telephone=no" />
9 + <meta name="robots" content="noindex,nofollow">
10 + <link type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS" />
11 + <link type="text/css" href="styles/style-sharing.css" media="screen" rel="stylesheet" title="CSS" />
12 + <link type="text/css" href="styles/xterm.css" media="screen" rel="stylesheet" title="CSS" />
13 + <link rel="apple-touch-icon" href="/favicon-303x303.png" />
14 + <script type="text/javascript" src="scripts/common-0.0.1{{min}}.js"></script>
15 + <script type="text/javascript" src="scripts/amt-redir-ws-0.1.0{{{min}}}.js"></script>
16 + <script type="text/javascript" src="scripts/amt-wsman-ws-0.2.0{{{min}}}.js"></script>
17 + <script type="text/javascript" src="scripts/agent-redir-ws-0.1.1{{{min}}}.js"></script>
18 + <script type="text/javascript" src="scripts/agent-redir-rtc-0.1.0{{{min}}}.js"></script>
19 + <script type="text/javascript" src="scripts/agent-desktop-0.0.2{{min}}.js"></script>
20 + <script type="text/javascript" src="scripts/amt-desktop-0.0.2{{min}}.js"></script>
21 + <script type="text/javascript" src="scripts/amt-terminal-0.0.2{{min}}.js"></script>
22 + <script type="text/javascript" src="scripts/zlib{{min}}.js"></script>
23 + <script type="text/javascript" src="scripts/zlib-inflate{{min}}.js"></script>
24 + <script type="text/javascript" src="scripts/zlib-adler32{{min}}.js"></script>
25 + <script type="text/javascript" src="scripts/zlib-crc32{{min}}.js"></script>
26 + <script type="text/javascript" src="scripts/amt-terminal-0.0.2{{min}}.js"></script>
27 + <script type="text/javascript" src="scripts/xterm{{{min}}}.js"></script>
28 + <script type="text/javascript" src="scripts/xterm-addon-fit{{{min}}}.js"></script>
29 + <script keeplink=1 type="text/javascript" src="scripts/filesaver.min.js"></script>
30 + <title>{{{title}}}</title>
31 +</head>
32 +<body style="overflow:hidden;background-color:black;margin:0;padding:0;border:0;font-size:13px;font-family:\'Trebuchet MS\', Arial, Helvetica, sans-serif">
33 + <div id=p11 class="noselect" style="overflow:hidden;position:absolute;left:0;top:0;right:0;bottom:32px;display:none">
34 + <div id=deskarea0>
35 + <div id=deskarea1 class="areaHead" style="line-height:24px">
36 + <div class="toright2">
37 + <span id="p11power"></span>&nbsp;
38 + <input id="deskActionsSettings" type="button" value="Settings..." title="Edit remote desktop settings" onkeypress="return false" onkeydown="return false" onclick="showDesktopSettings()" class="mR" />
39 + <div id="desktopCustomUiButtons" style="float:left"></div>
40 + <div id="deskRecordIcon" class='deskareaicon' title="Server is recording this session" style="display:none;background-color:red;width:12px;height:12px;border-radius:6px;margin-top:5px"></div>
41 + </div>
42 + <div>
43 + <div id="idx_deskFullBtn2" onclick=deskToggleFull(event)>&nbsp;&#x2716;</div>
44 + <span id=connectbutton1span><input type=button id=connectbutton1 cmenu="deskConnectButton" value="Connect" onclick=connectDesktop(event,3) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
45 + <span id=connectbutton1hspan>&nbsp;<input type=button id=connectbutton1h value="HW Connect" title="Connect using Intel AMT hardware KVM" onclick=connectDesktop(event,2) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
46 + <span id=disconnectbutton1span>&nbsp;<input type=button id=disconnectbutton1 value="Disconnect" onclick=connectDesktop(event,0) onkeypress="return false" onkeydown="return false" /></span>
47 + &nbsp;<span id="deskstatus">Disconnected</span><span id="deskmetadata"></span>
48 + </div>
49 + </div>
50 + <div id=deskarea3x style="max-height:calc(100vh - 60px);height:calc(100vh - 60px);">
51 + <div id=DeskFocus oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></div>
52 + <div id=DeskParent>
53 + <canvas id=Desk width=640 height=480 oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event) onmousewheel=dmousewheel(event)></canvas>
54 + </div>
55 + <div id=p11DeskConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=clearConsoleMsg()></div>
56 + </div>
57 + <div id=deskarea4 class="areaFoot" style="min-height:24px">
58 + <div class="toright2">
59 + <span id="DeskLatency" title="Desktop Session Latency"></span>
60 + <span id="DeskTimer" style="display:none" title="Session time"></span>&nbsp;
61 + <select id=termdisplays style="display:none" onchange=deskSetDisplay(event) onkeypress="return false" onkeydown="return false"></select>&nbsp;
62 + <span id=DeskSaveImageButton title="Save a screenshot of the remote desktop"><img src='images/icon-camera.png' onclick=deskSaveImage() height=16 width=16 style=padding-top:2px /></span>
63 + </div>
64 + <div style="height:22px">
65 + <select id="deskkeys">
66 + <option value=10>Ctrl+Alt+Del</option>
67 + <option value=5>Win</option>
68 + <option value=0>Win+Down</option>
69 + <option value=1>Win+Up</option>
70 + <option value=2>Win+L</option>
71 + <option value=3>Win+M</option>
72 + <option value=4>Shift+Win+M</option>
73 + <option value=6>Win+R</option>
74 + <option value=7>Alt-F4</option>
75 + <option value=8>Ctrl-W</option>
76 + <option value=9>Alt-Tab</option>
77 + <option value=11>Win+Left</option>
78 + <option value=12>Win+Right</option>
79 + </select>
80 + <input id="DeskWD" type=button value="Send" onkeypress="return false" onkeydown="return false" onclick="deskSendKeys()" />
81 + <input id="DeskClip" style="" type="button" value="Clipboard" onkeypress="return false" onkeydown="return false" onclick="showDeskClip()" />
82 + <input id="DeskType" style="" type="button" value="Type" onkeypress="return false" onkeydown="return false" onclick="showDeskType()" />
83 + <label><span id="DeskControlSpan" title="Toggle mouse and keyboard input"><input id="DeskControl" type="checkbox" onkeypress="return false" onkeydown="return false" onclick="toggleKvmControl()" />Input</span></label>&nbsp;
84 + </div>
85 + </div>
86 + </div>
87 + </div>
88 + <div id=p12 class="noselect" style="overflow:hidden;position:absolute;left:0;top:0;right:0;bottom:32px;display:none;background-color:black">
89 + <div id="p12warning" onclick=showFeaturesDlg()>
90 + <div class="icon2"></div>
91 + <div class="warningbox">Intel&reg; AMT Redirection port or KVM feature is disabled<span id="p12warninga">, click here to enable it.</span></div>
92 + </div>
93 + <div id="p12warning2" onclick=showPowerActionDlg()>
94 + <div class="icon2"></div>
95 + <div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div>
96 + </div>
97 + <div class="areaHead" style="position:absolute;top:0;left:0;right:0;height:24px">
98 + <div class="toright2">
99 + <div id="termRecordIcon" class='deskareaicon' title="Server is recording this session" style="display:none;background-color:red;width:12px;height:12px;border-radius:6px;margin-top:5px;margin-left:5px"></div>
100 + <div id="p12power" style="margin-top:3px;margin-right:4px;float:right"></div>
101 + </div>
102 + <div>
103 + <span id="connectbutton2span"><input type="button" id="connectbutton2" cmenu="termConnectButton" value="Connect" onclick=connectTerminal(event,1) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
104 + <span id="disconnectbutton2span">&nbsp;<input type="button" id="disconnectbutton2" value="Disconnect" onclick=connectTerminal(event,0) onkeypress="return false" onkeydown="return false" /></span>
105 + &nbsp;<span id="termstatus">Disconnected</span><span id="termtitle"></span>
106 + </div>
107 + </div>
108 + <div id="termarea3xdiv" style="position:absolute;top:28px;bottom:28px;left:0;right:0"></div>
109 + <div class="areaFoot" style="position:absolute;bottom:0;left:0;right:0;height:24px">
110 + <div class="toright2">
111 + <span id="TermLatency" title="Terminal Session Latency"></span>
112 + <span id="TermTimer" title="Session time"></span>&nbsp;
113 + <span id="terminalSettingsButtons" style="display:none">
114 + <input id="id_tcrbutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="CR+LF" title="Toggle what the return key will send" onclick="termToggleCr()" />
115 + <input id="id_tfxkeysbutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="Intel (F10 = ESC+[OM)" title="Toggle F1 to F10 keys emulation type" onclick="termToggleFx()" />
116 + <input id="id_ttypebutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="Extended Ascii" title="Toggle terminal emulation type" onclick="termToggleType()" />
117 + </span>
118 + <span id="terminalSizeDropDown" style="display:none">
119 + <select id="termSizeList" onkeypress="return false"><option value="1">80x25</option><option value="2">100x30</option></select>
120 + </span>
121 + <span id="specialKeyDropDown">
122 + <select id="specialkeylist" onkeypress="return false"></select>
123 + <input id="specialkeylistinput" type="button" onkeypress="return false" class="bottombutton" value="Send" title="Send the selected special key" onclick="sendSpecialKey()" />
124 + </span>
125 + </div>
126 + <div>
127 + &nbsp;
128 + <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="ctrlcbutton" value="Ctl-C" onclick="termSendKey(3,'ctrlcbutton')" />
129 + <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="ctrlxbutton" value="Ctl-X" onclick="termSendKey(24,'ctrlxbutton')" />
130 + <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="escbutton" value="ESC" onclick="termSendKey(27,'escbutton')" />
131 + <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="bsbutton" value="Backspace" onclick="termSendKey(8,'bsbutton')" style="display:none" />
132 + <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="pastebutton" value="Paste" title="Paste text into the terminal" onclick="showTermPasteDialog()" style="display:none" />
133 + </div>
134 + </div>
135 + <div id=p12TermConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:45px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p12clearConsoleMsg()></div>
136 + </div>
137 + <div id=p13 class="noselect" style="overflow:hidden;position:absolute;left:0;top:0;right:0;bottom:32px;display:none;background-color:white">
138 + <div id="p13toolbar" style="position:absolute;left:0;top:0;right:0;bottom:28px">
139 + <div class="areaHead">
140 + <div class="toright2">
141 + <div id="filesRecordIcon" class='deskareaicon' title="Server is recording this session" style="display:none;background-color:red;width:12px;height:12px;border-radius:6px;margin-top:5px;margin-left:5px"></div>
142 + <div id="p13power" style="margin-top:3px;margin-right:4px;float:right"></div>
143 + </div>
144 + <div>
145 + <input id=p13Connect value="Connect" onclick=connectFiles(event) type="button" />
146 + &nbsp;<span id=p13Status>Disconnected</span>
147 + </div>
148 + </div>
149 + <div id="fileArea2" class="areaHead2" valign=bottom>
150 + <div id="p13rightOfButtons" class="toright2"></div>
151 + <div>
152 + <input type=button id=p13FolderUp disabled="disabled" onclick="p13folderup()" value="Up" />&nbsp;
153 + <input type=button id=p13SelectAllButton disabled="disabled" onclick="p13selectallfile()" value="Select All" />&nbsp;
154 + <input type=button id=p13RenameFileButton disabled="disabled" value="Rename" onclick="p13renamefile()" />&nbsp;
155 + <input type=button id=p13DeleteFileButton disabled="disabled" value="Delete" onclick="p13deletefile()" />&nbsp;
156 + <input type=button id=p13ViewFileButton disabled="disabled" value="Edit" onclick="p13viewfile()" />&nbsp;
157 + <input type=button id=p13NewFolderButton disabled="disabled" value="New Folder" onclick="p13createfolder()" />&nbsp;
158 + <input type=button id=p13UploadButton disabled="disabled" value="Upload" onclick="p13uploadFile()" />&nbsp;
159 + <input type=button id=p13CutButton disabled="disabled" value="Cut" onclick="p13copyFile(1)" />&nbsp;
160 + <input type=button id=p13CopyButton disabled="disabled" value="Copy" onclick="p13copyFile(0)" />&nbsp;
161 + <input type=button id=p13PasteButton disabled="disabled" value="Paste" onclick="p13pasteFile()" />&nbsp;
162 + <input type=button id=p13ZipButton disabled="disabled" value="Zip" onclick="p13zipFiles()" />&nbsp;
163 + <input type=button id=p13RefreshButton disabled="disabled" value="Refresh" onclick="p13folderup(9999)" />&nbsp;
164 + <input type=button id=p13FindButton disabled="disabled" value="Find" onclick="p13findfile()" />&nbsp;
165 + </div>
166 + </div>
167 + <div class="areaHead3" style="line-height:28px">
168 + <div class="toright2">
169 + <select id=p13sortdropdown onchange=p13updateFiles()>
170 + <option value=1 selected="selected">Sort by name</option>
171 + <option value=2>Sort by size</option>
172 + <option value=3>Sort by date</option>
173 + <option value=4>Descend by name</option>
174 + <option value=5>Descend by size</option>
175 + <option value=6>Descend by date</option>
176 + </select>
177 + </div>
178 + <div>&nbsp;&nbsp;<span id="p13currentpath"></span></div>
179 + </div>
180 + <div id="fileArea4" style="height:calc(100vh - 146px)">
181 + <div id=p13FilesConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:165px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p13clearConsoleMsg()></div>
182 + <div id="p13filetable" style="width:100%;height:100%">
183 + <div id="p13bigok" style="display:none"><b>&checkmark;</b></div>
184 + <div id="p13bigfail" style="display:none"><b>&#10007;</b></div>
185 + <span id="p13files"></span>
186 + </div>
187 + </div>
188 + </div>
189 + <div class="areaFoot" style="position:absolute;left:0;right:0;bottom:0;height:28px">
190 + &nbsp;<span id="p13bottomstatus"></span>
191 + </div>
192 + </div>
193 + <div id=xfooter style="height:32px;width:100%;text-align:center;background-color:#113962;position:absolute;bottom:0px;margin:0;padding:0">
194 + <table id=xfooterMenu cellpadding=0 cellspacing=0 style="height:32px;width:100%;color:white;cursor:pointer;table-layout:fixed"></table>
195 + </div>
196 + <div id=dialog class="noselect" style="display:none">
197 + <div id=dialogHeader>
198 + <div tabindex=0 id=id_dialogclose onclick=setDialogMode() onkeypress="if (event.key == 'Enter') setDialogMode()">&#x2716;</div>
199 + <div id=id_dialogtitle></div>
200 + </div>
201 + <div id=dialogBody>
202 + <div id=dialog1>
203 + <div id=id_dialogMessage style=""></div>
204 + </div>
205 + <div id=dialog2 style="">
206 + <div id=id_dialogOptions></div>
207 + </div>
208 + <div id=dialog7 style="">
209 + <div id="d7meshkvm">
210 + <h4>Agent Remote Desktop</h4>
211 + <div>
212 + <div>Quality</div>
213 + <select id="d7bitmapquality" dir="rtl"></select>
214 + </div>
215 + <div>
216 + <div>Scaling</div>
217 + <select id="d7bitmapscaling" style="" dir="rtl">
218 + <option selected=selected value=1024>100%</option>
219 + <option value=896>87.5%</option>
220 + <option value=768>75%</option>
221 + <option value=640>62.5%</option>
222 + <option value=512>50%</option>
223 + <option value=384>37.5%</option>
224 + <option value=256>25%</option>
225 + <option value=128>12.5%</option>
226 + </select>
227 + </div>
228 + <div>
229 + <div>Frame rate</div>
230 + <select id="d7framelimiter" dir="rtl">
231 + <option selected=selected value=50>Fast</option>
232 + <option value=100>Medium</option>
233 + <option value=400>Slow</option>
234 + <option value=1000>Very slow</option>
235 + </select>
236 + </div>
237 + <div id="d7desktopOtherSettings">
238 + <div>Other Settings</div>
239 + <div id="d7otherset2" style="display:block">
240 + <label style="display:block"><input type="checkbox" id="d7deskSwapMouse" />Swap Mouse Buttons</label>
241 + <label style="display:block"><input type="checkbox" id="d7deskRemoteKeyMap" />Use Remote Keyboard Map</label>
242 + </div>
243 + </div>
244 + </div>
245 + <div id="d7amtkvm">
246 + <h4>Intel&reg; AMT Hardware KVM</h4>
247 + <div>
248 + <div>Image Encoding</div>
249 + <select id="d7desktopmode">
250 + <option value="1">RLE8, Fastest</option>
251 + <option value="2">RLE16, Recommended</option>
252 + <option value="3">RAW8, Slow</option>
253 + <option value="4">RAW16, Very Slow</option>
254 + </select>
255 + </div>
256 + <div>
257 + <div>Other Settings</div>
258 + <div id="d7otherset" style="display:block">
259 + <label style="display:block"><input type="checkbox" id="d7showfocus" />Show Focus Tool</label>
260 + <label style="display:block"><input type="checkbox" id="d7showcursor" />Show Local Mouse Cursor</label>
261 + <label style="display:block"><input type="checkbox" id="d7localKeyMap" />Local Keyboard Map</label>
262 + </div>
263 + </div>
264 + </div>
265 + </div>
266 + </div>
267 + <div id="idx_dlgButtonBar">
268 + <input id="idx_dlgCancelButton" type="button" value="Cancel" style="" onclick="dialogclose(0)">
269 + <input id="idx_dlgOkButton" type="button" value="OK" style="" onclick="dialogclose(1)">
270 + <div><input id="idx_dlgDeleteButton" type="button" value="Delete" style="display:none" onclick="dialogclose(2)"></div>
271 + </div>
272 + </div>
273 + <script>
274 + var sessionActivity = null;
275 + var desktop = null;
276 + var agentPresent = true;
277 + var intelAmtPresent = false;
278 + var desktopsettings = { encoding: 2, showfocus: false, showmouse: true, showcad: true, quality: 40, scaling: 1024, framerate: 100, localkeymap: false };
279 + var serverPublicNamePort = '{{{serverDnsName}}}:{{{serverPublicPort}}}';
280 + var domain = '{{{domain}}}';
281 + var domainUrl = '{{{domainurl}}}';
282 + var authCookie = '{{{authCookie}}}';
283 + var viewOnly = parseInt('{{{viewOnly}}}');
284 + var urlargs = parseUriArgs();
285 + var debugmode = urlargs.debug;
286 + var attemptWebRTC = false;
287 + var updateSessionTimer = null;
288 + var StatusStrs = ["Disconnected", "Connecting...", "Setup...", "Connected", "Intel&reg; AMT Connected"];
289 + var nodeName = decodeURIComponent('{{{nodeName}}}');
290 + var webPageFullScreen = false;
291 + var expire = '{{{expire}}}';
292 + if (expire != '') {
293 + QH('p11power', printFlexDateTime(new Date(parseInt(expire))));
294 + QH('p12power', printFlexDateTime(new Date(parseInt(expire))));
295 + QH('p13power', printFlexDateTime(new Date(parseInt(expire))));
296 + }
297 + var features = parseInt('{{{features}}}');
298 + var features2 = parseInt('{{{features2}}}');
299 +
300 + // Load desktop settings
301 + var t = null;
302 + try { t = localStorage.getItem('desktopsettings'); } catch (ex) { }
303 + if (t != null) { try { desktopsettings = JSON.parse(t); } catch (ex) { } }
304 + if (((features2 & 1) == 0) && (desktopsettings.quality > 60)) { desktopsettings.quality = 60; }
305 +
306 + // Terminal
307 + var terminal = null;
308 + var xterm = null;
309 + var xtermfit = null;
310 + var xtermResizeTimer = null;
311 +
312 + // Files
313 + var files = null
314 +
315 + // Console messages timers
316 + var p11DeskConsoleMsgTimer = null;
317 + var p12TermConsoleMsgTimer = null;
318 + var p13FilesConsoleMsgTimer = null;
319 +
320 + function start() {
321 + window.onresize = deskAdjust;
322 + document.onkeypress = ondockeypress;
323 + document.onkeydown = ondockeydown;
324 + document.onkeyup = ondockeyup;
325 + setupDesktop();
326 + setupTerminal();
327 + setupFiles();
328 +
329 + // Set the document title
330 + if (nodeName.length > 0) { document.title += ' - ' + nodeName; }
331 +
332 + // View only mode
333 + if (viewOnly == 1) {
334 + QV('deskkeys', false);
335 + QV('DeskWD', false);
336 + QV('DeskClip', false);
337 + QV('DeskType', false);
338 + QV('DeskControlSpan', false);
339 + }
340 +
341 + // Setup upload drag & drop
342 + Q('p13filetable').addEventListener('drop', p13fileDragDrop, false);
343 + Q('p13filetable').addEventListener('dragover', p13fileDragOver, false);
344 + Q('p13filetable').addEventListener('dragleave', p13fileDragLeave, false);
345 +
346 + // Setup feature visibility
347 + QV('LeftMenuDesktop', features & 2);
348 + QV('LeftMenuTerminal', features & 1);
349 + QV('LeftMenuFiles', features & 4);
350 + if (features & 2) { go(11); } // Goto desktop
351 + else if (features & 1) { go(12); } // Goto terminal
352 + else if (features & 4) { go(13); } // Goto files
353 + deskAdjust();
354 + }
355 +
356 + //
357 + // Desktop
358 + //
359 +
360 + function isInputAllowed() { return (viewOnly != 1) && (Q('DeskControl').checked == true); }
361 + function clearConsoleMsg() { QH('p11DeskConsoleMsg', ''); }
362 +
363 + function deskAdjust() {
364 + if ((xxcurrentView == 12) && (terminal != null) && (xtermfit != null)) { xtermfit.fit(); } // Terminal
365 + QS('fileArea4')['height'] = 'calc(100vh - ' + (90 + Q('fileArea2').clientHeight) + 'px)'; // Files
366 + var parentH = Q('DeskParent').clientHeight, parentW = Q('DeskParent').clientWidth;
367 + var deskH = Q('Desk').height, deskW = Q('Desk').width;
368 +
369 + // Fixed aspect ratio
370 + if ((parentH / parentW) > (deskH / deskW)) {
371 + var hNew = ((deskH * parentW) / deskW) + 'px';
372 + QS('Desk').height = hNew;
373 + QS('Desk').width = '100%';
374 + } else {
375 + var wNew = ((deskW * parentH) / deskH) + 'px';
376 + QS('Desk').height = '100%';
377 + QS('Desk').width = wNew;
378 + }
379 + QS('Desk')['margin-top'] = null;
380 + QS('DeskParent').overflow = 'hidden';
381 + }
382 +
383 + function setupDesktop() {
384 + // Setup the remote desktop
385 + if (desktop != null) { desktop.Stop(); desktop = null; }
386 +
387 + // If the device desktop is already connected in multi-desktop, use that.
388 + if (desktop == null) {
389 + // Device is not already connected, just setup a blank canvas
390 + QH('DeskParent', '<canvas id=Desk oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></canvas>');
391 +
392 + // Setup the mouse wheel
393 + Q('Desk').addEventListener('DOMMouseScroll', function (e) { return dmousewheel(e); });
394 + Q('Desk').addEventListener('mousewheel', function (e) { return dmousewheel(e); });
395 + }
396 + updateDesktopButtons();
397 + deskAdjust();
398 + updateMetadata(desktop, 'deskmetadata');
399 + }
400 +
401 + // Show and enable the right buttons
402 + function updateDesktopButtons() {
403 + var deskState = 0;
404 + if (desktop != null) { deskState = desktop.State; }
405 +
406 + // Show the right buttons
407 + QV('disconnectbutton1span', (deskState != 0));
408 + QV('connectbutton1span', (deskState == 0) && (agentPresent));
409 + QV('connectbutton1hspan', (deskState == 0) && (intelAmtPresent));
410 +
411 + // Show the right settings
412 + QV('d7meshkvm', agentPresent && ((deskState == 0) || (desktop.contype == 1)));
413 + QV('d7amtkvm', intelAmtPresent && ((deskState == 0) || (desktop.contype == 2)));
414 +
415 + // Enable buttons
416 + QE('connectbutton1', agentPresent);
417 + QE('connectbutton1h', intelAmtPresent);
418 + //QV('DeskClip', agentPresent && ((desktop == null) || (desktop.contype != 2))); // Clipboard not supported on macOS
419 + QV('DeskClip', false); // Clipboard not supported on this page
420 + QE('DeskClip', deskState == 3);
421 + QE('DeskType', deskState == 3);
422 + QV('DeskWD', viewOnly != 1);
423 + QE('DeskWD', deskState == 3);
424 + QV('deskkeys', viewOnly != 1);
425 + QE('deskkeys', deskState == 3);
426 +
427 + // Display this only if we have Chat & Notify permissions
428 + QV('DeskSaveImageButton', (deskState == 3) && (Q('Desk')['toBlob'] != null));
429 + QV('DeskControlSpan', viewOnly != 1);
430 + QV('deskActionsBtn', (browserfullscreen == false));
431 + QV('deskActionsSettings', (browserfullscreen == false));
432 + Q('DeskControl').checked = true;
433 + QS('DeskControlSpan').color = Q('DeskControl').checked ? null : 'red';
434 + }
435 +
436 + // Used to translate incoming agent console messages
437 + var agentConsoleMessages = ['', "Waiting for user to grant access...", "Denied", "Failed to start remote terminal session, {0} ({1})", "Timeout", "Received invalid network data"];
438 + function formatAgentConsoleMessage(msg, msgid, msgargs) {
439 + var r;
440 + if (msgargs == null) { msgargs = []; }
441 + while (msgargs.length < 3) { msgargs.push(''); } // We need to call the format function in a way that works with older browsers and minifier, can't use apply() or ...
442 + if (msgid && (msgid < agentConsoleMessages.length)) { r = EscapeHtml(format(agentConsoleMessages[msgid], (msgargs[0]), (msgargs[1]), (msgargs[2]))); } else { r = EscapeHtml(msg); }
443 + return r.split('\n').join('<br />') + '<br /><br />';
444 + }
445 +
446 + function connectDesktop(e, contype, tsid, consent) {
447 + if (xxdialogMode) return;
448 + if ((e != null) && (e.shiftKey != false) && (contype == 3)) { contype = 1; } // If the shift key is not pressed, don't try to ask for session list.
449 + QV('p11DeskSessionSelector', false);
450 + p11clearConsoleMsg();
451 + if (desktop == null) {
452 + if (contype == 2) {
453 + // Setup the Intel AMT remote desktop
454 + //if ((desktopNode.intelamt.user == null) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop, 2); return; }
455 + desktop = CreateAmtRedirect(CreateAmtRemoteDesktop('Desk'), authCookie);
456 + desktop.debugmode = debugmode;
457 + desktop.onStateChanged = onDesktopStateChange;
458 + desktop.m.stopInput = (viewOnly == 1);
459 + desktop.m.bpp = (desktopsettings.encoding == 1 || desktopsettings.encoding == 3) ? 1 : 2;
460 + desktop.m.useZRLE = (desktopsettings.encoding < 3);
461 + desktop.m.localKeyMap = desktopsettings.localkeymap;
462 + desktop.m.showmouse = desktopsettings.showmouse;
463 + desktop.m.onScreenSizeChange = deskAdjust;
464 + desktop.m.onKvmData = function (x) {
465 + //console.log('onKvmData (' + x.length + '): ' + x);
466 + // Send the presense probe only once if needed.
467 + if (x.length == 0) { if (!desktop.m._sentPresence) { desktop.m._sentPresence = true; desktop.m.sendKvmData(JSON.stringify({ action: 'present', ver: 1 })); } return; }
468 + var data = null;
469 + try { data = JSON.parse(x); } catch (e) { }
470 + if ((data != null) && (data.action != null)) {
471 + if (data.action == 'restart') {
472 + // Clear WebRTC channel
473 + webRtcDesktopReset();
474 + desktop.m.sendKvmData(JSON.stringify({ action: 'present', ver: 1 }));
475 + } else if ((data.action == 'present') && (webRtcDesktop == null)) {
476 + // Setup WebRTC channel
477 + webRtcDesktop = { platform: data.platform };
478 + var configuration = null; //{ "iceServers": [ { 'urls': 'stun:stun.services.mozilla.com' }, { 'urls': 'stun:stun.l.google.com:19302' } ] };
479 + if (typeof RTCPeerConnection !== 'undefined') { webRtcDesktop.webrtc = new RTCPeerConnection(configuration); }
480 + else if (typeof webkitRTCPeerConnection !== 'undefined') { webRtcDesktop.webrtc = new webkitRTCPeerConnection(configuration); }
481 +
482 + webRtcDesktop.webchannel = webRtcDesktop.webrtc.createDataChannel("DataChannel", {}); // { ordered: false, maxRetransmits: 2 }
483 + webRtcDesktop.webchannel.onopen = function () {
484 + // Switch to software KVM
485 + //if (urlvars && urlvars['kvmdatatrace']) { console.log('WebRTC Data Channel Open'); }
486 + console.log('WebRTC Data Channel Open');
487 + Q('deskstatus').textContent = StatusStrs[desktop.State] + ", Soft-KVM";
488 + desktop.m.hold(true);
489 + webRtcDesktop.webRtcActive = true;
490 + webRtcDesktop.softdesktop = CreateKvmDataChannel(webRtcDesktop.webchannel, CreateAgentRemoteDesktop('Desk', Q('id_mainarea')), desktop.m);
491 + webRtcDesktop.softdesktop.m.setRotation(desktop.m.rotation);
492 + webRtcDesktop.softdesktop.m.onScreenSizeChange = deskAdjust;
493 + if (desktopsettings.quality) { webRtcDesktop.softdesktop.m.CompressionLevel = desktopsettings.quality; } // Number from 1 to 100. 50 or less is best.
494 + if (desktopsettings.scaling) { webRtcDesktop.softdesktop.m.ScalingLevel = desktopsettings.scaling; }
495 + webRtcDesktop.softdesktop.Start();
496 +
497 + // Check if we can get remote file access
498 + // ###BEGIN###{DesktopInbandFiles}
499 + /*
500 + QV('go24', true); // Files
501 + downloadFile = null;
502 + p24files = webRtcDesktop.softdesktop;
503 + p24targetpath = '';
504 + webRtcDesktop.softdesktop.onControlMsg = onFilesControlData;
505 + webRtcDesktop.softdesktop.sendCtrlMsg(JSON.stringify({ action: 'ls', reqid: 1, path: '' })); // Ask for the root folder
506 + */
507 + // ###END###{DesktopInbandFiles}
508 + }
509 + webRtcDesktop.webchannel.onclose = function (event) {
510 + //if (urlvars['kvmdatatrace']) { console.log('WebRTC Data Channel Closed'); }
511 + console.log('WebRTC Data Channel Closed');
512 + webRtcDesktopReset();
513 + }
514 + webRtcDesktop.webrtc.onicecandidate = function (e) {
515 + if (e.candidate == null) {
516 + desktop.m.sendKvmData(JSON.stringify({ action: 'offer', ver: 1, sdp: webRtcDesktop.webrtcoffer.sdp }));
517 + } else {
518 + webRtcDesktop.webrtcoffer.sdp += ('a=' + e.candidate.candidate + '\r\n'); // New candidate, add it to the SDP
519 + }
520 + }
521 + webRtcDesktop.webrtc.oniceconnectionstatechange = function () {
522 + if ((webRtcDesktop != null) && (webRtcDesktop.webrtc != null) && ((webRtcDesktop.webrtc.iceConnectionState == 'disconnected') || (webRtcDesktop.webrtc.iceConnectionState == 'failed'))) { /*console.log('WebRTC ICE Failed');*/ webRtcDesktopReset(); }
523 + }
524 + webRtcDesktop.webrtc.createOffer(function (offer) {
525 + // Got the offer
526 + webRtcDesktop.webrtcoffer = offer;
527 + webRtcDesktop.webrtc.setLocalDescription(offer, function () { }, webRtcDesktopReset);
528 + }, webRtcDesktopReset, { mandatory: { OfferToReceiveAudio: false, OfferToReceiveVideo: false } });
529 + } else if ((data.action == 'answer') && (webRtcDesktop != null)) {
530 + // Complete the WebRTC channel
531 + webRtcDesktop.webrtc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp: data.sdp }), function () { }, webRtcDesktopReset);
532 + }
533 + }
534 + };
535 + desktop.Start(null, 16994, '*', '*', 0);
536 + desktop.contype = 2;
537 + } else if ((contype == null) || (contype == 1) || (contype == 3)) {
538 + // Setup the Mesh Agent remote desktop
539 + desktop = CreateAgentRedirect(null, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort, authCookie, null, domainUrl);
540 + desktop.m.stopInput = (viewOnly == 1);
541 + desktop.m.mouseCursorActive(true);
542 + desktop.debugmode = debugmode;
543 + desktop.m.debugmode = debugmode;
544 + desktop.attemptWebRTC = attemptWebRTC;
545 + desktop.options = {};
546 + if (tsid != null) { desktop.options.tsid = tsid; }
547 + if (consent != null) { desktop.options.consent = consent; }
548 + desktop.onStateChanged = onDesktopStateChange;
549 + desktop.onConsoleMessageChange = function () {
550 + if (desktop.consoleMessage) {
551 + Q('p11DeskConsoleMsg').innerHTML += formatAgentConsoleMessage(desktop.consoleMessage, desktop.consoleMessageId, desktop.consoleMessageArgs);
552 + QV('p11DeskConsoleMsg', true);
553 + if (p11DeskConsoleMsgTimer != null) { clearTimeout(p11DeskConsoleMsgTimer); }
554 + if (desktop.consoleMessageTimeout) { p11DeskConsoleMsgTimer = setTimeout(p11clearConsoleMsg, desktop.consoleMessageTimeout * 1000); }
555 + } else {
556 + p11clearConsoleMsg();
557 + }
558 + }
559 + desktop.onMetadataChange = function (metadata) { updateMetadata(desktop, 'deskmetadata'); }
560 + desktop.m.CompressionLevel = desktopsettings.quality; // Number from 1 to 100. 50 or less is best.
561 + desktop.m.ScalingLevel = desktopsettings.scaling;
562 + if (desktopsettings.framerate) { desktop.m.FrameRateTimer = desktopsettings.framerate; }
563 + desktop.m.onDisplayinfo = deskDisplayInfo;
564 + desktop.m.onScreenSizeChange = deskAdjust;
565 + desktop.Start(null);
566 + desktop.latency.callback = function (ms) { console.log('latency', ms); updateSessionTime(); };
567 + desktop.contype = 1;
568 + }
569 + } else {
570 + // Disconnect and clean up the remote desktop
571 + desktop.Stop();
572 + webRtcDesktopReset();
573 + desktop = null;
574 + //if (pluginHandler != null) { pluginHandler.callHook('onDesktopDisconnect'); }
575 + }
576 + }
577 +
578 + function updateMetadata(conn, elementid) {
579 + var str = '', viewerCount = 0;
580 + if (conn && (conn.State == 3)) {
581 + if (conn.metadata && conn.metadata.users) { for (var i in conn.metadata.users) { viewerCount += conn.metadata.users[i]; } }
582 + if (viewerCount > 1) { str = '<span onclick=showSessionMetadata(1) style=cursor:pointer>' + format(", {0} watching", viewerCount) + '</span>'; }
583 + }
584 + QH('deskmetadata', str);
585 + if ((conn == desktop) && (xxdialogTag == ('sessionMetadata1'))) { showSessionMetadata(1); }
586 + }
587 +
588 + function showSessionMetadata(cid) {
589 + if (xxdialogMode && (xxdialogTag != ('sessionMetadata' + cid))) return;
590 + if (xxdialogMode) { setDialogMode(0); }
591 + var conn = null;
592 + if (cid == 1) { conn = desktop; }
593 + if (conn && conn.metadata) {
594 + var x = '';
595 + if (conn.metadata.startTime) { x += addHtmlValue4("Start Time", printDateTime(new Date(conn.metadata.startTime))); }
596 + if (conn.metadata.users) {
597 + for (var i in conn.metadata.users) {
598 + var val = (conn.metadata.users[i] == 1) ? "1 connection" : format("{0} connections", conn.metadata.users[i]);
599 + var username = i.split('/')[2];
600 + if ((users != null) && (users[i] != null)) { username = users[i].name; }
601 + x += addHtmlValue4(format("User \"{0}\"", username), val);
602 + }
603 + }
604 + setDialogMode(2, "Session Information", 1, null, x, 'sessionMetadata' + cid);
605 + }
606 + }
607 +
608 + function p11clearConsoleMsg() { QH('p11DeskConsoleMsg', ''); QV('p11DeskConsoleMsg', false); if (p11DeskConsoleMsgTimer) { clearTimeout(p11DeskConsoleMsgTimer); p11DeskConsoleMsgTimer = null; } }
609 + function p12clearConsoleMsg() { QH('p12TermConsoleMsg', ''); QV('p12TermConsoleMsg', false); if (p12TermConsoleMsgTimer) { clearTimeout(p12TermConsoleMsgTimer); p12TermConsoleMsgTimer = null; } }
610 + function p13clearConsoleMsg() { QH('p13FilesConsoleMsg', ''); QV('p13FilesConsoleMsg', false); if (p13FilesConsoleMsgTimer) { clearTimeout(p13FilesConsoleMsgTimer); p13FilesConsoleMsgTimer = null; } }
611 +
612 + var webRtcDesktop = null;
613 + function webRtcDesktopReset() {
614 + if (webRtcDesktop == null) return;
615 + if (webRtcDesktop.softdesktop != null) { webRtcDesktop.softdesktop.Stop(); webRtcDesktop.softdesktop = null; }
616 + if (webRtcDesktop.webchannel != null) { try { webRtcDesktop.webchannel.close(); } catch (e) { } webRtcDesktop.webchannel = null; }
617 + if (webRtcDesktop.webrtc != null) { try { webRtcDesktop.webrtc.close(); } catch (e) { } webRtcDesktop.webrtc = null; }
618 + webRtcDesktop = null;
619 + // Switch back to hardware KVM
620 + if (desktop && desktop.m) {
621 + desktop.m.hold(false);
622 + Q('deskstatus').textContent = StatusStrs[desktop.State];
623 + }
624 + // ###BEGIN###{DesktopInbandFiles}
625 + /*
626 + p24files = null;
627 + p24downloadFileCancel() // If any downloads are in process, cancel them.
628 + p24uploadFileCancel(); // If any uploads are in process, cancel them.
629 + QV('go24', false); // Files
630 + if (currentView == 24) { go(14); }
631 + */
632 + // ###END###{DesktopInbandFiles}
633 + }
634 +
635 + function onDesktopStateChange(xdesktop, state) {
636 + var xstate = state;
637 + if ((xstate == 3) && (xdesktop.contype == 2)) { xstate++; }
638 + var str = StatusStrs[xstate];
639 + if ((desktop != null) && (desktop.webRtcActive == true)) { str += ", WebRTC"; }
640 + //if (desktop.m.stopInput == true) { str += ', Loopback'; }
641 + QH('deskstatus', str);
642 + switch (state) {
643 + case 0:
644 + // Stop recording
645 + if (desktop.m.recordedData != null) { deskRecordSession(); }
646 +
647 + // Disconnect and clean up the remote desktop
648 + desktop.Stop();
649 + desktop = null;
650 + QV('DeskFocus', false);
651 + QV('termdisplays', false);
652 + QV('deskRecordIcon', false);
653 + if (fullscreen == true) { deskToggleFull(); }
654 + webRtcDesktopReset();
655 + deskPreferedStickyDisplay = 0;
656 + break;
657 + case 2:
658 + break;
659 + case 3:
660 + if (desktop && (desktop.serverIsRecording == true)) { QV('deskRecordIcon', true); }
661 + desktop.startTime = new Date();
662 + if (updateSessionTimer == null) { updateSessionTimer = setInterval(updateSessionTime, 1000); }
663 + break;
664 + default:
665 + //console.log('Unknown onDesktopStateChange state', state);
666 + break;
667 + }
668 + updateDesktopButtons();
669 + deskAdjust();
670 + setTimeout(deskAdjust, 50);
671 + updateMetadata(desktop, 'deskmetadata');
672 + }
673 +
674 + function updateSessionTime() {
675 + // Desktop
676 + var latencyStr = '', seconds = 0;
677 + if (desktop && desktop.startTime) {
678 + if (desktop.latency && (desktop.latency.current >= 0)) { latencyStr = format('{0} ms, ', desktop.latency.current); }
679 + seconds = Math.floor((new Date() - desktop.startTime) / 1000);
680 + QH('DeskTimer', latencyStr + zeroPad(Math.floor(seconds / 3600), 2) + ':' + zeroPad((Math.floor(seconds / 60) % 60), 2) + ':' + zeroPad((seconds % 60), 2));
681 + } else {
682 + QH('DeskTimer', '');
683 + }
684 +
685 + if (desktop == null) { clearInterval(updateSessionTimer); updateSessionTimer = null; }
686 + }
687 +
688 + function showDesktopSettings() {
689 + if (xxdialogMode) return;
690 + applyDesktopSettings();
691 + updateDesktopButtons();
692 + setDialogMode(7, "Remote Desktop Settings", 3, showDesktopSettingsChanged);
693 + }
694 +
695 + function showDesktopSettingsChanged() {
696 + desktopsettings.encoding = d7desktopmode.value;
697 + desktopsettings.showfocus = d7showfocus.checked;
698 + desktopsettings.showmouse = d7showcursor.checked;
699 + desktopsettings.quality = d7bitmapquality.value;
700 + desktopsettings.scaling = d7bitmapscaling.value;
701 + desktopsettings.framerate = d7framelimiter.value;
702 + desktopsettings.swapmouse = d7deskSwapMouse.checked;
703 + desktopsettings.remotekeymap = d7deskRemoteKeyMap.checked;
704 + desktopsettings.localkeymap = d7localKeyMap.checked;
705 + localStorage.setItem('desktopsettings', JSON.stringify(desktopsettings));
706 + applyDesktopSettings();
707 + if (desktop) {
708 + if (desktop.contype == 1) {
709 + desktop.m.SwapMouse = desktopsettings.swapmouse;
710 + desktop.m.remoteKeyMap = desktopsettings.remotekeymap;
711 + if (desktop.State != 0) {
712 + desktop.m.SendCompressionLevel(1, desktopsettings.quality, desktopsettings.scaling, desktopsettings.framerate);
713 + }
714 + }
715 + if (desktop.contype == 2) {
716 + if (desktop.State != 0) { desktop.Stop(); setTimeout(function () { connectDesktop(null, 2); }, 50); }
717 + }
718 + }
719 + }
720 +
721 + function applyDesktopSettings() {
722 + var r = '', ops = (features2 & 1) ? [90, 80, 70, 60, 50, 40, 30, 20, 10, 5, 1] : [60, 50, 40, 30, 20, 10, 5, 1]
723 + for (var i in ops) { r += '<option value=' + ops[i] + '>' + ops[i] + '%</option>'; }
724 + QH('d7bitmapquality', r);
725 + d7desktopmode.value = desktopsettings.encoding;
726 + d7showfocus.checked = desktopsettings.showfocus;
727 + d7showcursor.checked = desktopsettings.showmouse;
728 + d7bitmapquality.value = 40; // Default value
729 + if (ops.indexOf(parseInt(desktopsettings.quality)) >= 0) { d7bitmapquality.value = desktopsettings.quality; }
730 + d7bitmapscaling.value = desktopsettings.scaling;
731 + if (desktopsettings.framerate) { d7framelimiter.value = desktopsettings.framerate; } else { d7framelimiter.value = 100; }
732 + if (desktopsettings.swapmouse != null) { d7deskSwapMouse.checked = desktopsettings.swapmouse; }
733 + if (desktopsettings.remotekeymap != null) { d7deskRemoteKeyMap.checked = desktopsettings.remotekeymap; }
734 + if (desktopsettings.localkeymap) { d7localKeyMap.checked = desktopsettings.localkeymap; }
735 + }
736 +
737 + // Enter browser fullscreen
738 + function enterBrowserFullscreen(elem) {
739 + if (elem.requestFullscreen) { elem.requestFullscreen(); }
740 + else if (elem.msRequestFullscreen) { elem.msRequestFullscreen(); }
741 + else if (elem.mozRequestFullScreen) { elem.mozRequestFullScreen(); }
742 + else if (elem.webkitRequestFullscreen) { elem.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); }
743 + }
744 +
745 + // Exit browser fullscreen
746 + function exitBrowserFullscreen() {
747 + if (document.exitFullscreen) { document.exitFullscreen(); }
748 + else if (document.msExitFullscreen) { document.msExitFullscreen(); }
749 + else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); }
750 + else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); }
751 + }
752 +
753 + // Return true if the browser is fullscreen. This is a delayed method that will return true/false late. Not very useful.
754 + function isBrowserFullscreen() {
755 + if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) { return false; } else { return true; }
756 + }
757 +
758 + var fullscreen = false;
759 + var browserfullscreen = false;
760 + function deskToggleFull(e) {
761 + var xtermActive = !((urlargs.xterm === 0) || ((terminal != null) && (xterm == null)));
762 + fullscreen = !fullscreen;
763 + if (fullscreen) {
764 + QC('body').add('fulldesk');
765 + QS('deskarea3x')['height'] = '100%';
766 + QS('deskarea3x')['max-height'] = '100%';
767 + if (xtermActive) {
768 + // XTerm terminal
769 + QS('termTable')['position'] = 'absolute';
770 + QS('termTable')['top'] = QS('termTable')['bottom'] = QS('termTable')['left'] = QS('termTable')['right'] = '0';
771 + } else {
772 + // Legacy terminal
773 + QS('termTable')['height'] = '100%';
774 + QS('termTable')['max-height'] = '100%';
775 + }
776 +
777 + // If shift is pressed, enter browser full screen.
778 + if (e.shiftKey == true) {
779 + enterBrowserFullscreen(Q('container'));
780 + browserfullscreen = true;
781 + }
782 + } else {
783 + QC('body').remove('fulldesk');
784 + var hide = urlargs.hide;
785 + if (footerBar == false) { hide |= 4; }
786 + var xh = (((hide & 1) ? 0 : 66) + ((hide & 2) ? 0 : 24) + ((hide & 4) ? 0 : 45) + ((hide & 8) ? 0 : 60)); // 0 to 195
787 + QS('deskarea3x')['height'] = 'calc(100vh - ' + (75 + xh) + 'px)';
788 + QS('deskarea3x')['max-height'] = 'calc(100vh - ' + (75 + xh) + 'px)';
789 + if (xtermActive) {
790 + // XTerm terminal
791 + QS('termTable')['position'] = null;
792 + QS('termTable')['top'] = QS('termTable')['bottom'] = QS('termTable')['left'] = QS('termTable')['right'] = null;
793 + } else {
794 + // Legacy terminal
795 + QS('termTable')['height'] = 'calc(100vh - ' + (75 + xh) + 'px)';
796 + QS('termTable')['max-height'] = 'calc(100vh - ' + (75 + xh) + 'px)';
797 + }
798 + if (browserfullscreen == true) { exitBrowserFullscreen(); browserfullscreen = false; }
799 + }
800 + deskAdjust();
801 + updateDesktopButtons();
802 + adjustPanels();
803 + //setTimeout(adjustPanels, 10);
804 + //setTimeout(function() { xtermfit.fit(); }, 10);
805 + if (xterm != null) { if (xxcurrentView == 12) { xtermfit.fit(); xterm.focus(); } }
806 + }
807 +
808 + function mdeskAdjust(mod, sw, sh, cv) {
809 + if (!mod || !sw || !sh || !cv) return;
810 +
811 + // Check if we are in single desktop mode
812 + if (cv.id == 'Desk') { deskAdjust(); return; }
813 +
814 + // Figure out and adjust the size to fill the width of the div
815 + var vsize = [{ x: 180, y: 101 }, { x: 302, y: 169 }, { x: 454, y: 255 }][Q('sizeselect').selectedIndex];
816 + var realw = vsize.x + 2, tw = Q('xdevices').clientWidth - 30, xw = Math.floor(tw / realw);
817 + xw = realw + Math.floor((tw - (xw * realw)) / xw);
818 + vsize.y = vsize.y * (xw / vsize.x);
819 + vsize.x = xw;
820 + var mh = vsize.y, mw = vsize.x;
821 + if (mod.State != 0) { mh = vsize.y; mw = (sw / sh) * vsize.y; }
822 + QS(cv.id)['max-height'] = mh + 'px';
823 + QS(cv.id)['max-width'] = mw + 'px';
824 + QS(cv.id)['margin-top'] = '0';
825 + QS(cv.id)['margin-bottom'] = '0';
826 + }
827 +
828 + // Remote desktop special key combos for Windows
829 + function deskSendKeys() {
830 + if (xxdialogMode || desktop == null || desktop.State != 3) return;
831 + var ks = Q('deskkeys').value;
832 + if (ks == 0) { // WIN+Down arrow
833 + if (desktop.contype == 2) {
834 + desktop.m.sendkey([[0xffe7, 1], [0xff54, 1], [0xff54, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, Down arrow press, Down arrow release, Meta-left release
835 + } else {
836 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 40], [desktop.m.KeyAction.UP, 40], [desktop.m.KeyAction.EXUP, 0x5B]]); // Agent: L-Winkey press, Down arrow press, Down arrow release, L-Winkey release
837 + }
838 + } else if (ks == 1) { // WIN+Up arrow
839 + if (desktop.contype == 2) {
840 + desktop.m.sendkey([[0xffe7, 1], [0xff52, 1], [0xff52, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, Up arrow press, Up arrow release, Meta-left release
841 + } else {
842 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 38], [desktop.m.KeyAction.UP, 38], [desktop.m.KeyAction.EXUP, 0x5B]]); // MeshAgent: L-Winkey press, Up arrow press, Up arrow release, L-Winkey release
843 + }
844 + } else if (ks == 2) { // WIN+L arrow
845 + if (desktop.contype == 2) {
846 + desktop.m.sendkey([[0xffe7, 1], [0x6c, 1], [0x6c, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, 'l' press, 'l' release, Meta-left release
847 + } else {
848 + desktop.sendCtrlMsg('{"action":"lock"}');
849 + //desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,76],[desktop.m.KeyAction.UP,76],[desktop.m.KeyAction.EXUP,0x5B]]); // MeshAgent: L-Winkey press, 'L' press, 'L' release, L-Winkey release
850 + //desktop.m.SendKeyMsgKC(desktop.m.KeyAction.EXDOWN, 0x5B);
851 + //desktop.m.SendKeyMsgKC(desktop.m.KeyAction.DOWN, 76);
852 + //desktop.m.SendKeyMsgKC(desktop.m.KeyAction.UP, 76);
853 + //desktop.m.SendKeyMsgKC(desktop.m.KeyAction.EXUP, 0x5B);
854 + }
855 + } else if (ks == 3) { // WIN+M arrow
856 + if (desktop.contype == 2) {
857 + desktop.m.sendkey([[0xffe7, 1], [0x6d, 1], [0x6d, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, 'm' press, 'm' release, Meta-left release
858 + } else {
859 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 77], [desktop.m.KeyAction.UP, 77], [desktop.m.KeyAction.EXUP, 0x5B]]); // MeshAgent: L-Winkey press, 'M' press, 'M' release, L-Winkey release
860 + }
861 + } else if (ks == 4) { // Shift+WIN+M arrow
862 + if (desktop.contype == 2) {
863 + desktop.m.sendkey([[0xffe1, 1], [0xffe7, 1], [0x6d, 1], [0x6d, 0], [0xffe7, 0], [0xffe1, 0]]); // Intel AMT: Shift-left down, Meta-left down, 'm' press, 'm' release, Meta-left release, Shift-left release
864 + } else {
865 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.DOWN, 16], [desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 77], [desktop.m.KeyAction.UP, 77], [desktop.m.KeyAction.EXUP, 0x5B], [desktop.m.KeyAction.UP, 16]]); // MeshAgent: L-shift press, L-Winkey press, 'M' press, 'M' release, L-Winkey release, L-shift release
866 + }
867 + } else if (ks == 5) { // WIN
868 + if (desktop.contype == 2) {
869 + desktop.m.sendkey([[0xffe7, 1], [0xffe7, 0]]); // Intel AMT: Meta-left down, Meta-left release
870 + } else {
871 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.EXUP, 0x5B]]); // MeshAgent: L-Winkey press, L-Winkey release
872 + }
873 + } else if (ks == 6) { // WIN+R
874 + if (desktop.contype == 2) {
875 + desktop.m.sendkey([[0xffe7, 1], [0x72, 1], [0x72, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, 'r' press, 'r' release, Meta-left release
876 + } else {
877 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 82], [desktop.m.KeyAction.UP, 82], [desktop.m.KeyAction.EXUP, 0x5B]]); // MeshAgent: L-Winkey press, 'R' press, 'R' release, L-Winkey release
878 + }
879 + } else if (ks == 7) { // ALT-F4
880 + if (desktop.contype == 2) {
881 + desktop.m.sendkey([[0xffe9, 1], [0xffc1, 1], [0xffc1, 0], [0xffe9, 0]]); // Intel AMT: Alt down, 'F4' press, 'F4' release, Alt release
882 + } else {
883 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 18], [desktop.m.KeyAction.DOWN, 115], [desktop.m.KeyAction.UP, 115], [desktop.m.KeyAction.EXUP, 18]]); // MeshAgent: Alt press, 'F4' press, 'F4' release, Alt release
884 + }
885 + } else if (ks == 8) { // CTRL-W
886 + if (desktop.contype == 2) {
887 + desktop.m.sendkey([[0xffe3, 1], [0x77, 1], [0x77, 0], [0xffe3, 0]]); // Intel AMT: Ctrl down, 'w' press, 'w' release, Ctrl release
888 + } else {
889 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 17], [desktop.m.KeyAction.DOWN, 87], [desktop.m.KeyAction.UP, 87], [desktop.m.KeyAction.EXUP, 17]]); // MeshAgent: Ctrl press, 'W' press, 'W' release, Ctrl release
890 + }
891 + } else if (ks == 9) { // ALT-TAB
892 + if (desktop.contype == 2) {
893 + desktop.m.sendkey([[0xffe9, 1], [0xff09, 1], [0xff09, 0], [0xffe9, 0]]); // Intel AMT: Alt down, 'TAB' press, 'TAB' release, Alt release
894 + } else {
895 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 18], [desktop.m.KeyAction.DOWN, 9], [desktop.m.KeyAction.UP, 9], [desktop.m.KeyAction.EXUP, 18]]); // MeshAgent: Alt press, 'TAB' press, 'TAB' release, Alt release
896 + }
897 + } else if (ks == 10) { // CTRL-ALT-DEL
898 + desktop.m.sendcad();
899 + } else if (ks == 11) { // WIN-LEFT
900 + if (desktop.contype == 2) {
901 + desktop.m.sendkey([[0xffe7, 1], [0xff51, 1], [0xff51, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, Left arrow press, Left arrow release, Meta-left release
902 + } else {
903 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 37], [desktop.m.KeyAction.UP, 37], [desktop.m.KeyAction.EXUP, 0x5B]]);
904 + }
905 + } else if (ks == 12) { // WIN-RIGHT
906 + if (desktop.contype == 2) {
907 + desktop.m.sendkey([[0xffe7, 1], [0xff53, 1], [0xff53, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, Right arrow press, Right arrow release, Meta-left release
908 + } else {
909 + desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 39], [desktop.m.KeyAction.UP, 39], [desktop.m.KeyAction.EXUP, 0x5B]]);
910 + }
911 + }
912 + }
913 +
914 + function ondockeypress(e) {
915 + setSessionActivity();
916 + if (!xxdialogMode && desktop && isInputAllowed()) {
917 + // Check what keys we are allows to send
918 + /*
919 + if (currentNode != null) {
920 + var meshrights = GetNodeRights(currentNode);
921 + var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
922 + if (inputAllowed == false) return false;
923 + var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
924 + if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
925 + }
926 + */
927 + return desktop.m.handleKeys(e);
928 + }
929 + }
930 +
931 + function ondockeydown(e) {
932 + setSessionActivity();
933 + if (!xxdialogMode && desktop && isInputAllowed()) {
934 + // Check what keys we are allows to send
935 + /*
936 + if (currentNode != null) {
937 + var meshrights = GetNodeRights(currentNode);
938 + var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
939 + if (inputAllowed == false) return false;
940 + var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
941 + if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
942 + }
943 + */
944 + return desktop.m.handleKeyDown(e);
945 + }
946 + }
947 +
948 + function ondockeyup(e) {
949 + setSessionActivity();
950 + if (!xxdialogMode && desktop && isInputAllowed()) {
951 + // Check what keys we are allows to send
952 + /*
953 + if (currentNode != null) {
954 + var meshrights = GetNodeRights(currentNode);
955 + var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
956 + if (inputAllowed == false) return false;
957 + var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
958 + if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
959 + }
960 + */
961 + return desktop.m.handleKeyUp(e);
962 + }
963 + }
964 +
965 + // Remote desktop typing
966 + function showDeskType() {
967 + if (xxdialogMode || desktop == null || desktop.State != 3) return;
968 + Q('DeskType').blur();
969 + var x = '<div>' + "Enter text and click OK to remotely type it using a US english keyboard. Make sure to place the remote cursor at the correct position before proceeding." + '<div>';
970 + x += '<textarea id=d2typeText style="margin-top:5px;width:100%;height:184px;resize:none" maxlength=2000></textarea>';
971 + setDialogMode(2, "Remote Keyboard Entry", 3, showDeskTypeEx, x);
972 + Q('d2typeText').focus();
973 + }
974 +
975 + var AmtDeskTypeTimer = null;
976 + var AmtDeskTypeContent = null;
977 + var DeskTypeTranslate = { 39: 222, 42: 106, 43: 107, 44: 188, 45: 189, 46: 190, 47: 191, 59: 186, 61: 187, 91: 219, 92: 220, 93: 221, 96: 192, 191: 111 };
978 + var DeskTypeShiftTranslate = { 33: 49, 34: 222, 35: 51, 36: 52, 37: 53, 38: 55, 40: 57, 41: 48, 58: 186, 60: 188, 62: 190, 63: 191, 64: 50, 94: 54, 95: 189, 106: 56, 107: 187, 123: 219, 124: 220, 125: 221, 126: 192 };
979 + function showDeskTypeEx() {
980 + var txt = Q('d2typeText').value, ltxt = Q('d2typeText').value.toUpperCase(), x = [], shift = false;
981 + if (desktop.contype == 2) {
982 + // Intel AMT
983 + for (var i in txt) { var a = txt.charCodeAt(i); x.push([a, 1], [a, 0]); }
984 + AmtDeskTypeContent = x;
985 + AmtDeskTypeTimer = setInterval(function () {
986 + var key = AmtDeskTypeContent.shift();
987 + if (desktop) { desktop.m.sendkey(key[0], key[1]); }
988 + if ((desktop == null) || (AmtDeskTypeContent.length == 0)) { clearInterval(AmtDeskTypeTimer); AmtDeskTypeContent = null; }
989 + }, 10);
990 + } else {
991 + // MeshAgent
992 + for (var i in txt) {
993 + var a = txt.charCodeAt(i), b = ltxt.charCodeAt(i);
994 + if (((a >= 65) && (a <= 90)) || ((a >= 97) && (a <= 122))) {
995 + if ((a == b) && (shift == false)) { x.push([desktop.m.KeyAction.DOWN, 16]); shift = true; } // LShift down
996 + if ((a != b) && (shift == true)) { x.push([desktop.m.KeyAction.UP, 16]); shift = false; } // LShift up
997 + } else if ((a >= 48) && (a <= 57)) {
998 + if (shift == true) { x.push([desktop.m.KeyAction.UP, 16]); shift = false; } // Shift up
999 + } else if (DeskTypeTranslate[a]) {
1000 + if (shift == true) { x.push([desktop.m.KeyAction.UP, 16]); shift = false; } // Shift up
1001 + b = DeskTypeTranslate[a];
1002 + } else if (DeskTypeShiftTranslate[a]) {
1003 + if (shift == false) { x.push([desktop.m.KeyAction.DOWN, 16]); shift = true; } // LShift down
1004 + b = DeskTypeShiftTranslate[a];
1005 + }
1006 + x.push([desktop.m.KeyAction.DOWN, b], [desktop.m.KeyAction.UP, b]);
1007 + }
1008 + if (shift == true) { x.push([desktop.m.KeyAction.UP, 16]); shift = false; } // Shift up
1009 + desktop.m.SendKeyMsgKC(x);
1010 + }
1011 + }
1012 +
1013 + /*
1014 + // Show clipboard dialog
1015 + function showDeskClip() {
1016 + if (xxdialogMode || desktop == null || desktop.State != 3) return;
1017 + Q('DeskClip').blur();
1018 + var x = '';
1019 + x += '<input id=dlgClipGet type=button value="Get Clipboard" style=width:120px onclick=showDeskClipGet()>';
1020 + x += '<input id=dlgClipSet type=button value="Set Clipboard" style=width:120px onclick=showDeskClipSet()>';
1021 + x += '<div id=dlgClipStatus style="display:inline-block;margin-left:8px" ></div>';
1022 + x += '<textarea id=d2clipText style="width:100%;height:184px;resize:none" maxlength=65535></textarea>';
1023 + x += '<input type=button value="Close" style=width:80px;float:right onclick=dialogclose(0)><div style=height:26px;margin-top:3px><span id=linuxClipWarn style=display:none>' + "Remote clipboard is valid for 60 seconds." + '</span>&nbsp;</div><div></div>';
1024 + setDialogMode(2, "Remote Clipboard", 8, null, x, 'clipboard');
1025 + Q('d2clipText').focus();
1026 + }
1027 +
1028 + function showDeskClipGet() {
1029 + if (desktop == null || desktop.State != 3) return;
1030 + meshserver.send({ action: 'msg', type: 'getclip', nodeid: currentNode._id });
1031 + }
1032 +
1033 + function showDeskClipSet() {
1034 + if (desktop == null || desktop.State != 3) return;
1035 + meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: Q('d2clipText').value });
1036 + //QV('linuxClipWarn', currentNode && currentNode.agent && (currentNode.agent.id > 4) && (currentNode.agent.id != 21) && (currentNode.agent.id != 22));
1037 + }
1038 + */
1039 +
1040 + // Send CTRL-ALT-DEL
1041 + function sendCAD() {
1042 + if (xxdialogMode || desktop == null || desktop.State != 3) return;
1043 + desktop.m.sendcad();
1044 + }
1045 +
1046 + /*
1047 + // Show process dialogs
1048 + function toggleDeskTools() {
1049 + if (xxdialogMode) return;
1050 + if (QS('DeskTools').display == 'none') {
1051 + QV('DeskTools', true);
1052 + Q('DeskTools').nodeid = currentNode._id;
1053 + QH('DeskToolsProcesses', '');
1054 + QH('DeskToolsServices', '');
1055 + QV('deskToolsTopTabService', false);
1056 + changeDeskToolTab(0)
1057 + refreshDeskTools(0);
1058 + refreshDeskTools(1);
1059 + } else {
1060 + QV('DeskTools', false);
1061 + }
1062 + }
1063 +
1064 + var deskToolTabSelection = 0;
1065 + function changeDeskToolTab(tabnum) {
1066 + deskToolTabSelection = tabnum;
1067 + QV('DeskToolsProcessTab', tabnum == 0);
1068 + QV('DeskToolsServiceTab', tabnum == 1);
1069 + QS('deskToolsTopTabProcess')['bottom'] = (tabnum == 0) ? '0px' : '3px';
1070 + QS('deskToolsTopTabService')['bottom'] = (tabnum == 1) ? '0px' : '3px';
1071 + QS('deskToolsTopTabProcess')['color'] = (tabnum == 0) ? 'black' : 'gray';
1072 + QS('deskToolsTopTabService')['color'] = (tabnum == 1) ? 'black' : 'gray';
1073 + }
1074 +
1075 + // Refresh all of the desktop tool panels
1076 + function refreshDeskTools(x) {
1077 + var sel = (x == null) ? deskToolTabSelection : x;
1078 + QV('DeskToolsRefreshButton', false);
1079 + setTimeout(refreshDeskToolsEx, 500);
1080 + if (sel == 0) meshserver.send({ action: 'msg', type: 'ps', nodeid: currentNode._id });
1081 + if (sel == 1) meshserver.send({ action: 'msg', type: 'services', nodeid: currentNode._id });
1082 + }
1083 + function refreshDeskToolsEx() { QV('DeskToolsRefreshButton', true); }
1084 + var deskTools = { sort: 1, ssort: 1, msg: null, smsg: null };
1085 + function sortProcess(sort) { deskTools.sort = sort; showDeskToolsProcesses(deskTools.msg); }
1086 + function sortService(sort) { deskTools.ssort = sort; showDeskToolsServices(deskTools.smsg); }
1087 + function sortProcessPid(a, b) { if (a.p > b.p) return 1; if (a.p < b.p) return (-1); return sortProcessName(a, b); }
1088 + function sortProcessName(a, b) { if (a.d > b.d) return 1; if (a.d < b.d) return (-1); return 0; }
1089 + function showDeskToolsProcesses(message) {
1090 + deskTools.msg = message;
1091 + if (message == null) { QH('DeskToolsProcesses', ''); return; }
1092 + if (Q('DeskTools').nodeid != message.nodeid) return;
1093 + var p = [], processes = null;
1094 + try { processes = JSON.parse(message.value); } catch (e) { }
1095 + if (processes != null) {
1096 + for (var pid in processes) { p.push({ p: parseInt(pid), c: processes[pid].cmd, d: processes[pid].cmd.toLowerCase(), u: processes[pid].user }); }
1097 + if (deskTools.sort == 0) { p.sort(sortProcessPid); } else if (deskTools.sort == 1) { p.sort(sortProcessName); }
1098 + var x = '';
1099 + for (var i in p) {
1100 + if (p[i].p != 0) {
1101 + var c = p[i].c;
1102 + if (c.length > 30) { c = '<span title="' + EscapeHtml(c) + '">' + EscapeHtml(c.substring(0, 30)) + '...</span>' } else { c = EscapeHtml(c); }
1103 + x += '<div class=deskToolsBar><div style=width:50px;float:left;text-align:right;padding-right:5px>' + EscapeHtml(p[i].p) + '</div><a href=# style=float:right;padding-right:5px;cursor:pointer title="' + "Stop process" + '" onclick=\'return stopProcess(' + EscapeHtml(p[i].p) + ',"' + EscapeHtml(p[i].c) + '")\'><img width=10 height=10 src="images/trash.png"></a><div style=float:right;padding-right:5px>' + (p[i].u ? EscapeHtml(p[i].u) : '') + '</div><div>' + c + '</div></div>';
1104 + }
1105 + }
1106 + QH('DeskToolsProcesses', x);
1107 + }
1108 + }
1109 + function showDeskToolsServices(message) {
1110 + deskTools.smsg = message;
1111 + if (message == null) { QH('DeskToolsProcesses', ''); return; }
1112 + if (Q('DeskTools').nodeid != message.nodeid) return;
1113 + QV('deskToolsTopTabService', true);
1114 + var s = [], services = null;
1115 + try { services = JSON.parse(message.value); } catch (e) { }
1116 + deskTools.services = services;
1117 + if (services != null) {
1118 + for (var i in services) {
1119 + if (services[i].status) {
1120 + // Windows
1121 + s.push({ p: capitalizeFirstLetter(services[i].status.state.toLowerCase()), d: services[i].displayName, i: i });
1122 + } else if (services[i].serviceType) {
1123 + // Linux (TODO: This the service status is not displayed, not sure start/stop/restart will work).
1124 + s.push({ p: services[i].serviceType, d: services[i].name, i: i });
1125 + }
1126 + }
1127 + if (deskTools.ssort == 0) { s.sort(sortProcessPid); } else if (deskTools.ssort == 1) { s.sort(sortProcessName); }
1128 + var x = '';
1129 + for (var i in s) {
1130 + if (s[i].p != 0) {
1131 + var c = s[i].d;
1132 + if (c.length > 30) { c = '<span title="' + c + '">' + c.substring(0, 30) + '...</span>' } else { c = EscapeHtml(c); }
1133 + x += '<div onclick=showServiceDetailsDialog(' + s[i].i + ') class=deskToolsBar><div style=width:70px;float:left;padding-right:5px>' + EscapeHtml(s[i].p) + '</div><div>' + c + '</div></div>';
1134 + }
1135 + }
1136 + QH('DeskToolsServices', x);
1137 + }
1138 + }
1139 +
1140 + function showServiceDetailsDialog(index) {
1141 + if (xxdialogMode) return;
1142 + var service = deskTools.services[index];
1143 + if (service != null) {
1144 + var x = '';
1145 + if (service.name) { x += addHtmlValue("Name", service.name); }
1146 + if (service.displayName) { x += addHtmlValue("Display name", service.displayName); }
1147 + if (service.status) {
1148 + if (service.status.state) { x += addHtmlValue("State", capitalizeFirstLetter(service.status.state.toLowerCase())); }
1149 + if (service.status.pid) { x += addHtmlValue("PID", service.status.pid); }
1150 + var serviceTypes = [];
1151 + if (service.status.isFileSystemDriver === true) { serviceTypes.push("FileSystemDriver"); }
1152 + if (service.status.isInteractive === true) { serviceTypes.push("Interactive"); }
1153 + if (service.status.isKernelDriver === true) { serviceTypes.push("KernelDriver"); }
1154 + if (service.status.isOwnProcess === true) { serviceTypes.push("OwnProcess"); }
1155 + if (service.status.isSharedProcess === true) { serviceTypes.push("SharedProcess"); }
1156 + if (serviceTypes.length > 0) { x += addHtmlValue("Type", serviceTypes.join(', ')); }
1157 + }
1158 + x += '<br/><div style=float:right;margin-bottom:12px><input type=button value="' + "Close" + '" onclick=showServiceDetailsDialogEx(0,' + index + ')></div><div style=margin-bottom:12px><input type=button value="' + "Start" + '" onclick=showServiceDetailsDialogEx(1,' + index + ')><input type=button value="' + "Stop" + '" onclick=showServiceDetailsDialogEx(2,' + index + ')><input type=button value="' + "Restart" + '" onclick=showServiceDetailsDialogEx(3,' + index + ')></div>';
1159 + setDialogMode(2, "Service Details", 8, null, x, name);
1160 + }
1161 + }
1162 +
1163 + function showServiceDetailsDialogEx(action, index) {
1164 + setDialogMode(0);
1165 + if (action == 0) return;
1166 + var service = deskTools.services[index];
1167 + if (service != null) {
1168 + if (action == 1) { meshserver.send({ action: 'msg', type: 'serviceStart', nodeid: currentNode._id, serviceName: service.name }); }
1169 + if (action == 2) { meshserver.send({ action: 'msg', type: 'serviceStop', nodeid: currentNode._id, serviceName: service.name }); }
1170 + if (action == 3) { meshserver.send({ action: 'msg', type: 'serviceRestart', nodeid: currentNode._id, serviceName: service.name }); }
1171 + setTimeout(function () { refreshDeskTools(1) }, 1000);
1172 + }
1173 + }
1174 + */
1175 +
1176 + // Toggle mouse and keyboard input
1177 + function toggleKvmControl() { QS('DeskControlSpan').color = Q('DeskControl').checked ? null : 'red'; }
1178 +
1179 + // Save the desktop image to file
1180 + function deskSaveImage() {
1181 + if (xxdialogMode || desktop == null || desktop.State != 3) return;
1182 + var d = new Date(), n = "Desktop" + '-' + d.getFullYear() + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2) + '-' + ('0' + d.getHours()).slice(-2) + '-' + ('0' + d.getMinutes()).slice(-2);
1183 + Q('Desk')['toBlob'](function (blob) { saveAs(blob, n + '.png'); });
1184 + }
1185 +
1186 + function deskDisplayInfo(sender, displays, selDisplay) {
1187 + var displayCount = 0, displaySelector = '';
1188 + for (var i in displays) {
1189 + displayCount++;
1190 + displaySelector += '<option' + ((selDisplay == i) ? ' selected' : '') + ' value=' + i + '>' + displays[i] + '</option>';
1191 + if ((deskPreferedStickyDisplay == i) && (selDisplay != deskPreferedStickyDisplay)) { desktop.m.SetDisplay(i); }
1192 + deskPreferedStickyDisplay = -1;
1193 + }
1194 + QH('termdisplays', displaySelector);
1195 + QV('termdisplays', displayCount > 1);
1196 + }
1197 +
1198 + function deskGetDisplayNumbers(e) { desktop.m.GetDisplayNumbers(); }
1199 + var deskPreferedStickyDisplay = -1;
1200 + function deskSetDisplay(e) { desktop.m.SetDisplay(deskPreferedStickyDisplay = parseInt(Q('termdisplays').value)); Q('termdisplays').blur(); }
1201 +
1202 + // Double click detection. This is important for macOS.
1203 + var dblClickDetectArgs = { t: 0, x: 0, y: 0 };
1204 + function dblClickDetect(e) {
1205 + if (e.buttons != 1) return;
1206 + var t = Date.now();
1207 + if (((t - dblClickDetectArgs.t) < 250) && (Math.abs(e.clientX - dblClickDetectArgs.x) < 2) && (Math.abs(e.clientY - dblClickDetectArgs.y) < 2)) {
1208 + if (!xxdialogMode && desktop != null && isInputAllowed()) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousedblclick(e); desktop.m.sendKeepAlive(); } else { desktop.m.mousedblclick(e); } }
1209 + }
1210 + dblClickDetectArgs.t = t;
1211 + dblClickDetectArgs.x = e.clientX;
1212 + dblClickDetectArgs.y = e.clientY;
1213 + }
1214 +
1215 + function dmousedown(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && isInputAllowed()) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousedown(e); desktop.m.sendKeepAlive(); } else { desktop.m.mousedown(e); } } dblClickDetect(e); }
1216 + function dmouseup(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && isInputAllowed()) if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mouseup(e); desktop.m.sendKeepAlive(); } else { desktop.m.mouseup(e); } }
1217 + function dmousemove(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && isInputAllowed()) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousemove(e); desktop.m.sendKeepAlive(); } else { desktop.m.mousemove(e); } } }
1218 + function dmousewheel(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && isInputAllowed()) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousewheel(e); desktop.m.sendKeepAlive(); } else { if (desktop.m.mousewheel) { desktop.m.mousewheel(e); } } haltEvent(e); return true; } return false; }
1219 +
1220 + //
1221 + // Terminal
1222 + //
1223 +
1224 + function setupTerminal() {
1225 + // Setup the terminal
1226 + if (terminal != null) { terminal.Stop(); terminal = null; }
1227 + updateTerminalButtons();
1228 +
1229 + // Terminal special keys
1230 + var x = '';
1231 + for (var c = 1; c < 27; c++) x += '<option value=\'' + c + '\'>' + "Ctrl" + '-' + String.fromCharCode(64 + c) + ' (' + c + ')</option>';
1232 + QH('specialkeylist', x);
1233 + }
1234 +
1235 + // Show and enable the right buttons
1236 + function updateTerminalButtons() {
1237 + var termState = ((terminal != null) && (terminal.state != 0));
1238 +
1239 + // Show the right buttons
1240 + QV('disconnectbutton2span', termState == true);
1241 + QV('connectbutton2span', termState == false);
1242 + //QV('terminalSizeDropDown', termState == false);
1243 +
1244 + // Enable buttons
1245 + QE('connectbutton2', true);
1246 +
1247 + // Key buttons
1248 + QE('ctrlcbutton', termState);
1249 + QE('ctrlxbutton', termState);
1250 + QE('escbutton', termState);
1251 + QE('bsbutton', termState);
1252 + QE('pastebutton', termState);
1253 + QE('specialkeylist', termState);
1254 + QE('specialkeylistinput', termState);
1255 +
1256 + // Terminal settings
1257 + QV('terminalSettingsButtons', (terminal) && (terminal.contype == 2));
1258 + if (terminal) {
1259 + Q('id_ttypebutton').value = terminalEmulations[terminal.m.terminalEmulation];
1260 + Q('id_tfxkeysbutton').value = fxEmulations[terminal.m.fxEmulation];
1261 + Q('id_tcrbutton').value = (terminal.m.lineFeed == '\r\n') ? "CR+LF" : "LF";
1262 + }
1263 +
1264 + // Display extra buttons on legacy terminal
1265 + var xtermActive = true;
1266 + QV('termarea3xdiv', xtermActive);
1267 + QV('bsbutton', !xtermActive);
1268 + QV('pastebutton', !xtermActive);
1269 + QV('devListToolbarViewIcons2', xtermActive);
1270 + QE('termSizeList', terminal == null);
1271 + }
1272 +
1273 + // Called when the terminal state changes
1274 + function onTerminalStateChange(xterminal, state) {
1275 + var xstate = state;
1276 + if ((xstate == 3) && (xterminal.contype == 2)) { xstate++; }
1277 + var str = StatusStrs[xstate];
1278 + if (terminal.webRtcActive == true) { str += ", WebRTC"; }
1279 + QH('termstatus', str);
1280 + switch (state) {
1281 + case 0:
1282 + // Disconnected, clear the terminal
1283 + QH('termtitle', '');
1284 + QV('termRecordIcon', false);
1285 + if (xterm == null) {
1286 + xterminal.m.TermResetScreen();
1287 + xterminal.m.TermDraw();
1288 + } else {
1289 + xterm.dispose();
1290 + xterm = xtermfit = null;
1291 + }
1292 + if (terminal != null) { terminal.Stop(); terminal = null; }
1293 + break;
1294 + case 3:
1295 + if (xterminal && (xterminal.serverIsRecording == true)) { QV('termRecordIcon', true); }
1296 + terminal.startTime = new Date();
1297 + if (updateSessionTimer == null) { updateSessionTimer = setInterval(updateSessionTime, 1000); }
1298 + if (xterm != null) { xterm.focus(); }
1299 + break;
1300 + default:
1301 + //console.log('Unhandled onTerminalStateChange state', state);
1302 + break;
1303 + }
1304 + updateTerminalButtons();
1305 + }
1306 +
1307 + function updateSessionTime() {
1308 + // Terminal
1309 + var latencyStr = '', seconds = 0;
1310 + if (terminal && terminal.startTime) {
1311 + if (terminal.latency && (terminal.latency.current >= 0)) { latencyStr = format('{0} ms, ', terminal.latency.current); }
1312 + seconds = Math.floor((new Date() - terminal.startTime) / 1000);
1313 + QH('TermTimer', latencyStr + zeroPad(Math.floor(seconds / 3600), 2) + ':' + zeroPad((Math.floor(seconds / 60) % 60), 2) + ':' + zeroPad((seconds % 60), 2));
1314 + } else {
1315 + QH('TermTimer', '');
1316 + }
1317 +
1318 + if (terminal == null) { clearInterval(updateSessionTimer); updateSessionTimer = null; }
1319 + }
1320 +
1321 + // DEBUG
1322 + var autoConnectTerminalTimer = null;
1323 + function autoConnectTerminal(e) { if (autoConnectTerminalTimer == null) { autoConnectTerminalTimer = setInterval(connectTerminal, 100); } else { clearInterval(autoConnectTerminalTimer); autoConnectTerminalTimer = null; } }
1324 +
1325 + // Handles a tunnel to a remote shell
1326 + function CreateRemoteTunnel(onTunnelUpdate, options) {
1327 + var obj = { protocol: 1 };
1328 + if ((options != null) && (typeof options.protocol == 'number')) { obj.protocol = options.protocol; }
1329 + obj.onTunnelUpdate = onTunnelUpdate;
1330 + obj.xxStateChange = function (state) { }
1331 + obj.ProcessBinaryData = function (data) { obj.onTunnelUpdate(data); }
1332 + obj.ProcessData = function (data) { obj.onTunnelUpdate(data); }
1333 + obj.terminalEmulation = 1;
1334 + obj.fxEmulation = 0;
1335 + obj.lineFeed = '\r\n';
1336 + return obj;
1337 + }
1338 +
1339 + function tunnelUpdate(data) { if (typeof data == 'string') { xterm.writeUtf8(data); } else { xterm.writeUtf8(new Uint8Array(data)); } }
1340 +
1341 + // Send the new terminal size to the agent
1342 + function xTermSendResize() {
1343 + xtermResizeTimer = null;
1344 + if ((xterm != null) && (terminal != null) && (terminal.sendCtrlMsg != null)) { terminal.sendCtrlMsg(JSON.stringify({ ctrlChannel: '102938', type: 'termsize', cols: xterm.cols, rows: xterm.rows })); }
1345 + }
1346 +
1347 + // Used to translate incoming agent console messages
1348 + var agentConsoleMessages = ['', "Waiting for user to grant access...", "Denied", "Failed to start remote terminal session, {0} ({1})", "Timeout", "Received invalid network data"];
1349 + function formatAgentConsoleMessage(msg, msgid, msgargs) {
1350 + var r;
1351 + if (msgargs == null) { msgargs = []; }
1352 + while (msgargs.length < 3) { msgargs.push(''); } // We need to call the format function in a way that works with older browsers and minifier, can't use apply() or ...
1353 + if (msgid && (msgid < agentConsoleMessages.length)) { r = EscapeHtml(format(agentConsoleMessages[msgid], (msgargs[0]), (msgargs[1]), (msgargs[2]))); } else { r = EscapeHtml(msg); }
1354 + return r.split('\n').join('<br />') + '<br /><br />';
1355 + }
1356 +
1357 + function connectTerminal(e, contype, options) {
1358 + p12clearConsoleMsg();
1359 + if (!terminal) {
1360 + // Terminal setup
1361 + var termoptions = { protocol: ((options != null) && (typeof options.protocol == 'number')) ? options.protocol : 1 };
1362 + if (options && options.requireLogin) { termoptions.requireLogin = true; }
1363 + /*
1364 + if ([1, 2, 3, 4, 21, 22].indexOf(currentNode.agent.id) == -1) {
1365 + if (Q('termSizeList').value == 1) { termoptions.cols = 80; termoptions.rows = 25; termoptions.xterm = true; }
1366 + else if (Q('termSizeList').value == 2) { termoptions.cols = 100; termoptions.rows = 30; termoptions.xterm = true; }
1367 + else if (Q('termSizeList').value == 3) {
1368 + // TODO: Try to improve terminal auto-size.
1369 + termoptions.cols = Math.floor((Q('column_l').clientWidth - 60) / 10);
1370 + termoptions.rows = Math.floor((Q('column_l').clientHeight - 120) / 20);
1371 + termoptions.xterm = true;
1372 + }
1373 + }
1374 + */
1375 +
1376 + /*
1377 + // If shift is pressed
1378 + if ((e && (e.shiftKey == true))) {
1379 + if (currentNode.agent.id > 4) {
1380 + if (termoptions.protocol == 1) { termoptions.protocol = 7; } // Switch to user shell
1381 + } else {
1382 + if (termoptions.protocol == 1) { termoptions.protocol = 6; } // Switch to Powershell
1383 + }
1384 + }
1385 + */
1386 +
1387 + // Setup a mesh agent xterm terminal
1388 + QV('termarea3xdiv', true);
1389 +
1390 + // Setup the terminal with auto-fit
1391 + if (xterm != null) { xterm.dispose(); }
1392 + xtermfit = new FitAddon.FitAddon();
1393 + xterm = new Terminal();
1394 + if (xtermfit) { xterm.loadAddon(xtermfit); }
1395 + xterm.open(Q('termarea3xdiv')); // termarea3x
1396 + xterm.onData(function (data) { if (terminal != null) { terminal.sendText(data); } })
1397 + if (xtermfit) { xtermfit.fit(); }
1398 + xterm.onTitleChange(function (title) { QH('termtitle', ' - ' + EscapeHtml(title)); });
1399 + xterm.onResize(function (size) {
1400 + // Despam resize
1401 + if (xtermResizeTimer) clearTimeout(xtermResizeTimer);
1402 + xtermResizeTimer = setTimeout(xTermSendResize, 200);
1403 + });
1404 +
1405 + // Setup a terminal tunnel to the agent
1406 + terminal = CreateAgentRedirect(null, CreateRemoteTunnel(tunnelUpdate, options), serverPublicNamePort, authCookie, null, domainUrl);
1407 + terminal.debugmode = debugmode;
1408 + terminal.m.debugmode = debugmode;
1409 + terminal.options = { cols: xterm.cols, rows: xterm.rows };
1410 + if (options && options.requireLogin) { terminal.options.requireLogin = true; }
1411 + terminal.Start(null);
1412 + terminal.onStateChanged = onTerminalStateChange;
1413 + terminal.contype = 1;
1414 + terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
1415 + terminal.onConsoleMessageChange = function (server, msg) {
1416 + if (terminal.consoleMessage) {
1417 + Q('p12TermConsoleMsg').innerHTML += formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs);
1418 + QV('p12TermConsoleMsg', true);
1419 + if (p12TermConsoleMsgTimer != null) { clearTimeout(p12TermConsoleMsgTimer); }
1420 + if (terminal.consoleMessageTimeout) { p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, terminal.consoleMessageTimeout * 1000); }
1421 + } else {
1422 + p12clearConsoleMsg();
1423 + }
1424 + };
1425 + } else {
1426 + terminal.Stop();
1427 + terminal = null;
1428 + }
1429 + Q('connectbutton2').blur(); // Deselect the connect button so the button does not get key presses.
1430 + }
1431 +
1432 + var terminalEmulations = ["UTF8 Terminal", "Extended ASCII", "Intel ASCII"];
1433 + function termToggleType() {
1434 + if (!terminal || xxdialogMode) return;
1435 + terminal.m.terminalEmulation = (terminal.m.terminalEmulation + 1) % 3;
1436 + Q('id_ttypebutton').value = terminalEmulations[terminal.m.terminalEmulation];
1437 + Q('id_ttypebutton').blur(); // Deselect the connect button so the button does not get key presses.
1438 + }
1439 +
1440 + var fxEmulations = ["Intel (F10 = ESC+[OM)", "Alternate (F10 = ESC+0)", "VT100+ (F10 = ESC+[OY)"];
1441 + function termToggleFx() {
1442 + if (!terminal || xxdialogMode) return;
1443 + terminal.m.fxEmulation = (terminal.m.fxEmulation + 1) % 3;
1444 + Q('id_tfxkeysbutton').value = fxEmulations[terminal.m.fxEmulation];
1445 + Q('id_tfxkeysbutton').blur(); // Deselect the connect button so the button does not get key presses.
1446 + }
1447 +
1448 + function termToggleCr() {
1449 + if (!terminal || xxdialogMode) return;
1450 + if (terminal.m.lineFeed == '\n') { terminal.m.lineFeed = '\r\n'; } else { terminal.m.lineFeed = '\n'; }
1451 + Q('id_tcrbutton').value = (terminal.m.lineFeed == '\r\n') ? "CR+LF" : "LF";
1452 + }
1453 +
1454 + function termSendKey(key, id) {
1455 + if (!terminal || xxdialogMode) return;
1456 + if (xterm != null) {
1457 + if (terminal.sendText) {
1458 + // MeshAgent
1459 + terminal.sendText(String.fromCharCode(key));
1460 + } else {
1461 + // CIRA
1462 + terminal.send(String.fromCharCode(key));
1463 + }
1464 + xterm.focus();
1465 + } else if (terminal != null) {
1466 + terminal.m.TermSendKey(key);
1467 + Q(id).blur(); // Deselect the connect button so the button does not get key presses.
1468 + }
1469 + }
1470 +
1471 + function showTermPasteDialog() {
1472 + if (!terminal || xxdialogMode) return;
1473 + Q('pastebutton').blur();
1474 + setDialogMode(2, "Paste", 3, showTermPasteDialogEx, '<textarea id=d2pasteText style="width:100%;height:184px;resize:none"></textarea>');
1475 + Q('d2pasteText').focus();
1476 + }
1477 +
1478 + function showTermPasteDialogEx() {
1479 + if (!terminal) return;
1480 + terminal.m.TermSendKeys(Q('d2pasteText').value);
1481 + }
1482 +
1483 + // Send special key
1484 + function sendSpecialKey() {
1485 + if (xterm != null) {
1486 + terminal.sendText(String.fromCharCode(Q('specialkeylist').value));
1487 + xterm.focus();
1488 + } else if (terminal != null) {
1489 + terminal.m.TermSendKey(Q('specialkeylist').value);
1490 + Q('specialkeylist').blur();
1491 + Q('specialkeylistinput').blur();
1492 + }
1493 + }
1494 +
1495 + function p12clearConsoleMsg() { QH('p12TermConsoleMsg', ''); QV('p12TermConsoleMsg', false); if (p12TermConsoleMsgTimer) { clearTimeout(p12TermConsoleMsgTimer); p12TermConsoleMsgTimer = null; } }
1496 +
1497 +
1498 +
1499 + //
1500 + // Files
1501 + //
1502 +
1503 + function setupFiles() {
1504 + // Setup the files tab
1505 + if (files != null) { files.Stop(); files = null; }
1506 + }
1507 +
1508 + function onFilesStateChange(xfiles, state) {
1509 + p13Connect.value = (state == 0) ? "Connect" : "Disconnect";
1510 + var str = StatusStrs[state];
1511 + if (files.webRtcActive == true) { str += ", WebRTC"; }
1512 + Q('p13Status').textContent = str;
1513 + switch (state) {
1514 + case 0:
1515 + // Disconnected, clear the files
1516 + QH('p13files', '');
1517 + p13filetree = null;
1518 + p13filetreelocation = [];
1519 + QH('p13currentpath', '');
1520 + QE('p13FolderUp', false);
1521 + QV('filesRecordIcon', false);
1522 + p13setActions();
1523 + if (files != null) { files.Stop(); files = null; }
1524 + if (xxdialogTag == 'fileMsgDialog') { setDialogMode(0); }
1525 + break;
1526 + case 3:
1527 + p13targetpath = '';
1528 + if (files) {
1529 + files.sendText({ action: 'ls', reqid: 1, path: '' });
1530 + if (files.serverIsRecording == true) { QV('filesRecordIcon', true); }
1531 + }
1532 + break;
1533 + default:
1534 + //console.log('Unknown onFilesStateChange state', state);
1535 + break;
1536 + }
1537 + }
1538 +
1539 + function CreateRemoteFiles(onFileUpdate) {
1540 + var obj = { protocol: 5 };
1541 + obj.onFileUpdate = onFileUpdate;
1542 + obj.xxStateChange = function (state) { }
1543 + obj.ProcessData = function (data) { obj.onFileUpdate(data); }
1544 + return obj;
1545 + }
1546 +
1547 + function connectFiles(e) {
1548 + p13clearConsoleMsg();
1549 + if (!files) {
1550 + // Setup a mesh agent files
1551 + files = CreateAgentRedirect(null, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, null, domainUrl);
1552 + files.attemptWebRTC = attemptWebRTC;
1553 + files.onStateChanged = onFilesStateChange;
1554 + files.onConsoleMessageChange = function () {
1555 + if (files.consoleMessage) {
1556 + Q('p13FilesConsoleMsg').innerHTML += formatAgentConsoleMessage(files.consoleMessage, files.consoleMessageId, files.consoleMessageArgs);
1557 + QV('p13FilesConsoleMsg', true);
1558 + if (p13FilesConsoleMsgTimer != null) { clearTimeout(p13FilesConsoleMsgTimer); }
1559 + if (files.consoleMessageTimeout) { p13FilesConsoleMsgTimer = setTimeout(p13clearConsoleMsg, files.consoleMessageTimeout * 1000); }
1560 + } else {
1561 + p13clearConsoleMsg();
1562 + }
1563 + }
1564 + files.Start(null);
1565 + } else {
1566 + //QH('Term', '');
1567 + files.Stop();
1568 + files = null;
1569 + }
1570 + p13clipboard = p13clipboardFolder = null;
1571 + p13clipboardCut = 0;
1572 + p13updateClipview();
1573 + }
1574 +
1575 + var p13filetree = null;
1576 + var p13targetpath = null;
1577 + var p13filetreelocation = [];
1578 +
1579 + function p13fileOperationDialogEx(b) { if ((b == 0) && (files != null)) { files.sendText({ action: 'cancel' }); } }
1580 +
1581 + function p13gotFiles(data) {
1582 + if ((data.length > 0) && (data.charCodeAt(0) != 123)) { p13gotDownloadBinaryData(data); return; } // This is ok because 4 first bytes is a control value.
1583 + //console.log('p13gotFiles', data);
1584 + try { data = JSON.parse(decode_utf8(data)); } catch (ex) { data = JSON.parse(data); }
1585 + if (data.action == 'download') { p13gotDownloadCommand(data); return; }
1586 +
1587 + // Find file result
1588 + if (data.action == 'findfile') {
1589 + if (xxdialogTag == data.reqid) {
1590 + if (data.r == null) {
1591 + QE('d2findFilter', true);
1592 + QE('filefind_dlgOkButton', true);
1593 + xxdialogTag = null;
1594 + if (Q('d2findResults').innerHTML == '') { QH('d2findResults', '<div style=text-align:center;margin:10px><i>' + "No files found" + '</i></div>'); }
1595 + } else {
1596 + QA('d2findResults', '<div style=white-space:nowrap>' + EscapeHtml(data.r) + '</div>');
1597 + }
1598 + }
1599 + return;
1600 + }
1601 +
1602 + // Process file upload commands
1603 + if ((data.action != null) && (data.action.startsWith('upload'))) { p13gotUploadData(data); return; }
1604 +
1605 + // Display a dialog message
1606 + if (data.action == 'dialogmessage') {
1607 + if ((data.msg == null) && (xxdialogTag == 'fileMsgDialog')) {
1608 + setDialogMode(0); // Close the dialog box
1609 + } else if ((data.msg == 'zipping') && ((!xxdialogMode) || (xxdialogTag == 'fileMsgDialog'))) {
1610 + // Show the dialog box message
1611 + setDialogMode(2, "File Operation", 10, p13fileOperationDialogEx, '<div style=margin:10px>' + "Compressing files..." + '<div>', 'fileMsgDialog');
1612 + } else if ((data.msg == 'zippingFile') && ((!xxdialogMode) || (xxdialogTag == 'fileMsgDialog'))) {
1613 + // Show the dialog box message
1614 + setDialogMode(2, "File Operation", 10, p13fileOperationDialogEx, '<div style=margin:10px>' + EscapeHtml(data.file) + '<div><br /><progress value=' + EscapeHtml(data.progress) + ' style=width:100% max=100 />', 'fileMsgDialog');
1615 + }
1616 + return;
1617 + }
1618 +
1619 + // Refresh file folder
1620 + if (data.action == 'refresh') { p13folderup(9999); return; }
1621 +
1622 + if (data.path != null) {
1623 + data.path = data.path.replace(/\//g, '\\');
1624 + if ((p13filetree != null) && (data.path == p13filetree.path)) {
1625 + // This is an update to the same folder
1626 + var checkedNames = p13getCheckedNames();
1627 + p13filetree = data;
1628 + p13updateFiles(checkedNames);
1629 + } else {
1630 + // Make both paths use the same seperator not start with /
1631 + var x1 = data.path.replace(/\//g, '\\'), x2 = p13targetpath.replace(/\//g, '\\');
1632 + while ((x1.length > 0) && (x1[0] == '\\')) { x1 = x1.substring(1); }
1633 + while ((x2.length > 0) && (x2[0] == '\\')) { x2 = x2.substring(1); }
1634 + if ((x1 == x2) || ((data.path == '\\') && (p13targetpath == ''))) {
1635 + // This is a different folder
1636 + p13filetree = data;
1637 + p13updateFiles();
1638 + }
1639 + }
1640 + }
1641 + }
1642 +
1643 + function p13getCheckedNames() {
1644 + // Save all existing checked boxes
1645 + var checkedNames = [], checkboxes = document.getElementsByName('fd');
1646 + for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedNames.push(p13filetree.dir[checkboxes[i].value].n) }; }
1647 + return checkedNames;
1648 + }
1649 +
1650 + function p13updateFiles(checkedNames) {
1651 + var html1 = '', html2 = '', displayPath = '<a href=# style=cursor:pointer onclick="return p13folderup(0)">' + "Root" + '</a>', fullPath = 'Root';
1652 +
1653 + // Work on parsing the file path
1654 + var x = p13filetree.path.split('\\');
1655 + p13filetreelocation = [];
1656 + for (var i in x) { if (x[i] != '') { p13filetreelocation.push(x[i]); } } // Remove empty spaces
1657 + for (var i in p13filetreelocation) { displayPath += ' / <a href=# style=cursor:pointer onclick="return p13folderup(' + (parseInt(i) + 1) + ')">' + EscapeHtml(p13filetreelocation[i]) + '</a>' } // Setup the path we display
1658 + var newlinkpath = p13filetreelocation.join('/');
1659 +
1660 + // Sort the files
1661 + var filetreexx = p13sort_files(p13filetree.dir);
1662 +
1663 + // Display all files and folders at this location
1664 + for (var i in filetreexx) {
1665 + // Figure out the name and shortname
1666 + var f = filetreexx[i], name = f.n, shortname;
1667 + shortname = name;
1668 + if (name.length > 70) { shortname = '<span title="' + EscapeHtml(name) + '">' + EscapeHtml(name.substring(0, 70)) + ("..." + '</span>'); } else { shortname = EscapeHtml(name); }
1669 + name = EscapeHtml(name);
1670 +
1671 + // Figure out the date
1672 + var fdatestr = '';
1673 + if (f.d != null) {
1674 + var fdate = new Date(f.d);
1675 + if (typeof f.d == 'number') { fdate = new Date(f.d * 1000); }
1676 + var fdatestr = printDateTime(fdate) + '&nbsp;';
1677 + }
1678 +
1679 + // Figure out the size
1680 + var fsize = '';
1681 + if (f.s != null) { fsize = getFileSizeStr(f.s); }
1682 +
1683 + var h = '';
1684 + if (f.t < 3) {
1685 + var right = '', title = '';
1686 + h = '<div class=filelist file=999><input file=999 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'>&nbsp;<span style=float:right title="' + title + '">' + right + '</span><span><div class=fileIcon' + f.t + ' onclick=p13folderset("' + encodeURIComponentEx(f.nx) + '")></div><a href=# style=cursor:pointer onclick=\'return p13folderset("' + encodeURIComponentEx(f.nx) + '")\'>' + shortname + '</a></span></div>';
1687 + } else {
1688 + var link = shortname;
1689 + if (f.s > 0) {
1690 + // Local link
1691 + //link = '<a href=# style=cursor:pointer onclick="return p13downloadfile(\'' + encodeURIComponentEx(newlinkpath + '/' + name) + '\',\'' + encodeURIComponentEx(name) + '\',' + f.s + ')">' + shortname + '</a>';
1692 + // Server link
1693 + //link = '<a href="devicefile.ashx?c=' + authCookie + '&m=' + currentNode.meshid.split('/')[2] + '&n=' + currentNode._id.split('/')[2] + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '" download="' + name + '" style=cursor:pointer>' + shortname + '</a>';
1694 + // Server link
1695 + //link = '<a onclick=downloadFile("devicefile.ashx?c=' + authCookie + '&m=' + currentNode.meshid.split('/')[2] + '&n=' + currentNode._id.split('/')[2] + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '","' + encodeURIComponentEx(name) + '") style=cursor:pointer>' + shortname + '</a>';
1696 + link = '<a onclick=downloadFile("devicefile.ashx?c=' + authCookie + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '","' + encodeURIComponentEx(name) + '") style=cursor:pointer>' + shortname + '</a>';
1697 + }
1698 + h = '<div id=fileEntry cmenu=filesContextMenu fileIndex=' + i + ' class=filelist file=3><input file=3 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'>&nbsp;<span class=fsize>' + fdatestr + '</span><span style=float:right>' + EscapeHtml(fsize) + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
1699 + }
1700 +
1701 + if (f.t < 3) { html1 += h; } else { html2 += h; }
1702 + }
1703 +
1704 + // Display the files and path
1705 + QH('p13files', html1 + html2);
1706 + QH('p13currentpath', displayPath);
1707 + QE('p13FolderUp', p13filetreelocation.length != 0);
1708 +
1709 + // Re-check all boxes if needed using names
1710 + if (checkedNames != null) { var checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkedNames.indexOf(p13filetree.dir[checkboxes[i].value].n) >= 0) { checkboxes[i].checked = true; } } }
1711 +
1712 + // Update the actions buttons
1713 + p13setActions();
1714 + }
1715 +
1716 + function p13folderset(x) {
1717 + p13targetpath = joinPaths(p13filetree.path, p13filetree.dir[x].n).split('\\').join('/');
1718 + files.sendText({ action: 'ls', reqid: 1, path: p13targetpath });
1719 + }
1720 +
1721 + function p13folderup(x) {
1722 + if (x == null) { p13filetreelocation.pop(); } else { while (p13filetreelocation.length > x) { p13filetreelocation.pop(); } }
1723 + p13targetpath = p13filetreelocation.join('/');
1724 + files.sendText({ action: 'ls', reqid: 1, path: p13targetpath });
1725 + return false;
1726 + }
1727 +
1728 + function p13findfile() {
1729 + if (xxdialogMode) return;
1730 + var x = addHtmlValue("Filter", '<input id=d2findFilter style="width:230px" onkeyup=p13findfileValidate() onkeydown=p13findfileDown(event) placeholder="*.txt"></input>');
1731 + x += '<div id=d2findResults style="width:100%;height:184px;background-color:white;border:1px solid black;padding:3px;overflow:scroll;margin-top:8px"></div>';
1732 + x += '<div style=margin-top:8px><input id=filefind_dlgCancelButton type=button value=' + "Close" + ' style=float:right;width:80px;margin-left:5px onclick=p13findfileCancel()>';
1733 + x += '<input id=filefind_dlgOkButton type=submit value=' + "Search" + ' style=float:right;width:80px onclick=p13findfileEx()><br /><br /></div>';
1734 + setDialogMode(2, "Find Files", 0, null, x);
1735 + p13findfileValidate();
1736 + Q('d2findFilter').focus();
1737 + }
1738 +
1739 + function p13findfileDown(e) { if (e.code == "Enter") { p13findfileEx(); } }
1740 + function p13findfileValidate() { QE('filefind_dlgOkButton', Q('d2findFilter').value.length > 0); }
1741 + function p13findfileCancel() { if (xxdialogTag != null) { files.sendText({ action: 'cancelfindfile', reqid: xxdialogTag }); } dialogclose(0) }
1742 +
1743 + function p13findfileEx(b, t) {
1744 + if (Q('d2findFilter').value.length == 0) return;
1745 + var winagent = true;//((currentNode.agent.id > 0) && (currentNode.agent.id < 5));
1746 + var slash = winagent ? '\\' : '/';
1747 + var path = p13filetreelocation.join(slash) + slash;
1748 + if (!winagent) { path = slash + path; }
1749 + xxdialogTag = 'find:' + Math.random();
1750 + files.sendText({ action: 'findfile', reqid: xxdialogTag, path: path, filter: Q('d2findFilter').value });
1751 + QH('d2findResults', '');
1752 + QE('d2findFilter', false);
1753 + QE('filefind_dlgOkButton', false);
1754 + }
1755 +
1756 + var p13sortorder;
1757 + function p13sort_filename(a, b) { if (a.ln > b.ln) return (1 * p13sortorder); if (a.ln < b.ln) return (-1 * p13sortorder); return 0; }
1758 + function p13sort_timestamp(a, b) { if (a.d > b.d) return (1 * p13sortorder); if (a.d < b.d) return (-1 * p13sortorder); return 0; }
1759 + function p13sort_bysize(a, b) { if (a.s == b.s) return p13sort_filename(a, b); return (((a.s - b.s)) * p13sortorder); }
1760 +
1761 + function p13sort_files(files) {
1762 + var r = [], sortselection = Q('p13sortdropdown').value;
1763 + for (var i in files) { files[i].nx = i; if (files[i].s == null) { files[i].s = 0; } if (files[i].n == null) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); }
1764 + p13sortorder = 1;
1765 + if (sortselection > 3) { p13sortorder = -1; sortselection -= 3; }
1766 + if (sortselection == 1) { r.sort(p13sort_filename); }
1767 + else if (sortselection == 2) { r.sort(p13sort_bysize); }
1768 + else if (sortselection == 3) { r.sort(p13sort_timestamp); }
1769 + return r;
1770 + }
1771 +
1772 + function p13setActions() {
1773 + var advancedFeatures = true; //(currentNode.agent.id != 14); // Reduct file feature on some devices.
1774 + if (p13filetree == null) {
1775 + QE('p13DeleteFileButton', false);
1776 + QE('p13NewFolderButton', false);
1777 + QE('p13UploadButton', false);
1778 + QE('p13RenameFileButton', false);
1779 + QE('p13ViewFileButton', false);
1780 + QE('p13SelectAllButton', false);
1781 + Q('p13SelectAllButton').value = "Select All";
1782 + QE('p13RefreshButton', false);
1783 + QE('p13FindButton', false);
1784 + QE('p13CutButton', false);
1785 + QE('p13CopyButton', false);
1786 + QE('p13ZipButton', false);
1787 + QE('p13PasteButton', false);
1788 + } else {
1789 + var cc = p13getFileSelCount(), tc = p13getFileCount(), sfc = p13getFileSelCount(false); // In order: number of entires selected, number of total entries, number of selected entires that are files (not folders)
1790 + var winAgent = true; //((currentNode.agent.id > 0) && (currentNode.agent.id < 5)) || (currentNode.agent.id == 14);
1791 + QE('p13DeleteFileButton', (cc > 0) && ((p13filetreelocation.length > 0) || (winAgent == false)));
1792 + QE('p13NewFolderButton', advancedFeatures && ((p13filetreelocation.length > 0) || (winAgent == false)));
1793 + QE('p13UploadButton', ((p13filetreelocation.length > 0) || (winAgent == false) /*|| (currentNode.agent.id == 14)*/));
1794 + QE('p13RenameFileButton', advancedFeatures && (cc == 1) && ((p13filetreelocation.length > 0) || (winAgent == false)));
1795 + QE('p13ViewFileButton', advancedFeatures && (cc == 1) && (sfc == 1) && ((p13filetreelocation.length > 0) || (winAgent == false)));
1796 + QE('p13SelectAllButton', tc > 0);
1797 + Q('p13SelectAllButton').value = (cc > 0 ? "Select None" : "Select All");
1798 + QE('p13RefreshButton', true);
1799 + QE('p13FindButton', advancedFeatures && ((p13filetreelocation.length > 0) || (winAgent == false)));
1800 + QE('p13CutButton', advancedFeatures && (cc > 0) && (cc == sfc) && ((p13filetreelocation.length > 0) || (winAgent == false)));
1801 + QE('p13CopyButton', advancedFeatures && (cc > 0) && (cc == sfc) && ((p13filetreelocation.length > 0) || (winAgent == false)));
1802 + QE('p13ZipButton', advancedFeatures && (cc > 0) && ((p13filetreelocation.length > 0) || (winAgent == false)));
1803 + QE('p13PasteButton', advancedFeatures && ((p13filetreelocation.length > 0) || (winAgent == false)) && ((p13clipboard != null) && (p13clipboard.length > 0)));
1804 + }
1805 + }
1806 +
1807 + function p13getFileSelCount(includeDirs) { var cc = 0; var checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && ((includeDirs != false) || (checkboxes[i].attributes.file.value == '3'))) cc++; } return cc; }
1808 + function p13getFileSelDirCount() { var cc = 0, checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '999')) cc++; } return cc; }
1809 + function p13getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fd'); return checkboxes.length; }
1810 + function p13selectallfile() { var nv = (p13getFileSelCount() == 0), checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = nv; } p13setActions(); }
1811 + function p13createfolder() { setDialogMode(2, "New Folder", 3, p13createfolderEx, '<input type=text id=p13renameinput maxlength=64 onkeyup=p13fileNameCheck(event) style=width:100% />'); focusTextBox('p13renameinput'); p13fileNameCheck(); }
1812 + function p13createfolderEx() { files.sendText({ action: 'mkdir', reqid: 1, path: p13filetreelocation.join('/') + '/' + Q('p13renameinput').value }); p13folderup(999); }
1813 + function p13deletefile() { var cc = p13getFileSelCount(), rec = (p13getFileSelDirCount() > 0) ? '<br /><br /><label><input type=checkbox id=p13recdeleteinput>' + "Recursive delete" + '</label><br>' : '<input type=checkbox id=p13recdeleteinput style=\'display:none\'>'; setDialogMode(2, "Delete", 3, p13deletefileEx, (cc > 1) ? (format("Delete {0} selected items?", cc) + rec) : ("Delete selected item?" + rec)); }
1814 + function p13deletefileEx() { var delfiles = [], checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { delfiles.push(p13filetree.dir[checkboxes[i].value].n); } } files.sendText({ action: 'rm', reqid: 1, path: p13filetreelocation.join('/'), delfiles: delfiles, rec: Q('p13recdeleteinput').checked }); p13folderup(999); }
1815 + function p13renamefile() { var renamefile, checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { renamefile = p13filetree.dir[checkboxes[i].value].n; } } setDialogMode(2, "Rename", 3, p13renamefileEx, '<input type=text id=p13renameinput maxlength=64 onkeyup=p13fileNameCheck(event) style=width:100% value="' + renamefile + '" />', { action: 'rename', path: p13filetreelocation.join('/'), oldname: renamefile }); focusTextBox('p13renameinput'); p13fileNameCheck(); }
1816 + function p13renamefileEx(b, t) { t.newname = Q('p13renameinput').value; files.sendText(t); p13folderup(999); }
1817 + function p13fileNameCheck(e) { var x = isFilenameValid(Q('p13renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e != null) && (e.keyCode == 13)) { dialogclose(1); } }
1818 + function p13uploadFile() { setDialogMode(2, "Upload File", 3, p13uploadFileEx, '<input type=file name=files id=p13uploadinput style=width:100% multiple=multiple onchange="updateUploadDialogOk(\'p13uploadinput\')" />'); updateUploadDialogOk('p13uploadinput'); }
1819 + function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q('p13uploadinput').files.length > 0); }
1820 + function p13uploadFileEx() { p13doUploadFiles(Q('p13uploadinput').files); }
1821 + function p13viewfile() {
1822 + var checkboxes = document.getElementsByName('fd');
1823 + for (var i = 0; i < checkboxes.length; i++) {
1824 + if (checkboxes[i].checked) {
1825 + if (p13filetree.dir[checkboxes[i].value].s <= 204800) {
1826 + p13downloadfile(encodeURIComponentEx(p13filetreelocation.join('/') + '/' + p13filetree.dir[checkboxes[i].value].n), encodeURIComponentEx(p13filetree.dir[checkboxes[i].value].n), p13filetree.dir[checkboxes[i].value].s, 'viewer');
1827 + } else { messagebox("File Editor", "Only files less than 200k can be edited."); }
1828 + break;
1829 + }
1830 + }
1831 + }
1832 +
1833 + function p13zipFiles() {
1834 + var inputFiles = [], checkboxes = document.getElementsByName('fd');
1835 + for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { inputFiles.push(p13filetree.dir[checkboxes[i].value].n); } }
1836 + setDialogMode(2, "Zip Filename", 3, p13zipFilesEx, '<input type=text id=p13renameinput maxlength=64 onkeyup=p13fileNameCheck(event) style=width:100% value="" />', { action: 'zip', path: p13filetreelocation.join('/'), files: inputFiles });
1837 + focusTextBox('p13renameinput');
1838 + p13fileNameCheck();
1839 + }
1840 +
1841 + function p13zipFilesEx(b, tag) {
1842 + tag.output = Q('p13renameinput').value;
1843 + if (!tag.output.toLowerCase().endsWith('.zip')) { tag.output += '.zip'; }
1844 + files.sendText(tag);
1845 + }
1846 +
1847 + var p13clipboard = null, p13clipboardFolder = null, p13clipboardCut = 0;
1848 + function p13copyFile(cut) { var checkboxes = document.getElementsByName('fd'); p13clipboard = []; p13clipboardCut = cut, p13clipboardFolder = p13targetpath; for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p13clipboard.push(p13filetree.dir[checkboxes[i].value].n); } } p13updateClipview(); }
1849 + function p13pasteFile() {
1850 + var x = '';
1851 + if ((p13clipboard != null) && (p13clipboard.length > 0)) {
1852 + if (p13clipboardCut == 0) {
1853 + if (p13clipboard.length > 1) { x = format("Confirm copy of {0} entries to this location?", p13clipboard.length); } else { x = format("Confirm copy of 1 entrie to this location?"); }
1854 + } else {
1855 + if (p13clipboard.length > 1) { x = format("Confirm move of {0} entries to this location?", p13clipboard.length); } else { x = format("Confirm move of 1 entrie to this location?"); }
1856 + }
1857 + }
1858 + setDialogMode(2, "Paste", 3, p13pasteFileEx, x);
1859 + }
1860 + function p13pasteFileEx() { files.sendText({ action: (p13clipboardCut == 0 ? 'copy' : 'move'), reqid: 1, scpath: p13clipboardFolder, dspath: p13targetpath, names: p13clipboard }); p13folderup(999); if (p13clipboardCut == 1) { p13clipboard = null, p13clipboardFolder = null, p13clipboardCut = 0; p13updateClipview(); } }
1861 + function p13updateClipview() {
1862 + var x = '';
1863 + if ((p13clipboard != null) && (p13clipboard.length > 0)) {
1864 + if (p13clipboardCut == 0) {
1865 + if (p13clipboard.length > 1) {
1866 + x = format("Holding {0} entries for copy" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.', p13clipboard.length);
1867 + } else {
1868 + x = format("Holding 1 entrie for copy" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.');
1869 + }
1870 + } else {
1871 + if (p13clipboard.length > 1) {
1872 + x = format("Holding {0} entries for move" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.', p13clipboard.length);
1873 + } else {
1874 + x = format("Holding 1 entrie for move" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.');
1875 + }
1876 + }
1877 + }
1878 + QH('p13bottomstatus', x);
1879 + p13setActions();
1880 + }
1881 + function p13clearClip() { p13clipboard = null; p13clipboardFolder = null; p13clipboardCut = 0; p13updateClipview(); return false; }
1882 +
1883 + function p13fileDragDrop(e) {
1884 + haltEvent(e);
1885 + QV('p13bigfail', false);
1886 + QV('p13bigok', false);
1887 + if ((e.dataTransfer == null) || (e.dataTransfer.files.length == 0) || (p13filetree == null)) return;
1888 +
1889 + // Check if these are files we can upload, remove all folders.
1890 + var files = [];
1891 + for (var i in e.dataTransfer.files) { if ((e.dataTransfer.files[i].size != null) && (e.dataTransfer.files[i].size != 0)) { files.push(e.dataTransfer.files[i]); } }
1892 + if (files.length == 0) return;
1893 +
1894 + p13doUploadFiles(files);
1895 + }
1896 +
1897 + var p13dragtimer = null;
1898 + function p13fileDragOver(e) {
1899 + haltEvent(e);
1900 + if (p13dragtimer != null) { clearTimeout(p13dragtimer); p13dragtimer = null; }
1901 + var ac = (p13filetree != null); // Set to true if we can accept the file
1902 + QV('p13bigok', ac);
1903 + QV('p13bigfail', !ac);
1904 + }
1905 +
1906 + function p13fileDragLeave(e) {
1907 + haltEvent(e);
1908 + if (e.target.id != 'p13filetable') {
1909 + QV('p13bigfail', false);
1910 + QV('p13bigok', false);
1911 + } else {
1912 + p13dragtimer = setTimeout(function () { QV('p13bigfail', false); QV('p13bigok', false); p13dragtimer = null; }, 10);
1913 + }
1914 + }
1915 +
1916 + //
1917 + // FILES DOWNLOAD
1918 + //
1919 +
1920 + var gdownloadFile; // Global state for file download
1921 +
1922 + // Called by the html page to start a download, arguments are: path, file name and file size.
1923 + function p13downloadfile(x, y, z, tag) {
1924 + if (xxdialogMode || !files) return;
1925 + gdownloadFile = { path: decodeURIComponent(x), file: decodeURIComponent(y), size: z, tsize: 0, data: '', state: 0, id: Math.random(), tag: tag }
1926 + //console.log('p13downloadFileCancel', gdownloadFile);
1927 + files.sendText({ action: 'download', sub: 'start', id: gdownloadFile.id, path: gdownloadFile.path });
1928 + setDialogMode(2, "Download File", 10, p13downloadFileCancel, '<div>' + gdownloadFile.file + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=' + z + ' />');
1929 + }
1930 +
1931 + // Called by the html page to cancel the download
1932 + function p13downloadFileCancel() { setDialogMode(0); files.sendText({ action: 'download', sub: 'cancel', id: gdownloadFile.id }); gdownloadFile = null; }
1933 +
1934 + // Called by the transport when download control command is received
1935 + function p13gotDownloadCommand(cmd) {
1936 + //console.log('p13gotDownloadCommand', cmd);
1937 + if ((gdownloadFile == null) || (cmd.id != gdownloadFile.id)) return;
1938 + if (cmd.sub == 'start') { gdownloadFile.state = 1; files.sendText({ action: 'download', sub: 'startack', id: gdownloadFile.id }); }
1939 + else if (cmd.sub == 'cancel') { gdownloadFile = null; setDialogMode(0); }
1940 + }
1941 +
1942 + // Called by the transport when binary data is received
1943 + function p13gotDownloadBinaryData(data) {
1944 + if (!gdownloadFile || gdownloadFile.state == 0) return;
1945 + if (data.length > 4) {
1946 + gdownloadFile.tsize += (data.length - 4); // Add to the total bytes received
1947 + gdownloadFile.data += data.substring(4); // Append the data
1948 + Q('d2progressBar').value = gdownloadFile.tsize; // Change the progress bar
1949 + }
1950 + if ((ReadInt(data, 0) & 1) != 0) { // Check end flag
1951 + if (gdownloadFile.tag == 'viewer') {
1952 + // View the file in the dialog box
1953 + setDialogMode(4, EscapeHtml(gdownloadFile.file), 3, p13editSaveBack, null, gdownloadFile.file);
1954 + QS('dialog').width = 'auto';
1955 + QS('dialog').bottom = '80px';
1956 + QS('dialog').top = QS('dialog').left = QS('dialog').right = '100px';
1957 + Q('d4editorarea').value = gdownloadFile.data;
1958 + gdownloadFile = null;
1959 + } else {
1960 + // Save the file to disk
1961 + saveAs(data2blob(gdownloadFile.data), gdownloadFile.file); gdownloadFile = null; setDialogMode(0); // Save the file
1962 + }
1963 + } else {
1964 + files.sendText({ action: 'download', sub: 'ack', id: gdownloadFile.id }); // Send the ACK
1965 + }
1966 + }
1967 +
1968 + var d4EditWrapVal = 0;
1969 + var d4EditSizeVal = 0;
1970 + function d4ToggleWrap(update) {
1971 + if (!update) { d4EditWrapVal = ++d4EditWrapVal % 2; }
1972 + Q('d4WrapButton').value = ["Wrap: ON", "Wrap: OFF"][d4EditWrapVal];
1973 + QS('d4editorarea').overflow = (d4EditWrapVal == 0) ? 'auto' : 'scroll';
1974 + QS('d4editorarea')['white-space'] = (d4EditWrapVal == 0) ? null : 'pre';
1975 + putstore('editorWrap', d4EditWrapVal);
1976 + }
1977 +
1978 + function d4ToggleSize(update) {
1979 + if (!update) { d4EditSizeVal = ++d4EditSizeVal % 4; }
1980 + QS('d4editorarea')['font-size'] = ['100%', '125%', '150%', '200%'][d4EditSizeVal];
1981 + Q('d4SizeButton').value = ["Size: 100%", "Size: 125%", "Size: 150%", "Size: 200%"][d4EditSizeVal];
1982 + putstore('editorSize', d4EditSizeVal);
1983 + }
1984 +
1985 + function p13editSaveBack(b, tag) {
1986 + var data = new TextEncoder().encode(Q('d4editorarea').value);
1987 + p13uploadFileContinue(1, [{ name: tag, size: data.byteLength, type: 'text/plain', xdata: data }]);
1988 + }
1989 +
1990 + //
1991 + // FILES UPLOAD
1992 + //
1993 +
1994 + var uploadFile;
1995 + function p13doUploadFiles(files) {
1996 + if (xxdialogMode) return;
1997 +
1998 + // Check if we are going to overwrite any files
1999 + var winAgent = true;//((currentNode.agent.id > 0) && (currentNode.agent.id < 5));
2000 + var targetFiles = [], overWriteCount = 0;
2001 + for (var i in p13filetree.dir) { if (winAgent) { targetFiles.push(p13filetree.dir[i].n.toLowerCase()); } else { targetFiles.push(p13filetree.dir[i].n); } }
2002 + for (var i = 0; i < files.length; i++) {
2003 + if (winAgent) {
2004 + if (targetFiles.indexOf(files[i].name.toLowerCase()) >= 0) { overWriteCount++; }
2005 + } else {
2006 + if (targetFiles.indexOf(files[i].name) >= 0) { overWriteCount++; }
2007 + }
2008 + }
2009 +
2010 + if (overWriteCount == 0) {
2011 + // If no overwrite, go ahead with upload
2012 + p13uploadFileContinue(1, files);
2013 + } else {
2014 + // Otherwise, prompt for confirmation
2015 + setDialogMode(2, "Upload File", 3, p13uploadFileContinue, format((overWriteCount == 1) ? "Upload will overwrite 1 file. Continue?" : "Upload will overwrite {0} files. Continue?", overWriteCount), files);
2016 + }
2017 + }
2018 +
2019 + function p13uploadFileContinue(b, files) {
2020 + uploadFile = {};
2021 + uploadFile.xpath = p13filetreelocation.join('/');
2022 + uploadFile.xfiles = files;
2023 + uploadFile.xfilePtr = -1;
2024 + setDialogMode(2, "Upload File", 10, p13uploadFileCancel, '<div id=p13dfileName>' + "Connecting..." + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=0 />');
2025 + p13uploadNextFile();
2026 + }
2027 +
2028 + // Perform SHA-384 hashing
2029 + const byteToHex = [];
2030 + for (var n = 0; n <= 0xff; ++n) { var hexOctet = n.toString(16).padStart(2, '0'); byteToHex.push(hexOctet); }
2031 + function arrayBufferToHex(arrayBuffer) { return Array.prototype.map.call(new Uint8Array(arrayBuffer), n => byteToHex[n]).join(''); }
2032 + function performHash(data, f) { window.crypto.subtle.digest('SHA-384', data).then(function (v) { f(arrayBufferToHex(v)); }, function () { f(null); }); }
2033 +
2034 + // Push the next file
2035 + function p13uploadNextFile() {
2036 + uploadFile.xfilePtr++;
2037 + if (uploadFile.xfiles.length > uploadFile.xfilePtr) {
2038 + uploadFile.xptr = 0;
2039 + var file = uploadFile.xfiles[uploadFile.xfilePtr];
2040 + QH('p13dfileName', file.name);
2041 + Q('d2progressBar').max = file.size;
2042 + Q('d2progressBar').value = 0;
2043 + if (file.xdata == null) {
2044 + // Load the data
2045 + uploadFile.xreader = new FileReader();
2046 + uploadFile.xreader.onload = function () {
2047 + uploadFile.xdata = uploadFile.xreader.result;
2048 +
2049 + // If the remote file already exists and is smaller then our file, see if we can resume the trasfer
2050 + var f = null;
2051 + for (var i in p13filetree.dir) { if (p13filetree.dir[i].n == file.name) { f = p13filetree.dir[i]; } }
2052 + if ((f != null) && (f.s <= uploadFile.xreader.result.byteLength)) {
2053 + performHash(uploadFile.xreader.result.slice(0, f.s), function (hash) {
2054 + files.sendText(JSON.stringify({ action: 'uploadhash', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, tag: { h: hash.toUpperCase(), s: f.s, skip: f.s == uploadFile.xreader.result.byteLength } }));
2055 + });
2056 + } else {
2057 + files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength }));
2058 + }
2059 + };
2060 + uploadFile.xreader.readAsArrayBuffer(file);
2061 + } else {
2062 + // Data already loaded
2063 + uploadFile.xdata = file.xdata;
2064 + files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength }));
2065 + }
2066 + } else {
2067 + p13uploadFileTransferDone();
2068 + }
2069 + }
2070 +
2071 + // Used to cancel the entire transfer.
2072 + function p13uploadFileCancel(button, tag) {
2073 + if (uploadFile != null) { files.sendText(JSON.stringify({ action: 'uploadcancel', reqid: uploadFile.xfilePtr })); uploadFile = null; }
2074 + p13uploadFileTransferDone();
2075 + }
2076 +
2077 + // Used to cancel the entire transfer.
2078 + function p13uploadFileTransferDone() {
2079 + uploadFile = null; // No more files to upload, clean up.
2080 + setDialogMode(0); // Close the dialog box
2081 + p13folderup(9999); // Refresh the current folder
2082 + }
2083 +
2084 + // Receive upload ack from the mesh agent, use this to keep sending more data
2085 + function p13gotUploadData(cmd) {
2086 + if ((uploadFile == null) || (parseInt(uploadFile.xfilePtr) != parseInt(cmd.reqid))) { return; }
2087 + switch (cmd.action) {
2088 + case 'uploadstart': { p13uploadNextPart(false); for (var i = 0; i < 8; i++) { p13uploadNextPart(true); } break; } // Send 8 more blocks of 16k to fill the websocket.
2089 + case 'uploadack': { p13uploadNextPart(false); break; }
2090 + case 'uploaddone': { if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadNextFile(); } else { p13uploadFileTransferDone(); } break; }
2091 + case 'uploaderror': { p13uploadFileCancel(); break; }
2092 + case 'uploadhash': {
2093 + var file = uploadFile.xfiles[uploadFile.xfilePtr];
2094 + if (file) {
2095 + if (cmd.tag.h === cmd.hash) {
2096 + if (cmd.tag.skip) {
2097 + p13uploadNextFile();
2098 + } else {
2099 + uploadFile.xptr = cmd.tag.s;
2100 + files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength, append: true }));
2101 + }
2102 + } else {
2103 + files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength, append: false }));
2104 + }
2105 + }
2106 + break;
2107 + }
2108 + }
2109 + }
2110 +
2111 + // Push the next part of the file into the websocket. If dataPriming is true, push more data only if it's not the last block of the file.
2112 + function p13uploadNextPart(dataPriming) {
2113 + var data = uploadFile.xdata, start = uploadFile.xptr;
2114 + if (start >= data.byteLength) {
2115 + files.sendText(JSON.stringify({ action: 'uploaddone', reqid: uploadFile.xfilePtr }));
2116 + } else {
2117 + var end = uploadFile.xptr + 16384;
2118 + if (end > data.byteLength) { if (dataPriming == true) { return; } end = data.byteLength; }
2119 + var dataslice = new Uint8Array(data.slice(start, end))
2120 + if ((dataslice[0] == 123) || (dataslice[0] == 0)) {
2121 + var datapart = new Uint8Array(end - start + 1);
2122 + datapart.set(dataslice, 1); // Add a zero char at the start of the send, this will indicate that it's not a JSON command.
2123 + files.send(datapart);
2124 + } else {
2125 + files.send(dataslice); // The data does not start with 0 or 123 "{" so it can't be confused for JSON.
2126 + }
2127 + uploadFile.xptr = end;
2128 + Q('d2progressBar').value = end;
2129 + }
2130 + }
2131 +
2132 +
2133 +
2134 + //
2135 + // POPUP DIALOG
2136 + //
2137 +
2138 + // null = Hidden, 1 = Generic Message
2139 + var xxdialogMode;
2140 + var xxdialogFunc;
2141 + var xxdialogButtons;
2142 + var xxdialogTag;
2143 + var xxcurrentView = -1;
2144 +
2145 + // Display a dialog box
2146 + // Parameters: Dialog Mode (0 = none), Dialog Title, Buttons (1 = OK, 2 = Cancel, 3 = OK & Cancel), Call back function(0 = Cancel, 1 = OK), Dialog Content (Mode 2 only)
2147 + function setDialogMode(x, y, b, f, c, tag) {
2148 + xxdialogMode = x;
2149 + xxdialogFunc = f;
2150 + xxdialogButtons = b;
2151 + xxdialogTag = tag;
2152 + QE('idx_dlgOkButton', true);
2153 + QV('idx_dlgOkButton', b & 1);
2154 + QV('idx_dlgCancelButton', b & 2);
2155 + QV('id_dialogclose', (b & 2) || (b & 8));
2156 + QV('idx_dlgDeleteButton', b & 4);
2157 + QV('idx_dlgButtonBar', b & 7);
2158 + if (y) QH('id_dialogtitle', y);
2159 + for (var i = 1; i < 8; i++) { QV('dialog' + i, i == x); } // Edit this line when more dialogs are added
2160 + QV('dialog', x);
2161 + if (c) { if (x == 2) { QH('id_dialogOptions', c); } else { QH('id_dialogMessage', c); } }
2162 + }
2163 +
2164 + function dialogclose(x) {
2165 + var f = xxdialogFunc, b = xxdialogButtons, t = xxdialogTag;
2166 + setDialogMode();
2167 + if (((b & 8) || x) && f) f(x, t);
2168 + }
2169 +
2170 + function go(x) {
2171 + if (xxdialogMode) return;
2172 + QV('p11', x == 11);
2173 + QV('p12', x == 12);
2174 + QV('p13', x == 13);
2175 + xxcurrentView = x;
2176 + setupDeviceMenu();
2177 + }
2178 +
2179 + function setupDeviceMenu() {
2180 + var menus = [];
2181 + if (features & 2) { menus.push({ n: "Desktop", f: 'go(11)', s: (xxcurrentView == 11) }); }
2182 + if (features & 1) { menus.push({ n: "Terminal", f: 'go(12)', s: (xxcurrentView == 12) }); }
2183 + if (features & 4) { menus.push({ n: "Files", f: 'go(13)', s: (xxcurrentView == 13) }); }
2184 + updateFooterMenu(menus);
2185 + }
2186 +
2187 + function updateFooterMenu(options) {
2188 + while (options != null && options.length < 3) { options.push({ n: '' }); }
2189 + var x = '', prev = '';
2190 + if (options != null) { for (var i in options) { x += '<td style="cursor:pointer' + ((prev == '') ? '' : ';border-left:solid 1px white') + (options[i].s ? ';background-color:#487099' : '') + '" onclick="' + options[i].f + '">' + options[i].n; prev = options[i].n; } }
2191 + QH('xfooterMenu', '<tr>' + x);
2192 + }
2193 +
2194 + function messagebox(t, m) { setSessionActivity(); QH('id_dialogMessage', m); setDialogMode(1, t, 1); }
2195 + function statusbox(t, m) { setSessionActivity(); QH('id_dialogMessage', m); setDialogMode(1, t); }
2196 + function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
2197 + function pad2(num) { var s = '00' + num; return s.substr(s.length - 2); }
2198 + function format(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); };
2199 + function setSessionActivity() { sessionActivity = Date.now(); /*QH('idleTimeoutNotify', '');*/ }
2200 + function printDate(d) { return d.toLocaleDateString(urlargs.locale); }
2201 + function printTime(d) { return d.toLocaleTimeString(urlargs.locale); }
2202 + function printDateTime(d) { return d.toLocaleString(urlargs.locale); }
2203 + function printFlexDateTime(d) { if (printDate(new Date()) == printDate(d)) { return format("Expires at {0}", printTime(d)); } else { return format("Expires {0}", printDateTime(d)); } }
2204 + function getFileSizeStr(size) { if (typeof size != 'number') { size = 0; } if (size == 1) return "1 byte"; return format("{0} bytes", size); }
2205 + function encodeURIComponentEx(txt) { return encodeURIComponent(txt).replace(/'/g, '%27'); };
2206 + function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); }
2207 + function focusTextBox(x) { setTimeout(function () { Q(x).selectionStart = Q(x).selectionEnd = 65535; Q(x).focus(); }, 0); }
2208 + var isFilenameValid = (function () { var x1 = /^[^\\/:\*\?"<>\|]+$/, x2 = /^\./, x3 = /^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname) { return x1.test(fname) && !x2.test(fname) && !x3.test(fname) && (fname[0] != '.'); } })();
2209 +
2210 + // Webkit seems to have a problem with "download" tag causing "network error", but openning the download in a hidden frame fixes it.
2211 + // So we do that for all browsers except FireFox
2212 + function downloadFile(link, name, closeDialog) {
2213 + var element = document.createElement('a');
2214 + element.setAttribute('href', link);
2215 + element.setAttribute('rel', 'noreferrer noopener');
2216 + element.setAttribute('target', 'fileDownloadFrame');
2217 + if (navigator.userAgent.indexOf('Firefox') >= 0) { element.setAttribute('download', decodeURIComponent(name ? name : '')); }
2218 + document.body.appendChild(element);
2219 + element.click();
2220 + document.body.removeChild(element);
2221 + if (closeDialog) { setDialogMode(0); }
2222 + }
2223 +
2224 + start();
2225 + </script>
2226 +</body>
2227 +</html>
\ No newline at end of file