Browse Source

Working on editors

Josh Engebretson 10 years ago
parent
commit
a74d90ffb2
37 changed files with 5321 additions and 8 deletions
  1. 1 1
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/editor/Editor.ts
  2. 2 2
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/main.ts
  3. 541 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/acorn.js
  4. 109 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/beautify.js
  5. 67 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/jsutils.js
  6. 1 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/tsconfig.json
  7. 5 1
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/MainFrame.ts
  8. 8 1
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ProjectFrame.ts
  9. 72 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ResourceFrame.ts
  10. 1 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/UIEvents.ts
  11. 10 0
      Source/Atomic/UI/UI.cpp
  12. 9 0
      Source/Atomic/UI/UIButton.cpp
  13. 2 0
      Source/Atomic/UI/UIButton.h
  14. 36 0
      Source/Atomic/UI/UITabContainer.cpp
  15. 26 0
      Source/Atomic/UI/UITabContainer.h
  16. 627 0
      Source/AtomicEditorWork/Editors/JSResourceEditor.cpp
  17. 66 0
      Source/AtomicEditorWork/Editors/JSResourceEditor.h
  18. 123 0
      Source/AtomicEditorWork/Editors/ResourceEditor.cpp
  19. 64 0
      Source/AtomicEditorWork/Editors/ResourceEditor.h
  20. 961 0
      Source/AtomicEditorWork/Javascript/JSAST.cpp
  21. 967 0
      Source/AtomicEditorWork/Javascript/JSAST.h
  22. 7 0
      Source/AtomicEditorWork/Javascript/JSASTSyntaxColorVisitor.cpp
  23. 360 0
      Source/AtomicEditorWork/Javascript/JSASTSyntaxColorVisitor.h
  24. 7 0
      Source/AtomicEditorWork/Javascript/JSASTVisitor.cpp
  25. 382 0
      Source/AtomicEditorWork/Javascript/JSASTVisitor.h
  26. 190 0
      Source/AtomicEditorWork/Javascript/JSAutocomplete.cpp
  27. 56 0
      Source/AtomicEditorWork/Javascript/JSAutocomplete.h
  28. 26 0
      Source/AtomicEditorWork/Javascript/JSErrorChecker.cpp
  29. 33 0
      Source/AtomicEditorWork/Javascript/JSErrorChecker.h
  30. 303 0
      Source/AtomicEditorWork/Javascript/JSSpiderMonkeyVM.cpp
  31. 56 0
      Source/AtomicEditorWork/Javascript/JSSpiderMonkeyVM.h
  32. 5 0
      Source/AtomicEditorWork/Javascript/JSTheme.cpp
  33. 182 0
      Source/AtomicEditorWork/Javascript/JSTheme.h
  34. 12 0
      Source/AtomicJS/Javascript/JSFileSystem.cpp
  35. 1 0
      Source/AtomicJS/Javascript/JSUI.cpp
  36. 1 1
      Source/AtomicJS/Packages/Atomic/UI.json
  37. 2 2
      Source/AtomicJS/Packages/Editor/Editor.json

+ 1 - 1
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/editor/Editor.ts

@@ -65,7 +65,7 @@ class Editor extends Atomic.ScriptObject
     // set initial size
     this.mainframe.setSize(graphics.width, graphics.height);
 
-    this.parseArguments();
+    this.parseArguments();    
 
   }
 

+ 2 - 2
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/main.ts

@@ -4,6 +4,6 @@
 
 /// <reference path="./AtomicWork.d.ts" />
 
-import Editor = require("./editor/Editor");
+import __Editor = require("./editor/Editor");
 
-var TheEditor = new Editor();
+var TheEditor = new __Editor();

File diff suppressed because it is too large
+ 541 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/acorn.js


File diff suppressed because it is too large
+ 109 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/beautify.js


+ 67 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/jsutils.js

@@ -0,0 +1,67 @@
+__atomic_acorn = require('./acorn');
+__atomic_beautify = require('./beautify');
+
+exports.parseToJSON = function (source) {
+
+	var start = new Date().getTime();
+
+	var comments = [];
+
+	var ast = __atomic_acorn.parse( source, {
+    	// collect ranges for each node
+    	ranges: true,
+    	locations: true,
+    	onComment: comments,
+	});
+
+	var end = new Date().getTime();
+	var time = end - start;
+
+	// print('Acorn parse time: ' + time);
+
+	ast["comments"] = comments;
+
+	start = new Date().getTime();
+	ast = JSON.stringify(ast);
+	end = new Date().getTime();
+	time = end - start;
+
+	// print('JSON.stringify time: ' + time);
+
+	return ast;
+
+}
+
+
+exports.parseErrorCheck = function(source) {
+
+
+	try {
+
+		__atomic_acorn.parse( source, {
+	    	ranges: true,
+	    	locations: true,
+		});
+
+	} catch(e) {
+
+        //if (!(e instanceof __atomic_acorn.SyntaxError)) throw e;
+
+        if (!(e instanceof SyntaxError))
+        	return false; // rethrow?
+
+        return e;
+
+    }
+
+
+	return false;
+
+}
+
+// TODO: options
+exports.jsBeautify = function (source) {
+
+	return __atomic_beautify.js_beautify(source);
+
+}

+ 1 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/tsconfig.json

@@ -18,6 +18,7 @@
         "./ui/MainFrame.ts",
         "./ui/MainFrameMenu.ts",
         "./ui/ProjectFrame.ts",
+        "./ui/ResourceFrame.ts",
         "./ui/ScriptWidget.ts",
         "./ui/UIEvents.ts",
         "./ui/modal/MessageModal.ts",

+ 5 - 1
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/MainFrame.ts

@@ -1,5 +1,6 @@
 import menubar = require("./MainFrameMenu");
 import ProjectFrame = require("./ProjectFrame");
+import ResourceFrame = require("./ResourceFrame");
 import MessageModal = require("./modal/MessageModal");
 import UIEvents = require("./UIEvents");
 
