sharing.handlebars 143 KB

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