file_share.html 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title locale="title/title">File Share</title>
  5. <link rel="stylesheet" href="../../script/semantic/semantic.min.css">
  6. <link rel="stylesheet" href="../../script/ao.css">
  7. <script src="../../script/jquery.min.js"></script>
  8. <script src="../../script/semantic/semantic.min.js"></script>
  9. <script src="../../script/ao_module.js"></script>
  10. <script type="text/javascript" src="../../script/applocale.js"></script>
  11. <script type="text/javascript" src="../../script/qrcode.min.js"></script>
  12. <meta charset="UTF-8">
  13. <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
  14. <style>
  15. body{
  16. background: var(--body_background) !important;
  17. }
  18. .sub.header.darkTheme{
  19. color: rgba(39, 19, 19, 0.623) !important;
  20. }
  21. .darkTheme:not(a){
  22. color: #eeeeee;
  23. }
  24. .yellow.message.darkTheme{
  25. box-shadow: none !important;
  26. border: 0px solid transparent !important;
  27. background-color: #5e550e !important;
  28. }
  29. .dropDown.darkTheme:not(.icon){
  30. background-color: #413f57 !important;
  31. border: 1px solid white;
  32. }
  33. .dropdown.darkThem .ui.label{
  34. color: rgb(175, 175, 175);
  35. background-color: #2f2d3d !important;
  36. }
  37. .dropdown.darkThem .ui.label:hover{
  38. color: white;
  39. opacity: 0.8;
  40. }
  41. </style>
  42. </head>
  43. <body>
  44. <div id="main" class="ui stackable grid">
  45. <div class="eight wide column">
  46. <div style="width: 100%;" class="qrcode" align="center">
  47. <div style="display: block; margin-left: auto; margin-right: auto;" align="center">
  48. <div id="qrcode" style="border: 10px solid white; background-color: white;">
  49. <h1><br><i class="ui loading spinner icon"></i><br></h1>
  50. </div>
  51. </div>
  52. <div style="width: 80%; text-align: center; margin-top:0.4em; ">
  53. <a id="sharelink" href="" target="_blank" style="font-size: 120%; padding-left: 20px; padding-right: 20px; word-break: break-all; overflow-wrap: anywhere;"></a>
  54. </div>
  55. </div>
  56. </div>
  57. <div id="shareSettingOptions" class="eight wide column" style="padding-left: 12px; display:none;">
  58. <div class="ui header">
  59. <span locale="share/setting/title">Share Settings</span>
  60. <div class="sub header" locale="share/setting/subtitle">Change who can see this file</div>
  61. </div>
  62. <div id="shareSettingForm" class="ui form">
  63. <div class="field">
  64. <label><p class="" locale="share/setting/options">Visable options:</p></label>
  65. <div class="ui checkboxes">
  66. <div class="ui radio checkbox">
  67. <input id="anyone" type="radio" class="shareoption" name="shareopt" value="anyone" onchange="updateSharePermission(this);">
  68. <label for="anyone">
  69. <div class="ui header">
  70. <div class="content ">
  71. <i class="globe icon"></i> <span locale="share/setting/anyoneWithLink">Anyone with the link</span>
  72. <div class="sub header" locale="share/setting/anyoneWithLink/desc">Anyone who has the link can access this file</div>
  73. </div>
  74. </div>
  75. </label>
  76. </div>
  77. <br><br>
  78. <div class="ui radio checkbox">
  79. <input id="signedin" type="radio" class="shareoption" value="signedin" name="shareopt" onchange="updateSharePermission(this);">
  80. <label for="signedin">
  81. <div class="ui header">
  82. <div class="content">
  83. <i class="user circle outline icon"></i> <span locale="share/setting/anyoneSignedIn">Anyone signed in</span>
  84. <div class="sub header" locale="share/setting/anyoneSignedIn/desc">Anyone who has signed in can access this file</div>
  85. </div>
  86. </div>
  87. </label>
  88. </div>
  89. <br><br>
  90. <div class="ui radio checkbox">
  91. <input id="samegroup" type="radio" class="shareoption" value="samegroup" name="shareopt" onchange="updateSharePermission(this);">
  92. <label for="samegroup">
  93. <div class="ui header">
  94. <div class="content">
  95. <i class="users icon"></i> <span locale="share/setting/sameGroup">Users in the same group</span>
  96. <div class="sub header" locale="share/setting/sameGroup/desc">Anyone who is in the same group as you do</div>
  97. </div>
  98. </div>
  99. </label>
  100. </div>
  101. <br><br>
  102. <div class="ui accordion" id="advanceShare">
  103. <div class="title">
  104. <i class="dropdown icon"></i>
  105. <span locale="share/setting/advance/title">Advance Share Options</span>
  106. </div>
  107. <div class="content">
  108. <div class="ui radio checkbox">
  109. <input id="users" type="radio" class="shareoption" value="users" name="shareopt" onchange="updateSharePermission(this);">
  110. <label for="users">
  111. <div class="ui header">
  112. <div class="content">
  113. <i class="clipboard check icon"></i> <span locale="share/setting/advance/users">Selected Users</span>
  114. <div class="sub header" locale="share/setting/advance/usersDesc">Selected users with matching user name</div>
  115. </div>
  116. </div>
  117. </label>
  118. </div>
  119. <div id="userselector">
  120. <p style="margin-top: 1em;" locale="share/setting/advance/usersInstruct"">Select target users from the list below</p>
  121. <select id="targetUsersList" class="ui fluid search dropdown" multiple="">
  122. <option value="">Users</option>
  123. </select>
  124. <div id="noUserWarning" class="ui yellow message" style="display:none;">
  125. <i class="caret up icon"></i> <span locale="share/setting/advance/addUserToSave">Add at least one target share user to save changes</span>
  126. </div>
  127. </div>
  128. <div class="ui radio checkbox" style="margin-top: 1em;">
  129. <input id="groups" type="radio" class="shareoption" value="groups" name="shareopt" onchange="updateSharePermission(this);">
  130. <label for="groups">
  131. <div class="ui header">
  132. <div class="content">
  133. <i class="sitemap icon"></i> <span locale="share/setting/advance/groups">Selected Groups</span>
  134. <div class="sub header" locale="share/setting/advance/groupsDesc">All the users that has access to any one of the selected group(s)</div>
  135. </div>
  136. </div>
  137. </label>
  138. </div>
  139. <div id="groupselector">
  140. <p style="margin-top: 1em;" locale="share/setting/advance/groupsInstruct">Select target groups from the list below</p>
  141. <select id="targetGroupList" class="ui fluid search dropdown" multiple="">
  142. <option value="">Groups</option>
  143. </select>
  144. <div id="noGroupWarning" class="ui yellow message" style="display:none;">
  145. <i class="caret up icon"></i> <span locale="share/setting/advance/addGroupToSave">Add at least one target share group to save changes</span>
  146. </div>
  147. </div>
  148. </div>
  149. </div>
  150. <br><br>
  151. <div id="udpateNotification" style="display:none; position: fixed; bottom: 1em; right: 1em;" class="ui green inverted segment">
  152. <i class=" checkmark icon"></i> <span locale="share/setting/updated">Share Setting Updated</span>
  153. </div>
  154. </div>
  155. </div>
  156. </div>
  157. </div>
  158. </div>
  159. <div class="ui divider actionButton"></div>
  160. <div class="actionButton" style="width: 100%; padding-right: 12px;" align="right">
  161. <div class="ui basic button popupbuttons allowHover" onclick="copyLinkToClipboard(this)">
  162. <i class="copy icon"></i> <span locale="button/copy">Copy</span>
  163. </div>
  164. <div id="sharingRemoveBtn" class="ui basic button popupbuttons allowHover" onclick="removeSharing()">
  165. <i class="red remove icon"></i> <span locale="button/remove">Remove</span>
  166. </div>
  167. </div>
  168. <script>
  169. /*
  170. Usage: Pass in file descriptor to start share a file
  171. var fd = encodeURIComponent(JSON.stringify({
  172. filename: "test.txt",
  173. filepath: "user:/Desktop/test.txt"
  174. })
  175. );
  176. window.open("file_share.html#" + fd);
  177. If you want to preset a file sharing mode (aka your module handle the share mode picking),
  178. Pass in with the "shareMode" paramter. Example fd as follow:
  179. {
  180. filename: "test.txt",
  181. filepath: "user:/Desktop/test.txt",
  182. shareMode: "signedin"
  183. }
  184. To remove a share, pass in "remove" for the share mode
  185. Supported Share Mode keywords {anyone/signedin/samegroup/remove}
  186. Other supported flags
  187. {
  188. QRCode: {true/false} //Show or Hide the QR Code and link
  189. ActionButtons: {true/false} //Show or Hide the action button on the bottom
  190. }
  191. */
  192. var shareCurrentEditingUUID = "";
  193. var shareingFileData = {};
  194. var darkTheme = false;
  195. var initialized = false;
  196. var fileSharingURL = "";
  197. var relpath = "../../";
  198. function applyDarkThemeMode(){
  199. $(".ui.message").addClass("inverted");
  200. darkTheme = true;
  201. }
  202. function removeQRCodeInDarkTheme(){
  203. if (darkTheme){
  204. $("#qrcode").css({
  205. "border":"0px solid transparent",
  206. "background-color":"#242330",
  207. })
  208. }
  209. }
  210. if ($(parent) && $(parent.document).find("body").hasClass("darkTheme")){
  211. //Switch to darkTheme mode immediately
  212. applyDarkThemeMode();
  213. }
  214. PageReady();
  215. function PageReady(){
  216. if (initialized){
  217. return;
  218. }
  219. //Load User & Group List
  220. $.get(relpath + "../system/users/list", function(data){
  221. var groups = {};
  222. $("#targetUsersList").html(`<option value="">Users</option>`);
  223. data.forEach(user => {
  224. $("#targetUsersList").append(`<option value="${user[0]}">${user[0]}</option>`);
  225. var userGroups = user[1];
  226. userGroups.forEach(thisGroup => {
  227. groups[thisGroup] = true;
  228. });
  229. });
  230. $("#targetGroupList").html(`<option value="">Groups</option>`);
  231. for (var [key, value] of Object.entries(groups)) {
  232. $("#targetGroupList").append(`<option value="${key}">${key}</option>`);
  233. }
  234. //Initiate the share details
  235. initFileDetails(shareingFileData, function(shareUUID){
  236. //Set the mode of share if it is defined
  237. if (shareingFileData.shareMode !== undefined && shareingFileData.shareMode == "remove"){
  238. //Remove the share UUID
  239. removeSharing();
  240. return;
  241. }
  242. if (shareingFileData.shareMode !== undefined){
  243. //As the share mode is defined by the caller, hide the setting interface
  244. $("#shareSettingOptions").hide();
  245. $("#sharelink").parent().css({
  246. "width": "100%",
  247. "text-align": "left",
  248. });
  249. $.ajax({
  250. url: relpath + "../system/file_system/share/edit",
  251. data: {uuid: shareCurrentEditingUUID, mode: shareingFileData.shareMode},
  252. success: function(data){
  253. if (data.error !== undefined){
  254. alert(data.error);
  255. return;
  256. }
  257. //Update the checkbox
  258. $(".shareoption").each(function(){
  259. if ($(this)[0].value != shareingFileData.shareMode){
  260. $(this)[0].checked = false;
  261. }else{
  262. $(this)[0].checked = true;
  263. }
  264. });
  265. }
  266. });
  267. }else{
  268. //Default: show the setting to allow user adjustment
  269. $("#main").css({
  270. "margin-top":"0em",
  271. "padding": "4px"
  272. });
  273. $("#shareSettingOptions").show();
  274. }
  275. $(".accordion").accordion();
  276. });
  277. });
  278. //Load theme style
  279. $.get(relpath + "../system/file_system/preference?key=file_explorer/theme",function(data){
  280. if (data == "darkTheme"){
  281. $("body").addClass("darkTheme");
  282. applyDarkThemeMode();
  283. }else{
  284. $("body").addClass("whiteTheme");
  285. }
  286. });
  287. initialized = true;
  288. //Do localization
  289. applocale.init(relpath + "../SystemAO/locale/file_share.json", function(){
  290. applocale.translate();
  291. });
  292. $(".checkbox").checkbox();
  293. $(".dropdown").dropdown();
  294. var inputFile = ao_module_loadInputFiles();
  295. if (inputFile == null){
  296. //No file selected
  297. $(".shareoption").parent().addClass("disabled");
  298. $("#qrcode").html(`<h1>No File</h1>`);
  299. $("#sharelink").text(``);
  300. $("#sharingRemoveBtn").addClass("disabled");
  301. return
  302. }
  303. //Make sure one file is choicen each time
  304. inputFile = inputFile[0];
  305. console.log("inputFile", inputFile);
  306. shareingFileData = inputFile;
  307. //Filter out the nessary display flags
  308. if (shareingFileData.QRCode !== undefined && shareingFileData.QRCode == false){
  309. $(".qrcode").hide();
  310. $("#shareSettingOptions").attr("class", "sixteen wide column");
  311. $("#shareSettingOptions").css("padding", "1em");
  312. $(".eight.wide").hide();
  313. }
  314. if (shareingFileData.ActionButtons !== undefined && shareingFileData.ActionButtons == false){
  315. $(".actionButton").hide();
  316. }
  317. }
  318. function initFileDetails(shareingFileData, callback=undefined){
  319. $.ajax({
  320. url: relpath + "../system/file_system/share/new",
  321. data: {path: shareingFileData.filepath},
  322. success: function(data){
  323. if (data.error !== undefined){
  324. alert(data.error);
  325. }else{
  326. console.log(data);
  327. updateShareLinkInfo(data.UUID);
  328. shareCurrentEditingUUID = data.UUID;
  329. $(".shareoption").each(function(){
  330. if ($(this)[0].value != data.Permission){
  331. $(this)[0].checked = false;
  332. }else{
  333. $(this)[0].checked = true;
  334. if (data.Permission == "users"){
  335. $("#advanceShare").accordion("open", 0);
  336. $("#targetUsersList").dropdown("set selected", data.Accessibles);
  337. $("#targetUsersList").parent().removeClass("disabled");
  338. $("#targetGroupList").parent().addClass("disabled");
  339. }else if (data.Permission == "groups"){
  340. $("#advanceShare").accordion("open", 0);
  341. $("#targetGroupList").dropdown("set selected", data.Accessibles);
  342. $("#targetUsersList").parent().addClass("disabled");
  343. $("#targetGroupList").parent().removeClass("disabled");
  344. }else{
  345. $("#targetGroupList").parent().addClass("disabled");
  346. $("#targetUsersList").parent().addClass("disabled");
  347. }
  348. }
  349. });
  350. $("#targetUsersList").on("change", function(evt){
  351. updateSharePermissionByType("users");
  352. });
  353. $("#targetGroupList").on("change", function(evt){
  354. updateSharePermissionByType("groups");
  355. });
  356. //If the file is from desktop, set share icon
  357. if (ao_module_virtualDesktop == true){
  358. var fileDir = shareingFileData.filepath.split("/");
  359. fileDir.pop();
  360. fileDir = fileDir.join("/");
  361. if (fileDir == "user:/Desktop"){
  362. //Remove share icon
  363. parent.setFileShareIndicator(shareingFileData.filename);
  364. }
  365. }
  366. if (callback != undefined){
  367. callback(data.UUID)
  368. }
  369. }
  370. }
  371. });
  372. }
  373. function removeSharing(){
  374. if (shareCurrentEditingUUID == ""){
  375. return
  376. }
  377. //The target file to remove
  378. $.ajax({
  379. url: relpath + "../system/file_system/share/delete",
  380. data: {uuid: shareCurrentEditingUUID},
  381. success: function(data){
  382. if (data.error !== undefined){
  383. alert(data.error);
  384. }else{
  385. //Removed!
  386. $(".button").addClass('disabled');
  387. $(".checkbox").addClass("disabled");
  388. $("#sharelink").text("");
  389. $("#sharelink").attr("href", "#");
  390. $("#qrcode").html(`<br><br><h1><i class="green checkmark icon"></i> ${applocale.getString("message/removed", "Share Removed")}</h1>`);
  391. removeQRCodeInDarkTheme();
  392. //If the file is located on desktop and it is web desktop mode
  393. if (ao_module_virtualDesktop == true){
  394. var fileDir = shareingFileData.filepath.split("/");
  395. fileDir.pop();
  396. fileDir = fileDir.join("/");
  397. if (fileDir == "user:/Desktop"){
  398. //Remove share icon
  399. parent.removeFileShareIndicator(shareingFileData.filename);
  400. }
  401. }
  402. }
  403. }
  404. });
  405. }
  406. function updateSharePermission(object){
  407. var newPermission = $(object).attr('value');
  408. updateSharePermissionByType(newPermission);
  409. }
  410. function updateSharePermissionByType(newPermission){
  411. if (newPermission == "users"){
  412. //Build the user list
  413. $("#targetUsersList").parent().removeClass("disabled");
  414. $("#targetGroupList").parent().addClass("disabled");
  415. var selectedUsers = $("#targetUsersList").val();
  416. $("#noGroupWarning").slideUp("fast");
  417. if (selectedUsers.length == 0){
  418. //Show tips message
  419. $("#noUserWarning").slideDown("fast");
  420. return;
  421. }else{
  422. $("#noUserWarning").slideUp("fast");
  423. }
  424. //Rewrite it to permission handling description
  425. newPermission = "users:" + selectedUsers.join(",");
  426. }else if (newPermission == "groups"){
  427. //Build the group list
  428. $("#targetUsersList").parent().addClass("disabled");
  429. $("#targetGroupList").parent().removeClass("disabled");
  430. var selectedGroups = $("#targetGroupList").val();
  431. $("#noUserWarning").slideUp("fast");
  432. if (selectedGroups.length == 0){
  433. //Show tips message
  434. $("#noGroupWarning").slideDown("fast");
  435. return;
  436. }else{
  437. $("#noGroupWarning").slideUp("fast");
  438. }
  439. //Rewrite it to permission handling description
  440. newPermission = "groups:" + selectedGroups.join(",");
  441. }
  442. $.ajax({
  443. url: relpath + "../system/file_system/share/edit",
  444. data: {uuid: shareCurrentEditingUUID, mode: newPermission},
  445. success: function(data){
  446. if (data.error !== undefined){
  447. alert(data.error);
  448. return;
  449. }
  450. $("#udpateNotification").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast");
  451. }
  452. });
  453. }
  454. function updateShareLinkInfo(uuid){
  455. $("#qrcode").html("");
  456. let protocol = "https://";
  457. if (location.protocol !== 'https:') {
  458. protocol = "http://";
  459. }
  460. var port = ":" + window.location.port;
  461. if (window.location.port == ""){
  462. port = "";
  463. }
  464. var shareURL = protocol + window.location.hostname + port + "/share/" + uuid;
  465. shareCurrentEditingUUID = uuid;
  466. fileSharingURL = shareURL;
  467. new QRCode(document.getElementById("qrcode"), shareURL);
  468. $("#sharelink").text(shareURL);
  469. $("#sharelink").attr("href", shareURL)
  470. }
  471. /*
  472. Dynamic Script Loader
  473. This is a really experimental implementation of importing a script from anywhere
  474. under the ArozOS web root. This section of code must be written in plain JS to make sure
  475. it works without jQuery and other libraries.
  476. This function try to load jQuery and ao_module from the script folder.
  477. Also loading the semantic js and the css main body
  478. */
  479. /*
  480. //The possible location for desktop.system, standard webapp module, SystemAO interfaces and iui sub-interfaces
  481. let possibleLocations = ["script/", "../script/", "../../script/", "../../../script/"];
  482. let loopCount = Math.min(possibleLocations.length, JSON.parse(JSON.stringify(window.location.toString())).split("/").length - 3);
  483. function tryLoad(relpath, filename, successCallback=undefined){
  484. var request = new XMLHttpRequest();
  485. request.open('GET', relpath + filename, true);
  486. request.onreadystatechange = function(){
  487. if (request.readyState === 4){
  488. if (request.status == 200 || request.status == 304) {
  489. if (typeof(successCallback) != "undefined"){
  490. successCallback(relpath, filename);
  491. }
  492. }
  493. }
  494. };
  495. request.send();
  496. }
  497. function injectOtherJavaScriptLibrary(relpath){
  498. //Check if ao_module is loaded
  499. if (typeof(ao_module_virtualDesktop) == "undefined"){
  500. var script = document.createElement('script');
  501. script.setAttribute('src', relpath + "ao_module.js");
  502. document.getElementsByTagName('head')[0].appendChild(script);
  503. }else{
  504. //This routine already run
  505. return;
  506. }
  507. //Inject QR Code library
  508. var script = document.createElement('script');
  509. script.setAttribute('src', relpath + "qrcode.min.js");
  510. document.getElementsByTagName('head')[0].appendChild(script);
  511. //Inject applocale
  512. script = document.createElement('script');
  513. script.setAttribute('src', relpath + "applocale.js");
  514. document.getElementsByTagName('head')[0].appendChild(script);
  515. //Inject semmantic css and js anyway
  516. var head = document.getElementsByTagName('head')[0];
  517. var link = document.createElement('link');
  518. link.id = "semantic";
  519. link.rel = 'stylesheet';
  520. link.type = 'text/css';
  521. link.href = relpath + 'semantic/semantic.min.css';
  522. link.media = 'all';
  523. head.appendChild(link);
  524. var script = document.createElement('script');
  525. script.setAttribute('src', relpath + "semantic/semantic.min.js");
  526. document.getElementsByTagName('head')[0].appendChild(script);
  527. setTimeout(function(){
  528. PageReady();
  529. }, 1000);
  530. }
  531. //Load jQuery first
  532. if (typeof(window.jQuery) == "undefined"){
  533. //jQuery not found. Load it
  534. for (var i = 0; i < loopCount; i++){
  535. var relpath = possibleLocations[i];
  536. tryLoad(relpath, "jquery.min.js", function(relpath, filename){
  537. //Generate the jquery script element
  538. var script = document.createElement('script');
  539. script.setAttribute('src', relpath + filename);
  540. document.getElementsByTagName('head')[0].appendChild(script);
  541. doAfterJqueryLoaded(function(){
  542. injectOtherJavaScriptLibrary(relpath);
  543. });
  544. });
  545. }
  546. }else{
  547. //jQuery exists. Load ao_module
  548. injectOtherJavaScriptLibrary();
  549. }
  550. function doAfterJqueryLoaded(callback){
  551. setTimeout(function(){
  552. //Wait until it is loaded
  553. if (typeof(window.jQuery) == "undefined"){
  554. doAfterJqueryLoaded(callback);
  555. return
  556. }else{
  557. callback();
  558. }
  559. }), 300;
  560. }
  561. */
  562. function copyLinkToClipboard(btn){
  563. //Copy text
  564. const area = document.createElement('textarea');
  565. document.body.appendChild(area);
  566. area.value = fileSharingURL;
  567. area.select();
  568. document.execCommand('copy');
  569. document.body.removeChild(area);
  570. //Do visual feedback
  571. let oldContent = $(btn).html();
  572. $(btn).html(`<i class="green checkmark icon"></i> ${applocale.getString("button/copied","Copied!")}`);
  573. setTimeout(function(){
  574. $(btn).html(oldContent);
  575. }, 3000);
  576. }
  577. </script>
  578. </body>
  579. </html>