@@ -7,7 +8,9 @@ import ScriptWidget = require("./ScriptWidget");
 
 class MainFrame extends ScriptWidget {
 
-	projectframe:ProjectFrame;	
+	projectframe:ProjectFrame;
+	resourceframe:ResourceFrame;
+
 	private messagemodal:MessageModal.MessageModal = new MessageModal.MessageModal();
 
 	constructor() {
@@ -17,6 +20,7 @@ class MainFrame extends ScriptWidget {
 		this.load("AtomicEditor/editor/ui/mainframe.tb.txt");
 
 		this.projectframe = new ProjectFrame(this);
+		this.resourceframe = new ResourceFrame(this);
 
 	}
 

+ 8 - 1
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ProjectFrame.ts

@@ -1,6 +1,7 @@
 
 
 import ScriptWidget = require("./ScriptWidget");
+import Editor = require("../editor/Editor");
 
 var UI = Atomic.UI;
 
@@ -72,6 +73,12 @@ class ProjectFrame extends ScriptWidget {
 
                 if (fs.dirExists(id)) {
                     this.selectCurrentContentFolder(id);
+                    return true;
+                }
+
+                if (fs.fileExists(id)) {
+                  this.sendEvent("EditResource", {"path" : id } );
+                  return true;
                 }
 
             }
@@ -88,7 +95,7 @@ class ProjectFrame extends ScriptWidget {
         var cfolder = this.currentContentFolder;
         this.currentContentFolder = "";
         this.refreshFolders();
-        
+
     }
 
     private refreshContent(fullpath: string) {

+ 72 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ResourceFrame.ts

@@ -0,0 +1,72 @@
+import ScriptWidget = require("./ScriptWidget");
+import UIEvents = require("./UIEvents");
+
+var UI = Atomic.UI;
+
+class ResourceFrame extends ScriptWidget {
+
+    tabcontainer:Atomic.UITabContainer;
+    resourceLayout:Atomic.UILayout;
+    resourceViewContainer:Atomic.UILayout;
+
+    editors: { [path: string] : Editor.ResourceEditor; } = {};
+
+    show(value:boolean) {
+
+      if (show) {
+
+      }
+
+    }
+
+    handleEditResource(data) {
+
+      var path = data.path;
+
+      if (this.editors[path]) {
+
+        return;
+
+      }
+
+      var ext = Atomic.getExtension(data.path);
+
+      var editor:Editor.ResourceEditor = null;
+
+      if (ext == ".js")
+      {
+          editor = new Editor.JSResourceEditor(path, this.tabcontainer);
+      }
+
+      if (editor)
+      {
+          this.editors[path] = editor;
+
+          //tabcontainer_->SetCurrentPage(tabcontainer_->GetNumPages()-1);
+          //editor->SetFocus();
+      }
+
+
+    }
+
+    constructor(parent:Atomic.UIWidget) {
+
+        super();
+
+        this.load("AtomicEditor/editor/ui/resourceframe.tb.txt");
+
+        this.gravity = UI.GRAVITY_ALL;
+
+        this.resourceViewContainer = <Atomic.UILayout> parent.getWidget("resourceviewcontainer");
+        this.tabcontainer = <Atomic.UITabContainer> this.getWidget("tabcontainer");
+        this.resourceLayout = <Atomic.UILayout> this.getWidget("resourcelayout");
+
+        this.resourceViewContainer.addChild(this);
+
+        this.subscribeToEvent(UIEvents.EditResource, (data) => this.handleEditResource(data));
+
+    }
+
+}
+
+export = ResourceFrame;

+ 1 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/UIEvents.ts

@@ -1,2 +1,3 @@
 
 export const MessageModalEvent = "MessageModalEvent";
+export const EditResource = "EditResource";

+ 10 - 0
Source/Atomic/UI/UI.cpp

@@ -11,6 +11,7 @@
 #include <TurboBadger/tb_message_window.h>
 #include <TurboBadger/tb_editfield.h>
 #include <TurboBadger/tb_select.h>
+#include <TurboBadger/tb_tab_container.h>
 #include <TurboBadger/image/tb_image_widget.h>
 
 void register_tbbf_font_renderer();
@@ -41,6 +42,7 @@ using namespace tb;
 #include "UISelectList.h"
 #include "UIMessageWindow.h"
 #include "UISkinImage.h"
+#include "UITabContainer.h"
 
 namespace tb
 {
@@ -543,6 +545,14 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
         return nwidget;
     }
 
+    if (widget->IsOfType<TBTabContainer>())
+    {
+        UITabContainer* nwidget = new UITabContainer(context_);
+        nwidget->SetWidget(widget);
+        widgetWrap_[widget] = nwidget;
+        return nwidget;
+    }
+
     return 0;
 }
 

+ 9 - 0
Source/Atomic/UI/UIButton.cpp

@@ -25,6 +25,15 @@ UIButton::UIButton(Context* context, bool createWidget) : UIWidget(context, fals
 
 UIButton::~UIButton()
 {
+
+}
+
+void UIButton::SetSqueezable(bool value)
+{
+    if (!widget_)
+        return;
+
+    ((TBButton*)widget_)->SetSqueezable(value);
 }
 
 bool UIButton::OnEvent(const tb::TBWidgetEvent &ev)

+ 2 - 0
Source/Atomic/UI/UIButton.h

@@ -16,6 +16,8 @@ public:
     UIButton(Context* context, bool createWidget = true);
     virtual ~UIButton();
 
+    void SetSqueezable(bool value);
+
 protected:
 
     virtual bool OnEvent(const tb::TBWidgetEvent &ev);

+ 36 - 0
Source/Atomic/UI/UITabContainer.cpp

@@ -0,0 +1,36 @@
+
+#include <TurboBadger/tb_widgets.h>
+#include <TurboBadger/tb_widgets_common.h>
+#include <TurboBadger/tb_tab_container.h>
+
+
+#include "UI.h"
+#include "UIEvents.h"
+#include "UITabContainer.h"
+
+using namespace tb;
+
+namespace Atomic
+{
+
+UITabContainer::UITabContainer(Context* context, bool createWidget) : UIWidget(context, false)
+{
+    if (createWidget)
+    {
+        widget_ = new TBTabContainer();
+        widget_->SetDelegate(this);
+        GetSubsystem<UI>()->WrapWidget(this, widget_);
+    }
+}
+
+UITabContainer::~UITabContainer()
+{
+
+}
+
+bool UITabContainer::OnEvent(const tb::TBWidgetEvent &ev)
+{
+    return false;
+}
+
+}

+ 26 - 0
Source/Atomic/UI/UITabContainer.h

@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "UIWidget.h"
+
+namespace Atomic
+{
+
+class UITabContainer : public UIWidget
+{
+    OBJECT(UITabContainer)
+
+public:
+
+    UITabContainer(Context* context, bool createWidget = true);
+    virtual ~UITabContainer();
+
+protected:
+
+    virtual bool OnEvent(const tb::TBWidgetEvent &ev);
+
+private:
+
+};
+
+}

+ 627 - 0
Source/AtomicEditorWork/Editors/JSResourceEditor.cpp

@@ -0,0 +1,627 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/Container/ArrayPtr.h>
+#include <Atomic/UI/UI.h>
+#include <Atomic/IO/Log.h>
+#include <Atomic/IO/File.h>
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Resource/ResourceCache.h>
+#include <Atomic/Core/CoreEvents.h>
+#include <AtomicJS/Javascript/JSVM.h>
+
+/*
+#include "../AEEvents.h"
+#include "../UI/UIFindTextWidget.h"
+#include "../AEJavascript.h"
+*/
+
+#include "JSResourceEditor.h"
+
+
+#include "../Javascript/JSAutocomplete.h"
+#include "../Javascript/JSTheme.h"
+#include "../Javascript/JSASTSyntaxColorVisitor.h"
+
+#include <TurboBadger/tb_message_window.h>
+#include <TurboBadger/tb_editfield.h>
+#include <TurboBadger/tb_style_edit.h>
+#include <TurboBadger/tb_style_edit_content.h>
+
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+JSResourceEditor ::JSResourceEditor(Context* context, const String &fullpath, UITabContainer *container) :
+    ResourceEditor(context, fullpath, container),
+    styleEdit_(0),
+    lineNumberList_(0),
+    editField_(0),
+    autocomplete_(0),
+    textDirty_(true),
+    textDelta_(0.0f),
+    modified_(false),
+    currentFindPos_(-1)
+{
+
+    TBLayout* layout = new TBLayout();
+    layout->SetLayoutSize(LAYOUT_SIZE_GRAVITY);
+    layout->SetGravity(WIDGET_GRAVITY_ALL);
+    layout->SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+
+    rootContentWidget_->GetInternalWidget()->AddChild(layout);
+
+    TBContainer* c = new TBContainer();
+    c->SetGravity(WIDGET_GRAVITY_ALL);
+
+    TBEditField* text = editField_ = new TBEditField();
+    text->SetMultiline(true);
+    text->SetWrapping(true);
+    text->SetGravity(WIDGET_GRAVITY_ALL);
+    text->SetStyling(true);
+    text->SetSkinBg(TBIDC("TextCode"));
+
+    TBFontDescription fd;
+    fd.SetID(TBIDC("Monaco"));
+    fd.SetSize(12);
+    text->SetFontDescription(fd);
+
+    SharedPtr<File> jsFile(GetSubsystem<ResourceCache>()->GetFile(fullpath));
+    assert(jsFile);
+
+    String source;
+    jsFile->ReadText(source);
+
+    String json;
+
+    JSASTProgram* program = NULL;
+
+    if (ParseJavascriptToJSON(source.CString(), json))
+    {
+        program = JSASTProgram::ParseFromJSON(fullpath, json);
+    }
+
+    text->SetText(source.CString());
+
+    lineNumberList_ = new TBSelectList();
+    lineNumberList_->SetFontDescription(fd);
+    lineNumberList_->SetSkinBg(TBIDC("LineNumberSelectList"));
+    lineNumberList_->GetScrollContainer()->SetScrollMode(SCROLL_MODE_OFF);
+    //lineNumberList_->GetScrollContainer()->SetIgnoreScrollEvents(true);
+    lineNumberList_->SetGravity(WIDGET_GRAVITY_ALL);
+    LayoutParams lp;
+    lp.max_w = 48;
+    lineNumberList_->SetLayoutParams(lp);
+
+    c->AddChild(text);
+
+    layout->AddChild(lineNumberList_);
+    layout->AddChild(c);
+    layout->SetSpacing(0);
+
+    TBStyleEdit* sedit = text->GetStyleEdit();
+    TBTextTheme* theme = new TBTextTheme();
+    for (unsigned i = 0; i < TB_MAX_TEXT_THEME_COLORS; i++)
+        theme->themeColors[i] = TBColor(255, 255, 255);
+
+    theme->themeColors[JSTHEME_LITERAL_STRING].SetFromString("#E6DB74", 7);
+
+    theme->themeColors[JSTHEME_LITERAL_NUMBER].SetFromString("#AE81FF", 7);
+    theme->themeColors[JSTHEME_LITERAL_REGEX].SetFromString("#AE81FF", 7);
+    theme->themeColors[JSTHEME_LITERAL_BOOLEAN].SetFromString("#AE81FF", 7);
+    theme->themeColors[JSTHEME_LITERAL_NULL].SetFromString("#AE81FF", 7);
+
+    theme->themeColors[JSTHEME_FUNCTION].SetFromString("#66D9EF", 7);
+    theme->themeColors[JSTHEME_VAR].SetFromString("#66D9EF", 7);
+
+
+    theme->themeColors[JSTHEME_KEYWORD].SetFromString("#f92672", 7);
+    theme->themeColors[JSTHEME_OPERATOR].SetFromString("#f92672", 7);
+
+    theme->themeColors[JSTHEME_CODE].SetFromString("#a6e22e", 7);
+    theme->themeColors[JSTHEME_COMMENT].SetFromString("#75715e", 7);
+
+    theme->themeColors[JSTHEME_FUNCTIONDECLARG].SetFromString("#FF9800", 7);
+
+    sedit->SetTextTheme(theme);
+
+    sedit->text_change_listener = this;
+
+    styleEdit_ = sedit;
+    UpdateLineNumbers();
+
+    if (program)
+    {
+        JSASTSyntaxColorVisitor syntaxColor(sedit);
+        syntaxColor.visit(program);
+    }
+
+    autocomplete_ = new JSAutocomplete(text);
+    autocomplete_->UpdateLocals();
+
+    SubscribeToEvent(E_UPDATE, HANDLER(JSResourceEditor, HandleUpdate));
+
+    // FIXME: Set the size at the end of setup, so all children are updated accordingly
+    // future size changes will be handled automatically
+    IntRect rect = container_->GetContentRoot()->GetRect();
+    rootContentWidget_->SetSize(rect.Width(), rect.Height());
+}
+
+JSResourceEditor::~JSResourceEditor()
+{
+}
+
+void JSResourceEditor::UpdateLineNumbers()
+{
+    if (!styleEdit_)
+        return;
+
+    TBGenericStringItemSource* lineSource = lineNumberList_->GetDefaultSource();
+
+    int lines = lineSource->GetNumItems();
+
+    int lineCount = styleEdit_->blocks.CountLinks();
+
+    if (lines == lineCount)
+        return;
+
+    while (lines > lineCount)
+    {
+        lineSource->DeleteItem(lineSource->GetNumItems() - 1);
+        lines --;
+    }
+
+    for (int i = lines; i < lineCount; i++)
+    {
+        String sline;
+        sline.AppendWithFormat("%i  ", i + 1);
+        TBGenericStringItem* item = new TBGenericStringItem(sline.CString());
+        lineSource->AddItem(item);
+
+    }
+
+    // item widgets don't exist until ValidateList
+    lineNumberList_->ValidateList();
+
+    for (int i = 0; i < lineCount; i++)
+    {
+        TBTextField* textField = (TBTextField* )lineNumberList_->GetItemWidget(i);
+
+        if (textField)
+        {
+            textField->SetTextAlign(TB_TEXT_ALIGN_RIGHT);
+            textField->SetSkinBg(TBIDC("TBSelectItemLineNumber"));
+        }
+
+    }
+
+}
+
+void JSResourceEditor::OnChange(TBStyleEdit* styleEdit)
+{
+    textDelta_ = 0.25f;
+    textDirty_ = true;
+    modified_ = true;
+
+    String filename = GetFileNameAndExtension(fullpath_);
+    filename += "*";
+    button_->SetText(filename.CString());
+
+    autocomplete_->Hide();
+
+    TBTextFragment* fragment = 0;
+    int ofs = styleEdit_->caret.pos.ofs;
+    fragment = styleEdit_->caret.pos.block->FindFragment(ofs, true);
+
+    if (fragment && fragment->len && (styleEdit_->caret.pos.ofs == (fragment->ofs + fragment->len)))
+    {
+        String value(fragment->Str(), fragment->len);
+        bool hasCompletions = autocomplete_->UpdateCompletions(value);
+
+        if (hasCompletions)
+        {
+            autocomplete_->SetPosition(TBPoint(fragment->xpos, (styleEdit_->caret.y - styleEdit_->scroll_y) + fragment->line_height));
+            autocomplete_->Show();
+        }
+    }
+
+    UpdateLineNumbers();
+}
+
+bool JSResourceEditor::OnEvent(const TBWidgetEvent &ev)
+{
+    if (ev.type == EVENT_TYPE_KEY_DOWN)
+    {
+        if (autocomplete_ && autocomplete_->Visible())
+        {
+            return autocomplete_->OnEvent(ev);
+        }
+
+        if (ev.special_key == TB_KEY_ESC)
+        {
+            //SendEvent(E_FINDTEXTCLOSE);
+        }
+
+    }
+
+    if (ev.type == EVENT_TYPE_SHORTCUT)
+    {
+        if (ev.ref_id == TBIDC("close"))
+        {
+            if (modified_)
+            {
+                TBMessageWindow *msg_win = new TBMessageWindow(container_->GetInternalWidget(), TBIDC("unsaved_jsmodifications_dialog"));
+                TBMessageWindowSettings settings(TB_MSG_OK_CANCEL, TBID(uint32(0)));
+                settings.dimmer = true;
+                settings.styling = true;
+                msg_win->Show("Unsaved Modifications", "There are unsaved modications.\nDo you wish to discard them and close?", &settings, 640, 360);
+            }
+            else
+            {
+                Close();
+            }
+
+        }
+
+        if (ev.ref_id == TBIDC("save") && modified_)
+        {
+            TBStr text;
+            styleEdit_->GetText(text);
+            File file(context_, fullpath_, FILE_WRITE);
+            file.Write((void*) text.CStr(), text.Length());
+            file.Close();
+
+            String filename = GetFileNameAndExtension(fullpath_);
+            button_->SetText(filename.CString());
+            modified_ = false;
+            //SendEvent(E_JAVASCRIPTSAVED);
+
+            return true;
+        }
+        else if (ev.ref_id == TBIDC("find"))
+        {
+            //using namespace FindTextOpen;
+            //SendEvent(E_FINDTEXTOPEN);
+        }
+        else if (ev.ref_id == TBIDC("findnext") || ev.ref_id == TBIDC("findprev"))
+        {
+            /*
+            String text;
+
+            FindTextWidget* finder = GetSubsystem<FindTextWidget>();
+            finder->GetFindText(text);
+
+            // TODO: get flags from finder
+            unsigned flags = FINDTEXT_FLAG_NONE;
+
+            if (ev.ref_id == TBIDC("findnext"))
+                flags |= FINDTEXT_FLAG_NEXT;
+            else if (ev.ref_id == TBIDC("findprev"))
+                flags |= FINDTEXT_FLAG_PREV;
+
+            flags |= FINDTEXT_FLAG_WRAP;
+
+            finder->Find(text, flags);
+            */
+        }
+        else if (ev.ref_id == TBIDC("beautify"))
+        {
+            TBStr text;
+            styleEdit_->GetText(text);
+
+            if (text.Length())
+            {
+                String output;
+                if (BeautifyJavascript(text.CStr(), output))
+                {
+                    if (output.Length())
+                    {
+                        styleEdit_->selection.SelectAll();
+                        styleEdit_->InsertText(output.CString(), output.Length());
+                    }
+                }
+            }
+        }
+        else if (ev.ref_id == TBIDC("cut") || ev.ref_id == TBIDC("copy") || ev.ref_id == TBIDC("paste")
+                 || ev.ref_id == TBIDC("selectall") || ev.ref_id == TBIDC("undo") || ev.ref_id == TBIDC("redo") )
+        {
+            editField_->OnEvent(ev);
+        }
+    }
+
+    if (ev.type == EVENT_TYPE_CLICK)
+    {
+        if (ev.target->GetID() == TBIDC("unsaved_jsmodifications_dialog"))
+        {
+            if (ev.ref_id == TBIDC("TBMessageWindow.ok"))
+            {
+                Close();
+            }
+            else
+            {
+                SetFocus();
+            }
+
+            return true;
+        }
+
+    }
+
+    return false;
+}
+
+void JSResourceEditor::HandleUpdate(StringHash eventType, VariantMap& eventData)
+{
+
+    if (!styleEdit_)
+        return;
+
+    // sync line number
+    lineNumberList_->GetScrollContainer()->ScrollTo(0, styleEdit_->scroll_y);
+    lineNumberList_->SetValue(styleEdit_->GetCaretLine());
+
+    if (autocomplete_->Visible())
+    {
+        TBTextFragment* fragment = 0;
+        int ofs = styleEdit_->caret.pos.ofs;
+        fragment = styleEdit_->caret.pos.block->FindFragment(ofs, true);
+
+        if (fragment && (styleEdit_->caret.pos.ofs == (fragment->ofs + fragment->len)))
+        {
+            String value(fragment->Str(), fragment->len);
+            bool hasCompletions = autocomplete_->UpdateCompletions(value);
+
+            if (!hasCompletions)
+            {
+                autocomplete_->Hide();
+            }
+        }
+
+    }
+
+    // Timestep parameter is same no matter what event is being listened to
+    float timeStep = eventData[Update::P_TIMESTEP].GetFloat();
+
+    if (!textDirty_)
+        return;
+
+    if (textDelta_ > 0.0f)
+    {
+        textDelta_ -= timeStep;
+        if (textDelta_ < 0.0f)
+        {
+            textDelta_ = 0.0f;
+        }
+        else
+        {
+            return;
+        }
+    }
+
+    TBStr text;
+    styleEdit_->GetText(text);
+
+    JSASTProgram* program = NULL;
+    String json;
+    if (ParseJavascriptToJSON(text.CStr(), json))
+    {
+        program = JSASTProgram::ParseFromJSON("fullpath", json);
+
+        if (program)
+        {
+            JSASTSyntaxColorVisitor syntaxColor(styleEdit_);
+            syntaxColor.visit(program);
+            delete program;
+        }
+
+    }
+
+    textDirty_ = false;
+
+    editField_->SetFocus(WIDGET_FOCUS_REASON_UNKNOWN);
+
+}
+
+void JSResourceEditor::FindTextClose()
+{
+    editField_->SetFocus(WIDGET_FOCUS_REASON_UNKNOWN);
+    styleEdit_->selection.SelectNothing();
+}
+
+bool JSResourceEditor::FindText(const String& findText, unsigned flags)
+{
+    /*
+    unsigned findLength = findText.Length();
+
+    if (!findLength)
+        return true;
+
+    TBStr _source;
+    styleEdit_->GetText(_source);
+    String source = _source.CStr();
+
+    unsigned pos = String::NPOS;
+    int startPos = currentFindPos_;
+
+    if (currentFindPos_ == -1)
+        startPos = styleEdit_->caret.GetGlobalOfs();
+    else
+    {
+        if (flags & FINDTEXT_FLAG_NEXT)
+            startPos += findLength;
+    }
+
+    if (flags & FINDTEXT_FLAG_PREV)
+    {
+        String pretext = source.Substring(0, startPos);
+        pos = pretext.FindLast(findText, String::NPOS, flags & FINDTEXT_FLAG_CASESENSITIVE ? true : false);
+    }
+    else
+    {
+        pos = source.Find(findText, startPos, flags & FINDTEXT_FLAG_CASESENSITIVE ? true : false);
+    }
+
+    if (pos == String::NPOS)
+    {
+        if (flags & FINDTEXT_FLAG_WRAP)
+        {
+            if (flags & FINDTEXT_FLAG_PREV)
+            {
+                pos = source.FindLast(findText, String::NPOS, flags & FINDTEXT_FLAG_CASESENSITIVE ? true : false);
+            }
+            else
+            {
+                pos = source.Find(findText, 0, flags & FINDTEXT_FLAG_CASESENSITIVE ? true : false);
+            }
+        }
+
+        if (pos == String::NPOS)
+        {
+            styleEdit_->selection.SelectNothing();
+            return true;
+        }
+    }
+
+    currentFindPos_ = pos;
+
+    styleEdit_->caret.SetGlobalOfs((int) pos + findLength);
+
+    int height = styleEdit_->layout_height;
+
+    int newy = styleEdit_->caret.y - height/2;
+
+    styleEdit_->SetScrollPos(styleEdit_->scroll_x, newy);
+
+    styleEdit_->selection.Select(pos, pos + findLength);
+    */
+    return true;
+}
+
+void JSResourceEditor::SetFocus()
+{
+    editField_->SetFocus(WIDGET_FOCUS_REASON_UNKNOWN);
+}
+
+void JSResourceEditor::GotoTokenPos(int tokenPos)
+{
+    styleEdit_->caret.SetGlobalOfs(tokenPos);
+
+    int height = styleEdit_->layout_height;
+
+    int newy = styleEdit_->caret.y - height/2;
+
+    styleEdit_->SetScrollPos(styleEdit_->scroll_x, newy);
+
+}
+
+void JSResourceEditor::GotoLineNumber(int lineNumber)
+{
+    int line = 0;
+    TBBlock *block = NULL;
+    for (block = styleEdit_->blocks.GetFirst(); block; block = block->GetNext())
+    {
+        if (lineNumber == line)
+            break;
+        line++;
+    }
+    if (!block)
+        return;
+
+    styleEdit_->caret.Place(block, 0);
+
+    int height = styleEdit_->layout_height;
+    int newy = styleEdit_->caret.y - height/2;
+
+    styleEdit_->SetScrollPos(styleEdit_->scroll_x, newy);
+
+}
+
+
+bool JSResourceEditor::HasUnsavedModifications()
+{
+    return modified_;
+}
+
+bool JSResourceEditor::ParseJavascriptToJSON(const char* source, String& json, bool loose)
+{
+
+    JSVM* vm = JSVM::GetJSVM(NULL);
+    duk_context* ctx = vm->GetJSContext();
+
+    int top = duk_get_top(ctx);
+
+    json.Clear();
+
+    duk_get_global_string(ctx, "require");
+    duk_push_string(ctx, "AtomicEditor/typescript/modules/jsutils");
+    if (duk_pcall(ctx, 1))
+    {
+        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
+        duk_set_top(ctx, top);
+        return false;
+    }
+
+    duk_get_prop_string(ctx, -1, "parseToJSON");
+    duk_push_string(ctx, source);
+    bool ok = true;
+
+    if (duk_pcall(ctx, 1))
+    {
+        ok = false;
+        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
+    }
+    else
+    {
+        json = duk_to_string(ctx, -1);
+    }
+
+    duk_set_top(ctx, top);
+
+    return ok;
+}
+
+bool JSResourceEditor::BeautifyJavascript(const char* source, String& output)
+{
+    JSVM* vm = JSVM::GetJSVM(NULL);
+    duk_context* ctx = vm->GetJSContext();
+
+    int top = duk_get_top(ctx);
+
+    output.Clear();
+
+    duk_get_global_string(ctx, "require");
+    duk_push_string(ctx, "AtomicEditor/typescript/modules/jsutils");
+
+    if (duk_pcall(ctx, 1))
+    {
+        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
+        duk_set_top(ctx, top);
+        return false;
+    }
+
+
+    duk_get_prop_string(ctx, -1, "jsBeautify");
+    duk_push_string(ctx, source);
+    bool ok = true;
+
+    if (duk_pcall(ctx, 1))
+    {
+        ok = false;
+        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
+    }
+    else
+    {
+        output = duk_to_string(ctx, -1);
+    }
+
+    // ignore result
+    duk_set_top(ctx, top);
+
+    return ok;
+
+}
+
+}

+ 66 - 0
Source/AtomicEditorWork/Editors/JSResourceEditor.h

@@ -0,0 +1,66 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include "ResourceEditor.h"
+#include <TurboBadger/tb_editfield.h>
+#include <TurboBadger/tb_style_edit.h>
+#include <TurboBadger/tb_select.h>
+
+using namespace Atomic;
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+class JSAutocomplete;
+
+class JSResourceEditor: public ResourceEditor, public TBStyleEditTextChangeListener
+{
+    OBJECT(JSResourceEditor);
+
+public:
+
+    JSResourceEditor(Context* context, const String& fullpath, UITabContainer* container);
+
+    virtual ~JSResourceEditor();
+
+    bool OnEvent(const TBWidgetEvent &ev);
+
+    bool FindText(const String& findText, unsigned flags);
+    void FindTextClose();
+
+    void OnChange(TBStyleEdit* styleEdit);
+    void HandleUpdate(StringHash eventType, VariantMap& eventData);    
+
+    void GotoTokenPos(int tokenPos);
+    void GotoLineNumber(int lineNumber);
+
+    void SetFocus();
+
+    bool HasUnsavedModifications();
+
+private:
+
+    bool ParseJavascriptToJSON(const char* source, String& json, bool loose = true);
+    bool BeautifyJavascript(const char* source, String& output);
+    void UpdateLineNumbers();
+
+    tb::TBStyleEdit* styleEdit_;
+    tb::TBSelectList* lineNumberList_;
+
+    TBEditField* editField_;
+    JSAutocomplete* autocomplete_;
+
+    float textDelta_;
+    bool textDirty_;
+
+    bool modified_;
+
+    int currentFindPos_;
+
+};
+
+}

+ 123 - 0
Source/AtomicEditorWork/Editors/ResourceEditor.cpp

@@ -0,0 +1,123 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include <TurboBadger/tb_tab_container.h>
+
+#include "AtomicEditor.h"
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Resource/ResourceEvents.h>
+
+#include "ResourceEditor.h"
+
+//#include "../UI/UIMainFrame.h"
+//#include "../UI/UIResourceFrame.h"
+
+namespace AtomicEditor
+{
+
+class EditorTabLayout: public TBLayout
+{
+public:
+
+    ResourceEditor* editor_;
+    TBButton* button_;
+    TBButton* close_;
+    TBTabContainer* container_;
+
+    void SetValue(int value)
+    {
+        button_->SetValue(value);
+    }
+
+    bool OnEvent(const TBWidgetEvent &ev)
+    {
+        if (ev.type == EVENT_TYPE_CLICK || ev.type == EVENT_TYPE_POINTER_DOWN)
+        {
+            if (ev.target->GetID() == TBIDC("tabclose"))
+            {
+                container_->OnEvent(ev);
+                editor_->Close();
+                return true;
+            }
+            else
+            {
+                TBWidgetEvent nevent = ev;
+                nevent.target = this;
+                container_->OnEvent(nevent);
+            }
+        }
+
+        return false;
+    }
+};
+
+ResourceEditor::ResourceEditor(Context* context, const String& fullpath, UITabContainer *container):
+    Object(context), fullpath_(fullpath), container_(container),
+    editorTabLayout_(0), rootContentWidget_(0), button_(0)
+{
+
+    String filename = GetFileNameAndExtension(fullpath_);
+
+    editorTabLayout_ = new EditorTabLayout();
+    editorTabLayout_->SetID(TBIDC("tab"));
+
+    button_ = new UIButton(context_);
+    button_->SetText(filename.CString());
+    button_->SetSqueezable(true);
+    button_->SetSkinBg("TBButton.flat");
+    button_->SetValue(1);
+    editorTabLayout_->AddChild(button_->GetInternalWidget());
+
+    TBButton* closebutton = new TBButton();
+    editorTabLayout_->AddChild(closebutton);
+    closebutton->SetSkinBg(TBIDC("TBWindow.close"));
+    closebutton->SetIsFocusable(false);
+    closebutton->SetID(TBIDC("tabclose"));
+
+    editorTabLayout_->editor_ = this;
+    editorTabLayout_->button_ = (TBButton*) button_->GetInternalWidget();
+    editorTabLayout_->close_ = closebutton;
+    editorTabLayout_->container_ = (TBTabContainer*) container->GetInternalWidget();
+
+    ((TBTabContainer*)container_->GetInternalWidget())->GetTabLayout()->AddChild(editorTabLayout_);
+
+    rootContentWidget_ = new UIWidget(context_);
+    rootContentWidget_->SetGravity(WIDGET_GRAVITY_ALL);
+    container_->GetContentRoot()->AddChild(rootContentWidget_);
+
+    SubscribeToEvent(E_FILECHANGED, HANDLER(ResourceEditor, HandleFileChanged));
+}
+
+ResourceEditor::~ResourceEditor()
+{
+
+}
+
+void ResourceEditor::HandleFileChanged(StringHash eventType, VariantMap& eventData)
+{
+    using namespace FileChanged;
+    const String& fileName = eventData[P_FILENAME].GetString();
+    const String& resourceName = eventData[P_RESOURCENAME].GetString();
+
+    if (fullpath_ == fileName)
+    {
+        FileSystem* fs = GetSubsystem<FileSystem>();
+        if (!fs->FileExists(fullpath_))
+            Close();
+    }
+}
+
+void ResourceEditor::Close(bool navigateToAvailabeResource)
+{
+    // keep us alive through the close
+    SharedPtr<ResourceEditor> keepalive(this);
+
+    ((TBTabContainer*)container_->GetInternalWidget())->GetTabLayout()->RemoveChild(editorTabLayout_);
+
+    //MainFrame* frame = GetSubsystem<MainFrame>();
+    //ResourceFrame* rframe = frame->GetResourceFrame();
+    //rframe->CloseResourceEditor(this, navigateToAvailabeResource);
+}
+
+}

+ 64 - 0
Source/AtomicEditorWork/Editors/ResourceEditor.h

@@ -0,0 +1,64 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+
+#include <Atomic/UI/UIButton.h>
+#include <Atomic/UI/UITabContainer.h>
+
+using namespace Atomic;
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+class EditorTabLayout;
+
+class ResourceEditor: public Object
+{
+    OBJECT(ResourceEditor);
+
+public:
+
+    ResourceEditor(Context* context, const String& fullpath, UITabContainer* container);
+
+    virtual ~ResourceEditor();
+
+    UIButton* GetButton() { return button_; }
+
+    virtual bool HasUnsavedModifications() { return false; }
+
+    virtual void SetFocus() {}
+    virtual void Close(bool navigateToAvailabeResource = true);
+
+    virtual bool OnEvent(const TBWidgetEvent &ev) { return false; }
+
+    virtual bool FindText(const String& text, unsigned flags) { return false; }
+    virtual void FindTextClose() { }
+
+    virtual bool RequiresInspector() { return false; }
+
+    const String& GetFullPath() { return fullpath_; }
+
+    UIWidget* GetRootContentWidget() { return rootContentWidget_; }
+
+protected:
+
+    String fullpath_;
+
+    EditorTabLayout* editorTabLayout_;
+
+    SharedPtr<UITabContainer> container_;
+    SharedPtr<UIWidget> rootContentWidget_;
+    SharedPtr<UIButton> button_;
+
+private:
+
+    void HandleFileChanged(StringHash eventType, VariantMap& eventData);
+
+};
+
+}

