interop.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. //
  2. // Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. // This is the interop file, exposing functions that can be called by the host game engine
  23. import "./utils/WindowExt";
  24. import * as editorCommands from "./editor/editorCommands";
  25. /**
  26. * Port to attach Chrome Dev Tools to
  27. * @type {Number}
  28. */
  29. const DEBUG_PORT = 3335;
  30. /**
  31. * Display "Attach dev tools now" alert on startup if this is set to true
  32. * @type {Boolean}
  33. */
  34. const DEBUG_ALERT = false;
  35. export default class HostInteropType {
  36. private static _inst: HostInteropType = null;
  37. private fileName: string = null;
  38. private fileExt: string = null;
  39. static getInstance(): HostInteropType {
  40. if (HostInteropType._inst == null) {
  41. HostInteropType._inst = new HostInteropType();
  42. }
  43. return HostInteropType._inst;
  44. }
  45. static EDITOR_SAVE_CODE = "editorSaveCode";
  46. static EDITOR_SAVE_FILE = "editorSaveFile";
  47. static EDITOR_LOAD_COMPLETE = "editorLoadComplete";
  48. static EDITOR_CHANGE = "editorChange";
  49. static EDITOR_GET_USER_PREFS = "editorGetUserPrefs";
  50. private setCodeLoaded;
  51. private editorReady = new Promise((resolve, reject) => {
  52. this.setCodeLoaded = resolve;
  53. });
  54. /**
  55. * Called from the host to notify the client what file to load
  56. * @param {string} codeUrl
  57. */
  58. loadCode(codeUrl: string) {
  59. const fileExt = codeUrl.indexOf(".") != -1 ? codeUrl.split(".").pop() : "";
  60. const filename = codeUrl.replace("atomic://", "");
  61. // Keep track of our filename
  62. this.fileName = filename;
  63. this.fileExt = fileExt;
  64. // go ahead and set the theme prior to pulling the file across
  65. editorCommands.configure(fileExt, filename);
  66. // get the code
  67. this.getResource(codeUrl).then((src: string) => {
  68. editorCommands.loadCodeIntoEditor(src, filename, fileExt);
  69. return atomicHostEvent(HostInteropType.EDITOR_GET_USER_PREFS);
  70. }).then(() => {
  71. this.setCodeLoaded();
  72. }).catch((e: Editor.ClientExtensions.AtomicErrorMessage) => {
  73. console.log("Error loading code: " + e.error_message);
  74. });
  75. }
  76. /**
  77. * Save the contents of the editor
  78. * @return {Promise}
  79. */
  80. saveCode(): Promise<any> {
  81. let source = editorCommands.getSourceText();
  82. return atomicHostEvent(HostInteropType.EDITOR_SAVE_CODE, {
  83. payload: source
  84. }).then(() => {
  85. editorCommands.codeSaved(this.fileName, this.fileExt, source);
  86. });
  87. }
  88. /**
  89. * Save the contents of a file as filename
  90. * @param {string} filename
  91. * @param {string} fileContents
  92. * @return {Promise}
  93. */
  94. saveFile(filename: string, fileContents: string): Promise<any> {
  95. return atomicHostEvent(HostInteropType.EDITOR_SAVE_FILE, {
  96. filename: filename,
  97. payload: fileContents
  98. });
  99. }
  100. /**
  101. * Call this function when the client is fully loaded up. This will notify the host that
  102. * it can start loading code
  103. */
  104. editorLoaded() {
  105. if (DEBUG_ALERT) {
  106. alert(`Attach chrome dev tools to this instance by navigating to http://localhost:${DEBUG_PORT}`);
  107. }
  108. editorCommands.editorLoaded();
  109. atomicHostEvent(HostInteropType.EDITOR_LOAD_COMPLETE);
  110. atomicHostRequest<string>("foo").then((d) => alert(d));
  111. }
  112. /**
  113. * Queries the host for a particular resource and returns it in a promise
  114. * @param {string} codeUrl
  115. * @return {Promise}
  116. */
  117. getResource(codeUrl: string): Promise<{}> {
  118. return new Promise(function(resolve, reject) {
  119. const xmlHttp = new XMLHttpRequest();
  120. xmlHttp.onreadystatechange = () => {
  121. if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
  122. resolve(xmlHttp.responseText);
  123. }
  124. };
  125. xmlHttp.open("GET", codeUrl, true); // true for asynchronous
  126. xmlHttp.send(null);
  127. });
  128. }
  129. /**
  130. * Returns a file resource from the resources directory
  131. * @param {string} filename name and path of file under the project directory or a fully qualified file name
  132. * @return {Promise}
  133. */
  134. getFileResource(filename: string): Promise<{}> {
  135. return this.getResource(`atomic://${filename}`);
  136. }
  137. /**
  138. * Notify the host that the contents of the editor has changed
  139. */
  140. notifyEditorChange() {
  141. atomicHostEvent(HostInteropType.EDITOR_CHANGE).catch((e: Editor.ClientExtensions.AtomicErrorMessage) => {
  142. console.log("Error on change: " + e.error_message);
  143. });
  144. }
  145. /**
  146. * Notify that a resource has been renamed
  147. * @param {string} path
  148. * @param {string} newPath
  149. */
  150. resourceRenamed(path: string, newPath: string) {
  151. this.fileName = newPath;
  152. editorCommands.resourceRenamed(path, newPath);
  153. }
  154. /**
  155. * Notify that a resource has been deleted
  156. * @param {string} path
  157. */
  158. resourceDeleted(path: string) {
  159. editorCommands.resourceDeleted(path);
  160. }
  161. /**
  162. * Host is notifying client that there are preferences to load and passing us JSON objects containing the prefs
  163. * of the prefs.
  164. */
  165. preferencesChanged(prefs: Editor.ClientExtensions.PreferencesChangedEventData) {
  166. editorCommands.preferencesChanged(prefs);
  167. }
  168. /**
  169. * This adds a global routine to the window object so that it can be called from the host
  170. * @param {string} routineName
  171. * @param {(} callback
  172. */
  173. addCustomHostRoutine(routineName: string, callback: (...args) => void) {
  174. window[routineName] = callback;
  175. }
  176. /**
  177. * Sets the editor instance
  178. * @param {any} editor
  179. */
  180. setEditor(editor: any) {
  181. editorCommands.setEditor(editor);
  182. }
  183. /**
  184. * Sets the editor used for debugging
  185. * @param {any} editor
  186. * @return {[type]}
  187. */
  188. setDebuggerEditor(editor: any) {
  189. editorCommands.setDebuggerEditor(editor);
  190. }
  191. /**
  192. * Called when a shortcut should be invoked, coming from the host editor
  193. * @param {Editor.EditorShortcutType} shortcut shortcut to be executed
  194. */
  195. invokeShortcut(shortcut: Editor.EditorShortcutType) {
  196. editorCommands.invokeShortcut(shortcut);
  197. }
  198. /**
  199. * Format the code inside the editor
  200. */
  201. formatCode() {
  202. editorCommands.formatCode();
  203. }
  204. /**
  205. * Jump to the provided line number
  206. */
  207. gotoLineNumber(lineNumber:number) {
  208. this.editorReady.then(() => {
  209. editorCommands.gotoLineNumber(lineNumber);
  210. });
  211. }
  212. /**
  213. * Jump to the provided position
  214. */
  215. gotoTokenPos(tokenPos:number) {
  216. editorCommands.gotoTokenPos(tokenPos);
  217. }
  218. }