Просмотр исходного кода

Merge remote-tracking branch 'upstream/master' into ShiftClone

# Conflicts:
#	Source/AtomicEditor/Editors/SceneEditor3D/Gizmo3D.cpp
#	Source/AtomicEditor/Editors/SceneEditor3D/Gizmo3D.h
raheelx 9 лет назад
Родитель
Сommit
fa49e3aeff
77 измененных файлов с 6994 добавлено и 872 удалено
  1. 4 0
      AUTHORS.md
  2. 63 6
      Build/CIScripts/GenEditorData.js
  3. 11 0
      Build/Scripts/BuildCommon.js
  4. 220 170
      Build/node_modules/typescript/lib/tsc.js
  5. 277 168
      Build/node_modules/typescript/lib/tsserver.js
  6. 7 8
      Build/node_modules/typescript/lib/typescript.d.ts
  7. 276 171
      Build/node_modules/typescript/lib/typescript.js
  8. 7 8
      Build/node_modules/typescript/lib/typescriptServices.d.ts
  9. 276 171
      Build/node_modules/typescript/lib/typescriptServices.js
  10. 19 15
      Build/node_modules/typescript/package.json
  11. 3 3
      Build/npm-shrinkwrap.json
  12. 3 2
      Build/package.json
  13. 1 1
      Data/AtomicEditor/CodeEditor/.gitignore
  14. 14 3
      Data/AtomicEditor/CodeEditor/Editor.html
  15. 3 0
      Data/AtomicEditor/CodeEditor/source/systemjs/system.js
  16. 1 0
      Data/AtomicEditor/TypeScriptSupport/.gitignore
  17. 9 0
      Data/AtomicEditor/TypeScriptSupport/duktape.d.ts
  18. 7 2
      Script/AtomicEditor/editor/Editor.ts
  19. 40 3
      Script/AtomicEditor/editor/EditorEvents.ts
  20. 297 0
      Script/AtomicEditor/hostExtensions/HostExtensionServices.ts
  21. 93 0
      Script/AtomicEditor/hostExtensions/ServiceLocator.ts
  22. 135 0
      Script/AtomicEditor/hostExtensions/coreExtensions/ProjectBasedExtensionLoader.ts
  23. 201 0
      Script/AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts
  24. 5 0
      Script/AtomicEditor/ui/EditorUI.ts
  25. 2 1
      Script/AtomicEditor/ui/frames/MainFrame.ts
  26. 1 1
      Script/AtomicEditor/ui/frames/ProjectFrame.ts
  27. 26 1
      Script/AtomicEditor/ui/frames/ResourceFrame.ts
  28. 1 1
      Script/AtomicEditor/ui/frames/inspector/InspectorFrame.ts
  29. 32 0
      Script/AtomicEditor/ui/frames/menus/MainFrameMenu.ts
  30. 17 6
      Script/AtomicEditor/ui/frames/menus/MenuItemSources.ts
  31. 1 1
      Script/AtomicEditor/ui/modal/CreateProject.ts
  32. 45 0
      Script/AtomicEditor/ui/modal/ExtensionWindow.ts
  33. 10 0
      Script/AtomicEditor/ui/modal/ModalOps.ts
  34. 26 4
      Script/AtomicEditor/ui/modal/UIResourceOps.ts
  35. 33 0
      Script/AtomicWebViewEditor/clientExtensions/ClientExtensionEventNames.ts
  36. 192 0
      Script/AtomicWebViewEditor/clientExtensions/ClientExtensionServices.ts
  37. 89 0
      Script/AtomicWebViewEditor/clientExtensions/ServiceLocator.ts
  38. 66 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/javascript/JavascriptLanguageExtension.ts
  39. 303 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/TypescriptLanguageExtension.ts
  40. 383 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageService.ts
  41. 374 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageServiceWebWorker.ts
  42. 61 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/workerLoader.ts
  43. 82 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/workerProcessTypes.ts
  44. 22 0
      Script/AtomicWebViewEditor/editor/editor.ts
  45. 125 0
      Script/AtomicWebViewEditor/editor/editorCommands.ts
  46. 0 20
      Script/AtomicWebViewEditor/editor/editorConfig.ts
  47. 208 50
      Script/AtomicWebViewEditor/interop.ts
  48. 2167 0
      Script/AtomicWebViewEditor/modules/typescript.d.ts
  49. 26 4
      Script/AtomicWebViewEditor/tsconfig.json
  50. 0 5
      Script/AtomicWebViewEditor/typings/AtomicQuery.d.ts
  51. 34 0
      Script/AtomicWebViewEditor/typings/WindowExt.d.ts
  52. 21 0
      Script/AtomicWebViewEditor/typings/systemjs.d.ts
  53. 29 0
      Script/AtomicWebViewEditor/typings/webworkers.d.ts
  54. 2 2
      Script/Packages/Atomic/Core.json
  55. 9 2
      Script/Packages/Atomic/Scene.json
  56. 1 1
      Script/TypeScript/AtomicWork.d.ts
  57. 339 0
      Script/TypeScript/EditorWork.d.ts
  58. 17 0
      Script/TypeScript/duktape.d.ts
  59. 15 2
      Script/tsconfig.json
  60. 0 2
      Script/tslint.json
  61. 29 0
      Source/Atomic/IPC/IPC.cpp
  62. 8 0
      Source/Atomic/IPC/IPC.h
  63. 14 0
      Source/Atomic/IPC/IPCWindows.cpp
  64. 1 1
      Source/Atomic/IPC/IPCWindows.h
  65. 6 0
      Source/Atomic/IPC/IPCWorker.cpp
  66. 21 0
      Source/Atomic/Resource/ResourceEvents.h
  67. 23 0
      Source/Atomic/UI/UISelectItem.cpp
  68. 5 0
      Source/Atomic/UI/UISelectItem.h
  69. 2 0
      Source/AtomicEditor/EditorMode/AEEditorMode.cpp
  70. 71 6
      Source/AtomicEditor/Editors/JSResourceEditor.cpp
  71. 5 1
      Source/AtomicEditor/Editors/JSResourceEditor.h
  72. 17 1
      Source/AtomicEditor/Editors/ResourceEditor.cpp
  73. 4 1
      Source/AtomicEditor/Editors/ResourceEditor.h
  74. 3 0
      Source/AtomicWebView/WebBrowserHost.cpp
  75. 14 3
      Source/AtomicWebView/WebClient.cpp
  76. 4 2
      Source/AtomicWebView/WebSchemeHandler.cpp
  77. 36 23
      Source/ThirdParty/SDL/src/video/cocoa/SDL_cocoaevents.m

+ 4 - 0
AUTHORS.md