+ 961 - 0
Source/AtomicEditorWork/Javascript/JSAST.cpp

@@ -0,0 +1,961 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+
+#include <rapidjson/document.h>
+#include <rapidjson/stringbuffer.h>
+#include <rapidjson/prettywriter.h>
+
+#include <Atomic/IO/Log.h>
+#include "JSAST.h"
+#include "JSASTVisitor.h"
+
+using namespace rapidjson;
+
+namespace AtomicEditor
+{
+
+JSASTNode::JSASTNode(JSASTType type) : type_(type), rangeStart_(0), rangeEnd_(0)
+{
+
+}
+
+bool JSASTNode::Parse(const rapidjson::Value& value)
+{
+    assert(value.IsObject());
+
+    for (Value::ConstMemberIterator itr = value.MemberBegin();
+         itr != value.MemberEnd(); ++itr)
+    {
+        String name = itr->name.GetString();
+
+        if (name == "loc")
+        {
+            ParseLoc(itr->value);
+        }
+        else if (name == "type")
+        {
+            // TODO: Verify type
+        }
+        else if (name == "range")
+        {
+            ParseRange(itr->value);
+        }
+
+    }
+
+    return true;
+
+}
+
+bool JSASTNode::ParseLoc(const rapidjson::Value& value)
+{
+    // SpiderMonkey can have this for loc :/
+    if (value.IsNull())
+        return true;
+
+    assert(value.IsObject());
+
+    const Value::Member* mstart = value.FindMember("start");
+    assert(mstart);
+
+    const Value::Member* mend = value.FindMember("end");
+    assert(mend);
+
+    loc_.startLine_ = mstart->value["line"].GetInt();
+    loc_.startColumn_ = mstart->value["column"].GetInt();
+    loc_.endLine_ = mend->value["line"].GetInt();
+    loc_.endColumn_ = mend->value["column"].GetInt();
+
+    return true;
+}
+
+bool JSASTNode::ParseRange(const rapidjson::Value& value)
+{
+    assert(value.IsArray() && value.Size() == 2);
+
+    rangeStart_ = value[SizeType(0)].GetInt();
+    rangeEnd_ = value[SizeType(1)].GetInt();
+
+    return true;
+
+}
+
+JSASTStatement* JSASTNode::ParseStatement(const rapidjson::Value &value)
+{
+    assert(value.IsObject());
+
+    JSASTStatement* statement = NULL;
+
+    const Value::Member* mtype = value.FindMember("type");
+    assert(mtype);
+
+    String type = mtype->value.GetString();
+
+    if (type == "ExpressionStatement")
+    {
+        statement = new JSASTExpressionStatement();
+    }
+    else if (type == "VariableDeclaration")
+    {
+        statement = new JSASTVariableDeclaration();
+    }
+    else if (type == "ReturnStatement")
+    {
+        statement = new JSASTReturnStatement();
+    }
+    else if (type == "FunctionDeclaration")
+    {
+        statement = new JSASTFunctionDeclaration();
+    }
+    else if (type == "IfStatement")
+    {
+        statement = new JSASTIfStatement();
+    }
+    else if (type == "BlockStatement")
+    {
+        statement = new JSASTBlockStatement();
+    }
+    else if (type == "EmptyStatement")
+    {
+        statement = new JSASTBlockStatement();
+    }
+    else if (type == "ForStatement")
+    {
+        statement = new JSASTForStatement();
+    }
+    else if (type == "LabeledStatement")
+    {
+        statement = new JSASTLabeledStatement();
+    }
+
+    if (!statement)
+    {
+        LOGWARNINGF("Unknown JSASTStatement: %s", type.CString());
+    }
+    else
+    {
+        statement->Parse(value);
+    }
+
+    return statement;
+
+}
+
+JSASTExpression* JSASTNode::ParseExpression(const rapidjson::Value &value, JSASTType astType)
+{
+    if (!value.IsObject())
+        return NULL;
+
+    JSASTExpression* expr = NULL;
+
+    const Value::Member* mtype = value.FindMember("type");
+    assert(mtype);
+
+    String type = mtype->value.GetString();
+    if (type == "Identifier" && (astType == JSAST_UNDEFINED || astType == JSAST_IDENTIFIER))
+    {
+        expr = new JSASTIdentifier();
+    }
+    else if (type == "Literal" && (astType == JSAST_UNDEFINED || astType == JSAST_LITERAL))
+    {
+        expr = new JSASTLiteral();
+    }
+    else if (type == "UnaryExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_UNARYEXPRESSION))
+    {
+        expr = new JSASTUnaryExpression();
+    }
+    else if (type == "UpdateExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_UPDATEEXPRESSION))
+    {
+        expr = new JSASTUpdateExpression();
+    }
+    else if (type == "NewExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_NEWEXPRESSION))
+    {
+        expr = new JSASTNewExpression();
+    }
+    else if (type == "FunctionExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_FUNCTIONEXPRESSION))
+    {
+        expr = new JSASTFunctionExpression();
+    }
+    else if (type == "BinaryExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_BINARYEXPRESSION))
+    {
+        expr = new JSASTBinaryExpression();
+    }
+    else if (type == "AssignmentExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_ASSIGNMENTEXPRESSION))
+    {
+        expr = new JSASTAssignmentExpression();
+    }
+    else if (type == "LogicalExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_LOGICALEXPRESSION))
+    {
+        expr = new JSASTAssignmentExpression();
+    }
+    else if (type == "CallExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_CALLEXPRESSION))
+    {
+        expr = new JSASTCallExpression();
+    }
+    else if (type == "VariableDeclarator" && (astType == JSAST_UNDEFINED || astType == JSAST_VARIABLEDECLARATOR))
+    {
+        expr = new JSASTVariableDeclarator();
+    }
+    else if (type == "MemberExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_MEMBEREXPRESSION))
+    {
+        expr = new JSASTMemberExpression();
+    }
+    else if (type == "ArrayExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_ARRAYEXPRESSION))
+    {
+        expr = new JSASTArrayExpression();
+    }
+    else if (type == "ObjectExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_OBJECTEXPRESSION))
+    {
+        expr = new JSASTObjectExpression();
+    }
+    else if (type == "ConditionalExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_CONDITIONALEXPRESSION))
+    {
+        expr = new JSASTConditionalExpression();
+    }
+    else if (type == "ThisExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_THISEXPRESSION))
+    {
+        expr = new JSASTThisExpression();
+    }
+
+    if (!expr)
+    {
+        LOGWARNINGF("Unknown JSASTExpression: %s", type.CString());
+    }
+    else
+    {
+        expr->Parse(value);
+    }
+
+    return expr;
+
+}
+
+bool JSASTNode::ParseExpressionArray(const rapidjson::Value &value, Vector<JSASTExpression *> &expressions)
+{
+
+    assert(value.IsArray());
+
+    for (Value::ConstValueIterator itr = value.Begin(); itr != value.End(); itr++)
+    {
+        JSASTExpression* expr = ParseExpression(*itr);
+        //assert(expr);
+        expressions.Push(expr);
+
+    }
+
+    return true;
+}
+
+
+bool JSASTNode::ParseStatementArray(const rapidjson::Value &value, Vector<JSASTStatement *> &statements)
+{
+
+    assert(value.IsArray());
+
+    for (Value::ConstValueIterator itr = value.Begin(); itr != value.End(); itr++)
+    {
+        JSASTStatement* stmt = ParseStatement(*itr);
+        //assert(stmt);
+        statements.Push(stmt);
+
+    }
+
+    return true;
+}
+
+bool JSASTThisExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    return true;
+}
+
+void JSASTThisExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTIdentifier::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    if (value.FindMember("name"))
+        name_ = value["name"].GetString();
+    return true;
+}
+
+void JSASTIdentifier::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTAssignmentExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    operator_ = value["operator"].GetString();
+
+    leftExpression_ = ParseExpression(value["left"]);
+    rightExpression_ = ParseExpression(value["right"]);
+
+    return true;
+}
+
+void JSASTAssignmentExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTLogicalExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    operator_ = value["operator"].GetString();
+
+    leftExpression_ = ParseExpression(value["left"]);
+    rightExpression_ = ParseExpression(value["right"]);
+
+    return true;
+}
+
+void JSASTLogicalExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+
+bool JSASTBinaryExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    operator_ = value["operator"].GetString();
+
+    leftExpression_ = ParseExpression(value["left"]);
+    rightExpression_ = ParseExpression(value["right"]);
+
+    return true;
+}
+
+void JSASTBinaryExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTLiteral::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    if (!value.FindMember("value") || !value.FindMember("raw"))
+    {
+        literalType_ = LITERAL_UNKNOWN;
+        return false;
+    }
+
+    const rapidjson::Value& v = value["value"];
+
+    raw_ = value["raw"].GetString();;
+
+    if (v.IsNull())
+        literalType_ = LITERAL_NULL;
+    else if (v.IsString())
+        literalType_ = LITERAL_STRING;
+    else if (v.IsNumber())
+        literalType_ = LITERAL_NUMBER;
+    else if (v.IsBool())
+        literalType_ = LITERAL_BOOLEAN;
+
+
+
+    return true;
+}
+
+void JSASTLiteral::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTArrayExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    ParseExpressionArray(value["elements"], elements_);
+
+    return true;
+}
+
+void JSASTArrayExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTProperty::Parse(const rapidjson::Value& value)
+{
+    key_ = ParseExpression(value["key"]);
+    assert(key_);
+
+    value_ = ParseExpression(value["value"]);
+    assert(value_);
+
+    return true;
+}
+
+void JSASTProperty::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTObjectExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    const rapidjson::Value& jprops = value["properties"];
+
+    assert(jprops.IsArray());
+
+    for (Value::ConstValueIterator itr = jprops.Begin(); itr != jprops.End(); itr++)
+    {
+
+        JSASTProperty* property = new JSASTProperty();
+        property->Parse(*itr);
+        properties_.Push(property);
+
+    }
+
+
+    return true;
+}
+
+void JSASTObjectExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTMemberExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    computed_ = value["computed"].GetBool();
+    object_ = ParseExpression(value["object"]);
+    property_ = ParseExpression(value["property"]);
+
+    return true;
+}
+
+void JSASTMemberExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+
+bool JSASTCallExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    callee_ = ParseExpression(value["callee"]);
+
+    ParseExpressionArray(value["arguments"], arguments_);
+
+    return true;
+
+}
+
+void JSASTCallExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTNewExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    callee_ = ParseExpression(value["callee"]);
+
+    ParseExpressionArray(value["arguments"], arguments_);
+
+    return true;
+
+}
+
+void JSASTNewExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTVariableDeclarator::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    id_ = ParseExpression(value["id"]);
+
+    const Value::Member* init = value.FindMember("init");
+    if (init && init->value.IsObject())
+    {
+        init_ = ParseExpression(init->value);
+    }
+
+    return true;
+
+}
+
+void JSASTVariableDeclarator::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTUnaryExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    argument_ = ParseExpression(value["argument"]);
+
+    prefix_ = value["prefix"].IsTrue();
+
+    operator_ = value["operator"].GetString();
+
+    return true;
+
+}
+
+void JSASTUnaryExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTUpdateExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    argument_ = ParseExpression(value["argument"]);
+
+    prefix_ = value["prefix"].IsTrue();
+
+    operator_ = value["operator"].GetString();
+
+    return true;
+
+}
+
+void JSASTUpdateExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTConditionalExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    test_ = ParseExpression(value["test"]);
+    consequent_ = ParseExpression(value["consequent"]);
+    alternate_ = ParseExpression(value["alternate"]);
+
+    return true;
+}
+
+void JSASTConditionalExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+
+bool JSASTFunctionExpression::Parse(const rapidjson::Value& value)
+{
+
+    JSASTExpression::Parse(value);
+
+    if (!value["id"].IsNull())
+    {
+        id_ = (JSASTIdentifier*) ParseExpression(value["id"],  JSAST_IDENTIFIER);
+    }
+
+    ParseExpressionArray(value["params"], params_);
+
+    const rapidjson::Value& body = value["body"];
+
+    String type = body["type"].GetString();
+
+    if (type == "BlockStatement")
+    {
+        body_ = (JSASTBlockStatement*) ParseStatement(body);
+    }
+    else
+    {
+        return true;
+    }
+
+
+    return true;
+}
+
+void JSASTFunctionExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+JSASTFunctionExpression::~JSASTFunctionExpression()
+{
+    if (id_)
+        delete id_;
+    if (rest_)
+        delete rest_;
+    if (body_)
+        delete body_;
+
+
+
+    for (unsigned i = 0; i < params_.Size(); i++)
+        delete params_[i];
+    for (unsigned i = 0; i < defaults_.Size(); i++)
+        delete defaults_[i];
+
+    params_.Clear();
+    defaults_.Clear();
+}
+
+// STATEMENTS
+
+bool JSASTExpressionStatement::Parse(const rapidjson::Value& value)
+{
+    JSASTStatement::Parse(value);
+
+    const Value::Member* expr = value.FindMember("expression");
+    assert(expr);
+
+    expression_ = ParseExpression(expr->value);
+
+    return expression_ != NULL;
+
+}
+
+void JSASTLabeledStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTLabeledStatement::Parse(const rapidjson::Value& value)
+{
+    JSASTStatement::Parse(value);
+
+    const Value::Member* body = value.FindMember("body");
+    assert(body);
+
+    const Value::Member* label = value.FindMember("label");
+    assert(label);
+
+
+    body_ = ParseStatement(body->value);
+    label_ = (JSASTIdentifier*) ParseExpression(label->value,  JSAST_IDENTIFIER);
+
+    return body_ && label_;
+
+}
+
+void JSASTExpressionStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTReturnStatement::Parse(const rapidjson::Value& value)
+{
+    JSASTStatement::Parse(value);
+
+    if (!value["argument"].IsNull())
+    {
+        argument_ = ParseExpression(value["argument"]);
+    }
+
+
+    return true;
+}
+
+void JSASTReturnStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+
+bool JSASTVariableDeclaration::Parse(const rapidjson::Value& value)
+{
+    JSASTDeclaration::Parse(value);
+
+    kind_ = value["kind"].GetString();
+
+    ParseExpressionArray(value["declarations"], declarations_);
+
+    return true;
+
+}
+
+void JSASTVariableDeclaration::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+JSASTComment::JSASTComment() : JSASTNode(JSAST_COMMENT) , isBlock_(false)
+{
+
+}
+
+JSASTComment::~JSASTComment()
+{
+
+}
+
+bool JSASTComment::Parse(const rapidjson::Value& value)
+{
+    JSASTNode::Parse(value);
+
+    // Block or Line
+    if (value.FindMember("type"))
+    {
+        String type = value["type"].GetString();
+        isBlock_ = type == "Block";
+    }
+
+    if (value.FindMember("value"))
+    {
+        value_ = value["value"].GetString();
+    }
+
+    return true;
+}
+
+void JSASTComment::Accept(JSASTVisitor *visitor)
+{
+    visitor->visit(this);
+}
+
+
+
+JSASTProgram::JSASTProgram(const String &path, const String &json) : JSASTNode(JSAST_PROGRAM)
+  , document_(new Document())
+{
+
+    if (document_->Parse<0>(json.CString()).HasParseError())
+    {
+        LOGERRORF("Could not parse JSON data from %s", path.CString());
+        return;
+    }
+    else
+    {
+        //StringBuffer buffer;
+        //PrettyWriter<StringBuffer> writer(buffer, &(document_->GetAllocator()));
+        //writer.SetIndent(' ', 3);
+        //document_->Accept(writer);
+        //LOGINFOF("%s", buffer.GetString());
+    }
+
+    Parse(*document_);
+
+}
+
+
+
+bool JSASTProgram::Parse(const rapidjson::Value& value)
+{
+    JSASTNode::Parse(value);
+
+    for (Value::ConstMemberIterator itr = value.MemberBegin();
+         itr != value.MemberEnd(); ++itr)
+    {
+        String name = itr->name.GetString();
+
+        if (name == "body")
+        {
+            ParseStatementArray(itr->value, body_);
+        }
+
+        if (name == "comments")
+        {
+            if (itr->value.IsArray())
+            {
+                for (Value::ConstValueIterator citr = itr->value.Begin();
+                     citr != itr->value.End();
+                     citr++)
+                {
+                    JSASTComment* comment = new JSASTComment();
+                    assert(citr->IsObject());
+                    comment->Parse(*citr);
+                    comments_.Push(comment);
+                }
+            }
+        }
+
+    }
+
+    return true;
+
+}
+
+JSASTProgram::~JSASTProgram()
+{
+    for (unsigned i = 0; i < comments_.Size(); i++)
+        delete comments_[i];
+
+    comments_.Clear();
+
+    delete document_;
+    document_ = 0;
+}
+
+void JSASTProgram::Accept(JSASTVisitor *visitor)
+{
+    visitor->visit(this);
+}
+
+
+JSASTProgram* JSASTProgram::ParseFromJSON(const String& path, const String& json)
+{
+    JSASTProgram* cunit = new JSASTProgram(path, json);
+
+    return cunit;
+}
+
+bool JSASTIfStatement::Parse(const rapidjson::Value& value)
+{
+    JSASTStatement::Parse(value);
+
+    test_ = ParseExpression(value["test"]);
+    consequent_ = ParseStatement(value["consequent"]);
+
+    if (!value["alternate"].IsNull())
+        alternate_ = ParseStatement(value["alternate"]);
+
+    return true;
+}
+
+void JSASTIfStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTFunctionDeclaration::Parse(const rapidjson::Value& value)
+{
+    JSASTDeclaration::Parse(value);
+
+    if (!value["id"].IsNull())
+    {
+        id_ = (JSASTIdentifier*) ParseExpression(value["id"],  JSAST_IDENTIFIER);
+    }
+
+    ParseExpressionArray(value["params"], params_);
+
+    const rapidjson::Value& body = value["body"];
+
+    String type = body["type"].GetString();
+
+    if (type == "BlockStatement")
+    {
+        body_ = (JSASTBlockStatement*) ParseStatement(body);
+    }
+    else
+    {
+
+    }
+
+
+    return true;
+}
+
+void JSASTFunctionDeclaration::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+JSASTFunctionDeclaration::~JSASTFunctionDeclaration()
+{
+    if (id_)
+        delete id_;
+    if (rest_)
+        delete rest_;
+    if (body_)
+        delete body_;
+
+    for (unsigned i = 0; i < params_.Size(); i++)
+        delete params_[i];
+    for (unsigned i = 0; i < defaults_.Size(); i++)
+        delete defaults_[i];
+
+    params_.Clear();
+    defaults_.Clear();
+}
+
+bool JSASTBlockStatement::Parse(const rapidjson::Value& value)
+{
+    if (value.FindMember("body"))
+    {
+        ParseStatementArray(value["body"], body_);
+
+    }
+
+    return true;
+
+}
+
+void JSASTBlockStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTForStatement::Parse(const rapidjson::Value& value)
+{
+    JSASTStatement::Parse(value);
+
+    if (value.FindMember("init") && !value.FindMember("init")->value.IsNull())
+    {
+        const Value::Member* member = value.FindMember("init");
+
+        String type = member->value.FindMember("type")->value.GetString();
+        if (type == "VariableDeclaration")
+        {
+            initVariable_ = (JSASTVariableDeclaration*) ParseStatement(member->value);
+        }
+        else
+        {
+            initExpression_ = ParseExpression(member->value);
+        }
+    }
+
+    if (value.FindMember("test") && !value.FindMember("test")->value.IsNull())
+    {
+        test_ = ParseExpression(value["test"]);
+    }
+
+    if (value.FindMember("update") && !value.FindMember("update")->value.IsNull())
+    {
+        update_ = ParseExpression(value["update"]);
+    }
+
+    if (value.FindMember("body"))
+    {
+        body_ = ParseStatement(value["body"]);
+    }
+
+    return true;
+}
+
+void JSASTForStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+}

