ao_module.js 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153
  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. ao_module_closeHandler();
  198. }
  199. //Close handler for WebApp special handling of ao_module_close()
  200. function ao_module_closeHandler(){
  201. if (!ao_module_virtualDesktop){
  202. window.close('','_parent','');
  203. window.location.href = ao_root + "SystemAO/closeTabInsturction.html";
  204. return;
  205. }
  206. parent.closeFwProcess(ao_module_windowID);
  207. }
  208. //Focus this floatWindow
  209. function ao_module_focus(){
  210. parent.MoveFloatWindowToTop(parent.getFloatWindowByID(ao_module_windowID));
  211. }
  212. //Set the floatWindow to top most mode
  213. function ao_module_setTopMost(){
  214. parent.PinFloatWindowToTopMostMode(parent.getFloatWindowByID(ao_module_windowID));
  215. }
  216. //Unset the floatWindow top most mode
  217. function ao_module_unsetTopMost(){
  218. parent.UnpinFloatWindowFromTopMostMode(parent.getFloatWindowByID(ao_module_windowID));
  219. }
  220. //Popup a file selection window for uplaod
  221. function ao_module_selectFiles(callback, fileType="file", accept="*", allowMultiple=false){
  222. var input = document.createElement('input');
  223. input.type = fileType;
  224. input.multiple = allowMultiple;
  225. input.accept = accept;
  226. input.onchange = e => {
  227. var files = e.target.files;
  228. callback(files);
  229. }
  230. input.click();
  231. }
  232. //Open a path with File Manager, optional highligh filename
  233. function ao_module_openPath(path, filename=undefined){
  234. //Trim away the last / if exists
  235. if (path.substr(path.length - 1, 1) == "/"){
  236. path = path.substr(0, path.length - 1);
  237. }
  238. if (filename == undefined){
  239. if (ao_module_virtualDesktop){
  240. parent.newFloatWindow({
  241. url: "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(path),
  242. appicon: "SystemAO/file_system/img/small_icon.png",
  243. width:1080,
  244. height:580,
  245. title: "File Manager"
  246. });
  247. }else{
  248. window.open(ao_root + "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(path))
  249. }
  250. }else{
  251. var fileObject = [{
  252. filepath: path + "/" + filename,
  253. filename: filename,
  254. }];
  255. if (ao_module_virtualDesktop){
  256. parent.newFloatWindow({
  257. url: "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(JSON.stringify(fileObject)),
  258. appicon: "SystemAO/file_system/img/small_icon.png",
  259. width:1080,
  260. height:580,
  261. title: "File Manager"
  262. });
  263. }else{
  264. window.open(ao_root + "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(JSON.stringify(fileObject)))
  265. }
  266. }
  267. }
  268. //Open a particular tab using System Setting module. Require
  269. //1) Setting Group
  270. //2) Setting Name
  271. function ao_module_openSetting(group, name){
  272. var requestObject = {
  273. group: group,
  274. name: name
  275. }
  276. requestObject = encodeURIComponent(JSON.stringify(requestObject));
  277. var openURL = "SystemAO/system_setting/index.html#" + requestObject;
  278. if (ao_module_virtualDesktop){
  279. ao_module_newfw({
  280. url: openURL,
  281. width: 1080,
  282. height: 580,
  283. appicon: "SystemAO/system_setting/img/small_icon.png",
  284. title: "System Setting"
  285. });
  286. }else{
  287. window.open(ao_root + openURL)
  288. }
  289. }
  290. /*
  291. ao_module_newfw(launchConfig) => Create a new floatWindow object from the given paramters
  292. Most basic usage: (With auto assign UID, size and location)
  293. ao_module_newfw({
  294. url: "Dummy/index.html",
  295. title: "Dummy Module",
  296. appicon: "Dummy/img/icon.png"
  297. });
  298. Example usage that involve all configs:
  299. ao_module_newfw({
  300. url: "Dummy/index.html",
  301. uid: "CustomUUID",
  302. width: 1024,
  303. height: 768,
  304. appicon: "Dummy/img/icon.png",
  305. title: "Dummy Module",
  306. left: 100,
  307. top: 100,
  308. parent: ao_module_windowID,
  309. callback: "childCallbackHandler"
  310. });
  311. */
  312. function ao_module_newfw(launchConfig){
  313. if (launchConfig["parent"] == undefined){
  314. launchConfig["parent"] = ao_module_windowID;
  315. }
  316. if (ao_module_virtualDesktop){
  317. parent.newFloatWindow(launchConfig);
  318. }else{
  319. window.open(ao_root + launchConfig.url);
  320. }
  321. }
  322. /*
  323. File Selector
  324. Open a file selector and return selected item back to the current window
  325. Tips: Unlike the beta version, you can use this function in both Virtual Desktop Mode and normal mode.
  326. Possible selection type:
  327. type => {file / folder / all / new}
  328. Example usage:
  329. ao_module_openFileSelector(fileSelected, "user:/Desktop", "file",true);
  330. function fileSelected(filedata){
  331. for (var i=0; i < filedata.length; i++){
  332. var filename = filedata[i].filename;
  333. var filepath = filedata[i].filepath;
  334. //Do something here
  335. }
  336. }
  337. If you want to create a new file or folder object, you can use the following options paramters
  338. option = {
  339. defaultName: "newfile.txt", //Default filename used in new operation
  340. fnameOverride: "myfunction", //For those defined with window.myfunction
  341. filter: ["mp3","aac","ogg","flac","wav"] //File extension filter
  342. }
  343. */
  344. let ao_module_fileSelectionListener;
  345. let ao_module_fileSelectorWindow;
  346. function ao_module_openFileSelector(callback,root="user:/", type="file",allowMultiple=false, options=undefined){
  347. var initInfo = {
  348. root: root,
  349. type: type,
  350. allowMultiple: allowMultiple,
  351. listenerUUID: "",
  352. options: options
  353. }
  354. var initInfoEncoded = encodeURIComponent(JSON.stringify(initInfo))
  355. if (ao_module_virtualDesktop){
  356. var callbackname = callback.name;
  357. if (typeof(options) != "undefined" && typeof(options.fnameOverride) != "undefined"){
  358. callbackname = options.fnameOverride;
  359. }
  360. console.log(callbackname);
  361. parent.newFloatWindow({
  362. url: "SystemAO/file_system/file_selector.html#" + initInfoEncoded,
  363. width: 700,
  364. height: 440,
  365. appicon: "SystemAO/file_system/img/selector.png",
  366. title: "Open",
  367. parent: ao_module_windowID,
  368. callback: callbackname
  369. });
  370. }else{
  371. //Create a return listener base on localStorage
  372. let listenerUUID = "fileSelector_" + new Date().getTime();
  373. ao_module_fileSelectionListener = setInterval(function(){
  374. if (localStorage.getItem(listenerUUID) === undefined || localStorage.getItem(listenerUUID)=== null){
  375. //Not ready
  376. }else{
  377. //File ready!
  378. var selectedFiles = JSON.parse(localStorage.getItem(listenerUUID));
  379. console.log("Removing Localstorage Item " + listenerUUID);
  380. localStorage.removeItem(listenerUUID);
  381. setTimeout(function(){
  382. localStorage.removeItem(listenerUUID);
  383. },500);
  384. if(selectedFiles == "&&selection_canceled&&"){
  385. //Selection canceled. Returm empty array
  386. callback([]);
  387. }else{
  388. //Files Selected
  389. callback(selectedFiles);
  390. }
  391. clearInterval(ao_module_fileSelectionListener);
  392. ao_module_fileSelectorWindow.close();
  393. }
  394. },1000);
  395. //Open the file selector in a new tab
  396. initInfo.listenerUUID = listenerUUID;
  397. initInfoEncoded = encodeURIComponent(JSON.stringify(initInfo))
  398. ao_module_fileSelectorWindow = window.open(ao_root + "SystemAO/file_system/file_selector.html#" + initInfoEncoded,);
  399. }
  400. }
  401. //Check if there is parent to callback
  402. function ao_module_hasParentCallback(){
  403. if (ao_module_virtualDesktop){
  404. //Check if parent callback exists
  405. var thisFw;
  406. $(parent.window.document.body).find(".floatWindow").each(function(){
  407. if ($(this).attr('windowid') == ao_module_windowID){
  408. thisFw = $(this);
  409. }
  410. });
  411. var parentWindowID = thisFw.attr("parent");
  412. var parentCallback = thisFw.attr("callback");
  413. if (parentWindowID == "" || parentCallback == ""){
  414. //No parent window defined
  415. return false;
  416. }
  417. //Check if parent windows is alive
  418. var parentWindow = undefined;
  419. $(parent.window.document.body).find(".floatWindow").each(function(){
  420. if ($(this).attr('windowid') == parentWindowID){
  421. parentWindow = $(this);
  422. }
  423. });
  424. if (parentWindow == undefined){
  425. //parent window not exists
  426. return false;
  427. }
  428. //Parent callback is set and ready to callback
  429. return true;
  430. }else{
  431. return false
  432. }
  433. }
  434. //Callback to parent with results
  435. function ao_module_parentCallback(data=""){
  436. if (ao_module_virtualDesktop){
  437. var thisFw;
  438. $(parent.window.document.body).find(".floatWindow").each(function(){
  439. if ($(this).attr('windowid') == ao_module_windowID){
  440. thisFw = $(this);
  441. }
  442. });
  443. var parentWindowID = thisFw.attr("parent");
  444. var parentCallback = thisFw.attr("callback");
  445. if (parentWindowID == "" || parentCallback == ""){
  446. //No parent window defined
  447. console.log("Undefined parent window ID or callback name");
  448. return false;
  449. }
  450. var parentWindow = undefined;
  451. $(parent.window.document.body).find(".floatWindow").each(function(){
  452. if ($(this).attr('windowid') == parentWindowID){
  453. parentWindow = $(this);
  454. }
  455. });
  456. if (parentWindow == undefined){
  457. //parent window not exists
  458. console.log("Parent Window not exists!")
  459. return false;
  460. }
  461. $(parentWindow).find('iframe')[0].contentWindow.eval(parentCallback + "(" + JSON.stringify(data) + ");")
  462. //Focus the parent windows
  463. parent.MoveFloatWindowToTop(parentWindow);
  464. return true;
  465. }else{
  466. console.log("[ao_module] WARNING! Invalid call to parentCallback under non-virtualDesktop mode");
  467. return false;
  468. }
  469. }
  470. function ao_module_agirun(scriptpath, data, callback, failedcallback = undefined, timeout=0){
  471. $.ajax({
  472. url: ao_root + "system/ajgi/interface?script=" + scriptpath,
  473. method: "POST",
  474. data: data,
  475. success: function(data){
  476. if (typeof(callback) != "undefined"){
  477. callback(data);
  478. }
  479. },
  480. error: function(){
  481. if (typeof(failedcallback) != "undefined"){
  482. failedcallback();
  483. }
  484. },
  485. timeout: timeout
  486. });
  487. }
  488. function ao_module_uploadFile(file, targetPath, callback=undefined, progressCallback=undefined, failedcallback=undefined) {
  489. let url = ao_root + 'system/file_system/upload'
  490. let formData = new FormData()
  491. let xhr = new XMLHttpRequest()
  492. formData.append('file', file);
  493. formData.append('path', targetPath);
  494. xhr.open('POST', url, true);
  495. xhr.upload.addEventListener("progress", function(e) {
  496. if (progressCallback !== undefined){
  497. progressCallback((e.loaded * 100.0 / e.total) || 100);
  498. }
  499. });
  500. xhr.addEventListener('readystatechange', function(e) {
  501. if (xhr.readyState == 4 && xhr.status == 200) {
  502. if (callback !== undefined){
  503. callback(e.target.response);
  504. }
  505. }
  506. else if (xhr.readyState == 4 && xhr.status != 200) {
  507. if (failedcallback !== undefined){
  508. failedcallback(xhr.status);
  509. }
  510. }
  511. })
  512. xhr.send(formData);
  513. }
  514. /*
  515. ao_module_storage, allow key-value storage per module settings.
  516. WARNING: NOT CROSS USER READ-WRITABLE
  517. ao_module_storage.setStorage(moduleName, configName,configValue);
  518. ao_module_storage.loadStorage(moduleName, configName);
  519. */
  520. class ao_module_storage {
  521. static setStorage(moduleName, configName,configValue){
  522. $.ajax({
  523. type: 'GET',
  524. url: ao_root + "system/file_system/preference",
  525. data: {key: moduleName + "/" + configName,value:configValue},
  526. success: function(data){},
  527. async:true
  528. });
  529. return true;
  530. }
  531. static loadStorage(moduleName, configName, callback=undefined){
  532. var result = "";
  533. if (callback == undefined){
  534. //Do not use async
  535. $.ajax({
  536. type: 'GET',
  537. url: ao_root + "system/file_system/preference",
  538. data: {key: moduleName + "/" + configName},
  539. success: function(data){
  540. if (data.error !== undefined){
  541. result = "";
  542. }else{
  543. result = data;
  544. }
  545. },
  546. error: function(data){result = "";},
  547. async:false,
  548. timeout: 3000
  549. });
  550. return result;
  551. }else{
  552. //Use sync method
  553. $.ajax({
  554. type: 'GET',
  555. url: ao_root + "system/file_system/preference",
  556. data: {key: moduleName + "/" + configName},
  557. success: function(data){
  558. if (data.error !== undefined){
  559. callback("");
  560. }else{
  561. callback(data);
  562. }
  563. },
  564. error: function(evt){
  565. callback("");
  566. },
  567. timeout: 30000
  568. });
  569. }
  570. }
  571. }
  572. class ao_module_codec{
  573. //Decode umfilename into standard filename in utf-8, which umfilename usually start with "inith"
  574. //Example: ao_module_codec.decodeUmFilename(umfilename_here);
  575. static decodeUmFilename(umfilename){
  576. if (umfilename.includes("inith")){
  577. var data = umfilename.split(".");
  578. if (data.length == 1){
  579. //This is a filename without extension
  580. data = data[0].replace("inith","");
  581. var decodedname = ao_module_codec.decode_utf8(ao_module_codec.hex2bin(data));
  582. if (decodedname != "false"){
  583. //This is a umfilename
  584. return decodedname;
  585. }else{
  586. //This is not a umfilename
  587. return umfilename;
  588. }
  589. }else{
  590. //This is a filename with extension
  591. var extension = data.pop();
  592. var filename = data[0];
  593. filename = filename.replace("inith",""); //Javascript replace only remove the first instances (i.e. the first inith in filename)
  594. var decodedname = ao_module_codec.decode_utf8(ao_module_codec.hex2bin(filename));
  595. if (decodedname != "false"){
  596. //This is a umfilename
  597. return decodedname + "." + extension;
  598. }else{
  599. //This is not a umfilename
  600. return umfilename;
  601. }
  602. }
  603. }else{
  604. //This is not umfilename as it doesn't have the inith prefix
  605. return umfilename;
  606. }
  607. }
  608. //Encode filename to UMfilename
  609. //Example: ao_module_codec.encodeUMFilename("test.stl");
  610. static encodeUMFilename(filename){
  611. if (filename.substring(0,5) != "inith"){
  612. //Check if the filename include extension.
  613. if (filename.includes(".")){
  614. //Filename with extension. pop it out first.
  615. var info = filename.split(".");
  616. var ext = info.pop();
  617. var filenameOnly = info.join(".");
  618. var encodedFilename = "inith" + ao_module_codec.decode_utf8(ao_module_codec.bin2hex(filenameOnly)) + "." + ext;
  619. return encodedFilename;
  620. }else{
  621. //Filename with no extension. Convert the whole name into UMfilename
  622. var encodedFilename = "inith" + ao_module_codec.decode_utf8(ao_module_codec.bin2hex(filename));
  623. return encodedFilename;
  624. }
  625. }else{
  626. //This is already a UMfilename. return the raw filename.
  627. return filename;
  628. }
  629. }
  630. //Decode hexFoldername into standard foldername in utf-8, return the original name if it is not a hex foldername
  631. //Example: ao_module_codec.decodeHexFoldername(hexFolderName_here);
  632. static decodeHexFoldername(folderName, prefix=true){
  633. var decodedFoldername = ao_module_codec.decode_utf8(ao_module_codec.hex2bin(folderName));
  634. if (decodedFoldername == "false"){
  635. //This is not a hex encoded foldername
  636. decodedFoldername = folderName;
  637. }else{
  638. //This is a hex encoded foldername
  639. if (prefix){
  640. decodedFoldername = "*" + decodedFoldername;
  641. }else{
  642. decodedFoldername =decodedFoldername;
  643. }
  644. }
  645. return decodedFoldername;
  646. }
  647. //Encode foldername into hexfoldername
  648. //Example: ao_module_codec.encodeHexFoldername("test");
  649. static encodeHexFoldername(folderName){
  650. var encodedFilename = "";
  651. if (ao_module_codec.decodeHexFoldername(folderName) == folderName){
  652. //This is not hex foldername. Encode it
  653. encodedFilename = ao_module_codec.decode_utf8(ao_module_codec.bin2hex(folderName));
  654. }else{
  655. //This folder name already encoded. Return the original value
  656. encodedFilename = folderName;
  657. }
  658. return encodedFilename;
  659. }
  660. static hex2bin(s){
  661. var ret = []
  662. var i = 0
  663. var l
  664. s += ''
  665. for (l = s.length; i < l; i += 2) {
  666. var c = parseInt(s.substr(i, 1), 16)
  667. var k = parseInt(s.substr(i + 1, 1), 16)
  668. if (isNaN(c) || isNaN(k)) return false
  669. ret.push((c << 4) | k)
  670. }
  671. return String.fromCharCode.apply(String, ret)
  672. }
  673. static bin2hex(s){
  674. var i
  675. var l
  676. var o = ''
  677. var n
  678. s += ''
  679. for (i = 0, l = s.length; i < l; i++) {
  680. n = s.charCodeAt(i)
  681. .toString(16)
  682. o += n.length < 2 ? '0' + n : n
  683. }
  684. return o
  685. }
  686. static decode_utf8(s) {
  687. return decodeURIComponent(escape(s));
  688. }
  689. }
  690. /**
  691. ArOZ Online Module Utils for quick deploy of ArOZ Online WebApps
  692. ao_module_utils.objectToAttr(object); //object to DOM attr
  693. ao_module_utils.attrToObject(attr); //DOM attr to Object
  694. ao_module_utils.getRandomUID(); //Get random UUID from timestamp
  695. ao_module_utils.getIconFromExt(ext); //Get icon tag from file extension
  696. ao_module_utils.getDropFileInfo(dropEvent); //Get the filepath and filename list from file explorer drag drop
  697. ao_module_utils.formatBytes(byte, decimals); //Format file byte size to human readable size
  698. ao_module_utils.timeConverter(unix_timestamp); //Get human readable timestamp
  699. **/
  700. class ao_module_utils{
  701. //Two simple functions for converting any Javascript object into string that can be put into the attr value of an DOM object
  702. static objectToAttr(object){
  703. return encodeURIComponent(JSON.stringify(object));
  704. }
  705. static attrToObject(attr){
  706. return JSON.parse(decodeURIComponent(attr));
  707. }
  708. //Get a random id for a new floatWindow, use with var uid = ao_module_utils.getRandomUID();
  709. static getRandomUID(){
  710. return new Date().getTime();
  711. }
  712. static stringToBlob(text, mimetype="text/plain"){
  713. var blob = new Blob([text], { type: mimetype });
  714. return blob
  715. }
  716. static blobToFile(blob, filename, mimetype="text/plain"){
  717. var file = new File([blob], filename, {type: mimetype});
  718. return file
  719. }
  720. //Get the icon of a file with given extension (ext), use with ao_module_utils.getIconFromExt("ext");
  721. static getIconFromExt(ext){
  722. var ext = ext.toLowerCase().trim();
  723. var iconList={
  724. md:"file text outline",
  725. txt:"file text outline",
  726. pdf:"file pdf outline",
  727. doc:"file word outline",
  728. docx:"file word outline",
  729. odt:"file word outline",
  730. xlsx:"file excel outline",
  731. ods:"file excel outline",
  732. ppt:"file powerpoint outline",
  733. pptx:"file powerpoint outline",
  734. odp:"file powerpoint outline",
  735. jpg:"file image outline",
  736. png:"file image outline",
  737. jpeg:"file image outline",
  738. gif:"file image outline",
  739. odg:"file image outline",
  740. psd:"file image outline",
  741. zip:"file archive outline",
  742. '7z':"file archive outline",
  743. rar:"file archive outline",
  744. tar:"file archive outline",
  745. mp3:"file audio outline",
  746. m4a:"file audio outline",
  747. flac:"file audio outline",
  748. wav:"file audio outline",
  749. aac:"file audio outline",
  750. mp4:"file video outline",
  751. webm:"file video outline",
  752. php:"file code outline",
  753. html:"file code outline",
  754. htm:"file code outline",
  755. js:"file code outline",
  756. css:"file code outline",
  757. xml:"file code outline",
  758. json:"file code outline",
  759. csv:"file code outline",
  760. odf:"file code outline",
  761. bmp:"file image outline",
  762. rtf:"file text outline",
  763. wmv:"file video outline",
  764. mkv:"file video outline",
  765. ogg:"file audio outline",
  766. stl:"cube",
  767. obj:"cube",
  768. "3ds":"cube",
  769. fbx:"cube",
  770. collada:"cube",
  771. step:"cube",
  772. iges:"cube",
  773. gcode:"cube",
  774. shortcut:"external square",
  775. opus:"file audio outline",
  776. apscene:"cubes"
  777. };
  778. var icon = "";
  779. if (ext == ""){
  780. icon = "folder outline";
  781. }else{
  782. icon = iconList[ext];
  783. if (icon == undefined){
  784. icon = "file outline"
  785. }
  786. }
  787. return icon;
  788. }
  789. //Get the drop file properties {filepath: xxx, filename: xxx} from file drop events from file exploere
  790. static getDropFileInfo(dropEvent){
  791. if (dropEvent.dataTransfer.getData("filedata") !== ""){
  792. var filelist = dropEvent.dataTransfer.getData("filedata");
  793. filelist = JSON.parse(filelist);
  794. return filelist;
  795. }
  796. }
  797. static readFileFromFileObject(fileObject, successCallback, failedCallback=undefined){
  798. let reader = new FileReader();
  799. reader.readAsText(fileObject);
  800. reader.onload = function() {
  801. successCallback(reader.result);
  802. };
  803. reader.onerror = function() {
  804. if (failedCallback != undefined){
  805. failedCallback(reader.error);
  806. }else{
  807. console.log(reader.error);
  808. }
  809. };
  810. }
  811. static durationConverter(seconds){
  812. var days = Math.floor(seconds / 86400);
  813. seconds -= days * 86400;
  814. var hours = Math.floor(seconds / 3600) % 24;
  815. seconds -= hours * 3600;
  816. var minutes = Math.floor(seconds / 60) % 60;
  817. seconds -= minutes * 60;
  818. var seconds = seconds % 60;
  819. var resultDuration = "";
  820. if (days > 0){
  821. resultDuration += days + " Day";
  822. if (days > 1){
  823. resultDuration+= "s"
  824. }
  825. resultDuration += " "
  826. }
  827. if (hours > 0){
  828. resultDuration += hours + " Hour"
  829. if (hours > 1){
  830. resultDuration += "s"
  831. }
  832. resultDuration += " "
  833. }
  834. if (minutes > 0){
  835. resultDuration += minutes + " Minute"
  836. if (minutes > 1){
  837. resultDuration += "s"
  838. }
  839. resultDuration += " "
  840. }
  841. if (seconds > 0){
  842. resultDuration += seconds + " Second"
  843. if (seconds > 1){
  844. resultDuration += "s"
  845. }
  846. resultDuration += " "
  847. }
  848. return resultDuration;
  849. }
  850. static timeConverter(UNIX_timestamp){
  851. var a = new Date(UNIX_timestamp * 1000);
  852. var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  853. var year = a.getFullYear();
  854. var month = months[a.getMonth()];
  855. var date = a.getDate();
  856. var hour = a.getHours().toString().padStart(2, "0");
  857. var min = a.getMinutes().toString().padStart(2, "0");
  858. var sec = a.getSeconds().toString().padStart(2, "0");
  859. var time = date + ' ' + month + ' ' + year + ' ' + hour + ':' + min + ':' + sec ;
  860. return time;
  861. }
  862. static getWebSocketEndpoint(){
  863. let protocol = "wss://";
  864. if (location.protocol !== 'https:') {
  865. protocol = "ws://";
  866. }
  867. let port = window.location.port;
  868. if (window.location.port == ""){
  869. if (location.protocol !== 'https:') {
  870. port = "80";
  871. }else{
  872. port = "443";
  873. }
  874. }
  875. let wsept = (protocol + window.location.hostname + ":" + port);
  876. return wsept;
  877. }
  878. 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]}
  879. }
  880. /*
  881. Backend Programming Logic
  882. These code are design to use with ao_backend.js for wrapping
  883. AGI programming gateway content
  884. */
  885. function ao_module_backend(){
  886. return {
  887. timeout: 500,
  888. start: function(libraryPath){
  889. this.libpath = libraryPath;
  890. //Initialize the parent objects
  891. this.appdata.parent = this;
  892. this.file.parent = this;
  893. this.http.parent = this;
  894. },
  895. _agi_run: function(data, callback, failedcallback = undefined){
  896. $.ajax({
  897. url: ao_root + "system/ajgi/interface?script=" + this.libpath,
  898. method: "POST",
  899. data: data,
  900. success: function(data){
  901. if (typeof(callback) != "undefined"){
  902. callback(data);
  903. }
  904. },
  905. error: function(){
  906. if (typeof(failedcallback) != "undefined"){
  907. failedcallback();
  908. }
  909. console.log("Request failed");
  910. },
  911. timeout: this.timeout
  912. })
  913. },
  914. appdata: {
  915. readFile: function(filepath, callback=undefined){
  916. this.parent._agi_run({
  917. opr: "appdata.readFile",
  918. filepath: filepath
  919. }, callback)
  920. },
  921. listDir: function(filepath, callback=undefined){
  922. this.parent._agi_run({
  923. opr: "appdata.listDir",
  924. filepath: filepath
  925. }, callback)
  926. },
  927. },
  928. file: {
  929. writeFile: function(filepath, content, callback=undefined){
  930. this.parent._agi_run({
  931. opr: "file.writeFile",
  932. filepath: filepath,
  933. content: content,
  934. }, callback)
  935. },
  936. readFile: function(filepath, callback=undefined){
  937. this.parent._agi_run({
  938. opr: "file.readFile",
  939. filepath: filepath
  940. }, callback)
  941. },
  942. deleteFile: function(filepath, callback=undefined){
  943. this.parent._agi_run({
  944. opr: "file.deleteFile",
  945. filepath: filepath
  946. }, callback)
  947. },
  948. readdir: function(filepath, callback=undefined){
  949. this.parent._agi_run({
  950. opr: "file.readdir",
  951. filepath: filepath
  952. }, callback)
  953. },
  954. walk: function(filepath, mode="all", callback=undefined){
  955. this.parent._agi_run({
  956. opr: "file.walk",
  957. filepath: filepath,
  958. mode: mode,
  959. }, callback)
  960. },
  961. glob: function(wildcard, sort="user", callback=undefined){
  962. this.parent._agi_run({
  963. opr: "file.glob",
  964. wildcard: wildcard,
  965. sort: sort,
  966. }, callback)
  967. },
  968. aglob: function(wildcard, sort="user", callback=undefined){
  969. this.parent._agi_run({
  970. opr: "file.aglob",
  971. wildcard: wildcard,
  972. sort: sort,
  973. }, callback)
  974. },
  975. filesize: function(filepath, callback=undefined){
  976. this.parent._agi_run({
  977. opr: "file.filesize",
  978. filepath: filepath
  979. }, callback)
  980. },
  981. fileExists: function(filepath, callback=undefined){
  982. this.parent._agi_run({
  983. opr: "file.fileExists",
  984. filepath: filepath
  985. }, callback)
  986. },
  987. isDir: function(filepath, callback=undefined){
  988. this.parent._agi_run({
  989. opr: "file.isDir",
  990. filepath: filepath
  991. }, callback)
  992. },
  993. mkdir: function(filepath, callback=undefined){
  994. this.parent._agi_run({
  995. opr: "file.mkdir",
  996. filepath: filepath
  997. }, callback)
  998. },
  999. mtime: function(filepath, callback=undefined){
  1000. this.parent._agi_run({
  1001. opr: "file.mtime",
  1002. filepath: filepath
  1003. }, callback)
  1004. },
  1005. rootName: function(filepath, callback=undefined){
  1006. this.parent._agi_run({
  1007. opr: "file.rootName",
  1008. filepath: filepath
  1009. }, callback)
  1010. }
  1011. },
  1012. http: {
  1013. get: function(targetURL, callback=undefined){
  1014. this.parent._agi_run({
  1015. opr: "http.get",
  1016. targetURL: targetURL
  1017. }, callback)
  1018. },
  1019. post: function(targetURL, postdata="", callback=undefined){
  1020. this.parent._agi_run({
  1021. opr: "http.post",
  1022. targetURL: targetURL,
  1023. postdata: postdata
  1024. }, callback)
  1025. },
  1026. head: function(targetURL, header="", callback=undefined){
  1027. this.parent._agi_run({
  1028. opr: "http.head",
  1029. targetURL: targetURL,
  1030. header: header
  1031. }, callback)
  1032. },
  1033. download: function(targetURL, saveDir="tmp:/", saveFilename="", callback=undefined){
  1034. if (saveFilename == ""){
  1035. saveFilename = targetURL.split("/").pop();
  1036. saveFilename = decodeURIComponent(saveFilename);
  1037. }
  1038. this.parent._agi_run({
  1039. opr: "http.download",
  1040. targetURL: targetURL,
  1041. saveDir: saveDir,
  1042. saveFilename: saveFilename
  1043. }, callback)
  1044. },
  1045. }
  1046. }
  1047. }