index.php 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. <?php
  2. include_once '../auth.php';
  3. if (!file_exists("tmp")){
  4. mkdir("tmp",0777,true);
  5. }
  6. ?>
  7. <!DOCTYPE html>
  8. <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  9. <html lang="en">
  10. <head>
  11. <meta charset="UTF-8">
  12. <script type='text/javascript' charset='utf-8'>
  13. // Hides mobile browser's address bar when page is done loading.
  14. window.addEventListener('load', function(e) {
  15. setTimeout(function() { window.scrollTo(0, 1); }, 1);
  16. }, false);
  17. </script>
  18. <meta charset="UTF-8">
  19. <script src="../script/jquery.min.js"></script>
  20. <script src="../script/ao_module.js"></script>
  21. <title>ArOZ Onlineβ</title>
  22. <style>
  23. body{
  24. background-color:#2b2b2b;
  25. }
  26. * {
  27. font-family: arial;
  28. }
  29. .topbar{
  30. background-color: #d6d6d6;
  31. overflow: hidden;
  32. position:fixed;
  33. top:0px;
  34. left:0;
  35. width:100%;height:25px;
  36. padding-left: 5px;
  37. }
  38. button{
  39. padding: 5;
  40. border: none;
  41. background: none;
  42. height:25px;
  43. }
  44. button:hover {
  45. background-color: #edeaea;
  46. cursor: pointer;
  47. }
  48. #codeArea{
  49. width:100%;
  50. position:fixed;
  51. top:48px;
  52. left:0px;
  53. }
  54. #tabs{
  55. background-color: #d6d6d6;
  56. position:fixed;
  57. width:100%;
  58. height:23px;
  59. top:25px;
  60. left:0px;
  61. overflow-x:auto;
  62. }
  63. .fileTab{
  64. background-color: #bcbcbc;
  65. display:inline;
  66. padding-left: 8px;
  67. padding-right: 1px;
  68. marign-left:1px;
  69. height:25px;
  70. border-bottom: 3px solid #878787;
  71. cursor: pointer;
  72. }
  73. .fileTab.focused{
  74. background-color: #edeaea;
  75. display:inline;
  76. border-bottom: 3px solid #5b4cff;
  77. cursor: pointer;
  78. }
  79. .closeBtn{
  80. display:inline;
  81. }
  82. .contextmenu{
  83. position:fixed;
  84. top:25px;
  85. left:0px;
  86. width:auto;
  87. height:auto;
  88. background-color:#d6d6d6;
  89. z-index:100;
  90. border-style: solid;
  91. border-width: 1px;
  92. border-color: #626263;
  93. font-size:small;
  94. max-height: 100%;
  95. overflow-y: auto;
  96. }
  97. .menuitem{
  98. padding-top: 2px;
  99. padding-bottom: 3px;
  100. padding-left: 25px;
  101. padding-right: 10px;
  102. }
  103. .menuitem:hover{
  104. background-color: #edeaea;
  105. cursor: pointer;
  106. }
  107. .middleFloat{
  108. position:fixed;
  109. top:10%;
  110. bottom: 10%;
  111. left: 30%;
  112. right: 30%;
  113. background-color:#efefef;
  114. padding:25px;
  115. overflow-y:auto;
  116. }
  117. .selectable{
  118. cursor: pointer;
  119. padding:1px;
  120. padding-left:10px;
  121. border: 1px solid transparent;
  122. }
  123. .selectable:hover{
  124. background-color:#ffffff;
  125. border: 1px solid #2890ff;
  126. }
  127. .scs{
  128. display:inline-block;
  129. margin: 3px;
  130. padding-left: 10px;
  131. padding-top: 10px;
  132. width: 20px !important;
  133. height:30px;
  134. border: 1px solid #c4c4c4;
  135. cursor:pointer;
  136. font-weight: bold;
  137. }
  138. .scs:hover{
  139. border: 1px solid #6e86a0;
  140. background-color:#a9c7e8;
  141. }
  142. .npalogo{
  143. background-color:#2b2b2b;
  144. color:white;
  145. padding:8px;
  146. }
  147. #tabList{
  148. position:fixed;
  149. right:0px;
  150. top:45px;
  151. min-width:100px;
  152. z-index:99;
  153. background-color:#d6d6d6;
  154. text-align:right;
  155. padding-bottom:5px;
  156. display:none;
  157. }
  158. #showList{
  159. position:absolute;
  160. top:0px;
  161. cursor:pointer;
  162. right:0px;
  163. padding:5px;
  164. margin-top:-2px;
  165. display:none;
  166. }
  167. #showList:hover{
  168. background-color:#ffffff;
  169. }
  170. </style>
  171. </head>
  172. <body>
  173. <div class="topbar">
  174. <button onClick="startTooggleMenu(this);">File</button>
  175. <button onClick="startTooggleMenu(this);">Edit</button>
  176. <button onClick="startTooggleMenu(this);">Search</button>
  177. <button onClick="startTooggleMenu(this);">Utils</button>
  178. <button onClick="startTooggleMenu(this);">Theme</button>
  179. <button onClick="startTooggleMenu(this);">Font_Size</button>
  180. <button onClick="startTooggleMenu(this);">About</button>
  181. </div>
  182. <div id="tabs" onClick="hideToggleMenu();">
  183. <div id="showList" onClick="showFullTabMenu();">⬇️ All Tabs</div>
  184. </div>
  185. <div id="tabList">
  186. </div>
  187. <div id="codeArea">
  188. </div>
  189. <div id="topbarMenu" class="contextmenu" style="display:none;">
  190. </div>
  191. <div id="aboutus" class="middleFloat" style="display:none;">
  192. <h3>📝 NotepadA ArOZ Online In-System Text Editor</h3>
  193. <p>Author: Toby Chui 2017-2019</p>
  194. <hr>
  195. <p>This web based text editor for ArOZ Online System are made possible by the ace editor, jQuery and ArOZ Project. Part of the system are licensed under BSD License or MIT license. Please refer to the individual license information under the library folder. <br><br>For the rest of the system and interface, all codes are CopyRight Toby Chui feat. IMUS Laboratory and licnesed under IMUS license (which is something similar to MIT license but with some extra licensing information about hardware). Developed under ArOZ Online System for experimental purpose.<br><br>
  196. Visit <a href="https://github.com/tobychui">https://github.com/tobychui</a> for more information.</p>
  197. <hr>
  198. <p>MIT License<p>
  199. <p style="font-size:70%;">Permission is hereby granted, free of charge, to any person obtaining a copy
  200. of this software and associated documentation files (the "Software"), to deal
  201. in the Software without restriction, including without limitation the rights
  202. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  203. copies of the Software, and to permit persons to whom the Software is
  204. furnished to do so, subject to the following conditions:
  205. The above copyright notice and this permission notice shall be included in all
  206. copies or substantial portions of the Software.
  207. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  208. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  209. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  210. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  211. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  212. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  213. SOFTWARE.</p>
  214. <p>BSD License<p>
  215. <p style="font-size:70%;">All rights reserved.
  216. Redistribution and use in source and binary forms, with or without
  217. modification, are permitted provided that the following conditions are met:
  218. 1. Redistributions of source code must retain the above copyright
  219. notice, this list of conditions and the following disclaimer.
  220. 2. Redistributions in binary form must reproduce the above copyright
  221. notice, this list of conditions and the following disclaimer in the
  222. documentation and/or other materials provided with the distribution.
  223. 3. All advertising materials mentioning features or use of this software
  224. must display the following acknowledgement:
  225. This product includes software developed by the <organization>.
  226. 4. Neither the name of the <organization> nor the
  227. names of its contributors may be used to endorse or promote products
  228. derived from this software without specific prior written permission.
  229. THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
  230. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  231. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  232. DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  233. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  234. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  235. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  236. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  237. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  238. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>
  239. <hr>
  240. <button style="background-color:white;border: 1px solid #707070;" onClick="$('#aboutus').hide();">Close</button>
  241. <br><br><br><br><br>
  242. </div>
  243. <div id="saveAsSelectionMenu" class="middleFloat" style="display:none;">
  244. <table id="directoryList" style="width:100%;font-size:80%;">
  245. <tr><th id="directoryPath">📂 /</th></tr>
  246. <tr><td>Initializing...</td></tr>
  247. </table>
  248. <br>
  249. <hr>
  250. Save Filename
  251. <input id="saveAsFilename" style="width:100%;"></input>
  252. <hr>
  253. <button style="background-color:white;border: 1px solid #707070;" onClick="saveToDirectory();">Save In Current Directory</button>
  254. <button style="background-color:white;border: 1px solid #707070;" onClick="$('#saveAsSelectionMenu').hide();">Close</button>
  255. <br><br><br><br><br>
  256. </div>
  257. <div id="specialCharInsert" class="middleFloat" style="display:none;">
  258. Insert Special Character
  259. <hr>
  260. <br>
  261. <div id="iscl" style="max-height:70%;left:0;right:0;overflow-y:scroll;overflow-wrap: break-word;">
  262. </div>
  263. <hr>
  264. <button style="background-color:white;border: 1px solid #707070;" onClick="$('#specialCharInsert').hide();">Close</button><p style="font-size:50%;display:inline-block;padding-left:20px;" id="scid">N/A</p>
  265. </div>
  266. <?php
  267. $draginFilePath = "";
  268. if (isset($_GET['filepath'])){
  269. $rootRealPath = str_replace("\\","/",realpath("../")). "/";
  270. if (file_exists($_GET['filepath']) == false){
  271. $_GET['filepath'] = "../" . $_GET['filepath'];
  272. }
  273. $fileRealPath = str_replace("\\","/",realpath($_GET['filepath']));
  274. $draginFilePath = str_replace($rootRealPath,"",$fileRealPath);
  275. echo $_GET['filepath'];
  276. }
  277. ?>
  278. <script>
  279. //Global variables
  280. var dragIn = "<?php echo $draginFilePath;?>";
  281. var theme = 'github';
  282. var isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
  283. var is_safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
  284. var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox')
  285. var lastSelectedMenuItem = "";
  286. var username = loadStorage("ArOZusername");
  287. var fontsize = 12;
  288. var VDI = !(!parent.isFunctionBar);
  289. var openedFilePath = [];
  290. var previousSavedState = true;
  291. var currentSaveAsPath = "";
  292. var insertTarget;
  293. var currentTabWidth = 0;
  294. var mainCodingWindow = true;
  295. var functionList = {
  296. File: ["📄 New","📂 Open File","Run in FloatWindow","Open current directory","Reload","💾 Save","💾 Save As","Close All Tabs","🖨 Print","Exit"],
  297. Edit: ["⤺ Undo","⤻ Redo","Open in New Tab","Insert Special Characters"],
  298. Search:["Find / Replace"],
  299. Utils:["🗃 Open Cache folder","🎨 Color Picker","📱 Mobile Preview","📔 CSS Document","📘 System Icons","🏗 gcc Builder"],
  300. Theme:["ambiance","chaos","chrome","clouds","clouds_midnight","cobalt","crimson_editor","dawn","dracula","dreamweaver","eclipse","github","gob","gruvbox","idle_fingers","iplastic","katzenmilch","kr_theme","kuroir","merbivore","merbivore_soft","mono_industrial","monokai","pastel_on_dark","solarized_dark","solarized_light","sqlserver","terminal","textmate","tomorrow","tomorrow_night","tomorrow_night_blue","tomorrow_night_bright","tomorrow_night_eighties","twilight","vibrant_ink","xcode"],
  301. Font_Size:["8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25"],
  302. About:["About NotepadA"]
  303. };
  304. //Init functions
  305. adjustCodeAreaHeight();
  306. bindListener();
  307. initTheme();
  308. initFontSize();
  309. initNotepadA();
  310. reloadAllTabs();
  311. loadAllSpecialCharacter();
  312. //Init notepadA. If there are previos stored page, load them into the environment. Or otherwise, just open a blank page.
  313. function initNotepadA(){
  314. if (!ao_module_virtualDesktop){
  315. $(".topbar").prepend('<div class="npalogo" style="display:inline;"><a href="../index.php">◀️</a> | NotepadA</div>');
  316. $(".topbar").css("padding-left","0px");
  317. }
  318. ao_module_setGlassEffectMode()
  319. ao_module_setWindowIcon("code");
  320. //ao_module_setWindowSize(1080,600);
  321. if (loadStorage("NotepadA_" + username + "_sessionFiles") != ""){
  322. var pages = JSON.parse(loadStorage("NotepadA_" + username + "_sessionFiles"));
  323. for (var i =0; i < pages.length;i++){
  324. let page = pages[i];
  325. newEditor(page);
  326. }
  327. if (dragIn != ""){
  328. //If there is a file dragin, and it hasn't been opened, open it
  329. if (pages.indexOf(dragIn) == -1){
  330. newEditor(dragIn);
  331. }else{
  332. focusTab($($(".fileTab")[pages.indexOf(dragIn)]).attr("tabid"));
  333. }
  334. //alert(dragIn);
  335. }
  336. }else{
  337. if (dragIn != ""){
  338. //If there is a file dragin, open it as well.
  339. newEditor(dragIn);
  340. }else{
  341. newTab();
  342. }
  343. }
  344. setTimeout(function(){
  345. setInterval(function(){
  346. var tabid = getFocusedTab();
  347. if (tabid.length == 0){
  348. return;
  349. }
  350. var tab = findTabWithAttr("framematch",tabid[0]);
  351. var saved = checkTabSaved(tabid[0]);
  352. if (saved != previousSavedState){
  353. previousSavedState = saved;
  354. if (saved){
  355. ao_module_setWindowTitle("NotepadA   📝 " + tab.attr("filename").replace("../../","/aor/"));
  356. document.title = "NotepadA   📝 " + tab.attr("filename").replace("../../","/aor/");
  357. }else{
  358. ao_module_setWindowTitle("NotepadA   *💾 📝 " + tab.attr("filename").replace("../../","/aor/"));
  359. document.title = "NotepadA   *💾 📝 " + tab.attr("filename").replace("../../","/aor/");
  360. }
  361. }
  362. },1000);
  363. },500);
  364. toggleFileTabsList();
  365. }
  366. //Add a new editor window to the editor (?) -->ALL FILE PATH PASSED IN MUST BE FROM AOR OR /media/storage*
  367. function newEditor(filepath){
  368. if (openedFilePath.includes(filepath)){
  369. //This page is already opened. Ignore this request and focus on the existing tab.
  370. $(".fileTab").each(function(){
  371. if ($(this).attr("filename").includes(filepath)){
  372. var tabid = $(this).attr("tabid");
  373. focusTab(tabid);
  374. }
  375. });
  376. return;
  377. }
  378. let tabid = Math.round((new Date()).getTime());
  379. var fileInternalIdentifier = "../../";
  380. var external = false;
  381. if (filepath.substring(0,14) == "/media/storage"){
  382. //This file is stored in external stoarge path. Use real path instead.
  383. fileInternalIdentifier = "";
  384. external = true;
  385. }else if (filepath.includes("SystemAOB/functions/extDiskAccess.php?file=")){
  386. //This file is opened using file explorer and it has attached an opener with the file.
  387. filepath = filepath.replace("SystemAOB/functions/extDiskAccess.php?file=","");
  388. external = true;
  389. }
  390. var tab = '<div class="fileTab" tabid="'+tabid+'" framematch="ca'+tabid+'" filename="' + fileInternalIdentifier + filepath +'">\
  391. loading... <div class="closeBtn">⨯</div>\
  392. </div>';
  393. var editorRelative = "../../";
  394. if (external){
  395. editorRelative = "";
  396. }
  397. var frame = '<div style="position:fixed;width:100%;"><iframe id="ca'+tabid+'" class="editor" src="ace/editor.php?theme='+theme+'&filename=' + editorRelative +filepath+'&fontsize=' + fontsize + '" width="100%" frameBorder="0"></iframe></div>';
  398. $("#tabs").append(tab);
  399. $("#codeArea").append(frame);
  400. updateFrameAttr('ca' + tabid);
  401. focusTab(tabid + "");
  402. if (openedFilePath.indexOf(filepath) == -1){
  403. openedFilePath.push(filepath);
  404. }
  405. setStorage("NotepadA_" + username + "_sessionFiles",JSON.stringify(openedFilePath));
  406. }
  407. function newTab(){
  408. let tabid = Math.round((new Date()).getTime());
  409. let filepath = 'NotepadA/tmp/newfile_' + tabid;
  410. var tab = '<div class="fileTab" tabid="'+tabid+'" framematch="ca'+tabid+'" filename="../../'+filepath+'">\
  411. loading... <div class="closeBtn">⨯</div>\
  412. </div>';
  413. var frame = '<div style=""><iframe id="ca'+tabid+'" class="editor" src="ace/editor.php?theme='+theme+'&filename=../../'+filepath+'&fontsize=' + fontsize + '" width="100%" frameBorder="0"></iframe></div>';
  414. $("#tabs").append(tab);
  415. $("#codeArea").append(frame);
  416. updateFrameAttr('ca' + tabid);
  417. setTimeout(function(){focusTab(tabid + "");},100);
  418. if (openedFilePath.indexOf(filepath) == -1){
  419. openedFilePath.push(filepath);
  420. }
  421. setStorage("NotepadA_" + username + "_sessionFiles",JSON.stringify(openedFilePath));
  422. }
  423. function saveToDirectory(){
  424. var targetPath = currentSaveAsPath;
  425. if (targetPath.includes("/media/storage") == false){
  426. //If it is not starting from the media root, stat with aor.
  427. //Remove the "/" at the front of the path
  428. targetPath = "../" + targetPath.substr(1);
  429. }
  430. var newfilename = $("#saveAsFilename").val();
  431. var framematch = getFocusedTab()[0];
  432. var editorContent = $("#" + framematch)[0].contentWindow.getEditorContenet();
  433. $.post( "writeCode.php", { filename: targetPath + newfilename, content: editorContent }).done(function( data ) {
  434. if (data.includes("ERROR") == false){
  435. //Finish writting to the new file. Open the new file in new tab
  436. if (targetPath.includes("/media/storage")==false && targetPath.includes("../")){
  437. targetPath = targetPath.replace("../","");
  438. }
  439. newEditor(targetPath + newfilename);
  440. $("#saveAsSelectionMenu").hide();
  441. }else{
  442. console.log(data);
  443. }
  444. });
  445. }
  446. function changeSaveAsPath(object){
  447. var newdir = $(object).attr("foldername");
  448. SetSaveAsPath(newdir);
  449. }
  450. function getSelectedText(){
  451. var id = getFocusedTab()[0];
  452. var text = $("#" + id)[0].contentWindow.getSelectedText();
  453. return text;
  454. }
  455. function insertText(text){
  456. var id = getFocusedTab()[0];
  457. var text = $("#" + id)[0].contentWindow.insertGivenText(text);
  458. }
  459. function saveToAOCC(){
  460. var content = getSelectedText();
  461. alert(content);
  462. }
  463. function SetSaveAsPath(dirpath){
  464. $("#directoryList").html("<tr><th>Loading...</th></tr>");
  465. if (dirpath == "../"){
  466. currentSaveAsPath = currentSaveAsPath.split("/")
  467. while (currentSaveAsPath.pop() == ""){
  468. ;
  469. }
  470. currentSaveAsPath = currentSaveAsPath.join("/") + "/";
  471. $.ajax({url: "getDir.php?directory=" + currentSaveAsPath, success: function(result){
  472. if (result.includes("ERROR") == false){
  473. //Update the current saveAs menu to latest directory
  474. $("#directoryList").html("<tr><th>📂 " + currentSaveAsPath + "</th></tr>");
  475. if (currentSaveAsPath != "/" && currentSaveAsPath != "/media/"){
  476. $("#directoryList").append('<tr><td class="selectable" foldername="../" onClick="changeSaveAsPath(this);">⏎ ../</td></tr>');
  477. }
  478. for (var i = 0; i < result.length; i++){
  479. var decodedFilename = ao_module_codec.decodeHexFoldername(result[i][1]);
  480. $("#directoryList").append('<tr><td class="selectable" foldername="'+result[i][1]+'" onClick="changeSaveAsPath(this);"> 📂 '+decodedFilename+'</td></tr>');
  481. }
  482. }else{
  483. console.log(result);
  484. }
  485. }});
  486. }else{
  487. currentSaveAsPath += dirpath + "/";
  488. $.ajax({url: "getDir.php?directory=" + currentSaveAsPath, success: function(result){
  489. if (result.includes("ERROR") == false){
  490. //Update the current saveAs menu to latest directory
  491. $("#directoryList").html("<tr><th>📂 " + currentSaveAsPath + "</th></tr>");
  492. if (currentSaveAsPath != "/" && currentSaveAsPath != "/media/"){
  493. $("#directoryList").append('<tr><td class="selectable" foldername="../" onClick="changeSaveAsPath(this);">⏎ ../</td></tr>');
  494. }
  495. for (var i = 0; i < result.length; i++){
  496. var decodedFilename = ao_module_codec.decodeHexFoldername(result[i][1]);
  497. $("#directoryList").append('<tr><td class="selectable" foldername="'+result[i][1]+'" onClick="changeSaveAsPath(this);"> 📂 '+decodedFilename+'</td></tr>');
  498. }
  499. }else{
  500. console.log(result);
  501. }
  502. }});
  503. }
  504. }
  505. function adjustCodeAreaHeight(){
  506. if (VDI){
  507. if (isFirefox){
  508. $("#tabs").css("top","22px").css("height","24px");
  509. $("#codeArea").css("top","45px")
  510. }
  511. if (isChrome){
  512. $("#tabs").css("top","22px").css("height","22px");
  513. $("#codeArea").css("top","42px");
  514. }
  515. if (is_safari){
  516. $("#tabs").css("top","22px").css("height","21px");
  517. $("#codeArea").css("top","41px");
  518. }
  519. var h = window.innerHeight;
  520. if (isChrome){
  521. h = h - 21;
  522. }else if (is_safari){
  523. h = h - 20;
  524. }else{
  525. h = h - 24;
  526. }
  527. $("#codeArea").css("height",h);
  528. $(".editor").each(function(i) {
  529. $(this).attr("height",h - 6);
  530. });
  531. }else{
  532. $(".editor").each(function(i) {
  533. $(this).css("height",$(document).height() - 38);
  534. });
  535. }
  536. }
  537. function checkTabSaved(framematch){
  538. if (typeof $("#" + framematch)[0].contentWindow.checkIsSaved !== "undefined") {
  539. var result = $("#" + framematch)[0].contentWindow.checkIsSaved();
  540. return result;
  541. }
  542. }
  543. $( window ).resize(function() {
  544. adjustCodeAreaHeight();
  545. toggleFileTabsList();
  546. });
  547. function showFullTabMenu(){
  548. $("#tabList").slideToggle('fast');
  549. }
  550. var inList = false;
  551. function toggleFileTabsList(){
  552. //Check if the width of the container is enough for holding all the filetabs. If no, move all of them into the filtab list.
  553. var totalWidthOfFileTabs = 0;
  554. var maxWidth = 0;
  555. $("#tabList").show();
  556. $(".fileTab").each(function(){
  557. totalWidthOfFileTabs = totalWidthOfFileTabs + $(this).width();
  558. if ($(this).width() > maxWidth){
  559. maxWidth = $(this).width();
  560. }
  561. });
  562. $("#tabList").hide();
  563. totalWidthOfFileTabs = parseInt(totalWidthOfFileTabs);
  564. if (window.innerWidth < totalWidthOfFileTabs && !inList){
  565. //window innerWidth space is less than the space needed to put all tabs. Move all of them into the list instead.
  566. $(".fileTab").each(function(){
  567. $("#tabList").append($(this));
  568. $("#tabList").append("<br>");
  569. });
  570. inList = true;
  571. $("#showList").show();
  572. $("#tabList").show();
  573. }else if (window.innerWidth > totalWidthOfFileTabs && inList){
  574. $(".fileTab").each(function(){
  575. $("#tabs").append($(this));
  576. });
  577. $("#tabList").html("");
  578. inList = false;
  579. $("#showList").hide();
  580. $("#tabList").hide();
  581. }
  582. currentTabWidth = totalWidthOfFileTabs;
  583. }
  584. function bindListener(){
  585. $(document).on('click', '.closeBtn', function () {
  586. removeTab($(this).parent().attr('tabid'));
  587. });
  588. $(document).on('click','.fileTab',function(){
  589. if ($(this).hasClass("closeBtn") == false){
  590. focusTab($(this).attr('tabid'));
  591. }
  592. });
  593. $(document).on('click','#codeArea',function(){
  594. $("#topbarMenu").hide();
  595. });
  596. $(document).on('click','.menuitem',function(){
  597. if (lastSelectedMenuItem == "Theme"){
  598. setTheme($(this).text());
  599. $("#topbarMenu").hide();
  600. }else if (lastSelectedMenuItem == "Font_Size"){
  601. setFontSize($(this).text());
  602. $("#topbarMenu").hide();
  603. }else if (lastSelectedMenuItem == "File"){
  604. handleFileMenu($(this).text());
  605. $("#topbarMenu").hide();
  606. }else if (lastSelectedMenuItem == "About"){
  607. handleAboutMenu($(this).text());
  608. $("#topbarMenu").hide();
  609. }else if (lastSelectedMenuItem == "Search"){
  610. if ($(this).text() != ""){
  611. launchFocusedTabSearchBox();
  612. }
  613. $("#topbarMenu").hide();
  614. }else if (lastSelectedMenuItem == "Edit"){
  615. handleEditMenu($(this).text());
  616. $("#topbarMenu").hide();
  617. }else if (lastSelectedMenuItem == "Utils"){
  618. handleCacheMenu($(this).text());
  619. $("#topbarMenu").hide();
  620. }else{
  621. alert(lastSelectedMenuItem);
  622. }
  623. });
  624. }
  625. function handleCacheMenu(itemText){
  626. var indexvalue = functionList.Utils.indexOf(itemText);
  627. switch (indexvalue){
  628. case 0:
  629. var url = "SystemAOB/functions/file_system/index.php?controlLv=2&finishing=embedded&subdir=NotepadA/tmp";
  630. if (!VDI){
  631. window.open("../" + url);
  632. break;
  633. }
  634. var uid = Math.round((new Date()).getTime() / 1000);
  635. var title = "NotepadA" + " - Cache View";
  636. var icon = "folder open";
  637. newfw(url,title,icon,uid,1080,580);
  638. break;
  639. case 1:
  640. var url = "NotepadA/utils/colorpicker/";
  641. var uid = "colorpicker";
  642. var title = "NotepadA" + " - Color Picker";
  643. var icon = "tint";
  644. newfw(url,title,icon,uid,360,196,undefined,undefined,false,true);
  645. break;
  646. case 2:
  647. var id = getFocusedTab()[0];
  648. var filepath = $("#" + id)[0].contentWindow.getFilepath().replace("../","");
  649. //Change the filepath relative from AOR to the preview script's relative path
  650. filepath = "../../../" + filepath;
  651. var url = "NotepadA/utils/mobipreview/index.php?preview=" + filepath;
  652. var uid = Math.round((new Date()).getTime() / 1000);
  653. var title = "NotepadA" + " - Mobile Preview";
  654. var icon = "mobile";
  655. newfw(url,title,icon,uid,335,550,undefined,undefined,true,true);
  656. break;
  657. case 3:
  658. //Open the CSS document in a new float window
  659. var url = "NotepadA/utils/tocasdoc/index.php";
  660. var uid = Math.round((new Date()).getTime() / 1000);
  661. var title = "NotepadA" + " - CSS LookUp";
  662. var icon = "css3";
  663. newfw(url,title,icon,uid,450,565,undefined,undefined,true,true);
  664. break;
  665. case 4:
  666. //Open the CSS document in a new float window
  667. var url = "NotepadA/utils/tocasdoc/icons.php";
  668. var uid = Math.round((new Date()).getTime() / 1000);
  669. var title = "NotepadA" + " - System Icon List";
  670. var icon = "bookmark";
  671. newfw(url,title,icon,uid,335,550,undefined,undefined,false,true);
  672. break;
  673. case 5:
  674. //Open gcc builder
  675. var url = "NotepadA/utils/gcci/index.php";
  676. var uid = Math.round((new Date()).getTime() / 1000);
  677. var title = "NotepadA" + " - GCCI";
  678. var icon = "code";
  679. newfw(url,title,icon,uid,420,550,undefined,undefined,false,true,ao_module_windowID);
  680. break;
  681. }
  682. }
  683. function handleEditMenu(itemText){
  684. var indexvalue = functionList.Edit.indexOf(itemText);
  685. switch (indexvalue){
  686. case 0:
  687. var id = getFocusedTab()[0];
  688. $("#" + id)[0].contentWindow.callUndo();
  689. break;
  690. case 1:
  691. var id = getFocusedTab()[0];
  692. $("#" + id)[0].contentWindow.callRedo();
  693. break;
  694. case 2:
  695. var id = getFocusedTab()[0];
  696. $("#" + id)[0].contentWindow.openInNewTab();
  697. //Call to openInNewTab function for opening new tab;
  698. break;
  699. case 3:
  700. //Insert a special character into the passage
  701. $("#specialCharInsert").show();
  702. var id = getFocusedTab()[0];
  703. insertTarget = $("#" + id)[0].contentWindow;
  704. break;
  705. }
  706. }
  707. function updateFrameAttr(framematch){
  708. //This function will update the tab title, iframe attr at the same time.
  709. var frame = $("#" + framematch);
  710. var tab = findTabWithAttr("framematch",framematch);
  711. var filepath = tab.attr("filename");
  712. var filedata = getFilenameAndBasedir(filepath);
  713. var basedir = filedata[0];
  714. var filename = filedata[1];
  715. //Update the tag text to the filename
  716. if (filename.substring(0,5) == "inith"){
  717. filename = ao_module_codec.decodeUmFilename(filename);
  718. }
  719. $(tab).html(filename + ' <div class="closeBtn">⨯</div>');
  720. //Update the iframe attr
  721. $(frame).attr("basedir",basedir);
  722. $(frame).attr("filename",filename);
  723. //
  724. adjustCodeAreaHeight();
  725. }
  726. function getFilenameAndBasedir(filepath){
  727. var f = filepath.split("/").pop();
  728. var b = filepath.replace(f,"");
  729. return [b,f];
  730. }
  731. function reloadAllTabs(){
  732. $(".fileTab").each(function(){
  733. var tid = $(this).attr("tabid");
  734. reloadTab(tid + "");
  735. updateFrameAttr($(this).attr("framematch"));
  736. });
  737. }
  738. function reloadTab(tabID){
  739. var object = findTabWithAttr("tabid",tabID);
  740. var filename = $(object).attr("filename");
  741. var codeAreaID = $(object).attr("framematch");
  742. $("#" + codeAreaID).attr("src","ace/editor.php?theme=" + theme + "&fontsize="+fontsize+"&filename=" + filename);
  743. }
  744. function launchFocusedTabSearchBox(){
  745. var framematch = getFocusedTab()[0];
  746. $("#" +framematch)[0].contentWindow.startSearchBox();
  747. //alert(framematch);
  748. }
  749. function handleOpenFileFunctionCall(fileData){
  750. result = JSON.parse(fileData);
  751. result = result[0]; //As only one file will be selected each time
  752. var filepath = result.filepath;
  753. var filename = result.filename;
  754. var match = false;
  755. var tabid = "";
  756. $(".fileTab").each(function(){
  757. if ($(this).attr("filename") == filepath){
  758. match = true;
  759. tabid = $(this).attr("tabid");
  760. }
  761. });
  762. if (!match){
  763. //This file is not opened yet. Open it
  764. newEditor(filepath);
  765. }else{
  766. //This file is opened. Focus to that tab
  767. focusTab(tabid);
  768. }
  769. }
  770. function handleFileMenu(itemText){
  771. var indexvalue = functionList.File.indexOf(itemText);
  772. switch (indexvalue){
  773. case 0:
  774. //New file
  775. newTab();
  776. break;
  777. case 1:
  778. //Open a file by fileSelector
  779. var uid = new Date().getTime();
  780. if (VDI){
  781. ao_module_openFileSelector(uid,"handleOpenFileFunctionCall");
  782. }else{
  783. ao_module_openFileSelectorTab(uid,"../",true,"file",handleOpenFileFunctionCall);
  784. }
  785. break;
  786. case 2:
  787. //Run this script in floatWindow
  788. if (!VDI){
  789. alert("[ERROR] Please launch the NotepadA in Virtual Desktop Mode to launch FloatWindow.");
  790. break;
  791. }
  792. var id = getFocusedTab()[0];
  793. var uid = Math.round((new Date()).getTime() / 1000);
  794. var url = $("#" + id)[0].contentWindow.getFilepath().replace("../","");
  795. var title = "NotepadA Runtime";
  796. var icon = "code";
  797. if (url.substr(0,14) == "/media/storage"){
  798. title += " (External Storage)";
  799. url = "SystemAOB/functions/extDiskAccess.php?file=" + url;
  800. newfw(url,title,icon,uid,1080,580);
  801. }else{
  802. newfw(url,title,icon,uid,1080,580);
  803. }
  804. break;
  805. case 3:
  806. //Open the current folder in explorer
  807. var base = getFocusedTab()[1];
  808. if (base == undefined){
  809. break;
  810. }
  811. if (base.includes("../../")){
  812. base = base.replace("../../","");
  813. }
  814. var url = "SystemAOB/functions/file_system/index.php?controlLv=2&finishing=embedded&subdir=" + base;
  815. if (base.substr(0,14) == "/media/storage"){
  816. //This file is located in external storage location. Open file editor in ext mode
  817. url = "SystemAOB/functions/file_system/index.php?controlLv=2&finishing=embedded&dir=" + base;
  818. }
  819. var uid = Math.round((new Date()).getTime() / 1000);
  820. var title = "NotepadA" + " - Folder View";
  821. var icon = "folder open";
  822. if (!VDI){
  823. window.open("../" + url);
  824. break;
  825. }
  826. newfw(url,title,icon,uid,1080,580);
  827. break;
  828. case 4:
  829. //Reload is pressed
  830. var currentFocusedtid = getFocusedTab()[0];
  831. if (currentFocusedtid == undefined){
  832. break;
  833. }
  834. var tabobject = findTabWithAttr("framematch",currentFocusedtid);
  835. reloadTab(tabobject.attr("tabid") + "");
  836. break;
  837. case 5:
  838. //Save is pressed
  839. var currentFocusedtid = getFocusedTab()[0];
  840. if (currentFocusedtid == undefined){
  841. break;
  842. }
  843. $("#" + currentFocusedtid)[0].contentWindow.Save();
  844. break;
  845. case 6:
  846. //save-as is pressed. Pop up the saveas menu to continue
  847. $("#saveAsSelectionMenu").show();
  848. currentSaveAsPath = "";
  849. SetSaveAsPath("");
  850. var focusedFramematch = getFocusedTab()[0];
  851. var filename = $("#" + focusedFramematch).attr("filename");
  852. var ext = filename.split(".").pop();
  853. if (ext == filename){
  854. ext = "txt";
  855. }
  856. setTimeout( function() {$("#saveAsSelectionMenu").scrollTop(0)}, 200 );
  857. $("#saveAsFilename").val("untitled." + ext);
  858. break;
  859. case 7:
  860. $(".fileTab").each(function(){
  861. removeTab($(this).attr("tabid"));
  862. });
  863. break;
  864. case 8:
  865. var id = getFocusedTab()[0];
  866. $("#" + id)[0].contentWindow.Print();
  867. break;
  868. case 9:
  869. if (VDI){
  870. window.location.href = "../SystemAOB/functions/killProcess.php"
  871. }else{
  872. window.location.href = "../index.php"
  873. }
  874. break;
  875. }
  876. }
  877. function handleAboutMenu(itemText){
  878. var indexvalue = functionList.About.indexOf(itemText);
  879. if (indexvalue == 0){
  880. $("#aboutus").show();
  881. }
  882. }
  883. function newfw(src,windowname,icon,uid,sizex,sizey,posx = undefined,posy = undefined,fixsize = undefined,tran = undefined, parentUID=undefined, callbackFunct=undefined){
  884. //Example
  885. //newEmbededWindow('Memo/index.php','Memo','sticky note outline','memoEmbedded',475,700);
  886. if (!VDI){
  887. window.open("../" + src);
  888. return;
  889. }
  890. parent.newEmbededWindow(src,windowname,icon,uid,sizex,sizey,posx,posy,fixsize,tran,parentUID,callbackFunct);
  891. }
  892. function getFocusedTab(){
  893. result = [];
  894. $(".fileTab").each(function(){
  895. if ($(this).hasClass("focused")){
  896. //This tab is focused. Check its filename and pathinfo
  897. var id = $(this).attr("framematch");
  898. var filename = $("#" + id).attr("filename");
  899. var basedir = $("#" + id).attr("basedir");
  900. result = [id,basedir,filename];
  901. }
  902. });
  903. return result;
  904. }
  905. function setTheme(themeName){
  906. setStorage("NotepadA_theme",themeName);
  907. theme = themeName;
  908. if (checkIfAllTabSaved() == false){
  909. //Ask the user if confirm close
  910. var confirmClose = confirm("Reloading NotepadA is required to apply the changes. Confirm?");
  911. if (confirmClose == false){
  912. return false;
  913. }
  914. }
  915. console.log("Updating theme to: " + themeName);
  916. //Reload all tabs with the corrispoding themes after changing theme settings
  917. reloadAllTabs();
  918. }
  919. function setFontSize(newsize){
  920. setStorage("NotepadA_fontsize",newsize);
  921. fontsize = newsize;
  922. if (checkIfAllTabSaved() == false){
  923. //Ask the user if confirm close
  924. var confirmClose = confirm("Reloading NotepadA is required to apply the changes. Confirm?");
  925. if (confirmClose == false){
  926. return false;
  927. }
  928. }
  929. reloadAllTabs();
  930. }
  931. function checkIfAllTabSaved(){
  932. var allSaved = true;
  933. $(".editor").each(function(){
  934. if ($(this)[0].contentWindow.checkIsSaved() == false){
  935. allSaved = false;
  936. }
  937. });
  938. return allSaved;
  939. }
  940. function initTheme(){
  941. if (loadStorage("NotepadA_theme") != ""){
  942. theme = loadStorage("NotepadA_theme");
  943. }
  944. }
  945. function initFontSize(){
  946. if (loadStorage("NotepadA_fontsize") != ""){
  947. fontsize = loadStorage("NotepadA_fontsize");
  948. }
  949. }
  950. function setStorage(configName,configValue){
  951. //localStorage.setItem(name, value);
  952. $.ajax({
  953. type: 'POST',
  954. url: "../SystemAOB/functions/user/userGlobalConfig.php",
  955. data: {module: "NotepadA",name:configName,value:configValue},
  956. success: function(data){},
  957. async:true
  958. });
  959. return true;
  960. }
  961. function loadStorage(configName){
  962. /* if (localStorage.getItem(name) == null){
  963. return "";
  964. }else{
  965. return localStorage.getItem(name);
  966. } */
  967. var result = "";
  968. $.ajax({
  969. type: 'POST',
  970. url: "../SystemAOB/functions/user/userGlobalConfig.php",
  971. data: {module: "NotepadA",name:configName},
  972. success: function(data){result = data;},
  973. error: function(data){result = "";},
  974. async:false,
  975. timeout: 3000
  976. });
  977. return result;
  978. }
  979. function removeTab(tabid){
  980. var focusingTabFramematchBeforeRemove = getFocusedTab()[0];
  981. let origianlTabID = findTabWithAttr("framematch",focusingTabFramematchBeforeRemove).attr("tabid");
  982. var targetTab = findTabWithAttr("tabid",tabid);
  983. var filepath = targetTab.attr("filename").replace("../../","");
  984. var codeAreaTag = $(targetTab).attr("framematch");
  985. //Check if the tab is saved
  986. var saved = checkTabSaved(targetTab.attr("framematch"));
  987. if (saved == false){
  988. //Ask the user if confirm close
  989. var confirmClose = confirm("This file is not saved. Confirm closing?");
  990. if (confirmClose == false){
  991. return false;
  992. }
  993. }
  994. //Remove this record from the filepath so we will not open this again by the enxt time we startup NotepadA
  995. openedFilePath.remove(filepath);
  996. if ($(targetTab).hasClass("focused")){
  997. //This tab is currently focused, move focus to another tab after closing
  998. $(targetTab).remove();
  999. //I have no idea why this only works with a delay... but just don't change this.
  1000. setTimeout(function(){
  1001. var focusTarget = $(".fileTab").first().attr("tabid");
  1002. focusTab(focusTarget); }, 100);
  1003. }else{
  1004. $(targetTab).remove();
  1005. setTimeout(function(){ focusTab(origianlTabID);}, 100);
  1006. }
  1007. $("#" + codeAreaTag).remove();
  1008. if ($(".fileTab").length == 0){
  1009. //There is no more tab left, create a new tab instead
  1010. setTimeout(function(){ newTab("untitled"); }, 100);
  1011. }
  1012. setStorage("NotepadA_" + username + "_sessionFiles",JSON.stringify(openedFilePath));
  1013. toggleFileTabsList();
  1014. }
  1015. function hideToggleMenu(){
  1016. $("#topbarMenu").hide();
  1017. }
  1018. $(".scs").hover(function(){
  1019. var keyid = $(this).attr("keyid");
  1020. $("#scid").html("HTML Keycode: #&" + keyid);
  1021. });
  1022. $(".scs").on("mousedown",function(){
  1023. insertTarget.insertChar($(this).text());
  1024. $("#specialCharInsert").hide();
  1025. });
  1026. function startTooggleMenu(object){
  1027. var menu = $(object).text();
  1028. var pos = $(object).offset().left - 5;
  1029. toogleMenu(pos,menu);
  1030. }
  1031. function toogleMenu(left,contentID){
  1032. $("#topbarMenu").css("left",left + "px");
  1033. if (contentID != lastSelectedMenuItem && $("#topbarMenu").is(":visible")){
  1034. $("#topbarMenu").hide();
  1035. }
  1036. loadOptionToMenu(contentID);
  1037. $("#topbarMenu").toggle();
  1038. lastSelectedMenuItem = contentID;
  1039. }
  1040. function loadOptionToMenu(menuItem){
  1041. var items = functionList[menuItem];
  1042. $("#topbarMenu").html("");
  1043. for (var i=0; i < items.length;i++){
  1044. $("#topbarMenu").append("<div class='menuitem'>" + items[i] + "</div>");
  1045. }
  1046. }
  1047. function focusTab(tabid){
  1048. //Defocus every tabs and hide all coding windows
  1049. $(".fileTab").each(function(i) {
  1050. $(this).removeClass("focused");
  1051. });
  1052. $(".editor").each(function(i) {
  1053. $(this).hide();
  1054. });
  1055. //Only show the tab and code window selected
  1056. var selectedTab = findTabWithAttr("tabid",tabid);
  1057. $(selectedTab).addClass("focused");
  1058. var corrispondingCodingTab = $(selectedTab).attr("framematch");
  1059. var targetFrame = $("#" + corrispondingCodingTab);
  1060. targetFrame.show();
  1061. if (selectedTab.attr("filename") != undefined){
  1062. ao_module_setWindowTitle("NotepadA   📝 " + selectedTab.attr("filename").replace("../../","/aor/"));
  1063. document.title = "NotepadA   📝 " + selectedTab.attr("filename").replace("../../","/aor/");
  1064. }else{
  1065. ao_module_setWindowTitle("NotepadA   📝 newfile");
  1066. document.title = "NotepadA   📝 newfile";
  1067. }
  1068. }
  1069. function findTabWithAttr(attr,value){
  1070. return $('div['+attr+'="'+value+'"]').each(function() {
  1071. return this;
  1072. });
  1073. }
  1074. function loadAllSpecialCharacter(){
  1075. $("#iscl").html("");
  1076. for (var i =161; i < 1023; i++){
  1077. if (i != 173){
  1078. $("#iscl").append("<div class='scs' keyid='" + i +"'>" + String.fromCharCode(i) + "</div>");
  1079. }
  1080. }
  1081. }
  1082. Array.prototype.remove = function() {
  1083. var what, a = arguments, L = a.length, ax;
  1084. while (L && this.length) {
  1085. what = a[--L];
  1086. while ((ax = this.indexOf(what)) !== -1) {
  1087. this.splice(ax, 1);
  1088. }
  1089. }
  1090. return this;
  1091. };
  1092. </script>
  1093. </body>
  1094. </html>