index.html 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta name="apple-mobile-web-app-capable" content="yes" />
  5. <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
  6. <meta charset="UTF-8">
  7. <title>NotepadA</title>
  8. <meta name="theme-color" content="#4b75ff">
  9. <link rel="stylesheet" href="../script/semantic/semantic.min.css">
  10. <link rel="stylesheet" href="notepadA.css">
  11. <script src="../script/jquery.min.js"></script>
  12. <script src="../script/semantic/semantic.min.js"></script>
  13. <script src="../script/ao_module.js"></script>
  14. <link data-name="vs/editor/editor.main" rel="stylesheet" href="script/monaco/vs/editor/editor.main.css">
  15. <style>
  16. html{
  17. height:100%;
  18. }
  19. body{
  20. height:100%;
  21. overflow-y:hidden;
  22. padding:1px;
  23. background-color:#292929;
  24. }
  25. #terminals{
  26. position: fixed;
  27. right: 0px;
  28. bottom: 24px;
  29. width: calc(100% - 240px);
  30. }
  31. </style>
  32. </head>
  33. <body>
  34. <!-- Main Menu -->
  35. <div class="npa menu">
  36. <div class="item"><img class="ui image" src="img/module_icon.png" style="height:20px;"></img></div>
  37. <div class="item selectable" onclick="initContextMenu('file',this);">File</div>
  38. <div class="item selectable" onclick="initContextMenu('edit',this);">Edit</div>
  39. <div class="item selectable" onclick="initContextMenu('view',this);">View</div>
  40. <div class="item selectable" onclick="initContextMenu('fontsize',this);">Font Size</div>
  41. <div class="item selectable" onclick="initContextMenu('tools',this);">Tools</div>
  42. <div class="item selectable" onclick="initContextMenu('help',this);">Help</div>
  43. </div>
  44. <div id="directoryList" onclick="hideContextMenu();">
  45. <div class="banner">Directory Listing</div>
  46. <div class="title item" onclick="event.preventDefault(); $('#openeditors').stop().finish().slideToggle('fast');"><i class="caret down icon"></i> OPEN EDITORS</div>
  47. <div class="subgroup" id="openeditors">
  48. </div>
  49. <div class="title item" onclick="toggleDirectoryExplorer(this);"><i class="caret down icon"></i><span id="openFolderTitle">NO FOLDER</span></div>
  50. <div class="subgroup" id="directoryExplorer">
  51. </div>
  52. </div>
  53. <div id="codeArea" onclick="hideContextMenu();">
  54. <div id="ca1" class="codeBoard">
  55. <div class="tabs" editorUID="mainEditor" ondrop="TabDrop(event)" ondragover="allowDrop(event)">
  56. </div>
  57. <div class="editor" editorUID="mainEditor" onclick="focusThisEditor(this);"></div>
  58. <div class="editorcover" editorUID="mainEditor" onclick="focusThisEditor(this);"></div>
  59. </div>
  60. <div id="ca2" class="codeBoard splitMode right" style="display:none;">
  61. <div class="tabs" editorUID="subEditor" ondrop="TabDrop(event)" ondragover="allowDrop(event)">
  62. </div>
  63. <div class="editor" editorUID="subEditor" onclick="focusThisEditor(this);"></div>
  64. <div class="editorcover" editorUID="subEditor" onclick="focusThisEditor(this);"></div>
  65. </div>
  66. <div id="terminals" style="display:none;">
  67. <div class="menubar">
  68. <p>TERMINAL</p>
  69. <div style="position: absolute; right: 12px;">
  70. <i style="cursor: pointer;" onclick="restartTerminal();" class="refresh icon"></i>
  71. <i style="cursor: pointer;" onclick="closeTerminal();" class="remove icon"></i>
  72. </div>
  73. </div>
  74. <div class="terminalWindow focused">
  75. <iframe id="initTerminal" src="../wstty/">
  76. </iframe>
  77. </div>
  78. </div>
  79. </div>
  80. <div class="bottommenu">
  81. <div class="item"><i class="exchange icon"></i><span id="pingStatus"></span></div>
  82. <div class="item extrapadding" id="messageField" style="display:none;"><i class="checkmark icon"></i>Connected</div>
  83. <div class="rightpadded">
  84. <div class="item" id="filepath"><i class="code file icon"></i> user:/</div>
  85. <div class="item" id="fileExt">N/A</div>
  86. <div class="item">NotepadA</div>
  87. </div>
  88. </div>
  89. <!-- Context Menu -->
  90. <div id="contextMenu" class="contextMenu">
  91. </div>
  92. <!-- Licnese information -->
  93. <div id="licenseInfo" class="ui modal">
  94. <i class="close icon"></i>
  95. <div class="header">
  96. NotepadA License
  97. </div>
  98. <div class="content">
  99. <div class="description">
  100. <div class="ui header">Copyright 2020-2021 Toby Chui</div>
  101. <p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  102. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  103. <br>
  104. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  105. </p>
  106. </div>
  107. </div>
  108. <div class="actions">
  109. <div class="ui positive right labeled icon button">
  110. OK
  111. <i class="checkmark icon"></i>
  112. </div>
  113. </div>
  114. </div>
  115. <!-- NotepadA Info-->
  116. <div id="aboutnpa" class="ui basic modal">
  117. <div class="ui icon header">
  118. <i class="code icon"></i>
  119. NotepadA Code Editor
  120. </div>
  121. <div class="content" align="center">
  122. <p>The default code editor for ArOZ Online 1.0<br>Developed by Toby Chui</p>
  123. </div>
  124. <div class="actions">
  125. <div class="ui green ok inverted button">
  126. <i class="checkmark icon"></i>
  127. OK
  128. </div>
  129. </div>
  130. </div>
  131. <!-- Copy and Paste menu reminder-->
  132. <div id="clipboardReminder" class="ui basic modal">
  133. <div class="ui icon header">
  134. <i id="clipboardReminderIcon" class="archive icon"></i>
  135. Keyboard Only
  136. </div>
  137. <div class="content" style="text-align:center;">
  138. <p id="clipboardReminderText"></p>
  139. </div>
  140. <div class="actions">
  141. <div class="ui green ok inverted button">
  142. <i class="checkmark icon"></i>
  143. OK
  144. </div>
  145. </div>
  146. </div>
  147. <script src="script/monaco/vs/loader.js"></script>
  148. <script>
  149. //Environment Paramters
  150. let editors = []; //List of opened editor
  151. let splitMode = false; //Split the screen into two half
  152. let focusedEditor = "mainEditor"; //The editor currently is focused
  153. let loadedModels = []; //Loaded models. Prevent duplicate loading problem
  154. let focusedFileInfo = [];
  155. let files = ao_module_loadInputFiles(); //Files imported from file system
  156. //Terminal realted
  157. let terminalStartingEndpoint = "../wstty/";
  158. //Initiation functions
  159. initEditor($("#ca1").find(".editor")[0], "mainEditor", files, restoreEditorState);
  160. initEditor($("#ca2").find(".editor")[0],"subEditor");
  161. //Load ready page into the editorCover
  162. $(".editorcover").load("home.html");
  163. //Get the editor by id
  164. function getEditor(uuid){
  165. var resultEditor = undefined;
  166. editors.forEach(thisEditor => {
  167. if (thisEditor.uuid == uuid){
  168. resultEditor = thisEditor;
  169. }
  170. });
  171. return resultEditor;
  172. }
  173. //Restore the previous editor state
  174. function restoreEditorState(editor){
  175. //Load previous state if exists
  176. if (window.location.hash.length > 1){
  177. var stateString = window.location.hash.substr(1);
  178. var oldState = JSON.parse(decodeURIComponent(stateString));
  179. //Load the previous statue opened folder
  180. if (oldState.folder !== undefined){
  181. //Load the previous opened folder
  182. var predictedName = oldState.folder.split("/").pop();
  183. if (oldState.folder.substr(oldState.folder.length -1) == "/"){
  184. var tmp = oldState.folder.split("/");
  185. tmp.pop();
  186. predictedName = tmp.pop();
  187. }
  188. openProjectFolder([{
  189. filename: predictedName,
  190. filepath: oldState.folder
  191. }], false);
  192. }
  193. //Open previous opened files
  194. if (oldState.files !== undefined){
  195. oldState.files.forEach(filepath => {
  196. openFile(filepath, false);
  197. })
  198. }
  199. //Open terminal if it is enabled previously
  200. if (oldState.terminal !== undefined && oldState.terminal == true){
  201. openTerminal();
  202. }
  203. }
  204. }
  205. //Restore previous editor states
  206. function restoreUserPreference(){
  207. getStorage("fontsize",function(fontsize){
  208. setFontSize(fontsize);
  209. });
  210. getStorage("splitmode",function(splitSetting){
  211. if (splitSetting.error !== undefined){
  212. return;
  213. }
  214. splitSetting = splitSetting.trim();
  215. if (splitSetting == "true"){
  216. splitMode = true;
  217. $("#ca1").attr("class","codeBoard splitMode left")
  218. $("#ca2").show();
  219. }else{
  220. splitMode = false;
  221. $("#ca1").attr("class","codeBoard")
  222. $("#ca2").hide();
  223. }
  224. //Update all editor's size
  225. for (var i =0; i < editors.length; i++){
  226. editors[i].editor.layout();
  227. }
  228. });
  229. }
  230. //Check if it is run under vdi mode. If not, establish checkauth ping to keep session alive
  231. if (!ao_module_virtualDesktop){
  232. setInterval(function(){
  233. $.get("../system/auth/checkLogin",function(data){
  234. if (data.error != undefined){
  235. $("#pingStatus").text(data.error);
  236. }else{
  237. var x = Date()
  238. $("#pingStatus").text(x);
  239. }
  240. });
  241. },30000);
  242. }else{
  243. $("#pingStatus").text("Web Desktop Mode");
  244. }
  245. $.get("../system/auth/checkLogin",function(data){
  246. var x = Date()
  247. $("#pingStatus").text(x);
  248. });
  249. //Bind the events of the tab objects
  250. function bindTabItemEvents(){
  251. $(".tabs .item").off("click").on('click',function(evt){
  252. if ($(this).hasClass("selected")){
  253. //This tab is already in focus.
  254. return;
  255. }
  256. //Add focused class to the tab
  257. $(this).parent().find(".item.selected").removeClass("selected");
  258. $(this).addClass("selected");
  259. //Check which editor this tab belongs to
  260. var editorUUID = $(this).parent().parent().find(".editor").attr("editoruid");
  261. var tabUUID = $(this).attr("uuid");
  262. var targetEditor;
  263. for (var i =0; i < editors.length; i++){
  264. if (editors[i].uuid == editorUUID){
  265. targetEditor = editors[i];
  266. }
  267. }
  268. if (typeof targetEditor === undefined){
  269. console.log("Error when loading tab");
  270. return;
  271. }
  272. changeTab(targetEditor,tabUUID);
  273. //Update the selected item in opened editor as well
  274. $("#openeditors .item.selected").removeClass("selected");
  275. $("#openeditors .item").each(function(){
  276. if ($(this).attr("uuid") == tabUUID){
  277. $(this).addClass("selected");
  278. }
  279. });
  280. });
  281. $("#openeditors .item").off("click").on("click",function(evt){
  282. //Update UI to match selected tab
  283. $("#openeditors .item.selected").removeClass('selected');
  284. $(this).addClass("selected");
  285. //Get the target editor UUID
  286. var tabID = $(this).attr("uuid");
  287. var editorUUID = $(this).attr("editorUUID");
  288. var targetEditorObject;
  289. for (var i =0; i < editors.length; i++){
  290. if (editors[i].uuid == editorUUID){
  291. targetEditorObject = editors[i];
  292. }
  293. }
  294. if (typeof targetEditorObject === undefined){
  295. console.log("Error when loading tab");
  296. return;
  297. }
  298. changeTab(targetEditorObject,tabID);
  299. //Also highlight the tab menu as well
  300. $(targetEditorObject.tabsMenu).find(".item.selected").removeClass('selected');
  301. $(targetEditorObject.tabsMenu).find(".item").each(function(){
  302. if ($(this).attr("uuid") == tabID){
  303. $(this).addClass("selected");
  304. }
  305. });
  306. });
  307. $(".tabs .item .closebtn").off("click").on('click',function(evt){
  308. evt.preventDefault();
  309. evt.stopImmediatePropagation();
  310. var tabUUID = $(this).parent().attr("uuid");
  311. var editorUUID = $(this).parent().attr("editorUUID");
  312. closeTabWithUUIDAndEditorID(tabUUID, editorUUID);
  313. });
  314. $("#openeditors .closebtn").off("click").on("click",function(evt){
  315. evt.stopImmediatePropagation();
  316. evt.preventDefault();
  317. var tabUUID = $(this).parent().attr("uuid");
  318. var editorUUID = $(this).parent().attr("editorUUID");
  319. closeTabWithUUIDAndEditorID(tabUUID, editorUUID);
  320. });
  321. }
  322. //Close a tab given the editorUUID and the tabUUID.
  323. function closeTabWithUUIDAndEditorID(tabUUID, editorUUID){
  324. //Find the editor object related to this tab
  325. let targetEditorObject;
  326. for (var i =0; i < editors.length; i++){
  327. if (editors[i].uuid == editorUUID){
  328. targetEditorObject = editors[i];
  329. }
  330. }
  331. if (targetEditorObject === undefined){
  332. console.log("Error when closing tab");
  333. return;
  334. }
  335. //Remove state and models from the editor object
  336. delete targetEditorObject.state[tabUUID];
  337. delete targetEditorObject.model[tabUUID];
  338. //Remove this tab from the list
  339. tabs = JSON.parse(JSON.stringify(targetEditorObject.tabs));
  340. for (var i =0; i < tabs.length; i++){
  341. if (tabUUID == tabs[i].tabUUID){
  342. targetEditorObject.tabs.splice(i,1);
  343. }
  344. }
  345. //Remove the tab objects from DOM
  346. removeTabFromDOMWithUUID(tabUUID);
  347. if (targetEditorObject.tabs.length > 0){
  348. //Switch to the first tab of the editor
  349. var newFocusedTabUUID = targetEditorObject.tabs[0].tabUUID;
  350. changeTab(targetEditorObject, newFocusedTabUUID);
  351. focusTabWithUUID(newFocusedTabUUID);
  352. }else{
  353. //No more tab lefts. Show the editor cover
  354. $(targetEditorObject.tabsMenu).parent().find(".editorcover").show();
  355. $(targetEditorObject.tabsMenu).parent().find(".editorcover").load("home.html");
  356. }
  357. }
  358. function getFocusedTabInfo(){
  359. var focusedEditorObject = getFocusedEditorObject();
  360. var focusedTabUUID = focusedEditorObject.currentTabUUID;
  361. for (var i =0; i < focusedEditorObject.tabs.length; i++){
  362. var thistab = focusedEditorObject.tabs[i];
  363. if (thistab.tabUUID == focusedTabUUID){
  364. //Update the global focused tab info as the current working-on file
  365. focusedFileInfo = thistab;
  366. return thistab;
  367. }
  368. }
  369. }
  370. function getCodeAreaFromEditorUUID(editorUUID){
  371. for (var i =0; i < editors.length; i++){
  372. if (editors[i].uuid == editorUUID){
  373. var tabsMenu = editors[i].tabsMenu;
  374. return $(tabsMenu).parent();
  375. }
  376. }
  377. }
  378. function updateFileStatusDisplay(filepath){
  379. $("#filepath").html(`<i class="code file icon"></i> ${filepath}`);
  380. var ext = filepath.split(".").pop();
  381. $("#fileExt").text(ext.toUpperCase());
  382. }
  383. function getFocusedEditorObject(){
  384. for (var i =0; i < editors.length; i++){
  385. if (editors[i].uuid == focusedEditor){
  386. return editors[i];
  387. }
  388. }
  389. }
  390. function focusThisEditor(object){
  391. var editorUUID = $(object).attr("editorUID");
  392. focusedEditor = editorUUID;
  393. //Update the target file info
  394. var tabInfo = getFocusedTabInfo();
  395. if (tabInfo !== undefined){
  396. updateFileStatusDisplay(tabInfo.filepath);
  397. focusTabWithUUID(tabInfo.tabUUID);
  398. }
  399. }
  400. function focusTabWithUUID(tabUUID){
  401. $(".tabs .item.selected").removeClass("selected");
  402. $(".tabs .item").each(function(){
  403. if ($(this).attr("uuid") == tabUUID){
  404. $(this).addClass("selected");
  405. }
  406. });
  407. $("#openeditors .item.selected").removeClass("selected");
  408. $("#openeditors .item").each(function(){
  409. if ($(this).attr("uuid") == tabUUID){
  410. $(this).addClass("selected");
  411. }
  412. });
  413. }
  414. function removeTabFromDOMWithUUID(tabUUID){
  415. $(".tabs .item").each(function(){
  416. if ($(this).attr("uuid") == tabUUID){
  417. $(this).remove();
  418. }
  419. });
  420. //Remove the tab from OPEN EDITORS
  421. $("#openeditors .item").each(function(){
  422. if ($(this).attr("uuid") == tabUUID){
  423. $(this).remove();
  424. }
  425. });
  426. }
  427. function focusTab(uuid){
  428. $(".tabs .item").each(function(){
  429. if ($(this).attr("uuid") == uuid){
  430. $(this).addClass("selected");
  431. }
  432. });
  433. $("#openeditors .item.selected").removeClass("selected");
  434. $("#openeditors .item").each(function(){
  435. if ($(this).attr("uuid") == uuid){
  436. $(this).addClass("selected");
  437. }
  438. });
  439. }
  440. //Main script section for UI handling
  441. //Load context menu
  442. function initContextMenu(target, object){
  443. if (target == "file"){
  444. //List items in files
  445. $("#contextMenu").html(`
  446. <div class="item">New File
  447. <div class="tips">Ctrl + N</div>
  448. </div>
  449. <div class="divider"></div>
  450. <div class="item" onclick="openFileWithSelector();">Open File
  451. <div class="tips">Ctrl + O</div>
  452. </div>
  453. <div class="item" onclick="openFolderWithSelector();">Open Folder
  454. <div class="tips">Ctrl + Shift + O</div>
  455. </div>
  456. <div class="divider"></div>
  457. <div class="item" onclick="saveCurrentFile();">Save</div>
  458. <div class="item" onclick="saveCurrentFileAs();">Save As</div>
  459. <div class="item" onclick="saveAllFiles();">Save All</div>
  460. <!--<div class="divider"></div>
  461. <div class="item">Preference</div>-->
  462. <div class="divider"></div>
  463. <div class="item" onclick="closeFileCurrentlyFocused();">Close File</div>
  464. <div class="item" onclick="closeAllFiles();">Close All Files</div>
  465. `);
  466. if (ao_module_virtualDesktop){
  467. $("#contextMenu").append(`<div class="item" onclick="hanleUserExit();">Exit</div>`);
  468. }
  469. }else if (target == "edit"){
  470. $("#contextMenu").html(`
  471. <div class="item" onclick="undo();">Undo
  472. <div class="tips">Ctrl + Z</div>
  473. </div>
  474. <div class="item" onclick="redo();">Redo
  475. <div class="tips">Ctrl + Y</div>
  476. </div>
  477. <div class="divider"></div>
  478. <div class="item" onclick="showClipboardReminders(1);">Cut
  479. <div class="tips">Ctrl + X</div>
  480. </div>
  481. <div class="item" onclick="showClipboardReminders(2);">Copy
  482. <div class="tips">Ctrl + C</div>
  483. </div>
  484. <div class="item" onclick="showClipboardReminders(3);">Paste
  485. <div class="tips">Ctrl + V</div>
  486. </div>
  487. <div class="item" onclick="showClipboardReminders(4);">Find
  488. <div class="tips">Ctrl + F</div>
  489. </div>
  490. <div class="item" onclick="showClipboardReminders(5);">Replace
  491. <div class="tips">Ctrl + H</div>
  492. </div>
  493. <div class="divider"></div>
  494. <div class="item" onclick="toggleSplit(true);">Split WorkSpace</div>
  495. <div class="item" onclick="toggleSplit(false);">Merge WorkSpace</div>
  496. `);
  497. }else if (target == "view"){
  498. $("#contextMenu").html(`
  499. <div class="item" onclick="newEditor(); hideContextMenu();">New Editor
  500. <div class="tips"><i class="add icon"></i></div>
  501. </div>
  502. <div class="item" onclick="openInNewTab(); hideContextMenu();">Open in new Tab
  503. <div class="tips"><i class="external icon"></i></div>
  504. </div>
  505. <div class="item" onclick="openInNewFloatWindow(); hideContextMenu();">Open in floatWindow
  506. <div class="tips"><i class="window restore outline icon"></i></div>
  507. </div>
  508. <div class="item" onclick="openFileInFileManager(); hideContextMenu();">Reveal in File Manager
  509. <div class="tips"><i class="folder outline icon"></i></div>
  510. </div>
  511. <div class="item" onclick="openTerminal(); hideContextMenu();">Open Terminal
  512. <div class="tips"><i class="code icon"></i></div>
  513. </div>
  514. <div class="divider"></div>
  515. <div class="item" onclick="downloadFile(); hideContextMenu();">Download
  516. <div class="tips"><i class="download icon"></i></div>
  517. </div>
  518. `);
  519. }else if (target == "fontsize"){
  520. $("#contextMenu").html('');
  521. for (var i = 8; i < 26; i++ ){
  522. $("#contextMenu").append(`<div class="item" style="font-size:${i}px;" onclick="setFontSize(${i});">${i} px</div>`);
  523. }
  524. }else if (target == "tools"){
  525. $("#contextMenu").html('');
  526. $("#contextMenu").html(`
  527. <div class="item" onclick="showColorPicker(); hideContextMenu();">Color Picker
  528. <div class="tips"><i class="eyedropper icon"></i></div>
  529. </div>
  530. <div class="item" onclick="showMobiPreview(); hideContextMenu();">Responsive Design Viewer
  531. <div class="tips"><i class="mobile icon"></i></div>
  532. </div>
  533. `);
  534. }else if (target == "help"){
  535. $("#contextMenu").html(`
  536. <div class="item" onclick="window.open('https://microsoft.github.io/monaco-editor/'); hideContextMenu();">About Monaco Editor</div>
  537. <div class="divider"></div>
  538. <div class="item" onclick="showLicense();">License</div>
  539. <div class="item" onclick="showAboutNotepadA();">About NotepadA</div>
  540. `);
  541. }
  542. $("#contextMenu").show();
  543. $("#contextMenu").css({
  544. left: $(object).offset().left,
  545. top: "28px"
  546. })
  547. }
  548. function showLicense(){
  549. $("#licenseInfo").modal("show");
  550. hideContextMenu();
  551. }
  552. function showAboutNotepadA(){
  553. $("#aboutnpa").modal("show");
  554. hideContextMenu();
  555. }
  556. function closeFileCurrentlyFocused(){
  557. var focusedEditor = getFocusedEditorObject();
  558. var editorUUID = focusedEditor.uuid;
  559. var tabsMenu = focusedEditor.tabsMenu;
  560. var focusedTabID = $(tabsMenu).find(".item.selected").attr("uuid");
  561. closeTabWithUUIDAndEditorID(focusedTabID,editorUUID);
  562. hideContextMenu();
  563. }
  564. function closeAllFiles(){
  565. editors.forEach(editor => {
  566. var editorId = editor.uuid;
  567. editor.tabs.forEach(tab => {
  568. closeTabWithUUIDAndEditorID(tab.tabUUID, editorId);
  569. });
  570. });
  571. hideContextMenu();
  572. }
  573. function saveAllFiles(){
  574. //Store the editor object before saveall
  575. let beforeTabInfo = getFocusedTabInfo();
  576. let beforeEditor = getFocusedEditorObject();
  577. //Iterate through all editors and all tabs to save
  578. for (var i = 0; i < editors.length; i++){
  579. var thisEditor = editors[i];
  580. for (var j =0; j < thisEditor.tabs.length; j++){
  581. var tabUUID = thisEditor.tabs[j].tabUUID;
  582. changeTab(thisEditor, tabUUID);
  583. saveCurrentFile();
  584. }
  585. }
  586. //Restore the editor view
  587. changeTab(beforeEditor, beforeTabInfo.tabUUID);
  588. hideContextMenu();
  589. }
  590. function setFontSize(fontsize){
  591. for (var i =0; i < editors.length; i++){
  592. var thisEditor = editors[i].editor;
  593. thisEditor.updateOptions({
  594. fontSize: fontsize
  595. });
  596. }
  597. setStorage("fontsize",fontsize);
  598. hideContextMenu();
  599. }
  600. function toggleSplit(useSplitMode){
  601. if (useSplitMode){
  602. $("#ca1").attr("class","codeBoard splitMode left")
  603. $("#ca2").show();
  604. setStorage("splitmode","true");
  605. }else{
  606. $("#ca1").attr("class","codeBoard")
  607. $("#ca2").hide();
  608. setStorage("splitmode","false");
  609. //Move all tabs from subEditor to main editor
  610. var mainEditor = getEditor("mainEditor");
  611. var subEditor = getEditor("subEditor");
  612. subEditor.tabs.forEach(tab => {
  613. var filepath = tab.filepath;
  614. //Open tab in main editor
  615. openFile(filepath, false, mainEditor);
  616. //Close tab in sub editor
  617. closeTabWithUUIDAndEditorID(tab.tabUUID, "subEditor");
  618. });
  619. }
  620. //Update all editor's size
  621. for (var i =0; i < editors.length; i++){
  622. editors[i].editor.layout();
  623. }
  624. hideContextMenu();
  625. }
  626. //Hide all the context menus
  627. function hideContextMenu(){
  628. $("#contextMenu").hide();
  629. }
  630. //open a file with a new editor by the given filepath
  631. function openFile(filepath, updateHashStatus=true, overrideEditor=undefined){
  632. //Get the focused editor
  633. var targetEditorGroup = undefined;
  634. if (overrideEditor == undefined){
  635. //Do not override editor
  636. for (var i =0; i < editors.length; i++){
  637. if (editors[i].uuid == focusedEditor){
  638. targetEditorGroup = editors[i];
  639. }
  640. }
  641. }else{
  642. //Use override editor
  643. targetEditorGroup = overrideEditor;
  644. }
  645. if (targetEditorGroup === undefined){
  646. alert("Unable to load editor.");
  647. return;
  648. }
  649. //Check if the file is already loaded in this editor
  650. for (var i = 0; i < targetEditorGroup.tabs.length; i++){
  651. var thisTab = targetEditorGroup.tabs[i];
  652. if (thisTab.filepath == filepath){
  653. //Focus this tab
  654. changeTab(targetEditorGroup, thisTab.tabUUID);
  655. return;
  656. }
  657. }
  658. //Get the file
  659. ao_module_agirun("NotepadA/backend/read.agi",{
  660. file: filepath
  661. }, function(filecontent){
  662. //Get the target Opening Editor
  663. let targetOpeningEditor = targetEditorGroup;
  664. if (typeof targetOpeningEditor === undefined){
  665. console.log("Error when trying to get editor info")
  666. return;
  667. }
  668. //Load the editor instances
  669. var editor = targetOpeningEditor.editor;
  670. var editorUUID = targetOpeningEditor.uuid;
  671. var models = targetOpeningEditor.model;
  672. var states = targetOpeningEditor.state;
  673. var currentTabUUID = targetOpeningEditor.currentTabUUID;
  674. var targetTabMenu = targetOpeningEditor.tabsMenu;
  675. //Save the current editor status into the data
  676. var currentState = editor.saveViewState();
  677. var currentModel = editor.getModel();
  678. states[currentTabUUID] = currentState;
  679. models[currentTabUUID] = currentModel;
  680. //Create a tab for this file
  681. var tabUUID = new Date().getTime();
  682. var filename = filepath.split("/").pop();
  683. $(targetTabMenu).find(".item.selected").removeClass("selected");
  684. $(targetTabMenu).append(`<div class="item selected fileTab" title="${filepath}" draggable="true" ondragstart="fileTabDrag(event)" uuid="${tabUUID}" editorUUID="${editorUUID}" filepath="${filepath}"><i class="code file icon"></i> ${filename} <div class="closebtn"><i class="remove icon"></i></i></div></div>`);
  685. //Add this tab into the open editor list
  686. $("#openeditors .item").removeClass("selected");
  687. $("#openeditors").append(`<div class="item selected" uuid="${tabUUID}" editorUUID="${editorUUID}"><div class="closebtn" style="display:inline-block;"><i class="remove icon"></i></div> <i class="code file icon"></i> ${filename} </div>`);
  688. //Update the current tag information
  689. targetOpeningEditor.currentTabUUID = tabUUID
  690. //Push the current tab into the array of tabs
  691. targetOpeningEditor.tabs.push({
  692. filename: filename,
  693. filepath: filepath,
  694. tabUUID: tabUUID,
  695. saveHash: filecontent.hashCode()
  696. });
  697. //Update title if nonVDI or update floatWindow title if under fdi
  698. if (ao_module_virtualDesktop){
  699. ao_module_setWindowTitle("NotepadA - " + filename);
  700. }else{
  701. document.title = "NotepadA - " + filename;
  702. }
  703. //Check if model is loaded before
  704. var model;
  705. if (loadedModels[filepath] === undefined){
  706. //Set the editor value and do model auto lanuage detection
  707. model = monaco.editor.createModel(
  708. filecontent,
  709. undefined,
  710. monaco.Uri.file("../media?file=" + encodeURIComponent(filepath))
  711. )
  712. loadedModels[filepath] = model;
  713. }else{
  714. //Load the already loaded model
  715. model = loadedModels[filepath];
  716. }
  717. editor.setModel(model)
  718. //Bind tab events
  719. bindTabItemEvents();
  720. //Update file info tab
  721. var tabInfo = getFocusedTabInfo();
  722. updateFileStatusDisplay(tabInfo.filepath);
  723. //Hide the editorCover
  724. var ca = getCodeAreaFromEditorUUID(editorUUID);
  725. $(ca).find(".editorcover").hide();
  726. //Write the opened filelist to window hash
  727. if (updateHashStatus){
  728. var currentState = getHashObject();
  729. currentState["files"] = getOpenedFiles();
  730. console.log(currentState);
  731. writeHashObject(currentState);
  732. }
  733. //Finally, focus on the newly opened tab
  734. focusTabWithUUID(tabUUID);
  735. });
  736. }
  737. function hash(s){
  738. return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
  739. }
  740. //Open fileSelector
  741. function openFileWithSelector(){
  742. ao_module_openFileSelector(externalFileLoader, "user:/Document", "file",true);
  743. hideContextMenu();
  744. }
  745. //Open folderSelector
  746. function openFolderWithSelector(){
  747. ao_module_openFileSelector(openProjectFolder, "user:/Document", "folder");
  748. hideContextMenu();
  749. }
  750. //Handle fileSelector return value
  751. function externalFileLoader(filedata){
  752. if (filedata.length > 0){
  753. for (var i=0; i < filedata.length; i++){
  754. filepath = filedata[i].filepath;
  755. openFile(filepath);
  756. }
  757. }
  758. }
  759. function openProjectFolder(filedata, rewriteHash=true){
  760. for (var i=0; i < filedata.length; i++){
  761. folderpath = filedata[i].filepath;
  762. foldername = filedata[i].filename;
  763. //Load this as the folder
  764. $("#openFolderTitle").text(foldername.toUpperCase());
  765. $("#directoryExplorer").html("");
  766. $.get("../system/file_system/listDir?dir=" + encodeURIComponent(folderpath),function(data){
  767. var folders = [];
  768. var files = [];
  769. if (data === null){
  770. data = [];
  771. }
  772. for (var i =0; i < data.length; i++){
  773. var filename = data[i].Filename;
  774. var ext = filename.split(".").pop();
  775. var icon = ao_module_utils.getIconFromExt(ext);
  776. var filepath = data[i].Filepath;
  777. if (data[i].IsDir){
  778. icon = "folder";
  779. folders.push(`
  780. <div class="folderObjectWrapper">
  781. <div class="item" title="${filepath}" style="overflow:hidden;" onclick="listfolder(this);">
  782. <div class="showmore" style="display:inline-block;"><i class="caret right icon"></i></div>
  783. <i class="${icon} icon"></i> ${filename}
  784. </div>
  785. </div>
  786. `);
  787. }else{
  788. files.push(`
  789. <div class="item" title="${filepath}" draggable="true" ondragstart="directoryFileDrag(event)" filepath="${filepath}" filename="${filename}" style="overflow:hidden;" onclick="openFileViaDirectoryExplorer(this, event);">
  790. <i class="${icon} icon"></i> ${filename}
  791. </div>
  792. `);
  793. }
  794. }
  795. $("#directoryExplorer").append(folders.join(""));
  796. $("#directoryExplorer").append(files.join(""));
  797. });
  798. if (rewriteHash){
  799. var currentState = getHashObject();
  800. currentState["folder"] = foldername;
  801. writeHashObject(currentState);
  802. }
  803. }
  804. }
  805. function listfolder(folder){
  806. var folderPath = $(folder).attr("title");
  807. //Check if the folder is already list. If yes, toggle the list
  808. if ($(folder).parent().find(".dirlist").length == 0){
  809. //Folder is not listed. Load from file system
  810. $(folder).parent().append(`<div class="dirlist"></div>`);
  811. $(folder).find(".showmore i").attr("class","caret down icon");
  812. var targetListObject = $(folder).parent().find(".dirlist");
  813. $.get("../system/file_system/listDir?dir=" + encodeURIComponent(folderPath),function(data){
  814. var folders = [];
  815. var files = [];
  816. if (data === null){
  817. data = [];
  818. }
  819. for (var i =0; i < data.length; i++){
  820. var filename = data[i].Filename;
  821. var ext = filename.split(".").pop();
  822. var icon = ao_module_utils.getIconFromExt(ext);
  823. var filepath = data[i].Filepath;
  824. if (data[i].IsDir){
  825. icon = "folder";
  826. folders.push(`
  827. <div class="folderObjectWrapper">
  828. <div class="subitem" title="${filepath}" style="overflow:hidden;" onclick="listfolder(this);">
  829. <div class="showmore" style="display:inline-block;"><i class="caret right icon"></i></div>
  830. <i class="${icon} icon"></i> ${filename}
  831. </div>
  832. </div>
  833. `);
  834. }else{
  835. files.push(`
  836. <div class="subitem" title="${filepath}" style="overflow:hidden;" onclick="openFileViaDirectoryExplorer(this,event);">
  837. <i class="${icon} icon"></i> ${filename}
  838. </div>
  839. `);
  840. }
  841. }
  842. //Push the items into the list
  843. $(targetListObject).append(folders.join(""));
  844. $(targetListObject).append(files.join(""));
  845. });
  846. }else{
  847. //Folder is already listed. Toggle show of filelist
  848. if ($(folder).find(".caret.down.icon").length > 0){
  849. //Folder list visiable. Hide it
  850. $(folder).parent().find(".dirlist").slideUp('fast');
  851. $(folder).find(".showmore i").attr("class","caret right icon");
  852. }else{
  853. //Folder list hidden. Show it
  854. $(folder).parent().find(".dirlist").slideDown('fast');
  855. $(folder).find(".showmore i").attr("class","caret down icon");
  856. }
  857. }
  858. }
  859. function openFileViaDirectoryExplorer(object, event){
  860. var filepath = $(object).attr('title');
  861. openFile(filepath);
  862. }
  863. function toggleDirectoryExplorer(btn){
  864. if ($(btn).find("i").hasClass("right")){
  865. $(btn).find("i").attr("class","caret down icon");
  866. $("#directoryExplorer").slideDown('fast');
  867. }else{
  868. $(btn).find("i").attr("class","caret right icon");
  869. $("#directoryExplorer").slideUp('fast');
  870. }
  871. }
  872. //Initiate an editor with the given uuid and targetDOM
  873. function initEditor(targetDOM, editorUUID, loadPendingFiles = [], callback=undefined){
  874. require.config({ paths: { 'vs': 'script/monaco/vs' }});
  875. window.GlobalEnvironment = { getWorkerUrl: () => proxy };
  876. let proxy = URL.createObjectURL(new Blob([`
  877. self.GlobalEnvironment = {
  878. baseUrl: 'script/monaco/'
  879. };
  880. importScripts('script/monaco/vs/base/worker/workerMain.js');
  881. `], { type: 'text/javascript' }));
  882. require(["vs/editor/editor.main"], function () {
  883. let editor = monaco.editor.create(targetDOM, {
  884. value: "",
  885. language: 'javascript',
  886. automaticLayout: true,
  887. theme: 'vs-dark'
  888. });
  889. //Bind save event for this editor
  890. editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S, function() {
  891. let thisEditor = editor;
  892. //Save this editor text to the current editor focused tab folder
  893. var tabInfo = getFocusedTabInfo();
  894. var filepath = tabInfo.filepath;
  895. var content = thisEditor.getValue();
  896. //console.log(filepath, content);
  897. saveContentToFile(tabInfo.tabUUID, filepath, content, function(data){
  898. if (data.error !== undefined){
  899. alert(data.error);
  900. }else{
  901. //OK
  902. console.log("File saved");
  903. msgbox("save","File Saved")
  904. }
  905. });
  906. });
  907. editors.push({
  908. uuid: editorUUID,
  909. tabsMenu: $(targetDOM).parent().find(".tabs"),
  910. editor: editor,
  911. currentTabUUID: "default",
  912. model: [],
  913. state: [],
  914. tabs: []
  915. });
  916. //Check if there are load pending files. If yes, load them into this editor
  917. if (loadPendingFiles !== null && loadPendingFiles.length > 0){
  918. for (var i =0; i < loadPendingFiles.length; i++){
  919. openFile(loadPendingFiles[i].filepath);
  920. }
  921. }
  922. //Initialize user settings
  923. restoreUserPreference();
  924. //Perform callback with editor object if required
  925. if (callback !== undefined){
  926. callback(editor);
  927. }
  928. });
  929. }
  930. /*
  931. Terminal Function Related
  932. */
  933. function terminalInit(){
  934. $(".ui.dropdown").dropdown();
  935. }
  936. var codeAreaBottomOffset = 0;
  937. function openTerminal(){
  938. //Open WsTTY on the editor
  939. codeAreaBottomOffset = 300;
  940. $("#terminals").show();
  941. updateCodeAreaSize();
  942. hideContextMenu();
  943. updateStatusHash("terminal",true);
  944. }
  945. function closeTerminal(){
  946. codeAreaBottomOffset = 0;
  947. $("#terminals").hide();
  948. updateCodeAreaSize();
  949. updateStatusHash("terminal",false);
  950. }
  951. function restartTerminal(){
  952. $("#terminals").find(".terminalWindow.focused").find("iframe").attr("src","");
  953. setTimeout(function(){
  954. $("#terminals").find(".terminalWindow.focused").find("iframe").attr("src",terminalStartingEndpoint);
  955. }, 1000);
  956. }
  957. $(window).on("resize", function(data){
  958. updateCodeAreaSize();
  959. });
  960. updateCodeAreaSize();
  961. function updateCodeAreaSize(){
  962. $("#codeArea").css({
  963. "height":window.innerHeight - 52 - codeAreaBottomOffset
  964. });
  965. editors.forEach(editor => {
  966. editor.editor.layout();
  967. })
  968. }
  969. function openFileInFileManager(){
  970. var tabInfo = getFocusedTabInfo();
  971. var filepath = tabInfo.filepath;
  972. var fileinfo = filepath.split("/");
  973. var filename = fileinfo.pop();
  974. var dirname = fileinfo.join("/");
  975. ao_module_openPath(dirname, filename);
  976. }
  977. function getHashObject(){
  978. if (window.location.hash.length > 1){
  979. var stateString = window.location.hash.substr(1);
  980. var currentState = JSON.parse(decodeURIComponent(stateString));
  981. return currentState
  982. }else{
  983. return {};
  984. }
  985. }
  986. function writeHashObject(newStateObject){
  987. window.location.hash = JSON.stringify(newStateObject);
  988. }
  989. function updateStatusHash(key, value){
  990. var currentState = getHashObject();
  991. currentState[key] = value;
  992. writeHashObject(currentState);
  993. }
  994. function getOpenedFiles(){
  995. var openedFilepaths = [];
  996. editors.forEach(editor => {
  997. editor.tabs.forEach(tab => {
  998. if (!openedFilepaths.includes(tab.filepath)){
  999. openedFilepaths.push(tab.filepath);
  1000. }
  1001. })
  1002. });
  1003. return openedFilepaths;
  1004. }
  1005. function changeTab(editorObject, targettabUUID) {
  1006. //Move focus to the selected editor
  1007. focusedEditor = editorObject.uuid;
  1008. //Get the basic information for the editor object
  1009. var editor = editorObject.editor;
  1010. var models = editorObject.model;
  1011. var states = editorObject.state;
  1012. var currentTabUUID = editorObject.currentTabUUID;
  1013. //save the current editor states
  1014. var currentState = editor.saveViewState();
  1015. var currentModel = editor.getModel();
  1016. states[currentTabUUID] = currentState;
  1017. models[currentTabUUID] = currentModel;
  1018. //Update the current Tab UUID
  1019. editorObject.currentTabUUID = targettabUUID;
  1020. //Load the content from the given uuid tab
  1021. editor.setModel(models[targettabUUID]);
  1022. editor.restoreViewState(states[targettabUUID]);
  1023. editor.focus();
  1024. //Update the target file info
  1025. var tabInfo = getFocusedTabInfo();
  1026. updateFileStatusDisplay(tabInfo.filepath);
  1027. //Update title if nonVDI or update floatWindow title if under fdi
  1028. if (ao_module_virtualDesktop){
  1029. ao_module_setWindowTitle("NotepadA - " + tabInfo.filename);
  1030. }else{
  1031. document.title = "NotepadA - " + tabInfo.filename
  1032. }
  1033. //Focus this tab
  1034. focusTabWithUUID(targettabUUID);
  1035. }
  1036. //Show clipboard reminders
  1037. function showClipboardReminders(rt){
  1038. var reminderText = "This function is keyboard shortcut only.";
  1039. var iconClass = "keyboard icon";
  1040. if (rt == 1){
  1041. reminderText = "Use the keyboard shortcut Ctrl + X to cut text from the editor.";
  1042. iconClass = "cut icon";
  1043. }else if (rt == 2){
  1044. reminderText = "Use the keyboard shortcut Ctrl + C for copying text from the editor";
  1045. iconClass = "copy icon";
  1046. }else if (rt == 3){
  1047. reminderText = "Use the keyboard shortcut Ctrl + V for pasting to editor.";
  1048. iconClass = "paste icon";
  1049. }else if (rt == 4){
  1050. reminderText = "Press Ctrl + F on the editor to start searching.";
  1051. iconClass = "search icon";
  1052. }else if (rt == 5){
  1053. reminderText = "Press Ctrl + H on the editor to start replacing text.";
  1054. iconClass = "sync alternate icon";
  1055. }
  1056. $("#clipboardReminderText").text(reminderText);
  1057. $("#clipboardReminderIcon").attr("class",iconClass)
  1058. $('#clipboardReminder').modal('show');
  1059. hideContextMenu();
  1060. }
  1061. //Editor function binding
  1062. function undo(){
  1063. editorObject = getFocusedEditorObject();
  1064. editorObject.editor.trigger('aaa','undo','aaa');
  1065. editorObject.editor.focus();
  1066. }
  1067. function redo(){
  1068. editorObject = getFocusedEditorObject();
  1069. editorObject.editor.trigger('aaa','redo','aaa');
  1070. editorObject.editor.focus();
  1071. }
  1072. //Save current file as a new file
  1073. function saveCurrentFileAs(){
  1074. var tabInfo = getFocusedTabInfo();
  1075. var filepath = tabInfo.filepath;
  1076. var fileinfo = filepath.split("/");
  1077. var filename = fileinfo.pop();
  1078. var dirname = fileinfo.join("/") + "/";
  1079. ao_module_openFileSelector(writeToSaveAsFile, dirname, "new",false,{
  1080. defaultName: filename
  1081. });
  1082. hideContextMenu();
  1083. }
  1084. function writeToSaveAsFile(filedata){
  1085. for (var i=0; i < filedata.length; i++){
  1086. var filename = filedata[i].filename;
  1087. var filepath = filedata[i].filepath;
  1088. var content = getFocusedEditorObject().editor.getValue();
  1089. //Write to this filepath
  1090. syscall("writeFile",{"filepath":filepath, "content":content}, function(data){
  1091. if (data.error !== undefined){
  1092. alert(data.error);
  1093. }else{
  1094. //OK
  1095. console.log("File saved");
  1096. msgbox("save","File Saved")
  1097. }
  1098. });
  1099. //Handle only one file write
  1100. return
  1101. }
  1102. }
  1103. function saveCurrentFile(){
  1104. var tabInfo = getFocusedTabInfo();
  1105. var focusedEditor = getFocusedEditorObject();
  1106. var filepath = tabInfo.filepath;
  1107. var content = focusedEditor.editor.getValue();
  1108. saveContentToFile(tabInfo.tabUUID, filepath, content, function(data){
  1109. if (data.error !== undefined){
  1110. alert(data.error);
  1111. }else{
  1112. //OK
  1113. console.log("File saved");
  1114. msgbox("save","File Saved")
  1115. }
  1116. });
  1117. hideContextMenu();
  1118. }
  1119. function getAllOpenedTabs(){
  1120. var results = [];
  1121. editors.forEach(editor => {
  1122. let thisEditor = editor;
  1123. editor.tabs.forEach(tab => {
  1124. let thisTab = tab;
  1125. results.push({
  1126. editor: thisEditor,
  1127. tab: thisTab
  1128. })
  1129. })
  1130. });
  1131. return results;
  1132. }
  1133. function saveContentToFile(tabUUID, filepath, content, callback=undefined){
  1134. syscall("writeFile",{"filepath":filepath, "content":content}, function(data){
  1135. if (callback !== undefined){
  1136. callback(data);
  1137. }
  1138. var contextHash = content.hashCode();
  1139. //Update all the tabs that contains this filepath
  1140. for (var i =0; i < editors.length; i++){
  1141. var thisEditor = editors[i];
  1142. for (var j =0; j < thisEditor.tabs.length; j++){
  1143. var thisTab = thisEditor.tabs[j];
  1144. if (thisTab.tabUUID == tabUUID){
  1145. thisTab.saveHash = contextHash;
  1146. }
  1147. }
  1148. }
  1149. });
  1150. }
  1151. function msgbox(icon, message){
  1152. $("#messageField").html(`<i class="${icon} icon"></i> ${message}`);
  1153. $("#messageField").stop().finish().hide().fadeIn('fast').delay(5000).fadeOut('fast');
  1154. }
  1155. function setStorage(key, value){
  1156. syscall("store",{opr:"set",key:key,value:value});
  1157. /*
  1158. $.post("./store",{opr: "set", key: key, value: value}).done(function(data){
  1159. if (data.error !== undefined){
  1160. console.log(data);
  1161. }
  1162. });
  1163. */
  1164. }
  1165. function getStorage(key, callback){
  1166. syscall("store",{opr:"get",key:key}, callback);
  1167. //$.post("./store",{opr: "get", key: key}).done(callback);
  1168. }
  1169. function showColorPicker(){
  1170. ao_module_newfw({
  1171. url: "NotepadA/tools/colorpicker/index.html",
  1172. width: 365,
  1173. height: 200,
  1174. appicon: "NotepadA/img/module_icon.png",
  1175. title: "Color Picker"
  1176. });
  1177. }
  1178. function showMobiPreview(){
  1179. var currentFileData = getCurrentFocusedFileData();
  1180. if (currentFileData == null){
  1181. //No opened files
  1182. return;
  1183. }
  1184. //Encode the filedata
  1185. var fd = encodeURIComponent(JSON.stringify([currentFileData]));
  1186. ao_module_newfw({
  1187. url: "NotepadA/tools/mobipreview/index.html#" + fd,
  1188. width: 350,
  1189. height: 625,
  1190. appicon: "NotepadA/img/module_icon.png",
  1191. title: "Responsive Design Viewer"
  1192. });
  1193. }
  1194. function openInNewTab(){
  1195. //Open the editor in new tab
  1196. var currentFileData = getCurrentFocusedFileData();
  1197. if (currentFileData == null){
  1198. //No opened files
  1199. return;
  1200. }
  1201. //Encode the filedata
  1202. var fd = encodeURIComponent(JSON.stringify([currentFileData]));
  1203. window.open("./index.html#" + fd);
  1204. }
  1205. function openInNewFloatWindow(){
  1206. //Open the editor in new tab
  1207. var currentFileData = getCurrentFocusedFileData();
  1208. if (currentFileData == null){
  1209. //No opened files
  1210. return;
  1211. }
  1212. //Encode the filedata
  1213. var fd = encodeURIComponent(JSON.stringify([currentFileData]));
  1214. ao_module_newfw({
  1215. url: "NotepadA/index.html#" + fd,
  1216. width: 890,
  1217. height: 625,
  1218. appicon: "NotepadA/img/module_icon.png",
  1219. title: "NotepadA"
  1220. });
  1221. }
  1222. function newEditor(){
  1223. ao_module_newfw({
  1224. url: "NotepadA/index.html",
  1225. width: 890,
  1226. height: 625,
  1227. appicon: "NotepadA/img/module_icon.png",
  1228. title: "NotepadA"
  1229. });
  1230. }
  1231. function downloadFile(){
  1232. var currentFileData = getCurrentFocusedFileData();
  1233. if (currentFileData == null){
  1234. //No opened files
  1235. alert("No file selected")
  1236. return;
  1237. }
  1238. window.open("../../../media?file=" + currentFileData.filepath+ "&download=true");
  1239. }
  1240. function getCurrentFocusedFileData(){
  1241. var currentEditor = getFocusedEditorObject();
  1242. var focusedTabUUID = currentEditor.currentTabUUID;
  1243. for (var i = 0; i < currentEditor.tabs.length; i++){
  1244. if (currentEditor.tabs[i].tabUUID == focusedTabUUID){
  1245. //This is the tab we are looking for!
  1246. return {
  1247. filepath: currentEditor.tabs[i].filepath,
  1248. filename: currentEditor.tabs[i].filename
  1249. }
  1250. }
  1251. }
  1252. return null;
  1253. }
  1254. function hanleUserExit(){
  1255. //Check if there are opened files
  1256. var openedTabs = getAllOpenedTabs();
  1257. if (openedTabs.length > 0){
  1258. if (confirm("Some files might not be saved. Confirm exit?")){
  1259. ao_module_close();
  1260. }
  1261. }else{
  1262. ao_module_close();
  1263. }
  1264. }
  1265. //Hashcode function for comparing save stated
  1266. String.prototype.hashCode = function() {
  1267. var hash = 0;
  1268. if (this.length == 0) {
  1269. return hash;
  1270. }
  1271. for (var i = 0; i < this.length; i++) {
  1272. var char = this.charCodeAt(i);
  1273. hash = ((hash<<5)-hash)+char;
  1274. hash = hash & hash; // Convert to 32bit integer
  1275. }
  1276. return hash;
  1277. }
  1278. //Call to the system core with AJGI script
  1279. function syscall(scriptName, data, callback=undefined){
  1280. $.ajax({
  1281. url: "../system/ajgi/interface?script=NotepadA/backend/" + scriptName + ".agi",
  1282. method: "POST",
  1283. data: data,
  1284. success: function(data){
  1285. if (callback !== undefined){
  1286. callback(data);
  1287. }
  1288. }
  1289. })
  1290. }
  1291. //Drag events for fileTabs
  1292. function allowDrop(ev) {
  1293. ev.preventDefault();
  1294. }
  1295. //Drag file from directory explorer to editor tabs
  1296. function directoryFileDrag(ev){
  1297. var tabFilepath = $(ev.target).attr("filepath");
  1298. var tabFilename = $(ev.target).attr("filename");
  1299. var uuid = Date.now(); //Generate a new UUID for this
  1300. ev.dataTransfer.setData("uuid", uuid);
  1301. ev.dataTransfer.setData("filename", tabFilename);
  1302. ev.dataTransfer.setData("filepath", tabFilepath);
  1303. }
  1304. //Drag tab from one editor top to another
  1305. function fileTabDrag(ev) {
  1306. var tabUUID = $(ev.target).attr("uuid");
  1307. var tabFilepath = $(ev.target).attr("filepath");
  1308. var tabFilename = $(ev.target).text();
  1309. ev.dataTransfer.setData("uuid", tabUUID);
  1310. ev.dataTransfer.setData("filename", tabFilename);
  1311. ev.dataTransfer.setData("filepath", tabFilepath);
  1312. }
  1313. function TabDrop(ev) {
  1314. if ($(ev.target).hasClass("tabs") == false){
  1315. return;
  1316. }
  1317. ev.preventDefault();
  1318. //Get the file information
  1319. var tabUUID = ev.dataTransfer.getData("uuid");
  1320. var tabFilename = ev.dataTransfer.getData("filename");
  1321. var tabFilepath = ev.dataTransfer.getData("filepath");
  1322. if (tabFilename == "" || tabFilepath == ""){
  1323. return;
  1324. }
  1325. //Get the target dropping editor
  1326. var targetEditor = getEditor($(ev.target).attr("editoruid"));
  1327. if (targetEditor == undefined){
  1328. console.log("Editor with given ID not found");
  1329. return
  1330. }
  1331. //Open the file in the given editor
  1332. openFile(tabFilepath, true, targetEditor);
  1333. console.log(targetEditor, tabUUID, tabFilename, tabFilepath);
  1334. }
  1335. </script>
  1336. </body>
  1337. </html>