You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

cloudrbanin.js 20 kB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. export default async function initCloudrain() {
  2. function paddingZeros(str, len) {
  3. str = str.toString();
  4. if (str.length < len) {
  5. str = new Array(len - str.length).fill('0').join('') + str;
  6. }
  7. return str;
  8. }
  9. function timeFormat(date) {
  10. return `${date.getFullYear()}-${paddingZeros(date.getMonth() + 1, 2)}-${paddingZeros(date.getDate(), 2)} ${paddingZeros(date.getHours(), 2)}:${paddingZeros(date.getMinutes(), 2)}:${paddingZeros(date.getSeconds(), 2)}`;
  11. }
  12. let debug_button = $(".cloudbrain_debug").data("debug");
  13. let debug_again_button = $(".cloudbrain_debug").data("debug-again");
  14. let timeid = window.setInterval(loadJobStatus, 15000);
  15. let timeidShow = window.setInterval(loadShowJobStatus, 15000);
  16. $(document).ready(loadJobStatus);
  17. $(document).ready(loadShowJobStatus);
  18. function loadJobStatus() {
  19. $(".job-status").each((index, job) => {
  20. const ID = job.dataset.jobid;
  21. if (!ID) return;
  22. const repoPath = job.dataset.repopath;
  23. // const computeResource = job.dataset.resource
  24. const versionname = job.dataset.version;
  25. const bootfile = job.dataset.bootfile;
  26. const status_text = $(`#${ID}-text`).text();
  27. const finalState = [
  28. "STOPPED",
  29. "CREATE_FAILED",
  30. "UNAVAILABLE",
  31. "DELETED",
  32. "RESIZE_FAILED",
  33. "SUCCEEDED",
  34. "IMAGE_FAILED",
  35. "SUBMIT_FAILED",
  36. "DELETE_FAILED",
  37. "KILLED",
  38. "COMPLETED",
  39. "FAILED",
  40. "CANCELED",
  41. "LOST",
  42. "START_FAILED",
  43. "SUBMIT_MODEL_FAILED",
  44. "DEPLOY_SERVICE_FAILED",
  45. "CHECK_FAILED",
  46. ];
  47. if (finalState.includes(status_text)) {
  48. return;
  49. }
  50. // const diffResource = computeResource == "NPU" ? 'modelarts/notebook' : 'cloudbrain'
  51. $.get(
  52. `/api/v1/repos/${repoPath}/${ID}?version_name=${versionname}`,
  53. (data) => {
  54. const ID = data.ID || data.JobID;
  55. const status = data.JobStatus;
  56. const duration = data.JobDuration;
  57. $("#duration-" + ID).text(duration);
  58. if (status != status_text) {
  59. $("#" + ID + "-icon")
  60. .removeClass()
  61. .addClass(status);
  62. $("#" + ID + "-text").text(status);
  63. finalState.includes(status) &&
  64. $("#" + ID + "-stop")
  65. .removeClass("blue")
  66. .addClass("disabled");
  67. }
  68. if (status === "RUNNING") {
  69. $("#ai-debug-" + ID)
  70. .removeClass("disabled")
  71. .addClass("blue")
  72. .text(debug_button)
  73. .css("margin", "0 1rem");
  74. $("#model-image-" + ID)
  75. .removeClass("disabled")
  76. .addClass("blue");
  77. }
  78. if (status !== "RUNNING") {
  79. // $('#model-debug-'+ID).removeClass('blue')
  80. // $('#model-debug-'+ID).addClass('disabled')
  81. $("#model-image-" + ID)
  82. .removeClass("blue")
  83. .addClass("disabled");
  84. }
  85. if (
  86. ["CREATING", "STOPPING", "WAITING", "STARTING"].includes(status)
  87. ) {
  88. $("#ai-debug-" + ID)
  89. .removeClass("blue")
  90. .addClass("disabled");
  91. }
  92. if (
  93. [
  94. "STOPPED",
  95. "FAILED",
  96. "START_FAILED",
  97. "CREATE_FAILED",
  98. "SUCCEEDED",
  99. ].includes(status)
  100. ) {
  101. if (!bootfile) {
  102. $("#ai-debug-" + ID)
  103. .removeClass("disabled")
  104. .addClass("blue")
  105. .text(debug_again_button)
  106. .css("margin", "0");
  107. } else {
  108. $("#ai-debug-" + ID).remove()
  109. }
  110. }
  111. if (["RUNNING", "WAITING"].includes(status)) {
  112. $("#ai-stop-" + ID)
  113. .removeClass("disabled")
  114. .addClass("blue");
  115. }
  116. if (
  117. [
  118. "CREATING",
  119. "STOPPING",
  120. "STARTING",
  121. "STOPPED",
  122. "FAILED",
  123. "START_FAILED",
  124. "SUCCEEDED",
  125. "COMPLETED",
  126. "CREATE_FAILED",
  127. ].includes(status)
  128. ) {
  129. $("#ai-stop-" + ID)
  130. .removeClass("blue")
  131. .addClass("disabled");
  132. }
  133. if (
  134. [
  135. "STOPPED",
  136. "FAILED",
  137. "START_FAILED",
  138. "KILLED",
  139. "COMPLETED",
  140. "SUCCEEDED",
  141. "CREATE_FAILED",
  142. ].includes(status)
  143. ) {
  144. $("#ai-delete-" + ID)
  145. .removeClass("disabled")
  146. .addClass("blue");
  147. } else {
  148. $("#ai-delete-" + ID)
  149. .removeClass("blue")
  150. .addClass("disabled");
  151. }
  152. }
  153. ).fail(function (err) {
  154. console.log(err);
  155. });
  156. });
  157. }
  158. function loadShowJobStatus() {
  159. $(".ui.accordion.border-according").each((index, job) => {
  160. const jobID = job.dataset.jobid;
  161. if (!jobID) return;
  162. const repoPath = job.dataset.repopath;
  163. const versionname = job.dataset.version;
  164. // ['IMAGE_FAILED','SUBMIT_FAILED','DELETE_FAILED','KILLED','COMPLETED','FAILED','CANCELED','LOST','START_FAILED']
  165. // if (job.textContent.trim() == 'IMAGE_FAILED' || job.textContent.trim() == 'SUBMIT_FAILED' || job.textContent.trim() == 'DELETE_FAILED'
  166. // || job.textContent.trim() == 'KILLED' || job.textContent.trim() == 'COMPLETED' || job.textContent.trim() == 'FAILED'
  167. // || job.textContent.trim() == 'CANCELED' || job.textContent.trim() == 'LOST') {
  168. // return
  169. // }
  170. let status = $(`#${versionname}-status-span`).text().trim();
  171. if (
  172. [
  173. "IMAGE_FAILED",
  174. "SUBMIT_FAILED",
  175. "DELETE_FAILED",
  176. "KILLED",
  177. "COMPLETED",
  178. "FAILED",
  179. "CANCELED",
  180. "LOST",
  181. "START_FAILED",
  182. "SUCCEEDED",
  183. "STOPPED",
  184. "CREATE_FAILED",
  185. ].includes(status)
  186. ) {
  187. return;
  188. }
  189. let stopArray = [
  190. "KILLED",
  191. "FAILED",
  192. "START_FAILED",
  193. "KILLING",
  194. "COMPLETED",
  195. "SUCCEEDED",
  196. "CREATE_FAILED",
  197. "STOPPED",
  198. ];
  199. let deleteArray = [
  200. "KILLED",
  201. "FAILED",
  202. "START_FAILED",
  203. "COMPLETED",
  204. "SUCCEEDED",
  205. "CREATE_FAILED",
  206. "STOPPED",
  207. ];
  208. $.get(
  209. `/api/v1/repos/${repoPath}/${jobID}?version_name=${versionname}`,
  210. (data) => {
  211. $(`#${versionname}-duration-span`).text(data.JobDuration);
  212. $(`#${versionname}-status-span span`).text(data.JobStatus);
  213. $(`#${versionname}-status-span i`).attr("class", data.JobStatus);
  214. // detail status and duration
  215. data.StartTime !== undefined && data.StartTime > 0 && $("#" + versionname + "-startTime").text(timeFormat(new Date(data.StartTime * 1000)));
  216. $("#" + versionname + "-duration").text(data.JobDuration);
  217. $("#" + versionname + "-status").text(data.JobStatus);
  218. $("#" + versionname + "-ai_center").text(data.AiCenter);
  219. if (stopArray.includes(data.JobStatus)) {
  220. $("#" + versionname + "-stop").addClass("disabled");
  221. }
  222. if (deleteArray.includes(data.JobStatus)) {
  223. $(`#${versionname}-delete`).removeClass("disabled");
  224. $(`#${versionname}-delete`).addClass("blue");
  225. }
  226. if (data.JobStatus === "COMPLETED") {
  227. $("#" + versionname + "-create-model")
  228. .removeClass("disabled")
  229. .addClass("blue");
  230. }
  231. }
  232. ).fail(function (err) {
  233. console.log(err);
  234. });
  235. });
  236. }
  237. function assertDelete(obj, versionName, repoPath) {
  238. if (obj.style.color == "rgb(204, 204, 204)") {
  239. return;
  240. } else {
  241. const delId = obj.parentNode.id;
  242. let flag = 1;
  243. $(".ui.basic.modal")
  244. .modal({
  245. onDeny: function () {
  246. flag = false;
  247. },
  248. onApprove: function () {
  249. if (!versionName) {
  250. document.getElementById(delId).submit();
  251. } else {
  252. deleteVersion(versionName, repoPath);
  253. }
  254. flag = true;
  255. },
  256. onHidden: function () {
  257. if (flag == false) {
  258. $(".alert")
  259. .html("您已取消操作")
  260. .removeClass("alert-success")
  261. .addClass("alert-danger")
  262. .show()
  263. .delay(1500)
  264. .fadeOut();
  265. }
  266. },
  267. })
  268. .modal("show");
  269. }
  270. }
  271. function deleteVersion(versionName, repoPath) {
  272. const url = `/api/v1/repos/${repoPath}`;
  273. $.post(url, { version_name: versionName }, (data) => {
  274. if (data.StatusOK === 0 || data.Code === 0) {
  275. location.reload();
  276. }
  277. }).fail(function (err) {
  278. console.log(err);
  279. });
  280. }
  281. $(".ui.basic.ai_delete").click(function () {
  282. const repoPath = this.dataset.repopath;
  283. const versionName = this.dataset.version;
  284. if (repoPath && versionName) {
  285. assertDelete(this, versionName, repoPath);
  286. } else {
  287. assertDelete(this);
  288. }
  289. });
  290. function stopDebug(ID, stopUrl,bootFile) {
  291. $.ajax({
  292. type: "POST",
  293. url: stopUrl,
  294. data: $("#stopForm-" + ID).serialize(),
  295. success: function (res) {
  296. if (res.result_code === "0") {
  297. $("#" + ID + "-icon")
  298. .removeClass()
  299. .addClass(res.status);
  300. $("#" + ID + "-text").text(res.status);
  301. if (res.status === "STOPPED") {
  302. if (!bootFile) {
  303. $("#ai-debug-" + ID)
  304. .removeClass("disabled")
  305. .addClass("blue")
  306. .text(debug_again_button)
  307. .css("margin", "0");
  308. } else {
  309. $("#ai-debug-" + ID).remove()
  310. }
  311. $("#ai-image-" + ID)
  312. .removeClass("blue")
  313. .addClass("disabled");
  314. $("#ai-model-debug-" + ID)
  315. .removeClass("blue")
  316. .addClass("disabled");
  317. $("#ai-delete-" + ID)
  318. .removeClass("disabled")
  319. .addClass("blue");
  320. $("#ai-stop-" + ID)
  321. .removeClass("blue")
  322. .addClass("disabled");
  323. } else {
  324. $("#ai-debug-" + ID)
  325. .removeClass("blue")
  326. .addClass("disabled");
  327. $("#ai-stop-" + ID)
  328. .removeClass("blue")
  329. .addClass("disabled");
  330. }
  331. } else {
  332. $(".alert")
  333. .html(res.error_msg)
  334. .removeClass("alert-success")
  335. .addClass("alert-danger")
  336. .show()
  337. .delay(2000)
  338. .fadeOut();
  339. }
  340. },
  341. error: function (res) {
  342. console.log(res);
  343. },
  344. });
  345. }
  346. $(".ui.basic.ai_stop").click(function () {
  347. const ID = this.dataset.jobid;
  348. const repoPath = this.dataset.repopath;
  349. const bootFile = this.dataset.bootfile
  350. stopDebug(ID, repoPath,bootFile);
  351. });
  352. function stopVersion(version_name, ID, repoPath) {
  353. const url = `/api/v1/repos/${repoPath}/${ID}/stop_version`;
  354. $.post(url, { version_name: version_name }, (data) => {
  355. if (data.StatusOK === 0) {
  356. $("#ai-stop-" + ID).removeClass("blue");
  357. $("#ai-stop-" + ID).addClass("disabled");
  358. refreshStatus(version_name, ID, repoPath);
  359. }
  360. }).fail(function (err) {
  361. console.log(err);
  362. });
  363. }
  364. function refreshStatus(version_name, ID, repoPath) {
  365. const url = `/api/v1/repos/${repoPath}/${ID}/?version_name${version_name}`;
  366. $.get(url, (data) => {
  367. $(`#${ID}-icon`).attr("class", data.JobStatus);
  368. // detail status and duration
  369. $(`#${ID}-text`).text(data.JobStatus);
  370. if (
  371. [
  372. "STOPPED",
  373. "FAILED",
  374. "START_FAILED",
  375. "KILLED",
  376. "COMPLETED",
  377. "SUCCEEDED",
  378. "CREATE_FAILED",
  379. ].includes(data.JobStatus)
  380. ) {
  381. $("#ai-delete-" + ID)
  382. .removeClass("disabled")
  383. .addClass("blue");
  384. }
  385. }).fail(function (err) {
  386. console.log(err);
  387. });
  388. }
  389. $(".ui.basic.ai_stop_version").click(function () {
  390. const ID = this.dataset.jobid;
  391. const repoPath = this.dataset.repopath;
  392. const versionName = this.dataset.version;
  393. stopVersion(versionName, ID, repoPath);
  394. });
  395. function getModelInfo(repoPath, modelName, versionName, jobName) {
  396. $.get(
  397. `${repoPath}/modelmanage/show_model_info_api?name=${modelName}`,
  398. (data) => {
  399. if (data.length === 0) {
  400. $(`#${jobName}`).popup("toggle");
  401. } else {
  402. let versionData = data.filter((item) => {
  403. return item.version === versionName;
  404. });
  405. if (versionData.length == 0) {
  406. $(`#${jobName}`).popup("toggle");
  407. } else {
  408. location.href = `${repoPath}/modelmanage/show_model_info?name=${modelName}`;
  409. }
  410. }
  411. }
  412. );
  413. }
  414. $(".goto_modelmanage").click(function () {
  415. const repoPath = this.dataset.repopath;
  416. const modelName = this.dataset.modelname;
  417. const versionName = this.dataset.version;
  418. const jobName = this.dataset.jobname;
  419. getModelInfo(repoPath, modelName, versionName, jobName);
  420. });
  421. function debugAgain(ID, debugUrl, redirect_to) {
  422. if ($("#" + ID + "-text").text() === "RUNNING") {
  423. window.open(debugUrl + "debug");
  424. } else {
  425. $.ajax({
  426. type: "POST",
  427. url: debugUrl + "restart?redirect_to=" + redirect_to,
  428. data: $("#debugAgainForm-" + ID).serialize(),
  429. success: function (res) {
  430. if (res["WechatRedirectUrl"]) {
  431. window.location.href = res["WechatRedirectUrl"];
  432. } else if (res.result_code === "0") {
  433. if (res.id !== ID) {
  434. location.reload();
  435. } else {
  436. $("#" + ID + "-icon")
  437. .removeClass()
  438. .addClass(res.status);
  439. $("#" + ID + "-text").text(res.status);
  440. $("#ai-debug-" + ID)
  441. .removeClass("blue")
  442. .addClass("disabled");
  443. $("#ai-delete-" + ID)
  444. .removeClass("blue")
  445. .addClass("disabled");
  446. $("#ai-debug-" + ID)
  447. .text(debug_button)
  448. .css("margin", "0 1rem");
  449. }
  450. } else if (res.result_code == "2") {
  451. $(".ui.modal.debug-again-alert").modal("show");
  452. } else {
  453. $(".alert")
  454. .html(res.error_msg)
  455. .removeClass("alert-success")
  456. .addClass("alert-danger")
  457. .show()
  458. .delay(3000)
  459. .fadeOut();
  460. }
  461. },
  462. error: function (res) {
  463. console.log(res);
  464. },
  465. });
  466. }
  467. }
  468. $(".ui.basic.ai_debug").click(function () {
  469. const ID = this.dataset.jobid;
  470. const repoPath = this.dataset.repopath;
  471. const redirect_to = this.dataset.linkpath;
  472. debugAgain(ID, repoPath, redirect_to);
  473. });
  474. function setWaitNums() {
  475. if ($(".cloudbrain-type").length === 0 && $(".gpu-type").length === 0) {
  476. return;
  477. }
  478. if (
  479. $(".cloudbrain-type").length !== 0 &&
  480. !$(".cloudbrain-type").data("queue")
  481. ) {
  482. return;
  483. }
  484. let waitNums = $(".cloudbrain-type").data("queue").split("map")[1];
  485. let test = new Map();
  486. let waitNumsArray = waitNums.split(" ");
  487. waitNumsArray.forEach((element, index) => {
  488. if (index === 0) {
  489. test.set(element.slice(1, -2), parseInt(element.slice(-1)));
  490. } else if (index === waitNumsArray.length - 1) {
  491. test.set(element.slice(0, -3), parseInt(element.slice(-2, -1)));
  492. } else {
  493. test.set(element.slice(0, -2), parseInt(element.slice(-1)));
  494. }
  495. });
  496. $(".ui.search.dropdown.gpu-type").dropdown({
  497. onChange: function (value, text, $selectedItem) {
  498. let gpuTypeNums = test.get(value);
  499. let gpuTypeNumString =
  500. $(".cloudbrain-type").data("queue-start") +
  501. " " +
  502. gpuTypeNums +
  503. " " +
  504. $(".cloudbrain-type").data("queue-end");
  505. $("#gpu-nums").text(gpuTypeNumString);
  506. },
  507. });
  508. }
  509. setWaitNums();
  510. }
  511. function userSearchControll() {
  512. if ($("#userCloud").length === 0) {
  513. return;
  514. }
  515. const params = new URLSearchParams(window.location.search);
  516. let cluster;
  517. if ($(".cloudbrain_debug").length === 1) {
  518. if (!params.get("cluster")) {
  519. cluster = $(".cloudbrain_debug").data("all-cluster");
  520. } else {
  521. if (params.get("cluster") === "resource_cluster_c2net") {
  522. cluster = $(".cloudbrain_debug").data("cluster-c2net");
  523. } else {
  524. cluster = $(".cloudbrain_debug").data("cluster-openi");
  525. }
  526. }
  527. }
  528. let jobType;
  529. if ($(".cloudbrain_debug").length === 1) {
  530. if (!params.get("jobType")) {
  531. jobType = $(".cloudbrain_debug").data("allTask");
  532. } else {
  533. if (params.get("jobType") === "DEBUG") {
  534. jobType = $(".cloudbrain_debug").data("debug-task");
  535. } else if (params.get("jobType") === "TRAIN") {
  536. jobType = $(".cloudbrain_debug").data("train-task");
  537. } else if (params.get("jobType") === "INFERENCE") {
  538. jobType = $(".cloudbrain_debug").data("inference-task");
  539. } else {
  540. jobType = $(".cloudbrain_debug").data("benchmark-task");
  541. }
  542. }
  543. }
  544. let aiCenter = !params.get("aiCenter")
  545. ? $(".cloudbrain_debug").data("all-aiCenter")
  546. : params.get("aiCenter");
  547. let listType = !params.get("listType")
  548. ? $(".cloudbrain_debug").data("all-compute")
  549. : params.get("listType");
  550. let jobStatus = !params.get("jobStatus")
  551. ? $(".cloudbrain_debug").data("all-status")
  552. : params.get("jobStatus").toUpperCase();
  553. const dropdownValueArray = [cluster, aiCenter, jobType, listType, jobStatus];
  554. $("#userCloud .default.text ").each(function (index, e) {
  555. index != 1 && $(e).text(dropdownValueArray[index]);
  556. });
  557. }
  558. function AdaminSearchControll() {
  559. if ($("#adminCloud").length === 0) {
  560. return;
  561. }
  562. const params = new URLSearchParams(window.location.search);
  563. let cluster;
  564. if ($(".cloudbrain_debug").length === 1) {
  565. if (!params.get("cluster")) {
  566. cluster = $(".cloudbrain_debug").data("all-cluster");
  567. } else {
  568. if (params.get("cluster") === "resource_cluster_c2net") {
  569. cluster = $(".cloudbrain_debug").data("cluster-c2net");
  570. } else {
  571. cluster = $(".cloudbrain_debug").data("cluster-openi");
  572. }
  573. }
  574. }
  575. let aiCenter = !params.get("aiCenter")
  576. ? $(".cloudbrain_debug").data("all-aiCenter")
  577. : params.get("aiCenter");
  578. let jobType = !params.get("jobType")
  579. ? $(".cloudbrain_debug").data("all-task")
  580. : params.get("jobType");
  581. let listType = !params.get("listType")
  582. ? $(".cloudbrain_debug").data("all-compute")
  583. : params.get("listType");
  584. let jobStatus = !params.get("jobStatus")
  585. ? $(".cloudbrain_debug").data("all-status")
  586. : params.get("jobStatus").toUpperCase();
  587. const dropdownValueArray = [cluster, aiCenter, jobType, listType, jobStatus];
  588. $("#adminCloud .default.text ").each(function (index, e) {
  589. index != 1 && $(e).text(dropdownValueArray[index]);
  590. });
  591. }
  592. userSearchControll();
  593. AdaminSearchControll();
  594. $(".message .close").on("click", function () {
  595. $(this).closest(".message").transition("fade");
  596. });