ao_module.js 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148
  1. /*
  2. ArOZ Online Module Javascript Wrapper
  3. This is a wrapper for module developers to access system API easier and need not to dig through the source code.
  4. Basically: Write less do more (?)
  5. WARNING! SOME FUNCTION ARE NOT COMPATIBILE WITH PREVIOUS VERSION OF AO_MODULE.JS.
  6. PLEASE REFER TO THE SYSTEM DOCUMENTATION FOR MORE INFORMATION.
  7. *** Please include this javascript file with relative path instead of absolute path.
  8. E.g. ../script/ao_module.js (OK)
  9. /script/ao_module.js (NOT OK)
  10. */
  11. var ao_module_virtualDesktop = false;
  12. try{
  13. ao_module_virtualDesktop = !(!parent.isDesktopMode);
  14. }catch(ex){
  15. //Running ArozOS inside iframe for some reason
  16. console.log("CORS Access Error. Entering compatibility mode with virtual desktop mode disabled.");
  17. }
  18. var ao_root = null;
  19. //Get the current windowID if in Virtual Desktop Mode, return false if VDI is not detected
  20. var ao_module_windowID = false;
  21. var ao_module_parentID = false;
  22. var ao_module_callback = false;
  23. var ao_module_ime = false;
  24. if (ao_module_virtualDesktop)ao_module_windowID = $(window.frameElement).parent().parent().attr("windowId");
  25. if (ao_module_virtualDesktop)ao_module_parentID = $(window.frameElement).parent().parent().attr("parent");
  26. if (ao_module_virtualDesktop)ao_module_callback = $(window.frameElement).parent().parent().attr("callback");
  27. if (ao_module_virtualDesktop)ao_module_parentURL = $(window.frameElement).parent().find("iframe").attr("src");
  28. ao_root = ao_module_getAORootFromScriptPath();
  29. /*
  30. Event bindings
  31. The following events are required for ao_module to operate normally
  32. under Web Desktop Mode.
  33. */
  34. document.addEventListener("DOMContentLoaded", function() {
  35. if (ao_module_virtualDesktop){
  36. if (parent.window.ime == null){
  37. return;
  38. }
  39. //Add window focus handler
  40. document.addEventListener("mousedown", function(event) {
  41. //When click on this document, focus this
  42. ao_module_focus();
  43. if (event.target.tagName == "INPUT" || event.target.tagName == "TEXTAREA"){
  44. }else{
  45. if (parent.window.ime.focus != null){
  46. if (ao_module_ime){
  47. //This is clicking on ime windows. Do not change focus
  48. }else{
  49. parent.window.ime.focus = null;
  50. }
  51. }
  52. }
  53. }, true);
  54. //Add IME registration handler
  55. var inputFields = document.querySelectorAll("input,textarea");
  56. for (var i = 0; i < inputFields.length; i++){
  57. if ($(inputFields[i]).attr("type") != undefined){
  58. var thisType = $(inputFields[i]).attr("type");
  59. if ((thisType == "text" || thisType =="search" || thisType =="url")){
  60. //Supported types of input
  61. ao_module_bindCustomIMEEvents(inputFields[i]);
  62. }else{
  63. //Not supported type of inputs
  64. }
  65. }else{
  66. //text area
  67. ao_module_bindCustomIMEEvents(inputFields[i]);
  68. }
  69. }
  70. }
  71. /*
  72. //Load html2canvas
  73. if ($){
  74. $.getScript(ao_root + "script/html2canvas.min.js", function() {
  75. console.log("Html2canvas loaded")
  76. });
  77. }
  78. */
  79. });
  80. /*
  81. Startup Section Script
  82. These functions handle the startup of an ao_module and adapt them into the
  83. standard arozos desktop eco-system api
  84. */
  85. //Function handle to bind custom IME events
  86. function ao_module_bindCustomIMEEvents(object){
  87. parent.bindObjectToIMEEvents(object);
  88. }
  89. function ao_module_screenshot(callback){
  90. html2canvas(document.querySelector("body")).then(screenshot => {
  91. return callback(screenshot);
  92. });
  93. }
  94. //Get the ao_root from script includsion path
  95. function ao_module_getAORootFromScriptPath(){
  96. var possibleRoot = "";
  97. $("script").each(function(){
  98. if (this.hasAttribute("src") && $(this).attr("src").includes("ao_module.js")){
  99. var tmp = $(this).attr("src");
  100. tmp = tmp.split("script/ao_module.js");
  101. possibleRoot = tmp[0];
  102. }
  103. });
  104. return possibleRoot;
  105. }
  106. //Get the input filename and filepath from the window hash paramter
  107. function ao_module_loadInputFiles(){
  108. try{
  109. if (window.location.hash.length == 0){
  110. return null;
  111. }
  112. var inputFileInfo = window.location.hash.substring(1,window.location.hash.length);
  113. inputFileInfo = JSON.parse(decodeURIComponent(inputFileInfo));
  114. return inputFileInfo
  115. }catch{
  116. return null;
  117. }
  118. }
  119. //Set the ao_module window to fixed size (not allowing resize)
  120. function ao_module_setFixedWindowSize(){
  121. if (!ao_module_virtualDesktop){
  122. return;
  123. }
  124. parent.setFloatWindowResizePolicy(ao_module_windowID, false);
  125. }
  126. //Restore a float window to be resizble
  127. function ao_module_setResizableWindowSize(){
  128. if (!ao_module_virtualDesktop){
  129. return;
  130. }
  131. parent.setFloatWindowResizePolicy(ao_module_windowID, true);
  132. }
  133. //Update the window size of the given float window object
  134. function ao_module_setWindowSize(width, height){
  135. if (!ao_module_virtualDesktop){
  136. return;
  137. }
  138. parent.setFloatWindowSize(ao_module_windowID, width, height)
  139. }
  140. //Update the floatWindow title
  141. function ao_module_setWindowTitle(newTitle){
  142. if (!ao_module_virtualDesktop){
  143. document.title = newTitle;
  144. return;
  145. }
  146. parent.setFloatWindowTitle(ao_module_windowID, newTitle);
  147. }
  148. //Set new window theme, default dark, support {dark/white}
  149. function ao_module_setWindowTheme(newtheme="dark"){
  150. if (!ao_module_virtualDesktop){
  151. return;
  152. }
  153. parent.setFloatWindowTheme(ao_module_windowID, newtheme);
  154. }
  155. //Check if there are any windows with the same path.
  156. //If yes, replace its hash content and reload to the new one and close the current floatWindow
  157. function ao_module_makeSingleInstance(){
  158. $(window.parent.document).find(".floatWindow").each(function(){
  159. if ($(this).attr("windowid") == ao_module_windowID){
  160. return
  161. }
  162. var currentPath = window.location.pathname;
  163. if ("/" + $(this).find("iframe").attr('src').split("#").shift() == currentPath){
  164. //Another instance already running. Replace it with the current path
  165. $(this).find("iframe").attr('src', window.location.pathname.substring(1) + window.location.hash);
  166. $(this).find("iframe")[0].contentWindow.location.reload();
  167. //Move the other instant to top
  168. var targetfw = parent.getFloatWindowByID($(this).attr("windowid"))
  169. parent.MoveFloatWindowToTop(targetfw);
  170. //Close the instance
  171. ao_module_close();
  172. return true
  173. }
  174. });
  175. return false
  176. }
  177. //Use for cross frame communication, example:
  178. //let targetOpeningInstances = ao_module_getInstanceByPath("NotepadA/index.html")
  179. function ao_module_getInstanceByPath(matchingPath){
  180. let targetInstance = "";
  181. $(window.parent.document).find(".floatWindow").each(function(){
  182. if ($(this).attr("windowid") == ao_module_windowID){
  183. return
  184. }
  185. let thisfwPath = $(this).find("iframe").attr('src').split("#").shift();
  186. if (thisfwPath == matchingPath){
  187. targetInstance = $(this).attr("windowid");
  188. }
  189. });
  190. if (targetInstance == ""){
  191. return null;
  192. }
  193. return parent.getFloatWindowByID(targetInstance);
  194. }
  195. //Close the current window
  196. function ao_module_close(){
  197. if (!ao_module_virtualDesktop){
  198. window.close('','_parent','');
  199. window.location.href = ao_root + "SystemAO/closeTabInsturction.html";
  200. return;
  201. }
  202. parent.closeFwProcess(ao_module_windowID);
  203. }
  204. //Focus this floatWindow
  205. function ao_module_focus(){
  206. parent.MoveFloatWindowToTop(parent.getFloatWindowByID(ao_module_windowID));
  207. }
  208. //Set the floatWindow to top most mode
  209. function ao_module_setTopMost(){
  210. parent.PinFloatWindowToTopMostMode(parent.getFloatWindowByID(ao_module_windowID));
  211. }
  212. //Unset the floatWindow top most mode
  213. function ao_module_unsetTopMost(){
  214. parent.UnpinFloatWindowFromTopMostMode(parent.getFloatWindowByID(ao_module_windowID));
  215. }
  216. //Popup a file selection window for uplaod
  217. function ao_module_selectFiles(callback, fileType="file", accept="*", allowMultiple=false){
  218. var input = document.createElement('input');
  219. input.type = fileType;
  220. input.multiple = allowMultiple;
  221. input.accept = accept;
  222. input.onchange = e => {
  223. var files = e.target.files;
  224. callback(files);
  225. }
  226. input.click();
  227. }
  228. //Open a path with File Manager, optional highligh filename
  229. function ao_module_openPath(path, filename=undefined){
  230. //Trim away the last / if exists
  231. if (path.substr(path.length - 1, 1) == "/"){
  232. path = path.substr(0, path.length - 1);
  233. }
  234. if (filename == undefined){
  235. if (ao_module_virtualDesktop){
  236. parent.newFloatWindow({
  237. url: "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(path),
  238. appicon: "SystemAO/file_system/img/small_icon.png",
  239. width:1080,
  240. height:580,
  241. title: "File Manager"
  242. });
  243. }else{
  244. window.open(ao_root + "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(path))
  245. }
  246. }else{
  247. var fileObject = [{
  248. filepath: path + "/" + filename,
  249. filename: filename,
  250. }];
  251. if (ao_module_virtualDesktop){
  252. parent.newFloatWindow({
  253. url: "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(JSON.stringify(fileObject)),
  254. appicon: "SystemAO/file_system/img/small_icon.png",
  255. width:1080,
  256. height:580,
  257. title: "File Manager"
  258. });
  259. }else{
  260. window.open(ao_root + "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(JSON.stringify(fileObject)))
  261. }
  262. }
  263. }
  264. //Open a particular tab using System Setting module. Require
  265. //1) Setting Group
  266. //2) Setting Name
  267. function ao_module_openSetting(group, name){
  268. var requestObject = {
  269. group: group,
  270. name: name
  271. }
  272. requestObject = encodeURIComponent(JSON.stringify(requestObject));
  273. var openURL = "SystemAO/system_setting/index.html#" + requestObject;
  274. if (ao_module_virtualDesktop){
  275. ao_module_newfw({
  276. url: openURL,
  277. width: 1080,
  278. height: 580,
  279. appicon: "SystemAO/system_setting/img/small_icon.png",
  280. title: "System Setting"
  281. });
  282. }else{
  283. window.open(ao_root + openURL)
  284. }
  285. }
  286. /*
  287. ao_module_newfw(launchConfig) => Create a new floatWindow object from the given paramters
  288. Most basic usage: (With auto assign UID, size and location)
  289. ao_module_newfw({
  290. url: "Dummy/index.html",
  291. title: "Dummy Module",
  292. appicon: "Dummy/img/icon.png"
  293. });
  294. Example usage that involve all configs:
  295. ao_module_newfw({
  296. url: "Dummy/index.html",
  297. uid: "CustomUUID",
  298. width: 1024,
  299. height: 768,
  300. appicon: "Dummy/img/icon.png",
  301. title: "Dummy Module",
  302. left: 100,
  303. top: 100,
  304. parent: ao_module_windowID,
  305. callback: "childCallbackHandler"
  306. });
  307. */
  308. function ao_module_newfw(launchConfig){
  309. if (launchConfig["parent"] == undefined){
  310. launchConfig["parent"] = ao_module_windowID;
  311. }
  312. if (ao_module_virtualDesktop){
  313. parent.newFloatWindow(launchConfig);
  314. }else{
  315. window.open(ao_root + launchConfig.url);
  316. }
  317. }
  318. /*
  319. File Selector
  320. Open a file selector and return selected item back to the current window
  321. Tips: Unlike the beta version, you can use this function in both Virtual Desktop Mode and normal mode.
  322. Possible selection type:
  323. type => {file / folder / all / new}
  324. Example usage:
  325. ao_module_openFileSelector(fileSelected, "user:/Desktop", "file",true);
  326. function fileSelected(filedata){
  327. for (var i=0; i < filedata.length; i++){
  328. var filename = filedata[i].filename;
  329. var filepath = filedata[i].filepath;
  330. //Do something here
  331. }
  332. }
  333. If you want to create a new file or folder object, you can use the following options paramters
  334. option = {
  335. defaultName: "newfile.txt", //Default filename used in new operation
  336. fnameOverride: "myfunction", //For those defined with window.myfunction
  337. filter: ["mp3","aac","ogg","flac","wav"] //File extension filter
  338. }
  339. */
  340. let ao_module_fileSelectionListener;
  341. let ao_module_fileSelectorWindow;
  342. function ao_module_openFileSelector(callback,root="user:/", type="file",allowMultiple=false, options=undefined){
  343. var initInfo = {
  344. root: root,
  345. type: type,
  346. allowMultiple: allowMultiple,
  347. listenerUUID: "",
  348. options: options
  349. }
  350. var initInfoEncoded = encodeURIComponent(JSON.stringify(initInfo))
  351. if (ao_module_virtualDesktop){
  352. var callbackname = callback.name;
  353. if (typeof(options) != "undefined" && typeof(options.fnameOverride) != "undefined"){
  354. callbackname = options.fnameOverride;
  355. }
  356. console.log(callbackname);
  357. parent.newFloatWindow({
  358. url: "SystemAO/file_system/file_selector.html#" + initInfoEncoded,
  359. width: 700,
  360. height: 440,
  361. appicon: "SystemAO/file_system/img/selector.png",
  362. title: "Open",
  363. parent: ao_module_windowID,
  364. callback: callbackname
  365. });
  366. }else{
  367. //Create a return listener base on localStorage
  368. let listenerUUID = "fileSelector_" + new Date().getTime();
  369. ao_module_fileSelectionListener = setInterval(function(){
  370. if (localStorage.getItem(listenerUUID) === undefined || localStorage.getItem(listenerUUID)=== null){
  371. //Not ready
  372. }else{
  373. //File ready!
  374. var selectedFiles = JSON.parse(localStorage.getItem(listenerUUID));
  375. console.log("Removing Localstorage Item " + listenerUUID);
  376. localStorage.removeItem(listenerUUID);
  377. setTimeout(function(){
  378. localStorage.removeItem(listenerUUID);
  379. },500);
  380. if(selectedFiles == "&&selection_canceled&&"){
  381. //Selection canceled. Returm empty array
  382. callback([]);
  383. }else{
  384. //Files Selected
  385. callback(selectedFiles);
  386. }
  387. clearInterval(ao_module_fileSelectionListener);
  388. ao_module_fileSelectorWindow.close();
  389. }
  390. },1000);
  391. //Open the file selector in a new tab
  392. initInfo.listenerUUID = listenerUUID;
  393. initInfoEncoded = encodeURIComponent(JSON.stringify(initInfo))
  394. ao_module_fileSelectorWindow = window.open(ao_root + "SystemAO/file_system/file_selector.html#" + initInfoEncoded,);
  395. }
  396. }
  397. //Check if there is parent to callback
  398. function ao_module_hasParentCallback(){
  399. if (ao_module_virtualDesktop){
  400. //Check if parent callback exists
  401. var thisFw;
  402. $(parent.window.document.body).find(".floatWindow").each(function(){
  403. if ($(this).attr('windowid') == ao_module_windowID){
  404. thisFw = $(this);
  405. }
  406. });
  407. var parentWindowID = thisFw.attr("parent");
  408. var parentCallback = thisFw.attr("callback");
  409. if (parentWindowID == "" || parentCallback == ""){
  410. //No parent window defined
  411. return false;
  412. }
  413. //Check if parent windows is alive
  414. var parentWindow = undefined;
  415. $(parent.window.document.body).find(".floatWindow").each(function(){
  416. if ($(this).attr('windowid') == parentWindowID){
  417. parentWindow = $(this);
  418. }
  419. });
  420. if (parentWindow == undefined){
  421. //parent window not exists
  422. return false;
  423. }
  424. //Parent callback is set and ready to callback
  425. return true;
  426. }else{
  427. return false
  428. }
  429. }
  430. //Callback to parent with results
  431. function ao_module_parentCallback(data=""){
  432. if (ao_module_virtualDesktop){
  433. var thisFw;
  434. $(parent.window.document.body).find(".floatWindow").each(function(){
  435. if ($(this).attr('windowid') == ao_module_windowID){
  436. thisFw = $(this);
  437. }
  438. });
  439. var parentWindowID = thisFw.attr("parent");
  440. var parentCallback = thisFw.attr("callback");
  441. if (parentWindowID == "" || parentCallback == ""){
  442. //No parent window defined
  443. console.log("Undefined parent window ID or callback name");
  444. return false;
  445. }
  446. var parentWindow = undefined;
  447. $(parent.window.document.body).find(".floatWindow").each(function(){
  448. if ($(this).attr('windowid') == parentWindowID){
  449. parentWindow = $(this);
  450. }
  451. });
  452. if (parentWindow == undefined){
  453. //parent window not exists
  454. console.log("Parent Window not exists!")
  455. return false;
  456. }
  457. $(parentWindow).find('iframe')[0].contentWindow.eval(parentCallback + "(" + JSON.stringify(data) + ");")
  458. //Focus the parent windows
  459. parent.MoveFloatWindowToTop(parentWindow);
  460. return true;
  461. }else{
  462. console.log("[ao_module] WARNING! Invalid call to parentCallback under non-virtualDesktop mode");
  463. return false;
  464. }
  465. }
  466. function ao_module_agirun(scriptpath, data, callback, failedcallback = undefined, timeout=0){
  467. $.ajax({
  468. url: ao_root + "system/ajgi/interface?script=" + scriptpath,
  469. method: "POST",
  470. data: data,
  471. success: function(data){
  472. if (typeof(callback) != "undefined"){
  473. callback(data);
  474. }
  475. },
  476. error: function(){
  477. if (typeof(failedcallback) != "undefined"){
  478. failedcallback();
  479. }
  480. },
  481. timeout: timeout
  482. });
  483. }
  484. function ao_module_uploadFile(file, targetPath, callback=undefined, progressCallback=undefined, failedcallback=undefined) {
  485. let url = ao_root + 'system/file_system/upload'
  486. let formData = new FormData()
  487. let xhr = new XMLHttpRequest()
  488. formData.append('file', file);
  489. formData.append('path', targetPath);
  490. xhr.open('POST', url, true);
  491. xhr.upload.addEventListener("progress", function(e) {
  492. if (progressCallback !== undefined){
  493. progressCallback((e.loaded * 100.0 / e.total) || 100);
  494. }
  495. });
  496. xhr.addEventListener('readystatechange', function(e) {
  497. if (xhr.readyState == 4 && xhr.status == 200) {
  498. if (callback !== undefined){
  499. callback(e.target.response);
  500. }
  501. }
  502. else if (xhr.readyState == 4 && xhr.status != 200) {
  503. if (failedcallback !== undefined){
  504. failedcallback(xhr.status);
  505. }
  506. }
  507. })
  508. xhr.send(formData);
  509. }
  510. /*
  511. ao_module_storage, allow key-value storage per module settings.
  512. WARNING: NOT CROSS USER READ-WRITABLE
  513. ao_module_storage.setStorage(moduleName, configName,configValue);
  514. ao_module_storage.loadStorage(moduleName, configName);
  515. */
  516. class ao_module_storage {
  517. static setStorage(moduleName, configName,configValue){
  518. $.ajax({
  519. type: 'GET',
  520. url: ao_root + "system/file_system/preference",
  521. data: {key: moduleName + "/" + configName,value:configValue},
  522. success: function(data){},
  523. async:true
  524. });
  525. return true;
  526. }
  527. static loadStorage(moduleName, configName, callback=undefined){
  528. var result = "";
  529. if (callback == undefined){
  530. //Do not use async
  531. $.ajax({
  532. type: 'GET',
  533. url: ao_root + "system/file_system/preference",
  534. data: {key: moduleName + "/" + configName},
  535. success: function(data){
  536. if (data.error !== undefined){
  537. result = "";
  538. }else{
  539. result = data;
  540. }
  541. },
  542. error: function(data){result = "";},
  543. async:false,
  544. timeout: 3000
  545. });
  546. return result;
  547. }else{
  548. //Use sync method
  549. $.ajax({
  550. type: 'GET',
  551. url: ao_root + "system/file_system/preference",
  552. data: {key: moduleName + "/" + configName},
  553. success: function(data){
  554. if (data.error !== undefined){
  555. callback("");
  556. }else{
  557. callback(data);
  558. }
  559. },
  560. error: function(evt){
  561. callback("");
  562. },
  563. timeout: 30000
  564. });
  565. }
  566. }
  567. }
  568. class ao_module_codec{
  569. //Decode umfilename into standard filename in utf-8, which umfilename usually start with "inith"
  570. //Example: ao_module_codec.decodeUmFilename(umfilename_here);
  571. static decodeUmFilename(umfilename){
  572. if (umfilename.includes("inith")){
  573. var data = umfilename.split(".");
  574. if (data.length == 1){
  575. //This is a filename without extension
  576. data = data[0].replace("inith","");
  577. var decodedname = ao_module_codec.decode_utf8(ao_module_codec.hex2bin(data));
  578. if (decodedname != "false"){
  579. //This is a umfilename
  580. return decodedname;
  581. }else{
  582. //This is not a umfilename
  583. return umfilename;
  584. }
  585. }else{
  586. //This is a filename with extension
  587. var extension = data.pop();
  588. var filename = data[0];
  589. filename = filename.replace("inith",""); //Javascript replace only remove the first instances (i.e. the first inith in filename)
  590. var decodedname = ao_module_codec.decode_utf8(ao_module_codec.hex2bin(filename));
  591. if (decodedname != "false"){
  592. //This is a umfilename
  593. return decodedname + "." + extension;
  594. }else{
  595. //This is not a umfilename
  596. return umfilename;
  597. }
  598. }
  599. }else{
  600. //This is not umfilename as it doesn't have the inith prefix
  601. return umfilename;
  602. }
  603. }
  604. //Encode filename to UMfilename
  605. //Example: ao_module_codec.encodeUMFilename("test.stl");
  606. static encodeUMFilename(filename){
  607. if (filename.substring(0,5) != "inith"){
  608. //Check if the filename include extension.
  609. if (filename.includes(".")){
  610. //Filename with extension. pop it out first.
  611. var info = filename.split(".");
  612. var ext = info.pop();
  613. var filenameOnly = info.join(".");
  614. var encodedFilename = "inith" + ao_module_codec.decode_utf8(ao_module_codec.bin2hex(filenameOnly)) + "." + ext;
  615. return encodedFilename;
  616. }else{
  617. //Filename with no extension. Convert the whole name into UMfilename
  618. var encodedFilename = "inith" + ao_module_codec.decode_utf8(ao_module_codec.bin2hex(filename));
  619. return encodedFilename;
  620. }
  621. }else{
  622. //This is already a UMfilename. return the raw filename.
  623. return filename;
  624. }
  625. }
  626. //Decode hexFoldername into standard foldername in utf-8, return the original name if it is not a hex foldername
  627. //Example: ao_module_codec.decodeHexFoldername(hexFolderName_here);
  628. static decodeHexFoldername(folderName, prefix=true){
  629. var decodedFoldername = ao_module_codec.decode_utf8(ao_module_codec.hex2bin(folderName));
  630. if (decodedFoldername == "false"){
  631. //This is not a hex encoded foldername
  632. decodedFoldername = folderName;
  633. }else{
  634. //This is a hex encoded foldername
  635. if (prefix){
  636. decodedFoldername = "*" + decodedFoldername;
  637. }else{
  638. decodedFoldername =decodedFoldername;
  639. }
  640. }
  641. return decodedFoldername;
  642. }
  643. //Encode foldername into hexfoldername
  644. //Example: ao_module_codec.encodeHexFoldername("test");
  645. static encodeHexFoldername(folderName){
  646. var encodedFilename = "";
  647. if (ao_module_codec.decodeHexFoldername(folderName) == folderName){
  648. //This is not hex foldername. Encode it
  649. encodedFilename = ao_module_codec.decode_utf8(ao_module_codec.bin2hex(folderName));
  650. }else{
  651. //This folder name already encoded. Return the original value
  652. encodedFilename = folderName;
  653. }
  654. return encodedFilename;
  655. }
  656. static hex2bin(s){
  657. var ret = []
  658. var i = 0
  659. var l
  660. s += ''
  661. for (l = s.length; i < l; i += 2) {
  662. var c = parseInt(s.substr(i, 1), 16)
  663. var k = parseInt(s.substr(i + 1, 1), 16)
  664. if (isNaN(c) || isNaN(k)) return false
  665. ret.push((c << 4) | k)
  666. }
  667. return String.fromCharCode.apply(String, ret)
  668. }
  669. static bin2hex(s){
  670. var i
  671. var l
  672. var o = ''
  673. var n
  674. s += ''
  675. for (i = 0, l = s.length; i < l; i++) {
  676. n = s.charCodeAt(i)
  677. .toString(16)
  678. o += n.length < 2 ? '0' + n : n
  679. }
  680. return o
  681. }
  682. static decode_utf8(s) {
  683. return decodeURIComponent(escape(s));
  684. }
  685. }
  686. /**
  687. ArOZ Online Module Utils for quick deploy of ArOZ Online WebApps
  688. ao_module_utils.objectToAttr(object); //object to DOM attr
  689. ao_module_utils.attrToObject(attr); //DOM attr to Object
  690. ao_module_utils.getRandomUID(); //Get random UUID from timestamp
  691. ao_module_utils.getIconFromExt(ext); //Get icon tag from file extension
  692. ao_module_utils.getDropFileInfo(dropEvent); //Get the filepath and filename list from file explorer drag drop
  693. ao_module_utils.formatBytes(byte, decimals); //Format file byte size to human readable size
  694. ao_module_utils.timeConverter(unix_timestamp); //Get human readable timestamp
  695. **/
  696. class ao_module_utils{
  697. //Two simple functions for converting any Javascript object into string that can be put into the attr value of an DOM object
  698. static objectToAttr(object){
  699. return encodeURIComponent(JSON.stringify(object));
  700. }
  701. static attrToObject(attr){
  702. return JSON.parse(decodeURIComponent(attr));
  703. }
  704. //Get a random id for a new floatWindow, use with var uid = ao_module_utils.getRandomUID();
  705. static getRandomUID(){
  706. return new Date().getTime();
  707. }
  708. static stringToBlob(text, mimetype="text/plain"){
  709. var blob = new Blob([text], { type: mimetype });
  710. return blob
  711. }
  712. static blobToFile(blob, filename, mimetype="text/plain"){
  713. var file = new File([blob], filename, {type: mimetype});
  714. return file
  715. }
  716. //Get the icon of a file with given extension (ext), use with ao_module_utils.getIconFromExt("ext");
  717. static getIconFromExt(ext){
  718. var ext = ext.toLowerCase().trim();
  719. var iconList={
  720. md:"file text outline",
  721. txt:"file text outline",
  722. pdf:"file pdf outline",
  723. doc:"file word outline",
  724. docx:"file word outline",
  725. odt:"file word outline",
  726. xlsx:"file excel outline",
  727. ods:"file excel outline",
  728. ppt:"file powerpoint outline",
  729. pptx:"file powerpoint outline",
  730. odp:"file powerpoint outline",
  731. jpg:"file image outline",
  732. png:"file image outline",
  733. jpeg:"file image outline",
  734. gif:"file image outline",
  735. odg:"file image outline",
  736. psd:"file image outline",
  737. zip:"file archive outline",
  738. '7z':"file archive outline",
  739. rar:"file archive outline",
  740. tar:"file archive outline",
  741. mp3:"file audio outline",
  742. m4a:"file audio outline",
  743. flac:"file audio outline",
  744. wav:"file audio outline",
  745. aac:"file audio outline",
  746. mp4:"file video outline",
  747. webm:"file video outline",
  748. php:"file code outline",
  749. html:"file code outline",
  750. htm:"file code outline",
  751. js:"file code outline",
  752. css:"file code outline",
  753. xml:"file code outline",
  754. json:"file code outline",
  755. csv:"file code outline",
  756. odf:"file code outline",
  757. bmp:"file image outline",
  758. rtf:"file text outline",
  759. wmv:"file video outline",
  760. mkv:"file video outline",
  761. ogg:"file audio outline",
  762. stl:"cube",
  763. obj:"cube",
  764. "3ds":"cube",
  765. fbx:"cube",
  766. collada:"cube",
  767. step:"cube",
  768. iges:"cube",
  769. gcode:"cube",
  770. shortcut:"external square",
  771. opus:"file audio outline",
  772. apscene:"cubes"
  773. };
  774. var icon = "";
  775. if (ext == ""){
  776. icon = "folder outline";
  777. }else{
  778. icon = iconList[ext];
  779. if (icon == undefined){
  780. icon = "file outline"
  781. }
  782. }
  783. return icon;
  784. }
  785. //Get the drop file properties {filepath: xxx, filename: xxx} from file drop events from file exploere
  786. static getDropFileInfo(dropEvent){
  787. if (dropEvent.dataTransfer.getData("filedata") !== ""){
  788. var filelist = dropEvent.dataTransfer.getData("filedata");
  789. filelist = JSON.parse(filelist);
  790. return filelist;
  791. }
  792. }
  793. static readFileFromFileObject(fileObject, successCallback, failedCallback=undefined){
  794. let reader = new FileReader();
  795. reader.readAsText(fileObject);
  796. reader.onload = function() {
  797. successCallback(reader.result);
  798. };
  799. reader.onerror = function() {
  800. if (failedCallback != undefined){
  801. failedCallback(reader.error);
  802. }else{
  803. console.log(reader.error);
  804. }
  805. };
  806. }
  807. static durationConverter(seconds){
  808. var days = Math.floor(seconds / 86400);
  809. seconds -= days * 86400;
  810. var hours = Math.floor(seconds / 3600) % 24;
  811. seconds -= hours * 3600;
  812. var minutes = Math.floor(seconds / 60) % 60;
  813. seconds -= minutes * 60;
  814. var seconds = seconds % 60;
  815. var resultDuration = "";
  816. if (days > 0){
  817. resultDuration += days + " Day";
  818. if (days > 1){
  819. resultDuration+= "s"
  820. }
  821. resultDuration += " "
  822. }
  823. if (hours > 0){
  824. resultDuration += hours + " Hour"
  825. if (hours > 1){
  826. resultDuration += "s"
  827. }
  828. resultDuration += " "
  829. }
  830. if (minutes > 0){
  831. resultDuration += minutes + " Minute"
  832. if (minutes > 1){
  833. resultDuration += "s"
  834. }
  835. resultDuration += " "
  836. }
  837. if (seconds > 0){
  838. resultDuration += seconds + " Secound"
  839. if (seconds > 1){
  840. resultDuration += "s"
  841. }
  842. resultDuration += " "
  843. }
  844. return resultDuration;
  845. }
  846. static timeConverter(UNIX_timestamp){
  847. var a = new Date(UNIX_timestamp * 1000);
  848. var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  849. var year = a.getFullYear();
  850. var month = months[a.getMonth()];
  851. var date = a.getDate();
  852. var hour = a.getHours().toString().padStart(2, "0");
  853. var min = a.getMinutes().toString().padStart(2, "0");
  854. var sec = a.getSeconds().toString().padStart(2, "0");
  855. var time = date + ' ' + month + ' ' + year + ' ' + hour + ':' + min + ':' + sec ;
  856. return time;
  857. }
  858. static getWebSocketEndpoint(){
  859. let protocol = "wss://";
  860. if (location.protocol !== 'https:') {
  861. protocol = "ws://";
  862. }
  863. let port = window.location.port;
  864. if (window.location.port == ""){
  865. if (location.protocol !== 'https:') {
  866. port = "80";
  867. }else{
  868. port = "443";
  869. }
  870. }
  871. let wsept = (protocol + window.location.hostname + ":" + port);
  872. return wsept;
  873. }
  874. static formatBytes(a,b=2){if(0===a)return"0 Bytes";const c=0>b?0:b,d=Math.floor(Math.log(a)/Math.log(1024));return parseFloat((a/Math.pow(1024,d)).toFixed(c))+" "+["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"][d]}
  875. }
  876. /*
  877. Backend Programming Logic
  878. These code are design to use with ao_backend.js for wrapping
  879. AGI programming gateway content
  880. */
  881. function ao_module_backend(){
  882. return {
  883. timeout: 500,
  884. start: function(libraryPath){
  885. this.libpath = libraryPath;
  886. //Initialize the parent objects
  887. this.appdata.parent = this;
  888. this.file.parent = this;
  889. this.http.parent = this;
  890. },
  891. _agi_run: function(data, callback, failedcallback = undefined){
  892. $.ajax({
  893. url: ao_root + "system/ajgi/interface?script=" + this.libpath,
  894. method: "POST",
  895. data: data,
  896. success: function(data){
  897. if (typeof(callback) != "undefined"){
  898. callback(data);
  899. }
  900. },
  901. error: function(){
  902. if (typeof(failedcallback) != "undefined"){
  903. failedcallback();
  904. }
  905. console.log("Request failed");
  906. },
  907. timeout: this.timeout
  908. })
  909. },
  910. appdata: {
  911. readFile: function(filepath, callback=undefined){
  912. this.parent._agi_run({
  913. opr: "appdata.readFile",
  914. filepath: filepath
  915. }, callback)
  916. },
  917. listDir: function(filepath, callback=undefined){
  918. this.parent._agi_run({
  919. opr: "appdata.listDir",
  920. filepath: filepath
  921. }, callback)
  922. },
  923. },
  924. file: {
  925. writeFile: function(filepath, content, callback=undefined){
  926. this.parent._agi_run({
  927. opr: "file.writeFile",
  928. filepath: filepath,
  929. content: content,
  930. }, callback)
  931. },
  932. readFile: function(filepath, callback=undefined){
  933. this.parent._agi_run({
  934. opr: "file.readFile",
  935. filepath: filepath
  936. }, callback)
  937. },
  938. deleteFile: function(filepath, callback=undefined){
  939. this.parent._agi_run({
  940. opr: "file.deleteFile",
  941. filepath: filepath
  942. }, callback)
  943. },
  944. readdir: function(filepath, callback=undefined){
  945. this.parent._agi_run({
  946. opr: "file.readdir",
  947. filepath: filepath
  948. }, callback)
  949. },
  950. walk: function(filepath, mode="all", callback=undefined){
  951. this.parent._agi_run({
  952. opr: "file.walk",
  953. filepath: filepath,
  954. mode: mode,
  955. }, callback)
  956. },
  957. glob: function(wildcard, sort="user", callback=undefined){
  958. this.parent._agi_run({
  959. opr: "file.glob",
  960. wildcard: wildcard,
  961. sort: sort,
  962. }, callback)
  963. },
  964. aglob: function(wildcard, sort="user", callback=undefined){
  965. this.parent._agi_run({
  966. opr: "file.aglob",
  967. wildcard: wildcard,
  968. sort: sort,
  969. }, callback)
  970. },
  971. filesize: function(filepath, callback=undefined){
  972. this.parent._agi_run({
  973. opr: "file.filesize",
  974. filepath: filepath
  975. }, callback)
  976. },
  977. fileExists: function(filepath, callback=undefined){
  978. this.parent._agi_run({
  979. opr: "file.fileExists",
  980. filepath: filepath
  981. }, callback)
  982. },
  983. isDir: function(filepath, callback=undefined){
  984. this.parent._agi_run({
  985. opr: "file.isDir",
  986. filepath: filepath
  987. }, callback)
  988. },
  989. mkdir: function(filepath, callback=undefined){
  990. this.parent._agi_run({
  991. opr: "file.mkdir",
  992. filepath: filepath
  993. }, callback)
  994. },
  995. mtime: function(filepath, callback=undefined){
  996. this.parent._agi_run({
  997. opr: "file.mtime",
  998. filepath: filepath
  999. }, callback)
  1000. },
  1001. rootName: function(filepath, callback=undefined){
  1002. this.parent._agi_run({
  1003. opr: "file.rootName",
  1004. filepath: filepath
  1005. }, callback)
  1006. }
  1007. },
  1008. http: {
  1009. get: function(targetURL, callback=undefined){
  1010. this.parent._agi_run({
  1011. opr: "http.get",
  1012. targetURL: targetURL
  1013. }, callback)
  1014. },
  1015. post: function(targetURL, postdata="", callback=undefined){
  1016. this.parent._agi_run({
  1017. opr: "http.post",
  1018. targetURL: targetURL,
  1019. postdata: postdata
  1020. }, callback)
  1021. },
  1022. head: function(targetURL, header="", callback=undefined){
  1023. this.parent._agi_run({
  1024. opr: "http.head",
  1025. targetURL: targetURL,
  1026. header: header
  1027. }, callback)
  1028. },
  1029. download: function(targetURL, saveDir="tmp:/", saveFilename="", callback=undefined){
  1030. if (saveFilename == ""){
  1031. saveFilename = targetURL.split("/").pop();
  1032. saveFilename = decodeURIComponent(saveFilename);
  1033. }
  1034. this.parent._agi_run({
  1035. opr: "http.download",
  1036. targetURL: targetURL,
  1037. saveDir: saveDir,
  1038. saveFilename: saveFilename
  1039. }, callback)
  1040. },
  1041. }
  1042. }
  1043. }