ao_module.js 40 KB

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