Microsoft Power Platform/Dynamics 365 CE - Generate TypeScript Definitions

Automatically creates TypeScript type definitions compatible with @types/xrm by extracting form attributes and controls from Dynamics 365/Power Platform model-driven applications.

当前为 2025-04-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Microsoft Power Platform/Dynamics 365 CE - Generate TypeScript Definitions
  3. // @namespace https://github.com/gncnpk/xrm-generate-ts-overloads
  4. // @author Gavin Canon-Phratsachack (https://github.com/gncnpk)
  5. // @version 1.984
  6. // @license GPL-3.0
  7. // @description Automatically creates TypeScript type definitions compatible with @types/xrm by extracting form attributes and controls from Dynamics 365/Power Platform model-driven applications.
  8. // @match https://*.dynamics.com/main.aspx?appid=*&pagetype=entityrecord&etn=*&id=*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. "use strict";
  14.  
  15. const groupItemsByType = (items) => {
  16. return Object.entries(items).reduce((acc, [itemName, itemType]) => {
  17. if (!acc[itemType]) {
  18. acc[itemType] = [];
  19. }
  20. acc[itemType].push(itemName);
  21. return acc;
  22. }, {});
  23. };
  24. // Create a button element and style it to be fixed in the bottom-right corner.
  25. const btn = document.createElement("button");
  26. btn.textContent = "Generate TypeScript Definitions";
  27. btn.style.position = "fixed";
  28. btn.style.bottom = "20px";
  29. btn.style.right = "20px";
  30. btn.style.padding = "10px";
  31. btn.style.backgroundColor = "#007ACC";
  32. btn.style.color = "#fff";
  33. btn.style.border = "none";
  34. btn.style.borderRadius = "5px";
  35. btn.style.cursor = "pointer";
  36. btn.style.zIndex = 10000;
  37. document.body.appendChild(btn);
  38.  
  39. btn.addEventListener("click", () => {
  40. // Mapping objects for Xrm attribute and control types.
  41. var attributeTypeMapping = {
  42. boolean: "Xrm.Attributes.BooleanAttribute",
  43. datetime: "Xrm.Attributes.DateAttribute",
  44. decimal: "Xrm.Attributes.NumberAttribute",
  45. double: "Xrm.Attributes.NumberAttribute",
  46. integer: "Xrm.Attributes.NumberAttribute",
  47. lookup: "Xrm.Attributes.LookupAttribute",
  48. memo: "Xrm.Attributes.StringAttribute",
  49. money: "Xrm.Attributes.NumberAttribute",
  50. multiselectoptionset: "Xrm.Attributes.MultiselectOptionSetAttribute",
  51. optionset: "Xrm.Attributes.OptionSetAttribute",
  52. string: "Xrm.Attributes.StringAttribute",
  53. };
  54.  
  55. var controlTypeMapping = {
  56. standard: "Xrm.Controls.StandardControl",
  57. iframe: "Xrm.Controls.IframeControl",
  58. lookup: "Xrm.Controls.LookupControl",
  59. optionset: "Xrm.Controls.OptionSetControl",
  60. "customsubgrid:MscrmControls.Grid.GridControl":
  61. "Xrm.Controls.GridControl",
  62. subgrid: "Xrm.Controls.GridControl",
  63. timelinewall: "Xrm.Controls.TimelineWall",
  64. quickform: "Xrm.Controls.QuickFormControl",
  65. };
  66.  
  67. var specificControlTypeMapping = {
  68. boolean: "Xrm.Controls.BooleanControl",
  69. datetime: "Xrm.Controls.DateControl",
  70. decimal: "Xrm.Controls.NumberControl",
  71. double: "Xrm.Controls.NumberControl",
  72. integer: "Xrm.Controls.NumberControl",
  73. lookup: "Xrm.Controls.LookupControl",
  74. memo: "Xrm.Controls.StringControl",
  75. money: "Xrm.Controls.NumberControl",
  76. multiselectoptionset: "Xrm.Controls.MultiselectOptionSetControl",
  77. optionset: "Xrm.Controls.OptionSetControl",
  78. string: "Xrm.Controls.StringControl",
  79. };
  80.  
  81. // Object to hold the type information.
  82. const typeInfo = {
  83. subGrids: {},
  84. quickViews: {},
  85. formAttributes: {},
  86. formControls: {},
  87. possibleEnums: [],
  88. formTabs: {},
  89. formEnums: {},
  90. quickViewControls: {},
  91. quickViewAttributes: {}
  92. };
  93. class Subgrid {
  94. constructor() {
  95. this.attributes = {};
  96. this.enums = {};
  97. }
  98. }
  99. class QuickForm {
  100. constructor() {
  101. this.attributes = {};
  102. this.controls = {};
  103. this.enums = {};
  104. }
  105. }
  106.  
  107. class Tab {
  108. constructor() {
  109. this.sections = {};
  110. }
  111. }
  112.  
  113. const currentFormName = Xrm.Page.ui.formSelector
  114. .getCurrentItem()
  115. .getLabel();
  116.  
  117. // Loop through all controls on the form.
  118. if (
  119. typeof Xrm !== "undefined" &&
  120. Xrm.Page &&
  121. typeof Xrm.Page.getControl === "function"
  122. ) {
  123. Xrm.Page.getControl().forEach((ctrl) => {
  124. const ctrlType = ctrl.getControlType();
  125. const mappedType = controlTypeMapping[ctrlType];
  126. if (mappedType) {
  127. typeInfo.formControls[ctrl.getName()] = mappedType;
  128. }
  129. });
  130. } else {
  131. alert("Xrm.Page is not available on this page.");
  132. return;
  133. }
  134.  
  135. // Loop through all tabs and sections on the form.
  136. if (typeof Xrm.Page.ui.tabs.get === "function") {
  137. Xrm.Page.ui.tabs.get().forEach((tab) => {
  138. let formTab = (typeInfo.formTabs[tab.getName()] = new Tab());
  139. tab.sections.forEach((section) => {
  140. formTab.sections[section.getName()] = `${section.getName()}_section`;
  141. });
  142. });
  143. }
  144.  
  145. // Loop through all attributes on the form.
  146. if (typeof Xrm.Page.getAttribute === "function") {
  147. Xrm.Page.getAttribute().forEach((attr) => {
  148. const attrType = attr.getAttributeType();
  149. const attrName = attr.getName();
  150. const mappedType = attributeTypeMapping[attrType];
  151. const mappedControlType = specificControlTypeMapping[attrType];
  152. if (mappedType) {
  153. typeInfo.formAttributes[attrName] = mappedType;
  154. attr.controls.forEach((ctrl) => {
  155. typeInfo.formControls[ctrl.getName()] = mappedControlType;
  156. });
  157. }
  158. if (
  159. attr.getAttributeType() === "optionset" &&
  160. attr.controls.get().length > 0
  161. ) {
  162. const enumValues = attr.getOptions();
  163. if (enumValues) {
  164. typeInfo.formEnums[attrName] = { attribute: "", values: [] };
  165. typeInfo.formEnums[attrName].values = enumValues;
  166. typeInfo.formEnums[attrName].attribute = attrName;
  167. typeInfo.formAttributes[attrName] = `${attrName}_attribute`;
  168. }
  169. }
  170. });
  171. }
  172.  
  173. // Loop through all subgrids on the form.
  174. if (typeof Xrm.Page.getControl === "function") {
  175. Xrm.Page.getControl().forEach((ctrl) => {
  176. if (
  177. ctrl.getControlType() === "subgrid" ||
  178. ctrl.getControlType() ===
  179. "customsubgrid:MscrmControls.Grid.GridControl"
  180. ) {
  181. const gridRow = ctrl.getGrid().getRows().get(0);
  182. const gridName = ctrl.getName();
  183. let subgrid = (typeInfo.subGrids[gridName] = new Subgrid());
  184. typeInfo.formControls[gridName] = `${gridName}_gridcontrol`;
  185. if (gridRow !== null) {
  186. gridRow.data.entity.attributes.forEach((attr) => {
  187. const attrType = attr.getAttributeType();
  188. const attrName = attr.getName();
  189. const mappedType = attributeTypeMapping[attrType];
  190. if (mappedType) {
  191. subgrid.attributes[attrName] = mappedType;
  192. }
  193. if (
  194. attr.getAttributeType() === "optionset" &&
  195. attr.controls.get().length > 0
  196. ) {
  197. const enumValues = attr.getOptions();
  198. if (enumValues) {
  199. subgrid.enums[attrName] = { attribute: "", values: [] };
  200. subgrid.enums[attrName].values = enumValues;
  201. subgrid.enums[attrName].attribute = attrName;
  202. subgrid.attributes[attrName] = `${attrName}_attribute`;
  203. }
  204. }
  205. });
  206. }
  207. }
  208. });
  209. }
  210.  
  211. // Loop through all Quick View controls and attributes on the form.
  212. if (typeof Xrm.Page.ui.quickForms.get === "function") {
  213. Xrm.Page.ui.quickForms.get().forEach((ctrl) => {
  214. const quickViewName = ctrl.getName();
  215. let quickView = (typeInfo.quickViews[quickViewName] = new QuickForm());
  216. const ctrlType = ctrl.getControlType();
  217. typeInfo.formControls[
  218. quickViewName
  219. ] = `${quickViewName}_quickformcontrol`;
  220. ctrl.getControl().forEach((subCtrl) => {
  221. if (typeof subCtrl.getAttribute !== "function") {
  222. return;
  223. }
  224. const subCtrlAttrType = subCtrl.getAttribute().getAttributeType();
  225. const mappedControlType =
  226. specificControlTypeMapping[subCtrlAttrType] ??
  227. controlTypeMapping[subCtrl.getControlType()];
  228. if (mappedControlType) {
  229. quickView.controls[subCtrl.getName()] = mappedControlType;
  230. }
  231. });
  232. ctrl.getAttribute().forEach((attr) => {
  233. const attrType = attr.getAttributeType();
  234. const attrName = attr.getName();
  235. const mappedType = attributeTypeMapping[attrType];
  236. if (mappedType) {
  237. quickView.attributes[attrName] = mappedType;
  238. }
  239. if (attrType === "optionset" && attr.controls.get().length > 0) {
  240. const enumValues = attr.getOptions();
  241. if (enumValues) {
  242. quickView.enums[attrName] = { attribute: "", values: [] };
  243. quickView.enums[attrName].values = enumValues;
  244. quickView.enums[attrName].attribute = attrName;
  245. quickView.attributes[attrName] = `${attrName}_attribute`;
  246. }
  247. }
  248. });
  249. });
  250. }
  251.  
  252. // Build the TypeScript overload string.
  253. let outputTS = `// These TypeScript definitions were generated automatically on: ${new Date().toDateString()}\n`;
  254. for (const [originalEnumName, enumValues] of Object.entries(
  255. typeInfo.formEnums
  256. )) {
  257. if (typeInfo.possibleEnums.includes(originalEnumName)) {
  258. continue;
  259. }
  260. typeInfo.possibleEnums.push(originalEnumName);
  261. let enumName = `${originalEnumName}_enum`;
  262. let enumTemplate = [];
  263. let textLiteralTypes = [];
  264. let valueLiteralTypes = [];
  265. for (const enumValue of enumValues.values) {
  266. enumTemplate.push(
  267. ` ${enumValue.text.replace(/\W/g, "").replace(/[0-9]/g, "")} = ${
  268. enumValue.value
  269. }`
  270. );
  271. textLiteralTypes.push(`"${enumValue.text}"`);
  272. valueLiteralTypes.push(
  273. `${enumName}.${enumValue.text
  274. .replace(/\W/g, "")
  275. .replace(/[0-9]/g, "")}`
  276. );
  277. }
  278. outputTS += `
  279. const enum ${enumName} {
  280. ${enumTemplate.join(",\n")}
  281. }
  282. `;
  283. outputTS += `
  284. interface ${enumValues.attribute}_value extends Xrm.OptionSetValue {
  285. text: ${textLiteralTypes.join(" | ")};
  286. value: ${valueLiteralTypes.join(" | ")};
  287. }
  288. `;
  289. outputTS += `
  290. interface ${enumValues.attribute}_attribute extends Xrm.Attributes.OptionSetAttribute<${enumName}> {
  291. `
  292. valueLiteralTypes.forEach((value, index) => {
  293. outputTS+= `getOption(value: ${value}): {text: ${textLiteralTypes[index]}, value: ${value}};\n`;
  294. })
  295. outputTS+=`getOption(value: ${enumValues.attribute}_value['value']): ${enumValues.attribute}_value | null;
  296. getOptions(): ${enumValues.attribute}_value[];
  297. getSelectedOption(): ${enumValues.attribute}_value | null;
  298. getValue(): ${enumName} | null;
  299. setValue(value: ${enumName} | null): void;
  300. getText(): ${enumValues.attribute}_value['text'] | null;
  301. }
  302. `;
  303. }
  304. for (const [subgridName, subgrid] of Object.entries(typeInfo.subGrids)) {
  305. for (const [enumName, enumValues] of Object.entries(subgrid.enums)) {
  306. if (typeInfo.possibleEnums.includes(enumName)) {
  307. continue;
  308. }
  309. typeInfo.possibleEnums.push(enumName);
  310. let enumTemplate = [];
  311. let textLiteralTypes = [];
  312. let valueLiteralTypes = [];
  313. for (const enumValue of enumValues.values) {
  314. enumTemplate.push(
  315. ` ${enumValue.text.replace(/\W/g, "").replace(/[0-9]/g, "")} = ${
  316. enumValue.value
  317. }`
  318. );
  319. textLiteralTypes.push(`"${enumValue.text}"`);
  320. valueLiteralTypes.push(
  321. `${enumName}_enum.${enumValue.text
  322. .replace(/\W/g, "")
  323. .replace(/[0-9]/g, "")}`
  324. );
  325. }
  326. outputTS += `
  327. const enum ${enumName}_enum {
  328. ${enumTemplate.join(",\n")}
  329. }
  330. `;
  331. outputTS += `
  332. interface ${enumValues.attribute}_value extends Xrm.OptionSetValue {
  333. text: ${textLiteralTypes.join(" | ")};
  334. value: ${valueLiteralTypes.join(" | ")};
  335. }
  336. `;
  337. outputTS += `
  338. interface ${enumValues.attribute}_attribute extends Xrm.Attributes.OptionSetAttribute<${enumName}_enum> {
  339. `
  340. valueLiteralTypes.forEach((value, index) => {
  341. outputTS+= `getOption(value: ${value}): {text: ${textLiteralTypes[index]}, value: ${value}};\n`;
  342. })
  343. outputTS+=`
  344. getOption(value: ${enumValues.attribute}_value['value']): ${enumValues.attribute}_value | null;
  345. getOptions(): ${enumValues.attribute}_value[];
  346. getSelectedOption(): ${enumValues.attribute}_value | null;
  347. getValue(): ${enumName}_enum | null;
  348. setValue(value: ${enumName}_enum | null): void;
  349. getText(): ${enumValues.attribute}_value['text'] | null;
  350. }
  351. `;
  352. }
  353.  
  354. outputTS += `
  355. interface ${subgridName}_attributes extends Xrm.Collection.ItemCollection<${new Set(
  356. Object.values(subgrid.attributes)
  357. )
  358. .map((attrType) => `${attrType}`)
  359. .join(" | ")}> {`;
  360. for (const [attrType, attrNames] of Object.entries(
  361. groupItemsByType(subgrid.attributes)
  362. )) {
  363. outputTS += `get(itemName: "${attrNames.join("\" | \"")}"): ${attrType};\n`;
  364. }
  365. outputTS += `}
  366. `;
  367.  
  368. outputTS += `
  369.  
  370. interface ${subgridName}_entity extends Xrm.Entity {
  371. attributes: ${subgridName}_attributes;
  372. }
  373. interface ${subgridName}_data extends Xrm.Data {
  374. entity: ${subgridName}_entity;
  375. }
  376. interface ${subgridName}_gridrow extends Xrm.Controls.Grid.GridRow {
  377. data: ${subgridName}_data;
  378. }
  379. interface ${subgridName}_grid extends Xrm.Controls.Grid {
  380. getRows(): Xrm.Collection.ItemCollection<${subgridName}_gridrow>;
  381. }
  382. interface ${subgridName}_gridcontrol extends Xrm.Controls.GridControl {
  383. getGrid(): ${subgridName}_grid;
  384. }
  385. interface ${subgridName}_context extends Xrm.FormContext {`;
  386. for (const [attrType, attrNames] of Object.entries(
  387. groupItemsByType(subgrid.attributes)
  388. )) {
  389. outputTS += `getAttribute(attributeName: "${attrNames.join("\" | \"")}"): ${attrType};\n`;
  390. }
  391. outputTS += `getAttribute(attributeNameOrIndex: string | number): ${subgridName}_attributes | null;`
  392. outputTS += `getAttribute(delegateFunction?): ${subgridName}_attributes[];`;
  393. outputTS += `
  394. }
  395. `;
  396. outputTS += `
  397. interface ${subgridName}_eventcontext extends Xrm.Events.EventContext {
  398. getFormContext(): ${subgridName}_context;
  399. }`;
  400. }
  401. for (const [quickViewName, quickView] of Object.entries(
  402. typeInfo.quickViews
  403. )) {
  404. for (const [enumName, enumValues] of Object.entries(quickView.enums)) {
  405. if (typeInfo.possibleEnums.includes(enumName)) {
  406. continue;
  407. }
  408. typeInfo.possibleEnums.push(enumName);
  409. let enumTemplate = [];
  410. let textLiteralTypes = [];
  411. let valueLiteralTypes = [];
  412. for (const enumValue of enumValues.values) {
  413. enumTemplate.push(
  414. ` ${enumValue.text.replace(/\W/g, "").replace(/[0-9]/g, "")} = ${
  415. enumValue.value
  416. }`
  417. );
  418. textLiteralTypes.push(`"${enumValue.text}"`);
  419. valueLiteralTypes.push(
  420. `${enumName}_enum.${enumValue.text
  421. .replace(/\W/g, "")
  422. .replace(/[0-9]/g, "")}`
  423. );
  424. }
  425. outputTS += `
  426. const enum ${enumName}_enum {
  427. ${enumTemplate.join(",\n")}
  428. }
  429. `;
  430. outputTS += `
  431. interface ${enumValues.attribute}_value extends Xrm.OptionSetValue {
  432. text: ${textLiteralTypes.join(" | ")};
  433. value: ${valueLiteralTypes.join(" | ")};
  434. }
  435. `;
  436. outputTS += `
  437. interface ${enumValues.attribute}_attribute extends Xrm.Attributes.OptionSetAttribute<${enumName}_enum> {
  438. `
  439. valueLiteralTypes.forEach((value, index) => {
  440. outputTS+= `getOption(value: ${value}): {text: ${textLiteralTypes[index]}, value: ${value}};\n`;
  441. })
  442. outputTS+=`
  443. getOption(value: ${enumValues.attribute}_value['value']): ${enumValues.attribute}_value | null;
  444. getOptions(): ${enumValues.attribute}_value[];
  445. getSelectedOption(): ${enumValues.attribute}_value | null;
  446. getValue(): ${enumName}_enum | null;
  447. setValue(value: ${enumName}_enum | null): void;
  448. getText(): ${enumValues.attribute}_value['text'] | null;
  449. }
  450. `;
  451. }
  452. outputTS += `type ${quickViewName}_controls_types = ${new Set(
  453. Object.values(quickView.controls)
  454. )
  455. .map((controlType) => `${controlType}`)
  456. .join(" | ")}\n`;
  457. outputTS += `
  458. type ${quickViewName}_attributes_types = ${new Set(
  459. Object.values(quickView.attributes)
  460. )
  461. .map((attrType) => `${attrType}`)
  462. .join(" | ")}\n`;
  463. outputTS += `type ${quickViewName}_controls_literals = ${new Set(
  464. Object.keys(quickView.controls)
  465. )
  466. .map((controlName) => `"${controlName}"`)
  467. .join(" | ")}\n`;
  468. outputTS += `
  469. type ${quickViewName}_attributes_literals = ${new Set(
  470. Object.keys(quickView.attributes)
  471. )
  472. .map((attrName) => `"${attrName}"`)
  473. .join(" | ")}\n`;
  474. outputTS += `
  475. interface ${quickViewName}_quickformcontrol extends Xrm.Controls.QuickFormControl {
  476. `;
  477. for (const [controlType, controlNames] of Object.entries(
  478. groupItemsByType(quickView.controls)
  479. )) {
  480. outputTS += `getControl(controlName: "${controlNames.join("\" | \"")}"): ${controlType};\n`;
  481. }
  482. outputTS += `
  483. getControl(): ${quickViewName}_controls_types[]
  484. getControl(controlNameOrIndex: string | number): ${quickViewName}_controls_types | null;
  485. `;
  486. for (const [attrType, attrNames] of Object.entries(
  487. groupItemsByType(quickView.attributes)
  488. )) {
  489. outputTS += `getAttribute(attributeName: "${attrNames.join("\" | \"")}"): ${attrType};\n`;
  490. }
  491. outputTS += `
  492. getAttribute(attributeNameOrIndex: string | number): ${quickViewName}_attributes_types | null;
  493. getAttribute(): ${quickViewName}_attributes_types[];
  494. }`;
  495. }
  496. for (const [tabName, tab] of Object.entries(typeInfo.formTabs)) {
  497. outputTS += `
  498. interface ${tabName}_sections extends Xrm.Collection.ItemCollection<Xrm.Controls.Section> {`;
  499. outputTS += `get(itemName:"${Object.keys(tab.sections).join("\" | \"")}"): Xrm.Controls.Section;\n`;
  500. outputTS += `}`;
  501.  
  502. outputTS += `
  503. interface ${tabName}_tab extends Xrm.Controls.Tab {
  504. sections: ${tabName}_sections;
  505. }`;
  506. }
  507. outputTS += `
  508. interface ${currentFormName}_tabs extends Xrm.Collection.ItemCollection<${new Set(
  509. Object.keys(typeInfo.formTabs)
  510. )
  511. .map((tabName) => `${tabName}_tab`)
  512. .join(" | ")}> {`;
  513. for (const [itemName, itemType] of Object.entries(typeInfo.formTabs)) {
  514. outputTS += `get(itemName:"${itemName}"): ${itemName}_tab;\n`;
  515. }
  516. outputTS += `}`;
  517. outputTS += `
  518. interface ${currentFormName}_quickforms extends Xrm.Collection.ItemCollection<${new Set(
  519. Object.keys(typeInfo.quickViews)
  520. )
  521. .map((quickViewName) => `${quickViewName}_quickformcontrol`)
  522. .join(" | ")}> {`;
  523. for (const [itemName, itemType] of Object.entries(typeInfo.quickViews)) {
  524. outputTS += `get(itemName:"${itemName}"): ${itemName}_quickformcontrol;\n`;
  525. }
  526. outputTS += `}`;
  527. outputTS += `
  528. type ${currentFormName}_attributes_types = ${new Set(
  529. Object.values(typeInfo.formAttributes)
  530. )
  531. .map((attrType) => `${attrType}`)
  532. .join(" | ")}\n`;
  533. outputTS += `
  534. type ${currentFormName}_attributes_literals = ${new Set(
  535. Object.keys(typeInfo.formAttributes)
  536. )
  537. .map((attrName) => `"${attrName}"`)
  538. .join(" | ")}\n`;
  539. outputTS += `
  540. type ${currentFormName}_controls_types = ${new Set(
  541. Object.values(typeInfo.formControls)
  542. )
  543. .map((ctrlType) => `${ctrlType}`)
  544. .join(" | ")}\n`
  545. outputTS += `
  546. type ${currentFormName}_controls_literals = ${new Set(
  547. Object.keys(typeInfo.formControls)
  548. )
  549. .map((ctrlName) => `"${ctrlName}"`)
  550. .join(" | ")}\n`;
  551. outputTS += `
  552. interface ${currentFormName}_ui extends Xrm.Ui {
  553. quickForms: ${currentFormName}_quickforms | null;
  554. tabs: ${currentFormName}_tabs;
  555.  
  556. }
  557. `;
  558. outputTS += `
  559. interface ${currentFormName}_context extends Xrm.FormContext {
  560. ui: ${currentFormName}_ui;
  561. `;
  562. for (const [controlType, controlNames] of Object.entries(
  563. groupItemsByType(typeInfo.formControls)
  564. )) {
  565. outputTS += `getControl(controlName: "${controlNames.join("\" | \"")}"): ${controlType};\n`;
  566. }
  567. outputTS += `
  568. getControl(controlNameOrIndex: string | number): ${currentFormName}_controls_types | null;
  569. getControl(delegateFunction?): ${currentFormName}_controls_types[];
  570. `
  571. for (const [attrType, attrNames] of Object.entries(
  572. groupItemsByType(typeInfo.formAttributes)
  573. )) {
  574. outputTS += `getAttribute(attributeName: "${attrNames.join("\" | \"")}"): ${attrType};\n`;
  575. }
  576. outputTS += `
  577. getAttribute(attributeNameOrIndex: string | number): ${currentFormName}_attributes_types | null;
  578. getAttribute(delegateFunction?): ${currentFormName}_attributes_types[];
  579. }`;
  580. outputTS += `
  581. interface ${currentFormName}_eventcontext extends Xrm.Events.EventContext {
  582. getFormContext(): ${currentFormName}_context;
  583. }`;
  584.  
  585. // Create a new window with a textarea showing the output.
  586. // The textarea is set to readonly to prevent editing.
  587. const w = window.open(
  588. "",
  589. "_blank",
  590. "width=600,height=400,menubar=no,toolbar=no,location=no,resizable=yes"
  591. );
  592. if (w) {
  593. w.document.write(
  594. "<html><head><title>TypeScript Definitions</title></head><body>"
  595. );
  596. w.document.write(
  597. '<textarea readonly style="width:100%; height:90%;">' +
  598. outputTS +
  599. "</textarea>"
  600. );
  601. w.document.write("</body></html>");
  602. w.document.close();
  603. } else {
  604. // Fallback to prompt if popups are blocked.
  605. prompt("Copy the TypeScript definition:", outputTS);
  606. }
  607. });
  608. })();