bootflags.html 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
  6. <title>Runtime</title>
  7. <script src="../../script/jquery.min.js"></script>
  8. <script src="../../script/ao_module.js"></script>
  9. <script src="../../script/applocale.js"></script>
  10. <style>
  11. /*
  12. Runtime (startup parameters)
  13. Scoped entirely to #rt-root to avoid conflicts with the parent
  14. system-settings CSS. Follows the look of the Host Info page.
  15. */
  16. #rt-root {
  17. --rt-text: #1d1d1f;
  18. --rt-dim: #6e6e73;
  19. --rt-muted: #98989d;
  20. --rt-divider: #e0e0e3;
  21. --rt-card: #ffffff;
  22. --rt-card-bd: #e5e5e7;
  23. --rt-field: #ffffff;
  24. --rt-field-bd: #d2d2d7;
  25. --rt-soft: rgba(0, 0, 0, 0.035);
  26. --rt-accent: #0071e3;
  27. --rt-accent-bg: rgba(0, 113, 227, 0.10);
  28. --rt-toggle: #d1d1d6;
  29. --rt-ok: #1f9d55;
  30. --rt-ok-bg: rgba(31, 157, 85, 0.10);
  31. --rt-err: #d93025;
  32. --rt-err-bg: rgba(217, 48, 37, 0.09);
  33. --rt-shadow: rgba(0, 0, 0, 0.13);
  34. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  35. font-size: 14px;
  36. -webkit-font-smoothing: antialiased;
  37. color: var(--rt-text);
  38. padding: 20px;
  39. max-width: 760px;
  40. margin: 0 auto;
  41. box-sizing: border-box;
  42. }
  43. #rt-root.dark {
  44. --rt-text: #f0f0f0;
  45. --rt-dim: #a0a0a5;
  46. --rt-muted: #737378;
  47. --rt-divider: #3a3a3a;
  48. --rt-card: #2b2b2b;
  49. --rt-card-bd: #3b3b3b;
  50. --rt-field: #1f1f1f;
  51. --rt-field-bd: #48484a;
  52. --rt-soft: rgba(255, 255, 255, 0.05);
  53. --rt-accent: #2997ff;
  54. --rt-accent-bg: rgba(41, 151, 255, 0.14);
  55. --rt-toggle: #48484a;
  56. --rt-ok: #4cc47f;
  57. --rt-ok-bg: rgba(76, 196, 127, 0.13);
  58. --rt-err: #ff6b61;
  59. --rt-err-bg: rgba(255, 107, 97, 0.13);
  60. --rt-shadow: rgba(0, 0, 0, 0.45);
  61. }
  62. #rt-root *, #rt-root *::before, #rt-root *::after { box-sizing: border-box; }
  63. /* Header */
  64. #rt-head {
  65. display: flex;
  66. align-items: center;
  67. gap: 16px;
  68. padding: 8px 0 22px;
  69. }
  70. #rt-head-icon {
  71. width: 56px;
  72. height: 56px;
  73. border-radius: 14px;
  74. flex-shrink: 0;
  75. object-fit: cover;
  76. }
  77. #rt-title {
  78. font-size: 21px;
  79. font-weight: 600;
  80. letter-spacing: -0.3px;
  81. color: var(--rt-text) !important;
  82. margin-bottom: 3px;
  83. }
  84. #rt-subtitle {
  85. font-size: 13px;
  86. color: var(--rt-dim) !important;
  87. line-height: 1.45;
  88. }
  89. /* Groups */
  90. .rt-group { margin-bottom: 22px; }
  91. .rt-group-title {
  92. font-size: 12px;
  93. font-weight: 600;
  94. letter-spacing: 0.2px;
  95. text-transform: uppercase;
  96. color: var(--rt-muted) !important;
  97. margin: 0 0 8px 4px;
  98. }
  99. .rt-card {
  100. background: var(--rt-card);
  101. border: 1px solid var(--rt-card-bd);
  102. border-radius: 12px;
  103. overflow: hidden;
  104. }
  105. .rt-row {
  106. display: flex;
  107. align-items: center;
  108. justify-content: space-between;
  109. gap: 18px;
  110. padding: 13px 16px;
  111. border-bottom: 1px solid var(--rt-divider);
  112. }
  113. .rt-row:last-child { border-bottom: none; }
  114. .rt-row-text { min-width: 0; flex: 1; }
  115. .rt-row-label {
  116. font-size: 13.5px;
  117. font-weight: 500;
  118. color: var(--rt-text) !important;
  119. display: flex;
  120. align-items: center;
  121. flex-wrap: wrap;
  122. gap: 8px;
  123. }
  124. .rt-row-desc {
  125. font-size: 12px;
  126. color: var(--rt-dim) !important;
  127. margin-top: 3px;
  128. line-height: 1.45;
  129. }
  130. .rt-flag {
  131. display: none;
  132. font-size: 11px;
  133. font-weight: 500;
  134. font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  135. color: var(--rt-accent) !important;
  136. background: var(--rt-accent-bg);
  137. padding: 1px 7px;
  138. border-radius: 20px;
  139. white-space: nowrap;
  140. cursor: help;
  141. }
  142. .rt-flag.show { display: inline-block; }
  143. /* Controls */
  144. .rt-control {
  145. flex-shrink: 0;
  146. display: flex;
  147. align-items: center;
  148. gap: 8px;
  149. }
  150. #rt-root input[type="text"],
  151. #rt-root input[type="number"] {
  152. font-family: inherit;
  153. font-size: 13.5px;
  154. color: var(--rt-text);
  155. background: var(--rt-field);
  156. border: 1px solid var(--rt-field-bd);
  157. border-radius: 8px;
  158. padding: 7px 10px;
  159. outline: none;
  160. transition: border-color 0.12s, box-shadow 0.12s;
  161. }
  162. #rt-root input[type="text"] { width: 240px; }
  163. #rt-root input[type="number"] { width: 120px; text-align: right; }
  164. #rt-root input[type="text"]:focus,
  165. #rt-root input[type="number"]:focus {
  166. border-color: var(--rt-accent);
  167. box-shadow: 0 0 0 3px var(--rt-accent-bg);
  168. }
  169. #rt-root input.rt-invalid {
  170. border-color: var(--rt-err);
  171. box-shadow: 0 0 0 3px var(--rt-err-bg);
  172. }
  173. .rt-unit {
  174. font-size: 12px;
  175. color: var(--rt-dim) !important;
  176. min-width: 34px;
  177. }
  178. /* Toggle switch */
  179. .rt-switch {
  180. position: relative;
  181. display: inline-block;
  182. width: 42px;
  183. height: 24px;
  184. flex-shrink: 0;
  185. cursor: pointer;
  186. }
  187. .rt-switch input {
  188. position: absolute;
  189. opacity: 0;
  190. width: 100%;
  191. height: 100%;
  192. margin: 0;
  193. cursor: pointer;
  194. z-index: 1;
  195. }
  196. .rt-switch .rt-track {
  197. position: absolute;
  198. inset: 0;
  199. border-radius: 24px;
  200. background: var(--rt-toggle);
  201. transition: background 0.18s;
  202. }
  203. .rt-switch .rt-track::after {
  204. content: "";
  205. position: absolute;
  206. top: 2px;
  207. left: 2px;
  208. width: 20px;
  209. height: 20px;
  210. border-radius: 50%;
  211. background: #ffffff;
  212. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  213. transition: transform 0.18s;
  214. }
  215. .rt-switch input:checked + .rt-track { background: var(--rt-accent); }
  216. .rt-switch input:checked + .rt-track::after { transform: translateX(18px); }
  217. .rt-switch input:focus-visible + .rt-track { box-shadow: 0 0 0 3px var(--rt-accent-bg); }
  218. /* Info note */
  219. .rt-note {
  220. display: flex;
  221. gap: 10px;
  222. align-items: flex-start;
  223. padding: 12px 14px;
  224. border-radius: 10px;
  225. background: var(--rt-soft);
  226. font-size: 12.5px;
  227. line-height: 1.5;
  228. color: var(--rt-dim) !important;
  229. margin-bottom: 18px;
  230. }
  231. .rt-note svg {
  232. width: 16px;
  233. height: 16px;
  234. flex-shrink: 0;
  235. margin-top: 1px;
  236. color: var(--rt-accent);
  237. }
  238. .rt-note code {
  239. font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  240. font-size: 11.5px;
  241. }
  242. /* Action bar */
  243. #rt-actions {
  244. display: flex;
  245. align-items: center;
  246. justify-content: flex-end;
  247. gap: 10px;
  248. flex-wrap: wrap;
  249. padding-top: 16px;
  250. border-top: 1px solid var(--rt-divider);
  251. }
  252. #rt-status {
  253. margin-right: auto;
  254. font-size: 12.5px;
  255. padding: 5px 10px;
  256. border-radius: 7px;
  257. display: none;
  258. align-items: center;
  259. gap: 6px;
  260. }
  261. #rt-status.ok { display: inline-flex; color: var(--rt-ok) !important; background: var(--rt-ok-bg); }
  262. #rt-status.err { display: inline-flex; color: var(--rt-err) !important; background: var(--rt-err-bg); }
  263. #rt-status svg { width: 14px; height: 14px; flex-shrink: 0; }
  264. .rt-btn {
  265. font-family: inherit;
  266. font-size: 13.5px;
  267. padding: 8px 18px;
  268. border-radius: 8px;
  269. border: none;
  270. cursor: pointer;
  271. transition: opacity 0.12s, background 0.12s;
  272. }
  273. .rt-btn:disabled { opacity: 0.45; cursor: default; }
  274. .rt-btn-primary { background: var(--rt-accent); color: #ffffff !important; }
  275. .rt-btn-primary:not(:disabled):hover { opacity: 0.88; }
  276. .rt-btn-plain {
  277. background: transparent;
  278. color: var(--rt-accent) !important;
  279. }
  280. .rt-btn-plain:not(:disabled):hover { background: var(--rt-accent-bg); }
  281. /* Responsive */
  282. @media (max-width: 560px) {
  283. #rt-root { padding: 14px; }
  284. .rt-row {
  285. flex-direction: column;
  286. align-items: stretch;
  287. gap: 10px;
  288. }
  289. .rt-control { justify-content: flex-start; }
  290. .rt-row.rt-row-toggle {
  291. flex-direction: row;
  292. align-items: center;
  293. }
  294. #rt-root input[type="text"] { width: 100%; }
  295. .rt-control-text { width: 100%; }
  296. #rt-status { width: 100%; margin-right: 0; }
  297. }
  298. </style>
  299. </head>
  300. <body>
  301. <div id="rt-root">
  302. <div id="rt-head">
  303. <img id="rt-head-icon" src="../info/img/runtime.png" alt="">
  304. <div>
  305. <div id="rt-title" locale="runtime/title">Runtime</div>
  306. <div id="rt-subtitle" locale="runtime/subtitle">Adjust startup parameters while the system is running. Changes are saved and restored on every boot.</div>
  307. </div>
  308. </div>
  309. <form id="rt-form" autocomplete="off" onsubmit="return false;">
  310. <div class="rt-group">
  311. <div class="rt-group-title" locale="runtime/group_general">General</div>
  312. <div class="rt-card">
  313. <div class="rt-row">
  314. <div class="rt-row-text">
  315. <div class="rt-row-label">
  316. <span locale="runtime/host_name">Host Name</span>
  317. <span class="rt-flag" data-for="Hostname">-hostname</span>
  318. </div>
  319. <div class="rt-row-desc" locale="runtime/host_name_note">Name shown to users and on the local network. Network discovery picks up a new name after a restart.</div>
  320. </div>
  321. <div class="rt-control rt-control-text">
  322. <input class="rt-config" type="text" name="Hostname" placeholder="My ArOZ" maxlength="64">
  323. </div>
  324. </div>
  325. </div>
  326. </div>
  327. <div class="rt-group">
  328. <div class="rt-group-title" locale="runtime/group_upload">Upload &amp; File I/O</div>
  329. <div class="rt-card">
  330. <div class="rt-row">
  331. <div class="rt-row-text">
  332. <div class="rt-row-label">
  333. <span locale="runtime/max_upload_size">Max Upload Size</span>
  334. <span class="rt-flag" data-for="MaxUploadSize">-max_upload_size</span>
  335. </div>
  336. <div class="rt-row-desc" locale="runtime/max_upload_size_note">Largest file a user can upload. Must not exceed the memory available on this host.</div>
  337. </div>
  338. <div class="rt-control">
  339. <input class="rt-config" type="number" name="MaxUploadSize" min="1" step="1" placeholder="8192">
  340. <span class="rt-unit">MB</span>
  341. </div>
  342. </div>
  343. <div class="rt-row">
  344. <div class="rt-row-text">
  345. <div class="rt-row-label">
  346. <span locale="runtime/file_upload_buffer">File Upload Buffer</span>
  347. <span class="rt-flag" data-for="MaxFileUploadBuff">-upload_buf</span>
  348. </div>
  349. <div class="rt-row-desc" locale="runtime/file_upload_buffer_note">Uploads larger than this are buffered to disk instead of memory (slower).</div>
  350. </div>
  351. <div class="rt-control">
  352. <input class="rt-config" type="number" name="MaxFileUploadBuff" min="1" step="1" placeholder="25">
  353. <span class="rt-unit">MB</span>
  354. </div>
  355. </div>
  356. <div class="rt-row">
  357. <div class="rt-row-text">
  358. <div class="rt-row-label">
  359. <span locale="runtime/file_io_buffer">File IO Buffer</span>
  360. <span class="rt-flag" data-for="FileIOBuffer">-iobuf</span>
  361. </div>
  362. <div class="rt-row-desc" locale="runtime/file_io_buffer_note">Memory used for each file operation. Default is 1024.</div>
  363. </div>
  364. <div class="rt-control">
  365. <input class="rt-config" type="number" name="FileIOBuffer" min="1" step="1" placeholder="1024">
  366. <span class="rt-unit" locale="runtime/unit_bytes">Bytes</span>
  367. </div>
  368. </div>
  369. </div>
  370. </div>
  371. <div class="rt-group">
  372. <div class="rt-group-title" locale="runtime/group_web">Web Services</div>
  373. <div class="rt-card">
  374. <div class="rt-row rt-row-toggle">
  375. <div class="rt-row-text">
  376. <div class="rt-row-label">
  377. <span locale="runtime/disable_ip_resolver">Disable IP Resolver</span>
  378. <span class="rt-flag" data-for="DisableIPResolver">-disable_ip_resolver</span>
  379. </div>
  380. <div class="rt-row-desc" locale="runtime/disable_ip_resolver_note">Do not try to analyze the client IP address. Useful when the server is hosted behind NAT or a reverse proxy.</div>
  381. </div>
  382. <label class="rt-switch">
  383. <input class="rt-config" type="checkbox" name="DisableIPResolver">
  384. <span class="rt-track"></span>
  385. </label>
  386. </div>
  387. <div class="rt-row rt-row-toggle">
  388. <div class="rt-row-text">
  389. <div class="rt-row-label">
  390. <span locale="runtime/enable_home_page">Enable Home Page</span>
  391. <span class="rt-flag" data-for="EnableHomePage">-homepage</span>
  392. </div>
  393. <div class="rt-row-desc" locale="runtime/enable_home_page_note">Allow users to host their own static web pages.</div>
  394. </div>
  395. <label class="rt-switch">
  396. <input class="rt-config" type="checkbox" name="EnableHomePage">
  397. <span class="rt-track"></span>
  398. </label>
  399. </div>
  400. <div class="rt-row rt-row-toggle">
  401. <div class="rt-row-text">
  402. <div class="rt-row-label">
  403. <span locale="runtime/enable_directory_listing">Enable Directory Listing</span>
  404. <span class="rt-flag" data-for="EnableDirListing">-dir_list</span>
  405. </div>
  406. <div class="rt-row-desc" locale="runtime/enable_directory_listing_note">Allow listing the contents of a directory if the request URL is a folder.</div>
  407. </div>
  408. <label class="rt-switch">
  409. <input class="rt-config" type="checkbox" name="EnableDirListing">
  410. <span class="rt-track"></span>
  411. </label>
  412. </div>
  413. </div>
  414. </div>
  415. <div class="rt-note">
  416. <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
  417. <circle cx="8" cy="8" r="6.5"/>
  418. <path d="M8 7.2v4" stroke-linecap="round"/>
  419. <circle cx="8" cy="4.9" r="0.6" fill="currentColor" stroke="none"/>
  420. </svg>
  421. <div locale="runtime/priority_note">Saved values are restored when the system starts. A value passed as a start parameter (for example <code>-hostname</code>) takes priority: it replaces the saved value on that boot. Settings marked with their flag name were set this way.</div>
  422. </div>
  423. <div id="rt-actions">
  424. <span id="rt-status"></span>
  425. <button type="button" class="rt-btn rt-btn-plain" id="rt-revert" disabled locale="runtime/revert">Revert</button>
  426. <button type="button" class="rt-btn rt-btn-primary" id="rt-save" disabled locale="runtime/save">Save</button>
  427. </div>
  428. </form>
  429. </div><!-- #rt-root -->
  430. <script>
  431. (function () {
  432. /*
  433. Own locale instance rather than the shared global one: this page is
  434. loaded as a fragment into System Settings, which keeps its own strings
  435. on the global applocale.
  436. */
  437. var rtLocale = (typeof NewAppLocale === "function") ? NewAppLocale() : {
  438. init: function (f, cb) { cb(); },
  439. translate: function () {},
  440. getString: function (k, fallback) { return fallback; }
  441. };
  442. function rtStr(key, fallback) {
  443. try {
  444. return rtLocale.getString(key, fallback) || fallback;
  445. } catch (e) {
  446. return fallback;
  447. }
  448. }
  449. var ICON_OK = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3.5 8.5l3 3 6-7"/></svg>';
  450. var ICON_ERR = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><circle cx="8" cy="8" r="6.5"/><path d="M8 4.8v4"/><path d="M8 11.2v0.1"/></svg>';
  451. var root = document.getElementById("rt-root");
  452. var loaded = null; // last values read from the server
  453. var statusTimer = null;
  454. /* Theme */
  455. function rtApplyTheme() {
  456. try {
  457. var theme = (typeof preferredTheme !== "undefined") ? preferredTheme :
  458. (parent && parent.preferredTheme) ? parent.preferredTheme : undefined;
  459. if (theme && theme !== "auto") {
  460. root.classList.toggle("dark", theme === "dark");
  461. } else if (typeof ao_module_getSystemThemeColor === "function") {
  462. ao_module_getSystemThemeColor(function (c) {
  463. root.classList.toggle("dark", c !== "whiteTheme");
  464. });
  465. }
  466. } catch (e) {}
  467. }
  468. window.detailPageThemeCallback = function (isDark) {
  469. var el = document.getElementById("rt-root");
  470. if (el) el.classList.toggle("dark", isDark);
  471. };
  472. /* Form helpers */
  473. function fields() {
  474. return root.querySelectorAll(".rt-config");
  475. }
  476. function readForm() {
  477. var result = {};
  478. fields().forEach(function (input) {
  479. if (input.type === "checkbox") {
  480. result[input.name] = input.checked;
  481. } else if (input.type === "number") {
  482. result[input.name] = parseInt(input.value, 10);
  483. } else {
  484. result[input.name] = input.value.trim();
  485. }
  486. });
  487. return result;
  488. }
  489. function fillForm(data) {
  490. fields().forEach(function (input) {
  491. if (!(input.name in data)) return;
  492. if (input.type === "checkbox") {
  493. input.checked = !!data[input.name];
  494. } else {
  495. input.value = data[input.name];
  496. }
  497. input.classList.remove("rt-invalid");
  498. });
  499. var fromFlag = data.FromStartFlag || [];
  500. root.querySelectorAll(".rt-flag").forEach(function (badge) {
  501. var isSet = fromFlag.indexOf(badge.getAttribute("data-for")) >= 0;
  502. badge.classList.toggle("show", isSet);
  503. badge.title = rtStr("runtime/flag_tooltip", "Set by a start parameter this boot. It will replace the saved value again on the next boot unless the parameter is removed.");
  504. });
  505. }
  506. function isDirty() {
  507. if (loaded === null) return false;
  508. var current = readForm();
  509. for (var key in current) {
  510. if (current[key] !== loaded[key]) return true;
  511. }
  512. return false;
  513. }
  514. function validate() {
  515. var ok = true;
  516. fields().forEach(function (input) {
  517. var bad = false;
  518. if (input.type === "number") {
  519. var v = parseInt(input.value, 10);
  520. bad = !(v > 0);
  521. } else if (input.type === "text") {
  522. bad = input.value.trim() === "";
  523. }
  524. input.classList.toggle("rt-invalid", bad);
  525. if (bad) ok = false;
  526. });
  527. return ok;
  528. }
  529. function refreshButtons() {
  530. var dirty = isDirty();
  531. document.getElementById("rt-save").disabled = !dirty;
  532. document.getElementById("rt-revert").disabled = !dirty;
  533. }
  534. function showStatus(ok, text) {
  535. var el = document.getElementById("rt-status");
  536. clearTimeout(statusTimer);
  537. el.className = ok ? "ok" : "err";
  538. el.innerHTML = (ok ? ICON_OK : ICON_ERR);
  539. var span = document.createElement("span");
  540. span.textContent = text;
  541. el.appendChild(span);
  542. if (ok) {
  543. statusTimer = setTimeout(function () { el.className = ""; }, 3500);
  544. }
  545. }
  546. /* Server I/O */
  547. function rtLoad() {
  548. $.get("../../system/bootflags", function (data) {
  549. if (data == null || data.error !== undefined) {
  550. showStatus(false, (data && data.error) || rtStr("runtime/load_failed", "Unable to load the current settings"));
  551. return;
  552. }
  553. loaded = {};
  554. for (var key in data) {
  555. if (key !== "FromStartFlag") loaded[key] = data[key];
  556. }
  557. fillForm(data);
  558. refreshButtons();
  559. }).fail(function () {
  560. showStatus(false, rtStr("runtime/load_failed", "Unable to load the current settings"));
  561. });
  562. }
  563. function rtSave() {
  564. if (!validate()) {
  565. showStatus(false, rtStr("runtime/invalid", "Check the highlighted fields: values must be larger than zero and the host name cannot be empty."));
  566. return;
  567. }
  568. var btn = document.getElementById("rt-save");
  569. btn.disabled = true;
  570. $.ajax({
  571. url: "../../system/bootflags",
  572. method: "POST",
  573. data: { opr: "set", value: JSON.stringify(readForm()) },
  574. success: function (data) {
  575. if (data != null && data.error !== undefined) {
  576. showStatus(false, data.error);
  577. refreshButtons();
  578. return;
  579. }
  580. showStatus(true, rtStr("runtime/saved", "Saved. The new values are active and will be restored on the next boot."));
  581. rtLoad();
  582. },
  583. error: function () {
  584. showStatus(false, rtStr("runtime/save_failed", "Could not save the settings"));
  585. refreshButtons();
  586. }
  587. });
  588. }
  589. /* Boot */
  590. rtApplyTheme();
  591. fields().forEach(function (input) {
  592. input.addEventListener("input", function () {
  593. input.classList.remove("rt-invalid");
  594. refreshButtons();
  595. });
  596. input.addEventListener("change", refreshButtons);
  597. });
  598. document.getElementById("rt-save").addEventListener("click", rtSave);
  599. document.getElementById("rt-revert").addEventListener("click", function () {
  600. if (loaded) fillForm($.extend({}, loaded, { FromStartFlag: currentFlagFields() }));
  601. refreshButtons();
  602. });
  603. document.getElementById("rt-form").addEventListener("keydown", function (e) {
  604. if (e.key === "Enter" && e.target.tagName === "INPUT" && !document.getElementById("rt-save").disabled) {
  605. e.preventDefault();
  606. rtSave();
  607. }
  608. });
  609. function currentFlagFields() {
  610. var list = [];
  611. root.querySelectorAll(".rt-flag.show").forEach(function (b) {
  612. list.push(b.getAttribute("data-for"));
  613. });
  614. return list;
  615. }
  616. rtLocale.init("../locale/system_settings/runtime.json", function () {
  617. rtLocale.translate();
  618. rtLoad();
  619. });
  620. })();
  621. </script>
  622. </body>
  623. </html>