+ 967 - 0
Source/AtomicEditorWork/Javascript/JSAST.h

@@ -0,0 +1,967 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+// see https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API
+
+#include <Atomic/Container/Str.h>
+#include <Atomic/Container/Vector.h>
+
+using namespace Atomic;
+
+namespace rapidjson
+{
+template<typename CharType> struct UTF8;
+class CrtAllocator;
+template <typename BaseAllocator> class MemoryPoolAllocator;
+template <typename Encoding, typename Allocator> class GenericValue;
+typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
+template <typename Encoding, typename Allocator> class GenericDocument;
+typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Document;
+}
+
+namespace AtomicEditor
+{
+
+enum JSASTType
+{
+    JSAST_UNDEFINED,
+    JSAST_COMMENT,
+    JSAST_PROGRAM,
+    JSAST_PROPERTY,
+    // Expression
+    JSAST_ASSIGNMENTEXPRESSION,
+    JSAST_LOGICALEXPRESSION,
+    JSAST_IDENTIFIER,
+    JSAST_CALLEXPRESSION,
+    JSAST_VARIABLEDECLARATOR,
+    JSAST_BINARYEXPRESSION,
+    JSAST_MEMBEREXPRESSION,
+    JSAST_LITERAL,
+    JSAST_ARRAYEXPRESSION,
+    JSAST_FUNCTIONEXPRESSION,
+    JSAST_NEWEXPRESSION,
+    JSAST_UNARYEXPRESSION,
+    JSAST_UPDATEEXPRESSION,
+    JSAST_CONDITIONALEXPRESSION,
+    JSAST_OBJECTEXPRESSION,
+    JSAST_THISEXPRESSION,
+    // Statements
+    JSAST_RETURNSTATEMENT,
+    JSAST_EMPTYSTATEMENT,
+    JSAST_EXPRESSIONSTATEMENT,
+    JSAST_LABELEDSTATEMENT,
+    JSAST_VARIABLEDECLARATION,
+    JSAST_FUNCTIONDECLARATION,
+    JSAST_IFSTATEMENT,
+    JSAST_BLOCKSTATEMENT
+};
+
+
+class JSASTProgram;
+class JSASTStatement;
+class JSASTEmptyStatement;
+class JSASTExpressionStatement;
+class JSASTExpression;
+class JSASTVisitor;
+class JSASTBlockStatement;
+
+class JSASTNode
+{    
+public:
+
+    struct Loc
+    {
+        int startLine_;
+        int startColumn_;
+        int endLine_;
+        int endColumn_;
+
+        Loc()
+        {
+            startLine_ = startColumn_ = -1;
+            endLine_ = endColumn_ = -1;
+        }
+
+        bool Valid() const { return startLine_ != -1 && startColumn_ != -1 &&
+                    endLine_ != -1 && endColumn_ != -1; }
+
+    };
+
+    JSASTNode(JSASTType type);
+    virtual bool Parse(const rapidjson::Value& value);
+
+    JSASTType GetType() { return type_; }
+    const Loc& GetLoc() { return loc_; }
+
+    virtual void Accept(JSASTVisitor* visitor) = 0;
+
+protected:
+
+    bool ParseLoc(const rapidjson::Value &value);
+    bool ParseRange(const rapidjson::Value &value);
+
+    JSASTStatement* ParseStatement(const rapidjson::Value &value);
+    JSASTExpression* ParseExpression(const rapidjson::Value &value, JSASTType astType = JSAST_UNDEFINED);
+
+    bool ParseExpressionArray(const rapidjson::Value &value, Vector<JSASTExpression *> &expressions);
+    bool ParseStatementArray(const rapidjson::Value &value, Vector<JSASTStatement *> &statements);
+
+private:
+
+    Loc loc_;
+    JSASTType type_;
+    // character range
+    int rangeStart_;
+    int rangeEnd_;
+
+
+};
+
+class JSASTComment: public JSASTNode
+{
+public:
+
+    JSASTComment();
+    virtual ~JSASTComment();
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetValue() { return value_; }
+    bool IsBlock() { return isBlock_; }
+
+private:
+
+    bool isBlock_;
+    String value_;
+
+};
+
+class JSASTProgram : public JSASTNode
+{
+public:
+
+    JSASTProgram(const String& path, const String& json);
+    virtual ~JSASTProgram();
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    static JSASTProgram* ParseFromJSON(const String& path, const String& json);
+
+    unsigned GetStatementCount() { return body_.Size(); }
+    JSASTStatement* GetStatement(unsigned idx) { return body_[idx]; }
+
+    unsigned GetCommentCount() { return comments_.Size(); }
+    JSASTComment* GetComment(unsigned idx) { return comments_[idx]; }
+
+
+protected:
+
+private:
+
+    Vector<JSASTStatement*> body_;
+    rapidjson::Document* document_;
+
+    Vector<JSASTComment*> comments_;
+
+};
+
+class JSASTExpression : public JSASTNode
+{
+public:
+
+    virtual ~JSASTExpression() {}
+
+    virtual bool Parse(const rapidjson::Value& value)
+    {
+        return JSASTNode::Parse(value);
+    }
+
+protected:
+    JSASTExpression(JSASTType type) :  JSASTNode(type) {}
+
+};
+
+
+class JSASTIdentifier : public JSASTExpression
+{
+
+public:
+    JSASTIdentifier() : JSASTExpression(JSAST_IDENTIFIER) {}
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+private:
+
+    String name_;
+};
+
+class JSASTThisExpression : public JSASTExpression
+{
+
+public:
+    JSASTThisExpression() : JSASTExpression(JSAST_THISEXPRESSION) {}
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+private:
+
+};
+
+
+class JSASTLiteral : public JSASTExpression
+{
+
+public:
+
+    enum LiteralType
+    {
+        LITERAL_UNKNOWN,
+        LITERAL_NULL,
+        LITERAL_STRING,
+        LITERAL_NUMBER,
+        LITERAL_BOOLEAN,
+        LITERAL_REGEX
+    };
+
+    JSASTLiteral() : JSASTExpression(JSAST_LITERAL), literalType_(LITERAL_UNKNOWN) {}
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    LiteralType GetLiteralType() { return literalType_; }
+
+private:
+
+    //value: string | boolean | null | number | RegExp;
+    LiteralType literalType_;
+    String raw_;
+};
+
+class JSASTArrayExpression : public JSASTExpression
+{
+
+public:
+    JSASTArrayExpression() : JSASTExpression(JSAST_ARRAYEXPRESSION) {}
+
+    virtual ~JSASTArrayExpression()
+    {
+        for (unsigned i = 0; i < elements_.Size(); i++)
+            delete elements_[i];
+
+        elements_.Clear();
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    unsigned GetElementCount() { return elements_.Size(); }
+    JSASTExpression* GetElement(unsigned idx) { return elements_[idx]; }
+
+private:
+
+    Vector<JSASTExpression*> elements_;
+
+};
+
+class JSASTProperty: public JSASTNode
+{
+
+public:
+
+    JSASTProperty() : JSASTNode(JSAST_PROPERTY), key_(0), value_(0) {}
+    virtual ~JSASTProperty()
+    {
+        if (key_)
+            delete key_;
+
+        if (value_)
+            delete value_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetKey() { return key_; }
+    JSASTExpression* GetValue() { return value_; }
+
+
+private:
+    JSASTExpression* key_;
+    JSASTExpression* value_;
+
+};
+
+
+class JSASTObjectExpression : public JSASTExpression
+{
+
+public:
+    JSASTObjectExpression() : JSASTExpression(JSAST_OBJECTEXPRESSION) {}
+
+    virtual ~JSASTObjectExpression()
+    {
+        for (unsigned i = 0; i < properties_.Size(); i++)
+            delete properties_[i];
+
+        properties_.Clear();
+    }
+
+    unsigned GetPropertyCount() { return properties_.Size(); }
+    JSASTProperty* GetProperty(unsigned idx) { return properties_[idx]; }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+private:
+
+    Vector<JSASTProperty*> properties_;
+};
+
+
+
+class JSASTAssignmentExpression : public JSASTExpression
+{
+
+public:
+    JSASTAssignmentExpression() : JSASTExpression(JSAST_ASSIGNMENTEXPRESSION),
+        leftExpression_(0), rightExpression_(0) {}
+
+    virtual ~JSASTAssignmentExpression()
+    {
+        if (leftExpression_)
+            delete leftExpression_;
+        if (rightExpression_)
+            delete rightExpression_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetOperator() { return operator_; }
+    JSASTExpression* GetLeft() { return leftExpression_; }
+    JSASTExpression* GetRight() { return rightExpression_; }
+
+private:
+
+    String operator_;
+    JSASTExpression* leftExpression_;
+    JSASTExpression* rightExpression_;
+
+};
+
+class JSASTLogicalExpression : public JSASTExpression
+{
+
+public:
+    JSASTLogicalExpression() : JSASTExpression(JSAST_LOGICALEXPRESSION),
+        leftExpression_(0), rightExpression_(0) {}
+
+    virtual ~JSASTLogicalExpression()
+    {
+        if (leftExpression_)
+            delete leftExpression_;
+        if (rightExpression_)
+            delete rightExpression_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetOperator() { return operator_; }
+    JSASTExpression* GetLeft() { return leftExpression_; }
+    JSASTExpression* GetRight() { return rightExpression_; }
+
+private:
+
+    String operator_;
+    JSASTExpression* leftExpression_;
+    JSASTExpression* rightExpression_;
+
+};
+
+
+class JSASTUnaryExpression : public JSASTExpression
+{
+
+public:
+    JSASTUnaryExpression() : JSASTExpression(JSAST_UNARYEXPRESSION),
+        argument_(0), prefix_(false) {}
+
+    virtual ~JSASTUnaryExpression()
+    {
+        if (argument_)
+            delete argument_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetOperator() { return operator_;}
+
+    JSASTExpression* GetArgument() { return argument_; }
+
+private:
+
+    String operator_;
+    bool prefix_;
+    JSASTExpression* argument_;
+
+};
+
+class JSASTUpdateExpression : public JSASTExpression
+{
+
+public:
+    JSASTUpdateExpression() : JSASTExpression(JSAST_UPDATEEXPRESSION),
+        argument_(0), prefix_(false) {}
+
+    virtual ~JSASTUpdateExpression()
+    {
+        if (argument_)
+            delete argument_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetOperator() { return operator_;}
+
+    JSASTExpression* GetArgument() { return argument_; }
+
+private:
+
+    String operator_;
+    bool prefix_;
+    JSASTExpression* argument_;
+
+};
+
+class JSASTFunctionExpression : public JSASTExpression
+{
+
+public:
+    JSASTFunctionExpression() : JSASTExpression(JSAST_FUNCTIONEXPRESSION),
+        id_(0), rest_(0), body_(0), generator_(false), expression_(false) {}
+
+    virtual ~JSASTFunctionExpression();
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTIdentifier* GetID() { return id_; }
+    JSASTBlockStatement* GetBodyStatement() { return body_; }
+
+    unsigned GetParamsCount() { return params_.Size(); }
+    JSASTExpression* GetParam(unsigned idx) { return params_[idx]; }
+
+private:
+
+    JSASTIdentifier* id_;
+    Vector<JSASTExpression*> params_;
+    Vector<JSASTExpression*> defaults_;
+    JSASTIdentifier* rest_;
+
+    JSASTBlockStatement* body_;
+
+    bool generator_;
+    bool expression_;
+
+};
+
+
+class JSASTBinaryExpression : public JSASTExpression
+{
+
+public:
+    JSASTBinaryExpression() : JSASTExpression(JSAST_BINARYEXPRESSION),
+        leftExpression_(0), rightExpression_(0) {}
+
+    virtual ~JSASTBinaryExpression()
+    {
+        if (leftExpression_)
+            delete leftExpression_;
+        if (rightExpression_)
+            delete rightExpression_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetLeft() { return leftExpression_; }
+    JSASTExpression* GetRight() { return rightExpression_; }
+
+    const String& GetOperator() { return operator_;}
+
+private:
+
+    String operator_;
+    JSASTExpression* leftExpression_;
+    JSASTExpression* rightExpression_;
+
+};
+
+class JSASTMemberExpression : public JSASTExpression
+{
+
+public:
+    JSASTMemberExpression() : JSASTExpression(JSAST_MEMBEREXPRESSION),
+        object_(0), property_(0), computed_(false) {}
+
+    virtual ~JSASTMemberExpression()
+    {
+        if (object_)
+            delete object_;
+        if (property_)
+            delete property_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetObject() { return object_;}
+    JSASTExpression* GetProperty() { return property_; }
+
+
+private:
+
+    JSASTExpression* object_;
+    JSASTExpression* property_;
+    bool computed_;
+};
+
+class JSASTConditionalExpression : public JSASTExpression
+{
+public:
+
+    JSASTConditionalExpression() : JSASTExpression(JSAST_CONDITIONALEXPRESSION), test_(0), consequent_(0), alternate_(0) {}
+
+    virtual ~JSASTConditionalExpression()
+    {
+        if (test_)
+            delete test_;
+        if (consequent_)
+            delete consequent_;
+        if (alternate_)
+            delete alternate_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetTest() { return test_; }
+    JSASTExpression* GetConsequent() { return consequent_; }
+    JSASTExpression* GetAlternate() { return alternate_; }
+
+private:
+
+    JSASTExpression* test_;
+    JSASTExpression* consequent_;
+    JSASTExpression* alternate_;
+
+};
+
+class JSASTCallExpression : public JSASTExpression
+{
+
+public:
+    JSASTCallExpression() : JSASTExpression(JSAST_CALLEXPRESSION),
+        callee_(0){}
+
+    virtual ~JSASTCallExpression()
+    {
+        if (callee_)
+            delete callee_;
+
+        for (unsigned i = 0; i < arguments_.Size(); i++)
+        {
+            delete arguments_[i];
+        }
+
+        arguments_.Clear();
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetCallee() { return callee_; }
+    unsigned GetArgumentCount() { return arguments_.Size(); }
+    JSASTExpression* GetArgument(unsigned idx) { return arguments_[idx]; }
+
+private:
+
+    JSASTExpression* callee_;
+    Vector<JSASTExpression*> arguments_;
+
+};
+
+class JSASTNewExpression : public JSASTExpression
+{
+
+public:
+    JSASTNewExpression() : JSASTExpression(JSAST_NEWEXPRESSION),
+        callee_(0){}
+
+    virtual ~JSASTNewExpression()
+    {
+        if (callee_)
+            delete callee_;
+
+        for (unsigned i = 0; i < arguments_.Size(); i++)
+        {
+            delete arguments_[i];
+        }
+
+        arguments_.Clear();
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetCallee() { return callee_; }
+    unsigned GetArgumentCount() { return arguments_.Size(); }
+    JSASTExpression* GetArgument(unsigned idx) { return arguments_[idx]; }
+
+
+private:
+
+    JSASTExpression* callee_;
+    Vector<JSASTExpression*> arguments_;
+
+};
+
+class JSASTVariableDeclarator : public JSASTExpression
+{
+public:
+
+    JSASTVariableDeclarator() : JSASTExpression(JSAST_VARIABLEDECLARATOR), id_(0), init_(0) {}
+
+    virtual ~JSASTVariableDeclarator()
+    {
+        if (id_)
+            delete id_;
+        if (init_)
+            delete init_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetID() { return id_; }
+    JSASTExpression* GetInit() { return init_; }
+
+
+private:
+
+    JSASTExpression* id_;
+    JSASTExpression* init_;
+
+};
+
+class JSASTStatement : public JSASTNode
+{
+
+public:
+
+    virtual ~JSASTStatement() {}
+
+    virtual bool Parse(const rapidjson::Value& value)
+    {
+        return JSASTNode::Parse(value);
+    }
+
+protected:
+    JSASTStatement(JSASTType type) :  JSASTNode(type) {}
+
+};
+
+class JSASTEmptyStatement : public JSASTStatement
+{
+
+public:
+    JSASTEmptyStatement() : JSASTStatement(JSAST_EMPTYSTATEMENT) {}
+
+    virtual bool Parse(const rapidjson::Value& value)
+    {
+        return JSASTStatement::Parse(value);
+    }
+    virtual void Accept(JSASTVisitor* visitor);
+
+};
+
+class JSASTExpressionStatement : public JSASTStatement
+{
+
+public:
+    JSASTExpressionStatement(JSASTType type = JSAST_EXPRESSIONSTATEMENT) : JSASTStatement(type),
+        expression_(0) {}
+
+    virtual ~JSASTExpressionStatement()
+    {
+        if (expression_)
+            delete expression_;
+    }
+
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetExpression() { return expression_; }
+
+protected:
+
+    JSASTExpression* expression_;
+
+};
+
+class JSASTLabeledStatement : public JSASTStatement
+{
+
+public:
+    JSASTLabeledStatement(JSASTType type = JSAST_LABELEDSTATEMENT) : JSASTStatement(type),
+        label_(0), body_(0) {}
+
+    virtual ~JSASTLabeledStatement()
+    {
+        if (label_)
+            delete label_;
+
+        if (body_)
+            delete body_;
+
+    }
+
+    JSASTIdentifier* GetLabel() { return label_; }
+    JSASTStatement* GetBody() { return body_; }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+protected:
+
+    JSASTIdentifier* label_;
+    JSASTStatement* body_;
+
+};
+
+
+class JSASTReturnStatement : public JSASTStatement
+{
+
+public:
+    JSASTReturnStatement(JSASTType type = JSAST_RETURNSTATEMENT) : JSASTStatement(type),
+        argument_(0) {}
+
+    virtual ~JSASTReturnStatement()
+    {
+        if (argument_)
+            delete argument_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetArgument() { return argument_; }
+
+protected:
+
+    JSASTExpression* argument_;
+
+};
+
+
+class JSASTDeclaration : public JSASTStatement
+{
+
+public:
+
+    virtual bool Parse(const rapidjson::Value& value) { return JSASTStatement::Parse(value); }
+
+protected:
+
+    JSASTDeclaration(JSASTType type) : JSASTStatement(type) {}
+
+};
+
+class JSASTBlockStatement : public JSASTStatement
+{
+public:
+    JSASTBlockStatement() : JSASTStatement(JSAST_BLOCKSTATEMENT) {}
+
+    virtual ~JSASTBlockStatement()
+    {
+        for (unsigned i = 0; i < body_.Size(); i++)
+            delete body_[i];
+
+        body_.Clear();
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    unsigned GetStatementCount() { return body_.Size(); }
+    JSASTStatement* GetStatement(unsigned idx) { return body_[idx]; }
+
+
+private:
+
+    Vector<JSASTStatement*> body_;
+
+};
+
+class JSASTIfStatement : public JSASTStatement
+{
+public:
+
+    JSASTIfStatement() : JSASTStatement(JSAST_IFSTATEMENT), test_(0), consequent_(0), alternate_(0) {}
+
+    virtual ~JSASTIfStatement()
+    {
+        if (test_)
+            delete test_;
+        if (consequent_)
+            delete consequent_;
+        if (alternate_)
+            delete alternate_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetTest() { return test_; }
+    JSASTStatement* GetConsequent() { return consequent_; }
+    JSASTStatement* GetAlternate() { return alternate_; }
+
+private:
+
+    JSASTExpression* test_;
+    JSASTStatement* consequent_;
+    JSASTStatement* alternate_;
+
+};
+
+class JSASTVariableDeclaration : public JSASTDeclaration
+{
+
+public:
+
+    JSASTVariableDeclaration() : JSASTDeclaration(JSAST_VARIABLEDECLARATION) {}
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetKind()  { return kind_; }
+
+    unsigned GetDeclarationsCount() { return declarations_.Size(); }
+    JSASTExpression* GetDeclaration(unsigned idx) { return declarations_[idx]; }
+
+protected:
+
+    // "var" | "let" | "const";
+    String kind_;
+
+    // Array of JSASTVariableDeclarator
+    Vector<JSASTExpression*> declarations_;
+
+};
+
+
+class JSASTFunctionDeclaration : public JSASTDeclaration
+{
+
+public:
+    JSASTFunctionDeclaration() : JSASTDeclaration(JSAST_FUNCTIONDECLARATION),
+        id_(0), rest_(0), body_(0), generator_(false), expression_(false) {}
+
+    virtual ~JSASTFunctionDeclaration();
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTIdentifier* GetID() { return id_; }
+    JSASTBlockStatement* GetBodyStatement() { return body_; }
+
+    unsigned GetParamsCount() { return params_.Size(); }
+    JSASTExpression* GetParam(unsigned idx) { return params_[idx]; }
+
+private:
+
+    JSASTIdentifier* id_;
+    Vector<JSASTExpression*> params_;
+    Vector<JSASTExpression*> defaults_;
+    JSASTIdentifier* rest_;
+
+    JSASTBlockStatement* body_;
+
+    bool generator_;
+    bool expression_;
+
+};
+
+/*
+interface ForStatement <: Statement {
+    type: "ForStatement";
+    init: VariableDeclaration | Expression | null;
+    test: Expression | null;
+    update: Expression | null;
+    body: Statement;
+}
+*/
+
+
+class JSASTForStatement : public JSASTStatement
+{
+public:
+
+    JSASTForStatement() : JSASTStatement(JSAST_IFSTATEMENT),
+        initVariable_(0), initExpression_(0), test_(0), update_(0), body_(0)
+    {
+
+    }
+
+    virtual ~JSASTForStatement()
+    {
+        if (initVariable_)
+            delete initVariable_;
+
+        if (initExpression_)
+            delete initExpression_;
+
+        if (test_)
+            delete(test_);
+
+        if(update_)
+            delete update_;
+
+        if (body_)
+            delete body_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetTest() { return test_; }
+    JSASTVariableDeclaration* GetInitVariable() { return initVariable_; }
+    JSASTExpression* GetInitExpression() { return initExpression_; }
+    JSASTExpression* GetUpdate() { return update_; }
+    JSASTStatement* GetBody() { return body_; }
+
+private:
+    JSASTVariableDeclaration* initVariable_;
+    JSASTExpression* initExpression_;
+    JSASTExpression* test_;
+    JSASTExpression* update_;
+    JSASTStatement* body_;
+
+};
+
+
+}
+
+

+ 7 - 0
Source/AtomicEditorWork/Javascript/JSASTSyntaxColorVisitor.cpp

@@ -0,0 +1,7 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+
+#include "JSASTSyntaxColorVisitor.h"

+ 360 - 0
Source/AtomicEditorWork/Javascript/JSASTSyntaxColorVisitor.h

@@ -0,0 +1,360 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+
+#include "JSASTVisitor.h"
+#include "JSTheme.h"
+
+#include <TurboBadger/tb_style_edit.h>
+
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+// TBBlock::InsertText, handle text input, split/layout
+
+class JSASTSyntaxColorVisitor : public JSASTTraversalVisitor
+{
+public:
+
+    JSASTSyntaxColorVisitor(TBStyleEdit* styleEdit) : styleEdit_(styleEdit) {}
+
+    using JSASTTraversalVisitor::visit;
+
+    virtual JSASTExpression* visit(JSASTIdentifier* expr)
+    {
+        //SetLocColor(expr->GetLoc(), 6);
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTLiteral* expr)
+    {
+        JSThemeColor color = JSTHEME_NORMAL;
+        switch (expr->GetLiteralType())
+        {
+        case JSASTLiteral::LITERAL_BOOLEAN:
+            color = JSTHEME_LITERAL_BOOLEAN;
+            break;
+        case JSASTLiteral::LITERAL_STRING:
+            color = JSTHEME_LITERAL_STRING;
+            break;
+        case JSASTLiteral::LITERAL_NULL:
+            color = JSTHEME_LITERAL_NULL;
+            break;
+        case JSASTLiteral::LITERAL_NUMBER:
+            color = JSTHEME_LITERAL_NUMBER;
+            break;
+        case JSASTLiteral::LITERAL_REGEX:
+            color = JSTHEME_LITERAL_REGEX;
+            break;
+
+        default:
+            break;
+        }
+
+        SetLocColor(expr->GetLoc(), color);
+        return expr;
+    }
+
+    virtual JSASTStatement* visit(JSASTFunctionDeclaration* decl)
+    {
+        SetLocColor(decl->GetLoc(), JSTHEME_FUNCTION, true);
+
+        JSASTNode* id = decl->GetID();
+        if (id)
+            SetLocColor(id->GetLoc(), JSTHEME_CODE, true);
+
+        for (unsigned i = 0; i < decl->GetParamsCount(); i++)
+        {
+            JSASTExpression* p = decl->GetParam(i);
+            SetLocColor(p->GetLoc(), JSTHEME_FUNCTIONDECLARG, true);
+        }
+
+
+        return JSASTTraversalVisitor::visit(decl);
+
+    }
+
+    virtual JSASTStatement* visit(JSASTVariableDeclaration* decl)
+    {
+        SetLocColor(decl->GetLoc(), JSTHEME_VAR, true);
+
+        SetLocColor(decl->GetLoc(), "=", JSTHEME_KEYWORD, true);
+
+        for (unsigned i = 0; i < decl->GetDeclarationsCount(); i++)
+        {
+            JSASTExpression* expr = decl->GetDeclaration(i);
+
+            if (expr->GetType() == JSAST_VARIABLEDECLARATOR)
+            {
+                JSASTVariableDeclarator* v = (JSASTVariableDeclarator*) expr;
+
+                if (JSASTExpression* init = v->GetInit())
+                {
+                    if (init->GetType() == JSAST_FUNCTIONEXPRESSION)
+                    {
+                        if (v->GetID())
+                            SetLocColor(v->GetID()->GetLoc(), JSTHEME_CODE, true);
+                    }
+                }
+            }
+
+        }
+
+        return JSASTTraversalVisitor::visit(decl);
+    }
+
+    virtual JSASTComment* visit(JSASTComment* comment)
+    {
+        SetLocColor(comment->GetLoc(), JSTHEME_COMMENT, false);
+        return comment;
+    }
+
+    virtual JSASTStatement* visit(JSASTIfStatement* stmt)
+    {
+        SetLocColor(stmt->GetLoc(), JSTHEME_KEYWORD, true);
+        return JSASTTraversalVisitor::visit(stmt);
+    }
+
+    virtual JSASTExpression* visit(JSASTConditionalExpression* expr)
+    {
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+
+    virtual JSASTStatement* visit(JSASTForStatement* stmt)
+    {
+        SetLocColor(stmt->GetLoc(), JSTHEME_KEYWORD, true);
+        return JSASTTraversalVisitor::visit(stmt);
+    }
+
+
+    virtual JSASTExpression* visit(JSASTCallExpression* expr)
+    {
+
+        JSASTNode* locNode = expr->GetCallee();
+        if (locNode->GetType() == JSAST_MEMBEREXPRESSION)
+        {
+            locNode = ((JSASTMemberExpression*) locNode)->GetProperty();
+        }
+
+        //SetLocColor(locNode->GetLoc(), 2);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+    virtual JSASTStatement* visit(JSASTReturnStatement *stmt)
+    {
+        SetLocColor(stmt->GetLoc(), JSTHEME_KEYWORD, true);
+
+        return JSASTTraversalVisitor::visit(stmt);
+    }
+
+    virtual JSASTExpression* visit(JSASTUnaryExpression* expr)
+    {
+        const String& op = expr->GetOperator();
+
+        SetLocColor(expr->GetLoc(), op, JSTHEME_KEYWORD, false);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+    virtual JSASTExpression* visit(JSASTUpdateExpression* expr)
+    {
+        const String& op = expr->GetOperator();
+
+        SetLocColor(expr->GetLoc(), op, JSTHEME_KEYWORD, false);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+
+    virtual JSASTExpression* visit(JSASTBinaryExpression* expr)
+    {
+        const String& op = expr->GetOperator();
+
+        SetLocColor(expr->GetLoc(), op, JSTHEME_KEYWORD, false);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+    virtual JSASTExpression* visit(JSASTAssignmentExpression* expr)
+    {
+        const String& op = expr->GetOperator();
+
+        SetLocColor(expr->GetLoc(), op, JSTHEME_KEYWORD, false);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+    virtual JSASTExpression* visit(JSASTLogicalExpression* expr)
+    {
+        const String& op = expr->GetOperator();
+
+        SetLocColor(expr->GetLoc(), op, JSTHEME_KEYWORD, false);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+
+    virtual JSASTExpression* visit(JSASTNewExpression* expr)
+    {
+        SetLocColor(expr->GetLoc(), JSTHEME_KEYWORD, true);
+
+        JSASTNode* locNode = expr->GetCallee();
+
+        if (locNode->GetType() == JSAST_MEMBEREXPRESSION)
+        {
+            locNode = ((JSASTMemberExpression*) locNode)->GetProperty();
+        }
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+    virtual JSASTExpression* visit(JSASTFunctionExpression* expr)
+    {
+        SetLocColor(expr->GetLoc(), "function", JSTHEME_FUNCTION, true);
+
+        for (unsigned i = 0; i < expr->GetParamsCount(); i++)
+        {
+            JSASTExpression* p = expr->GetParam(i);
+            SetLocColor(p->GetLoc(), JSTHEME_FUNCTIONDECLARG, true);
+        }
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+
+private:
+
+    void SetLocColor(const JSASTNode::Loc& loc, unsigned color, bool single = false)
+    {
+        if (!loc.Valid())
+            return;
+
+        PODVector<TBTextFragment*> fragments;
+        GetTextFragments(loc, fragments);
+        for (unsigned i = 0; i < fragments.Size(); i++)
+        {
+            fragments.At(i)->themeColor = color;
+            if (single)
+                break;
+        }
+    }
+
+    void SetLocColor(const JSASTNode::Loc& loc, const String& value, unsigned color, bool single = false)
+    {
+        if (!loc.Valid())
+            return;
+
+        String substring = value;
+
+        PODVector<TBTextFragment*> fragments;
+        GetTextFragments(loc, fragments);
+        for (unsigned i = 0; i < fragments.Size(); i++)
+        {
+            TBTextFragment* fragment = fragments[i];
+
+            if (!strncmp(substring.CString(), fragment->Str(), substring.Length()))
+            {
+                fragments.At(i)->themeColor = color;
+
+                substring = substring.Substring(fragments.At(i)->len);
+
+                if (single || !substring.Length())
+                    break;
+            }
+        }
+    }
+
+
+    TBTextFragment* GetFragmentAt( const JSASTNode::Loc& loc)
+    {
+        if (!loc.Valid())
+            return NULL;
+
+        int line = loc.startLine_ - 1;
+
+        TBBlock *block = styleEdit_->blocks.GetFirst();
+        int count = 0;
+        while (block && count < line)
+        {
+            block = block->GetNext();
+            count++;
+        }
+
+        if (!block)
+            return NULL;
+
+        for (TBTextFragment* frag = block->fragments.GetFirst(); frag; frag = frag->GetNext())
+        {
+            if (frag->ofs >= loc.startColumn_)
+                return frag;
+        }
+
+        return NULL;
+
+    }
+
+
+    void GetTextFragments( const JSASTNode::Loc& loc,  PODVector<TBTextFragment*>& fragments)
+    {
+        if (!loc.Valid())
+            return;
+
+        for (int lcounter = loc.startLine_; lcounter <= loc.endLine_; lcounter++)
+        {
+            int line = lcounter - 1;
+
+            TBBlock *block = styleEdit_->blocks.GetFirst();
+            int count = 0;
+            while (block && count < line)
+            {
+                block = block->GetNext();
+                count++;
+            }
+
+            if (!block)
+                return;
+
+            for (TBTextFragment* frag = block->fragments.GetFirst(); frag; frag = frag->GetNext())
+            {
+                if (loc.startLine_ == loc.endLine_)
+                {
+                    if (frag->ofs >= loc.startColumn_ && frag->ofs < loc.endColumn_)
+                        fragments.Push(frag);
+                }
+                else
+                {
+                    if (lcounter == loc.startLine_)
+                    {
+                        if (frag->ofs >= loc.startColumn_)
+                            fragments.Push(frag);
+
+                    }
+                    else if (lcounter == loc.endLine_)
+                    {
+                        if (frag->ofs < loc.endColumn_)
+                            fragments.Push(frag);
+
+                    }
+                    else
+                    {
+                        fragments.Push(frag);
+                    }
+                }
+            }
+        }
+
+    }
+
+    TBStyleEdit* styleEdit_;
+
+};
+
+
+}

+ 7 - 0
Source/AtomicEditorWork/Javascript/JSASTVisitor.cpp

@@ -0,0 +1,7 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+
+#include "JSASTVisitor.h"

+ 382 - 0
Source/AtomicEditorWork/Javascript/JSASTVisitor.h

@@ -0,0 +1,382 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include "JSAST.h"
+
+namespace AtomicEditor
+{
+
+class JSASTVisitor
+{
+public:
+
+    virtual JSASTProgram* visit(JSASTProgram *program) = 0;
+
+    virtual JSASTStatement* visit(JSASTBlockStatement *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTExpressionStatement *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTIfStatement *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTFunctionDeclaration *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTVariableDeclaration *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTReturnStatement *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTEmptyStatement *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTForStatement *stmt) = 0;
+
+    virtual JSASTExpression* visit(JSASTAssignmentExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTLogicalExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTConditionalExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTThisExpression* expr) = 0;
+
+    virtual JSASTExpression* visit(JSASTIdentifier* expr) = 0;
+    virtual JSASTExpression* visit(JSASTCallExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTBinaryExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTMemberExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTLiteral* expr) = 0;
+    virtual JSASTExpression* visit(JSASTArrayExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTObjectExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTFunctionExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTNewExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTUnaryExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTUpdateExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTVariableDeclarator* expr) = 0;    
+
+    virtual JSASTProperty* visit(JSASTProperty* property) = 0;
+    virtual JSASTStatement*  visit(JSASTLabeledStatement* property) = 0;
+
+    virtual JSASTComment* visit(JSASTComment* expr) = 0;
+
+};
+
+class JSASTTraversalVisitor : public JSASTVisitor
+{
+public:
+
+    virtual JSASTProgram* visit(JSASTProgram *program)
+    {
+        // this would cause recursion
+        // program->Accept(this);
+
+        unsigned count = program->GetCommentCount();
+        for (unsigned i = 0; i < count; i++)
+        {
+            JSASTComment* cmt = program->GetComment(i);
+            if (cmt)
+                cmt->Accept(this);
+        }
+
+
+        count = program->GetStatementCount();
+        for (unsigned i = 0; i < count; i++)
+        {
+            JSASTStatement* stmt = program->GetStatement(i);
+            if (stmt)
+                stmt->Accept(this);
+        }
+
+        return program;
+
+    }
+
+    virtual JSASTStatement* visit(JSASTForStatement *stmt)
+    {
+        if (stmt->GetInitVariable())
+            stmt->GetInitVariable()->Accept(this);
+        if (stmt->GetInitExpression())
+            stmt->GetInitExpression()->Accept(this);
+
+        if (stmt->GetTest())
+            stmt->GetTest()->Accept(this);
+
+        if (stmt->GetUpdate())
+            stmt->GetUpdate()->Accept(this);
+
+        if (stmt->GetBody())
+            stmt->GetBody()->Accept(this);
+
+        return stmt;
+
+    }
+
+    virtual JSASTStatement* visit(JSASTBlockStatement *block)
+    {
+        unsigned count = block->GetStatementCount();
+
+        for (unsigned i = 0; i < count; i++)
+        {
+            JSASTStatement* stmt = block->GetStatement(i);
+            if (stmt)
+                stmt->Accept(this);
+        }
+
+        return block;
+
+    }
+
+    virtual JSASTStatement* visit(JSASTExpressionStatement *stmt)
+    {
+        if (stmt->GetExpression())
+            stmt->GetExpression()->Accept(this);
+
+        return stmt;
+    }
+
+    virtual JSASTStatement* visit(JSASTIfStatement *stmt)
+    {
+        if (stmt->GetTest())
+            stmt->GetTest()->Accept(this);
+        if (stmt->GetConsequent())
+            stmt->GetConsequent()->Accept(this);
+        if (stmt->GetAlternate())
+            stmt->GetAlternate()->Accept(this);
+
+        return stmt;
+    }
+
+    virtual JSASTStatement* visit(JSASTFunctionDeclaration *stmt)
+    {
+        if (stmt->GetID())
+            stmt->GetID()->Accept(this);
+
+        for (unsigned i = 0; i < stmt->GetParamsCount(); i++)
+            stmt->GetParam(i)->Accept(this);
+
+        JSASTBlockStatement* body = stmt->GetBodyStatement();
+
+        if (body)
+            body->Accept(this);
+
+        return stmt;
+
+    }
+
+    virtual JSASTStatement* visit(JSASTVariableDeclaration *stmt)
+    {
+        for (unsigned i = 0; i < stmt->GetDeclarationsCount(); i++)
+            stmt->GetDeclaration(i)->Accept(this);
+
+        return stmt;
+    }
+
+    virtual JSASTStatement* visit(JSASTReturnStatement *stmt)
+    {
+        if (stmt->GetArgument())
+            stmt->GetArgument()->Accept(this);
+
+        return stmt;
+    }
+
+    virtual JSASTStatement* visit(JSASTEmptyStatement *stmt)
+    {
+        return stmt;
+    }
+
+    virtual JSASTExpression* visit(JSASTAssignmentExpression* expr)
+    {
+        if (expr->GetLeft())
+            expr->GetLeft()->Accept(this);
+
+        if (expr->GetRight())
+            expr->GetRight()->Accept(this);
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTConditionalExpression *expr)
+    {
+        if (expr->GetTest())
+            expr->GetTest()->Accept(this);
+        if (expr->GetConsequent())
+            expr->GetConsequent()->Accept(this);
+        if (expr->GetAlternate())
+            expr->GetAlternate()->Accept(this);
+
+        return expr;
+    }
+
+
+    virtual JSASTExpression* visit(JSASTLogicalExpression* expr)
+    {
+        if (expr->GetLeft())
+            expr->GetLeft()->Accept(this);
+
+        if (expr->GetRight())
+            expr->GetRight()->Accept(this);
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTIdentifier* expr)
+    {
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTCallExpression* expr)
+    {
+        if (expr->GetCallee())
+            expr->GetCallee()->Accept(this);
+
+        for (unsigned i = 0; i < expr->GetArgumentCount(); i++)
+        {   if (expr->GetArgument(i))
+                expr->GetArgument(i)->Accept(this);
+        }
+
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTBinaryExpression* expr)
+    {
+        if (expr->GetLeft())
+            expr->GetLeft()->Accept(this);
+
+        if (expr->GetRight())
+            expr->GetRight()->Accept(this);
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTMemberExpression* expr)
+    {
+        if (expr->GetObject())
+            expr->GetObject()->Accept(this);
+
+        if (expr->GetProperty())
+            expr->GetProperty()->Accept(this);
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTLiteral* expr)
+    {
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTArrayExpression* expr)
+    {
+        for (unsigned i = 0; i < expr->GetElementCount(); i++)
+        {
+            JSASTExpression* arrayElement = expr->GetElement(i);
+            if (arrayElement)
+                arrayElement->Accept(this);
+        }
+
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTObjectExpression* expr)
+    {
+        for (unsigned i = 0; i < expr->GetPropertyCount(); i++)
+        {
+            JSASTProperty* property = expr->GetProperty(i);
+
+            if (property)
+                property->Accept(this);
+        }
+
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTFunctionExpression* expr)
+    {
+        if (expr->GetID())
+            expr->GetID()->Accept(this);
+
+        for (unsigned i = 0; i < expr->GetParamsCount(); i++)
+            expr->GetParam(i)->Accept(this);
+
+        JSASTBlockStatement* body = expr->GetBodyStatement();
+
+        if (body)
+            body->Accept(this);
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTNewExpression* expr)
+    {
+        if (expr->GetCallee())
+            expr->GetCallee()->Accept(this);
+
+        for (unsigned i = 0; i < expr->GetArgumentCount(); i++)
+        {
+            if (expr->GetArgument(i))
+                expr->GetArgument(i)->Accept(this);
+        }
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTUnaryExpression* expr)
+    {
+        if (expr->GetArgument())
+            expr->GetArgument()->Accept(this);
+
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTUpdateExpression* expr)
+    {
+        if (expr->GetArgument())
+            expr->GetArgument()->Accept(this);
+
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTVariableDeclarator* expr)
+    {
+        if (expr->GetID())
+            expr->GetID()->Accept(this);
+
+        if (expr->GetInit())
+            expr->GetInit()->Accept(this);
+
+
+        return expr;
+    }
+
+    virtual JSASTComment* visit(JSASTComment* comment)
+    {
+        return comment;
+    }
+
+    virtual JSASTProperty* visit(JSASTProperty* property)
+    {
+        if (property->GetKey())
+            property->GetKey()->Accept(this);
+        if (property->GetValue())
+            property->GetValue()->Accept(this);
+
+        return property;
+    }
+
+    virtual JSASTStatement* visit(JSASTLabeledStatement* labeledStatement)
+    {
+        if (labeledStatement->GetLabel())
+            labeledStatement->GetLabel()->Accept(this);
+        if (labeledStatement->GetBody())
+            labeledStatement->GetBody()->Accept(this);
+
+        return labeledStatement;
+    }
+
+
+    virtual JSASTExpression* visit(JSASTThisExpression* expr)
+    {
+        return expr;
+    }
+
+
+};
+
+
+
+}
+
+

+ 190 - 0
Source/AtomicEditorWork/Javascript/JSAutocomplete.cpp

@@ -0,0 +1,190 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/IO/Log.h>
+#include "JSAutocomplete.h"
+
+
+namespace AtomicEditor
+{
+
+JSAutocomplete::JSAutocomplete(TBEditField* editField) :
+    editField_(editField),
+    styleEdit_(editField->GetStyleEdit()),
+    autoList_(0)
+{
+    autoList_ = new TBSelectList();
+    autoList_->SetSource(&autoSource_);
+
+    autoList_->SetSize(150, 100);
+    autoList_->SetIgnoreInput(true);
+
+    editField->AddChild(autoList_);
+
+    Hide();
+}
+
+bool JSAutocomplete::UpdateCompletions(const String& value)
+{
+    if (value == currentValue_)
+        return autoSource_.GetNumItems() > 0;
+
+    currentValue_ = value;
+
+    String lstring(value[0]);
+    if (!locals_.Contains(lstring))
+    {
+        return false;
+    }
+
+    autoSource_.DeleteAllItems();
+
+    const List<String>& tokens = locals_[lstring];
+
+    for (List<String>::ConstIterator i = tokens.Begin(); i != tokens.End(); ++i)
+    {
+        if (*i != value && i->StartsWith(value))
+        {
+            autoSource_.AddItem(new TBGenericStringItem(i->CString()));
+        }
+
+    }
+
+    autoList_->SetValue(0);
+
+    return autoSource_.GetNumItems() > 0;
+
+}
+
+void JSAutocomplete::UpdateLocals()
+{
+    TBBlock *block = styleEdit_->blocks.GetFirst();
+    while (block)
+    {
+        for (TBTextFragment* frag = block->fragments.GetFirst(); frag; frag = frag->GetNext())
+        {
+            if (frag->len > 3)
+            {
+                String lstring(frag->Str()[0]);
+
+                String token(frag->Str(), frag->len);
+
+                if (!locals_[lstring].Contains(token))
+                {
+                    //LOGINFOF("Adding Token %s to locals", token.CString());
+                    locals_[lstring].Push(token);
+                }
+
+            }
+
+        }
+
+        block = block->GetNext();
+    }
+}
+
+void JSAutocomplete::SetPosition(const TBPoint &pos)
+{
+    autoList_->SetPosition(pos);
+}
+
+bool JSAutocomplete::OnEvent(const TBWidgetEvent &ev)
+{
+    if (ev.type == EVENT_TYPE_KEY_DOWN)
+    {
+        if (ev.special_key == TB_KEY_UP)
+        {
+            int v = autoList_->GetValue() - 1;
+            if (v < 0)
+            {
+                styleEdit_->autocomplete_visible = false;
+                autoList_->SetVisibilility(WIDGET_VISIBILITY_INVISIBLE);
+                autoList_->SetValue(0);
+                return styleEdit_->KeyDown(ev.key, ev.special_key, ev.modifierkeys);
+
+            }
+            else
+            {
+                autoList_->SetValue(v);
+            }
+        }
+        else if (ev.special_key == TB_KEY_DOWN)
+        {
+            int v = autoList_->GetValue() + 1;
+            if (v >= autoList_->GetSource()->GetNumItems())
+            {
+                styleEdit_->autocomplete_visible = false;
+                autoList_->SetVisibilility(WIDGET_VISIBILITY_INVISIBLE);
+                autoList_->SetValue(0);
+                return styleEdit_->KeyDown(ev.key, ev.special_key, ev.modifierkeys);
+            }
+            else
+            {
+                autoList_->SetValue(v);
+            }
+        }
+        else if (ev.special_key == TB_KEY_ENTER || ev.special_key == TB_KEY_TAB)
+        {
+            TBStr str = autoList_->GetSource()->GetItemString(autoList_->GetValue());
+
+            TBTextFragment* fragment = 0;
+            int ofs = styleEdit_->caret.pos.ofs;
+            if (ofs >= 0)
+            {
+                fragment = styleEdit_->caret.pos.block->FindFragment(ofs, true);
+                if (fragment)
+                {
+                    int gofs = fragment->GetGlobalOfs();
+                    styleEdit_->selection.Select(gofs, gofs + fragment->len);
+                    styleEdit_->InsertText(str.CStr(), str.Length());
+                }
+            }
+
+            styleEdit_->autocomplete_visible = false;
+            autoList_->SetVisibilility(WIDGET_VISIBILITY_INVISIBLE);
+            autoList_->SetValue(0);
+
+            return true;
+        }
+        else if (ev.special_key == TB_KEY_ESC)
+        {
+            styleEdit_->autocomplete_visible = false;
+            autoList_->SetVisibilility(WIDGET_VISIBILITY_INVISIBLE);
+            autoList_->SetValue(0);
+            return true;
+        }
+
+    }
+
+    return false;
+}
+
+JSAutocomplete::~JSAutocomplete()
+{
+    autoList_->SetSource(NULL);
+    autoSource_.DeleteAllItems();
+}
+
+void JSAutocomplete::Show()
+{
+    styleEdit_->autocomplete_visible = true;
+    autoList_->SetVisibilility(WIDGET_VISIBILITY_VISIBLE);
+    autoList_->SetValue(0);
+}
+
+void JSAutocomplete::Hide()
+{
+    styleEdit_->autocomplete_visible = false;
+    autoList_->SetVisibilility(WIDGET_VISIBILITY_INVISIBLE);
+    autoList_->SetValue(0);
+}
+
+bool JSAutocomplete::Visible()
+{
+    return autoList_->GetVisibility() == WIDGET_VISIBILITY_VISIBLE;
+}
+
+
+}

+ 56 - 0
Source/AtomicEditorWork/Javascript/JSAutocomplete.h

@@ -0,0 +1,56 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/Container/Str.h>
+#include <Atomic/Container/HashMap.h>
+#include <Atomic/Container/List.h>
+#include <Atomic/Math/StringHash.h>
+
+#include <TurboBadger/tb_select.h>
+#include <TurboBadger/tb_editfield.h>
+#include <TurboBadger/tb_style_edit.h>
+
+using namespace Atomic;
+
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+// per document autocompletion
+class JSAutocomplete
+{
+public:
+
+    JSAutocomplete(TBEditField* editField);
+
+    ~JSAutocomplete();
+
+    void Show();
+    void Hide();
+    bool Visible();
+
+    void SetPosition(const TBPoint& pos);
+    bool UpdateCompletions(const String& value);
+
+    bool OnEvent(const TBWidgetEvent &ev);
+
+    void UpdateLocals();
+
+private:
+
+    HashMap<StringHash, List<String> > locals_;
+
+    TBEditField* editField_;
+    TBStyleEdit* styleEdit_;
+    TBSelectList* autoList_;
+    TBGenericStringItemSource autoSource_;
+    String currentValue_;
+
+};
+
+
+}

+ 26 - 0
Source/AtomicEditorWork/Javascript/JSErrorChecker.cpp

@@ -0,0 +1,26 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/Core/Context.h>
+#include "JSErrorChecker.h"
+
+namespace AtomicEditor
+{
+
+/// Construct.
+JSErrorChecker::JSErrorChecker(Context* context, duk_context *ctx) :
+    Object(context),
+    ctx_(ctx)
+{
+
+}
+
+/// Destruct.
+JSErrorChecker::~JSErrorChecker()
+{
+
+}
+
+}

+ 33 - 0
Source/AtomicEditorWork/Javascript/JSErrorChecker.h

@@ -0,0 +1,33 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Duktape/duktape.h>
+
+#include <Atomic/Core/Object.h>
+
+using namespace Atomic;
+
+namespace AtomicEditor
+{
+
+class JSErrorChecker : public Object
+{
+    OBJECT(JSErrorChecker);
+
+public:
+
+    /// Construct.
+    JSErrorChecker(Context* context, duk_context* ctx);
+    /// Destruct.
+    ~JSErrorChecker();
+
+private:
+
+    duk_context* ctx_;
+
+};
+
+}

+ 303 - 0
Source/AtomicEditorWork/Javascript/JSSpiderMonkeyVM.cpp

@@ -0,0 +1,303 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#ifdef USE_SPIDERMONKEY
+
+#include "AtomicEditor.h"
+#include "/Users/josh/Desktop/SpiderMonkey/include/mozjs-37a1/jsapi.h"
+#include "js/RootingAPI.h"
+#include "JSSpiderMonkeyVM.h"
+
+using namespace JS;
+
+#include "Context.h"
+#include "FileSystem.h"
+#include "ResourceCache.h"
+#include "JSFile.h"
+
+static RootedObject* __global = NULL;
+
+// The class of the global object.
+static JSClass globalClass = {
+    "global",
+    JSCLASS_GLOBAL_FLAGS,
+    JS_PropertyStub,
+    JS_DeletePropertyStub,
+    JS_PropertyStub,
+    JS_StrictPropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    nullptr, nullptr, nullptr, nullptr,
+    JS_GlobalObjectTraceHook
+};
+
+// The error reporter callback.
+void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
+    fprintf(stderr, "%s:%u:%s\n",
+            report->filename ? report->filename : "[no filename]",
+            (unsigned int) report->lineno,
+            message);
+}
+
+
+namespace AtomicEditor
+{
+
+
+
+WeakPtr<JSSpiderMonkeyVM> JSSpiderMonkeyVM::instance_;
+
+static bool
+print(JSContext *cx, unsigned argc, jsval *vp)
+{
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+    for (unsigned i = 0; i < args.length(); i++) {
+        JSString *str = JS::ToString(cx, args[i]);
+        if (!str)
+            return false;
+        char *bytes = JS_EncodeString(cx, str);
+        if (!bytes)
+            return false;
+        printf("%s%s", i ? " " : "", bytes);
+        JS_free(cx, bytes);
+    }
+
+    putchar('\n');
+    fflush(stdout);
+    args.rval().setUndefined();
+    return true;
+}
+
+JSSpiderMonkeyVM::JSSpiderMonkeyVM(Context* context) :
+    Object(context), runtime_(0), jscontext_(0),
+    autorequest_(0), autocompartment_(0)
+{
+    context_->RegisterSubsystem(this);
+    instance_ = this;
+
+    // Initialize the JS engine.
+    if (!JS_Init())
+    {
+        assert(0);
+    }
+
+    // Create a JS runtime.
+    runtime_ = JS_NewRuntime(32L * 1024L * 1024L);
+
+    if (runtime_)
+    {
+        jscontext_ = JS_NewContext(runtime_, 8192);
+        if (jscontext_)
+        {
+            JS_SetErrorReporter(runtime_, reportError);
+        }
+        else
+        {
+            assert(0);
+        }
+
+    }
+    else
+    {
+        assert(0);
+    }
+
+    // Enter a request before running anything in the context.
+    autorequest_ = new JSAutoRequest(jscontext_);
+
+    // Create the global object and a new compartment.
+    __global = new RootedObject(jscontext_);
+    *__global = JS_NewGlobalObject(jscontext_, &globalClass, nullptr,
+                                   JS::DontFireOnNewGlobalHook);
+    // Enter the new global object's compartment.
+    autocompartment_ = new JSAutoCompartment(jscontext_, *__global);
+
+    // Populate the global object with the standard globals, like Object and
+    // Array.
+    if (!JS_InitStandardClasses(jscontext_, *__global))
+    {
+        assert(0);
+    }
+
+    if (!JS_InitReflect(jscontext_, *__global))
+    {
+        assert(0);
+    }
+
+    JS_DefineFunction(jscontext_, *__global, "print", (JSNative) print, 0, 0);
+
+    ExecuteFile("AtomicEditor/javascript/modules/acorn_spidermonkey.js");
+
+}
+
+JSSpiderMonkeyVM::~JSSpiderMonkeyVM()
+{
+    instance_ = NULL;
+
+    if (autocompartment_)
+        delete autocompartment_;
+
+    if (__global)
+    {
+        delete __global;
+        __global = NULL;
+    }
+
+    if (autorequest_)
+        delete autorequest_;
+
+    // Shut everything down.
+    if (context_)
+        JS_DestroyContext(jscontext_);
+    if (runtime_)
+        JS_DestroyRuntime(runtime_);
+
+    JS_ShutDown();
+
+}
+
+bool JSSpiderMonkeyVM::ExecuteFile(const String& path)
+{
+    SharedPtr<JSFile> jsfile;
+    jsfile = GetSubsystem<ResourceCache>()->GetResource<JSFile>(path);
+
+    JS::RootedValue returnValue(jscontext_);
+
+    OwningCompileOptions options(jscontext_);
+
+    options.setFileAndLine(jscontext_, path.CString(), 0);
+    options.setCompileAndGo(true);
+    bool ok = JS::Evaluate(jscontext_, *__global, options,
+                           jsfile->GetSource(), strlen(jsfile->GetSource()), &returnValue);
+
+    return ok;
+
+}
+
+bool JSSpiderMonkeyVM::ParseJavascriptToJSON(const char* source, String& json)
+{
+    json.Clear();
+
+    bool ok = true;
+
+    JS::Rooted<JS::Value> v(jscontext_);
+    JS::RootedValue returnValue(jscontext_);
+    JS::RootedValue jsource(jscontext_, JS::StringValue(JS_NewStringCopyZ(jscontext_, source)));
+    JS_CallFunctionName(jscontext_, *__global, "__atomic_parse_to_json", HandleValueArray(jsource), &returnValue);
+
+    JS::RootedString hm(jscontext_);
+    hm = JS::ToString(jscontext_, returnValue);
+
+    const char* strjson = JS_EncodeStringToUTF8(jscontext_, hm);
+
+    json = strjson;
+
+    //printf("%s\n", strjson);
+
+    JS_free(jscontext_, (void *) strjson);
+
+    return ok;
+
+}
+
+
+bool JSSpiderMonkeyVM::ParseJavascriptToJSONWithSpiderMonkeyReflectAPI(const char* source, String& json)
+{
+    json.Clear();
+
+    bool ok = true;
+
+    JS::Rooted<JS::Value> v(jscontext_);
+    JS::RootedValue returnValue(jscontext_);
+    JS::RootedValue jsource(jscontext_, JS::StringValue(JS_NewStringCopyZ(jscontext_, source)));
+
+    // get the Reflect object
+    if (!JS_GetProperty(jscontext_, *__global, "Reflect", &v))
+        return false;
+
+    JS::RootedObject reflectObject(jscontext_);
+    JS_ValueToObject(jscontext_, v, &reflectObject);
+
+    JS_CallFunctionName(jscontext_, reflectObject, "parse", HandleValueArray(jsource), &returnValue);
+
+    JS::RootedObject jsonObject(jscontext_);
+    if (!JS_GetProperty(jscontext_, *__global, "JSON", &v))
+        return false;
+
+    JS_ValueToObject(jscontext_, v, &jsonObject);
+
+    // pretty printed, can remove for speed
+    JS::AutoValueArray<3> args(jscontext_);
+    args[0].set(returnValue);
+    args[1].set(JS::NullValue());
+    args[2].setNumber(3.0);
+
+    JS::RootedValue jjson(jscontext_);
+    JS_CallFunctionName(jscontext_, jsonObject, "stringify", HandleValueArray(args), &jjson);
+
+    JS::RootedString hm(jscontext_);
+    hm = JS::ToString(jscontext_, jjson);
+
+    const char* strjson = JS_EncodeStringToUTF8(jscontext_, hm);
+
+    json = strjson;
+
+    //printf("%s\n", strjson);
+
+    JS_free(jscontext_, (void *) strjson);
+
+    return ok;
+
+}
+
+bool JSSpiderMonkeyVM::ReadZeroTerminatedSourceFile(const String& path, String& source)
+{
+    File file(instance_->GetContext());
+    file.Open(path);
+
+    unsigned size = file.GetSize();
+
+    SharedArrayPtr<char> data;
+    data = new char[size + 1];
+    data[size] = '\0';
+    file.Read(data, size);
+
+    source = data;
+
+    return true;
+
+}
+
+
+}
+
+int run(JSContext *cx) {
+    // Enter a request before running anything in the context.
+    JSAutoRequest ar(cx);
+
+    // Create the global object and a new compartment.
+    RootedObject global(cx);
+    global = JS_NewGlobalObject(cx, &globalClass, nullptr,
+                                JS::DontFireOnNewGlobalHook);
+    if (!global)
+        return 1;
+
+    // Enter the new global object's compartment.
+    JSAutoCompartment ac(cx, global);
+
+    // Populate the global object with the standard globals, like Object and
+    // Array.
+    if (!JS_InitStandardClasses(cx, global))
+        return 1;
+
+    // Your application code here. This may include JSAPI calls to create your
+    // own custom JS objects and run scripts.
+
+    return 0;
+}
+
+#endif
+

+ 56 - 0
Source/AtomicEditorWork/Javascript/JSSpiderMonkeyVM.h

@@ -0,0 +1,56 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#ifdef USE_SPIDERMONKEY
+#pragma once
+
+#include "Object.h"
+
+using namespace Atomic;
+
+struct JSRuntime;
+struct JSContext;
+class JSAutoRequest;
+class JSAutoCompartment;
+
+namespace AtomicEditor
+{
+
+// Notes: SpiderMonkey Reflect API is giving some bad loc's, which seems to be an issue with the
+// C++ generator not passing in tokens for some nodes (MemberExpression.property for example)
+// probably should use http://esprima.org/
+class JSSpiderMonkeyVM : public Object
+{
+    OBJECT(JSSpiderMonkeyVM);
+
+public:
+    /// Construct.
+    JSSpiderMonkeyVM(Context* context);
+    /// Destruct.
+    ~JSSpiderMonkeyVM();
+
+    bool ExecuteFile(const String& path);
+
+    bool ParseJavascriptToJSON(const char* source, String& json);
+
+    bool ParseJavascriptToJSONWithSpiderMonkeyReflectAPI(const char* source, String& json);
+
+private:
+
+    bool ReadZeroTerminatedSourceFile(const String& path, String& source);
+
+    // for access within duktape callbacks
+    static WeakPtr<JSSpiderMonkeyVM> instance_;
+
+    JSRuntime* runtime_;
+    JSContext* jscontext_;
+    JSAutoRequest* autorequest_;
+    JSAutoCompartment* autocompartment_;
+
+};
+
+
+}
+
+#endif

+ 5 - 0
Source/AtomicEditorWork/Javascript/JSTheme.cpp

@@ -0,0 +1,5 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"

+ 182 - 0
Source/AtomicEditorWork/Javascript/JSTheme.h

@@ -0,0 +1,182 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+namespace AtomicEditor
+{
+
+// //http://hilightjs.org
+
+enum JSThemeColor
+{
+    JSTHEME_NORMAL,
+    JSTHEME_LITERAL_STRING,
+    JSTHEME_LITERAL_BOOLEAN,
+    JSTHEME_LITERAL_NUMBER,
+    JSTHEME_LITERAL_REGEX,
+    JSTHEME_LITERAL_NULL,
+    JSTHEME_KEYWORD,
+    JSTHEME_OPERATOR,
+    JSTHEME_FUNCTION,
+    JSTHEME_VAR,
+    JSTHEME_CODE,
+    JSTHEME_COMMENT,
+    JSTHEME_FUNCTIONDECLARG
+};
+
+
+}
+/*
+
+.hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: #23241f;
+  -webkit-text-size-adjust: none;
+}
+
+.hljs,
+.hljs-tag,
+.css .hljs-rules,
+.css .hljs-value,
+.aspectj .hljs-function,
+.css .hljs-function
+.hljs-preprocessor,
+.hljs-pragma {
+  color: #f8f8f2;
+}
+
+.hljs-strongemphasis,
+.hljs-strong,
+.hljs-emphasis {
+  color: #a8a8a2;
+}
+
+.hljs-bullet,
+.hljs-blockquote,
+.hljs-horizontal_rule,
+.hljs-number,
+.hljs-regexp,
+.alias .hljs-keyword,
+.hljs-literal,
+.hljs-hexcolor {
+  color: #ae81ff;
+}
+
+.hljs-tag .hljs-value,
+.hljs-code,
+.hljs-title,
+.css .hljs-class,
+.hljs-class .hljs-title:last-child {
+  color: #a6e22e;
+}
+
+.hljs-link_url {
+  font-size: 80%;
+}
+
+.hljs-strong,
+.hljs-strongemphasis {
+  font-weight: bold;
+}
+
+.hljs-emphasis,
+.hljs-strongemphasis,
+.hljs-class .hljs-title:last-child,
+.hljs-typename {
+  font-style: italic;
+}
+
+.hljs-keyword,
+.ruby .hljs-class .hljs-keyword:first-child,
+.ruby .hljs-function .hljs-keyword,
+.hljs-function,
+.hljs-change,
+.hljs-winutils,
+.hljs-flow,
+.nginx .hljs-title,
+.tex .hljs-special,
+.hljs-header,
+.hljs-attribute,
+.hljs-symbol,
+.hljs-symbol .hljs-string,
+.hljs-tag .hljs-title,
+.hljs-value,
+.alias .hljs-keyword:first-child,
+.css .hljs-tag,
+.css .unit,
+.css .hljs-important {
+  color: #f92672;
+}
+
+.hljs-function .hljs-keyword,
+.hljs-class .hljs-keyword:first-child,
+.hljs-aspect .hljs-keyword:first-child,
+.hljs-constant,
+.hljs-typename,
+.css .hljs-attribute {
+  color: #66d9ef;
+}
+
+.hljs-variable,
+.hljs-params,
+.hljs-class .hljs-title,
+.hljs-aspect .hljs-title {
+  color: #f8f8f2;
+}
+
+.hljs-string,
+.css .hljs-id,
+.hljs-subst,
+.hljs-type,
+.ruby .hljs-class .hljs-parent,
+.hljs-built_in,
+.django .hljs-template_tag,
+.django .hljs-variable,
+.smalltalk .hljs-class,
+.django .hljs-filter .hljs-argument,
+.smalltalk .hljs-localvars,
+.smalltalk .hljs-array,
+.hljs-attr_selector,
+.hljs-pseudo,
+.hljs-addition,
+.hljs-stream,
+.hljs-envvar,
+.apache .hljs-tag,
+.apache .hljs-cbracket,
+.tex .hljs-command,
+.hljs-prompt,
+.hljs-link_label,
+.hljs-link_url {
+  color: #e6db74;
+}
+
+.hljs-comment,
+.hljs-javadoc,
+.hljs-annotation,
+.hljs-decorator,
+.hljs-pi,
+.hljs-doctype,
+.hljs-deletion,
+.hljs-shebang,
+.apache .hljs-sqbracket,
+.tex .hljs-formula {
+  color: #75715e;
+}
+
+.coffeescript .javascript,
+.javascript .xml,
+.tex .hljs-formula,
+.xml .javascript,
+.xml .vbscript,
+.xml .css,
+.xml .hljs-cdata,
+.xml .php,
+.php .xml {
+  opacity: 0.5;
+}
+
+*/

+ 12 - 0
Source/AtomicJS/Javascript/JSFileSystem.cpp

@@ -45,6 +45,15 @@ static int Atomic_AddTrailingSlash(duk_context* ctx)
     return 1;
 }
 
+static int Atomic_GetExtenstion(duk_context* ctx)
+{
+    String path = duk_require_string(ctx, 0);
+
+    duk_push_string(ctx, GetExtension(path).CString());
+
+    return 1;
+}
+
 static int Atomic_GetParentPath(duk_context* ctx)
 {
     String path = duk_require_string(ctx, 0);
@@ -70,6 +79,9 @@ void jsapi_init_filesystem(JSVM* vm)
     duk_push_c_function(ctx, Atomic_GetParentPath, 1);
     duk_put_prop_string(ctx, -2, "getParentPath");
 
+    duk_push_c_function(ctx, Atomic_GetExtenstion, 1);
+    duk_put_prop_string(ctx, -2, "getExtension");
+
     duk_pop(ctx); // pop Atomic object
 }
 

+ 1 - 0
Source/AtomicJS/Javascript/JSUI.cpp

@@ -50,6 +50,7 @@ JSUI::JSUI(Context* context) : Object(context),
     uiTypes_["UIListView"] = true;
     uiTypes_["UIMessageWindow"] = true;
     uiTypes_["UISkinImage"] = true;
+    uiTypes_["UITabContainer"] = true;
 
 }
 

+ 1 - 1
Source/AtomicJS/Packages/Atomic/UI.json

@@ -7,7 +7,7 @@
 								"UISelectItem", "UISelectItemSource", "UIMenuWindow", "UIEditField",
 								"UIImageWidget", "UIClickLabel", "UICheckBox", "UIMenuItem", "UIMenuItemSource",
 								"UISelectList", "UIListView", "UIMessageWindow", "UILayoutParams", "UIFontDescription",
-								"UISkinImage"],
+								"UISkinImage", "UITabContainer"],
 	"overloads" : {
 	}
 }

+ 2 - 2
Source/AtomicJS/Packages/Editor/Editor.json

@@ -1,5 +1,5 @@
 {
 	"name" : "Editor",
-	"sources" : ["Source/AtomicEditorWork/JSTest"],
-	"classes" : ["MyJSClass"]
+	"sources" : ["Source/AtomicEditorWork/JSTest", "Source/AtomicEditorWork/Editors"],
+	"classes" : ["MyJSClass", "ResourceEditor", "JSResourceEditor"]
 }

Some files were not shown because too many files changed in this diff