interop.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // This is the interop file, exposing functions that can be called by the host game engine
  2. import editor from "./editor/editor";
  3. import * as editorConfig from "./editor/editorConfig";
  4. /**
  5. * Port to attach Chrome Dev Tools to
  6. * @type {Number}
  7. */
  8. const DEBUG_PORT = 3335;
  9. /**
  10. * Display "Attach dev tools now" alert on startup if this is set to true
  11. * @type {Boolean}
  12. */
  13. const DEBUG_ALERT = false;
  14. /**
  15. * Promise version of atomic query
  16. * @param {string} message the query to use to pass to atomicQuery. If there is no payload, this will be passed directly, otherwise it will be passed in a data object
  17. * @param {any} payload optional data to send
  18. * @return {Promise}
  19. */
  20. function atomicQueryPromise(message: any): Promise<{}> {
  21. return new Promise(function(resolve, reject) {
  22. let queryMessage = message;
  23. // if message is coming in as an object then let's stringify it
  24. if (typeof (message) != "string") {
  25. queryMessage = JSON.stringify(message);
  26. }
  27. window.atomicQuery({
  28. request: queryMessage,
  29. persistent: false,
  30. onSuccess: resolve,
  31. onFailure: (error_code, error_message) => reject({ error_code: error_code, error_message: error_message })
  32. });
  33. });
  34. }
  35. export default class HostInteropType {
  36. static EDITOR_SAVE_CODE = "editorSaveCode";
  37. static EDITOR_SAVE_FILE = "editorSaveFile";
  38. static EDITOR_LOAD_COMPLETE = "editorLoadComplete";
  39. static EDITOR_CHANGE = "editorChange";
  40. private static _inst: HostInteropType = null;
  41. static getInstance(): HostInteropType {
  42. if (HostInteropType._inst == null) {
  43. HostInteropType._inst = new HostInteropType();
  44. }
  45. return HostInteropType._inst;
  46. }
  47. constructor() {
  48. // Set up the window object so the host can call into it
  49. window.HOST_loadCode = this.loadCode.bind(this);
  50. window.HOST_saveCode = this.saveCode.bind(this);
  51. }
  52. /**
  53. * Called from the host to notify the client what file to load
  54. * @param {string} codeUrl
  55. */
  56. loadCode(codeUrl: string) {
  57. console.log("Load Code called for :" + codeUrl);
  58. const fileExt = codeUrl.split(".").pop();
  59. const filename = codeUrl.replace("atomic://", "");
  60. // go ahead and set the theme prior to pulling the file across
  61. editorConfig.configure(fileExt, filename);
  62. // get the code
  63. this.getResource(codeUrl).then((src: string) => {
  64. editorConfig.loadCodeIntoEditor(src, filename, fileExt);
  65. }).catch((e: Editor.ClientExtensions.AtomicErrorMessage) => {
  66. console.log("Error loading code: " + e.error_message);
  67. });
  68. }
  69. /**
  70. * Save the contents of the editor
  71. * @return {Promise}
  72. */
  73. saveCode(): Promise<{}> {
  74. return atomicQueryPromise({
  75. message: HostInteropType.EDITOR_SAVE_CODE,
  76. payload: editor.session.getValue()
  77. });
  78. }
  79. /**
  80. * Save the contents of a file as filename
  81. * @param {string} filename
  82. * @param {string} fileContents
  83. * @return {Promise}
  84. */
  85. saveFile(filename: string, fileContents: string): Promise<{}> {
  86. return atomicQueryPromise({
  87. message: HostInteropType.EDITOR_SAVE_FILE,
  88. filename: filename,
  89. payload: fileContents
  90. });
  91. }
  92. /**
  93. * Call this function when the client is fully loaded up. This will notify the host that
  94. * it can start loading code
  95. */
  96. editorLoaded() {
  97. if (DEBUG_ALERT) {
  98. alert(`Attach chrome dev tools to this instance by navigating to http://localhost:${DEBUG_PORT}`);
  99. }
  100. atomicQueryPromise(HostInteropType.EDITOR_LOAD_COMPLETE);
  101. }
  102. /**
  103. * Queries the host for a particular resource and returns it in a promise
  104. * @param {string} codeUrl
  105. * @return {Promise}
  106. */
  107. getResource(codeUrl: string): Promise<{}> {
  108. return new Promise(function(resolve, reject) {
  109. const xmlHttp = new XMLHttpRequest();
  110. xmlHttp.onreadystatechange = () => {
  111. if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
  112. resolve(xmlHttp.responseText);
  113. }
  114. };
  115. xmlHttp.open("GET", codeUrl, true); // true for asynchronous
  116. xmlHttp.send(null);
  117. });
  118. }
  119. /**
  120. * Returns a file resource from the resources directory
  121. * @param {string} filename name and path of file under the project directory or a fully qualified file name
  122. * @return {Promise}
  123. */
  124. getFileResource(filename: string): Promise<{}> {
  125. return this.getResource(`atomic://${filename}`);
  126. }
  127. /**
  128. * Notify the host that the contents of the editor has changed
  129. */
  130. notifyEditorChange() {
  131. atomicQueryPromise(HostInteropType.EDITOR_CHANGE).catch((e: Editor.ClientExtensions.AtomicErrorMessage) => {
  132. console.log("Error on change: " + e.error_message);
  133. });
  134. }
  135. }
  136. HostInteropType.getInstance().editorLoaded();