@@ -27,6 +27,10 @@
 
 - Gareth Fouche (https://github.com/GarethNN)
 
+- Wynand van Vuuren (https://github.com/Vlamboljant)
+
+- Johnny Wahib (https://github.com/JohnnyWahib)
+
 ### Contribution Copyright and Licensing
 
 Atomic Game Engine contribution copyrights are held by their authors.  Each author retains the copyright to their contribution and agrees to irrevocably license the contribution under the Atomic Game Engine Contribution License `CONTRIBUTION_LICENSE.md`.  Please see `CONTRIBUTING.md` for more details.

+ 63 - 6
Build/CIScripts/GenEditorData.js

@@ -6,6 +6,8 @@ var buildDir = bcommon.artifactsRoot + "Build/EditorData/";
 var jsDocFolder = bcommon.artifactsRoot + "Build/JSDoc/";
 var atomicRoot = bcommon.atomicRoot;
 var atomicTool = host.getAtomicToolBinary();
+var glob = require("glob");
+var Tslint = require("tslint");
 
 namespace('build', function() {
 
@@ -87,6 +89,45 @@ namespace('build', function() {
 
   });
 
+  // Linting task
+  task('lint_typescript', {
+      async: true
+  }, function(fileMask, failOnError) {
+
+    console.log("TSLINT: Linting files in " + fileMask);
+    var lintConfig = JSON.parse(fs.readFileSync("./Script/tslint.json"));
+    var options = {
+        configuration: lintConfig,
+        formatter: "prose"
+    };
+
+    // lint
+    // Since TSLint does not yet support recursively searching for files, then we need to
+    // create a command per file.  The main issue with this is that it will abort on the first error instead
+    // of listing out all lint errors
+    glob(fileMask, function(err, results) {
+      var lintErrors = [];
+      results.forEach(function(filename) {
+
+        var contents = fs.readFileSync(filename, "utf8");
+
+        var ll = new Tslint(filename, contents, options);
+        var result = ll.lint();
+        if (result.failureCount > 0) {
+            lintErrors.push(result.output);
+        }
+      });
+      if (lintErrors.length > 0) {
+          console.warn("TSLINT: WARNING - Lint errors detected");
+          console.warn(lintErrors.join(''));
+          if (failOnError) {
+              fail("TSLint errors detected");
+          }
+      }
+      complete();
+    });
+  });
+
   task('compileeditorscripts', ["build:genscriptbindings"],{
     async: true
   }, function() {
@@ -102,17 +143,33 @@ namespace('build', function() {
       atomicRoot + "Build/Mac/node/node " + tsc + " -p ./Script/AtomicWebViewEditor"
     ];
 
-    jake.exec(cmds, function() {
-
       // will be copied when editor resources are copied
 
-      complete();
+    var lintTask = jake.Task['build:lint_typescript'];
 
-    }, {
-      printStdout: true
-    });
+    lintTask.addListener('complete', function () {
+      console.log("\n\nLint: Typescript linting complete.\n\n");
+      jake.exec(cmds, function() {
+
+          // copy some external dependencies into the editor modules directory
+         var editorModulesDir = "./Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts/AtomicEditor/modules";
+         var webeditorModulesDir = "./Data/AtomicEditor/CodeEditor/source/editorCore/modules";
+         var nodeModulesDir = "./Build/node_modules";
+         fs.mkdirsSync(editorModulesDir);
+         // TypeScript
+         fs.copySync(nodeModulesDir + "/typescript/lib/typescript.js", webeditorModulesDir + "/typescript.js")
+
+         // copy lib.core.d.ts into the tool data directory
+         fs.mkdirsSync("./Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts/AtomicEditor/TypeScriptSupport");
+         fs.copySync("./Build/node_modules/typescript/lib/lib.core.d.ts","./Data/AtomicEditor/TypeScriptSupport/lib.core.d.ts")
+         complete();
 
+      }, {
+        printStdout: true
+      });
+    });
 
+    lintTask.invoke("{./Script/AtomicEditor/**/*.ts,./Script/AtomicWebViewEditor/**/*.ts}", false);
   });
 
   task('geneditordata', ["build:compileeditorscripts", "build:ios_deploy", "build:gendocs"], {

+ 11 - 0
Build/Scripts/BuildCommon.js

@@ -88,6 +88,17 @@ namespace('build', function() {
             console.log("\n\nLint: Typescript linting complete.\n\n");
             jake.exec(cmds, function() {
 
+                // copy some external dependencies into the editor modules directory
+               var editorModulesDir = "./Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts/AtomicEditor/modules";
+               var webeditorModulesDir = "./Data/AtomicEditor/CodeEditor/source/editorCore/modules";
+               var nodeModulesDir = "./Build/node_modules";
+               fs.mkdirsSync(editorModulesDir);
+               // TypeScript
+               fs.copySync(nodeModulesDir + "/typescript/lib/typescript.js", webeditorModulesDir + "/typescript.js")
+
+               // copy lib.core.d.ts into the tool data directory
+               fs.mkdirsSync("./Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts/AtomicEditor/TypeScriptSupport");
+               fs.copySync("./Build/node_modules/typescript/lib/lib.core.d.ts","./Data/AtomicEditor/TypeScriptSupport/lib.core.d.ts")
                complete();
 
             }, {

Разница между файлами не показана из-за своего большого размера
+ 220 - 170
Build/node_modules/typescript/lib/tsc.js


Разница между файлами не показана из-за своего большого размера
+ 277 - 168
Build/node_modules/typescript/lib/tsserver.js


+ 7 - 8
Build/node_modules/typescript/lib/typescript.d.ts

@@ -1178,7 +1178,8 @@ declare namespace ts {
         buildSignatureDisplay(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): void;
         buildParameterDisplay(parameter: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
-        buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags): void;
+        buildTypePredicateDisplay(predicate: TypePredicate, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
@@ -1218,17 +1219,18 @@ declare namespace ts {
         This = 0,
         Identifier = 1,
     }
-    interface TypePredicate {
+    interface TypePredicateBase {
         kind: TypePredicateKind;
         type: Type;
     }
-    interface ThisTypePredicate extends TypePredicate {
+    interface ThisTypePredicate extends TypePredicateBase {
         _thisTypePredicateBrand: any;
     }
-    interface IdentifierTypePredicate extends TypePredicate {
+    interface IdentifierTypePredicate extends TypePredicateBase {
         parameterName: string;
         parameterIndex: number;
     }
+    type TypePredicate = IdentifierTypePredicate | ThisTypePredicate;
     enum SymbolFlags {
         None = 0,
         FunctionScopedVariable = 1,
@@ -1329,7 +1331,6 @@ declare namespace ts {
         ESSymbol = 16777216,
         ThisType = 33554432,
         ObjectLiteralPatternWithComputedProperties = 67108864,
-        PredicateType = 134217728,
         StringLike = 258,
         NumberLike = 132,
         ObjectType = 80896,
@@ -1342,9 +1343,6 @@ declare namespace ts {
         symbol?: Symbol;
         pattern?: DestructuringPattern;
     }
-    interface PredicateType extends Type {
-        predicate: ThisTypePredicate | IdentifierTypePredicate;
-    }
     interface StringLiteralType extends Type {
         text: string;
     }
@@ -1479,6 +1477,7 @@ declare namespace ts {
         forceConsistentCasingInFileNames?: boolean;
         allowSyntheticDefaultImports?: boolean;
         allowJs?: boolean;
+        noImplicitUseStrict?: boolean;
         [option: string]: string | number | boolean;
     }
     enum ModuleKind {

Разница между файлами не показана из-за своего большого размера
+ 276 - 171
Build/node_modules/typescript/lib/typescript.js


+ 7 - 8
Build/node_modules/typescript/lib/typescriptServices.d.ts

@@ -1178,7 +1178,8 @@ declare namespace ts {
         buildSignatureDisplay(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): void;
         buildParameterDisplay(parameter: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
-        buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags): void;
+        buildTypePredicateDisplay(predicate: TypePredicate, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
@@ -1218,17 +1219,18 @@ declare namespace ts {
         This = 0,
         Identifier = 1,
     }
-    interface TypePredicate {
+    interface TypePredicateBase {
         kind: TypePredicateKind;
         type: Type;
     }
-    interface ThisTypePredicate extends TypePredicate {
+    interface ThisTypePredicate extends TypePredicateBase {
         _thisTypePredicateBrand: any;
     }
-    interface IdentifierTypePredicate extends TypePredicate {
+    interface IdentifierTypePredicate extends TypePredicateBase {
         parameterName: string;
         parameterIndex: number;
     }
+    type TypePredicate = IdentifierTypePredicate | ThisTypePredicate;
     enum SymbolFlags {
         None = 0,
         FunctionScopedVariable = 1,
@@ -1329,7 +1331,6 @@ declare namespace ts {
         ESSymbol = 16777216,
         ThisType = 33554432,
         ObjectLiteralPatternWithComputedProperties = 67108864,
-        PredicateType = 134217728,
         StringLike = 258,
         NumberLike = 132,
         ObjectType = 80896,
@@ -1342,9 +1343,6 @@ declare namespace ts {
         symbol?: Symbol;
         pattern?: DestructuringPattern;
     }
-    interface PredicateType extends Type {
-        predicate: ThisTypePredicate | IdentifierTypePredicate;
-    }
     interface StringLiteralType extends Type {
         text: string;
     }
@@ -1479,6 +1477,7 @@ declare namespace ts {
         forceConsistentCasingInFileNames?: boolean;
         allowSyntheticDefaultImports?: boolean;
         allowJs?: boolean;
+        noImplicitUseStrict?: boolean;
         [option: string]: string | number | boolean;
     }
     enum ModuleKind {

Разница между файлами не показана из-за своего большого размера
+ 276 - 171
Build/node_modules/typescript/lib/typescriptServices.js


+ 19 - 15
Build/node_modules/typescript/package.json

@@ -1,15 +1,19 @@
 {
   "_args": [
     [
-      "[email protected]",
-      "/Users/shaddockh/Development/AtomicProjects/AtomicGameEngineFork/AtomicGameEngine/Build"
+      "typescript@^1.8.0",
+      "/Users/shaddockh/Development/Atomic/AtomicFork/AtomicGameEngine/Build"
     ]
   ],
-  "_from": "typescript@>=1.8.0 <1.9.0",
-  "_id": "[email protected].0",
+  "_from": "typescript@>=1.8.0 <2.0.0",
+  "_id": "[email protected].2",
   "_inCache": true,
   "_installable": true,
   "_location": "/typescript",
+  "_npmOperationalInternal": {
+    "host": "packages-6-west.internal.npmjs.com",
+    "tmp": "tmp/typescript-1.8.2.tgz_1456180363305_0.6699868906289339"
+  },
   "_npmUser": {
     "email": "[email protected]",
     "name": "typescript"
@@ -18,20 +22,20 @@
   "_phantomChildren": {},
   "_requested": {
     "name": "typescript",
-    "raw": "[email protected]",
-    "rawSpec": "1.8",
+    "raw": "typescript@^1.8.0",
+    "rawSpec": "^1.8.0",
     "scope": null,
-    "spec": ">=1.8.0 <1.9.0",
+    "spec": ">=1.8.0 <2.0.0",
     "type": "range"
   },
   "_requiredBy": [
     "/"
   ],
-  "_resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.0.tgz",
-  "_shasum": "cc5bc63d7f7d84ea26debd7adb774c0362b0ec11",
+  "_resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.2.tgz",
+  "_shasum": "4d2ad7db172be67a913d09862b510133bad61b33",
   "_shrinkwrap": null,
-  "_spec": "[email protected]",
-  "_where": "/Users/shaddockh/Development/AtomicProjects/AtomicGameEngineFork/AtomicGameEngine/Build",
+  "_spec": "typescript@^1.8.0",
+  "_where": "/Users/shaddockh/Development/Atomic/AtomicFork/AtomicGameEngine/Build",
   "author": {
     "name": "Microsoft Corp."
   },
@@ -63,13 +67,13 @@
   },
   "directories": {},
   "dist": {
-    "shasum": "cc5bc63d7f7d84ea26debd7adb774c0362b0ec11",
-    "tarball": "http://registry.npmjs.org/typescript/-/typescript-1.8.0.tgz"
+    "shasum": "4d2ad7db172be67a913d09862b510133bad61b33",
+    "tarball": "http://registry.npmjs.org/typescript/-/typescript-1.8.2.tgz"
   },
   "engines": {
     "node": ">=0.8.0"
   },
-  "gitHead": "a288b84632c1400806df55025ca6b568cfa4d00e",
+  "gitHead": "e5dd34f9e69f517182abfc996a10b8312b14e015",
   "homepage": "http://typescriptlang.org/",
   "keywords": [
     "Microsoft",
@@ -105,5 +109,5 @@
     "test": "jake runtests"
   },
   "typings": "./lib/typescript.d.ts",
-  "version": "1.8.0"
+  "version": "1.8.2"
 }

+ 3 - 3
Build/npm-shrinkwrap.json

@@ -163,9 +163,9 @@
       "resolved": "https://registry.npmjs.org/tslint/-/tslint-3.2.1.tgz"
     },
     "typescript": {
-      "version": "1.8.0",
-      "from": "typescript@>=1.8.0 <1.9.0",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.0.tgz"
+      "version": "1.8.2",
+      "from": "typescript@>=1.8.0 <2.0.0",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.2.tgz"
     },
     "underscore.string": {
       "version": "3.1.1",

+ 3 - 2
Build/package.json

@@ -5,6 +5,7 @@
     "glob": "^6.0.3",
     "jake": "^8.0.12",
     "tslint": "^3.2.0",
-    "typescript": "^1.8.0"
-  }
+    "typescript": "^1.8.2"
+  },
+  "devDependencies": {}
 }

+ 1 - 1
Data/AtomicEditor/CodeEditor/.gitignore

@@ -1 +1 @@
-editor.bundle.*
+source/editorCore

+ 14 - 3
Data/AtomicEditor/CodeEditor/Editor.html

@@ -25,10 +25,21 @@
 
   <script src="./source/ace/ace.js" type="text/javascript" charset="utf-8"></script>
   <script src="./source/ace/ext-language_tools.js"></script>
-  <script src="./source/systemjs/system-register-only.js" type="text/javascript" charset="utf-8"></script>
-  <script src="./editor.bundle.js" type="text/javascript" charset="utf-8"></script>
+  <script src="./source/systemjs/system.js" type="text/javascript" charset="utf-8"></script>
   <script>
-    System.import('./interop');
+    System.config({
+        "baseURL": "/",
+        "defaultJSExtensions": true,
+        // TODO: figure out how to make this be loaded in by the extension instead of being hard-coded in the html page
+        meta: {
+        './source/editorCore/modules/typescript.js': {
+          format: 'global',
+          exports: 'ts',
+        }
+      }
+    });
+
+    System.import('./source/editorCore/interop');
   </script>
 
 </body>

Разница между файлами не показана из-за своего большого размера
+ 3 - 0
Data/AtomicEditor/CodeEditor/source/systemjs/system.js


+ 1 - 0
Data/AtomicEditor/TypeScriptSupport/.gitignore

@@ -0,0 +1 @@
+lib.core.d.ts

+ 9 - 0
Data/AtomicEditor/TypeScriptSupport/duktape.d.ts

@@ -0,0 +1,9 @@
+// Duktape built-ins
+
+// extracted from lib.d.ts
+declare interface Console {
+	log(message?: any, ...optionalParams: any[]): void;
+}
+
+declare var console:Console;
+

+ 7 - 2
Script/AtomicEditor/editor/Editor.ts

@@ -63,7 +63,7 @@ class Editor extends Atomic.ScriptObject {
 
         this.subscribeToEvent(EditorEvents.LoadProject, (data) => this.handleEditorLoadProject(data));
         this.subscribeToEvent(EditorEvents.CloseProject, (data) => this.handleEditorCloseProject(data));
-        this.subscribeToEvent("ProjectUnloaded", (data) => {
+        this.subscribeToEvent(EditorEvents.ProjectUnloadedNotification, (data) => {
             Atomic.graphics.windowTitle = "AtomicEditor";
             this.handleProjectUnloaded(data);
         });
@@ -149,7 +149,11 @@ class Editor extends Atomic.ScriptObject {
             return false;
 
         }
-        return system.loadProject(event.path);
+        const loaded = system.loadProject(event.path);
+        if (loaded) {
+            this.sendEvent(EditorEvents.LoadProjectNotification, event);
+        }
+        return loaded;
     }
 
     closeAllResourceEditors() {
@@ -171,6 +175,7 @@ class Editor extends Atomic.ScriptObject {
 
     handleEditorCloseProject(event) {
         this.projectCloseRequested = true;
+        this.sendEvent(EditorEvents.ProjectUnloadedNotification, event);
         this.closeAllResourceEditors();
     }
 

+ 40 - 3
Script/AtomicEditor/editor/EditorEvents.ts

@@ -20,6 +20,7 @@
 // THE SOFTWARE.
 //
 
+// TODO: migrate these interfaces out to the d.ts and migrate the static strings to some common location
 export const ModalError = "ModalError";
 export interface ModalErrorEvent {
 
@@ -27,7 +28,7 @@ export interface ModalErrorEvent {
   message: string;
 
 }
-
+export const PlayerStartRequest = "EditorPlayRequest";
 export const PlayerStarted = "EditorPlayerStarted";
 export const PlayerStopped = "EditorPlayerStopped";
 export const PlayerPaused = "EditorPlayerPaused";
@@ -65,8 +66,10 @@ export interface ContentFolderChangedEvent {
 
 export const CloseProject = "EditorCloseProject";
 export const ProjectClosed = "EditorProjectClosed";
+export const ProjectUnloadedNotification = "ProjectUnloadedNotification";
 
 export const LoadProject = "EditorLoadProject";
+export const LoadProjectNotification = "EditorLoadProjectNotification";
 export interface LoadProjectEvent {
 
   // The full path to the .atomic file
@@ -77,10 +80,14 @@ export interface LoadProjectEvent {
 export const SaveAllResources = "EditorSaveAllResources";
 
 export const SaveResource = "EditorSaveResource";
+/**
+ * Called once the resource has been saved
+ * @type {String}
+ */
+export const SaveResourceNotification = "EditorSaveResourceNotification";
 export interface SaveResourceEvent {
 
-  // The full path to the resource to save
-  // empty or undefined for current
+  // The full path to the resource to save / empty or undefined for current
   path: string;
 
 }
@@ -101,6 +108,36 @@ export interface EditResourceEvent {
 
 }
 
+export const DeleteResource = "EditorDeleteResource";
+/**
+ * Called once the resource has been deleted
+ * @type {String}
+ */
+export const DeleteResourceNotification = "DeleteResourceNotification";
+export interface DeleteResourceEvent {
+
+  // The full path to the resource to edit
+  path: string;
+
+}
+
+export const RenameResource = "EditorRenameResource";
+/**
+ * Called once the resource has been renamed
+ * @type {String}
+ */
+export const RenameResourceNotification = "RenameResourceNotification";
+export interface RenameResourceEvent {
+
+  // The full path to the resource to edit
+  path: string;
+  newPath: string;
+  newName: string;
+
+  // the asset to delete
+  asset: ToolCore.Asset;
+}
+
 export const SceneEditStateChange = "SceneEditStateChange";
 export interface SceneEditStateChangeEvent {
 

+ 297 - 0
Script/AtomicEditor/hostExtensions/HostExtensionServices.ts

@@ -0,0 +1,297 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import * as EditorEvents from "../editor/EditorEvents";
+import * as EditorUI from "../ui/EditorUI";
+import MainFramMenu = require("../ui/frames/menus/MainFrameMenu");
+import ModalOps = require("../ui/modal/ModalOps");
+/**
+ * Generic registry for storing Editor Extension Services
+ */
+export class ServiceRegistry<T extends Editor.Extensions.EditorService> implements Editor.Extensions.ServiceRegistry<T> {
+    registeredServices: T[] = [];
+
+    /**
+     * Adds a service to the registered services list for this type of service
+     * @param  {T}      service the service to register
+     */
+    register(service: T) {
+        this.registeredServices.push(service);
+    }
+
+    unregister(service: T) {
+        var index = this.registeredServices.indexOf(service, 0);
+        if (index > -1) {
+            this.registeredServices.splice(index, 1);
+        }
+    }
+}
+
+export interface ServiceEventSubscriber {
+    /**
+     * Allow this service registry to subscribe to events that it is interested in
+     * @param  {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
+     */
+    subscribeToEvents(topLevelWindow: Atomic.UIWidget);
+}
+
+/**
+ * Registry for service extensions that are concerned about project events
+ */
+export class ProjectServiceRegistry extends ServiceRegistry<Editor.HostExtensions.ProjectService> implements Editor.HostExtensions.ProjectServiceRegistry {
+    constructor() {
+        super();
+    }
+
+    /**
+     * Allow this service registry to subscribe to events that it is interested in
+     * @param  {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
+     */
+    subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
+        eventDispatcher.subscribeToEvent(EditorEvents.LoadProjectNotification, (ev) => this.projectLoaded(ev));
+        eventDispatcher.subscribeToEvent(EditorEvents.CloseProject, (ev) => this.projectUnloaded(ev));
+        eventDispatcher.subscribeToEvent(EditorEvents.PlayerStartRequest, () => this.playerStarted());
+    }
+
+    /**
+     * Called when the project is unloaded
+     * @param  {[type]} data Event info from the project unloaded event
+     */
+    projectUnloaded(data) {
+        // Need to use a for loop for length down to 0 because extensions *could* delete themselves from the list on projectUnloaded
+        for (let i = this.registeredServices.length - 1; i >= 0; i--) {
+            let service = this.registeredServices[i];
+            // Notify services that the project has been unloaded
+            try {
+                if (service.projectUnloaded) {
+                    service.projectUnloaded();
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e} \n\n ${e.stack}`);
+            }
+        };
+    }
+
+    /**
+     * Called when the project is loaded
+     * @param  {[type]} data Event info from the project unloaded event
+     */
+    projectLoaded(ev: Editor.EditorEvents.LoadProjectEvent) {
+        // Need to use a for loop and don't cache the length because the list of services *may* change while processing.  Extensions could be appended to the end
+        for (let i = 0; i < this.registeredServices.length; i++) {
+            let service = this.registeredServices[i];
+            try {
+                // Notify services that the project has just been loaded
+                if (service.projectLoaded) {
+                    service.projectLoaded(ev);
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
+            }
+        };
+    }
+
+    playerStarted() {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Notify services that the project has just been loaded
+                if (service.playerStarted) {
+                    service.playerStarted();
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+}
+
+/**
+ * Registry for service extensions that are concerned about Resources
+ */
+export class ResourceServiceRegistry extends ServiceRegistry<Editor.HostExtensions.ResourceService> implements Editor.HostExtensions.ResourceServiceRegistry {
+    constructor() {
+        super();
+    }
+
+    /**
+     * Allow this service registry to subscribe to events that it is interested in
+     * @param  {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
+     */
+    subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
+        eventDispatcher.subscribeToEvent(EditorEvents.SaveResourceNotification, (ev) => this.saveResource(ev));
+        eventDispatcher.subscribeToEvent(EditorEvents.DeleteResourceNotification, (ev) => this.deleteResource(ev));
+        eventDispatcher.subscribeToEvent(EditorEvents.RenameResourceNotification, (ev) => this.renameResource(ev));
+    }
+
+    /**
+     * Called after a resource has been saved
+     * @param  {Editor.EditorEvents.SaveResourceEvent} ev
+     */
+    saveResource(ev: Editor.EditorEvents.SaveResourceEvent) {
+        // run through and find any services that can handle this.
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can save
+                if (service.save) {
+                    service.save(ev);
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when a resource has been deleted
+     */
+    deleteResource(ev: Editor.EditorEvents.DeleteResourceEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can delete
+                if (service.delete) {
+                    service.delete(ev);
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when a resource has been renamed
+     * @param  {Editor.EditorEvents.RenameResourceEvent} ev
+     */
+    renameResource(ev: Editor.EditorEvents.RenameResourceEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can handle the rename
+                if (service.rename) {
+                    service.rename(ev);
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
+            }
+        });
+    }
+
+}
+
+/**
+ * Registry for service extensions that are concerned about and need access to parts of the editor user interface
+ * Note: we may want to move this out into it's own file since it has a bunch of editor dependencies
+ */
+export class UIServiceRegistry extends ServiceRegistry<Editor.HostExtensions.UIService> implements Editor.HostExtensions.UIServiceRegistry {
+    constructor() {
+        super();
+    }
+
+    private mainFrameMenu: MainFramMenu = null;
+    private modalOps: ModalOps;
+
+    init(menu: MainFramMenu, modalOps: ModalOps) {
+        // Only set these once
+        if (this.mainFrameMenu == null) {
+            this.mainFrameMenu = menu;
+        }
+        if (this.modalOps == null) {
+            this.modalOps = modalOps;
+        }
+    }
+
+    /**
+     * Adds a new menu to the plugin menu
+     * @param  {string} id
+     * @param  {any} items
+     * @return {Atomic.UIMenuItemSource}
+     */
+    createPluginMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
+        return this.mainFrameMenu.createPluginMenuItemSource(id, items);
+    }
+
+    /**
+     * Removes a previously added menu from the plugin menu
+     * @param  {string} id
+     */
+    removePluginMenuItemSource(id: string) {
+        this.mainFrameMenu.removePluginMenuItemSource(id);
+    }
+
+    /**
+     * Disaplays a modal window
+     * @param  {Editor.Modal.ModalWindow} window
+     */
+    showModalWindow(windowText: string, uifilename: string, handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void): Editor.Modal.ExtensionWindow {
+        return this.modalOps.showExtensionWindow(windowText, uifilename, handleWidgetEventCB);
+    }
+
+    /**
+     * Called when a menu item has been clicked
+     * @param  {string} refId
+     * @type {boolean} return true if handled
+     */
+    menuItemClicked(refId: string): boolean {
+
+        // run through and find any services that can handle this.
+        let holdResult = false;
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can handle it
+                if (service.menuItemClicked) {
+                    if (service.menuItemClicked(refId)) {
+                        holdResult = true;
+                    }
+                }
+            } catch (e) {
+               EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
+            }
+        });
+        return holdResult;
+    }
+
+    /**
+     * Allow this service registry to subscribe to events that it is interested in
+     * @param  {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
+     */
+    subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
+        // Placeholder
+        //eventDispatcher.subscribeToEvent(EditorEvents.SaveResourceNotification, (ev) => this.doSomeUiMessage(ev));
+    }
+
+    /**
+     * Called after a resource has been saved
+     * @param  {Editor.EditorEvents.SaveResourceEvent} ev
+     */
+    doSomeUiMessage(ev: Editor.EditorEvents.SaveResourceEvent) {
+        // PLACEHOLDER
+        // run through and find any services that can handle this.
+        this.registeredServices.forEach((service) => {
+            // try {
+            //     // Verify that the service contains the appropriate methods and that it can save
+            //     if (service.save) {
+            //         service.save(ev);
+            //     }
+            // } catch (e) {
+            //    EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
+            // }
+        });
+    }
+}

+ 93 - 0
Script/AtomicEditor/hostExtensions/ServiceLocator.ts

@@ -0,0 +1,93 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import * as HostExtensionServices from "./HostExtensionServices";
+import * as EditorUI from "../ui/EditorUI";
+import ProjectBasedExtensionLoader from "./coreExtensions/ProjectBasedExtensionLoader";
+import TypescriptLanguageExtension from "./languageExtensions/TypscriptLanguageExtension";
+
+/**
+ * Generic service locator of editor services that may be injected by either a plugin
+ * or by the editor itself.
+ */
+export class ServiceLocatorType implements Editor.HostExtensions.HostServiceLocator {
+
+    constructor() {
+        this.resourceServices = new HostExtensionServices.ResourceServiceRegistry();
+        this.projectServices = new HostExtensionServices.ProjectServiceRegistry();
+        this.uiServices = new HostExtensionServices.UIServiceRegistry();
+    }
+
+    private eventDispatcher: Atomic.UIWidget = null;
+
+    resourceServices: HostExtensionServices.ResourceServiceRegistry;
+    projectServices: HostExtensionServices.ProjectServiceRegistry;
+    uiServices: HostExtensionServices.UIServiceRegistry;
+
+    loadService(service: Editor.HostExtensions.HostEditorService) {
+        try {
+            service.initialize(this);
+        } catch (e) {
+            EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
+        }
+    }
+
+    /**
+     * This is where the top level window will allow the service locator to listen for events and act on them.
+     * @param  {Atomic.UIWidget} frame
+     */
+    subscribeToEvents(frame: Atomic.UIWidget) {
+        this.eventDispatcher = frame;
+        this.resourceServices.subscribeToEvents(this);
+        this.projectServices.subscribeToEvents(this);
+        this.uiServices.subscribeToEvents(this);
+    }
+
+    /**
+     * Send a custom event.  This can be used by services to publish custom events
+     * @param  {string} eventType
+     * @param  {any} data
+     */
+    sendEvent(eventType: string, data: any) {
+        if (this.eventDispatcher) {
+            this.eventDispatcher.sendEvent(eventType, data);
+        }
+    }
+
+    /**
+     * Subscribe to an event and provide a callback.  This can be used by services to subscribe to custom events
+     * @param  {string} eventType
+     * @param  {any} callback
+     */
+    subscribeToEvent(eventType: string, callback: (data: any) => void) {
+        if (this.eventDispatcher) {
+            this.eventDispatcher.subscribeToEvent(eventType, callback);
+        }
+    }
+}
+// Singleton service locator that can be referenced
+const serviceLocator = new ServiceLocatorType();
+export default serviceLocator;
+
+// Load up all the internal services
+serviceLocator.loadService(new ProjectBasedExtensionLoader());
+serviceLocator.loadService(new TypescriptLanguageExtension());

+ 135 - 0
Script/AtomicEditor/hostExtensions/coreExtensions/ProjectBasedExtensionLoader.ts

@@ -0,0 +1,135 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/// <reference path="../../../TypeScript/duktape.d.ts" />
+
+import * as EditorEvents from "../../editor/EditorEvents";
+
+/**
+ * Resource extension that supports the web view typescript extension
+ */
+export default class ProjectBasedExtensionLoader implements Editor.HostExtensions.ProjectService {
+    name: string = "ProjectBasedExtensionLoader";
+    description: string = "This service supports loading extensions that reside in the project under {ProjectRoot}/Editor and named '*.Service.js'.";
+
+    private serviceRegistry: Editor.HostExtensions.HostServiceLocator = null;
+    private modSearchRewritten = false;
+
+    /**
+     * Prefix to use to detect "special" require paths
+     * @type {String}
+     */
+    private static duktapeRequirePrefix = "project:";
+
+    /**
+     * Inject this language service into the registry
+     * @return {[type]}             True if successful
+     */
+    initialize(serviceRegistry: Editor.HostExtensions.HostServiceLocator) {
+
+        // Let's rewrite the mod search
+        this.rewriteModSearch();
+
+        // We care project events
+        serviceRegistry.projectServices.register(this);
+        this.serviceRegistry = serviceRegistry;
+    }
+
+    /**
+     * Rewrite the duktape modSearch routine so that we can intercept any
+     * require calls with a "project:" prefix.  Duktape will fail if it receives
+     * a require call with a fully qualified path starting with a "/" (at least on OSX and Linux),
+     * so we will need to detect any of these project level requires and allow Atomic to go to the
+     * file system and manually pull these in to provide to duktape
+     */
+    private rewriteModSearch() {
+        Duktape.modSearch = (function(origModSearch) {
+            return function(id: string, require, exports, module) {
+                let system = ToolCore.getToolSystem();
+                if (id.indexOf(ProjectBasedExtensionLoader.duktapeRequirePrefix) == 0) {
+                    let path = id.substr(ProjectBasedExtensionLoader.duktapeRequirePrefix.length) + ".js";
+
+                    // For safety, only allow bringing modules in from the project directory.  This could be
+                    // extended to look for some global extension directory to pull extensions from such as
+                    // ~/.atomicExtensions/...
+                    if (system.project && path.indexOf(system.project.projectPath) == 0) {
+                        console.log(`Searching for project based include: ${path}`);
+                        // we have a project based require
+                        if (Atomic.fileSystem.fileExists(path)) {
+                            let include = new Atomic.File(path, Atomic.FILE_READ);
+                            try {
+                                return include.readText();
+                            } finally {
+                                include.close();
+                            }
+                        } else {
+                            throw new Error(`Cannot find project module: ${path}`);
+                        }
+                    } else {
+                        throw new Error(`Extension at ${path} does not reside in the project directory ${system.project.projectPath}`);
+                    }
+                } else {
+                    return origModSearch(id, require, exports, module);
+                }
+            };
+        })(Duktape.modSearch);
+    }
+    /**
+     * Called when the project is being loaded to allow the typscript language service to reset and
+     * possibly compile
+     */
+    projectLoaded(ev: Editor.EditorEvents.LoadProjectEvent) {
+        // got a load, we need to reset the language service
+        console.log(`${this.name}: received a project loaded event for project at ${ev.path}`);
+        let system = ToolCore.getToolSystem();
+        if (system.project) {
+            let fileSystem = Atomic.getFileSystem();
+            let editorScriptsPath = Atomic.addTrailingSlash(system.project.resourcePath) + "EditorData/";
+            if (fileSystem.dirExists(editorScriptsPath)) {
+                let filenames = fileSystem.scanDir(editorScriptsPath, "*.js", Atomic.SCAN_FILES, true);
+                filenames.forEach((filename) => {
+                    // Filtered search in Atomic doesn't due true wildcarding, only handles extension filters
+                    // in the future this may be better handled with some kind of manifest file
+                    if (filename.toLowerCase().lastIndexOf(".plugin.js") >= 0) {
+                        var extensionPath = editorScriptsPath + filename;
+                        extensionPath = extensionPath.substring(0, extensionPath.length - 3);
+
+                        console.log(`Detected project extension at: ${extensionPath} `);
+                        // Note: duktape does not yet support unloading modules,
+                        // but will return the same object when passed a path the second time.
+                        let resourceServiceModule = require(ProjectBasedExtensionLoader.duktapeRequirePrefix + extensionPath);
+
+                        // Handle situation where the service is either exposed by a typescript default export
+                        // or as the module.export (depends on if it is being written in typescript, javascript, es6, etc.)
+                        let resourceService: Editor.HostExtensions.HostEditorService = null;
+                        if (resourceServiceModule.default) {
+                            resourceService = resourceServiceModule.default;
+                        } else {
+                            resourceService = resourceServiceModule;
+                        }
+                        this.serviceRegistry.loadService(resourceService);
+                    }
+                });
+            }
+        }
+    }
+}

+ 201 - 0
Script/AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts

@@ -0,0 +1,201 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import * as EditorEvents from "../../editor/EditorEvents";
+
+/**
+ * Resource extension that supports the web view typescript extension
+ */
+export default class TypescriptLanguageExtension implements Editor.HostExtensions.ResourceService, Editor.HostExtensions.ProjectService {
+    name: string = "HostTypeScriptLanguageExtension";
+    description: string = "This service supports the typscript webview extension.";
+
+    /**
+     * Indicates if this project contains typescript files.
+     * @type {Boolean}
+     */
+    private isTypescriptProject = false;
+    private serviceRegistry: Editor.HostExtensions.HostServiceLocator = null;
+    /**
+     * Determines if the file name/path provided is something we care about
+     * @param  {string} path
+     * @return {boolean}
+     */
+    private isValidFiletype(path: string): boolean {
+        if (this.isTypescriptProject) {
+            const ext = Atomic.getExtension(path);
+            if (ext == ".ts") {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Seed the language service with all of the relevant files in the project.  This updates the tsconifg.atomic file in
+     * the root of the resources directory.
+     */
+    private loadProjectFiles() {
+        let projectFiles: Array<string> = [];
+
+        //scan all the files in the project for any typescript files so we can determine if this is a typescript project
+        Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.ts", Atomic.SCAN_FILES, true).forEach(filename => {
+            projectFiles.push(Atomic.addTrailingSlash(ToolCore.toolSystem.project.resourcePath) + filename);
+            this.isTypescriptProject = true;
+        });
+
+        // only build out a tsconfig.atomic if we actually have typescript files in the project
+        if (this.isTypescriptProject) {
+            // First we need to load in a copy of the lib.core.d.ts that is necessary for the hosted typescript compiler
+            projectFiles.push(Atomic.addTrailingSlash(Atomic.addTrailingSlash(ToolCore.toolEnvironment.toolDataDir) + "TypeScriptSupport") + "lib.core.d.ts");
+
+            // Load up a copy of the duktape.d.ts
+            projectFiles.push(Atomic.addTrailingSlash(Atomic.addTrailingSlash(ToolCore.toolEnvironment.toolDataDir) + "TypeScriptSupport") + "duktape.d.ts");
+
+
+            // Look in a 'typings' directory for any typescript definition files
+            const typingsDir = Atomic.addTrailingSlash(ToolCore.toolSystem.project.projectPath) + "typings";
+            Atomic.fileSystem.scanDir(typingsDir, "*.d.ts", Atomic.SCAN_FILES, true).forEach(filename => {
+                projectFiles.push(Atomic.addTrailingSlash(typingsDir) + filename);
+            });
+
+            let files = projectFiles.map((f: string) => {
+                if (f.indexOf(ToolCore.toolSystem.project.resourcePath) != -1) {
+                    // if we are in the resources directory, just pass back the path from resources down
+                    return f.replace(Atomic.addTrailingSlash(ToolCore.toolSystem.project.projectPath), "");
+                } else {
+                    // otherwise return the full path
+                    return f;
+                }
+            });
+
+            let tsConfig = {
+                files: files
+            };
+
+            let filename = Atomic.addTrailingSlash(ToolCore.toolSystem.project.resourcePath) + "tsconfig.atomic";
+            let script = new Atomic.File(filename, Atomic.FILE_WRITE);
+            try {
+                script.writeString(JSON.stringify(tsConfig));
+                script.flush();
+            } finally {
+                script.close();
+            }
+        }
+    }
+
+    /**
+     * Inject this language service into the registry
+     * @return {[type]}             True if successful
+     */
+    initialize(serviceRegistry: Editor.HostExtensions.HostServiceLocator) {
+        // We care about both resource events as well as project events
+        serviceRegistry.resourceServices.register(this);
+        serviceRegistry.projectServices.register(this);
+        this.serviceRegistry = serviceRegistry;
+    }
+
+    /**
+     * Handle the delete.  This should delete the corresponding javascript file
+     * @param  {Editor.EditorEvents.DeleteResourceEvent} ev
+     */
+    delete(ev: Editor.EditorEvents.DeleteResourceEvent) {
+        if (this.isValidFiletype(ev.path)) {
+            console.log(`${this.name}: received a delete resource event`);
+
+            // Delete the corresponding js file
+            let jsFile = ev.path.replace(/\.ts$/, ".js");
+            let jsFileAsset = ToolCore.assetDatabase.getAssetByPath(jsFile);
+            if (jsFileAsset) {
+                console.log(`${this.name}: deleting corresponding .js file`);
+                ToolCore.assetDatabase.deleteAsset(jsFileAsset);
+
+                let eventData: EditorEvents.DeleteResourceEvent = {
+                    path: jsFile
+                };
+
+                this.serviceRegistry.sendEvent(EditorEvents.DeleteResourceNotification, eventData);
+
+                // rebuild the tsconfig.atomic
+                this.loadProjectFiles();
+            }
+        }
+    }
+
+    /**
+     * Handle the rename.  Should rename the corresponding .js file
+     * @param  {Editor.EditorEvents.RenameResourceEvent} ev
+     */
+    rename(ev: Editor.EditorEvents.RenameResourceEvent) {
+        if (this.isValidFiletype(ev.path)) {
+            console.log(`${this.name}: received a rename resource event`);
+
+            // Rename the corresponding js file
+            let jsFile = ev.path.replace(/\.ts$/, ".js");
+            let jsFileNew = ev.newPath.replace(/\.ts$/, ".js"); // rename doesn't want extension
+            let jsFileAsset = ToolCore.assetDatabase.getAssetByPath(jsFile);
+            if (jsFileAsset) {
+                console.log(`${this.name}: renaming corresponding .js file`);
+                jsFileAsset.rename(ev.newName);
+
+                let eventData: EditorEvents.RenameResourceEvent = {
+                    path: jsFile,
+                    newPath: jsFileNew,
+                    newName: ev.newName,
+                    asset: jsFileAsset
+                };
+
+                this.serviceRegistry.sendEvent(EditorEvents.RenameResourceNotification, eventData);
+
+                // rebuild the tsconfig.atomic
+                this.loadProjectFiles();
+            }
+        }
+    }
+
+    /**
+     * Handles the save event and detects if a typescript file has been added to a non-typescript project
+     * @param  {Editor.EditorEvents.SaveResourceEvent} ev
+     * @return {[type]}
+     */
+    save(ev: Editor.EditorEvents.SaveResourceEvent) {
+        // let's check to see if we have created a typescript file
+        if (!this.isTypescriptProject) {
+            if (Atomic.getExtension(ev.path) == ".ts") {
+                this.isTypescriptProject = true;
+                this.loadProjectFiles();
+            }
+        }
+    }
+
+    /*** ProjectService implementation ****/
+
+    /**
+     * Called when the project is being loaded to allow the typscript language service to reset and
+     * possibly compile
+     */
+    projectLoaded(ev: Editor.EditorEvents.LoadProjectEvent) {
+        // got a load, we need to reset the language service
+        console.log(`${this.name}: received a project loaded event for project at ${ev.path}`);
+        this.loadProjectFiles();
+    }
+}

+ 5 - 0
Script/AtomicEditor/ui/EditorUI.ts

@@ -24,6 +24,7 @@ import EditorEvents = require("editor/EditorEvents");
 import MainFrame = require("./frames/MainFrame");
 import ModalOps = require("./modal/ModalOps");
 import Shortcuts = require("./Shortcuts");
+import ServiceLocator from "../hostExtensions/ServiceLocator";
 
 // this is designed with public get functions to solve
 // circular dependency issues in TS
@@ -94,6 +95,10 @@ class EditorUI extends Atomic.ScriptObject {
     this.modalOps = new ModalOps();
     this.shortcuts = new Shortcuts();
 
+    // Hook the service locator into the event system and give it the ui objects it needs
+    ServiceLocator.uiServices.init(this.mainframe.menu, this.modalOps);
+    ServiceLocator.subscribeToEvents(this.mainframe);
+
     this.subscribeToEvent(EditorEvents.ModalError, (event:EditorEvents.ModalErrorEvent) => {
       this.showModalError(event.title, event.message);
     });

+ 2 - 1
Script/AtomicEditor/ui/frames/MainFrame.ts

@@ -33,6 +33,7 @@ import ScriptWidget = require("ui/ScriptWidget");
 import MainFrameMenu = require("./menus/MainFrameMenu");
 
 import MenuItemSources = require("./menus/MenuItemSources");
+import * as EditorEvents from "../../editor/EditorEvents";
 
 class MainFrame extends ScriptWidget {
 
@@ -68,7 +69,7 @@ class MainFrame extends ScriptWidget {
             this.enableProjectMenus();
         });
 
-        this.subscribeToEvent("ProjectUnloaded", (data) => {
+        this.subscribeToEvent(EditorEvents.ProjectUnloadedNotification, (data) => {
             this.showWelcomeFrame(true);
             this.disableProjectMenus();
         });

+ 1 - 1
Script/AtomicEditor/ui/frames/ProjectFrame.ts

@@ -69,7 +69,7 @@ class ProjectFrame extends ScriptWidget {
 
         // events
         this.subscribeToEvent("ProjectLoaded", (data) => this.handleProjectLoaded(data));
-        this.subscribeToEvent("ProjectUnloaded", (data) => this.handleProjectUnloaded(data));
+        this.subscribeToEvent(EditorEvents.ProjectUnloadedNotification, (data) => this.handleProjectUnloaded(data));
         this.subscribeToEvent("DragEnded", (data: Atomic.DragEndedEvent) => this.handleDragEnded(data));
 
         this.subscribeToEvent("ResourceAdded", (ev: ToolCore.ResourceAddedEvent) => this.handleResourceAdded(ev));

+ 26 - 1
Script/AtomicEditor/ui/frames/ResourceFrame.ts

@@ -55,14 +55,29 @@ class ResourceFrame extends ScriptWidget {
 
         if (this.currentResourceEditor) {
             this.currentResourceEditor.save();
+            // Grab the path to this file and pass it to the save resource
+            this.sendEvent(EditorEvents.SaveResourceNotification, {
+                path: ev.path || this.currentResourceEditor.fullPath
+            });
         }
 
     }
 
+    handleDeleteResource(ev: EditorEvents.DeleteResourceEvent) {
+        var editor = this.editors[ev.path];
+        if (editor) {
+            editor.close(true);
+            delete this.editors[ev.path];
+        }
+    }
+
     handleSaveAllResources(data) {
 
         for (var i in this.editors) {
             this.editors[i].save();
+            this.sendEvent(EditorEvents.SaveResourceNotification, {
+                path: this.editors[i].fullPath
+            });
         }
 
     }
@@ -203,6 +218,14 @@ class ResourceFrame extends ScriptWidget {
 
     }
 
+    handleRenameResource(ev:EditorEvents.RenameResourceEvent) {
+        var editor = this.editors[ev.path];
+        if (editor) {
+            this.editors[ev.newPath] = editor;
+            delete this.editors[ev.path];
+        }
+    }
+
     handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
 
         if (ev.type == Atomic.UI_EVENT_TYPE_TAB_CHANGED && ev.target == this.tabcontainer) {
@@ -268,11 +291,13 @@ class ResourceFrame extends ScriptWidget {
 
         this.resourceViewContainer.addChild(this);
 
-        this.subscribeToEvent("ProjectUnloaded", (data) => this.handleProjectUnloaded(data));
+        this.subscribeToEvent(EditorEvents.ProjectUnloadedNotification, (data) => this.handleProjectUnloaded(data));
         this.subscribeToEvent(EditorEvents.EditResource, (data) => this.handleEditResource(data));
         this.subscribeToEvent(EditorEvents.SaveResource, (data) => this.handleSaveResource(data));
         this.subscribeToEvent(EditorEvents.SaveAllResources, (data) => this.handleSaveAllResources(data));
         this.subscribeToEvent(EditorEvents.EditorResourceClose, (ev: EditorEvents.EditorCloseResourceEvent) => this.handleCloseResource(ev));
+        this.subscribeToEvent(EditorEvents.RenameResourceNotification, (ev: EditorEvents.RenameResourceEvent) => this.handleRenameResource(ev));
+        this.subscribeToEvent(EditorEvents.DeleteResourceNotification, (data) => this.handleDeleteResource(data));
 
         this.subscribeToEvent(UIEvents.ResourceEditorChanged, (data) => this.handleResourceEditorChanged(data));
 

+ 1 - 1
Script/AtomicEditor/ui/frames/inspector/InspectorFrame.ts

@@ -54,7 +54,7 @@ class InspectorFrame extends ScriptWidget {
         var container = this.getWidget("inspectorcontainer");
 
         this.subscribeToEvent(EditorEvents.EditResource, (data) => this.handleEditResource(data));
-        this.subscribeToEvent("ProjectUnloaded", (data) => this.handleProjectUnloaded(data));
+        this.subscribeToEvent(EditorEvents.ProjectUnloadedNotification, (data) => this.handleProjectUnloaded(data));
 
         this.subscribeToEvent(EditorEvents.ActiveSceneEditorChange, (data) => this.handleActiveSceneEditorChanged(data));
 

+ 32 - 0
Script/AtomicEditor/ui/frames/menus/MainFrameMenu.ts

@@ -25,9 +25,12 @@ import EditorEvents = require("../../../editor/EditorEvents");
 import EditorUI = require("../../EditorUI");
 import MenuItemSources = require("./MenuItemSources");
 import Preferences = require("editor/Preferences");
+import ServiceLocator from "../../../hostExtensions/ServiceLocator";
 
 class MainFrameMenu extends Atomic.ScriptObject {
 
+    private pluginMenuItemSource: Atomic.UIMenuItemSource;
+
     constructor() {
 
         super();
@@ -41,6 +44,27 @@ class MainFrameMenu extends Atomic.ScriptObject {
 
     }
 
+    createPluginMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
+        if (!this.pluginMenuItemSource) {
+            var developerMenuItemSource = MenuItemSources.getMenuItemSource("menu developer");
+            this.pluginMenuItemSource = MenuItemSources.createSubMenuItemSource(developerMenuItemSource ,"Plugins", {});
+        }
+
+        return MenuItemSources.createSubMenuItemSource(this.pluginMenuItemSource , id, items);
+
+    }
+
+    removePluginMenuItemSource(id: string) {
+        if (this.pluginMenuItemSource) {
+            this.pluginMenuItemSource.removeItemWithStr(id);
+            if (0 == this.pluginMenuItemSource.itemCount) {
+                var developerMenuItemSource = MenuItemSources.getMenuItemSource("menu developer");
+                developerMenuItemSource.removeItemWithStr("Plugins");
+                this.pluginMenuItemSource = null;
+            }
+        }
+    }
+
     handlePopupMenu(target: Atomic.UIWidget, refid: string): boolean {
 
         if (target.id == "menu edit popup") {
@@ -218,12 +242,14 @@ class MainFrameMenu extends Atomic.ScriptObject {
             if (refid == "developer assetdatabase scan") {
 
               ToolCore.assetDatabase.scan();
+              return true;
 
             }
 
             if (refid == "developer assetdatabase force") {
 
               ToolCore.assetDatabase.reimportAllAssets();
+              return true;
 
             }
 
@@ -234,8 +260,12 @@ class MainFrameMenu extends Atomic.ScriptObject {
                 myPrefs.saveEditorWindowData(myPrefs.editorWindow);
                 myPrefs.savePlayerWindowData(myPrefs.playerWindow);
                 Atomic.getEngine().exit();
+                return true;
             }
 
+            // If we got here, then we may have been injected by a plugin.  Notify the plugins
+            return ServiceLocator.uiServices.menuItemClicked(refid);
+
         } else if (target.id == "menu tools popup") {
 
             if (refid == "tools toggle profiler") {
@@ -286,6 +316,8 @@ class MainFrameMenu extends Atomic.ScriptObject {
                 return true;
             }
 
+        } else {
+            console.log("Menu: " + target.id + " clicked");
         }
 
     }

+ 17 - 6
Script/AtomicEditor/ui/frames/menus/MenuItemSources.ts

@@ -77,12 +77,7 @@ function createMenuItemSourceRecursive(items: any): Atomic.UIMenuItemSource {
 
             }
             else if (typeof value === "object") {
-
-                var subsrc = createMenuItemSourceRecursive(value);
-
-                var item = new Atomic.UIMenuItem(key);
-                item.subSource = subsrc;
-                src.addItem(item);
+                createSubMenuItemSource(src, key, value);
 
             }
 
@@ -95,6 +90,16 @@ function createMenuItemSourceRecursive(items: any): Atomic.UIMenuItemSource {
 
 }
 
+export function createSubMenuItemSource(src: Atomic.UIMenuItemSource, id: string, items: any): Atomic.UIMenuItemSource {
+    var subsrc = createMenuItemSourceRecursive(items);
+
+    var item = new Atomic.UIMenuItem(id);
+    item.subSource = subsrc;
+    src.addItem(item);
+
+    return subsrc;
+}
+
 export function createMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
 
     srcLookup[id] = createMenuItemSourceRecursive(items);
@@ -102,3 +107,9 @@ export function createMenuItemSource(id: string, items: any): Atomic.UIMenuItemS
     return srcLookup[id];
 
 }
+
+export function deleteMenuItemSource(id: string) {
+    if (srcLookup[id]) {
+        delete srcLookup[id];
+    }
+}

+ 1 - 1
Script/AtomicEditor/ui/modal/CreateProject.ts

@@ -24,7 +24,7 @@ import EditorEvents = require("../../editor/EditorEvents");
 import EditorUI = require("../EditorUI");
 import ModalWindow = require("./ModalWindow");
 
-import ProjectTemplates = require("../resources/ProjectTemplates");
+import ProjectTemplates = require("../../resources/ProjectTemplates");
 
 class CreateProject extends ModalWindow {
 

+ 45 - 0
Script/AtomicEditor/ui/modal/ExtensionWindow.ts

@@ -0,0 +1,45 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import EditorUI = require("../EditorUI");
+import ModalWindow = require("./ModalWindow");
+
+class ExtensionWindow extends ModalWindow {
+
+    private handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void;
+
+    constructor(windowText: string, uifilename: string, handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void) {
+
+        super();
+
+        this.init(windowText, uifilename);
+
+        this.handleWidgetEventCB = handleWidgetEventCB;
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
+        if (this.handleWidgetEventCB)
+            this.handleWidgetEventCB(ev);
+    }
+}
+
+export = ExtensionWindow;

+ 10 - 0
Script/AtomicEditor/ui/modal/ModalOps.ts

@@ -43,6 +43,8 @@ import UIResourceOps = require("./UIResourceOps");
 
 import SnapSettingsWindow = require("./SnapSettingsWindow");
 
+import ExtensionWindow = require("./ExtensionWindow");
+
 import ProjectTemplates = require("../../resources/ProjectTemplates");
 
 
@@ -280,6 +282,14 @@ class ModalOps extends Atomic.ScriptObject {
 
     }
 
+    showExtensionWindow(windowText: string, uifilename: string, handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void): Editor.Modal.ExtensionWindow {
+        if (this.show()) {
+
+            this.opWindow = new ExtensionWindow(windowText, uifilename, handleWidgetEventCB);
+            return this.opWindow;
+        }
+    }
+
     private show(): boolean {
 
         if (this.dimmer.parent) {

+ 26 - 4
Script/AtomicEditor/ui/modal/UIResourceOps.ts

@@ -56,9 +56,15 @@ export class ResourceDelete extends ModalWindow {
 
                 this.hide();
 
+                let eventData = {
+                    path: this.asset.path
+                };
+
                 var db = ToolCore.getAssetDatabase();
                 db.deleteAsset(this.asset);
 
+                this.sendEvent(EditorEvents.DeleteResourceNotification, eventData);
+
                 return true;
             }
 
@@ -146,8 +152,10 @@ export class CreateComponent extends ModalWindow {
                 var componentName = this.nameField.text;
                 var outputFile = Atomic.addTrailingSlash(this.resourcePath) + componentName;
 
-                if (outputFile.indexOf(".js") == -1) outputFile += ".js";
-
+                // Check to see if we have a file extension.  If we don't then assume .js
+                if (outputFile.indexOf(".") == -1) {
+                    outputFile += ".js";
+                }
 
                 if (ResourceOps.CreateNewComponent(outputFile, componentName)) {
 
@@ -199,7 +207,10 @@ export class CreateScript extends ModalWindow {
                 var scriptName = this.nameField.text;
                 var outputFile = Atomic.addTrailingSlash(this.resourcePath) + scriptName;
 
-                if (outputFile.indexOf(".js") == -1) outputFile += ".js";
+                // Check to see if we have a file extension.  If we don't then assume .js
+                if (outputFile.indexOf(".") == -1) {
+                    outputFile += ".js";
+                }
 
 
                 if (ResourceOps.CreateNewScript(outputFile, scriptName)) {
@@ -364,9 +375,20 @@ export class RenameAsset extends ModalWindow {
 
                 this.hide();
 
-                if (this.asset.name != this.nameEdit.text)
+                if (this.asset.name != this.nameEdit.text) {
+                    let oldPath = this.asset.path;
                     this.asset.rename(this.nameEdit.text);
 
+                    let eventData: EditorEvents.RenameResourceEvent = {
+                        path: oldPath,
+                        newPath: this.asset.path,
+                        newName: this.nameEdit.text,
+                        asset: this.asset
+                    };
+
+                    this.sendEvent(EditorEvents.RenameResourceNotification, eventData);
+                }
+
                 return true;
             }
 

+ 33 - 0
Script/AtomicWebViewEditor/clientExtensions/ClientExtensionEventNames.ts

@@ -0,0 +1,33 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * All of the publicly available events that client extensions listen to
+ */
+export default class ClientExtensionEventNames {
+    static CodeLoadedEvent = "CodeLoadedEvent";
+    static ConfigureEditorEvent = "ConfigureEditorEvent";
+    static CodeSavedEvent = "CodeSavedEvent";
+    static ResourceRenamedEvent = "ResourceRenamedEvent";
+    static ResourceDeletedEvent = "ResourceDeletedEvent";
+    static ProjectUnloadedEvent = "ProjectUnloadedEvent";
+}

+ 192 - 0
Script/AtomicWebViewEditor/clientExtensions/ClientExtensionServices.ts

@@ -0,0 +1,192 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import ClientExtensionEventNames from "./ClientExtensionEventNames";
+
+// Entry point for web view extensions -- extensions that live inside the web view
+interface EventSubscription {
+    eventName: string;
+    callback: (data: any) => any;
+}
+
+/**
+ * Implements an event dispatcher for the client services
+ */
+export class EventDispatcher implements Editor.Extensions.EventDispatcher {
+    private subscriptions: EventSubscription[] = [];
+
+    sendEvent(eventType: string, data: any) {
+        this.subscriptions.forEach(sub => {
+            if (sub.eventName == eventType) {
+                sub.callback(data);
+            }
+        });
+    }
+
+    subscribeToEvent(eventType, callback) {
+        this.subscriptions.push({
+            eventName: eventType,
+            callback: callback
+        });
+    }
+}
+
+
+/**
+ * Generic registry for storing Editor Extension Services
+ */
+class ServiceRegistry<T extends Editor.Extensions.EditorService> implements Editor.Extensions.ServiceRegistry<T> {
+    registeredServices: T[] = [];
+
+    /**
+     * Adds a service to the registered services list for this type of service
+     * @param  {T}      service the service to register
+     */
+    register(service: T) {
+        this.registeredServices.push(service);
+    }
+
+    unregister(service: T) {
+        var index = this.registeredServices.indexOf(service, 0);
+        if (index > -1) {
+            this.registeredServices.splice(index, 1);
+        }
+    }
+}
+
+export class ExtensionServiceRegistry extends ServiceRegistry<Editor.ClientExtensions.WebViewService> {
+
+    /**
+     * Allow this service registry to subscribe to events that it is interested in
+     * @param  {EventDispatcher} eventDispatcher The global event dispatcher
+     */
+    subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.CodeLoadedEvent, (ev) => this.codeLoaded(ev));
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ConfigureEditorEvent, (ev) => this.configureEditor(ev));
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ResourceRenamedEvent, (ev) => this.renameResource(ev));
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ProjectUnloadedEvent, (ev) => this.projectUnloaded());
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ResourceDeletedEvent, (ev) => this.deleteResource(ev));
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.CodeSavedEvent, (ev) => this.saveCode(ev));
+    }
+
+    /**
+     * Called when code is loaded
+     * @param  {Editor.EditorEvents.CodeLoadedEvent} ev Event info about the file that is being loaded
+     */
+    codeLoaded(ev: Editor.EditorEvents.CodeLoadedEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Notify services that the project has just been loaded
+                if (service.codeLoaded) {
+                    service.codeLoaded(ev);
+                }
+            } catch (e) {
+                alert(`Extension Error:\n Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called after code has been saved
+     * @param  {Editor.EditorEvents.SaveResourceEvent} ev
+     */
+    saveCode(ev: Editor.EditorEvents.CodeSavedEvent) {
+        // run through and find any services that can handle this.
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can save
+                if (service.save) {
+                    service.save(ev);
+                }
+            } catch (e) {
+                alert(`Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when a resource has been deleted
+     */
+    deleteResource(ev: Editor.EditorEvents.DeleteResourceEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can delete
+                if (service.delete) {
+                    service.delete(ev);
+                }
+            } catch (e) {
+                alert(`Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when a resource has been renamed
+     * @param  {Editor.EditorEvents.RenameResourceEvent} ev
+     */
+    renameResource(ev: Editor.EditorEvents.RenameResourceEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can handle the rename
+                if (service.rename) {
+                    service.rename(ev);
+                }
+            } catch (e) {
+                alert(`Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when the editor is requesting to be configured for a particular file
+     * @param  {Editor.EditorEvents.EditorFileEvent} ev
+     */
+    configureEditor(ev: Editor.EditorEvents.EditorFileEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Notify services that the project has just been loaded
+                if (service.configureEditor) {
+                    service.configureEditor(ev);
+                }
+            } catch (e) {
+                alert(`Extension Error:\n Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+
+    /**
+     * Called when the project is unloaded
+     */
+    projectUnloaded() {
+        this.registeredServices.forEach((service) => {
+            // Notify services that the project has been unloaded
+            try {
+                if (service.projectUnloaded) {
+                    service.projectUnloaded();
+                }
+            } catch (e) {
+                alert(`Extension Error:\n Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+}

+ 89 - 0
Script/AtomicWebViewEditor/clientExtensions/ServiceLocator.ts

@@ -0,0 +1,89 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+import HostInteropType from "../interop";
+import * as ClientExtensionServices from "./ClientExtensionServices";
+
+// Initialize and configure the extensions
+import tsExtension from "./languageExtensions/typescript/TypescriptLanguageExtension";
+import jsExtension from "./languageExtensions/javascript/JavascriptLanguageExtension";
+
+/**
+ * Generic service locator of editor services that may be injected by either a plugin
+ * or by the editor itself.
+ */
+export class ClientServiceLocatorType implements Editor.ClientExtensions.ClientServiceLocator {
+
+    constructor() {
+        this.services = new ClientExtensionServices.ExtensionServiceRegistry();
+        this.services.subscribeToEvents(this);
+    }
+
+    private services: ClientExtensionServices.ExtensionServiceRegistry;
+    private eventDispatcher: Editor.Extensions.EventDispatcher = new ClientExtensionServices.EventDispatcher();
+
+    /**
+     * Returns the Host Interop module
+     * @return {Editor.ClientExtensions.HostInterop}
+     */
+    getHostInterop(): Editor.ClientExtensions.HostInterop {
+        return HostInteropType.getInstance();
+    }
+
+    loadService(service: Editor.ClientExtensions.ClientEditorService) {
+        try {
+            this.services.register(service);
+            service.initialize(this);
+        } catch (e) {
+            alert(`Extension Error:\n Error detected in extension ${service.name}\n \n ${e.stack}`);
+        }
+    }
+
+    /**
+     * Send a custom event.  This can be used by services to publish custom events
+     * @param  {string} eventType
+     * @param  {any} data
+     */
+    sendEvent(eventType: string, data: any) {
+        if (this.eventDispatcher) {
+            this.eventDispatcher.sendEvent(eventType, data);
+        }
+    }
+
+    /**
+     * Subscribe to an event and provide a callback.  This can be used by services to subscribe to custom events
+     * @param  {string} eventType
+     * @param  {any} callback
+     */
+    subscribeToEvent(eventType: string, callback: (data: any) => void) {
+        if (this.eventDispatcher) {
+            this.eventDispatcher.subscribeToEvent(eventType, callback);
+        }
+    }
+}
+
+// Singleton service locator that can be referenced
+const serviceLocator = new ClientServiceLocatorType();
+export default serviceLocator;
+
+// Load up all the internal services
+serviceLocator.loadService(new tsExtension());
+serviceLocator.loadService(new jsExtension());

+ 66 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/javascript/JavascriptLanguageExtension.ts

@@ -0,0 +1,66 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * Resource extension that handles configuring the editor for Javascript
+ */
+export default class JavascriptLanguageExtension implements Editor.ClientExtensions.WebViewService {
+    name: string = "ClientJavascriptLanguageExtension";
+    description: string = "Javascript language services for the editor.";
+
+    private serviceLocator: Editor.ClientExtensions.ClientServiceLocator;
+
+    /**
+    * Initialize the language service
+     * @param  {Editor.ClientExtensions.ClientServiceLocator} serviceLocator
+     */
+    initialize(serviceLocator: Editor.ClientExtensions.ClientServiceLocator) {
+        // initialize the language service
+        this.serviceLocator = serviceLocator;
+    }
+
+    /**
+     * Determines if the file name/path provided is something we care about
+     * @param  {string} path
+     * @return {boolean}
+     */
+    private isValidFiletype(path: string): boolean {
+        let ext = path.split(".").pop();
+        return ext == "js";
+    }
+
+    /**
+     * Called when the editor needs to be configured for a particular file
+     * @param  {Editor.EditorEvents.EditorFileEvent} ev
+     */
+    configureEditor(ev: Editor.EditorEvents.EditorFileEvent) {
+        if (this.isValidFiletype(ev.filename)) {
+            let editor = <AceAjax.Editor>ev.editor;
+            editor.session.setMode("ace/mode/javascript");
+
+            editor.setOptions({
+                enableBasicAutocompletion: true,
+                enableLiveAutocompletion: true
+            });
+        }
+    }
+}

+ 303 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/TypescriptLanguageExtension.ts

@@ -0,0 +1,303 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// Based upon the TypeScript language services example at https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#incremental-build-support-using-the-language-services
+
+import * as ts from "../../../modules/typescript";
+import * as WorkerProcessTypes from "./workerprocess/workerProcessTypes";
+import ClientExtensionEventNames from "../../ClientExtensionEventNames";
+
+/**
+ * Resource extension that handles compiling or transpling typescript on file save.
+ */
+export default class TypescriptLanguageExtension implements Editor.ClientExtensions.WebViewService {
+    name: string = "ClientTypescriptLanguageExtension";
+    description: string = "This extension handles typescript language features such as completion, compilation, etc.";
+
+    /**
+     * current filename
+     * @type {string}
+     */
+    private filename: string;
+
+    private serviceLocator: Editor.ClientExtensions.ClientServiceLocator;
+
+    private worker: SharedWorker.SharedWorker;
+
+    private editor;
+
+    /**
+     * Perform a full compile on save, or just transpile the current file
+     * @type {boolean}
+     */
+    fullCompile: boolean = true;
+
+    /**
+    * Inject this language service into the registry
+     * @param  {Editor.ClientExtensions.ClientServiceLocator} serviceLocator
+     */
+    initialize(serviceLocator: Editor.ClientExtensions.ClientServiceLocator) {
+        // initialize the language service
+        this.serviceLocator = serviceLocator;
+    }
+
+    /**
+     * Determines if the file name/path provided is something we care about
+     * @param  {string} path
+     * @return {boolean}
+     */
+    private isValidFiletype(path: string): boolean {
+        let ext = path.split(".").pop();
+        return ext == "ts";
+    }
+
+    /**
+     * Utility function that handles sending a request to the worker process and then when
+     * a response is received pass it back to the caller.  Since this is all handled async,
+     * it will be facilitated by passing back a promise
+     * @param  {string} responseChannel The unique string name of the response message channel
+     * @param  {any} message
+     * @return {PromiseLike}
+     */
+    private workerRequest(responseChannel: string, message: any): PromiseLike<{}> {
+        let worker = this.worker;
+
+        return new Promise((resolve, reject) => {
+            const responseCallback = function(e: WorkerProcessTypes.WorkerProcessMessage<any>) {
+                if (e.data.command == responseChannel) {
+                    worker.port.removeEventListener("message", responseCallback);
+                    resolve(e.data);
+                }
+            };
+            this.worker.port.addEventListener("message", responseCallback);
+            this.worker.port.postMessage(message);
+        });
+    }
+
+    /**
+     * Called when the editor needs to be configured for a particular file
+     * @param  {Editor.EditorEvents.EditorFileEvent} ev
+     */
+    configureEditor(ev: Editor.EditorEvents.EditorFileEvent) {
+        if (this.isValidFiletype(ev.filename)) {
+            let editor = <AceAjax.Editor>ev.editor;
+            editor.session.setMode("ace/mode/typescript");
+
+            editor.setOptions({
+                enableBasicAutocompletion: true,
+                enableLiveAutocompletion: true
+            });
+
+            this.editor = editor; // cache this so that we can reference it later
+        }
+    }
+
+    /**
+     * Called when code is first loaded into the editor
+     * @param  {CodeLoadedEvent} ev
+     * @return {[type]}
+     */
+    codeLoaded(ev: Editor.EditorEvents.CodeLoadedEvent) {
+        if (this.isValidFiletype(ev.filename)) {
+            this.filename = ev.filename;
+
+            let editor = ev.editor;
+
+            // we only want the typescript completer, otherwise we get a LOT of noise
+            editor.completers = [this.buildWordCompleter(ev.filename)];
+
+            // Build our worker
+            this.buildWorker();
+
+            // post a message to the shared web worker
+            this.worker.port.postMessage({ command: WorkerProcessTypes.Connect, sender: "Typescript Language Extension", filename: ev.filename });
+        }
+    }
+
+    /**
+     * Handler for any messages initiated by the worker process
+     * @param  {WorkerProcessTypes.WorkerProcessMessage} e
+     */
+    handleWorkerMessage(e: WorkerProcessTypes.WorkerProcessMessage<any>) {
+        switch (e.data.command) {
+            case WorkerProcessTypes.Message:
+                console.log(e.data.message);
+                break;
+            case WorkerProcessTypes.Alert:
+                alert(e.data.message);
+                break;
+            case WorkerProcessTypes.AnnotationsUpdated:
+                this.setAnnotations(e.data);
+                break;
+        }
+    }
+
+    /**
+     * Set annotations based upon issues reported by the typescript language service
+     * @param  {WorkerProcessTypes.GetAnnotationsResponseMessageData} event
+     */
+    setAnnotations(event: WorkerProcessTypes.GetAnnotationsResponseMessageData) {
+        // grab the existing annotations and filter out any TS annotations
+        let oldAnnotations = this.editor.session.getAnnotations().filter(ann => !ann.tsAnnotation);
+        this.editor.session.clearAnnotations();
+
+        // Mark these annotations as special
+        event.annotations.forEach(ann => ann.tsAnnotation = true);
+        this.editor.session.setAnnotations(oldAnnotations.concat(event.annotations));
+    }
+
+    /**
+     * Build/Attach to the shared web worker
+     */
+    buildWorker() {
+
+        this.worker = new SharedWorker("./source/editorCore/clientExtensions/languageExtensions/typescript/workerprocess/workerLoader.js");
+
+        // hook up the event listener
+        this.worker.port.addEventListener("message", this.handleWorkerMessage.bind(this), false);
+
+        // Tell the SharedWorker we're closing
+        addEventListener("beforeunload", () => {
+            this.worker.port.postMessage({ command: WorkerProcessTypes.Disconnect });
+        });
+
+        this.worker.port.start();
+    }
+
+    /**
+     * Builds the word completer for the Ace Editor.  This will handle determining which items to display in the popup and in which order
+     * @param  {string} filename the filename of the current file
+     * @return {[type]} returns a completer
+     */
+    private buildWordCompleter(filename: string): {
+        getDocTooltip?: (selected: WorkerProcessTypes.WordCompletion) => void,
+        getCompletions: (editor, session, pos, prefix, callback) => void
+    } {
+        let extension = this;
+        let wordCompleter = {
+            getDocTooltip: function(selected: WorkerProcessTypes.WordCompletion) {
+                const message: WorkerProcessTypes.GetDocTooltipMessageData = {
+                    command: WorkerProcessTypes.GetDocTooltip,
+                    filename: extension.filename,
+                    completionItem: selected,
+                    pos: selected.pos
+                };
+
+                // Since the doc tooltip built in function of Ace doesn't support async calls to retrieve the tootip,
+                // we need to go ahead and call the worker and then force the display of the tooltip when we get
+                // a result back
+                extension.workerRequest(WorkerProcessTypes.DocTooltipResponse, message)
+                    .then((e: WorkerProcessTypes.GetDocTooltipResponseMessageData) => {
+                    extension.editor.completer.showDocTooltip(e);
+                });
+            },
+
+            getCompletions: function(editor, session, pos, prefix, callback) {
+                const message: WorkerProcessTypes.GetCompletionsMessageData = {
+                    command: WorkerProcessTypes.GetCompletions,
+                    filename: extension.filename,
+                    pos: pos,
+                    sourceText: editor.session.getValue(),
+                    prefix: prefix
+                };
+
+                extension.workerRequest(WorkerProcessTypes.CompletionResponse, message)
+                    .then((e: WorkerProcessTypes.GetCompletionsResponseMessageData) => {
+                    callback(null, e.completions);
+                });
+            }
+        };
+        return wordCompleter;
+    }
+
+    /**
+     * Called once a resource has been saved
+     * @param  {Editor.EditorEvents.SaveResourceEvent} ev
+     */
+    save(ev: Editor.EditorEvents.CodeSavedEvent) {
+        if (this.isValidFiletype(ev.filename)) {
+            console.log(`${this.name}: received a save resource event for ${ev.filename}`);
+
+            const message: WorkerProcessTypes.SaveMessageData = {
+                command: ClientExtensionEventNames.CodeSavedEvent,
+                filename: ev.filename,
+                fileExt: ev.fileExt,
+                code: ev.code,
+                editor: null // cannot send editor across the boundary
+            };
+
+            this.worker.port.postMessage(message);
+        }
+    }
+
+    /**
+     * Handle the delete.  This should delete the corresponding javascript file
+     * @param  {Editor.EditorEvents.DeleteResourceEvent} ev
+     */
+    delete(ev: Editor.EditorEvents.DeleteResourceEvent) {
+        if (this.isValidFiletype(ev.path)) {
+            console.log(`${this.name}: received a delete resource event for ${ev.path}`);
+
+            // notify the typescript language service that the file has been deleted
+            const message: WorkerProcessTypes.DeleteMessageData = {
+                command: ClientExtensionEventNames.ResourceDeletedEvent,
+                path: ev.path
+            };
+
+            this.worker.port.postMessage(message);
+        }
+    }
+
+    /**
+     * Handle the rename.  Should rename the corresponding .js file
+     * @param  {Editor.EditorEvents.RenameResourceEvent} ev
+     */
+    rename(ev: Editor.EditorEvents.RenameResourceEvent) {
+        if (this.isValidFiletype(ev.path)) {
+            console.log(`${this.name}: received a rename resource event for ${ev.path} -> ${ev.newPath}`);
+
+            // notify the typescript language service that the file has been renamed
+            const message: WorkerProcessTypes.RenameMessageData = {
+                command: ClientExtensionEventNames.ResourceRenamedEvent,
+                path: ev.path,
+                newPath: ev.newPath
+            };
+
+            this.worker.port.postMessage(message);
+        }
+    }
+
+    /**
+     * Handle when the project is unloaded so that resources can be freed
+     */
+    projectUnloaded() {
+        if (this.worker) {
+
+            console.log(`${this.name}: received a project unloaded event`);
+
+            const message: WorkerProcessTypes.WorkerProcessMessageData = {
+                command: ClientExtensionEventNames.ProjectUnloadedEvent
+            };
+
+            this.worker.port.postMessage(message);
+        }
+    }
+}

+ 383 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageService.ts

@@ -0,0 +1,383 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+// Based upon the TypeScript language services example at https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#incremental-build-support-using-the-language-services
+
+import * as ts from "../../../../modules/typescript";
+
+/**
+ * Abstraction over the file system
+ */
+export interface FileSystemInterface {
+    /**
+     * Deterimine if the particular file exists in the resources
+     * @param  {string} filename
+     * @return {boolean}
+     */
+    fileExists(filename: string): boolean;
+    /**
+     * Grab the contents of the file
+     * @param  {string} filename
+     * @return {string}
+     */
+    getFile(filename: string): string;
+    /**
+     * Write the contents to the file specified
+     * @param  {string} filename
+     * @param  {string} contents
+     */
+    writeFile(filename: string, contents: string);
+
+    /**
+     * Returns the current directory / root of the source tree
+     * @return {string}
+     */
+    getCurrentDirectory(): string;
+}
+
+/**
+ * Resource extension that handles compiling or transpling typescript on file save.
+ */
+export class TypescriptLanguageService {
+
+    constructor(fs: FileSystemInterface) {
+        this.fs = fs;
+
+        // Create the language service files
+        this.documentRegistry = ts.createDocumentRegistry();
+        this.createLanguageService(this.documentRegistry);
+    }
+
+    private fs: FileSystemInterface = null;
+    private languageService: ts.LanguageService = null;
+    private documentRegistry: ts.DocumentRegistry = null;
+
+    public compilerOptions: ts.CompilerOptions = {
+        noEmitOnError: true,
+        noImplicitAny: false,
+        target: ts.ScriptTarget.ES5,
+        module: ts.ModuleKind.CommonJS,
+        noLib: true
+    };
+
+    name: string = "TypescriptLanguageService";
+
+    /**
+     * Perform a full compile on save, or just transpile the current file
+     * @type {boolean}
+     */
+    fullCompile: boolean = true;
+
+    private projectFiles: string[] = [];
+    private versionMap: ts.Map<{ version: number, snapshot?: ts.IScriptSnapshot }> = {};
+
+    private createLanguageService(documentRegistry: ts.DocumentRegistry) {
+
+        // Create the language service host to allow the LS to communicate with the host
+        const servicesHost: ts.LanguageServiceHost = {
+            getScriptFileNames: () => this.projectFiles,
+            getScriptVersion: (fileName) => this.versionMap[fileName] && this.versionMap[fileName].version.toString(),
+            getScriptSnapshot: (filename) => {
+                const scriptVersion = this.versionMap[filename];
+
+                // Grab the cached version
+                if (scriptVersion) {
+                    if (scriptVersion.snapshot) {
+                        return scriptVersion.snapshot;
+                    } else {
+                        console.log(`!!! creating snapshot for ${filename}`);
+                        let sourceFile = this.documentRegistry.acquireDocument(filename, this.compilerOptions, ts.ScriptSnapshot.fromString(""), scriptVersion.version.toString());
+                        return ts.ScriptSnapshot.fromString(sourceFile.text);
+                    }
+                }
+            },
+            getCurrentDirectory: () => this.fs.getCurrentDirectory(),
+            getCompilationSettings: () => this.compilerOptions,
+            getDefaultLibFileName: (options) => undefined
+        };
+
+        this.languageService = ts.createLanguageService(servicesHost, documentRegistry);
+    }
+
+    /**
+     * Adds a file to the internal project cache
+     * @param  {string} filename the full path of the file to add
+     * @param [{sring}] fileContents optional file contents.  If not provided, the filesystem object will be queried
+     */
+    addProjectFile(filename: string, fileContents?: string) {
+        if (this.projectFiles.indexOf(filename) == -1) {
+            console.log("Added project file: " + filename);
+            this.versionMap[filename] = {
+                version: 0,
+                snapshot: ts.ScriptSnapshot.fromString(fileContents || this.fs.getFile(filename))
+            };
+            this.projectFiles.push(filename);
+            this.documentRegistry.acquireDocument(
+                filename,
+                this.compilerOptions,
+                this.versionMap[filename].snapshot,
+                "0");
+        }
+    }
+
+    /**
+     * Updates the internal file representation.
+     * @param  {string} filename name of the file
+     * @param  {string} fileContents optional contents of the file.  If not provided, the file system object will be queried
+     * @return {ts.SourceFile}
+     */
+    updateProjectFile(filename: string, fileContents?: string): ts.SourceFile {
+        console.log("Updated project file: " + filename);
+        this.versionMap[filename].version++;
+        this.versionMap[filename].snapshot = ts.ScriptSnapshot.fromString(fileContents || this.fs.getFile(filename));
+        return this.documentRegistry.updateDocument(
+            filename,
+            this.compilerOptions,
+            this.versionMap[filename].snapshot,
+            this.versionMap[filename].version.toString());
+    }
+
+    /**
+     * Returns the list of project files
+     * @return {string[]}
+     */
+    getProjectFiles(): string[] {
+        return this.projectFiles;
+    }
+
+    getPreEmitWarnings(filename: string, options?: ts.CompilerOptions) {
+        options = options || this.compilerOptions;
+
+        let allDiagnostics = this.compileFile(filename);
+        let results = [];
+
+        allDiagnostics.forEach(diagnostic => {
+            let lineChar = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
+            let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
+            results.push({
+                row: lineChar.line,
+                column: lineChar.character,
+                text: message,
+                type: diagnostic.category == 1 ? "error" : "warning"
+            });
+        });
+        return results;
+    }
+
+    /**
+     * Simply transpile the typescript file.  This is much faster and only checks for syntax errors
+     * @param {string[]}           fileNames array of files to transpile
+     * @param {ts.CompilerOptions} options   compiler options
+     */
+    transpile(fileNames: string[], options?: ts.CompilerOptions): void {
+        options = options || this.compilerOptions;
+        this.compilerOptions = options;
+
+        fileNames.forEach((fileName) => {
+            console.log(`${this.name}:  Transpiling ${fileName}`);
+            let script = this.fs.getFile(fileName);
+            let diagnostics: ts.Diagnostic[] = [];
+            let result = ts.transpile(script, options, fileName, diagnostics);
+            if (diagnostics.length) {
+                this.logErrors(diagnostics);
+            }
+            if (diagnostics.length == 0) {
+                this.fs.writeFile(fileName.replace(".ts", ".js"), result);
+            }
+        });
+    }
+
+    /**
+     * Converts the line and character offset to a position
+     * @param  {ts.SourceFile} file
+     * @param  {number} line
+     * @param  {number} character
+     * @return {number}
+     */
+    getPositionOfLineAndCharacter(file: ts.SourceFile, line: number, character: number): number {
+        return ts.getPositionOfLineAndCharacter(file, line, character);
+    }
+
+    /**
+     * Returns the completions available at the provided position
+     * @param  {string} filename
+     * @param  {number} position
+     * @return {ts.CompletionInfo}
+     */
+    getCompletions(filename: string, pos: number): ts.CompletionInfo {
+        return this.languageService.getCompletionsAtPosition(filename, pos);
+    }
+
+    /**
+     * Returns details of this completion entry
+     * @param  {string} filename
+     * @param  {number} pos
+     * @param  {string} entryname
+     * @return {ts.CompletionEntryDetails}
+     */
+    getCompletionEntryDetails(filename: string, pos: number, entryname: string): ts.CompletionEntryDetails {
+        return this.languageService.getCompletionEntryDetails(filename, pos, entryname);
+    }
+    /**
+     * Compile the provided file to javascript with full type checking etc
+     * @param  {string}  a list of file names to compile
+     * @param  {ts.CompilerOptions} options for the compiler
+     */
+    compile(files: string[], options?: ts.CompilerOptions): ts.Diagnostic[] {
+        let start = new Date().getTime();
+
+        options = options || this.compilerOptions;
+
+        //Make sure we have these files in the project
+        files.forEach((file) => {
+            this.addProjectFile(file);
+        });
+
+        let errors: ts.Diagnostic[] = [];
+
+        if (files.length == 0) {
+            // We haven't passed any files in, so let's compile them all
+            this.projectFiles.forEach(filename => {
+                errors = errors.concat(this.compileFile(filename));
+            });
+        } else {
+            // Only compile the files that are newly edited
+            files.forEach(filename => {
+                errors = errors.concat(this.compileFile(filename));
+            });
+        }
+
+        console.log(`${this.name}: Compiling complete after ${new Date().getTime() - start} ms`);
+        return errors;
+    }
+
+    /**
+     * Delete a file from the project store
+     * @param  {string} filepath
+     */
+    deleteProjectFile(filepath: string) {
+        if (this.versionMap[filepath]) {
+            delete this.versionMap[filepath];
+        }
+        let idx = this.projectFiles.indexOf(filepath);
+        if (idx > -1) {
+            console.log(`delete project file from ${filepath}`);
+            this.projectFiles.splice(idx, 1);
+        }
+    }
+
+    /**
+     * rename a file in the project store
+     * @param  {string} filepath the old path to the file
+     * @param  {string} newpath the new path to the file
+     */
+    renameProjectFile(filepath: string, newpath: string): void {
+        let oldFile = this.versionMap[filepath];
+        if (oldFile) {
+            console.log(`Rename project file from ${filepath} to ${newpath}`);
+            delete this.versionMap[filepath];
+            this.versionMap[newpath] = oldFile;
+        }
+        let idx = this.projectFiles.indexOf(filepath);
+        if (idx > -1) {
+            console.log(`Update project files array from ${filepath} to ${newpath}`);
+            this.projectFiles[idx] = newpath;
+        }
+    }
+
+    /**
+     * clear out any caches, etc.
+     */
+    reset() {
+        this.projectFiles = [];
+        this.versionMap = {};
+        //this.languageService = null;
+    }
+
+    /**
+     * Compile an individual file
+     * @param  {string} filename the file to compile
+     * @return {[ts.Diagnostic]} a list of any errors
+     */
+    private compileFile(filename: string): ts.Diagnostic[] {
+        console.log(`${this.name}: Compiling version ${this.versionMap[filename].version} of ${filename}`);
+        //if (!filename.match("\.d\.ts$")) {
+        try {
+            return this.emitFile(filename);
+        } catch (err) {
+            console.log(`${this.name}: problem encountered compiling ${filename}: ${err}`);
+            return [];
+            // console.log(err.stack);
+        }
+        //}
+    }
+
+    /**
+     * writes out a file from the compile process
+     * @param  {string} filename [description]
+     * @return {[ts.Diagnostic]} a list of any errors
+     */
+    private emitFile(filename: string): ts.Diagnostic[] {
+        let output = this.languageService.getEmitOutput(filename);
+        let allDiagnostics: ts.Diagnostic[] = [];
+        if (output.emitSkipped) {
+            console.log(`${this.name}: Failure Emitting ${filename}`);
+            allDiagnostics = this.languageService.getCompilerOptionsDiagnostics()
+                .concat(this.languageService.getSyntacticDiagnostics(filename))
+                .concat(this.languageService.getSemanticDiagnostics(filename));
+        }
+
+        output.outputFiles.forEach(o => {
+            this.fs.writeFile(o.name, o.text);
+        });
+        return allDiagnostics;
+    }
+
+    /**
+     * Logs errors from the diagnostics returned from the compile/transpile process
+     * @param {ts.Diagnostic} diagnostics information about the errors
+     * @return {[type]}          [description]
+     */
+    private logErrors(diagnostics: ts.Diagnostic[]) {
+        let msg = [];
+
+        diagnostics.forEach(diagnostic => {
+            let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
+            if (diagnostic.file) {
+                let d = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
+                let line = d.line;
+                let character = d.character;
+
+                // NOTE: Destructuring throws an error in chrome web view
+                //let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
+
+                msg.push(`${this.name}:  Error ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
+            }
+            else {
+                msg.push(`${this.name}  Error: ${message}`);
+            }
+        });
+        console.log(`TypeScript Errors:\n${msg.join("\n") }`);
+        throw new Error(`TypeScript Errors:\n${msg.join("\n") }`);
+    }
+
+}

+ 374 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageServiceWebWorker.ts

@@ -0,0 +1,374 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+
+/**
+ * Typescript Language Worker - Handles bringing down the source, providing completions, and compiling.
+ * since this is a shared web worker, each editor tab will be sharing the same data.
+ */
+import * as ts from "../../../../modules/typescript";
+import {TypescriptLanguageService, FileSystemInterface} from "./TypescriptLanguageService";
+import * as WorkerProcessTypes from "./workerProcessTypes";
+import ClientExtensionEventNames from "../../../ClientExtensionEventNames";
+
+interface TSConfigFile {
+    compilerOptions?: ts.CompilerOptions;
+    files: Array<string>;
+}
+
+/**
+ * Queries the host for a particular resource and returns it in a promise
+ * @param  {string} codeUrl
+ * @return {Promise}
+ */
+function getResource(codeUrl: string): Promise<{}> {
+    return new Promise(function(resolve, reject) {
+        const xmlHttp = new XMLHttpRequest();
+        xmlHttp.onreadystatechange = () => {
+            if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
+                resolve(xmlHttp.responseText);
+            }
+        };
+        xmlHttp.open("GET", codeUrl, true); // true for asynchronous
+        xmlHttp.send(null);
+    });
+}
+
+/**
+ * Returns a file resource from the resources directory
+ * @param  {string} filename name and path of file under the project directory or a fully qualified file name
+ * @return {Promise}
+ */
+function getFileResource(filename: string): Promise<{}> {
+    return getResource(`atomic://${filename}`);
+}
+
+/**
+ * Class that provides access to the Atomic filesystem routines
+ */
+class WebFileSystem implements FileSystemInterface {
+
+    private fileCache = {};
+    /**
+     * Deterimine if the particular file exists in the resources
+     * @param  {string} filename
+     * @return {boolean}
+     */
+    fileExists(filename: string): boolean {
+        return this.fileCache[filename] != null;
+    }
+
+    /**
+     * Cache a file in the filesystem
+     * @param  {string} filename
+     * @param  {string} file
+     */
+    cacheFile(filename: string, file: string) {
+        this.fileCache[filename] = file;
+    }
+
+    /**
+     * Grab the contents of the file
+     * @param  {string} filename
+     * @return {string}
+     */
+    getFile(filename: string): string {
+        console.log("FS.GETFILE!!");
+        // return HostInterop.getResource("atomic:" + filename);
+        return this.fileCache[filename];
+    }
+
+    /**
+     * Write the contents to the file specified
+     * @param  {string} filename
+     * @param  {string} contents
+     */
+    writeFile(filename: string, contents: string) {
+        //TODO:
+        /*let script = new Atomic.File(filename, Atomic.FILE_WRITE);
+        try {
+            script.writeString(contents);
+            script.flush();
+        } finally {
+            script.close();
+        }
+        */
+    }
+
+    /**
+     * Returns the current directory / root of the source tree
+     * @return {string}
+     */
+    getCurrentDirectory(): string {
+        return "";
+    }
+
+}
+
+export default class TypescriptLanguageServiceWebWorker {
+    /**
+     * Number of editors connected to this shared worker
+     * @type {Number}
+     */
+    connections = 0;
+
+    /**
+     * The language service that will handle building
+     * @type {TypescriptLanguageService}
+     */
+    languageService: TypescriptLanguageService = null;
+
+    fs: WebFileSystem; // needed?
+
+    projectLoaded = false;
+
+    constructor() {
+        this.fs = new WebFileSystem();
+        this.languageService = new TypescriptLanguageService(this.fs);
+    }
+
+    /**
+     * Handle when an editor connects to this worker
+     * @param  {any} e
+     */
+    connect(e) {
+        let port: MessagePort = e.ports[0];
+        this.connections++;
+
+        port.addEventListener("message", (e: WorkerProcessTypes.WorkerProcessMessage<any>) => {
+            switch (e.data.command) {
+                case WorkerProcessTypes.Connect:
+                    this.handleHELO(port, e.data);
+                    break;
+                case WorkerProcessTypes.Disconnect:
+                    this.handleCLOSE(port, e.data);
+                    break;
+                case WorkerProcessTypes.GetCompletions:
+                    this.handleGetCompletions(port, e.data);
+                    break;
+                case WorkerProcessTypes.GetDocTooltip:
+                    this.handleGetDocTooltip(port, e.data);
+                    break;
+                case ClientExtensionEventNames.CodeSavedEvent:
+                    this.handleSave(port, e.data);
+                    break;
+                case ClientExtensionEventNames.ResourceRenamedEvent:
+                    this.handleRename(port, e.data);
+                    break;
+                case ClientExtensionEventNames.ResourceDeletedEvent:
+                    this.handleDelete(port, e.data);
+                    break;
+                case ClientExtensionEventNames.ProjectUnloadedEvent:
+                    this.handleProjectUnloaded(port);
+                    break;
+                case WorkerProcessTypes.GetAnnotations:
+                    this.handleGetAnnotations(port, e.data);
+                    break;
+            }
+
+        }, false);
+
+        port.start();
+    }
+
+    /**
+     * Called when we want to reset the language service
+     * @return {[type]}
+     */
+    reset() {
+        this.languageService.reset();
+        this.projectLoaded = false;
+    }
+
+    /**
+     * Seed the language service with all of the relevant files in the project
+     * @return {Promise}
+     */
+    private loadProjectFiles() {
+        // Let's query the backend and get a list of the current files
+        // and delete any that may be been removed and sync up
+        return getFileResource("resources/tsconfig.atomic").then((jsonTsConfig: string) => {
+            let promises: PromiseLike<void>[] = [];
+            let tsConfig: TSConfigFile = JSON.parse(jsonTsConfig);
+
+            if (tsConfig.compilerOptions) {
+                this.languageService.compilerOptions = tsConfig.compilerOptions;
+            };
+
+            let existingFiles = this.languageService.getProjectFiles();
+
+            // see if anything was deleted
+            existingFiles.forEach((f) => {
+                if (tsConfig.files.indexOf(f) == -1) {
+                    this.languageService.deleteProjectFile(f);
+                }
+            });
+
+            // load up any new files that may have been added
+            tsConfig.files.forEach((f) => {
+                if (existingFiles.indexOf(f) == -1) {
+                    promises.push(getFileResource(f).then((code: string) => {
+                        this.languageService.addProjectFile(f, code);
+                    }));
+                }
+            });
+            return Promise.all(promises);
+        }).then(() => {
+            // Let's seed the compiler state
+            // this.languageService.compile([this.filename]);
+            this.projectLoaded = true;
+        });
+    }
+
+    /**
+     * Handle initial tab connection
+     * @param  {MessagePort} port
+     * @param  {any} eventData
+     */
+    handleHELO(port: MessagePort, eventData: any | {
+        sender: string,
+        filename: string
+    }) {
+        port.postMessage({ command: WorkerProcessTypes.Message, message: "Hello " + eventData.sender + " (port #" + this.connections + ")" });
+        this.loadProjectFiles().then(() => {
+            let diagnostics = this.languageService.compile([eventData.filename]);
+            this.handleGetAnnotations(port, eventData);
+        });
+    }
+
+    /**
+     * Handle when a tab closes
+     * @param  {MessagePort} port
+     * @param  {any} eventData
+     */
+    handleCLOSE(port: MessagePort, eventData: any) {
+        this.connections--;
+        if (this.connections <= 0) {
+            this.reset();
+        }
+        console.log("Got a close");
+    }
+
+    /**
+     * Get completions
+     * @param  {MessagePort} port
+     * @param  {WorkerProcessCommands.GetCompletionsMessage} eventData
+     */
+    handleGetCompletions(port: MessagePort, eventData: WorkerProcessTypes.GetCompletionsMessageData) {
+        let sourceFile = this.languageService.updateProjectFile(eventData.filename, eventData.sourceText);
+
+        let newpos = this.languageService.getPositionOfLineAndCharacter(sourceFile, eventData.pos.row, eventData.pos.column);
+        let completions = this.languageService.getCompletions(eventData.filename, newpos);
+
+        let message: WorkerProcessTypes.GetCompletionsResponseMessageData = {
+            command: WorkerProcessTypes.CompletionResponse,
+            completions: []
+        };
+        let langService = this.languageService;
+
+        if (completions) {
+            message.completions = completions.entries.map((completion: ts.CompletionEntry) => {
+                let value = completion.name;
+                let completionItem: WorkerProcessTypes.WordCompletion = {
+                    caption: completion.name,
+                    value: value,
+                    score: 100 - parseInt(completion.sortText, 0),
+                    meta: completion.kind,
+                    pos: newpos
+                };
+
+                //completionItem.docTooltip = this.getDocTooltip(eventData.filename, newpos, completionItem);
+                return completionItem;
+            });
+        }
+
+        port.postMessage(message);
+    }
+
+    /**
+     * Get the documentation popup information for the current completion item
+     * @param  {MessagePort} port
+     * @param  {WorkerProcessCommands.GetDocTooltipMessageData} eventData
+     * @return {[type]}
+     */
+    handleGetDocTooltip(port: MessagePort, eventData: WorkerProcessTypes.GetDocTooltipMessageData) {
+        let message: WorkerProcessTypes.GetDocTooltipResponseMessageData = {
+            command: WorkerProcessTypes.DocTooltipResponse
+        };
+        const details = this.languageService.getCompletionEntryDetails(eventData.filename, eventData.pos, eventData.completionItem.caption);
+        if (details) {
+            let docs = details.displayParts.map(part => part.text).join("");
+            if (details.documentation) {
+                docs += "<p>" + details.documentation.map(part => part.text).join("") + "</p>";
+            }
+
+            message.docHTML = docs;
+        }
+
+        port.postMessage(message);
+    }
+
+    handleGetAnnotations(port: MessagePort, eventData: WorkerProcessTypes.GetAnnotationsMessageData) {
+        let message: WorkerProcessTypes.GetAnnotationsResponseMessageData = {
+            command: WorkerProcessTypes.AnnotationsUpdated,
+            annotations: this.languageService.getPreEmitWarnings(eventData.filename)
+        };
+
+        port.postMessage(message);
+    }
+
+    /**
+     * Called when the file has been saved.  This will also send back annotations to the caller
+     * @param  {MessagePort} port
+     * @param  {WorkerProcessCommands.SaveMessageData} eventData
+     */
+    handleSave(port: MessagePort, eventData: WorkerProcessTypes.SaveMessageData) {
+        this.languageService.updateProjectFile(eventData.filename, eventData.code);
+        this.handleGetAnnotations(port, eventData);
+    }
+
+    /**
+     * Called when a file has been deleted
+     * @param  {MessagePort} port
+     * @param  {WorkerProcessCommands.DeleteMessageData} eventData
+     */
+    handleDelete(port: MessagePort, eventData: WorkerProcessTypes.DeleteMessageData) {
+        this.languageService.deleteProjectFile(eventData.path);
+    }
+
+    /**
+     * Called when a file has been renamed
+     * @param  {MessagePort} port
+     * @param  {WorkerProcessCommands.RenameMessageData} eventData
+     */
+    handleRename(port: MessagePort, eventData: WorkerProcessTypes.RenameMessageData) {
+        this.languageService.renameProjectFile(eventData.path, eventData.newPath);
+    }
+
+    /**
+     * Called when the project has been closed
+     * @param  {MessagePort} port
+     */
+    handleProjectUnloaded(port: MessagePort) {
+        this.reset();
+    }
+}

+ 61 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/workerLoader.ts

@@ -0,0 +1,61 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+
+/**
+ * Typescript Language Worker - Handles bringing down the source, providing completions, and compiling.
+ * since this is a shared web worker, each editor tab will be sharing the same data.
+ */
+"use strict";
+
+// Bring in the system js library so we can import the modules we need.  The path here is based upon the location
+// of this SharedWorker in the directory tree
+importScripts("../../../../../systemjs/system.js");
+System.config({
+    defaultJSExtensions: true,
+    meta: {
+        "../../../../modules/typescript.js": {
+            format: "global",
+            exports: "ts"
+        }
+    }
+});
+
+// hook into the first time a tab connects...need to do some set up and some async pulling down of required modules
+function firstTimeConnect(e: any) {
+    // Going low-level to use the systemjs import system.  import will return a promise that will resolve
+    // to the module being required in.  Once this is loaded in and instantiated, everything downstream can just
+    // use normal typescript style imports to bring in other modules.
+    System.import("./TypescriptLanguageServiceWebWorker").then((TypescriptLanguageServiceWebWorker) => {
+        const worker = new TypescriptLanguageServiceWebWorker.default();
+
+        // remove the event listener for the first connect
+        self.removeEventListener("connect", firstTimeConnect);
+
+        // hook up the permanent event listener
+        self.addEventListener("connect", worker.connect.bind(worker));
+
+        // forward the connect call
+        worker.connect(e);
+    });
+}
+self.addEventListener("connect", firstTimeConnect);

+ 82 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/workerProcessTypes.ts

@@ -0,0 +1,82 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+export interface WorkerProcessMessage<T> extends MessageEvent {
+    data: T;
+}
+
+export interface WorkerProcessMessageData {
+    command: string;
+}
+
+export interface SaveMessageData extends WorkerProcessMessageData, Editor.EditorEvents.CodeSavedEvent { }
+export interface DeleteMessageData extends WorkerProcessMessageData, Editor.EditorEvents.DeleteResourceEvent {}
+export interface RenameMessageData extends WorkerProcessMessageData, Editor.EditorEvents.RenameResourceEvent {}
+
+export const GetCompletions = "COMPLETIONS";
+export const CompletionResponse = "COMPLETION_RESPONSE";
+export interface WordCompletion {
+    caption: string;
+    meta: string;
+    score: number;
+    value: string;
+    pos: number;
+    snippet?: string;
+    docHTML?: string;
+    docText?: string;
+}
+
+export interface GetCompletionsMessageData extends WorkerProcessMessageData {
+    sourceText: string;
+    filename: string;
+    pos: { row: number, column: number };
+    prefix: string;
+}
+
+export interface GetCompletionsResponseMessageData extends WorkerProcessMessageData {
+    completions: Array<WordCompletion>;
+}
+
+export const GetDocTooltip = "DOC_TOOLTIP";
+export const DocTooltipResponse = "DOC_TOOLTIP_RESPONSE";
+export interface GetDocTooltipMessageData extends WorkerProcessMessageData {
+    completionItem: WordCompletion;
+    filename: string;
+    pos: number;
+}
+
+export interface GetDocTooltipResponseMessageData extends WorkerProcessMessageData {
+    docText?: string;
+    docHTML?: string;
+}
+
+export const GetAnnotations = "ANNOTATIONS";
+export const AnnotationsUpdated = "ANNOTATIONS_RESPONSE";
+export interface GetAnnotationsMessageData extends SaveMessageData {};
+export interface GetAnnotationsResponseMessageData extends WorkerProcessMessageData {
+    annotations: any[];
+}
+
+export const Connect = "HELO";
+export const Disconnect = "CLOSE";
+export const Message = "MESSAGE";
+export const Alert = "ALERT";

+ 22 - 0
Script/AtomicWebViewEditor/editor/editor.ts

@@ -1,3 +1,25 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
 // Instantiate the editor
 const editor = ace.edit("editor");
 

+ 125 - 0
Script/AtomicWebViewEditor/editor/editorCommands.ts

@@ -0,0 +1,125 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import editor from "./editor";
+import serviceLocator from "../clientExtensions/ServiceLocator";
+import HostInterop from "../interop";
+import ClientExtensionEventNames from "../clientExtensions/ClientExtensionEventNames";
+
+
+/**
+ * Set the editor theme and configuration based upon the file extension
+ * @param  {string} fileExt
+ */
+export function configure(fileExt: string, filename: string) {
+
+    // set a default theme
+    editor.setTheme("ace/theme/monokai");
+
+    // set a default mode
+    editor.session.setMode("ace/mode/javascript");
+
+    // give the language extensions the opportunity to configure the editor based upon the file type
+    serviceLocator.sendEvent(ClientExtensionEventNames.ConfigureEditorEvent, {
+        fileExt: fileExt,
+        filename: filename,
+        editor: editor
+    });
+}
+
+/**
+ * Returns the text in the editor instance
+ * @return {string}
+ */
+export function getSourceText() : string {
+    return editor.session.getValue();
+}
+
+/**
+ * Loads a file of code into the editor and wires up the change events
+ * @param  {string} code
+ * @param {string} filename
+ * @param  {string} fileExt
+ */
+export function loadCodeIntoEditor(code: string, filename: string, fileExt: string) {
+    editor.session.setValue(code);
+    editor.gotoLine(0);
+
+    editor.getSession().on("change", function(e) {
+        HostInterop.getInstance().notifyEditorChange();
+    });
+
+    serviceLocator.sendEvent(ClientExtensionEventNames.CodeLoadedEvent, {
+        code: code,
+        filename: filename,
+        fileExt: fileExt,
+        editor: editor
+    });
+
+}
+
+/**
+ * Called when the project is getting unloaded
+ */
+export function projectUnloaded() {
+    serviceLocator.sendEvent(ClientExtensionEventNames.ProjectUnloadedEvent, null);
+}
+
+/**
+ * Called when a resource is getting renamed
+ * @param  {string} path
+ * @param  {string} newPath
+ */
+export function resourceRenamed(path: string, newPath: string) {
+    let data:Editor.EditorEvents.RenameResourceEvent = {
+        path: path,
+        newPath: newPath
+    };
+    serviceLocator.sendEvent(ClientExtensionEventNames.ResourceRenamedEvent, data);
+}
+
+/**
+ * Called when a resource is getting deleted
+ * @param  {string} path
+ */
+export function resourceDeleted(path: string) {
+    let data:Editor.EditorEvents.DeleteResourceEvent = {
+        path: path
+    };
+    serviceLocator.sendEvent(ClientExtensionEventNames.ResourceDeletedEvent, data);
+}
+
+/**
+ * Called when a resource is saved
+ * @param  {string} path
+ * @param {string} fileExt
+ * @param {string} contents
+ */
+export function codeSaved(path: string, fileExt: string, contents: string) {
+    let data:Editor.EditorEvents.CodeSavedEvent = {
+        filename: path,
+        fileExt: fileExt,
+        editor: editor,
+        code: contents
+    };
+    serviceLocator.sendEvent(ClientExtensionEventNames.CodeSavedEvent, data);
+}

+ 0 - 20
Script/AtomicWebViewEditor/editor/editorConfig.ts

@@ -1,20 +0,0 @@
-import editor from "./editor";
-
-/**
- * Set the editor theme and configuration based upon the file extension
- * @param  {string} fileExt
- */
-export function configure(fileExt: string) {
-    editor.setTheme("ace/theme/monokai");
-    switch (fileExt.toLowerCase()) {
-        case "ts":
-            editor.session.setMode("ace/mode/typescript");
-            break;
-        case "js":
-            editor.session.setMode("ace/mode/javascript");
-            break;
-        default:
-            editor.session.setMode("ace/mode/javascript");
-            break;
-    }
-}

+ 208 - 50
Script/AtomicWebViewEditor/interop.ts

@@ -1,60 +1,218 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
 // This is the interop file, exposing functions that can be called by the host game engine
-import editor from "./editor/editor";
-import * as editorConfig from "./editor/editorConfig";
+import * as editorCommands from "./editor/editorCommands";
 
-export function saveCode() {
+/**
+ * Port to attach Chrome Dev Tools to
+ * @type {Number}
+ */
+const DEBUG_PORT = 3335;
 
-  const data = {
-    message: "saveCode",
-    payload: editor.session.getValue()
-  };
+/**
+ * Display "Attach dev tools now" alert on startup if this is set to true
+ * @type {Boolean}
+ */
+const DEBUG_ALERT = false;
 
-  window.atomicQuery({
-    request: JSON.stringify(data),
-    persistent: false,
-    onSuccess: function(response) {/**/ },
-    onFailure: function(error_code, error_message) {
-      console.log("Error getting code");
-    }
-  });
-}
+/**
+ * Promise version of atomic query
+ * @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
+ * @param  {any} payload optional data to send
+ * @return {Promise}
+ */
+function atomicQueryPromise(message: any): Promise<{}> {
+    return new Promise(function(resolve, reject) {
+        let queryMessage = message;
 
-export function codeLoaded(value: string, fileExt: string) {
-  editor.session.setValue(value);
-  editor.gotoLine(0);
-
-  editor.getSession().on("change", function(e) {
-    window.atomicQuery({
-      request: "change",
-      persistent: false,
-      onSuccess: (response) => {/**/ },
-      onFailure: (error_code, error_message) => {
-        console.log("Error on change");
-      }
+        // if message is coming in as an object then let's stringify it
+        if (typeof (message) != "string") {
+            queryMessage = JSON.stringify(message);
+        }
+
+        window.atomicQuery({
+            request: queryMessage,
+            persistent: false,
+            onSuccess: resolve,
+            onFailure: (error_code, error_message) => reject({ error_code: error_code, error_message: error_message })
+        });
     });
-  });
 }
 
-export function loadCode(codeUrl: string) {
-  const fileExt = codeUrl.split(".").pop();
-
-  // go ahead and set the theme prior to pulling the file across
-  editorConfig.configure(fileExt);
-
-  const p = new Promise(function(resolve, reject) {
-    const xmlHttp = new XMLHttpRequest();
-    xmlHttp.onreadystatechange = () => {
-      if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
-        resolve(xmlHttp.responseText);
-      }
-    };
-    xmlHttp.open("GET", codeUrl, true); // true for asynchronous
-    xmlHttp.send(null);
-  }).then(function(src: string) {
-    codeLoaded(src, fileExt);
-  });
+export default class HostInteropType {
+
+    private static _inst: HostInteropType = null;
+    private fileName: string = null;
+    private fileExt: string = null;
+
+    static getInstance(): HostInteropType {
+        if (HostInteropType._inst == null) {
+            HostInteropType._inst = new HostInteropType();
+        }
+        return HostInteropType._inst;
+    }
+
+    static EDITOR_SAVE_CODE = "editorSaveCode";
+    static EDITOR_SAVE_FILE = "editorSaveFile";
+    static EDITOR_LOAD_COMPLETE = "editorLoadComplete";
+    static EDITOR_CHANGE = "editorChange";
+
+    constructor() {
+        // Set up the window object so the host can call into it
+        window.HOST_loadCode = this.loadCode.bind(this);
+        window.HOST_saveCode = this.saveCode.bind(this);
+
+        window.HOST_projectUnloaded = this.projectUnloaded.bind(this);
+        window.HOST_resourceRenamed = this.resourceRenamed.bind(this);
+        window.HOST_resourceDeleted = this.resourceDeleted.bind(this);
+    }
+
+    /**
+     * Called from the host to notify the client what file to load
+     * @param  {string} codeUrl
+     */
+    loadCode(codeUrl: string) {
+        console.log("Load Code called for :" + codeUrl);
+        const fileExt = codeUrl.indexOf(".") != -1 ? codeUrl.split(".").pop() : "";
+        const filename = codeUrl.replace("atomic://", "");
+
+        // Keep track of our filename
+        this.fileName = filename;
+        this.fileExt = fileExt;
+
+        // go ahead and set the theme prior to pulling the file across
+        editorCommands.configure(fileExt, filename);
+
+        // get the code
+        this.getResource(codeUrl).then((src: string) => {
+            editorCommands.loadCodeIntoEditor(src, filename, fileExt);
+        }).catch((e: Editor.ClientExtensions.AtomicErrorMessage) => {
+            console.log("Error loading code: " + e.error_message);
+        });
+    }
+
+    /**
+     * Save the contents of the editor
+     * @return {Promise}
+     */
+    saveCode(): Promise<any> {
+        let source = editorCommands.getSourceText();
+        return atomicQueryPromise({
+            message: HostInteropType.EDITOR_SAVE_CODE,
+            payload: source
+        }).then(() => {
+            editorCommands.codeSaved(this.fileName, this.fileExt, source);
+        });
+    }
+
+    /**
+     * Save the contents of a file as filename
+     * @param  {string} filename
+     * @param  {string} fileContents
+     * @return {Promise}
+     */
+    saveFile(filename: string, fileContents: string): Promise<any> {
+        const fileExt = filename.indexOf(".") != -1 ? filename.split(".").pop() : "";
+        return atomicQueryPromise({
+            message: HostInteropType.EDITOR_SAVE_FILE,
+            filename: filename,
+            payload: fileContents
+        }).then(() => {
+            editorCommands.codeSaved(filename, fileExt, fileContents);
+        });
+    }
+
+    /**
+     * Call this function when the client is fully loaded up.  This will notify the host that
+     * it can start loading code
+     */
+    editorLoaded() {
+        if (DEBUG_ALERT) {
+            alert(`Attach chrome dev tools to this instance by navigating to http://localhost:${DEBUG_PORT}`);
+        }
+        atomicQueryPromise(HostInteropType.EDITOR_LOAD_COMPLETE);
+    }
+
+    /**
+     * Queries the host for a particular resource and returns it in a promise
+     * @param  {string} codeUrl
+     * @return {Promise}
+     */
+    getResource(codeUrl: string): Promise<{}> {
+        return new Promise(function(resolve, reject) {
+            const xmlHttp = new XMLHttpRequest();
+            xmlHttp.onreadystatechange = () => {
+                if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
+                    resolve(xmlHttp.responseText);
+                }
+            };
+            xmlHttp.open("GET", codeUrl, true); // true for asynchronous
+            xmlHttp.send(null);
+        });
+    }
+
+    /**
+     * Returns a file resource from the resources directory
+     * @param  {string} filename name and path of file under the project directory or a fully qualified file name
+     * @return {Promise}
+     */
+    getFileResource(filename: string): Promise<{}> {
+        return this.getResource(`atomic://${filename}`);
+    }
+
+    /**
+     * Notify the host that the contents of the editor has changed
+     */
+    notifyEditorChange() {
+        atomicQueryPromise(HostInteropType.EDITOR_CHANGE).catch((e: Editor.ClientExtensions.AtomicErrorMessage) => {
+            console.log("Error on change: " + e.error_message);
+        });
+    }
+
+    /**
+     * Notify that the project has been unloaded
+     */
+    projectUnloaded() {
+        editorCommands.projectUnloaded();
+    }
+
+    /**
+     * Notify that a resource has been renamed
+     * @param  {string} path
+     * @param  {string} newPath
+     */
+    resourceRenamed(path: string, newPath: string) {
+        this.fileName = newPath;
+        editorCommands.resourceRenamed(path, newPath);
+    }
+
+    /**
+     * Notify that a resource has been deleted
+     * @param  {string} path
+     */
+    resourceDeleted(path: string) {
+        editorCommands.resourceDeleted(path);
+    }
 }
 
-// Set up the window object so the host can call into it
-window.loadCode = loadCode;
-window.saveCode = saveCode;
+HostInteropType.getInstance().editorLoaded();

+ 2167 - 0
Script/AtomicWebViewEditor/modules/typescript.d.ts

@@ -0,0 +1,2167 @@
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved. 
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0  
+ 
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
+MERCHANTABLITY OR NON-INFRINGEMENT. 
+ 
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+
+declare namespace ts {
+    interface Map<T> {
+        [index: string]: T;
+    }
+    interface FileMap<T> {
+        get(fileName: string): T;
+        set(fileName: string, value: T): void;
+        contains(fileName: string): boolean;
+        remove(fileName: string): void;
+        forEachValue(f: (v: T) => void): void;
+        clear(): void;
+    }
+    interface TextRange {
+        pos: number;
+        end: number;
+    }
+    const enum SyntaxKind {
+        Unknown = 0,
+        EndOfFileToken = 1,
+        SingleLineCommentTrivia = 2,
+        MultiLineCommentTrivia = 3,
+        NewLineTrivia = 4,
+        WhitespaceTrivia = 5,
+        ShebangTrivia = 6,
+        ConflictMarkerTrivia = 7,
+        NumericLiteral = 8,
+        StringLiteral = 9,
+        RegularExpressionLiteral = 10,
+        NoSubstitutionTemplateLiteral = 11,
+        TemplateHead = 12,
+        TemplateMiddle = 13,
+        TemplateTail = 14,
+        OpenBraceToken = 15,
+        CloseBraceToken = 16,
+        OpenParenToken = 17,
+        CloseParenToken = 18,
+        OpenBracketToken = 19,
+        CloseBracketToken = 20,
+        DotToken = 21,
+        DotDotDotToken = 22,
+        SemicolonToken = 23,
+        CommaToken = 24,
+        LessThanToken = 25,
+        LessThanSlashToken = 26,
+        GreaterThanToken = 27,
+        LessThanEqualsToken = 28,
+        GreaterThanEqualsToken = 29,
+        EqualsEqualsToken = 30,
+        ExclamationEqualsToken = 31,
+        EqualsEqualsEqualsToken = 32,
+        ExclamationEqualsEqualsToken = 33,
+        EqualsGreaterThanToken = 34,
+        PlusToken = 35,
+        MinusToken = 36,
+        AsteriskToken = 37,
+        AsteriskAsteriskToken = 38,
+        SlashToken = 39,
+        PercentToken = 40,
+        PlusPlusToken = 41,
+        MinusMinusToken = 42,
+        LessThanLessThanToken = 43,
+        GreaterThanGreaterThanToken = 44,
+        GreaterThanGreaterThanGreaterThanToken = 45,
+        AmpersandToken = 46,
+        BarToken = 47,
+        CaretToken = 48,
+        ExclamationToken = 49,
+        TildeToken = 50,
+        AmpersandAmpersandToken = 51,
+        BarBarToken = 52,
+        QuestionToken = 53,
+        ColonToken = 54,
+        AtToken = 55,
+        EqualsToken = 56,
+        PlusEqualsToken = 57,
+        MinusEqualsToken = 58,
+        AsteriskEqualsToken = 59,
+        AsteriskAsteriskEqualsToken = 60,
+        SlashEqualsToken = 61,
+        PercentEqualsToken = 62,
+        LessThanLessThanEqualsToken = 63,
+        GreaterThanGreaterThanEqualsToken = 64,
+        GreaterThanGreaterThanGreaterThanEqualsToken = 65,
+        AmpersandEqualsToken = 66,
+        BarEqualsToken = 67,
+        CaretEqualsToken = 68,
+        Identifier = 69,
+        BreakKeyword = 70,
+        CaseKeyword = 71,
+        CatchKeyword = 72,
+        ClassKeyword = 73,
+        ConstKeyword = 74,
+        ContinueKeyword = 75,
+        DebuggerKeyword = 76,
+        DefaultKeyword = 77,
+        DeleteKeyword = 78,
+        DoKeyword = 79,
+        ElseKeyword = 80,
+        EnumKeyword = 81,
+        ExportKeyword = 82,
+        ExtendsKeyword = 83,
+        FalseKeyword = 84,
+        FinallyKeyword = 85,
+        ForKeyword = 86,
+        FunctionKeyword = 87,
+        IfKeyword = 88,
+        ImportKeyword = 89,
+        InKeyword = 90,
+        InstanceOfKeyword = 91,
+        NewKeyword = 92,
+        NullKeyword = 93,
+        ReturnKeyword = 94,
+        SuperKeyword = 95,
+        SwitchKeyword = 96,
+        ThisKeyword = 97,
+        ThrowKeyword = 98,
+        TrueKeyword = 99,
+        TryKeyword = 100,
+        TypeOfKeyword = 101,
+        VarKeyword = 102,
+        VoidKeyword = 103,
+        WhileKeyword = 104,
+        WithKeyword = 105,
+        ImplementsKeyword = 106,
+        InterfaceKeyword = 107,
+        LetKeyword = 108,
+        PackageKeyword = 109,
+        PrivateKeyword = 110,
+        ProtectedKeyword = 111,
+        PublicKeyword = 112,
+        StaticKeyword = 113,
+        YieldKeyword = 114,
+        AbstractKeyword = 115,
+        AsKeyword = 116,
+        AnyKeyword = 117,
+        AsyncKeyword = 118,
+        AwaitKeyword = 119,
+        BooleanKeyword = 120,
+        ConstructorKeyword = 121,
+        DeclareKeyword = 122,
+        GetKeyword = 123,
+        IsKeyword = 124,
+        ModuleKeyword = 125,
+        NamespaceKeyword = 126,
+        RequireKeyword = 127,
+        NumberKeyword = 128,
+        SetKeyword = 129,
+        StringKeyword = 130,
+        SymbolKeyword = 131,
+        TypeKeyword = 132,
+        FromKeyword = 133,
+        OfKeyword = 134,
+        QualifiedName = 135,
+        ComputedPropertyName = 136,
+        TypeParameter = 137,
+        Parameter = 138,
+        Decorator = 139,
+        PropertySignature = 140,
+        PropertyDeclaration = 141,
+        MethodSignature = 142,
+        MethodDeclaration = 143,
+        Constructor = 144,
+        GetAccessor = 145,
+        SetAccessor = 146,
+        CallSignature = 147,
+        ConstructSignature = 148,
+        IndexSignature = 149,
+        TypePredicate = 150,
+        TypeReference = 151,
+        FunctionType = 152,
+        ConstructorType = 153,
+        TypeQuery = 154,
+        TypeLiteral = 155,
+        ArrayType = 156,
+        TupleType = 157,
+        UnionType = 158,
+        IntersectionType = 159,
+        ParenthesizedType = 160,
+        ObjectBindingPattern = 161,
+        ArrayBindingPattern = 162,
+        BindingElement = 163,
+        ArrayLiteralExpression = 164,
+        ObjectLiteralExpression = 165,
+        PropertyAccessExpression = 166,
+        ElementAccessExpression = 167,
+        CallExpression = 168,
+        NewExpression = 169,
+        TaggedTemplateExpression = 170,
+        TypeAssertionExpression = 171,
+        ParenthesizedExpression = 172,
+        FunctionExpression = 173,
+        ArrowFunction = 174,
+        DeleteExpression = 175,
+        TypeOfExpression = 176,
+        VoidExpression = 177,
+        AwaitExpression = 178,
+        PrefixUnaryExpression = 179,
+        PostfixUnaryExpression = 180,
+        BinaryExpression = 181,
+        ConditionalExpression = 182,
+        TemplateExpression = 183,
+        YieldExpression = 184,
+        SpreadElementExpression = 185,
+        ClassExpression = 186,
+        OmittedExpression = 187,
+        ExpressionWithTypeArguments = 188,
+        AsExpression = 189,
+        TemplateSpan = 190,
+        SemicolonClassElement = 191,
+        Block = 192,
+        VariableStatement = 193,
+        EmptyStatement = 194,
+        ExpressionStatement = 195,
+        IfStatement = 196,
+        DoStatement = 197,
+        WhileStatement = 198,
+        ForStatement = 199,
+        ForInStatement = 200,
+        ForOfStatement = 201,
+        ContinueStatement = 202,
+        BreakStatement = 203,
+        ReturnStatement = 204,
+        WithStatement = 205,
+        SwitchStatement = 206,
+        LabeledStatement = 207,
+        ThrowStatement = 208,
+        TryStatement = 209,
+        DebuggerStatement = 210,
+        VariableDeclaration = 211,
+        VariableDeclarationList = 212,
+        FunctionDeclaration = 213,
+        ClassDeclaration = 214,
+        InterfaceDeclaration = 215,
+        TypeAliasDeclaration = 216,
+        EnumDeclaration = 217,
+        ModuleDeclaration = 218,
+        ModuleBlock = 219,
+        CaseBlock = 220,
+        ImportEqualsDeclaration = 221,
+        ImportDeclaration = 222,
+        ImportClause = 223,
+        NamespaceImport = 224,
+        NamedImports = 225,
+        ImportSpecifier = 226,
+        ExportAssignment = 227,
+        ExportDeclaration = 228,
+        NamedExports = 229,
+        ExportSpecifier = 230,
+        MissingDeclaration = 231,
+        ExternalModuleReference = 232,
+        JsxElement = 233,
+        JsxSelfClosingElement = 234,
+        JsxOpeningElement = 235,
+        JsxText = 236,
+        JsxClosingElement = 237,
+        JsxAttribute = 238,
+        JsxSpreadAttribute = 239,
+        JsxExpression = 240,
+        CaseClause = 241,
+        DefaultClause = 242,
+        HeritageClause = 243,
+        CatchClause = 244,
+        PropertyAssignment = 245,
+        ShorthandPropertyAssignment = 246,
+        EnumMember = 247,
+        SourceFile = 248,
+        JSDocTypeExpression = 249,
+        JSDocAllType = 250,
+        JSDocUnknownType = 251,
+        JSDocArrayType = 252,
+        JSDocUnionType = 253,
+        JSDocTupleType = 254,
+        JSDocNullableType = 255,
+        JSDocNonNullableType = 256,
+        JSDocRecordType = 257,
+        JSDocRecordMember = 258,
+        JSDocTypeReference = 259,
+        JSDocOptionalType = 260,
+        JSDocFunctionType = 261,
+        JSDocVariadicType = 262,
+        JSDocConstructorType = 263,
+        JSDocThisType = 264,
+        JSDocComment = 265,
+        JSDocTag = 266,
+        JSDocParameterTag = 267,
+        JSDocReturnTag = 268,
+        JSDocTypeTag = 269,
+        JSDocTemplateTag = 270,
+        SyntaxList = 271,
+        Count = 272,
+        FirstAssignment = 56,
+        LastAssignment = 68,
+        FirstReservedWord = 70,
+        LastReservedWord = 105,
+        FirstKeyword = 70,
+        LastKeyword = 134,
+        FirstFutureReservedWord = 106,
+        LastFutureReservedWord = 114,
+        FirstTypeNode = 151,
+        LastTypeNode = 160,
+        FirstPunctuation = 15,
+        LastPunctuation = 68,
+        FirstToken = 0,
+        LastToken = 134,
+        FirstTriviaToken = 2,
+        LastTriviaToken = 7,
+        FirstLiteralToken = 8,
+        LastLiteralToken = 11,
+        FirstTemplateToken = 11,
+        LastTemplateToken = 14,
+        FirstBinaryOperator = 25,
+        LastBinaryOperator = 68,
+        FirstNode = 135,
+    }
+    const enum NodeFlags {
+        Export = 1,
+        Ambient = 2,
+        Public = 16,
+        Private = 32,
+        Protected = 64,
+        Static = 128,
+        Abstract = 256,
+        Async = 512,
+        Default = 1024,
+        MultiLine = 2048,
+        Synthetic = 4096,
+        DeclarationFile = 8192,
+        Let = 16384,
+        Const = 32768,
+        OctalLiteral = 65536,
+        Namespace = 131072,
+        ExportContext = 262144,
+        ContainsThis = 524288,
+        Modifier = 2035,
+        AccessibilityModifier = 112,
+        BlockScoped = 49152,
+    }
+    const enum JsxFlags {
+        None = 0,
+        IntrinsicNamedElement = 1,
+        IntrinsicIndexedElement = 2,
+        ClassElement = 4,
+        UnknownElement = 8,
+        IntrinsicElement = 3,
+    }
+    interface Node extends TextRange {
+        kind: SyntaxKind;
+        flags: NodeFlags;
+        decorators?: NodeArray<Decorator>;
+        modifiers?: ModifiersArray;
+        parent?: Node;
+    }
+    interface NodeArray<T> extends Array<T>, TextRange {
+        hasTrailingComma?: boolean;
+    }
+    interface ModifiersArray extends NodeArray<Node> {
+        flags: number;
+    }
+    interface Identifier extends PrimaryExpression {
+        text: string;
+        originalKeywordKind?: SyntaxKind;
+    }
+    interface QualifiedName extends Node {
+        left: EntityName;
+        right: Identifier;
+    }
+    type EntityName = Identifier | QualifiedName;
+    type DeclarationName = Identifier | LiteralExpression | ComputedPropertyName | BindingPattern;
+    interface Declaration extends Node {
+        _declarationBrand: any;
+        name?: DeclarationName;
+    }
+    interface ComputedPropertyName extends Node {
+        expression: Expression;
+    }
+    interface Decorator extends Node {
+        expression: LeftHandSideExpression;
+    }
+    interface TypeParameterDeclaration extends Declaration {
+        name: Identifier;
+        constraint?: TypeNode;
+        expression?: Expression;
+    }
+    interface SignatureDeclaration extends Declaration {
+        typeParameters?: NodeArray<TypeParameterDeclaration>;
+        parameters: NodeArray<ParameterDeclaration>;
+        type?: TypeNode;
+    }
+    interface VariableDeclaration extends Declaration {
+        parent?: VariableDeclarationList;
+        name: Identifier | BindingPattern;
+        type?: TypeNode;
+        initializer?: Expression;
+    }
+    interface VariableDeclarationList extends Node {
+        declarations: NodeArray<VariableDeclaration>;
+    }
+    interface ParameterDeclaration extends Declaration {
+        dotDotDotToken?: Node;
+        name: Identifier | BindingPattern;
+        questionToken?: Node;
+        type?: TypeNode;
+        initializer?: Expression;
+    }
+    interface BindingElement extends Declaration {
+        propertyName?: Identifier;
+        dotDotDotToken?: Node;
+        name: Identifier | BindingPattern;
+        initializer?: Expression;
+    }
+    interface PropertyDeclaration extends Declaration, ClassElement {
+        name: DeclarationName;
+        questionToken?: Node;
+        type?: TypeNode;
+        initializer?: Expression;
+    }
+    interface ObjectLiteralElement extends Declaration {
+        _objectLiteralBrandBrand: any;
+    }
+    interface PropertyAssignment extends ObjectLiteralElement {
+        _propertyAssignmentBrand: any;
+        name: DeclarationName;
+        questionToken?: Node;
+        initializer: Expression;
+    }
+    interface ShorthandPropertyAssignment extends ObjectLiteralElement {
+        name: Identifier;
+        questionToken?: Node;
+        equalsToken?: Node;
+        objectAssignmentInitializer?: Expression;
+    }
+    interface VariableLikeDeclaration extends Declaration {
+        propertyName?: Identifier;
+        dotDotDotToken?: Node;
+        name: DeclarationName;
+        questionToken?: Node;
+        type?: TypeNode;
+        initializer?: Expression;
+    }
+    interface BindingPattern extends Node {
+        elements: NodeArray<BindingElement>;
+    }
+    /**
+     * Several node kinds share function-like features such as a signature,
+     * a name, and a body. These nodes should extend FunctionLikeDeclaration.
+     * Examples:
+     * - FunctionDeclaration
+     * - MethodDeclaration
+     * - AccessorDeclaration
+     */
+    interface FunctionLikeDeclaration extends SignatureDeclaration {
+        _functionLikeDeclarationBrand: any;
+        asteriskToken?: Node;
+        questionToken?: Node;
+        body?: Block | Expression;
+    }
+    interface FunctionDeclaration extends FunctionLikeDeclaration, Statement {
+        name?: Identifier;
+        body?: Block;
+    }
+    interface MethodDeclaration extends FunctionLikeDeclaration, ClassElement, ObjectLiteralElement {
+        body?: Block;
+    }
+    interface ConstructorDeclaration extends FunctionLikeDeclaration, ClassElement {
+        body?: Block;
+    }
+    interface SemicolonClassElement extends ClassElement {
+        _semicolonClassElementBrand: any;
+    }
+    interface AccessorDeclaration extends FunctionLikeDeclaration, ClassElement, ObjectLiteralElement {
+        _accessorDeclarationBrand: any;
+        body: Block;
+    }
+    interface IndexSignatureDeclaration extends SignatureDeclaration, ClassElement {
+        _indexSignatureDeclarationBrand: any;
+    }
+    interface TypeNode extends Node {
+        _typeNodeBrand: any;
+    }
+    interface FunctionOrConstructorTypeNode extends TypeNode, SignatureDeclaration {
+        _functionOrConstructorTypeNodeBrand: any;
+    }
+    interface TypeReferenceNode extends TypeNode {
+        typeName: EntityName;
+        typeArguments?: NodeArray<TypeNode>;
+    }
+    interface TypePredicateNode extends TypeNode {
+        parameterName: Identifier;
+        type: TypeNode;
+    }
+    interface TypeQueryNode extends TypeNode {
+        exprName: EntityName;
+    }
+    interface TypeLiteralNode extends TypeNode, Declaration {
+        members: NodeArray<Node>;
+    }
+    interface ArrayTypeNode extends TypeNode {
+        elementType: TypeNode;
+    }
+    interface TupleTypeNode extends TypeNode {
+        elementTypes: NodeArray<TypeNode>;
+    }
+    interface UnionOrIntersectionTypeNode extends TypeNode {
+        types: NodeArray<TypeNode>;
+    }
+    interface UnionTypeNode extends UnionOrIntersectionTypeNode {
+    }
+    interface IntersectionTypeNode extends UnionOrIntersectionTypeNode {
+    }
+    interface ParenthesizedTypeNode extends TypeNode {
+        type: TypeNode;
+    }
+    interface StringLiteral extends LiteralExpression, TypeNode {
+        _stringLiteralBrand: any;
+    }
+    interface Expression extends Node {
+        _expressionBrand: any;
+        contextualType?: Type;
+    }
+    interface UnaryExpression extends Expression {
+        _unaryExpressionBrand: any;
+    }
+    interface IncrementExpression extends UnaryExpression {
+        _incrementExpressionBrand: any;
+    }
+    interface PrefixUnaryExpression extends IncrementExpression {
+        operator: SyntaxKind;
+        operand: UnaryExpression;
+    }
+    interface PostfixUnaryExpression extends IncrementExpression {
+        operand: LeftHandSideExpression;
+        operator: SyntaxKind;
+    }
+    interface PostfixExpression extends UnaryExpression {
+        _postfixExpressionBrand: any;
+    }
+    interface LeftHandSideExpression extends IncrementExpression {
+        _leftHandSideExpressionBrand: any;
+    }
+    interface MemberExpression extends LeftHandSideExpression {
+        _memberExpressionBrand: any;
+    }
+    interface PrimaryExpression extends MemberExpression {
+        _primaryExpressionBrand: any;
+    }
+    interface DeleteExpression extends UnaryExpression {
+        expression: UnaryExpression;
+    }
+    interface TypeOfExpression extends UnaryExpression {
+        expression: UnaryExpression;
+    }
+    interface VoidExpression extends UnaryExpression {
+        expression: UnaryExpression;
+    }
+    interface AwaitExpression extends UnaryExpression {
+        expression: UnaryExpression;
+    }
+    interface YieldExpression extends Expression {
+        asteriskToken?: Node;
+        expression?: Expression;
+    }
+    interface BinaryExpression extends Expression {
+        left: Expression;
+        operatorToken: Node;
+        right: Expression;
+    }
+    interface ConditionalExpression extends Expression {
+        condition: Expression;
+        questionToken: Node;
+        whenTrue: Expression;
+        colonToken: Node;
+        whenFalse: Expression;
+    }
+    interface FunctionExpression extends PrimaryExpression, FunctionLikeDeclaration {
+        name?: Identifier;
+        body: Block | Expression;
+    }
+    interface ArrowFunction extends Expression, FunctionLikeDeclaration {
+        equalsGreaterThanToken: Node;
+    }
+    interface LiteralExpression extends PrimaryExpression {
+        text: string;
+        isUnterminated?: boolean;
+        hasExtendedUnicodeEscape?: boolean;
+    }
+    interface TemplateExpression extends PrimaryExpression {
+        head: LiteralExpression;
+        templateSpans: NodeArray<TemplateSpan>;
+    }
+    interface TemplateSpan extends Node {
+        expression: Expression;
+        literal: LiteralExpression;
+    }
+    interface ParenthesizedExpression extends PrimaryExpression {
+        expression: Expression;
+    }
+    interface ArrayLiteralExpression extends PrimaryExpression {
+        elements: NodeArray<Expression>;
+    }
+    interface SpreadElementExpression extends Expression {
+        expression: Expression;
+    }
+    interface ObjectLiteralExpression extends PrimaryExpression, Declaration {
+        properties: NodeArray<ObjectLiteralElement>;
+    }
+    interface PropertyAccessExpression extends MemberExpression {
+        expression: LeftHandSideExpression;
+        dotToken: Node;
+        name: Identifier;
+    }
+    interface ElementAccessExpression extends MemberExpression {
+        expression: LeftHandSideExpression;
+        argumentExpression?: Expression;
+    }
+    interface CallExpression extends LeftHandSideExpression {
+        expression: LeftHandSideExpression;
+        typeArguments?: NodeArray<TypeNode>;
+        arguments: NodeArray<Expression>;
+    }
+    interface ExpressionWithTypeArguments extends TypeNode {
+        expression: LeftHandSideExpression;
+        typeArguments?: NodeArray<TypeNode>;
+    }
+    interface NewExpression extends CallExpression, PrimaryExpression {
+    }
+    interface TaggedTemplateExpression extends MemberExpression {
+        tag: LeftHandSideExpression;
+        template: LiteralExpression | TemplateExpression;
+    }
+    type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator;
+    interface AsExpression extends Expression {
+        expression: Expression;
+        type: TypeNode;
+    }
+    interface TypeAssertion extends UnaryExpression {
+        type: TypeNode;
+        expression: UnaryExpression;
+    }
+    type AssertionExpression = TypeAssertion | AsExpression;
+    interface JsxElement extends PrimaryExpression {
+        openingElement: JsxOpeningElement;
+        children: NodeArray<JsxChild>;
+        closingElement: JsxClosingElement;
+    }
+    interface JsxOpeningElement extends Expression {
+        _openingElementBrand?: any;
+        tagName: EntityName;
+        attributes: NodeArray<JsxAttribute | JsxSpreadAttribute>;
+    }
+    interface JsxSelfClosingElement extends PrimaryExpression, JsxOpeningElement {
+        _selfClosingElementBrand?: any;
+    }
+    type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement;
+    interface JsxAttribute extends Node {
+        name: Identifier;
+        initializer?: Expression;
+    }
+    interface JsxSpreadAttribute extends Node {
+        expression: Expression;
+    }
+    interface JsxClosingElement extends Node {
+        tagName: EntityName;
+    }
+    interface JsxExpression extends Expression {
+        expression?: Expression;
+    }
+    interface JsxText extends Node {
+        _jsxTextExpressionBrand: any;
+    }
+    type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement;
+    interface Statement extends Node {
+        _statementBrand: any;
+    }
+    interface Block extends Statement {
+        statements: NodeArray<Statement>;
+    }
+    interface VariableStatement extends Statement {
+        declarationList: VariableDeclarationList;
+    }
+    interface ExpressionStatement extends Statement {
+        expression: Expression;
+    }
+    interface IfStatement extends Statement {
+        expression: Expression;
+        thenStatement: Statement;
+        elseStatement?: Statement;
+    }
+    interface IterationStatement extends Statement {
+        statement: Statement;
+    }
+    interface DoStatement extends IterationStatement {
+        expression: Expression;
+    }
+    interface WhileStatement extends IterationStatement {
+        expression: Expression;
+    }
+    interface ForStatement extends IterationStatement {
+        initializer?: VariableDeclarationList | Expression;
+        condition?: Expression;
+        incrementor?: Expression;
+    }
+    interface ForInStatement extends IterationStatement {
+        initializer: VariableDeclarationList | Expression;
+        expression: Expression;
+    }
+    interface ForOfStatement extends IterationStatement {
+        initializer: VariableDeclarationList | Expression;
+        expression: Expression;
+    }
+    interface BreakOrContinueStatement extends Statement {
+        label?: Identifier;
+    }
+    interface ReturnStatement extends Statement {
+        expression?: Expression;
+    }
+    interface WithStatement extends Statement {
+        expression: Expression;
+        statement: Statement;
+    }
+    interface SwitchStatement extends Statement {
+        expression: Expression;
+        caseBlock: CaseBlock;
+    }
+    interface CaseBlock extends Node {
+        clauses: NodeArray<CaseOrDefaultClause>;
+    }
+    interface CaseClause extends Node {
+        expression?: Expression;
+        statements: NodeArray<Statement>;
+    }
+    interface DefaultClause extends Node {
+        statements: NodeArray<Statement>;
+    }
+    type CaseOrDefaultClause = CaseClause | DefaultClause;
+    interface LabeledStatement extends Statement {
+        label: Identifier;
+        statement: Statement;
+    }
+    interface ThrowStatement extends Statement {
+        expression: Expression;
+    }
+    interface TryStatement extends Statement {
+        tryBlock: Block;
+        catchClause?: CatchClause;
+        finallyBlock?: Block;
+    }
+    interface CatchClause extends Node {
+        variableDeclaration: VariableDeclaration;
+        block: Block;
+    }
+    interface ClassLikeDeclaration extends Declaration {
+        name?: Identifier;
+        typeParameters?: NodeArray<TypeParameterDeclaration>;
+        heritageClauses?: NodeArray<HeritageClause>;
+        members: NodeArray<ClassElement>;
+    }
+    interface ClassDeclaration extends ClassLikeDeclaration, Statement {
+    }
+    interface ClassExpression extends ClassLikeDeclaration, PrimaryExpression {
+    }
+    interface ClassElement extends Declaration {
+        _classElementBrand: any;
+    }
+    interface InterfaceDeclaration extends Declaration, Statement {
+        name: Identifier;
+        typeParameters?: NodeArray<TypeParameterDeclaration>;
+        heritageClauses?: NodeArray<HeritageClause>;
+        members: NodeArray<Declaration>;
+    }
+    interface HeritageClause extends Node {
+        token: SyntaxKind;
+        types?: NodeArray<ExpressionWithTypeArguments>;
+    }
+    interface TypeAliasDeclaration extends Declaration, Statement {
+        name: Identifier;
+        typeParameters?: NodeArray<TypeParameterDeclaration>;
+        type: TypeNode;
+    }
+    interface EnumMember extends Declaration {
+        name: DeclarationName;
+        initializer?: Expression;
+    }
+    interface EnumDeclaration extends Declaration, Statement {
+        name: Identifier;
+        members: NodeArray<EnumMember>;
+    }
+    interface ModuleDeclaration extends Declaration, Statement {
+        name: Identifier | LiteralExpression;
+        body: ModuleBlock | ModuleDeclaration;
+    }
+    interface ModuleBlock extends Node, Statement {
+        statements: NodeArray<Statement>;
+    }
+    interface ImportEqualsDeclaration extends Declaration, Statement {
+        name: Identifier;
+        moduleReference: EntityName | ExternalModuleReference;
+    }
+    interface ExternalModuleReference extends Node {
+        expression?: Expression;
+    }
+    interface ImportDeclaration extends Statement {
+        importClause?: ImportClause;
+        moduleSpecifier: Expression;
+    }
+    interface ImportClause extends Declaration {
+        name?: Identifier;
+        namedBindings?: NamespaceImport | NamedImports;
+    }
+    interface NamespaceImport extends Declaration {
+        name: Identifier;
+    }
+    interface ExportDeclaration extends Declaration, Statement {
+        exportClause?: NamedExports;
+        moduleSpecifier?: Expression;
+    }
+    interface NamedImportsOrExports extends Node {
+        elements: NodeArray<ImportOrExportSpecifier>;
+    }
+    type NamedImports = NamedImportsOrExports;
+    type NamedExports = NamedImportsOrExports;
+    interface ImportOrExportSpecifier extends Declaration {
+        propertyName?: Identifier;
+        name: Identifier;
+    }
+    type ImportSpecifier = ImportOrExportSpecifier;
+    type ExportSpecifier = ImportOrExportSpecifier;
+    interface ExportAssignment extends Declaration, Statement {
+        isExportEquals?: boolean;
+        expression: Expression;
+    }
+    interface FileReference extends TextRange {
+        fileName: string;
+    }
+    interface CommentRange extends TextRange {
+        hasTrailingNewLine?: boolean;
+        kind: SyntaxKind;
+    }
+    interface JSDocTypeExpression extends Node {
+        type: JSDocType;
+    }
+    interface JSDocType extends TypeNode {
+        _jsDocTypeBrand: any;
+    }
+    interface JSDocAllType extends JSDocType {
+        _JSDocAllTypeBrand: any;
+    }
+    interface JSDocUnknownType extends JSDocType {
+        _JSDocUnknownTypeBrand: any;
+    }
+    interface JSDocArrayType extends JSDocType {
+        elementType: JSDocType;
+    }
+    interface JSDocUnionType extends JSDocType {
+        types: NodeArray<JSDocType>;
+    }
+    interface JSDocTupleType extends JSDocType {
+        types: NodeArray<JSDocType>;
+    }
+    interface JSDocNonNullableType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocNullableType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocRecordType extends JSDocType, TypeLiteralNode {
+        members: NodeArray<JSDocRecordMember>;
+    }
+    interface JSDocTypeReference extends JSDocType {
+        name: EntityName;
+        typeArguments: NodeArray<JSDocType>;
+    }
+    interface JSDocOptionalType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocFunctionType extends JSDocType, SignatureDeclaration {
+        parameters: NodeArray<ParameterDeclaration>;
+        type: JSDocType;
+    }
+    interface JSDocVariadicType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocConstructorType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocThisType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocRecordMember extends PropertyDeclaration {
+        name: Identifier | LiteralExpression;
+        type?: JSDocType;
+    }
+    interface JSDocComment extends Node {
+        tags: NodeArray<JSDocTag>;
+    }
+    interface JSDocTag extends Node {
+        atToken: Node;
+        tagName: Identifier;
+    }
+    interface JSDocTemplateTag extends JSDocTag {
+        typeParameters: NodeArray<TypeParameterDeclaration>;
+    }
+    interface JSDocReturnTag extends JSDocTag {
+        typeExpression: JSDocTypeExpression;
+    }
+    interface JSDocTypeTag extends JSDocTag {
+        typeExpression: JSDocTypeExpression;
+    }
+    interface JSDocParameterTag extends JSDocTag {
+        preParameterName?: Identifier;
+        typeExpression?: JSDocTypeExpression;
+        postParameterName?: Identifier;
+        isBracketed: boolean;
+    }
+    interface SourceFile extends Declaration {
+        statements: NodeArray<Statement>;
+        endOfFileToken: Node;
+        fileName: string;
+        text: string;
+        amdDependencies: {
+            path: string;
+            name: string;
+        }[];
+        moduleName: string;
+        referencedFiles: FileReference[];
+        languageVariant: LanguageVariant;
+        /**
+         * lib.d.ts should have a reference comment like
+         *
+         *  /// <reference no-default-lib="true"/>
+         *
+         * If any other file has this comment, it signals not to include lib.d.ts
+         * because this containing file is intended to act as a default library.
+         */
+        hasNoDefaultLib: boolean;
+        languageVersion: ScriptTarget;
+    }
+    interface ScriptReferenceHost {
+        getCompilerOptions(): CompilerOptions;
+        getSourceFile(fileName: string): SourceFile;
+        getCurrentDirectory(): string;
+    }
+    interface ParseConfigHost extends ModuleResolutionHost {
+        readDirectory(rootDir: string, extension: string, exclude: string[]): string[];
+    }
+    interface WriteFileCallback {
+        (fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void): void;
+    }
+    class OperationCanceledException {
+    }
+    interface CancellationToken {
+        isCancellationRequested(): boolean;
+        /** @throws OperationCanceledException if isCancellationRequested is true */
+        throwIfCancellationRequested(): void;
+    }
+    interface Program extends ScriptReferenceHost {
+        /**
+         * Get a list of root file names that were passed to a 'createProgram'
+         */
+        getRootFileNames(): string[];
+        /**
+         * Get a list of files in the program
+         */
+        getSourceFiles(): SourceFile[];
+        /**
+         * Emits the JavaScript and declaration files.  If targetSourceFile is not specified, then
+         * the JavaScript and declaration files will be produced for all the files in this program.
+         * If targetSourceFile is specified, then only the JavaScript and declaration for that
+         * specific file will be generated.
+         *
+         * If writeFile is not specified then the writeFile callback from the compiler host will be
+         * used for writing the JavaScript and declaration files.  Otherwise, the writeFile parameter
+         * will be invoked when writing the JavaScript and declaration files.
+         */
+        emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
+        getOptionsDiagnostics(cancellationToken?: CancellationToken): Diagnostic[];
+        getGlobalDiagnostics(cancellationToken?: CancellationToken): Diagnostic[];
+        getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
+        getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
+        getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
+        /**
+         * Gets a type checker that can be used to semantically analyze source fils in the program.
+         */
+        getTypeChecker(): TypeChecker;
+    }
+    interface SourceMapSpan {
+        /** Line number in the .js file. */
+        emittedLine: number;
+        /** Column number in the .js file. */
+        emittedColumn: number;
+        /** Line number in the .ts file. */
+        sourceLine: number;
+        /** Column number in the .ts file. */
+        sourceColumn: number;
+        /** Optional name (index into names array) associated with this span. */
+        nameIndex?: number;
+        /** .ts file (index into sources array) associated with this span */
+        sourceIndex: number;
+    }
+    interface SourceMapData {
+        sourceMapFilePath: string;
+        jsSourceMappingURL: string;
+        sourceMapFile: string;
+        sourceMapSourceRoot: string;
+        sourceMapSources: string[];
+        sourceMapSourcesContent?: string[];
+        inputSourceFileNames: string[];
+        sourceMapNames?: string[];
+        sourceMapMappings: string;
+        sourceMapDecodedMappings: SourceMapSpan[];
+    }
+    /** Return code used by getEmitOutput function to indicate status of the function */
+    enum ExitStatus {
+        Success = 0,
+        DiagnosticsPresent_OutputsSkipped = 1,
+        DiagnosticsPresent_OutputsGenerated = 2,
+    }
+    interface EmitResult {
+        emitSkipped: boolean;
+        diagnostics: Diagnostic[];
+    }
+    interface TypeChecker {
+        getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type;
+        getDeclaredTypeOfSymbol(symbol: Symbol): Type;
+        getPropertiesOfType(type: Type): Symbol[];
+        getPropertyOfType(type: Type, propertyName: string): Symbol;
+        getSignaturesOfType(type: Type, kind: SignatureKind): Signature[];
+        getIndexTypeOfType(type: Type, kind: IndexKind): Type;
+        getBaseTypes(type: InterfaceType): ObjectType[];
+        getReturnTypeOfSignature(signature: Signature): Type;
+        getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
+        getSymbolAtLocation(node: Node): Symbol;
+        getShorthandAssignmentValueSymbol(location: Node): Symbol;
+        getTypeAtLocation(node: Node): Type;
+        typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
+        symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
+        getSymbolDisplayBuilder(): SymbolDisplayBuilder;
+        getFullyQualifiedName(symbol: Symbol): string;
+        getAugmentedPropertiesOfType(type: Type): Symbol[];
+        getRootSymbols(symbol: Symbol): Symbol[];
+        getContextualType(node: Expression): Type;
+        getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature;
+        getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature;
+        isImplementationOfOverload(node: FunctionLikeDeclaration): boolean;
+        isUndefinedSymbol(symbol: Symbol): boolean;
+        isArgumentsSymbol(symbol: Symbol): boolean;
+        getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
+        isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
+        getAliasedSymbol(symbol: Symbol): Symbol;
+        getExportsOfModule(moduleSymbol: Symbol): Symbol[];
+        getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type;
+        getJsxIntrinsicTagNames(): Symbol[];
+        isOptionalParameter(node: ParameterDeclaration): boolean;
+    }
+    interface SymbolDisplayBuilder {
+        buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildSymbolDisplay(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void;
+        buildSignatureDisplay(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildParameterDisplay(parameter: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags): void;
+        buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+    }
+    interface SymbolWriter {
+        writeKeyword(text: string): void;
+        writeOperator(text: string): void;
+        writePunctuation(text: string): void;
+        writeSpace(text: string): void;
+        writeStringLiteral(text: string): void;
+        writeParameter(text: string): void;
+        writeSymbol(text: string, symbol: Symbol): void;
+        writeLine(): void;
+        increaseIndent(): void;
+        decreaseIndent(): void;
+        clear(): void;
+        trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void;
+        reportInaccessibleThisError(): void;
+    }
+    const enum TypeFormatFlags {
+        None = 0,
+        WriteArrayAsGenericType = 1,
+        UseTypeOfFunction = 2,
+        NoTruncation = 4,
+        WriteArrowStyleSignature = 8,
+        WriteOwnNameForAnyLike = 16,
+        WriteTypeArgumentsOfSignature = 32,
+        InElementType = 64,
+        UseFullyQualifiedType = 128,
+    }
+    const enum SymbolFormatFlags {
+        None = 0,
+        WriteTypeParametersOrArguments = 1,
+        UseOnlyExternalAliasing = 2,
+    }
+    interface TypePredicate {
+        parameterName: string;
+        parameterIndex: number;
+        type: Type;
+    }
+    const enum SymbolFlags {
+        None = 0,
+        FunctionScopedVariable = 1,
+        BlockScopedVariable = 2,
+        Property = 4,
+        EnumMember = 8,
+        Function = 16,
+        Class = 32,
+        Interface = 64,
+        ConstEnum = 128,
+        RegularEnum = 256,
+        ValueModule = 512,
+        NamespaceModule = 1024,
+        TypeLiteral = 2048,
+        ObjectLiteral = 4096,
+        Method = 8192,
+        Constructor = 16384,
+        GetAccessor = 32768,
+        SetAccessor = 65536,
+        Signature = 131072,
+        TypeParameter = 262144,
+        TypeAlias = 524288,
+        ExportValue = 1048576,
+        ExportType = 2097152,
+        ExportNamespace = 4194304,
+        Alias = 8388608,
+        Instantiated = 16777216,
+        Merged = 33554432,
+        Transient = 67108864,
+        Prototype = 134217728,
+        SyntheticProperty = 268435456,
+        Optional = 536870912,
+        ExportStar = 1073741824,
+        Enum = 384,
+        Variable = 3,
+        Value = 107455,
+        Type = 793056,
+        Namespace = 1536,
+        Module = 1536,
+        Accessor = 98304,
+        FunctionScopedVariableExcludes = 107454,
+        BlockScopedVariableExcludes = 107455,
+        ParameterExcludes = 107455,
+        PropertyExcludes = 107455,
+        EnumMemberExcludes = 107455,
+        FunctionExcludes = 106927,
+        ClassExcludes = 899519,
+        InterfaceExcludes = 792960,
+        RegularEnumExcludes = 899327,
+        ConstEnumExcludes = 899967,
+        ValueModuleExcludes = 106639,
+        NamespaceModuleExcludes = 0,
+        MethodExcludes = 99263,
+        GetAccessorExcludes = 41919,
+        SetAccessorExcludes = 74687,
+        TypeParameterExcludes = 530912,
+        TypeAliasExcludes = 793056,
+        AliasExcludes = 8388608,
+        ModuleMember = 8914931,
+        ExportHasLocal = 944,
+        HasExports = 1952,
+        HasMembers = 6240,
+        BlockScoped = 418,
+        PropertyOrAccessor = 98308,
+        Export = 7340032,
+    }
+    interface Symbol {
+        flags: SymbolFlags;
+        name: string;
+        declarations?: Declaration[];
+        valueDeclaration?: Declaration;
+        members?: SymbolTable;
+        exports?: SymbolTable;
+    }
+    interface SymbolTable {
+        [index: string]: Symbol;
+    }
+    const enum TypeFlags {
+        Any = 1,
+        String = 2,
+        Number = 4,
+        Boolean = 8,
+        Void = 16,
+        Undefined = 32,
+        Null = 64,
+        Enum = 128,
+        StringLiteral = 256,
+        TypeParameter = 512,
+        Class = 1024,
+        Interface = 2048,
+        Reference = 4096,
+        Tuple = 8192,
+        Union = 16384,
+        Intersection = 32768,
+        Anonymous = 65536,
+        Instantiated = 131072,
+        ObjectLiteral = 524288,
+        ESSymbol = 16777216,
+        ThisType = 33554432,
+        StringLike = 258,
+        NumberLike = 132,
+        ObjectType = 80896,
+        UnionOrIntersection = 49152,
+        StructuredType = 130048,
+    }
+    type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
+    interface Type {
+        flags: TypeFlags;
+        symbol?: Symbol;
+        pattern?: DestructuringPattern;
+    }
+    interface StringLiteralType extends Type {
+        text: string;
+    }
+    interface ObjectType extends Type {
+    }
+    interface InterfaceType extends ObjectType {
+        typeParameters: TypeParameter[];
+        outerTypeParameters: TypeParameter[];
+        localTypeParameters: TypeParameter[];
+        thisType: TypeParameter;
+    }
+    interface InterfaceTypeWithDeclaredMembers extends InterfaceType {
+        declaredProperties: Symbol[];
+        declaredCallSignatures: Signature[];
+        declaredConstructSignatures: Signature[];
+        declaredStringIndexType: Type;
+        declaredNumberIndexType: Type;
+    }
+    interface TypeReference extends ObjectType {
+        target: GenericType;
+        typeArguments: Type[];
+    }
+    interface GenericType extends InterfaceType, TypeReference {
+    }
+    interface TupleType extends ObjectType {
+        elementTypes: Type[];
+    }
+    interface UnionOrIntersectionType extends Type {
+        types: Type[];
+    }
+    interface UnionType extends UnionOrIntersectionType {
+    }
+    interface IntersectionType extends UnionOrIntersectionType {
+    }
+    interface TypeParameter extends Type {
+        constraint: Type;
+    }
+    const enum SignatureKind {
+        Call = 0,
+        Construct = 1,
+    }
+    interface Signature {
+        declaration: SignatureDeclaration;
+        typeParameters: TypeParameter[];
+        parameters: Symbol[];
+        typePredicate?: TypePredicate;
+    }
+    const enum IndexKind {
+        String = 0,
+        Number = 1,
+    }
+    interface DiagnosticMessage {
+        key: string;
+        category: DiagnosticCategory;
+        code: number;
+    }
+    /**
+     * A linked list of formatted diagnostic messages to be used as part of a multiline message.
+     * It is built from the bottom up, leaving the head to be the "main" diagnostic.
+     * While it seems that DiagnosticMessageChain is structurally similar to DiagnosticMessage,
+     * the difference is that messages are all preformatted in DMC.
+     */
+    interface DiagnosticMessageChain {
+        messageText: string;
+        category: DiagnosticCategory;
+        code: number;
+        next?: DiagnosticMessageChain;
+    }
+    interface Diagnostic {
+        file: SourceFile;
+        start: number;
+        length: number;
+        messageText: string | DiagnosticMessageChain;
+        category: DiagnosticCategory;
+        code: number;
+    }
+    enum DiagnosticCategory {
+        Warning = 0,
+        Error = 1,
+        Message = 2,
+    }
+    const enum ModuleResolutionKind {
+        Classic = 1,
+        NodeJs = 2,
+    }
+    interface CompilerOptions {
+        allowNonTsExtensions?: boolean;
+        charset?: string;
+        declaration?: boolean;
+        diagnostics?: boolean;
+        emitBOM?: boolean;
+        help?: boolean;
+        init?: boolean;
+        inlineSourceMap?: boolean;
+        inlineSources?: boolean;
+        jsx?: JsxEmit;
+        listFiles?: boolean;
+        locale?: string;
+        mapRoot?: string;
+        module?: ModuleKind;
+        newLine?: NewLineKind;
+        noEmit?: boolean;
+        noEmitHelpers?: boolean;
+        noEmitOnError?: boolean;
+        noErrorTruncation?: boolean;
+        noImplicitAny?: boolean;
+        noLib?: boolean;
+        noResolve?: boolean;
+        out?: string;
+        outFile?: string;
+        outDir?: string;
+        preserveConstEnums?: boolean;
+        project?: string;
+        removeComments?: boolean;
+        rootDir?: string;
+        sourceMap?: boolean;
+        sourceRoot?: string;
+        suppressExcessPropertyErrors?: boolean;
+        suppressImplicitAnyIndexErrors?: boolean;
+        target?: ScriptTarget;
+        version?: boolean;
+        watch?: boolean;
+        isolatedModules?: boolean;
+        experimentalDecorators?: boolean;
+        emitDecoratorMetadata?: boolean;
+        moduleResolution?: ModuleResolutionKind;
+        [option: string]: string | number | boolean;
+    }
+    const enum ModuleKind {
+        None = 0,
+        CommonJS = 1,
+        AMD = 2,
+        UMD = 3,
+        System = 4,
+        ES6 = 5,
+        ES2015 = 5,
+    }
+    const enum JsxEmit {
+        None = 0,
+        Preserve = 1,
+        React = 2,
+    }
+    const enum NewLineKind {
+        CarriageReturnLineFeed = 0,
+        LineFeed = 1,
+    }
+    interface LineAndCharacter {
+        line: number;
+        character: number;
+    }
+    const enum ScriptTarget {
+        ES3 = 0,
+        ES5 = 1,
+        ES6 = 2,
+        ES2015 = 2,
+        Latest = 2,
+    }
+    const enum LanguageVariant {
+        Standard = 0,
+        JSX = 1,
+    }
+    interface ParsedCommandLine {
+        options: CompilerOptions;
+        fileNames: string[];
+        errors: Diagnostic[];
+    }
+    interface ModuleResolutionHost {
+        fileExists(fileName: string): boolean;
+        readFile(fileName: string): string;
+    }
+    interface ResolvedModule {
+        resolvedFileName: string;
+        isExternalLibraryImport?: boolean;
+    }
+    interface ResolvedModuleWithFailedLookupLocations {
+        resolvedModule: ResolvedModule;
+        failedLookupLocations: string[];
+    }
+    interface CompilerHost extends ModuleResolutionHost {
+        getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
+        getCancellationToken?(): CancellationToken;
+        getDefaultLibFileName(options: CompilerOptions): string;
+        writeFile: WriteFileCallback;
+        getCurrentDirectory(): string;
+        getCanonicalFileName(fileName: string): string;
+        useCaseSensitiveFileNames(): boolean;
+        getNewLine(): string;
+        resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
+    }
+    interface TextSpan {
+        start: number;
+        length: number;
+    }
+    interface TextChangeRange {
+        span: TextSpan;
+        newLength: number;
+    }
+}
+declare namespace ts {
+    interface System {
+        args: string[];
+        newLine: string;
+        useCaseSensitiveFileNames: boolean;
+        write(s: string): void;
+        readFile(path: string, encoding?: string): string;
+        writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
+        watchFile?(path: string, callback: (path: string, removed?: boolean) => void): FileWatcher;
+        watchDirectory?(path: string, callback: (path: string) => void, recursive?: boolean): FileWatcher;
+        resolvePath(path: string): string;
+        fileExists(path: string): boolean;
+        directoryExists(path: string): boolean;
+        createDirectory(path: string): void;
+        getExecutingFilePath(): string;
+        getCurrentDirectory(): string;
+        readDirectory(path: string, extension?: string, exclude?: string[]): string[];
+        getMemoryUsage?(): number;
+        exit(exitCode?: number): void;
+    }
+    interface FileWatcher {
+        close(): void;
+    }
+    var sys: System;
+}
+declare namespace ts {
+    interface ErrorCallback {
+        (message: DiagnosticMessage, length: number): void;
+    }
+    interface Scanner {
+        getStartPos(): number;
+        getToken(): SyntaxKind;
+        getTextPos(): number;
+        getTokenPos(): number;
+        getTokenText(): string;
+        getTokenValue(): string;
+        hasExtendedUnicodeEscape(): boolean;
+        hasPrecedingLineBreak(): boolean;
+        isIdentifier(): boolean;
+        isReservedWord(): boolean;
+        isUnterminated(): boolean;
+        reScanGreaterToken(): SyntaxKind;
+        reScanSlashToken(): SyntaxKind;
+        reScanTemplateToken(): SyntaxKind;
+        scanJsxIdentifier(): SyntaxKind;
+        reScanJsxToken(): SyntaxKind;
+        scanJsxToken(): SyntaxKind;
+        scan(): SyntaxKind;
+        setText(text: string, start?: number, length?: number): void;
+        setOnError(onError: ErrorCallback): void;
+        setScriptTarget(scriptTarget: ScriptTarget): void;
+        setLanguageVariant(variant: LanguageVariant): void;
+        setTextPos(textPos: number): void;
+        lookAhead<T>(callback: () => T): T;
+        tryScan<T>(callback: () => T): T;
+    }
+    function tokenToString(t: SyntaxKind): string;
+    function getPositionOfLineAndCharacter(sourceFile: SourceFile, line: number, character: number): number;
+    function getLineAndCharacterOfPosition(sourceFile: SourceFile, position: number): LineAndCharacter;
+    function isWhiteSpace(ch: number): boolean;
+    function isLineBreak(ch: number): boolean;
+    function couldStartTrivia(text: string, pos: number): boolean;
+    function getLeadingCommentRanges(text: string, pos: number): CommentRange[];
+    function getTrailingCommentRanges(text: string, pos: number): CommentRange[];
+    /** Optionally, get the shebang */
+    function getShebang(text: string): string;
+    function isIdentifierStart(ch: number, languageVersion: ScriptTarget): boolean;
+    function isIdentifierPart(ch: number, languageVersion: ScriptTarget): boolean;
+    function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean, languageVariant?: LanguageVariant, text?: string, onError?: ErrorCallback, start?: number, length?: number): Scanner;
+}
+declare namespace ts {
+    function getDefaultLibFileName(options: CompilerOptions): string;
+    function textSpanEnd(span: TextSpan): number;
+    function textSpanIsEmpty(span: TextSpan): boolean;
+    function textSpanContainsPosition(span: TextSpan, position: number): boolean;
+    function textSpanContainsTextSpan(span: TextSpan, other: TextSpan): boolean;
+    function textSpanOverlapsWith(span: TextSpan, other: TextSpan): boolean;
+    function textSpanOverlap(span1: TextSpan, span2: TextSpan): TextSpan;
+    function textSpanIntersectsWithTextSpan(span: TextSpan, other: TextSpan): boolean;
+    function textSpanIntersectsWith(span: TextSpan, start: number, length: number): boolean;
+    function decodedTextSpanIntersectsWith(start1: number, length1: number, start2: number, length2: number): boolean;
+    function textSpanIntersectsWithPosition(span: TextSpan, position: number): boolean;
+    function textSpanIntersection(span1: TextSpan, span2: TextSpan): TextSpan;
+    function createTextSpan(start: number, length: number): TextSpan;
+    function createTextSpanFromBounds(start: number, end: number): TextSpan;
+    function textChangeRangeNewSpan(range: TextChangeRange): TextSpan;
+    function textChangeRangeIsUnchanged(range: TextChangeRange): boolean;
+    function createTextChangeRange(span: TextSpan, newLength: number): TextChangeRange;
+    let unchangedTextChangeRange: TextChangeRange;
+    /**
+     * Called to merge all the changes that occurred across several versions of a script snapshot
+     * into a single change.  i.e. if a user keeps making successive edits to a script we will
+     * have a text change from V1 to V2, V2 to V3, ..., Vn.
+     *
+     * This function will then merge those changes into a single change range valid between V1 and
+     * Vn.
+     */
+    function collapseTextChangeRangesAcrossMultipleVersions(changes: TextChangeRange[]): TextChangeRange;
+    function getTypeParameterOwner(d: Declaration): Declaration;
+    function arrayStructurallyIsEqualTo<T>(array1: Array<T>, array2: Array<T>): boolean;
+}
+declare namespace ts {
+    function getNodeConstructor(kind: SyntaxKind): new () => Node;
+    function createNode(kind: SyntaxKind): Node;
+    function forEachChild<T>(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T;
+    function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes?: boolean): SourceFile;
+    function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile;
+}
+declare namespace ts {
+    const version: string;
+    function findConfigFile(searchPath: string): string;
+    function resolveTripleslashReference(moduleName: string, containingFile: string): string;
+    function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations;
+    function nodeModuleNameResolver(moduleName: string, containingFile: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations;
+    function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations;
+    function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost;
+    function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
+    function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string;
+    function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program;
+}
+declare namespace ts {
+    function parseCommandLine(commandLine: string[], readFile?: (path: string) => string): ParsedCommandLine;
+    /**
+      * Read tsconfig.json file
+      * @param fileName The path to the config file
+      */
+    function readConfigFile(fileName: string, readFile: (path: string) => string): {
+        config?: any;
+        error?: Diagnostic;
+    };
+    /**
+      * Parse the text of the tsconfig.json file
+      * @param fileName The path to the config file
+      * @param jsonText The text of the config file
+      */
+    function parseConfigFileTextToJson(fileName: string, jsonText: string): {
+        config?: any;
+        error?: Diagnostic;
+    };
+    /**
+      * Parse the contents of a config file (tsconfig.json).
+      * @param json The contents of the config file to parse
+      * @param basePath A root directory to resolve relative path entries in the config
+      *    file to. e.g. outDir
+      */
+    function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine;
+}
+declare namespace ts {
+    /** The version of the language service API */
+    let servicesVersion: string;
+    interface Node {
+        getSourceFile(): SourceFile;
+        getChildCount(sourceFile?: SourceFile): number;
+        getChildAt(index: number, sourceFile?: SourceFile): Node;
+        getChildren(sourceFile?: SourceFile): Node[];
+        getStart(sourceFile?: SourceFile): number;
+        getFullStart(): number;
+        getEnd(): number;
+        getWidth(sourceFile?: SourceFile): number;
+        getFullWidth(): number;
+        getLeadingTriviaWidth(sourceFile?: SourceFile): number;
+        getFullText(sourceFile?: SourceFile): string;
+        getText(sourceFile?: SourceFile): string;
+        getFirstToken(sourceFile?: SourceFile): Node;
+        getLastToken(sourceFile?: SourceFile): Node;
+    }
+    interface Symbol {
+        getFlags(): SymbolFlags;
+        getName(): string;
+        getDeclarations(): Declaration[];
+        getDocumentationComment(): SymbolDisplayPart[];
+    }
+    interface Type {
+        getFlags(): TypeFlags;
+        getSymbol(): Symbol;
+        getProperties(): Symbol[];
+        getProperty(propertyName: string): Symbol;
+        getApparentProperties(): Symbol[];
+        getCallSignatures(): Signature[];
+        getConstructSignatures(): Signature[];
+        getStringIndexType(): Type;
+        getNumberIndexType(): Type;
+        getBaseTypes(): ObjectType[];
+    }
+    interface Signature {
+        getDeclaration(): SignatureDeclaration;
+        getTypeParameters(): Type[];
+        getParameters(): Symbol[];
+        getReturnType(): Type;
+        getDocumentationComment(): SymbolDisplayPart[];
+    }
+    interface SourceFile {
+        getLineAndCharacterOfPosition(pos: number): LineAndCharacter;
+        getLineStarts(): number[];
+        getPositionOfLineAndCharacter(line: number, character: number): number;
+        update(newText: string, textChangeRange: TextChangeRange): SourceFile;
+    }
+    /**
+     * Represents an immutable snapshot of a script at a specified time.Once acquired, the
+     * snapshot is observably immutable. i.e. the same calls with the same parameters will return
+     * the same values.
+     */
+    interface IScriptSnapshot {
+        /** Gets a portion of the script snapshot specified by [start, end). */
+        getText(start: number, end: number): string;
+        /** Gets the length of this script snapshot. */
+        getLength(): number;
+        /**
+         * Gets the TextChangeRange that describe how the text changed between this text and
+         * an older version.  This information is used by the incremental parser to determine
+         * what sections of the script need to be re-parsed.  'undefined' can be returned if the
+         * change range cannot be determined.  However, in that case, incremental parsing will
+         * not happen and the entire document will be re - parsed.
+         */
+        getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange;
+        /** Releases all resources held by this script snapshot */
+        dispose?(): void;
+    }
+    module ScriptSnapshot {
+        function fromString(text: string): IScriptSnapshot;
+    }
+    interface PreProcessedFileInfo {
+        referencedFiles: FileReference[];
+        importedFiles: FileReference[];
+        ambientExternalModules: string[];
+        isLibFile: boolean;
+    }
+    interface HostCancellationToken {
+        isCancellationRequested(): boolean;
+    }
+    interface LanguageServiceHost {
+        getCompilationSettings(): CompilerOptions;
+        getNewLine?(): string;
+        getProjectVersion?(): string;
+        getScriptFileNames(): string[];
+        getScriptVersion(fileName: string): string;
+        getScriptSnapshot(fileName: string): IScriptSnapshot;
+        getLocalizedDiagnosticMessages?(): any;
+        getCancellationToken?(): HostCancellationToken;
+        getCurrentDirectory(): string;
+        getDefaultLibFileName(options: CompilerOptions): string;
+        log?(s: string): void;
+        trace?(s: string): void;
+        error?(s: string): void;
+        useCaseSensitiveFileNames?(): boolean;
+        resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
+    }
+    interface LanguageService {
+        cleanupSemanticCache(): void;
+        getSyntacticDiagnostics(fileName: string): Diagnostic[];
+        getSemanticDiagnostics(fileName: string): Diagnostic[];
+        getCompilerOptionsDiagnostics(): Diagnostic[];
+        /**
+         * @deprecated Use getEncodedSyntacticClassifications instead.
+         */
+        getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
+        /**
+         * @deprecated Use getEncodedSemanticClassifications instead.
+         */
+        getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
+        getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications;
+        getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications;
+        getCompletionsAtPosition(fileName: string, position: number): CompletionInfo;
+        getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails;
+        getQuickInfoAtPosition(fileName: string, position: number): QuickInfo;
+        getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan;
+        getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan;
+        getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems;
+        getRenameInfo(fileName: string, position: number): RenameInfo;
+        findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[];
+        getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
+        getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
+        getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
+        findReferences(fileName: string, position: number): ReferencedSymbol[];
+        getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[];
+        /** @deprecated */
+        getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
+        getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
+        getNavigationBarItems(fileName: string): NavigationBarItem[];
+        getOutliningSpans(fileName: string): OutliningSpan[];
+        getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
+        getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[];
+        getIndentationAtPosition(fileName: string, position: number, options: EditorOptions): number;
+        getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[];
+        getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): TextChange[];
+        getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[];
+        getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion;
+        getEmitOutput(fileName: string): EmitOutput;
+        getProgram(): Program;
+        getSourceFile(fileName: string): SourceFile;
+        dispose(): void;
+    }
+    interface Classifications {
+        spans: number[];
+        endOfLineState: EndOfLineState;
+    }
+    interface ClassifiedSpan {
+        textSpan: TextSpan;
+        classificationType: string;
+    }
+    interface NavigationBarItem {
+        text: string;
+        kind: string;
+        kindModifiers: string;
+        spans: TextSpan[];
+        childItems: NavigationBarItem[];
+        indent: number;
+        bolded: boolean;
+        grayed: boolean;
+    }
+    interface TodoCommentDescriptor {
+        text: string;
+        priority: number;
+    }
+    interface TodoComment {
+        descriptor: TodoCommentDescriptor;
+        message: string;
+        position: number;
+    }
+    class TextChange {
+        span: TextSpan;
+        newText: string;
+    }
+    interface TextInsertion {
+        newText: string;
+        /** The position in newText the caret should point to after the insertion. */
+        caretOffset: number;
+    }
+    interface RenameLocation {
+        textSpan: TextSpan;
+        fileName: string;
+    }
+    interface ReferenceEntry {
+        textSpan: TextSpan;
+        fileName: string;
+        isWriteAccess: boolean;
+    }
+    interface DocumentHighlights {
+        fileName: string;
+        highlightSpans: HighlightSpan[];
+    }
+    module HighlightSpanKind {
+        const none: string;
+        const definition: string;
+        const reference: string;
+        const writtenReference: string;
+    }
+    interface HighlightSpan {
+        fileName?: string;
+        textSpan: TextSpan;
+        kind: string;
+    }
+    interface NavigateToItem {
+        name: string;
+        kind: string;
+        kindModifiers: string;
+        matchKind: string;
+        isCaseSensitive: boolean;
+        fileName: string;
+        textSpan: TextSpan;
+        containerName: string;
+        containerKind: string;
+    }
+    interface EditorOptions {
+        IndentSize: number;
+        TabSize: number;
+        NewLineCharacter: string;
+        ConvertTabsToSpaces: boolean;
+        IndentStyle: IndentStyle;
+    }
+    enum IndentStyle {
+        None = 0,
+        Block = 1,
+        Smart = 2,
+    }
+    interface FormatCodeOptions extends EditorOptions {
+        InsertSpaceAfterCommaDelimiter: boolean;
+        InsertSpaceAfterSemicolonInForStatements: boolean;
+        InsertSpaceBeforeAndAfterBinaryOperators: boolean;
+        InsertSpaceAfterKeywordsInControlFlowStatements: boolean;
+        InsertSpaceAfterFunctionKeywordForAnonymousFunctions: boolean;
+        InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean;
+        InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: boolean;
+        PlaceOpenBraceOnNewLineForFunctions: boolean;
+        PlaceOpenBraceOnNewLineForControlBlocks: boolean;
+        [s: string]: boolean | number | string;
+    }
+    interface DefinitionInfo {
+        fileName: string;
+        textSpan: TextSpan;
+        kind: string;
+        name: string;
+        containerKind: string;
+        containerName: string;
+    }
+    interface ReferencedSymbol {
+        definition: DefinitionInfo;
+        references: ReferenceEntry[];
+    }
+    enum SymbolDisplayPartKind {
+        aliasName = 0,
+        className = 1,
+        enumName = 2,
+        fieldName = 3,
+        interfaceName = 4,
+        keyword = 5,
+        lineBreak = 6,
+        numericLiteral = 7,
+        stringLiteral = 8,
+        localName = 9,
+        methodName = 10,
+        moduleName = 11,
+        operator = 12,
+        parameterName = 13,
+        propertyName = 14,
+        punctuation = 15,
+        space = 16,
+        text = 17,
+        typeParameterName = 18,
+        enumMemberName = 19,
+        functionName = 20,
+        regularExpressionLiteral = 21,
+    }
+    interface SymbolDisplayPart {
+        text: string;
+        kind: string;
+    }
+    interface QuickInfo {
+        kind: string;
+        kindModifiers: string;
+        textSpan: TextSpan;
+        displayParts: SymbolDisplayPart[];
+        documentation: SymbolDisplayPart[];
+    }
+    interface RenameInfo {
+        canRename: boolean;
+        localizedErrorMessage: string;
+        displayName: string;
+        fullDisplayName: string;
+        kind: string;
+        kindModifiers: string;
+        triggerSpan: TextSpan;
+    }
+    interface SignatureHelpParameter {
+        name: string;
+        documentation: SymbolDisplayPart[];
+        displayParts: SymbolDisplayPart[];
+        isOptional: boolean;
+    }
+    /**
+     * Represents a single signature to show in signature help.
+     * The id is used for subsequent calls into the language service to ask questions about the
+     * signature help item in the context of any documents that have been updated.  i.e. after
+     * an edit has happened, while signature help is still active, the host can ask important
+     * questions like 'what parameter is the user currently contained within?'.
+     */
+    interface SignatureHelpItem {
+        isVariadic: boolean;
+        prefixDisplayParts: SymbolDisplayPart[];
+        suffixDisplayParts: SymbolDisplayPart[];
+        separatorDisplayParts: SymbolDisplayPart[];
+        parameters: SignatureHelpParameter[];
+        documentation: SymbolDisplayPart[];
+    }
+    /**
+     * Represents a set of signature help items, and the preferred item that should be selected.
+     */
+    interface SignatureHelpItems {
+        items: SignatureHelpItem[];
+        applicableSpan: TextSpan;
+        selectedItemIndex: number;
+        argumentIndex: number;
+        argumentCount: number;
+    }
+    interface CompletionInfo {
+        isMemberCompletion: boolean;
+        isNewIdentifierLocation: boolean;
+        entries: CompletionEntry[];
+    }
+    interface CompletionEntry {
+        name: string;
+        kind: string;
+        kindModifiers: string;
+        sortText: string;
+    }
+    interface CompletionEntryDetails {
+        name: string;
+        kind: string;
+        kindModifiers: string;
+        displayParts: SymbolDisplayPart[];
+        documentation: SymbolDisplayPart[];
+    }
+    interface OutliningSpan {
+        /** The span of the document to actually collapse. */
+        textSpan: TextSpan;
+        /** The span of the document to display when the user hovers over the collapsed span. */
+        hintSpan: TextSpan;
+        /** The text to display in the editor for the collapsed region. */
+        bannerText: string;
+        /**
+          * Whether or not this region should be automatically collapsed when
+          * the 'Collapse to Definitions' command is invoked.
+          */
+        autoCollapse: boolean;
+    }
+    interface EmitOutput {
+        outputFiles: OutputFile[];
+        emitSkipped: boolean;
+    }
+    const enum OutputFileType {
+        JavaScript = 0,
+        SourceMap = 1,
+        Declaration = 2,
+    }
+    interface OutputFile {
+        name: string;
+        writeByteOrderMark: boolean;
+        text: string;
+    }
+    const enum EndOfLineState {
+        None = 0,
+        InMultiLineCommentTrivia = 1,
+        InSingleQuoteStringLiteral = 2,
+        InDoubleQuoteStringLiteral = 3,
+        InTemplateHeadOrNoSubstitutionTemplate = 4,
+        InTemplateMiddleOrTail = 5,
+        InTemplateSubstitutionPosition = 6,
+    }
+    enum TokenClass {
+        Punctuation = 0,
+        Keyword = 1,
+        Operator = 2,
+        Comment = 3,
+        Whitespace = 4,
+        Identifier = 5,
+        NumberLiteral = 6,
+        StringLiteral = 7,
+        RegExpLiteral = 8,
+    }
+    interface ClassificationResult {
+        finalLexState: EndOfLineState;
+        entries: ClassificationInfo[];
+    }
+    interface ClassificationInfo {
+        length: number;
+        classification: TokenClass;
+    }
+    interface Classifier {
+        /**
+         * Gives lexical classifications of tokens on a line without any syntactic context.
+         * For instance, a token consisting of the text 'string' can be either an identifier
+         * named 'string' or the keyword 'string', however, because this classifier is not aware,
+         * it relies on certain heuristics to give acceptable results. For classifications where
+         * speed trumps accuracy, this function is preferable; however, for true accuracy, the
+         * syntactic classifier is ideal. In fact, in certain editing scenarios, combining the
+         * lexical, syntactic, and semantic classifiers may issue the best user experience.
+         *
+         * @param text                      The text of a line to classify.
+         * @param lexState                  The state of the lexical classifier at the end of the previous line.
+         * @param syntacticClassifierAbsent Whether the client is *not* using a syntactic classifier.
+         *                                  If there is no syntactic classifier (syntacticClassifierAbsent=true),
+         *                                  certain heuristics may be used in its place; however, if there is a
+         *                                  syntactic classifier (syntacticClassifierAbsent=false), certain
+         *                                  classifications which may be incorrectly categorized will be given
+         *                                  back as Identifiers in order to allow the syntactic classifier to
+         *                                  subsume the classification.
+         * @deprecated Use getLexicalClassifications instead.
+         */
+        getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent: boolean): ClassificationResult;
+        getEncodedLexicalClassifications(text: string, endOfLineState: EndOfLineState, syntacticClassifierAbsent: boolean): Classifications;
+    }
+    /**
+      * The document registry represents a store of SourceFile objects that can be shared between
+      * multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
+      * of files in the context.
+      * SourceFile objects account for most of the memory usage by the language service. Sharing
+      * the same DocumentRegistry instance between different instances of LanguageService allow
+      * for more efficient memory utilization since all projects will share at least the library
+      * file (lib.d.ts).
+      *
+      * A more advanced use of the document registry is to serialize sourceFile objects to disk
+      * and re-hydrate them when needed.
+      *
+      * To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
+      * to all subsequent createLanguageService calls.
+      */
+    interface DocumentRegistry {
+        /**
+          * Request a stored SourceFile with a given fileName and compilationSettings.
+          * The first call to acquire will call createLanguageServiceSourceFile to generate
+          * the SourceFile if was not found in the registry.
+          *
+          * @param fileName The name of the file requested
+          * @param compilationSettings Some compilation settings like target affects the
+          * shape of a the resulting SourceFile. This allows the DocumentRegistry to store
+          * multiple copies of the same file for different compilation settings.
+          * @parm scriptSnapshot Text of the file. Only used if the file was not found
+          * in the registry and a new one was created.
+          * @parm version Current version of the file. Only used if the file was not found
+          * in the registry and a new one was created.
+          */
+        acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile;
+        /**
+          * Request an updated version of an already existing SourceFile with a given fileName
+          * and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile
+          * to get an updated SourceFile.
+          *
+          * @param fileName The name of the file requested
+          * @param compilationSettings Some compilation settings like target affects the
+          * shape of a the resulting SourceFile. This allows the DocumentRegistry to store
+          * multiple copies of the same file for different compilation settings.
+          * @param scriptSnapshot Text of the file.
+          * @param version Current version of the file.
+          */
+        updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile;
+        /**
+          * Informs the DocumentRegistry that a file is not needed any longer.
+          *
+          * Note: It is not allowed to call release on a SourceFile that was not acquired from
+          * this registry originally.
+          *
+          * @param fileName The name of the file to be released
+          * @param compilationSettings The compilation settings used to acquire the file
+          */
+        releaseDocument(fileName: string, compilationSettings: CompilerOptions): void;
+        reportStats(): string;
+    }
+    module ScriptElementKind {
+        const unknown: string;
+        const warning: string;
+        const keyword: string;
+        const scriptElement: string;
+        const moduleElement: string;
+        const classElement: string;
+        const localClassElement: string;
+        const interfaceElement: string;
+        const typeElement: string;
+        const enumElement: string;
+        const variableElement: string;
+        const localVariableElement: string;
+        const functionElement: string;
+        const localFunctionElement: string;
+        const memberFunctionElement: string;
+        const memberGetAccessorElement: string;
+        const memberSetAccessorElement: string;
+        const memberVariableElement: string;
+        const constructorImplementationElement: string;
+        const callSignatureElement: string;
+        const indexSignatureElement: string;
+        const constructSignatureElement: string;
+        const parameterElement: string;
+        const typeParameterElement: string;
+        const primitiveType: string;
+        const label: string;
+        const alias: string;
+        const constElement: string;
+        const letElement: string;
+    }
+    module ScriptElementKindModifier {
+        const none: string;
+        const publicMemberModifier: string;
+        const privateMemberModifier: string;
+        const protectedMemberModifier: string;
+        const exportedModifier: string;
+        const ambientModifier: string;
+        const staticModifier: string;
+        const abstractModifier: string;
+    }
+    class ClassificationTypeNames {
+        static comment: string;
+        static identifier: string;
+        static keyword: string;
+        static numericLiteral: string;
+        static operator: string;
+        static stringLiteral: string;
+        static whiteSpace: string;
+        static text: string;
+        static punctuation: string;
+        static className: string;
+        static enumName: string;
+        static interfaceName: string;
+        static moduleName: string;
+        static typeParameterName: string;
+        static typeAliasName: string;
+        static parameterName: string;
+        static docCommentTagName: string;
+    }
+    const enum ClassificationType {
+        comment = 1,
+        identifier = 2,
+        keyword = 3,
+        numericLiteral = 4,
+        operator = 5,
+        stringLiteral = 6,
+        regularExpressionLiteral = 7,
+        whiteSpace = 8,
+        text = 9,
+        punctuation = 10,
+        className = 11,
+        enumName = 12,
+        interfaceName = 13,
+        moduleName = 14,
+        typeParameterName = 15,
+        typeAliasName = 16,
+        parameterName = 17,
+        docCommentTagName = 18,
+    }
+    interface DisplayPartsSymbolWriter extends SymbolWriter {
+        displayParts(): SymbolDisplayPart[];
+    }
+    function displayPartsToString(displayParts: SymbolDisplayPart[]): string;
+    function getDefaultCompilerOptions(): CompilerOptions;
+    interface TranspileOptions {
+        compilerOptions?: CompilerOptions;
+        fileName?: string;
+        reportDiagnostics?: boolean;
+        moduleName?: string;
+        renamedDependencies?: Map<string>;
+    }
+    interface TranspileOutput {
+        outputText: string;
+        diagnostics?: Diagnostic[];
+        sourceMapText?: string;
+    }
+    function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput;
+    function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string;
+    function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean): SourceFile;
+    let disableIncrementalParsing: boolean;
+    function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile;
+    function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string;
+    function createDocumentRegistry(useCaseSensitiveFileNames?: boolean): DocumentRegistry;
+    function preProcessFile(sourceText: string, readImportFiles?: boolean): PreProcessedFileInfo;
+    function createLanguageService(host: LanguageServiceHost, documentRegistry?: DocumentRegistry): LanguageService;
+    function createClassifier(): Classifier;
+    /**
+      * Get the path of the default library files (lib.d.ts) as distributed with the typescript
+      * node package.
+      * The functionality is not supported if the ts module is consumed outside of a node module.
+      */
+    function getDefaultLibFilePath(options: CompilerOptions): string;
+}
+
+export = ts;

+ 26 - 4
Script/AtomicWebViewEditor/tsconfig.json

@@ -6,20 +6,42 @@
         "noImplicitAny": false,
         "removeComments": true,
         "noLib": false,
-        "outFile": "../../Data/AtomicEditor/CodeEditor/editor.bundle.js",
+        "outDir": "../../Data/AtomicEditor/CodeEditor/source/editorCore",
         "sourceMap": true
     },
     "filesGlob": [
-        "./**/*.ts"
+        "./**/*.ts",
+        "../TypeScript/*.ts"
     ],
     "atom": {
         "rewriteTsconfig": true
     },
     "files": [
+        "./clientExtensions/ClientExtensionEventNames.ts",
+        "./clientExtensions/ClientExtensionServices.ts",
+        "./clientExtensions/languageExtensions/javascript/JavascriptLanguageExtension.ts",
+        "./clientExtensions/languageExtensions/typescript/TypescriptLanguageExtension.ts",
+        "./clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageService.ts",
+        "./clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageServiceWebWorker.ts",
+        "./clientExtensions/languageExtensions/typescript/workerprocess/workerLoader.ts",
+        "./clientExtensions/languageExtensions/typescript/workerprocess/workerProcessTypes.ts",
+        "./clientExtensions/ServiceLocator.ts",
         "./editor/editor.ts",
-        "./editor/editorConfig.ts",
+        "./editor/editorCommands.ts",
         "./interop.ts",
+        "./modules/typescript.d.ts",
         "./typings/ace.d.ts",
-        "./typings/AtomicQuery.d.ts"
+        "./typings/systemjs.d.ts",
+        "./typings/webworkers.d.ts",
+        "./typings/WindowExt.d.ts",
+        "../TypeScript/Atomic.d.ts",
+        "../TypeScript/AtomicNET.d.ts",
+        "../TypeScript/AtomicPlayer.d.ts",
+        "../TypeScript/AtomicWork.d.ts",
+        "../TypeScript/duktape.d.ts",
+        "../TypeScript/Editor.d.ts",
+        "../TypeScript/EditorWork.d.ts",
+        "../TypeScript/ToolCore.d.ts",
+        "../TypeScript/WebView.d.ts"
     ]
 }

+ 0 - 5
Script/AtomicWebViewEditor/typings/AtomicQuery.d.ts

@@ -1,5 +0,0 @@
-interface Window {
-    atomicQuery: any;
-    loadCode: (codeUrl) => void;
-    saveCode: () => void;
-}

+ 34 - 0
Script/AtomicWebViewEditor/typings/WindowExt.d.ts

@@ -0,0 +1,34 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+/**
+ * Defines the interface to what is available for the host to call or for the client to call on the window object
+ */
+interface Window {
+    atomicQuery: any;
+    HOST_loadCode: (codeUrl) => void;
+    HOST_saveCode: () => void;
+
+    HOST_projectUnloaded: () => void;
+    HOST_resourceRenamed: (path: string, newPath: string) => void;
+    HOST_resourceDeleted: (path: string) => void;
+}

+ 21 - 0
Script/AtomicWebViewEditor/typings/systemjs.d.ts

@@ -0,0 +1,21 @@
+// Type definitions for System.js 0.18.4
+// Project: https://github.com/systemjs/systemjs
+// Definitions by: Ludovic HENIN <https://github.com/ludohenin/>, Nathan Walker <https://github.com/NathanWalker/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+interface System {
+  import(name: string): any;
+  defined: any;
+  amdDefine: () => void;
+  amdRequire: () => void;
+  baseURL: string;
+  paths: { [key: string]: string };
+  meta: { [key: string]: Object };
+  config: any;
+}
+
+declare var System: System;
+
+declare module "systemjs" {
+  export = System;
+}

+ 29 - 0
Script/AtomicWebViewEditor/typings/webworkers.d.ts

@@ -0,0 +1,29 @@
+// Type definitions for SharedWorker
+// Project: http://www.w3.org/TR/workers/
+// Definitions by: Toshiya Nakakura <https://github.com/nakakura>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+declare module SharedWorker {
+    interface AbstractWorker extends EventTarget {
+        onerror: (ev: ErrorEvent) => any;
+    }
+
+    export interface SharedWorker extends AbstractWorker {
+        /**
+         * the value it was assigned by the object's constructor.
+         * It represents the MessagePort for communicating with the shared worker.
+         * @type {MessagePort}
+         */
+        port: MessagePort;
+    }
+}
+
+declare var SharedWorker: {
+    prototype: SharedWorker.SharedWorker;
+    /***
+     *
+     * @param {string} stringUrl    Pathname to JavaScript file
+     * @param {string} name         Name of the worker to execute
+     */
+    new (stringUrl: string, name?: string): SharedWorker.SharedWorker;
+};

+ 2 - 2
Script/Packages/Atomic/Core.json

@@ -19,8 +19,8 @@
 
 		"Object" : [
 			"sendEvent(eventType:string, data?:Object);",
-			"subscribeToEvent(eventType:string, callback:(data:any)=>void);",
-			"subscribeToEvent(sender:AObject, eventType:string, callback:(data:any)=>void);"
+			"subscribeToEvent(eventType:string, callback:(data:any) => void);",
+			"subscribeToEvent(sender:AObject, eventType:string, callback:(data: any) => void);"
 		]
 	},
 	"haxe_decl" : {

+ 9 - 2
Script/Packages/Atomic/Scene.json

@@ -39,14 +39,21 @@
 			"getChildrenWithName(name:string, recursive?:boolean):Node[];",
 			"getChildrenWithComponent(componentType:string, recursive?:boolean):Node[];",
 			"getComponents(componentType?:string, recursive?:boolean):Component[];",
+			"getComponent<T extends Atomic.Component>(type: string): T;",
 			"getChildAtIndex(index:number):Node;",
 			"createJSComponent(name:string, args?:{});",
-			"getJSComponent(name:string, recursive?:boolean):JSComponent;",
+			"getJSComponent(name:string, recursive?:boolean): JSComponent;",
+			"getJSComponent<T extends Atomic.JSComponent>(name:string, recursive?:boolean): T;",
 			"createChildPrefab(childName:string, prefabPath:string):Node;",
-			"loadPrefab(prefabPath:string):boolean;"
+			"loadPrefab(prefabPath:string):boolean;",
+			"createComponent<T extends Atomic.Component>(type: string, mode?: CreateMode, id?: number): T;",
+			"getOrCreateComponent<T extends Atomic.Component>(type: string, mode?: CreateMode, id?: number): T;"
 		],
 		"Scene" : [
 			"getMainCamera():Camera;"
+		],
+		"Component": [
+		    "getComponent<T extends Atomic.Component>(type: string): T;"
 		]
 	},
 	"haxe_decl" : {

+ 1 - 1
Script/TypeScript/AtomicWork.d.ts

@@ -2,7 +2,7 @@
 /// <reference path="ToolCore.d.ts" />
 /// <reference path="Editor.d.ts" />
 /// <reference path="AtomicPlayer.d.ts" />
-
+/// <reference path="AtomicNET.d.ts" />
 
 declare module Atomic {
 

+ 339 - 0
Script/TypeScript/EditorWork.d.ts

@@ -0,0 +1,339 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+/// <reference path="Atomic.d.ts" />
+/// <reference path="Editor.d.ts" />
+
+declare module Editor.EditorEvents {
+
+    export interface ModalErrorEvent {
+
+        title: string;
+        message: string;
+
+    }
+
+    export interface PlayerLogEvent {
+
+        message: string;
+        level: number;
+
+    }
+
+    export interface ActiveSceneEditorChangeEvent {
+
+        sceneEditor: Editor.SceneEditor3D;
+
+    }
+
+    export interface SceneClosedEvent {
+
+        scene: Atomic.Scene;
+
+    }
+
+    export interface ContentFolderChangedEvent {
+
+        path: string;
+
+    }
+
+    export interface LoadProjectEvent {
+
+        // The full path to the .atomic file
+        path: string;
+
+    }
+
+    /**
+     * Called once the resource has been saved
+     * @type {String}
+     */
+    export interface SaveResourceEvent {
+
+        // The full path to the resource to save / empty or undefined for current
+        path: string;
+
+    }
+
+    export interface LoadResourceEvent {
+
+        // The full path to the resource to load
+        path: string;
+
+    }
+
+    export interface EditorFileEvent {
+        filename: string;
+        fileExt: string;
+        editor: any;
+    }
+
+    export interface CodeLoadedEvent extends EditorFileEvent {
+        code: string;
+    }
+
+    export interface CodeSavedEvent extends EditorFileEvent {
+        code: string;
+    }
+
+    export interface EditorCloseResourceEvent {
+
+        editor: Editor.ResourceEditor;
+        navigateToAvailableResource: boolean;
+
+    }
+
+    export interface EditResourceEvent {
+
+        // The full path to the resource to edit
+        path: string;
+
+    }
+
+    /**
+     * Called once the resource has been deleted
+     * @type {String}
+     */
+    export interface DeleteResourceEvent {
+
+        // The full path to the resource to edit
+        path: string;
+
+    }
+
+    /**
+     * Called once the resource has been renamed
+     * @type {String}
+     */
+    export interface RenameResourceEvent {
+
+        /**
+         * Original path of the resource
+         * @type {string}
+         */
+        path: string;
+
+        /**
+         * New path of the resource
+         * @type {string}
+         */
+        newPath: string;
+
+        /**
+         * New base name of the resource (no path or extension)
+         * @type {string}
+         */
+        newName?: string;
+
+        // the asset being changed
+        asset?: ToolCore.Asset;
+    }
+
+    export interface SceneEditStateChangeEvent {
+
+        serializable: Atomic.Serializable;
+
+    }
+}
+
+declare module Editor.Extensions {
+
+    /**
+     * Base interface for any editor services.
+     */
+    export interface EditorService {
+        /**
+         * Unique name of this service
+         * @type {string}
+         */
+        name: string;
+
+        /**
+         * Description of this service
+         * @type {string}
+         */
+        description: string;
+
+    }
+
+    interface EventDispatcher {
+        /**
+         * Send a custom event.  This can be used by services to publish custom events
+         * @param  {string} eventType
+         * @param  {any} data
+         */
+        sendEvent(eventType: string, data: any);
+
+        /**
+         * Subscribe to an event and provide a callback.  This can be used by services to subscribe to custom events
+         * @param  {string} eventType
+         * @param  {any} callback
+         */
+        subscribeToEvent(eventType, callback);
+    }
+
+    /**
+     * Generic service locator of editor services that may be injected by either a plugin
+     * or by the editor itself.
+     */
+    export interface ServiceLoader extends EventDispatcher {
+        /**
+         * Loads a service into a service registry
+         * @param  {EditorService} service
+         */
+        loadService(service: EditorService): void;
+    }
+
+    /**
+     * Service registry interface for registering services
+     */
+    export interface ServiceRegistry<T extends EditorService> {
+        registeredServices: T[];
+
+        /**
+         * Adds a service to the registered services list for this type of service
+         * @param  {T}      service the service to register
+         */
+        register(service: T);
+        /**
+         * Removes a service from the registered services list for this type of service
+         * @param  {T}      service the service to unregister
+         */
+        unregister(service: T);
+    }
+}
+
+declare module Editor.Modal {
+    export interface ExtensionWindow extends Atomic.UIWindow {
+        hide();
+    }
+}
+
+declare module Editor.HostExtensions {
+
+    /**
+     * Generic service locator of editor services that may be injected by either a plugin
+     * or by the editor itself.
+     */
+    export interface HostServiceLocator extends Editor.Extensions.ServiceLoader {
+        resourceServices: ResourceServiceRegistry;
+        projectServices: ProjectServiceRegistry;
+        uiServices: UIServiceRegistry;
+    }
+
+    export interface HostEditorService extends Editor.Extensions.EditorService {
+        /**
+         * Called by the service locator at load time
+         */
+        initialize(serviceLocator: Editor.Extensions.ServiceLoader);
+    }
+
+    export interface ResourceService extends Editor.Extensions.EditorService {
+        save?(ev: EditorEvents.SaveResourceEvent);
+        delete?(ev: EditorEvents.DeleteResourceEvent);
+        rename?(ev: EditorEvents.RenameResourceEvent);
+    }
+    export interface ResourceServiceRegistry extends Editor.Extensions.ServiceRegistry<ResourceService> { }
+
+    export interface ProjectService extends Editor.Extensions.EditorService {
+        projectUnloaded?();
+        projectLoaded?(ev: EditorEvents.LoadProjectEvent);
+        playerStarted?();
+    }
+    export interface ProjectServiceRegistry extends Editor.Extensions.ServiceRegistry<ProjectService> { }
+
+    export interface UIService extends Editor.Extensions.EditorService {
+        menuItemClicked?(refId: string): boolean;
+    }
+    export interface UIServiceRegistry extends Editor.Extensions.ServiceRegistry<UIService> {
+        createPluginMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource;
+        removePluginMenuItemSource(id: string);
+        showModalWindow(windowText: string, uifilename: string, handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void): Editor.Modal.ExtensionWindow;
+        menuItemClicked(refId: string): boolean;
+    }
+}
+
+/**
+ * Interfaces for client extensions
+ */
+declare module Editor.ClientExtensions {
+
+    /**
+     * Generic service locator of editor services that may be injected by either a plugin
+     * or by the editor itself.
+     */
+    export interface ClientServiceLocator extends Editor.Extensions.ServiceLoader {
+        getHostInterop(): HostInterop;
+    }
+
+    export interface ClientEditorService extends Editor.Extensions.EditorService {
+        /**
+         * Called by the service locator at load time
+         */
+        initialize(serviceLocator: ClientServiceLocator);
+    }
+
+    export interface WebViewService extends Editor.Extensions.EditorService {
+        configureEditor?(ev: EditorEvents.EditorFileEvent);
+        codeLoaded?(ev: EditorEvents.CodeLoadedEvent);
+        save?(ev: EditorEvents.CodeSavedEvent);
+        delete?(ev: EditorEvents.DeleteResourceEvent);
+        rename?(ev: EditorEvents.RenameResourceEvent);
+        projectUnloaded?();
+    }
+
+    export interface AtomicErrorMessage {
+        error_code: number;
+        error_message: string;
+    }
+
+    /**
+     * Interface for functions that are available from the client web view to call on the host
+     */
+    export interface HostInterop {
+
+        /**
+         * Called from the host to notify the client what file to load
+         * @param  {string} codeUrl
+         */
+        loadCode(codeUrl: string);
+
+        /**
+         * Save the contents of the editor
+         * @return {Promise}
+         */
+        saveCode(): PromiseLike<{}>;
+
+        /**
+         * Save the contents of a file as filename
+         * @param  {string} filename
+         * @param  {string} fileContents
+         * @return {Promise}
+         */
+        saveFile(filename: string, fileContents: string): PromiseLike<{}>;
+
+        /**
+         * Queries the host for a particular resource and returns it in a promise
+         * @param  {string} codeUrl
+         * @return {Promise}
+         */
+        getResource(codeUrl: string): PromiseLike<{}>;
+
+        /**
+         * Returns a file resource from the resources directory
+         * @param  {string} filename name and path of file under the project directory or a fully qualified file name
+         * @return {Promise}
+         */
+        getFileResource(filename: string): PromiseLike<{}>;
+
+        /**
+         * Notify the host that the contents of the editor has changed
+         */
+        notifyEditorChange();
+    }
+}

+ 17 - 0
Script/TypeScript/duktape.d.ts

@@ -0,0 +1,17 @@
+// Duktape built-ins
+
+// extracted from lib.d.ts
+declare interface Console {
+    log(message?: any, ...optionalParams: any[]): void;
+}
+
+declare var console: Console;
+
+// Duktape require isn't recognized as a function, but can be used as one
+declare function require(filename: string): any;
+
+declare interface DuktapeModule {
+    modSearch(id: string, require, exports, module);
+}
+
+declare var Duktape: DuktapeModule;

+ 15 - 2
Script/tsconfig.json

@@ -8,7 +8,8 @@
         "noLib": false,
         "outDir": "../Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts",
         "moduleResolution": "classic",
-        "sourceMap": true
+        "sourceMap": true,
+        "experimentalDecorators": true
     },
     "filesGlob": [
         "./ToolCore/**/*.ts",
@@ -24,6 +25,10 @@
         "./AtomicEditor/editor/EditorEvents.ts",
         "./AtomicEditor/editor/EditorLicense.ts",
         "./AtomicEditor/editor/Preferences.ts",
+        "./AtomicEditor/hostExtensions/coreExtensions/ProjectBasedExtensionLoader.ts",
+        "./AtomicEditor/hostExtensions/HostExtensionServices.ts",
+        "./AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts",
+        "./AtomicEditor/hostExtensions/ServiceLocator.ts",
         "./AtomicEditor/main.ts",
         "./AtomicEditor/resources/ProjectTemplates.ts",
         "./AtomicEditor/resources/ResourceOps.ts",
@@ -90,6 +95,14 @@
         "./AtomicEditor/ui/ScriptWidget.ts",
         "./AtomicEditor/ui/Shortcuts.ts",
         "./AtomicEditor/ui/UIEvents.ts",
-        "./TypeScript/AtomicWork.d.ts"
+        "./TypeScript/Atomic.d.ts",
+        "./TypeScript/AtomicNET.d.ts",
+        "./TypeScript/AtomicPlayer.d.ts",
+        "./TypeScript/AtomicWork.d.ts",
+        "./TypeScript/duktape.d.ts",
+        "./TypeScript/Editor.d.ts",
+        "./TypeScript/EditorWork.d.ts",
+        "./TypeScript/ToolCore.d.ts",
+        "./TypeScript/WebView.d.ts"
     ]
 }

+ 0 - 2
Script/tslint.json

@@ -23,9 +23,7 @@
     "no-duplicate-variable": true,
     "no-empty": false,
     "no-eval": true,
-    "no-imports": true,
     "no-string-literal": false,
-    "no-trailing-comma": true,
     "no-trailing-whitespace": true,
     "no-unused-variable": false,
     "no-unreachable": true,

+ 29 - 0
Source/Atomic/IPC/IPC.cpp

@@ -33,6 +33,13 @@
 #include "IPC.h"
 #include "IPCEvents.h"
 
+#if defined(ATOMIC_PLATFORM_WINDOWS)
+
+#include <windows.h>
+#undef PostMessage
+
+#endif
+
 namespace Atomic
 {
 
@@ -40,6 +47,28 @@ IPC::IPC(Context* context) : Object(context),
     workerChannelID_(0)
 {
     SubscribeToEvent(E_BEGINFRAME, HANDLER(IPC, HandleBeginFrame));
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+
+    jobHandle_ = CreateJobObject(NULL, NULL);
+    if (!jobHandle_)
+    {
+        LOGERROR("IPC::IPC - Unable to create IPC job");
+    }
+    else
+    {
+        JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
+
+        // Configure all child processes associated with the job to terminate when main process is closed
+        jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+        if (0 == SetInformationJobObject(jobHandle_, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)))
+        {
+            LOGERROR("IPC::IPC - Unable set job information");
+            jobHandle_ = 0;
+        }
+    }
+
+#endif
 }
 
 IPC::~IPC()

+ 8 - 0
Source/Atomic/IPC/IPC.h

@@ -65,6 +65,10 @@ public:
     void SendEventToBroker(StringHash eventType);
     void SendEventToBroker(StringHash eventType, VariantMap& eventData);
 
+#ifdef ATOMIC_PLATFORM_WINDOWS
+    IPCHandle GetJobHandle() const { return jobHandle_; }
+#endif
+
 private:
 
     // if non-zero we're a worked and this is out broker's channel id
@@ -82,6 +86,10 @@ private:
     // valid on child
     SharedPtr<IPCWorker> worker_;
 
+#ifdef ATOMIC_PLATFORM_WINDOWS
+    IPCHandle jobHandle_;
+#endif
+
 };
 
 }

+ 14 - 0
Source/Atomic/IPC/IPCWindows.cpp

@@ -29,8 +29,11 @@
 #include <string>
 
 #include "../Core/Timer.h"
+#include "../IO/Log.h"
 #include "IPCWindows.h"
 
+#include "IPC.h"
+
 typedef std::wstring IPCWString;
 
 namespace Atomic
@@ -303,6 +306,17 @@ bool IPCProcess::Launch(const String& command, const Vector<String>& args, const
         return false;
     }
 
+    IPC* ipc = GetSubsystem<IPC>();
+    IPCHandle jobHandle = ipc->GetJobHandle();
+
+    if (jobHandle)
+    {
+        if (0 == AssignProcessToJobObject(jobHandle, pi.hProcess))
+        {
+            LOGERROR("IPCProcess::Launch - unable to assign job");
+        }
+    }
+
     pid_ = pi.hProcess;
     ::CloseHandle(pi.hThread);
 

+ 1 - 1
Source/Atomic/IPC/IPCWindows.h

@@ -132,7 +132,7 @@ private:
 
     IPCHandle pid_;
     IPCHandle clientRead_;
-    IPCHandle clientWrite_;
+    IPCHandle clientWrite_;    
 };
 
 }

+ 6 - 0
Source/Atomic/IPC/IPCWorker.cpp

@@ -101,10 +101,16 @@ void IPCWorker::ThreadFunction()
 {
     while (shouldRun_)
     {
+
+// On windows we use a job object to control process lifetime, we don't have a 
+// parent pid (these change and are reused on Windows, so we would need to DuplicateHandle and pass
+// to child on command line
+#ifndef ATOMIC_PLATFORM_WINDOWS
         if (!otherProcess_->IsRunning())
         {
             break;
         }
+#endif
 
         if (!Receive())
         {

+ 21 - 0
Source/Atomic/Resource/ResourceEvents.h

@@ -74,6 +74,27 @@ EVENT(E_RESOURCEBACKGROUNDLOADED, ResourceBackgroundLoaded)
     PARAM(P_SUCCESS, Success);                      // bool
     PARAM(P_RESOURCE, Resource);                    // Resource pointer
 }
+    
+/// Resource was renamed
+EVENT(E_RENAMERESOURCENOTIFICATION, RenameResourceNotification)
+{
+    PARAM(P_RESOURCEPATH, Path);                    // String
+    PARAM(P_NEWRESOURCEPATH, NewPath);              // String
+    PARAM(P_NEWNAME, NewName);                      // String
+    PARAM(P_RESOURCE, Asset);                       // Resource pointer
+}
+
+/// Resource was deleted
+EVENT(E_DELETERESOURCENOTIFICATION, DeleteResourceNotification)
+{
+    PARAM(P_RESOURCEPATH, Path);                    // String
+}
+
+/// Project was unloaded
+EVENT(E_PROJECTUNLOADEDNOTIFICATION, ProjecUnloadedNotification)
+{
+}
+    
 
 /// Language changed.
 EVENT(E_CHANGELANGUAGE, ChangeLanguage)

+ 23 - 0
Source/Atomic/UI/UISelectItem.cpp

@@ -84,6 +84,29 @@ UISelectItemSource::~UISelectItemSource()
 
 }
 
+void UISelectItemSource::RemoveItemWithId(const String& id)
+{
+    tb::TBID test = TBID(id.CString());
+    for (List<SharedPtr<UISelectItem> >::Iterator itr = items_.Begin(); itr != items_.End(); itr++)
+    {
+        if ((*itr)->GetID() == test) {
+            items_.Erase(itr);
+            break;
+        }
+    }
+}
+
+void UISelectItemSource::RemoveItemWithStr(const String& str)
+{
+    for (List<SharedPtr<UISelectItem> >::Iterator itr = items_.Begin(); itr != items_.End(); itr++)
+    {
+        if ((*itr)->GetStr() == str) {
+            items_.Erase(itr);
+            break;
+        }
+    }
+}
+
 TBSelectItemSource *UISelectItemSource::GetTBItemSource()
 {
     // caller's responsibility to clean up

+ 5 - 0
Source/Atomic/UI/UISelectItem.h

@@ -44,6 +44,8 @@ public:
 
     void SetString(const String& str) { str_ = str; }
     void SetID(const String& id);
+    const String& GetStr() { return str_; }
+    tb::TBID GetID() { return id_; }
     void SetSkinImage(const String& skinImage);
     void SetSubSource(UISelectItemSource *subSource);
 
@@ -72,6 +74,9 @@ public:
     virtual ~UISelectItemSource();
 
     void AddItem(UISelectItem* item) { items_.Push(SharedPtr<UISelectItem>(item)); }
+    void RemoveItemWithId(const String& id);
+    void RemoveItemWithStr(const String& str);
+    int GetItemCount() { return items_.Size(); }
 
     void Clear() { items_.Clear(); }
 

+ 2 - 0
Source/AtomicEditor/EditorMode/AEEditorMode.cpp

@@ -77,6 +77,8 @@ void EditorMode::HandleIPCWorkerStarted(StringHash eventType, VariantMap& eventD
 
     startupData["debugHudMode"] = debugHud ? debugHud->GetMode() : (unsigned) 0;
 
+    SendEvent(E_EDITORPLAYREQUEST);
+
     playerBroker_->PostMessage(E_IPCINITIALIZE, startupData);
 
     SendEvent(E_EDITORPLAYERSTARTED);

+ 71 - 6
Source/AtomicEditor/Editors/JSResourceEditor.cpp

@@ -27,6 +27,7 @@
 #include <Atomic/IO/FileSystem.h>
 #include <Atomic/Resource/ResourceCache.h>
 #include <Atomic/Resource/JSONFile.h>
+#include <Atomic/Resource/ResourceEvents.h>
 
 #include <Atomic/Core/CoreEvents.h>
 #include <AtomicJS/Javascript/JSVM.h>
@@ -84,19 +85,60 @@ JSResourceEditor ::JSResourceEditor(Context* context, const String &fullpath, UI
     SubscribeToEvent(webClient_, E_WEBVIEWLOADEND, HANDLER(JSResourceEditor, HandleWebViewLoadEnd));
     SubscribeToEvent(messageHandler_, E_WEBMESSAGE, HANDLER(JSResourceEditor, HandleWebMessage));
 
+    SubscribeToEvent(E_RENAMERESOURCENOTIFICATION, HANDLER(JSResourceEditor, HandleRenameResourceNotification));
+    SubscribeToEvent(E_DELETERESOURCENOTIFICATION, HANDLER(JSResourceEditor, HandleDeleteResourceNotification));
+    SubscribeToEvent(E_PROJECTUNLOADEDNOTIFICATION, HANDLER(JSResourceEditor, HandleProjectUnloadedNotification));
 
     c->AddChild(webView_->GetInternalWidget());
 
 }
-
+    
 JSResourceEditor::~JSResourceEditor()
 {
 
 }
-
+   
+String getNormalizedPath(const String& path)
+{
+    // Full path is the fully qualified path from the root of the filesystem.  In order
+    // to take advantage of the resource caching system, let's trim it down to just the
+    // path inside the resources directory including the Resources directory so that the casing
+    // is correct.
+    const String& RESOURCES_MARKER = "resources/";
+    return path.SubstringUTF8(path.ToLower().Find(RESOURCES_MARKER));
+}
+    
+void JSResourceEditor::HandleRenameResourceNotification(StringHash eventType, VariantMap& eventData)
+{
+    using namespace RenameResourceNotification;
+    const String& newPath = eventData[P_NEWRESOURCEPATH].GetString();
+    const String& path = eventData[P_RESOURCEPATH].GetString();
+    
+    webClient_->ExecuteJavaScript(ToString("HOST_resourceRenamed(\"%s\",\"%s\");", getNormalizedPath(path).CString(), getNormalizedPath(newPath).CString()));
+    
+    if (fullpath_.Compare(path) == 0) {
+        fullpath_ = newPath;
+        SetModified(modified_);
+    }
+}
+    
+void JSResourceEditor::HandleDeleteResourceNotification(StringHash eventType, VariantMap& eventData)
+{
+    using namespace DeleteResourceNotification;
+    const String& path = eventData[P_RESOURCEPATH].GetString();
+    
+    webClient_->ExecuteJavaScript(ToString("HOST_resourceDeleted(\"%s\");", getNormalizedPath(path).CString()));
+}
+    
+void JSResourceEditor::HandleProjectUnloadedNotification(StringHash eventType, VariantMap& eventData)
+{
+    webClient_->ExecuteJavaScript("HOST_projectUnloaded();");
+}
+    
 void JSResourceEditor::HandleWebViewLoadEnd(StringHash eventType, VariantMap& eventData)
 {
-    webClient_->ExecuteJavaScript(ToString("loadCode(\"atomic://resources/%s\");", fullpath_.CString()));
+    // need to wait until we get an editor load complete message since we could
+    // still be streaming things in.
 }
 
 void JSResourceEditor::HandleWebMessage(StringHash eventType, VariantMap& eventData)
@@ -104,25 +146,48 @@ void JSResourceEditor::HandleWebMessage(StringHash eventType, VariantMap& eventD
     using namespace WebMessage;
 
     const String& request = eventData[P_REQUEST].GetString();
+    const String& EDITOR_CHANGE = "editorChange";
+    const String& EDITOR_SAVE_CODE = "editorSaveCode";
+    const String& EDITOR_SAVE_FILE = "editorSaveFile";
+    const String& EDITOR_LOAD_COMPLETE = "editorLoadComplete";
+    
+    String normalizedPath = getNormalizedPath(fullpath_);
+    
     WebMessageHandler* handler = static_cast<WebMessageHandler*>(eventData[P_HANDLER].GetPtr());
 
-    if (request == "change")
+    if (request == EDITOR_CHANGE)
     {
         SetModified(true);
     }
+    else if (request == EDITOR_LOAD_COMPLETE)
+    {
+        // We need to wait until the editor javascript is all required in to call the
+        // method to load the code.  The HandleWebViewLoadEnd event is getting called
+        // too soon.
+        webClient_->ExecuteJavaScript(ToString("HOST_loadCode(\"atomic://%s\");", normalizedPath.CString()));
+    }
     else
     {
         JSONValue jvalue;
         if (JSONFile::ParseJSON(request, jvalue, false))
         {
             String message = jvalue["message"].GetString();
-            if (message == "saveCode")
+            if (message == EDITOR_SAVE_CODE)
             {
                 String code = jvalue["payload"].GetString();
                 File file(context_, fullpath_, FILE_WRITE);
                 file.Write((void*) code.CString(), code.Length());
                 file.Close();
             }
+            else if (message == EDITOR_SAVE_FILE)
+            {
+                String code = jvalue["payload"].GetString();
+                String fn = jvalue["filename"].GetString();
+                // TODO: determine if we are absolute path or partial path
+                File file(context_, fn, FILE_WRITE);
+                file.Write((void*) code.CString(), code.Length());
+                file.Close();
+            }
         }
     }
 
@@ -178,7 +243,7 @@ bool JSResourceEditor::Save()
     if (!modified_)
         return true;
 
-    webClient_->ExecuteJavaScript("saveCode();");
+    webClient_->ExecuteJavaScript("HOST_saveCode();");
 
     SetModified(false);
 

+ 5 - 1
Source/AtomicEditor/Editors/JSResourceEditor.h

@@ -60,7 +60,7 @@ public:
     void SetFocus();
 
     bool Save();
-
+    
 private:
 
     void HandleWebViewLoadEnd(StringHash eventType, VariantMap& eventData);
@@ -68,6 +68,10 @@ private:
 
     bool BeautifyJavascript(const char* source, String& output);
 
+    void HandleRenameResourceNotification(StringHash eventType, VariantMap& eventData);
+    void HandleDeleteResourceNotification(StringHash eventType, VariantMap& eventData);
+    void HandleProjectUnloadedNotification(StringHash eventType, VariantMap& eventData);
+    
     SharedPtr<UIWebView> webView_;
     WeakPtr<WebClient> webClient_;
     WeakPtr<WebMessageHandler> messageHandler_;

+ 17 - 1
Source/AtomicEditor/Editors/ResourceEditor.cpp

@@ -155,6 +155,8 @@ ResourceEditor::ResourceEditor(Context* context, const String& fullpath, UITabCo
     container_->GetContentRoot()->AddChild(rootContentWidget_);
 
     SubscribeToEvent(E_FILECHANGED, HANDLER(ResourceEditor, HandleFileChanged));
+    SubscribeToEvent(E_RENAMERESOURCENOTIFICATION, HANDLER(ResourceEditor, HandleRenameResourceNotification));
+    
 }
 
 ResourceEditor::~ResourceEditor()
@@ -178,6 +180,14 @@ void ResourceEditor::HandleFileChanged(StringHash eventType, VariantMap& eventDa
     */
 }
 
+void ResourceEditor::HandleRenameResourceNotification(StringHash eventType, VariantMap& eventData)
+{
+    using namespace RenameResourceNotification;
+    const String& newPath = eventData[P_NEWRESOURCEPATH].GetString();
+    fullpath_ = newPath;
+    SetModified(modified_);
+}
+    
 void ResourceEditor::RequestClose()
 {
     editorTabLayout_->RequestClose();
@@ -218,5 +228,11 @@ void ResourceEditor::SetModified(bool modified)
         button_->SetText(filename.CString());
     }
 }
-
+    
+    
+void ResourceEditor::ProjectUnloaded() {}
+    
+void ResourceEditor::Delete() {}
+    
+    
 }

+ 4 - 1
Source/AtomicEditor/Editors/ResourceEditor.h

@@ -65,6 +65,9 @@ public:
     virtual void Redo() {}
 
     virtual bool Save() { return true; }
+    
+    virtual void ProjectUnloaded();
+    virtual void Delete();
 
     UIWidget* GetRootContentWidget() { return rootContentWidget_; }
 
@@ -89,7 +92,7 @@ protected:
 private:
 
     void HandleFileChanged(StringHash eventType, VariantMap& eventData);
-
+    void HandleRenameResourceNotification(StringHash eventType, VariantMap& eventData);
 };
 
 }

+ 3 - 0
Source/AtomicWebView/WebBrowserHost.cpp

@@ -129,6 +129,9 @@ WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 
     CefSettings settings;
     settings.windowless_rendering_enabled = true;
+    
+    // Enable remote debugging on port 3335
+    settings.remote_debugging_port = 3335;
 
     d_ = new WebBrowserHostPrivate(this);
 

+ 14 - 3
Source/AtomicWebView/WebClient.cpp

@@ -90,7 +90,7 @@ public:
     CefRefPtr<CefRenderHandler> GetRenderHandler() OVERRIDE
     {
 
-        if (webClient_->renderHandler_.Null())
+        if (webClient_.Null() || webClient_->renderHandler_.Null())
             return nullptr;
 
         return webClient_->renderHandler_->GetCEFRenderHandler();
@@ -487,6 +487,14 @@ public:
 
     IMPLEMENT_REFCOUNTING(WebClientPrivate);
 
+    void ClearReferences()
+    {
+        browser_ = nullptr;
+        webBrowserHost_ = nullptr;
+        webClient_ = nullptr;
+        browserSideRouter_ = nullptr;
+    }
+
 private:
 
     String initialLoadString_;
@@ -503,6 +511,7 @@ private:
 WebClient::WebClient(Context* context) : Object(context)
 {
     d_ = new WebClientPrivate(this);
+    d_->AddRef();
 
     SubscribeToEvent(E_WEBVIEWGLOBALPROPERTIESCHANGED, HANDLER(WebClient, HandleWebViewGlobalPropertiesChanged));
 }
@@ -520,10 +529,12 @@ WebClient::~WebClient()
         }
 
         d_->CloseBrowser(true);
+        d_->ClearReferences();
+        d_->Release();
     }
 
-    renderHandler_ = 0;
-    //d_->Release();
+    d_ = nullptr;
+    renderHandler_ = 0;    
 }
 
 void WebClient::SendMouseClickEvent(int x, int y, unsigned button, bool mouseUp, unsigned modifier, int clickCount) const

+ 4 - 2
Source/AtomicWebView/WebSchemeHandler.cpp

@@ -62,8 +62,8 @@ public:
             return false;
 
         ConvertCEFString(request->GetURL(), url_);
-        // shave off atomic://resources/
-        url_ = url_.SubstringUTF8(19);
+        //shave off the atomic:// part
+        url_ = url_.SubstringUTF8(9);
 
         if (IsAbsolutePath(url_))
         {
@@ -71,6 +71,8 @@ public:
         }
         else
         {
+            //shave off the resources part so we can grab the value from the resource cache
+            url_ = url_.SubstringUTF8(10);
             ResourceCache* cache = webBrowserHost_->GetSubsystem<ResourceCache>();
             file_ = cache->GetFile(url_, false);
         }

+ 36 - 23
Source/ThirdParty/SDL/src/video/cocoa/SDL_cocoaevents.m

@@ -39,7 +39,7 @@
 @interface SDLApplication : NSApplication
 
 - (void)terminate:(id)sender;
-
+- (void)sendEvent:(NSEvent *)theEvent;
 @end
 
 @implementation SDLApplication
@@ -50,6 +50,41 @@
     SDL_SendQuit();
 }
 
+// ATOMIC: Fixes missing WebView keystrokes on OSX
+// (see https://bugzilla.libsdl.org/show_bug.cgi?id=3107)
+// Dispatch events here so that we can handle events caught by
+// nextEventMatchingMask in SDL, as well as events caught by other
+// processes (such as CEF) that are passed down to NSApp.
+- (void)sendEvent:(NSEvent *)theEvent
+{
+    SDL_VideoDevice *_this = SDL_GetVideoDevice();
+
+    switch ([theEvent type]) {
+        case NSLeftMouseDown:
+        case NSOtherMouseDown:
+        case NSRightMouseDown:
+        case NSLeftMouseUp:
+        case NSOtherMouseUp:
+        case NSRightMouseUp:
+        case NSLeftMouseDragged:
+        case NSRightMouseDragged:
+        case NSOtherMouseDragged: /* usually middle mouse dragged */
+        case NSMouseMoved:
+        case NSScrollWheel:
+            Cocoa_HandleMouseEvent(_this, theEvent);
+            break;
+        case NSKeyDown:
+        case NSKeyUp:
+        case NSFlagsChanged:
+            Cocoa_HandleKeyEvent(_this, theEvent);
+            break;
+        default:
+            break;
+    }
+
+    [super sendEvent:theEvent];
+}
+
 @end // SDLApplication
 
 /* setAppleMenu disappeared from the headers in 10.4 */
@@ -316,28 +351,6 @@ Cocoa_PumpEvents(_THIS)
             break;
         }
 
-        switch ([event type]) {
-        case NSLeftMouseDown:
-        case NSOtherMouseDown:
-        case NSRightMouseDown:
-        case NSLeftMouseUp:
-        case NSOtherMouseUp:
-        case NSRightMouseUp:
-        case NSLeftMouseDragged:
-        case NSRightMouseDragged:
-        case NSOtherMouseDragged: /* usually middle mouse dragged */
-        case NSMouseMoved:
-        case NSScrollWheel:
-            Cocoa_HandleMouseEvent(_this, event);
-            break;
-        case NSKeyDown:
-        case NSKeyUp:
-        case NSFlagsChanged:
-            Cocoa_HandleKeyEvent(_this, event);
-            break;
-        default:
-            break;
-        }
         /* Pass through to NSApp to make sure everything stays in sync */
         [NSApp sendEvent:event];
     }

Некоторые файлы не были показаны из-за большого количества измененных файлов